From 3e45412327a2654a77944249962b3652e6142299 Mon Sep 17 00:00:00 2001 From: Ondřej Surý Date: Mon, 17 Jan 2011 12:40:45 +0100 Subject: Imported Upstream version 2011.01.12 --- .hgignore | 45 - AUTHORS | 40 + CONTRIBUTORS | 54 +- LICENSE | 69 +- PATENTS | 22 + README | 20 + doc/ExpressivenessOfGo.pdf | Bin 0 -> 859406 bytes doc/Makefile | 4 +- doc/all.css | 197 + doc/button_background.png | Bin 0 -> 126 bytes doc/code.html | 48 +- doc/codelab/wiki/Makefile | 15 +- doc/codelab/wiki/final-noclosure.go | 34 +- doc/codelab/wiki/final-noerror.go | 8 +- doc/codelab/wiki/final-parsetemplate.go | 32 +- doc/codelab/wiki/final-template.go | 16 +- doc/codelab/wiki/final.go | 30 +- doc/codelab/wiki/http-sample.go | 4 +- doc/codelab/wiki/index.html | 147 +- doc/codelab/wiki/notemplate.go | 8 +- doc/codelab/wiki/part2.go | 4 +- doc/codelab/wiki/srcextract.go | 2 +- doc/codelab/wiki/wiki.html | 35 +- doc/codereview_with_mq.html | 6 +- doc/codewalk/urlpoll.go | 6 +- doc/community.html | 53 + doc/contrib.html | 45 + doc/contribute.html | 12 +- doc/devel/release.html | 1143 ++++ doc/devel/roadmap.html | 51 +- doc/docs.html | 195 + doc/effective_go.html | 161 +- doc/frontpage.css | 140 + doc/gccgo_install.html | 42 +- doc/go_faq.html | 894 ++- doc/go_for_cpp_programmers.html | 4 +- doc/go_lang_faq.html | 491 -- doc/go_learning.html | 141 - doc/go_mem.html | 35 +- doc/go_programming_faq.html | 307 - doc/go_spec.html | 497 +- doc/go_tutorial.html | 109 +- doc/go_tutorial.txt | 77 +- doc/godocs.js | 23 +- doc/ie.css | 1 + doc/install.html | 392 +- doc/logo.png | Bin 0 -> 1938 bytes doc/play_overlay.png | Bin 0 -> 1703 bytes doc/playground.html | 27 + doc/progs/cat.go | 6 +- doc/progs/cat_rot13.go | 12 +- doc/progs/file.go | 8 +- doc/progs/helloworld3.go | 4 +- doc/progs/run | 12 +- doc/progs/sortmain.go | 2 +- doc/progs/sum.go | 4 +- doc/root.html | 210 +- doc/style.css | 282 - doc/talks/gofrontend-gcc-summit-2010.pdf | Bin 0 -> 125185 bytes doc/talks/io2010/balance.go | 8 +- doc/video-001.png | Bin 0 -> 29228 bytes doc/video-002.png | Bin 0 -> 22027 bytes doc/video-003.png | Bin 0 -> 11189 bytes doc/video-004.png | Bin 0 -> 22713 bytes doc/video-snap.jpg | Bin 3269 -> 0 bytes include/libc.h | 8 +- include/u.h | 5 + lib/codereview/codereview.py | 2605 +++---- lib/godoc/godoc.html | 178 +- lib/godoc/package.html | 38 +- lib/godoc/package.txt | 20 +- lib/godoc/search.html | 49 +- lib/godoc/search.txt | 24 +- lib/godoc/source.html | 23 - misc/arm/a | 23 +- misc/bash/go | 2 +- misc/bbedit/Go.plist | 11 + misc/cgo/gmp/Makefile | 2 +- misc/cgo/life/Makefile | 20 +- misc/cgo/life/c-life.c | 2 + misc/cgo/life/golden.out | 17 + misc/cgo/life/life.go | 2 +- misc/cgo/life/life.h | 1 + misc/cgo/life/main.go | 2 +- misc/cgo/life/test.bash | 11 + misc/cgo/stdio/Makefile | 11 +- misc/cgo/stdio/align.go | 78 + misc/cgo/stdio/chain.go | 4 +- misc/cgo/stdio/fib.go | 2 +- misc/cgo/stdio/file.go | 28 +- misc/cgo/stdio/hello.go | 23 +- misc/cgo/stdio/test.bash | 5 +- misc/cgo/stdio/test.go | 144 + misc/cgo/stdio/test1.go | 29 + misc/dashboard/README | 14 +- misc/dashboard/buildcontrol.py | 41 +- misc/dashboard/buildcron.sh | 4 +- misc/dashboard/builder.sh | 25 +- misc/dashboard/builder/Makefile | 14 + misc/dashboard/builder/doc.go | 54 + misc/dashboard/builder/exec.go | 65 + misc/dashboard/builder/hg.go | 54 + misc/dashboard/builder/http.go | 70 + misc/dashboard/builder/main.go | 340 + misc/dashboard/godashboard/package.py | 184 +- misc/dashboard/godashboard/project-edit.html | 10 +- misc/dashboard/godashboard/project-notify.txt | 2 +- misc/dashboard/godashboard/toutf8.py | 14 + misc/dashboard/googlecode_upload.py | 248 + misc/emacs/go-mode.el | 6 +- misc/fraise/go.plist | 93 + misc/fraise/readme.txt | 16 + misc/goplay/Makefile | 13 + misc/goplay/README | 1 + misc/goplay/doc.go | 25 + misc/goplay/goplay.go | 314 + misc/kate/go.xml | 2 + misc/vim/syntax/go.vim | 10 +- misc/zsh/go | 14 + pkg/~place-holder~ | 4 - src/Make.386 | 11 - src/Make.amd64 | 11 - src/Make.arm | 11 - src/Make.ccmd | 44 + src/Make.clib | 34 + src/Make.cmd | 15 +- src/Make.common | 23 +- src/Make.conf | 22 - src/Make.inc | 146 + src/Make.pkg | 100 +- src/all-arm.bash | 30 - src/all-nacl.bash | 48 - src/all.bash | 10 +- src/clean.bash | 19 +- src/cmd/5a/Makefile | 25 +- src/cmd/5a/a.h | 5 +- src/cmd/5a/a.y | 8 +- src/cmd/5a/lex.c | 10 +- src/cmd/5c/Makefile | 21 +- src/cmd/5c/list.c | 16 +- src/cmd/5c/mul.c | 4 +- src/cmd/5c/peep.c | 2 - src/cmd/5c/reg.c | 10 +- src/cmd/5c/swt.c | 42 +- src/cmd/5c/txt.c | 61 +- src/cmd/5g/Makefile | 25 +- src/cmd/5g/cgen.c | 232 +- src/cmd/5g/cgen64.c | 6 +- src/cmd/5g/galign.c | 1 - src/cmd/5g/gg.h | 6 +- src/cmd/5g/ggen.c | 87 +- src/cmd/5g/gobj.c | 6 +- src/cmd/5g/gsubr.c | 221 +- src/cmd/5g/list.c | 34 +- src/cmd/5g/opt.h | 2 +- src/cmd/5g/peep.c | 1511 ++++ src/cmd/5g/reg.c | 1329 ++++ src/cmd/5l/5.out.h | 8 +- src/cmd/5l/Makefile | 24 +- src/cmd/5l/asm.c | 1310 ++-- src/cmd/5l/doc.go | 4 +- src/cmd/5l/l.h | 143 +- src/cmd/5l/list.c | 146 +- src/cmd/5l/noop.c | 1371 ++-- src/cmd/5l/obj.c | 475 +- src/cmd/5l/optab.c | 63 +- src/cmd/5l/pass.c | 803 +-- src/cmd/5l/prof.c | 214 + src/cmd/5l/softfloat.c | 113 +- src/cmd/5l/span.c | 671 +- src/cmd/5l/thumb.c | 78 +- src/cmd/6a/Makefile | 23 +- src/cmd/6a/a.h | 9 +- src/cmd/6a/a.y | 14 +- src/cmd/6a/lex.c | 10 +- src/cmd/6c/Makefile | 21 +- src/cmd/6c/cgen.c | 10 +- src/cmd/6c/div.c | 4 +- src/cmd/6c/list.c | 15 +- src/cmd/6c/mul.c | 2 +- src/cmd/6c/peep.c | 2 - src/cmd/6c/reg.c | 12 +- src/cmd/6c/sgen.c | 4 + src/cmd/6c/swt.c | 29 +- src/cmd/6c/txt.c | 23 +- src/cmd/6g/Makefile | 21 +- src/cmd/6g/cgen.c | 257 +- src/cmd/6g/galign.c | 1 - src/cmd/6g/gg.h | 2 + src/cmd/6g/ggen.c | 85 +- src/cmd/6g/gobj.c | 4 +- src/cmd/6g/gsubr.c | 149 +- src/cmd/6g/list.c | 18 +- src/cmd/6g/reg.c | 20 +- src/cmd/6l/6.out.h | 6 +- src/cmd/6l/Makefile | 26 +- src/cmd/6l/asm.c | 1389 ++-- src/cmd/6l/doc.go | 4 +- src/cmd/6l/l.h | 140 +- src/cmd/6l/list.c | 73 +- src/cmd/6l/obj.c | 549 +- src/cmd/6l/optab.c | 6 +- src/cmd/6l/pass.c | 1112 +-- src/cmd/6l/prof.c | 171 + src/cmd/6l/span.c | 1282 ++-- src/cmd/8a/Makefile | 23 +- src/cmd/8a/a.h | 9 +- src/cmd/8a/a.y | 8 +- src/cmd/8a/lex.c | 10 +- src/cmd/8c/Makefile | 21 +- src/cmd/8c/cgen64.c | 4 +- src/cmd/8c/div.c | 4 +- src/cmd/8c/list.c | 30 +- src/cmd/8c/mul.c | 2 +- src/cmd/8c/reg.c | 10 +- src/cmd/8c/swt.c | 38 +- src/cmd/8c/txt.c | 11 +- src/cmd/8g/Makefile | 21 +- src/cmd/8g/cgen.c | 69 +- src/cmd/8g/galign.c | 1 - src/cmd/8g/ggen.c | 116 +- src/cmd/8g/gobj.c | 4 +- src/cmd/8g/gsubr.c | 14 +- src/cmd/8g/list.c | 14 +- src/cmd/8g/reg.c | 22 +- src/cmd/8l/8.out.h | 8 +- src/cmd/8l/Makefile | 29 +- src/cmd/8l/asm.c | 1328 ++-- src/cmd/8l/doc.go | 4 +- src/cmd/8l/l.h | 136 +- src/cmd/8l/list.c | 87 +- src/cmd/8l/obj.c | 505 +- src/cmd/8l/optab.c | 4 +- src/cmd/8l/pass.c | 1058 +-- src/cmd/8l/prof.c | 173 + src/cmd/8l/span.c | 1183 ++-- src/cmd/cc/Makefile | 21 +- src/cmd/cc/acid.c | 12 +- src/cmd/cc/cc.h | 3 +- src/cmd/cc/com.c | 6 +- src/cmd/cc/dcl.c | 39 +- src/cmd/cc/dpchk.c | 13 +- src/cmd/cc/lex.c | 10 +- src/cmd/cc/macbody | 2 +- src/cmd/cc/pgen.c | 8 +- src/cmd/cc/pickle.c | 10 +- src/cmd/cc/pswt.c | 4 +- src/cmd/cc/sub.c | 8 +- src/cmd/cgo/Makefile | 2 +- src/cmd/cgo/ast.go | 465 +- src/cmd/cgo/doc.go | 25 + src/cmd/cgo/gcc.go | 543 +- src/cmd/cgo/main.go | 256 +- src/cmd/cgo/out.go | 524 +- src/cmd/cgo/util.go | 51 +- src/cmd/clean.bash | 4 +- src/cmd/cov/Makefile | 22 +- src/cmd/cov/main.c | 2 +- src/cmd/ebnflint/Makefile | 4 +- src/cmd/ebnflint/ebnflint.go | 6 +- src/cmd/gc/Makefile | 23 +- src/cmd/gc/align.c | 76 +- src/cmd/gc/builtin.c.boot | 18 +- src/cmd/gc/closure.c | 2 +- src/cmd/gc/const.c | 22 +- src/cmd/gc/dcl.c | 20 +- src/cmd/gc/doc.go | 9 +- src/cmd/gc/export.c | 3 +- src/cmd/gc/gen.c | 20 +- src/cmd/gc/go.errors | 9 + src/cmd/gc/go.h | 48 +- src/cmd/gc/go.y | 174 +- src/cmd/gc/lex.c | 203 +- src/cmd/gc/mkbuiltin | 8 +- src/cmd/gc/mparith1.c | 42 +- src/cmd/gc/mparith2.c | 8 +- src/cmd/gc/mparith3.c | 30 +- src/cmd/gc/obj.c | 107 +- src/cmd/gc/print.c | 61 +- src/cmd/gc/range.c | 17 +- src/cmd/gc/reflect.c | 116 +- src/cmd/gc/runtime.go | 22 +- src/cmd/gc/select.c | 13 +- src/cmd/gc/sinit.c | 62 +- src/cmd/gc/subr.c | 177 +- src/cmd/gc/typecheck.c | 490 +- src/cmd/gc/unsafe.c | 20 +- src/cmd/gc/walk.c | 405 +- src/cmd/godefs/Makefile | 14 +- src/cmd/godefs/main.c | 10 +- src/cmd/godefs/stabs.c | 13 +- src/cmd/godoc/Makefile | 5 +- src/cmd/godoc/codewalk.go | 51 +- src/cmd/godoc/dirtrees.go | 341 + src/cmd/godoc/doc.go | 17 +- src/cmd/godoc/format.go | 342 + src/cmd/godoc/godoc.go | 1031 ++- src/cmd/godoc/index.go | 295 +- src/cmd/godoc/main.go | 80 +- src/cmd/godoc/mapping.go | 94 +- src/cmd/godoc/snippet.go | 53 +- src/cmd/godoc/spec.go | 37 +- src/cmd/godoc/utils.go | 109 + src/cmd/gofmt/Makefile | 6 +- src/cmd/gofmt/doc.go | 4 + src/cmd/gofmt/gofmt.go | 25 +- src/cmd/gofmt/rewrite.go | 61 +- src/cmd/gofmt/simplify.go | 67 + src/cmd/gofmt/test.sh | 5 +- src/cmd/gofmt/testdata/composites.golden | 104 + src/cmd/gofmt/testdata/composites.input | 104 + src/cmd/gofmt/testdata/test.sh | 65 + src/cmd/goinstall/Makefile | 2 +- src/cmd/goinstall/doc.go | 41 +- src/cmd/goinstall/download.go | 57 +- src/cmd/goinstall/main.go | 91 +- src/cmd/goinstall/make.go | 80 +- src/cmd/goinstall/parse.go | 77 +- src/cmd/gomake/doc.go | 36 + src/cmd/gopack/Makefile | 13 +- src/cmd/gopack/ar.c | 33 +- src/cmd/gopack/doc.go | 2 +- src/cmd/gotest/Makefile | 16 +- src/cmd/gotest/gotest | 73 +- src/cmd/gotest/gotry | 167 + src/cmd/govet/Makefile | 11 + src/cmd/govet/doc.go | 38 + src/cmd/govet/govet.go | 325 + src/cmd/goyacc/Makefile | 2 +- src/cmd/goyacc/goyacc.go | 41 +- src/cmd/goyacc/units.y | 8 +- src/cmd/hgpatch/Makefile | 2 +- src/cmd/hgpatch/main.go | 12 +- src/cmd/ld/data.c | 914 +++ src/cmd/ld/dwarf.c | 2547 +++++++ src/cmd/ld/dwarf.h | 29 + src/cmd/ld/dwarf_defs.h | 503 ++ src/cmd/ld/elf.c | 124 +- src/cmd/ld/elf.h | 8 +- src/cmd/ld/go.c | 182 +- src/cmd/ld/ldelf.c | 816 +++ src/cmd/ld/ldmacho.c | 794 +++ src/cmd/ld/lib.c | 546 +- src/cmd/ld/lib.h | 97 + src/cmd/ld/macho.c | 394 +- src/cmd/ld/macho.h | 19 +- src/cmd/ld/pe.c | 307 +- src/cmd/ld/pe.h | 14 +- src/cmd/ld/symtab.c | 259 + src/cmd/make.bash | 10 +- src/cmd/nm/Makefile | 18 +- src/cmd/nm/nm.c | 17 +- src/cmd/prof/Makefile | 24 +- src/cmd/prof/gopprof | 41 +- src/cmd/prof/main.c | 37 +- src/env.bash | 62 +- src/lib9/Makefile | 36 +- src/lib9/dirfwstat.c | 2 +- src/lib9/dirstat.c | 2 +- src/lib9/time.c | 4 +- src/lib9/utf/Makefile | 16 + src/lib9/utf/mkrunetype.c | 19 +- src/lib9/utf/runetype.c | 27 +- src/lib9/utf/runetypebody-5.2.0.c | 1541 +++++ src/libbio/Makefile | 20 +- src/libbio/bprint.c | 70 +- src/libbio/bseek.c | 2 +- src/libcgo/386.S | 61 - src/libcgo/Makefile | 42 - src/libcgo/amd64.S | 81 - src/libcgo/darwin_386.c | 144 - src/libcgo/darwin_amd64.c | 78 - src/libcgo/freebsd_386.c | 65 - src/libcgo/freebsd_amd64.c | 74 - src/libcgo/libcgo.h | 60 - src/libcgo/linux_386.c | 57 - src/libcgo/linux_amd64.c | 71 - src/libcgo/linux_arm.c | 1 - src/libcgo/nacl_386.c | 1 - src/libcgo/util.c | 46 - src/libcgo/windows_386.c | 45 - src/libcgo/windows_amd64.c | 45 - src/libmach/5obj.c | 3 + src/libmach/8db.c | 51 +- src/libmach/Makefile | 22 +- src/libmach/darwin.c | 11 +- src/libmach/executable.c | 62 +- src/libmach/linux.c | 58 +- src/libmach/windows.c | 59 + src/make.bash | 65 +- src/pkg/Makefile | 91 +- src/pkg/archive/tar/Makefile | 2 +- src/pkg/archive/tar/reader_test.go | 2 +- src/pkg/archive/tar/writer.go | 4 +- src/pkg/archive/tar/writer_test.go | 2 +- src/pkg/archive/zip/Makefile | 12 + src/pkg/archive/zip/reader.go | 278 + src/pkg/archive/zip/reader_test.go | 180 + src/pkg/archive/zip/struct.go | 33 + src/pkg/archive/zip/testdata/gophercolor16x16.png | Bin 0 -> 785 bytes src/pkg/archive/zip/testdata/r.zip | Bin 0 -> 440 bytes src/pkg/archive/zip/testdata/readme.notzip | Bin 0 -> 1905 bytes src/pkg/archive/zip/testdata/readme.zip | Bin 0 -> 1885 bytes src/pkg/archive/zip/testdata/test.zip | Bin 0 -> 1170 bytes src/pkg/asn1/Makefile | 2 +- src/pkg/asn1/asn1.go | 194 +- src/pkg/asn1/asn1_test.go | 193 +- src/pkg/asn1/common.go | 5 + src/pkg/asn1/marshal.go | 40 +- src/pkg/asn1/marshal_test.go | 55 +- src/pkg/big/Makefile | 2 +- src/pkg/big/arith.go | 228 +- src/pkg/big/arith_arm.s | 289 +- src/pkg/big/arith_test.go | 197 +- src/pkg/big/int.go | 69 +- src/pkg/big/int_test.go | 453 +- src/pkg/big/nat.go | 24 +- src/pkg/big/nat_test.go | 162 +- src/pkg/big/rat.go | 136 +- src/pkg/big/rat_test.go | 219 +- src/pkg/bufio/Makefile | 2 +- src/pkg/bufio/bufio.go | 174 +- src/pkg/bufio/bufio_test.go | 223 +- src/pkg/bytes/Makefile | 2 +- src/pkg/bytes/asm_amd64.s | 93 +- src/pkg/bytes/buffer.go | 78 +- src/pkg/bytes/buffer_test.go | 61 +- src/pkg/bytes/bytes.go | 287 +- src/pkg/bytes/bytes_test.go | 625 +- src/pkg/cmath/Makefile | 2 +- src/pkg/cmath/cmath_test.go | 110 +- src/pkg/compress/flate/Makefile | 2 +- src/pkg/compress/flate/deflate.go | 148 +- src/pkg/compress/flate/deflate_test.go | 139 +- src/pkg/compress/flate/huffman_code.go | 1 - src/pkg/compress/flate/inflate.go | 31 +- src/pkg/compress/gzip/Makefile | 2 +- src/pkg/compress/gzip/gunzip_test.go | 18 +- src/pkg/compress/zlib/Makefile | 2 +- src/pkg/compress/zlib/reader_test.go | 12 +- src/pkg/container/heap/Makefile | 2 +- src/pkg/container/list/Makefile | 2 +- src/pkg/container/list/list.go | 70 +- src/pkg/container/list/list_test.go | 24 +- src/pkg/container/ring/Makefile | 2 +- src/pkg/container/vector/Makefile | 8 +- src/pkg/container/vector/intvector.go | 21 +- src/pkg/container/vector/intvector_test.go | 101 +- src/pkg/container/vector/numbers_test.go | 2 +- src/pkg/container/vector/stringvector.go | 21 +- src/pkg/container/vector/stringvector_test.go | 101 +- src/pkg/container/vector/vector.go | 21 +- src/pkg/container/vector/vector_test.go | 101 +- src/pkg/crypto/aes/Makefile | 2 +- src/pkg/crypto/aes/aes_test.go | 22 +- src/pkg/crypto/aes/block.go | 4 +- src/pkg/crypto/aes/cipher.go | 4 +- src/pkg/crypto/aes/const.go | 16 +- src/pkg/crypto/block/Makefile | 2 +- src/pkg/crypto/block/cbc.go | 8 +- src/pkg/crypto/block/cbc_aes_test.go | 102 - src/pkg/crypto/block/cfb.go | 10 +- src/pkg/crypto/block/cfb_aes_test.go | 18 +- src/pkg/crypto/block/cipher.go | 13 +- src/pkg/crypto/block/cmac.go | 2 +- src/pkg/crypto/block/cmac_aes_test.go | 24 +- src/pkg/crypto/block/ctr.go | 4 +- src/pkg/crypto/block/ctr_aes_test.go | 110 - src/pkg/crypto/block/eax.go | 8 +- src/pkg/crypto/block/eax_aes_test.go | 20 +- src/pkg/crypto/block/ecb.go | 10 +- src/pkg/crypto/block/ecb_aes_test.go | 8 +- src/pkg/crypto/block/ecb_test.go | 4 +- src/pkg/crypto/block/ofb.go | 2 +- src/pkg/crypto/block/ofb_aes_test.go | 6 +- src/pkg/crypto/blowfish/Makefile | 2 +- src/pkg/crypto/blowfish/blowfish_test.go | 72 +- src/pkg/crypto/blowfish/cipher.go | 4 +- src/pkg/crypto/cast5/Makefile | 11 + src/pkg/crypto/cast5/cast5.go | 536 ++ src/pkg/crypto/cast5/cast5_test.go | 104 + src/pkg/crypto/cipher/Makefile | 16 + src/pkg/crypto/cipher/cbc.go | 78 + src/pkg/crypto/cipher/cbc_aes_test.go | 89 + src/pkg/crypto/cipher/cfb.go | 64 + src/pkg/crypto/cipher/cfb_test.go | 35 + src/pkg/crypto/cipher/cipher.go | 63 + src/pkg/crypto/cipher/common_test.go | 28 + src/pkg/crypto/cipher/ctr.go | 51 + src/pkg/crypto/cipher/ctr_aes_test.go | 101 + src/pkg/crypto/cipher/io.go | 57 + src/pkg/crypto/cipher/ocfb.go | 112 + src/pkg/crypto/cipher/ocfb_test.go | 39 + src/pkg/crypto/elliptic/Makefile | 11 + src/pkg/crypto/elliptic/elliptic.go | 376 + src/pkg/crypto/elliptic/elliptic_test.go | 331 + src/pkg/crypto/hmac/Makefile | 2 +- src/pkg/crypto/hmac/hmac.go | 44 +- src/pkg/crypto/hmac/hmac_test.go | 127 +- src/pkg/crypto/md4/Makefile | 2 +- src/pkg/crypto/md4/md4.go | 5 +- src/pkg/crypto/md4/md4_test.go | 62 +- src/pkg/crypto/md5/Makefile | 2 +- src/pkg/crypto/md5/md5.go | 5 +- src/pkg/crypto/md5/md5_test.go | 62 +- src/pkg/crypto/ocsp/Makefile | 11 + src/pkg/crypto/ocsp/ocsp.go | 203 + src/pkg/crypto/ocsp/ocsp_test.go | 97 + src/pkg/crypto/openpgp/armor/Makefile | 12 + src/pkg/crypto/openpgp/armor/armor.go | 220 + src/pkg/crypto/openpgp/armor/armor_test.go | 97 + src/pkg/crypto/openpgp/armor/encode.go | 162 + src/pkg/crypto/openpgp/error/Makefile | 11 + src/pkg/crypto/openpgp/error/error.go | 46 + src/pkg/crypto/rand/Makefile | 16 +- src/pkg/crypto/rand/rand.go | 113 +- src/pkg/crypto/rand/rand_unix.go | 125 + src/pkg/crypto/rand/rand_windows.go | 43 + src/pkg/crypto/rc4/Makefile | 2 +- src/pkg/crypto/rc4/rc4.go | 10 +- src/pkg/crypto/rc4/rc4_test.go | 12 +- src/pkg/crypto/ripemd160/Makefile | 2 +- src/pkg/crypto/ripemd160/ripemd160.go | 5 +- src/pkg/crypto/ripemd160/ripemd160_test.go | 16 +- src/pkg/crypto/rsa/Makefile | 2 +- src/pkg/crypto/rsa/pkcs1v15.go | 20 +- src/pkg/crypto/rsa/pkcs1v15_test.go | 36 +- src/pkg/crypto/rsa/rsa_test.go | 30 +- src/pkg/crypto/sha1/Makefile | 2 +- src/pkg/crypto/sha1/sha1.go | 5 +- src/pkg/crypto/sha1/sha1_test.go | 62 +- src/pkg/crypto/sha256/Makefile | 2 +- src/pkg/crypto/sha256/sha256.go | 5 +- src/pkg/crypto/sha256/sha256_test.go | 124 +- src/pkg/crypto/sha512/Makefile | 2 +- src/pkg/crypto/sha512/sha512.go | 5 +- src/pkg/crypto/sha512/sha512_test.go | 124 +- src/pkg/crypto/subtle/Makefile | 2 +- src/pkg/crypto/subtle/constant_time_test.go | 16 +- src/pkg/crypto/tls/Makefile | 4 +- src/pkg/crypto/tls/ca_set.go | 50 +- src/pkg/crypto/tls/cipher_suites.go | 102 + src/pkg/crypto/tls/common.go | 176 +- src/pkg/crypto/tls/conn.go | 232 +- src/pkg/crypto/tls/conn_test.go | 52 + src/pkg/crypto/tls/generate_cert.go | 70 + src/pkg/crypto/tls/handshake_client.go | 215 +- src/pkg/crypto/tls/handshake_client_test.go | 211 + src/pkg/crypto/tls/handshake_messages.go | 367 +- src/pkg/crypto/tls/handshake_messages_test.go | 37 + src/pkg/crypto/tls/handshake_server.go | 189 +- src/pkg/crypto/tls/handshake_server_test.go | 443 +- src/pkg/crypto/tls/key_agreement.go | 246 + src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py | 55 + src/pkg/crypto/tls/prf.go | 39 +- src/pkg/crypto/tls/prf_test.go | 18 +- src/pkg/crypto/tls/tls.go | 108 +- src/pkg/crypto/twofish/Makefile | 11 + src/pkg/crypto/twofish/twofish.go | 358 + src/pkg/crypto/twofish/twofish_test.go | 129 + src/pkg/crypto/x509/Makefile | 2 +- src/pkg/crypto/x509/x509.go | 222 +- src/pkg/crypto/x509/x509_test.go | 43 +- src/pkg/crypto/xtea/Makefile | 2 +- src/pkg/crypto/xtea/block.go | 4 +- src/pkg/crypto/xtea/cipher.go | 4 +- src/pkg/crypto/xtea/xtea_test.go | 28 +- src/pkg/debug/dwarf/Makefile | 2 +- src/pkg/debug/dwarf/type.go | 26 +- src/pkg/debug/elf/Makefile | 2 +- src/pkg/debug/elf/elf.go | 917 +-- src/pkg/debug/elf/elf_test.go | 44 +- src/pkg/debug/elf/file.go | 161 +- src/pkg/debug/elf/file_test.go | 150 +- src/pkg/debug/gosym/Makefile | 2 +- src/pkg/debug/gosym/pclinetest.s | 35 +- src/pkg/debug/gosym/pclntab_test.go | 2 +- src/pkg/debug/macho/Makefile | 2 +- src/pkg/debug/macho/file.go | 167 +- src/pkg/debug/macho/file_test.go | 6 +- src/pkg/debug/macho/macho.go | 90 +- src/pkg/debug/pe/Makefile | 12 + src/pkg/debug/pe/file.go | 231 + src/pkg/debug/pe/file_test.go | 99 + src/pkg/debug/pe/pe.go | 51 + src/pkg/debug/pe/testdata/gcc-386-mingw-exec | Bin 0 -> 29941 bytes src/pkg/debug/pe/testdata/gcc-386-mingw-obj | Bin 0 -> 2372 bytes src/pkg/debug/pe/testdata/hello.c | 8 + src/pkg/debug/proc/Makefile | 2 +- src/pkg/debug/proc/proc_linux.go | 29 +- src/pkg/debug/proc/proc_nacl.go | 20 - src/pkg/debug/proc/regs_linux_386.go | 24 +- src/pkg/debug/proc/regs_linux_amd64.go | 2 +- src/pkg/debug/proc/regs_nacl_386.go | 5 - src/pkg/deps.bash | 7 +- src/pkg/ebnf/Makefile | 2 +- src/pkg/ebnf/ebnf.go | 91 +- src/pkg/ebnf/ebnf_test.go | 8 +- src/pkg/ebnf/parser.go | 68 +- src/pkg/encoding/ascii85/Makefile | 2 +- src/pkg/encoding/ascii85/ascii85_test.go | 8 +- src/pkg/encoding/base64/Makefile | 2 +- src/pkg/encoding/base64/base64_test.go | 50 +- src/pkg/encoding/binary/Makefile | 2 +- src/pkg/encoding/binary/binary.go | 59 +- src/pkg/encoding/binary/binary_test.go | 82 +- src/pkg/encoding/git85/Makefile | 2 +- src/pkg/encoding/git85/git_test.go | 8 +- src/pkg/encoding/hex/Makefile | 2 +- src/pkg/encoding/hex/hex.go | 2 +- src/pkg/encoding/hex/hex_test.go | 112 +- src/pkg/encoding/line/Makefile | 11 + src/pkg/encoding/line/line.go | 95 + src/pkg/encoding/line/line_test.go | 89 + src/pkg/encoding/pem/Makefile | 2 +- src/pkg/encoding/pem/pem.go | 13 +- src/pkg/encoding/pem/pem_test.go | 28 +- src/pkg/exec/Makefile | 16 +- src/pkg/exec/exec.go | 46 +- src/pkg/exec/exec_test.go | 46 +- src/pkg/exec/lp_unix.go | 45 + src/pkg/exec/lp_windows.go | 66 + src/pkg/exp/4s/4s.go | 79 - src/pkg/exp/4s/4s.html | 26 - src/pkg/exp/4s/5s.go | 9 - src/pkg/exp/4s/5s.html | 26 - src/pkg/exp/4s/Makefile | 20 - src/pkg/exp/4s/data.go | 142 - src/pkg/exp/4s/xs.go | 742 -- src/pkg/exp/bignum/Makefile | 14 - src/pkg/exp/bignum/arith.go | 121 - src/pkg/exp/bignum/arith_amd64.s | 41 - src/pkg/exp/bignum/bignum.go | 1024 --- src/pkg/exp/bignum/bignum_test.go | 681 -- src/pkg/exp/bignum/integer.go | 520 -- src/pkg/exp/bignum/nrdiv_test.go | 188 - src/pkg/exp/bignum/rational.go | 205 - src/pkg/exp/datafmt/Makefile | 2 +- src/pkg/exp/datafmt/datafmt.go | 8 +- src/pkg/exp/datafmt/datafmt_test.go | 10 +- src/pkg/exp/datafmt/parser.go | 38 +- src/pkg/exp/draw/Makefile | 4 +- src/pkg/exp/draw/arith.go | 155 - src/pkg/exp/draw/color.go | 105 - src/pkg/exp/draw/draw.go | 207 +- src/pkg/exp/draw/draw_test.go | 129 +- src/pkg/exp/draw/event.go | 66 +- src/pkg/exp/draw/x11/Makefile | 2 +- src/pkg/exp/draw/x11/conn.go | 170 +- src/pkg/exp/eval/Makefile | 16 +- src/pkg/exp/eval/bridge.go | 12 +- src/pkg/exp/eval/compiler.go | 14 +- src/pkg/exp/eval/eval_test.go | 49 +- src/pkg/exp/eval/expr.go | 206 +- src/pkg/exp/eval/expr1.go | 198 +- src/pkg/exp/eval/expr_test.go | 91 +- src/pkg/exp/eval/func.go | 15 +- src/pkg/exp/eval/gen.go | 126 +- src/pkg/exp/eval/main.go | 12 +- src/pkg/exp/eval/scope.go | 40 +- src/pkg/exp/eval/stmt.go | 101 +- src/pkg/exp/eval/test.bash | 6 +- src/pkg/exp/eval/type.go | 85 +- src/pkg/exp/eval/typec.go | 54 +- src/pkg/exp/eval/util.go | 39 - src/pkg/exp/eval/value.go | 18 +- src/pkg/exp/eval/world.go | 33 +- src/pkg/exp/iterable/Makefile | 12 - src/pkg/exp/iterable/array.go | 59 - src/pkg/exp/iterable/iterable.go | 344 - src/pkg/exp/iterable/iterable_test.go | 387 -- src/pkg/exp/nacl/README | 36 - src/pkg/exp/nacl/av/Makefile | 13 - src/pkg/exp/nacl/av/av.go | 299 - src/pkg/exp/nacl/av/event.go | 472 -- src/pkg/exp/nacl/av/image.go | 85 - src/pkg/exp/nacl/srpc/Makefile | 13 - src/pkg/exp/nacl/srpc/client.go | 210 - src/pkg/exp/nacl/srpc/msg.go | 526 -- src/pkg/exp/nacl/srpc/server.go | 201 - src/pkg/exp/ogle/Makefile | 6 +- src/pkg/exp/ogle/cmd.go | 12 +- src/pkg/exp/ogle/process.go | 14 +- src/pkg/exp/ogle/rtype.go | 4 +- src/pkg/exp/ogle/vars.go | 4 +- src/pkg/exp/spacewar/Makefile | 18 - src/pkg/exp/spacewar/code.go | 7556 --------------------- src/pkg/exp/spacewar/pdp1.go | 389 -- src/pkg/exp/spacewar/spacewar.go | 196 - src/pkg/exp/spacewar/spacewar.html | 21 - src/pkg/expvar/Makefile | 2 +- src/pkg/expvar/expvar.go | 26 +- src/pkg/expvar/expvar_test.go | 24 +- src/pkg/flag/Makefile | 2 +- src/pkg/flag/export_test.go | 32 + src/pkg/flag/flag.go | 114 +- src/pkg/flag/flag_test.go | 24 +- src/pkg/fmt/Makefile | 3 +- src/pkg/fmt/doc.go | 163 + src/pkg/fmt/fmt_test.go | 612 +- src/pkg/fmt/format.go | 11 + src/pkg/fmt/print.go | 384 +- src/pkg/fmt/scan.go | 138 +- src/pkg/fmt/scan_test.go | 422 +- src/pkg/go/ast/Makefile | 3 +- src/pkg/go/ast/ast.go | 562 +- src/pkg/go/ast/filter.go | 41 +- src/pkg/go/ast/print.go | 217 + src/pkg/go/ast/scope.go | 259 +- src/pkg/go/ast/walk.go | 301 +- src/pkg/go/doc/Makefile | 2 +- src/pkg/go/doc/comment.go | 25 +- src/pkg/go/doc/doc.go | 74 +- src/pkg/go/parser/Makefile | 2 +- src/pkg/go/parser/interface.go | 80 +- src/pkg/go/parser/parser.go | 769 +-- src/pkg/go/parser/parser_test.go | 37 +- src/pkg/go/printer/Makefile | 2 +- src/pkg/go/printer/nodes.go | 257 +- src/pkg/go/printer/printer.go | 241 +- src/pkg/go/printer/printer_test.go | 24 +- src/pkg/go/printer/testdata/comments.golden | 32 + src/pkg/go/printer/testdata/comments.input | 34 + src/pkg/go/printer/testdata/declarations.golden | 115 +- src/pkg/go/printer/testdata/declarations.input | 83 +- src/pkg/go/printer/testdata/expressions.golden | 77 +- src/pkg/go/printer/testdata/expressions.input | 74 +- src/pkg/go/printer/testdata/expressions.raw | 77 +- src/pkg/go/printer/testdata/statements.golden | 81 +- src/pkg/go/printer/testdata/statements.input | 70 + src/pkg/go/scanner/Makefile | 2 +- src/pkg/go/scanner/scanner.go | 336 +- src/pkg/go/scanner/scanner_test.go | 496 +- src/pkg/go/token/Makefile | 3 +- src/pkg/go/token/position.go | 409 ++ src/pkg/go/token/position_test.go | 158 + src/pkg/go/token/token.go | 41 +- src/pkg/go/typechecker/Makefile | 13 + src/pkg/go/typechecker/scope.go | 119 + src/pkg/go/typechecker/testdata/test0.go | 94 + src/pkg/go/typechecker/testdata/test1.go | 13 + src/pkg/go/typechecker/testdata/test3.go | 38 + src/pkg/go/typechecker/testdata/test4.go | 11 + src/pkg/go/typechecker/typechecker.go | 484 ++ src/pkg/go/typechecker/typechecker_test.go | 167 + src/pkg/go/typechecker/universe.go | 38 + src/pkg/gob/Makefile | 4 +- src/pkg/gob/codec_test.go | 772 ++- src/pkg/gob/debug.go | 310 +- src/pkg/gob/decode.go | 488 +- src/pkg/gob/decoder.go | 121 +- src/pkg/gob/doc.go | 307 + src/pkg/gob/encode.go | 541 +- src/pkg/gob/encoder.go | 81 +- src/pkg/gob/encoder_test.go | 205 +- src/pkg/gob/error.go | 41 + src/pkg/gob/type.go | 265 +- src/pkg/gob/type_test.go | 12 +- src/pkg/hash/Makefile | 2 +- src/pkg/hash/adler32/Makefile | 2 +- src/pkg/hash/adler32/adler32_test.go | 64 +- src/pkg/hash/crc32/Makefile | 2 +- src/pkg/hash/crc32/crc32_test.go | 62 +- src/pkg/hash/crc64/Makefile | 2 +- src/pkg/hash/crc64/crc64.go | 2 +- src/pkg/hash/crc64/crc64_test.go | 62 +- src/pkg/html/Makefile | 15 + src/pkg/html/doc.go | 106 + src/pkg/html/entity.go | 2250 ++++++ src/pkg/html/entity_test.go | 26 + src/pkg/html/escape.go | 224 + src/pkg/html/parse.go | 666 ++ src/pkg/html/parse_test.go | 158 + src/pkg/html/token.go | 398 ++ src/pkg/html/token_test.go | 231 + src/pkg/http/Makefile | 2 +- src/pkg/http/client.go | 70 +- src/pkg/http/fs.go | 143 +- src/pkg/http/fs_test.go | 172 + src/pkg/http/lex_test.go | 10 +- src/pkg/http/pprof/Makefile | 2 +- src/pkg/http/pprof/pprof.go | 26 +- src/pkg/http/readrequest_test.go | 20 +- src/pkg/http/request.go | 113 +- src/pkg/http/request_test.go | 60 +- src/pkg/http/requestwrite_test.go | 26 +- src/pkg/http/response.go | 8 +- src/pkg/http/response_test.go | 46 +- src/pkg/http/responsewrite_test.go | 19 +- src/pkg/http/serve_test.go | 135 + src/pkg/http/server.go | 542 +- src/pkg/http/status.go | 6 + src/pkg/http/testdata/file | 1 + src/pkg/http/transfer.go | 23 +- src/pkg/http/triv.go | 77 +- src/pkg/http/url.go | 287 +- src/pkg/http/url_test.go | 377 +- src/pkg/image/Makefile | 4 +- src/pkg/image/color.go | 45 + src/pkg/image/format.go | 86 + src/pkg/image/geom.go | 223 + src/pkg/image/image.go | 482 +- src/pkg/image/jpeg/Makefile | 4 +- src/pkg/image/jpeg/reader.go | 33 +- src/pkg/image/names.go | 71 +- src/pkg/image/png/Makefile | 2 +- src/pkg/image/png/reader.go | 248 +- src/pkg/image/png/reader_test.go | 84 +- src/pkg/image/png/writer.go | 162 +- src/pkg/image/png/writer_test.go | 27 +- src/pkg/index/suffixarray/Makefile | 12 + src/pkg/index/suffixarray/qsufsort.go | 164 + src/pkg/index/suffixarray/suffixarray.go | 182 + src/pkg/index/suffixarray/suffixarray_test.go | 234 + src/pkg/io/Makefile | 3 +- src/pkg/io/io.go | 25 +- src/pkg/io/io_test.go | 76 + src/pkg/io/ioutil/Makefile | 2 +- src/pkg/io/ioutil/ioutil.go | 2 +- src/pkg/io/ioutil/ioutil_test.go | 12 +- src/pkg/io/ioutil/tempfile_test.go | 2 +- src/pkg/io/multi.go | 60 + src/pkg/io/multi_test.go | 88 + src/pkg/io/pipe.go | 27 +- src/pkg/io/pipe_test.go | 12 +- src/pkg/json/Makefile | 2 +- src/pkg/json/decode.go | 50 +- src/pkg/json/decode_test.go | 107 +- src/pkg/json/encode.go | 99 +- src/pkg/json/scanner.go | 19 +- src/pkg/json/scanner_test.go | 22 +- src/pkg/json/stream.go | 3 +- src/pkg/json/stream_test.go | 4 +- src/pkg/log/Makefile | 2 +- src/pkg/log/log.go | 283 +- src/pkg/log/log_test.go | 70 +- src/pkg/math/Makefile | 10 +- src/pkg/math/all_test.go | 700 +- src/pkg/math/bits.go | 2 +- src/pkg/math/const.go | 10 +- src/pkg/math/exp.go | 129 +- src/pkg/math/exp2.go | 2 +- src/pkg/math/exp_amd64.s | 92 +- src/pkg/math/exp_port.go | 192 + src/pkg/math/exp_test.go | 10 + src/pkg/math/frexp.go | 4 +- src/pkg/math/hypot_amd64.s | 50 + src/pkg/math/log.go | 8 - src/pkg/math/log10.go | 13 + src/pkg/math/log10_386.s | 19 + src/pkg/math/log10_decl.go | 8 + src/pkg/math/log_386.s | 16 - src/pkg/math/log_amd64.s | 109 + src/pkg/math/log_decl.go | 2 - src/pkg/math/logb.go | 4 +- src/pkg/math/modf.go | 6 +- src/pkg/math/sincos_amd64.s | 143 + src/pkg/math/sqrt_port.go | 4 +- src/pkg/math/tan.go | 2 +- src/pkg/mime/Makefile | 4 +- src/pkg/mime/grammar.go | 36 + src/pkg/mime/mediatype.go | 120 + src/pkg/mime/mediatype_test.go | 117 + src/pkg/mime/multipart/Makefile | 11 + src/pkg/mime/multipart/multipart.go | 280 + src/pkg/mime/multipart/multipart_test.go | 204 + src/pkg/mime/type.go | 42 +- src/pkg/net/Makefile | 21 +- src/pkg/net/dial.go | 2 +- src/pkg/net/dialgoogle_test.go | 2 +- src/pkg/net/dict/Makefile | 7 + src/pkg/net/dict/dict.go | 212 + src/pkg/net/dnsclient.go | 63 +- src/pkg/net/dnsmsg.go | 5 +- src/pkg/net/dnsname_test.go | 69 + src/pkg/net/fd.go | 99 +- src/pkg/net/fd_freebsd.go | 6 +- src/pkg/net/fd_nacl.go | 33 - src/pkg/net/fd_windows.go | 160 +- src/pkg/net/hosts.go | 14 +- src/pkg/net/hosts_test.go | 10 +- src/pkg/net/ip.go | 5 + src/pkg/net/ip_test.go | 56 +- src/pkg/net/iprawsock.go | 15 +- src/pkg/net/ipsock.go | 6 +- src/pkg/net/net.go | 6 + src/pkg/net/net_test.go | 16 +- src/pkg/net/parse_test.go | 5 + src/pkg/net/port.go | 5 +- src/pkg/net/port_test.go | 49 +- src/pkg/net/resolv_windows.go | 83 + src/pkg/net/server_test.go | 9 + src/pkg/net/sock.go | 18 +- src/pkg/net/srv_test.go | 22 + src/pkg/net/tcpsock.go | 32 +- src/pkg/net/textproto/Makefile | 14 + src/pkg/net/textproto/pipeline.go | 117 + src/pkg/net/textproto/reader.go | 492 ++ src/pkg/net/textproto/reader_test.go | 140 + src/pkg/net/textproto/textproto.go | 122 + src/pkg/net/textproto/writer.go | 119 + src/pkg/net/textproto/writer_test.go | 35 + src/pkg/net/timeout_test.go | 5 + src/pkg/net/udpsock.go | 12 +- src/pkg/net/unixsock.go | 38 +- src/pkg/netchan/Makefile | 2 +- src/pkg/netchan/common.go | 121 +- src/pkg/netchan/export.go | 238 +- src/pkg/netchan/import.go | 128 +- src/pkg/netchan/netchan_test.go | 329 +- src/pkg/nntp/Makefile | 11 - src/pkg/nntp/nntp.go | 709 -- src/pkg/nntp/nntp_test.go | 241 - src/pkg/once/Makefile | 11 - src/pkg/once/once.go | 59 - src/pkg/once/once_test.go | 30 - src/pkg/os/Makefile | 9 +- src/pkg/os/dir_darwin.go | 12 +- src/pkg/os/dir_freebsd.go | 12 +- src/pkg/os/dir_linux.go | 12 +- src/pkg/os/dir_nacl.go | 77 - src/pkg/os/env.go | 119 +- src/pkg/os/env_test.go | 59 + src/pkg/os/env_unix.go | 83 +- src/pkg/os/env_windows.go | 98 + src/pkg/os/exec.go | 23 +- src/pkg/os/file.go | 45 +- src/pkg/os/file_unix.go | 10 +- src/pkg/os/file_windows.go | 39 +- src/pkg/os/inotify/Makefile | 14 + src/pkg/os/inotify/inotify_linux.go | 291 + src/pkg/os/inotify/inotify_linux_test.go | 99 + src/pkg/os/os_test.go | 420 +- src/pkg/os/path.go | 8 +- src/pkg/os/path_test.go | 79 +- src/pkg/os/signal/Makefile | 2 +- src/pkg/os/stat_nacl.go | 38 - src/pkg/os/stat_windows.go | 10 +- src/pkg/os/sys_nacl.go | 7 - src/pkg/os/types.go | 2 +- src/pkg/patch/Makefile | 2 +- src/pkg/patch/patch_test.go | 28 +- src/pkg/path/Makefile | 16 +- src/pkg/path/match.go | 74 + src/pkg/path/match_test.go | 128 +- src/pkg/path/path.go | 20 +- src/pkg/path/path_test.go | 176 +- src/pkg/path/path_unix.go | 11 + src/pkg/path/path_windows.go | 11 + src/pkg/rand/Makefile | 2 +- src/pkg/rand/rand_test.go | 12 +- src/pkg/reflect/Makefile | 2 +- src/pkg/reflect/all_test.go | 375 +- src/pkg/reflect/type.go | 2 +- src/pkg/reflect/value.go | 95 +- src/pkg/regexp/Makefile | 2 +- src/pkg/regexp/all_test.go | 479 +- src/pkg/regexp/find_test.go | 459 ++ src/pkg/regexp/regexp.go | 950 +-- src/pkg/rpc/Makefile | 2 +- src/pkg/rpc/client.go | 21 +- src/pkg/rpc/debug.go | 36 +- src/pkg/rpc/jsonrpc/Makefile | 2 +- src/pkg/rpc/jsonrpc/all_test.go | 13 +- src/pkg/rpc/jsonrpc/client.go | 17 +- src/pkg/rpc/jsonrpc/server.go | 14 +- src/pkg/rpc/server.go | 191 +- src/pkg/rpc/server_test.go | 126 +- src/pkg/runtime/386/asm.s | 90 +- src/pkg/runtime/386/closure.c | 17 +- src/pkg/runtime/386/memmove.s | 3 +- src/pkg/runtime/386/vlop.s | 4 +- src/pkg/runtime/386/vlrt.c | 12 +- src/pkg/runtime/Makefile | 42 +- src/pkg/runtime/amd64/asm.s | 290 +- src/pkg/runtime/amd64/closure.c | 12 +- src/pkg/runtime/amd64/memmove.s | 2 +- src/pkg/runtime/amd64/traceback.c | 44 +- src/pkg/runtime/arm/asm.s | 112 +- src/pkg/runtime/arm/cas5.s | 8 +- src/pkg/runtime/arm/cas6.s | 2 +- src/pkg/runtime/arm/closure.c | 14 +- src/pkg/runtime/arm/memmove.s | 2 +- src/pkg/runtime/arm/memset.s | 2 +- src/pkg/runtime/arm/softfloat.c | 784 ++- src/pkg/runtime/arm/traceback.c | 32 +- src/pkg/runtime/arm/vlop.s | 22 +- src/pkg/runtime/arm/vlrt.c | 854 +-- src/pkg/runtime/cgo/386.S | 67 + src/pkg/runtime/cgo/Makefile | 61 + src/pkg/runtime/cgo/amd64.S | 73 + src/pkg/runtime/cgo/arm.S | 1 + src/pkg/runtime/cgo/callbacks.c | 73 + src/pkg/runtime/cgo/cgo.go | 17 + src/pkg/runtime/cgo/darwin_386.c | 144 + src/pkg/runtime/cgo/darwin_amd64.c | 125 + src/pkg/runtime/cgo/freebsd.c | 13 + src/pkg/runtime/cgo/freebsd_386.c | 59 + src/pkg/runtime/cgo/freebsd_amd64.c | 58 + src/pkg/runtime/cgo/iscgo.c | 14 + src/pkg/runtime/cgo/libcgo.h | 60 + src/pkg/runtime/cgo/linux_386.c | 68 + src/pkg/runtime/cgo/linux_amd64.c | 58 + src/pkg/runtime/cgo/linux_arm.c | 19 + src/pkg/runtime/cgo/nacl_386.c | 19 + src/pkg/runtime/cgo/util.c | 51 + src/pkg/runtime/cgo/windows_386.c | 57 + src/pkg/runtime/cgo/windows_amd64.c | 47 + src/pkg/runtime/cgocall.c | 54 +- src/pkg/runtime/cgocall.h | 8 +- src/pkg/runtime/chan.c | 294 +- src/pkg/runtime/chan_defs.go | 56 + src/pkg/runtime/complex.c | 26 +- src/pkg/runtime/darwin/386/defs.h | 1 + src/pkg/runtime/darwin/386/rt0.s | 2 +- src/pkg/runtime/darwin/386/signal.c | 105 +- src/pkg/runtime/darwin/386/sys.s | 85 +- src/pkg/runtime/darwin/amd64/defs.h | 1 + src/pkg/runtime/darwin/amd64/rt0.s | 2 +- src/pkg/runtime/darwin/amd64/signal.c | 120 +- src/pkg/runtime/darwin/amd64/sys.s | 122 +- src/pkg/runtime/darwin/defs.c | 1 + src/pkg/runtime/darwin/mem.c | 25 +- src/pkg/runtime/darwin/os.h | 34 +- src/pkg/runtime/darwin/runtime_defs.go | 23 + src/pkg/runtime/darwin/signals.h | 2 +- src/pkg/runtime/darwin/thread.c | 187 +- src/pkg/runtime/debug.go | 23 +- src/pkg/runtime/error.go | 4 +- src/pkg/runtime/export_test.go | 17 + src/pkg/runtime/extern.go | 59 +- src/pkg/runtime/float.c | 52 +- src/pkg/runtime/freebsd/386/defs.h | 1 + src/pkg/runtime/freebsd/386/rt0.s | 2 +- src/pkg/runtime/freebsd/386/signal.c | 104 +- src/pkg/runtime/freebsd/386/sys.s | 70 +- src/pkg/runtime/freebsd/amd64/defs.h | 1 + src/pkg/runtime/freebsd/amd64/rt0.s | 2 +- src/pkg/runtime/freebsd/amd64/signal.c | 120 +- src/pkg/runtime/freebsd/amd64/sys.s | 81 +- src/pkg/runtime/freebsd/defs.c | 1 + src/pkg/runtime/freebsd/mem.c | 25 +- src/pkg/runtime/freebsd/os.h | 8 +- src/pkg/runtime/freebsd/runtime_defs.go | 14 + src/pkg/runtime/freebsd/signals.h | 2 +- src/pkg/runtime/freebsd/thread.c | 88 +- src/pkg/runtime/goc2c.c | 6 +- src/pkg/runtime/hashmap.c | 241 +- src/pkg/runtime/hashmap.h | 27 +- src/pkg/runtime/hashmap_defs.go | 51 + src/pkg/runtime/iface.c | 192 +- src/pkg/runtime/iface_defs.go | 18 + src/pkg/runtime/linux/386/defs.h | 1 + src/pkg/runtime/linux/386/rt0.s | 10 +- src/pkg/runtime/linux/386/signal.c | 108 +- src/pkg/runtime/linux/386/sys.s | 55 +- src/pkg/runtime/linux/amd64/defs.h | 1 + src/pkg/runtime/linux/amd64/rt0.s | 2 +- src/pkg/runtime/linux/amd64/signal.c | 124 +- src/pkg/runtime/linux/amd64/sys.s | 96 +- src/pkg/runtime/linux/arm/defs.h | 27 +- src/pkg/runtime/linux/arm/rt0.s | 2 +- src/pkg/runtime/linux/arm/signal.c | 118 +- src/pkg/runtime/linux/arm/sys.s | 47 +- src/pkg/runtime/linux/defs.c | 1 + src/pkg/runtime/linux/defs2.c | 1 + src/pkg/runtime/linux/defs_arm.c | 29 +- src/pkg/runtime/linux/mem.c | 27 +- src/pkg/runtime/linux/os.h | 10 +- src/pkg/runtime/linux/runtime_defs.go | 14 + src/pkg/runtime/linux/signals.h | 2 +- src/pkg/runtime/linux/thread.c | 90 +- src/pkg/runtime/malloc.goc | 165 +- src/pkg/runtime/malloc.h | 88 +- src/pkg/runtime/malloc_defs.go | 130 + src/pkg/runtime/mcache.c | 25 +- src/pkg/runtime/mcentral.c | 66 +- src/pkg/runtime/mfinal.c | 48 +- src/pkg/runtime/mfixalloc.c | 8 +- src/pkg/runtime/mgc0.c | 227 +- src/pkg/runtime/mheap.c | 122 +- src/pkg/runtime/mheapmap32.c | 16 +- src/pkg/runtime/mheapmap32.h | 10 +- src/pkg/runtime/mheapmap32_defs.go | 23 + src/pkg/runtime/mheapmap64.c | 16 +- src/pkg/runtime/mheapmap64.h | 10 +- src/pkg/runtime/mheapmap64_defs.go | 31 + src/pkg/runtime/mkasmh.sh | 46 +- src/pkg/runtime/mkversion.c | 6 +- src/pkg/runtime/mprof.goc | 30 +- src/pkg/runtime/msize.c | 82 +- src/pkg/runtime/nacl/386/closure.c | 247 - src/pkg/runtime/nacl/386/defs.h | 17 - src/pkg/runtime/nacl/386/rt0.s | 8 - src/pkg/runtime/nacl/386/signal.c | 14 - src/pkg/runtime/nacl/386/sys.s | 138 - src/pkg/runtime/nacl/defs.c | 27 - src/pkg/runtime/nacl/mem.c | 28 - src/pkg/runtime/nacl/os.h | 9 - src/pkg/runtime/nacl/signals.h | 0 src/pkg/runtime/nacl/thread.c | 164 - src/pkg/runtime/plan9/386/defs.h | 1 + src/pkg/runtime/plan9/386/rt0.s | 32 + src/pkg/runtime/plan9/386/signal.c | 16 + src/pkg/runtime/plan9/386/sys.s | 76 + src/pkg/runtime/plan9/mem.c | 49 + src/pkg/runtime/plan9/os.h | 27 + src/pkg/runtime/plan9/runtime_defs.go | 23 + src/pkg/runtime/plan9/signals.h | 1 + src/pkg/runtime/plan9/thread.c | 140 + src/pkg/runtime/pprof/Makefile | 2 +- src/pkg/runtime/print.c | 140 +- src/pkg/runtime/proc.c | 545 +- src/pkg/runtime/reflect.goc | 28 +- src/pkg/runtime/rune.c | 7 +- src/pkg/runtime/runtime-gdb.py | 398 ++ src/pkg/runtime/runtime.c | 286 +- src/pkg/runtime/runtime.h | 378 +- src/pkg/runtime/runtime1.goc | 2 +- src/pkg/runtime/runtime_defs.go | 200 + src/pkg/runtime/sema.goc | 36 +- src/pkg/runtime/sigqueue.goc | 24 +- src/pkg/runtime/slice.c | 308 +- src/pkg/runtime/softfloat64.go | 498 ++ src/pkg/runtime/softfloat64_test.go | 198 + src/pkg/runtime/string.goc | 136 +- src/pkg/runtime/symtab.c | 72 +- src/pkg/runtime/tiny/386/rt0.s | 8 +- src/pkg/runtime/tiny/386/signal.c | 8 +- src/pkg/runtime/tiny/386/sys.s | 18 +- src/pkg/runtime/tiny/README | 32 +- src/pkg/runtime/tiny/mem.c | 31 +- src/pkg/runtime/tiny/runtime_defs.go | 14 + src/pkg/runtime/tiny/thread.c | 44 +- src/pkg/runtime/type.go | 6 + src/pkg/runtime/windows/386/rt0.s | 2 +- src/pkg/runtime/windows/386/signal.c | 9 +- src/pkg/runtime/windows/386/sys.s | 157 +- src/pkg/runtime/windows/mem.c | 47 +- src/pkg/runtime/windows/os.h | 34 +- src/pkg/runtime/windows/runtime_defs.go | 22 + src/pkg/runtime/windows/syscall.goc | 54 +- src/pkg/runtime/windows/thread.c | 268 +- src/pkg/scanner/Makefile | 2 +- src/pkg/scanner/scanner.go | 24 +- src/pkg/scanner/scanner_test.go | 319 +- src/pkg/smtp/Makefile | 12 + src/pkg/smtp/auth.go | 69 + src/pkg/smtp/smtp.go | 295 + src/pkg/smtp/smtp_test.go | 182 + src/pkg/sort/Makefile | 3 +- src/pkg/sort/search.go | 110 + src/pkg/sort/search_test.go | 137 + src/pkg/sort/sort.go | 18 +- src/pkg/strconv/Makefile | 2 +- src/pkg/strconv/atob_test.go | 26 +- src/pkg/strconv/atof.go | 44 +- src/pkg/strconv/atof_test.go | 131 +- src/pkg/strconv/atoi.go | 2 +- src/pkg/strconv/atoi_test.go | 204 +- src/pkg/strconv/decimal.go | 56 +- src/pkg/strconv/decimal_test.go | 72 +- src/pkg/strconv/fp_test.go | 2 +- src/pkg/strconv/ftoa.go | 12 +- src/pkg/strconv/ftoa_test.go | 178 +- src/pkg/strconv/itoa_test.go | 116 +- src/pkg/strconv/quote.go | 13 +- src/pkg/strconv/quote_test.go | 154 +- src/pkg/strings/Makefile | 2 +- src/pkg/strings/strings.go | 158 +- src/pkg/strings/strings_test.go | 481 +- src/pkg/sync/Makefile | 3 +- src/pkg/sync/asm_arm5.s | 2 +- src/pkg/sync/mutex.go | 7 +- src/pkg/sync/once.go | 35 + src/pkg/sync/once_test.go | 37 + src/pkg/sync/rwmutex.go | 2 +- src/pkg/syscall/Makefile | 10 +- src/pkg/syscall/asm_linux_amd64.s | 23 + src/pkg/syscall/asm_linux_arm.s | 78 +- src/pkg/syscall/asm_nacl_386.s | 119 - src/pkg/syscall/exec.go | 312 - src/pkg/syscall/exec_unix.go | 312 + src/pkg/syscall/exec_windows.go | 203 + src/pkg/syscall/mkall.sh | 18 +- src/pkg/syscall/mkerrors.sh | 44 +- src/pkg/syscall/mkerrors_nacl.sh | 41 - src/pkg/syscall/mkerrors_windows.sh | 210 + src/pkg/syscall/mksyscall.sh | 41 +- src/pkg/syscall/mksyscall_windows.sh | 40 +- src/pkg/syscall/mksysnum_linux.sh | 16 +- src/pkg/syscall/mksysnum_nacl.sh | 29 - src/pkg/syscall/syscall.go | 4 +- src/pkg/syscall/syscall_bsd.go | 19 +- src/pkg/syscall/syscall_darwin.go | 14 +- src/pkg/syscall/syscall_freebsd.go | 14 +- src/pkg/syscall/syscall_linux.go | 157 +- src/pkg/syscall/syscall_linux_386.go | 37 +- src/pkg/syscall/syscall_linux_amd64.go | 23 + src/pkg/syscall/syscall_linux_arm.go | 71 +- src/pkg/syscall/syscall_nacl.go | 353 - src/pkg/syscall/syscall_nacl_386.go | 19 - src/pkg/syscall/syscall_windows.go | 208 +- src/pkg/syscall/types_linux.c | 17 +- src/pkg/syscall/types_nacl.c | 123 - src/pkg/syscall/zerrors_darwin_386.go | 174 +- src/pkg/syscall/zerrors_darwin_amd64.go | 170 +- src/pkg/syscall/zerrors_freebsd_386.go | 1253 ++-- src/pkg/syscall/zerrors_freebsd_amd64.go | 150 +- src/pkg/syscall/zerrors_linux_386.go | 448 +- src/pkg/syscall/zerrors_linux_amd64.go | 452 +- src/pkg/syscall/zerrors_linux_arm.go | 133 +- src/pkg/syscall/zerrors_nacl_386.go | 246 - src/pkg/syscall/zerrors_windows_386.go | 404 +- src/pkg/syscall/zsyscall_darwin_386.go | 297 +- src/pkg/syscall/zsyscall_darwin_amd64.go | 296 +- src/pkg/syscall/zsyscall_freebsd_386.go | 294 +- src/pkg/syscall/zsyscall_freebsd_amd64.go | 295 +- src/pkg/syscall/zsyscall_linux_386.go | 422 +- src/pkg/syscall/zsyscall_linux_amd64.go | 470 +- src/pkg/syscall/zsyscall_linux_arm.go | 480 +- src/pkg/syscall/zsyscall_nacl_386.go | 229 - src/pkg/syscall/zsyscall_windows_386.go | 673 +- src/pkg/syscall/zsysnum_freebsd_386.go | 3 +- src/pkg/syscall/zsysnum_freebsd_amd64.go | 3 +- src/pkg/syscall/zsysnum_linux_386.go | 29 +- src/pkg/syscall/zsysnum_linux_amd64.go | 17 +- src/pkg/syscall/zsysnum_nacl_386.go | 61 - src/pkg/syscall/zsysnum_windows_386.go | 3 +- src/pkg/syscall/ztypes_linux_386.go | 72 +- src/pkg/syscall/ztypes_linux_amd64.go | 86 +- src/pkg/syscall/ztypes_linux_arm.go | 168 +- src/pkg/syscall/ztypes_nacl.go | 9 - src/pkg/syscall/ztypes_nacl_386.go | 105 - src/pkg/syscall/ztypes_windows_386.go | 248 +- src/pkg/syslog/Makefile | 2 +- src/pkg/syslog/syslog.go | 2 +- src/pkg/syslog/syslog_test.go | 2 +- src/pkg/tabwriter/Makefile | 2 +- src/pkg/tabwriter/tabwriter.go | 122 +- src/pkg/tabwriter/tabwriter_test.go | 164 +- src/pkg/template/Makefile | 2 +- src/pkg/template/format.go | 22 +- src/pkg/template/template.go | 178 +- src/pkg/template/template_test.go | 295 +- src/pkg/testing/Makefile | 3 +- src/pkg/testing/benchmark.go | 57 +- src/pkg/testing/iotest/Makefile | 2 +- src/pkg/testing/iotest/logger.go | 12 +- src/pkg/testing/quick/Makefile | 2 +- src/pkg/testing/regexp.go | 831 --- src/pkg/testing/regexp_test.go | 307 - src/pkg/testing/script/Makefile | 2 +- src/pkg/testing/testing.go | 30 +- src/pkg/time/Makefile | 5 +- src/pkg/time/format.go | 41 +- src/pkg/time/sleep.go | 135 +- src/pkg/time/sleep_test.go | 108 + src/pkg/time/tick.go | 59 +- src/pkg/time/tick_test.go | 2 +- src/pkg/time/time_test.go | 116 +- src/pkg/time/zoneinfo.go | 243 - src/pkg/time/zoneinfo_unix.go | 7 +- src/pkg/time/zoneinfo_windows.go | 7 +- src/pkg/try/Makefile | 11 + src/pkg/try/try.go | 174 + src/pkg/try/try_test.go | 60 + src/pkg/unicode/Makefile | 2 +- src/pkg/unicode/digit_test.go | 4 +- src/pkg/unicode/letter_test.go | 158 +- src/pkg/unicode/maketables.go | 34 +- src/pkg/unicode/script_test.go | 320 +- src/pkg/unicode/tables.go | 6834 +++++++++---------- src/pkg/utf16/Makefile | 2 +- src/pkg/utf16/utf16_test.go | 17 +- src/pkg/utf8/Makefile | 3 +- src/pkg/utf8/string.go | 211 + src/pkg/utf8/string_test.go | 109 + src/pkg/utf8/utf8.go | 69 +- src/pkg/utf8/utf8_test.go | 191 +- src/pkg/websocket/Makefile | 2 +- src/pkg/websocket/client.go | 113 +- src/pkg/websocket/server.go | 75 +- src/pkg/websocket/websocket.go | 92 +- src/pkg/websocket/websocket_test.go | 133 +- src/pkg/xml/Makefile | 2 +- src/pkg/xml/read.go | 6 +- src/pkg/xml/read_test.go | 16 +- src/pkg/xml/xml.go | 638 +- src/pkg/xml/xml_test.go | 68 +- src/quietgcc.bash | 11 +- src/run.bash | 75 +- src/sudo.bash | 3 + test/64bit.go | 302 +- test/append.go | 227 + test/arm-pass.txt | 444 -- test/assign.go | 48 +- test/assign1.go | 110 +- test/bench/fannkuch-parallel.go | 2 +- test/bench/fasta.c | 4 +- test/bench/fasta.go | 47 +- test/bench/k-nucleotide-parallel.go | 2 + test/bench/pidigits.go | 8 +- test/bench/regex-dna-parallel.go | 4 +- test/bench/regex-dna.go | 2 +- test/bench/timing.log | 108 +- test/bench/timing.sh | 42 +- test/bigalg.go | 86 +- test/blank.go | 62 +- test/blank1.go | 4 +- test/bugs/bug260.go | 55 - test/bugs/bug274.go | 29 - test/bugs/bug286.go | 94 - test/chan/fifo.go | 26 +- test/chan/goroutines.go | 32 +- test/chan/nonblock.go | 98 +- test/chan/perm.go | 68 +- test/chan/powser1.go | 579 +- test/chan/powser2.go | 585 +- test/chan/select3.go | 203 + test/chan/select4.go | 25 + test/char_lit.go | 7 +- test/char_lit1.go | 16 +- test/closedchan.go | 98 +- test/closure.go | 11 + test/cmp1.go | 54 + test/cmp2.go | 6 +- test/cmp3.go | 6 +- test/cmp4.go | 8 +- test/cmp5.go | 8 +- test/cmp6.go | 42 + test/cmplx.go | 1 + test/cmplxdivide.go | 5 + test/complit.go | 44 +- test/compos.go | 8 +- test/const.go | 164 +- test/const1.go | 114 +- test/const2.go | 2 +- test/const3.go | 6 + test/convert3.go | 4 +- test/convlit.go | 40 +- test/copy.go | 61 +- test/ddd.go | 34 +- test/ddd1.go | 19 + test/decl.go | 30 +- test/declbad.go | 42 +- test/defer.go | 2 +- test/env.go | 17 +- test/eof.go | 9 + test/eof1.go | 9 + test/errchk | 100 +- test/escape.go | 158 +- test/fixedbugs/bug045.go | 2 +- test/fixedbugs/bug059.go | 2 +- test/fixedbugs/bug146.go | 2 +- test/fixedbugs/bug195.go | 4 +- test/fixedbugs/bug206.go | 4 +- test/fixedbugs/bug243.go | 8 +- test/fixedbugs/bug251.go | 6 +- test/fixedbugs/bug252.go | 4 +- test/fixedbugs/bug255.go | 6 +- test/fixedbugs/bug260.go | 61 + test/fixedbugs/bug273.go | 28 +- test/fixedbugs/bug274.go | 29 + test/fixedbugs/bug278.go | 8 +- test/fixedbugs/bug284.go | 70 +- test/fixedbugs/bug286.go | 94 + test/fixedbugs/bug289.go | 26 + test/fixedbugs/bug290.go | 15 + test/fixedbugs/bug291.go | 23 + test/fixedbugs/bug292.go | 22 + test/fixedbugs/bug293.go | 37 + test/fixedbugs/bug294.go | 79 + test/fixedbugs/bug295.go | 17 + test/fixedbugs/bug296.go | 88 + test/fixedbugs/bug297.go | 15 + test/fixedbugs/bug298.go | 11 + test/fixedbugs/bug299.go | 27 + test/fixedbugs/bug300.go | 29 + test/fixedbugs/bug301.go | 18 + test/fixedbugs/bug302.dir/main.go | 12 + test/fixedbugs/bug302.dir/p.go | 1011 +++ test/fixedbugs/bug302.go | 6 + test/fixedbugs/bug303.go | 37 + test/fixedbugs/bug304.go | 18 + test/fixedbugs/bug305.go | 24 + test/fixedbugs/bug306.dir/p1.go | 9 + test/fixedbugs/bug306.dir/p2.go | 8 + test/fixedbugs/bug306.go | 7 + test/fixedbugs/bug307.go | 15 + test/fixedbugs/bug308.go | 19 + test/fixedbugs/bug309.go | 19 + test/fixedbugs/bug310.go | 20 + test/fixedbugs/bug311.go | 20 + test/fixedbugs/bug312.go | 22 + test/fixedbugs/bug313.dir/a.go | 11 + test/fixedbugs/bug313.dir/b.go | 11 + test/fixedbugs/bug313.go | 19 + test/fixedbugs/bug314.go | 31 + test/fixedbugs/bug315.go | 18 + test/fixedbugs/bug316.go | 17 + test/fixedbugs/bug317.go | 16 + test/float_lit.go | 224 +- test/floatcmp.go | 18 +- test/for.go | 42 +- test/func.go | 74 +- test/func1.go | 4 +- test/func2.go | 24 +- test/func3.go | 14 +- test/func4.go | 4 +- test/garbage/Makefile | 2 +- test/garbage/parser.go | 8 +- test/gc.go | 2 +- test/gc1.go | 4 +- test/golden-arm.out | 110 - test/golden.out | 14 - test/hashmap.go | 123 +- test/helloworld.go | 2 +- test/if.go | 100 +- test/if1.go | 4 +- test/import.go | 8 +- test/import1.go | 4 +- test/index.go | 224 + test/indirect1.go | 2 +- test/initialize.go | 12 +- test/initializerr.go | 10 +- test/initsyscall.go | 4 +- test/int_lit.go | 6 +- test/interface/bigdata.go | 52 +- test/interface/convert1.go | 18 +- test/interface/convert2.go | 18 +- test/interface/embed.go | 63 +- test/interface/embed0.go | 24 +- test/interface/embed1.go | 50 +- test/interface/embed2.go | 70 + test/interface/explicit.go | 12 +- test/interface/fail.go | 12 +- test/interface/fake.go | 78 +- test/interface/receiver.go | 4 +- test/interface/returntype.go | 6 +- test/interface/struct.go | 152 +- test/iota.go | 156 +- test/ken/array.go | 8 +- test/ken/convert.go | 431 ++ test/ken/cplx4.go | 2 +- test/ken/slicearray.go | 24 +- test/ken/sliceslice.go | 20 + test/literal.go | 356 +- test/mallocrand.go | 10 +- test/mallocrep.go | 3 +- test/map.go | 412 +- test/method.go | 24 +- test/method1.go | 8 +- test/method2.go | 19 +- test/named1.go | 2 +- test/nil.go | 36 +- test/nilptr/arrayindex.go | 7 +- test/nilptr/arrayindex1.go | 9 +- test/nilptr/arraytoslice.go | 11 +- test/nilptr/arraytoslice1.go | 11 +- test/nilptr/arraytoslice2.go | 13 +- test/nilptr/slicearray.go | 9 +- test/nilptr/structfield.go | 13 +- test/nilptr/structfield1.go | 13 +- test/nilptr/structfield2.go | 15 +- test/nilptr/structfieldaddr.go | 13 +- test/nul1.go | 6 +- test/parentype.go | 12 +- test/peano.go | 13 +- test/printbig.go | 2 +- test/range.go | 199 +- test/recover2.go | 10 +- test/recover3.go | 17 +- test/run | 39 +- test/run-arm | 102 - test/runtime.go | 2 +- test/sieve.go | 20 +- test/sigchld.go | 5 +- test/sinit.go | 8 +- test/solitaire.go | 119 + test/string_lit.go | 54 +- test/stringrange.go | 54 +- test/switch.go | 158 +- test/switch1.go | 6 +- test/syntax/chan.go | 17 + test/syntax/slice.go | 9 - test/syntax/topexpr.go | 6 +- test/syntax/vareq.go | 2 +- test/syntax/vareq1.go | 2 +- test/test0.go | 64 +- test/turing.go | 51 +- test/typeswitch.go | 78 +- test/undef.go | 10 +- test/utf.go | 60 +- test/varerr.go | 4 +- test/zerodivide.go | 44 +- 1496 files changed, 98110 insertions(+), 65999 deletions(-) delete mode 100644 .hgignore create mode 100644 PATENTS create mode 100644 doc/ExpressivenessOfGo.pdf create mode 100644 doc/all.css create mode 100644 doc/button_background.png create mode 100644 doc/community.html create mode 100644 doc/contrib.html create mode 100644 doc/docs.html create mode 100644 doc/frontpage.css delete mode 100644 doc/go_lang_faq.html delete mode 100644 doc/go_learning.html delete mode 100644 doc/go_programming_faq.html create mode 100644 doc/ie.css create mode 100644 doc/logo.png create mode 100644 doc/play_overlay.png create mode 100644 doc/playground.html delete mode 100644 doc/style.css create mode 100644 doc/talks/gofrontend-gcc-summit-2010.pdf create mode 100644 doc/video-001.png create mode 100644 doc/video-002.png create mode 100644 doc/video-003.png create mode 100644 doc/video-004.png delete mode 100644 doc/video-snap.jpg delete mode 100644 lib/godoc/source.html create mode 100644 misc/cgo/life/golden.out create mode 100755 misc/cgo/life/test.bash create mode 100644 misc/cgo/stdio/align.go create mode 100644 misc/cgo/stdio/test.go create mode 100644 misc/cgo/stdio/test1.go create mode 100644 misc/dashboard/builder/Makefile create mode 100644 misc/dashboard/builder/doc.go create mode 100644 misc/dashboard/builder/exec.go create mode 100644 misc/dashboard/builder/hg.go create mode 100644 misc/dashboard/builder/http.go create mode 100644 misc/dashboard/builder/main.go create mode 100644 misc/dashboard/godashboard/toutf8.py create mode 100755 misc/dashboard/googlecode_upload.py create mode 100644 misc/fraise/go.plist create mode 100644 misc/fraise/readme.txt create mode 100644 misc/goplay/Makefile create mode 100644 misc/goplay/README create mode 100644 misc/goplay/doc.go create mode 100644 misc/goplay/goplay.go create mode 100644 misc/zsh/go delete mode 100644 pkg/~place-holder~ delete mode 100644 src/Make.386 delete mode 100644 src/Make.amd64 delete mode 100644 src/Make.arm create mode 100644 src/Make.ccmd create mode 100644 src/Make.clib delete mode 100644 src/Make.conf create mode 100644 src/Make.inc delete mode 100755 src/all-arm.bash delete mode 100755 src/all-nacl.bash create mode 100644 src/cmd/5g/peep.c create mode 100644 src/cmd/5g/reg.c create mode 100644 src/cmd/5l/prof.c create mode 100644 src/cmd/6l/prof.c create mode 100644 src/cmd/8l/prof.c create mode 100644 src/cmd/godoc/dirtrees.go create mode 100644 src/cmd/godoc/format.go create mode 100644 src/cmd/godoc/utils.go create mode 100644 src/cmd/gofmt/simplify.go create mode 100644 src/cmd/gofmt/testdata/composites.golden create mode 100644 src/cmd/gofmt/testdata/composites.input create mode 100755 src/cmd/gofmt/testdata/test.sh create mode 100644 src/cmd/gomake/doc.go create mode 100755 src/cmd/gotest/gotry create mode 100644 src/cmd/govet/Makefile create mode 100644 src/cmd/govet/doc.go create mode 100644 src/cmd/govet/govet.go create mode 100644 src/cmd/ld/data.c create mode 100644 src/cmd/ld/dwarf.c create mode 100644 src/cmd/ld/dwarf.h create mode 100644 src/cmd/ld/dwarf_defs.h create mode 100644 src/cmd/ld/ldelf.c create mode 100644 src/cmd/ld/ldmacho.c create mode 100644 src/cmd/ld/symtab.c create mode 100644 src/lib9/utf/Makefile create mode 100644 src/lib9/utf/runetypebody-5.2.0.c delete mode 100755 src/libcgo/386.S delete mode 100755 src/libcgo/Makefile delete mode 100644 src/libcgo/amd64.S delete mode 100644 src/libcgo/darwin_386.c delete mode 100644 src/libcgo/darwin_amd64.c delete mode 100644 src/libcgo/freebsd_386.c delete mode 100644 src/libcgo/freebsd_amd64.c delete mode 100644 src/libcgo/libcgo.h delete mode 100644 src/libcgo/linux_386.c delete mode 100644 src/libcgo/linux_amd64.c delete mode 100644 src/libcgo/linux_arm.c delete mode 100644 src/libcgo/nacl_386.c delete mode 100644 src/libcgo/util.c delete mode 100755 src/libcgo/windows_386.c delete mode 100755 src/libcgo/windows_amd64.c create mode 100644 src/libmach/windows.c create mode 100644 src/pkg/archive/zip/Makefile create mode 100644 src/pkg/archive/zip/reader.go create mode 100644 src/pkg/archive/zip/reader_test.go create mode 100644 src/pkg/archive/zip/struct.go create mode 100644 src/pkg/archive/zip/testdata/gophercolor16x16.png create mode 100644 src/pkg/archive/zip/testdata/r.zip create mode 100644 src/pkg/archive/zip/testdata/readme.notzip create mode 100644 src/pkg/archive/zip/testdata/readme.zip create mode 100644 src/pkg/archive/zip/testdata/test.zip delete mode 100644 src/pkg/crypto/block/cbc_aes_test.go delete mode 100644 src/pkg/crypto/block/ctr_aes_test.go create mode 100644 src/pkg/crypto/cast5/Makefile create mode 100644 src/pkg/crypto/cast5/cast5.go create mode 100644 src/pkg/crypto/cast5/cast5_test.go create mode 100644 src/pkg/crypto/cipher/Makefile create mode 100644 src/pkg/crypto/cipher/cbc.go create mode 100644 src/pkg/crypto/cipher/cbc_aes_test.go create mode 100644 src/pkg/crypto/cipher/cfb.go create mode 100644 src/pkg/crypto/cipher/cfb_test.go create mode 100644 src/pkg/crypto/cipher/cipher.go create mode 100644 src/pkg/crypto/cipher/common_test.go create mode 100644 src/pkg/crypto/cipher/ctr.go create mode 100644 src/pkg/crypto/cipher/ctr_aes_test.go create mode 100644 src/pkg/crypto/cipher/io.go create mode 100644 src/pkg/crypto/cipher/ocfb.go create mode 100644 src/pkg/crypto/cipher/ocfb_test.go create mode 100644 src/pkg/crypto/elliptic/Makefile create mode 100644 src/pkg/crypto/elliptic/elliptic.go create mode 100644 src/pkg/crypto/elliptic/elliptic_test.go create mode 100644 src/pkg/crypto/ocsp/Makefile create mode 100644 src/pkg/crypto/ocsp/ocsp.go create mode 100644 src/pkg/crypto/ocsp/ocsp_test.go create mode 100644 src/pkg/crypto/openpgp/armor/Makefile create mode 100644 src/pkg/crypto/openpgp/armor/armor.go create mode 100644 src/pkg/crypto/openpgp/armor/armor_test.go create mode 100644 src/pkg/crypto/openpgp/armor/encode.go create mode 100644 src/pkg/crypto/openpgp/error/Makefile create mode 100644 src/pkg/crypto/openpgp/error/error.go create mode 100644 src/pkg/crypto/rand/rand_unix.go create mode 100755 src/pkg/crypto/rand/rand_windows.go create mode 100644 src/pkg/crypto/tls/cipher_suites.go create mode 100644 src/pkg/crypto/tls/conn_test.go create mode 100644 src/pkg/crypto/tls/generate_cert.go create mode 100644 src/pkg/crypto/tls/handshake_client_test.go create mode 100644 src/pkg/crypto/tls/key_agreement.go create mode 100644 src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py create mode 100644 src/pkg/crypto/twofish/Makefile create mode 100644 src/pkg/crypto/twofish/twofish.go create mode 100644 src/pkg/crypto/twofish/twofish_test.go create mode 100644 src/pkg/debug/pe/Makefile create mode 100644 src/pkg/debug/pe/file.go create mode 100644 src/pkg/debug/pe/file_test.go create mode 100644 src/pkg/debug/pe/pe.go create mode 100644 src/pkg/debug/pe/testdata/gcc-386-mingw-exec create mode 100644 src/pkg/debug/pe/testdata/gcc-386-mingw-obj create mode 100644 src/pkg/debug/pe/testdata/hello.c delete mode 100644 src/pkg/debug/proc/proc_nacl.go delete mode 100644 src/pkg/debug/proc/regs_nacl_386.go create mode 100644 src/pkg/encoding/line/Makefile create mode 100644 src/pkg/encoding/line/line.go create mode 100644 src/pkg/encoding/line/line_test.go create mode 100644 src/pkg/exec/lp_unix.go create mode 100644 src/pkg/exec/lp_windows.go delete mode 100644 src/pkg/exp/4s/4s.go delete mode 100644 src/pkg/exp/4s/4s.html delete mode 100644 src/pkg/exp/4s/5s.go delete mode 100644 src/pkg/exp/4s/5s.html delete mode 100644 src/pkg/exp/4s/Makefile delete mode 100644 src/pkg/exp/4s/data.go delete mode 100644 src/pkg/exp/4s/xs.go delete mode 100644 src/pkg/exp/bignum/Makefile delete mode 100644 src/pkg/exp/bignum/arith.go delete mode 100644 src/pkg/exp/bignum/arith_amd64.s delete mode 100644 src/pkg/exp/bignum/bignum.go delete mode 100644 src/pkg/exp/bignum/bignum_test.go delete mode 100644 src/pkg/exp/bignum/integer.go delete mode 100644 src/pkg/exp/bignum/nrdiv_test.go delete mode 100644 src/pkg/exp/bignum/rational.go delete mode 100644 src/pkg/exp/draw/arith.go delete mode 100644 src/pkg/exp/draw/color.go mode change 100644 => 100755 src/pkg/exp/eval/expr1.go delete mode 100644 src/pkg/exp/eval/util.go delete mode 100644 src/pkg/exp/iterable/Makefile delete mode 100644 src/pkg/exp/iterable/array.go delete mode 100644 src/pkg/exp/iterable/iterable.go delete mode 100644 src/pkg/exp/iterable/iterable_test.go delete mode 100644 src/pkg/exp/nacl/README delete mode 100644 src/pkg/exp/nacl/av/Makefile delete mode 100644 src/pkg/exp/nacl/av/av.go delete mode 100644 src/pkg/exp/nacl/av/event.go delete mode 100644 src/pkg/exp/nacl/av/image.go delete mode 100644 src/pkg/exp/nacl/srpc/Makefile delete mode 100644 src/pkg/exp/nacl/srpc/client.go delete mode 100644 src/pkg/exp/nacl/srpc/msg.go delete mode 100644 src/pkg/exp/nacl/srpc/server.go delete mode 100644 src/pkg/exp/spacewar/Makefile delete mode 100644 src/pkg/exp/spacewar/code.go delete mode 100644 src/pkg/exp/spacewar/pdp1.go delete mode 100644 src/pkg/exp/spacewar/spacewar.go delete mode 100644 src/pkg/exp/spacewar/spacewar.html create mode 100644 src/pkg/flag/export_test.go create mode 100644 src/pkg/fmt/doc.go create mode 100644 src/pkg/go/ast/print.go create mode 100644 src/pkg/go/token/position.go create mode 100644 src/pkg/go/token/position_test.go create mode 100644 src/pkg/go/typechecker/Makefile create mode 100644 src/pkg/go/typechecker/scope.go create mode 100644 src/pkg/go/typechecker/testdata/test0.go create mode 100644 src/pkg/go/typechecker/testdata/test1.go create mode 100644 src/pkg/go/typechecker/testdata/test3.go create mode 100644 src/pkg/go/typechecker/testdata/test4.go create mode 100644 src/pkg/go/typechecker/typechecker.go create mode 100644 src/pkg/go/typechecker/typechecker_test.go create mode 100644 src/pkg/go/typechecker/universe.go create mode 100644 src/pkg/gob/doc.go create mode 100644 src/pkg/gob/error.go create mode 100644 src/pkg/html/Makefile create mode 100644 src/pkg/html/doc.go create mode 100644 src/pkg/html/entity.go create mode 100644 src/pkg/html/entity_test.go create mode 100644 src/pkg/html/escape.go create mode 100644 src/pkg/html/parse.go create mode 100644 src/pkg/html/parse_test.go create mode 100644 src/pkg/html/token.go create mode 100644 src/pkg/html/token_test.go create mode 100644 src/pkg/http/fs_test.go create mode 100644 src/pkg/http/serve_test.go create mode 100644 src/pkg/http/testdata/file create mode 100644 src/pkg/image/format.go create mode 100644 src/pkg/image/geom.go create mode 100644 src/pkg/index/suffixarray/Makefile create mode 100644 src/pkg/index/suffixarray/qsufsort.go create mode 100644 src/pkg/index/suffixarray/suffixarray.go create mode 100644 src/pkg/index/suffixarray/suffixarray_test.go create mode 100644 src/pkg/io/multi.go create mode 100644 src/pkg/io/multi_test.go create mode 100644 src/pkg/math/exp_port.go create mode 100644 src/pkg/math/exp_test.go create mode 100644 src/pkg/math/hypot_amd64.s create mode 100644 src/pkg/math/log10.go create mode 100644 src/pkg/math/log10_386.s create mode 100644 src/pkg/math/log10_decl.go create mode 100644 src/pkg/math/log_amd64.s create mode 100644 src/pkg/math/sincos_amd64.s create mode 100644 src/pkg/mime/grammar.go create mode 100644 src/pkg/mime/mediatype.go create mode 100644 src/pkg/mime/mediatype_test.go create mode 100644 src/pkg/mime/multipart/Makefile create mode 100644 src/pkg/mime/multipart/multipart.go create mode 100644 src/pkg/mime/multipart/multipart_test.go create mode 100644 src/pkg/net/dict/Makefile create mode 100644 src/pkg/net/dict/dict.go create mode 100644 src/pkg/net/dnsname_test.go delete mode 100644 src/pkg/net/fd_nacl.go create mode 100644 src/pkg/net/resolv_windows.go create mode 100644 src/pkg/net/srv_test.go create mode 100644 src/pkg/net/textproto/Makefile create mode 100644 src/pkg/net/textproto/pipeline.go create mode 100644 src/pkg/net/textproto/reader.go create mode 100644 src/pkg/net/textproto/reader_test.go create mode 100644 src/pkg/net/textproto/textproto.go create mode 100644 src/pkg/net/textproto/writer.go create mode 100644 src/pkg/net/textproto/writer_test.go delete mode 100644 src/pkg/nntp/Makefile delete mode 100644 src/pkg/nntp/nntp.go delete mode 100644 src/pkg/nntp/nntp_test.go delete mode 100644 src/pkg/once/Makefile delete mode 100644 src/pkg/once/once.go delete mode 100644 src/pkg/once/once_test.go delete mode 100644 src/pkg/os/dir_nacl.go create mode 100644 src/pkg/os/env_test.go mode change 100755 => 100644 src/pkg/os/env_unix.go mode change 100755 => 100644 src/pkg/os/env_windows.go create mode 100644 src/pkg/os/inotify/Makefile create mode 100644 src/pkg/os/inotify/inotify_linux.go create mode 100644 src/pkg/os/inotify/inotify_linux_test.go delete mode 100644 src/pkg/os/stat_nacl.go delete mode 100644 src/pkg/os/sys_nacl.go create mode 100644 src/pkg/path/path_unix.go create mode 100644 src/pkg/path/path_windows.go create mode 100644 src/pkg/regexp/find_test.go create mode 100755 src/pkg/runtime/cgo/386.S create mode 100644 src/pkg/runtime/cgo/Makefile create mode 100644 src/pkg/runtime/cgo/amd64.S create mode 100644 src/pkg/runtime/cgo/arm.S create mode 100644 src/pkg/runtime/cgo/callbacks.c create mode 100644 src/pkg/runtime/cgo/cgo.go create mode 100644 src/pkg/runtime/cgo/darwin_386.c create mode 100644 src/pkg/runtime/cgo/darwin_amd64.c create mode 100644 src/pkg/runtime/cgo/freebsd.c create mode 100644 src/pkg/runtime/cgo/freebsd_386.c create mode 100644 src/pkg/runtime/cgo/freebsd_amd64.c create mode 100644 src/pkg/runtime/cgo/iscgo.c create mode 100644 src/pkg/runtime/cgo/libcgo.h create mode 100644 src/pkg/runtime/cgo/linux_386.c create mode 100644 src/pkg/runtime/cgo/linux_amd64.c create mode 100644 src/pkg/runtime/cgo/linux_arm.c create mode 100644 src/pkg/runtime/cgo/nacl_386.c create mode 100644 src/pkg/runtime/cgo/util.c create mode 100755 src/pkg/runtime/cgo/windows_386.c create mode 100755 src/pkg/runtime/cgo/windows_amd64.c create mode 100644 src/pkg/runtime/chan_defs.go create mode 100644 src/pkg/runtime/darwin/runtime_defs.go create mode 100644 src/pkg/runtime/export_test.go create mode 100644 src/pkg/runtime/freebsd/runtime_defs.go create mode 100644 src/pkg/runtime/hashmap_defs.go create mode 100644 src/pkg/runtime/iface_defs.go create mode 100644 src/pkg/runtime/linux/runtime_defs.go create mode 100644 src/pkg/runtime/malloc_defs.go create mode 100644 src/pkg/runtime/mheapmap32_defs.go create mode 100644 src/pkg/runtime/mheapmap64_defs.go delete mode 100644 src/pkg/runtime/nacl/386/closure.c delete mode 100644 src/pkg/runtime/nacl/386/defs.h delete mode 100644 src/pkg/runtime/nacl/386/rt0.s delete mode 100644 src/pkg/runtime/nacl/386/signal.c delete mode 100644 src/pkg/runtime/nacl/386/sys.s delete mode 100644 src/pkg/runtime/nacl/defs.c delete mode 100644 src/pkg/runtime/nacl/mem.c delete mode 100644 src/pkg/runtime/nacl/os.h delete mode 100644 src/pkg/runtime/nacl/signals.h delete mode 100644 src/pkg/runtime/nacl/thread.c create mode 100644 src/pkg/runtime/plan9/386/defs.h create mode 100644 src/pkg/runtime/plan9/386/rt0.s create mode 100644 src/pkg/runtime/plan9/386/signal.c create mode 100644 src/pkg/runtime/plan9/386/sys.s create mode 100644 src/pkg/runtime/plan9/mem.c create mode 100644 src/pkg/runtime/plan9/os.h create mode 100644 src/pkg/runtime/plan9/runtime_defs.go create mode 100644 src/pkg/runtime/plan9/signals.h create mode 100644 src/pkg/runtime/plan9/thread.c create mode 100644 src/pkg/runtime/runtime-gdb.py create mode 100644 src/pkg/runtime/runtime_defs.go create mode 100644 src/pkg/runtime/softfloat64.go create mode 100644 src/pkg/runtime/softfloat64_test.go create mode 100644 src/pkg/runtime/tiny/runtime_defs.go create mode 100644 src/pkg/runtime/windows/runtime_defs.go create mode 100644 src/pkg/smtp/Makefile create mode 100644 src/pkg/smtp/auth.go create mode 100644 src/pkg/smtp/smtp.go create mode 100644 src/pkg/smtp/smtp_test.go create mode 100644 src/pkg/sort/search.go create mode 100644 src/pkg/sort/search_test.go create mode 100644 src/pkg/sync/once.go create mode 100644 src/pkg/sync/once_test.go delete mode 100644 src/pkg/syscall/asm_nacl_386.s delete mode 100644 src/pkg/syscall/exec.go create mode 100644 src/pkg/syscall/exec_unix.go create mode 100644 src/pkg/syscall/exec_windows.go delete mode 100755 src/pkg/syscall/mkerrors_nacl.sh create mode 100755 src/pkg/syscall/mkerrors_windows.sh delete mode 100644 src/pkg/syscall/mksysnum_nacl.sh delete mode 100644 src/pkg/syscall/syscall_nacl.go delete mode 100644 src/pkg/syscall/syscall_nacl_386.go delete mode 100644 src/pkg/syscall/types_nacl.c delete mode 100644 src/pkg/syscall/zerrors_nacl_386.go delete mode 100644 src/pkg/syscall/zsyscall_nacl_386.go delete mode 100644 src/pkg/syscall/zsysnum_nacl_386.go delete mode 100644 src/pkg/syscall/ztypes_nacl.go delete mode 100644 src/pkg/syscall/ztypes_nacl_386.go delete mode 100644 src/pkg/testing/regexp.go delete mode 100644 src/pkg/testing/regexp_test.go delete mode 100644 src/pkg/time/zoneinfo.go create mode 100644 src/pkg/try/Makefile create mode 100644 src/pkg/try/try.go create mode 100644 src/pkg/try/try_test.go create mode 100644 src/pkg/utf8/string.go create mode 100644 src/pkg/utf8/string_test.go create mode 100644 test/append.go delete mode 100644 test/arm-pass.txt delete mode 100644 test/bugs/bug260.go delete mode 100644 test/bugs/bug274.go delete mode 100644 test/bugs/bug286.go create mode 100644 test/chan/select3.go create mode 100644 test/chan/select4.go create mode 100644 test/cmp6.go create mode 100644 test/eof.go create mode 100644 test/eof1.go create mode 100644 test/fixedbugs/bug260.go create mode 100644 test/fixedbugs/bug274.go create mode 100644 test/fixedbugs/bug286.go create mode 100644 test/fixedbugs/bug289.go create mode 100644 test/fixedbugs/bug290.go create mode 100644 test/fixedbugs/bug291.go create mode 100644 test/fixedbugs/bug292.go create mode 100644 test/fixedbugs/bug293.go create mode 100644 test/fixedbugs/bug294.go create mode 100644 test/fixedbugs/bug295.go create mode 100644 test/fixedbugs/bug296.go create mode 100644 test/fixedbugs/bug297.go create mode 100644 test/fixedbugs/bug298.go create mode 100644 test/fixedbugs/bug299.go create mode 100644 test/fixedbugs/bug300.go create mode 100644 test/fixedbugs/bug301.go create mode 100644 test/fixedbugs/bug302.dir/main.go create mode 100644 test/fixedbugs/bug302.dir/p.go create mode 100644 test/fixedbugs/bug302.go create mode 100644 test/fixedbugs/bug303.go create mode 100644 test/fixedbugs/bug304.go create mode 100644 test/fixedbugs/bug305.go create mode 100644 test/fixedbugs/bug306.dir/p1.go create mode 100644 test/fixedbugs/bug306.dir/p2.go create mode 100644 test/fixedbugs/bug306.go create mode 100644 test/fixedbugs/bug307.go create mode 100644 test/fixedbugs/bug308.go create mode 100644 test/fixedbugs/bug309.go create mode 100644 test/fixedbugs/bug310.go create mode 100644 test/fixedbugs/bug311.go create mode 100644 test/fixedbugs/bug312.go create mode 100644 test/fixedbugs/bug313.dir/a.go create mode 100644 test/fixedbugs/bug313.dir/b.go create mode 100644 test/fixedbugs/bug313.go create mode 100644 test/fixedbugs/bug314.go create mode 100644 test/fixedbugs/bug315.go create mode 100644 test/fixedbugs/bug316.go create mode 100644 test/fixedbugs/bug317.go delete mode 100644 test/golden-arm.out create mode 100644 test/index.go create mode 100644 test/interface/embed2.go create mode 100644 test/ken/convert.go delete mode 100755 test/run-arm create mode 100644 test/solitaire.go create mode 100644 test/syntax/chan.go delete mode 100644 test/syntax/slice.go diff --git a/.hgignore b/.hgignore deleted file mode 100644 index c80814058..000000000 --- a/.hgignore +++ /dev/null @@ -1,45 +0,0 @@ -syntax:glob -.DS_Store -.git -.gitignore -*.[568ao] -*.ao -*.so -*.pyc -._* -.nfs.* -[568a].out -*~ -*.orig -core -_obj -_test -doc/htmlgen -src/cmd/6a/6a -y.tab.[ch] -src/cmd/?l/enam.c -src/cmd/gc/builtin.c -src/cmd/gc/mkbuiltin1 -src/cmd/gc/opnames.h -src/cmd/gc/y.output -src/cmd/gc/y1.tab.c -src/cmd/gc/yerr.h -src/pkg/Make.deps -src/pkg/exp/ogle/ogle -src/pkg/os/signal/unix.go -src/pkg/runtime/*/asm.h -src/pkg/runtime/goc2c -src/pkg/runtime/mkversion -src/pkg/runtime/runtime.acid.* -src/pkg/runtime/version.go -src/pkg/github.com/ -src/pkg/*.googlecode.com/ -test/pass.out -test/run.out -test/times.out -test/garbage/*.out - -syntax:regexp -^pkg/ -^src/cmd/(.*)/6?\1$ -^.*/core.[0-9]*$ diff --git a/AUTHORS b/AUTHORS index b41da7dc9..03bc7f43d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -10,6 +10,7 @@ Abhinav Gupta Adrian O'Grady +Albert Strasheim Alex Brainman Amrut Joshi Andrei Vieru @@ -17,14 +18,22 @@ Andrew Skiba Andrey Mirtchovski Andy Davis Anh Hai Trinh +Anschel Schaffer-Cohen +Anthony Martin Anthony Starks Aron Nopanen Arvindh Rajesh Tamilmani Ben Olive +Benny Siegert +Caine Tighe Charles L. Dorian +Chris Jones Chris Lennert +Christian Himpel Christopher Wedgwood Conrad Meyer +Corey Thomasson +Dan Sinclair Daniel Fleischman Daniel Theophanes David G. Andersen @@ -32,33 +41,54 @@ David Titarenco Dean Prichard Devon H. O'Dell Eden Li +Eoghan Sherry +Eric Clark +Eric Eisner Evan Shaw Fazlul Shahriar Firmansyah Adiputra +Florian Uekermann Giles Lean Google Inc. +Graham Miller Gustavo Niemeyer +Harley Laue Hector Chu Icarus Sparry Isaac Wagner +James Fysh James Meneghello James Toy James Whitehead Jan H. Hosang Jan Mercl +Jim McGrath Joe Poirier Jonathan Wills Josh Goebel +Jukka-Pekka Kekkonen +Kai Backman Kei Son +Keith Rarick Ken Friedenbach Kevin Ballard Kyle Consalus +Kyle Lemons +Lorenzo Stoakes +Markus Duft +Martin Neubauer +Mathieu Lonjaret Micah Stetson Michael Elkins Michael Hoisie +Mikio Hara +Mikkel Krautz Moriyoshi Koizumi Môshe van der Sterre +Nicholas Waples +Nigel Kerr Paolo Giarrusso +Patrick Gavlin Petar Maymounkov Peter Froehlich Peter Mundy @@ -67,14 +97,24 @@ Raif S. Naffah Risto Jaakko Saarelma Roger Peppe Ross Light +Ryan Hitchman +Scott Lawrence +Sebastien Binet Sergei Skorobogatov Sergey 'SnakE' Gromov Sergio Luis O. B. Correia Spring Mc +Stefan Nilsson Stephen Weinberg Sven Almgren +Tarmigan Casebolt Timo Savola Tor Andersson +Vincent Ambo Vinu Rajashekhar +Wei Guangjing William Josephson Yongjian Xu +Yasuhiro Matsumoto +Yuusei Kuwana +Yuval Pavel Zholkover diff --git a/CONTRIBUTORS b/CONTRIBUTORS index caa4fb358..317cb525b 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -23,12 +23,18 @@ # Names should be added to this file like so: # Name +# +# An entry with two email addresses specifies that the +# first address should be used in the submit logs and +# that the second address should be recognized as the +# same person when interacting with Rietveld. # Please keep the list sorted. Abhinav Gupta Adam Langley Adrian O'Grady +Albert Strasheim Alex Brainman Amrut Joshi Andrei Vieru @@ -37,22 +43,32 @@ Andrew Skiba Andrey Mirtchovski Andy Davis Anh Hai Trinh +Anschel Schaffer-Cohen +Anthony Martin Anthony Starks Aron Nopanen Arvindh Rajesh Tamilmani Austin Clements +Balazs Lecz Ben Eitzen Ben Olive -Bill Neubauer -Brad Fitzpatrick +Benny Siegert +Bill Neubauer +Brad Fitzpatrick Brendan O'Dea +Caine Tighe Cary Hull Charles L. Dorian +Chris Jones Chris Lennert +Christian Himpel Christopher Wedgwood Conrad Meyer +Corey Thomasson +Dan Sinclair Daniel Fleischman Daniel Nadasi +Berengar Lehr Daniel Theophanes David G. Andersen David Symonds @@ -60,12 +76,18 @@ David Titarenco Dean Prichard Devon H. O'Dell Eden Li +Eoghan Sherry +Eric Clark +Eric Eisner Evan Shaw Fazlul Shahriar Firmansyah Adiputra +Florian Uekermann Fumitoshi Ukai Giles Lean +Graham Miller Gustavo Niemeyer +Harley Laue Hector Chu Ian Lance Taylor Icarus Sparry @@ -73,31 +95,47 @@ Isaac Wagner Ivan Krasin Jacob Baskin James Aguilar +James Fysh James Meneghello James Toy James Whitehead +Jamie Gennis Jan H. Hosang Jan Mercl +Jim McGrath Joe Poirier Jonathan Wills Josh Goebel +Jukka-Pekka Kekkonen Kai Backman Kei Son +Keith Rarick Ken Friedenbach Ken Thompson Kevin Ballard Kirklin McDonald Kyle Consalus +Kyle Lemons Larry Hosken +Lorenzo Stoakes +Luuk van Dijk Mark Zavislak +Markus Duft +Martin Neubauer +Mathieu Lonjaret Maxim Ushakov Micah Stetson Michael Elkins Michael Hoisie +Mikio Hara +Mikkel Krautz Moriyoshi Koizumi Môshe van der Sterre +Nicholas Waples +Nigel Kerr Nigel Tao Paolo Giarrusso +Patrick Gavlin Petar Maymounkov Peter Froehlich Peter McKenzie @@ -112,21 +150,31 @@ Robert Griesemer Roger Peppe Ross Light Russ Cox -Sam Thorogood +Ryan Hitchman +Sam Thorogood Scott Schwartz +Scott Lawrence +Sebastien Binet Sergei Skorobogatov Sergey 'SnakE' Gromov Sergio Luis O. B. Correia Spring Mc +Stefan Nilsson Stephen Ma Stephen Weinberg Sven Almgren +Tarmigan Casebolt Tom Szymanski Timo Savola Tor Andersson Trevor Strohman +Vincent Ambo Vinu Rajashekhar Vish Subramanian +Wei Guangjing William Josephson Yongjian Xu +Yasuhiro Matsumoto +Yuusei Kuwana +Yuval Pavel Zholkover Yves Junqueira diff --git a/LICENSE b/LICENSE index d77335ff8..6a66aea5e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,42 +1,27 @@ -// Copyright (c) 2009 The Go Authors. 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. -// -// Subject to the terms and conditions of this License, Google hereby -// grants to You a perpetual, worldwide, non-exclusive, no-charge, -// royalty-free, irrevocable (except as stated in this section) patent -// license to make, have made, use, offer to sell, sell, import, and -// otherwise transfer this implementation of Go, where such license -// applies only to those patent claims licensable by Google that are -// necessarily infringed by use of this implementation of Go. If You -// institute patent litigation against any entity (including a -// cross-claim or counterclaim in a lawsuit) alleging that this -// implementation of Go or a Contribution incorporated within this -// implementation of Go constitutes direct or contributory patent -// infringement, then any patent licenses granted to You under this -// License for this implementation of Go shall terminate as of the date -// such litigation is filed. +Copyright (c) 2009 The Go Authors. 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. diff --git a/PATENTS b/PATENTS new file mode 100644 index 000000000..733099041 --- /dev/null +++ b/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/README b/README index e25bbdb8d..8bf9e7b8c 100644 --- a/README +++ b/README @@ -9,3 +9,23 @@ and then visiting http://localhost:6060/doc/install.html. Unless otherwise noted, the Go source files are distributed under the BSD-style license found in the LICENSE file. + +-- + +Binary Distribution Notes + +If you have just untarred a binary Go distribution, you need to set +the environment variable $GOROOT to the full path of the go +directory (the one containing this README). You can omit the +variable if you unpack it into /usr/local/go, or if you rebuild +from sources by running all.bash (see doc/install.html). +You should also add the Go binary directory $GOROOT/bin +to your shell's path. + +For example, if you extracted the tar file into $HOME/go, you might +put the following in your .profile: + + export GOROOT=$HOME/go + export PATH=$PATH:$GOROOT/bin + +See doc/install.html for more details. diff --git a/doc/ExpressivenessOfGo.pdf b/doc/ExpressivenessOfGo.pdf new file mode 100644 index 000000000..f1931d081 Binary files /dev/null and b/doc/ExpressivenessOfGo.pdf differ diff --git a/doc/Makefile b/doc/Makefile index 0abb881b1..d992a39f3 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include $(GOROOT)/src/Make.$(GOARCH) +include ../src/Make.inc TARG=htmlgen GOFILES=\ htmlgen.go\ -include $(GOROOT)/src/Make.cmd +include ../src/Make.cmd diff --git a/doc/all.css b/doc/all.css new file mode 100644 index 000000000..f70ef1599 --- /dev/null +++ b/doc/all.css @@ -0,0 +1,197 @@ +/* General Styles */ +body { + font-family: "Bitstream Vera Sans", Verdana, sans-serif; + font-size: 81.25%; + line-height: 1.23em; + padding: 0; + margin: 1.23em; + background: white; + color: black; +} +a { + color: #04a; + text-decoration: none; +} +a:visited { + color: #04a; +} +a:hover { + color: #a40; + text-decoration: underline; +} +a:active { + color: #c00; +} +code, pre { + font-size: 1.2em; +} +pre { + background: #F0F0F0; + padding: 0.5em 1em; +} + +/* Top bar */ +#container { + width: 100%; + margin: auto; +} +#topnav { + height: 55px; + background: url(/doc/logo.png) no-repeat top left; +} +a#logo-box { + display: block; + height: 55px; +} +h1#title { + display: none; +} +#nav-main { + float: right; + width: 500px; + margin-top: -5px; + text-align: center; +} +#nav-main ul { + padding-left: 0; + margin-left: 0; + margin-bottom: 0.5em; +} +#nav-main li a { + display: inline; + display: inline-block; + padding: .46em .62em .38em .62em; +} +#nav-main li a:link, +#nav-main li a:visited { + color: #000; +} +#nav-main li { + display: inline; + display: inline-block; + background: #e6e6e6 url(/doc/button_background.png) repeat-x; + border: solid 1px #999; + margin-left: -1px; + text-shadow: #fff 0 1px 0; + box-shadow: 0 1px 1px #ccc; + -moz-box-shadow: 0 1px 1px #ccc; + -webkit-box-shadow: 0 1px 1px #ccc; +} +#nav-main li:first-child { + -moz-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; +} +#nav-main li:last-child { + -moz-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; +} +#nav-main .quickref { + color: #444; +} +#nav-main .quickref .sep { + color: #999; +} +#search { + width: 100px; + margin-left: 0.5em; +} +#search.inactive { + text-align: center; + color: #444; +} + +/* Footer */ +#site-info { + position: relative; + text-align: center; +} +#site-info, #site-info a:link, #site-info a:visited { + color: #aaa; +} + +/* Content */ +#content { + clear: both; + padding: 0; + position: relative; + margin-top: 1.5em; + margin-bottom: 1.5em; + border-top: solid 1px #aaa; + border-bottom: solid 1px #aaa; +} +.left-column { + width: 49%; + float: left; +} +.right-column { + width: 49%; + float: right; +} +.end-columns { + clear: both; +} +#content h1 { + margin-bottom: -0em; + padding: 0; +} +#content h2 { + border-top: 1px solid #ddd; + background: #E2E7F0; + padding: 5px; + margin: 1.5em 0 0; +} +#content .subtitle { + margin-top: 1em; + display: block; +} +.navtop a { + font-weight: normal; font-size: 7pt; + float: right; color: #999; +} + +/* Content and Code Highlighting */ +pre.ebnf, pre.grammar { + background: #FFFFE0; +} +span.comment { + color: #002090; +} +span.highlight { + background: #FF9900; + font-weight: bold; +} +span.highlight-comment { + background: #FF9900; + font-weight: bold; + color: #002090; +} +span.selection { + background: #FFFF00 +} +span.selection-comment { + color: #002090; + background: #FFFF00 +} +span.selection-highlight { + background: #FF9900; + font-weight: bold; +} +span.selection-highlight-comment { + background: #FF9900; + font-weight: bold; + color: #002090; +} +span.alert { + color: #D00000; +} +#nav table { + width: 100%; +} +.detail { + padding: 0.25em 1em; + background: #F4F4F4; +} diff --git a/doc/button_background.png b/doc/button_background.png new file mode 100644 index 000000000..86a3b3086 Binary files /dev/null and b/doc/button_background.png differ diff --git a/doc/code.html b/doc/code.html index 14bb6f9fe..55afe09af 100644 --- a/doc/code.html +++ b/doc/code.html @@ -64,7 +64,7 @@ is illustrated by src/pkg/c

-include ../../../Make.$(GOARCH)
+include ../../../Make.inc
 
 TARG=container/vector
 GOFILES=\
@@ -80,7 +80,7 @@ Outside the Go source tree (for personal packages), the standard form is
 

-include $(GOROOT)/src/Make.$(GOARCH)
+include $(GOROOT)/src/Make.inc
 
 TARG=mypackage
 GOFILES=\
@@ -98,6 +98,14 @@ even if $(GOROOT) contains spaces.
 This makes it easy for programmers to try Go.
 

+

+If you have not set $GOROOT in your environment, +you must run gomake to use this form of makefile. +Gomake also takes care to invoke GNU Make +even on systems where it is installed as gmake +rather than make. +

+

TARG is the target install path for the package, the string that clients will use to import it. @@ -131,8 +139,8 @@ cd $GOROOT/src/pkg

to update the dependency file Make.deps. -(This happens automatically each time you run make all -or make build.) +(This happens automatically each time you run all.bash +or make.bash.)

@@ -169,6 +177,32 @@ Writing clean, idiomatic Go code is beyond the scope of this document. that topic.

+

Building programs

+

To build a Go program with gomake, create a Makefile alongside your program's +source files. It should be similar to the example above, but include +Make.cmd instead of Make.pkg: + +

+include $(GOROOT)/src/Make.inc
+
+TARG=helloworld
+GOFILES=\
+	helloworld.go\
+
+include $(GOROOT)/src/Make.cmd
+
+ +

Running gomake will compile helloworld.go +and produce an executable named helloworld in the current +directory. +

+ +

+Running gomake install will build helloworld if +necessary and copy it to the $GOBIN directory +($GOROOT/bin/ is the default). +

+

Testing

@@ -259,7 +293,7 @@ Finally, the Makefile:

-include $(GOROOT)/src/Make.$(GOARCH)
+include $(GOROOT)/src/Make.inc
 
 TARG=numbers
 GOFILES=\
@@ -269,13 +303,13 @@ include $(GOROOT)/src/Make.pkg
 

-Running make install will build and install the package to +Running gomake install will build and install the package to the $GOROOT/pkg/ directory (it can then be used by any program on the system).

-Running make test (or just running the command +Running gomake test (or just running the command gotest) will rebuild the package, including the numbers_test.go file, and then run the TestDouble function. The output "PASS" indicates that all tests passed diff --git a/doc/codelab/wiki/Makefile b/doc/codelab/wiki/Makefile index 76ab5c5bc..e0549fc8e 100644 --- a/doc/codelab/wiki/Makefile +++ b/doc/codelab/wiki/Makefile @@ -2,16 +2,11 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../src/Make.$(GOARCH) +include ../../../src/Make.inc all: index.html -# ugly hack to deal with whitespaces in $GOROOT -nullstring := -space := $(nullstring) # a space at the end -QUOTED_GOROOT:=$(subst $(space),\ ,$(GOROOT)) - -include $(QUOTED_GOROOT)/src/Make.common +include ../../../src/Make.common CLEANFILES+=index.html srcextract.bin htmlify.bin @@ -23,7 +18,7 @@ test: final.bin rm -f final.6 final.bin %.bin: %.$O - $(QUOTED_GOBIN)/$(LD) -o $@ $< -%.$O: - $(QUOTED_GOBIN)/$(GC) $*.go + $(LD) -o $@ $< +%.$O: + $(GC) $*.go diff --git a/doc/codelab/wiki/final-noclosure.go b/doc/codelab/wiki/final-noclosure.go index d4ce71560..2f48565ca 100644 --- a/doc/codelab/wiki/final-noclosure.go +++ b/doc/codelab/wiki/final-noclosure.go @@ -27,21 +27,21 @@ func loadPage(title string) (*page, os.Error) { return &page{title: title, body: body}, nil } -func viewHandler(c *http.Conn, r *http.Request) { - title, err := getTitle(c, r) +func viewHandler(w http.ResponseWriter, r *http.Request) { + title, err := getTitle(w, r) if err != nil { return } p, err := loadPage(title) if err != nil { - http.Redirect(c, "/edit/"+title, http.StatusFound) + http.Redirect(w, r, "/edit/"+title, http.StatusFound) return } - renderTemplate(c, "view", p) + renderTemplate(w, "view", p) } -func editHandler(c *http.Conn, r *http.Request) { - title, err := getTitle(c, r) +func editHandler(w http.ResponseWriter, r *http.Request) { + title, err := getTitle(w, r) if err != nil { return } @@ -49,11 +49,11 @@ func editHandler(c *http.Conn, r *http.Request) { if err != nil { p = &page{title: title} } - renderTemplate(c, "edit", p) + renderTemplate(w, "edit", p) } -func saveHandler(c *http.Conn, r *http.Request) { - title, err := getTitle(c, r) +func saveHandler(w http.ResponseWriter, r *http.Request) { + title, err := getTitle(w, r) if err != nil { return } @@ -61,21 +61,21 @@ func saveHandler(c *http.Conn, r *http.Request) { p := &page{title: title, body: []byte(body)} err = p.save() if err != nil { - http.Error(c, err.String(), http.StatusInternalServerError) + http.Error(w, err.String(), http.StatusInternalServerError) return } - http.Redirect(c, "/view/"+title, http.StatusFound) + http.Redirect(w, r, "/view/"+title, http.StatusFound) } -func renderTemplate(c *http.Conn, tmpl string, p *page) { +func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { t, err := template.ParseFile(tmpl+".html", nil) if err != nil { - http.Error(c, err.String(), http.StatusInternalServerError) + http.Error(w, err.String(), http.StatusInternalServerError) return } - err = t.Execute(p, c) + err = t.Execute(p, w) if err != nil { - http.Error(c, err.String(), http.StatusInternalServerError) + http.Error(w, err.String(), http.StatusInternalServerError) } } @@ -83,10 +83,10 @@ const lenPath = len("/view/") var titleValidator = regexp.MustCompile("^[a-zA-Z0-9]+$") -func getTitle(c *http.Conn, r *http.Request) (title string, err os.Error) { +func getTitle(w http.ResponseWriter, r *http.Request) (title string, err os.Error) { title = r.URL.Path[lenPath:] if !titleValidator.MatchString(title) { - http.NotFound(c, r) + http.NotFound(w, r) err = os.NewError("Invalid Page Title") } return diff --git a/doc/codelab/wiki/final-noerror.go b/doc/codelab/wiki/final-noerror.go index 3b699452a..cf4852265 100644 --- a/doc/codelab/wiki/final-noerror.go +++ b/doc/codelab/wiki/final-noerror.go @@ -28,21 +28,21 @@ func loadPage(title string) (*page, os.Error) { const lenPath = len("/view/") -func editHandler(c *http.Conn, r *http.Request) { +func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, err := loadPage(title) if err != nil { p = &page{title: title} } t, _ := template.ParseFile("edit.html", nil) - t.Execute(p, c) + t.Execute(p, w) } -func viewHandler(c *http.Conn, r *http.Request) { +func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, _ := loadPage(title) t, _ := template.ParseFile("view.html", nil) - t.Execute(p, c) + t.Execute(p, w) } func main() { diff --git a/doc/codelab/wiki/final-parsetemplate.go b/doc/codelab/wiki/final-parsetemplate.go index 93b956b9d..f02d116b2 100644 --- a/doc/codelab/wiki/final-parsetemplate.go +++ b/doc/codelab/wiki/final-parsetemplate.go @@ -27,43 +27,43 @@ func loadPage(title string) (*page, os.Error) { return &page{title: title, body: body}, nil } -func viewHandler(c *http.Conn, r *http.Request, title string) { +func viewHandler(w http.ResponseWriter, r *http.Request, title string) { p, err := loadPage(title) if err != nil { - http.Redirect(c, "/edit/"+title, http.StatusFound) + http.Redirect(w, r, "/edit/"+title, http.StatusFound) return } - renderTemplate(c, "view", p) + renderTemplate(w, "view", p) } -func editHandler(c *http.Conn, r *http.Request, title string) { +func editHandler(w http.ResponseWriter, r *http.Request, title string) { p, err := loadPage(title) if err != nil { p = &page{title: title} } - renderTemplate(c, "edit", p) + renderTemplate(w, "edit", p) } -func saveHandler(c *http.Conn, r *http.Request, title string) { +func saveHandler(w http.ResponseWriter, r *http.Request, title string) { body := r.FormValue("body") p := &page{title: title, body: []byte(body)} err := p.save() if err != nil { - http.Error(c, err.String(), http.StatusInternalServerError) + http.Error(w, err.String(), http.StatusInternalServerError) return } - http.Redirect(c, "/view/"+title, http.StatusFound) + http.Redirect(w, r, "/view/"+title, http.StatusFound) } -func renderTemplate(c *http.Conn, tmpl string, p *page) { +func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { t, err := template.ParseFile(tmpl+".html", nil) if err != nil { - http.Error(c, err.String(), http.StatusInternalServerError) + http.Error(w, err.String(), http.StatusInternalServerError) return } - err = t.Execute(p, c) + err = t.Execute(p, w) if err != nil { - http.Error(c, err.String(), http.StatusInternalServerError) + http.Error(w, err.String(), http.StatusInternalServerError) } } @@ -71,14 +71,14 @@ const lenPath = len("/view/") var titleValidator = regexp.MustCompile("^[a-zA-Z0-9]+$") -func makeHandler(fn func(*http.Conn, *http.Request, string)) http.HandlerFunc { - return func(c *http.Conn, r *http.Request) { +func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] if !titleValidator.MatchString(title) { - http.NotFound(c, r) + http.NotFound(w, r) return } - fn(c, r, title) + fn(w, r, title) } } diff --git a/doc/codelab/wiki/final-template.go b/doc/codelab/wiki/final-template.go index 06c9366ad..0bb133d3a 100644 --- a/doc/codelab/wiki/final-template.go +++ b/doc/codelab/wiki/final-template.go @@ -28,32 +28,32 @@ func loadPage(title string) (*page, os.Error) { const lenPath = len("/view/") -func editHandler(c *http.Conn, r *http.Request) { +func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, err := loadPage(title) if err != nil { p = &page{title: title} } - renderTemplate(c, "edit", p) + renderTemplate(w, "edit", p) } -func viewHandler(c *http.Conn, r *http.Request) { +func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, _ := loadPage(title) - renderTemplate(c, "view", p) + renderTemplate(w, "view", p) } -func saveHandler(c *http.Conn, r *http.Request) { +func saveHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] body := r.FormValue("body") p := &page{title: title, body: []byte(body)} p.save() - http.Redirect(c, "/view/"+title, http.StatusFound) + http.Redirect(w, r, "/view/"+title, http.StatusFound) } -func renderTemplate(c *http.Conn, tmpl string, p *page) { +func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { t, _ := template.ParseFile(tmpl+".html", nil) - t.Execute(p, c) + t.Execute(p, w) } func main() { diff --git a/doc/codelab/wiki/final.go b/doc/codelab/wiki/final.go index 0186729c2..0c0206bc0 100644 --- a/doc/codelab/wiki/final.go +++ b/doc/codelab/wiki/final.go @@ -27,32 +27,32 @@ func loadPage(title string) (*page, os.Error) { return &page{title: title, body: body}, nil } -func viewHandler(c *http.Conn, r *http.Request, title string) { +func viewHandler(w http.ResponseWriter, r *http.Request, title string) { p, err := loadPage(title) if err != nil { - http.Redirect(c, "/edit/"+title, http.StatusFound) + http.Redirect(w, r, "/edit/"+title, http.StatusFound) return } - renderTemplate(c, "view", p) + renderTemplate(w, "view", p) } -func editHandler(c *http.Conn, r *http.Request, title string) { +func editHandler(w http.ResponseWriter, r *http.Request, title string) { p, err := loadPage(title) if err != nil { p = &page{title: title} } - renderTemplate(c, "edit", p) + renderTemplate(w, "edit", p) } -func saveHandler(c *http.Conn, r *http.Request, title string) { +func saveHandler(w http.ResponseWriter, r *http.Request, title string) { body := r.FormValue("body") p := &page{title: title, body: []byte(body)} err := p.save() if err != nil { - http.Error(c, err.String(), http.StatusInternalServerError) + http.Error(w, err.String(), http.StatusInternalServerError) return } - http.Redirect(c, "/view/"+title, http.StatusFound) + http.Redirect(w, r, "/view/"+title, http.StatusFound) } var templates = make(map[string]*template.Template) @@ -63,10 +63,10 @@ func init() { } } -func renderTemplate(c *http.Conn, tmpl string, p *page) { - err := templates[tmpl].Execute(p, c) +func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { + err := templates[tmpl].Execute(p, w) if err != nil { - http.Error(c, err.String(), http.StatusInternalServerError) + http.Error(w, err.String(), http.StatusInternalServerError) } } @@ -74,14 +74,14 @@ const lenPath = len("/view/") var titleValidator = regexp.MustCompile("^[a-zA-Z0-9]+$") -func makeHandler(fn func(*http.Conn, *http.Request, string)) http.HandlerFunc { - return func(c *http.Conn, r *http.Request) { +func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] if !titleValidator.MatchString(title) { - http.NotFound(c, r) + http.NotFound(w, r) return } - fn(c, r, title) + fn(w, r, title) } } diff --git a/doc/codelab/wiki/http-sample.go b/doc/codelab/wiki/http-sample.go index 11d5d7861..33379a1b6 100644 --- a/doc/codelab/wiki/http-sample.go +++ b/doc/codelab/wiki/http-sample.go @@ -5,8 +5,8 @@ import ( "http" ) -func handler(c *http.Conn, r *http.Request) { - fmt.Fprintf(c, "Hi there, I love %s!", r.URL.Path[1:]) +func handler(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:]) } func main() { diff --git a/doc/codelab/wiki/index.html b/doc/codelab/wiki/index.html index 107e49f26..c494a3ced 100644 --- a/doc/codelab/wiki/index.html +++ b/doc/codelab/wiki/index.html @@ -1,7 +1,4 @@ -

- -

Writing Web Applications

- +

Introduction

@@ -243,8 +240,8 @@ import ( "http" ) -func handler(c *http.Conn, r *http.Request) { - fmt.Fprintf(c, "Hi there, I love %s!", r.URL.Path[1:]) +func handler(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:]) } func main() { @@ -269,12 +266,12 @@ This function will block until the program is terminated.

The function handler is of the type http.HandlerFunc. -It takes an http.Conn and http.Request as its -arguments. +It takes an http.ResponseWriter and an http.Request as +its arguments.

-An http.Conn is the server end of an HTTP connection; by writing +An http.ResponseWriter value assembles the HTTP server's response; by writing to it, we send data to the HTTP client.

@@ -317,10 +314,10 @@ Let's create a handler to view a wiki page:
 const lenPath = len("/view/")
 
-func viewHandler(c *http.Conn, r *http.Request) {
+func viewHandler(w http.ResponseWriter, r *http.Request) {
 	title := r.URL.Path[lenPath:]
 	p, _ := loadPage(title)
-	fmt.Fprintf(c, "<h1>%s</h1><div>%s</div>", p.title, p.body)
+	fmt.Fprintf(w, "<h1>%s</h1><div>%s</div>", p.title, p.body)
 }
 
@@ -336,7 +333,7 @@ begin with "/view/", which is not part of the page title.

The function then loads the page data, formats the page with a string of simple -HTML, and writes it to c, the http.Conn. +HTML, and writes it to w, the http.ResponseWriter.

@@ -409,13 +406,13 @@ and displays an HTML form.

-func editHandler(c *http.Conn, r *http.Request) {
+func editHandler(w http.ResponseWriter, r *http.Request) {
 	title := r.URL.Path[lenPath:]
 	p, err := loadPage(title)
 	if err != nil {
 		p = &page{title: title}
 	}
-	fmt.Fprintf(c, "<h1>Editing %s</h1>"+
+	fmt.Fprintf(w, "<h1>Editing %s</h1>"+
 		"<form action=\"/save/%s\" method=\"POST\">"+
 		"<textarea name=\"body\">%s</textarea><br>"+
 		"<input type=\"submit\" value=\"Save\">"+
@@ -471,14 +468,14 @@ HTML:
 

-func editHandler(c *http.Conn, r *http.Request) {
+func editHandler(w http.ResponseWriter, r *http.Request) {
 	title := r.URL.Path[lenPath:]
 	p, err := loadPage(title)
 	if err != nil {
 		p = &page{title: title}
 	}
 	t, _ := template.ParseFile("edit.html", nil)
-	t.Execute(p, c)
+	t.Execute(p, w)
 }
 
@@ -491,7 +488,7 @@ The function template.ParseFile will read the contents of The method t.Execute replaces all occurrences of {title} and {body} with the values of p.title and p.body, and writes the resultant -HTML to the http.Conn. +HTML to the http.ResponseWriter.

@@ -526,11 +523,11 @@ Modify viewHandler accordingly:

-func viewHandler(c *http.Conn, r *http.Request) {
+func viewHandler(w http.ResponseWriter, r *http.Request) {
 	title := r.URL.Path[lenPath:]
 	p, _ := loadPage(title)
 	t, _ := template.ParseFile("view.html", nil)
-	t.Execute(p, c)
+	t.Execute(p, w)
 }
 
@@ -541,24 +538,24 @@ to its own function:

-func viewHandler(c *http.Conn, r *http.Request) {
+func viewHandler(w http.ResponseWriter, r *http.Request) {
 	title := r.URL.Path[lenPath:]
 	p, _ := loadPage(title)
-	renderTemplate(c, "view", p)
+	renderTemplate(w, "view", p)
 }
 
-func editHandler(c *http.Conn, r *http.Request) {
+func editHandler(w http.ResponseWriter, r *http.Request) {
 	title := r.URL.Path[lenPath:]
 	p, err := loadPage(title)
 	if err != nil {
 		p = &page{title: title}
 	}
-	renderTemplate(c, "edit", p)
+	renderTemplate(w, "edit", p)
 }
 
-func renderTemplate(c *http.Conn, tmpl string, p *page) {
+func renderTemplate(w http.ResponseWriter, tmpl string, p *page) {
 	t, _ := template.ParseFile(tmpl+".html", nil)
-	t.Execute(p, c)
+	t.Execute(p, w)
 }
 
@@ -576,13 +573,13 @@ redirect the client to the edit page so the content may be created:

-func viewHandler(c *http.Conn, r *http.Request, title string) {
+func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
 	p, err := loadPage(title)
 	if err != nil {
-		http.Redirect(c, "/edit/"+title, http.StatusFound)
+		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
 		return
 	}
-	renderTemplate(c, "view", p)
+	renderTemplate(w, "view", p)
 }
 
@@ -599,12 +596,12 @@ The function saveHandler will handle the form submission.

-func saveHandler(c *http.Conn, r *http.Request) {
+func saveHandler(w http.ResponseWriter, r *http.Request) {
 	title := r.URL.Path[lenPath:]
 	body := r.FormValue("body")
 	p := &page{title: title, body: []byte(body)}
 	p.save()
-	http.Redirect(c, "/view/"+title, http.StatusFound)
+	http.Redirect(w, r, "/view/"+title, http.StatusFound)
 }
 
@@ -637,15 +634,15 @@ First, let's handle the errors in renderTemplate:

-func renderTemplate(c *http.Conn, tmpl string, p *page) {
+func renderTemplate(w http.ResponseWriter, tmpl string, p *page) {
 	t, err := template.ParseFile(tmpl+".html", nil)
 	if err != nil {
-		http.Error(c, err.String(), http.StatusInternalServerError)
+		http.Error(w, err.String(), http.StatusInternalServerError)
 		return
 	}
-	err = t.Execute(p, c)
+	err = t.Execute(p, w)
 	if err != nil {
-		http.Error(c, err.String(), http.StatusInternalServerError)
+		http.Error(w, err.String(), http.StatusInternalServerError)
 	}
 }
 
@@ -661,15 +658,15 @@ Now let's fix up saveHandler:

-func saveHandler(c *http.Conn, r *http.Request, title string) {
+func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
 	body := r.FormValue("body")
 	p := &page{title: title, body: []byte(body)}
 	err := p.save()
 	if err != nil {
-		http.Error(c, err.String(), http.StatusInternalServerError)
+		http.Error(w, err.String(), http.StatusInternalServerError)
 		return
 	}
-	http.Redirect(c, "/view/"+title, http.StatusFound)
+	http.Redirect(w, r, "/view/"+title, http.StatusFound)
 }
 
@@ -728,10 +725,10 @@ the Execute method on the appropriate Template from templates:
-func renderTemplate(c *http.Conn, tmpl string, p *page) {
-	err := templates[tmpl].Execute(p, c)
+func renderTemplate(w http.ResponseWriter, tmpl string, p *page) {
+	err := templates[tmpl].Execute(p, w)
 	if err != nil {
-		http.Error(c, err.String(), http.StatusInternalServerError)
+		http.Error(w, err.String(), http.StatusInternalServerError)
 	}
 }
 
@@ -768,10 +765,10 @@ URL, and tests it against our titleValidator expression:

-func getTitle(c *http.Conn, r *http.Request) (title string, err os.Error) {
+func getTitle(w http.ResponseWriter, r *http.Request) (title string, err os.Error) {
 	title = r.URL.Path[lenPath:]
 	if !titleValidator.MatchString(title) {
-		http.NotFound(c, r)
+		http.NotFound(w, r)
 		err = os.NewError("Invalid Page Title")
 	}
 	return
@@ -790,21 +787,21 @@ Let's put a call to getTitle in each of the handlers:
 

-func viewHandler(c *http.Conn, r *http.Request) {
-	title, err := getTitle(c, r)
+func viewHandler(w http.ResponseWriter, r *http.Request) {
+	title, err := getTitle(w, r)
 	if err != nil {
 		return
 	}
 	p, err := loadPage(title)
 	if err != nil {
-		http.Redirect(c, "/edit/"+title, http.StatusFound)
+		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
 		return
 	}
-	renderTemplate(c, "view", p)
+	renderTemplate(w, "view", p)
 }
 
-func editHandler(c *http.Conn, r *http.Request) {
-	title, err := getTitle(c, r)
+func editHandler(w http.ResponseWriter, r *http.Request) {
+	title, err := getTitle(w, r)
 	if err != nil {
 		return
 	}
@@ -812,11 +809,11 @@ func editHandler(c *http.Conn, r *http.Request) {
 	if err != nil {
 		p = &page{title: title}
 	}
-	renderTemplate(c, "edit", p)
+	renderTemplate(w, "edit", p)
 }
 
-func saveHandler(c *http.Conn, r *http.Request) {
-	title, err := getTitle(c, r)
+func saveHandler(w http.ResponseWriter, r *http.Request) {
+	title, err := getTitle(w, r)
 	if err != nil {
 		return
 	}
@@ -824,10 +821,10 @@ func saveHandler(c *http.Conn, r *http.Request) {
 	p := &page{title: title, body: []byte(body)}
 	err = p.save()
 	if err != nil {
-		http.Error(c, err.String(), http.StatusInternalServerError)
+		http.Error(w, err.String(), http.StatusInternalServerError)
 		return
 	}
-	http.Redirect(c, "/view/"+title, http.StatusFound)
+	http.Redirect(w, r, "/view/"+title, http.StatusFound)
 }
 
@@ -848,9 +845,9 @@ a title string:

-func viewHandler(c *http.Conn, r *http.Request, title string)
-func editHandler(c *http.Conn, r *http.Request, title string)
-func saveHandler(c *http.Conn, r *http.Request, title string)
+func viewHandler(w http.ResponseWriter, r *http.Request, title string)
+func editHandler(w http.ResponseWriter, r *http.Request, title string)
+func saveHandler(w http.ResponseWriter, r *http.Request, title string)
 

@@ -860,8 +857,8 @@ type, and returns a function of type http.HandlerFunc

-func makeHandler(fn func (*http.Conn, *http.Request, string)) http.HandlerFunc {
-	return func(c *http.Conn, r *http.Request) {
+func makeHandler(fn func (http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
 		// Here we will extract the page title from the Request,
 		// and call the provided handler 'fn'
 	}
@@ -881,28 +878,28 @@ Now we can take the code from getTitle and use it here
 

-func makeHandler(fn func(*http.Conn, *http.Request, string)) http.HandlerFunc {
-	return func(c *http.Conn, r *http.Request) {
+func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
 		title := r.URL.Path[lenPath:]
 		if !titleValidator.MatchString(title) {
-			http.NotFound(c, r)
+			http.NotFound(w, r)
 			return
 		}
-		fn(c, r, title)
+		fn(w, r, title)
 	}
 }
 

The closure returned by makeHandler is a function that takes -an http.Conn and http.Request (in other words, -an http.HandlerFunc). +an http.ResponseWriter and http.Request (in other +words, an http.HandlerFunc). The closure extracts the title from the request path, and validates it with the titleValidator regexp. If the title is invalid, an error will be written to the -Conn using the http.NotFound function. +ResponseWriter using the http.NotFound function. If the title is valid, the enclosed handler function -fn will be called with the Conn, +fn will be called with the ResponseWriter, Request, and title as arguments.

@@ -927,32 +924,32 @@ making them much simpler:

-func viewHandler(c *http.Conn, r *http.Request, title string) {
+func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
 	p, err := loadPage(title)
 	if err != nil {
-		http.Redirect(c, "/edit/"+title, http.StatusFound)
+		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
 		return
 	}
-	renderTemplate(c, "view", p)
+	renderTemplate(w, "view", p)
 }
 
-func editHandler(c *http.Conn, r *http.Request, title string) {
+func editHandler(w http.ResponseWriter, r *http.Request, title string) {
 	p, err := loadPage(title)
 	if err != nil {
 		p = &page{title: title}
 	}
-	renderTemplate(c, "edit", p)
+	renderTemplate(w, "edit", p)
 }
 
-func saveHandler(c *http.Conn, r *http.Request, title string) {
+func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
 	body := r.FormValue("body")
 	p := &page{title: title, body: []byte(body)}
 	err := p.save()
 	if err != nil {
-		http.Error(c, err.String(), http.StatusInternalServerError)
+		http.Error(w, err.String(), http.StatusInternalServerError)
 		return
 	}
-	http.Redirect(c, "/view/"+title, http.StatusFound)
+	http.Redirect(w, r, "/view/"+title, http.StatusFound)
 }
 
@@ -996,5 +993,3 @@ Here are some simple tasks you might want to tackle on your own: (hint: you could use regexp.ReplaceAllFunc to do this) - -
diff --git a/doc/codelab/wiki/notemplate.go b/doc/codelab/wiki/notemplate.go index a61d905e3..c1f952c83 100644 --- a/doc/codelab/wiki/notemplate.go +++ b/doc/codelab/wiki/notemplate.go @@ -28,19 +28,19 @@ func loadPage(title string) (*page, os.Error) { const lenPath = len("/view/") -func viewHandler(c *http.Conn, r *http.Request) { +func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, _ := loadPage(title) - fmt.Fprintf(c, "

%s

%s
", p.title, p.body) + fmt.Fprintf(w, "

%s

%s
", p.title, p.body) } -func editHandler(c *http.Conn, r *http.Request) { +func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, err := loadPage(title) if err != nil { p = &page{title: title} } - fmt.Fprintf(c, "

Editing %s

"+ + fmt.Fprintf(w, "

Editing %s

"+ "
"+ "
"+ ""+ diff --git a/doc/codelab/wiki/part2.go b/doc/codelab/wiki/part2.go index c2c29dc3b..8d4454a74 100644 --- a/doc/codelab/wiki/part2.go +++ b/doc/codelab/wiki/part2.go @@ -28,10 +28,10 @@ func loadPage(title string) (*page, os.Error) { const lenPath = len("/view/") -func viewHandler(c *http.Conn, r *http.Request) { +func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, _ := loadPage(title) - fmt.Fprintf(c, "

%s

%s
", p.title, p.body) + fmt.Fprintf(w, "

%s

%s
", p.title, p.body) } func main() { diff --git a/doc/codelab/wiki/srcextract.go b/doc/codelab/wiki/srcextract.go index 607375183..0addc61c4 100644 --- a/doc/codelab/wiki/srcextract.go +++ b/doc/codelab/wiki/srcextract.go @@ -25,7 +25,7 @@ func main() { os.Exit(2) } // load file - file, err := parser.ParseFile(*srcFn, nil, nil, 0) + file, err := parser.ParseFile(*srcFn, nil, 0) if err != nil { log.Exit(err) } diff --git a/doc/codelab/wiki/wiki.html b/doc/codelab/wiki/wiki.html index c7f44ded4..919385edf 100644 --- a/doc/codelab/wiki/wiki.html +++ b/doc/codelab/wiki/wiki.html @@ -1,7 +1,4 @@ -
- -

Writing Web Applications

- +

Introduction

@@ -233,12 +230,12 @@ This function will block until the program is terminated.

The function handler is of the type http.HandlerFunc. -It takes an http.Conn and http.Request as its -arguments. +It takes an http.ResponseWriter and an http.Request as +its arguments.

-An http.Conn is the server end of an HTTP connection; by writing +An http.ResponseWriter value assembles the HTTP server's response; by writing to it, we send data to the HTTP client.

@@ -296,7 +293,7 @@ begin with "/view/", which is not part of the page title.

The function then loads the page data, formats the page with a string of simple -HTML, and writes it to c, the http.Conn. +HTML, and writes it to w, the http.ResponseWriter.

@@ -418,7 +415,7 @@ The function template.ParseFile will read the contents of The method t.Execute replaces all occurrences of {title} and {body} with the values of p.title and p.body, and writes the resultant -HTML to the http.Conn. +HTML to the http.ResponseWriter.

@@ -670,9 +667,9 @@ a title string:

-func viewHandler(c *http.Conn, r *http.Request, title string)
-func editHandler(c *http.Conn, r *http.Request, title string)
-func saveHandler(c *http.Conn, r *http.Request, title string)
+func viewHandler(w http.ResponseWriter, r *http.Request, title string)
+func editHandler(w http.ResponseWriter, r *http.Request, title string)
+func saveHandler(w http.ResponseWriter, r *http.Request, title string)
 

@@ -682,8 +679,8 @@ type, and returns a function of type http.HandlerFunc

-func makeHandler(fn func (*http.Conn, *http.Request, string)) http.HandlerFunc {
-	return func(c *http.Conn, r *http.Request) {
+func makeHandler(fn func (http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
 		// Here we will extract the page title from the Request,
 		// and call the provided handler 'fn'
 	}
@@ -708,14 +705,14 @@ Now we can take the code from getTitle and use it here
 
 

The closure returned by makeHandler is a function that takes -an http.Conn and http.Request (in other words, -an http.HandlerFunc). +an http.ResponseWriter and http.Request (in other +words, an http.HandlerFunc). The closure extracts the title from the request path, and validates it with the titleValidator regexp. If the title is invalid, an error will be written to the -Conn using the http.NotFound function. +ResponseWriter using the http.NotFound function. If the title is valid, the enclosed handler function -fn will be called with the Conn, +fn will be called with the ResponseWriter, Request, and title as arguments.

@@ -782,5 +779,3 @@ Here are some simple tasks you might want to tackle on your own: (hint: you could use regexp.ReplaceAllFunc to do this) - -
diff --git a/doc/codereview_with_mq.html b/doc/codereview_with_mq.html index 7b2e0f3bf..33f415f13 100644 --- a/doc/codereview_with_mq.html +++ b/doc/codereview_with_mq.html @@ -36,11 +36,11 @@ prevent that case:
 [hooks]
 # Prevent "hg pull" if MQ patches are applied.
-prechangegroup.mq-no-pull = ! hg qtop > /dev/null 2>&1
+prechangegroup.mq-no-pull = ! hg qtop > /dev/null 2>&1
 # Prevent "hg push" if MQ patches are applied.
-preoutgoing.mq-no-push = ! hg qtop > /dev/null 2>&1
+preoutgoing.mq-no-push = ! hg qtop > /dev/null 2>&1
 # Prevent "hg update" if MQ patches are applied.
-preupdate.mq-no-update = ! hg qtop > /dev/null 2>&1
+preupdate.mq-no-update = ! hg qtop > /dev/null 2>&1
 

Making a change

diff --git a/doc/codewalk/urlpoll.go b/doc/codewalk/urlpoll.go index 2629f2b68..b51be9502 100644 --- a/doc/codewalk/urlpoll.go +++ b/doc/codewalk/urlpoll.go @@ -52,9 +52,9 @@ func StateMonitor(updateInterval int64) chan<- State { // logState prints a state map. func logState(s map[string]string) { - log.Stdout("Current state:") + log.Println("Current state:") for k, v := range s { - log.Stdoutf(" %s %s", k, v) + log.Printf(" %s %s", k, v) } } @@ -69,7 +69,7 @@ type Resource struct { func (r *Resource) Poll() string { resp, err := http.Head(r.url) if err != nil { - log.Stderr("Error", r.url, err) + log.Println("Error", r.url, err) r.errCount++ return err.String() } diff --git a/doc/community.html b/doc/community.html new file mode 100644 index 000000000..c3b16cacb --- /dev/null +++ b/doc/community.html @@ -0,0 +1,53 @@ + + +
+ +

The Go Community

+ +

Go Nuts Mailing List

+

The golang-nuts +mailing list is for general Go discussion.

+ +

Go Packages Dashboard

+

A list of the most popular goinstall'd +Go libraries.

+ +

Go Project Dashboard

+

A list of external Go projects including programs and libraries.

+ +

Go IRC Channel

+

#go-nuts on irc.freenode.net is the official Go IRC channel.

+ +

@go_nuts at Twitter

+

The Go project's official Twitter account.

+ +
+ +
+ +

Blogs

+ +

The Go Blog

+

+The Go project's official blog, maintained by the core Go developers. +

+ +

research!rsc

+

+Posts labelled 'Go' by Russ Cox, one of the core Go developers. +

+ +

Airs

+

+Posts labelled 'Programming' by Ian Lance Taylor, one of the core Go developers. +

+ +

nf.id.au

+

+Posts labelled 'Go' by Andrew Gerrand, one of the core Go developers. +

+ +
+ +
+ diff --git a/doc/contrib.html b/doc/contrib.html new file mode 100644 index 000000000..121cc45dc --- /dev/null +++ b/doc/contrib.html @@ -0,0 +1,45 @@ + + +
+ +

Resources for Developers

+ +

Issue Tracker

+

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

+

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

+ +

Build Status

+

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

+ +

Contribution Guidelines

+

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

+

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

+ +
+ +
+ +

The Go Project

+ +

Roadmap

+

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

+ +

Release History

+

A summarization of the changes between tagged releases of Go.

+ +

Developer Mailing List

+

The golang-dev +mailing list is for discussing and reviewing code for the Go project.

+

For general discussion of Go programming, see golang-nuts.

+ +

Checkins Mailing List

+

A mailing list that receives a message summarizing each checkin to the Go repository.

+ +
+ +
+ diff --git a/doc/contribute.html b/doc/contribute.html index 6814274ba..ba70c9600 100644 --- a/doc/contribute.html +++ b/doc/contribute.html @@ -1,4 +1,4 @@ - +

Introduction

@@ -218,9 +218,9 @@ mailing list.

Replace “<enter description here>” with a description of your change. -The first line of the change description is conventionally -a one-line summary of the change and is used as the -subject for code review mail; the rest of the +The first line of the change description is conventionally a one-line +summary of the change, prefixed by the primary affected package, +and is used as the subject for code review mail; the rest of the description elaborates.

@@ -245,7 +245,7 @@ Reviewer: golang-dev@googlegroups.com CC: math-nuts@swtch.com Description: - Sin, Cos, Tan: improved precision for very large arguments + math: improved Sin, Cos and Tan precision for very large arguments. See Bimmler and Shaney, ``Extreme sinusoids,'' J. Math 3(14). Fixes issue 159. @@ -522,7 +522,7 @@ This rigmarole needs to be done only for your first submission.

Code that you contribute should use the standard copyright header:

-// Copyright 2010 The Go Authors. All rights reserved.
+// 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.
 
diff --git a/doc/devel/release.html b/doc/devel/release.html index 39c521c1e..ecf125953 100644 --- a/doc/devel/release.html +++ b/doc/devel/release.html @@ -5,6 +5,1149 @@

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

+

2011-01-12

+ +
+The json, gob, and template packages have changed, and code that uses them
+may need to be updated after this release. They will no longer read or write
+unexported struct fields. When marshalling a struct with json or gob the
+unexported fields will be silently ignored. Attempting to unmarshal json or
+gob data into an unexported field will generate an error. Accessing an
+unexported field from a template will cause the Execute function to return
+an error.
+
+Godoc now supports regular expression full text search, and this
+functionality is now available on golang.org.
+
+Other changes:
+* arm: initial cut at arm optimizer.
+* bytes.Buffer: Fix bug in UnreadByte.
+* cgo: export unsafe.Pointer as void*, fix enum const conflict,
+        output alignment fix (thanks Gustavo Niemeyer).
+* crypto/block: mark as deprecated.
+* crypto/openpgp: add error and armor.
+* crypto: add twofish package (thanks Berengar Lehr).
+* doc/spec: remove Maxalign from spec.
+* encoding/line: new package for reading lines from an io.Reader.
+* go/ast: correct end position for Index and TypeAssert expressions.
+* gob: make (en|dec)code(Ui|I)nt methods rather than functions.
+* godefs: better handling of enums.
+* gofmt: don't attempt certain illegal rewrites,
+        rewriter matches apply to expressions only.
+* goinstall: preliminary support for cgo packages (thanks Gustavo Niemeyer).
+* hg: add cgo/_cgo_* to .hgignore.
+* http: fix text displayed in Redirect.
+* ld: fix exported dynamic symbols on Mach-O,
+        permit a Mach-O symbol to be exported in the dynamic symbol table.
+* log: add methods for exit and panic.
+* net: use closesocket api instead of CloseHandle on Windows (thanks Alex Brainman).
+* netchan: make fields exported for gob change.
+* os: add Sync to *File, wraps syscall.Fsync.
+* runtime/cgo: Add callbacks to support SWIG.
+* runtime: Restore scheduler stack position if cgo callback panics.
+* suffixarray: faster creation algorithm (thanks Eric Eisner).
+* syscall: fix mksysnum_linux.sh (thanks Anthony Martin).
+* time.NewTicker: panic for intervals <= 0.
+* time: add AfterFunc to call a function after a duration (thanks Roger Peppe),
+        fix tick accuracy when using multiple Tickers (thanks Eoghan Sherry).
+ +

2011-01-06

+ +
+This release includes several fixes and changes:
+
+* build: Make.pkg: use installed runtime.h for cgo.
+* cgo: disallow use of C.errno.
+* crypto/cipher: fix OCFB,
+        make NewCBCEncrypter return BlockMode.
+* doc: 6l: fix documentation of -L flag,
+        add golanguage.ru to foreign-language doc list,
+        effective go: explain the effect of repanicking better,
+        update Effective Go for template API change,
+        update contribution guidelines to prefix the change description.
+* encoding/binary: reject types with implementation-dependent sizes (thanks Patrick Gavlin).
+* exp/evalsimple fix handling of slices like s[:2] (thanks Sebastien Binet).
+* fmt: made format string handling more efficient,
+        normalize processing of format string.
+* gc: return constant floats for parts of complex constants (thanks Anthony Martin),
+        rewrite complex /= to l = l / r (thanks Patrick Gavlin),
+        fix &^=.
+* go/ast: provide complete node text range info.
+* gob: generate a better error message in one confusing place.
+* godoc: fix godoc -src (thanks Icarus Sparry).
+* goinstall: add -clean flag (thanks Kyle Lemons),
+        add checkout concept (thanks Caine Tighe),
+        fix -u for bzr (thanks Gustavo Niemeyer).
+* http: permit empty Reason-Phrase in response Status-Line.
+* io: fix Copyn EOF handling.
+* net: fix close of Listener (thanks Michael Hoisie).
+* regexp: fix performance bug, make anchored searches fail fast,
+        fix prefix bug.
+* runtime/cgo: fix stackguard on FreeBSD/amd64 (thanks Anthony Martin).
+* strconv: atof: added 'E' as valid token for exponent (thanks Stefan Nilsson),
+        update ftoa comment for 'E' and 'G'.
+* strings: fix description of FieldsFunc (thanks Roger Peppe).
+* syscall: correct Linux Splice definition,
+        make Access second argument consistently uint32.
+
+ +

2010-12-22

+ +
+A small release this week. The most significant change is that some 
+outstanding cgo issues were resolved.
+
+* cgo: handle references to symbols in shared libraries.
+* crypto/elliptic: add serialisation and key pair generation.
+* crypto/hmac: add HMAC-SHA256 (thanks Anthony Martin).
+* crypto/tls: add ECDHE support ("Elliptic Curve Diffie Hellman Ephemeral"),
+        add support code for generating handshake scripts for testing.
+* darwin, freebsd: ignore write failure (during print, panic).
+* exp/draw: remove Border function.
+* expvar: quote StringFunc output, same as String output.
+* hash/crc64: fix typo in Sum.
+* ld: allow relocations pointing at ELF .bss symbols, ignore stab symbols.
+* misc/cgo/life: fix, add to build.
+* regexp: add HasMeta, HasOperator, and String methods to Regexp.
+* suffixarray: implemented FindAllIndex regexp search.
+* test/bench: update numbers for regex-dna after speedup to regexp.
+* time: explain the formats a little better.
+
+ +

2010-12-15

+ +
+Package crypto/cipher has been started, to replace crypto/block.
+As part of the changes, rc4.Cipher’s XORKeyStream method signature has changed from
+        XORKeyStream(buf []byte)
+to
+        XORKeyStream(dst, src []byte)
+to implement the cipher.Stream interface.  If you use crypto/block, you’ll need
+to switch to crypto/cipher once it is complete.
+
+Package smtp’s StartTLS now takes a *tls.Config argument.
+
+Package reflect’s ArrayCopy has been renamed to Copy.  There are new functions
+Append and AppendSlice.
+
+The print/println bootstrapping functions now write to standard error.
+To write to standard output, use fmt.Print[ln].
+
+A new tool, govet, has been added to the Go distribution. Govet is a static
+checker for Go programs. At the moment, and for the forseeable future,
+it only checks arguments to print calls.
+
+The cgo tool for writing Go bindings for C code has changed so that it no
+longer uses stub .so files (like cgo_stdio.so).  Cgo-based packages using the
+standard Makefiles should build without any changes.  Any alternate build
+mechanisms will need to be updated.
+
+The C and Go compilers (6g, 6c, 8g, 8c, 5g, 5c) now align structs according to
+the maximum alignment of the fields they contain; previously they aligned
+structs to word boundaries.  This may break non-cgo-based code that attempts to
+mix C and Go.
+
+NaCl support has been removed. The recent linker changes broke NaCl support
+a month ago, and there are no known users of it.
+If necessary, the NaCl code can be recovered from the repository history.
+
+* 5g/8g, 8l, ld, prof: fix output of 32-bit values (thanks Eoghan Sherry).
+* [68]l and runtime: GDB support for interfaces and goroutines.
+* 6l, 8l: support for linking ELF and Mach-O .o files.
+* all: simplify two-variable ranges with unused second variable (thanks Ryan Hitchman).
+* arm: updated soft float support.
+* codereview: keep quiet when not in use (thanks Eoghan Sherry).
+* compress/flate: implement Flush, equivalent to zlib's Z_SYNC_FLUSH.
+* crypto/tls: use rand.Reader in cert generation example (thanks Anthony Martin).
+* dashboard: fix project tag filter.
+* debug/elf, debug/macho: add ImportedLibraries, ImportedSymbols.
+* doc/go_mem: goroutine exit is not special.
+* event.go: another print glitch from gocheck.
+* gc: bug fixes,
+        syntax error for incomplete chan type (thanks Ryan Hitchman).
+* go/ast: fix ast.Walk.
+* gob: document the byte count used in the encoding of values,
+        fix bug sending zero-length top-level slices and maps,
+        Register should use the original type, not the indirected one.
+* godashboard: support submitting projects with non-ascii names (thanks Ryan Hitchman)
+* godefs: guard against structs with pad fields
+* godoc: added textual search, to enable use -fulltext flag.
+* gofmt: simplify "x, _ = range y" to "x = range y".
+* gopack: allow ELF/Mach-O objects in .a files without clearing allobj.
+* go/token,scanner: fix comments so godoc aligns properly.
+* govet: on error continue to the next file (thanks Christopher Wedgwood).
+* html: improved parsing.
+* http: ServeFile handles Range header for partial requests.
+* json: check for invalid UTF-8.
+* ld: allow .o files with no symbols,
+        reading of ELF object files,
+        reading of Mach-O object files.
+* math: change float64 bias constant from 1022 to 1023 (thanks Eoghan Sherry),
+        rename the MinFloat constant to SmallestNonzeroFloat.
+* nm: silently ignore .o files in .a files.
+* os: fix test of RemoveAll.
+* os/inotify: new package (thanks Balazs Lecz).
+* os: make MkdirAll work with symlinks (thanks Ryan Hitchman).
+* regexp: speed up by about 30%; also simplify code for brackets.
+* runtime/linux/386: set FPU to 64-bit precision.
+* runtime: remove paranoid mapping at 0.
+* suffixarray: add Bytes function.
+* syscall: add network interface constants for linux/386, linux/amd64 (thanks Mikio Hara).
+* syscall/windows: restrict access rights param of OpenProcess(),
+        remove \r and \n from error messages (thanks Alex Brainman).
+* test/bench: fixes to timing.sh (thanks Anthony Martin).
+* time: fix bug in Ticker: shutdown using channel rather than memory.
+* token/position: provide FileSet.File, provide files iterator.
+* xml: disallow invalid Unicode code points (thanks Nigel Kerr).
+
+ +

2010-12-08

+ +
+This release includes some package changes. If you use the crypto/tls or
+go/parser packages your code may require changes.
+
+The crypto/tls package's Dial function now takes an additional *Config
+argument.  Most uses will pass nil to get the same default behavior as before.
+See the documentation for details:
+        http://golang.org/pkg/crypto/tls/#Config
+        http://golang.org/pkg/crypto/tls/#Dial
+
+The go/parser package's ParseFile function now takes a *token.FileSet as its
+first argument. This is a pointer to a data structure used to store
+position information. If you don't care about position information you
+can pass "token.NewFileSet()". See the documentation for details:
+        http://golang.org/pkg/go/parser/#ParseFile
+
+This release also splits the patent grant text out of the LICENSE file into a
+separate PATENTS file and changes it to be more like the WebM grant.
+These clarifications were made at the request of the Fedora project.
+
+Other changes:
+* [68]l: generate debug info for builtin structured types, prettyprinting in gdb.
+* 8l: add dynimport to import table in Windows PE (thanks Wei Guangjing).
+* 8l, runtime: fix Plan 9 386 build (thanks Yuval Pavel Zholkover).
+* all: fix broken calls to Printf etc.
+* bufio: make Reader.Read implement io.Reader semantics (thanks Roger Peppe).
+* build: allow archiver to be specified by HOST_AR (thanks Albert Strasheim).
+* bytes: add Buffer.UnreadRune, Buffer.UnreadByte (thanks Roger Peppe).
+* crypto/tls: fix build of certificate generation example (thanks Christian Himpel).
+* doc/install: describe GOHOSTOS and GOHOSTARCH.
+* errchk: accept multiple source files (thanks Eoghan Sherry).
+* exec.LookPath: return os.PathError instad of os.ENOENT (thanks Michael Hoisie)..
+* flag: fix format error in boolean error report,
+        handle multiple calls to flag.Parse.
+* fmt: add %U format for standard Unicode representation of code point values.
+* gc: fix method offsets of anonymous interfaces (thanks Eoghan Sherry),
+        skip undefined symbols in import . (thanks Eoghan Sherry).
+* go/scanner: remove Tokenize - was only used in tests
+* gobuilder: add buildroot command-line flag (thanks Devon H. O'Dell).
+* html: unescape numeric entities (thanks Ryan Hitchman).
+* http: Add EncodeQuery, helper for constructing query strings.
+* ld: fix dwarf decoding of 64-bit reflect values (thanks Eoghan Sherry).
+* math: improve accuracy of Exp2 (thanks Eoghan Sherry).
+* runtime: add Goroutines (thanks Keith Rarick).
+* sync: small naming fix for armv5 (thanks Dean Prichard).
+* syscall, net: Add Recvmsg and Sendmsg on Linux (thanks Albert Strasheim).
+* time: make After use fewer goroutines and host processes (thanks Roger Peppe).
+
+ +

2010-12-02

+ +
+Several package changes in this release may require you to update your code if
+you use the bytes, template, or utf8 packages. In all cases, any outdated code
+will fail to compile rather than behave erroneously.
+
+The bytes package has changed. Its Add and AddByte functions have been removed,
+as their functionality is provided by the recently-introduced built-in function
+“append”. Any code that uses them will need to be changed:
+s = bytes.Add(s, b)    ->    s = append(s, b...)
+s = bytes.AddByte(b, c)    ->    s = append(s, b)
+s = bytes.Add(nil, c)    ->    append([]byte(nil), c)
+
+The template package has changed. Your code will need to be updated if it calls
+the HTMLFormatter or StringFormatter functions, or implements its own formatter
+functions. The function signature for formatter types has changed to:
+        func(wr io.Writer, formatter string, data ...interface{})
+to allow multiple arguments to the formatter.  No templates will need updating.
+See the change for examples:
+        http://code.google.com/p/go/source/detail?r=2c2be793120e
+
+The template change permits the implementation of multi-word variable
+instantiation for formatters. Before one could say
+        {field}
+or
+        {field|formatter}
+Now one can also say
+        {field1 field2 field3}
+or
+        {field1 field2 field3|formatter}
+and the fields are passed as successive arguments to the formatter,
+by analogy to fmt.Print.
+
+The utf8 package has changed. The order of EncodeRune’s arguments has been
+reversed to satisfy the convention of “destination first”.
+Any code that uses EncodeRune will need to be updated.
+
+Other changes:
+* [68]l: correct dwarf location for globals and ranges for arrays.
+* big: fix (*Rat) SetFrac64(a, b) when b < 0 (thanks Eoghan Sherry).
+* compress/flate: fix typo in comment (thanks Mathieu Lonjaret).
+* crypto/elliptic: use a Jacobian transform for better performance.
+* doc/code.html: fix reference to "gomake build" (thanks Anschel Schaffer-Cohen).
+* doc/roadmap: update gdb status.
+* doc/spec: fixed some omissions and type errors.
+* doc: some typo fixes (thanks Peter Mundy).
+* exp/eval: build fix for parser.ParseFile API change (thanks Anschel Schaffer-Cohen).
+* fmt: Scan accepts Inf and NaN,
+        allow "% X" as well as "% x".
+* go/printer: preserve newlines in func parameter lists (thanks Jamie Gennis).
+* http: consume request body before next request.
+* log: ensure writes are atomic (thanks Roger Peppe).
+* path: Windows support for Split (thanks Benny Siegert).
+* runtime: fix SysFree to really free memory on Windows (thanks Alex Brainman),
+        parallel definitions in Go for all C structs.
+* sort: avoid overflow in pivot calculation,
+        reduced stack depth to lg(n) in quickSort (thanks Stefan Nilsson).
+* strconv: Atof on Infs and NaNs.
+
+ +

2010-11-23

+ +
+This release includes a backwards-incompatible package change to the
+sort.Search function (introduced in the last release).
+See the change for details and examples of how you might change your code:
+        http://code.google.com/p/go/source/detail?r=102866c369
+
+* build: automatically #define _64BIT in 6c.
+* cgo: print required space after parameter name in wrapper function.
+* crypto/cipher: new package to replace crypto/block (thanks Adam Langley).
+* crypto/elliptic: new package, implements elliptic curves over prime fields (thanks Adam Langley).
+* crypto/x509: policy OID support and fixes (thanks Adam Langley).
+* doc: add link to codewalks,
+        fix recover() documentation (thanks Anschel Schaffer-Cohen),
+        explain how to write Makefiles for commands.
+* exec: enable more tests on windows (thanks Alex Brainman).
+* gc: adjustable hash code in typecheck of composite literals
+        (thanks to vskrap, Andrey Mirtchovski, and Eoghan Sherry).
+* gc: better error message for bad type in channel send (thanks Anthony Martin).
+* godoc: bug fix in relativePath,
+        compute search index for all file systems under godoc's observation,
+        use correct time stamp to indicate accuracy of search result.
+* index/suffixarray: use sort.Search.
+* net: add ReadFrom and WriteTo windows version (thanks Wei Guangjing).
+* reflect: remove unnecessary casts in Get methods.
+* rpc: add RegisterName to allow override of default type name.
+* runtime: free memory allocated by windows CommandLineToArgv (thanks Alex Brainman).
+* sort: simplify Search (thanks Roger Peppe).
+* strings: add LastIndexAny (thanks Benny Siegert).
+
+ +

2010-11-10

+ +
+The birthday release includes a new Search capability inside the sort package.
+It takes an unusual but very general and easy-to-use approach to searching
+arbitrary indexable sorted data.  See the documentation for details:
+    http://golang.org/pkg/sort/#Search
+
+The ARM port now uses the hardware floating point unit (VFP).  It still has a
+few bugs, mostly around conversions between unsigned integer and floating-point
+values, but it's stabilizing.
+
+In addition, there have been many smaller fixes and updates: 
+
+* 6l: generate dwarf variable names with disambiguating suffix.
+* container/list: make Remove return Value of removed element.
+    makes it easier to remove first or last item.
+* crypto: add cast5 (default PGP cipher),
+    switch block cipher methods to be destination first.
+* crypto/tls: use pool building for certificate checking
+* go/ast: change embedded token.Position fields to named fields
+    (preparation for a different position representation)
+* net: provide public access to file descriptors (thanks Keith Rarick)
+* os: add Expand function to evaluate environment variables.
+* path: add Glob (thanks Benny Siegert)
+* runtime: memequal optimization (thanks Graham Miller)
+    prefix all external symbols with "runtime·" to avoid
+    conflicts linking with external C libraries.
+
+ +

2010-11-02

+ +
+This release includes a language change: the new built-in function, append.
+Append makes growing slices much simpler. See the spec for details:
+        http://golang.org/doc/go_spec.html#Appending_and_copying_slices
+
+Other changes:
+* 8l: pe generation fixes (thanks Alex Brainman).
+* doc: Effective Go: append and a few words about "..." args.
+* build: fiddle with make variables.
+* codereview: fix sync and download in Python 2.7 (thanks Fazlul Shahriar).
+* debug/pe, cgo: add windows support (thanks Wei Guangjing ).
+* go/ast: add Inspect function for easy AST inspection w/o a visitor.
+* go/printer: do not remove parens around composite literals starting with
+        a type name in control clauses.
+* go/scanner: bug fixes, revisions, and more tests.
+* gob: several fixes and documentation updates.
+* godoc: bug fix (bug introduced with revision 3ee58453e961).
+* gotest: print empty benchmark list in a way that gofmt will leave alone.
+* http server: correctly respond with 304 NotModified (thanks Michael Hoisie).
+* kate: update list of builtins (thanks Evan Shaw).
+* libutf: update to Unicode 5.2.0 to match pkg/unicode (thanks Anthony Martin).
+* misc/bbedit: update list of builtins (thanks Anthony Starks).
+* misc/vim: update list of builtins.
+* mkrunetype: install a Makefile and tweak it slightly so it can be built.
+* netchan: fix locking bug.
+* pidigits: minor improvements (thanks Evan Shaw).
+* rpc: fix client deadlock bug.
+* src: use append where appropriate (often instead of vector).
+* strings: add Contains helper function (thanks Brad Fitzpatrick).
+* syscall: SIO constants for Linux (thanks Albert Strasheim),
+        Stat(path) on windows (thanks Alex Brainman).
+* test/ken/convert.go: add conversion torture test.
+* testing: add Benchmark (thanks Roger Peppe).
+
+ +

2010-10-27

+ +
+*** This release changes the encoding used by package gob. 
+    If you store gobs on disk, see below. ***
+
+The ARM port (5g) now passes all tests. The optimizer is not yet enabled, and
+floating point arithmetic is performed entirely in software. Work is underway
+to address both of these deficiencies.
+
+The syntax for arrays, slices, and maps of composite literals has been
+simplified. Within a composite literal of array, slice, or map type, elements
+that are themselves composite literals may elide the type if it is identical to
+the outer literal’s element type. For example, these expressions:
+	[][]int{[]int{1, 2, 3}, []int{4, 5}}
+	map[string]Point{“x”: Point{1.5, -3.5}, “y”: Point{0, 0}}
+can be simplified to:
+	[][]int{{1, 2, 3}, {4, 5}}
+	map[string]Point{“x”: {1.5, -3.5}, “y”: {0, 0}}
+Gofmt can make these simplifications mechanically when invoked with the 
+new -s flag.
+
+The built-in copy function can now copy bytes from a string value to a []byte.
+Code like this (for []byte b and string s): 
+	for i := 0; i < len(s); i++ {
+		b[i] = s[i]
+	}
+can be rewritten as:
+	copy(b, s)
+
+The gob package can now encode and decode interface values containing types
+registered ahead of time with the new Register function. These changes required
+a backwards-incompatible change to the wire format.  Data written with the old
+version of the package will not be readable with the new one, and vice versa.
+(Steps were made in this change to make sure this doesn’t happen again.) 
+We don’t know of anyone using gobs to create permanent data, but if you do this
+and need help converting, please let us know, and do not update to this release
+yet.  We will help you convert your data.
+
+Other changes:
+* 5g, 6g, 8g: generate code for string index instead of calling function.
+* 5l, 6l, 8l: introduce sub-symbols.
+* 6l/8l: global and local variables and type info.
+* Make.inc: delete unnecessary -fno-inline flag to quietgcc.
+* arm: precise float64 software floating point, bug fixes.
+* big: arm assembly, faster software mulWW, divWW.
+* build: only print "You need to add foo to PATH" when needed.
+* container/list: fix Remove bug and use pointer to self as identifier.
+* doc: show page title in browser title bar,
+        update roadmap.
+* encoding/binary: give LittleEndian, BigEndian specific types.
+* go/parser: consume auto-inserted semi when calling ParseExpr().
+* gobuilder: pass GOHOSTOS and GOHOSTARCH to build,
+        write build and benchmarking logs to disk.
+* goinstall: display helpful message when encountering a cgo package,
+        fix test for multiple package names (thanks Fazlul Shahriar).
+* gotest: generate correct gofmt-formatted _testmain.go.
+* image/png: speed up paletted encoding ~25% (thanks Brad Fitzpatrick).
+* misc: update python scripts to specify python2 as python3 is now "python".
+* net: fix comment on Dial to mention unix/unixgram.
+* rpc: expose Server type to allow multiple RPC Server instances.
+* runtime: print unknown types in panic.
+* spec: append built-in (not yet implemented).
+* src: gofmt -s -w src misc.
+        update code to use copy-from-string.
+* test/bench: update numbers.
+* websocket: fix short Read.
+
+ +

2010-10-20

+ +
+This release removes the log package's deprecated functions.
+Code that has not been updated to use the new interface will break.
+See the previous release notes for details:
+	http://golang.org/doc/devel/release.html#2010-10-13
+
+Also included are major improvements to the linker. It is now faster, 
+uses less memory, and more parallelizable (but not yet parallel).
+
+The nntp package has been removed from the standard library.
+Its new home is the nntp-go project at Google Code:
+	http://code.google.com/p/nntp-go
+You can install it with goinstall:
+	goinstall nntp-go.googlecode.com/hg/nntp
+And import it in your code like so:
+	import "nntp-go.googlecode.com/hg/nntp"
+
+Other changes:
+* 6g: avoid too-large immediate constants.
+* 8l, runtime: initial support for Plan 9 (thanks Yuval Pavel Zholkover).
+* 6l, 8l: more improvements on exporting debug information (DWARF).
+* arm: code gen fixes. Most tests now pass, except for floating point code.
+* big: add random number generation (thanks Florian Uekermann).
+* gc: keep track of real actual type of identifiers,
+	report that shift must be unsigned integer,
+	select receive with implicit conversion.
+* goplay: fix to run under windows (thanks Yasuhiro Matsumoto).
+* http: do not close connection after sending HTTP/1.0 request.
+* netchan: add new method Hangup to terminate transmission on a channel.
+* os: change TestForkExec so it can run on windows (thanks Yasuhiro Matsumoto).
+* runtime: don't let select split stack.
+* syscall/arm: correct 64-bit system call arguments.
+
+ +

2010-10-13

+ +
+This release includes changes to the log package, the removal of exp/iterable,
+two new tools (gotry and goplay), one small language change, and many other
+changes and fixes.  If you use the log or iterable packages, you need to make
+changes to your code.
+
+The log package has changed.  Loggers now have only one output, and output to
+standard error by default.  The names have also changed, although the old names
+are still supported.  They will be deleted in the next release, though, so it
+would be good to update now if you can.  For most purposes all you need to do
+is make these substitutions:
+        log.Stderr -> log.Println or log.Print
+        log.Stderrf -> log.Printf
+        log.Crash -> log.Panicln or log.Panic
+        log.Crashf -> log.Panicf
+        log.Exit -> log.Exitln or log.Exit
+        log.Exitf -> log.Exitf (no change)
+Calls to log.New() must drop the second argument.
+Also, custom loggers with exit or panic properties will need to be reworked.
+For full details, see the change description:
+        http://code.google.com/p/go/source/detail?r=d8a3c7563d
+
+The language change is that uses of pointers to interface values no longer
+automatically dereference the pointer.  A pointer to an interface value is more
+often a beginner’s bug than correct code.
+
+The package exp/iterable has been removed. It was an interesting experiment,
+but it encourages writing inefficient code and has outlived its utility.
+
+The new tools:
+* gotry: an exercise in reflection and an unusual tool. Run 'gotry' for details.
+* goplay: a stand-alone version of the Go Playground. See misc/goplay.
+
+Other changes:
+* 6l: Mach-O fixes, and fix to work with OS X nm/otool (thanks Jim McGrath).
+* [568]a: correct line numbers for statements.
+* arm: code generation and runtime fixes,
+	adjust recover for new reflect.call,
+	enable 6 more tests after net fix.
+* big: fix panic and round correctly in Rat.FloatString (thanks Anthony Martin).
+* build: Make.cmd: remove $(OFILES) (thanks Eric Clark),
+        Make.pkg: remove .so before installing new one,
+        add GOHOSTOS and GOHOSTARCH environment variables.
+* crypto/tls: better error messages for certificate issues,
+        make SetReadTimeout work.
+* doc: add Sydney University video,
+	add The Expressiveness of Go talk.
+* exp/draw/x11: support X11 vendors other than "The X.Org Foundation".
+* expvar: add (*Int).Set (thanks Sam Thorogood).
+* fmt: add Errorf helper function,
+        allow %d on []byte.
+* gc: O(1) string comparison when lengths differ,
+        various bug fixes.
+* http: return the correct error if a header line is too long.
+* image: add image.Tiled type, the Go equivalent of Plan 9's repl bit.
+* ld: be less picky about bad line number info.
+* misc/cgo/life: fix for new slice rules (thanks Graham Miller).
+* net: allow _ in DNS names.
+* netchan: export before import when testing, and
+        zero out request to ensure correct gob decoding. (thanks Roger Peppe).
+* os: make tests work on windows (thanks Alex Brainman).
+* runtime: bug fix: serialize mcache allocation,
+        correct iteration of large map values,
+        faster strequal, memequal (thanks Graham Miller),
+        fix argument dump in traceback,
+        fix tiny build.
+* smtp: new package (thanks Evan Shaw).
+* syscall: add sockaddr_ll support for linux/386, linux/amd64 (thanks Mikio Hara),
+        add ucred structure for SCM_CREDENTIALS over UNIX sockets. (thanks Albert Strasheim).
+* syscall: implement WaitStatus and Wait4() for windows (thanks Wei Guangjing).
+* time: add After.
+* websocket: enable tests on windows (thanks Alex Brainman).
+
+ +

2010-09-29

+ +
+This release includes some minor language changes and some significant package
+changes. You may need to change your code if you use ...T parameters or the
+http package.
+
+The semantics and syntax of forwarding ...T parameters have changed.
+        func message(f string, s ...interface{}) { fmt.Printf(f, s) }
+Here, s has type []interface{} and contains the parameters passed to message.
+Before this language change, the compiler recognized when a function call
+passed a ... parameter to another ... parameter of the same type, and just
+passed it as though it was a list of arguments.  But this meant that you
+couldn't control whether to pass the slice as a single argument and you
+couldn't pass a regular slice as a ... parameter, which can be handy.  This
+change gives you that control at the cost of a few characters in the call.
+If you want the promotion to ...,  append ... to the argument:
+        func message(f string, s ...interface{}) { fmt.Printf(f, s...) }
+Without the ..., s would be passed to Printf as a single argument of type
+[]interface{}.  The bad news is you might need to fix up some of your code, 
+but the compiler will detect the situation and warn you.
+
+Also, the http.Handler and http.HandlerFunc types have changed. Where http
+handler functions previously accepted an *http.Conn, they now take an interface
+type http.ResponseWriter. ResponseWriter implements the same methods as *Conn,
+so in most cases the only change required will be changing the type signature
+of your handler function's first parameter. See:
+  http://golang.org/pkg/http/#Handler
+
+The utf8 package has a new type, String, that provides efficient indexing 
+into utf8 strings by rune (previously an expensive conversion to []int 
+was required). See:
+  http://golang.org/pkg/utf8/#String
+
+The compiler will now automatically insert a semicolon at the end of a file if
+one is not found. This effect of this is that Go source files are no longer
+required to have a trailing newline.
+
+Other changes:
+* 6prof: more accurate usage message.
+* archive/zip: new package for reading Zip files.
+* arm: fix code generation, 10 more package tests pass.
+* asn1: make interface consistent with json.
+* bufio.UnreadRune: fix bug at EOF.
+* build: clear custom variables like GREP_OPTIONS,
+        silence warnings generated by ubuntu gcc,
+        use full path when compiling libraries.
+* bytes, strings: change lastIndexFunc to use DecodeLastRune (thanks Roger Peppe).
+* doc: add to and consolidate non-english doc references,
+        consolidate FAQs into a single file, go_faq.html,
+        updates for new http interface.
+* fmt/Printf: document and tweak error messages produced for bad formats.
+* gc: allow select case expr = <-c,
+        eliminate duplicates in method table,
+        fix reflect table method receiver,
+        improve error message for x \= 0.
+* go/scanner: treat EOF like a newline for purposes of semicolon insertion.
+* gofmt: stability improvements.
+* gotest: leave _testmain.go for "make clean" to clean up.
+* http: correct escaping of different parts of URL,
+        support HTTP/1.0 Keep-Alive.
+* json: do not write to unexported fields.
+* libcgo: don't build for NaCl,
+        set g, m in thread local storage for windows 386 (thanks Wei Guangjing).
+* math: Fix off-by-one error in Ilogb and Logb.  (thanks Charles L. Dorian).
+* misc/dashboard/builder: remove build files after benchmarking.
+* nacl: update instructions for new SDK.
+* net: enable v4-over-v6 on ip sockets,
+        fix crash in DialIP.
+* os: check for valid arguments in windows Readdir (thanks Peter Mundy).
+* runtime: add mmap of null page just in case,
+        correct stats in SysFree,
+        fix unwindstack crash.
+* syscall: add IPPROTO_IPV6 and IPV6_V6ONLY const to fix nacl and windows build,
+        add inotify on Linux (thanks Balazs Lecz),
+        fix socketpair in syscall_bsd,
+        fix windows value of IPV6_V6ONLY (thanks Alex Brainman),
+        implement windows version of Utimes (thanks Alex Brainman),
+        make mkall.sh work for nacl.
+* test: Add test that causes incorrect error from gccgo.
+* utf8: add DecodeLastRune and DecodeLastRuneInString (thanks Roger Peppe).
+* xml: Allow entities inside CDATA tags (thanks Dan Sinclair).
+
+ +

2010-09-22

+ +
+This release includes new package functionality, and many bug fixes and changes.
+It also improves support for the arm and nacl platforms.
+
+* 5l: avoid fixed buffers in list.
+* 6l, 8l: clean up ELF code, fix NaCl.
+* 6l/8l: emit DWARF frame info.
+* Make.inc: make GOOS detection work on windows (thanks Alex Brainman).
+* build: fixes for native arn build,
+        make all.bash run on Ubuntu ARM.
+* cgo: bug fixes,
+        show preamble gcc errors (thanks Eric Clark).
+* crypto/x509, crypto/tls: improve root matching and observe CA flag.
+* crypto: Fix certificate validation.
+* doc: variable-width layout.
+* env.bash: fix building in directory with spaces in the path (thanks Alex Brainman).
+* exp/4s, exp/nacl/av: sync to recent exp/draw changes.
+* exp/draw/x11: mouse location is a signed integer.
+* exp/nacl/av: update color to max out at 1<<16-1 instead of 1<<32-1.
+* fmt: support '*' for width or precision (thanks Anthony Martin).
+* gc: improvements to static initialization,
+        make sure path names are canonical.
+* gob: make robust when decoding a struct with non-struct data.
+* gobuilder: add -cmd for user-specified build command,
+        add -rev= flag to build specific revision and exit,
+        fix bug that caused old revisions to be rebuilt.
+* godoc: change default filter file name to "",
+        don't use quadratic algorithm to filter paths,
+        show "Last update" info for directory listings.
+* http: new redirect test,
+        URLEscape now escapes all reserved characters as per the RFC.
+* nacl: fix zero-length writes.
+* net/dict: parse response correctly (thanks Fazlul Shahriar).
+* netchan: add a cross-connect test,
+        handle closing of channels,
+        provide a method (Importer.Errors()) to recover protocol errors.
+* os: make Open() O_APPEND flag work on windows (thanks Alex Brainman),
+        make RemoveAll() work on windows (thanks Alex Brainman).
+* pkg/Makefile: disable netchan test to fix windows build (thanks Alex Brainman).
+* regexp: delete Iter methods.
+* runtime: better panic for send to nil channel.
+* strings: fix minor bug in LastIndexFunc (thanks Roger Peppe).
+* suffixarray: a package for creating suffixarray-based indexes.
+* syscall: Use vsyscall for syscall.Gettimeofday and .Time on linux amd64.
+* test: fix NaCl build.
+* windows: fix netchan test by using 127.0.0.1.
+
+ +

2010-09-15

+ +
+This release includes a language change: the lower bound of a subslice may
+now be omitted, in which case the value will default to 0.
+For example, s[0:10] may now be written as s[:10], and s[0:] as s[:].
+
+The release also includes important bug fixes for the ARM architecture,
+as well as the following fixes and changes:
+
+* 5g: register allocation bugs
+* 6c, 8c: show line numbers in -S output
+* 6g, 6l, 8g, 8l: move read-only data to text segment
+* 6l, 8l: make etext accurate; introduce rodata, erodata.
+* arm: fix build bugs.
+        make libcgo build during OS X cross-compile
+        remove reference to deleted file syntax/slice.go
+        use the correct stat syscalls
+        work around reg allocator bug in 5g
+* bufio: add UnreadRune.
+* build: avoid bad environment interactions
+        fix build for tiny
+        generate, clean .exe files on Windows (thanks Joe Poirier)
+        test for _WIN32, not _MINGW32 (thanks Joe Poirier)
+        work with GNU Make 3.82 (thanks Jukka-Pekka Kekkonen)
+* cgo: add typedef for uintptr in generated headers
+        silence warning for C call returning const pointer
+* codereview: convert email address to lower case before checking CONTRIBUTORS
+* crypto/tls: don't return an error from Close()
+* doc/tutorial: update for slice changes.
+* exec: separate LookPath implementations for unix/windows (thanks Joe Poirier)
+* exp/draw/x11: allow clean shutdown when the user closes the window.
+* exp/draw: clip destination rectangle to the image bounds.
+        fast path for drawing overlapping image.RGBAs.
+        fix double-counting of pt.Min for the src and mask points.
+        reintroduce the MouseEvent.Nsec timestamp.
+        rename Context to Window, and add a Close method.
+* exp/debug: preliminary support for 'copy' function (thanks Sebastien Binet)
+* fmt.Fscan: use UnreadRune to preserve data across calls.
+* gc: better printing of named constants, func literals in errors
+        many bug fixes
+        fix line number printing with //line directives
+        fix symbol table generation on windows (thanks Alex Brainman)
+        implement comparison rule from spec change 33abb649cb63
+        implement new slice spec (thanks Scott Lawrence)
+        make string x + y + z + ... + w efficient
+        more accurate line numbers for ATEXT
+        remove &[10]int -> []int conversion
+* go-mode.el: fix highlighting for 'chan' type (thanks Scott Lawrence)
+* godoc: better support for directory trees for user-supplied paths
+        use correct delay time (bug fix)
+* gofmt, go/printer: update internal estimated position correctly
+* goinstall: warn when package name starts with http:// (thanks Scott Lawrence)
+* http: check https certificate against host name
+        do not cache CanonicalHeaderKey (thanks Jukka-Pekka Kekkonen)
+* image: change a ColorImage's minimum point from (0, 0) to (-1e9, -1e9).
+        introduce Intersect and Union rectangle methods.
+* ld: handle quoted spaces in package path (thanks Dan Sinclair)
+* libcgo: fix NaCl build.
+* libmach: fix build on arm host
+        fix new thread race with Linux
+* math: make portable Tan(Pi/2) return NaN
+* misc/dashboard/builder: gobuilder, a continuous build client
+* net: disable tests for functions not available on windows (thanks Alex Brainman)
+* netchan: make -1 unlimited, as advertised.
+* os, exec: rename argv0 to name
+* path: add IsAbs (thanks Ivan Krasin)
+* runtime: fix bug in tracebacks
+        fix crash trace on amd64
+        fix windows build (thanks Alex Brainman)
+        use manual stack for garbage collection
+* spec: add examples for slices with omitted index expressions.
+        allow omission of low slice bound (thanks Scott Lawrence)
+* syscall: fix windows Gettimeofday (thanks Alex Brainman)
+* test(arm): disable zerodivide.go because compilation fails.
+* test(windows): disable tests that cause the build to fail (thanks Joe Poirier)
+* test/garbage/parser: sync with recent parser changes
+* test: Add test for //line
+        Make gccgo believe that the variables can change.
+        Recognize gccgo error messages.
+        Reduce race conditions in chan/nonblock.go.
+        Run garbage collector before testing malloc numbers.
+* websocket: Add support for secure WebSockets (thanks Jukka-Pekka Kekkonen)
+* windows: disable unimplemented tests (thanks Joe Poirier)
+
+ +

2010-09-06

+ +
+This release includes the syntactic modernization of more than 100 files in /test,
+and these additions, changes, and fixes: 
+* 6l/8l: emit DWARF in macho.
+* 8g: use FCHS, not FMUL, for minus float.
+* 8l: emit DWARF in ELF,
+        suppress emitting DWARF in Windows PE (thanks Alex Brainman).
+* big: added RatString, some simplifications.
+* build: create bin and pkg directories as needed; drop from hg,
+        delete Make.386 Make.amd64 Make.arm (obsoleted by Make.inc),
+        fix cgo with -j2,
+        let pkg/Makefile coordinate building of Go commands,
+        never use quietgcc in Make.pkg,
+        remove more references to GOBIN and GOROOT (thanks Christian Himpel).
+* codereview: Fix uploading for Mercurial 1.6.3 (thanks Evan Shaw),
+        consistent indent, cut dead code,
+        fix hang on standard hg commands,
+        print status when tasks take longer than 30 seconds,
+        really disable codereview when not available,
+        upload files in parallel (5x improvement on large CLs).
+* crypto/hmac: make Sum idempotent (thanks Jukka-Pekka Kekkonen).
+* doc: add links to more German docs,
+        add round-robin flag to io2010 balance example,
+        fix a bug in the example in Constants subsection (thanks James Fysh),
+        various changes for validating HTML (thanks Scott Lawrence).
+* fmt: delete erroneous sentence about return value for Sprint*.
+* gc: appease bison version running on FreeBSD builder,
+        fix spurious syntax error.
+* go/doc: use correct escaper for URL.
+* go/printer: align ImportPaths in ImportDecls (thanks Scott Lawrence).
+* go/typechecker: 2nd step towards augmenting AST with full type information.
+* gofmt: permit omission of first index in slice expression.
+* goinstall: added -a flag to mean "all remote packages" (thanks Scott Lawrence),
+        assume go binaries are in path (following new convention),
+        use https for Google Code checkouts.
+* gotest: allow make test of cgo packages (without make install).
+* http: add Date to server, Last-Modified and If-Modified-Since to file server,
+        add PostForm function to post url-encoded key/value data,
+        obscure passwords in return value of URL.String (thanks Scott Lawrence).
+* image: introduce Config type and DecodeConfig function.
+* libcgo: update Makefile to use Make.inc.
+* list: update comment to state that the zero value is ready to use.
+* math: amd64 version of Sincos (thanks Charles L. Dorian).
+* misc/bash: add *.go completion for gofmt (thanks Scott Lawrence).
+* misc/emacs: make _ a word symbol (thanks Scott Lawrence).
+* misc: add zsh completion (using compctl),
+        syntax highlighting for Fraise.app (OS X) (thanks Vincent Ambo).
+* net/textproto: Handle multi-line responses (thanks Evan Shaw).
+* net: add LookupMX (thanks Corey Thomasson).
+* netchan: Fix race condition in test,
+        rather than 0, make -1 mean infinite (a la strings.Split et al),
+        use acknowledgements on export send.
+        new methods Sync and Drain for clean teardown.
+* regexp: interpret all Go characer escapes \a \b \f \n \r \t \v.
+* rpc: fix bug that caused private methods to attempt to be registered.
+* runtime: Correct commonType.kind values to match compiler,
+        add GOOS, GOARCH; fix FuncLine,
+        special case copy, equal for one-word interface values (thanks Kyle Consalus).
+* scanner: fix incorrect reporting of error in Next (thanks Kyle Consalus).
+* spec: clarify that arrays must be addressable to be sliceable.
+* template: fix space handling around actions.
+* test/solitaire: an exercise in backtracking and string conversions.
+* test: Recognize gccgo error messages and other fixes.
+* time: do not crash in String on nil Time.
+* tutorial: regenerate HTML to pick up change to progs/file.go.
+* websocket: fix missing Sec-WebSocket-Protocol on server response (thanks Jukka-Pekka Kekkonen).
+
+ +

2010-08-25

+ +
+This release includes changes to the build system that will likely require you
+to make changes to your environment variables and Makefiles.
+
+All environment variables are now optional:
+ - $GOOS and $GOARCH are now optional; their values should now be inferred 
+   automatically by the build system,
+ - $GOROOT is now optional, but if you choose not to set it you must run
+   'gomake' instead of 'make' or 'gmake' when developing Go programs
+   using the conventional Makefiles,
+ - $GOBIN remains optional and now defaults to $GOROOT/bin;
+   if you wish to use this new default, make sure it is in your $PATH
+   and that you have removed the existing binaries from $HOME/bin.
+
+As a result of these changes, the Go Makefiles have changed. If your Makefiles
+inherit from the Go Makefiles, you must change this line:
+    include ../../Make.$(GOARCH)
+to this:
+    include ../../Make.inc
+
+This release also removes the deprecated functions in regexp and the 
+once package. Any code that still uses them will break.
+See the notes from the last release for details:
+    http://golang.org/doc/devel/release.html#2010-08-11
+
+Other changes:
+* 6g: better registerization for slices, strings, interface values
+* 6l: line number information in DWARF format
+* build: $GOBIN defaults to $GOROOT/bin,
+        no required environment variables
+* cgo: add C.GoStringN (thanks Eric Clark).
+* codereview: fix issues with leading tabs in CL descriptions,
+        do not send "Abandoned" mail if the CL has not been mailed.
+* crypto/ocsp: add missing Makefile.
+* crypto/tls: client certificate support (thanks Mikkel Krautz).
+* doc: update gccgo information for recent changes.
+        fix errors in Effective Go.
+* fmt/print: give %p priority, analogous to %T,
+        honor Formatter in Print, Println.
+* gc: fix parenthesization check.
+* go/ast: facility for printing AST nodes,
+        first step towards augmenting AST with full type information.
+* go/printer: do not modify tabwriter.Escape'd text.
+* gofmt: do not modify multi-line string literals,
+        print AST nodes by setting -ast flag.
+* http: fix typo in http.Request documentation (thanks Scott Lawrence)
+        parse query string always, not just in GET
+* image/png: support 16-bit color.
+* io: ReadAtLeast now errors if min > len(buf).
+* jsonrpc: use `error: null` for success, not `error: ""`.
+* libmach: implement register fetch for 32-bit x86 kernel.
+* net: make IPv6 String method standards-compliant (thanks Mikio Hara).
+* os: FileInfo.Permission() now returns uint32 (thanks Scott Lawrence),
+        implement env using native Windows API (thanks Alex Brainman).
+* reflect: allow PtrValue.PointTo(nil).
+* runtime: correct line numbers for .goc files,
+        fix another stack split bug,
+        fix freebsd/386 mmap.
+* syscall: regenerate syscall/z* files for linux/386, linux/amd64, linux/arm.
+* tabwriter: Introduce a new flag StripEscape.
+* template: fix handling of space around actions,
+        vars preceded by white space parse correctly (thanks Roger Peppe).
+* test: add test case that crashes gccgo.
+* time: parse no longer requires minutes for time zone (thanks Jan H. Hosang)
+* yacc: fix bounds check in error recovery.
+
+ +

2010-08-11

+ +
+This release introduces some package changes. You may need to change your
+code if you use the once, regexp, image, or exp/draw packages.
+
+The type Once has been added to the sync package. The new sync.Once will
+supersede the functionality provided by the once package. We intend to remove
+the once package after this release. See:
+    http://golang.org/pkg/sync/#Once
+All instances of once in the standard library have been replaced with
+sync.Once. Reviewing these changes may help you modify your existing code. 
+The relevant changeset:
+    http://code.google.com/p/go/source/detail?r=fa2c43595119
+
+A new set of methods has been added to the regular expression package, regexp.
+These provide a uniformly named approach to discovering the matches of an
+expression within a piece of text; see the package documentation for details: 
+    http://golang.org/pkg/regexp/
+These new methods will, in a later release, replace the old methods for
+matching substrings.  The following methods are deprecated:
+    Execute (use FindSubmatchIndex)
+    ExecuteString (use FindStringSubmatchIndex)
+    MatchStrings(use FindStringSubmatch)
+    MatchSlices (use FindSubmatch)
+    AllMatches (use FindAll; note that n<0 means 'all matches'; was n<=0)
+    AllMatchesString (use FindAllString; note that n<0 means 'all matches'; was n<=0)
+(Plus there are ten new methods you didn't know you wanted.) 
+Please update your code to use the new routines before the next release.
+
+An image.Image now has a Bounds rectangle, where previously it ranged 
+from (0, 0) to (Width, Height). Loops that previously looked like:
+    for y := 0; y < img.Height(); y++ {
+        for x := 0; x < img.Width(); x++ {
+            // Do something with img.At(x, y)
+        }
+    }
+should instead be:
+    b := img.Bounds()
+    for y := b.Min.Y; y < b.Max.Y; y++ {
+        for x := b.Min.X; x < b.Max.X; x++ {
+            // Do something with img.At(x, y)
+        }
+    }
+The Point and Rectangle types have also moved from exp/draw to image.
+
+Other changes:
+* arm: bugfixes and syscall (thanks Kai Backman).
+* asn1: fix incorrect encoding of signed integers (thanks Nicholas Waples).
+* big: fixes to bitwise functions (thanks Evan Shaw).
+* bytes: add IndexRune, FieldsFunc and To*Special (thanks Christian Himpel).
+* encoding/binary: add complex (thanks Roger Peppe).
+* exp/iterable: add UintArray (thanks Anschel Schaffer-Cohen).
+* godoc: report Status 404 if a pkg or file is not found.
+* gofmt: better reporting for unexpected semicolon errors.
+* html: new package, an HTML tokenizer.
+* image: change image representation from slice-of-slices to linear buffer,
+        introduce Decode and RegisterFormat,
+        introduce Transparent and Opaque,
+        replace Width and Height by Bounds, add the Point and Rect types.
+* libbio: fix Bprint to address 6g issues with large data structures.
+* math: fix amd64 Hypot (thanks Charles L. Dorian).
+* net/textproto: new package, with example net/dict.
+* os: fix ForkExec() handling of envv == nil (thanks Alex Brainman).
+* png: grayscale support (thanks Mathieu Lonjaret).
+* regexp: document that backslashes are the escape character.
+* rpc: catch errors from ReadResponseBody.
+* runtime: memory free fix (thanks Alex Brainman).
+* template: add ParseFile method to template.Template.
+* test/peano: use directly recursive type def.
+
+ +

2010-08-04

+ +
+This release includes a change to os.Open (and co.). The file permission
+argument has been changed to a uint32. Your code may require changes - a simple
+conversion operation at most.
+
+Other changes:
+* amd64: use segment memory for thread-local storage.
+* arm: add gdb support to android launcher script,
+        bugfixes (stack clobbering, indices),
+        disable another flaky test,
+        remove old qemu dependency from gotest.
+* bufio: introduce Peek.
+* bytes: added test case for explode with blank string (thanks Scott Lawrence).
+* cgo: correct multiple return value function invocations (thanks Christian Himpel).
+* crypto/x509: unwrap Subject Key Identifier (thanks Adam Langley).
+* gc: index bounds tests and other fixes.
+* gofmt/go/parser: strengthen syntax checks.
+* goinstall: check for error from exec.*Cmd.Wait() (thanks Alex Brainman).
+* image/png: use image-specific methods for checking opacity.
+* image: introduce Gray and Gray16 types,
+        remove the named colors except for Black and White.
+* json: object members must have a value (thanks Anthony Martin).
+* misc/vim: highlight misspelled words only in comments (thanks Christian Himpel).
+* os: Null device (thanks Peter Mundy).
+* runtime: do not fall through in SIGBUS/SIGSEGV.
+* strings: fix Split("", "", -1) (thanks Scott Lawrence).
+* syscall: make go errors not clash with windows errors (thanks Alex Brainman).
+* test/run: diff old new,
+* websocket: correct challenge response (thanks Tarmigan Casebolt),
+        fix bug involving spaces in header keys (thanks Bill Neubauer). 
+
+ +

2010-07-29

+ +
+* 5g: more soft float support and several bugfixes.
+* asn1: Enumerated, Flag and GeneralizedTime support.
+* build: clean.bash to check that GOOS and GOARCH are set.
+* bytes: add IndexFunc and LastIndexFunc (thanks Fazlul Shahriar),
+	add Title.
+* cgo: If CC is set in environment, use it rather than "gcc",
+	use new command line syntax: -- separates cgo flags from gcc flags.
+* codereview: avoid crash if no config,
+	don't run gofmt with an empty file list,
+	make 'hg submit' work with Mercurial 1.6.
+* crypto/ocsp: add package to parse OCSP responses.
+* crypto/tls: add client-side SNI support and PeerCertificates.
+* exp/bignum: delete package - functionality subsumed by package big.
+* fmt.Print: fix bug in placement of spaces introduced when ...T went in.
+* fmt.Scanf: handle trailing spaces.
+* gc: fix smaller-than-pointer-sized receivers in interfaces,
+	floating point precision/normalization fixes,
+	graceful exit on seg fault,
+	import dot shadowing bug,
+	many fixes including better handling of invalid input,
+	print error detail about failure to open import.
+* gccgo_install.html: add description of the port to RTEMS (thanks Vinu Rajashekhar).
+* gobs: fix bug in singleton arrays.
+* godoc: display synopses for all packages that have some kind of documentation..
+* gofmt: fix some linebreak issues.
+* http: add https client support (thanks Fazlul Shahriar),
+	write body when content length unknown (thanks James Whitehead).
+* io: MultiReader and MultiWriter (thanks Brad Fitzpatrick),
+	fix another race condition in Pipes.
+* ld: many fixes including better handling of invalid input.
+* libmach: correct handling of .5 files with D_REGREG addresses.
+* linux/386: use Xen-friendly ELF TLS instruction sequence.
+* mime: add AddExtensionType (thanks Yuusei Kuwana).
+* misc/vim: syntax file recognizes constants like 1e9 (thanks Petar Maymounkov).
+* net: TCPConn.SetNoDelay, back by popular demand.
+* net(windows): fix crashing Read/Write when passed empty slice on (thanks Alex Brainman),
+	implement LookupHost/Port/SRV (thanks Wei Guangjing),
+	properly handle EOF in (*netFD).Read() (thanks Alex Brainman).
+* runtime: fix bug introduced in revision 4a01b8d28570 (thanks Alex Brainman),
+	rename cgo2c, *.cgo to goc2c, *.goc (thanks Peter Mundy).
+* scanner: better comment.
+* strings: add Title.
+* syscall: add ForkExec, Syscall12 on Windows (thanks Daniel Theophanes),
+	improve windows errno handling (thanks Alex Brainman).
+* syscall(windows): fix FormatMessage (thanks Peter Mundy),
+	implement Pipe() (thanks Wei Guangjing).
+* time: fix parsing of minutes in time zones.
+* utf16(windows): fix cyclic dependency when testing (thanks Peter Mundy).
+
+ +

2010-07-14

+ +
+This release includes a package change. In container/vector, the Iter method
+has been removed from the Vector, IntVector, and StringVector types. Also, the
+Data method has been renamed to Copy to better express its actual behavior.
+Now that Vector is just a slice, any for loops ranging over v.Iter() or
+v.Data() can be changed to range over v instead.
+
+Other changes:
+* big: Improvements to Rat.SetString (thanks Evan Shaw),
+        add sign, abs, Rat.IsInt.
+* cgo: various bug fixes.
+* codereview: Fix for Mercurial >= 1.6 (thanks Evan Shaw).
+* crypto/rand: add Windows implementation (thanks Peter Mundy).
+* crypto/tls: make HTTPS servers easier,
+        add client OCSP stapling support.
+* exp/eval: converted from bignum to big (thanks Evan Shaw).
+* gc: implement new len spec, range bug fix, optimization.
+* go/parser: require that '...' parameters are followed by a type.
+* http: fix ParseURL to handle //relative_path properly.
+* io: fix SectionReader Seek to seek backwards (thanks Peter Mundy).
+* json: Add HTMLEscape (thanks Micah Stetson).
+* ld: bug fixes.
+* math: amd64 version of log (thanks Charles L. Dorian).
+* mime/multipart: new package to parse multipart MIME messages
+        and HTTP multipart/form-data support.
+* os: use TempFile with default TempDir for test files (thanks Peter Mundy).
+* runtime/tiny: add docs for additional VMs, fix build (thanks Markus Duft).
+* runtime: better error for send/recv on nil channel.
+* spec: clarification of channel close(),
+        lock down some details about channels and select,
+        restrict when len(x) is constant,
+        specify len/cap for nil slices, maps, and channels.
+* windows: append .exe to binary names (thanks Joe Poirier).
+
+

2010-07-01

diff --git a/doc/devel/roadmap.html b/doc/devel/roadmap.html
index eace183f7..021ed6478 100644
--- a/doc/devel/roadmap.html
+++ b/doc/devel/roadmap.html
@@ -2,6 +2,7 @@
 
 

Go Roadmap

+

This page lists features and ideas being developed or discussed by the Go team. This list will be updated as work continues. @@ -26,6 +27,9 @@ Variant types. A way to define a type as being the union of some set of types.

  • Generics. An active topic of discussion. +
  • +Methods for operators, to allow a type to use arithmetic notation for +expressions.

    @@ -38,14 +42,10 @@ with a cycle detector running in a separate core.
  • Debugger.
  • -Native Client (NaCl) support. -
  • App Engine support.
  • Improved CGO including some mechanism for calling back from C to Go.
  • -SWIG support. -
  • Improved implementation documentation. @@ -56,28 +56,55 @@ Gc compiler roadmap
  • Implement goto restrictions.
  • -Safe compilation mode: generate code that is guaranteed not to obtain -an invalid memory address other than via import "unsafe". -
  • -Generate ELF debug info. -
  • Improved optimization.
  • 5g: Better floating point support. +
  • +Use escape analysis to keep more data on stack. -

    +

    Gccgo compiler roadmap

    • Implement goto restrictions.
    • -Implement garbage collection. -
    • Use goroutines rather than threads.
    • Separate gcc interface from frontend proper.
    • Use escape analysis to keep more data on stack.
    + +

    Done

    + +
      +
    • +gc: Generate DWARF debug info. +
    • +gc: Provide gdb support for runtime facilities. +
    • +Safe compilation mode: generate code that is guaranteed not to obtain an invalid memory address other than via import "unsafe". +
    • +Gccgo: garbage collection. +
    • +SWIG support. +
    • +Simpler semicolon rules. +
    • +A more general definition of ... in parameter lists. +
    • +Explicit conversions from string +to []byte and []int. +
    • +A function that will be run by the garbage collector when an item is freed +(runtime.SetFinalizer). +
    • +Public continuous build and benchmark infrastructure (gobuilder). +
    • +Package manager (goinstall). +
    • +A means of recovering from a panic (recover). +
    + diff --git a/doc/docs.html b/doc/docs.html new file mode 100644 index 000000000..e8152bb35 --- /dev/null +++ b/doc/docs.html @@ -0,0 +1,195 @@ + + +
    + +

    Learning Go

    + +

    +If you're new to Go, we recommend you work through the +tutorial. The +language specification has all the details should +you want to explore. +

    +

    +Once you've learned a little about the language, +Effective Go will help you learn the style and +idioms of programming in Go. +

    + +

    A Tutorial for the Go Programming Language

    +

    +The first tutorial. An introductory text that touches upon several core +concepts: syntax, types, allocation, constants, I/O, sorting, printing, +goroutines, and channels. +

    + +

    Effective Go

    +

    +A document that gives tips for writing clear, idiomatic Go code. +A must read for any new Go programmer. It augments the tutorial and +the language specification, both of which should be read first. +

    + +

    Frequently Asked Questions (FAQ)

    +

    +Answers to common questions about Go. +

    + +

    How to write Go code

    +

    +How to write a new package and how to test code. +

    + +

    Codelab: Writing Web Applications

    +

    +This codelab takes the reader through the creation of a simple wiki web +application. It touches on structs, methods, file I/O, http, regular expressions, +and closures. +

    + +

    Codewalks

    +

    +Guided tours of Go programs. +

    + +

    Go for C++ Programmers

    +

    +An introduction to Go for C++ programmers. +

    + +

    Non-English Documentation

    + +

    Chinese — 中文

    + + + +

    German — Deutsch

    + + + +

    Japanese — 日本語

    + + +

    Russian — Русский

    + + +
    + + +
    + +

    References

    + +

    Keep these under your pillow.

    + +

    Package Documentation

    +

    +The built-in documentation for the Go standard library. +

    + +

    Command Documentation

    +

    +The built-in documentation for the Go tools. +

    + +

    Language Specification

    +

    +The official Go Language specification. +

    + +

    The Go Memory Model

    +

    +A document that specifies the conditions under which reads of a variable in +one goroutine can be guaranteed to observe values produced by writes to the +same variable in a different goroutine. +

    + +

    Videos and Talks

    + +

    Go Programming

    +

    +A presentation delivered by Rob Pike and Russ Cox at Google I/O 2010. It +illustrates how programming in Go differs from other languages through a set of +examples demonstrating features particular to Go. These include concurrency, +embedded types, methods on any type, and program construction using interfaces. +

    + +

    Practical Go Programming

    +

    +This talk presents the development of a complete web application in Go. +It looks at design, storage, concurrency, and scaling issues in detail, using +the simple example of an URL shortening service. +See the presentation slides. +

    + +

    The Go Tech Talk

    +

    +An hour-long talk delivered by Rob Pike at Google in October 2009. +The language's first public introduction. (See the slides in PDF format.) The language has changed since it was made, +but it's still a good introduction. +

    + +

    gocoding YouTube Channel

    +

    +A YouTube channel that includes screencasts and other Go-related videos: +

    + + +

    The Expressiveness Of Go

    +

    +A discussion of the qualities that make Go an expressive and comprehensible +language. The talk was presented by Rob Pike at JAOO 2010. +The recording of the event was lost due to a hardware error. +

    + +

    Another Go at Language Design

    +

    +A tour, with some background, of the major features of Go, intended for +an audience new to the language. The talk was presented at OSCON 2010. +See the presentation slides. +

    +

    +This talk was also delivered at Sydney University in September 2010. A video +of the lecture is available +here. +

    + +

    Go Emerging Languages Conference Talk

    +

    +Rob Pike's Emerging Languages Conference presentation delivered in July 2010. See the presentation slides. Abstract: +

    +

    +Go’s approach to concurrency differs from that of many languages, even those +(such as Erlang) that make concurrency central, yet it has deep roots. The path +from Hoare’s 1978 paper to Go provides insight into how and why Go works as it +does. +

    + +

    The Go frontend for GCC

    +

    +A description of the Go language frontend for gcc. +Ian Lance Taylor's paper delivered at the GCC Summit 2010. +

    + +

    The Go Promo Video

    +

    +A short promotional video featuring Russ Cox demonstrating Go's fast compiler. +

    + +
    + +
    diff --git a/doc/effective_go.html b/doc/effective_go.html index 41a7b8af9..26e317b5d 100644 --- a/doc/effective_go.html +++ b/doc/effective_go.html @@ -207,7 +207,7 @@ have a doc comment.

    -Doc comments work best as complete English sentences, which allow +Doc comments work best as complete sentences, which allow a wide variety of automated presentations. The first sentence should be a one-sentence summary that starts with the name being declared. @@ -463,7 +463,7 @@ statement, it's common to see one used to set up a local variable.

     if err := file.Chmod(0664); err != nil {
    -    log.Stderr(err)
    +    log.Print(err)
         return err
     }
     
    @@ -794,7 +794,7 @@ func Contents(filename string) (string, os.Error) { buf := make([]byte, 100) for { n, err := f.Read(buf[0:]) - result = bytes.Add(result, buf[0:n]) + result = append(result, buf[0:n]...) // append is discussed later. if err != nil { if err == os.EOF { break @@ -815,7 +815,7 @@ which is much clearer than placing it at the end of the function.

    -The arguments to the deferred function (which includes the receiver if +The arguments to the deferred function (which include the receiver if the function is a method) are evaluated when the defer executes, not when the call executes. Besides avoiding worries about variables changing values as the function executes, this means @@ -1218,6 +1218,11 @@ func Append(slice, data[]byte) []byte { We must return the slice afterwards because, although Append can modify the elements of slice, the slice itself (the run-time data structure holding the pointer, length, and capacity) is passed by value. +

    +The idea of appending to a slice is so useful it's captured by the +append built-in function. To understand that function's +design, though, we need a little more information, so we'll return +to it later.

    @@ -1288,7 +1293,7 @@ func offset(tz string) int { if seconds, ok := timeZone[tz]; ok { return seconds } - log.Stderr("unknown time zone", tz) + log.Println("unknown time zone", tz) return 0 }
  • @@ -1326,13 +1331,15 @@ You don't need to provide a format string. For each of Printf, Fprintf and Sprintf there is another pair of functions, for instance Print and Println. These functions do not take a format string but instead generate a default -format for each argument. The ln version also inserts a blank -between arguments if neither is a string and appends a newline to the output. +format for each argument. The Println versions also insert a blank +between arguments and append a newline to the output while +the Print versions add blanks only if the operand on neither side is a string. In this example each line produces the same output.

     fmt.Printf("Hello %d\n", 23)
     fmt.Fprint(os.Stdout, "Hello ", 23, "\n")
    +fmt.Println("Hello", 23)
     fmt.Println(fmt.Sprint("Hello ", 23))
     

    @@ -1453,16 +1460,20 @@ Within the function Printf, v acts like a variable of []interface{} but if it is passed to another variadic function, it acts like a regular list of arguments. Here is the implementation of the -function log.Stderr we used above. It passes its arguments directly to +function log.Println we used above. It passes its arguments directly to fmt.Sprintln for the actual formatting.

    -// Stderr is a helper function for easy logging to stderr. It is analogous to Fprintln(os.Stderr).
    -func Stderr(v ...interface{}) {
    -    stderr.Output(2, fmt.Sprintln(v))  // Output takes parameters (int, string)
    +// Println prints to the standard logger in the manner of fmt.Println.
    +func Println(v ...interface{}) {
    +    std.Output(2, fmt.Sprintln(v...))  // Output takes parameters (int, string)
     }
     

    +We write ... after v in the nested call to Sprintln to tell the +compiler to treat v as a list of arguments; otherwise it would just pass +v as a single slice argument. +

    There's even more to printing than we've covered here. See the godoc documentation for package fmt for the details.

    @@ -1482,6 +1493,47 @@ func Min(a ...int) int { }
    +

    Append

    +

    +Now we have the missing piece we needed to explain the design of +the append built-in function. The signature of append +is different from our custom Append function above. +Schematically, it's like this: +

    +func append(slice []T, elements...T) []T
    +
    +where T is a placeholder for any given type. You can't +actually write a function in Go where the type T +is determined by the caller. +That's why append is built in: it needs support from the +compiler. +

    +What append does is append the elements to the end of +the slice and return the result. The result needs to be returned +because, as with our hand-written Append, the underlying +array may change. This simple example +

    +x := []int{1,2,3}
    +x = append(x, 4, 5, 6)
    +fmt.Println(x)
    +
    +prints [1 2 3 4 5 6]. So append works a +little like Printf, collecting an arbitrary number of +arguments. +

    +But what if we wanted to do what our Append does and +append a slice to a slice? Easy: use ... at the call +site, just as we did in the call to Output above. This +snippet produces identical output to the one above. +

    +x := []int{1,2,3}
    +y := []int{4,5,6}
    +x = append(x, y...)
    +fmt.Println(x)
    +
    +Without that ..., it wouldn't compile because the types +would be wrong; y is not of type int. +

    Initialization

    @@ -1537,26 +1589,29 @@ automatically for printing, even as part of a general type. func (b ByteSize) String() string { switch { case b >= YB: - return fmt.Sprintf("%.2fYB", b/YB) + return fmt.Sprintf("%.2fYB", float64(b/YB)) case b >= ZB: - return fmt.Sprintf("%.2fZB", b/ZB) + return fmt.Sprintf("%.2fZB", float64(b/ZB)) case b >= EB: - return fmt.Sprintf("%.2fEB", b/EB) + return fmt.Sprintf("%.2fEB", float64(b/EB)) case b >= PB: - return fmt.Sprintf("%.2fPB", b/PB) + return fmt.Sprintf("%.2fPB", float64(b/PB)) case b >= TB: - return fmt.Sprintf("%.2fTB", b/TB) + return fmt.Sprintf("%.2fTB", float64(b/TB)) case b >= GB: - return fmt.Sprintf("%.2fGB", b/GB) + return fmt.Sprintf("%.2fGB", float64(b/GB)) case b >= MB: - return fmt.Sprintf("%.2fMB", b/MB) + return fmt.Sprintf("%.2fMB", float64(b/MB)) case b >= KB: - return fmt.Sprintf("%.2fKB", b/KB) + return fmt.Sprintf("%.2fKB", float64(b/KB)) } - return fmt.Sprintf("%.2fB", b) + return fmt.Sprintf("%.2fB", float64(b)) }

    +(The float64 conversions prevent Sprintf +from recurring back through the String method for +ByteSize.) The expression YB prints as 1.00YB, while ByteSize(1e13) prints as 9.09TB.

    @@ -1849,10 +1904,18 @@ that implements Handler can serve HTTP requests.

     type Handler interface {
    -    ServeHTTP(*Conn, *Request)
    +    ServeHTTP(ResponseWriter, *Request)
     }
     

    +ResponseWriter is itself an interface that provides access +to the methods needed to return the response to the client. +Those methods include the standard Write method, so an +http.ResponseWriter can be used wherever an io.Writer +can be used. +Request is a struct containing a parsed representation +of the request from the client. +

    For brevity, let's ignore POSTs and assume HTTP requests are always GETs; that simplification does not affect the way the handlers are set up. Here's a trivial but complete implementation of a handler to @@ -1865,13 +1928,14 @@ type Counter struct { n int } -func (ctr *Counter) ServeHTTP(c *http.Conn, req *http.Request) { +func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) { ctr.n++ - fmt.Fprintf(c, "counter = %d\n", ctr.n) + fmt.Fprintf(w, "counter = %d\n", ctr.n) }

    -(Keeping with our theme, note how Fprintf can print to an HTTP connection.) +(Keeping with our theme, note how Fprintf can print to an +http.ResponseWriter.) For reference, here's how to attach such a server to a node on the URL tree.

     import "http"
    @@ -1887,9 +1951,9 @@ But why make Counter a struct?  An integer is all that's needed.
     // Simpler counter server.
     type Counter int
     
    -func (ctr *Counter) ServeHTTP(c *http.Conn, req *http.Request) {
    +func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
         *ctr++
    -    fmt.Fprintf(c, "counter = %d\n", *ctr)
    +    fmt.Fprintf(w, "counter = %d\n", *ctr)
     }
     

    @@ -1901,9 +1965,9 @@ has been visited? Tie a channel to the web page. // (Probably want the channel to be buffered.) type Chan chan *http.Request -func (ch Chan) ServeHTTP(c *http.Conn, req *http.Request) { +func (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) { ch <- req - fmt.Fprint(c, "notification sent") + fmt.Fprint(w, "notification sent") }

    @@ -1930,11 +1994,11 @@ The http package contains this code: // ordinary functions as HTTP handlers. If f is a function // with the appropriate signature, HandlerFunc(f) is a // Handler object that calls f. -type HandlerFunc func(*Conn, *Request) +type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(c, req). -func (f HandlerFunc) ServeHTTP(c *Conn, req *Request) { - f(c, req) +func (f HandlerFunc) ServeHTTP(w ResponseWriter, req *Request) { + f(w, req) }

    @@ -1950,9 +2014,9 @@ to have the right signature.

     // Argument server.
    -func ArgServer(c *http.Conn, req *http.Request) {
    +func ArgServer(w http.ResponseWriter, req *http.Request) {
         for i, s := range os.Args {
    -        fmt.Fprintln(c, s)
    +        fmt.Fprintln(w, s)
         }
     }
     
    @@ -2014,7 +2078,7 @@ two methods explicitly, but it's easier and more evocative to embed the two interfaces to form the new one, like this:

    -// ReadWrite is the interface that groups the basic Read and Write methods.
    +// ReadWriter is the interface that combines the Reader and Writer interfaces.
     type ReadWriter interface {
         Reader
         Writer
    @@ -2119,7 +2183,7 @@ func NewJob(command string, logger *log.Logger) *Job {
     or with a composite literal,
     

    -job := &Job{command, log.New(os.Stderr, nil, "Job: ", log.Ldate)}
    +job := &Job{command, log.New(os.Stderr, "Job: ", log.Ldate)}
     

    If we need to refer to an embedded field directly, the type name of the field, @@ -2603,7 +2667,7 @@ func CubeRoot(x float64) float64 { } } // A million iterations has not converged; something is wrong. - panic(fmt.Sprintf("CubeRoot(%g) did not converge", x) + panic(fmt.Sprintf("CubeRoot(%g) did not converge", x)) }

    @@ -2654,14 +2718,14 @@ inside a server without killing the other executing goroutines.
     func server(workChan <-chan *Work) {
         for work := range workChan {
    -        safelyDo(work)
    +        go safelyDo(work)
         }
     }
     
     func safelyDo(work *Work) {
         defer func() {
             if err := recover(); err != nil {
    -            log.Stderr("work failed:", err)
    +            log.Println("work failed:", err)
             }
         }()
         do(work)
    @@ -2728,7 +2792,7 @@ user-triggered errors.
     

    -With this error handling in place, the error method +With error handling in place, the error method makes it easy to report parse errors without worrying about unwinding the parse stack by hand.

    @@ -2740,6 +2804,17 @@ Useful though this pattern is, it should be used only within a package. to its client. That is a good rule to follow.

    +

    +By the way, this re-panic idiom changes the panic value if an actual +error occurs. However, both the original and new failures will be +presented in the crash report, so the root cause of the problem will +still be visible. Thus this simple re-panic approach is usually +sufficient—it's a crash after all—but if you want to +display only the original value, you can write a little more code to +filter unexpected problems and re-panic with the original error. +That's left as an exercise for the reader. +

    +

    A web server

    @@ -2789,12 +2864,12 @@ func main() { } } -func QR(c *http.Conn, req *http.Request) { - templ.Execute(req.FormValue("s"), c) +func QR(w http.ResponseWriter, req *http.Request) { + templ.Execute(req.FormValue("s"), w) } -func UrlHtmlFormatter(w io.Writer, v interface{}, fmt string) { - template.HTMLEscape(w, []byte(http.URLEscape(v.(string)))) +func UrlHtmlFormatter(w io.Writer, fmt string, v ...interface{}) { + template.HTMLEscape(w, []byte(http.URLEscape(v[0].(string)))) } diff --git a/doc/frontpage.css b/doc/frontpage.css new file mode 100644 index 000000000..bcdca6401 --- /dev/null +++ b/doc/frontpage.css @@ -0,0 +1,140 @@ +/* Overloads to all.css */ +#container { width: 76em } +.left-column { width: 48%; } +.right-column { width: 48%; } + +/* Frontpage styles */ +#content-introductory code { + font-family: "Bitstream Vera Sans Mono", "Andale Mono", monospace; +} +#content-introductory input, select, textarea { + font-family: "Bitstream Vera Sans", Verdana, sans-serif; + font-size: 1em; +} +span.keyword { + font-family: Cambria, Georgia, Times, "Times New Roman", serif; + font-size: 1.15em; + font-style: italic; +} +#content h3, #content h2 { + margin: 0; + font-size: 1em; + background: none; + border: none; + padding: 0; +} +#content .more { + color: #999; + font-weight: normal; +} +#frontpage h2#branding-tagline { + font-weight: normal; + font-style: italic; +} +#resources { + position: relative; + margin-top: 1em; +} +#resources h3 { + margin-top: 0; + margin-bottom: -.5em; + font-size: 1em; + font-weight: normal; +} +#resources-users { + float: left; + width: 48%; +} +#resources-contributors { + float: right; + width: 50%; +} +#resources ul { + padding-left: 2em; +} +#resources li { + margin-bottom: 0.5em; +} +#content-rotating { + height: 200px; +} +#content-videos { + float: left; + width: 170px; +} +#content-videos .thumbnail { + width: 150px; + height: 103px; + background-repeat: no-repeat; + border: none; +} +#content-videos .thumbnail._001 { + background: url(/doc/video-001.png); +} +#content-videos .thumbnail._002 { + background: url(/doc/video-002.png); +} +#content-videos .thumbnail._003 { + background: url(/doc/video-003.png); +} +#content-videos .thumbnail._004 { + background: url(/doc/video-004.png); +} +#content-videos a.video { + display: inline-block; + width: 150px; + margin-right: .30em; + margin-top: 1.2em; +} +#content-videos a.video .caption { + display: block; + text-align: center; +} +#content-videos a.video .caption.title { + margin-top: .31em; + font-weight: bold; +} +#content-blog ul { + margin-top: 1em; + margin-left: 0; + padding-left: 0; +} +#content-blog li { + list-style: none; + margin-bottom: 1em; +} +#content-blog li a { + color: #999; + text-decoration: none; +} +#content-blog .date { + color: #999; + font-size: 0.8em; + display: inline-block; + margin-left: 0.5em; +} +#content-blog li a:link .title { + color: #04a; +} +#content-blog li a:visited .title { + color: #04a; +} +#content-blog li a:hover .title { + color: #a40; + text-decoration: underline; +} +#content-blog li a:active .title { + color: #c00; +} +.navtop { + display: none !important; +} +.how { + float: right; + font-size: 75%; +} +.unsupported { + font-weight: bold; + color: red; +} + diff --git a/doc/gccgo_install.html b/doc/gccgo_install.html index 3ffd6a645..393e57963 100644 --- a/doc/gccgo_install.html +++ b/doc/gccgo_install.html @@ -224,12 +224,9 @@ gccgo -o main main.o mypackage.o # Explicitly links with mypackage.o

    Some Go features are not yet implemented in gccgo. As of -2009-11-06, the following are not implemented: +2010-08-23, the following are not implemented:

      -
    • Garbage collection is not implemented. There is no way to free memory. - Thus long running programs are not supported. -
    • goroutines are implemented as NPTL threads. If you can not use the gold linker as described above, they are created with a fixed stack size, and the number of goroutines that may be created at @@ -263,14 +260,13 @@ Pointers in Go are pointers in C. A Go struct is the same as C struct with the same fields and types.

      -The Go string type is a pointer to a structure. -The current definition is -(this is expected to change): +The Go string type is currently defined as a two-element +structure (this is subject to change):

       struct __go_string {
      -  size_t __length;
      -  unsigned char __data[];
      +  const unsigned char *__data;
      +  int __length;
       };
       
      @@ -310,9 +306,10 @@ when the functions have equivalent types.

      Go interface, channel, and map -types have no corresponding C type (they roughly correspond to pointers -to structs in C, but the structs are deliberately undocumented). C -enum types correspond to some Go type, but precisely +types have no corresponding C type (interface is a +two-element struct and channel and map are +pointers to structs in C, but the structs are deliberately undocumented). C +enum types correspond to some integer type, but precisely which one is difficult to predict in general; use a cast. C union types have no corresponding Go type. C struct types containing bitfields have no corresponding Go type. C++ class types have @@ -359,12 +356,15 @@ i := c_open(&name[0], os.O_RDONLY, 0);

      The name of Go functions accessed from C is subject to change. At present the name of a Go function that does not have a receiver is -package.Functionname. To call it from C you must set the -name using a gcc extension similar to the gccgo +prefix.package.Functionname. The prefix is set by +the -fgo-prefix option used when the package is compiled; +if the option is not used, the default is simply go. +To call the function from C you must set the name using +a gcc extension similar to the gccgo extension.

      -extern int go_function(int) __asm__ ("mypackage.Function");
      +extern int go_function(int) __asm__ ("myprefix.mypackage.Function");
       

      @@ -395,3 +395,15 @@ grep '#GO' foo.s | grep -v INVALID | grep -v unknowndefine | grep -v undef > foo This procedure is full of unstated caveats and restrictions and we make no guarantee that it will not change in the future. It is more useful as a starting point for real Go code than as a regular procedure. + +

      RTEMS Port

      +

      +The gccgo compiler has been ported to +RTEMS. RTEMS is a real-time executive +that provides a high performance environment for embedded applications +on a range of processors and embedded hardware. The current gccgo +port is for x86. The goal is to extend the port to most of the + +architectures supported by RTEMS. For more information on the port, +as well as instructions on how to install it, please see this +RTEMS Wiki page. diff --git a/doc/go_faq.html b/doc/go_faq.html index 4f11baa80..1c7b85ef8 100644 --- a/doc/go_faq.html +++ b/doc/go_faq.html @@ -56,6 +56,21 @@ What is the origin of the name?

      “Ogle” would be a good name for a Go debugger. +

      +What's the origin of the mascot?

      + +

      +The mascot and logo were designed by +Renée French, who also designed +Glenda, +the Plan 9 bunny. +The gopher is derived from one she used for an WFMU +T-shirt design some years ago. +The logo and mascot are covered by the +Creative Commons Attribution 3.0 +license. +

      +

      What kind of a name is 6g?

      @@ -69,42 +84,102 @@ http://plan9.bell-labs.com/sys/doc/compiler.html 6 is the architecture letter for amd64 (or x86-64, if you prefer), while g stands for Go. -

      -Why not just write some libraries for C++ to do communication?

      +

      +What is the history of the project?

      +

      +Robert Griesemer, Rob Pike and Ken Thompson started sketching the +goals for a new language on the white board on September 21, 2007. +Within a few days the goals had settled into a plan to do something +and a fair idea of what it would be. Design continued part-time in +parallel with unrelated work. By January 2008, Ken had started work +on a compiler with which to explore ideas; it generated C code as its +output. By mid-year the language had become a full-time project and +had settled enough to attempt a production compiler. In May 2008, +Ian Taylor independently started on a GCC front end for Go using the +draft specification. Russ Cox joined in late 2008 and helped move the language +and libraries from prototype to reality. +

      -

      We considered doing that, but too many of the problems—lack of -garbage collection, long dependency chains, nested include files, -lack of concurrency awareness—are rooted in the design of -the C and C++ languages themselves. -We felt a viable solution required a more complete approach. +

      +Many others have contributed ideas, discussions, and code. +

      -

      -Why doesn't Go run on Windows yet?

      +

      +Why are you creating a new language?

      -We understand that a significant fraction of computers in the world -run Windows and it would be great if those computers could run Go -programs. A group of volunteers has made significant progress toward -porting Go to MinGW. -You can follow their progress at the Go Wiki's -WindowsPort page. +Go was born out of frustration with existing languages and +environments for systems programming. Programming had become too +difficult and the choice of languages was partly to blame. One had to +choose either efficient compilation, efficient execution, or ease of +programming; all three were not available in the same mainstream +language. Programmers who could were choosing ease over +safety and efficiency by moving to dynamically typed languages such as +Python and JavaScript rather than C++ or, to a lesser extent, Java. +

      +

      +Go is an attempt to combine the ease of programming of an interpreted, +dynamically typed +language with the efficiency and safety of a statically typed, compiled language. +It also aims to be modern, with support for networked and multicore +computing. Finally, it is intended to be fast: it should take +at most a few seconds to build a large executable on a single computer. +To meet these goals required addressing a number of +linguistic issues: an expressive but lightweight type system; +concurrency and garbage collection; rigid dependency specification; +and so on. These cannot be addressed well by libraries or tools; a new +language was called for.

      -

      -What's the origin of the mascot?

      +

      +What are Go's ancestors?

      -The mascot and logo were designed by -Renée French, who also designed -Glenda, -the Plan 9 bunny. -The gopher is derived from one she used for an WFMU -T-shirt design some years ago. -The logo and mascot are covered by the -Creative Commons Attribution 3.0 -license. +Go is mostly in the C family (basic syntax), +with significant input from the Pascal/Modula/Oberon +family (declarations, packages), +plus some ideas from languages +inspired by Tony Hoare's CSP, +such as Newsqueak and Limbo (concurrency). +However, it is a new language across the board. +In every respect the language was designed by thinking +about what programmers do and how to make programming, at least the +kind of programming we do, more effective, which means more fun. +

      + + +

      +What are the guiding principles in the design?

      +

      +Programming today involves too much bookkeeping, repetition, and +clerical work. As Dick Gabriel says, “Old programs read +like quiet conversations between a well-spoken research worker and a +well-studied mechanical colleague, not as a debate with a compiler. +Who'd have guessed sophistication bought such noise?” +The sophistication is worthwhile—no one wants to go back to +the old languages—but can it be more quietly achieved? +

      +

      +Go attempts to reduce the amount of typing in both senses of the word. +Throughout its design, we have tried to reduce clutter and +complexity. There are no forward declarations and no header files; +everything is declared exactly once. Initialization is expressive, +automatic, and easy to use. Syntax is clean and light on keywords. +Stuttering (foo.Foo* myFoo = new(foo.Foo)) is reduced by +simple type derivation using the := +declare-and-initialize construct. And perhaps most radically, there +is no type hierarchy: types just are, they don't have to +announce their relationships. These simplifications allow Go to be +expressive yet comprehensible without sacrificing, well, sophistication. +

      +

      +Another important principle is to keep the concepts orthogonal. +Methods can be implemented for any type; structures represent data while +interfaces represent abstraction; and so on. Orthogonality makes it +easier to understand what happens when things combine.

      +

      Usage

      @@ -116,37 +191,31 @@ if they enjoy it. Not every programmer will, but we hope enough will find satisfaction in the approach it offers to justify further development. -

      Is Google using Go -internally?

      +

      Is Google using Go internally?

      -

      The Go project was conceived to make it easier to write the kind -of servers and other software Google uses internally, but the -implementation isn't quite mature enough yet for large-scale -production use. While we continue development we are also doing -experiments with the language as a candidate server environment. It's -getting there. For instance, the server behind http://golang.org is a Go program; in -fact it's just the godoc document server running in a -production configuration. +

      Yes. There are now several Go programs deployed in +production inside Google. For instance, the server behind +http://golang.org is a Go program; +in fact it's just the godoc +document server running in a production configuration.

      -There are two Go compiler implementations, 6g and friends, generically called -gc, and gccgo. +There are two Go compiler implementations, 6g and friends, +generically called gc, and gccgo. Gc uses a different calling convention and linker and can therefore only be linked with C programs using the same convention. -There is such a C compiler but no C++ compiler. Gccgo is a -GCC front-end that can, with care, be linked with GCC-compiled -C or C++ programs. However, because Go is garbage-collected it will be -unwise to do so, at least naively. +There is such a C compiler but no C++ compiler. +Gccgo is a GCC front-end that can, with care, be linked with +GCC-compiled C or C++ programs.

      -There is a “foreign function interface” to allow safe calling of C-written -libraries from Go code. We expect to use SWIG to extend this capability -to C++ libraries. There is no safe way to call Go code from C or C++ yet. +The cgo program provides the mechanism for a +“foreign function interface” to allow safe calling of +C libraries from Go code. SWIG extends this capability to C++ libraries.

      Does Go support Google's protocol buffers?

      @@ -170,7 +239,35 @@ you will need to abide by the guidelines at

      Design

      -

      Why doesn't Go have feature X?

      +

      +What's up with Unicode identifiers?

      + +

      +It was important to us to extend the space of identifiers from the +confines of ASCII. Go's rule—identifier characters must be +letters or digits as defined by Unicode—is simple to understand +and to implement but has restrictions. Combining characters are +excluded by design, for instance. +Until there +is an agreed external definition of what an identifier might be, +plus a definition of canonicalization of identifiers that guarantees +no ambiguity, it seemed better to keep combining characters out of +the mix. Thus we have a simple rule that can be expanded later +without breaking programs, one that avoids bugs that would surely arise +from a rule that admits ambiguous identifiers. +

      + +

      +On a related note, since an exported identifier must begin with an +upper-case letter, identifiers created from “letters” +in some languages can, by definition, not be exported. For now the +only solution is to use something like X日本語, which +is clearly unsatisfactory; we are considering other options. The +case-for-visibility rule is unlikely to change however; it's one +of our favorite features of Go. +

      + +

      Why does Go not have feature X?

      Every language contains novel features and omits someone's favorite @@ -186,15 +283,145 @@ If it bothers you that Go is missing feature X, please forgive us and investigate the features that Go does have. You might find that they compensate in interesting ways for the lack of X. -

      -Why is the syntax so different from C++?

      +

      +Why does Go not have generic types?

      +

      +Generics may well be added at some point. We don't feel an urgency for +them, although we understand some programmers do. +

      +

      +Generics are convenient but they come at a cost in +complexity in the type system and run-time. We haven't yet found a +design that gives value proportionate to the complexity, although we +continue to think about it. Meanwhile, Go's built-in maps and slices, +plus the ability to use the empty interface to construct containers +(with explicit unboxing) mean in many cases it is possible to write +code that does what generics would enable, if less smoothly. +

      +

      +This remains an open issue. +

      +

      +Why does Go not have exceptions?

      -This and other language design questions are answered in -the separate language design FAQ. +We believe that coupling exceptions to a control +structure, as in the try-catch-finally idiom, results in +convoluted code. It also tends to encourage programmers to label +too many ordinary errors, such as failing to open a file, as +exceptional. +

      +

      +Go takes a different approach. Instead of exceptions, it has a couple +of built-in functions to signal and recover from truly exceptional +conditions. The recovery mechanism is executed only as part of a +function's state being torn down after an error, which is sufficient +to handle catastrophe but requires no extra control structures and, +when used well, can result in clean error-handling code. +

      +

      +See the Defer, Panic, and Recover article for details. +

      -

      -Object-Oriented Programming

      + +

      +Why does Go not have assertions?

      + +

      +Go doesn't provide assertions. They are undeniably convenient, but our +experience has been that programmers use them as a crutch to avoid thinking +about proper error handling and reporting. Proper error handling means that +servers continue operation after non-fatal errors instead of crashing. +Proper error reporting means that errors are direct and to the point, +saving the programmer from interpreting a large crash trace. Precise +errors are particularly important when the programmer seeing the errors is +not familiar with the code. + +

      +The same arguments apply to the use of assert() in test programs. Proper +error handling means letting other tests run after one has failed, so +that the person debugging the failure gets a complete picture of what is +wrong. It is more useful for a test to report that +isPrime gives the wrong answer for 2, 3, 5, and 7 (or for +2, 4, 8, and 16) than to report that isPrime gives the wrong +answer for 2 and therefore no more tests were run. The programmer who +triggers the test failure may not be familiar with the code that fails. +Time invested writing a good error message now pays off later when the +test breaks. + +

      +In testing, if the amount of extra code required to write +good errors seems repetitive and overwhelming, it might work better as a +table-driven test instead. +Go has excellent support for data structure literals. + +

      +We understand that this is a point of contention. There are many things in +the Go language and libraries that differ from modern practices, simply +because we feel it's sometimes worth trying a different approach. + +

      +Why build concurrency on the ideas of CSP?

      +

      +Concurrency and multi-threaded programming have a reputation +for difficulty. We believe the problem is due partly to complex +designs such as pthreads and partly to overemphasis on low-level details +such as mutexes, condition variables, and even memory barriers. +Higher-level interfaces enable much simpler code, even if there are still +mutexes and such under the covers. +

      +

      +One of the most successful models for providing high-level linguistic support +for concurrency comes from Hoare's Communicating Sequential Processes, or CSP. +Occam and Erlang are two well known languages that stem from CSP. +Go's concurrency primitives derive from a different part of the family tree +whose main contribution is the powerful notion of channels as first class objects. +

      + +

      +Why goroutines instead of threads?

      +

      +Goroutines are part of making concurrency easy to use. The idea, which has +been around for a while, is to multiplex independently executing +functions—coroutines, really—onto a set of threads. +When a coroutine blocks, such as by calling a blocking system call, +the run-time automatically moves other coroutines on the same operating +system thread to a different, runnable thread so they won't be blocked. +The programmer sees none of this, which is the point. +The result, which we call goroutines, can be very cheap: unless they spend a lot of time +in long-running system calls, they cost little more than the memory +for the stack. +

      +

      +To make the stacks small, Go's run-time uses segmented stacks. A newly +minted goroutine is given a few kilobytes, which is almost always enough. +When it isn't, the run-time allocates (and frees) extension segments automatically. +The overhead averages about three cheap instructions per function call. +It is practical to create hundreds of thousands of goroutines in the same +address space. If goroutines were just threads, system resources would +run out at a much smaller number. +

      + +

      +Why are map operations not defined to be atomic?

      + +

      +After long discussion it was decided that the typical use of maps did not require +safe access from multiple threads, and in those cases where it did, the map was +probably part of some larger data structure or computation that was already +synchronized. Therefore requiring that all map operations grab a mutex would slow +down most programs and add safety to few. This was not an easy decision, +however, since it means uncontrolled map access can crash the program. +

      + +

      +The language does not preclude atomic map updates. When required, such +as when hosting an untrusted program, the implementation could interlock +map access. +

      + + +

      Types

      Is Go an object-oriented language?

      @@ -220,6 +447,130 @@ How do I get dynamic dispatch of methods? The only way to have dynamically dispatched methods is through an interface. Methods on structs or other types are always resolved statically. +

      +Why is there no type inheritance?

      +

      +Object-oriented programming, at least in the best-known languages, +involves too much discussion of the relationships between types, +relationships that often could be derived automatically. Go takes a +different approach. +

      +

      +Rather than requiring the programmer to declare ahead of time that two +types are related, in Go a type automatically satisfies any interface +that specifies a subset of its methods. Besides reducing the +bookkeeping, this approach has real advantages. Types can satisfy +many interfaces at once, without the complexities of traditional +multiple inheritance. +Interfaces can be very lightweight—having one or even zero methods +in an interface can express useful concepts. +Interfaces can be added after the fact if a new idea comes along +or for testing—without annotating the original types. +Because there are no explicit relationships between types +and interfaces, there is no type hierarchy to manage or discuss. +

      +

      +It's possible to use these ideas to construct something analogous to +type-safe Unix pipes. For instance, see how fmt.Fprintf +enables formatted printing to any output, not just a file, or how the +bufio package can be completely separate from file I/O, +or how the crypto packages stitch together block and +stream ciphers. All these ideas stem from a single interface +(io.Writer) representing a single method +(Write). And that's only scratching the surface. +

      +

      +It takes some getting used to but this implicit style of type +dependency is one of the most exciting things about Go. +

      + +

      +Why is len a function and not a method?

      +

      +We debated this issue but decided +implementing len and friends as functions was fine in practice and +didn't complicate questions about the interface (in the Go type sense) +of basic types. +

      + +

      +Why does Go not support overloading of methods and operators?

      +

      +Method dispatch is simplified if it doesn't need to do type matching as well. +Experience with other languages told us that having a variety of +methods with the same name but different signatures was occasionally useful +but that it could also be confusing and fragile in practice. Matching only by name +and requiring consistency in the types was a major simplifying decision +in Go's type system. +

      +

      +Regarding operator overloading, it seems more a convenience than an absolute +requirement. Again, things are simpler without it. +

      + + +

      Values

      + +

      +Why does Go not provide implicit numeric conversions?

      +

      +The convenience of automatic conversion between numeric types in C is +outweighed by the confusion it causes. When is an expression unsigned? +How big is the value? Does it overflow? Is the result portable, independent +of the machine on which it executes? +It also complicates the compiler; “the usual arithmetic conversions” +are not easy to implement and inconsistent across architectures. +For reasons of portability, we decided to make things clear and straightforward +at the cost of some explicit conversions in the code. +The definition of constants in Go—arbitrary precision values free +of signedness and size annotations—ameliorates matters considerably, +though. +

      +

      +A related detail is that, unlike in C, int and int64 +are distinct types even if int is a 64-bit type. The int +type is generic; if you care about how many bits an integer holds, Go +encourages you to be explicit. +

      + +

      +Why are maps built in?

      +

      +The same reason strings are: they are such a powerful and important data +structure that providing one excellent implementation with syntactic support +makes programming more pleasant. We believe that Go's implementation of maps +is strong enough that it will serve for the vast majority of uses. +If a specific application can benefit from a custom implementation, it's possible +to write one but it will not be as convenient syntactically; this seems a reasonable tradeoff. +

      + + +

      +Why don't maps allow structs and arrays as keys?

      +

      +Map lookup requires an equality operator, which structs and arrays do not implement. +They don't implement equality because equality is not well defined on such types; +there are multiple considerations involving shallow vs. deep comparison, pointer vs. +value comparison, how to deal with recursive structures, and so on. +We may revisit this issue—and implementing equality for structs and arrays +will not invalidate any existing programs—but without a clear idea of what +equality of structs and arrays should mean, it was simpler to leave it out for now. +

      + +

      +Why are maps, slices, and channels references while arrays are values?

      +

      +There's a lot of history on that topic. Early on, maps and channels +were syntactically pointers and it was impossible to declare or use a +non-pointer instance. Also, we struggled with how arrays should work. +Eventually we decided that the strict separation of pointers and +values made the language harder to use. Introducing reference types, +including slices to handle the reference form of arrays, resolved +these issues. Reference types add some regrettable complexity to the +language but they have a large effect on usability: Go became a more +productive, comfortable language when they were introduced. +

      +

      Writing Code

      @@ -259,41 +610,284 @@ See the document Contributing to the Go project for more information about how to proceed. -

      -Where is assert?

      + +

      Pointers and Allocation

      + +

      +When are function parameters passed by value?

      -Go doesn't provide assertions. They are undeniably convenient, but our -experience has been that programmers use them as a crutch to avoid thinking -about proper error handling and reporting. Proper error handling means that -servers continue operation after non-fatal errors instead of crashing. -Proper error reporting means that errors are direct and to the point, -saving the programmer from interpreting a large crash trace. Precise -errors are particularly important when the programmer seeing the errors is -not familiar with the code. +Everything in Go is passed by value. A function always gets a copy of the +thing being passed, as if there were an assignment statement assigning the +value to the parameter. For instance, copying a pointer value makes a copy of +the pointer, not the data it points to. +

      -The same arguments apply to the use of assert() in test programs. Proper -error handling means letting other tests run after one has failed, so -that the person debugging the failure gets a complete picture of what is -wrong. It is more useful for a test to report that -isPrime gives the wrong answer for 2, 3, 5, and 7 (or for -2, 4, 8, and 16) than to report that isPrime gives the wrong -answer for 2 and therefore no more tests were run. The programmer who -triggers the test failure may not be familiar with the code that fails. -Time invested writing a good error message now pays off later when the -test breaks. +Map and slice values behave like pointers; they are descriptors that +contain pointers to the underlying map or slice data. Copying a map or +slice value doesn't copy the data it points to. Copying an interface value +makes a copy of the thing stored in the interface value. If the interface +value holds a struct, copying the interface value makes a copy of the +struct. If the interface value holds a pointer, copying the interface value +makes a copy of the pointer, but again not the data it points to. +

      + +

      +Should I define methods on values or pointers?

      + +
      +func (s *MyStruct) someMethod() { } // method on pointer
      +func (s MyStruct) someMethod() { }  // method on value
      +

      -In testing, if the amount of extra code required to write -good errors seems repetitive and overwhelming, it might work better as a -table-driven test instead. -Go has excellent support for data structure literals. +When defining a method on a type, the receiver (s in the above +example) behaves exactly is if it were an argument to the method. Define the +method on a pointer type if you need the method to modify the data the receiver +points to. Otherwise, it is often cleaner to define the method on a value type. +

      + +

      +What's the difference between new and make?

      -We understand that this is a point of contention. There are many things in -the Go language and libraries that differ from modern practices, simply -because we feel it's sometimes worth trying a different approach. +In short: new allocates memory, make initializes +the slice, map, and channel types. +

      + +

      +See the relevant section +of Effective Go for more details. +

      + +

      +Why is int 32 bits on 64 bit machines?

      + +

      +The size of int and float is implementation-specific. +The 64 bit Go compilers (both 6g and gccgo) use a 32 bit representation for +both int and float. Code that relies on a particular +size of value should use an explicitly sized type, like int64 or +float64. +

      + +

      Concurrency

      + +

      +What operations are atomic? What about mutexes?

      + +

      +We haven't fully defined it all yet, but some details about atomicity are +available in the Go Memory Model specification. +

      + +

      +Regarding mutexes, the sync +package implements them, but we hope Go programming style will +encourage people to try higher-level techniques. In particular, consider +structuring your program so that only one goroutine at a time is ever +responsible for a particular piece of data. +

      + +

      +Do not communicate by sharing memory. Instead, share memory by communicating. +

      + +

      +See the Share Memory By Communicating code walk and its associated article for a detailed discussion of this concept. +

      + +

      +Why doesn't my multi-goroutine program use multiple CPUs?

      + +

      +Under the gc compilers you must set GOMAXPROCS to allow the +runtime to utilise more than one OS thread. Under gccgo an OS +thread will be created for each goroutine, and GOMAXPROCS is +effectively equal to the number of running goroutines. +

      + +

      +Programs that perform concurrent computation should benefit from an increase in +GOMAXPROCS. (See the runtime package +documentation.) +

      + +

      +Why does using GOMAXPROCS > 1 sometimes make my program +slower?

      + +

      +(This is specific to the gc compilers. See above.) +

      + +

      +It depends on the nature of your program. +Programs that contain several goroutines that spend a lot of time +communicating on channels will experience performance degradation when using +multiple OS threads. This is because of the significant context-switching +penalty involved in sending data between threads. +

      + +

      +The Go runtime's scheduler is not as good as it needs to be. In future, it +should recognise such cases and optimize its use of OS threads. For now, +GOMAXPROCS should be set on a per-application basis. +

      + + +

      Functions and Methods

      + +

      +Why do T and *T have different method sets?

      + +

      +From the Go Spec: +

      + +
      +The method set of any other named type T consists of all methods +with receiver type T. The method set of the corresponding pointer +type *T is the set of all methods with receiver *T or +T (that is, it also contains the method set of T). +
      + +

      +If an interface value contains a pointer *T, +a method call can obtain a value by dereferencing the pointer, +but if an interface value contains a value T, +there is no useful way for a method call to obtain a pointer. +

      + +

      +If not for this restriction, this code: +

      + +
      +var buf bytes.Buffer
      +io.Copy(buf, os.Stdin)
      +
      + +

      +would copy standard input into a copy of buf, +not into buf itself. +This is almost never the desired behavior. +

      + +

      +Why am I confused by the way my closures behave as goroutines?

      + +

      +Some confusion may arise when using closures with concurrency. +Consider the following program: +

      + +
      +func main() {
      +	done := make(chan bool)
      +
      +	values = []string{ "a", "b", "c" }
      +	for _, v := range values {
      +		go func() {
      +			fmt.Println(v)
      +			done <- true
      +		}()
      +	}
      +
      +	// wait for all goroutines to complete before exiting
      +	for i := range values {
      +		<-done 
      +	}
      +}
      +
      + +

      +One might mistakenly expect to see a, b, c as the output. +What you'll probably see instead is c, c, c. This is because +each closure shares the same variable v. Each closure prints the +value of v at the time fmt.Println is executed, +rather than the value of v when the goroutine was launched. +

      + +

      +To bind the value of v to each closure as they are launched, one +could modify the inner loop to read: +

      + +
      +	for _, v := range values {
      +		go func(u) {
      +			fmt.Println(u)
      +			done <- true
      +		}(v)
      +	}
      +
      + +

      +In this example, the value of v is passed as an argument to the +anonymous function. That value is then accessible inside the function as +the variable u. +

      + +

      Control flow

      + +

      +Does Go have the ?: operator?

      + +

      +There is no ternary form in Go. You may use the following to achieve the same +result: +

      + +
      +if expr {
      +	n = trueVal
      +} else {
      +	n = falseVal
      +}
      +
      + +

      Packages and Testing

      + +

      +How do I create a multifile package?

      + +

      +Put all the source files for the package in a directory by themselves. +Source files can refer to items from different files at will; there is +no need for forward declarations or a header file. +

      + +

      +Other than being split into multiple files, the package will compile and test +just like a single-file package. +

      + +

      +How do I write a unit test?

      + +

      +Create a new file ending in _test.go in the same directory +as your package sources. Inside that file, import "testing" +and write functions of the form +

      + +
      +func TestFoo(t *testing.T) {
      +    ...
      +}
      +
      + +

      +Run gotest in that directory. +That script finds the Test functions, +builds a test binary, and runs it. +

      + +

      See the How to Write Go Code document for more details.

      +

      Implementation

      @@ -368,3 +962,143 @@ isn't fast enough yet (even if it were, taking care not to generate unnecessary garbage can have a huge effect).

      + +

      Changes from C

      + +

      +Why is the syntax so different from C?

      +

      +Other than declaration syntax, the differences are not major and stem +from two desires. First, the syntax should feel light, without too +many mandatory keywords, repetition, or arcana. Second, the language +has been designed to be easy to analyze +and can be parsed without a symbol table. This makes it much easier +to build tools such as debuggers, dependency analyzers, automated +documentation extractors, IDE plug-ins, and so on. C and its +descendants are notoriously difficult in this regard. +

      + +

      +Why are declarations backwards?

      +

      +They're only backwards if you're used to C. In C, the notion is that a +variable is declared like an expression denoting its type, which is a +nice idea, but the type and expression grammars don't mix very well and +the results can be confusing; consider function pointers. Go mostly +separates expression and type syntax and that simplifies things (using +prefix * for pointers is an exception that proves the rule). In C, +the declaration +

      +
      +	int* a, b;
      +
      +

      +declares a to be a pointer but not b; in Go +

      +
      +	var a, b *int;
      +
      +

      +declares both to be pointers. This is clearer and more regular. +Also, the := short declaration form argues that a full variable +declaration should present the same order as := so +

      +
      +	var a uint64 = 1;
      +
      +has the same effect as +
      +	a := uint64(1);
      +
      +

      +Parsing is also simplified by having a distinct grammar for types that +is not just the expression grammar; keywords such as func +and chan keep things clear. +

      + +

      +See the Go's Declaration Syntax article for more details. +

      + +

      +Why is there no pointer arithmetic?

      +

      +Safety. Without pointer arithmetic it's possible to create a +language that can never derive an illegal address that succeeds +incorrectly. Compiler and hardware technology have advanced to the +point where a loop using array indices can be as efficient as a loop +using pointer arithmetic. Also, the lack of pointer arithmetic can +simplify the implementation of the garbage collector. +

      + +

      +Why are ++ and -- statements and not expressions? And why postfix, not prefix?

      +

      +Without pointer arithmetic, the convenience value of pre- and postfix +increment operators drops. By removing them from the expression +hierarchy altogether, expression syntax is simplified and the messy +issues around order of evaluation of ++ and -- +(consider f(i++) and p[i] = q[++i]) +are eliminated as well. The simplification is +significant. As for postfix vs. prefix, either would work fine but +the postfix version is more traditional; insistence on prefix arose +with the STL, a library for a language whose name contains, ironically, a +postfix increment. +

      + +

      +Why are there braces but no semicolons? And why can't I put the opening +brace on the next line?

      +

      +Go uses brace brackets for statement grouping, a syntax familiar to +programmers who have worked with any language in the C family. +Semicolons, however, are for parsers, not for people, and we wanted to +eliminate them as much as possible. To achieve this goal, Go borrows +a trick from BCPL: the semicolons that separate statements are in the +formal grammar but are injected automatically, without lookahead, by +the lexer at the end of any line that could be the end of a statement. +This works very well in practice but has the effect that it forces a +brace style. For instance, the opening brace of a function cannot +appear on a line by itself. +

      +

      +Some have argued that the lexer should do lookahead to permit the +brace to live on the next line. We disagree. Since Go code is meant +to be formatted automatically by +gofmt, +some style must be chosen. That style may differ from what +you've used in C or Java, but Go is a new language and +gofmt's style is as good as any other. More +important—much more important—the advantages of a single, +programmatically mandated format for all Go programs greatly outweigh +any perceived disadvantages of the particular style. +Note too that Go's style means that an interactive implementation of +Go can use the standard syntax one line at a time without special rules. +

      + +

      +Why do garbage collection? Won't it be too expensive?

      +

      +One of the biggest sources of bookkeeping in systems programs is +memory management. We feel it's critical to eliminate that +programmer overhead, and advances in garbage collection +technology in the last few years give us confidence that we can +implement it with low enough overhead and no significant +latency. (The current implementation is a plain mark-and-sweep +collector but a replacement is in the works.) +

      +

      +Another point is that a large part of the difficulty of concurrent +and multi-threaded programming is memory management; +as objects get passed among threads it becomes cumbersome +to guarantee they become freed safely. +Automatic garbage collection makes concurrent code far easier to write. +Of course, implementing garbage collection in a concurrent environment is +itself a challenge, but meeting it once rather than in every +program helps everyone. +

      +

      +Finally, concurrency aside, garbage collection makes interfaces +simpler because they don't need to specify how memory is managed across them. +

      + diff --git a/doc/go_for_cpp_programmers.html b/doc/go_for_cpp_programmers.html index a2291715c..fae2ec44e 100644 --- a/doc/go_for_cpp_programmers.html +++ b/doc/go_for_cpp_programmers.html @@ -198,7 +198,7 @@ the else, causing a syntax error. Since semicolons do end statements, you may continue using them as in C++. However, that is not the recommended style. Idiomatic Go code omits unnecessary semicolons, which in practice is all of them other -than the initial loop clause and cases where you want several +than the initial for loop clause and cases where you want several short statements on a single line.

      @@ -668,7 +668,7 @@ func manager(ch chan cmd) { var val int = 0 for { c := <- ch - if c.get { c.val = val ch <- c } + if c.get { c.val = val; ch <- c } else { val = c.val } } } diff --git a/doc/go_lang_faq.html b/doc/go_lang_faq.html deleted file mode 100644 index b8deb1534..000000000 --- a/doc/go_lang_faq.html +++ /dev/null @@ -1,491 +0,0 @@ - - -

      Origins

      - -

      -What is the history of the project?

      -

      -Robert Griesemer, Rob Pike and Ken Thompson started sketching the -goals for a new language on the white board on September 21, 2007. -Within a few days the goals had settled into a plan to do something -and a fair idea of what it would be. Design continued part-time in -parallel with unrelated work. By January 2008, Ken had started work -on a compiler with which to explore ideas; it generated C code as its -output. By mid-year the language had become a full-time project and -had settled enough to attempt a production compiler. In May 2008, -Ian Taylor independently started on a GCC front end for Go using the -draft specification. Russ Cox joined in late 2008 and helped move the language -and libraries from prototype to reality. -

      - -

      -Many others have contributed ideas, discussions, and code. -

      - -

      -Why are you creating a new language?

      -

      -Go was born out of frustration with existing languages and -environments for systems programming. Programming had become too -difficult and the choice of languages was partly to blame. One had to -choose either efficient compilation, efficient execution, or ease of -programming; all three were not available in the same mainstream -language. Programmers who could were choosing ease over -safety and efficiency by moving to dynamically typed languages such as -Python and JavaScript rather than C++ or, to a lesser extent, Java. -

      -

      -Go is an attempt to combine the ease of programming of an interpreted, -dynamically typed -language with the efficiency and safety of a statically typed, compiled language. -It also aims to be modern, with support for networked and multicore -computing. Finally, it is intended to be fast: it should take -at most a few seconds to build a large executable on a single computer. -To meet these goals required addressing a number of -linguistic issues: an expressive but lightweight type system; -concurrency and garbage collection; rigid dependency specification; -and so on. These cannot be addressed well by libraries or tools; a new -language was called for. -

      - - -

      -What are Go's ancestors?

      -

      -Go is mostly in the C family (basic syntax), -with significant input from the Pascal/Modula/Oberon -family (declarations, packages), -plus some ideas from languages -inspired by Tony Hoare's CSP, -such as Newsqueak and Limbo (concurrency). -However, it is a new language across the board. -In every respect the language was designed by thinking -about what programmers do and how to make programming, at least the -kind of programming we do, more effective, which means more fun. -

      - -

      -What are the guiding principles in the design?

      -

      -Programming today involves too much bookkeeping, repetition, and -clerical work. As Dick Gabriel says, “Old programs read -like quiet conversations between a well-spoken research worker and a -well-studied mechanical colleague, not as a debate with a compiler. -Who'd have guessed sophistication bought such noise?” -The sophistication is worthwhile—no one wants to go back to -the old languages—but can it be more quietly achieved? -

      -

      -Go attempts to reduce the amount of typing in both senses of the word. -Throughout its design, we have tried to reduce clutter and -complexity. There are no forward declarations and no header files; -everything is declared exactly once. Initialization is expressive, -automatic, and easy to use. Syntax is clean and light on keywords. -Stuttering (foo.Foo* myFoo = new(foo.Foo)) is reduced by -simple type derivation using the := -declare-and-initialize construct. And perhaps most radically, there -is no type hierarchy: types just are, they don't have to -announce their relationships. These simplifications allow Go to be -expressive yet comprehensible without sacrificing, well, sophistication. -

      -

      -Another important principle is to keep the concepts orthogonal. -Methods can be implemented for any type; structures represent data while -interfaces represent abstraction; and so on. Orthogonality makes it -easier to understand what happens when things combine. -

      - - -

      Changes from C

      - -

      -Why is the syntax so different from C?

      -

      -Other than declaration syntax, the differences are not major and stem -from two desires. First, the syntax should feel light, without too -many mandatory keywords, repetition, or arcana. Second, the language -has been designed to be easy to analyze -and can be parsed without a symbol table. This makes it much easier -to build tools such as debuggers, dependency analyzers, automated -documentation extractors, IDE plug-ins, and so on. C and its -descendants are notoriously difficult in this regard. -

      - -

      -Why are declarations backwards?

      -

      -They're only backwards if you're used to C. In C, the notion is that a -variable is declared like an expression denoting its type, which is a -nice idea, but the type and expression grammars don't mix very well and -the results can be confusing; consider function pointers. Go mostly -separates expression and type syntax and that simplifies things (using -prefix * for pointers is an exception that proves the rule). In C, -the declaration -

      -
      -	int* a, b;
      -
      -

      -declares a to be a pointer but not b; in Go -

      -
      -	var a, b *int;
      -
      -

      -declares both to be pointers. This is clearer and more regular. -Also, the := short declaration form argues that a full variable -declaration should present the same order as := so -

      -
      -	var a uint64 = 1;
      -
      -has the same effect as -
      -	a := uint64(1);
      -
      -

      -Parsing is also simplified by having a distinct grammar for types that -is not just the expression grammar; keywords such as func -and chan keep things clear. -

      - -

      -Why is there no pointer arithmetic?

      -

      -Safety. Without pointer arithmetic it's possible to create a -language that can never derive an illegal address that succeeds -incorrectly. Compiler and hardware technology have advanced to the -point where a loop using array indices can be as efficient as a loop -using pointer arithmetic. Also, the lack of pointer arithmetic can -simplify the implementation of the garbage collector. -

      - -

      -Why are ++ and -- statements and not expressions? And why postfix, not prefix?

      -

      -Without pointer arithmetic, the convenience value of pre- and postfix -increment operators drops. By removing them from the expression -hierarchy altogether, expression syntax is simplified and the messy -issues around order of evaluation of ++ and -- -(consider f(i++) and p[i] = q[++i]) -are eliminated as well. The simplification is -significant. As for postfix vs. prefix, either would work fine but -the postfix version is more traditional; insistence on prefix arose -with the STL, a library for a language whose name contains, ironically, a -postfix increment. -

      - -

      -Why are there braces but no semicolons? And why can't I put the opening -brace on the next line?

      -

      -Go uses brace brackets for statement grouping, a syntax familiar to -programmers who have worked with any language in the C family. -Semicolons, however, are for parsers, not for people, and we wanted to -eliminate them as much as possible. To achieve this goal, Go borrows -a trick from BCPL: the semicolons that separate statements are in the -formal grammar but are injected automatically, without lookahead, by -the lexer at the end of any line that could be the end of a statement. -This works very well in practice but has the effect that it forces a -brace style. For instance, the opening brace of a function cannot -appear on a line by itself. -

      -

      -Some have argued that the lexer should do lookahead to permit the -brace to live on the next line. We disagree. Since Go code is meant -to be formatted automatically by -gofmt, -some style must be chosen. That style may differ from what -you've used in C or Java, but Go is a new language and -gofmt's style is as good as any other. More -important—much more important—the advantages of a single, -programmatically mandated format for all Go programs greatly outweigh -any perceived disadvantages of the particular style. -Note too that Go's style means that an interactive implementation of -Go can use the standard syntax one line at a time without special rules. -

      - -

      -Why do garbage collection? Won't it be too expensive?

      -

      -One of the biggest sources of bookkeeping in systems programs is -memory management. We feel it's critical to eliminate that -programmer overhead, and advances in garbage collection -technology in the last few years give us confidence that we can -implement it with low enough overhead and no significant -latency. (The current implementation is a plain mark-and-sweep -collector but a replacement is in the works.) -

      -

      -Another point is that a large part of the difficulty of concurrent -and multi-threaded programming is memory management; -as objects get passed among threads it becomes cumbersome -to guarantee they become freed safely. -Automatic garbage collection makes concurrent code far easier to write. -Of course, implementing garbage collection in a concurrent environment is -itself a challenge, but meeting it once rather than in every -program helps everyone. -

      -

      -Finally, concurrency aside, garbage collection makes interfaces -simpler because they don't need to specify how memory is managed across them. -

      - -

      What's up with Unicode identifiers?

      - -

      -It was important to us to extend the space of identifiers from the -confines of ASCII. Go's rule—identifier characters must be -letters or digits as defined by Unicode—is simple to understand -and to implement but has restrictions. Combining characters are -excluded by design, for instance. -Until there -is an agreed external definition of what an identifier might be, -plus a definition of canonicalization of identifiers that guarantees -no ambiguity, it seemed better to keep combining characters out of -the mix. Thus we have a simple rule that can be expanded later -without breaking programs, one that avoids bugs that would surely arise -from a rule that admits ambiguous identifiers. -

      - -

      -On a related note, since an exported identifier must begin with an -upper-case letter, identifiers created from “letters” -in some languages can, by definition, not be exported. For now the -only solution is to use something like X日本語, which -is clearly unsatisfactory; we are considering other options. The -case-for-visibility rule is unlikely to change however; it's one -of our favorite features of Go. -

      - -

      Absent features

      - -

      -Why does Go not have generic types?

      -

      -Generics may well be added at some point. We don't feel an urgency for -them, although we understand some programmers do. -

      -

      -Generics are convenient but they come at a cost in -complexity in the type system and run-time. We haven't yet found a -design that gives value proportionate to the complexity, although we -continue to think about it. Meanwhile, Go's built-in maps and slices, -plus the ability to use the empty interface to construct containers -(with explicit unboxing) mean in many cases it is possible to write -code that does what generics would enable, if less smoothly. -

      -

      -This remains an open issue. -

      - -

      -Why does Go not have exceptions?

      -

      -We believe that coupling exceptions to a control -structure, as in the try-catch-finally idiom, results in -convoluted code. It also tends to encourage programmers to label -too many ordinary errors, such as failing to open a file, as -exceptional. -

      -

      -Go takes a different approach. Instead of exceptions, it has a couple -of built-in functions to signal and recover from truly exceptional -conditions. The recovery mechanism is executed only as part of a -function's state being torn down after an error, which is sufficient -to handle catastrophe but requires no extra control structures and, -when used well, can result in clean error-handling code. -

      - -

      -Why does Go not have assertions?

      -

      -This is answered in the general FAQ. -

      - -

      Types

      - -

      -Why is there no type inheritance?

      -

      -Object-oriented programming, at least in the best-known languages, -involves too much discussion of the relationships between types, -relationships that often could be derived automatically. Go takes a -different approach. -

      -

      -Rather than requiring the programmer to declare ahead of time that two -types are related, in Go a type automatically satisfies any interface -that specifies a subset of its methods. Besides reducing the -bookkeeping, this approach has real advantages. Types can satisfy -many interfaces at once, without the complexities of traditional -multiple inheritance. -Interfaces can be very lightweight—having one or even zero methods -in an interface can express useful concepts. -Interfaces can be added after the fact if a new idea comes along -or for testing—without annotating the original types. -Because there are no explicit relationships between types -and interfaces, there is no type hierarchy to manage or discuss. -

      -

      -It's possible to use these ideas to construct something analogous to -type-safe Unix pipes. For instance, see how fmt.Fprintf -enables formatted printing to any output, not just a file, or how the -bufio package can be completely separate from file I/O, -or how the crypto packages stitch together block and -stream ciphers. All these ideas stem from a single interface -(io.Writer) representing a single method -(Write). And that's only scratching the surface. -

      -

      -It takes some getting used to but this implicit style of type -dependency is one of the most exciting things about Go. -

      - -

      -Why is len a function and not a method?

      -

      -We debated this issue but decided -implementing len and friends as functions was fine in practice and -didn't complicate questions about the interface (in the Go type sense) -of basic types. -

      - -

      -Why does Go not support overloading of methods and operators?

      -

      -Method dispatch is simplified if it doesn't need to do type matching as well. -Experience with other languages told us that having a variety of -methods with the same name but different signatures was occasionally useful -but that it could also be confusing and fragile in practice. Matching only by name -and requiring consistency in the types was a major simplifying decision -in Go's type system. -

      -

      -Regarding operator overloading, it seems more a convenience than an absolute -requirement. Again, things are simpler without it. -

      - -

      Values

      - -

      -Why does Go not provide implicit numeric conversions?

      -

      -The convenience of automatic conversion between numeric types in C is -outweighed by the confusion it causes. When is an expression unsigned? -How big is the value? Does it overflow? Is the result portable, independent -of the machine on which it executes? -It also complicates the compiler; “the usual arithmetic conversions” -are not easy to implement and inconsistent across architectures. -For reasons of portability, we decided to make things clear and straightforward -at the cost of some explicit conversions in the code. -The definition of constants in Go—arbitrary precision values free -of signedness and size annotations—ameliorates matters considerably, -though. -

      -

      -A related detail is that, unlike in C, int and int64 -are distinct types even if int is a 64-bit type. The int -type is generic; if you care about how many bits an integer holds, Go -encourages you to be explicit. -

      - -

      -Why are maps built in?

      -

      -The same reason strings are: they are such a powerful and important data -structure that providing one excellent implementation with syntactic support -makes programming more pleasant. We believe that Go's implementation of maps -is strong enough that it will serve for the vast majority of uses. -If a specific application can benefit from a custom implementation, it's possible -to write one but it will not be as convenient syntactically; this seems a reasonable tradeoff. -

      - - -

      -Why don't maps allow structs and arrays as keys?

      -

      -Map lookup requires an equality operator, which structs and arrays do not implement. -They don't implement equality because equality is not well defined on such types; -there are multiple considerations involving shallow vs. deep comparison, pointer vs. -value comparison, how to deal with recursive structures, and so on. -We may revisit this issue—and implementing equality for structs and arrays -will not invalidate any existing programs—but without a clear idea of what -equality of structs and arrays should mean, it was simpler to leave it out for now. -

      - -

      -Why are maps, slices, and channels references while arrays are values?

      -

      -There's a lot of history on that topic. Early on, maps and channels -were syntactically pointers and it was impossible to declare or use a -non-pointer instance. Also, we struggled with how arrays should work. -Eventually we decided that the strict separation of pointers and -values made the language harder to use. Introducing reference types, -including slices to handle the reference form of arrays, resolved -these issues. Reference types add some regrettable complexity to the -language but they have a large effect on usability: Go became a more -productive, comfortable language when they were introduced. -

      - -

      Concurrency

      - -

      -Why build concurrency on the ideas of CSP?

      -

      -Concurrency and multi-threaded programming have a reputation -for difficulty. We believe the problem is due partly to complex -designs such as pthreads and partly to overemphasis on low-level details -such as mutexes, condition variables, and even memory barriers. -Higher-level interfaces enable much simpler code, even if there are still -mutexes and such under the covers. -

      -

      -One of the most successful models for providing high-level linguistic support -for concurrency comes from Hoare's Communicating Sequential Processes, or CSP. -Occam and Erlang are two well known languages that stem from CSP. -Go's concurrency primitives derive from a different part of the family tree -whose main contribution is the powerful notion of channels as first class objects. -

      - -

      -Why goroutines instead of threads?

      -

      -Goroutines are part of making concurrency easy to use. The idea, which has -been around for a while, is to multiplex independently executing -functions—coroutines, really—onto a set of threads. -When a coroutine blocks, such as by calling a blocking system call, -the run-time automatically moves other coroutines on the same operating -system thread to a different, runnable thread so they won't be blocked. -The programmer sees none of this, which is the point. -The result, which we call goroutines, can be very cheap: unless they spend a lot of time -in long-running system calls, they cost little more than the memory -for the stack. -

      -

      -To make the stacks small, Go's run-time uses segmented stacks. A newly -minted goroutine is given a few kilobytes, which is almost always enough. -When it isn't, the run-time allocates (and frees) extension segments automatically. -The overhead averages about three cheap instructions per function call. -It is practical to create hundreds of thousands of goroutines in the same -address space. If goroutines were just threads, system resources would -run out at a much smaller number. -

      - -

      -Why are map operations not defined to be atomic?

      - -

      -After long discussion it was decided that the typical use of maps did not require -safe access from multiple threads, and in those cases where it did, the map was -probably part of some larger data structure or computation that was already -synchronized. Therefore requiring that all map operations grab a mutex would slow -down most programs and add safety to few. This was not an easy decision, -however, since it means uncontrolled map access can crash the program. -

      - -

      -The language does not preclude atomic map updates. When required, such -as when hosting an untrusted program, the implementation could interlock -map access. -

      diff --git a/doc/go_learning.html b/doc/go_learning.html deleted file mode 100644 index 072d047f0..000000000 --- a/doc/go_learning.html +++ /dev/null @@ -1,141 +0,0 @@ - - - -

      Recommendations

      -

      -If you're new to Go, we recommend following the -tutorial while consulting the -language spec. -Then read Effective Go, as it addresses many -common beginner questions. -

      - -

      Reference Materials

      -

      Keep these under your pillow.

      - -

      Package Documentation

      -

      -The built-in documentation for the Go standard library. -

      - -

      Command Documentation

      -

      -The built-in documentation for the Go tools. -

      - -

      Language Specification

      -

      -The official Go Language specification. -

      - -

      The Go Memory Model

      -

      -A document that specifies the conditions under which reads of a variable in -one goroutine can be guaranteed to observe values produced by writes to the -same variable in a different goroutine. -

      - -

      Tutorials

      - -

      A Tutorial for the Go Programming Language

      -

      -The first tutorial. An introductory text that touches upon several core -concepts: syntax, types, allocation, constants, I/O, sorting, printing, -goroutines, and channels. -

      - -

      Effective Go

      -

      -A document that gives tips for writing clear, idiomatic Go code. -A must read for any new Go programmer. It augments the tutorial and -the language spec, both of which should be read first. -

      - -

      Codelab: Writing Web Applications

      -

      -This codelab takes the reader through the creation of a simple wiki web -application. It touches on structs, methods, file I/O, http, regular expressions, -and closures. -

      - -

      Go for C++ Programmers

      -

      -An introduction to Go for C++ programmers. -

      - -

      How to write Go code

      -

      -How to write a new package and how to test code. -

      - -

      Frequently Asked Questions

      - -

      Go FAQ

      -

      -Answers to common questions about Go. -

      - -

      Language Design FAQ

      -

      -Answers to common questions about the design decisions behind Go. -

      - -

      Programming FAQ

      -

      -Answers to common questions about programming with Go. -

      - -

      Development

      - -

      Contributing to the Go project

      -

      -How to contribute changes to the Go project. -

      - -

      Roadmap

      -

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

      - -

      Release History

      -

      -A summarization of the changes between tagged releases of Go. -

      - -

      Videos

      - -

      The Go Tech Talk

      -

      -An hour-long talk delivered by Rob Pike at Google in October 2009. -The language's first public introduction. (See the slides in PDF format.) The language has changed since it was made, -but it's still a good introduction. -

      - - -

      gocoding YouTube Channel

      -

      -A YouTube channel that includes screencasts and other Go-related videos: -

      - - -

      The Go Promo Video

      -

      -A short promotional video featuring Russ Cox demonstrating Go's fast compiler. -

      - -

      Blog Posts

      -

      -Articles about Go from external blogs. -

      - -

      Go articles at research!rsc

      -

      -Posts labelled 'Go' by Russ Cox, one of the core Go developers. -

      - -

      Programming articles at Airs

      -

      -Posts labelled 'Programming' by Ian Lance Taylor, one of the core Go developers. -

      diff --git a/doc/go_mem.html b/doc/go_mem.html index 33bce5f7a..da45a07d7 100644 --- a/doc/go_mem.html +++ b/doc/go_mem.html @@ -143,6 +143,35 @@ calling hello will print "hello, world" at some point in the future (perhaps after hello has returned).

      +

      Goroutine destruction

      + +

      +The exit of a goroutine is not guaranteed to happen before +any event in the program. For example, in this program: +

      + +
      +var a string
      +
      +func hello() {
      +	go func() { a = "hello" }()
      +	print(a)
      +}
      +
      + +

      +the assignment to a is not followed by +any synchronization event, so it is not guaranteed to be +observed by any other goroutine. +In fact, an aggressive compiler might delete the entire go statement. +

      + +

      +If the effects of a goroutine must be observed by another goroutine, +use a synchronization mechanism such as a lock or channel +communication to establish a relative ordering. +

      +

      Channel communication

      @@ -276,8 +305,9 @@ before the n+1'th call to l.Lock.

      Once

      -The once package provides a safe mechanism for -initialization in the presence of multiple goroutines. +The sync package provides a safe mechanism for +initialization in the presence of multiple goroutines +through the use of the Once type. Multiple threads can execute once.Do(f) for a particular f, but only one will run f(), and the other calls block until f() has returned. @@ -293,6 +323,7 @@ In this program:

       var a string
      +var once sync.Once
       
       func setup() {
       	a = "hello, world"
      diff --git a/doc/go_programming_faq.html b/doc/go_programming_faq.html
      deleted file mode 100644
      index 3c4f0e1ba..000000000
      --- a/doc/go_programming_faq.html
      +++ /dev/null
      @@ -1,307 +0,0 @@
      -
      -
      -

      Pointers and Allocation

      - -

      -When are function paramters passed by value?

      - -

      -Everything in Go is passed by value. A function always gets a copy of the -thing being passed, as if there were an assignment statement assigning the -value to the parameter. For instance, copying a pointer value makes a copy of -the pointer, not the data it points to. -

      - -

      -Map and slice values behave like pointers; they are descriptors that -contain pointers to the underlying map or slice data. Copying a map or -slice value doesn't copy the data it points to. Copying an interface value -makes a copy of the thing stored in the interface value. If the interface -value holds a struct, copying the interface value makes a copy of the -struct. If the interface value holds a pointer, copying the interface value -makes a copy of the pointer, but again not the data it points to. -

      - -

      -Should I define methods on values or pointers?

      - -
      -func (s *MyStruct) someMethod() { } // method on pointer
      -func (s MyStruct) someMethod() { }  // method on value
      -
      - -

      -When defining a method on a type, the receiver (s in the above -example) behaves exactly is if it were an argument to the method. Define the -method on a pointer type if you need the method to modify the data the receiver -points to. Otherwise, it is often cleaner to define the method on a value type. -

      - -

      -What's the difference between new and make?

      - -

      -In short: new allocates memory, make initializes -the slice, map, and channel types. -

      - -

      -See the relevant section -of Effective Go for more details. -

      - -

      -Why is int 32 bits on 64 bit machines?

      - -

      -The size of int and float is implementation-specific. -The 64 bit Go compilers (both 6g and gccgo) use a 32 bit representation for -both int and float. Code that relies on a particular -size of value should use an explicitly sized type, like int64 or -float64. -

      - -

      Concurrent programming

      - -

      -What operations are atomic? What about mutexes?

      - -

      -We haven't fully defined it all yet, but some details about atomicity are -available in the Go Memory Model specification. -Also, some concurrency questions are answered in more detail in the language design FAQ. -

      - -

      -Regarding mutexes, the sync -package implements them, but we hope Go programming style will -encourage people to try higher-level techniques. In particular, consider -structuring your program so that only one goroutine at a time is ever -responsible for a particular piece of data. -

      - -

      -Do not communicate by sharing memory. Instead, share memory by communicating. -

      - -

      -Why doesn't my multi-goroutine program use multiple CPUs?

      - -

      -Under the gc compilers you must set GOMAXPROCS to allow the -runtime to utilise more than one OS thread. Under gccgo an OS -thread will be created for each goroutine, and GOMAXPROCS is -effectively equal to the number of running goroutines. -

      - -

      -Programs that perform concurrent computation should benefit from an increase in -GOMAXPROCS. (See the runtime package -documentation.) -

      - -

      -Why does using GOMAXPROCS > 1 sometimes make my program -slower?

      - -

      -(This is specific to the gc compilers. See above.) -

      - -

      -It depends on the nature of your program. -Programs that contain several goroutines that spend a lot of time -communicating on channels will experience performance degradation when using -multiple OS threads. This is because of the significant context-switching -penalty involved in sending data between threads. -

      - -

      -The Go runtime's scheduler is not as good as it needs to be. In future, it -should recognise such cases and optimize its use of OS threads. For now, -GOMAXPROCS should be set on a per-application basis. -

      - - -

      Functions and Methods

      - -

      -Why do T and *T have different method sets?

      - -

      -From the Go Spec: -

      - -
      -The method set of any other named type T consists of all methods -with receiver type T. The method set of the corresponding pointer -type *T is the set of all methods with receiver *T or -T (that is, it also contains the method set of T). -
      - -

      -If an interface value contains a pointer *T, -a method call can obtain a value by dereferencing the pointer, -but if an interface value contains a value T, -there is no useful way for a method call to obtain a pointer. -

      - -

      -If not for this restriction, this code: -

      - -
      -var buf bytes.Buffer
      -io.Copy(buf, os.Stdin)
      -
      - -

      -would copy standard input into a copy of buf, -not into buf itself. -This is almost never the desired behavior. -

      - -

      -Why am I confused by the way my closures behave as goroutines?

      - -

      -Some confusion may arise when using closures with concurrency. -Consider the following program: -

      - -
      -func main() {
      -	done := make(chan bool)
      -
      -	values = []string{ "a", "b", "c" }
      -	for _, v := range values {
      -		go func() {
      -			fmt.Println(v)
      -			done <- true
      -		}()
      -	}
      -
      -	// wait for all goroutines to complete before exiting
      -	for i := range values {
      -		<-done 
      -	}
      -}
      -
      - -

      -One might mistakenly expect to see a, b, c as the output. -What you'll probably see instead is c, c, c. This is because -each closure shares the same variable v. Each closure prints the -value of v at the time fmt.Println is executed, -rather than the value of v when the goroutine was launched. -

      - -

      -To bind the value of v to each closure as they are launched, one -could modify the inner loop to read: -

      - -
      -	for _, v := range values {
      -		go func(u) {
      -			fmt.Println(u)
      -			done <- true
      -		}(v)
      -	}
      -
      - -

      -In this example, the value of v is passed as an argument to the -anonymous function. That value is then accessible inside the function as -the variable u. -

      - -

      Control flow

      - -

      -Does Go have the ?: operator?

      - -

      -There is no ternary form in Go. You may use the following to achieve the same -result: -

      - -
      -if expr {
      -	n = trueVal
      -} else {
      -	n = falseVal
      -}
      -
      - -

      Packages and Testing

      - -

      -How do I create a multifile package?

      - -

      -Put all the source files for the package in a directory by themselves. -Source files can refer to items from different files at will; there is -no need for forward declarations or a header file. -

      - -

      -Other than being split into multiple files, the package will compile and test -just like a single-file package. -

      - -

      -How do I write a unit test?

      - -

      -Create a new file ending in _test.go in the same directory -as your package sources. Inside that file, import "testing" -and write functions of the form -

      - -
      -func TestFoo(t *testing.T) {
      -    ...
      -}
      -
      - -

      -Run gotest in that directory. -That script finds the Test functions, -builds a test binary, and runs it. -

      - - -

      Data Structures

      - -

      Why does the syntax for nested array literals seem overly verbose?

      - -

      -In Go, you must specify a 2-dimensional array literal like this: -

      - -
      -var intArray = [4][4]int{
      -	[4]int{1, 2, 3, 4},
      -	[4]int{2, 4, 8, 16},
      -	[4]int{3, 9, 27, 81},
      -	[4]int{4, 16, 64, 256},
      -}
      -
      - -

      -It seems that the [4]int could be inferred, but in general it's -hard to get this sort of thing right. -

      - -

      -Some of Go's designers had worked on other languages that derived types -automatically in such expressions, but the special cases that arise can -be messy, especially when interfaces, nil, constant conversions, and -such are involved. It seemed better to require the full type -information. That way there will be no surprises. -

      - diff --git a/doc/go_spec.html b/doc/go_spec.html index f296c2a38..e1c7e90e2 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,5 +1,5 @@ - + @@ -139,7 +138,7 @@ There are two forms of comments:
      1. Line comments start with the character sequence // -and continue through the next newline. A line comment acts like a newline. +and stop at the end of the line. A line comment acts like a newline.
      2. General comments start with the character sequence /* @@ -163,7 +162,7 @@ and delimiters, and literals. White space, formed from spaces (U+0020), horizontal tabs (U+0009), carriage returns (U+000D), and newlines (U+000A), is ignored except as it separates tokens -that would otherwise combine into a single token. Also, a newline +that would otherwise combine into a single token. Also, a newline or end of file may trigger the insertion of a semicolon. While breaking the input into tokens, the next token is the longest sequence of characters that form a @@ -527,9 +526,10 @@ A constant value is represented by an string literal, an identifier denoting a constant, a constant expression, or -the result value of some built-in functions such as unsafe.Sizeof -and cap or len applied to an array, -len applied to a string constant, +the result value of some built-in functions such as +unsafe.Sizeof applied to any value, +cap or len applied to +some expressions, real and imag applied to a complex constant and cmplx applied to numeric constants. The boolean truth values are represented by the predeclared constants @@ -590,13 +590,13 @@ for floating-point values, both the mantissa and exponent must be twice as large

        A type determines the set of values and operations specific to values of that type. A type may be specified by a (possibly qualified) type name -(§Qualified identifier, §Type declarations) or a type literal, +(§Qualified identifier, §Type declarations) or a type literal, which composes a new type from previously declared types.

         Type      = TypeName | TypeLit | "(" Type ")" .
        -TypeName  = QualifiedIdent.
        +TypeName  = QualifiedIdent .
         TypeLit   = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
         	    SliceType | MapType | ChannelType .
         
        @@ -754,8 +754,8 @@ ElementType = Type . The length is part of the array's type and must be a constant expression that evaluates to a non-negative integer value. The length of array a can be discovered -using the built-in function len(a), which is a -compile-time constant. The elements can be indexed by integer +using the built-in function len(a). +The elements can be indexed by integer indices 0 through the len(a)-1Indexes). Array types are always one-dimensional but may be composed to form multi-dimensional types. @@ -785,7 +785,7 @@ SliceType = "[" "]" ElementType .

        Like arrays, slices are indexable and have a length. The length of a slice s can be discovered by the built-in function -len(s); unlike with arrays it may change during +len(s); unlike with arrays it may change during execution. The elements can be addressed by integer indices 0 through len(s)-1Indexes). The slice index of a given element may be less than the index of the same element in the @@ -804,18 +804,14 @@ the length of the slice and the length of the array beyond the slice; a slice of length up to that capacity can be created by `slicing' a new one from the original slice (§Slices). The capacity of a slice a can be discovered using the -built-in function cap(a) and the relationship between -len() and cap() is: +built-in function cap(a).

        -
        -0 <= len(a) <= cap(a)
        -
        -

        -The length and capacity of a nil slice -are 0. A new, initialized slice value for a given element type T is -made using the built-in function make, which takes a slice type +A new, initialized slice value for a given element type T is +made using the built-in function +make, +which takes a slice type and parameters specifying the length and optionally the capacity:

        @@ -883,9 +879,10 @@ struct {

      -A field declared with a type but no explicit field name is an anonymous field. +A field declared with a type but no explicit field name is an anonymous field +(colloquially called an embedded field). Such a field type must be specified as -a type name T or as a pointer to a type name *T, +a type name T or as a pointer to a non-interface type name *T, and T itself may not be a pointer type. The unqualified type name acts as the field name.

      @@ -959,7 +956,7 @@ struct {

      A pointer type denotes the set of all pointers to variables of a given type, called the base type of the pointer. -The value of an unitialized pointer is nil. +The value of an uninitialized pointer is nil.

      @@ -976,7 +973,7 @@ BaseType = Type .
       
       

      A function type denotes the set of all functions with the same parameter -and result types. The value of an unitialized variable of function type +and result types. The value of an uninitialized variable of function type is nil.

      @@ -995,15 +992,14 @@ must either all be present or all be absent. If present, each name stands for one item (parameter or result) of the specified type; if absent, each type stands for one item of that type. Parameter and result lists are always parenthesized except that if there is exactly -one unnamed result it may written as an unparenthesized type. +one unnamed result it may be written as an unparenthesized type.

      -

      -If the function's last parameter has a type prefixed with ..., -the function may be invoked with zero or more arguments for that parameter, -each of which must be assignable -to the type that follows the .... -Such a function is called variadic. +

      +The final parameter in a function signature may have +a type prefixed with .... +A function with such a parameter is called variadic and +may be invoked with zero or more arguments for that parameter.

      @@ -1026,7 +1022,7 @@ An interface type specifies a method set called its inte
       A variable of interface type can store a value of any type with a method set
       that is any superset of the interface. Such a type is said to
       implement the interface.
      -The value of an unitialized variable of interface type is nil.
      +The value of an uninitialized variable of interface type is nil.
       

      @@ -1155,16 +1151,16 @@ map [string] interface {}
       

      -The number of elements is called the length and is never negative. -The length of a map m can be discovered using the -built-in function len(m) and may change during execution. -Values may be added and removed +The number of map elements is called its length. +For a map m, it can be discovered using the +built-in function len(m) +and may change during execution. Values may be added and removed during execution using special forms of assignment.

      A new, empty map value is made using the built-in -function make, which takes the map type and an optional -capacity hint as arguments: +function make, +which takes the map type and an optional capacity hint as arguments:

      @@ -1331,9 +1327,9 @@ A value x is assignable to a variable of type T
       x's type is identical to T.
       
    • -x's type V or T have identical -underlying types and V or T -is not a named type. +x's type V and T have identical +underlying types and at least one of V +or T is not a named type.
    • T is an interface type and @@ -1342,7 +1338,7 @@ is not a named type.
    • x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, -and V or T is not a named type. +and at least one of V or T is not a named type.
    • x is the predeclared identifier nil and T @@ -1496,8 +1492,8 @@ Zero value: nil Functions: - cap close closed cmplx copy imag len make - new panic print println real + append cap close closed cmplx copy imag len + make new panic print println real recover
    @@ -1665,7 +1661,7 @@ TypeSpec = identifier Type . type IntArray [16]int type ( - Point struct { x, y float } + Point struct { x, y float64 } Polar Point ) @@ -1835,7 +1831,7 @@ A function declaration binds an identifier to a function (§ FunctionDecl = "func" identifier Signature [ Body ] . -Body = Block. +Body = Block .

    @@ -1882,13 +1878,13 @@ Given type Point, the declarations

    -func (p *Point) Length() float {
    -	return Math.sqrt(p.x * p.x + p.y * p.y)
    +func (p *Point) Length() float64 {
    +	return math.Sqrt(p.x * p.x + p.y * p.y)
     }
     
    -func (p *Point) Scale(factor float) {
    -	p.x = p.x * factor
    -	p.y = p.y * factor
    +func (p *Point) Scale(factor float64) {
    +	p.x *= factor
    +	p.y *= factor
     }
     
    @@ -1910,7 +1906,7 @@ argument. For instance, the method Scale has type

    -(p *Point, factor float)
    +func(p *Point, factor float64)
     

    @@ -1975,15 +1971,16 @@ a single expression or a key-value pair.

    -CompositeLit  = LiteralType "{" [ ElementList [ "," ] ] "}" .
    +CompositeLit  = LiteralType LiteralValue .
     LiteralType   = StructType | ArrayType | "[" "..." "]" ElementType |
    -                SliceType | MapType | TypeName | "(" LiteralType ")" .
    +                SliceType | MapType | TypeName .
    +LiteralValue  = "{" [ ElementList [ "," ] ] "}" .
     ElementList   = Element { "," Element } .
     Element       = [ Key ":" ] Value .
     Key           = FieldName | ElementIndex .
     FieldName     = identifier .
     ElementIndex  = Expression .
    -Value         = Expression .
    +Value         = Expression | LiteralValue .
     

    @@ -2028,8 +2025,8 @@ For struct literals the following rules apply: Given the declarations

    -type Point struct { x, y, z float }
    -type Line struct { p, q Point }
    +type Point3D struct { x, y, z float64 }
    +type Line struct { p, q Point3D }
     

    @@ -2037,8 +2034,8 @@ one may write

    -origin := Point{}                            // zero value for Point
    -line := Line{origin, Point{y: -4, z: 12.3}}  // zero value for line.q.x
    +origin := Point3D{}                            // zero value for Point3D
    +line := Line{origin, Point3D{y: -4, z: 12.3}}  // zero value for line.q.x
     

    @@ -2061,7 +2058,7 @@ Taking the address of a composite literal (§Addres generates a unique pointer to an instance of the literal's value.

    -var pointer *Point = &Point{y: 1000}
    +var pointer *Point3D = &Point3D{y: 1000}
     

    @@ -2097,12 +2094,24 @@ and is a shortcut for a slice operation applied to an array literal: [n]T{x1, x2, ... xn}[0 : n] +

    +Within a composite literal of array, slice, or map type T, +elements that are themselves composite literals may elide the respective +literal type if it is identical to the element type of T. +

    + +
    +[...]Point{{1.5, -3.5}, {0, 0}}  // same as [...]Point{Point{1.5, -3.5}, Point{0, 0}}
    +[][]int{{1, 2, 3}, {4, 5}}       // same as [][]int{[]int{1, 2, 3}, []int{4, 5}}
    +
    +

    A parsing ambiguity arises when a composite literal using the -TypeName form of the LiteralType appears in the condition of an +TypeName form of the LiteralType appears between the +keyword and the opening brace of the block of an "if", "for", or "switch" statement, because the braces surrounding the expressions in the literal are confused with those introducing -a block of statements. To resolve the ambiguity in this rare case, +the block of statements. To resolve the ambiguity in this rare case, the composite literal must appear within parentheses.

    @@ -2185,9 +2194,10 @@ PrimaryExpr = Selector = "." identifier . Index = "[" Expression "]" . -Slice = "[" Expression ":" [ Expression ] "]" . +Slice = "[" [ Expression ] ":" [ Expression ] "]" . TypeAssertion = "." "(" Type ")" . -Call = "(" [ ExpressionList [ "," ] ] ")" . +Call = "(" [ ArgumentList [ "," ] ] ")" . +ArgumentList = ExpressionList [ "..." ] . @@ -2200,7 +2210,7 @@ Point{1, 2} m["foo"] s[i : j + 1] obj.color -Math.sin +math.Sin f.p[i].x() @@ -2217,8 +2227,7 @@ x.f

    denotes the field or method f of the value denoted by x -(or of *x if -x is of pointer type). The identifier f +(or sometimes *x; see below). The identifier f is called the (field or method) selector; it must not be the blank identifier. The type of the expression is the type of f. @@ -2261,16 +2270,13 @@ In all other cases, x.f is illegal.

    -Selectors automatically dereference pointers. -If x is of pointer type, x.y -is shorthand for (*x).y; if y -is also of pointer type, x.y.z is shorthand +Selectors automatically dereference pointers to structs. +If x is a pointer to a struct, x.y +is shorthand for (*x).y; if the field y +is also a pointer to a struct, x.y.z is shorthand for (*(*x).y).z, and so on. -If *x is of pointer type, dereferencing -must be explicit; -only one level of automatic dereferencing is provided. -For an x of type T containing an -anonymous field declared as *A, +If x contains an anonymous field of type *A, +where A is also a struct type, x.f is a shortcut for (*x.A).f.

    @@ -2428,14 +2434,14 @@ For a string, array, or slice a, the primary expression

    -a[lo : hi]
    +a[low : high]
     

    -constructs a substring or slice. The index expressions lo and -hi select which elements appear in the result. The result has +constructs a substring or slice. The index expressions low and +high select which elements appear in the result. The result has indexes starting at 0 and length equal to -hi - lo. +high - low. After slicing the array a

    @@ -2455,19 +2461,28 @@ s[2] == 4

    -For convenience, the hi expression may be omitted; the notation -a[lo :] is shorthand for a[lo : len(a)]. -For arrays or strings, the indexes -lo and hi must satisfy -0 <= lo <= hi <= length; -for slices, the upper bound is the capacity rather than the length. +For convenience, any of the index expressions may be omitted. A missing low +index defaults to zero; a missing high index defaults to the length of the +sliced operand: +

    + +
    +a[2:]	// same a[2 : len(a)]
    +a[:3]   // same as a[0 : 3]
    +a[:]    // same as a[0 : len(a)]
    +
    + +

    +For arrays or strings, the indexes low and high must +satisfy 0 <= low <= high <= length; for +slices, the upper bound is the capacity rather than the length.

    If the sliced operand is a string or slice, the result of the slice operation is a string or slice of the same type. -If the sliced operand is an array, the result of the slice operation is a slice -with the same element type as the array. +If the sliced operand is an array, it must be addressable +and the result of the slice operation is a slice with the same element type as the array.

    @@ -2605,9 +2620,9 @@ then within the function the argument is equivalent to a parameter of type []T. At each call of f, the argument passed to the final parameter is a new slice of type []T whose successive elements are -the actual arguments. The length of the slice is therefore the -number of arguments bound to the final parameter and -may differ for each call site. +the actual arguments, which all must be assignable +to the type T. The length of the slice is therefore the number of +arguments bound to the final parameter and may differ for each call site.

    @@ -2619,20 +2634,31 @@ Greeting("hello:", "Joe", "Anna", "Eileen")

    -Within Greeting, who will have value -[]string{"Joe", "Anna", "Eileen") +within Greeting, who will have the value +[]string{"Joe", "Anna", "Eileen"}

    +

    +If the final argument is assignable to a slice type []T, it may be +passed unchanged as the value for a ...T parameter if the argument +is followed by .... In this case no new slice is created. +

    -As a special case, if a function passes its own ... parameter -as the ... argument in a call to another function with -a ... parameter of identical type, -the parameter is passed directly. In short, a formal ... -parameter is passed unchanged as an actual ... parameter provided the -types match. +Given the slice s and call

    +
    +s := []string{"James", "Jasmine"}
    +Greeting("goodbye:", s...)
    +
    + +

    +within Greeting, who will have the same value as s +with the same underlying array. +

    + +

    Operators

    @@ -2903,7 +2929,7 @@ and string values. The result of a comparison is defined as follows: String values are compared byte-wise (lexically).

  • - Boolean values are are equal if they are either both + Boolean values are equal if they are either both true or both false.
  • @@ -3065,6 +3091,12 @@ to false and x is set to the zero value for its type (§The zero value).

    +

    +Except in a communications clause of a select statement, +sending or receiving from a nil channel causes a +run-time panic. +

    + -09 func sum(a []int) int { // returns an int +09 func sum(a []int) int { // returns an int 10 s := 0 11 for i := 0; i < len(a); i++ { 12 s += a[i] @@ -321,32 +333,27 @@ Using slices one can write this function (from sum.go): 15 }

    -and invoke it like this: -

    -

     
    -19        s := sum(&[3]int{1,2,3})  // a slice of the array is passed to sum
    -
    -

    Note how the return type (int) is defined for sum() by stating it after the parameter list. -The expression [3]int{1,2,3}—a type followed by a -brace-bounded -expression—is a constructor for a value, in this case an array -of 3 ints. -Putting an & -in front gives us the address of a unique instance of the value. We pass the -pointer to sum() by (implicitly) promoting it to a slice. +

    +To call the function, we slice the array. This intricate call (we'll show +a simpler way in a moment) constructs +an array and slices it: +

    +

    +    s := sum([3]int{1,2,3}[:])
    +

    If you are creating a regular array but want the compiler to count the elements for you, use ... as the array size:

    -    s := sum(&[...]int{1,2,3})
    +    s := sum([...]int{1,2,3}[:])
     

    -In practice, though, unless you're meticulous about storage layout within a -data structure, a slice itself—using empty brackets and no -&—is all you need: +That's fussier than necessary, though. +In practice, unless you're meticulous about storage layout within a +data structure, a slice itself—using empty brackets with no size—is all you need:

         s := sum([]int{1,2,3})
    @@ -470,8 +477,8 @@ sort of open/close/read/write interface.  Here's the start of file.go
     12    type File struct {
    -13        fd      int    // file descriptor number
    -14        name    string // file name at Open time
    +13        fd   int    // file descriptor number
    +14        name string // file name at Open time
     15    }
     

    @@ -524,7 +531,7 @@ object. We could write return n

    -but for simple structures like File it's easier to return the address of a nonce +but for simple structures like File it's easier to return the address of a composite literal, as is done here on line 21.

    We can use the factory to construct some familiar, exported variables of type *File: @@ -541,7 +548,7 @@ The newFile function was not exported because it's internal. The pr exported factory to use is Open:

     
    -30    func Open(name string, mode int, perm int) (file *File, err os.Error) {
    +30    func Open(name string, mode int, perm uint32) (file *File, err os.Error) {
     31        r, e := syscall.Open(name, mode, perm)
     32        if e != 0 {
     33            err = os.Errno(e)
    @@ -581,7 +588,7 @@ each of which declares a receiver variable file.
     40            return os.EINVAL
     41        }
     42        e := syscall.Close(file.fd)
    -43        file.fd = -1  // so it can't be closed again
    +43        file.fd = -1 // so it can't be closed again
     44        if e != 0 {
     45            return os.Errno(e)
     46        }
    @@ -642,24 +649,30 @@ We can now use our new package:
     13    func main() {
     14        hello := []byte("hello, world\n")
     15        file.Stdout.Write(hello)
    -16        file, err := file.Open("/does/not/exist",  0,  0)
    -17        if file == nil {
    +16        f, err := file.Open("/does/not/exist",  0,  0)
    +17        if f == nil {
     18            fmt.Printf("can't open file; err=%s\n",  err.String())
     19            os.Exit(1)
     20        }
     21    }
     

    -The ''./'' in the import of ''./file'' tells the compiler to use our own package rather than +The ''./'' in the import of ''./file'' tells the compiler +to use our own package rather than something from the directory of installed packages. +(Also, ''file.go'' must be compiled before we can import the +package.)

    -Finally we can run the program: +Now we can compile and run the program:

    -    % helloworld3
    +    $ 6g file.go                       # compile file package
    +    $ 6g helloworld3.go                # compile main package
    +    $ 6l -o helloworld3 helloworld3.6  # link - no need to mention "file"
    +    $ helloworld3
         hello, world
         can't open file; err=No such file or directory
    -    %
    +    $
     

    Rotting cats

    @@ -681,11 +694,11 @@ Building on the file package, here's a simple version of the Unix u 15 const NBUF = 512 16 var buf [NBUF]byte 17 for { -18 switch nr, er := f.Read(&buf); true { +18 switch nr, er := f.Read(buf[:]); true { 19 case nr < 0: 20 fmt.Fprintf(os.Stderr, "cat: error reading from %s: %s\n", f.String(), er.String()) 21 os.Exit(1) -22 case nr == 0: // EOF +22 case nr == 0: // EOF 23 return 24 case nr > 0: 25 if nw, ew := file.Stdout.Write(buf[0:nr]); nw != nr { @@ -696,7 +709,7 @@ Building on the file package, here's a simple version of the Unix u 30 }

    32 func main() { -33 flag.Parse() // Scans the arg list and sets up flags +33 flag.Parse() // Scans the arg list and sets up flags 34 if flag.NArg() == 0 { 35 cat(file.Stdin) 36 } @@ -757,7 +770,7 @@ we have a second implementation of the reader interface.

     
     31    type rotate13 struct {
    -32        source    reader
    +32        source reader
     33    }
     

    35 func newRotate13(source reader) *rotate13 { @@ -797,11 +810,11 @@ and use it from within a mostly unchanged cat() function: 57 r = newRotate13(r) 58 } 59 for { -60 switch nr, er := r.Read(&buf); { +60 switch nr, er := r.Read(buf[:]); { 61 case nr < 0: 62 fmt.Fprintf(os.Stderr, "cat: error reading from %s: %s\n", r.String(), er.String()) 63 os.Exit(1) -64 case nr == 0: // EOF +64 case nr == 0: // EOF 65 return 66 case nr > 0: 67 nw, ew := file.Stdout.Write(buf[0:nr]) @@ -823,11 +836,11 @@ even though under the covers it holds a pointer to a struct. Here it is in action:

    -    % echo abcdefghijklmnopqrstuvwxyz | ./cat
    +    $ echo abcdefghijklmnopqrstuvwxyz | ./cat
         abcdefghijklmnopqrstuvwxyz
    -    % echo abcdefghijklmnopqrstuvwxyz | ./cat --rot13
    +    $ echo abcdefghijklmnopqrstuvwxyz | ./cat --rot13
         nopqrstuvwxyzabcdefghijklm
    -    %
    +    $
     

    Fans of dependency injection may take cheer from how easily interfaces diff --git a/doc/go_tutorial.txt b/doc/go_tutorial.txt index 6ab6094c0..9c08bd278 100644 --- a/doc/go_tutorial.txt +++ b/doc/go_tutorial.txt @@ -227,14 +227,15 @@ In Go, since arrays are values, it's meaningful (and useful) to talk about pointers to arrays. The size of the array is part of its type; however, one can declare -a slice variable, to which one can assign a pointer to -any array -with the same element type or—much more commonly—a slice -expression of the form "a[low : high]", representing -the subarray indexed by "low" through "high-1". -Slices look a lot like arrays but have +a slice variable to hold a reference to any array, of any size, +with the same element type. +A slice +expression has the form "a[low : high]", representing +the internal array indexed from "low" through "high-1"; the resulting +slice is indexed from "0" through "high-low-1". +In short, slices look a lot like arrays but with no explicit size ("[]" vs. "[10]") and they reference a segment of -an underlying, often anonymous, regular array. Multiple slices +an underlying, usually anonymous, regular array. Multiple slices can share data if they represent pieces of the same array; multiple arrays can never share data. @@ -243,39 +244,43 @@ regular arrays; they're more flexible, have reference semantics, and are efficient. What they lack is the precise control of storage layout of a regular array; if you want to have a hundred elements of an array stored within your structure, you should use a regular -array. +array. To create one, use a compound value constructor—an +expression formed +from a type followed by a brace-bounded expression like this: + + [3]int{1,2,3} + +In this case the constructor builds an array of 3 "ints". When passing an array to a function, you almost always want to declare the formal parameter to be a slice. When you call -the function, take the address of the array and Go will -create (efficiently) a slice reference and pass that. +the function, slice the array to create +(efficiently) a slice reference and pass that. +By default, the lower and upper bounds of a slice match the +ends of the existing object, so the concise notation "[:]" +will slice the whole array. Using slices one can write this function (from "sum.go"): --PROG progs/sum.go /sum/ /^}/ -and invoke it like this: - ---PROG progs/sum.go /1,2,3/ - Note how the return type ("int") is defined for "sum()" by stating it after the parameter list. -The expression "[3]int{1,2,3}"—a type followed by a -brace-bounded -expression—is a constructor for a value, in this case an array -of 3 "ints". -Putting an "&" -in front gives us the address of a unique instance of the value. We pass the -pointer to "sum()" by (implicitly) promoting it to a slice. + +To call the function, we slice the array. This intricate call (we'll show +a simpler way in a moment) constructs +an array and slices it: + + s := sum([3]int{1,2,3}[:]) If you are creating a regular array but want the compiler to count the elements for you, use "..." as the array size: - s := sum(&[...]int{1,2,3}) + s := sum([...]int{1,2,3}[:]) -In practice, though, unless you're meticulous about storage layout within a -data structure, a slice itself—using empty brackets and no -"&"—is all you need: +That's fussier than necessary, though. +In practice, unless you're meticulous about storage layout within a +data structure, a slice itself—using empty brackets with no size—is all you need: s := sum([]int{1,2,3}) @@ -415,7 +420,7 @@ object. We could write n.name = name return n -but for simple structures like "File" it's easier to return the address of a nonce +but for simple structures like "File" it's easier to return the address of a composite literal, as is done here on line 21. We can use the factory to construct some familiar, exported variables of type "*File": @@ -471,15 +476,21 @@ We can now use our new package: --PROG progs/helloworld3.go /package/ END -The ''"./"'' in the import of ''"./file"'' tells the compiler to use our own package rather than +The ''"./"'' in the import of ''"./file"'' tells the compiler +to use our own package rather than something from the directory of installed packages. +(Also, ''"file.go"'' must be compiled before we can import the +package.) -Finally we can run the program: +Now we can compile and run the program: - % helloworld3 + $ 6g file.go # compile file package + $ 6g helloworld3.go # compile main package + $ 6l -o helloworld3 helloworld3.6 # link - no need to mention "file" + $ helloworld3 hello, world can't open file; err=No such file or directory - % + $ Rotting cats ---- @@ -549,11 +560,11 @@ even though under the covers it holds a pointer to a "struct". Here it is in action:

    -	% echo abcdefghijklmnopqrstuvwxyz | ./cat
    +	$ echo abcdefghijklmnopqrstuvwxyz | ./cat
     	abcdefghijklmnopqrstuvwxyz
    -	% echo abcdefghijklmnopqrstuvwxyz | ./cat --rot13
    +	$ echo abcdefghijklmnopqrstuvwxyz | ./cat --rot13
     	nopqrstuvwxyzabcdefghijklm
    -	%
    +	$
     
    Fans of dependency injection may take cheer from how easily interfaces diff --git a/doc/godocs.js b/doc/godocs.js index b2fc1b357..8b451547d 100644 --- a/doc/godocs.js +++ b/doc/godocs.js @@ -11,17 +11,32 @@ /* We want to do some stuff on page load (after the HTML is rendered). So listen for that: */ -if (window.addEventListener){ - window.addEventListener('load', godocs_onload, false); -} else if (window.attachEvent){ - window.attachEvent('onload', godocs_onload); +function bindEvent(el, e, fn) { + if (el.addEventListener){ + el.addEventListener(e, fn, false); + } else if (el.attachEvent){ + el.attachEvent('on'+e, fn); + } } +bindEvent(window, 'load', godocs_onload); function godocs_onload() { + godocs_bindSearchEvents(); godocs_generateTOC(); godocs_addTopLinks(); } +function godocs_bindSearchEvents() { + var search = document.getElementById('search'); + function clearInactive() { + if (search.className == "inactive") { + search.value = ""; + search.className = ""; + } + } + bindEvent(search, 'focus', clearInactive); +} + /* Generates a table of contents: looks for h2 and h3 elements and generates * links. "Decorates" the element with id=="nav" with this table of contents. */ diff --git a/doc/ie.css b/doc/ie.css new file mode 100644 index 000000000..bb89d54be --- /dev/null +++ b/doc/ie.css @@ -0,0 +1 @@ +#nav-main li { display: inline; } diff --git a/doc/install.html b/doc/install.html index 2c1ff2157..92b099fe8 100644 --- a/doc/install.html +++ b/doc/install.html @@ -1,6 +1,6 @@ - + -

    Introduction

    +

    Introduction

    Go is an open source project, distributed under a BSD-style license. @@ -8,6 +8,8 @@ This document explains how to check out the sources, build them on your own machine, and run them.

    +
    +

    There are two distinct ways to experiment with Go. This document focuses on the gc Go @@ -17,117 +19,6 @@ compiler using the GCC back end, see Setting up and using gccgo.

    -

    Environment variables

    - -

    -The Go compilation environment depends on three environment variables plus one optional variable: -

    - -
    -
    - $GOROOT -
    -
    The root of the Go tree. Typically this is $HOME/go - but it can be any directory. -
    - -
    -$GOOS and $GOARCH -
    -
    - The name of the target operating system and compilation architecture. - Choices for $GOOS are linux, - freebsd, - darwin (Mac OS X 10.5 or 10.6), - and nacl (Native Client, an incomplete port). - Choices for $GOARCH are amd64 (64-bit x86, the most mature port), - 386 (32-bit x86), and - arm (32-bit ARM, an incomplete port). - The valid combinations of $GOOS and $GOARCH are: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    $GOOS $GOARCH
    darwin 386
    darwin amd64
    freebsd 386
    freebsd amd64
    linux 386
    linux amd64
    linux arm
    nacl 386
    -

    -

    - -
    -$GOBIN (optional) -
    -
    - The location where binaries will be installed. - The default is $HOME/bin. - After installing, you will want to arrange to add this - directory to your $PATH, so you can use the tools. -
    - -
    -$GOARM (optional, arm, default=6) -
    -
    - The ARM architecture version the runtime libraries should target. - ARMv6 cores have more efficient synchronization primitives. Setting - $GOARM to 5 will compile the runtime libraries using - just SWP instructions that work on older architectures as well. - Running v6 code on an older core will cause an illegal instruction trap. -
    -
    - -

    -Note that $GOARCH and $GOOS identify the -target environment, not the environment you are running on. -In effect, you are always cross-compiling. -

    - -

    -Set these variables in your shell profile ($HOME/.bashrc, -$HOME/.profile, or equivalent). The settings might look -something like this: -

    - -
    -export GOROOT=$HOME/go
    -export GOARCH=amd64
    -export GOOS=linux
    -
    - -

    -Double-check them by listing your environment. (You will need to launch -a new shell or terminal window for the changes to take effect.) -

    - -
    -$ env | grep '^GO'
    -
    - -

    Ports

    -

    The Go compilers support three instruction sets. There are important differences in the quality of the compilers for the different @@ -147,17 +38,19 @@ architectures. 386 (a.k.a. x86 or x86-32); 8g,8l,8c,8a

    - Comparable to the amd64 port. Not as well soaked but - should be nearly as solid. - + Comparable to the amd64 port.
    arm (a.k.a. ARM); 5g,5l,5c,5a
    - It's got a couple of outstanding bugs but is improving. - Tested against QEMU and an android phone. - Only supports Linux binaries. + Incomplete. + It only supports Linux binaries, the optimizer is not enabled, + and floating point is performed entirely in software. + However, all tests pass. + Work on the optimizer and use of the VFP hardware + floating point unit is underway. + Tested against a Nexus One.
    @@ -169,74 +62,106 @@ support for segmented stacks, and a strong goroutine implementation.

    -See the separate gccgo document -for details about that compiler and environment. +The compilers can target the FreeBSD, Linux, +and OS X (a.k.a. Darwin) operating systems. +(A port to Microsoft Windows is in progress but incomplete. See the +Windows Port +page for details.) +The full set of supported combinations is listed in the discussion of +environment variables below.

    -

    Install C tools, if needed

    +
    -

    The Go tool chain is written in C. To build it, you need -to have GCC, the standard C libraries, the parser generator Bison, -make, awk, and the text editor ed installed. On OS X, they can be -installed as part of -Xcode. On Linux, use +

    Install C tools, if needed

    + +

    The Go tool chain is written in C. +To build it, you need these programs installed: +

      +
    • GCC, +
    • the standard C libraries, +
    • the parser generator Bison, +
    • make, +
    • awk, and +
    • the text editor ed. +

    -
    -$ sudo apt-get install bison gcc libc6-dev ed gawk make
    -
    +

    On OS X, they can be +installed as part of +Xcode. +

    -

    -(or the equivalent on your Linux distribution). +

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

    -

    Fetch the repository

    +

    Install Mercurial, if needed

    -If you do not have Mercurial installed (you do not have an hg command), -this command: +To perform the next step you must have Mercurial installed. (Check that you have an hg command.) This suffices to install Mercurial on most systems:

    -
    -$ sudo easy_install mercurial
    +sudo easy_install mercurial
     
    +(On Ubuntu/Debian, you might try apt-get install python-setuptools +python-dev build-essential first. The Mercurial in your distribution's +package repository will most likely be old and broken.) +

    +

    +If that fails, try installing manually from the Mercurial Download page.

    +

    -

    works on most systems. -(On Ubuntu/Debian, you might try apt-get install python-setuptools python-dev build-essential gcc first.) -If that fails, visit the Mercurial Download page.

    +

    Fetch the repository

    -

    Make sure the $GOROOT directory does not exist or is empty. +

    +

    Go will install to a directory named go. +Change to the directory that will be its parent +and make sure the go directory does not exist. Then check out the repository:

    -$ hg clone -r release https://go.googlecode.com/hg/ $GOROOT
    +$ hg clone -r release https://go.googlecode.com/hg/ go
     
    -

    Install Go

    +

    Install Go

    To build the Go distribution, run

    -$ cd $GOROOT/src
    +$ cd go/src
     $ ./all.bash
     

    -If all goes well, it will finish by printing +If all goes well, it will finish by printing output like:

     --- cd ../test
     N known bugs; 0 unexpected bugs
    +
    +---
    +Installed Go for linux/amd64 in /home/you/go.
    +Installed commands in /home/you/go/bin.
    +*** You need to add /home/you/go/bin to your $PATH. ***
    +The compiler is 6g.
     

    -where N is a number that varies from release to release. +where N is a number that varies from release to release +and the details on the last few lines will reflect the operating system, +architecture, and root directory used during the install.

    -

    Writing programs

    +
    + +

    For more information about ways to control the build, +see the discussion of environment variables below.

    +
    + +

    Writing programs

    Given a file file.go, compile it using @@ -299,34 +224,57 @@ command line. The linker learns about them by reading hello.6.

    +

    To build more complicated programs, you will probably want to use a Makefile. There are examples in places like -$GOROOT/src/cmd/godoc/Makefile -and $GOROOT/src/pkg/*/Makefile. +go/src/cmd/godoc/Makefile +and go/src/pkg/*/Makefile. The document about contributing to the Go project gives more detail about the process of building and testing Go programs.

    +
    -

    Keeping up with releases

    +

    What's next

    + +

    +Start by reading the Go Tutorial. +

    + +

    +Build a web application by following the Wiki +Codelab. +

    + +

    +Read Effective Go to learn about writing +idiomatic Go code. +

    + +

    +For the full story, consult Go's extensive +documentation. +

    + +

    Keeping up with releases

    New releases are announced on the Go Nuts mailing list. To update an existing tree to the latest release, you can run:

    -$ cd $GOROOT/src
    +$ cd go/src
     $ hg pull
     $ hg update release
     $ ./all.bash
     
    -

    Community resources

    +

    Community resources

    For real-time help, there may be users or developers on @@ -348,4 +296,144 @@ there is another mailing list, Environment variables + +

    +The Go compilation environment can be customized by environment variables. +None are required by the build, but you may wish to set them +to override the defaults. +

    + +
    +
    + $GOROOT +
    +
    + The root of the Go tree, often $HOME/go. + This defaults to the parent of the directory where all.bash is run. + If you choose not to set $GOROOT, you must + run gomake instead of make or gmake + when developing Go programs using the conventional makefiles. +
    + +
    + $GOROOT_FINAL +
    +
    + The value assumed by installed binaries and scripts when + $GOROOT is not set. + It defaults to the value used for $GOROOT. + If you want to build the Go tree in one location + but move it elsewhere after the build, set + $GOROOT_FINAL to the eventual location. +
    + +
    +$GOOS and $GOARCH +
    +
    + The name of the target operating system and compilation architecture. + These default to the values of $GOHOSTOS and + $GOHOSTARCH respectively (described below). +

    + Choices for $GOOS are linux, + freebsd, + darwin (Mac OS X 10.5 or 10.6), + and windows (Windows, an incomplete port). + Choices for $GOARCH are amd64 (64-bit x86, the most mature port), + 386 (32-bit x86), and + arm (32-bit ARM, an incomplete port). + The valid combinations of $GOOS and $GOARCH are: + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    $GOOS $GOARCH
    darwin 386
    darwin amd64
    freebsd 386
    freebsd amd64
    linux 386
    linux amd64
    linux arm incomplete
    windows 386 incomplete
    +

    + +
    +$GOHOSTOS and $GOHOSTARCH +
    +
    + The name of the host operating system and compilation architecture. + These default to the local system's operating system and + architecture. + +

    + Valid choices are the same as for $GOOS and + $GOARCH, listed above. + The specified values must be compatible with the local system. + For example, you should not set $GOHOSTARCH to + arm on an x86 system. +

    + +
    +$GOBIN +
    +
    + The location where binaries will be installed. + The default is $GOROOT/bin. + After installing, you will want to arrange to add this + directory to your $PATH, so you can use the tools. +
    + +
    +$GOARM (arm, default=6) +
    +
    + The ARM architecture version the runtime libraries should target. + ARMv6 cores have more efficient synchronization primitives. Setting + $GOARM to 5 will compile the runtime libraries using + just SWP instructions that work on older architectures as well. + Running v6 code on an older core will cause an illegal instruction trap. +
    +
    + +

    +Note that $GOARCH and $GOOS identify the +target environment, not the environment you are running on. +In effect, you are always cross-compiling. +By architecture, we mean the kind of binaries +that the target environment can run: +an x86-64 system running a 32-bit-only operating system +must set GOARCH to 386, +not amd64. +

    + +

    +If you choose to override the defaults, +set these variables in your shell profile ($HOME/.bashrc, +$HOME/.profile, or equivalent). The settings might look +something like this: +

    + +
    +export GOROOT=$HOME/go
    +export GOARCH=386
    +export GOOS=linux
    +
    diff --git a/doc/logo.png b/doc/logo.png new file mode 100644 index 000000000..076ce398e Binary files /dev/null and b/doc/logo.png differ diff --git a/doc/play_overlay.png b/doc/play_overlay.png new file mode 100644 index 000000000..20ef7f399 Binary files /dev/null and b/doc/play_overlay.png differ diff --git a/doc/playground.html b/doc/playground.html new file mode 100644 index 000000000..01d3adc9c --- /dev/null +++ b/doc/playground.html @@ -0,0 +1,27 @@ + + +
    +

    +The Go Playground is a web service that runs on +golang.org's servers. +The service receives a Go program, compiles, links, and runs the program inside +a sandbox, then returns the output. +

    + +

    +There are limitations to the programs that can be run in the Playground. +They must be single-threaded (but they may use many goroutines). +There are also limits on execution time, and CPU and memory usage. +The Playground can access only a subset of the standard library +(notably absent are network and file system access). +Therefore, the only communication a Playground program has to the outside world +is via standard output. +

    + +
    + +
    + +
    + +
    diff --git a/doc/progs/cat.go b/doc/progs/cat.go index f8d1a54fb..697e5f786 100644 --- a/doc/progs/cat.go +++ b/doc/progs/cat.go @@ -15,11 +15,11 @@ func cat(f *file.File) { const NBUF = 512 var buf [NBUF]byte for { - switch nr, er := f.Read(&buf); true { + switch nr, er := f.Read(buf[:]); true { case nr < 0: fmt.Fprintf(os.Stderr, "cat: error reading from %s: %s\n", f.String(), er.String()) os.Exit(1) - case nr == 0: // EOF + case nr == 0: // EOF return case nr > 0: if nw, ew := file.Stdout.Write(buf[0:nr]); nw != nr { @@ -30,7 +30,7 @@ func cat(f *file.File) { } func main() { - flag.Parse() // Scans the arg list and sets up flags + flag.Parse() // Scans the arg list and sets up flags if flag.NArg() == 0 { cat(file.Stdin) } diff --git a/doc/progs/cat_rot13.go b/doc/progs/cat_rot13.go index 42c6195fb..03fc02259 100644 --- a/doc/progs/cat_rot13.go +++ b/doc/progs/cat_rot13.go @@ -15,10 +15,10 @@ var rot13Flag = flag.Bool("rot13", false, "rot13 the input") func rot13(b byte) byte { if 'a' <= b && b <= 'z' { - b = 'a' + ((b - 'a') + 13) % 26 + b = 'a' + ((b-'a')+13)%26 } if 'A' <= b && b <= 'Z' { - b = 'A' + ((b - 'A') + 13) % 26 + b = 'A' + ((b-'A')+13)%26 } return b } @@ -29,7 +29,7 @@ type reader interface { } type rotate13 struct { - source reader + source reader } func newRotate13(source reader) *rotate13 { @@ -57,11 +57,11 @@ func cat(r reader) { r = newRotate13(r) } for { - switch nr, er := r.Read(&buf); { + switch nr, er := r.Read(buf[:]); { case nr < 0: fmt.Fprintf(os.Stderr, "cat: error reading from %s: %s\n", r.String(), er.String()) os.Exit(1) - case nr == 0: // EOF + case nr == 0: // EOF return case nr > 0: nw, ew := file.Stdout.Write(buf[0:nr]) @@ -73,7 +73,7 @@ func cat(r reader) { } func main() { - flag.Parse() // Scans the arg list and sets up flags + flag.Parse() // Scans the arg list and sets up flags if flag.NArg() == 0 { cat(file.Stdin) } diff --git a/doc/progs/file.go b/doc/progs/file.go index b2f2c0476..d3fb5ae9e 100644 --- a/doc/progs/file.go +++ b/doc/progs/file.go @@ -10,8 +10,8 @@ import ( ) type File struct { - fd int // file descriptor number - name string // file name at Open time + fd int // file descriptor number + name string // file name at Open time } func newFile(fd int, name string) *File { @@ -27,7 +27,7 @@ var ( Stderr = newFile(2, "/dev/stderr") ) -func Open(name string, mode int, perm int) (file *File, err os.Error) { +func Open(name string, mode int, perm uint32) (file *File, err os.Error) { r, e := syscall.Open(name, mode, perm) if e != 0 { err = os.Errno(e) @@ -40,7 +40,7 @@ func (file *File) Close() os.Error { return os.EINVAL } e := syscall.Close(file.fd) - file.fd = -1 // so it can't be closed again + file.fd = -1 // so it can't be closed again if e != 0 { return os.Errno(e) } diff --git a/doc/progs/helloworld3.go b/doc/progs/helloworld3.go index e065f02e6..adbcea324 100644 --- a/doc/progs/helloworld3.go +++ b/doc/progs/helloworld3.go @@ -13,8 +13,8 @@ import ( func main() { hello := []byte("hello, world\n") file.Stdout.Write(hello) - file, err := file.Open("/does/not/exist", 0, 0) - if file == nil { + f, err := file.Open("/does/not/exist", 0, 0) + if f == nil { fmt.Printf("can't open file; err=%s\n", err.String()) os.Exit(1) } diff --git a/doc/progs/run b/doc/progs/run index 07bc141df..29f1f8152 100755 --- a/doc/progs/run +++ b/doc/progs/run @@ -5,9 +5,7 @@ set -e -GOBIN="${GOBIN:-$HOME/bin}" - -. "$GOROOT"/src/Make.$GOARCH +eval $(gomake --no-print-directory -f ../../src/Make.inc go-env) if [ -z "$O" ]; then echo 'missing $O - maybe no Make.$GOARCH?' 1>&2 @@ -34,11 +32,11 @@ for i in \ ; do BASE=$(basename $i .go) - "$GOBIN"/$GC $i + $GC $i done function testit { - "$GOBIN"/$LD $1.$O + $LD $1.$O x=$(echo $(./$O.out $2 2>&1)) # extra echo canonicalizes if [ "$x" != "$3" ] then @@ -47,7 +45,7 @@ function testit { } function testitpipe { - "$GOBIN"/$LD $1.$O + $LD $1.$O x=$(echo $(./$O.out | $2 2>&1)) # extra echo canonicalizes if [ "$x" != "$3" ] then @@ -76,7 +74,7 @@ testitpipe sieve "sed 10q" "2 3 5 7 11 13 17 19 23 29" testitpipe sieve "sed 10q" "2 3 5 7 11 13 17 19 23 29" # server hangs; don't run it, just compile it -"$GOBIN"/$GC server.go +$GC server.go testit server1 "" "" rm -f $O.out *.$O diff --git a/doc/progs/sortmain.go b/doc/progs/sortmain.go index 6bd504a5b..a77ae7381 100644 --- a/doc/progs/sortmain.go +++ b/doc/progs/sortmain.go @@ -6,7 +6,7 @@ package main import ( "fmt" - "sort" + "./sort" ) func ints() { diff --git a/doc/progs/sum.go b/doc/progs/sum.go index 74fd5bca3..9caa799fd 100644 --- a/doc/progs/sum.go +++ b/doc/progs/sum.go @@ -6,7 +6,7 @@ package main import "fmt" -func sum(a []int) int { // returns an int +func sum(a []int) int { // returns an int s := 0 for i := 0; i < len(a); i++ { s += a[i] @@ -16,6 +16,6 @@ func sum(a []int) int { // returns an int func main() { - s := sum(&[3]int{1,2,3}) // a slice of the array is passed to sum + s := sum([3]int{1, 2, 3}[:]) // a slice of the array is passed to sum fmt.Print(s, "\n") } diff --git a/doc/root.html b/doc/root.html index 3a5a49515..f98f9c243 100644 --- a/doc/root.html +++ b/doc/root.html @@ -1,131 +1,97 @@ - + - - - - - -
    -

    Getting started

    - -
      -
    1. - Install Go. -
    2. - -
    3. - Read the tutorial. -
    4. - -
    5. - Learn the libraries. -
    6. -
    - -

    Slow compiles?
    Watch this

    - - - - -
    - -
    -
    - -
    -

    From the Go Blog:

    -
    Loading...
    -
    - - -
    - - - - - - -
    - - -
    a systems programming language
    expressive, concurrent, garbage-collected
    -
    - -

    Go is …

    - -

    … simple

    -
    -package main
    -
    -import "fmt"
    -
    -func main() {
    -  fmt.Printf("Hello, 世界\n")
    -}
    - -

    … fast

    - -

    -Go compilers produce fast code fast. Typical builds take a fraction of a second yet the resulting programs run nearly as quickly as comparable C or C++ code. -

    - -

    … safe

    - -

    Go is type safe and memory safe. Go has pointers but no pointer arithmetic. -For random access, use slices, which know their limits.

    - -

    … concurrent

    - -

    -Go promotes writing systems and servers as sets of lightweight -communicating processes, called goroutines, with strong support from the language. -Run thousands of goroutines if you want—and say good-bye to stack overflows. -

    - -

    … fun

    - -

    -Go has fast builds, clean syntax, garbage collection, -methods for any type, and run-time reflection. -It feels like a dynamic language but has the speed and safety of a static language. -It's a joy to use. -

    - -

    … open source

    - -

    -Go for it. -

    -
    +
    +
    +

    + The Go programming language is an open source project to make + programmers more productive. Go is expressive, concise, clean, + and efficient. Its concurrency mechanisms make it easy to write + programs that get the most out of multicore and networked machines, + while its novel type system enables flexible and modular program + construction. Go compiles quickly to machine code yet has the + convenience of garbage collection and the power of run-time reflection. + It's a fast, statically typed, compiled language that feels like a + dynamically typed, interpreted language. +

    +

    Check it out!

    +

    +

    + Install Go now, or try it right here in your browser:

    +
    + +
    +
    +
    + +

    Go Blog | More...

    +
    +
      +
    +
    +
    +

    Quick Links

    + +
    +
    +
    diff --git a/doc/style.css b/doc/style.css deleted file mode 100644 index cd57232a7..000000000 --- a/doc/style.css +++ /dev/null @@ -1,282 +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. -*/ - -/* ------------------------------------------------------------------------- */ -/* Styles meant to help page authors achieve beauty. */ - -a { - text-decoration: none; -} - -code, .code { - font-size: 100%; - font-family: monospace; - color: #0f398d; -} - -kbd { - font-size: 100%; - font-family: monospace; - font-weight: bold; -} - -pre.ebnf { - background-color: #ffffe9; -} - -pre.grammar { - background-color: #ffffe9; -} - -p.rule { - font-style: italic; -} - -span.event { - font-style: italic; -} - -span.alert { - color: #ff0000; -} - -body { - font: 13px Helvetica, Arial, sans-serif; - margin-bottom: 0px; -} - -h1 { - margin-top: 0; - padding-top: 0.75em; - margin-bottom: 0.75em; -} - -h2, h3, h4, h5, h6 { - font-family: Helvetica, Arial, sans-serif; - margin-bottom: 0.25em; -} - -h2 { - background-color: #e5ecf9; - margin-top: 2em; - border-top:1px solid #36C; - padding-left: 0.2em; -} - -pre { - font-size: 9pt; - background-color: #f8f8ff; - margin: 1em 0 0 0; - padding: .99em; - line-height: 125%; - overflow: auto; - word-wrap: break-word; -} - -li { - padding-bottom: 0.5em; -} - -li pre { - margin: 0.5em 0px 1em 0px; -} - -a:link { - color: #0f398d -} - -a:visited { - color: #0b2455 -} - -a:hover { - color: #0000ff -} - -/* ------------------------------------------------------------------------- */ -/* Styles used in the boilerplate-ish parts of pages. */ - -div#content { - margin-left: 20%; - padding: 0 1em 2em 1em; - margin-top: 0px; - margin-bottom: 0px; -/* - border-left: 2px solid #e5ecf9; - border-right: 2px solid #e5ecf9; - border-bottom: 2px solid #e5ecf9; -*/ -} - -#topnav { - margin: 0px; - padding-top: 0.2em; - width: 100%; - white-space: nowrap; - background-color: #ffffff; - border-bottom: 2px solid #36C; - font: bold 150% Helvetica, Arial, sans-serif; -} - -div#linkList { - margin-top: 1.5em; - padding-left: 0.5em; - font: 13px Helvetica, Arial, sans-serif; - float: left; - width: 18%; - background-color: #fffff0; - border: 2px solid #ba9836; -} - -div#linkList ul { - padding: 1px; - list-style-type: none; -} - -div#linkList li { - margin-left: 1em; - padding-bottom: 0.2em; -} - -div#linkList li.navhead { - font-weight: bold; - margin-left: 0px; - padding-bottom: 0.25em; -} - -#nav dl { - margin: 0 0.5em 0 0.5em; - padding: 0px; -} - -.navtop { - font-size: xx-small; - float: right; -} - -#footer { - margin: 2em 0 0 0; - text-align: center; - color: #555; - font-size: small; -} - -#footer p { - margin: 0px; -} - -#footer a { - color: #555; -} - -@media print { - div#linkList { - display: none; - } - .navtop { - display: none; - } - div#content { - margin-left: 0px; - border: none; - } -} - - -/* ------------------------------------------------------------------------- */ -/* Styles used by godoc */ - -table.layout { - border-width: 0px; - border-spacing: 0px; - padding: 0px; -} - -span.comment { - color: #0000a0; -} - -span.highlight { - font-weight: bold; - background-color: #ffffa0; -} - -span.subtitle { - font-weight: bold; - font-size: medium; -} - -/* same style as for gettingStarted */ -#menu { - margin-top: 1.5em; - margin-left: 1.75em; - margin-right: 0em; - float: right; - background-color: #fffff0; - padding-left: 1em; - padding-right: 1em; - padding-bottom: 0.75em; - border: 2px solid #ba9836; -} - -/* same color scheme as for gettingStarted */ -#content .popup { - position: absolute; - border: 1px solid #ba9836; - background-color: #fffff0; - margin-top: 3em; - padding: 3px; -} - -#content .identifier, -#content .type { - color: #008; -} - -/* ------------------------------------------------------------------------- */ -/* Styles for the frontpage */ - -#gettingStarted, #blog { - margin-top: 1.5em; - margin-left: 1.75em; - margin-right: 0em; - float: right; - clear: right; - background-color: #fffff0; - padding-left: 1em; - padding-right: 1em; - padding-bottom: 0.75em; - border: 2px solid #ba9836; - width: 160px; -} - -#blog { margin-bottom: 1.5em; } -#blog h1 { font-size: 1.2em; } -#blog #blogFeed a { font-size: 1.1em; display: block; margin-top: 1em; } - -#gettingStarted h1 { - padding-top: 0.3em; - margin-bottom: 0.2em; - font-size: 1.5em; -} - -#gettingStarted ol { - padding-left: 2em; -} - -#gettingStarted a img { - border: 1px solid blue; -} - -#frontpage { - width: 100%; -} - -#frontpage h3 { - margin-left: 3em; - font-size: 1.5em; - font-weight: normal; -} diff --git a/doc/talks/gofrontend-gcc-summit-2010.pdf b/doc/talks/gofrontend-gcc-summit-2010.pdf new file mode 100644 index 000000000..157fd7676 Binary files /dev/null and b/doc/talks/gofrontend-gcc-summit-2010.pdf differ diff --git a/doc/talks/io2010/balance.go b/doc/talks/io2010/balance.go index 6a0713831..b01f7468c 100644 --- a/doc/talks/io2010/balance.go +++ b/doc/talks/io2010/balance.go @@ -6,6 +6,7 @@ package main import ( "container/heap" + "flag" "fmt" "rand" "time" @@ -14,6 +15,8 @@ import ( const nRequester = 100 const nWorker = 10 +var roundRobin = flag.Bool("r", false, "use round-robin scheduling") + // Simulation of some work: just sleep for a while and report how long. func op() int { n := rand.Int63n(1e9) @@ -125,7 +128,7 @@ func (b *Balancer) print() { } func (b *Balancer) dispatch(req Request) { - if false { + if *roundRobin { w := b.pool[b.i] w.requests <- req w.pending++ @@ -144,7 +147,7 @@ func (b *Balancer) dispatch(req Request) { } func (b *Balancer) completed(w *Worker) { - if false { + if *roundRobin { w.pending-- return } @@ -156,6 +159,7 @@ func (b *Balancer) completed(w *Worker) { } func main() { + flag.Parse() work := make(chan Request) for i := 0; i < nRequester; i++ { go requester(work) diff --git a/doc/video-001.png b/doc/video-001.png new file mode 100644 index 000000000..d3468bbe8 Binary files /dev/null and b/doc/video-001.png differ diff --git a/doc/video-002.png b/doc/video-002.png new file mode 100644 index 000000000..4f7c5d184 Binary files /dev/null and b/doc/video-002.png differ diff --git a/doc/video-003.png b/doc/video-003.png new file mode 100644 index 000000000..3dff68602 Binary files /dev/null and b/doc/video-003.png differ diff --git a/doc/video-004.png b/doc/video-004.png new file mode 100644 index 000000000..92144549a Binary files /dev/null and b/doc/video-004.png differ diff --git a/doc/video-snap.jpg b/doc/video-snap.jpg deleted file mode 100644 index ae66c558e..000000000 Binary files a/doc/video-snap.jpg and /dev/null differ diff --git a/include/libc.h b/include/libc.h index ea6fc3b26..1103bcf81 100644 --- a/include/libc.h +++ b/include/libc.h @@ -292,9 +292,15 @@ extern char* getgoroot(void); extern char* getgoversion(void); #ifdef __MINGW32__ -extern int fork(); +struct timespec { + int tv_sec; + long tv_nsec; +}; +extern int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); +extern int fork(void); extern int pread(int fd, void *buf, int n, int off); extern int pwrite(int fd, void *buf, int n, int off); +#define execv(prog, argv) execv(prog, (const char* const*)(argv)) #define execvp(prog, argv) execvp(prog, (const char**)(argv)) #define lseek(fd, n, base) _lseeki64(fd, n, base) #define mkdir(path, perm) mkdir(path) diff --git a/include/u.h b/include/u.h index 6dd55a09c..3cc1f335c 100644 --- a/include/u.h +++ b/include/u.h @@ -68,6 +68,7 @@ extern "C" { #include #include #include /* for tolower */ +#include /* * OS-specific crap @@ -192,6 +193,10 @@ typedef u64int uint64; #undef _NEEDUINT #undef _NEEDULONG +#ifndef SIGBUS +#define SIGBUS SIGSEGV /* close enough */ +#endif + /* * Funny-named symbols to tip off 9l to autolink. */ diff --git a/lib/codereview/codereview.py b/lib/codereview/codereview.py index 2d0977caa..e8c84abec 100644 --- a/lib/codereview/codereview.py +++ b/lib/codereview/codereview.py @@ -1,12 +1,14 @@ -#!/usr/bin/env python -# +# coding=utf-8 +# (The line above is necessary so that I can use 世界 in the +# *comment* below without Python getting all bent out of shape.) + # Copyright 2007-2009 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -54,6 +56,12 @@ except: from mercurial.version import version as v hgversion = v.get_version() +try: + from mercurial.discovery import findcommonincoming +except: + def findcommonincoming(repo, remote): + return repo.findcommonincoming(remote) + oldMessage = """ The code review extension requires Mercurial 1.3 or newer. @@ -102,6 +110,35 @@ server = "codereview.appspot.com" server_url_base = None defaultcc = None contributors = {} +missing_codereview = None + +####################################################################### +# RE: UNICODE STRING HANDLING +# +# Python distinguishes between the str (string of bytes) +# and unicode (string of code points) types. Most operations +# work on either one just fine, but some (like regexp matching) +# require unicode, and others (like write) require str. +# +# As befits the language, Python hides the distinction between +# unicode and str by converting between them silently, but +# *only* if all the bytes/code points involved are 7-bit ASCII. +# This means that if you're not careful, your program works +# fine on "hello, world" and fails on "hello, 世界". And of course, +# the obvious way to be careful - use static types - is unavailable. +# So the only way is trial and error to find where to put explicit +# conversions. +# +# Because more functions do implicit conversion to str (string of bytes) +# than do implicit conversion to unicode (string of code points), +# the convention in this module is to represent all text as str, +# converting to unicode only when calling a unicode-only function +# and then converting back to str as soon as possible. + +def typecheck(s, t): + if type(s) != t: + raise util.Abort("type check failed: %s has type %s != %s" % (repr(s), type(s), t)) + ####################################################################### # Change list parsing. @@ -120,9 +157,9 @@ diff --git a/~rietveld~placeholder~ b/~rietveld~placeholder~ new file mode 100644 """ - class CL(object): def __init__(self, name): + typecheck(name, str) self.name = name self.desc = '' self.files = [] @@ -145,6 +182,7 @@ class CL(object): s += "Files:\n" for f in cl.files: s += "\t" + f + "\n" + typecheck(s, str) return s def EditorText(self): @@ -169,6 +207,7 @@ class CL(object): for f in cl.files: s += "\t" + f + "\n" s += "\n" + typecheck(s, str) return s def PendingText(self): @@ -183,6 +222,7 @@ class CL(object): s += "\tFiles:\n" for f in cl.files: s += "\t\t" + f + "\n" + typecheck(s, str) return s def Flush(self, ui, repo): @@ -210,13 +250,15 @@ class CL(object): s = s[0:55] + "..." if self.name != "new": s = "code review %s: %s" % (self.name, s) + typecheck(s, str) return s def Upload(self, ui, repo, send_mail=False, gofmt=True, gofmt_just_warn=False): if not self.files: ui.warn("no files in change list\n") if ui.configbool("codereview", "force_gofmt", True) and gofmt: - CheckGofmt(ui, repo, self.files, just_warn=gofmt_just_warn) + CheckFormat(ui, repo, self.files, just_warn=gofmt_just_warn) + set_status("uploading CL metadata + diffs") os.chdir(repo.root) form_fields = [ ("content_upload", "1"), @@ -229,13 +271,11 @@ class CL(object): ("subject", self.Subject()), ] - # NOTE(rsc): This duplicates too much of RealMain, - # but RealMain doesn't have the most reusable interface. if self.name != "new": form_fields.append(("issue", self.name)) vcs = None if self.files: - vcs = GuessVCS(upload_options) + vcs = MercurialVCS(upload_options, ui, repo) data = vcs.GenerateDiff(self.files) files = vcs.GetBaseFiles(data) if len(data) > MAX_UPLOAD_SIZE: @@ -255,6 +295,7 @@ class CL(object): patchset = lines[1].strip() patches = [x.split(" ", 1) for x in lines[2:]] ui.status(msg + "\n") + set_status("uploaded CL metadata + diffs") if not response_body.startswith("Issue created.") and not response_body.startswith("Issue updated."): raise util.Abort("failed to update issue: " + response_body) issue = msg[msg.rfind("/")+1:] @@ -262,12 +303,16 @@ class CL(object): if not self.url: self.url = server_url_base + self.name if not uploaded_diff_file: + set_status("uploading patches") patches = UploadSeparatePatches(issue, rpc, patchset, data, upload_options) if vcs: + set_status("uploading base files") vcs.UploadBaseFiles(issue, rpc, patches, patchset, upload_options, files) if send_mail: + set_status("sending mail") MySend("/" + issue + "/mail", payload="") self.web = True + set_status("flushing changes to disk") self.Flush(ui, repo) return @@ -281,14 +326,18 @@ class CL(object): pmsg += "I'd like you to review this change.\n" else: pmsg += "Please take another look.\n" + typecheck(pmsg, str) PostMessage(ui, self.name, pmsg, subject=self.Subject()) self.mailed = True self.Flush(ui, repo) def GoodCLName(name): + typecheck(name, str) return re.match("^[0-9]+$", name) def ParseCL(text, name): + typecheck(text, str) + typecheck(name, str) sname = None lineno = 0 sections = { @@ -332,6 +381,7 @@ def ParseCL(text, name): i = line.find('#') if i >= 0: line = line[0:i].rstrip() + line = line.strip() if line == '': continue cl.files.append(line) @@ -349,18 +399,22 @@ def ParseCL(text, name): return cl, 0, '' def SplitCommaSpace(s): + typecheck(s, str) s = s.strip() if s == "": return [] return re.split(", *", s) def CutDomain(s): + typecheck(s, str) i = s.find('@') if i >= 0: s = s[0:i] return s def JoinComma(l): + for s in l: + typecheck(s, str) return ", ".join(l) def ExceptionDetail(): @@ -379,6 +433,8 @@ def IsLocalCL(ui, repo, name): # Load CL from disk and/or the web. def LoadCL(ui, repo, name, web=True): + typecheck(name, str) + set_status("loading CL " + name) if not GoodCLName(name): return None, "invalid CL name" dir = CodeReviewDir(ui, repo) @@ -412,8 +468,40 @@ def LoadCL(ui, repo, name, web=True): cl.desc = f['description'] cl.url = server_url_base + name cl.web = True + set_status("loaded CL " + name) return cl, '' +global_status = None + +def set_status(s): + # print >>sys.stderr, "\t", time.asctime(), s + global global_status + global_status = s + +class StatusThread(threading.Thread): + def __init__(self): + threading.Thread.__init__(self) + def run(self): + # pause a reasonable amount of time before + # starting to display status messages, so that + # most hg commands won't ever see them. + time.sleep(30) + + # now show status every 15 seconds + while True: + time.sleep(15 - time.time() % 15) + s = global_status + if s is None: + continue + if s == "": + s = "(unknown status)" + print >>sys.stderr, time.asctime(), s + +def start_status_thread(): + t = StatusThread() + t.setDaemon(True) # allowed to exit if t is still running + t.start() + class LoadCLThread(threading.Thread): def __init__(self, ui, repo, dir, f, web): threading.Thread.__init__(self) @@ -467,6 +555,7 @@ def RepoDir(ui, repo): url = url[5:] if url.endswith('/'): url = url[:-1] + typecheck(url, str) return url # Find (or make) code review directory. On error, ui.warn and return None @@ -481,10 +570,12 @@ def CodeReviewDir(ui, repo): except: ui.warn('cannot mkdir %s: %s\n' % (dir, ExceptionDetail())) return None + typecheck(dir, str) return dir # Strip maximal common leading white space prefix from text def StripCommon(text): + typecheck(text, str) ws = None for line in text.split('\n'): line = line.rstrip() @@ -513,17 +604,22 @@ def StripCommon(text): t += line + '\n' while len(t) >= 2 and t[-2:] == '\n\n': t = t[:-1] + typecheck(t, str) return t # Indent text with indent. def Indent(text, indent): + typecheck(text, str) + typecheck(indent, str) t = '' for line in text.split('\n'): t += indent + line + '\n' + typecheck(t, str) return t # Return the first line of l def line1(text): + typecheck(text, str) return text.split('\n')[0] _change_prolog = """# Change list. @@ -592,16 +688,21 @@ def getremote(ui, repo, opts): # delete it in an attempt to "help" proxy = os.environ.get('http_proxy') source = hg.parseurl(ui.expandpath("default"), None)[0] - other = hg.repository(cmdutil.remoteui(repo, opts), source) + try: + remoteui = hg.remoteui # hg 1.6 + except: + remoteui = cmdutil.remoteui + other = hg.repository(remoteui(repo, opts), source) if proxy is not None: os.environ['http_proxy'] = proxy return other def Incoming(ui, repo, opts): - _, incoming, _ = repo.findcommonincoming(getremote(ui, repo, opts)) + _, incoming, _ = findcommonincoming(repo, getremote(ui, repo, opts)) return incoming def EditCL(ui, repo, cl): + set_status(None) # do not show status s = cl.EditorText() while True: s = ui.edit(s, ui.username()) @@ -660,7 +761,7 @@ original_match = None def ReplacementForCmdutilMatch(repo, pats=[], opts={}, globbed=False, default='relpath'): taken = [] files = [] - pats = pats or [] + pats = pats or [] for p in pats: if p.startswith('@'): taken.append(p) @@ -670,7 +771,7 @@ def ReplacementForCmdutilMatch(repo, pats=[], opts={}, globbed=False, default='r cl, err = LoadCL(repo.ui, repo, clname, web=False) if err != '': raise util.Abort("loading CL " + clname + ": " + err) - if cl.files == None: + if not cl.files: raise util.Abort("no files in CL " + clname) files = Add(files, cl.files) pats = Sub(pats, taken) + ['path:'+f for f in files] @@ -682,14 +783,21 @@ def RelativePath(path, cwd): return path[n+1:] return path +def CheckFormat(ui, repo, files, just_warn=False): + set_status("running gofmt") + CheckGofmt(ui, repo, files, just_warn) + CheckTabfmt(ui, repo, files, just_warn) + # Check that gofmt run on the list of files does not change them -def CheckGofmt(ui, repo, files, just_warn=False): +def CheckGofmt(ui, repo, files, just_warn): files = [f for f in files if (f.startswith('src/') or f.startswith('test/bench/')) and f.endswith('.go')] if not files: return cwd = os.getcwd() files = [RelativePath(repo.root + '/' + f, cwd) for f in files] files = [f for f in files if os.access(f, 0)] + if not files: + return try: cmd = subprocess.Popen(["gofmt", "-l"] + files, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) cmd.stdin.close() @@ -698,6 +806,7 @@ def CheckGofmt(ui, repo, files, just_warn=False): data = cmd.stdout.read() errors = cmd.stderr.read() cmd.wait() + set_status("done with gofmt") if len(errors) > 0: ui.warn("gofmt errors:\n" + errors.rstrip() + "\n") return @@ -709,6 +818,32 @@ def CheckGofmt(ui, repo, files, just_warn=False): raise util.Abort(msg) return +# Check that *.[chys] files indent using tabs. +def CheckTabfmt(ui, repo, files, just_warn): + files = [f for f in files if f.startswith('src/') and re.search(r"\.[chys]$", f)] + if not files: + return + cwd = os.getcwd() + files = [RelativePath(repo.root + '/' + f, cwd) for f in files] + files = [f for f in files if os.access(f, 0)] + badfiles = [] + for f in files: + try: + for line in open(f, 'r'): + if line.startswith(' '): + badfiles.append(f) + break + except: + # ignore cannot open file, etc. + pass + if len(badfiles) > 0: + msg = "these files use spaces for indentation (use tabs instead):\n\t" + "\n\t".join(badfiles) + if just_warn: + ui.warn("warning: " + msg + "\n") + else: + raise util.Abort(msg) + return + ####################################################################### # Mercurial commands @@ -742,6 +877,9 @@ def change(ui, repo, *pats, **opts): before running hg change -d 123456. """ + if missing_codereview: + return missing_codereview + dirty = {} if len(pats) > 0 and GoodCLName(pats[0]): name = pats[0] @@ -776,7 +914,7 @@ def change(ui, repo, *pats, **opts): if opts["delete"]: if cl.copied_from: return "original author must delete CL; hg change -D will remove locally" - PostMessage(ui, cl.name, "*** Abandoned ***") + PostMessage(ui, cl.name, "*** Abandoned ***", send_mail=cl.mailed) EditDesc(cl.name, closed="checked") cl.Delete(ui, repo) return @@ -825,6 +963,9 @@ def code_login(ui, repo, **opts): Logs in to the code review server, saving a cookie in a file in your home directory. """ + if missing_codereview: + return missing_codereview + MySend(None) def clpatch(ui, repo, clname, **opts): @@ -837,6 +978,9 @@ def clpatch(ui, repo, clname, **opts): Submitting an imported patch will keep the original author's name as the Author: line but add your own name to a Committer: line. """ + if missing_codereview: + return missing_codereview + cl, patch, err = DownloadCL(ui, repo, clname) argv = ["hgpatch"] if opts["no_incoming"]: @@ -869,6 +1013,9 @@ def download(ui, repo, clname, **opts): Download prints a description of the given change list followed by its diff, downloaded from the code review server. """ + if missing_codereview: + return missing_codereview + cl, patch, err = DownloadCL(ui, repo, clname) if err != "": return err @@ -884,6 +1031,9 @@ def file(ui, repo, clname, pat, *pats, **opts): The -d option only removes files from the change list. It does not edit them or remove them from the repository. """ + if missing_codereview: + return missing_codereview + pats = tuple([pat] + list(pats)) if not GoodCLName(clname): return "invalid CL name " + clname @@ -941,6 +1091,9 @@ def gofmt(ui, repo, *pats, **opts): Applies gofmt to the modified files in the repository that match the given patterns. """ + if missing_codereview: + return missing_codereview + files = ChangedExistingFiles(ui, repo, pats, opts) files = [f for f in files if f.endswith(".go")] if not files: @@ -965,6 +1118,9 @@ def mail(ui, repo, *pats, **opts): Uploads a patch to the code review server and then sends mail to the reviewer and CC list asking for a review. """ + if missing_codereview: + return missing_codereview + cl, err = CommandLineCL(ui, repo, pats, opts, defaultcc=defaultcc) if err != "": return err @@ -990,6 +1146,9 @@ def pending(ui, repo, *pats, **opts): Lists pending changes followed by a list of unassigned but modified files. """ + if missing_codereview: + return missing_codereview + m = LoadAllCL(ui, repo, web=True) names = m.keys() names.sort() @@ -1007,11 +1166,13 @@ def pending(ui, repo, *pats, **opts): def reposetup(ui, repo): global original_match if original_match is None: + start_status_thread() original_match = cmdutil.match cmdutil.match = ReplacementForCmdutilMatch RietveldSetup(ui, repo) def CheckContributor(ui, repo, user=None): + set_status("checking CONTRIBUTORS file") if not user: user = ui.config("ui", "username") if not user: @@ -1022,9 +1183,10 @@ def CheckContributor(ui, repo, user=None): return userline def FindContributor(ui, repo, user, warn=True): + user = user.lower() m = re.match(r".*<(.*)>", user) if m: - user = m.group(1).lower() + user = m.group(1) if user not in contributors: if warn: @@ -1040,6 +1202,9 @@ def submit(ui, repo, *pats, **opts): Submits change to remote repository. Bails out if the local repository is not in sync with the remote one. """ + if missing_codereview: + return missing_codereview + repo.ui.quiet = True if not opts["no_incoming"] and Incoming(ui, repo, opts): return "local repository out of date; must sync before submit" @@ -1075,7 +1240,7 @@ def submit(ui, repo, *pats, **opts): # check gofmt for real; allowed upload to warn in order to save CL. cl.Flush(ui, repo) - CheckGofmt(ui, repo, cl.files) + CheckFormat(ui, repo, cl.files) about += "%s%s\n" % (server_url_base, cl.name) @@ -1152,6 +1317,9 @@ def sync(ui, repo, **opts): Incorporates recent changes from the remote repository into the local repository. """ + if missing_codereview: + return missing_codereview + if not opts["local"]: ui.status = sync_note ui.note = sync_note @@ -1226,15 +1394,14 @@ def sync_changes(ui, repo): ui.warn("CL %s has no files; suggest hg change -d %s\n" % (cl.name, cl.name)) return -def uisetup(ui): - if "^commit|ci" in commands.table: - commands.table["^commit|ci"] = (nocommit, [], "") - def upload(ui, repo, name, **opts): """upload diffs to the code review server Uploads the current modifications for a given change to the server. """ + if missing_codereview: + return missing_codereview + repo.ui.quiet = True cl, err = LoadCL(ui, repo, name, web=True) if err != "": @@ -1281,11 +1448,6 @@ cmdtable = { [], "", ), - "commit|ci": ( - nocommit, - [], - "", - ), "^download": ( download, [], @@ -1383,7 +1545,7 @@ class FormParser(HTMLParser): self.handle_data("&" + name + ";") def handle_data(self, data): if self.curdata is not None: - self.curdata += data.decode("utf-8").encode("utf-8") + self.curdata += data # XML parser def XMLGet(ui, path): @@ -1399,13 +1561,14 @@ def IsRietveldSubmitted(ui, clname, hex): if feed is None: return False for sum in feed.findall("{http://www.w3.org/2005/Atom}entry/{http://www.w3.org/2005/Atom}summary"): - text = sum.findtext("", None).strip() + text = sum.text.strip() m = re.match('\*\*\* Submitted as [^*]*?([0-9a-f]+) \*\*\*', text) if m is not None and len(m.group(1)) >= 8 and hex.startswith(m.group(1)): return True return False def DownloadCL(ui, repo, clname): + set_status("downloading CL " + clname) cl, err = LoadCL(ui, repo, clname) if err != "": return None, None, "error loading CL %s: %s" % (clname, ExceptionDetail()) @@ -1432,7 +1595,7 @@ def DownloadCL(ui, repo, clname): # Find author - first entry will be author who created CL. nick = None for author in feed.findall("{http://www.w3.org/2005/Atom}entry/{http://www.w3.org/2005/Atom}author/{http://www.w3.org/2005/Atom}name"): - nick = author.findtext("", None).strip() + nick = author.text.strip() break if not nick: return None, None, "CL has no author" @@ -1460,96 +1623,102 @@ def DownloadCL(ui, repo, clname): return cl, diffdata, "" def MySend(request_path, payload=None, - content_type="application/octet-stream", - timeout=None, force_auth=True, - **kwargs): - """Run MySend1 maybe twice, because Rietveld is unreliable.""" - try: - return MySend1(request_path, payload, content_type, timeout, force_auth, **kwargs) - except Exception, e: - if type(e) == urllib2.HTTPError and e.code == 403: # forbidden, it happens - raise - print >>sys.stderr, "Loading "+request_path+": "+ExceptionDetail()+"; trying again in 2 seconds." - time.sleep(2) - return MySend1(request_path, payload, content_type, timeout, force_auth, **kwargs) - + content_type="application/octet-stream", + timeout=None, force_auth=True, + **kwargs): + """Run MySend1 maybe twice, because Rietveld is unreliable.""" + try: + return MySend1(request_path, payload, content_type, timeout, force_auth, **kwargs) + except Exception, e: + if type(e) == urllib2.HTTPError and e.code == 403: # forbidden, it happens + raise + print >>sys.stderr, "Loading "+request_path+": "+ExceptionDetail()+"; trying again in 2 seconds." + time.sleep(2) + return MySend1(request_path, payload, content_type, timeout, force_auth, **kwargs) # Like upload.py Send but only authenticates when the # redirect is to www.google.com/accounts. This keeps # unnecessary redirects from happening during testing. def MySend1(request_path, payload=None, - content_type="application/octet-stream", - timeout=None, force_auth=True, - **kwargs): - """Sends an RPC and returns the response. - - Args: - request_path: The path to send the request to, eg /api/appversion/create. - payload: The body of the request, or None to send an empty request. - content_type: The Content-Type header to use. - timeout: timeout in seconds; default None i.e. no timeout. - (Note: for large requests on OS X, the timeout doesn't work right.) - kwargs: Any keyword arguments are converted into query string parameters. - - Returns: - The response body, as a string. - """ - # TODO: Don't require authentication. Let the server say - # whether it is necessary. - global rpc - if rpc == None: - rpc = GetRpcServer(upload_options) - self = rpc - if not self.authenticated and force_auth: - self._Authenticate() - if request_path is None: - return - - old_timeout = socket.getdefaulttimeout() - socket.setdefaulttimeout(timeout) - try: - tries = 0 - while True: - tries += 1 - args = dict(kwargs) - url = "http://%s%s" % (self.host, request_path) - if args: - url += "?" + urllib.urlencode(args) - req = self._CreateRequest(url=url, data=payload) - req.add_header("Content-Type", content_type) - try: - f = self.opener.open(req) - response = f.read() - f.close() - # Translate \r\n into \n, because Rietveld doesn't. - response = response.replace('\r\n', '\n') - return response - except urllib2.HTTPError, e: - if tries > 3: - raise - elif e.code == 401: - self._Authenticate() - elif e.code == 302: - loc = e.info()["location"] - if not loc.startswith('https://www.google.com/a') or loc.find('/ServiceLogin') < 0: - return '' - self._Authenticate() - else: - raise - finally: - socket.setdefaulttimeout(old_timeout) + content_type="application/octet-stream", + timeout=None, force_auth=True, + **kwargs): + """Sends an RPC and returns the response. + + Args: + request_path: The path to send the request to, eg /api/appversion/create. + payload: The body of the request, or None to send an empty request. + content_type: The Content-Type header to use. + timeout: timeout in seconds; default None i.e. no timeout. + (Note: for large requests on OS X, the timeout doesn't work right.) + kwargs: Any keyword arguments are converted into query string parameters. + + Returns: + The response body, as a string. + """ + # TODO: Don't require authentication. Let the server say + # whether it is necessary. + global rpc + if rpc == None: + rpc = GetRpcServer(upload_options) + self = rpc + if not self.authenticated and force_auth: + self._Authenticate() + if request_path is None: + return + + old_timeout = socket.getdefaulttimeout() + socket.setdefaulttimeout(timeout) + try: + tries = 0 + while True: + tries += 1 + args = dict(kwargs) + url = "http://%s%s" % (self.host, request_path) + if args: + url += "?" + urllib.urlencode(args) + req = self._CreateRequest(url=url, data=payload) + req.add_header("Content-Type", content_type) + try: + f = self.opener.open(req) + response = f.read() + f.close() + # Translate \r\n into \n, because Rietveld doesn't. + response = response.replace('\r\n', '\n') + # who knows what urllib will give us + if type(response) == unicode: + response = response.encode("utf-8") + typecheck(response, str) + return response + except urllib2.HTTPError, e: + if tries > 3: + raise + elif e.code == 401: + self._Authenticate() + elif e.code == 302: + loc = e.info()["location"] + if not loc.startswith('https://www.google.com/a') or loc.find('/ServiceLogin') < 0: + return '' + self._Authenticate() + else: + raise + finally: + socket.setdefaulttimeout(old_timeout) def GetForm(url): f = FormParser() - f.feed(MySend(url)) + f.feed(MySend(url).decode("utf-8")) # f.feed wants unicode f.close() + # convert back to utf-8 to restore sanity + m = {} for k,v in f.map.items(): - f.map[k] = v.replace("\r\n", "\n"); - return f.map + m[k.encode("utf-8")] = v.replace("\r\n", "\n").encode("utf-8") + return m # Fetch the settings for the CL, like reviewer and CC list, by # scraping the Rietveld editing forms. def GetSettings(issue): + set_status("getting issue metadata from web") # The /issue/edit page has everything but only the # CL owner is allowed to fetch it (and submit it). f = None @@ -1566,6 +1735,7 @@ def GetSettings(issue): return f def EditDesc(issue, subject=None, desc=None, reviewers=None, cc=None, closed=None): + set_status("uploading change to description") form_fields = GetForm("/" + issue + "/edit") if subject is not None: form_fields['subject'] = subject @@ -1584,6 +1754,7 @@ def EditDesc(issue, subject=None, desc=None, reviewers=None, cc=None, closed=Non sys.exit(2) def PostMessage(ui, issue, message, reviewers=None, cc=None, send_mail=True, subject=None): + set_status("uploading message") form_fields = GetForm("/" + issue + "/publish") if reviewers is not None: form_fields['reviewers'] = reviewers @@ -1610,17 +1781,36 @@ def PostMessage(ui, issue, message, reviewers=None, cc=None, send_mail=True, sub class opt(object): pass +def disabled(*opts, **kwopts): + raise util.Abort("commit is disabled when codereview is in use") + def RietveldSetup(ui, repo): global defaultcc, upload_options, rpc, server, server_url_base, force_google_account, verbosity, contributors + global missing_codereview + repo_config_path = '' # Read repository-specific options from lib/codereview/codereview.cfg try: - f = open(repo.root + '/lib/codereview/codereview.cfg') + repo_config_path = repo.root + '/lib/codereview/codereview.cfg' + f = open(repo_config_path) for line in f: if line.startswith('defaultcc: '): defaultcc = SplitCommaSpace(line[10:]) except: - pass + # If there are no options, chances are good this is not + # a code review repository; stop now before we foul + # things up even worse. Might also be that repo doesn't + # even have a root. See issue 959. + if repo_config_path == '': + missing_codereview = 'codereview disabled: repository has no root' + else: + missing_codereview = 'codereview disabled: cannot open ' + repo_config_path + return + + # Should only modify repository with hg submit. + # Disable the built-in Mercurial commands that might + # trip things up. + cmdutil.commit = disabled try: f = open(repo.root + '/CONTRIBUTORS', 'r') @@ -1640,15 +1830,6 @@ def RietveldSetup(ui, repo): contributors[email.lower()] = (name, email) for extra in m.group(3).split(): contributors[extra[1:-1].lower()] = (name, email) - - - # TODO(rsc): If the repository config has no codereview section, - # do not enable the extension. This allows users to - # put the extension in their global .hgrc but only - # enable it for some repositories. - # if not ui.has_section("codereview"): - # cmdtable = {} - # return if not ui.verbose: verbosity = 0 @@ -1693,11 +1874,7 @@ def RietveldSetup(ui, repo): rpc = None ####################################################################### -# We keep a full copy of upload.py here to avoid import path hell. -# It would be nice if hg added the hg repository root -# to the default PYTHONPATH. - -# Edit .+2, 0: - print msg + Args: + msg: The string to print. + """ + if verbosity > 0: + print msg def ErrorExit(msg): - """Print an error message to stderr and exit.""" - print >>sys.stderr, msg - sys.exit(1) + """Print an error message to stderr and exit.""" + print >>sys.stderr, msg + sys.exit(1) class ClientLoginError(urllib2.HTTPError): - """Raised to indicate there was an error authenticating with ClientLogin.""" + """Raised to indicate there was an error authenticating with ClientLogin.""" - def __init__(self, url, code, msg, headers, args): - urllib2.HTTPError.__init__(self, url, code, msg, headers, None) - self.args = args - self.reason = args["Error"] + def __init__(self, url, code, msg, headers, args): + urllib2.HTTPError.__init__(self, url, code, msg, headers, None) + self.args = args + self.reason = args["Error"] class AbstractRpcServer(object): - """Provides a common interface for a simple RPC server.""" - - def __init__(self, host, auth_function, host_override=None, extra_headers={}, - save_cookies=False): - """Creates a new HttpRpcServer. - - Args: - host: The host to send requests to. - auth_function: A function that takes no arguments and returns an - (email, password) tuple when called. Will be called if authentication - is required. - host_override: The host header to send to the server (defaults to host). - extra_headers: A dict of extra headers to append to every request. - save_cookies: If True, save the authentication cookies to local disk. - If False, use an in-memory cookiejar instead. Subclasses must - implement this functionality. Defaults to False. - """ - self.host = host - self.host_override = host_override - self.auth_function = auth_function - self.authenticated = False - self.extra_headers = extra_headers - self.save_cookies = save_cookies - self.opener = self._GetOpener() - if self.host_override: - logging.info("Server: %s; Host: %s", self.host, self.host_override) - else: - logging.info("Server: %s", self.host) - - def _GetOpener(self): - """Returns an OpenerDirector for making HTTP requests. - - Returns: - A urllib2.OpenerDirector object. - """ - raise NotImplementedError() - - def _CreateRequest(self, url, data=None): - """Creates a new urllib request.""" - logging.debug("Creating request for: '%s' with payload:\n%s", url, data) - req = urllib2.Request(url, data=data) - if self.host_override: - req.add_header("Host", self.host_override) - for key, value in self.extra_headers.iteritems(): - req.add_header(key, value) - return req - - def _GetAuthToken(self, email, password): - """Uses ClientLogin to authenticate the user, returning an auth token. - - Args: - email: The user's email address - password: The user's password - - Raises: - ClientLoginError: If there was an error authenticating with ClientLogin. - HTTPError: If there was some other form of HTTP error. - - Returns: - The authentication token returned by ClientLogin. - """ - account_type = "GOOGLE" - if self.host.endswith(".google.com") and not force_google_account: - # Needed for use inside Google. - account_type = "HOSTED" - req = self._CreateRequest( - url="https://www.google.com/accounts/ClientLogin", - data=urllib.urlencode({ - "Email": email, - "Passwd": password, - "service": "ah", - "source": "rietveld-codereview-upload", - "accountType": account_type, - }), - ) - try: - response = self.opener.open(req) - response_body = response.read() - response_dict = dict(x.split("=") - for x in response_body.split("\n") if x) - return response_dict["Auth"] - except urllib2.HTTPError, e: - if e.code == 403: - body = e.read() - response_dict = dict(x.split("=", 1) for x in body.split("\n") if x) - raise ClientLoginError(req.get_full_url(), e.code, e.msg, - e.headers, response_dict) - else: - raise - - def _GetAuthCookie(self, auth_token): - """Fetches authentication cookies for an authentication token. - - Args: - auth_token: The authentication token returned by ClientLogin. - - Raises: - HTTPError: If there was an error fetching the authentication cookies. - """ - # This is a dummy value to allow us to identify when we're successful. - continue_location = "http://localhost/" - args = {"continue": continue_location, "auth": auth_token} - req = self._CreateRequest("http://%s/_ah/login?%s" % - (self.host, urllib.urlencode(args))) - try: - response = self.opener.open(req) - except urllib2.HTTPError, e: - response = e - if (response.code != 302 or - response.info()["location"] != continue_location): - raise urllib2.HTTPError(req.get_full_url(), response.code, response.msg, - response.headers, response.fp) - self.authenticated = True - - def _Authenticate(self): - """Authenticates the user. - - The authentication process works as follows: - 1) We get a username and password from the user - 2) We use ClientLogin to obtain an AUTH token for the user - (see http://code.google.com/apis/accounts/AuthForInstalledApps.html). - 3) We pass the auth token to /_ah/login on the server to obtain an - authentication cookie. If login was successful, it tries to redirect - us to the URL we provided. - - If we attempt to access the upload API without first obtaining an - authentication cookie, it returns a 401 response (or a 302) and - directs us to authenticate ourselves with ClientLogin. - """ - for i in range(3): - credentials = self.auth_function() - try: - auth_token = self._GetAuthToken(credentials[0], credentials[1]) - except ClientLoginError, e: - if e.reason == "BadAuthentication": - print >>sys.stderr, "Invalid username or password." - continue - if e.reason == "CaptchaRequired": - print >>sys.stderr, ( - "Please go to\n" - "https://www.google.com/accounts/DisplayUnlockCaptcha\n" - "and verify you are a human. Then try again.") - break - if e.reason == "NotVerified": - print >>sys.stderr, "Account not verified." - break - if e.reason == "TermsNotAgreed": - print >>sys.stderr, "User has not agreed to TOS." - break - if e.reason == "AccountDeleted": - print >>sys.stderr, "The user account has been deleted." - break - if e.reason == "AccountDisabled": - print >>sys.stderr, "The user account has been disabled." - break - if e.reason == "ServiceDisabled": - print >>sys.stderr, ("The user's access to the service has been " - "disabled.") - break - if e.reason == "ServiceUnavailable": - print >>sys.stderr, "The service is not available; try again later." - break - raise - self._GetAuthCookie(auth_token) - return - - def Send(self, request_path, payload=None, - content_type="application/octet-stream", - timeout=None, - **kwargs): - """Sends an RPC and returns the response. - - Args: - request_path: The path to send the request to, eg /api/appversion/create. - payload: The body of the request, or None to send an empty request. - content_type: The Content-Type header to use. - timeout: timeout in seconds; default None i.e. no timeout. - (Note: for large requests on OS X, the timeout doesn't work right.) - kwargs: Any keyword arguments are converted into query string parameters. - - Returns: - The response body, as a string. - """ - # TODO: Don't require authentication. Let the server say - # whether it is necessary. - if not self.authenticated: - self._Authenticate() - - old_timeout = socket.getdefaulttimeout() - socket.setdefaulttimeout(timeout) - try: - tries = 0 - while True: - tries += 1 - args = dict(kwargs) - url = "http://%s%s" % (self.host, request_path) - if args: - url += "?" + urllib.urlencode(args) - req = self._CreateRequest(url=url, data=payload) - req.add_header("Content-Type", content_type) - try: - f = self.opener.open(req) - response = f.read() - f.close() - return response - except urllib2.HTTPError, e: - if tries > 3: - raise - elif e.code == 401 or e.code == 302: - self._Authenticate() - else: - raise - finally: - socket.setdefaulttimeout(old_timeout) + """Provides a common interface for a simple RPC server.""" + + def __init__(self, host, auth_function, host_override=None, extra_headers={}, save_cookies=False): + """Creates a new HttpRpcServer. + + Args: + host: The host to send requests to. + auth_function: A function that takes no arguments and returns an + (email, password) tuple when called. Will be called if authentication + is required. + host_override: The host header to send to the server (defaults to host). + extra_headers: A dict of extra headers to append to every request. + save_cookies: If True, save the authentication cookies to local disk. + If False, use an in-memory cookiejar instead. Subclasses must + implement this functionality. Defaults to False. + """ + self.host = host + self.host_override = host_override + self.auth_function = auth_function + self.authenticated = False + self.extra_headers = extra_headers + self.save_cookies = save_cookies + self.opener = self._GetOpener() + if self.host_override: + logging.info("Server: %s; Host: %s", self.host, self.host_override) + else: + logging.info("Server: %s", self.host) + + def _GetOpener(self): + """Returns an OpenerDirector for making HTTP requests. + + Returns: + A urllib2.OpenerDirector object. + """ + raise NotImplementedError() + + def _CreateRequest(self, url, data=None): + """Creates a new urllib request.""" + logging.debug("Creating request for: '%s' with payload:\n%s", url, data) + req = urllib2.Request(url, data=data) + if self.host_override: + req.add_header("Host", self.host_override) + for key, value in self.extra_headers.iteritems(): + req.add_header(key, value) + return req + + def _GetAuthToken(self, email, password): + """Uses ClientLogin to authenticate the user, returning an auth token. + + Args: + email: The user's email address + password: The user's password + + Raises: + ClientLoginError: If there was an error authenticating with ClientLogin. + HTTPError: If there was some other form of HTTP error. + + Returns: + The authentication token returned by ClientLogin. + """ + account_type = "GOOGLE" + if self.host.endswith(".google.com") and not force_google_account: + # Needed for use inside Google. + account_type = "HOSTED" + req = self._CreateRequest( + url="https://www.google.com/accounts/ClientLogin", + data=urllib.urlencode({ + "Email": email, + "Passwd": password, + "service": "ah", + "source": "rietveld-codereview-upload", + "accountType": account_type, + }), + ) + try: + response = self.opener.open(req) + response_body = response.read() + response_dict = dict(x.split("=") for x in response_body.split("\n") if x) + return response_dict["Auth"] + except urllib2.HTTPError, e: + if e.code == 403: + body = e.read() + response_dict = dict(x.split("=", 1) for x in body.split("\n") if x) + raise ClientLoginError(req.get_full_url(), e.code, e.msg, e.headers, response_dict) + else: + raise + + def _GetAuthCookie(self, auth_token): + """Fetches authentication cookies for an authentication token. + + Args: + auth_token: The authentication token returned by ClientLogin. + + Raises: + HTTPError: If there was an error fetching the authentication cookies. + """ + # This is a dummy value to allow us to identify when we're successful. + continue_location = "http://localhost/" + args = {"continue": continue_location, "auth": auth_token} + req = self._CreateRequest("http://%s/_ah/login?%s" % (self.host, urllib.urlencode(args))) + try: + response = self.opener.open(req) + except urllib2.HTTPError, e: + response = e + if (response.code != 302 or + response.info()["location"] != continue_location): + raise urllib2.HTTPError(req.get_full_url(), response.code, response.msg, response.headers, response.fp) + self.authenticated = True + + def _Authenticate(self): + """Authenticates the user. + + The authentication process works as follows: + 1) We get a username and password from the user + 2) We use ClientLogin to obtain an AUTH token for the user + (see http://code.google.com/apis/accounts/AuthForInstalledApps.html). + 3) We pass the auth token to /_ah/login on the server to obtain an + authentication cookie. If login was successful, it tries to redirect + us to the URL we provided. + + If we attempt to access the upload API without first obtaining an + authentication cookie, it returns a 401 response (or a 302) and + directs us to authenticate ourselves with ClientLogin. + """ + for i in range(3): + credentials = self.auth_function() + try: + auth_token = self._GetAuthToken(credentials[0], credentials[1]) + except ClientLoginError, e: + if e.reason == "BadAuthentication": + print >>sys.stderr, "Invalid username or password." + continue + if e.reason == "CaptchaRequired": + print >>sys.stderr, ( + "Please go to\n" + "https://www.google.com/accounts/DisplayUnlockCaptcha\n" + "and verify you are a human. Then try again.") + break + if e.reason == "NotVerified": + print >>sys.stderr, "Account not verified." + break + if e.reason == "TermsNotAgreed": + print >>sys.stderr, "User has not agreed to TOS." + break + if e.reason == "AccountDeleted": + print >>sys.stderr, "The user account has been deleted." + break + if e.reason == "AccountDisabled": + print >>sys.stderr, "The user account has been disabled." + break + if e.reason == "ServiceDisabled": + print >>sys.stderr, "The user's access to the service has been disabled." + break + if e.reason == "ServiceUnavailable": + print >>sys.stderr, "The service is not available; try again later." + break + raise + self._GetAuthCookie(auth_token) + return + + def Send(self, request_path, payload=None, + content_type="application/octet-stream", + timeout=None, + **kwargs): + """Sends an RPC and returns the response. + + Args: + request_path: The path to send the request to, eg /api/appversion/create. + payload: The body of the request, or None to send an empty request. + content_type: The Content-Type header to use. + timeout: timeout in seconds; default None i.e. no timeout. + (Note: for large requests on OS X, the timeout doesn't work right.) + kwargs: Any keyword arguments are converted into query string parameters. + + Returns: + The response body, as a string. + """ + # TODO: Don't require authentication. Let the server say + # whether it is necessary. + if not self.authenticated: + self._Authenticate() + + old_timeout = socket.getdefaulttimeout() + socket.setdefaulttimeout(timeout) + try: + tries = 0 + while True: + tries += 1 + args = dict(kwargs) + url = "http://%s%s" % (self.host, request_path) + if args: + url += "?" + urllib.urlencode(args) + req = self._CreateRequest(url=url, data=payload) + req.add_header("Content-Type", content_type) + try: + f = self.opener.open(req) + response = f.read() + f.close() + return response + except urllib2.HTTPError, e: + if tries > 3: + raise + elif e.code == 401 or e.code == 302: + self._Authenticate() + else: + raise + finally: + socket.setdefaulttimeout(old_timeout) class HttpRpcServer(AbstractRpcServer): - """Provides a simplified RPC-style interface for HTTP requests.""" - - def _Authenticate(self): - """Save the cookie jar after authentication.""" - super(HttpRpcServer, self)._Authenticate() - if self.save_cookies: - StatusUpdate("Saving authentication cookies to %s" % self.cookie_file) - self.cookie_jar.save() - - def _GetOpener(self): - """Returns an OpenerDirector that supports cookies and ignores redirects. - - Returns: - A urllib2.OpenerDirector object. - """ - opener = urllib2.OpenerDirector() - opener.add_handler(urllib2.ProxyHandler()) - opener.add_handler(urllib2.UnknownHandler()) - opener.add_handler(urllib2.HTTPHandler()) - opener.add_handler(urllib2.HTTPDefaultErrorHandler()) - opener.add_handler(urllib2.HTTPSHandler()) - opener.add_handler(urllib2.HTTPErrorProcessor()) - if self.save_cookies: - self.cookie_file = os.path.expanduser("~/.codereview_upload_cookies_" + server) - self.cookie_jar = cookielib.MozillaCookieJar(self.cookie_file) - if os.path.exists(self.cookie_file): - try: - self.cookie_jar.load() - self.authenticated = True - StatusUpdate("Loaded authentication cookies from %s" % - self.cookie_file) - except (cookielib.LoadError, IOError): - # Failed to load cookies - just ignore them. - pass - else: - # Create an empty cookie file with mode 600 - fd = os.open(self.cookie_file, os.O_CREAT, 0600) - os.close(fd) - # Always chmod the cookie file - os.chmod(self.cookie_file, 0600) - else: - # Don't save cookies across runs of update.py. - self.cookie_jar = cookielib.CookieJar() - opener.add_handler(urllib2.HTTPCookieProcessor(self.cookie_jar)) - return opener - - -parser = optparse.OptionParser(usage="%prog [options] [-- diff_options]") -parser.add_option("-y", "--assume_yes", action="store_true", - dest="assume_yes", default=False, - help="Assume that the answer to yes/no questions is 'yes'.") -# Logging -group = parser.add_option_group("Logging options") -group.add_option("-q", "--quiet", action="store_const", const=0, - dest="verbose", help="Print errors only.") -group.add_option("-v", "--verbose", action="store_const", const=2, - dest="verbose", default=1, - help="Print info level logs (default).") -group.add_option("--noisy", action="store_const", const=3, - dest="verbose", help="Print all logs.") -# Review server -group = parser.add_option_group("Review server options") -group.add_option("-s", "--server", action="store", dest="server", - default="codereview.appspot.com", - metavar="SERVER", - help=("The server to upload to. The format is host[:port]. " - "Defaults to '%default'.")) -group.add_option("-e", "--email", action="store", dest="email", - metavar="EMAIL", default=None, - help="The username to use. Will prompt if omitted.") -group.add_option("-H", "--host", action="store", dest="host", - metavar="HOST", default=None, - help="Overrides the Host header sent with all RPCs.") -group.add_option("--no_cookies", action="store_false", - dest="save_cookies", default=True, - help="Do not save authentication cookies to local disk.") -# Issue -group = parser.add_option_group("Issue options") -group.add_option("-d", "--description", action="store", dest="description", - metavar="DESCRIPTION", default=None, - help="Optional description when creating an issue.") -group.add_option("-f", "--description_file", action="store", - dest="description_file", metavar="DESCRIPTION_FILE", - default=None, - help="Optional path of a file that contains " - "the description when creating an issue.") -group.add_option("-r", "--reviewers", action="store", dest="reviewers", - metavar="REVIEWERS", default=None, - help="Add reviewers (comma separated email addresses).") -group.add_option("--cc", action="store", dest="cc", - metavar="CC", default=None, - help="Add CC (comma separated email addresses).") -group.add_option("--private", action="store_true", dest="private", - default=False, - help="Make the issue restricted to reviewers and those CCed") -# Upload options -group = parser.add_option_group("Patch options") -group.add_option("-m", "--message", action="store", dest="message", - metavar="MESSAGE", default=None, - help="A message to identify the patch. " - "Will prompt if omitted.") -group.add_option("-i", "--issue", type="int", action="store", - metavar="ISSUE", default=None, - help="Issue number to which to add. Defaults to new issue.") -group.add_option("--download_base", action="store_true", - dest="download_base", default=False, - help="Base files will be downloaded by the server " - "(side-by-side diffs may not work on files with CRs).") -group.add_option("--rev", action="store", dest="revision", - metavar="REV", default=None, - help="Branch/tree/revision to diff against (used by DVCS).") -group.add_option("--send_mail", action="store_true", - dest="send_mail", default=False, - help="Send notification email to reviewers.") -group.add_option("--vcs", action="store", dest="vcs", - metavar="VCS", default=None, - help=("Version control system (optional, usually upload.py " - "already guesses the right VCS).")) + """Provides a simplified RPC-style interface for HTTP requests.""" + + def _Authenticate(self): + """Save the cookie jar after authentication.""" + super(HttpRpcServer, self)._Authenticate() + if self.save_cookies: + StatusUpdate("Saving authentication cookies to %s" % self.cookie_file) + self.cookie_jar.save() + + def _GetOpener(self): + """Returns an OpenerDirector that supports cookies and ignores redirects. + + Returns: + A urllib2.OpenerDirector object. + """ + opener = urllib2.OpenerDirector() + opener.add_handler(urllib2.ProxyHandler()) + opener.add_handler(urllib2.UnknownHandler()) + opener.add_handler(urllib2.HTTPHandler()) + opener.add_handler(urllib2.HTTPDefaultErrorHandler()) + opener.add_handler(urllib2.HTTPSHandler()) + opener.add_handler(urllib2.HTTPErrorProcessor()) + if self.save_cookies: + self.cookie_file = os.path.expanduser("~/.codereview_upload_cookies_" + server) + self.cookie_jar = cookielib.MozillaCookieJar(self.cookie_file) + if os.path.exists(self.cookie_file): + try: + self.cookie_jar.load() + self.authenticated = True + StatusUpdate("Loaded authentication cookies from %s" % self.cookie_file) + except (cookielib.LoadError, IOError): + # Failed to load cookies - just ignore them. + pass + else: + # Create an empty cookie file with mode 600 + fd = os.open(self.cookie_file, os.O_CREAT, 0600) + os.close(fd) + # Always chmod the cookie file + os.chmod(self.cookie_file, 0600) + else: + # Don't save cookies across runs of update.py. + self.cookie_jar = cookielib.CookieJar() + opener.add_handler(urllib2.HTTPCookieProcessor(self.cookie_jar)) + return opener def GetRpcServer(options): - """Returns an instance of an AbstractRpcServer. - - Returns: - A new AbstractRpcServer, on which RPC calls can be made. - """ - - rpc_server_class = HttpRpcServer - - def GetUserCredentials(): - """Prompts the user for a username and password.""" - email = options.email - if email is None: - email = GetEmail("Email (login for uploading to %s)" % options.server) - password = getpass.getpass("Password for %s: " % email) - return (email, password) - - # If this is the dev_appserver, use fake authentication. - host = (options.host or options.server).lower() - if host == "localhost" or host.startswith("localhost:"): - email = options.email - if email is None: - email = "test@example.com" - logging.info("Using debug user %s. Override with --email" % email) - server = rpc_server_class( - options.server, - lambda: (email, "password"), - host_override=options.host, - extra_headers={"Cookie": - 'dev_appserver_login="%s:False"' % email}, - save_cookies=options.save_cookies) - # Don't try to talk to ClientLogin. - server.authenticated = True - return server - - return rpc_server_class(options.server, GetUserCredentials, - host_override=options.host, - save_cookies=options.save_cookies) + """Returns an instance of an AbstractRpcServer. + + Returns: + A new AbstractRpcServer, on which RPC calls can be made. + """ + + rpc_server_class = HttpRpcServer + + def GetUserCredentials(): + """Prompts the user for a username and password.""" + email = options.email + if email is None: + email = GetEmail("Email (login for uploading to %s)" % options.server) + password = getpass.getpass("Password for %s: " % email) + return (email, password) + + # If this is the dev_appserver, use fake authentication. + host = (options.host or options.server).lower() + if host == "localhost" or host.startswith("localhost:"): + email = options.email + if email is None: + email = "test@example.com" + logging.info("Using debug user %s. Override with --email" % email) + server = rpc_server_class( + options.server, + lambda: (email, "password"), + host_override=options.host, + extra_headers={"Cookie": 'dev_appserver_login="%s:False"' % email}, + save_cookies=options.save_cookies) + # Don't try to talk to ClientLogin. + server.authenticated = True + return server + + return rpc_server_class(options.server, GetUserCredentials, + host_override=options.host, save_cookies=options.save_cookies) def EncodeMultipartFormData(fields, files): - """Encode form fields for multipart/form-data. - - Args: - fields: A sequence of (name, value) elements for regular form fields. - files: A sequence of (name, filename, value) elements for data to be - uploaded as files. - Returns: - (content_type, body) ready for httplib.HTTP instance. - - Source: - http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306 - """ - BOUNDARY = '-M-A-G-I-C---B-O-U-N-D-A-R-Y-' - CRLF = '\r\n' - lines = [] - for (key, value) in fields: - lines.append('--' + BOUNDARY) - lines.append('Content-Disposition: form-data; name="%s"' % key) - lines.append('') - if type(value) == unicode: - value = value.encode("utf-8") - lines.append(value) - for (key, filename, value) in files: - if type(filename) == unicode: - filename = filename.encode("utf-8") - if type(value) == unicode: - value = value.encode("utf-8") - lines.append('--' + BOUNDARY) - lines.append('Content-Disposition: form-data; name="%s"; filename="%s"' % - (key, filename)) - lines.append('Content-Type: %s' % GetContentType(filename)) - lines.append('') - lines.append(value) - lines.append('--' + BOUNDARY + '--') - lines.append('') - body = CRLF.join(lines) - content_type = 'multipart/form-data; boundary=%s' % BOUNDARY - return content_type, body + """Encode form fields for multipart/form-data. + + Args: + fields: A sequence of (name, value) elements for regular form fields. + files: A sequence of (name, filename, value) elements for data to be + uploaded as files. + Returns: + (content_type, body) ready for httplib.HTTP instance. + + Source: + http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306 + """ + BOUNDARY = '-M-A-G-I-C---B-O-U-N-D-A-R-Y-' + CRLF = '\r\n' + lines = [] + for (key, value) in fields: + typecheck(key, str) + typecheck(value, str) + lines.append('--' + BOUNDARY) + lines.append('Content-Disposition: form-data; name="%s"' % key) + lines.append('') + lines.append(value) + for (key, filename, value) in files: + typecheck(key, str) + typecheck(filename, str) + typecheck(value, str) + lines.append('--' + BOUNDARY) + lines.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename)) + lines.append('Content-Type: %s' % GetContentType(filename)) + lines.append('') + lines.append(value) + lines.append('--' + BOUNDARY + '--') + lines.append('') + body = CRLF.join(lines) + content_type = 'multipart/form-data; boundary=%s' % BOUNDARY + return content_type, body def GetContentType(filename): - """Helper to guess the content-type from the filename.""" - return mimetypes.guess_type(filename)[0] or 'application/octet-stream' + """Helper to guess the content-type from the filename.""" + return mimetypes.guess_type(filename)[0] or 'application/octet-stream' # Use a shell for subcommands on Windows to get a PATH search. use_shell = sys.platform.startswith("win") def RunShellWithReturnCode(command, print_output=False, - universal_newlines=True, - env=os.environ): - """Executes a command and returns the output from stdout and the return code. - - Args: - command: Command to execute. - print_output: If True, the output is printed to stdout. - If False, both stdout and stderr are ignored. - universal_newlines: Use universal_newlines flag (default: True). - - Returns: - Tuple (output, return code) - """ - logging.info("Running %s", command) - p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - shell=use_shell, universal_newlines=universal_newlines, - env=env) - if print_output: - output_array = [] - while True: - line = p.stdout.readline() - if not line: - break - print line.strip("\n") - output_array.append(line) - output = "".join(output_array) - else: - output = p.stdout.read() - p.wait() - errout = p.stderr.read() - if print_output and errout: - print >>sys.stderr, errout - p.stdout.close() - p.stderr.close() - return output, p.returncode + universal_newlines=True, env=os.environ): + """Executes a command and returns the output from stdout and the return code. + + Args: + command: Command to execute. + print_output: If True, the output is printed to stdout. + If False, both stdout and stderr are ignored. + universal_newlines: Use universal_newlines flag (default: True). + + Returns: + Tuple (output, return code) + """ + logging.info("Running %s", command) + p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + shell=use_shell, universal_newlines=universal_newlines, env=env) + if print_output: + output_array = [] + while True: + line = p.stdout.readline() + if not line: + break + print line.strip("\n") + output_array.append(line) + output = "".join(output_array) + else: + output = p.stdout.read() + p.wait() + errout = p.stderr.read() + if print_output and errout: + print >>sys.stderr, errout + p.stdout.close() + p.stderr.close() + return output, p.returncode def RunShell(command, silent_ok=False, universal_newlines=True, - print_output=False, env=os.environ): - data, retcode = RunShellWithReturnCode(command, print_output, - universal_newlines, env) - if retcode: - ErrorExit("Got error status from %s:\n%s" % (command, data)) - if not silent_ok and not data: - ErrorExit("No output from %s" % command) - return data + print_output=False, env=os.environ): + data, retcode = RunShellWithReturnCode(command, print_output, universal_newlines, env) + if retcode: + ErrorExit("Got error status from %s:\n%s" % (command, data)) + if not silent_ok and not data: + ErrorExit("No output from %s" % command) + return data class VersionControlSystem(object): - """Abstract base class providing an interface to the VCS.""" - - def __init__(self, options): - """Constructor. - - Args: - options: Command line options. - """ - self.options = options - - def GenerateDiff(self, args): - """Return the current diff as a string. - - Args: - args: Extra arguments to pass to the diff command. - """ - raise NotImplementedError( - "abstract method -- subclass %s must override" % self.__class__) - - def GetUnknownFiles(self): - """Return a list of files unknown to the VCS.""" - raise NotImplementedError( - "abstract method -- subclass %s must override" % self.__class__) - - def CheckForUnknownFiles(self): - """Show an "are you sure?" prompt if there are unknown files.""" - unknown_files = self.GetUnknownFiles() - if unknown_files: - print "The following files are not added to version control:" - for line in unknown_files: - print line - prompt = "Are you sure to continue?(y/N) " - answer = raw_input(prompt).strip() - if answer != "y": - ErrorExit("User aborted") - - def GetBaseFile(self, filename): - """Get the content of the upstream version of a file. - - Returns: - A tuple (base_content, new_content, is_binary, status) - base_content: The contents of the base file. - new_content: For text files, this is empty. For binary files, this is - the contents of the new file, since the diff output won't contain - information to reconstruct the current file. - is_binary: True iff the file is binary. - status: The status of the file. - """ - - raise NotImplementedError( - "abstract method -- subclass %s must override" % self.__class__) - - - def GetBaseFiles(self, diff): - """Helper that calls GetBase file for each file in the patch. - - Returns: - A dictionary that maps from filename to GetBaseFile's tuple. Filenames - are retrieved based on lines that start with "Index:" or - "Property changes on:". - """ - files = {} - for line in diff.splitlines(True): - if line.startswith('Index:') or line.startswith('Property changes on:'): - unused, filename = line.split(':', 1) - # On Windows if a file has property changes its filename uses '\' - # instead of '/'. - filename = filename.strip().replace('\\', '/') - files[filename] = self.GetBaseFile(filename) - return files - - - def UploadBaseFiles(self, issue, rpc_server, patch_list, patchset, options, - files): - """Uploads the base files (and if necessary, the current ones as well).""" - - def UploadFile(filename, file_id, content, is_binary, status, is_base): - """Uploads a file to the server.""" - file_too_large = False - if is_base: - type = "base" - else: - type = "current" - if len(content) > MAX_UPLOAD_SIZE: - print ("Not uploading the %s file for %s because it's too large." % - (type, filename)) - file_too_large = True - content = "" - checksum = md5(content).hexdigest() - if options.verbose > 0 and not file_too_large: - print "Uploading %s file for %s" % (type, filename) - url = "/%d/upload_content/%d/%d" % (int(issue), int(patchset), file_id) - form_fields = [("filename", filename), - ("status", status), - ("checksum", checksum), - ("is_binary", str(is_binary)), - ("is_current", str(not is_base)), - ] - if file_too_large: - form_fields.append(("file_too_large", "1")) - if options.email: - form_fields.append(("user", options.email)) - ctype, body = EncodeMultipartFormData(form_fields, - [("data", filename, content)]) - response_body = rpc_server.Send(url, body, - content_type=ctype) - if not response_body.startswith("OK"): - StatusUpdate(" --> %s" % response_body) - sys.exit(1) - - patches = dict() - [patches.setdefault(v, k) for k, v in patch_list] - for filename in patches.keys(): - base_content, new_content, is_binary, status = files[filename] - file_id_str = patches.get(filename) - if file_id_str.find("nobase") != -1: - base_content = None - file_id_str = file_id_str[file_id_str.rfind("_") + 1:] - file_id = int(file_id_str) - if base_content != None: - UploadFile(filename, file_id, base_content, is_binary, status, True) - if new_content != None: - UploadFile(filename, file_id, new_content, is_binary, status, False) - - def IsImage(self, filename): - """Returns true if the filename has an image extension.""" - mimetype = mimetypes.guess_type(filename)[0] - if not mimetype: - return False - return mimetype.startswith("image/") - - def IsBinary(self, filename): - """Returns true if the guessed mimetyped isnt't in text group.""" - mimetype = mimetypes.guess_type(filename)[0] - if not mimetype: - return False # e.g. README, "real" binaries usually have an extension - # special case for text files which don't start with text/ - if mimetype in TEXT_MIMETYPES: - return False - return not mimetype.startswith("text/") - - -class SubversionVCS(VersionControlSystem): - """Implementation of the VersionControlSystem interface for Subversion.""" - - def __init__(self, options): - super(SubversionVCS, self).__init__(options) - if self.options.revision: - match = re.match(r"(\d+)(:(\d+))?", self.options.revision) - if not match: - ErrorExit("Invalid Subversion revision %s." % self.options.revision) - self.rev_start = match.group(1) - self.rev_end = match.group(3) - else: - self.rev_start = self.rev_end = None - # Cache output from "svn list -r REVNO dirname". - # Keys: dirname, Values: 2-tuple (ouput for start rev and end rev). - self.svnls_cache = {} - # SVN base URL is required to fetch files deleted in an older revision. - # Result is cached to not guess it over and over again in GetBaseFile(). - required = self.options.download_base or self.options.revision is not None - self.svn_base = self._GuessBase(required) - - def GuessBase(self, required): - """Wrapper for _GuessBase.""" - return self.svn_base - - def _GuessBase(self, required): - """Returns the SVN base URL. - - Args: - required: If true, exits if the url can't be guessed, otherwise None is - returned. - """ - info = RunShell(["svn", "info"]) - for line in info.splitlines(): - words = line.split() - if len(words) == 2 and words[0] == "URL:": - url = words[1] - scheme, netloc, path, params, query, fragment = urlparse.urlparse(url) - username, netloc = urllib.splituser(netloc) - if username: - logging.info("Removed username from base URL") - if netloc.endswith("svn.python.org"): - if netloc == "svn.python.org": - if path.startswith("/projects/"): - path = path[9:] - elif netloc != "pythondev@svn.python.org": - ErrorExit("Unrecognized Python URL: %s" % url) - base = "http://svn.python.org/view/*checkout*%s/" % path - logging.info("Guessed Python base = %s", base) - elif netloc.endswith("svn.collab.net"): - if path.startswith("/repos/"): - path = path[6:] - base = "http://svn.collab.net/viewvc/*checkout*%s/" % path - logging.info("Guessed CollabNet base = %s", base) - elif netloc.endswith(".googlecode.com"): - path = path + "/" - base = urlparse.urlunparse(("http", netloc, path, params, - query, fragment)) - logging.info("Guessed Google Code base = %s", base) - else: - path = path + "/" - base = urlparse.urlunparse((scheme, netloc, path, params, - query, fragment)) - logging.info("Guessed base = %s", base) - return base - if required: - ErrorExit("Can't find URL in output from svn info") - return None - - def GenerateDiff(self, args): - cmd = ["svn", "diff"] - if self.options.revision: - cmd += ["-r", self.options.revision] - cmd.extend(args) - data = RunShell(cmd) - count = 0 - for line in data.splitlines(): - if line.startswith("Index:") or line.startswith("Property changes on:"): - count += 1 - logging.info(line) - if not count: - ErrorExit("No valid patches found in output from svn diff") - return data - - def _CollapseKeywords(self, content, keyword_str): - """Collapses SVN keywords.""" - # svn cat translates keywords but svn diff doesn't. As a result of this - # behavior patching.PatchChunks() fails with a chunk mismatch error. - # This part was originally written by the Review Board development team - # who had the same problem (http://reviews.review-board.org/r/276/). - # Mapping of keywords to known aliases - svn_keywords = { - # Standard keywords - 'Date': ['Date', 'LastChangedDate'], - 'Revision': ['Revision', 'LastChangedRevision', 'Rev'], - 'Author': ['Author', 'LastChangedBy'], - 'HeadURL': ['HeadURL', 'URL'], - 'Id': ['Id'], - - # Aliases - 'LastChangedDate': ['LastChangedDate', 'Date'], - 'LastChangedRevision': ['LastChangedRevision', 'Rev', 'Revision'], - 'LastChangedBy': ['LastChangedBy', 'Author'], - 'URL': ['URL', 'HeadURL'], - } - - def repl(m): - if m.group(2): - return "$%s::%s$" % (m.group(1), " " * len(m.group(3))) - return "$%s$" % m.group(1) - keywords = [keyword - for name in keyword_str.split(" ") - for keyword in svn_keywords.get(name, [])] - return re.sub(r"\$(%s):(:?)([^\$]+)\$" % '|'.join(keywords), repl, content) - - def GetUnknownFiles(self): - status = RunShell(["svn", "status", "--ignore-externals"], silent_ok=True) - unknown_files = [] - for line in status.split("\n"): - if line and line[0] == "?": - unknown_files.append(line) - return unknown_files - - def ReadFile(self, filename): - """Returns the contents of a file.""" - file = open(filename, 'rb') - result = "" - try: - result = file.read() - finally: - file.close() - return result - - def GetStatus(self, filename): - """Returns the status of a file.""" - if not self.options.revision: - status = RunShell(["svn", "status", "--ignore-externals", filename]) - if not status: - ErrorExit("svn status returned no output for %s" % filename) - status_lines = status.splitlines() - # If file is in a cl, the output will begin with - # "\n--- Changelist 'cl_name':\n". See - # http://svn.collab.net/repos/svn/trunk/notes/changelist-design.txt - if (len(status_lines) == 3 and - not status_lines[0] and - status_lines[1].startswith("--- Changelist")): - status = status_lines[2] - else: - status = status_lines[0] - # If we have a revision to diff against we need to run "svn list" - # for the old and the new revision and compare the results to get - # the correct status for a file. - else: - dirname, relfilename = os.path.split(filename) - if dirname not in self.svnls_cache: - cmd = ["svn", "list", "-r", self.rev_start, dirname or "."] - out, returncode = RunShellWithReturnCode(cmd) - if returncode: - ErrorExit("Failed to get status for %s." % filename) - old_files = out.splitlines() - args = ["svn", "list"] - if self.rev_end: - args += ["-r", self.rev_end] - cmd = args + [dirname or "."] - out, returncode = RunShellWithReturnCode(cmd) - if returncode: - ErrorExit("Failed to run command %s" % cmd) - self.svnls_cache[dirname] = (old_files, out.splitlines()) - old_files, new_files = self.svnls_cache[dirname] - if relfilename in old_files and relfilename not in new_files: - status = "D " - elif relfilename in old_files and relfilename in new_files: - status = "M " - else: - status = "A " - return status - - def GetBaseFile(self, filename): - status = self.GetStatus(filename) - base_content = None - new_content = None - - # If a file is copied its status will be "A +", which signifies - # "addition-with-history". See "svn st" for more information. We need to - # upload the original file or else diff parsing will fail if the file was - # edited. - if status[0] == "A" and status[3] != "+": - # We'll need to upload the new content if we're adding a binary file - # since diff's output won't contain it. - mimetype = RunShell(["svn", "propget", "svn:mime-type", filename], - silent_ok=True) - base_content = "" - is_binary = bool(mimetype) and not mimetype.startswith("text/") - if is_binary and self.IsImage(filename): - new_content = self.ReadFile(filename) - elif (status[0] in ("M", "D", "R") or - (status[0] == "A" and status[3] == "+") or # Copied file. - (status[0] == " " and status[1] == "M")): # Property change. - args = [] - if self.options.revision: - url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start) - else: - # Don't change filename, it's needed later. - url = filename - args += ["-r", "BASE"] - cmd = ["svn"] + args + ["propget", "svn:mime-type", url] - mimetype, returncode = RunShellWithReturnCode(cmd) - if returncode: - # File does not exist in the requested revision. - # Reset mimetype, it contains an error message. - mimetype = "" - get_base = False - is_binary = bool(mimetype) and not mimetype.startswith("text/") - if status[0] == " ": - # Empty base content just to force an upload. - base_content = "" - elif is_binary: - if self.IsImage(filename): - get_base = True - if status[0] == "M": - if not self.rev_end: - new_content = self.ReadFile(filename) - else: - url = "%s/%s@%s" % (self.svn_base, filename, self.rev_end) - new_content = RunShell(["svn", "cat", url], - universal_newlines=True, silent_ok=True) - else: - base_content = "" - else: - get_base = True - - if get_base: - if is_binary: - universal_newlines = False - else: - universal_newlines = True - if self.rev_start: - # "svn cat -r REV delete_file.txt" doesn't work. cat requires - # the full URL with "@REV" appended instead of using "-r" option. - url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start) - base_content = RunShell(["svn", "cat", url], - universal_newlines=universal_newlines, - silent_ok=True) - else: - base_content = RunShell(["svn", "cat", filename], - universal_newlines=universal_newlines, - silent_ok=True) - if not is_binary: - args = [] - if self.rev_start: - url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start) - else: - url = filename - args += ["-r", "BASE"] - cmd = ["svn"] + args + ["propget", "svn:keywords", url] - keywords, returncode = RunShellWithReturnCode(cmd) - if keywords and not returncode: - base_content = self._CollapseKeywords(base_content, keywords) - else: - StatusUpdate("svn status returned unexpected output: %s" % status) - sys.exit(1) - return base_content, new_content, is_binary, status[0:5] - - -class GitVCS(VersionControlSystem): - """Implementation of the VersionControlSystem interface for Git.""" - - def __init__(self, options): - super(GitVCS, self).__init__(options) - # Map of filename -> (hash before, hash after) of base file. - # Hashes for "no such file" are represented as None. - self.hashes = {} - # Map of new filename -> old filename for renames. - self.renames = {} - - def GenerateDiff(self, extra_args): - # This is more complicated than svn's GenerateDiff because we must convert - # the diff output to include an svn-style "Index:" line as well as record - # the hashes of the files, so we can upload them along with our diff. - - # Special used by git to indicate "no such content". - NULL_HASH = "0"*40 - - extra_args = extra_args[:] - if self.options.revision: - extra_args = [self.options.revision] + extra_args - extra_args.append('-M') - - # --no-ext-diff is broken in some versions of Git, so try to work around - # this by overriding the environment (but there is still a problem if the - # git config key "diff.external" is used). - env = os.environ.copy() - if 'GIT_EXTERNAL_DIFF' in env: del env['GIT_EXTERNAL_DIFF'] - gitdiff = RunShell(["git", "diff", "--no-ext-diff", "--full-index"] - + extra_args, env=env) - svndiff = [] - filecount = 0 - filename = None - for line in gitdiff.splitlines(): - match = re.match(r"diff --git a/(.*) b/(.*)$", line) - if match: - filecount += 1 - # Intentionally use the "after" filename so we can show renames. - filename = match.group(2) - svndiff.append("Index: %s\n" % filename) - if match.group(1) != match.group(2): - self.renames[match.group(2)] = match.group(1) - else: - # The "index" line in a git diff looks like this (long hashes elided): - # index 82c0d44..b2cee3f 100755 - # We want to save the left hash, as that identifies the base file. - match = re.match(r"index (\w+)\.\.(\w+)", line) - if match: - before, after = (match.group(1), match.group(2)) - if before == NULL_HASH: - before = None - if after == NULL_HASH: - after = None - self.hashes[filename] = (before, after) - svndiff.append(line + "\n") - if not filecount: - ErrorExit("No valid patches found in output from git diff") - return "".join(svndiff) - - def GetUnknownFiles(self): - status = RunShell(["git", "ls-files", "--exclude-standard", "--others"], - silent_ok=True) - return status.splitlines() - - def GetFileContent(self, file_hash, is_binary): - """Returns the content of a file identified by its git hash.""" - data, retcode = RunShellWithReturnCode(["git", "show", file_hash], - universal_newlines=not is_binary) - if retcode: - ErrorExit("Got error status from 'git show %s'" % file_hash) - return data - - def GetBaseFile(self, filename): - hash_before, hash_after = self.hashes.get(filename, (None,None)) - base_content = None - new_content = None - is_binary = self.IsBinary(filename) - status = None - - if filename in self.renames: - status = "A +" # Match svn attribute name for renames. - if filename not in self.hashes: - # If a rename doesn't change the content, we never get a hash. - base_content = RunShell(["git", "show", filename]) - elif not hash_before: - status = "A" - base_content = "" - elif not hash_after: - status = "D" - else: - status = "M" - - is_image = self.IsImage(filename) - - # Grab the before/after content if we need it. - # We should include file contents if it's text or it's an image. - if not is_binary or is_image: - # Grab the base content if we don't have it already. - if base_content is None and hash_before: - base_content = self.GetFileContent(hash_before, is_binary) - # Only include the "after" file if it's an image; otherwise it - # it is reconstructed from the diff. - if is_image and hash_after: - new_content = self.GetFileContent(hash_after, is_binary) - - return (base_content, new_content, is_binary, status) + """Abstract base class providing an interface to the VCS.""" + + def __init__(self, options): + """Constructor. + + Args: + options: Command line options. + """ + self.options = options + + def GenerateDiff(self, args): + """Return the current diff as a string. + + Args: + args: Extra arguments to pass to the diff command. + """ + raise NotImplementedError( + "abstract method -- subclass %s must override" % self.__class__) + + def GetUnknownFiles(self): + """Return a list of files unknown to the VCS.""" + raise NotImplementedError( + "abstract method -- subclass %s must override" % self.__class__) + + def CheckForUnknownFiles(self): + """Show an "are you sure?" prompt if there are unknown files.""" + unknown_files = self.GetUnknownFiles() + if unknown_files: + print "The following files are not added to version control:" + for line in unknown_files: + print line + prompt = "Are you sure to continue?(y/N) " + answer = raw_input(prompt).strip() + if answer != "y": + ErrorExit("User aborted") + + def GetBaseFile(self, filename): + """Get the content of the upstream version of a file. + + Returns: + A tuple (base_content, new_content, is_binary, status) + base_content: The contents of the base file. + new_content: For text files, this is empty. For binary files, this is + the contents of the new file, since the diff output won't contain + information to reconstruct the current file. + is_binary: True iff the file is binary. + status: The status of the file. + """ + + raise NotImplementedError( + "abstract method -- subclass %s must override" % self.__class__) + + + def GetBaseFiles(self, diff): + """Helper that calls GetBase file for each file in the patch. + + Returns: + A dictionary that maps from filename to GetBaseFile's tuple. Filenames + are retrieved based on lines that start with "Index:" or + "Property changes on:". + """ + files = {} + for line in diff.splitlines(True): + if line.startswith('Index:') or line.startswith('Property changes on:'): + unused, filename = line.split(':', 1) + # On Windows if a file has property changes its filename uses '\' + # instead of '/'. + filename = filename.strip().replace('\\', '/') + files[filename] = self.GetBaseFile(filename) + return files + + + def UploadBaseFiles(self, issue, rpc_server, patch_list, patchset, options, + files): + """Uploads the base files (and if necessary, the current ones as well).""" + + def UploadFile(filename, file_id, content, is_binary, status, is_base): + """Uploads a file to the server.""" + set_status("uploading " + filename) + file_too_large = False + if is_base: + type = "base" + else: + type = "current" + if len(content) > MAX_UPLOAD_SIZE: + print ("Not uploading the %s file for %s because it's too large." % + (type, filename)) + file_too_large = True + content = "" + checksum = md5(content).hexdigest() + if options.verbose > 0 and not file_too_large: + print "Uploading %s file for %s" % (type, filename) + url = "/%d/upload_content/%d/%d" % (int(issue), int(patchset), file_id) + form_fields = [ + ("filename", filename), + ("status", status), + ("checksum", checksum), + ("is_binary", str(is_binary)), + ("is_current", str(not is_base)), + ] + if file_too_large: + form_fields.append(("file_too_large", "1")) + if options.email: + form_fields.append(("user", options.email)) + ctype, body = EncodeMultipartFormData(form_fields, [("data", filename, content)]) + response_body = rpc_server.Send(url, body, content_type=ctype) + if not response_body.startswith("OK"): + StatusUpdate(" --> %s" % response_body) + sys.exit(1) + + # Don't want to spawn too many threads, nor do we want to + # hit Rietveld too hard, or it will start serving 500 errors. + # When 8 works, it's no better than 4, and sometimes 8 is + # too many for Rietveld to handle. + MAX_PARALLEL_UPLOADS = 4 + + sema = threading.BoundedSemaphore(MAX_PARALLEL_UPLOADS) + upload_threads = [] + finished_upload_threads = [] + + class UploadFileThread(threading.Thread): + def __init__(self, args): + threading.Thread.__init__(self) + self.args = args + def run(self): + UploadFile(*self.args) + finished_upload_threads.append(self) + sema.release() + + def StartUploadFile(*args): + sema.acquire() + while len(finished_upload_threads) > 0: + t = finished_upload_threads.pop() + upload_threads.remove(t) + t.join() + t = UploadFileThread(args) + upload_threads.append(t) + t.start() + + def WaitForUploads(): + for t in upload_threads: + t.join() + + patches = dict() + [patches.setdefault(v, k) for k, v in patch_list] + for filename in patches.keys(): + base_content, new_content, is_binary, status = files[filename] + file_id_str = patches.get(filename) + if file_id_str.find("nobase") != -1: + base_content = None + file_id_str = file_id_str[file_id_str.rfind("_") + 1:] + file_id = int(file_id_str) + if base_content != None: + StartUploadFile(filename, file_id, base_content, is_binary, status, True) + if new_content != None: + StartUploadFile(filename, file_id, new_content, is_binary, status, False) + WaitForUploads() + + def IsImage(self, filename): + """Returns true if the filename has an image extension.""" + mimetype = mimetypes.guess_type(filename)[0] + if not mimetype: + return False + return mimetype.startswith("image/") + + def IsBinary(self, filename): + """Returns true if the guessed mimetyped isnt't in text group.""" + mimetype = mimetypes.guess_type(filename)[0] + if not mimetype: + return False # e.g. README, "real" binaries usually have an extension + # special case for text files which don't start with text/ + if mimetype in TEXT_MIMETYPES: + return False + return not mimetype.startswith("text/") + +class FakeMercurialUI(object): + def __init__(self): + self.quiet = True + self.output = '' + + def write(self, *args, **opts): + self.output += ' '.join(args) +use_hg_shell = False # set to True to shell out to hg always; slower class MercurialVCS(VersionControlSystem): - """Implementation of the VersionControlSystem interface for Mercurial.""" - - def __init__(self, options, repo_dir): - super(MercurialVCS, self).__init__(options) - # Absolute path to repository (we can be in a subdir) - self.repo_dir = os.path.normpath(repo_dir) - # Compute the subdir - cwd = os.path.normpath(os.getcwd()) - assert cwd.startswith(self.repo_dir) - self.subdir = cwd[len(self.repo_dir):].lstrip(r"\/") - if self.options.revision: - self.base_rev = self.options.revision - else: - mqparent, err = RunShellWithReturnCode(['hg', 'log', '--rev', 'qparent', '--template={node}']) - if not err: - self.base_rev = mqparent - else: - self.base_rev = RunShell(["hg", "parent", "-q"]).split(':')[1].strip() - def _GetRelPath(self, filename): - """Get relative path of a file according to the current directory, - given its logical path in the repo.""" - assert filename.startswith(self.subdir), (filename, self.subdir) - return filename[len(self.subdir):].lstrip(r"\/") - - def GenerateDiff(self, extra_args): - # If no file specified, restrict to the current subdir - extra_args = extra_args or ["."] - cmd = ["hg", "diff", "--git", "-r", self.base_rev] + extra_args - data = RunShell(cmd, silent_ok=True) - svndiff = [] - filecount = 0 - for line in data.splitlines(): - m = re.match("diff --git a/(\S+) b/(\S+)", line) - if m: - # Modify line to make it look like as it comes from svn diff. - # With this modification no changes on the server side are required - # to make upload.py work with Mercurial repos. - # NOTE: for proper handling of moved/copied files, we have to use - # the second filename. - filename = m.group(2) - svndiff.append("Index: %s" % filename) - svndiff.append("=" * 67) - filecount += 1 - logging.info(line) - else: - svndiff.append(line) - if not filecount: - ErrorExit("No valid patches found in output from hg diff") - return "\n".join(svndiff) + "\n" - - def GetUnknownFiles(self): - """Return a list of files unknown to the VCS.""" - args = [] - status = RunShell(["hg", "status", "--rev", self.base_rev, "-u", "."], - silent_ok=True) - unknown_files = [] - for line in status.splitlines(): - st, fn = line.split(" ", 1) - if st == "?": - unknown_files.append(fn) - return unknown_files - - def GetBaseFile(self, filename): - # "hg status" and "hg cat" both take a path relative to the current subdir - # rather than to the repo root, but "hg diff" has given us the full path - # to the repo root. - base_content = "" - new_content = None - is_binary = False - oldrelpath = relpath = self._GetRelPath(filename) - # "hg status -C" returns two lines for moved/copied files, one otherwise - out = RunShell(["hg", "status", "-C", "--rev", self.base_rev, relpath]) - out = out.splitlines() - # HACK: strip error message about missing file/directory if it isn't in - # the working copy - if out[0].startswith('%s: ' % relpath): - out = out[1:] - status, what = out[0].split(' ', 1) - if len(out) > 1 and status == "A" and what == relpath: - oldrelpath = out[1].strip() - status = "M" - if ":" in self.base_rev: - base_rev = self.base_rev.split(":", 1)[0] - else: - base_rev = self.base_rev - if status != "A": - base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath], - silent_ok=True) - is_binary = "\0" in base_content # Mercurial's heuristic - if status != "R": - new_content = open(relpath, "rb").read() - is_binary = is_binary or "\0" in new_content - if is_binary and base_content: - # Fetch again without converting newlines - base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath], - silent_ok=True, universal_newlines=False) - if not is_binary or not self.IsImage(relpath): - new_content = None - return base_content, new_content, is_binary, status + """Implementation of the VersionControlSystem interface for Mercurial.""" + + def __init__(self, options, ui, repo): + super(MercurialVCS, self).__init__(options) + self.ui = ui + self.repo = repo + # Absolute path to repository (we can be in a subdir) + self.repo_dir = os.path.normpath(repo.root) + # Compute the subdir + cwd = os.path.normpath(os.getcwd()) + assert cwd.startswith(self.repo_dir) + self.subdir = cwd[len(self.repo_dir):].lstrip(r"\/") + if self.options.revision: + self.base_rev = self.options.revision + else: + mqparent, err = RunShellWithReturnCode(['hg', 'log', '--rev', 'qparent', '--template={node}']) + if not err: + self.base_rev = mqparent + else: + self.base_rev = RunShell(["hg", "parents", "-q"]).split(':')[1].strip() + def _GetRelPath(self, filename): + """Get relative path of a file according to the current directory, + given its logical path in the repo.""" + assert filename.startswith(self.subdir), (filename, self.subdir) + return filename[len(self.subdir):].lstrip(r"\/") + + def GenerateDiff(self, extra_args): + # If no file specified, restrict to the current subdir + extra_args = extra_args or ["."] + cmd = ["hg", "diff", "--git", "-r", self.base_rev] + extra_args + data = RunShell(cmd, silent_ok=True) + svndiff = [] + filecount = 0 + for line in data.splitlines(): + m = re.match("diff --git a/(\S+) b/(\S+)", line) + if m: + # Modify line to make it look like as it comes from svn diff. + # With this modification no changes on the server side are required + # to make upload.py work with Mercurial repos. + # NOTE: for proper handling of moved/copied files, we have to use + # the second filename. + filename = m.group(2) + svndiff.append("Index: %s" % filename) + svndiff.append("=" * 67) + filecount += 1 + logging.info(line) + else: + svndiff.append(line) + if not filecount: + ErrorExit("No valid patches found in output from hg diff") + return "\n".join(svndiff) + "\n" + + def GetUnknownFiles(self): + """Return a list of files unknown to the VCS.""" + args = [] + status = RunShell(["hg", "status", "--rev", self.base_rev, "-u", "."], + silent_ok=True) + unknown_files = [] + for line in status.splitlines(): + st, fn = line.split(" ", 1) + if st == "?": + unknown_files.append(fn) + return unknown_files + + def GetBaseFile(self, filename): + set_status("inspecting " + filename) + # "hg status" and "hg cat" both take a path relative to the current subdir + # rather than to the repo root, but "hg diff" has given us the full path + # to the repo root. + base_content = "" + new_content = None + is_binary = False + oldrelpath = relpath = self._GetRelPath(filename) + # "hg status -C" returns two lines for moved/copied files, one otherwise + if use_hg_shell: + out = RunShell(["hg", "status", "-C", "--rev", self.base_rev, relpath]) + else: + fui = FakeMercurialUI() + ret = commands.status(fui, self.repo, *[relpath], **{'rev': [self.base_rev], 'copies': True}) + if ret: + raise util.Abort(ret) + out = fui.output + out = out.splitlines() + # HACK: strip error message about missing file/directory if it isn't in + # the working copy + if out[0].startswith('%s: ' % relpath): + out = out[1:] + status, what = out[0].split(' ', 1) + if len(out) > 1 and status == "A" and what == relpath: + oldrelpath = out[1].strip() + status = "M" + if ":" in self.base_rev: + base_rev = self.base_rev.split(":", 1)[0] + else: + base_rev = self.base_rev + if status != "A": + if use_hg_shell: + base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath], silent_ok=True) + else: + base_content = str(self.repo[base_rev][oldrelpath].data()) + is_binary = "\0" in base_content # Mercurial's heuristic + if status != "R": + new_content = open(relpath, "rb").read() + is_binary = is_binary or "\0" in new_content + if is_binary and base_content and use_hg_shell: + # Fetch again without converting newlines + base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath], + silent_ok=True, universal_newlines=False) + if not is_binary or not self.IsImage(relpath): + new_content = None + return base_content, new_content, is_binary, status # NOTE: The SplitPatch function is duplicated in engine.py, keep them in sync. def SplitPatch(data): - """Splits a patch into separate pieces for each file. - - Args: - data: A string containing the output of svn diff. - - Returns: - A list of 2-tuple (filename, text) where text is the svn diff output - pertaining to filename. - """ - patches = [] - filename = None - diff = [] - for line in data.splitlines(True): - new_filename = None - if line.startswith('Index:'): - unused, new_filename = line.split(':', 1) - new_filename = new_filename.strip() - elif line.startswith('Property changes on:'): - unused, temp_filename = line.split(':', 1) - # When a file is modified, paths use '/' between directories, however - # when a property is modified '\' is used on Windows. Make them the same - # otherwise the file shows up twice. - temp_filename = temp_filename.strip().replace('\\', '/') - if temp_filename != filename: - # File has property changes but no modifications, create a new diff. - new_filename = temp_filename - if new_filename: - if filename and diff: - patches.append((filename, ''.join(diff))) - filename = new_filename - diff = [line] - continue - if diff is not None: - diff.append(line) - if filename and diff: - patches.append((filename, ''.join(diff))) - return patches + """Splits a patch into separate pieces for each file. + + Args: + data: A string containing the output of svn diff. + + Returns: + A list of 2-tuple (filename, text) where text is the svn diff output + pertaining to filename. + """ + patches = [] + filename = None + diff = [] + for line in data.splitlines(True): + new_filename = None + if line.startswith('Index:'): + unused, new_filename = line.split(':', 1) + new_filename = new_filename.strip() + elif line.startswith('Property changes on:'): + unused, temp_filename = line.split(':', 1) + # When a file is modified, paths use '/' between directories, however + # when a property is modified '\' is used on Windows. Make them the same + # otherwise the file shows up twice. + temp_filename = temp_filename.strip().replace('\\', '/') + if temp_filename != filename: + # File has property changes but no modifications, create a new diff. + new_filename = temp_filename + if new_filename: + if filename and diff: + patches.append((filename, ''.join(diff))) + filename = new_filename + diff = [line] + continue + if diff is not None: + diff.append(line) + if filename and diff: + patches.append((filename, ''.join(diff))) + return patches def UploadSeparatePatches(issue, rpc_server, patchset, data, options): - """Uploads a separate patch for each file in the diff output. - - Returns a list of [patch_key, filename] for each file. - """ - patches = SplitPatch(data) - rv = [] - for patch in patches: - if len(patch[1]) > MAX_UPLOAD_SIZE: - print ("Not uploading the patch for " + patch[0] + - " because the file is too large.") - continue - form_fields = [("filename", patch[0])] - if not options.download_base: - form_fields.append(("content_upload", "1")) - files = [("data", "data.diff", patch[1])] - ctype, body = EncodeMultipartFormData(form_fields, files) - url = "/%d/upload_patch/%d" % (int(issue), int(patchset)) - print "Uploading patch for " + patch[0] - response_body = rpc_server.Send(url, body, content_type=ctype) - lines = response_body.splitlines() - if not lines or lines[0] != "OK": - StatusUpdate(" --> %s" % response_body) - sys.exit(1) - rv.append([lines[1], patch[0]]) - return rv - - -def GuessVCSName(): - """Helper to guess the version control system. - - This examines the current directory, guesses which VersionControlSystem - we're using, and returns an string indicating which VCS is detected. - - Returns: - A pair (vcs, output). vcs is a string indicating which VCS was detected - and is one of VCS_GIT, VCS_MERCURIAL, VCS_SUBVERSION, or VCS_UNKNOWN. - output is a string containing any interesting output from the vcs - detection routine, or None if there is nothing interesting. - """ - # Mercurial has a command to get the base directory of a repository - # Try running it, but don't die if we don't have hg installed. - # NOTE: we try Mercurial first as it can sit on top of an SVN working copy. - try: - out, returncode = RunShellWithReturnCode(["hg", "root"]) - if returncode == 0: - return (VCS_MERCURIAL, out.strip()) - except OSError, (errno, message): - if errno != 2: # ENOENT -- they don't have hg installed. - raise - - # Subversion has a .svn in all working directories. - if os.path.isdir('.svn'): - logging.info("Guessed VCS = Subversion") - return (VCS_SUBVERSION, None) - - # Git has a command to test if you're in a git tree. - # Try running it, but don't die if we don't have git installed. - try: - out, returncode = RunShellWithReturnCode(["git", "rev-parse", - "--is-inside-work-tree"]) - if returncode == 0: - return (VCS_GIT, None) - except OSError, (errno, message): - if errno != 2: # ENOENT -- they don't have git installed. - raise - - return (VCS_UNKNOWN, None) - - -def GuessVCS(options): - """Helper to guess the version control system. - - This verifies any user-specified VersionControlSystem (by command line - or environment variable). If the user didn't specify one, this examines - the current directory, guesses which VersionControlSystem we're using, - and returns an instance of the appropriate class. Exit with an error - if we can't figure it out. - - Returns: - A VersionControlSystem instance. Exits if the VCS can't be guessed. - """ - vcs = options.vcs - if not vcs: - vcs = os.environ.get("CODEREVIEW_VCS") - if vcs: - v = VCS_ABBREVIATIONS.get(vcs.lower()) - if v is None: - ErrorExit("Unknown version control system %r specified." % vcs) - (vcs, extra_output) = (v, None) - else: - (vcs, extra_output) = GuessVCSName() - - if vcs == VCS_MERCURIAL: - if extra_output is None: - extra_output = RunShell(["hg", "root"]).strip() - return MercurialVCS(options, extra_output) - elif vcs == VCS_SUBVERSION: - return SubversionVCS(options) - elif vcs == VCS_GIT: - return GitVCS(options) - - ErrorExit(("Could not guess version control system. " - "Are you in a working copy directory?")) - - -def RealMain(argv, data=None): - """The real main function. - - Args: - argv: Command line arguments. - data: Diff contents. If None (default) the diff is generated by - the VersionControlSystem implementation returned by GuessVCS(). - - Returns: - A 2-tuple (issue id, patchset id). - The patchset id is None if the base files are not uploaded by this - script (applies only to SVN checkouts). - """ - logging.basicConfig(format=("%(asctime).19s %(levelname)s %(filename)s:" - "%(lineno)s %(message)s ")) - os.environ['LC_ALL'] = 'C' - options, args = parser.parse_args(argv[1:]) - global verbosity - verbosity = options.verbose - if verbosity >= 3: - logging.getLogger().setLevel(logging.DEBUG) - elif verbosity >= 2: - logging.getLogger().setLevel(logging.INFO) - vcs = GuessVCS(options) - if isinstance(vcs, SubversionVCS): - # base field is only allowed for Subversion. - # Note: Fetching base files may become deprecated in future releases. - base = vcs.GuessBase(options.download_base) - else: - base = None - if not base and options.download_base: - options.download_base = True - logging.info("Enabled upload of base file") - if not options.assume_yes: - vcs.CheckForUnknownFiles() - if data is None: - data = vcs.GenerateDiff(args) - files = vcs.GetBaseFiles(data) - if verbosity >= 1: - print "Upload server:", options.server, "(change with -s/--server)" - if options.issue: - prompt = "Message describing this patch set: " - else: - prompt = "New issue subject: " - message = options.message or raw_input(prompt).strip() - if not message: - ErrorExit("A non-empty message is required") - rpc_server = GetRpcServer(options) - form_fields = [("subject", message)] - if base: - form_fields.append(("base", base)) - if options.issue: - form_fields.append(("issue", str(options.issue))) - if options.email: - form_fields.append(("user", options.email)) - if options.reviewers: - for reviewer in options.reviewers.split(','): - if "@" in reviewer and not reviewer.split("@")[1].count(".") == 1: - ErrorExit("Invalid email address: %s" % reviewer) - form_fields.append(("reviewers", options.reviewers)) - if options.cc: - for cc in options.cc.split(','): - if "@" in cc and not cc.split("@")[1].count(".") == 1: - ErrorExit("Invalid email address: %s" % cc) - form_fields.append(("cc", options.cc)) - description = options.description - if options.description_file: - if options.description: - ErrorExit("Can't specify description and description_file") - file = open(options.description_file, 'r') - description = file.read() - file.close() - if description: - form_fields.append(("description", description)) - # Send a hash of all the base file so the server can determine if a copy - # already exists in an earlier patchset. - base_hashes = "" - for file, info in files.iteritems(): - if not info[0] is None: - checksum = md5(info[0]).hexdigest() - if base_hashes: - base_hashes += "|" - base_hashes += checksum + ":" + file - form_fields.append(("base_hashes", base_hashes)) - if options.private: - if options.issue: - print "Warning: Private flag ignored when updating an existing issue." - else: - form_fields.append(("private", "1")) - # If we're uploading base files, don't send the email before the uploads, so - # that it contains the file status. - if options.send_mail and options.download_base: - form_fields.append(("send_mail", "1")) - if not options.download_base: - form_fields.append(("content_upload", "1")) - if len(data) > MAX_UPLOAD_SIZE: - print "Patch is large, so uploading file patches separately." - uploaded_diff_file = [] - form_fields.append(("separate_patches", "1")) - else: - uploaded_diff_file = [("data", "data.diff", data)] - ctype, body = EncodeMultipartFormData(form_fields, uploaded_diff_file) - response_body = rpc_server.Send("/upload", body, content_type=ctype) - patchset = None - if not options.download_base or not uploaded_diff_file: - lines = response_body.splitlines() - if len(lines) >= 2: - msg = lines[0] - patchset = lines[1].strip() - patches = [x.split(" ", 1) for x in lines[2:]] - else: - msg = response_body - else: - msg = response_body - if not response_body.startswith("Issue created.") and \ - not response_body.startswith("Issue updated."): - print >>sys.stderr, msg - sys.exit(0) - issue = msg[msg.rfind("/")+1:] - - if not uploaded_diff_file: - result = UploadSeparatePatches(issue, rpc_server, patchset, data, options) - if not options.download_base: - patches = result - - if not options.download_base: - vcs.UploadBaseFiles(issue, rpc_server, patches, patchset, options, files) - if options.send_mail: - rpc_server.Send("/" + issue + "/mail", payload="") - return issue, patchset - - -def main(): - try: - RealMain(sys.argv) - except KeyboardInterrupt: - print - StatusUpdate("Interrupted.") - sys.exit(1) + """Uploads a separate patch for each file in the diff output. + Returns a list of [patch_key, filename] for each file. + """ + patches = SplitPatch(data) + rv = [] + for patch in patches: + set_status("uploading patch for " + patch[0]) + if len(patch[1]) > MAX_UPLOAD_SIZE: + print ("Not uploading the patch for " + patch[0] + + " because the file is too large.") + continue + form_fields = [("filename", patch[0])] + if not options.download_base: + form_fields.append(("content_upload", "1")) + files = [("data", "data.diff", patch[1])] + ctype, body = EncodeMultipartFormData(form_fields, files) + url = "/%d/upload_patch/%d" % (int(issue), int(patchset)) + print "Uploading patch for " + patch[0] + response_body = rpc_server.Send(url, body, content_type=ctype) + lines = response_body.splitlines() + if not lines or lines[0] != "OK": + StatusUpdate(" --> %s" % response_body) + sys.exit(1) + rv.append([lines[1], patch[0]]) + return rv diff --git a/lib/godoc/godoc.html b/lib/godoc/godoc.html index aef7f4dc4..f1d9c2ad9 100644 --- a/lib/godoc/godoc.html +++ b/lib/godoc/godoc.html @@ -1,136 +1,45 @@ - + - - - {Title|html-esc} - - - - + +{.section Title} + {@|html-esc} - The Go Programming Language +{.or} + The Go Programming Language +{.end} + + + - - - - -
    - - - - - - -
    - Go Home Page - -
    The Go Programming Language
    -
    - -
    -
    - - - -
    +
    +
    +

    The Go Programming Language

    + + +
    +
    {.section Menu} +
    +

    Build version {Version|html-esc}. Except as noted, this content is licensed under a Creative Commons Attribution 3.0 License.

    +
    - - - diff --git a/lib/godoc/package.html b/lib/godoc/package.html index 0eff78e45..5dc61b7cd 100644 --- a/lib/godoc/package.html +++ b/lib/godoc/package.html @@ -6,17 +6,19 @@ {.section PAst}
    -	{@|html}
    +	{@ FSet|html}
     	
    {.end} {.section PDoc} {.section IsPkg} + {# ImportPath is a string - no need for FSet}

    import "{ImportPath|html-esc}"

    {.end} {Doc|html-comment} {.section IsPkg} {.section Filenames} + {# Filenames are strings - no need for FSet}

    Package files

    @@ -31,44 +33,46 @@

    Constants

    {.repeated section @} {Doc|html-comment} -
    {Decl|html}
    +
    {Decl FSet|html}
    {.end} {.end} {.section Vars}

    Variables

    {.repeated section @} {Doc|html-comment} -
    {Decl|html}
    +
    {Decl FSet|html}
    {.end} {.end} {.section Funcs} {.repeated section @} -

    func {Name|html-esc}

    -

    {Decl|html}

    + {# Name is a string - no need for FSet} +

    func {Name|html-esc}

    +

    {Decl FSet|html}

    {Doc|html-comment} {.end} {.end} {.section Types} {.repeated section @} -

    type {Type.Name|html-esc}

    + {# Type.Name is a string - no need for FSet} +

    type {Type.Name FSet|html-esc}

    {Doc|html-comment} -

    {Decl|html}

    +

    {Decl FSet|html}

    {.repeated section Consts} {Doc|html-comment} -
    {Decl|html}
    +
    {Decl FSet|html}
    {.end} {.repeated section Vars} {Doc|html-comment} -
    {Decl|html}
    +
    {Decl FSet|html}
    {.end} {.repeated section Factories} -

    func {Name|html-esc}

    -

    {Decl|html}

    +

    func {Name|html-esc}

    +

    {Decl FSet|html}

    {Doc|html-comment} {.end} {.repeated section Methods} -

    func ({Recv|html}) {Name|html-esc}

    -

    {Decl|html}

    +

    func ({Recv FSet|html}) {Name|html-esc}

    +

    {Decl FSet|html}

    {Doc|html-comment} {.end} {.end} @@ -83,12 +87,14 @@ {.section PList}

    Other packages

    + {# PLIst entries are strings - no need for FSet} {.repeated section @} - {@|html}
    + {@|html-esc}
    {.end}

    {.end} {.section Dirs} + {# DirList entries are numbers and strings - no need for FSet}

    Subdirectories

    @@ -98,12 +104,12 @@ - + {.repeated section List} {Depth|padding} - + diff --git a/lib/godoc/package.txt b/lib/godoc/package.txt index 124771edd..6fe992dbe 100644 --- a/lib/godoc/package.txt +++ b/lib/godoc/package.txt @@ -1,5 +1,5 @@ {.section PAst} -{@} +{@ FSet} {.end} {.section PDoc} {.section IsPkg} @@ -12,14 +12,14 @@ COMMAND DOCUMENTATION {.end} {.section Doc} -{@} +{@ FSet} {.end} {.section Consts} CONSTANTS {.repeated section @} -{Decl} +{Decl FSet} {Doc} {.end} {.end} @@ -28,7 +28,7 @@ CONSTANTS VARIABLES {.repeated section @} -{Decl} +{Decl FSet} {Doc} {.end} {.end} @@ -37,7 +37,7 @@ VARIABLES FUNCTIONS {.repeated section @} -{Decl} +{Decl FSet} {Doc} {.end} {.end} @@ -46,22 +46,22 @@ FUNCTIONS TYPES {.repeated section @} -{Decl} +{Decl FSet} {Doc} {.repeated section Consts} -{Decl} +{Decl FSet} {Doc} {.end} {.repeated section Vars} -{Decl} +{Decl FSet} {Doc} {.end} {.repeated section Factories} -{Decl} +{Decl FSet} {Doc} {.end} {.repeated section Methods} -{Decl} +{Decl FSet} {Doc} {.end} {.end} diff --git a/lib/godoc/search.html b/lib/godoc/search.html index febd7e569..3d3dd1958 100644 --- a/lib/godoc/search.html +++ b/lib/godoc/search.html @@ -4,10 +4,9 @@ license that can be found in the LICENSE file. --> -{.section Accurate} -{.or} +{.section Alert}

    - Indexing in progress - result may be inaccurate + {@}

    {.end} {.section Alt} @@ -26,8 +25,8 @@ {.repeated section Files} {.repeated section Groups} {.repeated section Infos} - {File.Path|url-src}:{@|infoLine} -
    {@|infoSnippet}
    + {File.Path|url-src}:{@|infoLine} + {@|infoSnippet} {.end} {.end} {.end} @@ -38,7 +37,7 @@ {.repeated section @}

    package {Pak.Name|html-esc}

    {.repeated section Files} - {File.Path|url-src} + {File.Path|url-src}
    Synopsis
    ....
    {Name|html-esc}{Name|html-esc} {Synopsis|html-esc}
    {.repeated section Groups} @@ -47,7 +46,7 @@ @@ -57,12 +56,36 @@ {.end} {.end} {.end} -{.section Illegal} -

    - Illegal query syntax -

    +{.section Textual} + {.section Complete} +

    {Found|html-esc} textual occurrences

    + {.or} +

    More than {Found|html-esc} textual occurrences

    +

    + Not all files or lines containing "{Query|html-esc}" are shown. +

    + {.end}

    - A legal query is a single identifier (such as ToLower) - or a qualified identifier (such as math.Sin). +

    {.repeated section Infos} - {@|infoLine} + {@|infoLine} {.end}
    + {.repeated section @} + + + + + + + + {.end} + {.section Complete} + {.or} + + {.end} +
    + {Filename|url-src}: + {Lines|numlines} + {.repeated section Lines} + {@|html-esc} + {.end} +
    ...

    {.end} diff --git a/lib/godoc/search.txt b/lib/godoc/search.txt index 90266292c..eff4d36fc 100644 --- a/lib/godoc/search.txt +++ b/lib/godoc/search.txt @@ -1,9 +1,8 @@ QUERY -{Query} + {Query} -{.section Accurate} -{.or} -INDEXING IN PROGRESS - RESULT MAY BE INACCURATE +{.section Alert} +{@} {.end} {.section Alt} @@ -45,9 +44,18 @@ package {Pak.Name} {.end} {.end} {.end} -{.section Illegal} -ILLEGAL QUERY SYNTAX +{.section Textual} +{.section Complete} +{Found} TEXTUAL OCCURENCES +{.or} +MORE THAN {Found} TEXTUAL OCCURENCES +{.end} -A legal query is a single identifier (such as ToLower) -or a qualified identifier (such as math.Sin). +{.repeated section @} +{Lines|numlines} {Filename|url-src} +{.end} +{.section Complete} +{.or} +... ... +{.end} {.end} diff --git a/lib/godoc/source.html b/lib/godoc/source.html deleted file mode 100644 index 645517012..000000000 --- a/lib/godoc/source.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - -{# Source is HTML-escaped elsewhere} -
    {Source}
    diff --git a/misc/arm/a b/misc/arm/a index 140b47e29..701f4941f 100755 --- a/misc/arm/a +++ b/misc/arm/a @@ -29,7 +29,7 @@ exp () rloc=/data/local/tmp/retval rsize=$(adb shell "ls -l $rloc"|tr -s ' '|cut -d' ' -f4) rcheck=38 -if [ $rsize != $rcheck ] ; then +if [ "$rsize" != "$rcheck" ]; then # echo "debug: retval size incorrect want $rcheck, got $rsize. uploading" echo >/tmp/adb.retval '#!/system/bin/sh "$@" @@ -39,11 +39,20 @@ echo RETVAL: $?' fi # run the main binary -if [ "$*" != "$1" ]; then - args=$(echo $*| cut -d' ' -f2-) +if [ "-g" == "$1" ]; then + adb forward tcp:$2 tcp:$2 + args=$(echo $*| cut -d' ' -f4-) + adb push $3 /data/local/tmp/$3 >/dev/null 2>&1 + adb shell "$(exp GOARCH) $(exp GOTRACEBACK) $(exp GOGC) \ + gdbserver :$2 /data/local/tmp/retval /data/local/tmp/$3 $args" \ + 2>&1|tr -d '\r' |tee /tmp/adb.out|grep -v RETVAL +else + if [ "$*" != "$1" ]; then + args=$(echo $*| cut -d' ' -f2-) + fi + adb push $1 /data/local/tmp/$1 >/dev/null 2>&1 + adb shell "$(exp GOARCH) $(exp GOTRACEBACK) $(exp GOGC) \ + /data/local/tmp/retval /data/local/tmp/$1 $args" \ + 2>&1|tr -d '\r' |tee /tmp/adb.out|grep -v RETVAL fi -adb push $1 /data/local/tmp/$1 >/dev/null 2>&1 -adb shell "$(exp GOARCH) $(exp GOTRACEBACK) $(exp GOGC) \ - /data/local/tmp/retval /data/local/tmp/$1 $args" \ - 2>&1|tr -d '\r' |tee /tmp/adb.out|grep -v RETVAL exit $(grep RETVAL /tmp/adb.out|tr -d '\n\r'| cut -d' ' -f2) diff --git a/misc/bash/go b/misc/bash/go index 711020ac9..caced154f 100644 --- a/misc/bash/go +++ b/misc/bash/go @@ -3,4 +3,4 @@ complete -f -X '!*.8' 8l complete -f -X '!*.6' 6l complete -f -X '!*.5' 5l -complete -f -X '!*.go' 8g 6g 5g +complete -f -X '!*.go' 8g 6g 5g gofmt gccgo diff --git a/misc/bbedit/Go.plist b/misc/bbedit/Go.plist index 71bb9bc5e..39c8f0dc3 100755 --- a/misc/bbedit/Go.plist +++ b/misc/bbedit/Go.plist @@ -6,15 +6,22 @@ BBLMColorsSyntax = YES; BBLMIsCaseSensitive = YES; BBLMKeywordList = ( + append, bool, break, byte, cap, case, chan, + close, + closed, cmplx, + complex, + complex128, + complex64, const, continue, + copy, default, defer, else, @@ -43,8 +50,12 @@ new, nil, package, + panic, + print, + println, range, real, + recover, return, select, string, diff --git a/misc/cgo/gmp/Makefile b/misc/cgo/gmp/Makefile index ad5db33c2..fc6209f27 100644 --- a/misc/cgo/gmp/Makefile +++ b/misc/cgo/gmp/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../src/Make.$(GOARCH) +include ../../../src/Make.inc TARG=gmp diff --git a/misc/cgo/life/Makefile b/misc/cgo/life/Makefile index cbcdc9927..5a10380ed 100644 --- a/misc/cgo/life/Makefile +++ b/misc/cgo/life/Makefile @@ -2,30 +2,20 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../src/Make.$(GOARCH) +include ../../../src/Make.inc TARG=life CGOFILES=\ - life.go + life.go\ -LDPATH_freebsd=-Wl,-R,`pwd` -LDPATH_linux=-Wl,-R,`pwd` -LDPATH_darwin= +CGO_OFILES=\ + c-life.o\ -CGO_LDFLAGS=_cgo_export.o c-life.so $(LDPATH_$(GOOS)) -CGO_DEPS=_cgo_export.o c-life.so - -CLEANFILES += life +CLEANFILES+=life include ../../../src/Make.pkg -c-life.o: c-life.c _cgo_export.h - gcc $(_CGO_CFLAGS_$(GOARCH)) -g -c -fPIC $(CFLAGS) c-life.c - -c-life.so: c-life.o - gcc $(_CGO_CFLAGS_$(GOARCH)) -o $@ c-life.o $(_CGO_LDFLAGS_$(GOOS)) - life: install main.go $(GC) main.go $(LD) -o $@ main.$O diff --git a/misc/cgo/life/c-life.c b/misc/cgo/life/c-life.c index 71555a9c7..657245595 100644 --- a/misc/cgo/life/c-life.c +++ b/misc/cgo/life/c-life.c @@ -6,6 +6,8 @@ #include "life.h" #include "_cgo_export.h" +const int MYCONST = 0; + // Do the actual manipulation of the life board in C. This could be // done easily in Go, we are just using C for demonstration // purposes. diff --git a/misc/cgo/life/golden.out b/misc/cgo/life/golden.out new file mode 100644 index 000000000..539d2106d --- /dev/null +++ b/misc/cgo/life/golden.out @@ -0,0 +1,17 @@ +* life + + + XXX XXX + + + + + + + + XXX XXX + + + + + diff --git a/misc/cgo/life/life.go b/misc/cgo/life/life.go index 036802853..ec000ce3a 100644 --- a/misc/cgo/life/life.go +++ b/misc/cgo/life/life.go @@ -23,7 +23,7 @@ var chans [4]chan bool //export GoStart // Double return value is just for testing. func GoStart(i, xdim, ydim, xstart, xend, ystart, yend C.int, a *C.int, n *C.int) (int, int) { - c := make(chan bool) + c := make(chan bool, int(C.MYCONST)) go func() { C.DoStep(xdim, ydim, xstart, xend, ystart, yend, a, n) c <- true diff --git a/misc/cgo/life/life.h b/misc/cgo/life/life.h index b6e94cf1d..b2011b25f 100644 --- a/misc/cgo/life/life.h +++ b/misc/cgo/life/life.h @@ -4,3 +4,4 @@ extern void Step(int, int, int *, int *); extern void DoStep(int, int, int, int, int, int, int *, int *); +extern const int MYCONST; diff --git a/misc/cgo/life/main.go b/misc/cgo/life/main.go index 7c2c0c73e..9cfed434b 100644 --- a/misc/cgo/life/main.go +++ b/misc/cgo/life/main.go @@ -29,7 +29,7 @@ func main() { } } - life.Run(*gen, *dim, *dim, &a) + life.Run(*gen, *dim, *dim, a[:]) for i := 0; i < *dim; i++ { for j := 0; j < *dim; j++ { diff --git a/misc/cgo/life/test.bash b/misc/cgo/life/test.bash new file mode 100755 index 000000000..5c5fba1a9 --- /dev/null +++ b/misc/cgo/life/test.bash @@ -0,0 +1,11 @@ +#!/bin/sh +# 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. + +set -e +gomake life +echo '*' life >run.out +./life >>run.out +diff run.out golden.out +gomake clean diff --git a/misc/cgo/stdio/Makefile b/misc/cgo/stdio/Makefile index 2e3d46631..fc925e607 100644 --- a/misc/cgo/stdio/Makefile +++ b/misc/cgo/stdio/Makefile @@ -2,16 +2,19 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../src/Make.$(GOARCH) +include ../../../src/Make.inc TARG=stdio CGOFILES=\ - file.go + align.go\ + file.go\ + test.go\ + test1.go\ CLEANFILES+=hello fib chain run.out include ../../../src/Make.pkg %: install %.go - $(QUOTED_GOBIN)/$(GC) $*.go - $(QUOTED_GOBIN)/$(LD) -o $@ $*.$O + $(GC) $*.go + $(LD) -o $@ $*.$O diff --git a/misc/cgo/stdio/align.go b/misc/cgo/stdio/align.go new file mode 100644 index 000000000..6cdfd902f --- /dev/null +++ b/misc/cgo/stdio/align.go @@ -0,0 +1,78 @@ +package stdio + +/* +#include + +typedef unsigned char Uint8; +typedef unsigned short Uint16; + +typedef enum { + MOD1 = 0x0000, + MODX = 0x8000 +} SDLMod; + +typedef enum { + A = 1, + B = 322, + SDLK_LAST +} SDLKey; + +typedef struct SDL_keysym { + Uint8 scancode; + SDLKey sym; + SDLMod mod; + Uint16 unicode; +} SDL_keysym; + +typedef struct SDL_KeyboardEvent { + Uint8 typ; + Uint8 which; + Uint8 state; + SDL_keysym keysym; +} SDL_KeyboardEvent; + +void makeEvent(SDL_KeyboardEvent *event) { + unsigned char *p; + int i; + + p = (unsigned char*)event; + for (i=0; ityp == typ && e->which == which && e->state == state && e->keysym.scancode == scan && e->keysym.sym == sym && e->keysym.mod == mod && e->keysym.unicode == uni; +} + +void cTest(SDL_KeyboardEvent *event) { + printf("C: %#x %#x %#x %#x %#x %#x %#x\n", event->typ, event->which, event->state, + event->keysym.scancode, event->keysym.sym, event->keysym.mod, event->keysym.unicode); + fflush(stdout); +} + +*/ +import "C" + +import ( + "fmt" + "syscall" +) + +func TestAlign() { + if syscall.ARCH == "amd64" { + // alignment is known to be broken on amd64. + // http://code.google.com/p/go/issues/detail?id=609 + return + } + var evt C.SDL_KeyboardEvent + C.makeEvent(&evt) + if C.same(&evt, evt.typ, evt.which, evt.state, evt.keysym.scancode, evt.keysym.sym, evt.keysym.mod, evt.keysym.unicode) == 0 { + fmt.Println("*** bad alignment") + C.cTest(&evt) + fmt.Printf("Go: %#x %#x %#x %#x %#x %#x %#x\n", + evt.typ, evt.which, evt.state, evt.keysym.scancode, + evt.keysym.sym, evt.keysym.mod, evt.keysym.unicode) + fmt.Println(evt) + } +} diff --git a/misc/cgo/stdio/chain.go b/misc/cgo/stdio/chain.go index dd5e01542..c2b105072 100644 --- a/misc/cgo/stdio/chain.go +++ b/misc/cgo/stdio/chain.go @@ -22,7 +22,7 @@ func link(left chan<- int, right <-chan int) { runtime.LockOSThread() for { v := <-right - stdio.Puts(strconv.Itoa(v)) + stdio.Stdout.WriteString(strconv.Itoa(v) + "\n") left <- 1+v } } @@ -38,6 +38,6 @@ func main() { for i := 0; i < R; i++ { right <- 0 x := <-leftmost - stdio.Puts(strconv.Itoa(x)) + stdio.Stdout.WriteString(strconv.Itoa(x) + "\n") } } diff --git a/misc/cgo/stdio/fib.go b/misc/cgo/stdio/fib.go index 63ae04988..c02e31fd8 100644 --- a/misc/cgo/stdio/fib.go +++ b/misc/cgo/stdio/fib.go @@ -26,7 +26,7 @@ func fibber(c, out chan int64, i int64) { } for { j := <-c - stdio.Puts(strconv.Itoa64(j)) + stdio.Stdout.WriteString(strconv.Itoa64(j) + "\n") out <- j <-out i += j diff --git a/misc/cgo/stdio/file.go b/misc/cgo/stdio/file.go index 7d1f22280..021cbf909 100644 --- a/misc/cgo/stdio/file.go +++ b/misc/cgo/stdio/file.go @@ -10,33 +10,35 @@ see ../gmp/gmp.go. package stdio -// TODO(rsc): Remove fflushstdout when C.fflush(C.stdout) works in cgo. - /* #include #include +#include +#include -void fflushstdout(void) { fflush(stdout); } +char* greeting = "hello, world"; */ import "C" import "unsafe" -/* type File C.FILE var Stdout = (*File)(C.stdout) var Stderr = (*File)(C.stderr) -func (f *File) WriteString(s string) { - p := C.CString(s); - C.fputs(p, (*C.FILE)(f)); - C.free(p); -} -*/ +// Test reference to library symbol. +// Stdout and stderr are too special to be a reliable test. +var myerr = C.sys_errlist -func Puts(s string) { +func (f *File) WriteString(s string) { p := C.CString(s) - C.puts(p) + C.fputs(p, (*C.FILE)(f)) C.free(unsafe.Pointer(p)) - C.fflushstdout() + f.Flush() +} + +func (f *File) Flush() { + C.fflush((*C.FILE)(f)) } + +var Greeting = C.GoString(C.greeting) diff --git a/misc/cgo/stdio/hello.go b/misc/cgo/stdio/hello.go index 47f9de02f..9cb6e6884 100644 --- a/misc/cgo/stdio/hello.go +++ b/misc/cgo/stdio/hello.go @@ -4,9 +4,26 @@ package main -import "stdio" +import ( + "os" + "stdio" +) func main() { - // stdio.Stdout.WriteString("hello, world\n"); - stdio.Puts("hello, world") + stdio.Stdout.WriteString(stdio.Greeting + "\n") + + l := stdio.Atol("123") + if l != 123 { + println("Atol 123: ", l) + panic("bad atol") + } + + n, err := stdio.Strtol("asdf", 123) + if n != 0 || err != os.EINVAL { + println("Strtol: ", n, err) + panic("bad atoi2") + } + + stdio.TestAlign() + stdio.TestEnum() } diff --git a/misc/cgo/stdio/test.bash b/misc/cgo/stdio/test.bash index b8b5f6911..82e3f7b45 100755 --- a/misc/cgo/stdio/test.bash +++ b/misc/cgo/stdio/test.bash @@ -4,8 +4,7 @@ # license that can be found in the LICENSE file. set -e -GOBIN="${GOBIN:-$HOME/bin}" -"$GOBIN"/gomake hello fib chain +gomake hello fib chain echo '*' hello >run.out ./hello >>run.out echo '*' fib >>run.out @@ -13,4 +12,4 @@ echo '*' fib >>run.out echo '*' chain >>run.out ./chain >>run.out diff run.out golden.out -"$GOBIN"/gomake clean +gomake clean diff --git a/misc/cgo/stdio/test.go b/misc/cgo/stdio/test.go new file mode 100644 index 000000000..8f21603ca --- /dev/null +++ b/misc/cgo/stdio/test.go @@ -0,0 +1,144 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains test cases for cgo. + +package stdio + +/* +#include +#include +#include +#include + +#define SHIFT(x, y) ((x)<<(y)) +#define KILO SHIFT(1, 10) + +enum E { + Enum1 = 1, + Enum2 = 2, +}; + +typedef unsigned char uuid_t[20]; + +void uuid_generate(uuid_t x) { + x[0] = 0; +} + +struct S { + int x; +}; + +extern enum E myConstFunc(struct S* const ctx, int const id, struct S **const filter); + +enum E myConstFunc(struct S *const ctx, int const id, struct S **const filter) { return 0; } + +// issue 1222 +typedef union { + long align; +} xxpthread_mutex_t; + +struct ibv_async_event { + union { + int x; + } element; +}; + +struct ibv_context { + xxpthread_mutex_t mutex; +}; +*/ +import "C" +import ( + "os" + "unsafe" +) + +const EINVAL = C.EINVAL /* test #define */ + +var KILO = C.KILO + +func uuidgen() { + var uuid C.uuid_t + C.uuid_generate(&uuid[0]) +} + +func Size(name string) (int64, os.Error) { + var st C.struct_stat + p := C.CString(name) + _, err := C.stat(p, &st) + C.free(unsafe.Pointer(p)) + if err != nil { + return 0, err + } + return int64(C.ulong(st.st_size)), nil +} + +func Strtol(s string, base int) (int, os.Error) { + p := C.CString(s) + n, err := C.strtol(p, nil, C.int(base)) + C.free(unsafe.Pointer(p)) + return int(n), err +} + +func Atol(s string) int { + p := C.CString(s) + n := C.atol(p) + C.free(unsafe.Pointer(p)) + return int(n) +} + +func TestConst() { + C.myConstFunc(nil, 0, nil) +} + +func TestEnum() { + if C.Enum1 != 1 || C.Enum2 != 2 { + println("bad enum", C.Enum1, C.Enum2) + } +} + +func TestAtol() { + l := Atol("123") + if l != 123 { + println("Atol 123: ", l) + panic("bad atol") + } +} + +func TestErrno() { + n, err := Strtol("asdf", 123) + if n != 0 || err != os.EINVAL { + println("Strtol: ", n, err) + panic("bad strtol") + } +} + +func TestMultipleAssign() { + p := C.CString("123") + n, m := C.strtol(p, nil, 345), C.strtol(p, nil, 10) + if n != 0 || m != 234 { + println("Strtol x2: ", n, m) + panic("bad strtol x2") + } + C.free(unsafe.Pointer(p)) +} + +var ( + uint = (C.uint)(0) + ulong C.ulong + char C.char +) + +type Context struct { + ctx *C.struct_ibv_context +} + +func Test() { + TestAlign() + TestAtol() + TestEnum() + TestErrno() + TestConst() +} diff --git a/misc/cgo/stdio/test1.go b/misc/cgo/stdio/test1.go new file mode 100644 index 000000000..dce2ef83c --- /dev/null +++ b/misc/cgo/stdio/test1.go @@ -0,0 +1,29 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains test cases for cgo. + +package stdio + +/* +// issue 1222 +typedef union { + long align; +} xxpthread_mutex_t; + +struct ibv_async_event { + union { + int x; + } element; +}; + +struct ibv_context { + xxpthread_mutex_t mutex; +}; +*/ +import "C" + +type AsyncEvent struct { + event C.struct_ibv_async_event +} diff --git a/misc/dashboard/README b/misc/dashboard/README index b2bc3c2d3..72d5546a4 100644 --- a/misc/dashboard/README +++ b/misc/dashboard/README @@ -24,11 +24,19 @@ export GOARCH=XXX export GOOS=XXX export GOBIN=/gobuild/bin export PATH=$PATH:/gobuild/bin -export BUILDER=XXX +export BUILDER=$GOOS-$GOARCH export BUILDHOST=godashboard.appspot.com -* Write the key ~gobuild/.gobuildkey (you need to get it from someone who knows - the key) +* Write the key ~gobuild/.gobuildkey + You need to get it from someone who knows the key. + You may also use a filename of the form .gobuildkey-$BUILDER if you + wish to run builders for multiple targets. + +* Append your username and password googlecode.com credentials from + https://code.google.com/hosting/settings + to the buildkey file in the format "Username\nPassword\n". + (This is for uploading tarballs to the project downloads section, + and is an optional step.) * sudo apt-get install bison gcc libc6-dev ed make * cd ~gobuild diff --git a/misc/dashboard/buildcontrol.py b/misc/dashboard/buildcontrol.py index 91b684f79..ec503e7ff 100644 --- a/misc/dashboard/buildcontrol.py +++ b/misc/dashboard/buildcontrol.py @@ -18,6 +18,8 @@ buildhost = '' buildport = -1 buildkey = '' +upload_project = "go" + def main(args): global buildport, buildhost, buildkey @@ -35,14 +37,23 @@ def main(args): buildport = int(os.environ['BUILDPORT']) try: - buildkey = file('%s/.gobuildkey-%s' % (os.environ['HOME'], os.environ['BUILDER']), 'r').read().strip() + buildkeyfile = file('%s/.gobuildkey-%s' % (os.environ['HOME'], os.environ['BUILDER']), 'r') + buildkey = buildkeyfile.readline().strip() except IOError: try: - buildkey = file('%s/.gobuildkey' % os.environ['HOME'], 'r').read().strip() + buildkeyfile = file('%s/.gobuildkey' % os.environ['HOME'], 'r') + buildkey = buildkeyfile.readline().strip() except IOError: print >>sys.stderr, "Need key in ~/.gobuildkey-%s or ~/.gobuildkey" % os.environ['BUILDER'] return + # get upload credentials + try: + username = buildkeyfile.readline().strip() + password = buildkeyfile.readline().strip() + except: + username, password = None, None + if args[1] == 'init': return doInit(args) elif args[1] == 'hwget': @@ -55,6 +66,8 @@ def main(args): return doRecord(args) elif args[1] == 'benchmarks': return doBenchmarks(args) + elif args[1] == 'upload': + return doUpload(args, username, password) else: return usage(args[0]) @@ -68,6 +81,7 @@ Commands: next : get the next revision number to by built by the given builder record : record a build result benchmarks : record benchmark numbers + upload : upload tarball to googlecode ''' % name) return 1 @@ -165,6 +179,29 @@ def doBenchmarks(args): e.append(b) return command('benchmarks', {'node': c.node, 'builder': builder, 'benchmarkdata': binascii.b2a_base64(''.join(e))}) +def doUpload(args, username, password): + # fail gracefully if no username or password set + if not username or not password: + return + + if len(args) != 5: + return usage(args[0]) + builder = args[2] + summary = args[3] + filename = args[4] + + from googlecode_upload import upload + code, msg, url = upload( + filename, # filename + upload_project, # 'go' + username, + password, + summary, + builder.split('-'), # labels + ) + if code != 201: + raise Failed('Upload returned code %s msg "%s".' % (code, msg)) + def encodeMultipartFormdata(fields, files): """fields is a sequence of (name, value) elements for regular form fields. files is a sequence of (name, filename, value) elements for data to be uploaded as files""" diff --git a/misc/dashboard/buildcron.sh b/misc/dashboard/buildcron.sh index 5f4300796..7aa70ce57 100644 --- a/misc/dashboard/buildcron.sh +++ b/misc/dashboard/buildcron.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/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. @@ -48,7 +48,7 @@ fi mkdir -p $GOROOT/bin cd $GOROOT/.. -cp go/misc/dashboard/builder.sh go/misc/dashboard/buildcontrol.py . +cp go/misc/dashboard/{builder.sh,buildcontrol.py,googlecode_upload.py} . chmod a+x builder.sh buildcontrol.py cd go ../buildcontrol.py next $BUILDER diff --git a/misc/dashboard/builder.sh b/misc/dashboard/builder.sh index b302acec2..4a8d117bf 100644 --- a/misc/dashboard/builder.sh +++ b/misc/dashboard/builder.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # Copyright 2009 The Go Authors. All rights reserved. # Use of this source code is governed by a BSD-style @@ -31,8 +31,9 @@ fi export PATH=$PATH:`pwd`/candidate/bin export GOBIN=`pwd`/candidate/bin +export GOROOT_FINAL=/usr/local/go -while true ; do +while true ; do ( cd go || fatal "Cannot cd into 'go'" hg pull -u || fatal "hg sync failed" rev=`python ../buildcontrol.py next $BUILDER` @@ -72,7 +73,23 @@ while true ; do python ../../../buildcontrol.py benchmarks $BUILDER $rev ../../benchmarks || fatal "Cannot record benchmarks" cd .. || fatal "failed to cd out of pkg" fi + # check if we're at a release (via the hg summary) + # if so, package the tar.gz and upload to googlecode + SUMMARY=$(hg log -l 1 | grep summary\: | awk '{print $2}') + if [[ "x${SUMMARY:0:7}" == "xrelease" ]]; then + echo "Uploading binary to googlecode" + TARBALL="go.$SUMMARY.$BUILDER.tar.gz" + ./clean.bash --nopkg + # move contents of candidate/ to candidate/go/ for archival + cd ../.. || fatal "Cannot cd up" + mv candidate go-candidate || fatal "Cannot rename candidate" + mkdir candidate || fatal "Cannot mkdir candidate" + mv go-candidate candidate/go || fatal "Cannot mv directory" + cd candidate || fatal "Cannot cd candidate" + # build tarball + tar czf ../$TARBALL go || fatal "Cannot create tarball" + ../buildcontrol.py upload $BUILDER $SUMMARY ../$TARBALL || fatal "Cannot upload tarball" + fi fi - cd ../.. || fatal "Cannot cd up" sleep 10 -done +) done diff --git a/misc/dashboard/builder/Makefile b/misc/dashboard/builder/Makefile new file mode 100644 index 000000000..7270a3f42 --- /dev/null +++ b/misc/dashboard/builder/Makefile @@ -0,0 +1,14 @@ +# 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 ../../../src/Make.inc + +TARG=gobuilder +GOFILES=\ + exec.go\ + hg.go\ + http.go\ + main.go\ + +include ../../../src/Make.cmd diff --git a/misc/dashboard/builder/doc.go b/misc/dashboard/builder/doc.go new file mode 100644 index 000000000..54a9adfc0 --- /dev/null +++ b/misc/dashboard/builder/doc.go @@ -0,0 +1,54 @@ +// 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. + +/* + +Go Builder is a continuous build client for the Go project. +It integrates with the Go Dashboard AppEngine application. + +Go Builder is intended to run continuously as a background process. + +It periodically pulls updates from the Go Mercurial repository. + +When a newer revision is found, Go Builder creates a clone of the repository, +runs all.bash, and reports build success or failure to the Go Dashboard. + +For a successful build, Go Builder will also run benchmarks +(cd $GOROOT/src/pkg; make bench) and send the results to the Go Dashboard. + +For a release revision (a change description that matches "release.YYYY-MM-DD"), +Go Builder will create a tar.gz archive of the GOROOT and deliver it to the +Go Google Code project's downloads section. + +Usage: + + gobuilder goos-goarch... + + Several goos-goarch combinations can be provided, and the builder will + build them in serial. + +Optional flags: + + -dashboard="godashboard.appspot.com": Go Dashboard Host + The location of the Go Dashboard application to which Go Builder will + report its results. + + -bench: Run benchmarks + + -release: Build and deliver binary release archive + +The key file should be located at $HOME/.gobuilder or, for a builder-specific +key, $HOME/.gobuilder-$BUILDER (eg, $HOME/.gobuilder-linux-amd64). + +The build key file is a text file of the format: + + godashboard-key + googlecode-username + googlecode-password + +If the Google Code credentials are not provided the archival step +will be skipped. + +*/ +package documentation diff --git a/misc/dashboard/builder/exec.go b/misc/dashboard/builder/exec.go new file mode 100644 index 000000000..6236c915a --- /dev/null +++ b/misc/dashboard/builder/exec.go @@ -0,0 +1,65 @@ +package main + +import ( + "bytes" + "exec" + "io" + "os" + "strings" +) + +// run is a simple wrapper for exec.Run/Close +func run(envv []string, dir string, argv ...string) os.Error { + bin, err := pathLookup(argv[0]) + if err != nil { + return err + } + p, err := exec.Run(bin, argv, envv, dir, + exec.DevNull, exec.DevNull, exec.PassThrough) + if err != nil { + return err + } + return p.Close() +} + +// runLog runs a process and returns the combined stdout/stderr, +// as well as writing it to logfile (if specified). +func runLog(envv []string, logfile, dir string, argv ...string) (output string, exitStatus int, err os.Error) { + bin, err := pathLookup(argv[0]) + if err != nil { + return + } + p, err := exec.Run(bin, argv, envv, dir, + exec.DevNull, exec.Pipe, exec.MergeWithStdout) + if err != nil { + return + } + defer p.Close() + b := new(bytes.Buffer) + var w io.Writer = b + if logfile != "" { + f, err := os.Open(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) + if err != nil { + return + } + defer f.Close() + w = io.MultiWriter(f, b) + } + _, err = io.Copy(w, p.Stdout) + if err != nil { + return + } + wait, err := p.Wait(0) + if err != nil { + return + } + return b.String(), wait.WaitStatus.ExitStatus(), nil +} + +// Find bin in PATH if a relative or absolute path hasn't been specified +func pathLookup(s string) (string, os.Error) { + if strings.HasPrefix(s, "/") || strings.HasPrefix(s, "./") || strings.HasPrefix(s, "../") { + return s, nil + } + return exec.LookPath(s) +} diff --git a/misc/dashboard/builder/hg.go b/misc/dashboard/builder/hg.go new file mode 100644 index 000000000..5d2f63a17 --- /dev/null +++ b/misc/dashboard/builder/hg.go @@ -0,0 +1,54 @@ +package main + +import ( + "fmt" + "os" + "strconv" + "strings" +) + +type Commit struct { + num int // mercurial revision number + node string // mercurial hash + parent string // hash of commit's parent + user string // author's Name + date string // date of commit + desc string // description +} + +// getCommit returns details about the Commit specified by the revision hash +func getCommit(rev string) (c Commit, err os.Error) { + defer func() { + if err != nil { + err = fmt.Errorf("getCommit: %s: %s", rev, err) + } + }() + parts, err := getCommitParts(rev) + if err != nil { + return + } + num, err := strconv.Atoi(parts[0]) + if err != nil { + return + } + parent := "" + if num > 0 { + prev := strconv.Itoa(num - 1) + if pparts, err := getCommitParts(prev); err == nil { + parent = pparts[1] + } + } + user := strings.Replace(parts[2], "<", "<", -1) + user = strings.Replace(user, ">", ">", -1) + return Commit{num, parts[1], parent, user, parts[3], parts[4]}, nil +} + +func getCommitParts(rev string) (parts []string, err os.Error) { + const format = "{rev}>{node}>{author|escape}>{date}>{desc}" + s, _, err := runLog(nil, "", goroot, + "hg", "log", "-r", rev, "-l", "1", "--template", format) + if err != nil { + return + } + return strings.Split(s, ">", 5), nil +} diff --git a/misc/dashboard/builder/http.go b/misc/dashboard/builder/http.go new file mode 100644 index 000000000..02f281061 --- /dev/null +++ b/misc/dashboard/builder/http.go @@ -0,0 +1,70 @@ +package main + +import ( + "bytes" + "encoding/base64" + "encoding/binary" + "fmt" + "http" + "os" + "regexp" +) + +// getHighWater returns the current highwater revision hash for this builder +func (b *Builder) getHighWater() (rev string, err os.Error) { + url := fmt.Sprintf("http://%s/hw-get?builder=%s", *dashboard, b.name) + r, _, err := http.Get(url) + if err != nil { + return + } + buf := new(bytes.Buffer) + _, err = buf.ReadFrom(r.Body) + if err != nil { + return + } + r.Body.Close() + return buf.String(), nil +} + +// recordResult sends build results to the dashboard +func (b *Builder) recordResult(buildLog string, c Commit) os.Error { + return httpCommand("build", map[string]string{ + "builder": b.name, + "key": b.key, + "node": c.node, + "parent": c.parent, + "user": c.user, + "date": c.date, + "desc": c.desc, + "log": buildLog, + }) +} + +// match lines like: "package.BechmarkFunc 100000 999 ns/op" +var benchmarkRegexp = regexp.MustCompile("([^\n\t ]+)[\t ]+([0-9]+)[\t ]+([0-9]+) ns/op") + +// recordBenchmarks sends benchmark results to the dashboard +func (b *Builder) recordBenchmarks(benchLog string, c Commit) os.Error { + results := benchmarkRegexp.FindAllStringSubmatch(benchLog, -1) + var buf bytes.Buffer + b64 := base64.NewEncoder(base64.StdEncoding, &buf) + for _, r := range results { + for _, s := range r[1:] { + binary.Write(b64, binary.BigEndian, uint16(len(s))) + b64.Write([]byte(s)) + } + } + b64.Close() + return httpCommand("benchmarks", map[string]string{ + "builder": b.name, + "key": b.key, + "node": c.node, + "benchmarkdata": buf.String(), + }) +} + +func httpCommand(cmd string, args map[string]string) os.Error { + url := fmt.Sprintf("http://%v/%v", *dashboard, cmd) + _, err := http.PostForm(url, args) + return err +} diff --git a/misc/dashboard/builder/main.go b/misc/dashboard/builder/main.go new file mode 100644 index 000000000..32a2e10da --- /dev/null +++ b/misc/dashboard/builder/main.go @@ -0,0 +1,340 @@ +package main + +import ( + "container/vector" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "path" + "regexp" + "strconv" + "strings" + "time" +) + +const ( + codeProject = "go" + codePyScript = "misc/dashboard/googlecode_upload.py" + hgUrl = "https://go.googlecode.com/hg/" + waitInterval = 10e9 // time to wait before checking for new revs + mkdirPerm = 0750 +) + +type Builder struct { + name string + goos, goarch string + key string + codeUsername string + codePassword string +} + +type BenchRequest struct { + builder *Builder + commit Commit + path string +} + +var ( + buildroot = flag.String("buildroot", path.Join(os.TempDir(), "gobuilder"), "Directory under which to build") + dashboard = flag.String("dashboard", "godashboard.appspot.com", "Go Dashboard Host") + runBenchmarks = flag.Bool("bench", false, "Run benchmarks") + buildRelease = flag.Bool("release", false, "Build and upload binary release archives") + buildRevision = flag.String("rev", "", "Build specified revision and exit") + buildCmd = flag.String("cmd", "./all.bash", "Build command (specify absolute or relative to go/src/)") +) + +var ( + goroot string + releaseRegexp = regexp.MustCompile(`^release\.[0-9\-.]+`) + benchRequests vector.Vector +) + +func main() { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "usage: %s goos-goarch...\n", os.Args[0]) + flag.PrintDefaults() + os.Exit(2) + } + flag.Parse() + if len(flag.Args()) == 0 { + flag.Usage() + } + goroot = path.Join(*buildroot, "goroot") + builders := make([]*Builder, len(flag.Args())) + for i, builder := range flag.Args() { + b, err := NewBuilder(builder) + if err != nil { + log.Exit(err) + } + builders[i] = b + } + if err := os.RemoveAll(*buildroot); err != nil { + log.Exitf("Error removing build root (%s): %s", *buildroot, err) + } + if err := os.Mkdir(*buildroot, mkdirPerm); err != nil { + log.Exitf("Error making build root (%s): %s", *buildroot, err) + } + if err := run(nil, *buildroot, "hg", "clone", hgUrl, goroot); err != nil { + log.Exit("Error cloning repository:", err) + } + // if specified, build revision and return + if *buildRevision != "" { + c, err := getCommit(*buildRevision) + if err != nil { + log.Exit("Error finding revision: ", err) + } + for _, b := range builders { + if err := b.buildCommit(c); err != nil { + log.Println(err) + } + runQueuedBenchmark() + } + return + } + // check for new commits and build them + for { + err := run(nil, goroot, "hg", "pull", "-u") + if err != nil { + log.Println("hg pull failed:", err) + time.Sleep(waitInterval) + continue + } + built := false + for _, b := range builders { + if b.build() { + built = true + } + } + // only run benchmarks if we didn't build anything + // so that they don't hold up the builder queue + if !built { + if !runQueuedBenchmark() { + // if we have no benchmarks to do, pause + time.Sleep(waitInterval) + } + // after running one benchmark, + // continue to find and build new revisions. + } + } +} + +func runQueuedBenchmark() bool { + if benchRequests.Len() == 0 { + return false + } + runBenchmark(benchRequests.Pop().(BenchRequest)) + return true +} + +func runBenchmark(r BenchRequest) { + // run benchmarks and send to dashboard + log.Println(r.builder.name, "benchmarking", r.commit.num) + defer os.RemoveAll(r.path) + pkg := path.Join(r.path, "go", "src", "pkg") + bin := path.Join(r.path, "go", "bin") + env := []string{ + "GOOS=" + r.builder.goos, + "GOARCH=" + r.builder.goarch, + "PATH=" + bin + ":" + os.Getenv("PATH"), + } + logfile := path.Join(r.path, "bench.log") + benchLog, _, err := runLog(env, logfile, pkg, "gomake", "bench") + if err != nil { + log.Println(r.builder.name, "gomake bench:", err) + return + } + if err = r.builder.recordBenchmarks(benchLog, r.commit); err != nil { + log.Println("recordBenchmarks:", err) + } +} + +func NewBuilder(builder string) (*Builder, os.Error) { + b := &Builder{name: builder} + + // get goos/goarch from builder string + s := strings.Split(builder, "-", 3) + if len(s) == 2 { + b.goos, b.goarch = s[0], s[1] + } else { + return nil, fmt.Errorf("unsupported builder form: %s", builder) + } + + // read keys from keyfile + fn := path.Join(os.Getenv("HOME"), ".gobuildkey") + if s := fn + "-" + b.name; isFile(s) { // builder-specific file + fn = s + } + c, err := ioutil.ReadFile(fn) + if err != nil { + return nil, fmt.Errorf("readKeys %s (%s): %s", b.name, fn, err) + } + v := strings.Split(string(c), "\n", -1) + b.key = v[0] + if len(v) >= 3 { + b.codeUsername, b.codePassword = v[1], v[2] + } + + return b, nil +} + +// build checks for a new commit for this builder +// and builds it if one is found. +// It returns true if a build was attempted. +func (b *Builder) build() bool { + defer func() { + err := recover() + if err != nil { + log.Println(b.name, "build:", err) + } + }() + c, err := b.nextCommit() + if err != nil { + log.Println(err) + return false + } + if c == nil { + return false + } + err = b.buildCommit(*c) + if err != nil { + log.Println(err) + } + return true +} + +// nextCommit returns the next unbuilt Commit for this builder +func (b *Builder) nextCommit() (nextC *Commit, err os.Error) { + defer func() { + if err != nil { + err = fmt.Errorf("%s nextCommit: %s", b.name, err) + } + }() + hw, err := b.getHighWater() + if err != nil { + return + } + c, err := getCommit(hw) + if err != nil { + return + } + next := c.num + 1 + c, err = getCommit(strconv.Itoa(next)) + if err == nil && c.num == next { + return &c, nil + } + return nil, nil +} + +func (b *Builder) buildCommit(c Commit) (err os.Error) { + defer func() { + if err != nil { + err = fmt.Errorf("%s buildCommit: %d: %s", b.name, c.num, err) + } + }() + + log.Println(b.name, "building", c.num) + + // create place in which to do work + workpath := path.Join(*buildroot, b.name+"-"+strconv.Itoa(c.num)) + err = os.Mkdir(workpath, mkdirPerm) + if err != nil { + return + } + benchRequested := false + defer func() { + if !benchRequested { + os.RemoveAll(workpath) + } + }() + + // clone repo + err = run(nil, workpath, "hg", "clone", goroot, "go") + if err != nil { + return + } + + // update to specified revision + err = run(nil, path.Join(workpath, "go"), + "hg", "update", "-r", strconv.Itoa(c.num)) + if err != nil { + return + } + + // set up environment for build/bench execution + env := []string{ + "GOOS=" + b.goos, + "GOARCH=" + b.goarch, + "GOHOSTOS=" + os.Getenv("GOHOSTOS"), + "GOHOSTARCH=" + os.Getenv("GOHOSTARCH"), + "GOROOT_FINAL=/usr/local/go", + "PATH=" + os.Getenv("PATH"), + } + srcDir := path.Join(workpath, "go", "src") + + // build + logfile := path.Join(workpath, "build.log") + buildLog, status, err := runLog(env, logfile, srcDir, *buildCmd) + if err != nil { + return fmt.Errorf("all.bash: %s", err) + } + if status != 0 { + // record failure + return b.recordResult(buildLog, c) + } + + // record success + if err = b.recordResult("", c); err != nil { + return fmt.Errorf("recordResult: %s", err) + } + + // send benchmark request if benchmarks are enabled + if *runBenchmarks { + benchRequests.Insert(0, BenchRequest{ + builder: b, + commit: c, + path: workpath, + }) + benchRequested = true + } + + // finish here if codeUsername and codePassword aren't set + if b.codeUsername == "" || b.codePassword == "" || !*buildRelease { + return + } + + // if this is a release, create tgz and upload to google code + if release := releaseRegexp.FindString(c.desc); release != "" { + // clean out build state + err = run(env, srcDir, "./clean.bash", "--nopkg") + if err != nil { + return fmt.Errorf("clean.bash: %s", err) + } + // upload binary release + fn := fmt.Sprintf("go.%s.%s-%s.tar.gz", release, b.goos, b.goarch) + err = run(nil, workpath, "tar", "czf", fn, "go") + if err != nil { + return fmt.Errorf("tar: %s", err) + } + err = run(nil, workpath, path.Join(goroot, codePyScript), + "-s", release, + "-p", codeProject, + "-u", b.codeUsername, + "-w", b.codePassword, + "-l", fmt.Sprintf("%s,%s", b.goos, b.goarch), + fn) + } + + return +} + +func isDirectory(name string) bool { + s, err := os.Stat(name) + return err == nil && s.IsDirectory() +} + +func isFile(name string) bool { + s, err := os.Stat(name) + return err == nil && (s.IsRegular() || s.IsSymlink()) +} diff --git a/misc/dashboard/godashboard/package.py b/misc/dashboard/godashboard/package.py index 6c3bd9995..cf59bf3e8 100644 --- a/misc/dashboard/godashboard/package.py +++ b/misc/dashboard/godashboard/package.py @@ -17,6 +17,7 @@ from google.appengine.ext.webapp import template from google.appengine.ext.webapp.util import run_wsgi_app from google.appengine.api import users from google.appengine.api import mail +from google.appengine.api import urlfetch import binascii import datetime import hashlib @@ -29,6 +30,11 @@ import time import urllib2 import sets +# local imports +import toutf8 + +template.register_template_library('toutf8') + # Storage model for package info recorded on server. # Just path, count, and time of last install. class Package(db.Model): @@ -50,36 +56,91 @@ re_bitbucket = re.compile(r'^bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+$') re_googlecode = re.compile(r'^[a-z0-9\-]+\.googlecode\.com/(svn|hg)$') re_github = re.compile(r'^github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+$') +def vc_to_web(path): + if re_bitbucket.match(path): + check_url = 'http://' + path + '/?cmd=heads' + web = 'http://' + path + '/' + elif re_github.match(path): + # github doesn't let you fetch the .git directory anymore. + # fetch .git/info/refs instead, like git clone would. + check_url = 'http://'+path+'.git/info/refs' + web = 'http://' + path + elif re_googlecode.match(path): + check_url = 'http://'+path + web = 'http://code.google.com/p/' + path[:path.index('.')] + else: + return False, False + return web, check_url + +re_bitbucket_web = re.compile(r'bitbucket\.org/([a-z0-9A-Z_.\-]+)/([a-z0-9A-Z_.\-]+)') +re_googlecode_web = re.compile(r'code.google.com/p/([a-z0-9\-]+)') +re_github_web = re.compile(r'github\.com/([a-z0-9A-Z_.\-]+)/([a-z0-9A-Z_.\-]+)') +re_striphttp = re.compile(r'http://(www\.)?') + +def web_to_vc(url): + url = re_striphttp.sub('', url) + m = re_bitbucket_web.match(url) + if m: + return 'bitbucket.org/'+m.group(1)+'/'+m.group(2) + m = re_github_web.match(url) + if m: + return 'github.com/'+m.group(1)+'/'+m.group(2) + m = re_googlecode_web.match(url) + if m: + path = m.group(1)+'.googlecode.com/' + # perform http request to path/hg to check if they're using mercurial + vcs = 'svn' + try: + response = urlfetch.fetch('http://'+path+'hg', deadline=1) + if response.status_code == 200: + vcs = 'hg' + except: pass + return path + vcs + return False + MaxPathLength = 100 +CacheTimeout = 3600 class PackagePage(webapp.RequestHandler): def get(self): if self.request.get('fmt') == 'json': return self.json() - q = Package.all() - q.order('-last_install') - by_time = q.fetch(100) + html = memcache.get('view-package') + if not html: + q = Package.all() + q.order('-last_install') + by_time = q.fetch(100) - q = Package.all() - q.order('-count') - by_count = q.fetch(100) + q = Package.all() + q.order('-count') + by_count = q.fetch(100) - self.response.headers['Content-Type'] = 'text/html; charset=utf-8' - path = os.path.join(os.path.dirname(__file__), 'package.html') - self.response.out.write(template.render(path, {"by_time": by_time, "by_count": by_count})) + self.response.headers['Content-Type'] = 'text/html; charset=utf-8' + path = os.path.join(os.path.dirname(__file__), 'package.html') + html = template.render( + path, + {"by_time": by_time, "by_count": by_count} + ) + memcache.set('view-package', html, time=CacheTimeout) + + self.response.out.write(html) def json(self): - self.response.set_status(200) - self.response.headers['Content-Type'] = 'text/plain; charset=utf-8' - q = Package.all() - s = '{"packages": [' - sep = '' - for r in q.fetch(1000): - s += '%s\n\t{"path": "%s", "last_install": "%s", "count": "%s"}' % (sep, r.path, r.last_install, r.count) - sep = ',' - s += '\n]}\n' - self.response.out.write(s) + json = memcache.get('view-package-json') + if not json: + self.response.set_status(200) + self.response.headers['Content-Type'] = 'text/plain; charset=utf-8' + q = Package.all() + s = '{"packages": [' + sep = '' + for r in q.fetch(1000): + s += '%s\n\t{"path": "%s", "last_install": "%s", "count": "%s"}' % (sep, r.path, r.last_install, r.count) + sep = ',' + s += '\n]}\n' + json = s + memcache.set('view-package-json', json, time=CacheTimeoout) + self.response.out.write(json) def can_get_url(self, url): try: @@ -104,18 +165,8 @@ class PackagePage(webapp.RequestHandler): p = Package.get_by_key_name(key) if p is None: # not in datastore - verify URL before creating - if re_bitbucket.match(path): - check_url = 'http://' + path + '/?cmd=heads' - web = 'http://' + path + '/' - elif re_github.match(path): - # github doesn't let you fetch the .git directory anymore. - # fetch .git/info/refs instead, like git clone would. - check_url = 'http://'+path+'.git/info/refs' - web = 'http://' + path - elif re_googlecode.match(path): - check_url = 'http://'+path - web = 'http://code.google.com/p/' + path[:path.index('.')] - else: + web, check_url = vc_to_web(path) + if not web: logging.error('unrecognized path: %s', path) return False if not self.can_get_url(check_url): @@ -150,9 +201,27 @@ class ProjectPage(webapp.RequestHandler): self.redirect(users.create_logout_url("/project")) elif self.request.path == "/project/edit" and admin: self.edit() + elif self.request.path == "/project/assoc" and admin: + self.assoc() else: self.list() + def assoc(self): + projects = Project.all() + for p in projects: + if p.package: + continue + path = web_to_vc(p.web_url) + if not path: + continue + pkg = Package.get_by_key_name("pkg-"+path) + if not pkg: + self.response.out.write('no: %s %s
    ' % (p.web_url, path)) + continue + p.package = pkg + p.put() + self.response.out.write('yes: %s %s
    ' % (p.web_url, path)) + def post(self): if self.request.path == "/project/edit": self.edit(True) @@ -177,30 +246,40 @@ class ProjectPage(webapp.RequestHandler): self.list({"submitMsg": "Your project has been submitted."}) - def list(self, data={}): - projects = Project.all().order('category').order('name') - - admin = users.is_current_user_admin() - if not admin: - projects = projects.filter('approved =', True) - - projects = list(projects) - - tags = sets.Set() - for p in projects: - for t in p.tags: - tags.add(t) - - tag = self.request.get("tag", None) + def list(self, additional_data={}): + cache_key = 'view-project-data' + tag = self.request.get('tag', None) if tag: - projects = filter(lambda x: tag in x.tags, projects) + cache_key += '-'+tag + data = memcache.get(cache_key) + admin = users.is_current_user_admin() + if admin or not data: + projects = Project.all().order('category').order('name') + if not admin: + projects = projects.filter('approved =', True) + projects = list(projects) + + tags = sets.Set() + for p in projects: + for t in p.tags: + tags.add(t) + + if tag: + projects = filter(lambda x: tag in x.tags, projects) + + data = {} + data['tag'] = tag + data['tags'] = tags + data['projects'] = projects + data['admin']= admin + if not admin: + memcache.set(cache_key, data, time=CacheTimeout) + + for k, v in additional_data.items(): + data[k] = v self.response.headers['Content-Type'] = 'text/html; charset=utf-8' path = os.path.join(os.path.dirname(__file__), 'project.html') - data["tag"] = tag - data["tags"] = tags - data["projects"] = projects - data["admin"] = admin self.response.out.write(template.render(path, data)) def edit(self, save=False): @@ -228,7 +307,8 @@ class ProjectPage(webapp.RequestHandler): p.approved = self.request.get("approved") == "1" p.tags = filter(lambda x: x, self.request.get("tags", "").split(",")) p.put() - self.redirect("/project") + memcache.delete('view-project-data') + self.redirect('/project') return # get all project categories and tags diff --git a/misc/dashboard/godashboard/project-edit.html b/misc/dashboard/godashboard/project-edit.html index 5f1ca3b11..ce18fb3fb 100644 --- a/misc/dashboard/godashboard/project-edit.html +++ b/misc/dashboard/godashboard/project-edit.html @@ -1,11 +1,11 @@ + - -
    @@ -38,8 +38,10 @@ var cats = [ {% endfor %} ]; -$('#tags').autocomplete(tags); -$('#cats').autocomplete(cats); +google.setOnLoadCallback(function() { + $('#tags').autocomplete({source:tags}); + $('#cats').autocomplete({source:cats}); +}); diff --git a/misc/dashboard/godashboard/project-notify.txt b/misc/dashboard/godashboard/project-notify.txt index 3a165908c..f55bf6421 100644 --- a/misc/dashboard/godashboard/project-notify.txt +++ b/misc/dashboard/godashboard/project-notify.txt @@ -5,5 +5,5 @@ Description: {{project.descr}} URL: {{project.web_url}} To edit/approve/delete: -http://godashboard.appspot.com/project/edit?name={{project.name|urlencode}} +http://godashboard.appspot.com/project/edit?name={{project.name|toutf8|urlencode}} diff --git a/misc/dashboard/godashboard/toutf8.py b/misc/dashboard/godashboard/toutf8.py new file mode 100644 index 000000000..544c681b6 --- /dev/null +++ b/misc/dashboard/godashboard/toutf8.py @@ -0,0 +1,14 @@ +# 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 is a Django custom template filter to work around the +# fact that GAE's urlencode filter doesn't handle unicode strings. + +from google.appengine.ext import webapp + +register = webapp.template.create_template_register() + +@register.filter +def toutf8(value): + return value.encode("utf-8") diff --git a/misc/dashboard/googlecode_upload.py b/misc/dashboard/googlecode_upload.py new file mode 100755 index 000000000..3b1d432ff --- /dev/null +++ b/misc/dashboard/googlecode_upload.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python2 +# +# Copyright 2006, 2007 Google Inc. All Rights Reserved. +# Author: danderson@google.com (David Anderson) +# +# Script for uploading files to a Google Code project. +# +# This is intended to be both a useful script for people who want to +# streamline project uploads and a reference implementation for +# uploading files to Google Code projects. +# +# To upload a file to Google Code, you need to provide a path to the +# file on your local machine, a small summary of what the file is, a +# project name, and a valid account that is a member or owner of that +# project. You can optionally provide a list of labels that apply to +# the file. The file will be uploaded under the same name that it has +# in your local filesystem (that is, the "basename" or last path +# component). Run the script with '--help' to get the exact syntax +# and available options. +# +# Note that the upload script requests that you enter your +# googlecode.com password. This is NOT your Gmail account password! +# This is the password you use on googlecode.com for committing to +# Subversion and uploading files. You can find your password by going +# to http://code.google.com/hosting/settings when logged in with your +# Gmail account. If you have already committed to your project's +# Subversion repository, the script will automatically retrieve your +# credentials from there (unless disabled, see the output of '--help' +# for details). +# +# If you are looking at this script as a reference for implementing +# your own Google Code file uploader, then you should take a look at +# the upload() function, which is the meat of the uploader. You +# basically need to build a multipart/form-data POST request with the +# right fields and send it to https://PROJECT.googlecode.com/files . +# Authenticate the request using HTTP Basic authentication, as is +# shown below. +# +# Licensed under the terms of the Apache Software License 2.0: +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Questions, comments, feature requests and patches are most welcome. +# Please direct all of these to the Google Code users group: +# http://groups.google.com/group/google-code-hosting + +"""Google Code file uploader script. +""" + +__author__ = 'danderson@google.com (David Anderson)' + +import httplib +import os.path +import optparse +import getpass +import base64 +import sys + + +def upload(file, project_name, user_name, password, summary, labels=None): + """Upload a file to a Google Code project's file server. + + Args: + file: The local path to the file. + project_name: The name of your project on Google Code. + user_name: Your Google account name. + password: The googlecode.com password for your account. + Note that this is NOT your global Google Account password! + summary: A small description for the file. + labels: an optional list of label strings with which to tag the file. + + Returns: a tuple: + http_status: 201 if the upload succeeded, something else if an + error occured. + http_reason: The human-readable string associated with http_status + file_url: If the upload succeeded, the URL of the file on Google + Code, None otherwise. + """ + # The login is the user part of user@gmail.com. If the login provided + # is in the full user@domain form, strip it down. + if user_name.endswith('@gmail.com'): + user_name = user_name[:user_name.index('@gmail.com')] + + form_fields = [('summary', summary)] + if labels is not None: + form_fields.extend([('label', l.strip()) for l in labels]) + + content_type, body = encode_upload_request(form_fields, file) + + upload_host = '%s.googlecode.com' % project_name + upload_uri = '/files' + auth_token = base64.b64encode('%s:%s'% (user_name, password)) + headers = { + 'Authorization': 'Basic %s' % auth_token, + 'User-Agent': 'Googlecode.com uploader v0.9.4', + 'Content-Type': content_type, + } + + server = httplib.HTTPSConnection(upload_host) + server.request('POST', upload_uri, body, headers) + resp = server.getresponse() + server.close() + + if resp.status == 201: + location = resp.getheader('Location', None) + else: + location = None + return resp.status, resp.reason, location + + +def encode_upload_request(fields, file_path): + """Encode the given fields and file into a multipart form body. + + fields is a sequence of (name, value) pairs. file is the path of + the file to upload. The file will be uploaded to Google Code with + the same file name. + + Returns: (content_type, body) ready for httplib.HTTP instance + """ + BOUNDARY = '----------Googlecode_boundary_reindeer_flotilla' + CRLF = '\r\n' + + body = [] + + # Add the metadata about the upload first + for key, value in fields: + body.extend( + ['--' + BOUNDARY, + 'Content-Disposition: form-data; name="%s"' % key, + '', + value, + ]) + + # Now add the file itself + file_name = os.path.basename(file_path) + f = open(file_path, 'rb') + file_content = f.read() + f.close() + + body.extend( + ['--' + BOUNDARY, + 'Content-Disposition: form-data; name="filename"; filename="%s"' + % file_name, + # The upload server determines the mime-type, no need to set it. + 'Content-Type: application/octet-stream', + '', + file_content, + ]) + + # Finalize the form body + body.extend(['--' + BOUNDARY + '--', '']) + + return 'multipart/form-data; boundary=%s' % BOUNDARY, CRLF.join(body) + + +def upload_find_auth(file_path, project_name, summary, labels=None, + user_name=None, password=None, tries=3): + """Find credentials and upload a file to a Google Code project's file server. + + file_path, project_name, summary, and labels are passed as-is to upload. + + Args: + file_path: The local path to the file. + project_name: The name of your project on Google Code. + summary: A small description for the file. + labels: an optional list of label strings with which to tag the file. + config_dir: Path to Subversion configuration directory, 'none', or None. + user_name: Your Google account name. + tries: How many attempts to make. + """ + + while tries > 0: + if user_name is None: + # Read username if not specified or loaded from svn config, or on + # subsequent tries. + sys.stdout.write('Please enter your googlecode.com username: ') + sys.stdout.flush() + user_name = sys.stdin.readline().rstrip() + if password is None: + # Read password if not loaded from svn config, or on subsequent tries. + print 'Please enter your googlecode.com password.' + print '** Note that this is NOT your Gmail account password! **' + print 'It is the password you use to access Subversion repositories,' + print 'and can be found here: http://code.google.com/hosting/settings' + password = getpass.getpass() + + status, reason, url = upload(file_path, project_name, user_name, password, + summary, labels) + # Returns 403 Forbidden instead of 401 Unauthorized for bad + # credentials as of 2007-07-17. + if status in [httplib.FORBIDDEN, httplib.UNAUTHORIZED]: + # Rest for another try. + user_name = password = None + tries = tries - 1 + else: + # We're done. + break + + return status, reason, url + + +def main(): + parser = optparse.OptionParser(usage='googlecode-upload.py -s SUMMARY ' + '-p PROJECT [options] FILE') + parser.add_option('-s', '--summary', dest='summary', + help='Short description of the file') + parser.add_option('-p', '--project', dest='project', + help='Google Code project name') + parser.add_option('-u', '--user', dest='user', + help='Your Google Code username') + parser.add_option('-w', '--password', dest='password', + help='Your Google Code password') + parser.add_option('-l', '--labels', dest='labels', + help='An optional list of comma-separated labels to attach ' + 'to the file') + + options, args = parser.parse_args() + + if not options.summary: + parser.error('File summary is missing.') + elif not options.project: + parser.error('Project name is missing.') + elif len(args) < 1: + parser.error('File to upload not provided.') + elif len(args) > 1: + parser.error('Only one file may be specified.') + + file_path = args[0] + + if options.labels: + labels = options.labels.split(',') + else: + labels = None + + status, reason, url = upload_find_auth(file_path, options.project, + options.summary, labels, + options.user, options.password) + if url: + print 'The file was uploaded successfully.' + print 'URL: %s' % url + return 0 + else: + print 'An error occurred. Your file was not uploaded.' + print 'Google Code upload server said: %s (%s)' % (reason, status) + return 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/misc/emacs/go-mode.el b/misc/emacs/go-mode.el index e27ee7438..2624e87cb 100644 --- a/misc/emacs/go-mode.el +++ b/misc/emacs/go-mode.el @@ -27,8 +27,8 @@ (defvar go-mode-syntax-table (let ((st (make-syntax-table))) - ;; Symbols - (modify-syntax-entry ?_ "_" st) + ;; Add _ to :word: character class + (modify-syntax-entry ?_ "w" st) ;; Operators (punctuation) (modify-syntax-entry ?+ "." st) @@ -92,7 +92,7 @@ some syntax analysis.") ;; Map key type (,(concat "\\\\s *\\(?:<-\\)?" type-name) 1 font-lock-type-face) ;; new/make type (,(concat "\\<\\(?:new\\|make\\)\\>\\(?:\\s \\|)\\)*(" type-name) 1 font-lock-type-face) ;; Type conversion diff --git a/misc/fraise/go.plist b/misc/fraise/go.plist new file mode 100644 index 000000000..298361501 --- /dev/null +++ b/misc/fraise/go.plist @@ -0,0 +1,93 @@ + + + + + beginCommand + + endCommand + + beginInstruction + + endInstruction + + beginVariable + + endVariable + + firstString + " + secondString + ' + firstSingleLineComment + // + secondSingleLineComment + + beginFirstMultiLineComment + /* + endFirstMultiLineComment + */ + beginSecondMultiLineComment + + endSecondMultiLineComment + + functionDefinition + ^func\s*.*\(.*\)\s?\{ + removeFromFunction + + keywordsCaseSensitive + + recolourKeywordIfAlreadyColoured + + keywords + + break + case + chan + const + continue + default + defer + else + fallthrough + for + func + go + goto + if + import + interface + map + package + range + return + select + struct + switch + type + var + bool + byte + chan + complex64 + complex128 + float + float32 + float64 + int + int8 + int16 + int32 + int64 + map + string + uint + uintptr + uint8 + uint16 + uint32 + uint64 + + autocompleteWords + + + diff --git a/misc/fraise/readme.txt b/misc/fraise/readme.txt new file mode 100644 index 000000000..fb0f2c8c1 --- /dev/null +++ b/misc/fraise/readme.txt @@ -0,0 +1,16 @@ +##Instructions for enabling Go syntax highlighting in Fraise.app## +1. Move go.plist to /Applications/Fraise.app/Contents/Resources/Syntax\ Definitions/ +2. Open /Applications/Fraise.app/Contents/Resources/SyntaxDefinitions.plist and add + + + name + GoogleGo + file + go + extensions + go + + +before + +3. Restart Fraise and you're good to Go! \ No newline at end of file diff --git a/misc/goplay/Makefile b/misc/goplay/Makefile new file mode 100644 index 000000000..28d024511 --- /dev/null +++ b/misc/goplay/Makefile @@ -0,0 +1,13 @@ +# Copyright 2010 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../src/Make.inc + +TARG=goplay + +GOFILES=\ + goplay.go\ + +include ../../src/Make.cmd + diff --git a/misc/goplay/README b/misc/goplay/README new file mode 100644 index 000000000..e8a1d290f --- /dev/null +++ b/misc/goplay/README @@ -0,0 +1 @@ +See doc.go. diff --git a/misc/goplay/doc.go b/misc/goplay/doc.go new file mode 100644 index 000000000..9685551bd --- /dev/null +++ b/misc/goplay/doc.go @@ -0,0 +1,25 @@ +// 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. + +// Goplay is a web interface for experimenting with Go code. +// It is similar to the Go Playground: http://golang.org/doc/play/ +// +// To use goplay, first build and install it: +// $ cd $GOROOT/misc/goplay +// $ gomake install +// Then, run it: +// $ goplay +// and load http://localhost:3999/ in a web browser. +// +// You should see a Hello World program, which you can compile and run by +// pressing shift-enter. There is also a "compile-on-keypress" feature that can +// be enabled by checking a checkbox. +// +// WARNING! CUIDADO! ACHTUNG! ATTENZIONE! +// A note on security: anyone with access to the goplay web interface can run +// arbitrary code on your computer. Goplay is not a sandbox, and has no other +// security mechanisms. Do not deploy it in untrusted environments. +// By default, goplay listens only on localhost. This can be overridden with +// the -http parameter. Do so at your own risk. +package documentation diff --git a/misc/goplay/goplay.go b/misc/goplay/goplay.go new file mode 100644 index 000000000..5923360f6 --- /dev/null +++ b/misc/goplay/goplay.go @@ -0,0 +1,314 @@ +// 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" + "http" + "io" + "io/ioutil" + "log" + "os" + "runtime" + "strconv" + "template" +) + +var ( + httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on") + htmlOutput = flag.Bool("html", false, "render program output as HTML") +) + +var ( + // a source of numbers, for naming temporary files + uniq = make(chan int) + // the architecture-identifying character of the tool chain, 5, 6, or 8 + archChar string +) + +func main() { + flag.Parse() + + // set archChar + switch runtime.GOARCH { + case "arm": + archChar = "5" + case "amd64": + archChar = "6" + case "386": + archChar = "8" + default: + log.Exitln("unrecognized GOARCH:", runtime.GOARCH) + } + + // source of unique numbers + go func() { + for i := 0; ; i++ { + uniq <- i + } + }() + + http.HandleFunc("/", FrontPage) + http.HandleFunc("/compile", Compile) + log.Exit(http.ListenAndServe(*httpListen, nil)) +} + +// FrontPage is an HTTP handler that renders the goplay interface. +// If a filename is supplied in the path component of the URI, +// its contents will be put in the interface's text area. +// Otherwise, the default "hello, world" program is displayed. +func FrontPage(w http.ResponseWriter, req *http.Request) { + data, err := ioutil.ReadFile(req.URL.Path[1:]) + if err != nil { + data = helloWorld + } + frontPage.Execute(data, w) +} + +// Compile is an HTTP handler that reads Go source code from the request, +// compiles and links the code (returning any errors), runs the program, +// and sends the program's output as the HTTP response. +func Compile(w http.ResponseWriter, req *http.Request) { + // x is the base name for .go, .6, executable files + x := os.TempDir() + "/compile" + strconv.Itoa(<-uniq) + src := x + ".go" + obj := x + "." + archChar + bin := x + if runtime.GOOS == "windows" { + bin += ".exe" + } + + // write request Body to x.go + f, err := os.Open(src, os.O_CREAT|os.O_WRONLY|os.O_TRUNC, 0666) + if err != nil { + error(w, nil, err) + return + } + defer os.Remove(src) + defer f.Close() + _, err = io.Copy(f, req.Body) + if err != nil { + error(w, nil, err) + return + } + f.Close() + + // build x.go, creating x.6 + out, err := run(archChar+"g", "-o", obj, src) + defer os.Remove(obj) + if err != nil { + error(w, out, err) + return + } + + // link x.6, creating x (the program binary) + out, err = run(archChar+"l", "-o", bin, obj) + defer os.Remove(bin) + if err != nil { + error(w, out, err) + return + } + + // run x + out, err = run(bin) + if err != nil { + error(w, out, err) + } + + // write the output of x as the http response + if *htmlOutput { + w.Write(out) + } else { + output.Execute(out, w) + } +} + +// error writes compile, link, or runtime errors to the HTTP connection. +// The JavaScript interface uses the 404 status code to identify the error. +func error(w http.ResponseWriter, out []byte, err os.Error) { + w.WriteHeader(404) + if out != nil { + output.Execute(out, w) + } else { + output.Execute(err.String(), w) + } +} + +// run executes the specified command and returns its output and an error. +func run(cmd ...string) ([]byte, os.Error) { + // find the specified binary + bin, err := exec.LookPath(cmd[0]) + if err != nil { + // report binary as well as the error + return nil, os.NewError(cmd[0] + ": " + err.String()) + } + + // run the binary and read its combined stdout and stderr into a buffer + p, err := exec.Run(bin, cmd, os.Environ(), "", exec.DevNull, exec.Pipe, exec.MergeWithStdout) + if err != nil { + return nil, err + } + var buf bytes.Buffer + io.Copy(&buf, p.Stdout) + w, err := p.Wait(0) + p.Close() + if err != nil { + return nil, err + } + + // set the error return value if the program had a non-zero exit status + if !w.Exited() || w.ExitStatus() != 0 { + err = os.ErrorString("running " + cmd[0] + ": " + w.String()) + } + + return buf.Bytes(), err +} + +var frontPage, output *template.Template // HTML templates + +func init() { + frontPage = template.New(nil) + frontPage.SetDelims("«", "»") + if err := frontPage.Parse(frontPageText); err != nil { + panic(err) + } + output = template.MustParse(outputText, nil) +} + +var outputText = `
    {@|html}
    ` + +var frontPageText = ` + + + + + + +
    + +
    +(Shift-Enter to compile and run.)     + Compile and run after each keystroke +
    +
    + +
    +
    +
    + + +` + +var helloWorld = []byte(`package main + +import "fmt" + +func main() { + fmt.Println("hello, world") +} +`) diff --git a/misc/kate/go.xml b/misc/kate/go.xml index e8728fd84..3a5c39c94 100644 --- a/misc/kate/go.xml +++ b/misc/kate/go.xml @@ -42,6 +42,7 @@ bool byte chan + complex complex64 complex128 float @@ -62,6 +63,7 @@ uint64 + append cap close closed diff --git a/misc/vim/syntax/go.vim b/misc/vim/syntax/go.vim index 244503ca3..7adbe8e35 100644 --- a/misc/vim/syntax/go.vim +++ b/misc/vim/syntax/go.vim @@ -85,8 +85,8 @@ syn match goType /\/ syn match goDeclaration /^func\>/ " Predefined functions and values -syn keyword goBuiltins cap close closed cmplx copy imag len make -syn keyword goBuiltins new panic panicln print println real +syn keyword goBuiltins append cap close closed cmplx copy imag len +syn keyword goBuiltins make new panic print println real recover syn keyword goConstants iota true false nil hi def link goBuiltins Keyword @@ -95,8 +95,8 @@ hi def link goConstants Keyword " Comments; their contents syn keyword goTodo contained TODO FIXME XXX BUG syn cluster goCommentGroup contains=goTodo -syn region goComment start="/\*" end="\*/" contains=@goCommentGroup -syn region goComment start="//" end="$" contains=@goCommentGroup +syn region goComment start="/\*" end="\*/" contains=@goCommentGroup,@Spell +syn region goComment start="//" end="$" contains=@goCommentGroup,@Spell hi def link goComment Comment hi def link goTodo Todo @@ -136,7 +136,7 @@ syn region goBlock start="{" end="}" transparent fold syn region goParen start='(' end=')' transparent " Integers -syn match goDecimalInt "\<\d\+\>" +syn match goDecimalInt "\<\d\+\([Ee]\d\+\)\?\>" syn match goHexadecimalInt "\<0x\x\+\>" syn match goOctalInt "\<0\o\+\>" syn match goOctalError "\<0\o*[89]\d*\>" diff --git a/misc/zsh/go b/misc/zsh/go new file mode 100644 index 000000000..f17763d93 --- /dev/null +++ b/misc/zsh/go @@ -0,0 +1,14 @@ +# install in /etc/zsh/zshrc or your personal .zshrc + +# gc +prefixes=(5 6 8) +for p in $prefixes; do + compctl -g "*.${p}" ${p}l + compctl -g "*.go" ${p}g +done + +# standard go tools +compctl -g "*.go" gofmt + +# gccgo +compctl -g "*.go" gccgo diff --git a/pkg/~place-holder~ b/pkg/~place-holder~ deleted file mode 100644 index 0ea2de6ea..000000000 --- a/pkg/~place-holder~ +++ /dev/null @@ -1,4 +0,0 @@ -Package binaries are installed in this directory tree. - -Mercurial does not maintain empty directories. -This file helps. diff --git a/src/Make.386 b/src/Make.386 deleted file mode 100644 index 9560cd0fd..000000000 --- a/src/Make.386 +++ /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. - -O=8 -AS=${O}a -CC=${O}c -GC=${O}g -LD=${O}l -OS=568vq -CFLAGS=-FVw diff --git a/src/Make.amd64 b/src/Make.amd64 deleted file mode 100644 index 20585c4a8..000000000 --- a/src/Make.amd64 +++ /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. - -O=6 -AS=${O}a -CC=${O}c -GC=${O}g -LD=${O}l -OS=568vq -CFLAGS=-FVw diff --git a/src/Make.arm b/src/Make.arm deleted file mode 100644 index 9acef0755..000000000 --- a/src/Make.arm +++ /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. - -O=5 -AS=${O}a -CC=${O}c -GC=${O}g -LD=${O}l -OS=568vq -CFLAGS=-FVw diff --git a/src/Make.ccmd b/src/Make.ccmd new file mode 100644 index 000000000..cb2b25512 --- /dev/null +++ b/src/Make.ccmd @@ -0,0 +1,44 @@ +# 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. + +# Makefile for commands written in C. + +ifeq (windows,$(findstring windows, $(shell uname | tr A-Z a-z | sed 's/mingw/windows/'))) +TARG:=$(TARG).exe +endif + +$(TARG): $(OFILES) $(LIB) + $(HOST_LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) $(LIB) -lbio -l9 -lm $(HOST_LDFLAGS) + +$(OFILES): $(HFILES) + +CLEANFILES+=y.tab.[ch] + +clean: + rm -f *.$(HOST_O) $(TARG) $(CLEANFILES) + +ifneq ($(NOINSTALL),1) +install: $(QUOTED_GOBIN)/$(TARG) +endif + +$(QUOTED_GOBIN)/$(TARG): $(TARG) + cp $(TARG) "$(GOBIN)"/$(TARG) + +y.tab.h: $(YFILES) + bison -y $(HOST_YFLAGS) $(YFILES) + +y.tab.c: y.tab.h + test -f y.tab.c && touch y.tab.c + +all: $(TARG) + +%.$(HOST_O): %.c + $(HOST_CC) $(HOST_CFLAGS) -c "$(PWD)/$*.c" + +# These are used by enough different Makefiles to be +# worth writing down in one place, even if they don't +# apply to every command that builds with Make.ccmd +../%l/enam.o: + cd ../$*l; $(MAKE) enam.o + diff --git a/src/Make.clib b/src/Make.clib new file mode 100644 index 000000000..ebe4f84b9 --- /dev/null +++ b/src/Make.clib @@ -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. + +# Makefile included for C libraries + +all: $(LIB) + +%.$(HOST_O): %.c + $(HOST_CC) $(HOST_CFLAGS) -c "$(PWD)/$*.c" + +$(OFILES): $(HFILES) + +ifneq ($(NOINSTALL),1) +install: $(QUOTED_GOROOT)/lib/$(LIB) +endif + +$(QUOTED_GOROOT)/lib/$(LIB): $(LIB) + cp $(LIB) "$(GOROOT)/lib/$(LIB)" + +$(LIB): $(OFILES) + $(HOST_AR) rsc $(LIB) $(OFILES) + +CLEANFILES+=y.tab.[ch] y.output a.out $(LIB) + +clean: + rm -f *.$(HOST_O) $(CLEANFILES) + + +y.tab.h: $(YFILES) + LANG=C LANGUAGE="en_US.UTF8" bison -v -y $(HOST_YFLAGS) $(YFILES) + +y.tab.c: y.tab.h + test -f y.tab.c && touch y.tab.c diff --git a/src/Make.cmd b/src/Make.cmd index b2a184b82..34f5663bc 100644 --- a/src/Make.cmd +++ b/src/Make.cmd @@ -2,22 +2,21 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -all: $(TARG) +ifeq ($(GOOS),windows) +TARG:=$(TARG).exe +endif -# ugly hack to deal with whitespaces in $GOROOT -nullstring := -space := $(nullstring) # a space at the end -QUOTED_GOROOT:=$(subst $(space),\ ,$(GOROOT)) +all: $(TARG) include $(QUOTED_GOROOT)/src/Make.common PREREQ+=$(patsubst %,%.make,$(DEPS)) -$(TARG): _go_.$O $(OFILES) - $(QUOTED_GOBIN)/$(LD) -o $@ _go_.$O $(OFILES) +$(TARG): _go_.$O + $(LD) -o $@ _go_.$O _go_.$O: $(GOFILES) $(PREREQ) - $(QUOTED_GOBIN)/$(GC) -o $@ $(GOFILES) + $(GC) -o $@ $(GOFILES) install: $(QUOTED_GOBIN)/$(TARG) diff --git a/src/Make.common b/src/Make.common index 42bb64e84..e3f415a1f 100644 --- a/src/Make.common +++ b/src/Make.common @@ -2,25 +2,12 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# GNU Make syntax: -ifndef GOBIN -GOBIN=$(HOME)/bin -endif - -# ugly hack to deal with whitespaces in $GOBIN -nullstring := -space := $(nullstring) # a space at the end -ifndef GOBIN -QUOTED_HOME=$(subst $(space),\ ,$(HOME)) -GOBIN=$(QUOTED_HOME)/bin -endif -QUOTED_GOBIN=$(subst $(space),\ ,$(GOBIN)) - -# ugly hack to deal with whitespaces in $GOROOT -QUOTED_GOROOT:=$(subst $(space),\ ,$(GOROOT)) - clean: rm -rf *.o *.a *.[$(OS)] [$(OS)].out $(CLEANFILES) %.make: - (cd $* && $(QUOTED_GOBIN)/gomake install) + (cd $* && gomake install) + +.PHONY: all clean nuke install coverage test bench testpackage-clean\ + importpath dir + diff --git a/src/Make.conf b/src/Make.conf deleted file mode 100644 index fa7177aa8..000000000 --- a/src/Make.conf +++ /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. - -CFLAGS=-ggdb -I"$(GOROOT)"/include -O2 -fno-inline -O=o -YFLAGS=-d -# GNU Make syntax: -nullstring := -space := $(nullstring) # a space at the end -ifndef GOBIN -QUOTED_HOME=$(subst $(space),\ ,$(HOME)) -GOBIN=$(QUOTED_HOME)/bin -endif -QUOTED_GOBIN=$(subst $(space),\ ,$(GOBIN)) - -CC=$(QUOTED_GOBIN)/quietgcc -LD=$(QUOTED_GOBIN)/quietgcc -PWD=$(shell pwd) - -%.$O: %.c - $(CC) $(CFLAGS) -c "$(PWD)"/$*.c diff --git a/src/Make.inc b/src/Make.inc new file mode 100644 index 000000000..2889c7edf --- /dev/null +++ b/src/Make.inc @@ -0,0 +1,146 @@ +# 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. + +# Makefile included by all other Go makefiles. + +# Clear variables that must come from Makefiles, +# not the environment. +LIB:= +TARG:= +GOFILES:= +HFILES:= +OFILES:= +YFILES:= + +# GOROOT must be set. +ifeq ($(GOROOT),) +$(error $$GOROOT is not set; use gomake or set $$GOROOT in your environment) +endif + +# Set up GOROOT_FINAL, GOARCH, GOOS if needed. +GOROOT_FINAL?=$(GOROOT) + +ifeq ($(GOHOSTOS),) +GOHOSTOS:=$(shell uname | tr A-Z a-z | sed 's/mingw/windows/; s/.*windows.*/windows/') +endif + +ifeq ($(GOOS),) +GOOS:=$(GOHOSTOS) +endif + +ifeq ($(GOOS),darwin) +else ifeq ($(GOOS),freebsd) +else ifeq ($(GOOS),linux) +else ifeq ($(GOOS),tiny) +else ifeq ($(GOOS),plan9) +else ifeq ($(GOOS),windows) +else +$(error Invalid $$GOOS '$(GOOS)'; must be darwin, freebsd, linux, plan9, tiny, or windows) +endif + +ifeq ($(GOHOSTARCH),) +ifeq ($(GOHOSTOS),darwin) +# Even on 64-bit platform, darwin uname -m prints i386. +# Check for amd64 with sysctl instead. +GOHOSTARCH:=${shell if sysctl machdep.cpu.extfeatures | grep EM64T >/dev/null; then echo amd64; else uname -m | sed 's/i386/386/'; fi} +else +# Ask uname -m for the processor. +GOHOSTARCH:=${shell uname -m | sed 's/^..86$$/386/; s/^.86$$/386/; s/x86_64/amd64/; s/arm.*/arm/'} +endif +endif + +ifeq ($(GOARCH),) +GOARCH:=$(GOHOSTARCH) +endif + +# darwin requires GOHOSTARCH match GOARCH +ifeq ($(GOOS),darwin) +GOHOSTARCH:=$(GOARCH) +endif + +ifeq ($(GOARCH),386) +O:=8 +else ifeq ($(GOARCH),amd64) +O:=6 +else ifeq ($(GOARCH),arm) + +O:=5 +ifeq ($(GOOS),linux) +else +$(error Invalid $$GOOS '$(GOOS)' for GOARCH=arm; must be linux) +endif + +else +$(error Invalid $$GOARCH '$(GOARCH)'; must be 386, amd64, or arm) +endif + +# Save for recursive make to avoid recomputing. +export GOARCH GOOS GOHOSTARCH GOHOSTOS + +# ugly hack to deal with whitespaces in $GOROOT +nullstring := +space := $(nullstring) # a space at the end +QUOTED_GOROOT:=$(subst $(space),\ ,$(GOROOT)) + +# default GOBIN +ifndef GOBIN +GOBIN=$(QUOTED_GOROOT)/bin +endif +QUOTED_GOBIN=$(subst $(space),\ ,$(GOBIN)) + +AS=${O}a +CC=${O}c +GC=${O}g +LD=${O}l +OS=568vq +CFLAGS=-FVw + +HOST_CC=quietgcc +HOST_LD=quietgcc +HOST_O=o +HOST_YFLAGS=-d +HOST_AR?=ar + +# These two variables can be overridden in the environment +# to build with other flags. They are like $CFLAGS and $LDFLAGS +# in a more typical GNU build. We are more explicit about the names +# here because there are different compilers being run during the +# build (both gcc and 6c, for example). +HOST_EXTRA_CFLAGS?=-ggdb -O2 +HOST_EXTRA_LDFLAGS?= + +HOST_CFLAGS=-I"$(GOROOT)/include" $(HOST_EXTRA_CFLAGS) +HOST_LDFLAGS=$(HOST_EXTRA_LDFLAGS) +PWD=$(shell pwd) + +# Make environment more standard. +LANG:= +LC_ALL:=C +LC_CTYPE:=C +GREP_OPTIONS:= +GREP_COLORS:= +export LANG LC_ALL LC_CTYPE GREP_OPTIONS GREP_COLORS + +go-env: + @echo export GOARCH=$(GOARCH) + @echo export GOOS=$(GOOS) + @echo export GOHOSTARCH=$(GOHOSTARCH) + @echo export GOHOSTOS=$(GOHOSTOS) + @echo export O=$O + @echo export AS="$(AS)" + @echo export CC="$(CC)" + @echo export GC="$(GC)" + @echo export LD="$(LD)" + @echo export OS="$(OS)" + @echo export CFLAGS="$(CFLAGS)" + @echo export LANG="$(LANG)" + @echo export LC_ALL="$(LC_ALL)" + @echo export LC_CTYPE="$(LC_CTYPE)" + @echo export GREP_OPTIONS="$(GREP_OPTIONS)" + @echo export GREP_COLORS="$(GREP_COLORS)" + @echo MAKE_GO_ENV_WORKED=1 + +# Don't let the targets in this file be used +# as the default make target. +.DEFAULT_GOAL:= diff --git a/src/Make.pkg b/src/Make.pkg index 39c19611e..ec7d5722e 100644 --- a/src/Make.pkg +++ b/src/Make.pkg @@ -6,13 +6,18 @@ all: package package: _obj/$(TARG).a testpackage: _test/$(TARG).a -# ugly hack to deal with whitespaces in $GOROOT -nullstring := -space := $(nullstring) # a space at the end -QUOTED_GOROOT:=$(subst $(space),\ ,$(GOROOT)) - include $(QUOTED_GOROOT)/src/Make.common +# The quietgcc wrapper is for our own source code +# while building the libraries, not arbitrary source code +# as encountered by cgo. +ifeq ($(HOST_CC),quietgcc) +HOST_CC:=gcc +endif +ifeq ($(HOST_LD),quietgcc) +HOST_LD:=gcc +endif + # GNU Make 3.80 has a bug in lastword # elem=$(lastword $(subst /, ,$(TARG))) TARG_words=$(subst /, ,$(TARG)) @@ -31,27 +36,26 @@ INSTALLFILES+=$(pkgdir)/$(TARG).a # The rest of the cgo rules are below, but these variable updates # must be done here so they apply to the main rules. ifdef CGOFILES -CGOTARG=cgo_$(subst /,_,$(TARG)) -GOFILES+=$(patsubst %.go,%.cgo1.go,$(CGOFILES)) -GOFILES+=_cgo_gotypes.go -OFILES+=_cgo_defun.$O -GCC_OFILES=$(patsubst %.go,%.cgo2.o,$(CGOFILES)) -INSTALLFILES+=$(pkgdir)/$(CGOTARG).so +GOFILES+=$(patsubst %.go,%.cgo1.go,$(CGOFILES)) _cgo_gotypes.go +CGO_OFILES+=$(patsubst %.go,%.cgo2.o,$(CGOFILES)) _cgo_export.o +OFILES+=_cgo_defun.$O _cgo_import.$O $(CGO_OFILES) endif PREREQ+=$(patsubst %,%.make,$(DEPS)) coverage: - $(QUOTED_GOBIN)/gotest - $(QUOTED_GOBIN)/6cov -g $(shell pwd) $O.out | grep -v '_test\.go:' + gotest + 6cov -g $(shell pwd) $O.out | grep -v '_test\.go:' -CLEANFILES+=*.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* *.so _obj _test _testmain.go +CLEANFILES+=*.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* +CLEANFILES+=_cgo_.c _cgo_import.c _cgo_main.c +CLEANFILES+=*.so _obj _test _testmain.go *.exe test: - $(QUOTED_GOBIN)/gotest + gotest bench: - $(QUOTED_GOBIN)/gotest -benchmarks=. -match="Do not run tests" + gotest -benchmarks=. -match="Do not run tests" nuke: clean rm -f $(pkgdir)/$(TARG).a @@ -66,20 +70,20 @@ $(pkgdir)/$(TARG).a: _obj/$(TARG).a cp _obj/$(TARG).a "$@" _go_.$O: $(GOFILES) $(PREREQ) - $(QUOTED_GOBIN)/$(GC) -o $@ $(GOFILES) + $(GC) -o $@ $(GOFILES) _gotest_.$O: $(GOFILES) $(GOTESTFILES) $(PREREQ) - $(QUOTED_GOBIN)/$(GC) -o $@ $(GOFILES) $(GOTESTFILES) + $(GC) -o $@ $(GOFILES) $(GOTESTFILES) _obj/$(TARG).a: _go_.$O $(OFILES) @mkdir -p _obj/$(dir) rm -f _obj/$(TARG).a - $(QUOTED_GOBIN)/gopack grc $@ _go_.$O $(OFILES) + gopack grc $@ _go_.$O $(OFILES) _test/$(TARG).a: _gotest_.$O $(OFILES) @mkdir -p _test/$(dir) rm -f _test/$(TARG).a - $(QUOTED_GOBIN)/gopack grc $@ _gotest_.$O $(OFILES) + gopack grc $@ _gotest_.$O $(OFILES) importpath: @echo $(TARG) @@ -107,58 +111,66 @@ dir: # x.cgo2.c - C implementations compiled with gcc to create a dynamic library # -_cgo_defun.c _cgo_gotypes.go _cgo_export.c _cgo_export.h: $(CGOFILES) - CGOPKGPATH=$(dir) $(QUOTED_GOBIN)/cgo $(CGO_CFLAGS) $(CGOFILES) +ifdef CGOFILES +_cgo_defun.c: $(CGOFILES) + CGOPKGPATH=$(dir) cgo -- $(CGO_CFLAGS) $(CGOFILES) -# Ugly but necessary -%.cgo1.go: _cgo_defun.c _cgo_gotypes.go +# Ugly but necessary - cgo writes these files too. +_cgo_gotypes.go _cgo_export.c _cgo_export.h _cgo_main.c: _cgo_defun.c @true -%.cgo2.c: _cgo_defun.c _cgo_gotypes.go +%.cgo1.go %.cgo2.c: _cgo_defun.c @true +endif + +# Compile rules for gcc source files. +%.o: %.c + $(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -g -fPIC -O2 -o $@ -c $(CGO_CFLAGS) $*.c -%.cgo2.o: %.cgo2.c - gcc $(_CGO_CFLAGS_$(GOARCH)) -fPIC -O2 -o $@ -c $(CGO_CFLAGS) $*.cgo2.c +# To find out which symbols are needed from external libraries +# and which libraries are needed, we build a simple a.out that +# links all the objects we just created and then use cgo -dynimport +# to inspect it. That is, we make gcc tell us which dynamic symbols +# and libraries are involved, instead of duplicating gcc's logic ourselves. +# After main we have to define all the symbols that will be provided +# by Go code. That's crosscall2 and any exported symbols. -_cgo_export.o: _cgo_export.c _cgo_export.h - gcc $(_CGO_CFLAGS_$(GOARCH)) -fPIC -O2 -o $@ -c $(CGO_CFLAGS) _cgo_export.c +_cgo_main.o: _cgo_main.c + $(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -g -fPIC -O2 -o $@ -c $(CGO_CFLAGS) _cgo_main.c + +_cgo1_.o: _cgo_main.o $(CGO_OFILES) + $(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -g -fPIC -O2 -o $@ $^ $(CGO_LDFLAGS) + +_cgo_import.c: _cgo1_.o + cgo -dynimport _cgo1_.o >_$@ && mv -f _$@ $@ # The rules above added x.cgo1.go and _cgo_gotypes.go to $(GOFILES), # added _cgo_defun.$O to $OFILES, and added the installed copy of # package_x.so (built from x.cgo2.c) to $(INSTALLFILES). -RUNTIME_CFLAGS_amd64=-D_64BIT -RUNTIME_CFLAGS=-I"$(GOROOT)/src/pkg/runtime" $(RUNTIME_CFLAGS_$(GOARCH)) - # Have to run gcc with the right size argument on hybrid 32/64 machines. _CGO_CFLAGS_386=-m32 _CGO_CFLAGS_amd64=-m64 _CGO_LDFLAGS_freebsd=-shared -lpthread -lm _CGO_LDFLAGS_linux=-shared -lpthread -lm _CGO_LDFLAGS_darwin=-dynamiclib -Wl,-undefined,dynamic_lookup +_CGO_LDFLAGS_windows=-shared -lm -mthreads - -# Compile x.cgo4.c with gcc to make package_x.so. +# Have to compile the runtime header. +RUNTIME_CFLAGS=-I"$(pkgdir)" # Compile _cgo_defun.c with 6c; needs access to the runtime headers. _cgo_defun.$O: _cgo_defun.c - $(QUOTED_GOBIN)/$(CC) $(CFLAGS) $(RUNTIME_CFLAGS) _cgo_defun.c - -_cgo_.so: $(GCC_OFILES) $(CGO_DEPS) - gcc $(_CGO_CFLAGS_$(GOARCH)) -o $@ $(GCC_OFILES) $(CGO_LDFLAGS) $(_CGO_LDFLAGS_$(GOOS)) - -$(pkgdir)/$(CGOTARG).so: _cgo_.so - @test -d $(QUOTED_GOROOT)/pkg && mkdir -p $(pkgdir)/$(dir) - cp _cgo_.so "$@" + $(CC) $(CFLAGS) $(RUNTIME_CFLAGS) _cgo_defun.c # Generic build rules. # These come last so that the rules above can override them # for more specific file names. %.$O: %.c - $(QUOTED_GOBIN)/$(CC) $(CFLAGS) $*.c + $(CC) $(CFLAGS) $*.c %.$O: %.s - $(QUOTED_GOBIN)/$(AS) $*.s + $(AS) $*.s %.$O: $(HFILES) diff --git a/src/all-arm.bash b/src/all-arm.bash deleted file mode 100755 index 73db3fb85..000000000 --- a/src/all-arm.bash +++ /dev/null @@ -1,30 +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. - -set -e - -export GOOS=linux -export GOARCH=arm - -bash make.bash - -# TODO(kaib): add in proper tests -#bash run.bash - -set -e - -xcd() { - echo - echo --- cd $1 - builtin cd $1 -} - -# temporarily turn GC off -# TODO(kaib): reenable GC once everything else works -export GOGC=off - -(xcd ../test -./run-arm -) || exit $? diff --git a/src/all-nacl.bash b/src/all-nacl.bash deleted file mode 100755 index 0238c2a3e..000000000 --- a/src/all-nacl.bash +++ /dev/null @@ -1,48 +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. - -# TODO(rsc): delete in favor of all.bash once nacl support is complete - -export GOARCH=386 -export GOOS=nacl -export GORUN=${GORUN:-$GOROOT/misc/nacl/naclrun} - -set -e -bash make.bash - -xcd() { - echo - echo --- cd $1 - builtin cd $1 -} - -(xcd pkg -make install -make test -) || exit $? - -(xcd pkg/exp/nacl/srpc -make clean -make install -) || exit $? - -(xcd pkg/exp/nacl/av -make clean -make install -) || exit $? - -(xcd pkg/exp/4s -make clean -make -) || exit $? - -(xcd pkg/exp/spacewar -make clean -make -) || exit $? - -(xcd ../test -./run -) || exit $? diff --git a/src/all.bash b/src/all.bash index 00c1ca74d..00110d2da 100755 --- a/src/all.bash +++ b/src/all.bash @@ -4,5 +4,11 @@ # license that can be found in the LICENSE file. set -e -bash make.bash -bash run.bash --no-rebuild +if [ ! -f make.bash ]; then + echo 'all.bash must be run from $GOROOT/src' 1>&2 + exit 1 +fi +. ./make.bash +bash run.bash --no-env --no-rebuild +installed # function defined by make.bash + diff --git a/src/clean.bash b/src/clean.bash index 567e6e319..d96eb52df 100755 --- a/src/clean.bash +++ b/src/clean.bash @@ -5,16 +5,21 @@ set -e -if [ -z "$GOROOT" ] ; then - echo '$GOROOT not set' +if [ ! -f env.bash ]; then + echo 'clean.bash must be run from $GOROOT/src' 1>&2 exit 1 fi +. ./env.bash +if [ ! -f Make.inc ] ; then + GOROOT_FINAL=${GOROOT_FINAL:-$GOROOT} + sed 's!@@GOROOT@@!'"$GOROOT_FINAL"'!' Make.inc.in >Make.inc +fi -GOBIN="${GOBIN:-$HOME/bin}" - -rm -rf "$GOROOT"/pkg/${GOOS}_$GOARCH +if [ "$1" != "--nopkg" ]; then + rm -rf "$GOROOT"/pkg/${GOOS}_$GOARCH +fi rm -f "$GOROOT"/lib/*.a -for i in lib9 libbio libcgo libmach cmd pkg \ +for i in lib9 libbio libmach cmd pkg \ ../misc/cgo/gmp ../misc/cgo/stdio \ ../test/bench ../test/garbage do( @@ -22,6 +27,6 @@ do( if test -f clean.bash; then bash clean.bash else - "$GOBIN"/gomake clean + gomake clean fi )done diff --git a/src/cmd/5a/Makefile b/src/cmd/5a/Makefile index f01b017da..f4463c97b 100644 --- a/src/cmd/5a/Makefile +++ b/src/cmd/5a/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 5a\ +TARG=5a HFILES=\ a.h\ @@ -15,26 +15,11 @@ HFILES=\ OFILES=\ y.tab.$O\ lex.$O\ -# ../5l/enam.$O\ + ../5l/enam.$O\ YFILES=\ a.y\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lbio -l9 - -$(OFILES): $(HFILES) +include ../../Make.ccmd lex.$O: ../cc/macbody ../cc/lexbody - -y.tab.h: $(YFILES) - bison -y $(YFLAGS) $(YFILES) - -y.tab.c: y.tab.h - test -f y.tab.c && touch y.tab.c - -clean: - rm -f *.$O $(TARG) *.5 enam.c 5.out a.out y.tab.h y.tab.c - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) diff --git a/src/cmd/5a/a.h b/src/cmd/5a/a.h index 6cd5af8c6..bc4f433e1 100644 --- a/src/cmd/5a/a.h +++ b/src/cmd/5a/a.h @@ -1,7 +1,7 @@ // Inferno utils/5a/a.h // http://code.google.com/p/inferno-os/source/browse/utils/5a/a.h // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -148,6 +148,7 @@ EXTERN int pass; EXTERN char* pathname; EXTERN int32 pc; EXTERN int peekc; +EXTERN int32 stmtline; EXTERN int sym; EXTERN char* symb; EXTERN int thechar; @@ -157,7 +158,7 @@ EXTERN Biobuf obuf; void* alloc(int32); void* allocn(void*, int32, int32); -void ensuresymb(int32); +void ensuresymb(int32); void errorexit(void); void pushio(void); void newio(void); diff --git a/src/cmd/5a/a.y b/src/cmd/5a/a.y index bb30ac698..b39c916ab 100644 --- a/src/cmd/5a/a.y +++ b/src/cmd/5a/a.y @@ -1,7 +1,7 @@ // Inferno utils/5a/a.y // http://code.google.com/p/inferno-os/source/browse/utils/5a/a.y // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -63,7 +63,11 @@ %type imm ximm name oreg ireg nireg ioreg imsr %% prog: -| prog line +| prog + { + stmtline = lineno; + } + line line: LLAB ':' diff --git a/src/cmd/5a/lex.c b/src/cmd/5a/lex.c index 2cc0993e4..b36094a78 100644 --- a/src/cmd/5a/lex.c +++ b/src/cmd/5a/lex.c @@ -1,7 +1,7 @@ // Inferno utils/5a/lex.c // http://code.google.com/p/inferno-os/source/browse/utils/5a/lex.c // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -658,10 +658,10 @@ jackpot: Bputc(&obuf, a); Bputc(&obuf, scond); Bputc(&obuf, reg); - Bputc(&obuf, lineno); - Bputc(&obuf, lineno>>8); - Bputc(&obuf, lineno>>16); - Bputc(&obuf, lineno>>24); + Bputc(&obuf, stmtline); + Bputc(&obuf, stmtline>>8); + Bputc(&obuf, stmtline>>16); + Bputc(&obuf, stmtline>>24); zaddr(g1, sf); zaddr(g2, st); diff --git a/src/cmd/5c/Makefile b/src/cmd/5c/Makefile index b534206f3..70b614e8a 100644 --- a/src/cmd/5c/Makefile +++ b/src/cmd/5c/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 5c\ +TARG=5c HFILES=\ gc.h\ @@ -26,18 +26,9 @@ OFILES=\ ../5l/enam.$O\ LIB=\ - ../cc/cc.a$O + ../cc/cc.a\ -$(TARG): $(OFILES) $(LIB) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) $(LIB) -lbio -l9 - -$(OFILES): $(HFILES) - -clean: - rm -f *.$O $(TARG) *.5 enam.c 5.out a.out - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +include ../../Make.ccmd %.$O: ../cc/%.c - $(CC) $(CFLAGS) -c -I. -o $@ ../cc/$*.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../cc/$*.c diff --git a/src/cmd/5c/list.c b/src/cmd/5c/list.c index c792c130d..ab0fae83c 100644 --- a/src/cmd/5c/list.c +++ b/src/cmd/5c/list.c @@ -59,7 +59,7 @@ Bconv(Fmt *fp) if(str[0]) strcat(str, " "); if(var[i].sym == S) { - sprint(ss, "$%ld", var[i].offset); + sprint(ss, "$%d", var[i].offset); s = ss; } else s = var[i].sym->name; @@ -204,7 +204,7 @@ Dconv(Fmt *fp) break; case D_BRANCH: - sprint(str, "%ld(PC)", a->offset-pc); + sprint(str, "%d(PC)", a->offset-pc); break; case D_FCONST: @@ -307,7 +307,7 @@ Nconv(Fmt *fp) a = va_arg(fp->args, Adr*); s = a->sym; if(s == S) { - sprint(str, "%ld", a->offset); + sprint(str, "%d", a->offset); goto out; } switch(a->name) { @@ -316,23 +316,23 @@ Nconv(Fmt *fp) break; case D_NONE: - sprint(str, "%ld", a->offset); + sprint(str, "%d", a->offset); break; case D_EXTERN: - sprint(str, "%s+%ld(SB)", s->name, a->offset); + sprint(str, "%s+%d(SB)", s->name, a->offset); break; case D_STATIC: - sprint(str, "%s<>+%ld(SB)", s->name, a->offset); + sprint(str, "%s<>+%d(SB)", s->name, a->offset); break; case D_AUTO: - sprint(str, "%s-%ld(SP)", s->name, -a->offset); + sprint(str, "%s-%d(SP)", s->name, -a->offset); break; case D_PARAM: - sprint(str, "%s+%ld(FP)", s->name, a->offset); + sprint(str, "%s+%d(FP)", s->name, a->offset); break; } out: diff --git a/src/cmd/5c/mul.c b/src/cmd/5c/mul.c index 121f67d5b..ff50c4845 100644 --- a/src/cmd/5c/mul.c +++ b/src/cmd/5c/mul.c @@ -115,7 +115,7 @@ mulcon0(int32 v) if(docode(hintab[g].hint, m->code, 1, 0)) return m; - print("multiply table failure %ld\n", v); + print("multiply table failure %d\n", v); m->code[0] = 0; return 0; @@ -132,7 +132,7 @@ no: if(gen1(g)) { if(docode(hint, m->code, 1, 0)) return m; - print("multiply table failure %ld\n", v); + print("multiply table failure %d\n", v); break; } } diff --git a/src/cmd/5c/peep.c b/src/cmd/5c/peep.c index 4959d414d..8945ee732 100644 --- a/src/cmd/5c/peep.c +++ b/src/cmd/5c/peep.c @@ -1324,8 +1324,6 @@ predicable(Prog *p) || p->as == ASIGNAME || p->as == ATEXT || p->as == AWORD - || p->as == ADYNT - || p->as == AINIT || p->as == ABCASE || p->as == ACASE) return 0; diff --git a/src/cmd/5c/reg.c b/src/cmd/5c/reg.c index f2d38d519..8c9794418 100644 --- a/src/cmd/5c/reg.c +++ b/src/cmd/5c/reg.c @@ -309,7 +309,7 @@ loop2: if(debug['R'] && debug['v']) { print("\nprop structure:\n"); for(r = firstr; r != R; r = r->link) { - print("%ld:%P", r->loop, r->prog); + print("%d:%P", r->loop, r->prog); for(z=0; zset.b[z] | r->refahead.b[z] | r->calahead.b[z] | @@ -955,7 +955,7 @@ paint1(Reg *r, int bn) if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) { change -= CLOAD * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tld %B $%d\n", r->loop, + print("%d%P\td %B $%d\n", r->loop, r->prog, blsh(bn), change); } for(;;) { @@ -965,21 +965,21 @@ paint1(Reg *r, int bn) if(r->use1.b[z] & bb) { change += CREF * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tu1 %B $%d\n", r->loop, + print("%d%P\tu1 %B $%d\n", r->loop, p, blsh(bn), change); } if((r->use2.b[z]|r->set.b[z]) & bb) { change += CREF * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tu2 %B $%d\n", r->loop, + print("%d%P\tu2 %B $%d\n", r->loop, p, blsh(bn), change); } if(STORE(r) & r->regdiff.b[z] & bb) { change -= CLOAD * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tst %B $%d\n", r->loop, + print("%d%P\tst %B $%d\n", r->loop, p, blsh(bn), change); } diff --git a/src/cmd/5c/swt.c b/src/cmd/5c/swt.c index cefbf53d9..43eb73c94 100644 --- a/src/cmd/5c/swt.c +++ b/src/cmd/5c/swt.c @@ -47,7 +47,7 @@ swit1(C1 *q, int nc, int32 def, Node *n) if(nc < 5) { for(i=0; ival); + print("case = %.8ux\n", q->val); gopcode(OEQ, nodconst(q->val), n, Z); patch(p, q->label); q++; @@ -60,7 +60,7 @@ swit1(C1 *q, int nc, int32 def, Node *n) i = nc / 2; r = q+i; if(debug['W']) - print("case > %.8lux\n", r->val); + print("case > %.8ux\n", r->val); gopcode(OGT, nodconst(r->val), n, Z); sp = p; gopcode(OEQ, nodconst(r->val), n, Z); /* just gen the B.EQ */ @@ -68,7 +68,7 @@ swit1(C1 *q, int nc, int32 def, Node *n) swit1(q, i, def, n); if(debug['W']) - print("case < %.8lux\n", r->val); + print("case < %.8ux\n", r->val); patch(sp, pc); swit1(r+1, nc-i-1, def, n); return; @@ -81,7 +81,7 @@ direct: patch(p, def); for(i=0; ival); + print("case = %.8ux\n", q->val); while(q->val != v) { nextpc(); p->as = ABCASE; @@ -227,7 +227,7 @@ mulcon(Node *n, Node *nn) return 0; } if(debug['M'] && debug['v']) - print("%L multiply: %ld\n", n->lineno, v); + print("%L multiply: %d\n", n->lineno, v); memmove(code, m->code, sizeof(m->code)); code[sizeof(m->code)] = 0; @@ -606,7 +606,7 @@ zaddr(char *bp, Adr *a, int s) } int32 -align(int32 i, Type *t, int op) +align(int32 i, Type *t, int op, int32 *maxalign) { int32 o; Type *v; @@ -620,7 +620,9 @@ align(int32 i, Type *t, int op) break; case Asu2: /* padding at end of a struct */ - w = SZ_LONG; + w = *maxalign; + if(w < 1) + w = 1; if(packflg) w = packflg; break; @@ -628,10 +630,16 @@ align(int32 i, Type *t, int op) case Ael1: /* initial align of struct element */ for(v=t; v->etype==TARRAY; v=v->link) ; - w = ewidth[v->etype]; - if(w <= 0 || w >= SZ_LONG) - w = SZ_LONG; - if(packflg) + if(v->etype == TSTRUCT || v->etype == TUNION) + w = v->align; + else { + w = ewidth[v->etype]; + if(w == 8) + w = 4; + } + if(w < 1 || w > SZ_LONG) + fatal(Z, "align"); + if(packflg) w = packflg; break; @@ -641,8 +649,8 @@ align(int32 i, Type *t, int op) case Aarg0: /* initial passbyptr argument in arg list */ if(typesuv[t->etype]) { - o = align(o, types[TIND], Aarg1); - o = align(o, types[TIND], Aarg2); + o = align(o, types[TIND], Aarg1, nil); + o = align(o, types[TIND], Aarg2, nil); } break; @@ -661,14 +669,16 @@ align(int32 i, Type *t, int op) break; case Aaut3: /* total align of automatic */ - o = align(o, t, Ael2); - o = align(o, t, Ael1); + o = align(o, t, Ael2, nil); + o = align(o, t, Ael1, nil); w = SZ_LONG; /* because of a pun in cc/dcl.c:contig() */ break; } o = xround(o, w); + if(maxalign != nil && *maxalign < w) + *maxalign = w; if(debug['A']) - print("align %s %ld %T = %ld\n", bnames[op], i, t, o); + print("align %s %d %T = %d\n", bnames[op], i, t, o); return o; } diff --git a/src/cmd/5c/txt.c b/src/cmd/5c/txt.c index 9dac0312f..0f17cea89 100644 --- a/src/cmd/5c/txt.c +++ b/src/cmd/5c/txt.c @@ -388,7 +388,7 @@ err: void regsalloc(Node *n, Node *nn) { - cursafe = align(cursafe, nn->type, Aaut3); + cursafe = align(cursafe, nn->type, Aaut3, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); *n = *nodsafe; n->xoffset = -(stkoff + cursafe); @@ -402,22 +402,22 @@ regaalloc1(Node *n, Node *nn) { nodreg(n, nn, REGARG); reg[REGARG]++; - curarg = align(curarg, nn->type, Aarg1); - curarg = align(curarg, nn->type, Aarg2); + curarg = align(curarg, nn->type, Aarg1, nil); + curarg = align(curarg, nn->type, Aarg2, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); } void regaalloc(Node *n, Node *nn) { - curarg = align(curarg, nn->type, Aarg1); + curarg = align(curarg, nn->type, Aarg1, nil); *n = *nn; n->op = OINDREG; n->reg = REGSP; n->xoffset = curarg + SZ_LONG; n->complex = 0; n->addable = 20; - curarg = align(curarg, nn->type, Aarg2); + curarg = align(curarg, nn->type, Aarg2, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); } @@ -580,7 +580,8 @@ void gmove(Node *f, Node *t) { int ft, tt, a; - Node nod; + Node nod, nod1; + Prog *p1; ft = f->type->etype; tt = t->type->etype; @@ -709,21 +710,53 @@ gmove(Node *f, Node *t) } break; case TUINT: - case TINT: case TULONG: + if(tt == TFLOAT || tt == TDOUBLE) { + // ugly and probably longer than necessary, + // but vfp has a single instruction for this, + // so hopefully it won't last long. + // + // tmp = f + // tmp1 = tmp & 0x80000000 + // tmp ^= tmp1 + // t = float(int32(tmp)) + // if(tmp1) + // t += 2147483648. + // + regalloc(&nod, f, Z); + regalloc(&nod1, f, Z); + gins(AMOVW, f, &nod); + gins(AMOVW, &nod, &nod1); + gins(AAND, nodconst(0x80000000), &nod1); + gins(AEOR, &nod1, &nod); + if(tt == TFLOAT) + gins(AMOVWF, &nod, t); + else + gins(AMOVWD, &nod, t); + gins(ACMP, nodconst(0), Z); + raddr(&nod1, p); + gins(ABEQ, Z, Z); + regfree(&nod); + regfree(&nod1); + p1 = p; + regalloc(&nod, t, Z); + gins(AMOVF, nodfconst(2147483648.), &nod); + gins(AADDF, &nod, t); + regfree(&nod); + patch(p1, pc); + return; + } + // fall through + + case TINT: case TLONG: case TIND: switch(tt) { case TDOUBLE: - case TVLONG: gins(AMOVWD, f, t); - if(ft == TULONG) { - } return; case TFLOAT: gins(AMOVWF, f, t); - if(ft == TULONG) { - } return; case TINT: case TUINT: @@ -741,7 +774,6 @@ gmove(Node *f, Node *t) case TSHORT: switch(tt) { case TDOUBLE: - case TVLONG: regalloc(&nod, f, Z); gins(AMOVH, f, &nod); gins(AMOVWD, &nod, t); @@ -771,7 +803,6 @@ gmove(Node *f, Node *t) case TUSHORT: switch(tt) { case TDOUBLE: - case TVLONG: regalloc(&nod, f, Z); gins(AMOVHU, f, &nod); gins(AMOVWD, &nod, t); @@ -801,7 +832,6 @@ gmove(Node *f, Node *t) case TCHAR: switch(tt) { case TDOUBLE: - case TVLONG: regalloc(&nod, f, Z); gins(AMOVB, f, &nod); gins(AMOVWD, &nod, t); @@ -831,7 +861,6 @@ gmove(Node *f, Node *t) case TUCHAR: switch(tt) { case TDOUBLE: - case TVLONG: regalloc(&nod, f, Z); gins(AMOVBU, f, &nod); gins(AMOVWD, &nod, t); diff --git a/src/cmd/5g/Makefile b/src/cmd/5g/Makefile index 123af19cd..6873fbc68 100644 --- a/src/cmd/5g/Makefile +++ b/src/cmd/5g/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 5g +TARG=5g HFILES=\ ../gc/go.h\ @@ -21,18 +21,15 @@ OFILES=\ ggen.$O\ gsubr.$O\ cgen.$O\ - cgen64.$O + cgen64.$O\ + cplx.$O\ + reg.$O\ + peep.$O\ LIB=\ - ../gc/gc.a$O + ../gc/gc.a\ -$(TARG): $(OFILES) $(LIB) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) $(LIB) -lbio -l9 -lm +include ../../Make.ccmd -$(OFILES): $(HFILES) - -clean: - rm -f *.o $(TARG) *.5 enam.c 5.out a.out - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +%.$O: ../gc/%.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../gc/$*.c diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c index 8072c3ceb..1328f4be6 100644 --- a/src/cmd/5g/cgen.c +++ b/src/cmd/5g/cgen.c @@ -4,33 +4,6 @@ #include "gg.h" -void -mgen(Node *n, Node *n1, Node *rg) -{ - n1->ostk = 0; - n1->op = OEMPTY; - - if(n->addable) { - *n1 = *n; - n1->ostk = 0; - if(n1->op == OREGISTER || n1->op == OINDREG) - reg[n->val.u.reg]++; - return; - } - if(n->type->width > widthptr) - tempname(n1, n->type); - else - regalloc(n1, n->type, rg); - cgen(n, n1); -} - -void -mfree(Node *n) -{ - if(n->op == OREGISTER) - regfree(n); -} - /* * generate: * res = n; @@ -55,12 +28,6 @@ cgen(Node *n, Node *res) if(res == N || res->type == T) fatal("cgen: res nil"); - // TODO compile complex - if(n != N && n->type != T && iscomplex[n->type->etype]) - return; - if(res != N && res->type != T && iscomplex[res->type->etype]) - return; - while(n->op == OCONVNOP) n = n->left; @@ -80,6 +47,7 @@ cgen(Node *n, Node *res) goto ret; } + // update addressability for string, slice // can't do in walk because n->left->addable // changes if n->left is an escaping local variable. @@ -96,7 +64,9 @@ cgen(Node *n, Node *res) // if both are addressable, move if(n->addable && res->addable) { - if (is64(n->type) || is64(res->type) || n->op == OREGISTER || res->op == OREGISTER) { + if(is64(n->type) || is64(res->type) || + n->op == OREGISTER || res->op == OREGISTER || + iscomplex[n->type->etype] || iscomplex[res->type->etype]) { gmove(n, res); } else { regalloc(&n1, n->type, N); @@ -126,8 +96,13 @@ cgen(Node *n, Node *res) return; } + if(complexop(n, res)) { + complexgen(n, res); + return; + } + // if n is sudoaddable generate addr and move - if (!is64(n->type) && !is64(res->type)) { + if (!is64(n->type) && !is64(res->type) && !iscomplex[n->type->etype] && !iscomplex[res->type->etype]) { a = optoas(OAS, n->type); if(sudoaddable(a, n, &addr, &w)) { if (res->op != OREGISTER) { @@ -195,8 +170,8 @@ cgen(Node *n, Node *res) case OREAL: case OIMAG: case OCMPLX: - // TODO compile complex - return; + fatal("unexpected complex"); + break; // these call bgen to get a bool value case OOROR: @@ -269,10 +244,26 @@ cgen(Node *n, Node *res) cgen(nl, res); break; } - - mgen(nl, &n1, res); - gmove(&n1, res); - mfree(&n1); + if(nl->addable && !is64(nl->type)) { + regalloc(&n1, nl->type, res); + gmove(nl, &n1); + } else { + if(n->type->width > widthptr || is64(nl->type) || isfloat[nl->type->etype]) + tempname(&n1, nl->type); + else + regalloc(&n1, nl->type, res); + cgen(nl, &n1); + } + if(n->type->width > widthptr || is64(n->type) || isfloat[n->type->etype]) + tempname(&n2, n->type); + else + regalloc(&n2, n->type, N); + gmove(&n1, &n2); + gmove(&n2, res); + if(n1.op == OREGISTER) + regfree(&n1); + if(n2.op == OREGISTER) + regfree(&n2); break; case ODOT: @@ -460,6 +451,41 @@ ret: ; } +/* + * generate array index into res. + * n might be any size; res is 32-bit. + * returns Prog* to patch to panic call. + */ +Prog* +cgenindex(Node *n, Node *res) +{ + Node tmp, lo, hi, zero, n1, n2; + + if(!is64(n->type)) { + cgen(n, res); + return nil; + } + + tempname(&tmp, types[TINT64]); + cgen(n, &tmp); + split64(&tmp, &lo, &hi); + gmove(&lo, res); + if(debug['B']) { + splitclean(); + return nil; + } + regalloc(&n1, types[TINT32], N); + regalloc(&n2, types[TINT32], N); + nodconst(&zero, types[TINT32], 0); + gmove(&hi, &n1); + gmove(&zero, &n2); + gcmp(ACMP, &n1, &n2); + regfree(&n2); + regfree(&n1); + splitclean(); + return gbranch(ABNE, T); +} + /* * generate: * res = &n; @@ -469,10 +495,10 @@ agen(Node *n, Node *res) { Node *nl, *nr; Node n1, n2, n3, n4, n5, tmp; - Prog *p1; + Prog *p1, *p2; uint32 w; uint64 v; - Type *t; + int r; if(debug['g']) { dump("\nagen-res", res); @@ -504,7 +530,22 @@ agen(Node *n, Node *res) break; case OCALLMETH: - cgen_callmeth(n, 0); + case OCALLFUNC: + // Release res so that it is available for cgen_call. + // Pick it up again after the call. + r = -1; + if(n->ullman >= UINF) { + if(res->op == OREGISTER || res->op == OINDREG) { + r = res->val.u.reg; + reg[r]--; + } + } + if(n->op == OCALLMETH) + cgen_callmeth(n, 0); + else + cgen_call(n, 0); + if(r >= 0) + reg[r]++; cgen_aret(n, res); break; @@ -513,36 +554,36 @@ agen(Node *n, Node *res) cgen_aret(n, res); break; - case OCALLFUNC: - cgen_call(n, 0); - cgen_aret(n, res); - break; - case OINDEX: - // TODO(rsc): uint64 indices + p2 = nil; // to be patched to panicindex. w = n->type->width; if(nr->addable) { - agenr(nl, &n3, res); - if(!isconst(nr, CTINT)) { + if(!isconst(nr, CTINT)) tempname(&tmp, types[TINT32]); - cgen(nr, &tmp); + if(!isconst(nl, CTSTR)) + agenr(nl, &n3, res); + if(!isconst(nr, CTINT)) { + p2 = cgenindex(nr, &tmp); regalloc(&n1, tmp.type, N); gmove(&tmp, &n1); } } else if(nl->addable) { if(!isconst(nr, CTINT)) { tempname(&tmp, types[TINT32]); - cgen(nr, &tmp); + p2 = cgenindex(nr, &tmp); regalloc(&n1, tmp.type, N); gmove(&tmp, &n1); } - regalloc(&n3, types[tptr], res); - agen(nl, &n3); + if(!isconst(nl, CTSTR)) { + regalloc(&n3, types[tptr], res); + agen(nl, &n3); + } } else { tempname(&tmp, types[TINT32]); - cgen(nr, &tmp); + p2 = cgenindex(nr, &tmp); nr = &tmp; - agenr(nl, &n3, res); + if(!isconst(nl, CTSTR)) + agenr(nl, &n3, res); regalloc(&n1, tmp.type, N); gins(optoas(OAS, tmp.type), &tmp, &n1); } @@ -556,9 +597,10 @@ agen(Node *n, Node *res) // constant index if(isconst(nr, CTINT)) { + if(isconst(nl, CTSTR)) + fatal("constant string constant index"); v = mpgetfix(nr->val.u.xval); - if(isslice(nl->type)) { - + if(isslice(nl->type) || nl->type->etype == TSTRING) { if(!debug['B'] && !n->etype) { n1 = n3; n1.op = OINDREG; @@ -582,13 +624,6 @@ agen(Node *n, Node *res) n1.type = types[tptr]; n1.xoffset = Array_array; gmove(&n1, &n3); - } else - if(!debug['B'] && !n->etype) { - if(v < 0) - yyerror("out of bounds on array"); - else - if(v >= nl->type->bound) - yyerror("out of bounds on array"); } nodconst(&n2, types[tptr], v*w); @@ -602,19 +637,17 @@ agen(Node *n, Node *res) break; } - // type of the index - t = types[TUINT32]; - if(issigned[n1.type->etype]) - t = types[TINT32]; - - regalloc(&n2, t, &n1); // i + regalloc(&n2, types[TINT32], &n1); // i gmove(&n1, &n2); regfree(&n1); if(!debug['B'] && !n->etype) { // check bounds regalloc(&n4, types[TUINT32], N); - if(isslice(nl->type)) { + if(isconst(nl, CTSTR)) { + nodconst(&n1, types[TUINT32], nl->val.u.sval->len); + gmove(&n1, &n4); + } else if(isslice(nl->type) || nl->type->etype == TSTRING) { n1 = n3; n1.op = OINDREG; n1.type = types[tptr]; @@ -627,11 +660,18 @@ agen(Node *n, Node *res) gcmp(optoas(OCMP, types[TUINT32]), &n2, &n4); regfree(&n4); p1 = gbranch(optoas(OLT, types[TUINT32]), T); + if(p2) + patch(p2, pc); ginscall(panicindex, 0); patch(p1, pc); } - - if(isslice(nl->type)) { + + if(isconst(nl, CTSTR)) { + regalloc(&n3, types[tptr], res); + p1 = gins(AMOVW, N, &n3); + datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from); + p1->from.type = D_CONST; + } else if(isslice(nl->type) || nl->type->etype == TSTRING) { n1 = n3; n1.op = OINDREG; n1.type = types[tptr]; @@ -653,10 +693,10 @@ agen(Node *n, Node *res) else if(w == 8) gshift(AADD, &n2, SHIFT_LL, 3, &n3); } else { - regalloc(&n4, t, N); - nodconst(&n1, t, w); + regalloc(&n4, types[TUINT32], N); + nodconst(&n1, types[TUINT32], w); gmove(&n1, &n4); - gins(optoas(OMUL, t), &n4, &n2); + gins(optoas(OMUL, types[TUINT32]), &n4, &n2); gins(optoas(OADD, types[tptr]), &n2, &n3); regfree(&n4); gmove(&n3, res); @@ -769,12 +809,8 @@ igen(Node *n, Node *a, Node *res) void agenr(Node *n, Node *a, Node *res) { - Node n1; - - tempname(&n1, types[tptr]); - agen(n, &n1); regalloc(a, types[tptr], res); - gmove(&n1, a); + agen(n, a); } /* @@ -796,15 +832,12 @@ bgen(Node *n, int true, Prog *to) if(n == N) n = nodbool(1); + if(n->ninit != nil) + genlist(n->ninit); + nl = n->left; nr = n->right; - // TODO compile complex - if(nl != N && nl->type != T && iscomplex[nl->type->etype]) - return; - if(nr != N && nr->type != T && iscomplex[nr->type->etype]) - return; - if(n->type == T) { convlit(&n, types[TBOOL]); if(n->type == T) @@ -925,6 +958,7 @@ bgen(Node *n, int true, Prog *to) goto ret; } a = brcom(a); + true = !true; } // make simplest on right @@ -985,6 +1019,11 @@ bgen(Node *n, int true, Prog *to) break; } + if(iscomplex[nl->type->etype]) { + complexbool(a, nl, nr, true, to); + break; + } + if(is64(nr->type)) { if(!nl->addable) { tempname(&n1, nl->type); @@ -1031,7 +1070,16 @@ bgen(Node *n, int true, Prog *to) cgen(nr, &n2); gcmp(optoas(OCMP, nr->type), &n1, &n2); - patch(gbranch(a, nr->type), to); + if(isfloat[nl->type->etype]) { + p1 = gbranch(ABVS, nr->type); + patch(gbranch(a, nr->type), to); + if(n->op == ONE) + patch(p1, to); + else + patch(p1, pc); + } else { + patch(gbranch(a, nr->type), to); + } regfree(&n1); regfree(&n2); @@ -1088,7 +1136,7 @@ stkof(Node *n) t = structfirst(&flist, getoutarg(t)); if(t != T) - return t->width; + return t->width + 4; // correct for LR break; } diff --git a/src/cmd/5g/cgen64.c b/src/cmd/5g/cgen64.c index a22f4a548..716ec5ed5 100644 --- a/src/cmd/5g/cgen64.c +++ b/src/cmd/5g/cgen64.c @@ -439,12 +439,12 @@ olsh_break: p3 = gbranch(ABLO, T); // shift == 32 + p1 = gins(AMOVW, &bh, &al); + p1->scond = C_SCOND_EQ; if(bh.type->etype == TINT32) p1 = gshift(AMOVW, &bh, SHIFT_AR, 31, &ah); else - p1 = gins(AEOR, &al, &al); - p1->scond = C_SCOND_EQ; - p1 = gins(AMOVW, &bh, &al); + p1 = gins(AEOR, &ah, &ah); p1->scond = C_SCOND_EQ; p4 = gbranch(ABEQ, T); diff --git a/src/cmd/5g/galign.c b/src/cmd/5g/galign.c index 76affbf00..9c8760aea 100644 --- a/src/cmd/5g/galign.c +++ b/src/cmd/5g/galign.c @@ -25,7 +25,6 @@ Typedef typedefs[] = void betypeinit(void) { - maxround = 4; widthptr = 4; zprog.link = P; diff --git a/src/cmd/5g/gg.h b/src/cmd/5g/gg.h index c62efeb6c..603c09fc8 100644 --- a/src/cmd/5g/gg.h +++ b/src/cmd/5g/gg.h @@ -33,12 +33,13 @@ struct Addr struct Prog { - short as; // opcode + short as; // opcode uint32 loc; // pc offset in this func uint32 lineno; // source line that generated this Addr from; // src address - Addr to; // dst address + Addr to; // dst address Prog* link; // next instruction in this func + void* regp; // points to enclosing Reg struct char reg; // doubles as width in DATA op uchar scond; }; @@ -90,6 +91,7 @@ void ginscall(Node*, int); * cgen */ void agen(Node*, Node*); +Prog* cgenindex(Node *, Node *); void igen(Node*, Node*, Node*); void agenr(Node *n, Node *a, Node *res); vlong fieldoffset(Type*, Node*); diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c index 3243bb863..42a89415d 100644 --- a/src/cmd/5g/ggen.c +++ b/src/cmd/5g/ggen.c @@ -61,11 +61,14 @@ compile(Node *fn) pl = newplist(); pl->name = curfn->nname; + + setlineno(curfn); nodconst(&nod1, types[TINT32], 0); ptxt = gins(ATEXT, curfn->nname, &nod1); afunclit(&ptxt->from); + ginit(); genlist(curfn->enter); pret = nil; @@ -78,6 +81,7 @@ compile(Node *fn) } genlist(curfn->nbody); + gclean(); checklabels(); if(nerrors != 0) goto ret; @@ -87,29 +91,32 @@ compile(Node *fn) if(pret) patch(pret, pc); + ginit(); + if(hasdefer) + ginscall(deferreturn, 0); if(curfn->exit) genlist(curfn->exit); + gclean(); if(nerrors != 0) goto ret; - if(hasdefer) - ginscall(deferreturn, 0); + if(curfn->endlineno) + lineno = curfn->endlineno; pc->as = ARET; // overwrite AEND pc->lineno = lineno; - /* TODO(kaib): Add back register optimizations - if(!debug['N'] || debug['R'] || debug['P']) + if(!debug['N'] || debug['R'] || debug['P']) { regopt(ptxt); - */ + } // fill in argument size ptxt->to.type = D_CONST2; ptxt->reg = 0; // flags - ptxt->to.offset2 = rnd(curfn->type->argwid, maxround); + ptxt->to.offset2 = rnd(curfn->type->argwid, widthptr); // fill in final stack size if(stksize > maxstksize) maxstksize = stksize; - ptxt->to.offset = rnd(maxstksize+maxarg, maxround); + ptxt->to.offset = rnd(maxstksize+maxarg, widthptr); maxstksize = 0; if(debug['f']) @@ -203,6 +210,7 @@ ginscall(Node *f, int proc) void cgen_callinter(Node *n, Node *res, int proc) { + int r; Node *i, *f; Node tmpi, nodo, nodr, nodsp; @@ -216,6 +224,14 @@ cgen_callinter(Node *n, Node *res, int proc) i = i->left; // interface + // Release res register during genlist and cgen, + // which might have their own function calls. + r = -1; + if(res != N && (res->op == OREGISTER || res->op == OINDREG)) { + r = res->val.u.reg; + reg[r]--; + } + if(!i->addable) { tempname(&tmpi, i->type); cgen(i, &tmpi); @@ -223,6 +239,8 @@ cgen_callinter(Node *n, Node *res, int proc) } genlist(n->list); // args + if(r >= 0) + reg[r]++; regalloc(&nodr, types[tptr], res); regalloc(&nodo, types[tptr], &nodr); @@ -427,10 +445,10 @@ cgen_asop(Node *n) case OOR: a = optoas(n->etype, nl->type); if(nl->addable) { - regalloc(&n2, nl->type, N); regalloc(&n3, nr->type, N); - cgen(nl, &n2); cgen(nr, &n3); + regalloc(&n2, nl->type, N); + cgen(nl, &n2); gins(a, &n3, &n2); cgen(&n2, nl); regfree(&n2); @@ -439,13 +457,14 @@ cgen_asop(Node *n) } if(nr->ullman < UINF) if(sudoaddable(a, nl, &addr, &w)) { + w = optoas(OAS, nl->type); regalloc(&n2, nl->type, N); - regalloc(&n3, nr->type, N); - p1 = gins(AMOVW, N, &n2); + p1 = gins(w, N, &n2); p1->from = addr; + regalloc(&n3, nr->type, N); cgen(nr, &n3); gins(a, &n3, &n2); - p1 = gins(AMOVW, &n2, N); + p1 = gins(w, &n2, N); p1->to = addr; regfree(&n2); regfree(&n3); @@ -544,7 +563,7 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) cgen(nl, &n1); sc = mpgetfix(nr->val.u.xval); if(sc == 0) { - return; + // nothing to do } else if(sc >= nl->type->width*8) { if(op == ORSH && issigned[nl->type->etype]) gshift(AMOVW, &n1, SHIFT_AR, w, &n1); @@ -682,6 +701,29 @@ regcmp(const void *va, const void *vb) static Prog* throwpc; +// We're only going to bother inlining if we can +// convert all the arguments to 32 bits safely. Can we? +static int +fix64(NodeList *nn, int n) +{ + NodeList *l; + Node *r; + int i; + + l = nn; + for(i=0; in->right; + if(is64(r->type) && !smallintconst(r)) { + if(r->op == OCONV) + r = r->left; + if(is64(r->type)) + return 0; + } + l = l->next; + } + return 1; +} + void getargs(NodeList *nn, Node *reg, int n) { @@ -710,7 +752,7 @@ getargs(NodeList *nn, Node *reg, int n) void cmpandthrow(Node *nl, Node *nr) { - vlong cl, cr; + vlong cl; Prog *p1; int op; Node *c, n1, n2; @@ -720,17 +762,8 @@ cmpandthrow(Node *nl, Node *nr) cl = mpgetfix(nl->val.u.xval); if(cl == 0) return; - if(smallintconst(nr)) { - cr = mpgetfix(nr->val.u.xval); - if(cl > cr) { - if(throwpc == nil) { - throwpc = pc; - ginscall(panicslice, 0); - } else - patch(gbranch(AB, T), throwpc); - } + if(smallintconst(nr)) return; - } // put the constant on the right op = brrev(op); @@ -794,6 +827,8 @@ cgen_inline(Node *n, Node *res) goto no; if(!n->left->addable) goto no; + if(n->left->sym == S) + goto no; if(n->left->sym->pkg != runtimepkg) goto no; if(strcmp(n->left->sym->name, "slicearray") == 0) @@ -811,6 +846,8 @@ cgen_inline(Node *n, Node *res) slicearray: if(!sleasy(res)) goto no; + if(!fix64(n->list, 5)) + goto no; getargs(n->list, nodes, 5); // if(hb[3] > nel[1]) goto throw @@ -902,6 +939,8 @@ slicearray: return 1; sliceslice: + if(!fix64(n->list, narg)) + goto no; ntemp.op = OXXX; if(!sleasy(n->list->n->right)) { Node *n0; diff --git a/src/cmd/5g/gobj.c b/src/cmd/5g/gobj.c index c4564ed66..bf59534b9 100644 --- a/src/cmd/5g/gobj.c +++ b/src/cmd/5g/gobj.c @@ -633,10 +633,10 @@ dsymptr(Sym *s, int off, Sym *x, int xoff) void -genembedtramp(Type *rcvr, Type *method, Sym *newnam) +genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface) { // TODO(kaib): re-implement genembedtramp - genwrapper(rcvr, method, newnam); + genwrapper(rcvr, method, newnam, iface); /* Sym *e; int c, d, o; @@ -705,7 +705,7 @@ out: p->to.type = D_OREG; p->to.reg = NREG; p->to.name = D_EXTERN; - p->to.sym = methodsym(method->sym, ptrto(f->type)); + p->to.sym = methodsym(method->sym, ptrto(f->type), 0); //print("4. %P\n", p); pc->as = ARET; // overwrite AEND diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c index 700602c35..133a21b3e 100644 --- a/src/cmd/5g/gsubr.c +++ b/src/cmd/5g/gsubr.c @@ -197,9 +197,50 @@ afunclit(Addr *a) } } +static int resvd[] = +{ + 9, // reserved for m + 10, // reserved for g +}; + +void +ginit(void) +{ + int i; + + for(i=0; iop == ONAME && iscomplex[n->type->etype]) + return; if(n->op != OREGISTER && n->op != OINDREG) fatal("regfree: not a register"); i = n->val.u.reg; @@ -498,6 +546,11 @@ gmove(Node *f, Node *t) tt = simsimtype(t->type); cvt = t->type; + if(iscomplex[ft] || iscomplex[tt]) { + complexmove(f, t); + return; + } + // cannot have two memory operands; // except 64-bit, which always copies via registers anyway. if(!is64(f->type) && !is64(t->type) && ismem(f) && ismem(t)) @@ -715,56 +768,109 @@ gmove(Node *f, Node *t) * float to integer */ case CASE(TFLOAT32, TINT8): - case CASE(TFLOAT32, TINT16): - case CASE(TFLOAT32, TINT32): case CASE(TFLOAT32, TUINT8): + case CASE(TFLOAT32, TINT16): case CASE(TFLOAT32, TUINT16): + case CASE(TFLOAT32, TINT32): case CASE(TFLOAT32, TUINT32): - fa = AMOVF; - a = AMOVFW; - ta = AMOVW; - goto fltconv; +// case CASE(TFLOAT32, TUINT64): case CASE(TFLOAT64, TINT8): - case CASE(TFLOAT64, TINT16): - case CASE(TFLOAT64, TINT32): case CASE(TFLOAT64, TUINT8): + case CASE(TFLOAT64, TINT16): case CASE(TFLOAT64, TUINT16): + case CASE(TFLOAT64, TINT32): case CASE(TFLOAT64, TUINT32): - fa = AMOVD; - a = AMOVDW; +// case CASE(TFLOAT64, TUINT64): + fa = AMOVF; + a = AMOVFW; + if(ft == TFLOAT64) { + fa = AMOVD; + a = AMOVDW; + } ta = AMOVW; - goto fltconv; + switch(tt) { + case TINT8: + ta = AMOVB; + break; + case TUINT8: + ta = AMOVBU; + break; + case TINT16: + ta = AMOVH; + break; + case TUINT16: + ta = AMOVHU; + break; + } - case CASE(TFLOAT32, TUINT64): - case CASE(TFLOAT64, TUINT64): - fatal("gmove TFLOAT, UINT64 not implemented"); + regalloc(&r1, types[ft], f); + regalloc(&r2, types[tt], t); + gins(fa, f, &r1); // load to fpu + p1 = gins(a, &r1, &r1); // convert to w + switch(tt) { + case TUINT8: + case TUINT16: + case TUINT32: + p1->scond |= C_UBIT; + } + gins(AMOVW, &r1, &r2); // copy to cpu + gins(ta, &r2, t); // store + regfree(&r1); + regfree(&r2); return; /* * integer to float */ case CASE(TINT8, TFLOAT32): - case CASE(TINT16, TFLOAT32): - case CASE(TINT32, TFLOAT32): case CASE(TUINT8, TFLOAT32): + case CASE(TINT16, TFLOAT32): case CASE(TUINT16, TFLOAT32): + case CASE(TINT32, TFLOAT32): case CASE(TUINT32, TFLOAT32): - fa = AMOVW; - a = AMOVWF; - ta = AMOVF; - goto fltconv; - case CASE(TINT8, TFLOAT64): - case CASE(TINT16, TFLOAT64): - case CASE(TINT32, TFLOAT64): case CASE(TUINT8, TFLOAT64): + case CASE(TINT16, TFLOAT64): case CASE(TUINT16, TFLOAT64): + case CASE(TINT32, TFLOAT64): case CASE(TUINT32, TFLOAT64): fa = AMOVW; - a = AMOVWD; - ta = AMOVD; - goto fltconv; + switch(ft) { + case TINT8: + fa = AMOVB; + break; + case TUINT8: + fa = AMOVBU; + break; + case TINT16: + fa = AMOVH; + break; + case TUINT16: + fa = AMOVHU; + break; + } + a = AMOVWF; + ta = AMOVF; + if(tt == TFLOAT64) { + a = AMOVWD; + ta = AMOVD; + } + regalloc(&r1, types[ft], f); + regalloc(&r2, types[tt], t); + gins(fa, f, &r1); // load to cpu + gins(AMOVW, &r1, &r2); // copy to fpu + p1 = gins(a, &r2, &r2); // convert + switch(ft) { + case TUINT8: + case TUINT16: + case TUINT32: + p1->scond |= C_UBIT; + } + gins(ta, &r2, t); // store + regfree(&r1); + regfree(&r2); + return; case CASE(TUINT64, TFLOAT32): case CASE(TUINT64, TFLOAT64): @@ -831,16 +937,6 @@ trunc64: splitclean(); return; -fltconv: - regalloc(&r1, types[ft], f); - regalloc(&r2, types[tt], t); - gins(fa, f, &r1); - gins(a, &r1, &r2); - gins(ta, &r2, t); - regfree(&r1); - regfree(&r2); - return; - fatal: // should not happen fatal("gmove %N -> %N", f, t); @@ -951,7 +1047,7 @@ gshift(int as, Node *lhs, int32 stype, int32 sval, Node *rhs) { Prog *p; - if (sval <= 0 || sval > 32) + if(sval <= 0 || sval > 32) fatal("bad shift value: %d", sval); sval = sval&0x1f; @@ -983,7 +1079,7 @@ checkoffset(Addr *a, int canemitcode) if(a->offset < unmappedzero) return; if(!canemitcode) - fatal("checkoffset %#llx, cannot emit code", a->offset); + fatal("checkoffset %#x, cannot emit code", a->offset); // cannot rely on unmapped nil page at 0 to catch // reference with large offset. instead, emit explicit @@ -1014,7 +1110,7 @@ naddr(Node *n, Addr *a, int canemitcode) break; case OREGISTER: - if (n->val.u.reg <= REGALLOC_RMAX) { + if(n->val.u.reg <= REGALLOC_RMAX) { a->type = D_REG; a->reg = n->val.u.reg; } else { @@ -1314,15 +1410,21 @@ optoas(int op, Type *t) case CASE(OAS, TBOOL): case CASE(OAS, TINT8): - case CASE(OAS, TUINT8): a = AMOVB; break; + case CASE(OAS, TUINT8): + a = AMOVBU; + break; + case CASE(OAS, TINT16): - case CASE(OAS, TUINT16): a = AMOVH; break; + case CASE(OAS, TUINT16): + a = AMOVHU; + break; + case CASE(OAS, TINT32): case CASE(OAS, TUINT32): case CASE(OAS, TPTR32): @@ -1554,7 +1656,7 @@ sudoaddable(int as, Node *n, Addr *a, int *w) int64 v; Node n1, n2, n3, n4, *nn, *l, *r; Node *reg, *reg1; - Prog *p1; + Prog *p1, *p2; Type *t; if(n->type == T) @@ -1579,6 +1681,8 @@ sudoaddable(int as, Node *n, Addr *a, int *w) goto odot; case OINDEX: + if(n->left->type->etype == TSTRING) + return 0; cleani += 2; reg = &clean[cleani-1]; reg1 = &clean[cleani-2]; @@ -1692,8 +1796,8 @@ oindex: if(issigned[r->type->etype]) t = types[TINT32]; regalloc(reg1, t, N); - regalloc(&n3, r->type, reg1); - cgen(r, &n3); + regalloc(&n3, types[TINT32], reg1); + p2 = cgenindex(r, &n3); gmove(&n3, reg1); regfree(&n3); @@ -1734,6 +1838,8 @@ oindex: gcmp(optoas(OCMP, types[TUINT32]), reg1, &n3); regfree(&n3); p1 = gbranch(optoas(OLT, types[TUINT32]), T); + if(p2) + patch(p2, pc); ginscall(panicindex, 0); patch(p1, pc); } @@ -1746,14 +1852,20 @@ oindex: gmove(&n2, reg); } - if (*w == 1) + switch(*w) { + case 1: gins(AADD, reg1, reg); - else if(*w == 2) + break; + case 2: gshift(AADD, reg1, SHIFT_LL, 1, reg); - else if(*w == 4) + break; + case 4: gshift(AADD, reg1, SHIFT_LL, 2, reg); - else if(*w == 8) + break; + case 8: gshift(AADD, reg1, SHIFT_LL, 3, reg); + break; + } naddr(reg1, a, 1); a->type = D_OREG; @@ -1799,19 +1911,6 @@ oindex_const: n1.type = types[tptr]; n1.xoffset = Array_array; gmove(&n1, reg); - - } else - if(!debug['B']) { - if(v < 0) { - yyerror("out of bounds on array"); - } else - if(o & OPtrto) { - if(v >= l->type->type->bound) - yyerror("out of bounds on array"); - } else - if(v >= l->type->bound) { - yyerror("out of bounds on array"); - } } n2 = *reg; diff --git a/src/cmd/5g/list.c b/src/cmd/5g/list.c index 19027829c..ce74d6478 100644 --- a/src/cmd/5g/list.c +++ b/src/cmd/5g/list.c @@ -49,28 +49,28 @@ listinit(void) int Pconv(Fmt *fp) { - char str[STRINGSZ]; + char str[STRINGSZ], str1[STRINGSZ]; Prog *p; p = va_arg(fp->args, Prog*); sconsize = 8; switch(p->as) { default: + snprint(str1, sizeof(str1), "%A%C", p->as, p->scond); if(p->reg == NREG) - snprint(str, sizeof(str), "%.4ld (%L) %-7A%C %D,%D", - p->loc, p->lineno, p->as, p->scond, &p->from, &p->to); - else if (p->from.type != D_FREG) - snprint(str, sizeof(str), "%.4ld (%L) %-7A%C %D,R%d,%D", - p->loc, p->lineno, p->as, p->scond, &p->from, p->reg, &p->to); + snprint(str, sizeof(str), "%.4d (%L) %-7s %D,%D", + p->loc, p->lineno, str1, &p->from, &p->to); else - snprint(str, sizeof(str), "%.4ld (%L) %-7A%C %D,F%d,%D", + if (p->from.type != D_FREG) { + snprint(str, sizeof(str), "%.4d (%L) %-7s %D,R%d,%D", + p->loc, p->lineno, str1, &p->from, p->reg, &p->to); + } else + snprint(str, sizeof(str), "%.4d (%L) %-7A%C %D,F%d,%D", p->loc, p->lineno, p->as, p->scond, &p->from, p->reg, &p->to); break; case ADATA: - case AINIT: - case ADYNT: - snprint(str, sizeof(str), "%.4ld (%L) %-7A %D/%d,%D", + snprint(str, sizeof(str), "%.4d (%L) %-7A %D/%d,%D", p->loc, p->lineno, p->as, &p->from, p->reg, &p->to); break; } @@ -154,10 +154,16 @@ Dconv(Fmt *fp) break; case D_BRANCH: - if(a->sym != S) - sprint(str, "%s+%d(APC)", a->sym->name, a->offset); - else - sprint(str, "%d(APC)", a->offset); + if(a->branch == P || a->branch->loc == 0) { + if(a->sym != S) + sprint(str, "%s+%d(APC)", a->sym->name, a->offset); + else + sprint(str, "%d(APC)", a->offset); + } else + if(a->sym != S) + sprint(str, "%s+%d(APC)", a->sym->name, a->branch->loc); + else + sprint(str, "%d(APC)", a->branch->loc); break; case D_FCONST: diff --git a/src/cmd/5g/opt.h b/src/cmd/5g/opt.h index 1b0366290..9a4e17571 100644 --- a/src/cmd/5g/opt.h +++ b/src/cmd/5g/opt.h @@ -128,7 +128,7 @@ Reg* rega(void); int rcmp(const void*, const void*); void regopt(Prog*); void addmove(Reg*, int, int, int); -Bits mkvar(Reg*, Adr*); +Bits mkvar(Reg *r, Adr *a, int); void prop(Reg*, Bits, Bits); void loopit(Reg*, int32); void synch(Reg*, Bits); diff --git a/src/cmd/5g/peep.c b/src/cmd/5g/peep.c new file mode 100644 index 000000000..32333e8a9 --- /dev/null +++ b/src/cmd/5g/peep.c @@ -0,0 +1,1511 @@ +// Inferno utils/5c/peep.c +// http://code.google.com/p/inferno-os/source/browse/utils/5g/peep.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +#include "gg.h" +#include "opt.h" + +int xtramodes(Reg*, Adr*); +int shiftprop(Reg *r); +void constprop(Adr *c1, Adr *v1, Reg *r); +void predicate(void); +int copyau1(Prog *p, Adr *v); +int isdconst(Addr *a); + +void +peep(void) +{ + Reg *r, *r1, *r2; + Prog *p, *p1; + int t; +/* + * complete R structure + */ + t = 0; + for(r=firstr; r!=R; r=r1) { + r1 = r->link; + if(r1 == R) + break; + p = r->prog->link; + while(p != r1->prog) + switch(p->as) { + default: + r2 = rega(); + r->link = r2; + r2->link = r1; + + r2->prog = p; + r2->p1 = r; + r->s1 = r2; + r2->s1 = r1; + r1->p1 = r2; + + r = r2; + t++; + + case ADATA: + case AGLOBL: + case ANAME: + case ASIGNAME: + p = p->link; + } + } + +loop1: + t = 0; + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + switch(p->as) { + case ASLL: + case ASRL: + case ASRA: + /* + * elide shift into D_SHIFT operand of subsequent instruction + */ + if(shiftprop(r)) { + excise(r); + t++; + break; + } + break; + + case AMOVW: + case AMOVF: + case AMOVD: + if(!regtyp(&p->to)) + break; + if(isdconst(&p->from)) { + constprop(&p->from, &p->to, r->s1); + break; + } + if(!regtyp(&p->from)) + break; + if(p->from.type != p->to.type) + break; + if(copyprop(r)) { + excise(r); + t++; + break; + } + if(subprop(r) && copyprop(r)) { + excise(r); + t++; + break; + } + } + } + if(t) + goto loop1; + /* + * look for MOVB x,R; MOVB R,R + */ + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + switch(p->as) { + default: + continue; + case AEOR: + /* + * EOR -1,x,y => MVN x,y + */ + if(isdconst(&p->from) && p->from.offset == -1) { + p->as = AMVN; + p->from.type = D_REG; + if(p->reg != NREG) + p->from.reg = p->reg; + else + p->from.reg = p->to.reg; + p->reg = NREG; + } + continue; + case AMOVH: + case AMOVHU: + case AMOVB: + case AMOVBU: + if(p->to.type != D_REG) + continue; + break; + } + r1 = r->link; + if(r1 == R) + continue; + p1 = r1->prog; + if(p1->as != p->as) + continue; + if(p1->from.type != D_REG || p1->from.reg != p->to.reg) + continue; + if(p1->to.type != D_REG || p1->to.reg != p->to.reg) + continue; + excise(r1); + } + + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + switch(p->as) { + case AMOVW: + case AMOVB: + case AMOVBU: + if(p->from.type == D_OREG && p->from.offset == 0) + xtramodes(r, &p->from); + else if(p->to.type == D_OREG && p->to.offset == 0) + xtramodes(r, &p->to); + else + continue; + break; + case ACMP: + /* + * elide CMP $0,x if calculation of x can set condition codes + */ + if(isdconst(&p->from) || p->from.offset != 0) + continue; + r2 = r->s1; + if(r2 == R) + continue; + t = r2->prog->as; + switch(t) { + default: + continue; + case ABEQ: + case ABNE: + case ABMI: + case ABPL: + break; + case ABGE: + t = ABPL; + break; + case ABLT: + t = ABMI; + break; + case ABHI: + t = ABNE; + break; + case ABLS: + t = ABEQ; + break; + } + r1 = r; + do + r1 = uniqp(r1); + while (r1 != R && r1->prog->as == ANOP); + if(r1 == R) + continue; + p1 = r1->prog; + if(p1->to.type != D_REG) + continue; + if(p1->to.reg != p->reg) + if(!(p1->as == AMOVW && p1->from.type == D_REG && p1->from.reg == p->reg)) + continue; + switch(p1->as) { + default: + continue; + case AMOVW: + if(p1->from.type != D_REG) + continue; + case AAND: + case AEOR: + case AORR: + case ABIC: + case AMVN: + case ASUB: + case ARSB: + case AADD: + case AADC: + case ASBC: + case ARSC: + break; + } + p1->scond |= C_SBIT; + r2->prog->as = t; + excise(r); + continue; + } + } + + predicate(); +} + +Reg* +uniqp(Reg *r) +{ + Reg *r1; + + r1 = r->p1; + if(r1 == R) { + r1 = r->p2; + if(r1 == R || r1->p2link != R) + return R; + } else + if(r->p2 != R) + return R; + return r1; +} + +Reg* +uniqs(Reg *r) +{ + Reg *r1; + + r1 = r->s1; + if(r1 == R) { + r1 = r->s2; + if(r1 == R) + return R; + } else + if(r->s2 != R) + return R; + return r1; +} + +int +regtyp(Adr *a) +{ + + if(a->type == D_REG) + return 1; + if(a->type == D_FREG) + return 1; + return 0; +} + +/* + * the idea is to substitute + * one register for another + * from one MOV to another + * MOV a, R0 + * ADD b, R0 / no use of R1 + * MOV R0, R1 + * would be converted to + * MOV a, R1 + * ADD b, R1 + * MOV R1, R0 + * hopefully, then the former or latter MOV + * will be eliminated by copy propagation. + */ +int +subprop(Reg *r0) +{ + Prog *p; + Adr *v1, *v2; + Reg *r; + int t; + + p = r0->prog; + v1 = &p->from; + if(!regtyp(v1)) + return 0; + v2 = &p->to; + if(!regtyp(v2)) + return 0; + for(r=uniqp(r0); r!=R; r=uniqp(r)) { + if(uniqs(r) == R) + break; + p = r->prog; + switch(p->as) { + case ABL: + return 0; + + case ACMP: + case ACMN: + case AADD: + case ASUB: + case ARSB: + case ASLL: + case ASRL: + case ASRA: + case AORR: + case AAND: + case AEOR: + case AMUL: + case ADIV: + case ADIVU: + + case ACMPF: + case ACMPD: + case AADDD: + case AADDF: + case ASUBD: + case ASUBF: + case AMULD: + case AMULF: + case ADIVD: + case ADIVF: + if(p->to.type == v1->type) + if(p->to.reg == v1->reg) { + if(p->reg == NREG) + p->reg = p->to.reg; + goto gotit; + } + break; + + case AMOVF: + case AMOVD: + case AMOVW: + if(p->to.type == v1->type) + if(p->to.reg == v1->reg) + goto gotit; + break; + + case AMOVM: + t = 1<reg; + if((p->from.type == D_CONST && (p->from.offset&t)) || + (p->to.type == D_CONST && (p->to.offset&t))) + return 0; + break; + } + if(copyau(&p->from, v2) || + copyau1(p, v2) || + copyau(&p->to, v2)) + break; + if(copysub(&p->from, v1, v2, 0) || + copysub1(p, v1, v2, 0) || + copysub(&p->to, v1, v2, 0)) + break; + } + return 0; + +gotit: + copysub(&p->to, v1, v2, 1); + if(debug['P']) { + print("gotit: %D->%D\n%P", v1, v2, r->prog); + if(p->from.type == v2->type) + print(" excise"); + print("\n"); + } + for(r=uniqs(r); r!=r0; r=uniqs(r)) { + p = r->prog; + copysub(&p->from, v1, v2, 1); + copysub1(p, v1, v2, 1); + copysub(&p->to, v1, v2, 1); + if(debug['P']) + print("%P\n", r->prog); + } + t = v1->reg; + v1->reg = v2->reg; + v2->reg = t; + if(debug['P']) + print("%P last\n", r->prog); + return 1; +} + +/* + * The idea is to remove redundant copies. + * v1->v2 F=0 + * (use v2 s/v2/v1/)* + * set v1 F=1 + * use v2 return fail + * ----------------- + * v1->v2 F=0 + * (use v2 s/v2/v1/)* + * set v1 F=1 + * set v2 return success + */ +int +copyprop(Reg *r0) +{ + Prog *p; + Adr *v1, *v2; + Reg *r; + + p = r0->prog; + v1 = &p->from; + v2 = &p->to; + if(copyas(v1, v2)) + return 1; + for(r=firstr; r!=R; r=r->link) + r->active = 0; + return copy1(v1, v2, r0->s1, 0); +} + +int +copy1(Adr *v1, Adr *v2, Reg *r, int f) +{ + int t; + Prog *p; + + if(r->active) { + if(debug['P']) + print("act set; return 1\n"); + return 1; + } + r->active = 1; + if(debug['P']) + print("copy %D->%D f=%d\n", v1, v2, f); + for(; r != R; r = r->s1) { + p = r->prog; + if(debug['P']) + print("%P", p); + if(!f && uniqp(r) == R) { + f = 1; + if(debug['P']) + print("; merge; f=%d", f); + } + t = copyu(p, v2, A); + switch(t) { + case 2: /* rar, cant split */ + if(debug['P']) + print("; %Drar; return 0\n", v2); + return 0; + + case 3: /* set */ + if(debug['P']) + print("; %Dset; return 1\n", v2); + return 1; + + case 1: /* used, substitute */ + case 4: /* use and set */ + if(f) { + if(!debug['P']) + return 0; + if(t == 4) + print("; %Dused+set and f=%d; return 0\n", v2, f); + else + print("; %Dused and f=%d; return 0\n", v2, f); + return 0; + } + if(copyu(p, v2, v1)) { + if(debug['P']) + print("; sub fail; return 0\n"); + return 0; + } + if(debug['P']) + print("; sub%D/%D", v2, v1); + if(t == 4) { + if(debug['P']) + print("; %Dused+set; return 1\n", v2); + return 1; + } + break; + } + if(!f) { + t = copyu(p, v1, A); + if(!f && (t == 2 || t == 3 || t == 4)) { + f = 1; + if(debug['P']) + print("; %Dset and !f; f=%d", v1, f); + } + } + if(debug['P']) + print("\n"); + if(r->s2) + if(!copy1(v1, v2, r->s2, f)) + return 0; + } + return 1; +} + +/* + * The idea is to remove redundant constants. + * $c1->v1 + * ($c1->v2 s/$c1/v1)* + * set v1 return + * The v1->v2 should be eliminated by copy propagation. + */ +void +constprop(Adr *c1, Adr *v1, Reg *r) +{ + Prog *p; + + if(debug['P']) + print("constprop %D->%D\n", c1, v1); + for(; r != R; r = r->s1) { + p = r->prog; + if(debug['P']) + print("%P", p); + if(uniqp(r) == R) { + if(debug['P']) + print("; merge; return\n"); + return; + } + if(p->as == AMOVW && copyas(&p->from, c1)) { + if(debug['P']) + print("; sub%D/%D", &p->from, v1); + p->from = *v1; + } else if(copyu(p, v1, A) > 1) { + if(debug['P']) + print("; %Dset; return\n", v1); + return; + } + if(debug['P']) + print("\n"); + if(r->s2) + constprop(c1, v1, r->s2); + } +} + +/* + * ASLL x,y,w + * .. (not use w, not set x y w) + * AXXX w,a,b (a != w) + * .. (not use w) + * (set w) + * ----------- changed to + * .. + * AXXX (x<prog; + if(p->to.type != D_REG) + FAIL("BOTCH: result not reg"); + n = p->to.reg; + a = zprog.from; + if(p->reg != NREG && p->reg != p->to.reg) { + a.type = D_REG; + a.reg = p->reg; + } + if(debug['P']) + print("shiftprop\n%P", p); + r1 = r; + for(;;) { + /* find first use of shift result; abort if shift operands or result are changed */ + r1 = uniqs(r1); + if(r1 == R) + FAIL("branch"); + if(uniqp(r1) == R) + FAIL("merge"); + p1 = r1->prog; + if(debug['P']) + print("\n%P", p1); + switch(copyu(p1, &p->to, A)) { + case 0: /* not used or set */ + if((p->from.type == D_REG && copyu(p1, &p->from, A) > 1) || + (a.type == D_REG && copyu(p1, &a, A) > 1)) + FAIL("args modified"); + continue; + case 3: /* set, not used */ + FAIL("BOTCH: noref"); + } + break; + } + /* check whether substitution can be done */ + switch(p1->as) { + default: + FAIL("non-dpi"); + case AAND: + case AEOR: + case AADD: + case AADC: + case AORR: + case ASUB: + case ARSB: + case ASBC: + case ARSC: + if(p1->reg == n || (p1->reg == NREG && p1->to.type == D_REG && p1->to.reg == n)) { + if(p1->from.type != D_REG) + FAIL("can't swap"); + p1->reg = p1->from.reg; + p1->from.reg = n; + switch(p1->as) { + case ASUB: + p1->as = ARSB; + break; + case ARSB: + p1->as = ASUB; + break; + case ASBC: + p1->as = ARSC; + break; + case ARSC: + p1->as = ASBC; + break; + } + if(debug['P']) + print("\t=>%P", p1); + } + case ABIC: + case ACMP: + case ACMN: + if(p1->reg == n) + FAIL("can't swap"); + if(p1->reg == NREG && p1->to.reg == n) + FAIL("shift result used twice"); + case AMVN: + if(p1->from.type == D_SHIFT) + FAIL("shift result used in shift"); + if(p1->from.type != D_REG || p1->from.reg != n) + FAIL("BOTCH: where is it used?"); + break; + } + /* check whether shift result is used subsequently */ + p2 = p1; + if(p1->to.reg != n) + for (;;) { + r1 = uniqs(r1); + if(r1 == R) + FAIL("inconclusive"); + p1 = r1->prog; + if(debug['P']) + print("\n%P", p1); + switch(copyu(p1, &p->to, A)) { + case 0: /* not used or set */ + continue; + case 3: /* set, not used */ + break; + default:/* used */ + FAIL("reused"); + } + break; + } + + /* make the substitution */ + p2->from.type = D_SHIFT; + p2->from.reg = NREG; + o = p->reg; + if(o == NREG) + o = p->to.reg; + + switch(p->from.type){ + case D_CONST: + o |= (p->from.offset&0x1f)<<7; + break; + case D_REG: + o |= (1<<4) | (p->from.reg<<8); + break; + } + switch(p->as){ + case ASLL: + o |= 0<<5; + break; + case ASRL: + o |= 1<<5; + break; + case ASRA: + o |= 2<<5; + break; + } + p2->from.offset = o; + if(debug['P']) + print("\t=>%P\tSUCCEED\n", p2); + return 1; +} + +Reg* +findpre(Reg *r, Adr *v) +{ + Reg *r1; + + for(r1=uniqp(r); r1!=R; r=r1,r1=uniqp(r)) { + if(uniqs(r1) != r) + return R; + switch(copyu(r1->prog, v, A)) { + case 1: /* used */ + case 2: /* read-alter-rewrite */ + return R; + case 3: /* set */ + case 4: /* set and used */ + return r1; + } + } + return R; +} + +Reg* +findinc(Reg *r, Reg *r2, Adr *v) +{ + Reg *r1; + Prog *p; + + + for(r1=uniqs(r); r1!=R && r1!=r2; r=r1,r1=uniqs(r)) { + if(uniqp(r1) != r) + return R; + switch(copyu(r1->prog, v, A)) { + case 0: /* not touched */ + continue; + case 4: /* set and used */ + p = r1->prog; + if(p->as == AADD) + if(isdconst(&p->from)) + if(p->from.offset > -4096 && p->from.offset < 4096) + return r1; + default: + return R; + } + } + return R; +} + +int +nochange(Reg *r, Reg *r2, Prog *p) +{ + Adr a[3]; + int i, n; + + if(r == r2) + return 1; + n = 0; + if(p->reg != NREG && p->reg != p->to.reg) { + a[n].type = D_REG; + a[n++].reg = p->reg; + } + switch(p->from.type) { + case D_SHIFT: + a[n].type = D_REG; + a[n++].reg = p->from.offset&0xf; + case D_REG: + a[n].type = D_REG; + a[n++].reg = p->from.reg; + } + if(n == 0) + return 1; + for(; r!=R && r!=r2; r=uniqs(r)) { + p = r->prog; + for(i=0; i 1) + return 0; + } + return 1; +} + +int +findu1(Reg *r, Adr *v) +{ + for(; r != R; r = r->s1) { + if(r->active) + return 0; + r->active = 1; + switch(copyu(r->prog, v, A)) { + case 1: /* used */ + case 2: /* read-alter-rewrite */ + case 4: /* set and used */ + return 1; + case 3: /* set */ + return 0; + } + if(r->s2) + if (findu1(r->s2, v)) + return 1; + } + return 0; +} + +int +finduse(Reg *r, Adr *v) +{ + Reg *r1; + + for(r1=firstr; r1!=R; r1=r1->link) + r1->active = 0; + return findu1(r, v); +} + +int +xtramodes(Reg *r, Adr *a) +{ + Reg *r1, *r2, *r3; + Prog *p, *p1; + Adr v; + + p = r->prog; + if(debug['h'] && p->as == AMOVB && p->from.type == D_OREG) /* byte load */ + return 0; + v = *a; + v.type = D_REG; + r1 = findpre(r, &v); + if(r1 != R) { + p1 = r1->prog; + if(p1->to.type == D_REG && p1->to.reg == v.reg) + switch(p1->as) { + case AADD: + if(p1->from.type == D_REG || + (p1->from.type == D_SHIFT && (p1->from.offset&(1<<4)) == 0 && + (p->as != AMOVB || (a == &p->from && (p1->from.offset&~0xf) == 0))) || + (p1->from.type == D_CONST && + p1->from.offset > -4096 && p1->from.offset < 4096)) + if(nochange(uniqs(r1), r, p1)) { + if(a != &p->from || v.reg != p->to.reg) + if (finduse(r->s1, &v)) { + if(p1->reg == NREG || p1->reg == v.reg) + /* pre-indexing */ + p->scond |= C_WBIT; + else return 0; + } + switch (p1->from.type) { + case D_REG: + /* register offset */ + a->type = D_SHIFT; + a->offset = p1->from.reg; + break; + case D_SHIFT: + /* scaled register offset */ + a->type = D_SHIFT; + case D_CONST: + /* immediate offset */ + a->offset = p1->from.offset; + break; + } + if(p1->reg != NREG) + a->reg = p1->reg; + excise(r1); + return 1; + } + break; + case AMOVW: + if(p1->from.type == D_REG) + if((r2 = findinc(r1, r, &p1->from)) != R) { + for(r3=uniqs(r2); r3->prog->as==ANOP; r3=uniqs(r3)) + ; + if(r3 == r) { + /* post-indexing */ + p1 = r2->prog; + a->reg = p1->to.reg; + a->offset = p1->from.offset; + p->scond |= C_PBIT; + if(!finduse(r, &r1->prog->to)) + excise(r1); + excise(r2); + return 1; + } + } + break; + } + } + if(a != &p->from || a->reg != p->to.reg) + if((r1 = findinc(r, R, &v)) != R) { + /* post-indexing */ + p1 = r1->prog; + a->offset = p1->from.offset; + p->scond |= C_PBIT; + excise(r1); + return 1; + } + return 0; +} + +/* + * return + * 1 if v only used (and substitute), + * 2 if read-alter-rewrite + * 3 if set + * 4 if set and used + * 0 otherwise (not touched) + */ +int +copyu(Prog *p, Adr *v, Adr *s) +{ + + switch(p->as) { + + default: + if(debug['P']) + print(" (?)"); + return 2; + + case AMOVM: + if(v->type != D_REG) + return 0; + if(p->from.type == D_CONST) { /* read reglist, read/rar */ + if(s != A) { + if(p->from.offset&(1<reg)) + return 1; + if(copysub(&p->to, v, s, 1)) + return 1; + return 0; + } + if(copyau(&p->to, v)) { + if(p->scond&C_WBIT) + return 2; + return 1; + } + if(p->from.offset&(1<reg)) + return 1; + } else { /* read/rar, write reglist */ + if(s != A) { + if(p->to.offset&(1<reg)) + return 1; + if(copysub(&p->from, v, s, 1)) + return 1; + return 0; + } + if(copyau(&p->from, v)) { + if(p->scond&C_WBIT) + return 2; + if(p->to.offset&(1<reg)) + return 4; + return 1; + } + if(p->to.offset&(1<reg)) + return 3; + } + return 0; + + case ANOP: /* read, write */ + case AMOVW: + case AMOVF: + case AMOVD: + case AMOVH: + case AMOVHU: + case AMOVB: + case AMOVBU: + case AMOVDW: + case AMOVWD: + case AMOVFD: + case AMOVDF: + if(p->scond&(C_WBIT|C_PBIT)) + if(v->type == D_REG) { + if(p->from.type == D_OREG || p->from.type == D_SHIFT) { + if(p->from.reg == v->reg) + return 2; + } else { + if(p->to.reg == v->reg) + return 2; + } + } + if(s != A) { + if(copysub(&p->from, v, s, 1)) + return 1; + if(!copyas(&p->to, v)) + if(copysub(&p->to, v, s, 1)) + return 1; + return 0; + } + if(copyas(&p->to, v)) { + if(copyau(&p->from, v)) + return 4; + return 3; + } + if(copyau(&p->from, v)) + return 1; + if(copyau(&p->to, v)) + return 1; + return 0; + + case AADD: /* read, read, write */ + case ASUB: + case ARSB: + case ASLL: + case ASRL: + case ASRA: + case AORR: + case AAND: + case AEOR: + case AMUL: + case ADIV: + case ADIVU: + case AADDF: + case AADDD: + case ASUBF: + case ASUBD: + case AMULF: + case AMULD: + case ADIVF: + case ADIVD: + + case ACMPF: + case ACMPD: + case ACMP: + case ACMN: + case ACASE: + if(s != A) { + if(copysub(&p->from, v, s, 1)) + return 1; + if(copysub1(p, v, s, 1)) + return 1; + if(!copyas(&p->to, v)) + if(copysub(&p->to, v, s, 1)) + return 1; + return 0; + } + if(copyas(&p->to, v)) { + if(p->reg == NREG) + p->reg = p->to.reg; + if(copyau(&p->from, v)) + return 4; + if(copyau1(p, v)) + return 4; + return 3; + } + if(copyau(&p->from, v)) + return 1; + if(copyau1(p, v)) + return 1; + if(copyau(&p->to, v)) + return 1; + return 0; + + case ABEQ: /* read, read */ + case ABNE: + case ABCS: + case ABHS: + case ABCC: + case ABLO: + case ABMI: + case ABPL: + case ABVS: + case ABVC: + case ABHI: + case ABLS: + case ABGE: + case ABLT: + case ABGT: + case ABLE: + if(s != A) { + if(copysub(&p->from, v, s, 1)) + return 1; + return copysub1(p, v, s, 1); + } + if(copyau(&p->from, v)) + return 1; + if(copyau1(p, v)) + return 1; + return 0; + + case AB: /* funny */ + if(s != A) { + if(copysub(&p->to, v, s, 1)) + return 1; + return 0; + } + if(copyau(&p->to, v)) + return 1; + return 0; + + case ARET: /* funny */ + if(v->type == D_REG) + if(v->reg == REGRET) + return 2; + if(v->type == D_FREG) + if(v->reg == FREGRET) + return 2; + + case ABL: /* funny */ + if(v->type == D_REG) { + if(v->reg <= REGEXT && v->reg > exregoffset) + return 2; + if(v->reg == REGARG) + return 2; + } + if(v->type == D_FREG) + if(v->reg <= FREGEXT && v->reg > exfregoffset) + return 2; + + if(s != A) { + if(copysub(&p->to, v, s, 1)) + return 1; + return 0; + } + if(copyau(&p->to, v)) + return 4; + return 3; + + case ATEXT: /* funny */ + if(v->type == D_REG) + if(v->reg == REGARG) + return 3; + return 0; + } + return 0; +} + +int +a2type(Prog *p) +{ + + switch(p->as) { + + case ACMP: + case ACMN: + + case AADD: + case ASUB: + case ARSB: + case ASLL: + case ASRL: + case ASRA: + case AORR: + case AAND: + case AEOR: + case AMUL: + case ADIV: + case ADIVU: + return D_REG; + + case ACMPF: + case ACMPD: + + case AADDF: + case AADDD: + case ASUBF: + case ASUBD: + case AMULF: + case AMULD: + case ADIVF: + case ADIVD: + return D_FREG; + } + return D_NONE; +} + +/* + * direct reference, + * could be set/use depending on + * semantics + */ +int +copyas(Adr *a, Adr *v) +{ + + if(regtyp(v)) { + if(a->type == v->type) + if(a->reg == v->reg) + return 1; + } else + if(v->type == D_CONST) { /* for constprop */ + if(a->type == v->type) + if(a->name == v->name) + if(a->sym == v->sym) + if(a->reg == v->reg) + if(a->offset == v->offset) + return 1; + } + return 0; +} + +/* + * either direct or indirect + */ +int +copyau(Adr *a, Adr *v) +{ + + if(copyas(a, v)) + return 1; + if(v->type == D_REG) { + if(a->type == D_CONST && a->reg != NREG) { + if(v->reg == a->reg) + return 1; + } else + if(a->type == D_OREG) { + if(v->reg == a->reg) + return 1; + } else + if(a->type == D_REGREG) { + if(v->reg == a->reg) + return 1; + if(a->offset == v->reg) + return 1; + } else + if(a->type == D_SHIFT) { + if((a->offset&0xf) == v->reg) + return 1; + if((a->offset&(1<<4)) && (a->offset>>8) == v->reg) + return 1; + } + } + return 0; +} + +int +copyau1(Prog *p, Adr *v) +{ + + if(regtyp(v)) { + if(a2type(p) == v->type) + if(p->reg == v->reg) { + if(a2type(p) != v->type) + print("botch a2type %P\n", p); + return 1; + } + } + return 0; +} + +/* + * substitute s for v in a + * return failure to substitute + */ +int +copysub(Adr *a, Adr *v, Adr *s, int f) +{ + + if(f) + if(copyau(a, v)) { + if(a->type == D_SHIFT) { + if((a->offset&0xf) == v->reg) + a->offset = (a->offset&~0xf)|s->reg; + if((a->offset&(1<<4)) && (a->offset>>8) == v->reg) + a->offset = (a->offset&~(0xf<<8))|(s->reg<<8); + } else + if(a->type == D_REGREG) { + if(a->offset == v->reg) + a->offset = s->reg; + if(a->reg == v->reg) + a->reg = s->reg; + } else + a->reg = s->reg; + } + return 0; +} + +int +copysub1(Prog *p1, Adr *v, Adr *s, int f) +{ + + if(f) + if(copyau1(p1, v)) + p1->reg = s->reg; + return 0; +} + +struct { + int opcode; + int notopcode; + int scond; + int notscond; +} predinfo[] = { + { ABEQ, ABNE, 0x0, 0x1, }, + { ABNE, ABEQ, 0x1, 0x0, }, + { ABCS, ABCC, 0x2, 0x3, }, + { ABHS, ABLO, 0x2, 0x3, }, + { ABCC, ABCS, 0x3, 0x2, }, + { ABLO, ABHS, 0x3, 0x2, }, + { ABMI, ABPL, 0x4, 0x5, }, + { ABPL, ABMI, 0x5, 0x4, }, + { ABVS, ABVC, 0x6, 0x7, }, + { ABVC, ABVS, 0x7, 0x6, }, + { ABHI, ABLS, 0x8, 0x9, }, + { ABLS, ABHI, 0x9, 0x8, }, + { ABGE, ABLT, 0xA, 0xB, }, + { ABLT, ABGE, 0xB, 0xA, }, + { ABGT, ABLE, 0xC, 0xD, }, + { ABLE, ABGT, 0xD, 0xC, }, +}; + +typedef struct { + Reg *start; + Reg *last; + Reg *end; + int len; +} Joininfo; + +enum { + Join, + Split, + End, + Branch, + Setcond, + Toolong +}; + +enum { + Falsecond, + Truecond, + Delbranch, + Keepbranch +}; + +int +isbranch(Prog *p) +{ + return (ABEQ <= p->as) && (p->as <= ABLE); +} + +int +predicable(Prog *p) +{ + switch(p->as) { + case ANOP: + case AXXX: + case ADATA: + case AGLOBL: + case AGOK: + case AHISTORY: + case ANAME: + case ASIGNAME: + case ATEXT: + case AWORD: + case ABCASE: + case ACASE: + return 0; + } + if(isbranch(p)) + return 0; + return 1; +} + +/* + * Depends on an analysis of the encodings performed by 5l. + * These seem to be all of the opcodes that lead to the "S" bit + * being set in the instruction encodings. + * + * C_SBIT may also have been set explicitly in p->scond. + */ +int +modifiescpsr(Prog *p) +{ + switch(p->as) { + case ATST: + case ATEQ: + case ACMN: + case ACMP: + case AMULU: + case ADIVU: + case AMUL: + case ADIV: + case AMOD: + case AMODU: + case ABL: + return 1; + } + if(p->scond & C_SBIT) + return 1; + return 0; +} + +/* + * Find the maximal chain of instructions starting with r which could + * be executed conditionally + */ +int +joinsplit(Reg *r, Joininfo *j) +{ + j->start = r; + j->last = r; + j->len = 0; + do { + if (r->p2 && (r->p1 || r->p2->p2link)) { + j->end = r; + return Join; + } + if (r->s1 && r->s2) { + j->end = r; + return Split; + } + j->last = r; + if (r->prog->as != ANOP) + j->len++; + if (!r->s1 && !r->s2) { + j->end = r->link; + return End; + } + if (r->s2) { + j->end = r->s2; + return Branch; + } + if (modifiescpsr(r->prog)) { + j->end = r->s1; + return Setcond; + } + r = r->s1; + } while (j->len < 4); + j->end = r; + return Toolong; +} + +Reg* +successor(Reg *r) +{ + if(r->s1) + return r->s1; + else + return r->s2; +} + +void +applypred(Reg *rstart, Joininfo *j, int cond, int branch) +{ + int pred; + Reg *r; + + if(j->len == 0) + return; + if(cond == Truecond) + pred = predinfo[rstart->prog->as - ABEQ].scond; + else + pred = predinfo[rstart->prog->as - ABEQ].notscond; + + for(r = j->start;; r = successor(r)) { + if (r->prog->as == AB) { + if (r != j->last || branch == Delbranch) + excise(r); + else { + if (cond == Truecond) + r->prog->as = predinfo[rstart->prog->as - ABEQ].opcode; + else + r->prog->as = predinfo[rstart->prog->as - ABEQ].notopcode; + } + } + else + if (predicable(r->prog)) + r->prog->scond = (r->prog->scond&~C_SCOND)|pred; + if (r->s1 != r->link) { + r->s1 = r->link; + r->link->p1 = r; + } + if (r == j->last) + break; + } +} + +void +predicate(void) +{ + Reg *r; + int t1, t2; + Joininfo j1, j2; + + for(r=firstr; r!=R; r=r->link) { + if (isbranch(r->prog)) { + t1 = joinsplit(r->s1, &j1); + t2 = joinsplit(r->s2, &j2); + if(j1.last->link != j2.start) + continue; + if(j1.end == j2.end) + if((t1 == Branch && (t2 == Join || t2 == Setcond)) || + (t2 == Join && (t1 == Join || t1 == Setcond))) { + applypred(r, &j1, Falsecond, Delbranch); + applypred(r, &j2, Truecond, Delbranch); + excise(r); + continue; + } + if(t1 == End || t1 == Branch) { + applypred(r, &j1, Falsecond, Keepbranch); + excise(r); + continue; + } + } + } +} + +int +isdconst(Addr *a) +{ + if(a->type == D_CONST && a->reg == NREG) + return 1; + return 0; +} diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c new file mode 100644 index 000000000..5011e75cc --- /dev/null +++ b/src/cmd/5g/reg.c @@ -0,0 +1,1329 @@ +// Inferno utils/5c/reg.c +// http://code.google.com/p/inferno-os/source/browse/utils/5g/reg.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +#include "gg.h" +#include "opt.h" + +#define P2R(p) (Reg*)(p->reg) + + void addsplits(void); + int noreturn(Prog *p); +static int first = 0; + +Reg* +rega(void) +{ + Reg *r; + + r = freer; + if(r == R) { + r = mal(sizeof(*r)); + } else + freer = r->link; + + *r = zreg; + return r; +} + +int +rcmp(const void *a1, const void *a2) +{ + Rgn *p1, *p2; + int c1, c2; + + p1 = (Rgn*)a1; + p2 = (Rgn*)a2; + c1 = p2->cost; + c2 = p1->cost; + if(c1 -= c2) + return c1; + return p2->varno - p1->varno; +} + +static void +setoutvar(void) +{ + Type *t; + Node *n; + Addr a; + Iter save; + Bits bit; + int z; + + t = structfirst(&save, getoutarg(curfn->type)); + while(t != T) { + n = nodarg(t, 1); + a = zprog.from; + naddr(n, &a, 0); + bit = mkvar(R, &a, 0); + for(z=0; zprog; + p->as = ANOP; + p->scond = zprog.scond; + p->from = zprog.from; + p->to = zprog.to; + p->reg = zprog.reg; +} + +static void +setaddrs(Bits bit) +{ + int i, n; + Var *v; + Sym *s; + + while(bany(&bit)) { + // convert each bit to a variable + i = bnum(bit); + s = var[i].sym; + n = var[i].name; + bit.b[i/32] &= ~(1L<<(i%32)); + + // disable all pieces of that variable + for(i=0; isym == s && v->name == n) + v->addr = 2; + } + } +} + +void +regopt(Prog *firstp) +{ + Reg *r, *r1; + Prog *p; + int i, z, nr; + uint32 vreg; + Bits bit; + +return; // disabled for the moment + if(first == 0) { + fmtinstall('Q', Qconv); + } + first++; + + if(debug['K']) { + if(first != 20) + return; +// debug['R'] = 2; +// debug['P'] = 2; + print("optimizing %S\n", curfn->nname->sym); + } + + // count instructions + nr = 0; + for(p=firstp; p!=P; p=p->link) + nr++; + + // if too big dont bother + if(nr >= 10000) { +// print("********** %S is too big (%d)\n", curfn->nname->sym, nr); + return; + } + + r1 = R; + firstr = R; + lastr = R; + nvar = 0; + regbits = 0; + for(z=0; zlink) { + switch(p->as) { + case ADATA: + case AGLOBL: + case ANAME: + case ASIGNAME: + continue; + } + r = rega(); + nr++; + if(firstr == R) { + firstr = r; + lastr = r; + } else { + lastr->link = r; + r->p1 = lastr; + lastr->s1 = r; + lastr = r; + } + r->prog = p; + p->regp = r; + + r1 = r->p1; + if(r1 != R) { + switch(r1->prog->as) { + case ARET: + case AB: + case ARFE: + r->p1 = R; + r1->s1 = R; + } + } + + /* + * left side always read + */ + bit = mkvar(r, &p->from, p->as==AMOVW); + for(z=0; zuse1.b[z] |= bit.b[z]; + + /* + * right side depends on opcode + */ + bit = mkvar(r, &p->to, 0); + if(bany(&bit)) + switch(p->as) { + default: + yyerror("reg: unknown op: %A", p->as); + break; + + /* + * right side write + */ + case ANOP: + case AMOVB: + case AMOVBU: + case AMOVH: + case AMOVHU: + case AMOVW: + case AMOVF: + case AMOVD: + for(z=0; zset.b[z] |= bit.b[z]; + break; + + /* + * funny + */ + case ABL: + for(z=0; zas == AMOVM) { + z = p->to.offset; + if(p->from.type == D_CONST) + z = p->from.offset; + for(i=0; z; i++) { + if(z&1) + regbits |= RtoB(i); + z >>= 1; + } + } + } + if(firstr == R) + return; + + /* + * pass 2 + * turn branch references to pointers + * build back pointers + */ + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + if(p->to.type == D_BRANCH) { + if(p->to.branch == P) + fatal("pnil %P", p); + r1 = p->to.branch->regp; + if(r1 == R) + fatal("rnil %P", p); + if(r1 == r) { + //fatal("ref to self %P", p); + continue; + } + r->s2 = r1; + r->p2link = r1->p2; + r1->p2 = r; + } + } + if(debug['R']) { + p = firstr->prog; + print("\n%L %D\n", p->lineno, &p->from); + } + + /* + * pass 2.5 + * find looping structure + */ + for(r = firstr; r != R; r = r->link) + r->active = 0; + change = 0; + loopit(firstr, nr); + + /* + * pass 3 + * iterate propagating usage + * back until flow graph is complete + */ +loop1: + change = 0; + for(r = firstr; r != R; r = r->link) + r->active = 0; + for(r = firstr; r != R; r = r->link) + if(r->prog->as == ARET) + prop(r, zbits, zbits); +loop11: + /* pick up unreachable code */ + i = 0; + for(r = firstr; r != R; r = r1) { + r1 = r->link; + if(r1 && r1->active && !r->active) { + prop(r, zbits, zbits); + i = 1; + } + } + if(i) + goto loop11; + if(change) + goto loop1; + + + /* + * pass 4 + * iterate propagating register/variable synchrony + * forward until graph is complete + */ +loop2: + change = 0; + for(r = firstr; r != R; r = r->link) + r->active = 0; + synch(firstr, zbits); + if(change) + goto loop2; + + addsplits(); + + if(debug['R'] > 1) { + print("\nprop structure:\n"); + for(r = firstr; r != R; r = r->link) { + print("%d:%P", r->loop, r->prog); + for(z=0; zset.b[z] | + r->refahead.b[z] | r->calahead.b[z] | + r->refbehind.b[z] | r->calbehind.b[z] | + r->use1.b[z] | r->use2.b[z]; + } + + if(bany(&bit)) { + print("\t"); + if(bany(&r->use1)) + print(" u1=%Q", r->use1); + if(bany(&r->use2)) + print(" u2=%Q", r->use2); + if(bany(&r->set)) + print(" st=%Q", r->set); + if(bany(&r->refahead)) + print(" ra=%Q", r->refahead); + if(bany(&r->calahead)) + print(" ca=%Q", r->calahead); + if(bany(&r->refbehind)) + print(" rb=%Q", r->refbehind); + if(bany(&r->calbehind)) + print(" cb=%Q", r->calbehind); + } + print("\n"); + } + } + + /* + * pass 5 + * isolate regions + * calculate costs (paint1) + */ + r = firstr; + if(r) { + for(z=0; zrefahead.b[z] | r->calahead.b[z]) & + ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]); + if(bany(&bit) & !r->refset) { + // should never happen - all variables are preset + if(debug['w']) + print("%L: used and not set: %Q\n", r->prog->lineno, bit); + r->refset = 1; + } + } + + for(r = firstr; r != R; r = r->link) + r->act = zbits; + rgp = region; + nregion = 0; + for(r = firstr; r != R; r = r->link) { + for(z=0; zset.b[z] & + ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]); + if(bany(&bit) && !r->refset) { + if(debug['w']) + print("%L: set and not used: %Q\n", r->prog->lineno, bit); + r->refset = 1; + excise(r); + } + for(z=0; zact.b[z] | addrs.b[z]); + while(bany(&bit)) { + i = bnum(bit); + rgp->enter = r; + rgp->varno = i; + change = 0; + if(debug['R'] > 1) + print("\n"); + paint1(r, i); + bit.b[i/32] &= ~(1L<<(i%32)); + if(change <= 0) { + if(debug['R']) + print("%L $%d: %Q\n", + r->prog->lineno, change, blsh(i)); + continue; + } + rgp->cost = change; + nregion++; + if(nregion >= NRGN) { + if(debug['R'] > 1) + print("too many regions\n"); + goto brk; + } + rgp++; + } + } +brk: + qsort(region, nregion, sizeof(region[0]), rcmp); + + /* + * pass 6 + * determine used registers (paint2) + * replace code (paint3) + */ + rgp = region; + for(i=0; ivarno); + vreg = paint2(rgp->enter, rgp->varno); + vreg = allreg(vreg, rgp); + if(debug['R']) { + if(rgp->regno >= NREG) + print("%L $%d F%d: %Q\n", + rgp->enter->prog->lineno, + rgp->cost, + rgp->regno-NREG, + bit); + else + print("%L $%d R%d: %Q\n", + rgp->enter->prog->lineno, + rgp->cost, + rgp->regno, + bit); + } + if(rgp->regno != 0) + paint3(rgp->enter, rgp->varno, vreg, rgp->regno); + rgp++; + } + /* + * pass 7 + * peep-hole on basic block + */ + if(!debug['R'] || debug['P']) { +// peep(); + } + + /* + * last pass + * eliminate nops + * free aux structures + */ + for(p = firstp; p != P; p = p->link) { + while(p->link != P && p->link->as == ANOP) + p->link = p->link->link; + if(p->to.type == D_BRANCH) + while(p->to.branch != P && p->to.branch->as == ANOP) + p->to.branch = p->to.branch->link; + } + if(r1 != R) { + r1->link = freer; + freer = firstr; + } +} + +void +addsplits(void) +{ + Reg *r, *r1; + int z, i; + Bits bit; + + for(r = firstr; r != R; r = r->link) { + if(r->loop > 1) + continue; + if(r->prog->as == ABL) + continue; + for(r1 = r->p2; r1 != R; r1 = r1->p2link) { + if(r1->loop <= 1) + continue; + for(z=0; zcalbehind.b[z] & + (r->refahead.b[z] | r->use1.b[z] | r->use2.b[z]) & + ~(r->calahead.b[z] & addrs.b[z]); + while(bany(&bit)) { + i = bnum(bit); + bit.b[i/32] &= ~(1L << (i%32)); + } + } + } +} + +/* + * add mov b,rn + * just after r + */ +void +addmove(Reg *r, int bn, int rn, int f) +{ + Prog *p, *p1; + Adr *a; + Var *v; + + p1 = mal(sizeof(*p1)); + *p1 = zprog; + p = r->prog; + + p1->link = p->link; + p->link = p1; + p1->lineno = p->lineno; + + v = var + bn; + + a = &p1->to; + a->sym = v->sym; + a->name = v->name; + a->offset = v->offset; + a->etype = v->etype; + a->type = D_OREG; + if(a->etype == TARRAY || a->sym == S) + a->type = D_CONST; + + switch(v->etype) { + default: + print("What is this %E\n", v->etype); + + case TINT32: + case TUINT32: + case TPTR32: + case TBOOL: + p1->as = AMOVW; + break; + case TINT8: + case TUINT8: + p1->as = AMOVB; + break; + case TINT16: + case TUINT16: + p1->as = AMOVH; + break; + case TFLOAT32: + p1->as = AMOVF; + break; + case TFLOAT64: + p1->as = AMOVD; + break; + } + + p1->from.type = D_REG; + p1->from.reg = rn; + if(rn >= NREG) { + p1->from.type = D_FREG; + p1->from.reg = rn-NREG; + } + if(!f) { + p1->from = *a; + *a = zprog.from; + a->type = D_REG; + a->reg = rn; + if(rn >= NREG) { + a->type = D_FREG; + a->reg = rn-NREG; + } + if(v->etype == TUINT8) + p1->as = AMOVBU; + if(v->etype == TUINT16) + p1->as = AMOVHU; + } + if(debug['R']) + print("%P\t.a%P\n", p, p1); +} + +static int +overlap(int32 o1, int w1, int32 o2, int w2) +{ + int32 t1, t2; + + t1 = o1+w1; + t2 = o2+w2; + + if(!(t1 > o2 && t2 > o1)) + return 0; + + return 1; +} + +Bits +mkvar(Reg *r, Adr *a, int docon) +{ + Var *v; + int i, t, n, et, z, w, flag; + int32 o; + Bits bit; + Sym *s; + + // mark registers used + t = a->type; + n = D_NONE; + + switch(t) { + default: + print("type %d %d %D\n", t, a->name, a); + goto none; + + case D_CONST: + if(a->reg != NREG) + r->regu |= RtoB(a->reg); + // fallthrough + + case D_NONE: + case D_FCONST: + case D_BRANCH: + goto none; + + case D_REGREG: + if(a->offset != NREG) + r->regu |= RtoB(a->offset); + // fallthrough + + case D_REG: + case D_SHIFT: + case D_OREG: + if(a->reg != NREG) + r->regu |= RtoB(a->reg); + break; + + case D_FREG: + if(a->reg != NREG) + r->regu |= FtoB(a->reg); + break; + } + + switch(a->name) { + default: + goto none; + + case D_EXTERN: + case D_STATIC: + case D_AUTO: + case D_PARAM: + n = a->name; + break; + } + + flag = 0; +// if(a->pun) +// flag = 1; + + s = a->sym; + if(s == S) + goto none; + if(s->name[0] == '.') + goto none; + et = a->etype; + o = a->offset; + w = a->width; + + for(i=0; isym == s && v->name == n) { + if(v->offset == o) + if(v->etype == et) + if(v->width == w) + if(!flag) + return blsh(i); + + // if they overlaps, disable both + if(overlap(v->offset, v->width, o, w)) { + v->addr = 1; + flag = 1; + } + } + } + + switch(et) { + case 0: + case TFUNC: + case TARRAY: + case TSTRING: + goto none; + } + + if(nvar >= NVAR) { + if(debug['w'] > 1 && s) + fatal("variable not optimized: %D", a); + 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 + + if(debug['R']) + print("bit=%2d et=%E pun=%d %D\n", i, et, flag, a); + +out: + bit = blsh(i); + if(n == D_EXTERN || n == D_STATIC) + for(z=0; zp1) { + for(z=0; zrefahead.b[z]; + if(ref.b[z] != r1->refahead.b[z]) { + r1->refahead.b[z] = ref.b[z]; + change++; + } + cal.b[z] |= r1->calahead.b[z]; + if(cal.b[z] != r1->calahead.b[z]) { + r1->calahead.b[z] = cal.b[z]; + change++; + } + } + switch(r1->prog->as) { + case ABL: + if(noreturn(r1->prog)) + break; + for(z=0; zset.b[z]) | + r1->use1.b[z] | r1->use2.b[z]; + cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]); + r1->refbehind.b[z] = ref.b[z]; + r1->calbehind.b[z] = cal.b[z]; + } + if(r1->active) + break; + r1->active = 1; + } + for(; r != r1; r = r->p1) + for(r2 = r->p2; r2 != R; r2 = r2->p2link) + prop(r2, r->refbehind, r->calbehind); +} + +/* + * find looping structure + * + * 1) find reverse postordering + * 2) find approximate dominators, + * the actual dominators if the flow graph is reducible + * otherwise, dominators plus some other non-dominators. + * See Matthew S. Hecht and Jeffrey D. Ullman, + * "Analysis of a Simple Algorithm for Global Data Flow Problems", + * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts, + * Oct. 1-3, 1973, pp. 207-217. + * 3) find all nodes with a predecessor dominated by the current node. + * such a node is a loop head. + * recursively, all preds with a greater rpo number are in the loop + */ +int32 +postorder(Reg *r, Reg **rpo2r, int32 n) +{ + Reg *r1; + + r->rpo = 1; + r1 = r->s1; + if(r1 && !r1->rpo) + n = postorder(r1, rpo2r, n); + r1 = r->s2; + if(r1 && !r1->rpo) + n = postorder(r1, rpo2r, n); + rpo2r[n] = r; + n++; + return n; +} + +int32 +rpolca(int32 *idom, int32 rpo1, int32 rpo2) +{ + int32 t; + + if(rpo1 == -1) + return rpo2; + while(rpo1 != rpo2){ + if(rpo1 > rpo2){ + t = rpo2; + rpo2 = rpo1; + rpo1 = t; + } + while(rpo1 < rpo2){ + t = idom[rpo2]; + if(t >= rpo2) + fatal("bad idom"); + rpo2 = t; + } + } + return rpo1; +} + +int +doms(int32 *idom, int32 r, int32 s) +{ + while(s > r) + s = idom[s]; + return s == r; +} + +int +loophead(int32 *idom, Reg *r) +{ + int32 src; + + src = r->rpo; + if(r->p1 != R && doms(idom, src, r->p1->rpo)) + return 1; + for(r = r->p2; r != R; r = r->p2link) + if(doms(idom, src, r->rpo)) + return 1; + return 0; +} + +void +loopmark(Reg **rpo2r, int32 head, Reg *r) +{ + if(r->rpo < head || r->active == head) + return; + r->active = head; + r->loop += LOOP; + if(r->p1 != R) + loopmark(rpo2r, head, r->p1); + for(r = r->p2; r != R; r = r->p2link) + loopmark(rpo2r, head, r); +} + +void +loopit(Reg *r, int32 nr) +{ + Reg *r1; + int32 i, d, me; + + if(nr > maxnr) { + rpo2r = mal(nr * sizeof(Reg*)); + idom = mal(nr * sizeof(int32)); + maxnr = nr; + } + d = postorder(r, rpo2r, 0); + if(d > nr) + fatal("too many reg nodes"); + nr = d; + for(i = 0; i < nr / 2; i++){ + r1 = rpo2r[i]; + rpo2r[i] = rpo2r[nr - 1 - i]; + rpo2r[nr - 1 - i] = r1; + } + for(i = 0; i < nr; i++) + rpo2r[i]->rpo = i; + + idom[0] = 0; + for(i = 0; i < nr; i++){ + r1 = rpo2r[i]; + me = r1->rpo; + d = -1; + if(r1->p1 != R && r1->p1->rpo < me) + d = r1->p1->rpo; + for(r1 = r1->p2; r1 != nil; r1 = r1->p2link) + if(r1->rpo < me) + d = rpolca(idom, d, r1->rpo); + idom[i] = d; + } + + for(i = 0; i < nr; i++){ + r1 = rpo2r[i]; + r1->loop++; + if(r1->p2 != R && loophead(idom, r1)) + loopmark(rpo2r, i, r1); + } +} + +void +synch(Reg *r, Bits dif) +{ + Reg *r1; + int z; + + for(r1 = r; r1 != R; r1 = r1->s1) { + for(z=0; zrefbehind.b[z] & r1->refahead.b[z])) | + r1->set.b[z] | r1->regdiff.b[z]; + if(dif.b[z] != r1->regdiff.b[z]) { + r1->regdiff.b[z] = dif.b[z]; + change++; + } + } + if(r1->active) + break; + r1->active = 1; + for(z=0; zcalbehind.b[z] & r1->calahead.b[z]); + if(r1->s2 != R) + synch(r1->s2, dif); + } +} + +uint32 +allreg(uint32 b, Rgn *r) +{ + Var *v; + int i; + + v = var + r->varno; + r->regno = 0; + switch(v->etype) { + + default: + fatal("unknown etype %d/%E", bitno(b), v->etype); + break; + + case TINT8: + case TUINT8: + case TINT16: + case TUINT16: + case TINT32: + case TUINT32: + case TINT: + case TUINT: + case TUINTPTR: + case TBOOL: + case TPTR32: + i = BtoR(~b); + if(i && r->cost >= 0) { + r->regno = i; + return RtoB(i); + } + break; + + case TFLOAT32: + case TFLOAT64: + case TFLOAT: + i = BtoF(~b); + if(i && r->cost >= 0) { + r->regno = i+NREG; + return FtoB(i); + } + break; + + case TINT64: + case TUINT64: + case TPTR64: + case TINTER: + case TSTRUCT: + case TARRAY: + break; + } + return 0; +} + +void +paint1(Reg *r, int bn) +{ + Reg *r1; + Prog *p; + int z; + uint32 bb; + + z = bn/32; + bb = 1L<<(bn%32); + if(r->act.b[z] & bb) + return; + for(;;) { + if(!(r->refbehind.b[z] & bb)) + break; + r1 = r->p1; + if(r1 == R) + break; + if(!(r1->refahead.b[z] & bb)) + break; + if(r1->act.b[z] & bb) + break; + r = r1; + } + + if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) { + change -= CLOAD * r->loop; + if(debug['R'] > 1) + print("%d%P\td %Q $%d\n", r->loop, + r->prog, blsh(bn), change); + } + for(;;) { + r->act.b[z] |= bb; + p = r->prog; + + if(r->use1.b[z] & bb) { + change += CREF * r->loop; + if(debug['R'] > 1) + print("%d%P\tu1 %Q $%d\n", r->loop, + p, blsh(bn), change); + } + + if((r->use2.b[z]|r->set.b[z]) & bb) { + change += CREF * r->loop; + if(debug['R'] > 1) + print("%d%P\tu2 %Q $%d\n", r->loop, + p, blsh(bn), change); + } + + if(STORE(r) & r->regdiff.b[z] & bb) { + change -= CLOAD * r->loop; + if(debug['R'] > 1) + print("%d%P\tst %Q $%d\n", r->loop, + p, blsh(bn), change); + } + + if(r->refbehind.b[z] & bb) + for(r1 = r->p2; r1 != R; r1 = r1->p2link) + if(r1->refahead.b[z] & bb) + paint1(r1, bn); + + if(!(r->refahead.b[z] & bb)) + break; + r1 = r->s2; + if(r1 != R) + if(r1->refbehind.b[z] & bb) + paint1(r1, bn); + r = r->s1; + if(r == R) + break; + if(r->act.b[z] & bb) + break; + if(!(r->refbehind.b[z] & bb)) + break; + } +} + +uint32 +paint2(Reg *r, int bn) +{ + Reg *r1; + int z; + uint32 bb, vreg; + + z = bn/32; + bb = 1L << (bn%32); + vreg = regbits; + if(!(r->act.b[z] & bb)) + return vreg; + for(;;) { + if(!(r->refbehind.b[z] & bb)) + break; + r1 = r->p1; + if(r1 == R) + break; + if(!(r1->refahead.b[z] & bb)) + break; + if(!(r1->act.b[z] & bb)) + break; + r = r1; + } + for(;;) { + r->act.b[z] &= ~bb; + + vreg |= r->regu; + + if(r->refbehind.b[z] & bb) + for(r1 = r->p2; r1 != R; r1 = r1->p2link) + if(r1->refahead.b[z] & bb) + vreg |= paint2(r1, bn); + + if(!(r->refahead.b[z] & bb)) + break; + r1 = r->s2; + if(r1 != R) + if(r1->refbehind.b[z] & bb) + vreg |= paint2(r1, bn); + r = r->s1; + if(r == R) + break; + if(!(r->act.b[z] & bb)) + break; + if(!(r->refbehind.b[z] & bb)) + break; + } + return vreg; +} + +void +paint3(Reg *r, int bn, int32 rb, int rn) +{ + Reg *r1; + Prog *p; + int z; + uint32 bb; + + z = bn/32; + bb = 1L << (bn%32); + if(r->act.b[z] & bb) + return; + for(;;) { + if(!(r->refbehind.b[z] & bb)) + break; + r1 = r->p1; + if(r1 == R) + break; + if(!(r1->refahead.b[z] & bb)) + break; + if(r1->act.b[z] & bb) + break; + r = r1; + } + + if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) + addmove(r, bn, rn, 0); + for(;;) { + r->act.b[z] |= bb; + p = r->prog; + + if(r->use1.b[z] & bb) { + if(debug['R']) + print("%P", p); + addreg(&p->from, rn); + if(debug['R']) + print("\t.c%P\n", p); + } + if((r->use2.b[z]|r->set.b[z]) & bb) { + if(debug['R']) + print("%P", p); + addreg(&p->to, rn); + if(debug['R']) + print("\t.c%P\n", p); + } + + if(STORE(r) & r->regdiff.b[z] & bb) + addmove(r, bn, rn, 1); + r->regu |= rb; + + if(r->refbehind.b[z] & bb) + for(r1 = r->p2; r1 != R; r1 = r1->p2link) + if(r1->refahead.b[z] & bb) + paint3(r1, bn, rb, rn); + + if(!(r->refahead.b[z] & bb)) + break; + r1 = r->s2; + if(r1 != R) + if(r1->refbehind.b[z] & bb) + paint3(r1, bn, rb, rn); + r = r->s1; + if(r == R) + break; + if(r->act.b[z] & bb) + break; + if(!(r->refbehind.b[z] & bb)) + break; + } +} + +void +addreg(Adr *a, int rn) +{ + + a->sym = 0; + a->name = D_NONE; + a->type = D_REG; + a->reg = rn; + if(rn >= NREG) { + a->type = D_FREG; + a->reg = rn-NREG; + } +} + +/* + * bit reg + * 0 R0 + * 1 R1 + * ... ... + * 10 R10 + */ +int32 +RtoB(int r) +{ + + if(r < 2 || r >= REGTMP-2) // excluded R9 and R10 for m and g + return 0; + return 1L << r; +} + +int +BtoR(int32 b) +{ + b &= 0x01fcL; // excluded R9 and R10 for m and g + if(b == 0) + return 0; + return bitno(b); +} + +/* + * bit reg + * 18 F2 + * 19 F3 + * ... ... + * 23 F7 + */ +int32 +FtoB(int f) +{ + + if(f < 2 || f > NFREG-1) + return 0; + return 1L << (f + 16); +} + +int +BtoF(int32 b) +{ + + b &= 0xfc0000L; + if(b == 0) + return 0; + return bitno(b) - 16; +} + +static Sym* symlist[10]; + +int +noreturn(Prog *p) +{ + Sym *s; + int i; + + if(symlist[0] == S) { + symlist[0] = pkglookup("panicindex", runtimepkg); + symlist[1] = pkglookup("panicslice", runtimepkg); + symlist[2] = pkglookup("throwinit", runtimepkg); + symlist[3] = pkglookup("panic", runtimepkg); + } + + s = p->to.sym; + if(s == S) + return 0; + for(i=0; symlist[i]!=S; i++) + if(s == symlist[i]) + return 1; + return 0; +} diff --git a/src/cmd/5l/5.out.h b/src/cmd/5l/5.out.h index c06441c1c..a25c0f71d 100644 --- a/src/cmd/5l/5.out.h +++ b/src/cmd/5l/5.out.h @@ -58,6 +58,7 @@ #define NFREG 8 #define FREGRET 0 #define FREGEXT 7 +#define FREGTMP 15 /* compiler allocates register variables F0 up */ /* compiler allocates external registers F7 down */ @@ -158,8 +159,8 @@ enum as ARET, ATEXT, AWORD, - ADYNT, - AINIT, + ADYNT_, + AINIT_, ABCASE, ACASE, @@ -188,7 +189,7 @@ enum as #define C_PBIT (1<<5) #define C_WBIT (1<<6) #define C_FBIT (1<<7) /* psr flags-only */ -#define C_UBIT (1<<7) /* up bit */ +#define C_UBIT (1<<7) /* up bit, unsigned bit */ #define C_SCOND_EQ 0 #define C_SCOND_NE 1 @@ -246,6 +247,7 @@ enum as /* internal only */ #define D_SIZE (D_NONE+40) +#define D_PCREL (D_NONE+41) /* * this is the ranlib header diff --git a/src/cmd/5l/Makefile b/src/cmd/5l/Makefile index b9780f098..71798724b 100644 --- a/src/cmd/5l/Makefile +++ b/src/cmd/5l/Makefile @@ -2,24 +2,29 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 5l\ +TARG=5l OFILES=\ asm.$O\ + data.$O\ elf.$O\ enam.$O\ + ldelf.$O\ + ldmacho.$O\ lib.$O\ list.$O\ noop.$O\ obj.$O\ optab.$O\ pass.$O\ + prof.$O\ thumb.$O\ softfloat.$O\ span.$O\ + symtab.$O\ go.$O\ HFILES=\ @@ -27,19 +32,12 @@ HFILES=\ ../5l/5.out.h\ ../ld/elf.h\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lbio -l9 - -$(OFILES): $(HFILES) +include ../../Make.ccmd enam.c: 5.out.h sh mkenam -clean: - rm -f *.o $(TARG) *.5 enam.c 5.out a.out - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +CLEANFILES+=enam.c %.$O: ../ld/%.c - $(CC) $(CFLAGS) -c -I. ../ld/$*.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. ../ld/$*.c diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c index 45e6e734f..7ceea59b6 100644 --- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Writing object files. + #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" @@ -50,127 +52,11 @@ entryvalue(void) s = lookup(a, 0); if(s->type == 0) return INITTEXT; - switch(s->type) { - case STEXT: - case SLEAF: - break; - case SDATA: - if(dlm) - return s->value+INITDAT; - default: + if(s->type != STEXT) diag("entry not text: %s", s->name); - } return s->value; } -vlong -addstring(Sym *s, char *str) -{ - int n, m; - vlong r; - Prog *p; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - n = strlen(str)+1; - while(n > 0) { - m = n; - if(m > NSNAME) - m = NSNAME; - p = newdata(s, s->value, m, D_EXTERN); - p->to.type = D_SCONST; - p->to.sval = mal(NSNAME); - memmove(p->to.sval, str, m); - s->value += m; - str += m; - n -= m; - } - return r; -} - -vlong -adduintxx(Sym *s, uint64 v, int wid) -{ - vlong r; - Prog *p; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, wid, D_EXTERN); - s->value += wid; - p->to.type = D_CONST; - p->to.offset = v; - return r; -} - -vlong -adduint8(Sym *s, uint8 v) -{ - return adduintxx(s, v, 1); -} - -vlong -adduint16(Sym *s, uint16 v) -{ - return adduintxx(s, v, 2); -} - -vlong -adduint32(Sym *s, uint32 v) -{ - return adduintxx(s, v, 4); -} - -vlong -adduint64(Sym *s, uint64 v) -{ - return adduintxx(s, v, 8); -} - -vlong -addaddr(Sym *s, Sym *t) -{ - vlong r; - Prog *p; - enum { Ptrsize = 4 }; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, Ptrsize, D_EXTERN); - s->value += Ptrsize; - p->to.type = D_ADDR; - p->to.index = D_EXTERN; - p->to.offset = 0; - p->to.sym = t; - return r; -} - -vlong -addsize(Sym *s, Sym *t) -{ - vlong r; - Prog *p; - enum { Ptrsize = 4 }; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, Ptrsize, D_EXTERN); - s->value += Ptrsize; - p->to.type = D_SIZE; - p->to.index = D_EXTERN; - p->to.offset = 0; - p->to.sym = t; - return r; -} - enum { ElfStrEmpty, ElfStrInterp, @@ -188,6 +74,8 @@ enum { ElfStrGosymtab, ElfStrGopclntab, ElfStrShstrtab, + ElfStrRelPlt, + ElfStrPlt, NElfStr }; @@ -209,21 +97,67 @@ needlib(char *name) return 0; } +int nelfsym = 1; + +void +adddynrel(Sym *s, Reloc *r) +{ + diag("adddynrel: unsupported binary format"); +} + +void +adddynsym(Sym *s) +{ + diag("adddynsym: not implemented"); +} + +static void +elfsetupplt(void) +{ + // TODO +} + +int +archreloc(Reloc *r, Sym *s, vlong *val) +{ + return -1; +} + +void +adddynlib(char *lib) +{ + Sym *s; + + if(!needlib(lib)) + return; + + if(iself) { + s = lookup(".dynstr", 0); + if(s->size == 0) + addstring(s, ""); + elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(s, lib)); + } else { + diag("adddynlib: unsupported binary format"); + } +} + void doelf(void) { - Sym *s, *shstrtab, *dynamic, *dynstr, *d; - int h, nsym, t; + Sym *s, *shstrtab, *dynstr; if(!iself) return; /* predefine strings we need for section headers */ shstrtab = lookup(".shstrtab", 0); + shstrtab->type = SELFDATA; shstrtab->reachable = 1; + elfstr[ElfStrEmpty] = addstring(shstrtab, ""); elfstr[ElfStrText] = addstring(shstrtab, ".text"); elfstr[ElfStrData] = addstring(shstrtab, ".data"); + addstring(shstrtab, ".rodata"); elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); if(!debug['s']) { elfstr[ElfStrGosymcounts] = addstring(shstrtab, ".gosymcounts"); @@ -241,6 +175,8 @@ doelf(void) elfstr[ElfStrDynsym] = addstring(shstrtab, ".dynsym"); elfstr[ElfStrDynstr] = addstring(shstrtab, ".dynstr"); elfstr[ElfStrRel] = addstring(shstrtab, ".rel"); + elfstr[ElfStrRelPlt] = addstring(shstrtab, ".rel.plt"); + elfstr[ElfStrPlt] = addstring(shstrtab, ".plt"); /* interpreter string */ s = lookup(".interp", 0); @@ -257,7 +193,8 @@ doelf(void) s = lookup(".dynstr", 0); s->type = SELFDATA; s->reachable = 1; - addstring(s, ""); + if(s->size == 0) + addstring(s, ""); dynstr = s; /* relocation table */ @@ -269,92 +206,35 @@ doelf(void) s = lookup(".got", 0); s->reachable = 1; s->type = SELFDATA; + + /* hash */ + s = lookup(".hash", 0); + s->reachable = 1; + s->type = SELFDATA; - /* got.plt - ??? */ + /* got.plt */ s = lookup(".got.plt", 0); s->reachable = 1; - s->type = SELFDATA; + s->type = SDATA; // writable, so not SELFDATA - /* hash */ - s = lookup(".hash", 0); + s = lookup(".plt", 0); + s->reachable = 1; + s->type = SELFDATA; + + s = lookup(".rel.plt", 0); s->reachable = 1; s->type = SELFDATA; + + elfsetupplt(); /* define dynamic elf table */ s = lookup(".dynamic", 0); s->reachable = 1; s->type = SELFDATA; - dynamic = s; - - /* - * relocation entries for dynimp symbols - */ - nsym = 1; // sym 0 is reserved - for(h=0; hlink) { - if(!s->reachable || (s->type != SDATA && s->type != SBSS) || s->dynimpname == nil) - continue; - - if(!s->dynexport) { - d = lookup(".rel", 0); - addaddr(d, s); - adduint32(d, ELF32_R_INFO(nsym, R_ARM_ABS32)); - } - - nsym++; - - d = lookup(".dynsym", 0); - adduint32(d, addstring(lookup(".dynstr", 0), s->dynimpname)); - /* value */ - if(!s->dynexport) - adduint32(d, 0); - else - addaddr(d, s); - - /* size of object */ - adduint32(d, 0); - - /* type */ - t = STB_GLOBAL << 4; - if(s->dynexport && s->type == STEXT) - t |= STT_FUNC; - else - t |= STT_OBJECT; - adduint8(d, t); - - /* reserved */ - adduint8(d, 0); - - /* section where symbol is defined */ - if(!s->dynexport) - adduint16(d, SHN_UNDEF); - else { - switch(s->type) { - default: - case STEXT: - t = 9; - break; - case SDATA: - t = 10; - break; - case SBSS: - t = 11; - break; - } - adduint16(d, t); - } - - if(!s->dynexport && needlib(s->dynimplib)) - elfwritedynent(dynamic, DT_NEEDED, addstring(dynstr, s->dynimplib)); - } - } - - elfdynhash(nsym); /* * .dynamic table */ - s = dynamic; elfwritedynentsym(s, DT_HASH, lookup(".hash", 0)); elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0)); elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE); @@ -365,6 +245,10 @@ doelf(void) elfwritedynent(s, DT_RELENT, ELF32RELSIZE); if(rpath) elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); + elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0)); + 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_NULL, 0); } } @@ -372,16 +256,18 @@ doelf(void) vlong datoff(vlong addr) { - if(addr >= INITDAT) - return addr - INITDAT + rnd(HEADR+textsize, INITRND); - diag("datoff %#llx", addr); + if(addr >= segdata.vaddr) + return addr - segdata.vaddr + segdata.fileoff; + if(addr >= segtext.vaddr) + return addr - segtext.vaddr + segtext.fileoff; + diag("datoff %#x", addr); return 0; } void shsym(Elf64_Shdr *sh, Sym *s) { - sh->addr = s->value + INITDAT; + sh->addr = symaddr(s); sh->off = datoff(sh->addr); sh->size = s->size; } @@ -400,103 +286,47 @@ phsh(Elf64_Phdr *ph, Elf64_Shdr *sh) void asmb(void) { - Prog *p; - int32 t, etext; + int32 t; int a, dynsym; - uint32 va, fo, w, symo, startva; - uint32 symdatva = SYMDATVA; + uint32 va, fo, w, startva; int strtabsize; - Optab *o; ElfEhdr *eh; ElfPhdr *ph, *pph; ElfShdr *sh; + Section *sect; strtabsize = 0; - symo = 0; if(debug['v']) - Bprint(&bso, "%5.2f asm\n", cputime()); + Bprint(&bso, "%5.2f asmb\n", cputime()); Bflush(&bso); - OFFSET = HEADR; - seek(cout, OFFSET, 0); - pc = INITTEXT; - for(p = firstp; p != P; p = p->link) { - setarch(p); - if(p->as == ATEXT) { - curtext = p; - autosize = p->to.offset + 4; - } - if(p->pc != pc) { - diag("phase error %lux sb %lux", - p->pc, pc); - if(!debug['a']) - prasm(curp); - pc = p->pc; - } - curp = p; - o = oplook(p); /* could probably avoid this call */ - if(thumb) - thumbasmout(p, o); - else - asmout(p, o); - pc += o->size; - } - while(pc-INITTEXT < textsize) { - cput(0); - pc++; - } - if(debug['a']) - Bprint(&bso, "\n"); - Bflush(&bso); - cflush(); + sect = segtext.sect; + seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); + codeblk(sect->vaddr, sect->len); - /* output strings in text segment */ - etext = INITTEXT + textsize; - for(t = pc; t < etext; t += sizeof(buf)-100) { - if(etext-t > sizeof(buf)-100) - datblk(t, sizeof(buf)-100, 1); - else - datblk(t, etext-t, 1); - } + /* output read-only data in text segment */ + sect = segtext.sect->next; + seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); + datblk(sect->vaddr, sect->len); - /* output section header strings */ - curtext = P; - switch(HEADTYPE) { - case 0: - case 1: - case 2: - case 5: - OFFSET = HEADR+textsize; - seek(cout, OFFSET, 0); - break; - case 3: - OFFSET = rnd(HEADR+textsize, 4096); - seek(cout, OFFSET, 0); - break; - case 6: - OFFSET = rnd(HEADR+textsize, INITRND); - seek(cout, OFFSET, 0); - break; - } - if(dlm){ - char buf[8]; + if(debug['v']) + Bprint(&bso, "%5.2f datblk\n", cputime()); + Bflush(&bso); - write(cout, buf, INITDAT-textsize); - textsize = INITDAT; - } - for(t = 0; t < datsize; t += sizeof(buf)-100) { - if(datsize-t > sizeof(buf)-100) - datblk(t, sizeof(buf)-100, 0); - else - datblk(t, datsize-t, 0); - } - cflush(); + seek(cout, segdata.fileoff, 0); + datblk(segdata.vaddr, segdata.filelen); + + /* output read-only data in text segment */ + sect = segtext.sect->next; + seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); + datblk(sect->vaddr, sect->len); /* output symbol table */ symsize = 0; lcsize = 0; if(!debug['s']) { + // TODO: rationalize if(debug['v']) Bprint(&bso, "%5.2f sym\n", cputime()); Bflush(&bso); @@ -508,45 +338,24 @@ asmb(void) debug['s'] = 1; break; case 2: - OFFSET = HEADR+textsize+datsize; + OFFSET = HEADR+textsize+segdata.filelen; seek(cout, OFFSET, 0); break; case 3: - OFFSET += rnd(datsize, 4096); + OFFSET += rnd(segdata.filelen, 4096); seek(cout, OFFSET, 0); break; case 6: - symo = rnd(HEADR+textsize, INITRND)+datsize+strtabsize; - symo = rnd(symo, INITRND); - seek(cout, symo + 8, 0); + OFFSET += segdata.filelen; + seek(cout, rnd(OFFSET, INITRND), 0); break; } - if(!debug['s']) - asmsym(); - if(debug['v']) - Bprint(&bso, "%5.2f pc\n", cputime()); - Bflush(&bso); - if(!debug['s']) - asmlc(); if(!debug['s']) asmthumbmap(); - if(dlm) - asmdyn(); - if(!debug['s']) - strnput("", INITRND-(8+symsize+lcsize)%INITRND); - cflush(); - seek(cout, symo, 0); - lputl(symsize); - lputl(lcsize); - cflush(); - } - else if(dlm){ - seek(cout, HEADR+textsize+datsize, 0); - asmdyn(); cflush(); } - curtext = P; + cursym = nil; if(debug['v']) Bprint(&bso, "%5.2f header\n", cputime()); Bflush(&bso); @@ -568,10 +377,10 @@ asmb(void) lputl(0xef000011); /* SWI - exit code */ lputl(textsize+HEADR); /* text size */ - lputl(datsize); /* data size */ + lputl(segdata.filelen); /* data size */ lputl(0); /* sym size */ - lputl(bsssize); /* bss size */ + lputl(segdata.len - segdata.filelen); /* bss size */ lputl(0); /* sym type */ lputl(INITTEXT-HEADR); /* text addr */ lputl(0); /* workspace - ignored */ @@ -586,13 +395,10 @@ asmb(void) lputl(0xe1a0f00e); /* B (R14) - zero init return */ break; case 2: /* plan 9 */ - if(dlm) - lput(0x80000000|0x647); /* magic */ - else - lput(0x647); /* magic */ + lput(0x647); /* magic */ lput(textsize); /* sizes */ - lput(datsize); - lput(bsssize); + lput(segdata.filelen); + lput(segdata.len - segdata.filelen); lput(symsize); /* nsyms */ lput(entryvalue()); /* va of entry */ lput(0L); @@ -601,8 +407,8 @@ asmb(void) case 3: /* boot for NetBSD */ lput((143<<16)|0413); /* magic */ lputl(rnd(HEADR+textsize, 4096)); - lputl(rnd(datsize, 4096)); - lputl(bsssize); + lputl(rnd(segdata.filelen, 4096)); + lputl(segdata.len - segdata.filelen); lputl(symsize); /* nsyms */ lputl(entryvalue()); /* va of entry */ lputl(0L); @@ -650,41 +456,8 @@ asmb(void) phsh(ph, sh); } - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_X+PF_R; - ph->vaddr = va; - ph->paddr = va; - ph->off = fo; - ph->filesz = w; - ph->memsz = w; - ph->align = INITRND; - - fo = rnd(fo+w, INITRND); - va = rnd(va+w, INITRND); - w = datsize; - - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_W+PF_R; - ph->off = fo; - ph->vaddr = va; - ph->paddr = va; - ph->filesz = w; - ph->memsz = w+bsssize; - ph->align = INITRND; - - if(!debug['s']) { - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_R; - ph->off = symo; - ph->vaddr = symdatva; - ph->paddr = symdatva; - ph->filesz = rnd(8+symsize+lcsize, INITRND); - ph->memsz = rnd(8+symsize+lcsize, INITRND); - ph->align = INITRND; - } + elfphload(&segtext); + elfphload(&segdata); /* Dynamic linking sections */ if (!debug['d']) { /* -d suppresses dynamic loader format */ @@ -766,81 +539,23 @@ asmb(void) ph->flags = PF_W+PF_R; ph->align = 4; - fo = ELFRESERVE; - va = startva + fo; - w = textsize; - - /* - * The alignments are bigger than they really need - * to be here, but they are necessary to keep the - * arm strip from moving everything around. - */ - - sh = newElfShdr(elfstr[ElfStrText]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_EXECINSTR; - sh->addr = va; - sh->off = fo; - sh->size = w; - sh->addralign = ELFRESERVE; - - fo = rnd(fo+w, INITRND); - va = rnd(va+w, INITRND); - w = datsize; - - sh = newElfShdr(elfstr[ElfStrData]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_WRITE+SHF_ALLOC; - sh->addr = va + elfdatsize; - sh->off = fo + elfdatsize; - sh->size = w - elfdatsize; - sh->addralign = INITRND; - - fo += w; - va += w; - w = bsssize; - - sh = newElfShdr(elfstr[ElfStrBss]); - sh->type = SHT_NOBITS; - sh->flags = SHF_WRITE+SHF_ALLOC; - sh->addr = va; - sh->off = fo; - sh->size = w; - sh->addralign = 4; + for(sect=segtext.sect; sect!=nil; sect=sect->next) + elfshbits(sect); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + elfshbits(sect); if (!debug['s']) { - fo = symo; - w = 8; - sh = newElfShdr(elfstr[ElfStrGosymtab]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; - sh->addralign = INITRND; - sh->addr = symdatva; - - fo += w; - w = symsize; - - sh = newElfShdr(elfstr[ElfStrGosymtab]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; sh->addralign = 1; - sh->addr = symdatva + 8; - - fo += w; - w = lcsize; + shsym(sh, lookup("symtab", 0)); sh = newElfShdr(elfstr[ElfStrGopclntab]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; sh->addralign = 1; - sh->addr = symdatva + 8 + lcsize; + shsym(sh, lookup("pclntab", 0)); } sh = newElfShstrtab(elfstr[ElfStrShstrtab]); @@ -879,26 +594,15 @@ asmb(void) } cflush(); if(debug['c']){ - print("textsize=%ld\n", textsize); - print("datsize=%ld\n", datsize); - print("bsssize=%ld\n", bsssize); - print("symsize=%ld\n", symsize); - print("lcsize=%ld\n", lcsize); - print("total=%ld\n", textsize+datsize+bsssize+symsize+lcsize); + print("textsize=%d\n", textsize); + print("datsize=%d\n", segdata.filelen); + print("bsssize=%d\n", segdata.len - segdata.filelen); + print("symsize=%d\n", symsize); + print("lcsize=%d\n", lcsize); + print("total=%d\n", textsize+segdata.len+symsize+lcsize); } } -void -strnput(char *s, int n) -{ - for(; *s; s++){ - cput(*s); - n--; - } - for(; n > 0; n--) - cput(0); -} - void cput(int c) { @@ -987,7 +691,7 @@ cflush(void) /* no bug if cbc < 0 since obuf(cbuf) followed by ibuf in buf! */ n = sizeof(buf.cbuf) - cbc; if(n) - write(cout, buf.cbuf, n); + ewrite(cout, buf.cbuf, n); cbp = buf.cbuf; cbc = sizeof(buf.cbuf); } @@ -996,223 +700,16 @@ void nopstat(char *f, Count *c) { if(c->outof) - Bprint(&bso, "%s delay %ld/%ld (%.2f)\n", f, + Bprint(&bso, "%s delay %d/%d (%.2f)\n", f, c->outof - c->count, c->outof, (double)(c->outof - c->count)/c->outof); } -void -asmsym(void) -{ - Prog *p; - Auto *a; - Sym *s; - int h; - - s = lookup("etext", 0); - if(s->type == STEXT) - putsymb(s->name, 'T', s->value, s->version); - - for(h=0; hlink) - switch(s->type) { - case SCONST: - putsymb(s->name, 'D', s->value, s->version); - continue; - - case SDATA: - case SELFDATA: - putsymb(s->name, 'D', s->value+INITDAT, s->version); - continue; - - case SBSS: - putsymb(s->name, 'B', s->value+INITDAT, s->version); - continue; - - case SFIXED: - putsymb(s->name, 'B', s->value, s->version); - continue; - - case SSTRING: - putsymb(s->name, 'T', s->value, s->version); - continue; - - case SFILE: - putsymb(s->name, 'f', s->value, s->version); - continue; - } - - for(p=textp; p!=P; p=p->cond) { - s = p->from.sym; - if(s->type != STEXT && s->type != SLEAF) - continue; - - /* filenames first */ - for(a=p->to.autom; a; a=a->link) - if(a->type == D_FILE) - putsymb(a->asym->name, 'z', a->aoffset, 0); - else - if(a->type == D_FILE1) - putsymb(a->asym->name, 'Z', a->aoffset, 0); - - if(!s->reachable) - continue; - - if(s->type == STEXT) - putsymb(s->name, 'T', s->value, s->version); - else - putsymb(s->name, 'L', s->value, s->version); - - /* frame, auto and param after */ - putsymb(".frame", 'm', p->to.offset+4, 0); - for(a=p->to.autom; a; a=a->link) - if(a->type == D_AUTO) - putsymb(a->asym->name, 'a', -a->aoffset, 0); - else - if(a->type == D_PARAM) - putsymb(a->asym->name, 'p', a->aoffset, 0); - } - if(debug['v'] || debug['n']) - Bprint(&bso, "symsize = %lud\n", symsize); - Bflush(&bso); -} - -void -putsymb(char *s, int t, int32 v, int ver) -{ - int i, f; - - if(t == 'f') - s++; - lput(v); - if(ver) - t += 'a' - 'A'; - cput(t+0x80); /* 0x80 is variable length */ - - if(t == 'Z' || t == 'z') { - cput(s[0]); - for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) { - cput(s[i]); - cput(s[i+1]); - } - cput(0); - cput(0); - i++; - } - else { - for(i=0; s[i]; i++) - cput(s[i]); - cput(0); - } - // TODO(rsc): handle go parameter - lput(0); - - symsize += 4 + 1 + i + 1 + 4; - - if(debug['n']) { - if(t == 'z' || t == 'Z') { - Bprint(&bso, "%c %.8lux ", t, v); - for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) { - f = ((s[i]&0xff) << 8) | (s[i+1]&0xff); - Bprint(&bso, "/%x", f); - } - Bprint(&bso, "\n"); - return; - } - if(ver) - Bprint(&bso, "%c %.8lux %s<%d>\n", t, v, s, ver); - else - Bprint(&bso, "%c %.8lux %s\n", t, v, s); - } -} - -#define MINLC 4 -void -asmlc(void) -{ - int32 oldpc, oldlc; - Prog *p; - int32 v, s; - - oldpc = INITTEXT; - oldlc = 0; - for(p = firstp; p != P; p = p->link) { - setarch(p); - if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) { - if(p->as == ATEXT) - curtext = p; - if(debug['L']) - Bprint(&bso, "%6lux %P\n", - p->pc, p); - continue; - } - if(debug['L']) - Bprint(&bso, "\t\t%6ld", lcsize); - v = (p->pc - oldpc) / MINLC; - while(v) { - s = 127; - if(v < 127) - s = v; - cput(s+128); /* 129-255 +pc */ - if(debug['L']) - Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128); - v -= s; - lcsize++; - } - s = p->line - oldlc; - oldlc = p->line; - oldpc = p->pc + MINLC; - if(s > 64 || s < -64) { - cput(0); /* 0 vv +lc */ - cput(s>>24); - cput(s>>16); - cput(s>>8); - cput(s); - if(debug['L']) { - if(s > 0) - Bprint(&bso, " lc+%ld(%d,%ld)\n", - s, 0, s); - else - Bprint(&bso, " lc%ld(%d,%ld)\n", - s, 0, s); - Bprint(&bso, "%6lux %P\n", - p->pc, p); - } - lcsize += 5; - continue; - } - if(s > 0) { - cput(0+s); /* 1-64 +lc */ - if(debug['L']) { - Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s); - Bprint(&bso, "%6lux %P\n", - p->pc, p); - } - } else { - cput(64-s); /* 65-128 -lc */ - if(debug['L']) { - Bprint(&bso, " lc%ld(%ld)\n", s, 64-s); - Bprint(&bso, "%6lux %P\n", - p->pc, p); - } - } - lcsize++; - } - while(lcsize & 1) { - s = 129; - cput(s); - lcsize++; - } - if(debug['v'] || debug['L']) - Bprint(&bso, "lcsize = %ld\n", lcsize); - Bflush(&bso); -} - static void outt(int32 f, int32 l) { if(debug['L']) - Bprint(&bso, "tmap: %lux-%lux\n", f, l); + Bprint(&bso, "tmap: %ux-%ux\n", f, l); lput(f); lput(l); } @@ -1227,186 +724,46 @@ asmthumbmap(void) return; pc = 0; lastt = -1; - for(p = firstp; p != P; p = p->link){ + for(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; pc = p->pc - INITTEXT; - if(p->as == ATEXT){ - setarch(p); - if(thumb){ - if(p->from.sym->foreign){ // 8 bytes of ARM first - if(lastt >= 0){ - outt(lastt, pc-1); - lastt = -1; - } - pc += 8; - } - if(lastt < 0) - lastt = pc; - } - else{ - if(p->from.sym->foreign){ // 4 bytes of THUMB first - if(lastt < 0) - lastt = pc; - pc += 4; - } + setarch(p); + if(thumb){ + if(p->from.sym->foreign){ // 8 bytes of ARM first if(lastt >= 0){ outt(lastt, pc-1); lastt = -1; } + pc += 8; } + if(lastt < 0) + lastt = pc; } - } - if(lastt >= 0) - outt(lastt, pc+1); -} - -void -datblk(int32 s, int32 n, int str) -{ - Sym *v; - Prog *p; - char *cast; - int32 a, l, fl, j, d; - int i, c; - - memset(buf.dbuf, 0, n+100); - for(p = datap; p != P; p = p->link) { - if(str != (p->from.sym->type == SSTRING)) - continue; - curp = p; - a = p->from.sym->value + p->from.offset; - l = a - s; - c = p->reg; - i = 0; - if(l < 0) { - if(l+c <= 0) - continue; - while(l < 0) { - l++; - i++; - } - } - if(l >= n) - continue; - if(p->as != AINIT && p->as != ADYNT) { - for(j=l+(c-i)-1; j>=l; j--) - if(buf.dbuf[j]) { - print("%P\n", p); - diag("multiple initialization"); - break; - } - } - switch(p->to.type) { - default: - diag("unknown mode in initialization%P", p); - break; - - case D_FCONST: - switch(c) { - default: - case 4: - fl = ieeedtof(p->to.ieee); - cast = (char*)&fl; - for(; ito.ieee; - for(; ito.sval[i]; - l++; - } - break; - - case D_CONST: - d = p->to.offset; - v = p->to.sym; - if(v) { - switch(v->type) { - case SUNDEF: - ckoff(v, d); - d += v->value; - break; - case STEXT: - case SLEAF: - d += v->value; -#ifdef CALLEEBX - d += fnpinc(v); -#else - if(v->thumb) - d++; // T bit -#endif - break; - case SSTRING: - d += v->value; - break; - case SDATA: - case SBSS: - if(p->to.type == D_SIZE) - d += v->size; - else - d += v->value + INITDAT; - break; - } - if(dlm) - dynreloc(v, a+INITDAT, 1); - } - cast = (char*)&d; - switch(c) { - default: - diag("bad nuxi %d %d%P", c, i, curp); - break; - case 1: - for(; ifrom.sym->foreign){ // 4 bytes of THUMB first + if(lastt < 0) + lastt = pc; + pc += 4; } - for(; ito.sbig[i]; - l++; + if(lastt >= 0){ + outt(lastt, pc-1); + lastt = -1; } - break; } + if(cursym->next == nil) + for(; p != P; p = p->link) + pc = p->pc = INITTEXT; } - write(cout, buf.dbuf, n); + if(lastt >= 0) + outt(lastt, pc+1); } void -asmout(Prog *p, Optab *o) +asmout(Prog *p, Optab *o, int32 *out) { int32 o1, o2, o3, o4, o5, o6, v; int r, rf, rt, rt2; - Sym *s; + Reloc *rel; PP = p; o1 = 0; @@ -1416,7 +773,7 @@ PP = p; o5 = 0; o6 = 0; armsize += o->size; -if(debug['P']) print("%ulx: %P type %d\n", (uint32)(p->pc), p, o->type); +if(debug['P']) print("%ux: %P type %d\n", (uint32)(p->pc), p, o->type); switch(o->type) { default: diag("unknown asm %d", o->type); @@ -1424,7 +781,7 @@ if(debug['P']) print("%ulx: %P type %d\n", (uint32)(p->pc), p, o->type); break; case 0: /* pseudo ops */ -if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym->name, p->from.sym->thumb, p->from.sym->foreign, p->from.sym->fnptr, p->from.sym->used); +if(debug['G']) print("%ux: %s: arm %d %d %d\n", (uint32)(p->pc), p->from.sym->name, p->from.sym->thumb, p->from.sym->foreign, p->from.sym->fnptr); break; case 1: /* op R,[R],R */ @@ -1485,14 +842,7 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym case 5: /* bra s */ v = -8; - if(p->cond == UP) { - s = p->to.sym; - if(s->type != SUNDEF) - diag("bad branch sym type"); - v = (uint32)s->value >> (Roffset-2); - dynreloc(s, p->pc, 0); - } - else if(p->cond != P) + if(p->cond != P) v = (p->cond->pc - pc) - 8; #ifdef CALLEEBX if(p->as == ABL) @@ -1553,18 +903,17 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym break; case 11: /* word */ - switch(aclass(&p->to)) { - case C_LCON: - if(!dlm) - break; - if(p->to.name != D_EXTERN && p->to.name != D_STATIC) - break; - case C_ADDR: - if(p->to.sym->type == SUNDEF) - ckoff(p->to.sym, p->to.offset); - dynreloc(p->to.sym, p->pc, 1); - } + aclass(&p->to); o1 = instoffset; + if(p->to.sym != S) { + rel = addrel(cursym); + rel->off = pc - cursym->value; + rel->siz = 4; + rel->type = D_ADDR; + rel->sym = p->to.sym; + rel->add = p->to.offset; + o1 = 0; + } break; case 12: /* movw $lcon, reg */ @@ -1890,38 +1239,15 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym case 54: /* floating point arith */ o1 = oprrr(p->as, p->scond); - if(p->from.type == D_FCONST) { - rf = chipfloat(p->from.ieee); - if(rf < 0){ - diag("invalid floating-point immediate\n%P", p); - rf = 0; - } - rf |= (1<<3); - } else - rf = p->from.reg; + rf = p->from.reg; rt = p->to.reg; r = p->reg; - if(p->to.type == D_NONE) - rt = 0; /* CMP[FD] */ - else if(o1 & (1<<15)) - r = 0; /* monadic */ - else if(r == NREG) + if(r == NREG) { r = rt; - o1 |= rf | (r<<16) | (rt<<12); - break; - - case 55: /* floating point fix and float */ - o1 = oprrr(p->as, p->scond); - rf = p->from.reg; - rt = p->to.reg; - if(p->to.type == D_NONE){ - rt = 0; - diag("to.type==D_NONE (asm/fp)"); + if(p->as == AMOVF || p->as == AMOVD) + r = 0; } - if(p->from.type == D_REG) - o1 |= (rf<<12) | (rt<<16); - else - o1 |= rf | (rt<<12); + o1 |= rf | (r<<16) | (rt<<12); break; case 56: /* move to FP[CS]R */ @@ -1983,11 +1309,8 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym break; case 63: /* bcase */ - if(p->cond != P) { + if(p->cond != P) o1 = p->cond->pc; - if(dlm) - dynreloc(S, p->pc, 1); - } break; /* reloc ops */ @@ -2114,7 +1437,7 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym if(p->to.sym->thumb) v |= 1; // T bit o1 = olr(8, REGPC, REGTMP, p->scond&C_SCOND); // mov 8(PC), Rtmp - o2 = oprrr(AADD, p->scond) | immrot(8) | (REGPC<<16) | (REGLINK<<12); // add 8,PC, LR + o2 = oprrr(AADD, p->scond) | immrot(8) | (REGPC<<16) | (REGLINK<<12); // add 8,PC, LR o3 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | REGTMP; // bx Rtmp o4 = opbra(AB, 14); // B over o6 o5 = v; @@ -2132,7 +1455,7 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym o1 |= immrot(instoffset); o1 |= p->to.reg << 16; o1 |= REGTMP << 12; - o2 = oprrr(AADD, p->scond) | immrot(0) | (REGPC<<16) | (REGLINK<<12); // mov PC, LR + o2 = oprrr(AADD, p->scond) | immrot(0) | (REGPC<<16) | (REGLINK<<12); // mov PC, LR o3 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | REGTMP; // BX Rtmp break; case 76: /* bx O(R) when returning from fn*/ @@ -2164,35 +1487,114 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym o1 |= p->to.reg << 12; o1 |= (p->scond & C_SCOND) << 28; break; + case 80: /* fmov zfcon,freg */ + if((p->scond & C_SCOND) != C_SCOND_NONE) + diag("floating point cannot be conditional"); // cant happen + o1 = 0xf3000110; // EOR 64 + + // always clears the double float register + r = p->to.reg; + o1 |= r << 0; + o1 |= r << 12; + o1 |= r << 16; + break; + case 81: /* fmov sfcon,freg */ + o1 = 0x0eb00a00; // VMOV imm 32 + if(p->as == AMOVD) + o1 = 0xeeb00b00; // VMOV imm 64 + o1 |= (p->scond & C_SCOND) << 28; + o1 |= p->to.reg << 12; + v = chipfloat(&p->from.ieee); + o1 |= (v&0xf) << 0; + o1 |= (v&0xf0) << 12; + break; + case 82: /* fcmp freg,freg, */ + o1 = oprrr(p->as, p->scond); + o1 |= (p->reg<<12) | (p->from.reg<<0); + o2 = 0x0ef1fa10; // VMRS R15 + o2 |= (p->scond & C_SCOND) << 28; + break; + case 83: /* fcmp freg,, */ + o1 = oprrr(p->as, p->scond); + o1 |= (p->from.reg<<12) | (1<<16); + o2 = 0x0ef1fa10; // VMRS R15 + o2 |= (p->scond & C_SCOND) << 28; + break; + case 84: /* movfw freg,freg - truncate float-to-fix */ + o1 = oprrr(p->as, p->scond); + o1 |= (p->from.reg<<0); + o1 |= (p->to.reg<<12); + break; + case 85: /* movwf freg,freg - fix-to-float */ + o1 = oprrr(p->as, p->scond); + o1 |= (p->from.reg<<0); + o1 |= (p->to.reg<<12); + break; + case 86: /* movfw freg,reg - truncate float-to-fix */ + // macro for movfw freg,FTMP; movw FTMP,reg + o1 = oprrr(p->as, p->scond); + o1 |= (p->from.reg<<0); + o1 |= (FREGTMP<<12); + o2 = oprrr(AMOVFW+AEND, p->scond); + o2 |= (FREGTMP<<16); + o2 |= (p->to.reg<<12); + break; + case 87: /* movwf reg,freg - fix-to-float */ + // macro for movw reg,FTMP; movwf FTMP,freg + o1 = oprrr(AMOVWF+AEND, p->scond); + o1 |= (p->from.reg<<12); + o1 |= (FREGTMP<<16); + o2 = oprrr(p->as, p->scond); + o2 |= (FREGTMP<<0); + o2 |= (p->to.reg<<12); + break; + case 88: /* movw reg,freg */ + o1 = oprrr(AMOVWF+AEND, p->scond); + o1 |= (p->from.reg<<12); + o1 |= (p->to.reg<<16); + break; + case 89: /* movw freg,reg */ + o1 = oprrr(AMOVFW+AEND, p->scond); + o1 |= (p->from.reg<<16); + o1 |= (p->to.reg<<12); + break; } + + out[0] = o1; + out[1] = o2; + out[2] = o3; + out[3] = o4; + out[4] = o5; + out[5] = o6; + return; v = p->pc; switch(o->size) { default: if(debug['a']) - Bprint(&bso, " %.8lux:\t\t%P\n", v, p); + Bprint(&bso, " %.8ux:\t\t%P\n", v, p); break; case 4: if(debug['a']) - Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p); + Bprint(&bso, " %.8ux: %.8ux\t%P\n", v, o1, p); lputl(o1); break; case 8: if(debug['a']) - Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", v, o1, o2, p); + Bprint(&bso, " %.8ux: %.8ux %.8ux%P\n", v, o1, o2, p); lputl(o1); lputl(o2); break; case 12: if(debug['a']) - Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux%P\n", v, o1, o2, o3, p); + Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux%P\n", v, o1, o2, o3, p); lputl(o1); lputl(o2); lputl(o3); break; case 16: if(debug['a']) - Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux%P\n", + Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux %.8ux%P\n", v, o1, o2, o3, o4, p); lputl(o1); lputl(o2); @@ -2201,7 +1603,7 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym break; case 20: if(debug['a']) - Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux%P\n", + Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux%P\n", v, o1, o2, o3, o4, o5, p); lputl(o1); lputl(o2); @@ -2211,7 +1613,7 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym break; case 24: if(debug['a']) - Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux%P\n", + Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux%P\n", v, o1, o2, o3, o4, o5, o6, p); lputl(o1); lputl(o2); @@ -2262,26 +1664,51 @@ oprrr(int a, int sc) case ASRA: return o | (0xd<<21) | (2<<5); case ASWI: return o | (0xf<<24); - case AADDD: return o | (0xe<<24) | (0x0<<20) | (1<<8) | (1<<7); - case AADDF: return o | (0xe<<24) | (0x0<<20) | (1<<8); - case AMULD: return o | (0xe<<24) | (0x1<<20) | (1<<8) | (1<<7); - case AMULF: return o | (0xe<<24) | (0x1<<20) | (1<<8); - case ASUBD: return o | (0xe<<24) | (0x2<<20) | (1<<8) | (1<<7); - case ASUBF: return o | (0xe<<24) | (0x2<<20) | (1<<8); - case ADIVD: return o | (0xe<<24) | (0x4<<20) | (1<<8) | (1<<7); - case ADIVF: return o | (0xe<<24) | (0x4<<20) | (1<<8); - case ACMPD: - case ACMPF: return o | (0xe<<24) | (0x9<<20) | (0xF<<12) | (1<<8) | (1<<4); /* arguably, ACMPF should expand to RNDF, CMPD */ - - case AMOVF: - case AMOVDF: return o | (0xe<<24) | (0x0<<20) | (1<<15) | (1<<8); - case AMOVD: - case AMOVFD: return o | (0xe<<24) | (0x0<<20) | (1<<15) | (1<<8) | (1<<7); - - case AMOVWF: return o | (0xe<<24) | (0<<20) | (1<<8) | (1<<4); - case AMOVWD: return o | (0xe<<24) | (0<<20) | (1<<8) | (1<<4) | (1<<7); - case AMOVFW: return o | (0xe<<24) | (1<<20) | (1<<8) | (1<<4); - case AMOVDW: return o | (0xe<<24) | (1<<20) | (1<<8) | (1<<4) | (1<<7); + case AADDD: return o | (0xe<<24) | (0x3<<20) | (0xb<<8) | (0<<4); + case AADDF: return o | (0xe<<24) | (0x3<<20) | (0xa<<8) | (0<<4); + case ASUBD: return o | (0xe<<24) | (0x3<<20) | (0xb<<8) | (4<<4); + case ASUBF: return o | (0xe<<24) | (0x3<<20) | (0xa<<8) | (4<<4); + case AMULD: return o | (0xe<<24) | (0x2<<20) | (0xb<<8) | (0<<4); + case AMULF: return o | (0xe<<24) | (0x2<<20) | (0xa<<8) | (0<<4); + case ADIVD: return o | (0xe<<24) | (0x8<<20) | (0xb<<8) | (0<<4); + case ADIVF: return o | (0xe<<24) | (0x8<<20) | (0xa<<8) | (0<<4); + case ACMPD: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xb<<8) | (0xc<<4); + case ACMPF: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xa<<8) | (0xc<<4); + + case AMOVF: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xa<<8) | (4<<4); + case AMOVD: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xb<<8) | (4<<4); + + case AMOVDF: return o | (0xe<<24) | (0xb<<20) | (7<<16) | (0xa<<8) | (0xc<<4) | + (1<<8); // dtof + case AMOVFD: return o | (0xe<<24) | (0xb<<20) | (7<<16) | (0xa<<8) | (0xc<<4) | + (0<<8); // dtof + + case AMOVWF: + if((sc & C_UBIT) == 0) + o |= 1<<7; /* signed */ + return o | (0xe<<24) | (0xb<<20) | (8<<16) | (0xa<<8) | (4<<4) | + (0<<18) | (0<<8); // toint, double + case AMOVWD: + if((sc & C_UBIT) == 0) + o |= 1<<7; /* signed */ + return o | (0xe<<24) | (0xb<<20) | (8<<16) | (0xa<<8) | (4<<4) | + (0<<18) | (1<<8); // toint, double + + case AMOVFW: + if((sc & C_UBIT) == 0) + o |= 1<<16; /* signed */ + return o | (0xe<<24) | (0xb<<20) | (8<<16) | (0xa<<8) | (4<<4) | + (1<<18) | (0<<8) | (1<<7); // toint, double, trunc + case AMOVDW: + if((sc & C_UBIT) == 0) + o |= 1<<16; /* signed */ + return o | (0xe<<24) | (0xb<<20) | (8<<16) | (0xa<<8) | (4<<4) | + (1<<18) | (1<<8) | (1<<7); // toint, double, trunc + + case AMOVWF+AEND: // copy WtoF + return o | (0xe<<24) | (0x0<<20) | (0xb<<8) | (1<<4); + case AMOVFW+AEND: // copy FtoW + return o | (0xe<<24) | (0x1<<20) | (0xb<<8) | (1<<4); } diag("bad rrr %d", a); prasm(curp); @@ -2337,12 +1764,13 @@ olr(int32 v, int b, int r, int sc) o |= 1 << 23; if(sc & C_WBIT) o |= 1 << 21; - o |= (0x1<<26) | (1<<20); + o |= (1<<26) | (1<<20); if(v < 0) { + if(sc & C_UBIT) diag(".U on neg offset"); v = -v; o ^= 1 << 23; } - if(v >= (1<<12)) + if(v >= (1<<12) || v < 0) diag("literal span too large: %d (R%d)\n%P", v, b, PP); o |= v; o |= b << 16; @@ -2367,7 +1795,7 @@ olhr(int32 v, int b, int r, int sc) v = -v; o ^= 1 << 23; } - if(v >= (1<<8)) + if(v >= (1<<8) || v < 0) diag("literal span too large: %d (R%d)\n%P", v, b, PP); o |= (v&0xf)|((v>>4)<<8)|(1<<22); o |= b << 16; @@ -2434,25 +1862,25 @@ ofsr(int a, int r, int32 v, int b, int sc, Prog *p) o |= 1 << 24; if(sc & C_WBIT) o |= 1 << 21; - o |= (6<<25) | (1<<24) | (1<<23); + o |= (6<<25) | (1<<24) | (1<<23) | (10<<8); if(v < 0) { v = -v; o ^= 1 << 23; } if(v & 3) diag("odd offset for floating point op: %d\n%P", v, p); - else if(v >= (1<<10)) + else + if(v >= (1<<10) || v < 0) diag("literal span too large: %d\n%P", v, p); o |= (v>>2) & 0xFF; o |= b << 16; o |= r << 12; - o |= 1 << 8; switch(a) { default: diag("bad fst %A", a); case AMOVD: - o |= 1<<15; + o |= 1 << 8; case AMOVF: break; } @@ -2481,28 +1909,104 @@ omvl(Prog *p, Adr *a, int dr) return o1; } -static Ieee chipfloats[] = { - {0x00000000, 0x00000000}, /* 0 */ - {0x00000000, 0x3ff00000}, /* 1 */ - {0x00000000, 0x40000000}, /* 2 */ - {0x00000000, 0x40080000}, /* 3 */ - {0x00000000, 0x40100000}, /* 4 */ - {0x00000000, 0x40140000}, /* 5 */ - {0x00000000, 0x3fe00000}, /* .5 */ - {0x00000000, 0x40240000}, /* 10 */ -}; +int +chipzero(Ieee *e) +{ + if(e->l != 0 || e->h != 0) + return -1; + return 0; +} int chipfloat(Ieee *e) { - Ieee *p; int n; + ulong h; - for(n = sizeof(chipfloats)/sizeof(chipfloats[0]); --n >= 0;){ - p = &chipfloats[n]; - if(p->l == e->l && p->h == e->h) - return n; - } + if(e->l != 0 || (e->h&0xffff) != 0) + goto no; + h = e->h & 0x7fc00000; + if(h != 0x40000000 && h != 0x3fc00000) + goto no; + n = 0; + + // sign bit (a) + if(e->h & 0x80000000) + n |= 1<<7; + + // exp sign bit (b) + if(h == 0x3fc00000) + n |= 1<<6; + + // rest of exp and mantissa (cd-efgh) + n |= (e->h >> 16) & 0x3f; + +//print("match %.8lux %.8lux %d\n", e->l, e->h, n); + return n; + +no: return -1; } + +void +genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) +{ + Auto *a; + Sym *s; + int h; + + s = lookup("etext", 0); + if(s->type == STEXT) + put(s, s->name, 'T', s->value, s->size, s->version, 0); + + for(h=0; hhash) { + switch(s->type) { + case SCONST: + case SRODATA: + case SDATA: + case SELFDATA: + if(!s->reachable) + continue; + put(s, s->name, 'D', s->value, s->size, s->version, s->gotype); + continue; + + case SBSS: + if(!s->reachable) + continue; + put(s, s->name, 'B', s->value, s->size, s->version, s->gotype); + continue; + + case SFILE: + put(nil, s->name, 'f', s->value, 0, s->version, 0); + continue; + } + } + } + + for(s = textp; s != nil; s = s->next) { + /* filenames first */ + for(a=s->autom; a; a=a->link) + if(a->type == D_FILE) + put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0); + else + if(a->type == D_FILE1) + put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0); + + put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); + + /* frame, auto and param after */ + put(nil, ".frame", 'm', s->text->to.offset+4, 0, 0, 0); + + for(a=s->autom; a; a=a->link) + if(a->type == D_AUTO) + put(nil, a->asym->name, 'a', -a->aoffset, 0, 0, a->gotype); + else + if(a->type == D_PARAM) + put(nil, a->asym->name, 'p', a->aoffset, 0, 0, a->gotype); + } + if(debug['v'] || debug['n']) + Bprint(&bso, "symsize = %ud\n", symsize); + Bflush(&bso); +} diff --git a/src/cmd/5l/doc.go b/src/cmd/5l/doc.go index b09995d71..6f7408116 100644 --- a/src/cmd/5l/doc.go +++ b/src/cmd/5l/doc.go @@ -20,8 +20,8 @@ Original options are listed in the link above. Options new in this version: --L dir1,dir2,.. - Search for libraries (package files) in the comma-separated list of directories. +-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. diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h index c6659cfab..4e7ccea88 100644 --- a/src/cmd/5l/l.h +++ b/src/cmd/5l/l.h @@ -45,20 +45,21 @@ enum /* do not undefine this - code will be removed eventually */ #define CALLEEBX +#define dynptrsize 0 + typedef struct Adr Adr; typedef struct Sym Sym; typedef struct Autom Auto; typedef struct Prog Prog; +typedef struct Reloc Reloc; typedef struct Optab Optab; typedef struct Oprang Oprang; typedef uchar Opcross[32][2][32]; typedef struct Count Count; -typedef struct Use Use; #define P ((Prog*)0) #define S ((Sym*)0) -#define U ((Use*)0) -#define TNAME (curtext&&curtext->from.sym?curtext->from.sym->name:noname) +#define TNAME (cursym?cursym->name:noname) struct Adr { @@ -66,14 +67,10 @@ struct Adr { int32 u0offset; char* u0sval; - Ieee* u0ieee; + Ieee u0ieee; char* u0sbig; } u0; - union - { - Auto* u1autom; - Sym* u1sym; - } u1; + Sym* sym; char type; uchar index; // not used on arm, required by ld/go.c char reg; @@ -85,11 +82,18 @@ struct Adr #define offset u0.u0offset #define sval u0.u0sval +#define scon sval #define ieee u0.u0ieee #define sbig u0.u0sbig -#define autom u1.u1autom -#define sym u1.u1sym +struct Reloc +{ + int32 off; + uchar siz; + int16 type; + int32 add; + Sym* sym; +}; struct Prog { @@ -112,35 +116,51 @@ struct Prog uchar reg; uchar align; }; + #define regused u0.u0regused #define forwd u0.u0forwd +#define datasize reg +#define textflag reg struct Sym { - char *name; + char* name; short type; short version; - short become; - short frame; - uchar subtype; uchar dupok; uchar reachable; uchar dynexport; + uchar leaf; + int32 dynid; + int32 plt; + int32 got; int32 value; int32 sig; int32 size; - uchar used; + uchar special; uchar thumb; // thumb code uchar foreign; // called by arm if thumb, by thumb if arm uchar fnptr; // used as fn ptr - Use* use; - Sym* link; - Prog* text; - Prog* data; + Sym* hash; // in hash table + Sym* next; // in text or data list + Sym* sub; // in SSUB list + Sym* outer; // container of sub Sym* gotype; char* file; char* dynimpname; char* dynimplib; + + // STEXT + Auto* autom; + Prog* text; + + // SDATA, SBSS + uchar* p; + int32 np; + int32 maxp; + Reloc* r; + int32 nr; + int32 maxr; }; #define SIGNINTERN (1729*325*1729) @@ -174,34 +194,24 @@ struct Count int32 count; int32 outof; }; -struct Use -{ - Prog* p; /* use */ - Prog* ct; /* curtext */ - Use* link; -}; enum { Sxxx, - + + /* order here is order in output file */ STEXT = 1, + SRODATA, + SELFDATA, SDATA, SBSS, - SDATA1, + SXREF, - SLEAF, SFILE, SCONST, - SSTRING, - SUNDEF, - SREMOVED, + SDYNIMPORT, - SIMPORT, - SEXPORT, - - SFIXED, - SELFDATA, + SSUB = 1<<8, LFROM = 1<<0, LTO = 1<<1, @@ -221,7 +231,9 @@ enum C_SCON, /* 0xffff */ C_BCON, /* thumb */ C_LCON, - C_FCON, + C_ZFCON, + C_SFCON, + C_LFCON, C_GCON, /* thumb */ C_RACON, @@ -229,9 +241,6 @@ enum C_LACON, C_GACON, /* thumb */ - C_RECON, - C_LECON, - C_SBRA, C_LBRA, C_GBRA, /* thumb */ @@ -242,12 +251,6 @@ enum C_SAUTO, /* -0xfff to 0xfff */ C_LAUTO, - C_HEXT, - C_FEXT, - C_HFEXT, - C_SEXT, - C_LEXT, - C_HOREG, C_FOREG, C_HFOREG, @@ -262,7 +265,7 @@ enum C_HREG, C_OFFPC, /* thumb */ - C_ADDR, /* relocatable address */ + C_ADDR, /* reference to relocatable address */ C_GOK, @@ -271,7 +274,6 @@ enum LABEL = 1<<1, LEAF = 1<<2, - BIG = (1<<12)-4, STRINGSZ = 200, NHASH = 10007, NHUNK = 100000, @@ -279,9 +281,7 @@ enum NENT = 100, MAXIO = 8192, MAXHIST = 20, /* limit of path elements for history symbols */ - - Roffset = 22, /* no. bits for offset in relocation address */ - Rindex = 10, /* no. bits for index in relocation address */ + MINLC = 4, }; EXTERN union @@ -310,23 +310,18 @@ EXTERN int32 INITTEXT; /* text location */ EXTERN char* INITENTRY; /* entry point */ EXTERN int32 autosize; EXTERN Biobuf bso; -EXTERN int32 bsssize; EXTERN int cbc; EXTERN uchar* cbp; EXTERN int cout; EXTERN Auto* curauto; EXTERN Auto* curhist; EXTERN Prog* curp; -EXTERN Prog* curtext; -EXTERN Prog* datap; -EXTERN int32 datsize; +EXTERN Sym* cursym; +EXTERN Sym* datap; EXTERN int32 elfdatsize; EXTERN char debug[128]; -EXTERN Prog* edatap; -EXTERN Prog* etextp; -EXTERN Prog* firstp; +EXTERN Sym* etextp; EXTERN char* noname; -EXTERN int xrefresolv; EXTERN Prog* lastp; EXTERN int32 lcsize; EXTERN char literal[32]; @@ -341,7 +336,7 @@ EXTERN uchar repop[ALAST]; EXTERN char* rpath; EXTERN uint32 stroffset; EXTERN int32 symsize; -EXTERN Prog* textp; +EXTERN Sym* textp; EXTERN int32 textsize; EXTERN int version; EXTERN char xcmp[C_GOK+1][C_GOK+1]; @@ -352,14 +347,6 @@ EXTERN int thumb; EXTERN int seenthumb; EXTERN int armsize; -EXTERN int doexp, dlm; -EXTERN int imports, nimports; -EXTERN int exports, nexports; -EXTERN char* EXPTAB; -EXTERN Prog undefp; - -#define UP (&undefp) - extern char* anames[]; extern Optab optab[]; extern Optab thumboptab[]; @@ -384,6 +371,7 @@ EXTERN Prog* prog_modu; int Aconv(Fmt*); int Cconv(Fmt*); int Dconv(Fmt*); +int Iconv(Fmt*); int Nconv(Fmt*); int Oconv(Fmt*); int Pconv(Fmt*); @@ -393,36 +381,29 @@ int thumbaclass(Adr*, Prog*); void addhist(int32, int); Prog* appendp(Prog*); void asmb(void); -void asmdyn(void); -void asmlc(void); void asmthumbmap(void); -void asmout(Prog*, Optab*); +void asmout(Prog*, Optab*, int32*); void thumbasmout(Prog*, Optab*); -void asmsym(void); int32 atolwhex(char*); Prog* brloop(Prog*); void buildop(void); void thumbbuildop(void); void buildrep(int, int); void cflush(void); -void ckoff(Sym*, int32); +int chipzero(Ieee*); int chipfloat(Ieee*); int cmp(int, int); int compound(Prog*); double cputime(void); -void datblk(int32, int32, int); void diag(char*, ...); void divsig(void); void dodata(void); void doprof1(void); void doprof2(void); -void dynreloc(Sym*, int32, int); int32 entryvalue(void); void exchange(Prog*); -void export(void); void follow(void); void hputl(int); -void import(void); int isnop(Prog*); void listinit(void); Sym* lookup(char*, int); @@ -430,10 +411,8 @@ void cput(int); void hput(int32); void lput(int32); void lputl(int32); -void mkfwd(void); void* mysbrk(uint32); void names(void); -Prog* newdata(Sym *s, int o, int w, int t); void nocache(Prog*); int ocmp(const void*, const void*); int32 opirr(int); @@ -454,18 +433,17 @@ void prasm(Prog*); void prepend(Prog*, Prog*); Prog* prg(void); int pseudo(Prog*); -void putsymb(char*, int, int32, int); int32 regoff(Adr*); int relinv(int); int32 rnd(int32, int32); void softfloat(void); void span(void); void strnput(char*, int); +int32 symaddr(Sym*); void undef(void); void wput(int32); void wputl(ushort w); void xdefine(char*, int, int32); -void xfol(Prog*); void noops(void); int32 immrot(uint32); int32 immaddr(int32); @@ -475,7 +453,6 @@ int isbranch(Prog*); int fnpinc(Sym *); int fninc(Sym *); void thumbcount(void); -void reachable(void); void fnptrs(void); void doelf(void); diff --git a/src/cmd/5l/list.c b/src/cmd/5l/list.c index 9cbb5501c..b4df89587 100644 --- a/src/cmd/5l/list.c +++ b/src/cmd/5l/list.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Printing. + #include "l.h" #include "../ld/lib.h" @@ -42,6 +44,7 @@ listinit(void) fmtinstall('S', Sconv); fmtinstall('N', Nconv); fmtinstall('O', Oconv); // C_type constants + fmtinstall('I', Iconv); } void @@ -53,7 +56,6 @@ prasm(Prog *p) int Pconv(Fmt *fp) { - char str[STRINGSZ], *s; Prog *p; int a; @@ -62,42 +64,41 @@ Pconv(Fmt *fp) a = p->as; switch(a) { default: - s = str; - s += sprint(s, "(%d)", p->line); + fmtprint(fp, "(%d)", p->line); if(p->reg == NREG) - sprint(s, " %A%C %D,%D", + fmtprint(fp, " %A%C %D,%D", a, p->scond, &p->from, &p->to); else if(p->from.type != D_FREG) - sprint(s, " %A%C %D,R%d,%D", + fmtprint(fp, " %A%C %D,R%d,%D", a, p->scond, &p->from, p->reg, &p->to); else - sprint(s, " %A%C %D,F%d,%D", + fmtprint(fp, " %A%C %D,F%d,%D", a, p->scond, &p->from, p->reg, &p->to); break; case ASWPW: case ASWPBU: - sprint(str, "(%d) %A%C R%d,%D,%D", + fmtprint(fp, "(%d) %A%C R%d,%D,%D", p->line, a, p->scond, p->reg, &p->from, &p->to); break; case ADATA: - case AINIT: - case ADYNT: - sprint(str, "(%d) %A%C %D/%d,%D", + case AINIT_: + case ADYNT_: + fmtprint(fp, "(%d) %A%C %D/%d,%D", p->line, a, p->scond, &p->from, p->reg, &p->to); break; case AWORD: - sprint(str, "WORD %x", p->to.offset); + fmtprint(fp, "(%d) WORD %D", p->line, &p->to); break; case ADWORD: - sprint(str, "DWORD %x %x", p->from.offset, p->to.offset); + fmtprint(fp, "(%d) DWORD %D %D", p->line, &p->from, &p->to); break; } - return fmtstrcpy(fp, str); + return 0; } int @@ -164,98 +165,98 @@ Dconv(Fmt *fp) switch(a->type) { default: - sprint(str, "GOK-type(%d)", a->type); + snprint(str, sizeof str, "GOK-type(%d)", a->type); break; case D_NONE: str[0] = 0; if(a->name != D_NONE || a->reg != NREG || a->sym != S) - sprint(str, "%N(R%d)(NONE)", a, a->reg); + snprint(str, sizeof str, "%N(R%d)(NONE)", a, a->reg); break; case D_CONST: if(a->reg == NREG) - sprint(str, "$%N", a); + snprint(str, sizeof str, "$%N", a); else - sprint(str, "$%N(R%d)", a, a->reg); + snprint(str, sizeof str, "$%N(R%d)", a, a->reg); break; case D_CONST2: - sprint(str, "$%d-%d", a->offset, a->offset2); + snprint(str, sizeof str, "$%d-%d", a->offset, a->offset2); break; case D_SHIFT: v = a->offset; op = "<<>>->@>" + (((v>>5) & 3) << 1); if(v & (1<<4)) - sprint(str, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15); + snprint(str, sizeof str, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15); else - sprint(str, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31); + snprint(str, sizeof str, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31); if(a->reg != NREG) - sprint(str+strlen(str), "(R%d)", a->reg); + seprint(str+strlen(str), str+sizeof str, "(R%d)", a->reg); break; case D_OCONST: - sprint(str, "$*$%N", a); + snprint(str, sizeof str, "$*$%N", a); if(a->reg != NREG) - sprint(str, "%N(R%d)(CONST)", a, a->reg); + snprint(str, sizeof str, "%N(R%d)(CONST)", a, a->reg); break; case D_OREG: if(a->reg != NREG) - sprint(str, "%N(R%d)", a, a->reg); + snprint(str, sizeof str, "%N(R%d)", a, a->reg); else - sprint(str, "%N", a); + snprint(str, sizeof str, "%N", a); break; case D_REG: - sprint(str, "R%d", a->reg); + snprint(str, sizeof str, "R%d", a->reg); if(a->name != D_NONE || a->sym != S) - sprint(str, "%N(R%d)(REG)", a, a->reg); + snprint(str, sizeof str, "%N(R%d)(REG)", a, a->reg); break; case D_REGREG: - sprint(str, "(R%d,R%d)", a->reg, (int)a->offset); + snprint(str, sizeof str, "(R%d,R%d)", a->reg, (int)a->offset); if(a->name != D_NONE || a->sym != S) - sprint(str, "%N(R%d)(REG)", a, a->reg); + snprint(str, sizeof str, "%N(R%d)(REG)", a, a->reg); break; case D_FREG: - sprint(str, "F%d", a->reg); + snprint(str, sizeof str, "F%d", a->reg); if(a->name != D_NONE || a->sym != S) - sprint(str, "%N(R%d)(REG)", a, a->reg); + snprint(str, sizeof str, "%N(R%d)(REG)", a, a->reg); break; case D_PSR: switch(a->reg) { case 0: - sprint(str, "CPSR"); + snprint(str, sizeof str, "CPSR"); break; case 1: - sprint(str, "SPSR"); + snprint(str, sizeof str, "SPSR"); break; default: - sprint(str, "PSR%d", a->reg); + snprint(str, sizeof str, "PSR%d", a->reg); break; } if(a->name != D_NONE || a->sym != S) - sprint(str, "%N(PSR%d)(REG)", a, a->reg); + snprint(str, sizeof str, "%N(PSR%d)(REG)", a, a->reg); break; case D_FPCR: switch(a->reg){ case 0: - sprint(str, "FPSR"); + snprint(str, sizeof str, "FPSR"); break; case 1: - sprint(str, "FPCR"); + snprint(str, sizeof str, "FPCR"); break; default: - sprint(str, "FCR%d", a->reg); + snprint(str, sizeof str, "FCR%d", a->reg); break; } if(a->name != D_NONE || a->sym != S) - sprint(str, "%N(FCR%d)(REG)", a, a->reg); + snprint(str, sizeof str, "%N(FCR%d)(REG)", a, a->reg); break; @@ -263,22 +264,22 @@ Dconv(Fmt *fp) if(curp->cond != P) { v = curp->cond->pc; if(a->sym != S) - sprint(str, "%s+%.5lux(BRANCH)", a->sym->name, v); + snprint(str, sizeof str, "%s+%.5ux(BRANCH)", a->sym->name, v); else - sprint(str, "%.5lux(BRANCH)", v); + snprint(str, sizeof str, "%.5ux(BRANCH)", v); } else if(a->sym != S) - sprint(str, "%s+%d(APC)", a->sym->name, a->offset); + snprint(str, sizeof str, "%s+%d(APC)", a->sym->name, a->offset); else - sprint(str, "%d(APC)", a->offset); + snprint(str, sizeof str, "%d(APC)", a->offset); break; case D_FCONST: - sprint(str, "$%e", ieeedtod(a->ieee)); + snprint(str, sizeof str, "$%e", ieeedtod(&a->ieee)); break; case D_SCONST: - sprint(str, "$\"%S\"", a->sval); + snprint(str, sizeof str, "$\"%S\"", a->sval); break; } return fmtstrcpy(fp, str); @@ -374,15 +375,45 @@ Sconv(Fmt *fp) return fmtstrcpy(fp, str); } +int +Iconv(Fmt *fp) +{ + int i, n; + uint32 *p; + char *s; + Fmt fmt; + + n = fp->prec; + fp->prec = 0; + if(!(fp->flags&FmtPrec) || n < 0) + return fmtstrcpy(fp, "%I"); + fp->flags &= ~FmtPrec; + p = va_arg(fp->args, uint32*); + + // format into temporary buffer and + // call fmtstrcpy to handle padding. + fmtstrinit(&fmt); + for(i=0; i 0) + fmtprint(&fmt, " "); + fmtprint(&fmt, "%.8ux", *p++); + } + s = fmtstrflush(&fmt); + fmtstrcpy(fp, s); + free(s); + return 0; +} + static char* cnames[] = { [C_ADDR] = "C_ADDR", [C_BCON] = "C_BCON", [C_FAUTO] = "C_FAUTO", - [C_FCON] = "C_FCON", + [C_ZFCON] = "C_SFCON", + [C_SFCON] = "C_SFCON", + [C_LFCON] = "C_LFCON", [C_FCR] = "C_FCR", - [C_FEXT] = "C_FEXT", [C_FOREG] = "C_FOREG", [C_FREG] = "C_FREG", [C_GACON] = "C_GACON", @@ -391,9 +422,7 @@ cnames[] = [C_GOK] = "C_GOK", [C_GOREG] = "C_GOREG", [C_HAUTO] = "C_HAUTO", - [C_HEXT] = "C_HEXT", [C_HFAUTO] = "C_HFAUTO", - [C_HFEXT] = "C_HFEXT", [C_HFOREG] = "C_HFOREG", [C_HOREG] = "C_HOREG", [C_HREG] = "C_HREG", @@ -401,8 +430,6 @@ cnames[] = [C_LAUTO] = "C_LAUTO", [C_LBRA] = "C_LBRA", [C_LCON] = "C_LCON", - [C_LECON] = "C_LECON", - [C_LEXT] = "C_LEXT", [C_LOREG] = "C_LOREG", [C_NCON] = "C_NCON", [C_NONE] = "C_NONE", @@ -411,7 +438,6 @@ cnames[] = [C_PSR] = "C_PSR", [C_RACON] = "C_RACON", [C_RCON] = "C_RCON", - [C_RECON] = "C_RECON", [C_REG] = "C_REG", [C_REGREG] = "C_REGREG", [C_ROREG] = "C_ROREG", @@ -419,7 +445,6 @@ cnames[] = [C_SAUTO] = "C_SAUTO", [C_SBRA] = "C_SBRA", [C_SCON] = "C_SCON", - [C_SEXT] = "C_SEXT", [C_SHIFT] = "C_SHIFT", [C_SOREG] = "C_SOREG", [C_SP] = "C_SP", @@ -443,19 +468,22 @@ Oconv(Fmt *fp) void diag(char *fmt, ...) { - char buf[STRINGSZ], *tn; + char buf[STRINGSZ], *tn, *sep; va_list arg; - tn = "??none??"; - if(curtext != P && curtext->from.sym != S) - tn = curtext->from.sym->name; + tn = ""; + sep = ""; + if(cursym != S) { + tn = cursym->name; + sep = ": "; + } va_start(arg, fmt); vseprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); - print("%s: %s\n", tn, buf); + print("%s%s%s\n", tn, sep, buf); nerrors++; - if(nerrors > 10) { + if(nerrors > 20) { print("too many errors\n"); errorexit(); } diff --git a/src/cmd/5l/noop.c b/src/cmd/5l/noop.c index 41d235a09..5def0d3f1 100644 --- a/src/cmd/5l/noop.c +++ b/src/cmd/5l/noop.c @@ -28,12 +28,16 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Code transformations. + #include "l.h" +#include "../ld/lib.h" // see ../../runtime/proc.c:/StackGuard enum { StackBig = 4096, + StackSmall = 128, }; static Sym* sym_div; @@ -110,14 +114,12 @@ void noops(void) { Prog *p, *q, *q1, *q2; - int o, curframe, curbecome, maxbecome, foreign; + int o, foreign; Prog *pmorestack; Sym *symmorestack; /* * find leaf subroutines - * become sizes - * frame sizes * strip NOPs * expand RET * expand BECOME pseudo @@ -127,780 +129,662 @@ noops(void) Bprint(&bso, "%5.2f noops\n", cputime()); Bflush(&bso); - pmorestack = P; symmorestack = lookup("runtime.morestack", 0); - - if(symmorestack->type == STEXT) - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - if(p->from.sym == symmorestack) { - pmorestack = p; - p->reg |= NOSPLIT; - break; - } - } + if(symmorestack->type != STEXT) { + diag("runtime·morestack not defined"); + errorexit(); } - // TODO(kaib): make lack of morestack an error -// if(pmorestack == P) -// diag("runtime·morestack not defined"); - - curframe = 0; - curbecome = 0; - maxbecome = 0; - curtext = 0; + pmorestack = symmorestack->text; + pmorestack->reg |= NOSPLIT; q = P; - for(p = firstp; p != P; p = p->link) { - setarch(p); - - /* find out how much arg space is used in this TEXT */ - if(p->to.type == D_OREG && p->to.reg == REGSP) - if(p->to.offset > curframe) - curframe = p->to.offset; - - switch(p->as) { - case ATEXT: - if(curtext && curtext->from.sym) { - curtext->from.sym->frame = curframe; - curtext->from.sym->become = curbecome; - if(curbecome > maxbecome) - maxbecome = curbecome; - } - curframe = 0; - curbecome = 0; - - p->mark |= LEAF; - curtext = p; - break; - - case ARET: - /* special form of RET is BECOME */ - if(p->from.type == D_CONST) - if(p->from.offset > curbecome) - curbecome = p->from.offset; - break; - - case ADIV: - case ADIVU: - case AMOD: - case AMODU: - q = p; - if(prog_div == P) - initdiv(); - if(curtext != P) - curtext->mark &= ~LEAF; - setdiv(p->as); - continue; - - case ANOP: - q1 = p->link; - q->link = q1; /* q is non-nop */ - q1->mark |= p->mark; - continue; - - case ABL: - case ABX: - if(curtext != P) - curtext->mark &= ~LEAF; - - case ABCASE: - case AB: - - case ABEQ: - case ABNE: - case ABCS: - case ABHS: - case ABCC: - case ABLO: - case ABMI: - case ABPL: - case ABVS: - case ABVC: - case ABHI: - case ABLS: - case ABGE: - case ABLT: - case ABGT: - case ABLE: - - q1 = p->cond; - if(q1 != P) { - while(q1->as == ANOP) { - q1 = q1->link; - p->cond = q1; - } - } - break; - } - q = p; - } - - if(curtext && curtext->from.sym) { - curtext->from.sym->frame = curframe; - curtext->from.sym->become = curbecome; - if(curbecome > maxbecome) - maxbecome = curbecome; - } - - if(debug['b']) - print("max become = %d\n", maxbecome); - xdefine("ALEFbecome", STEXT, maxbecome); - - curtext = 0; - for(p = firstp; p != P; p = p->link) { - setarch(p); - switch(p->as) { - case ATEXT: - curtext = p; - break; - case ABL: - // case ABX: - if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) { - o = maxbecome - curtext->from.sym->frame; - if(o <= 0) - break; - /* calling a become or calling a variable */ - if(p->to.sym == S || p->to.sym->become) { - curtext->to.offset += o; - if(debug['b']) { - curp = p; - print("%D calling %D increase %d\n", - &curtext->from, &p->to, o); + for(cursym = textp; cursym != nil; cursym = cursym->next) { + for(p = cursym->text; p != P; p = p->link) { + setarch(p); + + switch(p->as) { + case ATEXT: + p->mark |= LEAF; + break; + + case ARET: + break; + + case ADIV: + case ADIVU: + case AMOD: + case AMODU: + q = p; + if(prog_div == P) + initdiv(); + cursym->text->mark &= ~LEAF; + setdiv(p->as); + continue; + + case ANOP: + q1 = p->link; + q->link = q1; /* q is non-nop */ + if(q1 != P) + q1->mark |= p->mark; + continue; + + case ABL: + case ABX: + cursym->text->mark &= ~LEAF; + + case ABCASE: + case AB: + + case ABEQ: + case ABNE: + case ABCS: + case ABHS: + case ABCC: + case ABLO: + case ABMI: + case ABPL: + case ABVS: + case ABVC: + case ABHI: + case ABLS: + case ABGE: + case ABLT: + case ABGT: + case ABLE: + q1 = p->cond; + if(q1 != P) { + while(q1->as == ANOP) { + q1 = q1->link; + p->cond = q1; } } + break; } - break; + q = p; } } - for(p = firstp; p != P; p = p->link) { - setarch(p); - o = p->as; - switch(o) { - case ATEXT: - curtext = p; - autosize = p->to.offset + 4; - if(autosize <= 4) - if(curtext->mark & LEAF) { - p->to.offset = -4; - autosize = 0; - } - - if(!autosize && !(curtext->mark & LEAF)) { - if(debug['v']) - Bprint(&bso, "save suppressed in: %s\n", - curtext->from.sym->name); - Bflush(&bso); - curtext->mark |= LEAF; - } + for(cursym = textp; cursym != nil; cursym = cursym->next) { + for(p = cursym->text; p != P; p = p->link) { + setarch(p); + o = p->as; + switch(o) { + case ATEXT: + autosize = p->to.offset + 4; + if(autosize <= 4) + if(cursym->text->mark & LEAF) { + p->to.offset = -4; + autosize = 0; + } + + if(!autosize && !(cursym->text->mark & LEAF)) { + if(debug['v']) + Bprint(&bso, "save suppressed in: %s\n", + cursym->name); + Bflush(&bso); + cursym->text->mark |= LEAF; + } #ifdef CALLEEBX - if(p->from.sym->foreign){ - if(thumb) - // don't allow literal pool to seperate these - p = adword(0xe28f7001, 0xe12fff17, p); // arm add 1, pc, r7 and bx r7 - // p = aword(0xe12fff17, aword(0xe28f7001, p)); // arm add 1, pc, r7 and bx r7 - else - p = aword(0x4778, p); // thumb bx pc and 2 bytes padding - } + if(p->from.sym->foreign){ + if(thumb) + // don't allow literal pool to seperate these + p = adword(0xe28f7001, 0xe12fff17, p); // arm add 1, pc, r7 and bx r7 + // p = aword(0xe12fff17, aword(0xe28f7001, p)); // arm add 1, pc, r7 and bx r7 + else + p = aword(0x4778, p); // thumb bx pc and 2 bytes padding + } #endif - if(curtext->mark & LEAF) { - if(curtext->from.sym) - curtext->from.sym->type = SLEAF; - if(!autosize) + if(cursym->text->mark & LEAF) { + cursym->leaf = 1; + if(!autosize) + break; + } + + if(thumb){ + if(!(p->reg & NOSPLIT)) + diag("stack splitting not supported in thumb"); + if(!(cursym->text->mark & LEAF)){ + q = movrr(nil, REGLINK, REGTMPT-1, p); + p->link = q; + q1 = prg(); + q1->as = AMOVW; + q1->line = p->line; + q1->from.type = D_REG; + q1->from.reg = REGTMPT-1; + q1->to.type = D_OREG; + q1->to.name = D_NONE; + q1->to.reg = REGSP; + q1->to.offset = 0; + q1->link = q->link; + q->link = q1; + } + if(autosize){ + q2 = prg(); + q2->as = ASUB; + q2->line = p->line; + q2->from.type = D_CONST; + q2->from.offset = autosize; + q2->to.type = D_REG; + q2->to.reg = REGSP; + q2->link = p->link; + p->link = q2; + } break; - } - - if(thumb){ - if(!(p->reg & NOSPLIT)) - diag("stack splitting not supported in thumb"); - if(!(curtext->mark & LEAF)){ - q = movrr(nil, REGLINK, REGTMPT-1, p); - p->link = q; + } + + if(p->reg & NOSPLIT) { q1 = prg(); q1->as = AMOVW; + q1->scond |= C_WBIT; q1->line = p->line; q1->from.type = D_REG; - q1->from.reg = REGTMPT-1; + q1->from.reg = REGLINK; q1->to.type = D_OREG; - q1->to.name = D_NONE; + q1->to.offset = -autosize; q1->to.reg = REGSP; - q1->to.offset = 0; - q1->link = q->link; - q->link = q1; - } - if(autosize){ - q2 = prg(); - q2->as = ASUB; - q2->line = p->line; - q2->from.type = D_CONST; - q2->from.offset = autosize; - q2->to.type = D_REG; - q2->to.reg = REGSP; - q2->link = p->link; - p->link = q2; + q1->link = p->link; + p->link = q1; + } else if (autosize < StackBig) { + // split stack check for small functions + // MOVW g_stackguard(g), R1 + // CMP R1, $-autosize(SP) + // MOVW.LO $autosize, R1 + // MOVW.LO $args, R2 + // MOVW.LO R14, R3 + // BL.LO runtime.morestack(SB) // modifies LR + // MOVW.W R14,$-autosize(SP) + + // TODO(kaib): add more trampolines + // TODO(kaib): put stackguard in register + // TODO(kaib): add support for -K and underflow detection + + // MOVW g_stackguard(g), R1 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_OREG; + p->from.reg = REGG; + p->to.type = D_REG; + p->to.reg = 1; + + if(autosize < StackSmall) { + // CMP R1, SP + p = appendp(p); + p->as = ACMP; + p->from.type = D_REG; + p->from.reg = 1; + p->reg = REGSP; + } else { + // MOVW $-autosize(SP), R2 + // CMP R1, R2 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.reg = REGSP; + p->from.offset = -autosize; + p->to.type = D_REG; + p->to.reg = 2; + + p = appendp(p); + p->as = ACMP; + p->from.type = D_REG; + p->from.reg = 1; + p->reg = 2; + } + + // MOVW.LO $autosize, R1 + p = appendp(p); + p->as = AMOVW; + p->scond = C_SCOND_LO; + p->from.type = D_CONST; + p->from.offset = 0; + p->to.type = D_REG; + p->to.reg = 1; + + // MOVW.LO $args +4, R2 + // also need to store the extra 4 bytes. + p = appendp(p); + p->as = AMOVW; + p->scond = C_SCOND_LO; + p->from.type = D_CONST; + p->from.offset = ((cursym->text->to.offset2 + 3) & ~3) + 4; + p->to.type = D_REG; + p->to.reg = 2; + + // MOVW.LO R14, R3 + p = appendp(p); + p->as = AMOVW; + p->scond = C_SCOND_LO; + p->from.type = D_REG; + p->from.reg = REGLINK; + p->to.type = D_REG; + p->to.reg = 3; + + // BL.LO runtime.morestack(SB) // modifies LR + p = appendp(p); + p->as = ABL; + p->scond = C_SCOND_LO; + p->to.type = D_BRANCH; + p->to.sym = symmorestack; + p->cond = pmorestack; + + // MOVW.W R14,$-autosize(SP) + p = appendp(p); + p->as = AMOVW; + p->scond |= C_WBIT; + p->from.type = D_REG; + p->from.reg = REGLINK; + p->to.type = D_OREG; + p->to.offset = -autosize; + p->to.reg = REGSP; + } else { // > StackBig + // MOVW $autosize, R1 + // MOVW $args, R2 + // MOVW R14, R3 + // BL runtime.morestack(SB) // modifies LR + // MOVW.W R14,$-autosize(SP) + + // MOVW $autosize, R1 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.offset = autosize; + p->to.type = D_REG; + p->to.reg = 1; + + // MOVW $args +4, R2 + // also need to store the extra 4 bytes. + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.offset = ((cursym->text->to.offset2 + 3) & ~3) + 4; + p->to.type = D_REG; + p->to.reg = 2; + + // MOVW R14, R3 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_REG; + p->from.reg = REGLINK; + p->to.type = D_REG; + p->to.reg = 3; + + // BL runtime.morestack(SB) // modifies LR + p = appendp(p); + p->as = ABL; + p->to.type = D_BRANCH; + p->to.sym = symmorestack; + p->cond = pmorestack; + + // MOVW.W R14,$-autosize(SP) + p = appendp(p); + p->as = AMOVW; + p->scond |= C_WBIT; + p->from.type = D_REG; + p->from.reg = REGLINK; + p->to.type = D_OREG; + p->to.offset = -autosize; + p->to.reg = REGSP; } break; - } - - if(p->reg & NOSPLIT) { - q1 = prg(); - q1->as = AMOVW; - q1->scond |= C_WBIT; - q1->line = p->line; - q1->from.type = D_REG; - q1->from.reg = REGLINK; - q1->to.type = D_OREG; - q1->to.offset = -autosize; - q1->to.reg = REGSP; - q1->link = p->link; - p->link = q1; - } else if (autosize < StackBig) { - // split stack check for small functions - // MOVW g_stackguard(g), R1 - // CMP R1, $-autosize(SP) - // MOVW.LO $autosize, R1 - // MOVW.LO $args, R2 - // MOVW.LO R14, R3 - // BL.LO runtime.morestack(SB) // modifies LR - // MOVW.W R14,$-autosize(SP) - - // TODO(kaib): add more trampolines - // TODO(kaib): put stackguard in register - // TODO(kaib): add support for -K and underflow detection - - // MOVW g_stackguard(g), R1 - p = appendp(p); - p->as = AMOVW; - p->from.type = D_OREG; - p->from.reg = REGG; - p->to.type = D_REG; - p->to.reg = 1; - - // CMP R1, $-autosize(SP) - p = appendp(p); - p->as = ACMP; - p->from.type = D_REG; - p->from.reg = 1; - p->from.offset = -autosize; - p->reg = REGSP; - - // MOVW.LO $autosize, R1 - p = appendp(p); - p->as = AMOVW; - p->scond = C_SCOND_LO; - p->from.type = D_CONST; - p->from.offset = 0; - p->to.type = D_REG; - p->to.reg = 1; - - // MOVW.LO $args +4, R2 - // also need to store the extra 4 bytes. - p = appendp(p); - p->as = AMOVW; - p->scond = C_SCOND_LO; - p->from.type = D_CONST; - p->from.offset = ((curtext->to.offset2 + 3) & ~3) + 4; - p->to.type = D_REG; - p->to.reg = 2; - - // MOVW.LO R14, R3 - p = appendp(p); - p->as = AMOVW; - p->scond = C_SCOND_LO; - p->from.type = D_REG; - p->from.reg = REGLINK; - p->to.type = D_REG; - p->to.reg = 3; - - // BL.LO runtime.morestack(SB) // modifies LR - p = appendp(p); - p->as = ABL; - p->scond = C_SCOND_LO; - p->to.type = D_BRANCH; - p->to.sym = symmorestack; - p->cond = pmorestack; - - // MOVW.W R14,$-autosize(SP) - p = appendp(p); - p->as = AMOVW; - p->scond |= C_WBIT; - p->from.type = D_REG; - p->from.reg = REGLINK; - p->to.type = D_OREG; - p->to.offset = -autosize; - p->to.reg = REGSP; - } else { // > StackBig - // MOVW $autosize, R1 - // MOVW $args, R2 - // MOVW R14, R3 - // BL runtime.morestack(SB) // modifies LR - // MOVW.W R14,$-autosize(SP) - - // MOVW $autosize, R1 - p = appendp(p); - p->as = AMOVW; - p->from.type = D_CONST; - p->from.offset = autosize; - p->to.type = D_REG; - p->to.reg = 1; - - // MOVW $args +4, R2 - // also need to store the extra 4 bytes. - p = appendp(p); - p->as = AMOVW; - p->from.type = D_CONST; - p->from.offset = ((curtext->to.offset2 + 3) & ~3) + 4; - p->to.type = D_REG; - p->to.reg = 2; - - // MOVW R14, R3 - p = appendp(p); - p->as = AMOVW; - p->from.type = D_REG; - p->from.reg = REGLINK; - p->to.type = D_REG; - p->to.reg = 3; - - // BL runtime.morestack(SB) // modifies LR - p = appendp(p); - p->as = ABL; - p->to.type = D_BRANCH; - p->to.sym = symmorestack; - p->cond = pmorestack; - - // MOVW.W R14,$-autosize(SP) - p = appendp(p); - p->as = AMOVW; - p->scond |= C_WBIT; - p->from.type = D_REG; - p->from.reg = REGLINK; - p->to.type = D_OREG; - p->to.offset = -autosize; - p->to.reg = REGSP; - } - break; - - case ARET: - nocache(p); - foreign = seenthumb && curtext->from.sym != S && (curtext->from.sym->foreign || curtext->from.sym->fnptr); -// print("%s %d %d\n", curtext->from.sym->name, curtext->from.sym->foreign, curtext->from.sym->fnptr); - if(p->from.type == D_CONST) - goto become; - if(curtext->mark & LEAF) { - if(!autosize) { - if(thumb){ - p = fnret(p, REGLINK, foreign, p); + + case ARET: + nocache(p); + foreign = seenthumb && (cursym->foreign || cursym->fnptr); +// print("%s %d %d\n", cursym->name, cursym->foreign, cursym->fnptr); + if(cursym->text->mark & LEAF) { + if(!autosize) { + if(thumb){ + p = fnret(p, REGLINK, foreign, p); + break; + } +// if(foreign) print("ABXRET 1 %s\n", cursym->name); + p->as = foreign ? ABXRET : AB; + p->from = zprg.from; + p->to.type = D_OREG; + p->to.offset = 0; + p->to.reg = REGLINK; break; } -// if(foreign) print("ABXRET 1 %s\n", curtext->from.sym->name); - p->as = foreign ? ABXRET : AB; - p->from = zprg.from; - p->to.type = D_OREG; - p->to.offset = 0; - p->to.reg = REGLINK; - break; } - } - if(thumb){ - if(curtext->mark & LEAF){ - if(autosize){ - p->as = AADD; - p->from.type = D_CONST; - p->from.offset = autosize; + if(thumb){ + if(cursym->text->mark & LEAF){ + if(autosize){ + p->as = AADD; + p->from.type = D_CONST; + p->from.offset = autosize; + p->to.type = D_REG; + p->to.reg = REGSP; + q = nil; + } + else + q = p; + q = fnret(q, REGLINK, foreign, p); + if(q != p) + p->link = q; + } + else{ + p->as = AMOVW; + p->from.type = D_OREG; + p->from.name = D_NONE; + p->from.reg = REGSP; + p->from.offset = 0; p->to.type = D_REG; - p->to.reg = REGSP; - q = nil; + p->to.reg = REGTMPT-1; + if(autosize){ + q = prg(); + q->as = AADD; + q->from.type = D_CONST; + q->from.offset = autosize; + q->to.type = D_REG; + q->to.reg = REGSP; + q->link = p->link; + p->link = q; + } + else + q = p; + q1 = fnret(nil, REGTMPT-1, foreign, p); + q1->link = q->link; + q->link = q1; } - else - q = p; - q = fnret(q, REGLINK, foreign, p); - if(q != p) - p->link = q; + break; } - else{ + if(foreign) { +// if(foreign) print("ABXRET 3 %s\n", cursym->name); +#define R 1 p->as = AMOVW; p->from.type = D_OREG; p->from.name = D_NONE; p->from.reg = REGSP; p->from.offset = 0; p->to.type = D_REG; - p->to.reg = REGTMPT-1; - if(autosize){ - q = prg(); - q->as = AADD; - q->from.type = D_CONST; - q->from.offset = autosize; - q->to.type = D_REG; - q->to.reg = REGSP; - q->link = p->link; - p->link = q; - } - else - q = p; - q1 = fnret(nil, REGTMPT-1, foreign, p); + p->to.reg = R; + q = prg(); + q->as = AADD; + q->scond = p->scond; + q->line = p->line; + q->from.type = D_CONST; + q->from.offset = autosize; + q->to.type = D_REG; + q->to.reg = REGSP; + q->link = p->link; + p->link = q; + q1 = prg(); + q1->as = ABXRET; + q1->scond = p->scond; + q1->line = p->line; + q1->to.type = D_OREG; + q1->to.offset = 0; + q1->to.reg = R; q1->link = q->link; q->link = q1; +#undef R + } + else { + p->as = AMOVW; + p->scond |= C_PBIT; + p->from.type = D_OREG; + p->from.offset = autosize; + p->from.reg = REGSP; + p->to.type = D_REG; + p->to.reg = REGPC; } break; - } - if(foreign) { -// if(foreign) print("ABXRET 3 %s\n", curtext->from.sym->name); -#define R 1 + + case ADIV: + case ADIVU: + case AMOD: + case AMODU: + if(debug['M']) + break; + if(p->from.type != D_REG) + break; + if(p->to.type != D_REG) + break; + q1 = p; + + /* MOV a,4(SP) */ + q = prg(); + q->link = p->link; + p->link = q; + p = q; + p->as = AMOVW; - p->from.type = D_OREG; - p->from.name = D_NONE; - p->from.reg = REGSP; - p->from.offset = 0; - p->to.type = D_REG; - p->to.reg = R; + p->line = q1->line; + p->from.type = D_REG; + p->from.reg = q1->from.reg; + p->to.type = D_OREG; + p->to.reg = REGSP; + p->to.offset = 4; + + /* MOV b,REGTMP */ q = prg(); - q->as = AADD; - q->scond = p->scond; - q->line = p->line; - q->from.type = D_CONST; - q->from.offset = autosize; - q->to.type = D_REG; - q->to.reg = REGSP; q->link = p->link; p->link = q; - q1 = prg(); - q1->as = ABXRET; - q1->scond = p->scond; - q1->line = p->line; - q1->to.type = D_OREG; - q1->to.offset = 0; - q1->to.reg = R; - q1->link = q->link; - q->link = q1; -#undef R - } - else { + p = q; + p->as = AMOVW; - p->scond |= C_PBIT; - p->from.type = D_OREG; - p->from.offset = autosize; - p->from.reg = REGSP; + p->line = q1->line; + p->from.type = D_REG; + p->from.reg = q1->reg; + if(q1->reg == NREG) + p->from.reg = q1->to.reg; p->to.type = D_REG; - p->to.reg = REGPC; - } - break; - - become: - if(foreign){ - diag("foreign become - help"); - break; - } - if(thumb){ - diag("thumb become - help"); - break; - } - print("arm become\n"); - if(curtext->mark & LEAF) { - - if(!autosize) { - p->as = AB; - p->from = zprg.from; + p->to.reg = prog_div->from.sym->thumb ? REGTMPT : REGTMP; + p->to.offset = 0; + + /* CALL appropriate */ + q = prg(); + q->link = p->link; + p->link = q; + p = q; + +#ifdef CALLEEBX + p->as = ABL; +#else + if(prog_div->from.sym->thumb) + p->as = thumb ? ABL : ABX; + else + p->as = thumb ? ABX : ABL; +#endif + p->line = q1->line; + p->to.type = D_BRANCH; + p->cond = p; + switch(o) { + case ADIV: + p->cond = prog_div; + p->to.sym = sym_div; + break; + case ADIVU: + p->cond = prog_divu; + p->to.sym = sym_divu; + break; + case AMOD: + p->cond = prog_mod; + p->to.sym = sym_mod; + break; + case AMODU: + p->cond = prog_modu; + p->to.sym = sym_modu; break; } - } - q = prg(); - q->scond = p->scond; - q->line = p->line; - q->as = AB; - q->from = zprg.from; - q->to = p->to; - q->cond = p->cond; - q->link = p->link; - p->link = q; - if(thumb){ - q1 = prg(); - q1->line = p->line; - q1->as = AADD; - q1->from.type = D_CONST; - q1->from.offset = autosize; - q1->to.type = D_REG; - q1->to.reg = REGSP; + + /* MOV REGTMP, b */ + q = prg(); + q->link = p->link; + p->link = q; + p = q; + p->as = AMOVW; - p->line = p->line; - p->from.type = D_OREG; - p->from.name = D_NONE; - p->from.reg = REGSP; + p->line = q1->line; + p->from.type = D_REG; + p->from.reg = prog_div->from.sym->thumb ? REGTMPT : REGTMP; p->from.offset = 0; p->to.type = D_REG; - p->to.reg = REGTMPT-1; - q1->link = q; - p->link = q1; - q2 = movrr(nil, REGTMPT-1, REGLINK, p); - q2->link = q; - q1->link = q2; - break; - } - p->as = AMOVW; - p->scond |= C_PBIT; - p->from = zprg.from; - p->from.type = D_OREG; - p->from.offset = autosize; - p->from.reg = REGSP; - p->to = zprg.to; - p->to.type = D_REG; - p->to.reg = REGLINK; - - break; - - case ADIV: - case ADIVU: - case AMOD: - case AMODU: - if(debug['M']) - break; - if(p->from.type != D_REG) - break; - if(p->to.type != D_REG) - break; - q1 = p; - - /* MOV a,4(SP) */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - - p->as = AMOVW; - p->line = q1->line; - p->from.type = D_REG; - p->from.reg = q1->from.reg; - p->to.type = D_OREG; - p->to.reg = REGSP; - p->to.offset = 4; - - /* MOV b,REGTMP */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - - p->as = AMOVW; - p->line = q1->line; - p->from.type = D_REG; - p->from.reg = q1->reg; - if(q1->reg == NREG) - p->from.reg = q1->to.reg; - p->to.type = D_REG; - p->to.reg = prog_div != UP && prog_div->from.sym->thumb ? REGTMPT : REGTMP; - p->to.offset = 0; - - /* CALL appropriate */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - -#ifdef CALLEEBX - p->as = ABL; -#else - if(prog_div != UP && prog_div->from.sym->thumb) - p->as = thumb ? ABL : ABX; - else - p->as = thumb ? ABX : ABL; -#endif - p->line = q1->line; - p->to.type = D_BRANCH; - p->cond = p; - switch(o) { - case ADIV: - p->cond = prog_div; - p->to.sym = sym_div; - break; - case ADIVU: - p->cond = prog_divu; - p->to.sym = sym_divu; - break; - case AMOD: - p->cond = prog_mod; - p->to.sym = sym_mod; + p->to.reg = q1->to.reg; + + /* ADD $8,SP */ + q = prg(); + q->link = p->link; + p->link = q; + p = q; + + p->as = AADD; + p->from.type = D_CONST; + p->from.reg = NREG; + p->from.offset = 8; + p->reg = NREG; + p->to.type = D_REG; + p->to.reg = REGSP; + + /* SUB $8,SP */ + q1->as = ASUB; + q1->from.type = D_CONST; + q1->from.offset = 8; + q1->from.reg = NREG; + q1->reg = NREG; + q1->to.type = D_REG; + q1->to.reg = REGSP; + break; - case AMODU: - p->cond = prog_modu; - p->to.sym = sym_modu; + case AMOVW: + if(thumb){ + Adr *a = &p->from; + + if(a->type == D_CONST && ((a->name == D_NONE && a->reg == REGSP) || a->name == D_AUTO || a->name == D_PARAM) && (a->offset & 3)) + diag("SP offset not multiple of 4"); + } break; - } - - /* MOV REGTMP, b */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - - p->as = AMOVW; - p->line = q1->line; - p->from.type = D_REG; - p->from.reg = prog_div != UP && prog_div->from.sym->thumb ? REGTMPT : REGTMP; - p->from.offset = 0; - p->to.type = D_REG; - p->to.reg = q1->to.reg; - - /* ADD $8,SP */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - - p->as = AADD; - p->from.type = D_CONST; - p->from.reg = NREG; - p->from.offset = 8; - p->reg = NREG; - p->to.type = D_REG; - p->to.reg = REGSP; - - /* SUB $8,SP */ - q1->as = ASUB; - q1->from.type = D_CONST; - q1->from.offset = 8; - q1->from.reg = NREG; - q1->reg = NREG; - q1->to.type = D_REG; - q1->to.reg = REGSP; - - break; - case AMOVW: - if(thumb){ - Adr *a = &p->from; - - if(a->type == D_CONST && ((a->name == D_NONE && a->reg == REGSP) || a->name == D_AUTO || a->name == D_PARAM) && (a->offset & 3)) - diag("SP offset not multiple of 4"); - } - break; - case AMOVB: - case AMOVBU: - case AMOVH: - case AMOVHU: - if(thumb){ - if(p->from.type == D_OREG && (p->from.name == D_AUTO || p->from.name == D_PARAM || (p->from.name == D_CONST && p->from.reg == REGSP))){ - q = prg(); - *q = *p; - if(p->from.name == D_AUTO) - q->from.offset += autosize; - else if(p->from.name == D_PARAM) - q->from.offset += autosize+4; - q->from.name = D_NONE; - q->from.reg = REGTMPT; - p = movrr(p, REGSP, REGTMPT, p); - q->link = p->link; - p->link = q; + case AMOVB: + case AMOVBU: + case AMOVH: + case AMOVHU: + if(thumb){ + if(p->from.type == D_OREG && (p->from.name == D_AUTO || p->from.name == D_PARAM || (p->from.name == D_CONST && p->from.reg == REGSP))){ + q = prg(); + *q = *p; + if(p->from.name == D_AUTO) + q->from.offset += autosize; + else if(p->from.name == D_PARAM) + q->from.offset += autosize+4; + q->from.name = D_NONE; + q->from.reg = REGTMPT; + p = movrr(p, REGSP, REGTMPT, p); + q->link = p->link; + p->link = q; + } + if(p->to.type == D_OREG && (p->to.name == D_AUTO || p->to.name == D_PARAM || (p->to.name == D_CONST && p->to.reg == REGSP))){ + q = prg(); + *q = *p; + if(p->to.name == D_AUTO) + q->to.offset += autosize; + else if(p->to.name == D_PARAM) + q->to.offset += autosize+4; + q->to.name = D_NONE; + q->to.reg = REGTMPT; + p = movrr(p, REGSP, REGTMPT, p); + q->link = p->link; + p->link = q; + if(q->to.offset < 0 || q->to.offset > 255){ // complicated + p->to.reg = REGTMPT+1; // mov sp, r8 + q1 = prg(); + q1->line = p->line; + q1->as = AMOVW; + q1->from.type = D_CONST; + q1->from.offset = q->to.offset; + q1->to.type = D_REG; + q1->to.reg = REGTMPT; // mov $o, r7 + p->link = q1; + q1->link = q; + q1 = prg(); + q1->line = p->line; + q1->as = AADD; + q1->from.type = D_REG; + q1->from.reg = REGTMPT+1; + q1->to.type = D_REG; + q1->to.reg = REGTMPT; // add r8, r7 + p->link->link = q1; + q1->link = q; + q->to.offset = 0; // mov* r, 0(r7) + /* phew */ + } + } } - if(p->to.type == D_OREG && (p->to.name == D_AUTO || p->to.name == D_PARAM || (p->to.name == D_CONST && p->to.reg == REGSP))){ - q = prg(); - *q = *p; - if(p->to.name == D_AUTO) - q->to.offset += autosize; - else if(p->to.name == D_PARAM) - q->to.offset += autosize+4; - q->to.name = D_NONE; - q->to.reg = REGTMPT; - p = movrr(p, REGSP, REGTMPT, p); - q->link = p->link; - p->link = q; - if(q->to.offset < 0 || q->to.offset > 255){ // complicated - p->to.reg = REGTMPT+1; // mov sp, r8 - q1 = prg(); - q1->line = p->line; - q1->as = AMOVW; - q1->from.type = D_CONST; - q1->from.offset = q->to.offset; - q1->to.type = D_REG; - q1->to.reg = REGTMPT; // mov $o, r7 - p->link = q1; - q1->link = q; - q1 = prg(); - q1->line = p->line; - q1->as = AADD; - q1->from.type = D_REG; - q1->from.reg = REGTMPT+1; - q1->to.type = D_REG; - q1->to.reg = REGTMPT; // add r8, r7 - p->link->link = q1; - q1->link = q; - q->to.offset = 0; // mov* r, 0(r7) - /* phew */ + break; + case AMOVM: + if(thumb){ + if(p->from.type == D_OREG){ + if(p->from.offset == 0) + p->from.type = D_REG; + else + diag("non-zero AMOVM offset"); + } + else if(p->to.type == D_OREG){ + if(p->to.offset == 0) + p->to.type = D_REG; + else + diag("non-zero AMOVM offset"); } } - } - break; - case AMOVM: - if(thumb){ - if(p->from.type == D_OREG){ - if(p->from.offset == 0) + break; + case AB: + if(thumb && p->to.type == D_OREG){ + if(p->to.offset == 0){ + p->as = AMOVW; p->from.type = D_REG; - else - diag("non-zero AMOVM offset"); - } - else if(p->to.type == D_OREG){ - if(p->to.offset == 0) + p->from.reg = p->to.reg; p->to.type = D_REG; - else - diag("non-zero AMOVM offset"); - } - } - break; - case AB: - if(thumb && p->to.type == D_OREG){ - if(p->to.offset == 0){ - p->as = AMOVW; - p->from.type = D_REG; - p->from.reg = p->to.reg; - p->to.type = D_REG; - p->to.reg = REGPC; - } - else{ - p->as = AADD; - p->from.type = D_CONST; - p->from.offset = p->to.offset; - p->reg = p->to.reg; - p->to.type = D_REG; - p->to.reg = REGTMPT-1; - q = prg(); - q->as = AMOVW; - q->line = p->line; - q->from.type = D_REG; - q->from.reg = REGTMPT-1; - q->to.type = D_REG; - q->to.reg = REGPC; - q->link = p->link; - p->link = q; + p->to.reg = REGPC; + } + else{ + p->as = AADD; + p->from.type = D_CONST; + p->from.offset = p->to.offset; + p->reg = p->to.reg; + p->to.type = D_REG; + p->to.reg = REGTMPT-1; + q = prg(); + q->as = AMOVW; + q->line = p->line; + q->from.type = D_REG; + q->from.reg = REGTMPT-1; + q->to.type = D_REG; + q->to.reg = REGPC; + q->link = p->link; + p->link = q; + } } - } - if(seenthumb && !thumb && p->to.type == D_OREG && p->to.reg == REGLINK){ - // print("warn %s: b (R%d) assuming a return\n", curtext->from.sym->name, p->to.reg); - p->as = ABXRET; - } - break; - case ABL: - case ABX: - if(thumb && p->to.type == D_OREG){ - if(p->to.offset == 0){ - p->as = o; - p->from.type = D_NONE; - p->to.type = D_REG; + if(seenthumb && !thumb && p->to.type == D_OREG && p->to.reg == REGLINK){ + // print("warn %s: b (R%d) assuming a return\n", cursym->name, p->to.reg); + p->as = ABXRET; } - else{ - p->as = AADD; - p->from.type = D_CONST; - p->from.offset = p->to.offset; - p->reg = p->to.reg; - p->to.type = D_REG; - p->to.reg = REGTMPT-1; - q = prg(); - q->as = o; - q->line = p->line; - q->from.type = D_NONE; - q->to.type = D_REG; - q->to.reg = REGTMPT-1; - q->link = p->link; - p->link = q; + break; + case ABL: + case ABX: + if(thumb && p->to.type == D_OREG){ + if(p->to.offset == 0){ + p->as = o; + p->from.type = D_NONE; + p->to.type = D_REG; + } + else{ + p->as = AADD; + p->from.type = D_CONST; + p->from.offset = p->to.offset; + p->reg = p->to.reg; + p->to.type = D_REG; + p->to.reg = REGTMPT-1; + q = prg(); + q->as = o; + q->line = p->line; + q->from.type = D_NONE; + q->to.type = D_REG; + q->to.reg = REGTMPT-1; + q->link = p->link; + p->link = q; + } } + break; } - break; } } } @@ -911,12 +795,9 @@ sigdiv(char *n) Sym *s; s = lookup(n, 0); - if(s->type == STEXT){ + if(s->type == STEXT) if(s->sig == 0) s->sig = SIGNINTERN; - } - else if(s->type == 0 || s->type == SXREF) - s->type = SUNDEF; } void @@ -928,25 +809,10 @@ divsig(void) sigdiv("_modu"); } -static void -sdiv(Sym *s) -{ - if(s->type == 0 || s->type == SXREF){ - /* undefsym(s); */ - s->type = SXREF; - if(s->sig == 0) - s->sig = SIGNINTERN; - s->subtype = SIMPORT; - } - else if(s->type != STEXT) - diag("undefined: %s", s->name); -} - void initdiv(void) { Sym *s2, *s3, *s4, *s5; - Prog *p; if(prog_div != P) return; @@ -954,38 +820,25 @@ initdiv(void) sym_divu = s3 = lookup("_divu", 0); sym_mod = s4 = lookup("_mod", 0); sym_modu = s5 = lookup("_modu", 0); - if(dlm) { - sdiv(s2); if(s2->type == SXREF) prog_div = UP; - sdiv(s3); if(s3->type == SXREF) prog_divu = UP; - sdiv(s4); if(s4->type == SXREF) prog_mod = UP; - sdiv(s5); if(s5->type == SXREF) prog_modu = UP; - } - for(p = firstp; p != P; p = p->link) - if(p->as == ATEXT) { - if(p->from.sym == s2) - prog_div = p; - if(p->from.sym == s3) - prog_divu = p; - if(p->from.sym == s4) - prog_mod = p; - if(p->from.sym == s5) - prog_modu = p; - } + prog_div = s2->text; + prog_divu = s3->text; + prog_mod = s4->text; + prog_modu = s5->text; if(prog_div == P) { diag("undefined: %s", s2->name); - prog_div = curtext; + prog_div = cursym->text; } if(prog_divu == P) { diag("undefined: %s", s3->name); - prog_divu = curtext; + prog_divu = cursym->text; } if(prog_mod == P) { diag("undefined: %s", s4->name); - prog_mod = curtext; + prog_mod = cursym->text; } if(prog_modu == P) { diag("undefined: %s", s5->name); - prog_modu = curtext; + prog_modu = cursym->text; } } @@ -1000,7 +853,7 @@ setdiv(int as) case AMOD: p = prog_mod; break; case AMODU: p = prog_modu; break; } - if(p != UP && thumb != p->from.sym->thumb) + if(thumb != p->from.sym->thumb) p->from.sym->foreign = 1; } diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c index e3597e040..cb9ad9805 100644 --- a/src/cmd/5l/obj.c +++ b/src/cmd/5l/obj.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Reading object files. + #define EXTERN #include "l.h" #include "../ld/lib.h" @@ -50,28 +52,6 @@ char *thestring = "arm"; * -H5 -T0xC0008010 -R1024 is ipaq */ -static int -isobjfile(char *f) -{ - int n, v; - Biobuf *b; - char buf1[5], buf2[SARMAG]; - - b = Bopen(f, OREAD); - if(b == nil) - return 0; - n = Bread(b, buf1, 5); - if(n == 5 && (buf1[2] == 1 && buf1[3] == '<' || buf1[3] == 1 && buf1[4] == '<')) - v = 1; /* good enough for our purposes */ - else{ - Bseek(b, 0, 0); - n = Bread(b, buf2, SARMAG); - v = n == SARMAG && strncmp(buf2, ARMAG, SARMAG) == 0; - } - Bterm(b); - return v; -} - static char* linkername[] = { @@ -94,7 +74,6 @@ main(int argc, char *argv[]) cout = -1; listinit(); nerrors = 0; - curtext = P; outfile = "5.out"; HEADTYPE = -1; INITTEXT = -1; @@ -231,10 +210,10 @@ main(int argc, char *argv[]) break; } if(INITDAT != 0 && INITRND != 0) - print("warning: -D0x%lux is ignored because of -R0x%lux\n", + print("warning: -D0x%ux is ignored because of -R0x%ux\n", INITDAT, INITRND); if(debug['v']) - Bprint(&bso, "HEADER = -H0x%d -T0x%lux -D0x%lux -R0x%lux\n", + Bprint(&bso, "HEADER = -H0x%d -T0x%ux -D0x%ux -R0x%ux\n", HEADTYPE, INITTEXT, INITDAT, INITRND); Bflush(&bso); zprg.as = AGOK; @@ -247,9 +226,6 @@ main(int argc, char *argv[]) buildop(); thumbbuildop(); // could build on demand histgen = 0; - textp = P; - datap = P; - edatap = P; pc = 0; dtype = 4; nuxiinit(); @@ -257,8 +233,6 @@ main(int argc, char *argv[]) version = 0; cbp = buf.cbuf; cbc = sizeof(buf.cbuf); - firstp = prg(); - lastp = firstp; addlibpath("command line", "command line", argv[0], "main"); loadlib(); @@ -268,52 +242,30 @@ main(int argc, char *argv[]) for(i=0; ilink; - if(firstp == P) - goto out; - if(doexp || dlm){ - EXPTAB = "_exporttab"; - zerosig(EXPTAB); - zerosig("etext"); - zerosig("edata"); - zerosig("end"); - if(dlm){ - initdiv(); - import(); - HEADTYPE = 2; - INITTEXT = INITDAT = 0; - INITRND = 8; - INITENTRY = EXPTAB; - } - else - divsig(); - export(); + if(textp == nil) { + diag("no code"); + errorexit(); } + patch(); if(debug['p']) if(debug['1']) doprof1(); else doprof2(); - if(debug['u']) - reachable(); doelf(); - dodata(); - if(seenthumb && debug['f']) - fnptrs(); follow(); - if(firstp == P) { - diag("no code"); - errorexit(); - } softfloat(); noops(); span(); + pclntab(); + symtab(); + dodata(); + address(); + reloc(); asmb(); undef(); -out: if(debug['c']){ thumbcount(); print("ARM size = %d\n", armsize); @@ -327,7 +279,7 @@ out: errorexit(); } -void +static void zaddr(Biobuf *f, Adr *a, Sym *h[]) { int i, c; @@ -355,7 +307,7 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) if(a->type == D_CONST || a->type == D_OCONST) { if(a->name == D_EXTERN || a->name == D_STATIC) { s = a->sym; - if(s != S && (s->type == STEXT || s->type == SLEAF || s->type == SCONST || s->type == SXREF)) { + if(s != S && (s->type == STEXT || s->type == SCONST || s->type == SXREF)) { if(0 && !s->fnptr && s->name[0] != '.') print("%s used as function pointer\n", s->name); s->fnptr = 1; // over the top cos of SXREF @@ -398,9 +350,8 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) break; case D_FCONST: - a->ieee = mal(sizeof(Ieee)); - a->ieee->l = Bget4(f); - a->ieee->h = Bget4(f); + a->ieee.l = Bget4(f); + a->ieee.h = Bget4(f); break; } s = a->sym; @@ -441,7 +392,7 @@ void ldobj1(Biobuf *f, char *pkg, int64 len, char *pn) { int32 ipc; - Prog *p, *t; + Prog *p; Sym *h[NSYM], *s, *di; int v, o, r, skip; uint32 sig; @@ -449,7 +400,9 @@ ldobj1(Biobuf *f, char *pkg, int64 len, char *pn) int ntext; int32 eof; char src[1024], *x; + Prog *lastp; + lastp = nil; ntext = 0; eof = Boffset(f) + len; di = S; @@ -499,13 +452,17 @@ loop: if(sig != 0){ if(s->sig != 0 && s->sig != sig) - diag("incompatible type signatures %lux(%s) and %lux(%s) for %s", s->sig, s->file, sig, pn, s->name); + diag("incompatible type signatures %ux(%s) and %ux(%s) for %s", s->sig, s->file, sig, pn, s->name); s->sig = sig; s->file = pn; } if(debug['W']) print(" ANAME %s\n", s->name); + if(o < 0 || o >= nelem(h)) { + fprint(2, "%s: mangled input file\n", pn); + errorexit(); + } h[o] = s; if((v == D_EXTERN || v == D_STATIC) && s->type == 0) s->type = SXREF; @@ -533,8 +490,8 @@ loop: zaddr(f, &p->from, h); zaddr(f, &p->to, h); - if(p->reg > NREG) - diag("register out of range %d", p->reg); + if(p->as != ATEXT && p->as != AGLOBL && p->reg > NREG) + diag("register out of range %A %d", p->as, p->reg); p->link = P; p->cond = P; @@ -559,10 +516,10 @@ loop: case AEND: histtoauto(); - if(curtext != P) - curtext->to.autom = curauto; + if(cursym != nil && cursym->text) + cursym->autom = curauto; curauto = 0; - curtext = P; + cursym = nil; if(Boffset(f) == eof) return; goto newloop; @@ -582,98 +539,31 @@ loop: s->type = SBSS; s->value = 0; } - if(p->to.offset > s->value) - s->value = p->to.offset; + if(p->to.offset > s->size) + s->size = p->to.offset; if(p->reg & DUPOK) s->dupok = 1; break; - case ADYNT: - s = p->from.sym; - if(p->to.sym == S) { - diag("DYNT without a sym\n%P", p); - break; - } - di = p->to.sym; - p->reg = 4; - if(di->type == SXREF) { - if(debug['z']) - Bprint(&bso, "%P set to %d\n", p, dtype); - di->type = SCONST; - di->value = dtype; - dtype += 4; - } - if(s == S) - break; - - p->from.offset = di->value; - s->type = SDATA; - if(curtext == P) { - diag("DYNT not in text: %P", p); - break; - } - p->to.sym = curtext->from.sym; - p->to.type = D_CONST; - if(s != S) { - p->dlink = s->data; - s->data = p; - } - if(edatap == P) - datap = p; - else - edatap->link = p; - edatap = p; - break; - - case AINIT: - s = p->from.sym; - if(s == S) { - diag("INIT without a sym\n%P", p); - break; - } - if(di == S) { - diag("INIT without previous DYNT\n%P", p); - break; - } - p->from.offset = di->value; - s->type = SDATA; - if(s != S) { - p->dlink = s->data; - s->data = p; - } - if(edatap == P) - datap = p; - else - edatap->link = p; - edatap = p; - break; - case ADATA: // Assume that AGLOBL comes after ADATA. // If we've seen an AGLOBL that said this sym was DUPOK, // ignore any more ADATA we see, which must be // redefinitions. s = p->from.sym; - if(s != S && s->dupok) { + if(s->dupok) { if(debug['v']) Bprint(&bso, "skipping %s in %s: dupok\n", s->name, pn); goto loop; } - if(s != S) { - p->dlink = s->data; - s->data = p; - if(s->file == nil) - s->file = pn; - else if(s->file != pn) { - diag("multiple initialization for %s: in both %s and %s", s->name, s->file, pn); - errorexit(); - } + if(s->file == nil) + s->file = pn; + else if(s->file != pn) { + diag("multiple initialization for %s: in both %s and %s", s->name, s->file, pn); + errorexit(); } - if(edatap == P) - datap = p; - else - edatap->link = p; - edatap = p; + savedata(s, p); + unmal(p, sizeof *p); break; case AGOK: @@ -683,31 +573,24 @@ loop: break; case ATEXT: - s = p->from.sym; - if(ntext++ == 0 && s->type != 0 && s->type != SXREF) { - /* redefinition, so file has probably been seen before */ - if(debug['v']) - Bprint(&bso, "skipping: %s: redefinition: %s", pn, s->name); - return; - } - setarch(p); - setthumb(p); - p->align = 4; - if(curtext != P) { + if(cursym != nil && cursym->text) { histtoauto(); - curtext->to.autom = curauto; + cursym->autom = curauto; curauto = 0; } - skip = 0; - curtext = p; - autosize = (p->to.offset+3L) & ~3L; - p->to.offset = autosize; - autosize += 4; s = p->from.sym; if(s == S) { diag("TEXT must have a name\n%P", p); errorexit(); } + cursym = s; + if(ntext++ == 0 && s->type != 0 && s->type != SXREF) { + /* redefinition, so file has probably been seen before */ + if(debug['v']) + Bprint(&bso, "skipping: %s: redefinition: %s", pn, s->name); + return; + } + skip = 0; if(s->type != 0 && s->type != SXREF) { if(p->reg & DUPOK) { skip = 1; @@ -715,21 +598,24 @@ loop: } diag("redefinition: %s\n%P", s->name, p); } + if(etextp) + etextp->next = s; + else + textp = s; + etextp = s; + setarch(p); + setthumb(p); + p->align = 4; + autosize = (p->to.offset+3L) & ~3L; + p->to.offset = autosize; + autosize += 4; s->type = STEXT; s->text = p; s->value = pc; s->thumb = thumb; - lastp->link = p; lastp = p; p->pc = pc; pc++; - if(textp == P) { - textp = p; - etextp = p; - goto loop; - } - etextp->cond = p; - etextp = p; break; case ASUB: @@ -778,27 +664,15 @@ loop: if(skip) goto casedef; - if(p->from.type == D_FCONST && chipfloat(p->from.ieee) < 0) { + if(p->from.type == D_FCONST && chipfloat(&p->from.ieee) < 0 && + (chipzero(&p->from.ieee) < 0 || (p->scond & C_SCOND) != C_SCOND_NONE)) { /* size sb 9 max */ - sprint(literal, "$%lux", ieeedtof(p->from.ieee)); + sprint(literal, "$%ux", ieeedtof(&p->from.ieee)); s = lookup(literal, 0); if(s->type == 0) { s->type = SBSS; - s->value = 4; - t = prg(); - t->as = ADATA; - t->line = p->line; - t->from.type = D_OREG; - t->from.sym = s; - t->from.name = D_EXTERN; - t->reg = 4; - t->to = p->from; - if(edatap == P) - datap = t; - else - edatap->link = t; - edatap = t; - t->link = P; + adduint32(s, ieeedtof(&p->from.ieee)); + s->reachable = 0; } p->from.type = D_OREG; p->from.sym = s; @@ -813,28 +687,17 @@ loop: if(skip) goto casedef; - if(p->from.type == D_FCONST && chipfloat(p->from.ieee) < 0) { + if(p->from.type == D_FCONST && chipfloat(&p->from.ieee) < 0 && + (chipzero(&p->from.ieee) < 0 || (p->scond & C_SCOND) != C_SCOND_NONE)) { /* size sb 18 max */ - sprint(literal, "$%lux.%lux", - p->from.ieee->l, p->from.ieee->h); + sprint(literal, "$%ux.%ux", + p->from.ieee.l, p->from.ieee.h); s = lookup(literal, 0); if(s->type == 0) { s->type = SBSS; - s->value = 8; - t = prg(); - t->as = ADATA; - t->line = p->line; - t->from.type = D_OREG; - t->from.sym = s; - t->from.name = D_EXTERN; - t->reg = 8; - t->to = p->from; - if(edatap == P) - datap = t; - else - edatap->link = t; - edatap = t; - t->link = P; + adduint32(s, p->from.ieee.l); + adduint32(s, p->from.ieee.h); + s->reachable = 0; } p->from.type = D_OREG; p->from.sym = s; @@ -847,13 +710,17 @@ loop: casedef: if(skip) nopout(p); - + p->pc = pc; + pc++; if(p->to.type == D_BRANCH) p->to.offset += ipc; + if(lastp == nil) { + if(p->as != ANOP) + diag("unexpected instruction: %P", p); + break; + } lastp->link = p; lastp = p; - p->pc = pc; - pc++; break; } goto loop; @@ -872,191 +739,13 @@ prg(void) return p; } -void -doprof1(void) -{ - Sym *s; - int32 n; - Prog *p, *q; - - if(debug['v']) - Bprint(&bso, "%5.2f profile 1\n", cputime()); - Bflush(&bso); - s = lookup("__mcount", 0); - n = 1; - for(p = firstp->link; p != P; p = p->link) { - setarch(p); - if(p->as == ATEXT) { - q = prg(); - q->line = p->line; - q->link = datap; - datap = q; - q->as = ADATA; - q->from.type = D_OREG; - q->from.name = D_EXTERN; - q->from.offset = n*4; - q->from.sym = s; - q->reg = 4; - q->to = p->from; - q->to.type = D_CONST; - - q = prg(); - q->line = p->line; - q->pc = p->pc; - q->link = p->link; - p->link = q; - p = q; - p->as = AMOVW; - p->from.type = D_OREG; - p->from.name = D_EXTERN; - p->from.sym = s; - p->from.offset = n*4 + 4; - p->to.type = D_REG; - p->to.reg = thumb ? REGTMPT : REGTMP; - - q = prg(); - q->line = p->line; - q->pc = p->pc; - q->link = p->link; - p->link = q; - p = q; - p->as = AADD; - p->from.type = D_CONST; - p->from.offset = 1; - p->to.type = D_REG; - p->to.reg = thumb ? REGTMPT : REGTMP; - - q = prg(); - q->line = p->line; - q->pc = p->pc; - q->link = p->link; - p->link = q; - p = q; - p->as = AMOVW; - p->from.type = D_REG; - p->from.reg = thumb ? REGTMPT : REGTMP; - p->to.type = D_OREG; - p->to.name = D_EXTERN; - p->to.sym = s; - p->to.offset = n*4 + 4; - - n += 2; - continue; - } - } - q = prg(); - q->line = 0; - q->link = datap; - datap = q; - - q->as = ADATA; - q->from.type = D_OREG; - q->from.name = D_EXTERN; - q->from.sym = s; - q->reg = 4; - q->to.type = D_CONST; - q->to.offset = n; - - s->type = SBSS; - s->value = n*4; -} - -void -doprof2(void) -{ - Sym *s2, *s4; - Prog *p, *q, *ps2, *ps4; - - if(debug['v']) - Bprint(&bso, "%5.2f profile 2\n", cputime()); - Bflush(&bso); - s2 = lookup("_profin", 0); - s4 = lookup("_profout", 0); - if(s2->type != STEXT || s4->type != STEXT) { - diag("_profin/_profout not defined"); - return; - } - ps2 = P; - ps4 = P; - for(p = firstp; p != P; p = p->link) { - setarch(p); - if(p->as == ATEXT) { - if(p->from.sym == s2) { - ps2 = p; - p->reg = 1; - } - if(p->from.sym == s4) { - ps4 = p; - p->reg = 1; - } - } - } - for(p = firstp; p != P; p = p->link) { - setarch(p); - if(p->as == ATEXT) { - if(p->reg & NOPROF) { - for(;;) { - q = p->link; - if(q == P) - break; - if(q->as == ATEXT) - break; - p = q; - } - continue; - } - - /* - * BL profin, R2 - */ - q = prg(); - q->line = p->line; - q->pc = p->pc; - q->link = p->link; - p->link = q; - p = q; - p->as = ABL; - p->to.type = D_BRANCH; - p->cond = ps2; - p->to.sym = s2; - - continue; - } - if(p->as == ARET) { - /* - * RET - */ - q = prg(); - q->as = ARET; - q->from = p->from; - q->to = p->to; - q->link = p->link; - p->link = q; - - /* - * BL profout - */ - p->as = ABL; - p->from = zprg.from; - p->to = zprg.to; - p->to.type = D_BRANCH; - p->cond = ps4; - p->to.sym = s4; - - p = q; - - continue; - } - } -} - static void puntfp(Prog *p) { USED(p); /* floating point - punt for now */ - curtext->reg = NREG; /* ARM */ - curtext->from.sym->thumb = 0; + cursym->text->reg = NREG; /* ARM */ + cursym->thumb = 0; thumb = 0; // print("%s: generating ARM code (contains floating point ops %d)\n", curtext->from.sym->name, p->line); } diff --git a/src/cmd/5l/optab.c b/src/cmd/5l/optab.c index 92fe12fc2..96b216837 100644 --- a/src/cmd/5l/optab.c +++ b/src/cmd/5l/optab.c @@ -34,8 +34,6 @@ Optab optab[] = { /* struct Optab: OPCODE, from, prog->reg, to, type,size,param,flag */ - { ATEXT, C_LEXT, C_NONE, C_LCON, 0, 0, 0 }, - { ATEXT, C_LEXT, C_REG, C_LCON, 0, 0, 0 }, { ATEXT, C_ADDR, C_NONE, C_LCON, 0, 0, 0 }, { ATEXT, C_ADDR, C_REG, C_LCON, 0, 0, 0 }, @@ -56,7 +54,6 @@ Optab optab[] = { AMVN, C_SHIFT,C_NONE, C_REG, 3, 4, 0 }, { ACMP, C_SHIFT,C_REG, C_NONE, 3, 4, 0 }, - { AMOVW, C_RECON,C_NONE, C_REG, 4, 4, REGSB }, { AMOVW, C_RACON,C_NONE, C_REG, 4, 4, REGSP }, { AB, C_NONE, C_NONE, C_SBRA, 5, 4, 0, LPOOL }, @@ -81,7 +78,6 @@ Optab optab[] = { AWORD, C_NONE, C_NONE, C_LCON, 11, 4, 0 }, { AWORD, C_NONE, C_NONE, C_GCON, 11, 4, 0 }, - { AWORD, C_NONE, C_NONE, C_LEXT, 11, 4, 0 }, { AWORD, C_NONE, C_NONE, C_ADDR, 11, 4, 0 }, { AMOVW, C_NCON, C_NONE, C_REG, 12, 4, 0 }, @@ -109,85 +105,64 @@ Optab optab[] = { AMULL, C_REG, C_REG, C_REGREG, 17, 4, 0 }, - { AMOVW, C_REG, C_NONE, C_SEXT, 20, 4, REGSB }, { AMOVW, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, { AMOVW, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, - { AMOVB, C_REG, C_NONE, C_SEXT, 20, 4, REGSB }, { AMOVB, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, { AMOVB, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, - { AMOVBU, C_REG, C_NONE, C_SEXT, 20, 4, REGSB }, { AMOVBU, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, { AMOVBU, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, - { AMOVW, C_SEXT, C_NONE, C_REG, 21, 4, REGSB }, { AMOVW, C_SAUTO,C_NONE, C_REG, 21, 4, REGSP }, { AMOVW, C_SOREG,C_NONE, C_REG, 21, 4, 0 }, - { AMOVBU, C_SEXT, C_NONE, C_REG, 21, 4, REGSB }, { AMOVBU, C_SAUTO,C_NONE, C_REG, 21, 4, REGSP }, { AMOVBU, C_SOREG,C_NONE, C_REG, 21, 4, 0 }, - { AMOVB, C_SEXT, C_NONE, C_REG, 22, 12, REGSB }, { AMOVB, C_SAUTO,C_NONE, C_REG, 22, 12, REGSP }, { AMOVB, C_SOREG,C_NONE, C_REG, 22, 12, 0 }, - { AMOVH, C_SEXT, C_NONE, C_REG, 22, 12, REGSB }, { AMOVH, C_SAUTO,C_NONE, C_REG, 22, 12, REGSP }, { AMOVH, C_SOREG,C_NONE, C_REG, 22, 12, 0 }, - { AMOVHU, C_SEXT, C_NONE, C_REG, 22, 12, REGSB }, { AMOVHU, C_SAUTO,C_NONE, C_REG, 22, 12, REGSP }, { AMOVHU, C_SOREG,C_NONE, C_REG, 22, 12, 0 }, - { AMOVH, C_REG, C_NONE, C_SEXT, 23, 12, REGSB }, { AMOVH, C_REG, C_NONE, C_SAUTO, 23, 12, REGSP }, { AMOVH, C_REG, C_NONE, C_SOREG, 23, 12, 0 }, - { AMOVHU, C_REG, C_NONE, C_SEXT, 23, 12, REGSB }, { AMOVHU, C_REG, C_NONE, C_SAUTO, 23, 12, REGSP }, { AMOVHU, C_REG, C_NONE, C_SOREG, 23, 12, 0 }, - { AMOVW, C_REG, C_NONE, C_LEXT, 30, 8, REGSB, LTO }, { AMOVW, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, { AMOVW, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, { AMOVW, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO }, - { AMOVB, C_REG, C_NONE, C_LEXT, 30, 8, REGSB, LTO }, { AMOVB, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, { AMOVB, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, { AMOVB, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO }, - { AMOVBU, C_REG, C_NONE, C_LEXT, 30, 8, REGSB, LTO }, { AMOVBU, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, { AMOVBU, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, { AMOVBU, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO }, - { AMOVW, C_LEXT, C_NONE, C_REG, 31, 8, REGSB, LFROM }, { AMOVW, C_LAUTO,C_NONE, C_REG, 31, 8, REGSP, LFROM }, { AMOVW, C_LOREG,C_NONE, C_REG, 31, 8, 0, LFROM }, { AMOVW, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM }, - { AMOVBU, C_LEXT, C_NONE, C_REG, 31, 8, REGSB, LFROM }, { AMOVBU, C_LAUTO,C_NONE, C_REG, 31, 8, REGSP, LFROM }, { AMOVBU, C_LOREG,C_NONE, C_REG, 31, 8, 0, LFROM }, { AMOVBU, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM }, - { AMOVB, C_LEXT, C_NONE, C_REG, 32, 16, REGSB, LFROM }, { AMOVB, C_LAUTO,C_NONE, C_REG, 32, 16, REGSP, LFROM }, { AMOVB, C_LOREG,C_NONE, C_REG, 32, 16, 0, LFROM }, { AMOVB, C_ADDR, C_NONE, C_REG, 66, 16, 0, LFROM }, - { AMOVH, C_LEXT, C_NONE, C_REG, 32, 16, REGSB, LFROM }, { AMOVH, C_LAUTO,C_NONE, C_REG, 32, 16, REGSP, LFROM }, { AMOVH, C_LOREG,C_NONE, C_REG, 32, 16, 0, LFROM }, { AMOVH, C_ADDR, C_NONE, C_REG, 66, 16, 0, LFROM }, - { AMOVHU, C_LEXT, C_NONE, C_REG, 32, 16, REGSB, LFROM }, { AMOVHU, C_LAUTO,C_NONE, C_REG, 32, 16, REGSP, LFROM }, { AMOVHU, C_LOREG,C_NONE, C_REG, 32, 16, 0, LFROM }, { AMOVHU, C_ADDR, C_NONE, C_REG, 66, 16, 0, LFROM }, - { AMOVH, C_REG, C_NONE, C_LEXT, 33, 24, REGSB, LTO }, { AMOVH, C_REG, C_NONE, C_LAUTO, 33, 24, REGSP, LTO }, { AMOVH, C_REG, C_NONE, C_LOREG, 33, 24, 0, LTO }, { AMOVH, C_REG, C_NONE, C_ADDR, 67, 24, 0, LTO }, - { AMOVHU, C_REG, C_NONE, C_LEXT, 33, 24, REGSB, LTO }, { AMOVHU, C_REG, C_NONE, C_LAUTO, 33, 24, REGSP, LTO }, { AMOVHU, C_REG, C_NONE, C_LOREG, 33, 24, 0, LTO }, { AMOVHU, C_REG, C_NONE, C_ADDR, 67, 24, 0, LTO }, - { AMOVW, C_LECON,C_NONE, C_REG, 34, 8, REGSB, LFROM }, { AMOVW, C_LACON,C_NONE, C_REG, 34, 8, REGSP, LFROM }, { AMOVW, C_PSR, C_NONE, C_REG, 35, 4, 0 }, @@ -201,19 +176,15 @@ Optab optab[] = { ARFE, C_NONE, C_NONE, C_NONE, 41, 4, 0 }, - { AMOVF, C_FREG, C_NONE, C_FEXT, 50, 4, REGSB }, { AMOVF, C_FREG, C_NONE, C_FAUTO, 50, 4, REGSP }, { AMOVF, C_FREG, C_NONE, C_FOREG, 50, 4, 0 }, - { AMOVF, C_FEXT, C_NONE, C_FREG, 51, 4, REGSB }, { AMOVF, C_FAUTO,C_NONE, C_FREG, 51, 4, REGSP }, { AMOVF, C_FOREG,C_NONE, C_FREG, 51, 4, 0 }, - { AMOVF, C_FREG, C_NONE, C_LEXT, 52, 12, REGSB, LTO }, { AMOVF, C_FREG, C_NONE, C_LAUTO, 52, 12, REGSP, LTO }, { AMOVF, C_FREG, C_NONE, C_LOREG, 52, 12, 0, LTO }, - { AMOVF, C_LEXT, C_NONE, C_FREG, 53, 12, REGSB, LFROM }, { AMOVF, C_LAUTO,C_NONE, C_FREG, 53, 12, REGSP, LFROM }, { AMOVF, C_LOREG,C_NONE, C_FREG, 53, 12, 0, LFROM }, @@ -222,17 +193,8 @@ Optab optab[] = { AADDF, C_FREG, C_NONE, C_FREG, 54, 4, 0 }, { AADDF, C_FREG, C_REG, C_FREG, 54, 4, 0 }, - { AADDF, C_FCON, C_NONE, C_FREG, 54, 4, 0 }, - { AADDF, C_FCON, C_REG, C_FREG, 54, 4, 0 }, - { AMOVF, C_FCON, C_NONE, C_FREG, 54, 4, 0 }, { AMOVF, C_FREG, C_NONE, C_FREG, 54, 4, 0 }, - { ACMPF, C_FREG, C_REG, C_NONE, 54, 4, 0 }, - { ACMPF, C_FCON, C_REG, C_NONE, 54, 4, 0 }, - - { AMOVFW, C_FREG, C_NONE, C_REG, 55, 4, 0 }, - { AMOVFW, C_REG, C_NONE, C_FREG, 55, 4, 0 }, - { AMOVW, C_REG, C_NONE, C_FCR, 56, 4, 0 }, { AMOVW, C_FCR, C_NONE, C_REG, 57, 4, 0 }, @@ -248,41 +210,46 @@ Optab optab[] = { ACASE, C_REG, C_NONE, C_NONE, 62, 4, 0 }, { ABCASE, C_NONE, C_NONE, C_SBRA, 63, 4, 0 }, - { AMOVH, C_REG, C_NONE, C_HEXT, 70, 4, REGSB, V4 }, { AMOVH, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, V4 }, { AMOVH, C_REG, C_NONE, C_HOREG, 70, 4, 0, V4 }, - { AMOVHU, C_REG, C_NONE, C_HEXT, 70, 4, REGSB, V4 }, { AMOVHU, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, V4 }, { AMOVHU, C_REG, C_NONE, C_HOREG, 70, 4, 0, V4 }, - { AMOVB, C_HEXT, C_NONE, C_REG, 71, 4, REGSB, V4 }, { AMOVB, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, V4 }, { AMOVB, C_HOREG,C_NONE, C_REG, 71, 4, 0, V4 }, - { AMOVH, C_HEXT, C_NONE, C_REG, 71, 4, REGSB, V4 }, { AMOVH, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, V4 }, { AMOVH, C_HOREG,C_NONE, C_REG, 71, 4, 0, V4 }, - { AMOVHU, C_HEXT, C_NONE, C_REG, 71, 4, REGSB, V4 }, { AMOVHU, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, V4 }, { AMOVHU, C_HOREG,C_NONE, C_REG, 71, 4, 0, V4 }, - { AMOVH, C_REG, C_NONE, C_LEXT, 72, 8, REGSB, LTO|V4 }, { AMOVH, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO|V4 }, { AMOVH, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO|V4 }, - { AMOVHU, C_REG, C_NONE, C_LEXT, 72, 8, REGSB, LTO|V4 }, { AMOVHU, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO|V4 }, { AMOVHU, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO|V4 }, - { AMOVB, C_LEXT, C_NONE, C_REG, 73, 8, REGSB, LFROM|V4 }, { AMOVB, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM|V4 }, { AMOVB, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM|V4 }, - { AMOVH, C_LEXT, C_NONE, C_REG, 73, 8, REGSB, LFROM|V4 }, { AMOVH, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM|V4 }, { AMOVH, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM|V4 }, - { AMOVHU, C_LEXT, C_NONE, C_REG, 73, 8, REGSB, LFROM|V4 }, { AMOVHU, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM|V4 }, { AMOVHU, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM|V4 }, { ALDREX, C_SOREG,C_NONE, C_REG, 77, 4, 0 }, { ASTREX, C_SOREG,C_REG, C_REG, 78, 4, 0 }, + { AMOVF, C_ZFCON,C_NONE, C_FREG, 80, 4, 0 }, + { AMOVF, C_SFCON,C_NONE, C_FREG, 81, 4, 0 }, + + { ACMPF, C_FREG, C_REG, C_NONE, 82, 8, 0 }, + { ACMPF, C_FREG, C_NONE, C_NONE, 83, 8, 0 }, + + { AMOVFW, C_FREG, C_NONE, C_FREG, 84, 4, 0 }, + { AMOVWF, C_FREG, C_NONE, C_FREG, 85, 4, 0 }, + + { AMOVFW, C_FREG, C_NONE, C_REG, 86, 8, 0 }, + { AMOVWF, C_REG, C_NONE, C_FREG, 87, 8, 0 }, + + { AMOVW, C_REG, C_NONE, C_FREG, 88, 4, 0 }, + { AMOVW, C_FREG, C_NONE, C_REG, 89, 4, 0 }, + { AXXX, C_NONE, C_NONE, C_NONE, 0, 4, 0 }, }; diff --git a/src/cmd/5l/pass.c b/src/cmd/5l/pass.c index 06b1792b4..e16b34171 100644 --- a/src/cmd/5l/pass.c +++ b/src/cmd/5l/pass.c @@ -28,147 +28,12 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Code and data passes. + #include "l.h" #include "../ld/lib.h" -void -dodata(void) -{ - int i, t; - Sym *s; - Prog *p; - int32 orig, v; - - if(debug['v']) - Bprint(&bso, "%5.2f dodata\n", cputime()); - Bflush(&bso); - for(p = datap; p != P; p = p->link) { - s = p->from.sym; - if(p->as == ADYNT || p->as == AINIT) - s->value = dtype; - if(s->type == SBSS) - s->type = SDATA; - if(s->type != SDATA && s->type != SELFDATA) - diag("initialize non-data (%d): %s\n%P", - s->type, s->name, p); - v = p->from.offset + p->reg; - if(v > s->value) - diag("initialize bounds (%ld/%ld): %s\n%P", - v, s->value, s->name, p); - if((s->type == SBSS || s->type == SDATA) && (p->to.type == D_CONST || p->to.type == D_OCONST) && (p->to.name == D_EXTERN || p->to.name == D_STATIC)){ - s = p->to.sym; - if(s != S && (s->type == STEXT || s->type == SLEAF || s->type == SCONST || s->type == SXREF)) - s->fnptr = 1; - } - } - - if(debug['t']) { - /* - * pull out string constants - */ - for(p = datap; p != P; p = p->link) { - s = p->from.sym; - if(p->to.type == D_SCONST) - s->type = SSTRING; - } - } - - /* - * pass 0 - * assign elf data - must be segregated from real data - */ - orig = 0; - for(i=0; ilink) { - if(!s->reachable || s->type != SELFDATA) - continue; - v = s->value; - while(v & 3) - v++; - s->size = v; - s->value = orig; - orig += v; - } - elfdatsize = orig; - - /* - * pass 1 - * assign 'small' variables to data segment - * (rational is that data segment is more easily - * addressed through offset on R12) - */ - for(i=0; ilink) { - t = s->type; - if(t != SDATA && t != SBSS) - continue; - v = s->value; - if(v == 0) { - diag("%s: no size", s->name); - v = 1; - } - while(v & 3) - v++; - s->size = v; - s->value = v; - if(v > MINSIZ) - continue; - s->value = orig; - orig += v; - s->type = SDATA1; - } - - /* - * pass 2 - * assign large 'data' variables to data segment - */ - for(i=0; ilink) { - t = s->type; - if(t != SDATA) { - if(t == SDATA1) - s->type = SDATA; - continue; - } - v = s->value; - s->size = v; - s->value = orig; - orig += v; - } - - while(orig & 7) - orig++; - datsize = orig; - - /* - * pass 3 - * everything else to bss segment - */ - for(i=0; ilink) { - if(s->type != SBSS) - continue; - v = s->value; - s->size = v; - s->value = orig; - orig += v; - } - while(orig & 7) - orig++; - bsssize = orig-datsize; - - xdefine("setR12", SDATA, 0L+BIG); - xdefine("bdata", SDATA, 0L); - xdefine("data", SBSS, 0); - xdefine("edata", SDATA, datsize); - xdefine("end", SBSS, datsize+bsssize); - xdefine("etext", STEXT, 0L); - - if(debug['s']) - xdefine("symdat", SFIXED, 0); - else - xdefine("symdat", SFIXED, SYMDATVA); -} +static void xfol(Prog*, Prog**); void undef(void) @@ -177,7 +42,7 @@ undef(void) Sym *s; for(i=0; ilink) + for(s = hash[i]; s != S; s = s->hash) if(s->type == SXREF) diag("%s: not defined", s->name); } @@ -223,20 +88,23 @@ relinv(int a) void follow(void) { + Prog *firstp, *lastp; + if(debug['v']) Bprint(&bso, "%5.2f follow\n", cputime()); Bflush(&bso); - firstp = prg(); - lastp = firstp; - xfol(textp); - - firstp = firstp->link; - lastp->link = P; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + firstp = prg(); + lastp = firstp; + xfol(cursym->text, &lastp); + lastp->link = nil; + cursym->text = firstp->link; + } } -void -xfol(Prog *p) +static void +xfol(Prog *p, Prog **last) { Prog *q, *r; int a, i; @@ -246,12 +114,6 @@ loop: return; setarch(p); a = p->as; - if(a == ATEXT) - curtext = p; - if(!curtext->from.sym->reachable) { - p = p->cond; - goto loop; - } if(a == AB) { q = p->cond; if(q != P && q->as != ATEXT) { @@ -263,7 +125,7 @@ loop: } if(p->mark & FOLL) { for(i=0,q=p; i<4; i++,q=q->link) { - if(q == lastp) + if(q == *last || q == nil) break; a = q->as; if(a == ANOP) { @@ -272,7 +134,7 @@ loop: } if(a == AB || (a == ARET && q->scond == 14) || a == ARFE) goto copy; - if(!q->cond || (q->cond->mark&FOLL)) + if(q->cond == P || (q->cond->mark&FOLL)) continue; if(a != ABEQ && a != ABNE) continue; @@ -285,12 +147,12 @@ loop: r->mark |= FOLL; if(p != q) { p = p->link; - lastp->link = r; - lastp = r; + (*last)->link = r; + *last = r; continue; } - lastp->link = r; - lastp = r; + (*last)->link = r; + *last = r; if(a == AB || (a == ARET && q->scond == 14) || a == ARFE) return; r->as = ABNE; @@ -299,7 +161,7 @@ loop: r->cond = p->link; r->link = p->cond; if(!(r->link->mark&FOLL)) - xfol(r->link); + xfol(r->link, last); if(!(r->cond->mark&FOLL)) print("cant happen 2\n"); return; @@ -315,8 +177,8 @@ loop: p = q; } p->mark |= FOLL; - lastp->link = p; - lastp = p; + (*last)->link = p; + *last = p; if(a == AB || (a == ARET && p->scond == 14) || a == ARFE){ return; } @@ -329,7 +191,7 @@ loop: p->link = p->cond; p->cond = q; } - xfol(p->link); + xfol(p->link, last); q = brchain(p->cond); if(q == P) q = p->cond; @@ -349,7 +211,7 @@ patch(void) { int32 c, vexit; Prog *p, *q; - Sym *s, *s1; + Sym *s; int a; if(debug['v']) @@ -358,113 +220,71 @@ patch(void) mkfwd(); s = lookup("exit", 0); vexit = s->value; - for(p = firstp; p != P; p = p->link) { - setarch(p); - a = p->as; - if(a == ATEXT) - curtext = p; - if(seenthumb && a == ABL){ - // if((s = p->to.sym) != S && (s1 = curtext->from.sym) != S) - // print("%s calls %s\n", s1->name, s->name); - if((s = p->to.sym) != S && (s1 = curtext->from.sym) != S && s->thumb != s1->thumb) - s->foreign = 1; - } - if((a == ABL || a == ABX || a == AB || a == ARET) && - p->to.type != D_BRANCH && p->to.sym != S) { - s = p->to.sym; - switch(s->type) { - default: - diag("undefined: %s", s->name); - s->type = STEXT; - s->value = vexit; - continue; // avoid more error messages - case STEXT: - p->to.offset = s->value; - p->to.type = D_BRANCH; - break; - case SUNDEF: - if(p->as != ABL) - diag("help: SUNDEF in AB || ARET"); - p->to.offset = 0; - p->to.type = D_BRANCH; - p->cond = UP; - break; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + for(p = cursym->text; p != P; p = p->link) { + setarch(p); + a = p->as; + if(seenthumb && a == ABL){ + // if((s = p->to.sym) != S && (s1 = curtext->from.sym) != S) + // print("%s calls %s\n", s1->name, s->name); + if((s = p->to.sym) != S && s->thumb != cursym->thumb) + s->foreign = 1; } - } - if(p->to.type != D_BRANCH || p->cond == UP) - continue; - c = p->to.offset; - for(q = firstp; q != P;) { - if(q->forwd != P) - if(c >= q->forwd->pc) { - q = q->forwd; + if((a == ABL || a == ABX || a == AB || a == ARET) && + p->to.type != D_BRANCH && p->to.sym != S) { + s = p->to.sym; + switch(s->type) { + default: + diag("undefined: %s", s->name); + s->type = STEXT; + s->value = vexit; + continue; // avoid more error messages + case STEXT: + p->to.offset = s->value; + p->to.type = D_BRANCH; + break; + } + } + if(p->to.type != D_BRANCH) continue; + c = p->to.offset; + for(q = textp->text; q != P;) { + if(c == q->pc) + break; + if(q->forwd != P && c >= q->forwd->pc) + q = q->forwd; + else + q = q->link; } - if(c == q->pc) - break; - q = q->link; - } - if(q == P) { - diag("branch out of range %ld\n%P", c, p); - p->to.type = D_NONE; + if(q == P) { + diag("branch out of range %d\n%P", c, p); + p->to.type = D_NONE; + } + p->cond = q; } - p->cond = q; } - for(p = firstp; p != P; p = p->link) { - setarch(p); - a = p->as; - if(p->as == ATEXT) - curtext = p; - if(seenthumb && a == ABL) { + for(cursym = textp; cursym != nil; cursym = cursym->next) { + for(p = cursym->text; p != P; p = p->link) { + setarch(p); + a = p->as; + if(seenthumb && a == ABL) { #ifdef CALLEEBX - if(0) - {} + if(0) + {} #else - if((s = p->to.sym) != S && (s->foreign || s->fnptr)) - p->as = ABX; + if((s = p->to.sym) != S && (s->foreign || s->fnptr)) + p->as = ABX; #endif - else if(p->to.type == D_OREG) - p->as = ABX; - } - if(p->cond != P && p->cond != UP) { - p->cond = brloop(p->cond); - if(p->cond != P) - if(p->to.type == D_BRANCH) - p->to.offset = p->cond->pc; - } - } -} - -#define LOG 5 -void -mkfwd(void) -{ - Prog *p; - int32 dwn[LOG], cnt[LOG], i; - Prog *lst[LOG]; - - for(i=0; ilink) { - if(p->as == ATEXT) - curtext = p; - i--; - if(i < 0) - i = LOG-1; - p->forwd = P; - dwn[i]--; - if(dwn[i] <= 0) { - dwn[i] = cnt[i]; - if(lst[i] != P) - lst[i]->forwd = p; - lst[i] = p; + else if(p->to.type == D_OREG) + p->as = ABX; + } + if(p->cond != P) { + p->cond = brloop(p->cond); + if(p->cond != P) + if(p->to.type == D_BRANCH) + p->to.offset = p->cond->pc; + } } } } @@ -543,462 +363,3 @@ rnd(int32 v, int32 r) v -= c; return v; } - -#define Reachable(n) if((s = lookup(n, 0)) != nil) s->used++ - -static void -rused(Adr *a) -{ - Sym *s = a->sym; - - if(s == S) - return; - if(a->type == D_OREG || a->type == D_OCONST || a->type == D_CONST){ - if(a->name == D_EXTERN || a->name == D_STATIC){ - if(s->used == 0) - s->used = 1; - } - } - else if(a->type == D_BRANCH){ - if(s->used == 0) - s->used = 1; - } -} - -void -reachable() -{ - Prog *p, *prev, *prevt, *nextt, *q; - Sym *s, *s0; - int i, todo; - char *a; - - Reachable("_div"); - Reachable("_divu"); - Reachable("_mod"); - Reachable("_modu"); - a = INITENTRY; - if(*a >= '0' && *a <= '9') - return; - s = lookup(a, 0); - if(s == nil) - return; - if(s->type == 0){ - s->used = 1; // to stop asm complaining - for(p = firstp; p != P && p->as != ATEXT; p = p->link) - ; - if(p == nil) - return; - s = p->from.sym; - } - s->used = 1; - do{ - todo = 0; - for(p = firstp; p != P; p = p->link){ - if(p->as == ATEXT && (s0 = p->from.sym)->used == 1){ - todo = 1; - for(q = p->link; q != P && q->as != ATEXT; q = q->link){ - rused(&q->from); - rused(&q->to); - } - s0->used = 2; - } - } - for(p = datap; p != P; p = p->link){ - if((s0 = p->from.sym)->used == 1){ - todo = 1; - for(q = p; q != P; q = q->link){ // data can be scattered - if(q->from.sym == s0) - rused(&q->to); - } - s0->used = 2; - } - } - }while(todo); - prev = nil; - prevt = nextt = nil; - for(p = firstp; p != P; ){ - if(p->as == ATEXT){ - prevt = nextt; - nextt = p; - } - if(p->as == ATEXT && (s0 = p->from.sym)->used == 0){ - s0->type = SREMOVED; - for(q = p->link; q != P && q->as != ATEXT; q = q->link) - ; - if(q != p->cond) - diag("bad ptr in reachable()"); - if(prev == nil) - firstp = q; - else - prev->link = q; - if(q == nil) - lastp = prev; - if(prevt == nil) - textp = q; - else - prevt->cond = q; - if(q == nil) - etextp = prevt; - nextt = prevt; - if(debug['V']) - print("%s unused\n", s0->name); - p = q; - } - else{ - prev = p; - p = p->link; - } - } - prevt = nil; - for(p = datap; p != nil; ){ - if((s0 = p->from.sym)->used == 0){ - s0->type = SREMOVED; - prev = prevt; - for(q = p; q != nil; q = q->link){ - if(q->from.sym == s0){ - if(prev == nil) - datap = q->link; - else - prev->link = q->link; - } - else - prev = q; - } - if(debug['V']) - print("%s unused (data)\n", s0->name); - p = prevt->link; - } - else{ - prevt = p; - p = p->link; - } - } - for(i=0; ilink){ - if(s->used == 0) - s->type = SREMOVED; - } - } -} - -static void -fused(Adr *a, Prog *p, Prog *ct) -{ - Sym *s = a->sym; - Use *u; - - if(s == S) - return; - if(a->type == D_OREG || a->type == D_OCONST || a->type == D_CONST){ - if(a->name == D_EXTERN || a->name == D_STATIC){ - u = malloc(sizeof(Use)); - u->p = p; - u->ct = ct; - u->link = s->use; - s->use = u; - } - } - else if(a->type == D_BRANCH){ - u = malloc(sizeof(Use)); - u->p = p; - u->ct = ct; - u->link = s->use; - s->use = u; - } -} - -static int -ckfpuse(Prog *p, Prog *ct, Sym *fp, Sym *r) -{ - int reg; - - USED(fp); - USED(ct); - if(p->from.sym == r && p->as == AMOVW && (p->from.type == D_CONST || p->from.type == D_OREG) && p->reg == NREG && p->to.type == D_REG){ - reg = p->to.reg; - for(p = p->link; p != P && p->as != ATEXT; p = p->link){ - if((p->as == ABL || p->as == ABX) && p->to.type == D_OREG && p->to.reg == reg) - return 1; - if(!debug['F'] && (isbranch(p) || p->as == ARET)){ - // print("%s: branch %P in %s\n", fp->name, p, ct->from.sym->name); - return 0; - } - if((p->from.type == D_REG || p->from.type == D_OREG) && p->from.reg == reg){ - if(!debug['F'] && p->to.type != D_REG){ - // print("%s: store %P in %s\n", fp->name, p, ct->from.sym->name); - return 0; - } - reg = p->to.reg; - } - } - } - // print("%s: no MOVW O(R), R\n", fp->name); - return debug['F']; -} - -static void -setfpuse(Prog *p, Sym *fp, Sym *r) -{ - int reg; - - if(p->from.sym == r && p->as == AMOVW && (p->from.type == D_CONST || p->from.type == D_OREG) && p->reg == NREG && p->to.type == D_REG){ - reg = p->to.reg; - for(p = p->link; p != P && p->as != ATEXT; p = p->link){ - if((p->as == ABL || p->as == ABX) && p->to.type == D_OREG && p->to.reg == reg){ - fp->fnptr = 0; - p->as = ABL; // safe to do so -// print("simplified %s call\n", fp->name); - break; - } - if(!debug['F'] && (isbranch(p) || p->as == ARET)) - diag("bad setfpuse call"); - if((p->from.type == D_REG || p->from.type == D_OREG) && p->from.reg == reg){ - if(!debug['F'] && p->to.type != D_REG) - diag("bad setfpuse call"); - reg = p->to.reg; - } - } - } -} - -static int -cksymuse(Sym *s, int t) -{ - Prog *p; - - for(p = datap; p != P; p = p->link){ - if(p->from.sym == s && p->to.sym != nil && strcmp(p->to.sym->name, ".string") != 0 && p->to.sym->thumb != t){ - // print("%s %s %d %d ", p->from.sym->name, p->to.sym->name, p->to.sym->thumb, t); - return 0; - } - } - return 1; -} - -/* check the use of s at the given point */ -static int -ckuse(Sym *s, Sym *s0, Use *u) -{ - Sym *s1; - - s1 = u->p->from.sym; -// print("ckuse %s %s %s\n", s->name, s0->name, s1 ? s1->name : "nil"); - if(u->ct == nil){ /* in data area */ - if(s0 == s && !cksymuse(s1, s0->thumb)){ - // print("%s: cksymuse fails\n", s0->name); - return 0; - } - for(u = s1->use; u != U; u = u->link) - if(!ckuse(s1, s0, u)) - return 0; - } - else{ /* in text area */ - if(u->ct->from.sym->thumb != s0->thumb){ - // print("%s(%d): foreign call %s(%d)\n", s0->name, s0->thumb, u->ct->from.sym->name, u->ct->from.sym->thumb); - return 0; - } - return ckfpuse(u->p, u->ct, s0, s); - } - return 1; -} - -static void -setuse(Sym *s, Sym *s0, Use *u) -{ - Sym *s1; - - s1 = u->p->from.sym; - if(u->ct == nil){ /* in data area */ - for(u = s1->use; u != U; u = u->link) - setuse(s1, s0, u); - } - else{ /* in text area */ - setfpuse(u->p, s0, s); - } -} - -/* detect BX O(R) which can be done as BL O(R) */ -void -fnptrs() -{ - int i; - Sym *s; - Prog *p; - Use *u; - - for(i=0; ilink){ - if(s->fnptr && (s->type == STEXT || s->type == SLEAF || s->type == SCONST)){ - // print("%s : fnptr %d %d\n", s->name, s->thumb, s->foreign); - } - } - } - /* record use of syms */ - for(p = firstp; p != P; p = p->link){ - if(p->as == ATEXT) - curtext = p; - else{ - fused(&p->from, p, curtext); - fused(&p->to, p, curtext); - } - } - for(p = datap; p != P; p = p->link) - fused(&p->to, p, nil); - - /* now look for fn ptrs */ - for(i=0; ilink){ - if(s->fnptr && (s->type == STEXT || s->type == SLEAF || s->type == SCONST)){ - for(u = s->use; u != U; u = u->link){ - if(!ckuse(s, s, u)) - break; - } - if(u == U){ // can simplify - for(u = s->use; u != U; u = u->link) - setuse(s, s, u); - } - } - } - } - - /* now free Use structures */ -} - -void -import(void) -{ - int i; - Sym *s; - - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type == SXREF && (nimports == 0 || s->subtype == SIMPORT)){ - undefsym(s); - Bprint(&bso, "IMPORT: %s sig=%lux v=%ld\n", s->name, s->sig, s->value); - } -} - -void -ckoff(Sym *s, int32 v) -{ - if(v < 0 || v >= 1<name); -} - -Prog* -newdata(Sym *s, int o, int w, int t) -{ - Prog *p; - - p = prg(); - p->link = datap; - datap = p; - p->as = ADATA; - p->reg = w; - p->from.type = D_OREG; - p->from.name = t; - p->from.sym = s; - p->from.offset = o; - p->to.type = D_CONST; - p->to.name = D_NONE; - s->data = p; - return p; -} - -void -export(void) -{ - int i, j, n, off, nb, sv, ne; - Sym *s, *et, *str, **esyms; - Prog *p; - char buf[NSNAME], *t; - - n = 0; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT)) - n++; - esyms = malloc(n*sizeof(Sym*)); - ne = n; - n = 0; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT)) - esyms[n++] = s; - for(i = 0; i < ne-1; i++) - for(j = i+1; j < ne; j++) - if(strcmp(esyms[i]->name, esyms[j]->name) > 0){ - s = esyms[i]; - esyms[i] = esyms[j]; - esyms[j] = s; - } - - nb = 0; - off = 0; - et = lookup(EXPTAB, 0); - if(et->type != 0 && et->type != SXREF) - diag("%s already defined", EXPTAB); - et->type = SDATA; - str = lookup(".string", 0); - if(str->type == 0) - str->type = SDATA; - sv = str->value; - for(i = 0; i < ne; i++){ - s = esyms[i]; - Bprint(&bso, "EXPORT: %s sig=%lux t=%d\n", s->name, s->sig, s->type); - - /* signature */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.offset = s->sig; - - /* address */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.name = D_EXTERN; - p->to.sym = s; - - /* string */ - t = s->name; - n = strlen(t)+1; - for(;;){ - buf[nb++] = *t; - sv++; - if(nb >= NSNAME){ - p = newdata(str, sv-NSNAME, NSNAME, D_STATIC); - p->to.type = D_SCONST; - p->to.sval = malloc(NSNAME); - memmove(p->to.sval, buf, NSNAME); - nb = 0; - } - if(*t++ == 0) - break; - } - - /* name */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.name = D_STATIC; - p->to.sym = str; - p->to.offset = sv-n; - } - - if(nb > 0){ - p = newdata(str, sv-nb, nb, D_STATIC); - p->to.type = D_SCONST; - p->to.sval = malloc(NSNAME); - memmove(p->to.sval, buf, nb); - } - - for(i = 0; i < 3; i++){ - newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - } - et->value = off; - if(sv == 0) - sv = 1; - str->value = sv; - exports = ne; - free(esyms); -} diff --git a/src/cmd/5l/prof.c b/src/cmd/5l/prof.c new file mode 100644 index 000000000..ad115a8ca --- /dev/null +++ b/src/cmd/5l/prof.c @@ -0,0 +1,214 @@ +// Inferno utils/5l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Profiling. + +#include "l.h" +#include "../ld/lib.h" + +void +doprof1(void) +{ +#if 0 // TODO(rsc) + Sym *s; + int32 n; + Prog *p, *q; + + if(debug['v']) + Bprint(&bso, "%5.2f profile 1\n", cputime()); + Bflush(&bso); + s = lookup("__mcount", 0); + n = 1; + for(p = firstp->link; p != P; p = p->link) { + setarch(p); + if(p->as == ATEXT) { + q = prg(); + q->line = p->line; + q->link = datap; + datap = q; + q->as = ADATA; + q->from.type = D_OREG; + q->from.name = D_EXTERN; + q->from.offset = n*4; + q->from.sym = s; + q->reg = 4; + q->to = p->from; + q->to.type = D_CONST; + + q = prg(); + q->line = p->line; + q->pc = p->pc; + q->link = p->link; + p->link = q; + p = q; + p->as = AMOVW; + p->from.type = D_OREG; + p->from.name = D_EXTERN; + p->from.sym = s; + p->from.offset = n*4 + 4; + p->to.type = D_REG; + p->to.reg = thumb ? REGTMPT : REGTMP; + + q = prg(); + q->line = p->line; + q->pc = p->pc; + q->link = p->link; + p->link = q; + p = q; + p->as = AADD; + p->from.type = D_CONST; + p->from.offset = 1; + p->to.type = D_REG; + p->to.reg = thumb ? REGTMPT : REGTMP; + + q = prg(); + q->line = p->line; + q->pc = p->pc; + q->link = p->link; + p->link = q; + p = q; + p->as = AMOVW; + p->from.type = D_REG; + p->from.reg = thumb ? REGTMPT : REGTMP; + p->to.type = D_OREG; + p->to.name = D_EXTERN; + p->to.sym = s; + p->to.offset = n*4 + 4; + + n += 2; + continue; + } + } + q = prg(); + q->line = 0; + q->link = datap; + datap = q; + + q->as = ADATA; + q->from.type = D_OREG; + q->from.name = D_EXTERN; + q->from.sym = s; + q->reg = 4; + q->to.type = D_CONST; + q->to.offset = n; + + s->type = SBSS; + s->value = n*4; +#endif +} + +void +doprof2(void) +{ + Sym *s2, *s4; + Prog *p, *q, *ps2, *ps4; + + if(debug['v']) + Bprint(&bso, "%5.2f profile 2\n", cputime()); + Bflush(&bso); + s2 = lookup("_profin", 0); + s4 = lookup("_profout", 0); + if(s2->type != STEXT || s4->type != STEXT) { + diag("_profin/_profout not defined"); + return; + } + ps2 = P; + ps4 = P; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; + setarch(p); + if(cursym == s2) { + ps2 = p; + p->reg = 1; + } + if(cursym == s4) { + ps4 = p; + p->reg = 1; + } + } + for(cursym = textp; cursym != nil; cursym = cursym->next) + for(p = cursym->text; p != P; p = p->link) { + setarch(p); + if(p->as == ATEXT) { + if(p->reg & NOPROF) { + for(;;) { + q = p->link; + if(q == P) + break; + if(q->as == ATEXT) + break; + p = q; + } + continue; + } + + /* + * BL profin, R2 + */ + q = prg(); + q->line = p->line; + q->pc = p->pc; + q->link = p->link; + p->link = q; + p = q; + p->as = ABL; + p->to.type = D_BRANCH; + p->cond = ps2; + p->to.sym = s2; + + continue; + } + if(p->as == ARET) { + /* + * RET + */ + q = prg(); + q->as = ARET; + q->from = p->from; + q->to = p->to; + q->link = p->link; + p->link = q; + + /* + * BL profout + */ + p->as = ABL; + p->from = zprg.from; + p->to = zprg.to; + p->to.type = D_BRANCH; + p->cond = ps4; + p->to.sym = s4; + + p = q; + + continue; + } + } +} diff --git a/src/cmd/5l/softfloat.c b/src/cmd/5l/softfloat.c index 82874ee1c..fd66b0969 100644 --- a/src/cmd/5l/softfloat.c +++ b/src/cmd/5l/softfloat.c @@ -5,67 +5,82 @@ #define EXTERN #include "l.h" +// Software floating point. + void -softfloat() +softfloat(void) { Prog *p, *next, *psfloat; Sym *symsfloat; int wasfloat; - + + if(!debug['F']) + return; + symsfloat = lookup("_sfloat", 0); psfloat = P; if(symsfloat->type == STEXT) - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - if(p->from.sym == symsfloat) { - psfloat = p; - break; - } - } - } + psfloat = symsfloat->text; + + for(cursym = textp; cursym != nil; cursym = cursym->next) { + wasfloat = 0; + for(p = cursym->text; p != P; p = p->link) + if(p->cond != P) + p->cond->mark |= LABEL; + for(p = cursym->text; p != P; p = p->link) { + switch(p->as) { + case AMOVW: + if(p->to.type == D_FREG || p->from.type == D_FREG) + goto soft; + goto notsoft; + + case AMOVWD: + case AMOVWF: + case AMOVDW: + case AMOVFW: + case AMOVFD: + case AMOVDF: + case AMOVF: + case AMOVD: + + case ACMPF: + case ACMPD: + case AADDF: + case AADDD: + case ASUBF: + case ASUBD: + case AMULF: + case AMULD: + case ADIVF: + case ADIVD: + goto soft; - wasfloat = 0; - p = firstp; - for(p = firstp; p != P; p = p->link) { - switch(p->as) { - case AMOVWD: - case AMOVWF: - case AMOVDW: - case AMOVFW: - case AMOVFD: - case AMOVDF: - case AMOVF: - case AMOVD: - case ACMPF: - case ACMPD: - case AADDF: - case AADDD: - case ASUBF: - case ASUBD: - case AMULF: - case AMULD: - case ADIVF: - case ADIVD: - if (psfloat == P) - diag("floats used with _sfloat not defined"); - if (!wasfloat) { - next = prg(); - *next = *p; + default: + goto notsoft; - // BL _sfloat(SB) - *p = zprg; - p->link = next; - p->as = ABL; - p->to.type = D_BRANCH; - p->to.sym = symsfloat; - p->cond = psfloat; + soft: + if (psfloat == P) + diag("floats used with _sfloat not defined"); + if (!wasfloat || (p->mark&LABEL)) { + next = prg(); + *next = *p; + + // BL _sfloat(SB) + *p = zprg; + p->link = next; + p->as = ABL; + p->to.type = D_BRANCH; + p->to.sym = symsfloat; + p->cond = psfloat; + + p = next; + wasfloat = 1; + } + break; - p = next; - wasfloat = 1; + notsoft: + wasfloat = 0; } - break; - default: - wasfloat = 0; } } } diff --git a/src/cmd/5l/span.c b/src/cmd/5l/span.c index a97af07f9..be0f5e8b3 100644 --- a/src/cmd/5l/span.c +++ b/src/cmd/5l/span.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Instruction layout. + #include "l.h" #include "../ld/lib.h" @@ -52,9 +54,9 @@ ispad(Prog *p) { if(p->as != AMOVW) return 0; - if(p->from.type != D_REG || p->from.reg != REGSB) + if(p->from.type != D_REG || p->from.reg != REGTMP) return 0; - if(p->to.type != D_REG || p->to.reg != REGSB) + if(p->to.type != D_REG || p->to.reg != REGTMP) return 0; return 1; } @@ -100,7 +102,7 @@ fnpinc(Sym *s) if(!debug['f']) diag("fnptr == 0 in fnpinc"); if(s->foreign) - diag("bad usage in fnpinc %s %d %d %d", s->name, s->used, s->foreign, s->thumb); + diag("bad usage in fnpinc %s %d %d", s->name, s->foreign, s->thumb); return 0; } /* 0, 1, 2, 3 squared */ @@ -119,9 +121,9 @@ pad(Prog *p, int pc) q->as = AMOVW; q->line = p->line; q->from.type = D_REG; - q->from.reg = REGSB; + q->from.reg = REGTMP; q->to.type = D_REG; - q->to.reg = REGSB; + q->to.reg = REGTMP; q->pc = pc; q->link = p->link; return q; @@ -132,7 +134,7 @@ scan(Prog *op, Prog *p, int c) { Prog *q; - for(q = op->link; q != p; q = q->link){ + for(q = op->link; q != p && q != P; q = q->link){ q->pc = c; c += oplook(q)->size; nocache(q); @@ -163,11 +165,12 @@ void span(void) { Prog *p, *op; - Sym *setext, *s; Optab *o; - int m, bflag, i; - int32 c, otxt, v; + int m, bflag, i, v; + int32 c, otxt, out[6]; int lastthumb = -1; + Section *sect; + uchar *bp; if(debug['v']) Bprint(&bso, "%5.2f span\n", cputime()); @@ -176,61 +179,65 @@ span(void) bflag = 0; c = INITTEXT; op = nil; + p = nil; otxt = c; - for(p = firstp; p != P; op = p, p = p->link) { + for(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; setarch(p); p->pc = c; - o = oplook(p); - m = o->size; - // must check literal pool here in case p generates many instructions - if(blitrl){ - if(thumb && isbranch(p)) - pool.extra += brextra(p); - if(checkpool(op, p->as == ACASE ? casesz(p) : m)) - c = p->pc = scan(op, p, c); - } - if(m == 0) { - if(p->as == ATEXT) { - if(blitrl && lastthumb != -1 && lastthumb != thumb){ // flush literal pool - if(flushpool(op, 0, 1)) - c = p->pc = scan(op, p, c); - } - lastthumb = thumb; - curtext = p; - autosize = p->to.offset + 4; - if(p->from.sym != S) - p->from.sym->value = c; - /* need passes to resolve branches */ - if(c-otxt >= 1L<<17) - bflag = 1; - otxt = c; - if(thumb && blitrl) + cursym->value = c; + + lastthumb = thumb; + autosize = p->to.offset + 4; + if(p->from.sym != S) + p->from.sym->value = c; + /* need passes to resolve branches */ + if(c-otxt >= 1L<<17) + bflag = 1; + otxt = c; + if(thumb && blitrl) + pool.extra += brextra(p); + + for(op = p, p = p->link; p != P; op = p, p = p->link) { + curp = p; + setarch(p); + p->pc = c; + o = oplook(p); + m = o->size; + // must check literal pool here in case p generates many instructions + if(blitrl){ + if(thumb && isbranch(p)) pool.extra += brextra(p); + if(checkpool(op, p->as == ACASE ? casesz(p) : m)) + c = p->pc = scan(op, p, c); + } + if(m == 0) { + diag("zero-width instruction\n%P", p); continue; } - diag("zero-width instruction\n%P", p); - continue; - } - switch(o->flag & (LFROM|LTO|LPOOL)) { - case LFROM: - addpool(p, &p->from); - break; - case LTO: - addpool(p, &p->to); - break; - case LPOOL: - if ((p->scond&C_SCOND) == 14) + switch(o->flag & (LFROM|LTO|LPOOL)) { + case LFROM: + addpool(p, &p->from); + break; + case LTO: + addpool(p, &p->to); + break; + case LPOOL: + if ((p->scond&C_SCOND) == 14) + flushpool(p, 0, 0); + break; + } + if(p->as==AMOVW && p->to.type==D_REG && p->to.reg==REGPC && (p->scond&C_SCOND) == 14) flushpool(p, 0, 0); - break; + c += m; } - if(p->as==AMOVW && p->to.type==D_REG && p->to.reg==REGPC && (p->scond&C_SCOND) == 14) - flushpool(p, 0, 0); - c += m; - if(blitrl && p->link == P){ - if(thumb && isbranch(p)) - pool.extra += brextra(p); - checkpool(p, 0); + if(blitrl){ + if(thumb && isbranch(op)) + pool.extra += brextra(op); + if(checkpool(op, 0)) + c = scan(op, P, c); } + cursym->size = c - cursym->value; } /* @@ -244,48 +251,52 @@ span(void) Bprint(&bso, "%5.2f span1\n", cputime()); bflag = 0; c = INITTEXT; - for(p = firstp; p != P; p = p->link) { - setarch(p); - p->pc = c; - if(thumb && isbranch(p)) - nocache(p); - o = oplook(p); -/* very larg branches - if(o->type == 6 && p->cond) { - otxt = p->cond->pc - c; - if(otxt < 0) - otxt = -otxt; - if(otxt >= (1L<<17) - 10) { - q = prg(); - q->link = p->link; - p->link = q; - q->as = AB; - q->to.type = D_BRANCH; - q->cond = p->cond; - p->cond = q; - q = prg(); - q->link = p->link; - p->link = q; - q->as = AB; - q->to.type = D_BRANCH; - q->cond = q->link->link; - bflag = 1; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + cursym->value = c; + for(p = cursym->text; p != P; p = p->link) { + curp = p; + setarch(p); + p->pc = c; + if(thumb && isbranch(p)) + nocache(p); + o = oplook(p); +/* very large branches + if(o->type == 6 && p->cond) { + otxt = p->cond->pc - c; + if(otxt < 0) + otxt = -otxt; + if(otxt >= (1L<<17) - 10) { + q = prg(); + q->link = p->link; + p->link = q; + q->as = AB; + q->to.type = D_BRANCH; + q->cond = p->cond; + p->cond = q; + q = prg(); + q->link = p->link; + p->link = q; + q->as = AB; + q->to.type = D_BRANCH; + q->cond = q->link->link; + bflag = 1; + } } - } */ - m = o->size; - if(m == 0) { - if(p->as == ATEXT) { - curtext = p; - autosize = p->to.offset + 4; - if(p->from.sym != S) - p->from.sym->value = c; + m = o->size; + if(m == 0) { + if(p->as == ATEXT) { + autosize = p->to.offset + 4; + if(p->from.sym != S) + p->from.sym->value = c; + continue; + } + diag("zero-width instruction\n%P", p); continue; } - diag("zero-width instruction\n%P", p); - continue; + c += m; } - c += m; + cursym->size = c - cursym->value; } } @@ -304,107 +315,94 @@ span(void) c = INITTEXT; oop = op = nil; again = 0; - for(p = firstp; p != P; oop = op, op = p, p = p->link){ - setarch(p); - if(p->pc != c) - again = 1; - p->pc = c; - if(thumb && isbranch(p)) - nocache(p); - o = oplook(p); - m = o->size; - if(passes == 1 && thumb && isbranch(p)){ // start conservative so unneeded alignment is not added - if(p->as == ABL) - m = 4; - else - m = 2; - p->align = 0; - } - if(p->align){ - if((p->align == 4 && (c&3)) || (p->align == 2 && !(c&3))){ - if(ispad(op)){ - oop->link = p; - op = oop; - c -= 2; - p->pc = c; - } - else{ - op->link = pad(op, c); - op = op->link; - c += 2; - p->pc = c; - } + for(cursym = textp; cursym != nil; cursym = cursym->next) { + cursym->value = c; + for(p = cursym->text; p != P; oop = op, op = p, p = p->link) { + curp = p; + setarch(p); + if(p->pc != c) again = 1; + p->pc = c; + if(thumb && isbranch(p)) + nocache(p); + o = oplook(p); + m = o->size; + if(passes == 1 && thumb && isbranch(p)){ // start conservative so unneeded alignment is not added + if(p->as == ABL) + m = 4; + else + m = 2; + p->align = 0; } - } - if(m == 0) { - if(p->as == ATEXT) { - curtext = p; - autosize = p->to.offset + 4; - if(p->from.sym != S) - p->from.sym->value = c; - continue; + if(p->align){ + if((p->align == 4 && (c&3)) || (p->align == 2 && !(c&3))){ + if(ispad(op)){ + oop->link = p; + op = oop; + c -= 2; + p->pc = c; + } + else{ + op->link = pad(op, c); + op = op->link; + c += 2; + p->pc = c; + } + again = 1; + } + } + if(m == 0) { + if(p->as == ATEXT) { + autosize = p->to.offset + 4; + if(p->from.sym != S) + p->from.sym->value = c; + continue; + } } + c += m; } - c += m; + cursym->size = c - cursym->value; } if(c != lastc || again){ lastc = c; goto loop; } } - - if(0 && seenthumb){ // rm redundant padding - obsolete - int d; - - op = nil; - d = 0; - for(p = firstp; p != P; op = p, p = p->link){ - p->pc -= d; - if(p->as == ATEXT){ - if(p->from.sym != S) - p->from.sym->value -= d; -// if(p->from.sym != S) print("%s %ux %d %d %d\n", p->from.sym->name ? p->from.sym->name : "?", p->from.sym->value, p->from.sym->thumb, p->from.sym->foreign, p->from.sym->fnptr); - } - if(ispad(p) && p->link != P && ispad(p->link)){ - op->link = p->link->link; - d += 4; - p = op; + c = rnd(c, 8); + + /* + * lay out the code. all the pc-relative code references, + * even cross-function, are resolved now; + * only data references need to be relocated. + * with more work we could leave cross-function + * code references to be relocated too, and then + * perhaps we'd be able to parallelize the span loop above. + */ + for(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; + setarch(p); + autosize = p->to.offset + 4; + symgrow(cursym, cursym->size); + + bp = cursym->p; + for(p = p->link; p != P; p = p->link) { + curp = p; + pc = p->pc; + curp = p; + o = oplook(p); + asmout(p, o, out); + for(i=0; isize/4; i++) { + v = out[i]; + *bp++ = v; + *bp++ = v>>8; + *bp++ = v>>16; + *bp++ = v>>24; } } - // print("%d bytes removed (padding)\n", d); - c -= d; } - - if(debug['t']) { - /* - * add strings to text segment - */ - c = rnd(c, 8); - for(i=0; ilink) { - if(s->type != SSTRING) - continue; - v = s->value; - while(v & 3) - v++; - s->value = c; - c += v; - } - } - - c = rnd(c, 8); - - setext = lookup("etext", 0); - if(setext != S) { - setext->value = c; - textsize = c - INITTEXT; - } - if(INITRND) - INITDAT = rnd(c, INITRND); - if(debug['v']) - Bprint(&bso, "tsize = %lux\n", textsize); - Bflush(&bso); + sect = addsection(&segtext, ".text", 05); + sect->vaddr = INITTEXT; + sect->len = c - INITTEXT; } /* @@ -437,7 +435,7 @@ flushpool(Prog *p, int skip, int force) if(blitrl) { if(skip){ - if(0 && skip==1)print("note: flush literal pool at %lux: len=%lud ref=%lux\n", p->pc+4, pool.size, pool.start); + if(0 && skip==1)print("note: flush literal pool at %ux: len=%ud ref=%ux\n", p->pc+4, pool.size, pool.start); q = prg(); q->as = AB; q->to.type = D_BRANCH; @@ -523,10 +521,10 @@ xdefine(char *p, int t, int32 v) Sym *s; s = lookup(p, 0); - if(s->type == 0 || s->type == SXREF) { - s->type = t; - s->value = v; - } + s->type = t; + s->value = v; + s->reachable = 1; + s->special = 1; } int32 @@ -583,6 +581,38 @@ immhalf(int32 v) return 0; } +int32 +symaddr(Sym *s) +{ + int32 v; + + v = s->value; + switch(s->type) { + default: + diag("unexpected type %d in symaddr(%s)", s->type, s->name); + return 0; + + case STEXT: +/* TODO(rsc): what is this for? +#ifdef CALLEEBX + v += fnpinc(s); +#else + if(s->thumb) + v++; // T bit +#endif +*/ + break; + + case SELFDATA: + case SRODATA: + case SDATA: + case SBSS: + case SCONST: + break; + } + return v; +} + int aclass(Adr *a) { @@ -619,36 +649,9 @@ aclass(Adr *a) } s = a->sym; t = s->type; - if(t == 0 || t == SXREF) { - diag("undefined external: %s in %s", - s->name, TNAME); - s->type = SDATA; - } - if(dlm) { - switch(t) { - default: - instoffset = s->value + a->offset + INITDAT; - break; - case SUNDEF: - case STEXT: - case SCONST: - case SLEAF: - case SSTRING: - instoffset = s->value + a->offset; - break; - } - return C_ADDR; - } - instoffset = s->value + a->offset - BIG; - t = immaddr(instoffset); - if(t) { - if(immhalf(instoffset)) - return immfloat(t) ? C_HFEXT : C_HEXT; - if(immfloat(t)) - return C_FEXT; - return C_SEXT; - } - return C_LEXT; + instoffset = 0; // s.b. unused but just in case + return C_ADDR; + case D_AUTO: instoffset = autosize + a->offset; t = immaddr(instoffset); @@ -703,32 +706,17 @@ aclass(Adr *a) case D_STATIC: s = a->sym; t = s->type; - if(t == 0 || t == SXREF) { - diag("undefined external: %s in %s", - s->name, TNAME); - s->type = SDATA; - } - if(s->type == SFIXED) { - instoffset = s->value + a->offset; - return C_LCON; - } - instoffset = s->value + a->offset + INITDAT; - if(s->type == STEXT || s->type == SLEAF || s->type == SUNDEF) { - instoffset = s->value + a->offset; -#ifdef CALLEEBX - instoffset += fnpinc(s); -#else - if(s->thumb) - instoffset++; // T bit -#endif - return C_LCON; - } - return C_LCON; + instoffset = 0; // s.b. unused but just in case + return C_ADDR; } return C_GOK; case D_FCONST: - return C_FCON; + if(chipzero(&a->ieee) >= 0) + return C_ZFCON; + if(chipfloat(&a->ieee) >= 0) + return C_SFCON; + return C_LFCON; case D_CONST: case D_CONST2: @@ -753,37 +741,7 @@ aclass(Adr *a) if(s == S) break; t = s->type; - switch(t) { - case 0: - case SXREF: - diag("undefined external: %s in %s", - s->name, TNAME); - s->type = SDATA; - break; - case SFIXED: - instoffset = s->value + a->offset; - return C_LCON; - case SUNDEF: - case STEXT: - case SSTRING: - case SCONST: - case SLEAF: - instoffset = s->value + a->offset; -#ifdef CALLEEBX - instoffset += fnpinc(s); -#else - if(s->thumb) - instoffset++; // T bit -#endif - return C_LCON; - } - if(!dlm) { - instoffset = s->value + a->offset - BIG; - t = immrot(instoffset); - if(t && instoffset != 0) - return C_RECON; - } - instoffset = s->value + a->offset + INITDAT; + instoffset = 0; // s.b. unused but just in case return C_LCON; case D_AUTO: @@ -895,21 +853,11 @@ cmp(int a, int b) if(b == C_RACON) return 1; break; - case C_LECON: - if(b == C_RECON) + case C_LFCON: + if(b == C_ZFCON || b == C_SFCON) return 1; break; - case C_HFEXT: - return b == C_HEXT || b == C_FEXT; - case C_FEXT: - case C_HEXT: - return b == C_HFEXT; - case C_SEXT: - return cmp(C_HFEXT, b); - case C_LEXT: - return cmp(C_SEXT, b); - case C_HFAUTO: return b == C_HAUTO || b == C_FAUTO; case C_FAUTO: @@ -1091,17 +1039,20 @@ buildop(void) break; case AMOVFW: - oprange[AMOVWF] = oprange[r]; - oprange[AMOVWD] = oprange[r]; oprange[AMOVDW] = oprange[r]; break; + case AMOVWF: + oprange[AMOVWD] = oprange[r]; + break; + case AMULL: oprange[AMULA] = oprange[r]; oprange[AMULAL] = oprange[r]; oprange[AMULLU] = oprange[r]; oprange[AMULALU] = oprange[r]; break; + case ALDREX: case ASTREX: break; @@ -1146,159 +1097,3 @@ buildrep(int x, int as) oprange[as].start = 0; } */ - -enum{ - ABSD = 0, - ABSU = 1, - RELD = 2, - RELU = 3, -}; - -int modemap[4] = { 0, 1, -1, 2, }; - -typedef struct Reloc Reloc; - -struct Reloc -{ - int n; - int t; - uchar *m; - uint32 *a; -}; - -Reloc rels; - -static void -grow(Reloc *r) -{ - int t; - uchar *m, *nm; - uint32 *a, *na; - - t = r->t; - r->t += 64; - m = r->m; - a = r->a; - r->m = nm = malloc(r->t*sizeof(uchar)); - r->a = na = malloc(r->t*sizeof(uint32)); - memmove(nm, m, t*sizeof(uchar)); - memmove(na, a, t*sizeof(uint32)); - free(m); - free(a); -} - -void -dynreloc(Sym *s, int32 v, int abs) -{ - int i, k, n; - uchar *m; - uint32 *a; - Reloc *r; - - if(v&3) - diag("bad relocation address"); - v >>= 2; - if(s != S && s->type == SUNDEF) - k = abs ? ABSU : RELU; - else - k = abs ? ABSD : RELD; - /* Bprint(&bso, "R %s a=%ld(%lx) %d\n", s->name, a, a, k); */ - k = modemap[k]; - r = &rels; - n = r->n; - if(n >= r->t) - grow(r); - m = r->m; - a = r->a; - for(i = n; i > 0; i--){ - if(v < a[i-1]){ /* happens occasionally for data */ - m[i] = m[i-1]; - a[i] = a[i-1]; - } - else - break; - } - m[i] = k; - a[i] = v; - r->n++; -} - -static int -sput(char *s) -{ - char *p; - - p = s; - while(*s) - cput(*s++); - cput(0); - return s-p+1; -} - -void -asmdyn() -{ - int i, n, t, c; - Sym *s; - uint32 la, ra, *a; - vlong off; - uchar *m; - Reloc *r; - - cflush(); - off = seek(cout, 0, 1); - lput(0); - t = 0; - lput(imports); - t += 4; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->type == SUNDEF){ - lput(s->sig); - t += 4; - t += sput(s->name); - } - - la = 0; - r = &rels; - n = r->n; - m = r->m; - a = r->a; - lput(n); - t += 4; - for(i = 0; i < n; i++){ - ra = *a-la; - if(*a < la) - diag("bad relocation order"); - if(ra < 256) - c = 0; - else if(ra < 65536) - c = 1; - else - c = 2; - cput((c<<6)|*m++); - t++; - if(c == 0){ - cput(ra); - t++; - } - else if(c == 1){ - wput(ra); - t += 2; - } - else{ - lput(ra); - t += 4; - } - la = *a++; - } - - cflush(); - seek(cout, off, 0); - lput(t); - - if(debug['v']){ - Bprint(&bso, "import table entries = %d\n", imports); - Bprint(&bso, "export table entries = %d\n", exports); - } -} diff --git a/src/cmd/5l/thumb.c b/src/cmd/5l/thumb.c index 97292f640..b2ba630c3 100644 --- a/src/cmd/5l/thumb.c +++ b/src/cmd/5l/thumb.c @@ -203,9 +203,8 @@ thumbaclass(Adr *a, Prog *p) a->sym->name, TNAME); a->sym->type = SDATA; } - instoffset = a->sym->value + a->offset + INITDAT; - return C_LEXT; /* INITDAT unknown at this stage */ - // return immacon(instoffset, p, C_SEXT, C_LEXT); + instoffset = a->sym->value + a->offset; + return C_ADDR; /* INITDAT unknown at this stage */ case D_AUTO: instoffset = autosize + a->offset; return immauto(instoffset, p); @@ -235,8 +234,8 @@ thumbaclass(Adr *a, Prog *p) s->name, TNAME); s->type = SDATA; } - instoffset = s->value + a->offset + INITDAT; - if(s->type == STEXT || s->type == SLEAF){ + instoffset = s->value + a->offset; + if(s->type == STEXT){ instoffset = s->value + a->offset; #ifdef CALLEEBX instoffset += fnpinc(s); @@ -252,7 +251,7 @@ thumbaclass(Adr *a, Prog *p) return C_GOK; case D_FCONST: diag("D_FCONST in thumaclass"); - return C_FCON; + return C_LFCON; case D_CONST: switch(a->name) { case D_NONE: @@ -275,7 +274,6 @@ thumbaclass(Adr *a, Prog *p) break; case SCONST: case STEXT: - case SLEAF: instoffset = s->value + a->offset; #ifdef CALLEEBX instoffset += fnpinc(s); @@ -285,7 +283,7 @@ thumbaclass(Adr *a, Prog *p) #endif return C_LCON; } - instoffset = s->value + a->offset + INITDAT; + instoffset = s->value + a->offset; return C_LCON; /* INITDAT unknown at this stage */ // return immcon(instoffset, p); case D_AUTO: @@ -358,8 +356,8 @@ thumbaclass(Adr *a, Prog *p) // as a1 a2 a3 type size param lit vers Optab thumboptab[] = { - { ATEXT, C_LEXT, C_NONE, C_LCON, 0, 0, 0 }, - { ATEXT, C_LEXT, C_REG, C_LCON, 0, 0, 0 }, + { ATEXT, C_ADDR, C_NONE, C_LCON, 0, 0, 0 }, + { ATEXT, C_ADDR, C_REG, C_LCON, 0, 0, 0 }, { AMVN, C_REG, C_NONE, C_REG, 1, 2, 0 }, { ASRL, C_REG, C_NONE, C_REG, 1, 2, 0 }, { ACMP, C_REG, C_REG, C_NONE, 1, 2, 0 }, @@ -412,37 +410,27 @@ Optab thumboptab[] = { ASWI, C_NONE, C_NONE, C_LCON, 16, 2, 0 }, { AWORD, C_NONE, C_NONE, C_LCON, 17, 4, 0 }, { AWORD, C_NONE, C_NONE, C_GCON, 17, 4, 0 }, - { AWORD, C_NONE, C_NONE, C_LEXT, 17, 4, 0 }, + { AWORD, C_NONE, C_NONE, C_ADDR, 17, 4, 0 }, { ADWORD, C_LCON, C_NONE, C_LCON, 50, 8, 0 }, { AMOVW, C_SAUTO, C_NONE, C_REG, 18, 2, REGSP }, { AMOVW, C_LAUTO, C_NONE, C_REG, 33, 6, 0, LFROM }, // { AMOVW, C_OFFPC, C_NONE, C_REG, 18, 2, REGPC, LFROM }, - { AMOVW, C_SEXT, C_NONE, C_REG, 30, 4, 0 }, { AMOVW, C_SOREG, C_NONE, C_REG, 19, 2, 0 }, - { AMOVHU, C_SEXT, C_NONE, C_REG, 30, 4, 0 }, { AMOVHU, C_SOREG, C_NONE, C_REG, 19, 2, 0 }, - { AMOVBU, C_SEXT, C_NONE, C_REG, 30, 4, 0 }, { AMOVBU, C_SOREG, C_NONE, C_REG, 19, 2, 0 }, { AMOVW, C_REG, C_NONE, C_SAUTO, 20, 2, 0 }, { AMOVW, C_REG, C_NONE, C_LAUTO, 34, 6, 0, LTO }, - { AMOVW, C_REG, C_NONE, C_SEXT, 31, 4, 0 }, { AMOVW, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVH, C_REG, C_NONE, C_SEXT, 31, 4, 0 }, { AMOVH, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVB, C_REG, C_NONE, C_SEXT, 31, 4, 0 }, { AMOVB, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVHU, C_REG, C_NONE, C_SEXT, 31, 4, 0 }, { AMOVHU, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVBU, C_REG, C_NONE, C_SEXT, 31, 4, 0 }, { AMOVBU, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, { AMOVW, C_REG, C_NONE, C_REG, 22, 2, 0 }, { AMOVB, C_REG, C_NONE, C_REG, 23, 4, 0 }, { AMOVH, C_REG, C_NONE, C_REG, 23, 4, 0 }, { AMOVBU, C_REG, C_NONE, C_REG, 23, 4, 0 }, { AMOVHU, C_REG, C_NONE, C_REG, 23, 4, 0 }, - { AMOVH, C_SEXT, C_NONE, C_REG, 32, 6, 0 }, { AMOVH, C_SOREG, C_NONE, C_REG, 24, 4, 0 }, - { AMOVB, C_SEXT, C_NONE, C_REG, 32, 6, 0 }, { AMOVB, C_SOREG, C_NONE, C_REG, 24, 4, 0 }, { AMOVW, C_SACON, C_NONE, C_REG, 25, 2, 0 }, { AMOVW, C_LACON, C_NONE, C_REG, 35, 4, 0 }, @@ -469,16 +457,16 @@ Optab thumboptab[] = { AMOVB, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO }, { AMOVHU, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO }, { AMOVBU, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO }, - { AMOVW, C_LEXT, C_NONE, C_REG, 30, 4, 0, LFROM }, - { AMOVH, C_LEXT, C_NONE, C_REG, 32, 6, 0, LFROM }, - { AMOVB, C_LEXT, C_NONE, C_REG, 32, 6, 0, LFROM }, - { AMOVHU, C_LEXT, C_NONE, C_REG, 30, 4, 0, LFROM }, - { AMOVBU, C_LEXT, C_NONE, C_REG, 30, 4, 0, LFROM }, - { AMOVW, C_REG, C_NONE, C_LEXT, 31, 4, 0, LTO }, - { AMOVH, C_REG, C_NONE, C_LEXT, 31, 4, 0, LTO }, - { AMOVB, C_REG, C_NONE, C_LEXT, 31, 4, 0, LTO }, - { AMOVHU, C_REG, C_NONE, C_LEXT, 31, 4, 0, LTO }, - { AMOVBU, C_REG, C_NONE, C_LEXT, 31, 4, 0, LTO }, + { AMOVW, C_ADDR, C_NONE, C_REG, 30, 4, 0, LFROM }, + { AMOVH, C_ADDR, C_NONE, C_REG, 32, 6, 0, LFROM }, + { AMOVB, C_ADDR, C_NONE, C_REG, 32, 6, 0, LFROM }, + { AMOVHU, C_ADDR, C_NONE, C_REG, 30, 4, 0, LFROM }, + { AMOVBU, C_ADDR, C_NONE, C_REG, 30, 4, 0, LFROM }, + { AMOVW, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, + { AMOVH, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, + { AMOVB, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, + { AMOVHU, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, + { AMOVBU, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, { AXXX, C_NONE, C_NONE, C_NONE, 0, 2, 0 }, }; @@ -683,7 +671,7 @@ thumbasmout(Prog *p, Optab *o) rt = p->to.reg; r = p->reg; o1 = o2 = o3 = o4 = o5 = o6 = o7 = 0; -if(debug['P']) print("%ulx: %P type %d %d\n", (uint32)(p->pc), p, o->type, p->align); +if(debug['P']) print("%ux: %P type %d %d\n", (uint32)(p->pc), p, o->type, p->align); opcount[o->type] += o->size; switch(o->type) { default: @@ -691,7 +679,7 @@ if(debug['P']) print("%ulx: %P type %d %d\n", (uint32)(p->pc), p, o->type, p->al prasm(p); break; case 0: /* pseudo ops */ -if(debug['G']) print("%ulx: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); +if(debug['G']) print("%ux: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); break; case 1: /* op R, -, R or op R, R, - */ o1 = thumboprr(p->as); @@ -981,6 +969,7 @@ if(debug['G']) print("%ulx: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); } break; case 30: /* AMOVW... *addr, R */ + diag("likely broken"); // does this still refer to SB? thumbaclass(&p->from, p); o1 = mv(p, rt, instoffset); // MOV addr, rtmp o2 = thumbopmv(p->as, 1); @@ -988,6 +977,7 @@ if(debug['G']) print("%ulx: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); o2 |= (rt<<3) | rt; // MOV* 0(rtmp), R break; case 31: /* AMOVW... R, *addr */ + diag("likely broken"); // does this still refer to SB? thumbaclass(&p->to, p); o1 = mv(p, REGTMPT, instoffset); o2 = thumbopmv(p->as, 0); @@ -1162,29 +1152,29 @@ if(debug['G']) print("%ulx: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); switch(o->size) { default: if(debug['a']) - Bprint(&bso, " %.8lux:\t\t%P\n", v, p); + Bprint(&bso, " %.8ux:\t\t%P\n", v, p); break; case 2: if(debug['a']) - Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p); + Bprint(&bso, " %.8ux: %.8ux\t%P\n", v, o1, p); hputl(o1); break; case 4: if(debug['a']) - Bprint(&bso, " %.8lux: %.8lux %.8lux\t%P\n", v, o1, o2, p); + Bprint(&bso, " %.8ux: %.8ux %.8ux\t%P\n", v, o1, o2, p); hputl(o1); hputl(o2); break; case 6: if(debug['a']) - Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, p); + Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, p); hputl(o1); hputl(o2); hputl(o3); break; case 8: if(debug['a']) - Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, o4, p); + Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, p); hputl(o1); hputl(o2); hputl(o3); @@ -1192,7 +1182,7 @@ if(debug['G']) print("%ulx: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); break; case 10: if(debug['a']) - Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, o4, o5, p); + Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, o5, p); hputl(o1); hputl(o2); hputl(o3); @@ -1201,7 +1191,7 @@ if(debug['G']) print("%ulx: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); break; case 12: if(debug['a']) - Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, o4, o5, o6, p); + Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, o5, o6, p); hputl(o1); hputl(o2); hputl(o3); @@ -1211,7 +1201,7 @@ if(debug['G']) print("%ulx: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); break; case 14: if(debug['a']) - Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, o4, o5, o6, o7, p); + Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, o5, o6, o7, p); hputl(o1); hputl(o2); hputl(o3); @@ -1223,12 +1213,12 @@ if(debug['G']) print("%ulx: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); } if(debug['G']){ if(o->type == 17){ - print("%lx: word %ld\n", p->pc, (o2<<16)+o1); + print("%x: word %d\n", p->pc, (o2<<16)+o1); return; } if(o->type == 50){ - print("%lx: word %ld\n", p->pc, (o2<<16)+o1); - print("%lx: word %ld\n", p->pc, (o4<<16)+o3); + print("%x: word %d\n", p->pc, (o2<<16)+o1); + print("%x: word %d\n", p->pc, (o4<<16)+o3); return; } if(o->size > 0) dis(o1, p->pc); diff --git a/src/cmd/6a/Makefile b/src/cmd/6a/Makefile index 21d824708..30180bd24 100644 --- a/src/cmd/6a/Makefile +++ b/src/cmd/6a/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 6a\ +TARG=6a HFILES=\ a.h\ @@ -20,21 +20,6 @@ OFILES=\ YFILES=\ a.y\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lbio -l9 - -$(OFILES): $(HFILES) +include ../../Make.ccmd lex.$O: ../cc/macbody ../cc/lexbody - -y.tab.h: $(YFILES) - bison -y $(YFLAGS) $(YFILES) - -y.tab.c: y.tab.h - test -f y.tab.c && touch y.tab.c - -clean: - rm -f *.$O $(TARG) *.6 enam.c 6.out a.out y.tab.h y.tab.c - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) diff --git a/src/cmd/6a/a.h b/src/cmd/6a/a.h index a713acc9f..9030081ca 100644 --- a/src/cmd/6a/a.h +++ b/src/cmd/6a/a.h @@ -1,7 +1,7 @@ // Inferno utils/6a/a.h // http://code.google.com/p/inferno-os/source/browse/utils/6a/a.h // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -50,7 +50,7 @@ typedef struct Ref Ref; typedef struct Gen Gen; typedef struct Io Io; typedef struct Hist Hist; -typedef struct Gen2 Gen2; +typedef struct Gen2 Gen2; #define MAXALIGN 7 #define FPCHIP 1 @@ -161,6 +161,7 @@ EXTERN int pass; EXTERN char* pathname; EXTERN int32 pc; EXTERN int peekc; +EXTERN int32 stmtline; EXTERN int sym; EXTERN char* symb; EXTERN int thechar; @@ -168,9 +169,9 @@ EXTERN char* thestring; EXTERN int32 thunk; EXTERN Biobuf obuf; -void* alloc(int32); +void* alloc(int32); void* allocn(void*, int32, int32); -void ensuresymb(int32); +void ensuresymb(int32); void errorexit(void); void pushio(void); void newio(void); diff --git a/src/cmd/6a/a.y b/src/cmd/6a/a.y index 804f638a0..770f676fe 100644 --- a/src/cmd/6a/a.y +++ b/src/cmd/6a/a.y @@ -1,7 +1,7 @@ // Inferno utils/6a/a.y // http://code.google.com/p/inferno-os/source/browse/utils/6a/a.y // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -60,7 +60,11 @@ %type spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 spec9 %% prog: -| prog line +| prog + { + stmtline = lineno; + } + line line: LLAB ':' @@ -453,6 +457,12 @@ omem: $$.type = D_INDIR+D_SP; $$.offset = $1; } +| con '(' LSREG ')' + { + $$ = nullgen; + $$.type = D_INDIR+$3; + $$.offset = $1; + } | con '(' LLREG '*' con ')' { $$ = nullgen; diff --git a/src/cmd/6a/lex.c b/src/cmd/6a/lex.c index 81273b297..1b8bb6344 100644 --- a/src/cmd/6a/lex.c +++ b/src/cmd/6a/lex.c @@ -1,7 +1,7 @@ // Inferno utils/6a/lex.c // http://code.google.com/p/inferno-os/source/browse/utils/6a/lex.c // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -1260,10 +1260,10 @@ jackpot: } Bputc(&obuf, a); Bputc(&obuf, a>>8); - Bputc(&obuf, lineno); - Bputc(&obuf, lineno>>8); - Bputc(&obuf, lineno>>16); - Bputc(&obuf, lineno>>24); + Bputc(&obuf, stmtline); + Bputc(&obuf, stmtline>>8); + Bputc(&obuf, stmtline>>16); + Bputc(&obuf, stmtline>>24); zaddr(&g2->from, sf); zaddr(&g2->to, st); diff --git a/src/cmd/6c/Makefile b/src/cmd/6c/Makefile index 53a8e80e6..484e16def 100644 --- a/src/cmd/6c/Makefile +++ b/src/cmd/6c/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 6c\ +TARG=6c HFILES=\ gc.h\ @@ -28,18 +28,9 @@ OFILES=\ ../6l/enam.$O\ LIB=\ - ../cc/cc.a$O + ../cc/cc.a\ -$(TARG): $(OFILES) $(LIB) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) $(LIB) -lbio -l9 - -$(OFILES): $(HFILES) - -clean: - rm -f *.$O $(TARG) *.6 enam.c 6.out a.out - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +include ../../Make.ccmd %.$O: ../cc/%.c - $(CC) $(CFLAGS) -c -I. -o $@ ../cc/$*.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../cc/$*.c diff --git a/src/cmd/6c/cgen.c b/src/cmd/6c/cgen.c index 39452c989..90394884f 100644 --- a/src/cmd/6c/cgen.c +++ b/src/cmd/6c/cgen.c @@ -57,6 +57,12 @@ cgen(Node *n, Node *nn) l = n->left; r = n->right; o = n->op; + + if(n->op == OEXREG || (nn != Z && nn->op == OEXREG)) { + gmove(n, nn); + return; + } + if(n->addable >= INDEXED) { if(nn == Z) { switch(o) { @@ -1922,7 +1928,7 @@ vaddr(Node *n, int a) int32 hi64v(Node *n) { - if(align(0, types[TCHAR], Aarg1)) /* isbigendian */ + if(align(0, types[TCHAR], Aarg1, nil)) /* isbigendian */ return (int32)(n->vconst) & ~0L; else return (int32)((uvlong)n->vconst>>32) & ~0L; @@ -1931,7 +1937,7 @@ hi64v(Node *n) int32 lo64v(Node *n) { - if(align(0, types[TCHAR], Aarg1)) /* isbigendian */ + if(align(0, types[TCHAR], Aarg1, nil)) /* isbigendian */ return (int32)((uvlong)n->vconst>>32) & ~0L; else return (int32)(n->vconst) & ~0L; diff --git a/src/cmd/6c/div.c b/src/cmd/6c/div.c index d67ef8df7..bad6c5e27 100644 --- a/src/cmd/6c/div.c +++ b/src/cmd/6c/div.c @@ -120,7 +120,7 @@ sdivgen(Node *l, Node *r, Node *ax, Node *dx) if(c < 0) c = -c; a = sdiv(c, &m, &s); -//print("a=%d i=%ld s=%d m=%lux\n", a, (long)r->vconst, s, m); +//print("a=%d i=%d s=%d m=%ux\n", a, (long)r->vconst, s, m); gins(AMOVL, nodconst(m), ax); gins(AIMULL, l, Z); gins(AMOVL, l, ax); @@ -141,7 +141,7 @@ udivgen(Node *l, Node *r, Node *ax, Node *dx) Node nod; a = udiv(r->vconst, &m, &s, &t); -//print("a=%ud i=%ld p=%d s=%d m=%lux\n", a, (long)r->vconst, t, s, m); +//print("a=%ud i=%d p=%d s=%d m=%ux\n", a, (long)r->vconst, t, s, m); if(t != 0) { gins(AMOVL, l, ax); gins(ASHRL, nodconst(t), ax); diff --git a/src/cmd/6c/list.c b/src/cmd/6c/list.c index ba517ca0a..4293203c0 100644 --- a/src/cmd/6c/list.c +++ b/src/cmd/6c/list.c @@ -78,22 +78,23 @@ Pconv(Fmt *fp) p = va_arg(fp->args, Prog*); switch(p->as) { case ADATA: - sprint(str, " %A %D/%d,%D", - p->as, &p->from, p->from.scale, &p->to); + sprint(str, "(%L) %A %D/%d,%D", + p->lineno, p->as, &p->from, p->from.scale, &p->to); break; case ATEXT: if(p->from.scale) { - sprint(str, " %A %D,%d,%lD", - p->as, &p->from, p->from.scale, &p->to); + sprint(str, "(%L) %A %D,%d,%lD", + p->lineno, p->as, &p->from, p->from.scale, &p->to); break; } - sprint(str, " %A %D,%lD", - p->as, &p->from, &p->to); + sprint(str, "(%L) %A %D,%lD", + p->lineno, p->as, &p->from, &p->to); break; default: - sprint(str, " %A %D,%lD", p->as, &p->from, &p->to); + sprint(str, "(%L) %A %D,%lD", + p->lineno, p->as, &p->from, &p->to); break; } return fmtstrcpy(fp, str); diff --git a/src/cmd/6c/mul.c b/src/cmd/6c/mul.c index eefeacaed..ab6883e7a 100644 --- a/src/cmd/6c/mul.c +++ b/src/cmd/6c/mul.c @@ -355,7 +355,7 @@ mulgen1(uint32 v, Node *n) mulparam(v, p); found: -// print("v=%.lx a=%d n=%d s=%d g=%d o=%d \n", p->value, p->alg, p->neg, p->shift, p->arg, p->off); +// print("v=%.x a=%d n=%d s=%d g=%d o=%d \n", p->value, p->alg, p->neg, p->shift, p->arg, p->off); if(p->alg < 0) return 0; diff --git a/src/cmd/6c/peep.c b/src/cmd/6c/peep.c index 01793bfc5..13fd25e73 100644 --- a/src/cmd/6c/peep.c +++ b/src/cmd/6c/peep.c @@ -797,8 +797,6 @@ copyu(Prog *p, Adr *v, Adr *s) return 3; case ACALL: /* funny */ - if(REGEXT && v->type <= REGEXT && v->type > exregoffset) - return 2; if(REGARG >= 0 && v->type == REGARG) return 2; diff --git a/src/cmd/6c/reg.c b/src/cmd/6c/reg.c index 431501202..996128f75 100644 --- a/src/cmd/6c/reg.c +++ b/src/cmd/6c/reg.c @@ -448,7 +448,7 @@ regopt(Prog *p) if(debug['R'] && debug['v']) { print("\nlooping structure:\n"); for(r = firstr; r != R; r = r->link) { - print("%ld:%P", r->loop, r->prog); + print("%d:%P", r->loop, r->prog); for(z=0; zuse1.b[z] | r->use2.b[z] | @@ -1113,7 +1113,7 @@ paint1(Reg *r, int bn) if(LOAD(r) & ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])) & bb) { change -= CLOAD * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tld %B $%d\n", r->loop, + print("%d%P\td %B $%d\n", r->loop, r->prog, blsh(bn), change); } for(;;) { @@ -1123,21 +1123,21 @@ paint1(Reg *r, int bn) if(r->use1.b[z] & bb) { change += CREF * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tu1 %B $%d\n", r->loop, + print("%d%P\tu1 %B $%d\n", r->loop, p, blsh(bn), change); } if((r->use2.b[z]|r->set.b[z]) & bb) { change += CREF * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tu2 %B $%d\n", r->loop, + print("%d%P\tu2 %B $%d\n", r->loop, p, blsh(bn), change); } if(STORE(r) & r->regdiff.b[z] & bb) { change -= CLOAD * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tst %B $%d\n", r->loop, + print("%d%P\tst %B $%d\n", r->loop, p, blsh(bn), change); } @@ -1174,7 +1174,7 @@ regset(Reg *r, uint32 bb) while(b = bb & ~(bb-1)) { v.type = b & 0xFFFF? BtoR(b): BtoF(b); if(v.type == 0) - diag(Z, "zero v.type for %#lux", b); + diag(Z, "zero v.type for %#ux", b); c = copyu(r->prog, &v, A); if(c == 3) set |= b; diff --git a/src/cmd/6c/sgen.c b/src/cmd/6c/sgen.c index b8247a1b7..42045f8fa 100644 --- a/src/cmd/6c/sgen.c +++ b/src/cmd/6c/sgen.c @@ -131,6 +131,10 @@ xcom(Node *n) n->addable = 11; break; + case OEXREG: + n->addable = 0; + break; + case OREGISTER: n->addable = 12; break; diff --git a/src/cmd/6c/swt.c b/src/cmd/6c/swt.c index 668a1fdbc..47975a0c8 100644 --- a/src/cmd/6c/swt.c +++ b/src/cmd/6c/swt.c @@ -503,7 +503,7 @@ zaddr(Biobuf *b, Adr *a, int s) } int32 -align(int32 i, Type *t, int op) +align(int32 i, Type *t, int op, int32 *maxalign) { int32 o; Type *v; @@ -517,7 +517,9 @@ align(int32 i, Type *t, int op) break; case Asu2: /* padding at end of a struct */ - w = SZ_VLONG; + w = *maxalign; + if(w < 1) + w = 1; if(packflg) w = packflg; break; @@ -525,10 +527,13 @@ align(int32 i, Type *t, int op) case Ael1: /* initial align of struct element */ for(v=t; v->etype==TARRAY; v=v->link) ; - w = ewidth[v->etype]; - if(w <= 0 || w >= SZ_VLONG) - w = SZ_VLONG; - if(packflg) + if(v->etype == TSTRUCT || v->etype == TUNION) + w = v->align; + else + w = ewidth[v->etype]; + if(w < 1 || w > SZ_VLONG) + fatal(Z, "align"); + if(packflg) w = packflg; break; @@ -538,8 +543,8 @@ align(int32 i, Type *t, int op) case Aarg0: /* initial passbyptr argument in arg list */ if(typesu[t->etype]) { - o = align(o, types[TIND], Aarg1); - o = align(o, types[TIND], Aarg2); + o = align(o, types[TIND], Aarg1, nil); + o = align(o, types[TIND], Aarg2, nil); } break; @@ -560,13 +565,15 @@ align(int32 i, Type *t, int op) break; case Aaut3: /* total align of automatic */ - o = align(o, t, Ael1); - o = align(o, t, Ael2); + o = align(o, t, Ael1, nil); + o = align(o, t, Ael2, nil); break; } o = xround(o, w); + if(maxalign && *maxalign < w) + *maxalign = w; if(debug['A']) - print("align %s %ld %T = %ld\n", bnames[op], i, t, o); + print("align %s %d %T = %d\n", bnames[op], i, t, o); return o; } diff --git a/src/cmd/6c/txt.c b/src/cmd/6c/txt.c index f96c40f8e..a78ba227b 100644 --- a/src/cmd/6c/txt.c +++ b/src/cmd/6c/txt.c @@ -38,8 +38,7 @@ ginit(void) thechar = '6'; thestring = "amd64"; - exregoffset = REGEXT; - exfregoffset = FREGEXT; + dodefine("_64BIT"); listinit(); nstring = 0; mnstring = 0; @@ -425,7 +424,7 @@ err: void regsalloc(Node *n, Node *nn) { - cursafe = align(cursafe, nn->type, Aaut3); + cursafe = align(cursafe, nn->type, Aaut3, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); *n = *nodsafe; n->xoffset = -(stkoff + cursafe); @@ -441,22 +440,22 @@ regaalloc1(Node *n, Node *nn) diag(n, "regaalloc1 and REGARG<0"); nodreg(n, nn, REGARG); reg[REGARG]++; - curarg = align(curarg, nn->type, Aarg1); - curarg = align(curarg, nn->type, Aarg2); + curarg = align(curarg, nn->type, Aarg1, nil); + curarg = align(curarg, nn->type, Aarg2, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); } void regaalloc(Node *n, Node *nn) { - curarg = align(curarg, nn->type, Aarg1); + curarg = align(curarg, nn->type, Aarg1, nil); *n = *nn; n->op = OINDREG; n->reg = REGSP; n->xoffset = curarg; n->complex = 0; n->addable = 20; - curarg = align(curarg, nn->type, Aarg2); + curarg = align(curarg, nn->type, Aarg2, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); } @@ -491,6 +490,10 @@ naddr(Node *n, Adr *a) a->sym = S; break; + case OEXREG: + a->type = D_INDIR + D_GS; + a->offset = n->reg - 1; + break; case OIND: naddr(n->left, a); @@ -1502,11 +1505,11 @@ exreg(Type *t) int32 o; if(typechlpv[t->etype]) { - if(exregoffset <= REGEXT-4) + if(exregoffset >= 64) return 0; o = exregoffset; - exregoffset--; - return o; + exregoffset += 8; + return o+1; // +1 to avoid 0 == failure; naddr's case OEXREG will subtract 1. } return 0; } diff --git a/src/cmd/6g/Makefile b/src/cmd/6g/Makefile index 712cfc60c..023f5d111 100644 --- a/src/cmd/6g/Makefile +++ b/src/cmd/6g/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 6g +TARG=6g HFILES=\ ../gc/go.h\ @@ -26,18 +26,9 @@ OFILES=\ ../6l/enam.$O\ LIB=\ - ../gc/gc.a$O + ../gc/gc.a\ -$(TARG): $(OFILES) $(LIB) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) $(LIB) -lbio -l9 -lm - -$(OFILES): $(HFILES) - -clean: - rm -f $(TARG) enam.c 6.out a.out *.$O *.6 - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +include ../../Make.ccmd %.$O: ../gc/%.c - $(CC) $(CFLAGS) -c -I. -o $@ ../gc/$*.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../gc/$*.c diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c index aacc0d06f..d4d22fd61 100644 --- a/src/cmd/6g/cgen.c +++ b/src/cmd/6g/cgen.c @@ -418,7 +418,7 @@ void agen(Node *n, Node *res) { Node *nl, *nr; - Node n1, n2, n3, tmp, n4; + Node n1, n2, n3, tmp, n4, n5; Prog *p1; uint32 w; uint64 v; @@ -477,8 +477,10 @@ agen(Node *n, Node *res) regalloc(&n1, nr->type, N); cgen(nr, &n1); } - regalloc(&n3, types[tptr], res); - agen(nl, &n3); + if(!isconst(nl, CTSTR)) { + regalloc(&n3, types[tptr], res); + agen(nl, &n3); + } goto index; } tempname(&tmp, nr->type); @@ -486,8 +488,10 @@ agen(Node *n, Node *res) nr = &tmp; irad: - regalloc(&n3, types[tptr], res); - agen(nl, &n3); + if(!isconst(nl, CTSTR)) { + regalloc(&n3, types[tptr], res); + agen(nl, &n3); + } if(!isconst(nr, CTINT)) { regalloc(&n1, nr->type, N); cgen(nr, &n1); @@ -501,7 +505,7 @@ agen(Node *n, Node *res) // explicit check for nil if array is large enough // that we might derive too big a pointer. - if(!isslice(nl->type) && nl->type->width >= unmappedzero) { + if(isfixedarray(nl->type) && nl->type->width >= unmappedzero) { regalloc(&n4, types[tptr], &n3); gmove(&n3, &n4); n4.op = OINDREG; @@ -516,15 +520,16 @@ agen(Node *n, Node *res) // constant index if(isconst(nr, CTINT)) { + if(isconst(nl, CTSTR)) + fatal("constant string constant index"); // front end should handle v = mpgetfix(nr->val.u.xval); - if(isslice(nl->type)) { - + if(isslice(nl->type) || nl->type->etype == TSTRING) { if(!debug['B'] && !n->etype) { n1 = n3; n1.op = OINDREG; n1.type = types[tptr]; n1.xoffset = Array_nel; - nodconst(&n2, types[TUINT64], v); + nodconst(&n2, types[TUINT32], v); gins(optoas(OCMP, types[TUINT32]), &n1, &n2); p1 = gbranch(optoas(OGT, types[TUINT32]), T); ginscall(panicindex, 0); @@ -536,18 +541,9 @@ agen(Node *n, Node *res) n1.type = types[tptr]; n1.xoffset = Array_array; gmove(&n1, &n3); - } else - if(!debug['B'] && !n->etype) { - if(v < 0) - yyerror("out of bounds on array"); - else - if(v >= nl->type->bound) - yyerror("out of bounds on array"); } - nodconst(&n2, types[tptr], v*w); - gins(optoas(OADD, types[tptr]), &n2, &n3); - + ginscon(optoas(OADD, types[tptr]), v*w, &n3); gmove(&n3, res); regfree(&n3); break; @@ -564,20 +560,43 @@ agen(Node *n, Node *res) if(!debug['B'] && !n->etype) { // check bounds - if(isslice(nl->type)) { + n5.op = OXXX; + t = types[TUINT32]; + if(is64(nr->type)) + t = types[TUINT64]; + if(isconst(nl, CTSTR)) { + nodconst(&n1, t, nl->val.u.sval->len); + } else if(isslice(nl->type) || nl->type->etype == TSTRING) { n1 = n3; n1.op = OINDREG; - n1.type = types[tptr]; + n1.type = types[TUINT32]; n1.xoffset = Array_nel; - } else - nodconst(&n1, types[TUINT64], nl->type->bound); - gins(optoas(OCMP, types[TUINT32]), &n2, &n1); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); + if(is64(nr->type)) { + regalloc(&n5, t, N); + gmove(&n1, &n5); + n1 = n5; + } + } else { + nodconst(&n1, t, nl->type->bound); + } + gins(optoas(OCMP, t), &n2, &n1); + p1 = gbranch(optoas(OLT, t), T); + if(n5.op != OXXX) + regfree(&n5); ginscall(panicindex, 0); patch(p1, pc); } - if(isslice(nl->type)) { + if(isconst(nl, CTSTR)) { + regalloc(&n3, types[tptr], res); + p1 = gins(ALEAQ, N, &n3); + datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from); + p1->from.scale = 1; + p1->from.index = n2.val.u.reg; + goto indexdone; + } + + if(isslice(nl->type) || nl->type->etype == TSTRING) { n1 = n3; n1.op = OINDREG; n1.type = types[tptr]; @@ -591,12 +610,12 @@ agen(Node *n, Node *res) p1->from.index = p1->from.type; p1->from.type = p1->to.type + D_INDIR; } else { - nodconst(&n1, t, w); - gins(optoas(OMUL, t), &n1, &n2); + ginscon(optoas(OMUL, t), w, &n2); gins(optoas(OADD, types[tptr]), &n2, &n3); gmove(&n3, res); } + indexdone: gmove(&n3, res); regfree(&n2); regfree(&n3); @@ -616,10 +635,8 @@ agen(Node *n, Node *res) fatal("agen: bad ONAME class %#x", n->class); } cgen(n->heapaddr, res); - if(n->xoffset != 0) { - nodconst(&n1, types[TINT64], n->xoffset); - gins(optoas(OADD, types[tptr]), &n1, res); - } + if(n->xoffset != 0) + ginscon(optoas(OADD, types[tptr]), n->xoffset, res); break; case OIND: @@ -628,10 +645,8 @@ agen(Node *n, Node *res) case ODOT: agen(nl, res); - if(n->xoffset != 0) { - nodconst(&n1, types[TINT64], n->xoffset); - gins(optoas(OADD, types[tptr]), &n1, res); - } + if(n->xoffset != 0) + ginscon(optoas(OADD, types[tptr]), n->xoffset, res); break; case ODOTPTR: @@ -648,8 +663,7 @@ agen(Node *n, Node *res) gins(ATESTB, nodintconst(0), &n1); regfree(&n1); } - nodconst(&n1, types[TINT64], n->xoffset); - gins(optoas(OADD, types[tptr]), &n1, res); + ginscon(optoas(OADD, types[tptr]), n->xoffset, res); } break; } @@ -694,6 +708,9 @@ bgen(Node *n, int true, Prog *to) if(n == N) n = nodbool(1); + if(n->ninit != nil) + genlist(n->ninit); + nl = n->left; nr = n->right; @@ -1006,6 +1023,10 @@ sgen(Node *n, Node *ns, int32 w) if(w < 0) fatal("sgen copy %d", w); + if(w == 16) + if(componentgen(n, ns)) + return; + // offset on the stack osrc = stkof(n); odst = stkof(ns); @@ -1099,3 +1120,163 @@ sgen(Node *n, Node *ns, int32 w) restx(&nodr, &oldr); restx(&cx, &oldcx); } + +static int +cadable(Node *n) +{ + if(!n->addable) { + // dont know how it happens, + // but it does + return 0; + } + + switch(n->op) { + case ONAME: + return 1; + } + return 0; +} + +/* + * copy a structure component by component + * return 1 if can do, 0 if cant. + * nr is N for copy zero + */ +int +componentgen(Node *nr, Node *nl) +{ + Node nodl, nodr; + int freel, freer; + + freel = 0; + freer = 0; + + switch(nl->type->etype) { + default: + goto no; + + case TARRAY: + if(!isslice(nl->type)) + goto no; + case TSTRING: + case TINTER: + break; + } + + nodl = *nl; + if(!cadable(nl)) { + if(nr == N || !cadable(nr)) + goto no; + igen(nl, &nodl, N); + freel = 1; + } + + if(nr != N) { + nodr = *nr; + if(!cadable(nr)) { + igen(nr, &nodr, N); + freer = 1; + } + } + + switch(nl->type->etype) { + case TARRAY: + if(!isslice(nl->type)) + goto no; + + nodl.xoffset += Array_array; + nodl.type = ptrto(nl->type->type); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = types[TUINT32]; + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_cap-Array_nel; + nodl.type = types[TUINT32]; + + if(nr != N) { + nodr.xoffset += Array_cap-Array_nel; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + goto yes; + + case TSTRING: + nodl.xoffset += Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = types[TUINT32]; + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + goto yes; + + case TINTER: + nodl.xoffset += Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + goto yes; + + case TSTRUCT: + goto no; + } + +no: + if(freer) + regfree(&nodr); + if(freel) + regfree(&nodl); + return 0; + +yes: + if(freer) + regfree(&nodr); + if(freel) + regfree(&nodl); + return 1; +} diff --git a/src/cmd/6g/galign.c b/src/cmd/6g/galign.c index 68647e21b..bdfc9947e 100644 --- a/src/cmd/6g/galign.c +++ b/src/cmd/6g/galign.c @@ -25,7 +25,6 @@ Typedef typedefs[] = void betypeinit(void) { - maxround = 8; widthptr = 8; zprog.link = P; diff --git a/src/cmd/6g/gg.h b/src/cmd/6g/gg.h index 353a86dcd..7efb2c252 100644 --- a/src/cmd/6g/gg.h +++ b/src/cmd/6g/gg.h @@ -99,6 +99,7 @@ void cgen_aret(Node*, Node*); int cgen_inline(Node*, Node*); void restx(Node*, Node*); void savex(int, Node*, Node*, Node*, Type*); +int componentgen(Node*, Node*); /* * gsubr.c @@ -122,6 +123,7 @@ Node* nodarg(Type*, int); void nodreg(Node*, Type*, int); void nodindreg(Node*, Type*, int); void gconreg(int, vlong, int); +void ginscon(int, vlong, Node*); void buildtxt(void); Plist* newplist(void); int isfat(Type*); diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c index 59a6d529d..d9fa1793c 100644 --- a/src/cmd/6g/ggen.c +++ b/src/cmd/6g/ggen.c @@ -62,6 +62,8 @@ compile(Node *fn) pl = newplist(); pl->name = curfn->nname; + setlineno(curfn); + nodconst(&nod1, types[TINT32], 0); ptxt = gins(ATEXT, curfn->nname, &nod1); afunclit(&ptxt->from); @@ -83,6 +85,8 @@ compile(Node *fn) checklabels(); if(nerrors != 0) goto ret; + if(curfn->endlineno) + lineno = curfn->endlineno; if(curfn->type->outtuple != 0) ginscall(throwreturn, 0); @@ -90,13 +94,13 @@ compile(Node *fn) if(pret) patch(pret, pc); ginit(); + if(hasdefer) + ginscall(deferreturn, 0); if(curfn->exit) genlist(curfn->exit); gclean(); if(nerrors != 0) goto ret; - if(hasdefer) - ginscall(deferreturn, 0); pc->as = ARET; // overwrite AEND pc->lineno = lineno; @@ -105,11 +109,11 @@ compile(Node *fn) } // fill in argument size - ptxt->to.offset = rnd(curfn->type->argwid, maxround); + ptxt->to.offset = rnd(curfn->type->argwid, widthptr); // fill in final stack size ptxt->to.offset <<= 32; - ptxt->to.offset |= rnd(stksize+maxarg, maxround); + ptxt->to.offset |= rnd(stksize+maxarg, widthptr); if(debug['f']) frame(0); @@ -1041,7 +1045,12 @@ clearfat(Node *nl) if(debug['g']) dump("\nclearfat", nl); + w = nl->type->width; + if(w == 16) + if(componentgen(N, nl)) + return; + c = w % 8; // bytes q = w / 8; // quads @@ -1115,44 +1124,64 @@ getargs(NodeList *nn, Node *reg, int n) void cmpandthrow(Node *nl, Node *nr) { - vlong cl, cr; + vlong cl; Prog *p1; int op; Node *c; + Type *t; + Node n1; + + if(nl->op == OCONV && is64(nl->type)) + nl = nl->left; + if(nr->op == OCONV && is64(nr->type)) + nr = nr->left; op = OLE; if(smallintconst(nl)) { cl = mpgetfix(nl->val.u.xval); if(cl == 0) return; - if(smallintconst(nr)) { - cr = mpgetfix(nr->val.u.xval); - if(cl > cr) { - if(throwpc == nil) { - throwpc = pc; - ginscall(panicslice, 0); - } else - patch(gbranch(AJMP, T), throwpc); - } + if(smallintconst(nr)) return; - } - // put the constant on the right op = brrev(op); c = nl; nl = nr; nr = c; } - - gins(optoas(OCMP, types[TUINT32]), nl, nr); + if(is64(nr->type) && smallintconst(nr)) + nr->type = types[TUINT32]; + + n1.op = OXXX; + t = types[TUINT32]; + if(nl->type->width != t->width || nr->type->width != t->width) { + if((is64(nl->type) && nl->op != OLITERAL) || (is64(nr->type) && nr->op != OLITERAL)) + t = types[TUINT64]; + + // Check if we need to use a temporary. + // At least one of the arguments is 32 bits + // (the len or cap) so one temporary suffices. + if(nl->type->width != t->width && nl->op != OLITERAL) { + regalloc(&n1, t, nl); + gmove(nl, &n1); + nl = &n1; + } else if(nr->type->width != t->width && nr->op != OLITERAL) { + regalloc(&n1, t, nr); + gmove(nr, &n1); + nr = &n1; + } + } + gins(optoas(OCMP, t), nl, nr); + if(n1.op != OXXX) + regfree(&n1); if(throwpc == nil) { - p1 = gbranch(optoas(op, types[TUINT32]), T); + p1 = gbranch(optoas(op, t), T); throwpc = pc; ginscall(panicslice, 0); patch(p1, pc); } else { op = brcom(op); - p1 = gbranch(optoas(op, types[TUINT32]), T); + p1 = gbranch(optoas(op, t), T); patch(p1, throwpc); } } @@ -1183,6 +1212,8 @@ cgen_inline(Node *n, Node *res) goto no; if(!n->left->addable) goto no; + if(n->left->sym == S) + goto no; if(n->left->sym->pkg != runtimepkg) goto no; if(strcmp(n->left->sym->name, "slicearray") == 0) @@ -1261,10 +1292,8 @@ slicearray: if(smallintconst(&nodes[2]) && smallintconst(&nodes[4])) { v = mpgetfix(nodes[2].val.u.xval) * mpgetfix(nodes[4].val.u.xval); - if(v != 0) { - nodconst(&n1, types[tptr], v); - gins(optoas(OADD, types[tptr]), &n1, &nodes[0]); - } + if(v != 0) + ginscon(optoas(OADD, types[tptr]), v, &nodes[0]); } else { regalloc(&n1, types[tptr], &nodes[2]); gmove(&nodes[2], &n1); @@ -1310,6 +1339,7 @@ sliceslice: // if(lb[1] > old.nel[0]) goto throw; n2 = nodes[0]; n2.xoffset += Array_nel; + n2.type = types[TUINT32]; cmpandthrow(&nodes[1], &n2); // ret.nel = old.nel[0]-lb[1]; @@ -1329,6 +1359,7 @@ sliceslice: // if(hb[2] > old.cap[0]) goto throw; n2 = nodes[0]; n2.xoffset += Array_cap; + n2.type = types[TUINT32]; cmpandthrow(&nodes[2], &n2); // if(lb[1] > hb[2]) goto throw; @@ -1376,10 +1407,8 @@ sliceslice: gins(optoas(OAS, types[tptr]), &n2, &n1); v = mpgetfix(nodes[1].val.u.xval) * mpgetfix(nodes[3].val.u.xval); - if(v != 0) { - nodconst(&n2, types[tptr], v); - gins(optoas(OADD, types[tptr]), &n2, &n1); - } + if(v != 0) + ginscon(optoas(OADD, types[tptr]), v, &n1); } else { gmove(&nodes[1], &n1); if(!smallintconst(&nodes[3]) || mpgetfix(nodes[3].val.u.xval) != 1) diff --git a/src/cmd/6g/gobj.c b/src/cmd/6g/gobj.c index 7c05054b7..b667ae48a 100644 --- a/src/cmd/6g/gobj.c +++ b/src/cmd/6g/gobj.c @@ -633,7 +633,7 @@ dsymptr(Sym *s, int off, Sym *x, int xoff) } void -genembedtramp(Type *rcvr, Type *method, Sym *newnam) +genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface) { Sym *e; int c, d, o, mov, add, loaded; @@ -732,7 +732,7 @@ out: p = pc; gins(AJMP, N, N); p->to.type = D_EXTERN; - p->to.sym = methodsym(method->sym, ptrto(f->type)); + p->to.sym = methodsym(method->sym, ptrto(f->type), 0); //print("6. %P\n", p); pc->as = ARET; // overwrite AEND diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c index 52ff6fdea..ebb61ea94 100644 --- a/src/cmd/6g/gsubr.c +++ b/src/cmd/6g/gsubr.c @@ -147,6 +147,8 @@ ggloblnod(Node *nam, int32 width) p->to.sym = S; p->to.type = D_CONST; p->to.offset = width; + if(nam->readonly) + p->from.scale = RODATA; } void @@ -163,6 +165,7 @@ ggloblsym(Sym *s, int32 width, int dupok) p->to.offset = width; if(dupok) p->from.scale = DUPOK; + p->from.scale |= RODATA; } int @@ -427,11 +430,33 @@ fatal("shouldnt be used"); void gconreg(int as, vlong c, int reg) { - Node n1, n2; + Node nr; + + nodreg(&nr, types[TINT64], reg); + ginscon(as, c, &nr); +} + +/* + * generate + * as $c, n + */ +void +ginscon(int as, vlong c, Node *n2) +{ + Node n1, ntmp; nodconst(&n1, types[TINT64], c); - nodreg(&n2, types[TINT64], reg); - gins(as, &n1, &n2); + + if(as != AMOVQ && (c < -1LL<<31 || c >= 1LL<<31)) { + // cannot have 64-bit immediokate in ADD, etc. + // instead, MOV into register first. + regalloc(&ntmp, types[TINT64], N); + gins(AMOVQ, &n1, &ntmp); + gins(as, &ntmp, n2); + regfree(&ntmp); + return; + } + gins(as, &n1, n2); } #define CASE(a,b) (((a)<<16)|((b)<<0)) @@ -1683,12 +1708,28 @@ optoas(int op, Type *t) enum { - ODynam = 1<<0, + ODynam = 1<<0, + OAddable = 1<<1, }; static Node clean[20]; static int cleani = 0; +int +xgen(Node *n, Node *a, int o) +{ + regalloc(a, types[tptr], N); + + if(o & ODynam) + if(n->addable) + if(n->op != OINDREG) + if(n->op != OREGISTER) + return 1; + + agen(n, a); + return 0; +} + void sudoclean(void) { @@ -1716,7 +1757,7 @@ sudoaddable(int as, Node *n, Addr *a) int o, i, w; int oary[10]; int64 v; - Node n1, n2, n3, *nn, *l, *r; + Node n1, n2, n3, n4, *nn, *l, *r; Node *reg, *reg1; Prog *p1; Type *t; @@ -1743,6 +1784,8 @@ sudoaddable(int as, Node *n, Addr *a) goto odot; case OINDEX: + if(n->left->type->etype == TSTRING) + return 0; goto oindex; } return 0; @@ -1820,7 +1863,7 @@ oindex: if(l->type->etype != TARRAY) fatal("not ary"); if(l->type->bound < 0) - o += ODynam; + o |= ODynam; w = n->type->width; if(isconst(r, CTINT)) @@ -1836,9 +1879,6 @@ oindex: break; } -// if(sudoaddable(as, l, a)) -// goto oindex_sudo; - cleani += 2; reg = &clean[cleani-1]; reg1 = &clean[cleani-2]; @@ -1847,8 +1887,8 @@ oindex: // load the array (reg) if(l->ullman > r->ullman) { - regalloc(reg, types[tptr], N); - agen(l, reg); + if(xgen(l, reg, o)) + o |= OAddable; } // load the index (reg1) @@ -1863,49 +1903,89 @@ oindex: // load the array (reg) if(l->ullman <= r->ullman) { - regalloc(reg, types[tptr], N); - agen(l, reg); + if(xgen(l, reg, o)) + o |= OAddable; } if(!(o & ODynam) && l->type->width >= unmappedzero && l->op == OIND) { // cannot rely on page protections to // catch array ptr == 0, so dereference. n2 = *reg; + n2.xoffset = 0; n2.op = OINDREG; n2.type = types[TUINT8]; - n2.xoffset = 0; gins(ATESTB, nodintconst(0), &n2); } // check bounds if(!debug['B'] && !n->etype) { + // check bounds + n4.op = OXXX; + t = types[TUINT32]; if(o & ODynam) { - n2 = *reg; - n2.op = OINDREG; - n2.type = types[tptr]; - n2.xoffset = Array_nel; + if(o & OAddable) { + n2 = *l; + n2.xoffset += Array_nel; + n2.type = types[TUINT32]; + if(is64(r->type)) { + t = types[TUINT64]; + regalloc(&n4, t, N); + gmove(&n2, &n4); + n2 = n4; + } + } else { + n2 = *reg; + n2.xoffset = Array_nel; + n2.op = OINDREG; + n2.type = types[TUINT32]; + if(is64(r->type)) { + t = types[TUINT64]; + regalloc(&n4, t, N); + gmove(&n2, &n4); + n2 = n4; + } + } } else { + if(is64(r->type)) + t = types[TUINT64]; nodconst(&n2, types[TUINT64], l->type->bound); } - gins(optoas(OCMP, types[TUINT32]), reg1, &n2); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); + gins(optoas(OCMP, t), reg1, &n2); + p1 = gbranch(optoas(OLT, t), T); + if(n4.op != OXXX) + regfree(&n4); ginscall(panicindex, 0); patch(p1, pc); } if(o & ODynam) { - n2 = *reg; - n2.op = OINDREG; - n2.type = types[tptr]; - n2.xoffset = Array_array; - gmove(&n2, reg); + if(o & OAddable) { + n2 = *l; + n2.xoffset += Array_array; + n2.type = types[TUINT64]; + gmove(&n2, reg); + } else { + n2 = *reg; + n2.xoffset = Array_array; + n2.op = OINDREG; + n2.type = types[tptr]; + gmove(&n2, reg); + } } - naddr(reg1, a, 1); - a->offset = 0; - a->scale = w; - a->index = a->type; - a->type = reg->val.u.reg + D_INDIR; + if(o & OAddable) { + naddr(reg1, a, 1); + a->offset = 0; + a->scale = w; + a->index = a->type; + a->type = reg->val.u.reg + D_INDIR; + } else { + naddr(reg1, a, 1); + a->offset = 0; + a->scale = w; + a->index = a->type; + a->type = reg->val.u.reg + D_INDIR; + } goto yes; @@ -1915,15 +1995,6 @@ oindex_const: // can multiply by width statically v = mpgetfix(r->val.u.xval); - if(!debug['B'] && (o & ODynam) == 0) { - // array indexed by a constant bounds check - if(v < 0) { - yyerror("out of bounds on array"); - } else - if(v >= l->type->bound) { - yyerror("out of bounds on array"); - } - } if(sudoaddable(as, l, a)) goto oindex_const_sudo; diff --git a/src/cmd/6g/list.c b/src/cmd/6g/list.c index 9194b1dab..c8077c97a 100644 --- a/src/cmd/6g/list.c +++ b/src/cmd/6g/list.c @@ -47,24 +47,28 @@ Pconv(Fmt *fp) { char str[STRINGSZ]; Prog *p; + char scale[40]; p = va_arg(fp->args, Prog*); sconsize = 8; + scale[0] = '\0'; + if(p->from.scale != 0 && (p->as == AGLOBL || p->as == ATEXT)) + snprint(scale, sizeof scale, "%d,", p->from.scale); switch(p->as) { default: - snprint(str, sizeof(str), "%.4ld (%L) %-7A %D,%D", - p->loc, p->lineno, p->as, &p->from, &p->to); + snprint(str, sizeof(str), "%.4d (%L) %-7A %D,%s%D", + p->loc, p->lineno, p->as, &p->from, scale, &p->to); break; case ADATA: sconsize = p->from.scale; - snprint(str, sizeof(str), "%.4ld (%L) %-7A %D/%d,%D", + snprint(str, sizeof(str), "%.4d (%L) %-7A %D/%d,%D", p->loc, p->lineno, p->as, &p->from, sconsize, &p->to); break; case ATEXT: - snprint(str, sizeof(str), "%.4ld (%L) %-7A %D,%lD", - p->loc, p->lineno, p->as, &p->from, &p->to); + snprint(str, sizeof(str), "%.4d (%L) %-7A %D,%s%lD", + p->loc, p->lineno, p->as, &p->from, scale, &p->to); break; } return fmtstrcpy(fp, str); @@ -104,7 +108,7 @@ Dconv(Fmt *fp) if(a->branch == nil) snprint(str, sizeof(str), ""); else - snprint(str, sizeof(str), "%ld", a->branch->loc); + snprint(str, sizeof(str), "%d", a->branch->loc); break; case D_EXTERN: @@ -127,7 +131,7 @@ Dconv(Fmt *fp) if(fp->flags & FmtLong) { d1 = a->offset & 0xffffffffLL; d2 = (a->offset>>32) & 0xffffffffLL; - snprint(str, sizeof(str), "$%lud-%lud", (ulong)d1, (ulong)d2); + snprint(str, sizeof(str), "$%ud-%ud", (ulong)d1, (ulong)d2); break; } snprint(str, sizeof(str), "$%lld", a->offset); diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c index e92740e04..464627066 100644 --- a/src/cmd/6g/reg.c +++ b/src/cmd/6g/reg.c @@ -682,17 +682,17 @@ brk: print("\nstats\n"); if(ostats.ncvtreg) - print(" %4ld cvtreg\n", ostats.ncvtreg); + print(" %4d cvtreg\n", ostats.ncvtreg); if(ostats.nspill) - print(" %4ld spill\n", ostats.nspill); + print(" %4d spill\n", ostats.nspill); if(ostats.nreload) - print(" %4ld reload\n", ostats.nreload); + print(" %4d reload\n", ostats.nreload); if(ostats.ndelmov) - print(" %4ld delmov\n", ostats.ndelmov); + print(" %4d delmov\n", ostats.ndelmov); if(ostats.nvar) - print(" %4ld delmov\n", ostats.nvar); + print(" %4d delmov\n", ostats.nvar); if(ostats.naddr) - print(" %4ld delmov\n", ostats.naddr); + print(" %4d delmov\n", ostats.naddr); memset(&ostats, 0, sizeof(ostats)); } @@ -1268,7 +1268,7 @@ regset(Reg *r, uint32 bb) while(b = bb & ~(bb-1)) { v.type = b & 0xFFFF? BtoR(b): BtoF(b); if(v.type == 0) - fatal("zero v.type for %#lux", b); + fatal("zero v.type for %#ux", b); c = copyu(r->prog, &v, A); if(c == 3) set |= b; @@ -1486,7 +1486,7 @@ dumpone(Reg *r) int z; Bits bit; - print("%ld:%P", r->loop, r->prog); + print("%d:%P", r->loop, r->prog); for(z=0; zset.b[z] | @@ -1535,14 +1535,14 @@ dumpit(char *str, Reg *r0) if(r1 != R) { print(" pred:"); for(; r1 != R; r1 = r1->p2link) - print(" %.4lud", r1->prog->loc); + print(" %.4ud", r1->prog->loc); print("\n"); } // r1 = r->s1; // if(r1 != R) { // print(" succ:"); // for(; r1 != R; r1 = r1->s1) -// print(" %.4lud", r1->prog->loc); +// print(" %.4ud", r1->prog->loc); // print("\n"); // } } diff --git a/src/cmd/6l/6.out.h b/src/cmd/6l/6.out.h index ca5e485c0..709f82ccc 100644 --- a/src/cmd/6l/6.out.h +++ b/src/cmd/6l/6.out.h @@ -33,6 +33,7 @@ #define NOPROF (1<<0) #define DUPOK (1<<1) #define NOSPLIT (1<<2) +#define RODATA (1<<3) /* * amd64 @@ -389,8 +390,8 @@ enum as AEND, - ADYNT, - AINIT, + ADYNT_, + AINIT_, ASIGNAME, @@ -823,6 +824,7 @@ enum D_INDIR, /* additive */ D_SIZE = D_INDIR + D_INDIR, /* 6l internal */ + D_PCREL, T_TYPE = 1<<0, T_INDEX = 1<<1, diff --git a/src/cmd/6l/Makefile b/src/cmd/6l/Makefile index 72bde4465..fba1b42ae 100644 --- a/src/cmd/6l/Makefile +++ b/src/cmd/6l/Makefile @@ -2,23 +2,29 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 6l\ +TARG=6l OFILES=\ asm.$O\ + data.$O\ + dwarf.$O\ elf.$O\ enam.$O\ go.$O\ + ldelf.$O\ + ldmacho.$O\ lib.$O\ list.$O\ macho.$O\ obj.$O\ optab.$O\ pass.$O\ + prof.$O\ span.$O\ + symtab.$O\ HFILES=\ l.h\ @@ -26,20 +32,14 @@ HFILES=\ ../ld/lib.h\ ../ld/elf.h\ ../ld/macho.h\ + ../ld/dwarf.h\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lbio -l9 - -$(OFILES): $(HFILES) +include ../../Make.ccmd enam.c: 6.out.h sh mkenam -clean: - rm -f *.$O $(TARG) *.6 enam.c 6.out a.out - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +CLEANFILES+=enam.c %.$O: ../ld/%.c - $(CC) $(CFLAGS) -c -I. ../ld/$*.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. ../ld/$*.c diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c index b45557ebe..9726d227c 100644 --- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -28,9 +28,12 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Writing object files. + #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" +#include "../ld/dwarf.h" #include "../ld/macho.h" #define Dbufslop 100 @@ -41,7 +44,6 @@ char linuxdynld[] = "/lib64/ld-linux-x86-64.so.2"; char freebsddynld[] = "/libexec/ld-elf.so.1"; char zeroes[32]; -Prog* datsort(Prog *l); vlong entryvalue(void) @@ -55,15 +57,8 @@ entryvalue(void) s = lookup(a, 0); if(s->type == 0) return INITTEXT; - switch(s->type) { - case STEXT: - break; - case SDATA: - if(dlm) - return s->value+INITDAT; - default: + if(s->type != STEXT) diag("entry not text: %s", s->name); - } return s->value; } @@ -113,130 +108,13 @@ vputl(uint64 v) lputl(v>>32); } -void -strnput(char *s, int n) -{ - int i; - - for(i=0; itype == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - n = strlen(str)+1; - while(n > 0) { - m = n; - if(m > sizeof(p->to.scon)) - m = sizeof(p->to.scon); - p = newdata(s, s->value, m, D_EXTERN); - p->to.type = D_SCONST; - memmove(p->to.scon, str, m); - s->value += m; - str += m; - n -= m; - } - return r; -} - -vlong -adduintxx(Sym *s, uint64 v, int wid) -{ - vlong r; - Prog *p; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, wid, D_EXTERN); - s->value += wid; - p->to.type = D_CONST; - p->to.offset = v; - return r; -} - -vlong -adduint8(Sym *s, uint8 v) -{ - return adduintxx(s, v, 1); -} - -vlong -adduint16(Sym *s, uint16 v) -{ - return adduintxx(s, v, 2); -} - -vlong -adduint32(Sym *s, uint32 v) -{ - return adduintxx(s, v, 4); -} - -vlong -adduint64(Sym *s, uint64 v) -{ - return adduintxx(s, v, 8); -} - -vlong -addaddr(Sym *s, Sym *t) -{ - vlong r; - Prog *p; - enum { Ptrsize = 8 }; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, Ptrsize, D_EXTERN); - s->value += Ptrsize; - p->to.type = D_ADDR; - p->to.index = D_EXTERN; - p->to.offset = 0; - p->to.sym = t; - return r; -} - -vlong -addsize(Sym *s, Sym *t) -{ - vlong r; - Prog *p; - enum { Ptrsize = 8 }; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, Ptrsize, D_EXTERN); - s->value += Ptrsize; - p->to.type = D_SIZE; - p->to.index = D_EXTERN; - p->to.offset = 0; - p->to.sym = t; - return r; -} - vlong datoff(vlong addr) { - if(addr >= INITDAT) - return addr - INITDAT + rnd(HEADR+textsize, INITRND); + if(addr >= segdata.vaddr) + return addr - segdata.vaddr + segdata.fileoff; + if(addr >= segtext.vaddr) + return addr - segtext.vaddr + segtext.fileoff; diag("datoff %#llx", addr); return 0; } @@ -260,6 +138,8 @@ enum { ElfStrShstrtab, ElfStrSymtab, ElfStrStrtab, + ElfStrRelaPlt, + ElfStrPlt, NElfStr }; @@ -281,29 +161,461 @@ needlib(char *name) return 0; } +int nelfsym = 1; + +static void addpltsym(Sym*); +static void addgotsym(Sym*); + +void +adddynrel(Sym *s, Reloc *r) +{ + Sym *targ, *rela, *got; + + targ = r->sym; + cursym = s; + + switch(r->type) { + default: + if(r->type >= 256) { + diag("unexpected relocation type %d", r->type); + return; + } + break; + + // Handle relocations found in ELF object files. + case 256 + R_X86_64_PC32: + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected R_X86_64_PC32 relocation for dynamic symbol %s", targ->name); + if(targ->type == 0 || targ->type == SXREF) + diag("unknown symbol %s in pcrel", targ->name); + r->type = D_PCREL; + r->add += 4; + return; + + case 256 + R_X86_64_PLT32: + r->type = D_PCREL; + r->add += 4; + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add += targ->plt; + } + return; + + case 256 + R_X86_64_GOTPCREL: + if(targ->dynimpname == nil || targ->dynexport) { + // have symbol + // turn MOVQ of GOT entry into LEAQ of symbol itself + if(r->off < 2 || s->p[r->off-2] != 0x8b) { + diag("unexpected GOT_LOAD reloc for non-dynamic symbol %s", targ->name); + return; + } + s->p[r->off-2] = 0x8d; + r->type = D_PCREL; + r->add += 4; + return; + } + addgotsym(targ); + r->type = D_PCREL; + r->sym = lookup(".got", 0); + r->add += 4; + r->add += targ->got; + return; + + case 256 + R_X86_64_64: + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected R_X86_64_64 relocation for dynamic symbol %s", targ->name); + r->type = D_ADDR; + return; + + // Handle relocations found in Mach-O object files. + case 512 + MACHO_X86_64_RELOC_UNSIGNED*2 + 0: + case 512 + MACHO_X86_64_RELOC_SIGNED*2 + 0: + case 512 + MACHO_X86_64_RELOC_BRANCH*2 + 0: + // TODO: What is the difference between all these? + r->type = D_ADDR; + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected reloc for dynamic symbol %s", targ->name); + return; + + case 512 + MACHO_X86_64_RELOC_BRANCH*2 + 1: + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = targ->plt; + r->type = D_PCREL; + return; + } + // fall through + case 512 + MACHO_X86_64_RELOC_UNSIGNED*2 + 1: + case 512 + MACHO_X86_64_RELOC_SIGNED*2 + 1: + case 512 + MACHO_X86_64_RELOC_SIGNED_1*2 + 1: + case 512 + MACHO_X86_64_RELOC_SIGNED_2*2 + 1: + case 512 + MACHO_X86_64_RELOC_SIGNED_4*2 + 1: + r->type = D_PCREL; + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected pc-relative reloc for dynamic symbol %s", targ->name); + return; + + case 512 + MACHO_X86_64_RELOC_GOT_LOAD*2 + 1: + if(targ->dynimpname == nil || targ->dynexport) { + // have symbol + // turn MOVQ of GOT entry into LEAQ of symbol itself + if(r->off < 2 || s->p[r->off-2] != 0x8b) { + diag("unexpected GOT_LOAD reloc for non-dynamic symbol %s", targ->name); + return; + } + s->p[r->off-2] = 0x8d; + r->type = D_PCREL; + return; + } + // fall through + case 512 + MACHO_X86_64_RELOC_GOT*2 + 1: + if(targ->dynimpname == nil || targ->dynexport) + diag("unexpected GOT reloc for non-dynamic symbol %s", targ->name); + addgotsym(targ); + r->type = D_PCREL; + r->sym = lookup(".got", 0); + r->add += targ->got; + return; + } + + // Handle references to ELF symbols from our own object files. + if(targ->dynimpname == nil || targ->dynexport) + return; + + switch(r->type) { + case D_PCREL: + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = targ->plt; + return; + + case D_ADDR: + if(s->type != SDATA) + break; + if(iself) { + adddynsym(targ); + rela = lookup(".rela", 0); + addaddrplus(rela, s, r->off); + if(r->siz == 8) + adduint64(rela, ELF64_R_INFO(targ->dynid, R_X86_64_64)); + else + adduint64(rela, ELF64_R_INFO(targ->dynid, R_X86_64_32)); + adduint64(rela, r->add); + r->type = 256; // ignore during relocsym + return; + } + if(HEADTYPE == 6 && s->size == PtrSize && r->off == 0) { + // Mach-O relocations are a royal pain to lay out. + // They use a compact stateful bytecode representation + // that is too much bother to deal with. + // Instead, interpret the C declaration + // void *_Cvar_stderr = &stderr; + // as making _Cvar_stderr the name of a GOT entry + // for stderr. This is separate from the usual GOT entry, + // just in case the C code assigns to the variable, + // and of course it only works for single pointers, + // but we only need to support cgo and that's all it needs. + adddynsym(targ); + got = lookup(".got", 0); + s->type = got->type | SSUB; + s->outer = got; + s->sub = got->sub; + got->sub = s; + s->value = got->size; + adduint64(got, 0); + adduint32(lookup(".linkedit.got", 0), targ->dynid); + r->type = 256; // ignore during relocsym + return; + } + break; + } + + cursym = s; + diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); +} + +int +archreloc(Reloc *r, Sym *s, vlong *val) +{ + return -1; +} + +static void +elfsetupplt(void) +{ + Sym *plt, *got; + + plt = lookup(".plt", 0); + got = lookup(".got.plt", 0); + if(plt->size == 0) { + // pushq got+8(IP) + adduint8(plt, 0xff); + adduint8(plt, 0x35); + addpcrelplus(plt, got, 8); + + // jmpq got+16(IP) + adduint8(plt, 0xff); + adduint8(plt, 0x25); + addpcrelplus(plt, got, 16); + + // nopl 0(AX) + adduint32(plt, 0x00401f0f); + + // assume got->size == 0 too + addaddrplus(got, lookup(".dynamic", 0), 0); + adduint64(got, 0); + adduint64(got, 0); + } +} + +static void +addpltsym(Sym *s) +{ + if(s->plt >= 0) + return; + + adddynsym(s); + + if(iself) { + Sym *plt, *got, *rela; + + plt = lookup(".plt", 0); + got = lookup(".got.plt", 0); + rela = lookup(".rela.plt", 0); + if(plt->size == 0) + elfsetupplt(); + + // jmpq *got+size(IP) + adduint8(plt, 0xff); + adduint8(plt, 0x25); + addpcrelplus(plt, got, got->size); + + // add to got: pointer to current pos in plt + addaddrplus(got, plt, plt->size); + + // pushq $x + adduint8(plt, 0x68); + adduint32(plt, (got->size-24-8)/8); + + // jmpq .plt + adduint8(plt, 0xe9); + adduint32(plt, -(plt->size+4)); + + // rela + addaddrplus(rela, got, got->size-8); + adduint64(rela, ELF64_R_INFO(s->dynid, R_X86_64_JMP_SLOT)); + adduint64(rela, 0); + + s->plt = plt->size - 16; + } else if(HEADTYPE == 6) { // Mach-O + // To do lazy symbol lookup right, we're supposed + // to tell the dynamic loader which library each + // symbol comes from and format the link info + // section just so. I'm too lazy (ha!) to do that + // so for now we'll just use non-lazy pointers, + // which don't need to be told which library to use. + // + // http://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html + // has details about what we're avoiding. + + Sym *plt; + + addgotsym(s); + plt = lookup(".plt", 0); + + adduint32(lookup(".linkedit.plt", 0), s->dynid); + + // jmpq *got+size(IP) + s->plt = plt->size; + + adduint8(plt, 0xff); + adduint8(plt, 0x25); + addpcrelplus(plt, lookup(".got", 0), s->got); + } else { + diag("addpltsym: unsupported binary format"); + } +} + +static void +addgotsym(Sym *s) +{ + Sym *got, *rela; + + if(s->got >= 0) + return; + + adddynsym(s); + got = lookup(".got", 0); + s->got = got->size; + adduint64(got, 0); + + if(iself) { + rela = lookup(".rela", 0); + addaddrplus(rela, got, s->got); + adduint64(rela, ELF64_R_INFO(s->dynid, R_X86_64_GLOB_DAT)); + adduint64(rela, 0); + } else if(HEADTYPE == 6) { // Mach-O + adduint32(lookup(".linkedit.got", 0), s->dynid); + } else { + diag("addgotsym: unsupported binary format"); + } +} + +void +adddynsym(Sym *s) +{ + Sym *d, *str; + int t; + char *name; + + if(s->dynid >= 0) + return; + + if(s->dynimpname == nil) + diag("adddynsym: no dynamic name for %s", s->name); + + if(iself) { + s->dynid = nelfsym++; + + d = lookup(".dynsym", 0); + name = s->dynimpname; + if(name == nil) + name = s->name; + adduint32(d, addstring(lookup(".dynstr", 0), name)); + /* type */ + t = STB_GLOBAL << 4; + if(s->dynexport && s->type == STEXT) + t |= STT_FUNC; + else + t |= STT_OBJECT; + adduint8(d, t); + + /* reserved */ + adduint8(d, 0); + + /* section where symbol is defined */ + if(!s->dynexport && s->dynimpname != nil) + adduint16(d, SHN_UNDEF); + else { + switch(s->type) { + default: + case STEXT: + t = 11; + break; + case SRODATA: + t = 12; + break; + case SDATA: + t = 13; + break; + case SBSS: + t = 14; + break; + } + adduint16(d, t); + } + + /* value */ + if(s->type == SDYNIMPORT) + adduint64(d, 0); + else + addaddr(d, s); + + /* size of object */ + adduint64(d, 0); + + if(!s->dynexport && s->dynimplib && needlib(s->dynimplib)) { + elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, + addstring(lookup(".dynstr", 0), s->dynimplib)); + } + } else if(HEADTYPE == 6) { + // Mach-o symbol nlist64 + d = lookup(".dynsym", 0); + name = s->dynimpname; + if(name == nil) + name = s->name; + s->dynid = d->size/16; + // darwin still puts _ prefixes on all C symbols + str = lookup(".dynstr", 0); + adduint32(d, str->size); + adduint8(str, '_'); + addstring(str, name); + if(s->type == SDYNIMPORT) { + adduint8(d, 0x01); // type - N_EXT - external symbol + adduint8(d, 0); // section + } else { + adduint8(d, 0x0f); + switch(s->type) { + default: + case STEXT: + adduint8(d, 1); + break; + case SDATA: + adduint8(d, 2); + break; + case SBSS: + adduint8(d, 4); + break; + } + } + adduint16(d, 0); // desc + if(s->type == SDYNIMPORT) + adduint64(d, 0); // value + else + addaddr(d, s); + } else { + diag("adddynsym: unsupported binary format"); + } +} + +void +adddynlib(char *lib) +{ + Sym *s; + + if(!needlib(lib)) + return; + + if(iself) { + s = lookup(".dynstr", 0); + if(s->size == 0) + addstring(s, ""); + elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(s, lib)); + } else if(HEADTYPE == 6) { // Mach-O + machoadddynlib(lib); + } else { + diag("adddynlib: unsupported binary format"); + } +} + void doelf(void) { - Sym *s, *shstrtab, *dynamic, *dynstr, *d; - int h, nsym, t; + Sym *s, *shstrtab, *dynstr; if(HEADTYPE != 7 && HEADTYPE != 9) return; /* predefine strings we need for section headers */ shstrtab = lookup(".shstrtab", 0); + shstrtab->type = SELFDATA; + shstrtab->reachable = 1; + elfstr[ElfStrEmpty] = addstring(shstrtab, ""); elfstr[ElfStrText] = addstring(shstrtab, ".text"); elfstr[ElfStrData] = addstring(shstrtab, ".data"); elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); + addstring(shstrtab, ".elfdata"); + addstring(shstrtab, ".rodata"); if(!debug['s']) { elfstr[ElfStrGosymcounts] = addstring(shstrtab, ".gosymcounts"); elfstr[ElfStrGosymtab] = addstring(shstrtab, ".gosymtab"); elfstr[ElfStrGopclntab] = addstring(shstrtab, ".gopclntab"); - if(debug['e']) { - elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab"); - elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab"); - } + elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab"); + elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab"); + dwarfaddshstrings(shstrtab); } elfstr[ElfStrShstrtab] = addstring(shstrtab, ".shstrtab"); @@ -316,19 +628,21 @@ doelf(void) elfstr[ElfStrDynsym] = addstring(shstrtab, ".dynsym"); elfstr[ElfStrDynstr] = addstring(shstrtab, ".dynstr"); elfstr[ElfStrRela] = addstring(shstrtab, ".rela"); + elfstr[ElfStrRelaPlt] = addstring(shstrtab, ".rela.plt"); + elfstr[ElfStrPlt] = addstring(shstrtab, ".plt"); /* dynamic symbol table - first entry all zeros */ s = lookup(".dynsym", 0); s->type = SELFDATA; s->reachable = 1; - s->value += ELF64SYMSIZE; + s->size += ELF64SYMSIZE; /* dynamic string table */ s = lookup(".dynstr", 0); s->type = SELFDATA; s->reachable = 1; - s->value += ELF64SYMSIZE; - addstring(s, ""); + if(s->size == 0) + addstring(s, ""); dynstr = s; /* relocation table */ @@ -339,15 +653,24 @@ doelf(void) /* global offset table */ s = lookup(".got", 0); s->reachable = 1; + s->type = SDATA; // writable, so not SELFDATA + + /* hash */ + s = lookup(".hash", 0); + s->reachable = 1; s->type = SELFDATA; - /* got.plt - ??? */ s = lookup(".got.plt", 0); s->reachable = 1; + s->type = SDATA; // writable, not SELFDATA + + s = lookup(".plt", 0); + s->reachable = 1; s->type = SELFDATA; - /* hash */ - s = lookup(".hash", 0); + elfsetupplt(); + + s = lookup(".rela.plt", 0); s->reachable = 1; s->type = SELFDATA; @@ -355,78 +678,10 @@ doelf(void) s = lookup(".dynamic", 0); s->reachable = 1; s->type = SELFDATA; - dynamic = s; - - /* - * relocation entries for dynimport symbols - */ - nsym = 1; // sym 0 is reserved - for(h=0; hlink) { - if(!s->reachable || (s->type != STEXT && s->type != SDATA && s->type != SBSS) || s->dynimpname == nil) - continue; - - if(!s->dynexport) { - d = lookup(".rela", 0); - addaddr(d, s); - adduint64(d, ELF64_R_INFO(nsym, R_X86_64_64)); - adduint64(d, 0); - } - - nsym++; - - d = lookup(".dynsym", 0); - adduint32(d, addstring(lookup(".dynstr", 0), s->dynimpname)); - /* type */ - t = STB_GLOBAL << 4; - if(s->dynexport && s->type == STEXT) - t |= STT_FUNC; - else - t |= STT_OBJECT; - adduint8(d, t); - - /* reserved */ - adduint8(d, 0); - - /* section where symbol is defined */ - if(!s->dynexport) - adduint16(d, SHN_UNDEF); - else { - switch(s->type) { - default: - case STEXT: - t = 9; - break; - case SDATA: - t = 10; - break; - case SBSS: - t = 11; - break; - } - adduint16(d, t); - } - - /* value */ - if(!s->dynexport) - adduint64(d, 0); - else - addaddr(d, s); - - /* size of object */ - adduint64(d, 0); - - if(!s->dynexport && needlib(s->dynimplib)) - elfwritedynent(dynamic, DT_NEEDED, addstring(dynstr, s->dynimplib)); - } - } - - elfdynhash(nsym); /* * .dynamic table */ - s = dynamic; elfwritedynentsym(s, DT_HASH, lookup(".hash", 0)); elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0)); elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE); @@ -437,6 +692,11 @@ doelf(void) elfwritedynent(s, DT_RELAENT, ELF64RELASIZE); if(rpath) elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); + + elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0)); + elfwritedynent(s, DT_PLTREL, DT_RELA); + elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rela.plt", 0)); + elfwritedynentsym(s, DT_JMPREL, lookup(".rela.plt", 0)); elfwritedynent(s, DT_NULL, 0); } } @@ -463,15 +723,13 @@ phsh(ElfPhdr *ph, ElfShdr *sh) void asmb(void) { - Prog *p; - int32 v, magic; + int32 magic; int a, dynsym; - uchar *op1; vlong vl, va, startva, fo, w, symo, elfsymo, elfstro, elfsymsize, machlink; - vlong symdatva = SYMDATVA; ElfEhdr *eh; ElfPhdr *ph, *pph; ElfShdr *sh; + Section *sect; if(debug['v']) Bprint(&bso, "%5.2f asmb\n", cputime()); @@ -481,101 +739,51 @@ asmb(void) elfsymsize = 0; elfstro = 0; elfsymo = 0; - seek(cout, HEADR, 0); - pc = INITTEXT; - curp = firstp; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; - if(p->pc != pc) { - if(!debug['a']) - print("%P\n", curp); - diag("phase error %llux sb %llux in %s", p->pc, pc, TNAME); - pc = p->pc; - } - curp = p; - asmins(p); - a = (andptr - and); - if(cbc < a) - cflush(); - if(debug['a']) { - Bprint(&bso, pcstr, pc); - for(op1 = and; op1 < andptr; op1++) - Bprint(&bso, "%.2ux", *op1); - for(; op1 < and+Maxand; op1++) - Bprint(&bso, " "); - Bprint(&bso, "%P\n", curp); - } - if(dlm) { - if(p->as == ATEXT) - reloca = nil; - else if(reloca != nil) - diag("reloc failure: %P", curp); - } - memmove(cbp, and, a); - cbp += a; - pc += a; - cbc -= a; - } - cflush(); + + if(debug['v']) + Bprint(&bso, "%5.2f codeblk\n", cputime()); + Bflush(&bso); + + sect = segtext.sect; + seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); + codeblk(sect->vaddr, sect->len); + + /* output read-only data in text segment */ + sect = segtext.sect->next; + seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); + datblk(sect->vaddr, sect->len); + + if(debug['v']) + Bprint(&bso, "%5.2f datblk\n", cputime()); + Bflush(&bso); + seek(cout, segdata.fileoff, 0); + datblk(segdata.vaddr, segdata.filelen); + + machlink = 0; + if(HEADTYPE == 6) + machlink = domacholink(); switch(HEADTYPE) { default: - diag("unknown header type %ld", HEADTYPE); + diag("unknown header type %d", HEADTYPE); case 2: case 5: - seek(cout, HEADR+textsize, 0); break; case 6: debug['8'] = 1; /* 64-bit addresses */ - v = HEADR+textsize; - seek(cout, v, 0); - v = rnd(v, 4096) - v; - while(v > 0) { - cput(0); - v--; - } - cflush(); break; - case 7: case 9: debug['8'] = 1; /* 64-bit addresses */ - v = rnd(HEADR+textsize, INITRND); - seek(cout, v, 0); - /* index of elf text section; needed by asmelfsym, double-checked below */ - /* debug['d'] causes 8 extra sections before the .text section */ + /* !debug['d'] causes extra sections before the .text section */ elftextsh = 1; if(!debug['d']) - elftextsh += 8; + elftextsh += 10; break; } - if(debug['v']) - Bprint(&bso, "%5.2f datblk\n", cputime()); - Bflush(&bso); - - if(dlm){ - char buf[8]; - - write(cout, buf, INITDAT-textsize); - textsize = INITDAT; - } - - datap = datsort(datap); - for(v = 0; v < datsize; v += sizeof(buf)-Dbufslop) { - if(datsize-v > sizeof(buf)-Dbufslop) - datblk(v, sizeof(buf)-Dbufslop); - else - datblk(v, datsize-v); - } - - machlink = 0; - if(HEADTYPE == 6) - machlink = domacholink(); - symsize = 0; spsize = 0; lcsize = 0; @@ -589,14 +797,14 @@ asmb(void) case 2: case 5: debug['s'] = 1; - symo = HEADR+textsize+datsize; + symo = HEADR+segtext.len+segdata.filelen; break; case 6: - symo = rnd(HEADR+textsize, INITRND)+rnd(datsize, INITRND)+machlink; + symo = rnd(HEADR+segtext.len, INITRND)+rnd(segdata.filelen, INITRND)+machlink; break; case 7: case 9: - symo = rnd(HEADR+textsize, INITRND)+datsize; + symo = rnd(HEADR+segtext.len, INITRND)+segdata.filelen; symo = rnd(symo, INITRND); break; } @@ -608,18 +816,12 @@ asmb(void) * line number table */ seek(cout, symo+8, 0); - if(!debug['s']) - asmsym(); if(debug['v']) Bprint(&bso, "%5.2f sp\n", cputime()); Bflush(&bso); if(debug['v']) Bprint(&bso, "%5.2f pc\n", cputime()); Bflush(&bso); - if(!debug['s']) - asmlc(); - if(dlm) - asmdyn(); if(!debug['s']) strnput("", INITRND-(8+symsize+lcsize)%INITRND); cflush(); @@ -627,20 +829,20 @@ asmb(void) lputl(symsize); lputl(lcsize); cflush(); - if(!debug['s'] && debug['e']) { + if(!debug['s']) { elfsymo = symo+8+symsize+lcsize; seek(cout, elfsymo, 0); - asmelfsym(); + asmelfsym64(); cflush(); elfstro = seek(cout, 0, 1); elfsymsize = elfstro - elfsymo; - write(cout, elfstrdat, elfstrsize); - } - } else - if(dlm){ - seek(cout, HEADR+textsize+datsize, 0); - asmdyn(); - cflush(); + ewrite(cout, elfstrdat, elfstrsize); + + if(debug['v']) + Bprint(&bso, "%5.2f dwarf\n", cputime()); + + dwarfemitdebugsections(); + } } if(debug['v']) @@ -652,12 +854,10 @@ asmb(void) case 2: /* plan9 */ magic = 4*26*26+7; magic |= 0x00008000; /* fat header */ - if(dlm) - magic |= 0x80000000; /* dlm */ lputb(magic); /* magic */ - lputb(textsize); /* sizes */ - lputb(datsize); - lputb(bsssize); + lputb(segtext.filelen); /* sizes */ + lputb(segdata.filelen); + lputb(segdata.len - segdata.filelen); lputb(symsize); /* nsyms */ vl = entryvalue(); lputb(PADDR(vl)); /* va of entry */ @@ -667,19 +867,17 @@ asmb(void) break; case 3: /* plan9 */ magic = 4*26*26+7; - if(dlm) - magic |= 0x80000000; lputb(magic); /* magic */ - lputb(textsize); /* sizes */ - lputb(datsize); - lputb(bsssize); + lputb(segtext.filelen); /* sizes */ + lputb(segdata.filelen); + lputb(segdata.len - segdata.filelen); lputb(symsize); /* nsyms */ lputb(entryvalue()); /* va of entry */ lputb(spsize); /* sp offsets */ lputb(lcsize); /* line offsets */ break; case 6: - asmbmacho(symdatva, symo); + asmbmacho(); break; case 7: case 9: @@ -689,7 +887,7 @@ asmb(void) fo = HEADR; startva = INITTEXT - HEADR; va = startva + fo; - w = textsize; + w = segtext.filelen; /* This null SHdr must appear before all others */ sh = newElfShdr(elfstr[ElfStrEmpty]); @@ -724,41 +922,8 @@ asmb(void) phsh(ph, sh); } - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_X+PF_R; - ph->vaddr = va - fo; - ph->paddr = va - fo; - ph->off = 0; - ph->filesz = w + fo; - ph->memsz = w + fo; - ph->align = INITRND; - - fo = rnd(fo+w, INITRND); - va = rnd(va+w, INITRND); - w = datsize; - - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_W+PF_R; - ph->off = fo; - ph->vaddr = va; - ph->paddr = va; - ph->filesz = w; - ph->memsz = w+bsssize; - ph->align = INITRND; - - if(!debug['s']) { - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_R; - ph->off = symo; - ph->vaddr = symdatva; - ph->paddr = symdatva; - ph->filesz = rnd(8+symsize+lcsize, INITRND); - ph->memsz = rnd(8+symsize+lcsize, INITRND); - ph->align = INITRND; - } + elfphload(&segtext); + elfphload(&segdata); /* Dynamic linking sections */ if (!debug['d']) { /* -d suppresses dynamic loader format */ @@ -776,7 +941,7 @@ asmb(void) sh->entsize = 8; sh->addralign = 8; shsym(sh, lookup(".got.plt", 0)); - + dynsym = eh->shnum; sh = newElfShdr(elfstr[ElfStrDynsym]); sh->type = SHT_DYNSYM; @@ -793,6 +958,22 @@ asmb(void) sh->addralign = 1; shsym(sh, lookup(".dynstr", 0)); + sh = newElfShdr(elfstr[ElfStrRelaPlt]); + sh->type = SHT_RELA; + sh->flags = SHF_ALLOC; + sh->entsize = ELF64RELASIZE; + sh->addralign = 8; + sh->link = dynsym; + sh->info = eh->shnum; // .plt + shsym(sh, lookup(".rela.plt", 0)); + + sh = newElfShdr(elfstr[ElfStrPlt]); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_EXECINSTR; + sh->entsize = 16; + sh->addralign = 4; + shsym(sh, lookup(".plt", 0)); + sh = newElfShdr(elfstr[ElfStrHash]); sh->type = SHT_HASH; sh->flags = SHF_ALLOC; @@ -821,6 +1002,17 @@ asmb(void) ph->type = PT_DYNAMIC; ph->flags = PF_R + PF_W; phsh(ph, sh); + + /* + * Thread-local storage segment (really just size). + */ + if(tlsoffset != 0) { + ph = newElfPhdr(); + ph->type = PT_TLS; + ph->flags = PF_R; + ph->memsz = -tlsoffset; + ph->align = 8; + } } ph = newElfPhdr(); @@ -828,93 +1020,41 @@ asmb(void) ph->flags = PF_W+PF_R; ph->align = 8; - fo = ELFRESERVE; - va = startva + fo; - w = textsize; - if(elftextsh != eh->shnum) diag("elftextsh = %d, want %d", elftextsh, eh->shnum); - sh = newElfShdr(elfstr[ElfStrText]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_EXECINSTR; - sh->addr = va; - sh->off = fo; - sh->size = w; - sh->addralign = 8; - - fo = rnd(fo+w, INITRND); - va = rnd(va+w, INITRND); - w = datsize; - - sh = newElfShdr(elfstr[ElfStrData]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_WRITE+SHF_ALLOC; - sh->addr = va + elfdatsize; - sh->off = fo + elfdatsize; - sh->size = w - elfdatsize; - sh->addralign = 8; - - fo += w; - va += w; - w = bsssize; - - sh = newElfShdr(elfstr[ElfStrBss]); - sh->type = SHT_NOBITS; - sh->flags = SHF_WRITE+SHF_ALLOC; - sh->addr = va; - sh->off = fo; - sh->size = w; - sh->addralign = 8; + for(sect=segtext.sect; sect!=nil; sect=sect->next) + elfshbits(sect); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + elfshbits(sect); if (!debug['s']) { - fo = symo; - w = 8; - - sh = newElfShdr(elfstr[ElfStrGosymcounts]); + sh = newElfShdr(elfstr[ElfStrGosymtab]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; sh->addralign = 1; - sh->addr = symdatva; - - fo += w; - w = symsize; + shsym(sh, lookup("symtab", 0)); - sh = newElfShdr(elfstr[ElfStrGosymtab]); + sh = newElfShdr(elfstr[ElfStrGopclntab]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; sh->addralign = 1; - sh->addr = symdatva + 8; + shsym(sh, lookup("pclntab", 0)); - fo += w; - w = lcsize; + sh = newElfShdr(elfstr[ElfStrSymtab]); + sh->type = SHT_SYMTAB; + sh->off = elfsymo; + sh->size = elfsymsize; + sh->addralign = 8; + sh->entsize = 24; + sh->link = eh->shnum; // link to strtab - sh = newElfShdr(elfstr[ElfStrGopclntab]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; + sh = newElfShdr(elfstr[ElfStrStrtab]); + sh->type = SHT_STRTAB; + sh->off = elfstro; + sh->size = elfstrsize; sh->addralign = 1; - sh->addr = symdatva + 8 + symsize; - - if(debug['e']) { - sh = newElfShdr(elfstr[ElfStrSymtab]); - sh->type = SHT_SYMTAB; - sh->off = elfsymo; - sh->size = elfsymsize; - sh->addralign = 8; - sh->entsize = 24; - sh->link = eh->shnum; // link to strtab - - sh = newElfShdr(elfstr[ElfStrStrtab]); - sh->type = SHT_STRTAB; - sh->off = elfstro; - sh->size = elfstrsize; - sh->addralign = 1; - } + + dwarfaddelfheaders(); } sh = newElfShstrtab(elfstr[ElfStrShstrtab]); @@ -961,354 +1101,91 @@ cflush(void) n = sizeof(buf.cbuf) - cbc; if(n) - write(cout, buf.cbuf, n); + ewrite(cout, buf.cbuf, n); cbp = buf.cbuf; cbc = sizeof(buf.cbuf); } -void -outa(int n, uchar *cast, uchar *map, vlong l) -{ - int i, j; - - Bprint(&bso, pcstr, l); - for(i=0; ifrom.offset; - v2 = p2->from.offset; - if(v1 > v2) - return +1; - if(v1 < v2) - return -1; - return 0; + return seek(cout, 0, 1) + sizeof(buf.cbuf) - cbc; } -Prog* -dsort(Prog *l) -{ - Prog *l1, *l2, *le; - - if(l == 0 || l->link == 0) - return l; - - l1 = l; - l2 = l; - for(;;) { - l2 = l2->link; - if(l2 == 0) - break; - l2 = l2->link; - if(l2 == 0) - break; - l1 = l1->link; - } - - l2 = l1->link; - l1->link = 0; - l1 = dsort(l); - l2 = dsort(l2); - - /* set up lead element */ - if(datcmp(l1, l2) < 0) { - l = l1; - l1 = l1->link; - } else { - l = l2; - l2 = l2->link; - } - le = l; - - for(;;) { - if(l1 == 0) { - while(l2) { - le->link = l2; - le = l2; - l2 = l2->link; - } - le->link = 0; - break; - } - if(l2 == 0) { - while(l1) { - le->link = l1; - le = l1; - l1 = l1->link; - } - break; - } - if(datcmp(l1, l2) < 0) { - le->link = l1; - le = l1; - l1 = l1->link; - } else { - le->link = l2; - le = l2; - l2 = l2->link; - } - } - le->link = 0; - return l; -} - -static Prog *datp; - -Prog* -datsort(Prog *l) +vlong +rnd(vlong v, vlong r) { - Prog *p; - Adr *a; + vlong c; - for(p = l; p != P; p = p->link) { - a = &p->from; - a->offset += a->sym->value; - } - datp = dsort(l); - return datp; + if(r <= 0) + return v; + v += r - 1; + c = v % r; + if(c < 0) + c += r; + v -= c; + return v; } void -datblk(int32 s, int32 n) +genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) { - Prog *p; - uchar *cast; - int32 l, fl, j; - vlong o; - int i, c; - Adr *a; - - for(p = datp; p != P; p = p->link) { - a = &p->from; - l = a->offset - s; - if(l+a->scale < 0) - continue; - datp = p; - break; - } - - memset(buf.dbuf, 0, n+Dbufslop); - for(p = datp; p != P; p = p->link) { - a = &p->from; - - l = a->offset - s; - if(l >= n) - break; - - c = a->scale; - i = 0; - if(l < 0) { - if(l+c <= 0) + Auto *a; + Sym *s; + int h; + + for(h=0; hhash) { + switch(s->type&~SSUB) { + case SCONST: + case SRODATA: + case SDATA: + case SELFDATA: + case SMACHOGOT: + if(!s->reachable) + continue; + put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype); continue; - i = -l; - l = 0; - } - - curp = p; - if(!a->sym->reachable) - diag("unreachable symbol in datblk - %s", a->sym->name); - if(a->sym->type == SMACHO) - continue; - if(p->as != AINIT && p->as != ADYNT) { - for(j=l+(c-i)-1; j>=l; j--) - if(buf.dbuf[j]) { - print("%P\n", p); - diag("multiple initialization for %d %d", s, j); - break; - } - } - - switch(p->to.type) { - case D_FCONST: - switch(c) { - default: - case 4: - fl = ieeedtof(&p->to.ieee); - cast = (uchar*)&fl; - for(; ito.ieee; - for(; ito.scon[i]; - l++; - } - break; + case SBSS: + if(!s->reachable) + continue; + put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype); + continue; - default: - o = p->to.offset; - if(p->to.type == D_SIZE) - o += p->to.sym->size; - if(p->to.type == D_ADDR) { - if(p->to.index != D_STATIC && p->to.index != D_EXTERN) - diag("DADDR type%P", p); - if(p->to.sym) { - if(p->to.sym->type == SUNDEF) - ckoff(p->to.sym, o); - if(p->to.sym->type == Sxxx) { - curtext = p; // show useful name in diag's output - diag("missing symbol %s", p->to.sym->name); - } - o += p->to.sym->value; - if(p->to.sym->type != STEXT && p->to.sym->type != SUNDEF) - o += INITDAT; - if(dlm) - dynreloc(p->to.sym, l+s+INITDAT, 1); - } - } - fl = o; - cast = (uchar*)&fl; - switch(c) { - default: - diag("bad nuxi %d %d\n%P", c, i, curp); - break; - case 1: - for(; iname, 'f', s->value, 0, s->version, 0); + continue; } - break; } } - write(cout, buf.dbuf, n); - if(!debug['a']) - return; - - /* - * a second pass just to print the asm - */ - for(p = datap; p != P; p = p->link) { - a = &p->from; - - l = a->offset - s; - if(l >= n) - continue; - - c = a->scale; - i = 0; - if(l < 0) - continue; - - if(a->sym->type == SMACHO) + for(s = textp; s != nil; s = s->next) { + if(s->text == nil) continue; - switch(p->to.type) { - case D_FCONST: - switch(c) { - default: - case 4: - fl = ieeedtof(&p->to.ieee); - cast = (uchar*)&fl; - outa(c, cast, fnuxi4, l+s+INITDAT); - break; - case 8: - cast = (uchar*)&p->to.ieee; - outa(c, cast, fnuxi8, l+s+INITDAT); - break; - } - break; - - case D_SCONST: - outa(c, (uchar*)p->to.scon, nil, l+s+INITDAT); - break; - - default: - o = p->to.offset; - if(p->to.type == D_SIZE) - o += p->to.sym->size; - if(p->to.type == D_ADDR) { - if(p->to.sym) { - o += p->to.sym->value; - if(p->to.sym->type != STEXT && p->to.sym->type != SUNDEF) - o += INITDAT; - } - } - fl = o; - cast = (uchar*)&fl; - switch(c) { - case 1: - outa(c, cast, inuxi1, l+s+INITDAT); - break; - case 2: - outa(c, cast, inuxi2, l+s+INITDAT); - break; - case 4: - outa(c, cast, inuxi4, l+s+INITDAT); - break; - case 8: - cast = (uchar*)&o; - outa(c, cast, inuxi8, l+s+INITDAT); - break; - } - break; - } + /* filenames first */ + for(a=s->autom; a; a=a->link) + if(a->type == D_FILE) + put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0); + else + if(a->type == D_FILE1) + put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0); + + put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); + + /* frame, auto and param after */ + put(nil, ".frame", 'm', s->text->to.offset+8, 0, 0, 0); + + for(a=s->autom; a; a=a->link) + if(a->type == D_AUTO) + put(nil, a->asym->name, 'a', -a->aoffset, 0, 0, a->gotype); + else + if(a->type == D_PARAM) + put(nil, a->asym->name, 'p', a->aoffset, 0, 0, a->gotype); } + if(debug['v'] || debug['n']) + Bprint(&bso, "symsize = %ud\n", symsize); + Bflush(&bso); } - -vlong -rnd(vlong v, vlong r) -{ - vlong c; - - if(r <= 0) - return v; - v += r - 1; - c = v % r; - if(c < 0) - c += r; - v -= c; - return v; -} - diff --git a/src/cmd/6l/doc.go b/src/cmd/6l/doc.go index a74e9b5c0..501317f36 100644 --- a/src/cmd/6l/doc.go +++ b/src/cmd/6l/doc.go @@ -32,8 +32,8 @@ Options new in this version: Write Apple Mach-O binaries (default when $GOOS is darwin) -H7 Write Linux ELF binaries (default when $GOOS is linux) --L dir1,dir2,.. - Search for libraries (package files) in the comma-separated list of directories. +-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. diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h index 3db0b450a..1c52ea89d 100644 --- a/src/cmd/6l/l.h +++ b/src/cmd/6l/l.h @@ -44,7 +44,7 @@ enum #define P ((Prog*)0) #define S ((Sym*)0) -#define TNAME (curtext?curtext->from.sym->name:noname) +#define TNAME (cursym?cursym->name:noname) #define cput(c)\ { *cbp++ = c;\ if(--cbc <= 0)\ @@ -56,6 +56,7 @@ typedef struct Sym Sym; typedef struct Auto Auto; typedef struct Optab Optab; typedef struct Movtab Movtab; +typedef struct Reloc Reloc; struct Adr { @@ -67,11 +68,7 @@ struct Adr Ieee u0ieee; char *u0sbig; } u0; - union - { - Auto* u1autom; - Sym* u1sym; - } u1; + Sym* sym; short type; char index; char scale; @@ -83,18 +80,25 @@ struct Adr #define ieee u0.u0ieee #define sbig u0.u0sbig -#define autom u1.u1autom -#define sym u1.u1sym +struct Reloc +{ + int32 off; + uchar siz; + int32 type; + int64 add; + Sym* sym; +}; struct Prog { Adr from; Adr to; - Prog *forwd; + Prog* forwd; + Prog* comefrom; Prog* link; - Prog* dlink; Prog* pcond; /* work on this */ vlong pc; + int32 spadj; int32 line; short as; char ft; /* oclass cache */ @@ -102,9 +106,12 @@ struct Prog uchar mark; /* work on these */ uchar back; - char width; /* fake for DATA */ + char width; /* fake for DATA */ char mode; /* 16, 32, or 64 */ }; +#define datasize from.scale +#define textflag from.scale + struct Auto { Sym* asym; @@ -115,25 +122,39 @@ struct Auto }; struct Sym { - char *name; + char* name; short type; short version; - short become; - short frame; - uchar subtype; uchar dupok; uchar reachable; uchar dynexport; + uchar special; + int32 dynid; + int32 sig; + int32 plt; + int32 got; + Sym* hash; // in hash table + Sym* next; // in text or data list + Sym* sub; // in SSUB list + Sym* outer; // container of sub vlong value; vlong size; - int32 sig; - Sym* link; - Prog* text; - Prog* data; Sym* gotype; char* file; char* dynimpname; char* dynimplib; + + // STEXT + Auto* autom; + Prog* text; + + // SDATA, SBSS + uchar* p; + int32 np; + int32 maxp; + Reloc* r; + int32 nr; + int32 maxr; }; struct Optab { @@ -154,21 +175,25 @@ struct Movtab enum { Sxxx, + + /* order here is order in output file */ STEXT = 1, + SELFDATA, + SMACHOPLT, + SRODATA, SDATA, + SMACHOGOT, SBSS, - SDATA1, + SXREF, + SMACHODYNSTR, + SMACHODYNSYM, + SMACHOINDIRECTPLT, + SMACHOINDIRECTGOT, SFILE, SCONST, - SUNDEF, - - SIMPORT, - SEXPORT, - - SMACHO, - SFIXED, - SELFDATA, + SDYNIMPORT, + SSUB = 1<<8, NHASH = 10007, NHUNK = 100000, @@ -274,8 +299,6 @@ enum Rxx = 1<<1, /* extend sib index */ Rxb = 1<<0, /* extend modrm r/m, sib base, or opcode reg */ - Roffset = 22, /* no. bits for offset in relocation address */ - Rindex = 10, /* no. bits for index in relocation address */ Maxand = 10, /* in -a output width of the byte codes */ }; @@ -300,35 +323,30 @@ EXTERN union EXTERN int32 HEADR; EXTERN int32 HEADTYPE; -EXTERN vlong INITDAT; EXTERN int32 INITRND; EXTERN vlong INITTEXT; +EXTERN vlong INITDAT; EXTERN char* INITENTRY; /* entry point */ EXTERN Biobuf bso; -EXTERN int32 bsssize; EXTERN int cbc; EXTERN char* cbp; EXTERN char* pcstr; EXTERN Auto* curauto; EXTERN Auto* curhist; EXTERN Prog* curp; -EXTERN Prog* curtext; -EXTERN Prog* datap; -EXTERN Prog* edatap; -EXTERN vlong datsize; +EXTERN Sym* cursym; +EXTERN Sym* datap; EXTERN vlong elfdatsize; EXTERN char debug[128]; EXTERN char literal[32]; -EXTERN Prog* etextp; -EXTERN Prog* firstp; -EXTERN int xrefresolv; +EXTERN Sym* textp; +EXTERN Sym* etextp; EXTERN char ycover[Ymax*Ymax]; EXTERN uchar* andptr; EXTERN uchar* rexptr; EXTERN uchar and[30]; EXTERN int reg[D_NONE]; EXTERN int regrex[D_NONE+1]; -EXTERN Prog* lastp; EXTERN int32 lcsize; EXTERN int nerrors; EXTERN char* noname; @@ -338,8 +356,7 @@ EXTERN char* rpath; EXTERN int32 spsize; EXTERN Sym* symlist; EXTERN int32 symsize; -EXTERN Prog* textp; -EXTERN vlong textsize; +EXTERN int tlsoffset; EXTERN int version; EXTERN Prog zprg; EXTERN int dtype; @@ -347,45 +364,29 @@ EXTERN char* paramspace; EXTERN Sym* adrgotype; // type symbol on last Adr read EXTERN Sym* fromgotype; // type symbol on last p->from read -EXTERN Adr* reloca; -EXTERN int doexp; // export table -EXTERN int dlm; // dynamically loadable module -EXTERN int imports, nimports; -EXTERN int exports, nexports; -EXTERN char* EXPTAB; -EXTERN Prog undefp; EXTERN vlong textstksiz; EXTERN vlong textarg; extern char thechar; -EXTERN int dynptrsize; EXTERN int elfstrsize; EXTERN char* elfstrdat; EXTERN int elftextsh; -#define UP (&undefp) - extern Optab optab[]; extern Optab* opindex[]; extern char* anames[]; int Aconv(Fmt*); int Dconv(Fmt*); +int Iconv(Fmt*); int Pconv(Fmt*); int Rconv(Fmt*); int Sconv(Fmt*); void addhist(int32, int); void addstackmark(void); Prog* appendp(Prog*); -vlong addstring(Sym*, char*); -vlong adduint32(Sym*, uint32); -vlong adduint64(Sym*, uint64); -vlong addaddr(Sym*, Sym*); -vlong addsize(Sym*, Sym*); void asmb(void); void asmdyn(void); void asmins(Prog*); -void asmlc(void); -void asmsp(void); void asmsym(void); void asmelfsym(void); vlong atolwhex(char*); @@ -393,35 +394,29 @@ Prog* brchain(Prog*); Prog* brloop(Prog*); void buildop(void); void cflush(void); -void ckoff(Sym*, int32); Prog* copyp(Prog*); +vlong cpos(void); double cputime(void); void datblk(int32, int32); void deadcode(void); void diag(char*, ...); -void dobss(void); void dodata(void); void doelf(void); -void doinit(void); void domacho(void); void doprof1(void); void doprof2(void); void dostkoff(void); -void dynreloc(Sym*, uint32, int); vlong entryvalue(void); -void export(void); void follow(void); void gethunk(void); void gotypestrings(void); -void import(void); void listinit(void); Sym* lookup(char*, int); void lputb(int32); void lputl(int32); +void instinit(void); void main(int, char*[]); -void mkfwd(void); void* mysbrk(uint32); -Prog* newdata(Sym*, int, int, int); Prog* newtext(Prog*, Sym*); void nopout(Prog*); int opsize(Prog*); @@ -429,19 +424,14 @@ void patch(void); Prog* prg(void); void parsetextconst(vlong); int relinv(int); -int32 reuse(Prog*, Sym*); vlong rnd(vlong, vlong); void span(void); -void strnput(char*, int); void undef(void); -vlong vaddr(Adr*); vlong symaddr(Sym*); void vputl(uint64); void wputb(uint16); void wputl(uint16); void xdefine(char*, int, vlong); -void xfol(Prog*); -void zaddr(Biobuf*, Adr*, Sym*[]); void machseg(char*, vlong, vlong, vlong, vlong, uint32, uint32, uint32, uint32); void machsymseg(uint32, uint32); @@ -460,3 +450,9 @@ uint32 machheadr(void); #pragma varargck type "R" int #pragma varargck type "A" int #pragma varargck argpos diag 1 + +/* Used by ../ld/dwarf.c */ +enum +{ + DWARFREGSP = 7 +}; diff --git a/src/cmd/6l/list.c b/src/cmd/6l/list.c index 195e11d1d..f39efa2e8 100644 --- a/src/cmd/6l/list.c +++ b/src/cmd/6l/list.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Printing. + #include "l.h" #include "../ld/lib.h" @@ -42,46 +44,36 @@ listinit(void) fmtinstall('D', Dconv); fmtinstall('S', Sconv); fmtinstall('P', Pconv); + fmtinstall('I', Iconv); } int Pconv(Fmt *fp) { - char str[STRINGSZ], str1[STRINGSZ]; Prog *p; p = va_arg(fp->args, Prog*); - if(p == P) - return fmtstrcpy(fp, "

    "); - bigP = p; - - snprint(str1, sizeof(str1), "(%ld)", p->line); switch(p->as) { case ATEXT: if(p->from.scale) { - snprint(str, sizeof(str), "%-7s %-7A %D,%d,%lD", - str1, p->as, &p->from, p->from.scale, &p->to); + fmtprint(fp, "(%d) %A %D,%d,%D", + p->line, p->as, &p->from, p->from.scale, &p->to); break; } - snprint(str, sizeof(str), "%-7s %-7A %D,%lD", - str1, p->as, &p->from, &p->to); - break; - default: - snprint(str, sizeof(str), "%-7s %-7A %D,%D", - str1, p->as, &p->from, &p->to); + fmtprint(fp, "(%d) %A %D,%D", + p->line, p->as, &p->from, &p->to); break; - case ADATA: - case AINIT: - case ADYNT: - snprint(str, sizeof(str), "%-7s %-7A %D/%d,%D", - str1, p->as, &p->from, p->from.scale, &p->to); + case AINIT_: + case ADYNT_: + fmtprint(fp, "(%d) %A %D/%d,%D", + p->line, p->as, &p->from, p->from.scale, &p->to); break; } bigP = P; - return fmtstrcpy(fp, str); + return 0; } int @@ -187,7 +179,7 @@ Dconv(Fmt *fp) break; case D_FCONST: - snprint(str, sizeof(str), "$(%.8lux,%.8lux)", a->ieee.h, a->ieee.l); + snprint(str, sizeof(str), "$(%.8ux,%.8ux)", a->ieee.h, a->ieee.l); break; case D_SCONST: @@ -402,19 +394,48 @@ Sconv(Fmt *fp) return fmtstrcpy(fp, str); } +int +Iconv(Fmt *fp) +{ + int i, n; + uchar *p; + char *s; + Fmt fmt; + + n = fp->prec; + fp->prec = 0; + if(!(fp->flags&FmtPrec) || n < 0) + return fmtstrcpy(fp, "%I"); + fp->flags &= ~FmtPrec; + p = va_arg(fp->args, uchar*); + + // format into temporary buffer and + // call fmtstrcpy to handle padding. + fmtstrinit(&fmt); + for(i=0; ifrom.sym != S) - tn = curtext->from.sym->name; + tn = ""; + sep = ""; + if(cursym != S) { + tn = cursym->name; + sep = ": "; + } va_start(arg, fmt); vseprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); - print("%s: %s\n", tn, buf); + print("%s%s%s\n", tn, sep, buf); nerrors++; if(nerrors > 20) { diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c index 5a4b6a3fc..96d78c3b9 100644 --- a/src/cmd/6l/obj.c +++ b/src/cmd/6l/obj.c @@ -28,11 +28,14 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Reading object files. + #define EXTERN #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" #include "../ld/macho.h" +#include "../ld/dwarf.h" #include char *noname = ""; @@ -51,28 +54,6 @@ char* paramspace = "FP"; * options used: 189BLQSWabcjlnpsvz */ -static int -isobjfile(char *f) -{ - int n, v; - Biobuf *b; - char buf1[5], buf2[SARMAG]; - - b = Bopen(f, OREAD); - if(b == nil) - return 0; - n = Bread(b, buf1, 5); - if(n == 5 && (buf1[2] == 1 && buf1[3] == '<' || buf1[3] == 1 && buf1[4] == '<')) - v = 1; /* good enough for our purposes */ - else { - Bseek(b, 0, 0); - n = Bread(b, buf2, SARMAG); - v = n == SARMAG && strncmp(buf2, ARMAG, SARMAG) == 0; - } - Bterm(b); - return v; -} - void usage(void) { @@ -83,7 +64,7 @@ usage(void) void main(int argc, char *argv[]) { - int i, c; + int c; Binit(&bso, 1, OWRITE); cout = -1; @@ -187,6 +168,11 @@ main(int argc, char *argv[]) INITRND = 4096; break; case 6: /* apple MACH */ + /* + * OS X system constant - offset from 0(GS) to our TLS. + * Explained in ../../libcgo/darwin_amd64.c. + */ + tlsoffset = 0x8a0; machoinit(); HEADR = MACHORESERVE; if(INITRND == -1) @@ -198,6 +184,13 @@ main(int argc, char *argv[]) break; case 7: /* elf64 executable */ case 9: /* freebsd */ + /* + * 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. + */ + tlsoffset = -16; elfinit(); HEADR = ELFRESERVE; if(INITTEXT == -1) @@ -209,117 +202,13 @@ main(int argc, char *argv[]) break; } if(INITDAT != 0 && INITRND != 0) - print("warning: -D0x%llux is ignored because of -R0x%lux\n", + print("warning: -D0x%llux is ignored because of -R0x%ux\n", INITDAT, INITRND); if(debug['v']) - Bprint(&bso, "HEADER = -H%ld -T0x%llux -D0x%llux -R0x%lux\n", + Bprint(&bso, "HEADER = -H%d -T0x%llux -D0x%llux -R0x%ux\n", HEADTYPE, INITTEXT, INITDAT, INITRND); Bflush(&bso); - for(i=1; optab[i].as; i++) { - c = optab[i].as; - if(opindex[c] != nil) { - diag("phase error in optab: %d (%A)", i, c); - errorexit(); - } - opindex[c] = &optab[i]; - } - - for(i=0; i= D_AL && i <= D_R15B) { - reg[i] = (i-D_AL) & 7; - if(i >= D_SPB && i <= D_DIB) - regrex[i] = 0x40; - if(i >= D_R8B && i <= D_R15B) - regrex[i] = Rxr | Rxx | Rxb; - } - if(i >= D_AH && i<= D_BH) - reg[i] = 4 + ((i-D_AH) & 7); - if(i >= D_AX && i <= D_R15) { - reg[i] = (i-D_AX) & 7; - if(i >= D_R8) - regrex[i] = Rxr | Rxx | Rxb; - } - if(i >= D_F0 && i <= D_F0+7) - reg[i] = (i-D_F0) & 7; - if(i >= D_M0 && i <= D_M0+7) - reg[i] = (i-D_M0) & 7; - if(i >= D_X0 && i <= D_X0+15) { - reg[i] = (i-D_X0) & 7; - if(i >= D_X0+8) - regrex[i] = Rxr | Rxx | Rxb; - } - if(i >= D_CR+8 && i <= D_CR+15) - regrex[i] = Rxr; - } + instinit(); zprg.link = P; zprg.pcond = P; @@ -334,50 +223,20 @@ main(int argc, char *argv[]) pcstr = "%.6llux "; nuxiinit(); histgen = 0; - textp = P; - datap = P; - edatap = P; pc = 0; dtype = 4; version = 0; cbp = buf.cbuf; cbc = sizeof(buf.cbuf); - firstp = prg(); - lastp = firstp; addlibpath("command line", "command line", argv[0], "main"); loadlib(); - deadcode(); - - firstp = firstp->link; - if(firstp == P) - errorexit(); - - if(doexp || dlm){ - EXPTAB = "_exporttab"; - zerosig(EXPTAB); - zerosig("etext"); - zerosig("edata"); - zerosig("end"); - if(dlm){ - import(); - HEADTYPE = 2; - INITTEXT = 0; - INITDAT = 0; - INITRND = 8; - INITENTRY = EXPTAB; - } - export(); - } - patch(); follow(); doelf(); if(HEADTYPE == 6) domacho(); - dodata(); - dobss(); dostkoff(); paramspace = "SP"; /* (FP) now (SP) on output */ if(debug['p']) @@ -386,12 +245,18 @@ main(int argc, char *argv[]) else doprof2(); span(); - doinit(); + addexport(); + textaddress(); + pclntab(); + symtab(); + dodata(); + address(); + reloc(); asmb(); undef(); if(debug['v']) { Bprint(&bso, "%5.2f cpu time\n", cputime()); - Bprint(&bso, "%ld symbols\n", nsymbol); + Bprint(&bso, "%d symbols\n", nsymbol); Bprint(&bso, "%d sizeof adr\n", sizeof(Adr)); Bprint(&bso, "%d sizeof prog\n", sizeof(Prog)); } @@ -400,8 +265,19 @@ main(int argc, char *argv[]) errorexit(); } -void -zaddr(Biobuf *f, Adr *a, Sym *h[]) +static Sym* +zsym(char *pn, Biobuf *f, Sym *h[]) +{ + int o; + + o = Bgetc(f); + if(o < 0 || o >= NSYM || h[o] == nil) + mangle(pn); + return h[o]; +} + +static void +zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) { int t; int32 l; @@ -425,7 +301,7 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) } a->sym = S; if(t & T_SYM) - a->sym = h[Bgetc(f)]; + a->sym = zsym(pn, f, h); a->type = D_NONE; if(t & T_FCONST) { a->ieee.l = Bget4(f); @@ -438,16 +314,17 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) } if(t & T_TYPE) a->type = Bgetc(f); + if(a->type < 0 || a->type >= D_SIZE) + mangle(pn); adrgotype = S; if(t & T_GOTYPE) - adrgotype = h[Bgetc(f)]; + adrgotype = zsym(pn, f, h); s = a->sym; - if(s == S) - return; - t = a->type; + if(t == D_INDIR+D_GS) + a->offset += tlsoffset; if(t != D_AUTO && t != D_PARAM) { - if(adrgotype) + if(s && adrgotype) s->gotype = adrgotype; return; } @@ -484,7 +361,7 @@ void ldobj1(Biobuf *f, char *pkg, int64 len, char *pn) { vlong ipc; - Prog *p, *t; + Prog *p; int v, o, r, skip, mode; Sym *h[NSYM], *s, *di; uint32 sig; @@ -492,7 +369,9 @@ ldobj1(Biobuf *f, char *pkg, int64 len, char *pn) int ntext; vlong eof; char src[1024]; + Prog *lastp; + lastp = nil; ntext = 0; eof = Boffset(f) + len; di = S; @@ -549,7 +428,7 @@ loop: if(sig != 0){ if(s->sig != 0 && s->sig != sig) diag("incompatible type signatures" - "%lux(%s) and %lux(%s) for %s", + "%ux(%s) and %ux(%s) for %s", s->sig, s->file, sig, pn, s->name); s->sig = sig; s->file = pn; @@ -557,6 +436,8 @@ loop: if(debug['W']) print(" ANAME %s\n", s->name); + if(o < 0 || o >= nelem(h)) + mangle(pn); h[o] = s; if((v == D_EXTERN || v == D_STATIC) && s->type == 0) s->type = SXREF; @@ -571,6 +452,7 @@ loop: histfrogp++; } else collapsefrog(s); + dwarfaddfrag(s->value, s->name); } goto loop; } @@ -582,9 +464,18 @@ loop: p->mode = mode; p->ft = 0; p->tt = 0; - zaddr(f, &p->from, h); + zaddr(pn, f, &p->from, h); fromgotype = adrgotype; - zaddr(f, &p->to, h); + zaddr(pn, f, &p->to, h); + + switch(p->as) { + case ATEXT: + case ADATA: + case AGLOBL: + if(p->from.sym == S) + mangle(pn); + break; + } if(debug['W']) print("%P\n", p); @@ -606,10 +497,10 @@ loop: case AEND: histtoauto(); - if(curtext != P) - curtext->to.autom = curauto; + if(cursym != nil && cursym->text) + cursym->autom = curauto; curauto = 0; - curtext = P; + cursym = nil; if(Boffset(f) == eof) return; goto newloop; @@ -618,89 +509,41 @@ loop: s = p->from.sym; if(s->type == 0 || s->type == SXREF) { s->type = SBSS; - s->value = 0; + s->size = 0; } - if(s->type != SBSS) { + if(s->type != SBSS && !s->dupok) { diag("%s: redefinition: %s in %s", pn, s->name, TNAME); s->type = SBSS; - s->value = 0; + s->size = 0; } - if(p->to.offset > s->value) - s->value = p->to.offset; + if(p->to.offset > s->size) + s->size = p->to.offset; if(p->from.scale & DUPOK) s->dupok = 1; + if(p->from.scale & RODATA) + s->type = SRODATA; goto loop; - case ADYNT: - if(p->to.sym == S) { - diag("DYNT without a sym\n%P", p); - break; - } - di = p->to.sym; - p->from.scale = 4; - if(di->type == SXREF) { - if(debug['z']) - Bprint(&bso, "%P set to %d\n", p, dtype); - di->type = SCONST; - di->value = dtype; - dtype += 4; - } - if(p->from.sym == S) - break; - - p->from.offset = di->value; - p->from.sym->type = SDATA; - if(curtext == P) { - diag("DYNT not in text: %P", p); - break; - } - p->to.sym = curtext->from.sym; - p->to.type = D_ADDR; - p->to.index = D_EXTERN; - goto data; - - case AINIT: - if(p->from.sym == S) { - diag("INIT without a sym\n%P", p); - break; - } - if(di == S) { - diag("INIT without previous DYNT\n%P", p); - break; - } - p->from.offset = di->value; - p->from.sym->type = SDATA; - goto data; - case ADATA: - data: // Assume that AGLOBL comes after ADATA. // If we've seen an AGLOBL that said this sym was DUPOK, // ignore any more ADATA we see, which must be // redefinitions. s = p->from.sym; - if(s != S && s->dupok) { + if(s->dupok) { // if(debug['v']) // Bprint(&bso, "skipping %s in %s: dupok\n", s->name, pn); goto loop; } - if(s != S) { - p->dlink = s->data; - s->data = p; - if(s->file == nil) - s->file = pn; - else if(s->file != pn) { - diag("multiple initialization for %s: in both %s and %s", s->name, s->file, pn); - errorexit(); - } + if(s->file == nil) + s->file = pn; + else if(s->file != pn) { + diag("multiple initialization for %s: in both %s and %s", s->name, s->file, pn); + errorexit(); } - if(edatap == P) - datap = p; - else - edatap->link = p; - edatap = p; - p->link = P; + savedata(s, p); + unmal(p, sizeof *p); goto loop; case AGOK: @@ -710,23 +553,29 @@ loop: case ATEXT: s = p->from.sym; + if(s->text != nil) { + diag("%s: %s: redefinition", pn, s->name); + return; + } if(ntext++ == 0 && s->type != 0 && s->type != SXREF) { /* redefinition, so file has probably been seen before */ if(debug['v']) Bprint(&bso, "skipping: %s: redefinition: %s", pn, s->name); return; } - if(curtext != P) { + if(cursym != nil && cursym->text) { histtoauto(); - curtext->to.autom = curauto; + cursym->autom = curauto; curauto = 0; } skip = 0; - curtext = p; - if(s == S) { - diag("%s: no TEXT symbol: %P", pn, p); - errorexit(); - } + if(etextp) + etextp->next = s; + else + textp = s; + etextp = s; + s->text = p; + cursym = s; if(s->type != 0 && s->type != SXREF) { if(p->from.scale & DUPOK) { skip = 1; @@ -739,7 +588,10 @@ loop: diag("%s: type mismatch for %s", pn, s->name); s->gotype = fromgotype; } - newtext(p, s); + s->type = STEXT; + s->value = pc; + lastp = p; + p->pc = pc++; goto loop; case AMODE: @@ -772,24 +624,12 @@ loop: goto casdef; if(p->from.type == D_FCONST) { /* size sb 9 max */ - sprint(literal, "$%lux", ieeedtof(&p->from.ieee)); + sprint(literal, "$%ux", ieeedtof(&p->from.ieee)); s = lookup(literal, 0); if(s->type == 0) { - s->type = SBSS; - s->value = 4; - t = prg(); - t->as = ADATA; - t->line = p->line; - t->from.type = D_EXTERN; - t->from.sym = s; - t->from.scale = 4; - t->to = p->from; - if(edatap == P) - datap = t; - else - edatap->link = t; - edatap = t; - t->link = P; + s->type = SDATA; + adduint32(s, ieeedtof(&p->from.ieee)); + s->reachable = 0; } p->from.type = D_EXTERN; p->from.sym = s; @@ -817,25 +657,14 @@ loop: goto casdef; if(p->from.type == D_FCONST) { /* size sb 18 max */ - sprint(literal, "$%lux.%lux", + sprint(literal, "$%ux.%ux", p->from.ieee.l, p->from.ieee.h); s = lookup(literal, 0); if(s->type == 0) { - s->type = SBSS; - s->value = 8; - t = prg(); - t->as = ADATA; - t->line = p->line; - t->from.type = D_EXTERN; - t->from.sym = s; - t->from.scale = 8; - t->to = p->from; - if(edatap == P) - datap = t; - else - edatap->link = t; - edatap = t; - t->link = P; + s->type = SDATA; + adduint32(s, p->from.ieee.l); + adduint32(s, p->from.ieee.h); + s->reachable = 0; } p->from.type = D_EXTERN; p->from.sym = s; @@ -847,13 +676,18 @@ loop: default: if(skip) nopout(p); + p->pc = pc; + pc++; if(p->to.type == D_BRANCH) p->to.offset += ipc; + if(lastp == nil) { + if(p->as != ANOP) + diag("unexpected instruction: %P", p); + goto loop; + } lastp->link = p; lastp = p; - p->pc = pc; - pc++; goto loop; } goto loop; @@ -895,154 +729,3 @@ appendp(Prog *q) p->mode = q->mode; return p; } - -void -doprof1(void) -{ - Sym *s; - int32 n; - Prog *p, *q; - - if(debug['v']) - Bprint(&bso, "%5.2f profile 1\n", cputime()); - Bflush(&bso); - s = lookup("__mcount", 0); - n = 1; - for(p = firstp->link; p != P; p = p->link) { - if(p->as == ATEXT) { - q = prg(); - q->line = p->line; - q->link = datap; - datap = q; - q->as = ADATA; - q->from.type = D_EXTERN; - q->from.offset = n*4; - q->from.sym = s; - q->from.scale = 4; - q->to = p->from; - q->to.type = D_CONST; - - q = prg(); - q->line = p->line; - q->pc = p->pc; - q->link = p->link; - p->link = q; - p = q; - p->as = AADDL; - p->from.type = D_CONST; - p->from.offset = 1; - p->to.type = D_EXTERN; - p->to.sym = s; - p->to.offset = n*4 + 4; - - n += 2; - continue; - } - } - q = prg(); - q->line = 0; - q->link = datap; - datap = q; - - q->as = ADATA; - q->from.type = D_EXTERN; - q->from.sym = s; - q->from.scale = 4; - q->to.type = D_CONST; - q->to.offset = n; - - s->type = SBSS; - s->value = n*4; -} - -void -doprof2(void) -{ - Sym *s2, *s4; - Prog *p, *q, *ps2, *ps4; - - if(debug['v']) - Bprint(&bso, "%5.2f profile 2\n", cputime()); - Bflush(&bso); - - s2 = lookup("_profin", 0); - s4 = lookup("_profout", 0); - if(s2->type != STEXT || s4->type != STEXT) { - diag("_profin/_profout not defined"); - return; - } - - ps2 = P; - ps4 = P; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - if(p->from.sym == s2) { - p->from.scale = 1; - ps2 = p; - } - if(p->from.sym == s4) { - p->from.scale = 1; - ps4 = p; - } - } - } - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - curtext = p; - - if(p->from.scale & NOPROF) { /* dont profile */ - for(;;) { - q = p->link; - if(q == P) - break; - if(q->as == ATEXT) - break; - p = q; - } - continue; - } - - /* - * JMPL profin - */ - q = prg(); - q->line = p->line; - q->pc = p->pc; - q->link = p->link; - p->link = q; - p = q; - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = ps2; - p->to.sym = s2; - - continue; - } - if(p->as == ARET) { - /* - * RET - */ - q = prg(); - q->as = ARET; - q->from = p->from; - q->to = p->to; - q->link = p->link; - p->link = q; - - /* - * JAL profout - */ - p->as = ACALL; - p->from = zprg.from; - p->to = zprg.to; - p->to.type = D_BRANCH; - p->pcond = ps4; - p->to.sym = s4; - - p = q; - - continue; - } - } -} - diff --git a/src/cmd/6l/optab.c b/src/cmd/6l/optab.c index c729f0e23..6cc50313e 100644 --- a/src/cmd/6l/optab.c +++ b/src/cmd/6l/optab.c @@ -783,7 +783,7 @@ Optab optab[] = { AMOVBWSX, ymb_rl, Pq, 0xbe }, { AMOVBWZX, ymb_rl, Pq, 0xb6 }, { AMOVO, yxmov, Pe, 0x6f,0x7f }, - { AMOVOU, yxmov, Pf2, 0x6f,0x7f }, + { AMOVOU, yxmov, Pf3, 0x6f,0x7f }, { AMOVHLPS, yxr, Pm, 0x12 }, { AMOVHPD, yxmov, Pe, 0x16,0x17 }, { AMOVHPS, yxmov, Pm, 0x16,0x17 }, @@ -907,9 +907,9 @@ Optab optab[] = { APOPQ, ypopl, Py, 0x58,0x8f,(00) }, { APOPW, ypopl, Pe, 0x58,0x8f,(00) }, { APOR, ymm, Py, 0xeb,Pe,0xeb }, - { APSADBW, yxm, Pw, Pe,0xf6 }, + { APSADBW, yxm, Pq, 0xf6 }, { APSHUFHW, yxshuf, Pf3, 0x70 }, - { APSHUFL, yxm, Pw, Pe,0x70 }, + { APSHUFL, yxshuf, Pq, 0x70 }, { APSHUFLW, yxshuf, Pf2, 0x70 }, { APSHUFW, ymshuf, Pm, 0x70 }, { APSLLO, ypsdq, Pq, 0x73,(07) }, diff --git a/src/cmd/6l/pass.c b/src/cmd/6l/pass.c index f86942926..5c4ed00a6 100644 --- a/src/cmd/6l/pass.c +++ b/src/cmd/6l/pass.c @@ -28,9 +28,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Code and data passes. + #include "l.h" #include "../ld/lib.h" +static void xfol(Prog*, Prog**); + // see ../../runtime/proc.c:/StackGuard enum { @@ -38,157 +42,6 @@ enum StackBig = 4096, }; -void -dodata(void) -{ - int i; - Sym *s; - Prog *p; - int32 t, u; - - if(debug['v']) - Bprint(&bso, "%5.2f dodata\n", cputime()); - Bflush(&bso); - for(p = datap; p != P; p = p->link) { - curtext = p; // for diag messages - s = p->from.sym; - if(p->as == ADYNT || p->as == AINIT) - s->value = dtype; - if(s->type == SBSS) - s->type = SDATA; - if(s->type != SDATA && s->type != SELFDATA) - diag("initialize non-data (%d): %s\n%P", - s->type, s->name, p); - t = p->from.offset + p->width; - if(t > s->value) - diag("initialize bounds (%lld): %s\n%P", - s->value, s->name, p); - } - - /* allocate elf guys - must be segregated from real data */ - datsize = 0; - for(i=0; ilink) { - if(!s->reachable) - continue; - if(s->type != SELFDATA) - continue; - t = rnd(s->value, 8); - s->size = t; - s->value = datsize; - datsize += t; - } - elfdatsize = datsize; - - /* allocate small guys */ - for(i=0; ilink) { - if(!s->reachable) - continue; - if(s->type != SDATA) - if(s->type != SBSS) - continue; - t = s->value; - if(t == 0 && s->name[0] != '.') { - diag("%s: no size", s->name); - t = 1; - } - t = rnd(t, 4); - s->value = t; - if(t > MINSIZ) - continue; - if(t >= 8) - datsize = rnd(datsize, 8); - s->size = t; - s->value = datsize; - datsize += t; - s->type = SDATA1; - } - - /* allocate the rest of the data */ - for(i=0; ilink) { - if(!s->reachable) - continue; - if(s->type != SDATA) { - if(s->type == SDATA1) - s->type = SDATA; - continue; - } - t = s->value; - if(t >= 8) - datsize = rnd(datsize, 8); - s->size = t; - s->value = datsize; - datsize += t; - } - if(datsize) - datsize = rnd(datsize, 8); - - if(debug['j']) { - /* - * pad data with bss that fits up to next - * 8k boundary, then push data to 8k - */ - u = rnd(datsize, 8192); - u -= datsize; - for(i=0; ilink) { - if(!s->reachable) - continue; - if(s->type != SBSS) - continue; - t = s->value; - if(t > u) - continue; - u -= t; - s->size = t; - s->value = datsize; - s->type = SDATA; - datsize += t; - } - datsize += u; - } -} - -void -dobss(void) -{ - int i; - Sym *s; - int32 t; - - if(dynptrsize > 0) { - /* dynamic pointer section between data and bss */ - datsize = rnd(datsize, 8); - } - - /* now the bss */ - bsssize = 0; - for(i=0; ilink) { - if(!s->reachable) - continue; - if(s->type != SBSS) - continue; - t = s->value; - s->size = t; - if(t >= 8) - bsssize = rnd(bsssize, 8); - s->value = bsssize + dynptrsize + datsize; - bsssize += t; - } - - xdefine("data", SBSS, 0); - xdefine("edata", SBSS, datsize); - xdefine("end", SBSS, dynptrsize + bsssize + datsize); - - if(debug['s']) - xdefine("symdat", SFIXED, 0); - else - xdefine("symdat", SFIXED, SYMDATVA); -} - Prog* brchain(Prog *p) { @@ -205,19 +58,61 @@ brchain(Prog *p) void follow(void) { + Prog *firstp, *lastp; if(debug['v']) Bprint(&bso, "%5.2f follow\n", cputime()); Bflush(&bso); - firstp = prg(); - lastp = firstp; - xfol(textp); - lastp->link = P; - firstp = firstp->link; + + for(cursym = textp; cursym != nil; cursym = cursym->next) { + firstp = prg(); + lastp = firstp; + xfol(cursym->text, &lastp); + lastp->link = nil; + cursym->text = firstp->link; + } } -void -xfol(Prog *p) +static int +nofollow(int a) +{ + switch(a) { + case AJMP: + case ARET: + case AIRETL: + case AIRETQ: + case AIRETW: + case ARETFL: + case ARETFQ: + case ARETFW: + return 1; + } + return 0; +} + +static int +pushpop(int a) +{ + switch(a) { + case APUSHL: + case APUSHFL: + case APUSHQ: + case APUSHFQ: + case APUSHW: + case APUSHFW: + case APOPL: + case APOPFL: + case APOPQ: + case APOPFQ: + case APOPW: + case APOPFW: + return 1; + } + return 0; +} + +static void +xfol(Prog *p, Prog **last) { Prog *q; int i; @@ -226,55 +121,31 @@ xfol(Prog *p) loop: if(p == P) return; - if(p->as == ATEXT) - curtext = p; - if(!curtext->from.sym->reachable) { - p = p->pcond; - goto loop; - } if(p->as == AJMP) if((q = p->pcond) != P && q->as != ATEXT) { + /* mark instruction as done and continue layout at target of jump */ p->mark = 1; p = q; if(p->mark == 0) goto loop; } if(p->mark) { - /* copy up to 4 instructions to avoid branch */ + /* + * p goes here, but already used it elsewhere. + * copy up to 4 instructions or else branch to other copy. + */ for(i=0,q=p; i<4; i++,q=q->link) { if(q == P) break; - if(q == lastp) + if(q == *last) break; a = q->as; if(a == ANOP) { i--; continue; } - switch(a) { - case AJMP: - case ARET: - case AIRETL: - case AIRETQ: - case AIRETW: - case ARETFL: - case ARETFQ: - case ARETFW: - - case APUSHL: - case APUSHFL: - case APUSHQ: - case APUSHFQ: - case APUSHW: - case APUSHFW: - case APOPL: - case APOPFL: - case APOPQ: - case APOPFQ: - case APOPW: - case APOPFW: - goto brk; - } + if(nofollow(a) || pushpop(a)) + break; // NOTE(rsc): arm does goto copy if(q->pcond == P || q->pcond->mark) continue; if(a == ACALL || a == ALOOP) @@ -287,8 +158,8 @@ loop: q = copyp(p); p = p->link; q->mark = 1; - lastp->link = q; - lastp = q; + (*last)->link = q; + *last = q; if(q->as != a || q->pcond == P || q->pcond->mark) continue; @@ -296,14 +167,13 @@ loop: p = q->pcond; q->pcond = q->link; q->link = p; - xfol(q->link); + xfol(q->link, last); p = q->link; if(p->mark) return; goto loop; } } /* */ - brk:; q = prg(); q->as = AJMP; q->line = p->line; @@ -312,15 +182,22 @@ loop: q->pcond = p; p = q; } + + /* emit p */ p->mark = 1; - lastp->link = p; - lastp = p; + (*last)->link = p; + *last = p; a = p->as; - if(a == AJMP || a == ARET || a == AIRETL || a == AIRETQ || a == AIRETW || - a == ARETFL || a == ARETFQ || a == ARETFW) + + /* continue loop with what comes after p */ + if(nofollow(a)) return; - if(p->pcond != P) - if(a != ACALL) { + if(p->pcond != P && a != ACALL) { + /* + * some kind of conditional branch. + * recurse to follow one path. + * continue loop on the other. + */ q = brchain(p->link); if(q != P && q->mark) if(a != ALOOP) { @@ -328,7 +205,7 @@ loop: p->link = p->pcond; p->pcond = q; } - xfol(p->link); + xfol(p->link, last); q = brchain(p->pcond); if(q->mark) { p->pcond = q; @@ -376,31 +253,10 @@ relinv(int a) case AJOC: return AJOS; } diag("unknown relation: %s in %s", anames[a], TNAME); + errorexit(); return a; } -void -doinit(void) -{ - Sym *s; - Prog *p; - int x; - - for(p = datap; p != P; p = p->link) { - x = p->to.type; - if(x != D_EXTERN && x != D_STATIC) - continue; - s = p->to.sym; - if(s->type == 0 || s->type == SXREF) - diag("undefined %s initializer of %s", - s->name, p->from.sym->name); - p->to.offset += s->value; - p->to.type = D_CONST; - if(s->type == SDATA || s->type == SBSS) - p->to.offset += INITDAT; - } -} - void patch(void) { @@ -419,58 +275,58 @@ patch(void) s = lookup("exit", 0); vexit = s->value; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; + for(cursym = textp; cursym != nil; cursym = cursym->next) + for(p = cursym->text; p != P; p = p->link) { + if(HEADTYPE == 7 || HEADTYPE == 9) { + // ELF uses FS instead of GS. + if(p->from.type == D_INDIR+D_GS) + p->from.type = D_INDIR+D_FS; + if(p->to.type == D_INDIR+D_GS) + p->to.type = D_INDIR+D_FS; + } if(p->as == ACALL || (p->as == AJMP && p->to.type != D_BRANCH)) { s = p->to.sym; if(s) { if(debug['c']) Bprint(&bso, "%s calls %s\n", TNAME, s->name); - switch(s->type) { - default: + if((s->type&~SSUB) != STEXT) { /* diag prints TNAME first */ diag("undefined: %s", s->name); s->type = STEXT; s->value = vexit; continue; // avoid more error messages - case STEXT: - p->to.offset = s->value; - break; - case SUNDEF: - p->pcond = UP; - p->to.offset = 0; - break; } + if(s->text == nil) + continue; p->to.type = D_BRANCH; + p->to.offset = s->text->pc; + p->pcond = s->text; + continue; } } - if(p->to.type != D_BRANCH || p->pcond == UP) + if(p->to.type != D_BRANCH) continue; c = p->to.offset; - for(q = firstp; q != P;) { - if(q->forwd != P) - if(c >= q->forwd->pc) { - q = q->forwd; - continue; - } + for(q = cursym->text; q != P;) { if(c == q->pc) break; - q = q->link; + if(q->forwd != P && c >= q->forwd->pc) + q = q->forwd; + else + q = q->link; } if(q == P) { - diag("branch out of range in %s\n%P [%s]", - TNAME, p, p->to.sym ? p->to.sym->name : ""); + diag("branch out of range in %s (%#ux)\n%P [%s]", + TNAME, c, p, p->to.sym ? p->to.sym->name : ""); p->to.type = D_NONE; } p->pcond = q; } - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; + for(cursym = textp; cursym != nil; cursym = cursym->next) + for(p = cursym->text; p != P; p = p->link) { p->mark = 0; /* initialization for follow */ - if(p->pcond != P && p->pcond != UP) { + if(p->pcond != P) { p->pcond = brloop(p->pcond); if(p->pcond != P) if(p->to.type == D_BRANCH) @@ -479,40 +335,6 @@ patch(void) } } -#define LOG 5 -void -mkfwd(void) -{ - Prog *p; - int i; - int32 dwn[LOG], cnt[LOG]; - Prog *lst[LOG]; - - for(i=0; ilink) { - if(p->as == ATEXT) - curtext = p; - i--; - if(i < 0) - i = LOG-1; - p->forwd = P; - dwn[i]--; - if(dwn[i] <= 0) { - dwn[i] = cnt[i]; - if(lst[i] != P) - lst[i]->forwd = p; - lst[i] = p; - } - } -} - Prog* brloop(Prog *p) { @@ -553,378 +375,275 @@ dostkoff(void) { Prog *p, *q, *q1; int32 autoffset, deltasp; - int a, f, curframe, curbecome, maxbecome, pcsize; + int a, pcsize; uint32 moreconst1, moreconst2, i; for(i=0; ilink) { - if(p->as == ATEXT) { - for(i=0; ifrom.sym == symmorestack[i]) { - pmorestack[i] = p; - break; - } - } - } - } - - for(i=0; ilink) { - - /* find out how much arg space is used in this TEXT */ - if(p->to.type == (D_INDIR+D_SP)) - if(p->to.offset > curframe) - curframe = p->to.offset; - - switch(p->as) { - case ATEXT: - if(curtext && curtext->from.sym) { - curtext->from.sym->frame = curframe; - curtext->from.sym->become = curbecome; - if(curbecome > maxbecome) - maxbecome = curbecome; - } - curframe = 0; - curbecome = 0; - - curtext = p; - break; - - case ARET: - /* special form of RET is BECOME */ - if(p->from.type == D_CONST) - if(p->from.offset > curbecome) - curbecome = p->from.offset; - break; - } - } - if(curtext && curtext->from.sym) { - curtext->from.sym->frame = curframe; - curtext->from.sym->become = curbecome; - if(curbecome > maxbecome) - maxbecome = curbecome; - } - - if(debug['b']) - print("max become = %d\n", maxbecome); - xdefine("ALEFbecome", STEXT, maxbecome); - - curtext = 0; - for(p = firstp; p != P; p = p->link) { - switch(p->as) { - case ATEXT: - curtext = p; - break; - case ACALL: - if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) { - f = maxbecome - curtext->from.sym->frame; - if(f <= 0) - break; - /* calling a become or calling a variable */ - if(p->to.sym == S || p->to.sym->become) { - curtext->to.offset += f; - if(debug['b']) { - curp = p; - print("%D calling %D increase %d\n", - &curtext->from, &p->to, f); - } - } - } - break; - } + if(symmorestack[i]->type != STEXT) + diag("morestack trampoline not defined - %s", morename[i]); + pmorestack[i] = symmorestack[i]->text; } autoffset = 0; deltasp = 0; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - curtext = p; - parsetextconst(p->to.offset); - autoffset = textstksiz; - if(autoffset < 0) - autoffset = 0; - - q = P; - q1 = P; - if((p->from.scale & NOSPLIT) && autoffset >= StackSmall) - diag("nosplit func likely to overflow stack"); - - if(!(p->from.scale & NOSPLIT)) { - if(debug['K']) { - // 6l -K means check not only for stack - // overflow but stack underflow. - // On underflow, INT 3 (breakpoint). - // Underflow itself is rare but this also - // catches out-of-sync stack guard info - - p = appendp(p); - p->as = ACMPQ; - p->from.type = D_INDIR+D_R15; - p->from.offset = 8; - p->to.type = D_SP; - - p = appendp(p); - p->as = AJHI; - p->to.type = D_BRANCH; - p->to.offset = 4; - q1 = p; - - p = appendp(p); - p->as = AINT; - p->from.type = D_CONST; - p->from.offset = 3; - } - - if(autoffset < StackBig) { // do we need to call morestack? - if(autoffset <= StackSmall) { - // small stack - p = appendp(p); - p->as = ACMPQ; - p->from.type = D_SP; - p->to.type = D_INDIR+D_R15; - if(q1) { - q1->pcond = p; - q1 = P; - } - } else { - // large stack - p = appendp(p); - p->as = ALEAQ; - p->from.type = D_INDIR+D_SP; - p->from.offset = -(autoffset-StackSmall); - p->to.type = D_AX; - if(q1) { - q1->pcond = p; - q1 = P; - } + for(cursym = textp; cursym != nil; cursym = cursym->next) { + if(cursym->text == nil || cursym->text->link == nil) + continue; - p = appendp(p); - p->as = ACMPQ; - p->from.type = D_AX; - p->to.type = D_INDIR+D_R15; - } + p = cursym->text; + parsetextconst(p->to.offset); + autoffset = textstksiz; + if(autoffset < 0) + autoffset = 0; + + q = P; + q1 = P; + if((p->from.scale & NOSPLIT) && autoffset >= StackSmall) + diag("nosplit func likely to overflow stack"); + + if(!(p->from.scale & NOSPLIT)) { + p = appendp(p); // load g into CX + p->as = AMOVQ; + if(HEADTYPE == 7 || HEADTYPE == 9) // 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(debug['K']) { + // 6l -K means check not only for stack + // overflow but stack underflow. + // On underflow, INT 3 (breakpoint). + // Underflow itself is rare but this also + // catches out-of-sync stack guard info - // common - p = appendp(p); - p->as = AJHI; - p->to.type = D_BRANCH; - p->to.offset = 4; - q = p; - } + p = appendp(p); + p->as = ACMPQ; + p->from.type = D_INDIR+D_CX; + p->from.offset = 8; + p->to.type = D_SP; - /* 160 comes from 3 calls (3*8) 4 safes (4*8) and 104 guard */ - moreconst1 = 0; - if(autoffset+160 > 4096) - moreconst1 = (autoffset+160) & ~7LL; - moreconst2 = textarg; + p = appendp(p); + p->as = AJHI; + p->to.type = D_BRANCH; + p->to.offset = 4; + q1 = p; - // 4 varieties varieties (const1==0 cross const2==0) - // and 6 subvarieties of (const1==0 and const2!=0) p = appendp(p); - if(moreconst1 == 0 && moreconst2 == 0) { - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = pmorestack[0]; - p->to.sym = symmorestack[0]; - if(q1) { - q1->pcond = p; - q1 = P; - } - } else - if(moreconst1 != 0 && moreconst2 == 0) { - p->as = AMOVL; - p->from.type = D_CONST; - p->from.offset = moreconst1; - p->to.type = D_AX; - if(q1) { - q1->pcond = p; - q1 = P; - } + p->as = AINT; + p->from.type = D_CONST; + p->from.offset = 3; - p = appendp(p); - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = pmorestack[1]; - p->to.sym = symmorestack[1]; - } else - if(moreconst1 == 0 && moreconst2 <= 48 && moreconst2%8 == 0) { - i = moreconst2/8 + 3; - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = pmorestack[i]; - p->to.sym = symmorestack[i]; - if(q1) { - q1->pcond = p; - q1 = P; - } - } else - if(moreconst1 == 0 && moreconst2 != 0) { - p->as = AMOVL; - p->from.type = D_CONST; - p->from.offset = moreconst2; - p->to.type = D_AX; - if(q1) { - q1->pcond = p; - q1 = P; - } + p = appendp(p); + p->as = ANOP; + q1->pcond = p; + q1 = P; + } + if(autoffset < StackBig) { // do we need to call morestack? + if(autoffset <= StackSmall) { + // small stack p = appendp(p); - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = pmorestack[2]; - p->to.sym = symmorestack[2]; + p->as = ACMPQ; + p->from.type = D_SP; + p->to.type = D_INDIR+D_CX; } else { - p->as = AMOVQ; - p->from.type = D_CONST; - p->from.offset = (uint64)moreconst2 << 32; - p->from.offset |= moreconst1; + // large stack + p = appendp(p); + p->as = ALEAQ; + p->from.type = D_INDIR+D_SP; + p->from.offset = -(autoffset-StackSmall); p->to.type = D_AX; - if(q1) { - q1->pcond = p; - q1 = P; - } p = appendp(p); - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = pmorestack[3]; - p->to.sym = symmorestack[3]; + p->as = ACMPQ; + p->from.type = D_AX; + p->to.type = D_INDIR+D_CX; } - } - - if(q != P) - q->pcond = p->link; - if(autoffset) { + // common p = appendp(p); - p->as = AADJSP; - p->from.type = D_CONST; - p->from.offset = autoffset; - if(q != P) - q->pcond = p; + p->as = AJHI; + p->to.type = D_BRANCH; + p->to.offset = 4; + q = p; } - deltasp = autoffset; - if(debug['K'] > 1 && autoffset) { - // 6l -KK means double-check for stack overflow - // even after calling morestack and even if the - // function is marked as nosplit. - p = appendp(p); - p->as = AMOVQ; - p->from.type = D_INDIR+D_R15; - p->from.offset = 0; - p->to.type = D_BX; + /* 160 comes from 3 calls (3*8) 4 safes (4*8) and 104 guard */ + moreconst1 = 0; + if(autoffset+160+textarg > 4096) + moreconst1 = (autoffset+160) & ~7LL; + moreconst2 = textarg; - p = appendp(p); - p->as = ASUBQ; + // 4 varieties varieties (const1==0 cross const2==0) + // and 6 subvarieties of (const1==0 and const2!=0) + p = appendp(p); + if(moreconst1 == 0 && moreconst2 == 0) { + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = pmorestack[0]; + p->to.sym = symmorestack[0]; + } else + if(moreconst1 != 0 && moreconst2 == 0) { + p->as = AMOVL; p->from.type = D_CONST; - p->from.offset = StackSmall+32; - p->to.type = D_BX; - - p = appendp(p); - p->as = ACMPQ; - p->from.type = D_SP; - p->to.type = D_BX; + p->from.offset = moreconst1; + p->to.type = D_AX; p = appendp(p); - p->as = AJHI; + p->as = ACALL; p->to.type = D_BRANCH; - q1 = p; + p->pcond = pmorestack[1]; + p->to.sym = symmorestack[1]; + } else + if(moreconst1 == 0 && moreconst2 <= 48 && moreconst2%8 == 0) { + i = moreconst2/8 + 3; + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = pmorestack[i]; + p->to.sym = symmorestack[i]; + } else + if(moreconst1 == 0 && moreconst2 != 0) { + p->as = AMOVL; + p->from.type = D_CONST; + p->from.offset = moreconst2; + p->to.type = D_AX; p = appendp(p); - p->as = AINT; + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = pmorestack[2]; + p->to.sym = symmorestack[2]; + } else { + p->as = AMOVQ; p->from.type = D_CONST; - p->from.offset = 3; + p->from.offset = (uint64)moreconst2 << 32; + p->from.offset |= moreconst1; + p->to.type = D_AX; p = appendp(p); - p->as = ANOP; - q1->pcond = p; - q1 = P; + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = pmorestack[3]; + p->to.sym = symmorestack[3]; } } - pcsize = p->mode/8; - a = p->from.type; - if(a == D_AUTO) - p->from.offset += deltasp; - if(a == D_PARAM) - p->from.offset += deltasp + pcsize; - a = p->to.type; - if(a == D_AUTO) - p->to.offset += deltasp; - if(a == D_PARAM) - p->to.offset += deltasp + pcsize; - switch(p->as) { - default: - continue; - case APUSHL: - case APUSHFL: - deltasp += 4; - continue; - case APUSHQ: - case APUSHFQ: - deltasp += 8; - continue; - case APUSHW: - case APUSHFW: - deltasp += 2; - continue; - case APOPL: - case APOPFL: - deltasp -= 4; - continue; - case APOPQ: - case APOPFQ: - deltasp -= 8; - continue; - case APOPW: - case APOPFW: - deltasp -= 2; - continue; - case ARET: - break; - } - - if(autoffset != deltasp) - diag("unbalanced PUSH/POP"); - if(p->from.type == D_CONST) - goto become; + if(q != P) + q->pcond = p->link; if(autoffset) { + p = appendp(p); p->as = AADJSP; p->from.type = D_CONST; - p->from.offset = -autoffset; + p->from.offset = autoffset; + p->spadj = autoffset; + if(q != P) + q->pcond = p; + } + deltasp = autoffset; + if(debug['K'] > 1 && autoffset) { + // 6l -KK means double-check for stack overflow + // even after calling morestack and even if the + // function is marked as nosplit. p = appendp(p); - p->as = ARET; - } - continue; + p->as = AMOVQ; + p->from.type = D_INDIR+D_CX; + p->from.offset = 0; + p->to.type = D_BX; - become: - q = p; - p = appendp(p); - p->as = AJMP; - p->to = q->to; - p->pcond = q->pcond; + p = appendp(p); + p->as = ASUBQ; + p->from.type = D_CONST; + p->from.offset = StackSmall+32; + p->to.type = D_BX; - q->as = AADJSP; - q->from = zprg.from; - q->from.type = D_CONST; - q->from.offset = -autoffset; - q->to = zprg.to; - continue; + p = appendp(p); + p->as = ACMPQ; + p->from.type = D_SP; + p->to.type = D_BX; + + p = appendp(p); + p->as = AJHI; + p->to.type = D_BRANCH; + q1 = p; + + p = appendp(p); + p->as = AINT; + p->from.type = D_CONST; + p->from.offset = 3; + + p = appendp(p); + p->as = ANOP; + q1->pcond = p; + q1 = P; + } + + for(; p != P; p = p->link) { + pcsize = p->mode/8; + a = p->from.type; + if(a == D_AUTO) + p->from.offset += deltasp; + if(a == D_PARAM) + p->from.offset += deltasp + pcsize; + a = p->to.type; + if(a == D_AUTO) + p->to.offset += deltasp; + if(a == D_PARAM) + p->to.offset += deltasp + pcsize; + + switch(p->as) { + default: + continue; + case APUSHL: + case APUSHFL: + deltasp += 4; + p->spadj = 4; + continue; + case APUSHQ: + case APUSHFQ: + deltasp += 8; + p->spadj = 8; + continue; + case APUSHW: + case APUSHFW: + deltasp += 2; + p->spadj = 2; + continue; + case APOPL: + case APOPFL: + deltasp -= 4; + p->spadj = -4; + continue; + case APOPQ: + case APOPFQ: + deltasp -= 8; + p->spadj = -8; + continue; + case APOPW: + case APOPFW: + deltasp -= 2; + p->spadj = -2; + continue; + case ARET: + break; + } + + if(autoffset != deltasp) + diag("unbalanced PUSH/POP"); + + if(autoffset) { + p->as = AADJSP; + p->from.type = D_CONST; + p->from.offset = -autoffset; + p->spadj = -autoffset; + p = appendp(p); + p->as = ARET; + } + } } } @@ -975,180 +694,7 @@ undef(void) Sym *s; for(i=0; ilink) + for(s = hash[i]; s != S; s = s->hash) if(s->type == SXREF) diag("%s: not defined", s->name); } - -void -import(void) -{ - int i; - Sym *s; - - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type == SXREF && (nimports == 0 || s->subtype == SIMPORT)){ - if(s->value != 0) - diag("value != 0 on SXREF"); - undefsym(s); - Bprint(&bso, "IMPORT: %s sig=%lux v=%lld\n", s->name, s->sig, s->value); - if(debug['S']) - s->sig = 0; - } -} - -void -ckoff(Sym *s, int32 v) -{ - if(v < 0 || v >= 1<name); -} - -Prog* -newdata(Sym *s, int o, int w, int t) -{ - Prog *p; - - p = prg(); - if(edatap == P) - datap = p; - else - edatap->link = p; - edatap = p; - p->as = ADATA; - p->width = w; - p->from.scale = w; - p->from.type = t; - p->from.sym = s; - p->from.offset = o; - p->to.type = D_CONST; - p->dlink = s->data; - s->data = p; - return p; -} - -Prog* -newtext(Prog *p, Sym *s) -{ - if(p == P) { - p = prg(); - p->as = ATEXT; - p->from.sym = s; - } - s->type = STEXT; - s->text = p; - s->value = pc; - lastp->link = p; - lastp = p; - p->pc = pc++; - if(textp == P) - textp = p; - else - etextp->pcond = p; - etextp = p; - return p; -} - -void -export(void) -{ - int i, j, n, off, nb, sv, ne; - Sym *s, *et, *str, **esyms; - Prog *p; - char buf[NSNAME], *t; - - n = 0; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type != SXREF && - s->type != SUNDEF && - (nexports == 0 || s->subtype == SEXPORT)) - n++; - esyms = mal(n*sizeof(Sym*)); - ne = n; - n = 0; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type != SXREF && - s->type != SUNDEF && - (nexports == 0 || s->subtype == SEXPORT)) - esyms[n++] = s; - for(i = 0; i < ne-1; i++) - for(j = i+1; j < ne; j++) - if(strcmp(esyms[i]->name, esyms[j]->name) > 0){ - s = esyms[i]; - esyms[i] = esyms[j]; - esyms[j] = s; - } - - nb = 0; - off = 0; - et = lookup(EXPTAB, 0); - if(et->type != 0 && et->type != SXREF) - diag("%s already defined", EXPTAB); - et->type = SDATA; - str = lookup(".string", 0); - if(str->type == 0) - str->type = SDATA; - sv = str->value; - for(i = 0; i < ne; i++){ - s = esyms[i]; - if(debug['S']) - s->sig = 0; - /* Bprint(&bso, "EXPORT: %s sig=%lux t=%d\n", s->name, s->sig, s->type); */ - - /* signature */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.offset = s->sig; - - /* address */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.type = D_ADDR; - p->to.index = D_EXTERN; - p->to.sym = s; - - /* string */ - t = s->name; - n = strlen(t)+1; - for(;;){ - buf[nb++] = *t; - sv++; - if(nb >= NSNAME){ - p = newdata(str, sv-NSNAME, NSNAME, D_STATIC); - p->to.type = D_SCONST; - memmove(p->to.scon, buf, NSNAME); - nb = 0; - } - if(*t++ == 0) - break; - } - - /* name */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.type = D_ADDR; - p->to.index = D_STATIC; - p->to.sym = str; - p->to.offset = sv-n; - } - - if(nb > 0){ - p = newdata(str, sv-nb, nb, D_STATIC); - p->to.type = D_SCONST; - memmove(p->to.scon, buf, nb); - } - - for(i = 0; i < 3; i++){ - newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - } - et->value = off; - if(sv == 0) - sv = 1; - str->value = sv; - exports = ne; - free(esyms); -} diff --git a/src/cmd/6l/prof.c b/src/cmd/6l/prof.c new file mode 100644 index 000000000..25992a40b --- /dev/null +++ b/src/cmd/6l/prof.c @@ -0,0 +1,171 @@ +// Inferno utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Profiling. + +#include "l.h" +#include "../ld/lib.h" + +void +doprof1(void) +{ +#if 0 + Sym *s; + int32 n; + Prog *p, *q; + + if(debug['v']) + Bprint(&bso, "%5.2f profile 1\n", cputime()); + Bflush(&bso); + s = lookup("__mcount", 0); + n = 1; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; + q = prg(); + q->line = p->line; + q->link = datap; + datap = q; + q->as = ADATA; + q->from.type = D_EXTERN; + q->from.offset = n*4; + q->from.sym = s; + q->from.scale = 4; + q->to = p->from; + q->to.type = D_CONST; + + q = prg(); + q->line = p->line; + q->pc = p->pc; + q->link = p->link; + p->link = q; + p = q; + p->as = AADDL; + p->from.type = D_CONST; + p->from.offset = 1; + p->to.type = D_EXTERN; + p->to.sym = s; + p->to.offset = n*4 + 4; + + n += 2; + } + q = prg(); + q->line = 0; + q->link = datap; + datap = q; + + q->as = ADATA; + q->from.type = D_EXTERN; + q->from.sym = s; + q->from.scale = 4; + q->to.type = D_CONST; + q->to.offset = n; + + s->type = SBSS; + s->size = n*4; +#endif +} + +void +doprof2(void) +{ + Sym *s2, *s4; + Prog *p, *q, *ps2, *ps4; + + if(debug['v']) + Bprint(&bso, "%5.2f profile 2\n", cputime()); + Bflush(&bso); + + s2 = lookup("_profin", 0); + s4 = lookup("_profout", 0); + if(s2->type != STEXT || s4->type != STEXT) { + diag("_profin/_profout not defined"); + return; + } + + ps2 = P; + ps4 = P; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; + if(p->from.sym == s2) { + p->from.scale = 1; + ps2 = p; + } + if(p->from.sym == s4) { + p->from.scale = 1; + ps4 = p; + } + } + for(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; + + if(p->from.scale & NOPROF) /* dont profile */ + continue; + + /* + * JMPL profin + */ + q = prg(); + q->line = p->line; + q->pc = p->pc; + q->link = p->link; + p->link = q; + p = q; + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = ps2; + p->to.sym = s2; + + for(; p; p=p->link) { + if(p->as == ARET) { + /* + * RET + */ + q = prg(); + q->as = ARET; + q->from = p->from; + q->to = p->to; + q->link = p->link; + p->link = q; + + /* + * JAL profout + */ + p->as = ACALL; + p->from = zprg.from; + p->to = zprg.to; + p->to.type = D_BRANCH; + p->pcond = ps4; + p->to.sym = s4; + + p = q; + } + } + } +} diff --git a/src/cmd/6l/span.c b/src/cmd/6l/span.c index 15f931bcb..5251f19bb 100644 --- a/src/cmd/6l/span.c +++ b/src/cmd/6l/span.c @@ -28,34 +28,33 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Instruction layout. + #include "l.h" #include "../ld/lib.h" -#include "../ld/elf.h" static int rexflag; static int asmode; +static vlong vaddr(Adr*, Reloc*); void -span(void) +span1(Sym *s) { Prog *p, *q; - int32 v; - vlong c, idat; - int m, n, again; - - xdefine("etext", STEXT, 0L); - idat = INITDAT; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; - n = 0; - if(p->to.type == D_BRANCH) - if(p->pcond == P) - p->pcond = p; - if((q = p->pcond) != P) - if(q->back != 2) - n = 1; - p->back = n; + int32 c, v, loop; + uchar *bp; + int n, m, i; + + cursym = s; + + if(s->p != nil) + return; + + for(p = s->text; p != P; p = p->link) { + p->back = 2; // use short branches first time through + if((q = p->pcond) != P && (q->back & 2)) + p->back |= 1; // backward jump + if(p->as == AADJSP) { p->to.type = D_SP; v = -p->from.offset; @@ -70,378 +69,253 @@ span(void) p->as = ANOP; } } + n = 0; + do { + loop = 0; + memset(s->r, 0, s->nr*sizeof s->r[0]); + s->nr = 0; + s->np = 0; + c = 0; + for(p = s->text; p != P; p = p->link) { + p->pc = c; + + // process forward jumps to p + for(q = p->comefrom; q != P; q = q->forwd) { + v = p->pc - (q->pc + q->mark); + if(q->back & 2) { // short + if(v > 127) { + loop++; + q->back ^= 2; + } + s->p[q->pc+1] = v; + } else { + bp = s->p + q->pc + q->mark - 4; + *bp++ = v; + *bp++ = v>>8; + *bp++ = v>>16; + *bp++ = v>>24; + } + } + p->comefrom = P; -start: - if(debug['v']) - Bprint(&bso, "%5.2f span\n", cputime()); - Bflush(&bso); - c = INITTEXT; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; - if(p->to.type == D_BRANCH) - if(p->back) - p->pc = c; - asmins(p); - p->pc = c; - m = andptr-and; - p->mark = m; - c += m; - } - -loop: - n++; - if(debug['v']) - Bprint(&bso, "%5.2f span %d\n", cputime(), n); - Bflush(&bso); - if(n > 50) { - print("span must be looping\n"); - errorexit(); - } - again = 0; - c = INITTEXT; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; - if(p->to.type == D_BRANCH || p->back & 0100) { - if(p->back) - p->pc = c; asmins(p); + p->pc = c; m = andptr-and; - if(m != p->mark) { - p->mark = m; - again++; - } + symgrow(s, p->pc+m); + memmove(s->p+p->pc, and, m); + p->mark = m; + c += m; } - p->pc = c; - c += p->mark; - } - if(again) { - textsize = c; - goto loop; - } - if(INITRND) { - INITDAT = rnd(c, INITRND); - if(INITDAT != idat) { - idat = INITDAT; - goto start; + if(++n > 20) { + diag("span must be looping"); + errorexit(); + } + } while(loop); + s->size = c; + + if(debug['a'] > 1) { + print("span1 %s %lld (%d tries)\n %.6ux", s->name, s->size, n, 0); + for(i=0; inp; i++) { + print(" %.2ux", s->p[i]); + if(i%16 == 15) + print("\n %.6ux", i+1); + } + if(i%16) + print("\n"); + + for(i=0; inr; i++) { + Reloc *r; + + r = &s->r[i]; + print(" rel %#.4ux/%d %s%+lld\n", r->off, r->siz, r->sym->name, r->add); } } - xdefine("etext", STEXT, c); - if(debug['v']) - Bprint(&bso, "etext = %llux\n", c); - Bflush(&bso); - for(p = textp; p != P; p = p->pcond) - p->from.sym->value = p->pc; - textsize = c - INITTEXT; } void -xdefine(char *p, int t, vlong v) +span(void) { - Sym *s; + Prog *p, *q; + int32 v; + int n; - s = lookup(p, 0); - if(s->type == 0 || s->type == SXREF) { - s->type = t; - s->value = v; - } - if(s->type == STEXT && s->value == 0) - s->value = v; -} + if(debug['v']) + Bprint(&bso, "%5.2f span\n", cputime()); -void -putsymb(char *s, int t, vlong v, vlong size, int ver, Sym *go) -{ - int i, f, l; - vlong gv; - - if(t == 'f') - s++; - l = 4; - if(!debug['8']){ - lputb(v>>32); - l = 8; - } - lputb(v); - if(ver) - t += 'a' - 'A'; - cput(t+0x80); /* 0x80 is variable length */ - - if(t == 'Z' || t == 'z') { - cput(s[0]); - for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) { - cput(s[i]); - cput(s[i+1]); - } - cput(0); - cput(0); - i++; - } - else { - for(i=0; s[i]; i++) - cput(s[i]); - cput(0); - } - gv = 0; - if(go) { - if(!go->reachable) - diag("unreachable type %s", go->name); - gv = go->value+INITDAT; - } - if(l == 8) - lputb(gv>>32); - lputb(gv); - symsize += l + 1 + i+1 + l; - - if(debug['n']) { - if(t == 'z' || t == 'Z') { - Bprint(&bso, "%c %.8llux ", t, v); - for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) { - f = ((s[i]&0xff) << 8) | (s[i+1]&0xff); - Bprint(&bso, "/%x", f); + // NOTE(rsc): If we get rid of the globals we should + // be able to parallelize these iterations. + for(cursym = textp; cursym != nil; cursym = cursym->next) { + if(cursym->p != nil) + continue; + // TODO: move into span1 + for(p = cursym->text; p != P; p = p->link) { + n = 0; + if(p->to.type == D_BRANCH) + if(p->pcond == P) + p->pcond = p; + if((q = p->pcond) != P) + if(q->back != 2) + n = 1; + p->back = n; + if(p->as == AADJSP) { + p->to.type = D_SP; + v = -p->from.offset; + p->from.offset = v; + p->as = p->mode != 64? AADDL: AADDQ; + if(v < 0) { + p->as = p->mode != 64? ASUBL: ASUBQ; + v = -v; + p->from.offset = v; + } + if(v == 0) + p->as = ANOP; } - Bprint(&bso, "\n"); - return; } - if(ver) - Bprint(&bso, "%c %.8llux %s<%d> %s (%.8llux)\n", t, v, s, ver, go ? go->name : "", gv); - else - Bprint(&bso, "%c %.8llux %s %s (%.8llux)\n", t, v, s, go ? go->name : "", gv); + span1(cursym); } } void -genasmsym(void (*put)(char*, int, vlong, vlong, int, Sym*)) +xdefine(char *p, int t, vlong v) { - Prog *p; - Auto *a; Sym *s; - int h; - - s = lookup("etext", 0); - if(s->type == STEXT) - put(s->name, 'T', s->value, s->size, s->version, 0); - - for(h=0; hlink) { - switch(s->type) { - case SCONST: - if(!s->reachable) - continue; - put(s->name, 'D', s->value, s->size, s->version, s->gotype); - continue; - - case SDATA: - case SELFDATA: - if(!s->reachable) - continue; - put(s->name, 'D', s->value+INITDAT, s->size, s->version, s->gotype); - continue; - - case SMACHO: - if(!s->reachable) - continue; - put(s->name, 'D', s->value+INITDAT+datsize+bsssize, s->size, s->version, s->gotype); - continue; - - case SBSS: - if(!s->reachable) - continue; - put(s->name, 'B', s->value+INITDAT, s->size, s->version, s->gotype); - continue; - - case SFIXED: - put(s->name, 'B', s->value, s->size, s->version, s->gotype); - continue; - - case SFILE: - put(s->name, 'f', s->value, 0, s->version, 0); - continue; - } - } - } - - for(p = textp; p != P; p = p->pcond) { - s = p->from.sym; - if(s->type != STEXT) - continue; - /* filenames first */ - for(a=p->to.autom; a; a=a->link) - if(a->type == D_FILE) - put(a->asym->name, 'z', a->aoffset, 0, 0, 0); - else - if(a->type == D_FILE1) - put(a->asym->name, 'Z', a->aoffset, 0, 0, 0); - - if(!s->reachable) - continue; - put(s->name, 'T', s->value, s->size, s->version, s->gotype); - - /* frame, auto and param after */ - put(".frame", 'm', p->to.offset+8, 0, 0, 0); - - for(a=p->to.autom; a; a=a->link) - if(a->type == D_AUTO) - put(a->asym->name, 'a', -a->aoffset, 0, 0, a->gotype); - else - if(a->type == D_PARAM) - put(a->asym->name, 'p', a->aoffset, 0, 0, a->gotype); - } - if(debug['v'] || debug['n']) - Bprint(&bso, "symsize = %lud\n", symsize); - Bflush(&bso); + s = lookup(p, 0); + s->type = t; + s->value = v; + s->reachable = 1; + s->special = 1; } void -asmsym(void) +instinit(void) { - genasmsym(putsymb); -} + int c, i; -char *elfstrdat; -int elfstrsize; -int maxelfstr; - -int -putelfstr(char *s) -{ - int off, n; - - if(elfstrsize == 0 && s[0] != 0) { - // first entry must be empty string - putelfstr(""); - } - - n = strlen(s)+1; - if(elfstrsize+n > maxelfstr) { - maxelfstr = 2*(elfstrsize+n+(1<<20)); - elfstrdat = realloc(elfstrdat, maxelfstr); - } - off = elfstrsize; - elfstrsize += n; - memmove(elfstrdat+off, s, n); - return off; -} - -void -putelfsymb(char *s, int t, vlong addr, vlong size, int ver, Sym *go) -{ - int bind, type, shndx, stroff; - - bind = STB_GLOBAL; - switch(t) { - default: - return; - case 'T': - type = STT_FUNC; - shndx = elftextsh + 0; - break; - case 'D': - type = STT_OBJECT; - shndx = elftextsh + 1; - break; - case 'B': - type = STT_OBJECT; - shndx = elftextsh + 2; - break; + for(i=1; optab[i].as; i++) { + c = optab[i].as; + if(opindex[c] != nil) { + diag("phase error in optab: %d (%A)", i, c); + errorexit(); + } + opindex[c] = &optab[i]; } - - stroff = putelfstr(s); - lputl(stroff); // string - cput((bind<<4)|(type&0xF)); - cput(0); - wputl(shndx); - vputl(addr); - vputl(size); -} -void -asmelfsym(void) -{ - genasmsym(putelfsymb); -} - -void -asmlc(void) -{ - vlong oldpc; - Prog *p; - int32 oldlc, v, s; - - oldpc = INITTEXT; - oldlc = 0; - for(p = firstp; p != P; p = p->link) { - if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) { - if(p->as == ATEXT) - curtext = p; - if(debug['O']) - Bprint(&bso, "%6llux %P\n", - p->pc, p); - continue; + for(i=0; i= D_AL && i <= D_R15B) { + reg[i] = (i-D_AL) & 7; + if(i >= D_SPB && i <= D_DIB) + regrex[i] = 0x40; + if(i >= D_R8B && i <= D_R15B) + regrex[i] = Rxr | Rxx | Rxb; } - if(debug['O']) - Bprint(&bso, "\t\t%6ld", lcsize); - v = (p->pc - oldpc) / MINLC; - while(v) { - s = 127; - if(v < 127) - s = v; - cput(s+128); /* 129-255 +pc */ - if(debug['O']) - Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128); - v -= s; - lcsize++; + if(i >= D_AH && i<= D_BH) + reg[i] = 4 + ((i-D_AH) & 7); + if(i >= D_AX && i <= D_R15) { + reg[i] = (i-D_AX) & 7; + if(i >= D_R8) + regrex[i] = Rxr | Rxx | Rxb; } - s = p->line - oldlc; - oldlc = p->line; - oldpc = p->pc + MINLC; - if(s > 64 || s < -64) { - cput(0); /* 0 vv +lc */ - cput(s>>24); - cput(s>>16); - cput(s>>8); - cput(s); - if(debug['O']) { - if(s > 0) - Bprint(&bso, " lc+%ld(%d,%ld)\n", - s, 0, s); - else - Bprint(&bso, " lc%ld(%d,%ld)\n", - s, 0, s); - Bprint(&bso, "%6llux %P\n", - p->pc, p); - } - lcsize += 5; - continue; + if(i >= D_F0 && i <= D_F0+7) + reg[i] = (i-D_F0) & 7; + if(i >= D_M0 && i <= D_M0+7) + reg[i] = (i-D_M0) & 7; + if(i >= D_X0 && i <= D_X0+15) { + reg[i] = (i-D_X0) & 7; + if(i >= D_X0+8) + regrex[i] = Rxr | Rxx | Rxb; } - if(s > 0) { - cput(0+s); /* 1-64 +lc */ - if(debug['O']) { - Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s); - Bprint(&bso, "%6llux %P\n", - p->pc, p); - } - } else { - cput(64-s); /* 65-128 -lc */ - if(debug['O']) { - Bprint(&bso, " lc%ld(%ld)\n", s, 64-s); - Bprint(&bso, "%6llux %P\n", - p->pc, p); - } - } - lcsize++; + if(i >= D_CR+8 && i <= D_CR+15) + regrex[i] = Rxr; } - while(lcsize & 1) { - s = 129; - cput(s); - lcsize++; +} + +int +prefixof(Adr *a) +{ + switch(a->type) { + case D_INDIR+D_CS: + return 0x2e; + case D_INDIR+D_DS: + return 0x3e; + case D_INDIR+D_ES: + return 0x26; + case D_INDIR+D_FS: + return 0x64; + case D_INDIR+D_GS: + return 0x65; } - if(debug['v'] || debug['O']) - Bprint(&bso, "lcsize = %ld\n", lcsize); - Bflush(&bso); + return 0; } int @@ -641,11 +515,11 @@ oclass(Adr *a) } void -asmidx(Adr *a, int base) +asmidx(int scale, int index, int base) { int i; - switch(a->index) { + switch(index) { default: goto bad; @@ -670,10 +544,10 @@ asmidx(Adr *a, int base) case D_BP: case D_SI: case D_DI: - i = reg[(int)a->index] << 3; + i = reg[index] << 3; break; } - switch(a->scale) { + switch(scale) { default: goto bad; case 1: @@ -719,7 +593,7 @@ bas: *andptr++ = i; return; bad: - diag("asmidx: bad address %D", a); + diag("asmidx: bad address %d/%d/%d", scale, index, base); *andptr++ = 0; return; } @@ -727,10 +601,6 @@ bad: static void put4(int32 v) { - if(dlm && curp != P && reloca != nil){ - dynreloc(reloca->sym, curp->pc + andptr - &and[0], 1); - reloca = nil; - } andptr[0] = v; andptr[1] = v>>8; andptr[2] = v>>16; @@ -739,12 +609,25 @@ put4(int32 v) } static void -put8(vlong v) +relput4(Prog *p, Adr *a) { - if(dlm && curp != P && reloca != nil){ - dynreloc(reloca->sym, curp->pc + andptr - &and[0], 1); /* TO DO */ - reloca = nil; + vlong v; + Reloc rel, *r; + + v = vaddr(a, &rel); + if(rel.siz != 0) { + if(rel.siz != 4) + diag("bad reloc"); + r = addrel(cursym); + *r = rel; + r->off = p->pc + andptr - and; } + put4(v); +} + +static void +put8(vlong v) +{ andptr[0] = v; andptr[1] = v>>8; andptr[2] = v>>16; @@ -756,24 +639,41 @@ put8(vlong v) andptr += 8; } +/* +static void +relput8(Prog *p, Adr *a) +{ + vlong v; + Reloc rel, *r; + + v = vaddr(a, &rel); + if(rel.siz != 0) { + r = addrel(cursym); + *r = rel; + r->siz = 8; + r->off = p->pc + andptr - and; + } + put8(v); +} +*/ + vlong symaddr(Sym *s) { - Adr a; - - a.type = D_ADDR; - a.index = D_EXTERN; - a.offset = 0; - a.sym = s; - return vaddr(&a); + if(!s->reachable) + diag("unreachable symbol in symaddr - %s", s->name); + return s->value; } -vlong -vaddr(Adr *a) +static vlong +vaddr(Adr *a, Reloc *r) { int t; vlong v; Sym *s; + + if(r != nil) + memset(r, 0, sizeof *r); t = a->type; v = a->offset; @@ -783,34 +683,18 @@ vaddr(Adr *a) case D_STATIC: case D_EXTERN: s = a->sym; - if(s != nil) { - if(dlm && curp != P) - reloca = a; - switch(s->type) { - case SUNDEF: - ckoff(s, v); - case STEXT: - case SCONST: - if(!s->reachable) - diag("unreachable symbol in vaddr - %s", s->name); - if((uvlong)s->value < (uvlong)INITTEXT) - v += INITTEXT; /* TO DO */ - v += s->value; - break; - case SFIXED: - v += s->value; - break; - case SMACHO: - if(!s->reachable) - sysfatal("unreachable symbol in vaddr - %s", s->name); - v += INITDAT + datsize + s->value; - break; - default: - if(!s->reachable) - diag("unreachable symbol in vaddr - %s", s->name); - v += INITDAT + s->value; - } + if(!s->reachable) + diag("unreachable symbol in vaddr - %s", s->name); + if(r == nil) { + diag("need reloc for %D", a); + errorexit(); } + r->type = D_ADDR; + r->siz = 4; // TODO: 8 for external symbols + r->off = -1; // caller must fill in + r->sym = s; + r->add = v; + v = 0; } return v; } @@ -819,55 +703,51 @@ static void asmandsz(Adr *a, int r, int rex, int m64) { int32 v; - int t; - Adr aa; + int t, scale; + Reloc rel; rex &= (0x40 | Rxr); v = a->offset; t = a->type; + rel.siz = 0; if(a->index != D_NONE) { - if(t >= D_INDIR) { - t -= D_INDIR; - rexflag |= (regrex[(int)a->index] & Rxx) | (regrex[t] & Rxb) | rex; - if(t == D_NONE) { - *andptr++ = (0 << 6) | (4 << 0) | (r << 3); - asmidx(a, t); - put4(v); - return; - } - if(v == 0 && t != D_BP && t != D_R13) { - *andptr++ = (0 << 6) | (4 << 0) | (r << 3); - asmidx(a, t); - return; - } - if(v >= -128 && v < 128) { - *andptr++ = (1 << 6) | (4 << 0) | (r << 3); - asmidx(a, t); - *andptr++ = v; - return; + if(t < D_INDIR) { + switch(t) { + default: + goto bad; + case D_STATIC: + case D_EXTERN: + t = D_NONE; + v = vaddr(a, &rel); + break; + case D_AUTO: + case D_PARAM: + t = D_SP; + break; } - *andptr++ = (2 << 6) | (4 << 0) | (r << 3); - asmidx(a, t); - put4(v); + } else + t -= D_INDIR; + rexflag |= (regrex[(int)a->index] & Rxx) | (regrex[t] & Rxb) | rex; + if(t == D_NONE) { + *andptr++ = (0 << 6) | (4 << 0) | (r << 3); + asmidx(a->scale, a->index, t); + goto putrelv; return; } - switch(t) { - default: - goto bad; - case D_STATIC: - case D_EXTERN: - aa.type = D_NONE+D_INDIR; - break; - case D_AUTO: - case D_PARAM: - aa.type = D_SP+D_INDIR; - break; + if(v == 0 && rel.siz == 0 && t != D_BP && t != D_R13) { + *andptr++ = (0 << 6) | (4 << 0) | (r << 3); + asmidx(a->scale, a->index, t); + return; } - aa.offset = vaddr(a); - aa.index = a->index; - aa.scale = a->scale; - asmandsz(&aa, r, rex, m64); - return; + if(v >= -128 && v < 128 && rel.siz == 0) { + *andptr++ = (1 << 6) | (4 << 0) | (r << 3); + asmidx(a->scale, a->index, t); + *andptr++ = v; + return; + } + *andptr++ = (2 << 6) | (4 << 0) | (r << 3); + asmidx(a->scale, a->index, t); + goto putrelv; } if(t >= D_AL && t <= D_X0+15) { if(v) @@ -876,72 +756,84 @@ asmandsz(Adr *a, int r, int rex, int m64) rexflag |= (regrex[t] & (0x40 | Rxb)) | rex; return; } - if(t >= D_INDIR) { + + scale = a->scale; + if(t < D_INDIR) { + switch(a->type) { + default: + goto bad; + case D_STATIC: + case D_EXTERN: + t = D_NONE; + v = vaddr(a, &rel); + break; + case D_AUTO: + case D_PARAM: + t = D_SP; + break; + } + scale = 1; + } else t -= D_INDIR; - rexflag |= (regrex[t] & Rxb) | rex; - if(t == D_NONE) { - if(asmode != 64){ - *andptr++ = (0 << 6) | (5 << 0) | (r << 3); - put4(v); - return; - } - /* temporary */ - *andptr++ = (0 << 6) | (4 << 0) | (r << 3); /* sib present */ - *andptr++ = (0 << 6) | (4 << 3) | (5 << 0); /* DS:d32 */ - put4(v); + + rexflag |= (regrex[t] & Rxb) | rex; + if(t == D_NONE || (D_CS <= t && t <= D_GS)) { + if(asmode != 64){ + *andptr++ = (0 << 6) | (5 << 0) | (r << 3); + goto putrelv; + } + /* temporary */ + *andptr++ = (0 << 6) | (4 << 0) | (r << 3); /* sib present */ + *andptr++ = (0 << 6) | (4 << 3) | (5 << 0); /* DS:d32 */ + goto putrelv; + } + if(t == D_SP || t == D_R12) { + if(v == 0) { + *andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3); + asmidx(scale, D_NONE, t); return; } - if(t == D_SP || t == D_R12) { - if(v == 0) { - *andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3); - asmidx(a, t); - return; - } - if(v >= -128 && v < 128) { - *andptr++ = (1 << 6) | (reg[t] << 0) | (r << 3); - asmidx(a, t); - *andptr++ = v; - return; - } - *andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3); - asmidx(a, t); - put4(v); + if(v >= -128 && v < 128) { + *andptr++ = (1 << 6) | (reg[t] << 0) | (r << 3); + asmidx(scale, D_NONE, t); + *andptr++ = v; return; } - if(t >= D_AX && t <= D_R15) { - if(v == 0 && t != D_BP && t != D_R13) { - *andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3); - return; - } - if(v >= -128 && v < 128) { - andptr[0] = (1 << 6) | (reg[t] << 0) | (r << 3); - andptr[1] = v; - andptr += 2; - return; - } - *andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3); - put4(v); + *andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3); + asmidx(scale, D_NONE, t); + goto putrelv; + } + if(t >= D_AX && t <= D_R15) { + if(v == 0 && t != D_BP && t != D_R13) { + *andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3); return; } - goto bad; + if(v >= -128 && v < 128) { + andptr[0] = (1 << 6) | (reg[t] << 0) | (r << 3); + andptr[1] = v; + andptr += 2; + return; + } + *andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3); + goto putrelv; } - switch(a->type) { - default: - goto bad; - case D_STATIC: - case D_EXTERN: - aa.type = D_NONE+D_INDIR; - break; - case D_AUTO: - case D_PARAM: - aa.type = D_SP+D_INDIR; - break; + goto bad; + +putrelv: + if(rel.siz != 0) { + Reloc *r; + + if(rel.siz != 4) { + diag("bad rel"); + goto bad; + } + r = addrel(cursym); + *r = rel; + r->off = curp->pc + andptr - and; } - aa.index = D_NONE; - aa.scale = 1; - aa.offset = vaddr(a); - asmandsz(&aa, r, rex, m64); + put4(v); return; + bad: diag("asmand: bad address %D", a); return; @@ -1173,14 +1065,25 @@ doasm(Prog *p) Prog *q, pp; uchar *t; Movtab *mo; - int z, op, ft, tt, xo, l; + int z, op, ft, tt, xo, l, pre; vlong v; + Reloc rel, *r; + Adr *a; + + curp = p; // TODO o = opindex[p->as]; if(o == nil) { diag("asmins: missing op %P", p); return; } + + pre = prefixof(&p->from); + if(pre) + *andptr++ = pre; + pre = prefixof(&p->to); + if(pre) + *andptr++ = pre; if(p->ft == 0) p->ft = oclass(&p->from); @@ -1244,7 +1147,7 @@ found: diag("asmins: illegal in %d-bit mode: %P", p->mode, p); break; } - v = vaddr(&p->from); + op = o->op[z]; if(op == 0x0f) { *andptr++ = op; @@ -1350,64 +1253,74 @@ found: break; case Zm_ibo: - v = vaddr(&p->to); *andptr++ = op; asmando(&p->from, o->op[z+1]); - *andptr++ = v; + *andptr++ = vaddr(&p->to, nil); break; case Zibo_m: *andptr++ = op; asmando(&p->to, o->op[z+1]); - *andptr++ = v; + *andptr++ = vaddr(&p->from, nil); break; case Zibo_m_xm: z = mediaop(o, op, t[3], z); asmando(&p->to, o->op[z+1]); - *andptr++ = v; + *andptr++ = vaddr(&p->from, nil); break; case Z_ib: - v = vaddr(&p->to); case Zib_: + if(t[2] == Zib_) + a = &p->from; + else + a = &p->to; *andptr++ = op; - *andptr++ = v; + *andptr++ = vaddr(a, nil); break; case Zib_rp: rexflag |= regrex[p->to.type] & (Rxb|0x40); *andptr++ = op + reg[p->to.type]; - *andptr++ = v; + *andptr++ = vaddr(&p->from, nil); break; case Zil_rp: rexflag |= regrex[p->to.type] & Rxb; *andptr++ = op + reg[p->to.type]; if(o->prefix == Pe) { + v = vaddr(&p->from, nil); *andptr++ = v; *andptr++ = v>>8; } else - put4(v); + relput4(p, &p->from); break; case Zo_iw: *andptr++ = op; if(p->from.type != D_NONE){ + v = vaddr(&p->from, nil); *andptr++ = v; *andptr++ = v>>8; } break; case Ziq_rp: + v = vaddr(&p->from, &rel); l = v>>32; - if(l == 0){ + if(l == 0 && rel.siz != 8){ //p->mark |= 0100; //print("zero: %llux %P\n", v, p); rexflag &= ~(0x40|Rxw); rexflag |= regrex[p->to.type] & Rxb; *andptr++ = 0xb8 + reg[p->to.type]; + if(rel.type != 0) { + r = addrel(cursym); + *r = rel; + r->off = p->pc + andptr - and; + } put4(v); }else if(l == -1 && (v&((uvlong)1<<31))!=0){ /* sign extend */ //p->mark |= 0100; @@ -1419,6 +1332,11 @@ found: //print("all: %llux %P\n", v, p); rexflag |= regrex[p->to.type] & Rxb; *andptr++ = op + reg[p->to.type]; + if(rel.type != 0) { + r = addrel(cursym); + *r = rel; + r->off = p->pc + andptr - and; + } put8(v); } break; @@ -1426,53 +1344,54 @@ found: case Zib_rr: *andptr++ = op; asmand(&p->to, &p->to); - *andptr++ = v; + *andptr++ = vaddr(&p->from, nil); break; case Z_il: - v = vaddr(&p->to); case Zil_: - *andptr++ = op; - if(o->prefix == Pe) { - *andptr++ = v; - *andptr++ = v>>8; - } + if(t[2] == Zil_) + a = &p->from; else - put4(v); - break; - - case Zm_ilo: - v = vaddr(&p->to); + a = &p->to; *andptr++ = op; - asmando(&p->from, o->op[z+1]); if(o->prefix == Pe) { + v = vaddr(a, nil); *andptr++ = v; *andptr++ = v>>8; } else - put4(v); + relput4(p, a); break; + case Zm_ilo: case Zilo_m: *andptr++ = op; - asmando(&p->to, o->op[z+1]); + if(t[2] == Zilo_m) { + a = &p->from; + asmando(&p->to, o->op[z+1]); + } else { + a = &p->to; + asmando(&p->from, o->op[z+1]); + } if(o->prefix == Pe) { + v = vaddr(a, nil); *andptr++ = v; *andptr++ = v>>8; } else - put4(v); + relput4(p, a); break; case Zil_rr: *andptr++ = op; asmand(&p->to, &p->to); if(o->prefix == Pe) { + v = vaddr(&p->from, nil); *andptr++ = v; *andptr++ = v>>8; } else - put4(v); + relput4(p, &p->from); break; case Z_rp: @@ -1490,74 +1409,132 @@ found: asmand(&p->to, &p->to); break; - case Zbr: + case Zcall: q = p->pcond; - if(q) { - v = q->pc - p->pc - 2; - if(v >= -128 && v <= 127) { - *andptr++ = op; - *andptr++ = v; - } else { - v -= 6-2; - *andptr++ = 0x0f; - *andptr++ = o->op[z+1]; - *andptr++ = v; - *andptr++ = v>>8; - *andptr++ = v>>16; - *andptr++ = v>>24; - } + if(q == nil) { + diag("call without target"); + errorexit(); + } + if(q->as != ATEXT) { + // Could handle this case by making D_PCREL + // record the Prog* instead of the Sym*, but let's + // wait until the need arises. + diag("call of non-TEXT %P", q); + errorexit(); } + *andptr++ = op; + r = addrel(cursym); + r->off = p->pc + andptr - and; + r->sym = q->from.sym; + r->type = D_PCREL; + r->siz = 4; + put4(0); break; - case Zcall: + case Zbr: + case Zjmp: + // TODO: jump across functions needs reloc q = p->pcond; - if(q) { - v = q->pc - p->pc - 5; - if(dlm && curp != P && p->to.sym->type == SUNDEF){ - /* v = 0 - p->pc - 5; */ - v = 0; - ckoff(p->to.sym, v); - v += p->to.sym->value; - dynreloc(p->to.sym, p->pc+1, 0); + if(q == nil) { + diag("jmp/branch without target"); + errorexit(); + } + if(q->as == ATEXT) { + if(t[2] == Zbr) { + diag("branch to ATEXT"); + errorexit(); } - *andptr++ = op; - *andptr++ = v; - *andptr++ = v>>8; - *andptr++ = v>>16; - *andptr++ = v>>24; + *andptr++ = o->op[z+1]; + r = addrel(cursym); + r->off = p->pc + andptr - and; + r->sym = q->from.sym; + r->type = D_PCREL; + r->siz = 4; + put4(0); + break; } - break; + // Assumes q is in this function. + // TODO: Check in input, preserve in brchain. - case Zjmp: - q = p->pcond; - if(q) { - v = q->pc - p->pc - 2; - if(v >= -128 && v <= 127) { + // Fill in backward jump now. + if(p->back & 1) { + v = q->pc - (p->pc + 2); + if(v >= -128) { *andptr++ = op; *andptr++ = v; } else { v -= 5-2; + if(t[2] == Zbr) { + *andptr++ = 0x0f; + v--; + } *andptr++ = o->op[z+1]; *andptr++ = v; *andptr++ = v>>8; *andptr++ = v>>16; *andptr++ = v>>24; } + break; + } + + // Annotate target; will fill in later. + p->forwd = q->comefrom; + q->comefrom = p; + if(p->back & 2) { // short + *andptr++ = op; + *andptr++ = 0; + } else { + if(t[2] == Zbr) + *andptr++ = 0x0f; + *andptr++ = o->op[z+1]; + *andptr++ = 0; + *andptr++ = 0; + *andptr++ = 0; + *andptr++ = 0; } break; + +/* + v = q->pc - p->pc - 2; + if((v >= -128 && v <= 127) || p->pc == -1 || q->pc == -1) { + *andptr++ = op; + *andptr++ = v; + } else { + v -= 5-2; + if(t[2] == Zbr) { + *andptr++ = 0x0f; + v--; + } + *andptr++ = o->op[z+1]; + *andptr++ = v; + *andptr++ = v>>8; + *andptr++ = v>>16; + *andptr++ = v>>24; + } +*/ + break; case Zloop: q = p->pcond; - if(q) { - v = q->pc - p->pc - 2; - if(v < -128 && v > 127) - diag("loop too far: %P", p); - *andptr++ = op; - *andptr++ = v; + 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) { + rel.siz = op; + r = addrel(cursym); + *r = rel; + r->off = p->pc + andptr - and; + } *andptr++ = v; if(op > 1) { *andptr++ = v>>8; @@ -1730,6 +1707,7 @@ void asmins(Prog *p) { int n, np, c; + Reloc *r; rexflag = 0; andptr = and; @@ -1739,7 +1717,7 @@ asmins(Prog *p) /* * as befits the whole approach of the architecture, * the rex prefix must appear before the first opcode byte - * (and thus after any 66/67/f2/f3 prefix bytes, but + * (and thus after any 66/67/f2/f3/26/2e/3e prefix bytes, but * before the 0f opcode escape!), or it might be ignored. * note that the handbook often misleadingly shows 66/f2/f3 in `opcode'. */ @@ -1748,164 +1726,16 @@ asmins(Prog *p) n = andptr - and; for(np = 0; np < n; np++) { c = and[np]; - if(c != 0x66 && c != 0xf2 && c != 0xf3 && c != 0x67) + if(c != 0xf2 && c != 0xf3 && (c < 0x64 || c > 0x67) && c != 0x2e && c != 0x3e && c != 0x26) break; } + for(r=cursym->r+cursym->nr; r-- > cursym->r; ) { + if(r->off < p->pc) + break; + r->off++; + } memmove(and+np+1, and+np, n-np); and[np] = 0x40 | rexflag; andptr++; } } - -enum{ - ABSD = 0, - ABSU = 1, - RELD = 2, - RELU = 3, -}; - -int modemap[4] = { 0, 1, -1, 2, }; - -typedef struct Reloc Reloc; - -struct Reloc -{ - int n; - int t; - uchar *m; - uint32 *a; -}; - -Reloc rels; - -static void -grow(Reloc *r) -{ - int t; - uchar *m, *nm; - uint32 *a, *na; - - t = r->t; - r->t += 64; - m = r->m; - a = r->a; - r->m = nm = mal(r->t*sizeof(uchar)); - r->a = na = mal(r->t*sizeof(uint32)); - memmove(nm, m, t*sizeof(uchar)); - memmove(na, a, t*sizeof(uint32)); - free(m); - free(a); -} - -void -dynreloc(Sym *s, uint32 v, int abs) -{ - int i, k, n; - uchar *m; - uint32 *a; - Reloc *r; - - if(s->type == SUNDEF) - k = abs ? ABSU : RELU; - else - k = abs ? ABSD : RELD; - /* Bprint(&bso, "R %s a=%ld(%lx) %d\n", s->name, v, v, k); */ - k = modemap[k]; - r = &rels; - n = r->n; - if(n >= r->t) - grow(r); - m = r->m; - a = r->a; - for(i = n; i > 0; i--){ - if(v < a[i-1]){ /* happens occasionally for data */ - m[i] = m[i-1]; - a[i] = a[i-1]; - } - else - break; - } - m[i] = k; - a[i] = v; - r->n++; -} - -static int -sput(char *s) -{ - char *p; - - p = s; - while(*s) - cput(*s++); - cput(0); - return s-p+1; -} - -void -asmdyn() -{ - int i, n, t, c; - Sym *s; - uint32 la, ra, *a; - vlong off; - uchar *m; - Reloc *r; - - cflush(); - off = seek(cout, 0, 1); - lputb(0); - t = 0; - lputb(imports); - t += 4; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->type == SUNDEF){ - lputb(s->sig); - t += 4; - t += sput(s->name); - } - - la = 0; - r = &rels; - n = r->n; - m = r->m; - a = r->a; - lputb(n); - t += 4; - for(i = 0; i < n; i++){ - ra = *a-la; - if(*a < la) - diag("bad relocation order"); - if(ra < 256) - c = 0; - else if(ra < 65536) - c = 1; - else - c = 2; - cput((c<<6)|*m++); - t++; - if(c == 0){ - cput(ra); - t++; - } - else if(c == 1){ - wputb(ra); - t += 2; - } - else{ - lputb(ra); - t += 4; - } - la = *a++; - } - - cflush(); - seek(cout, off, 0); - lputb(t); - - if(debug['v']){ - Bprint(&bso, "import table entries = %d\n", imports); - Bprint(&bso, "export table entries = %d\n", exports); - } -} diff --git a/src/cmd/8a/Makefile b/src/cmd/8a/Makefile index beb575544..78d361dbd 100644 --- a/src/cmd/8a/Makefile +++ b/src/cmd/8a/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 8a\ +TARG=8a HFILES=\ a.h\ @@ -20,21 +20,6 @@ OFILES=\ YFILES=\ a.y\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lbio -l9 - -$(OFILES): $(HFILES) +include ../../Make.ccmd lex.$O: ../cc/macbody ../cc/lexbody - -y.tab.h: $(YFILES) - bison -y $(YFLAGS) $(YFILES) - -y.tab.c: y.tab.h - test -f y.tab.c && touch y.tab.c - -clean: - rm -f *.$O $(TARG) *.6 enam.c 6.out a.out y.tab.h y.tab.c - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) diff --git a/src/cmd/8a/a.h b/src/cmd/8a/a.h index 035db2551..fe6b17280 100644 --- a/src/cmd/8a/a.h +++ b/src/cmd/8a/a.h @@ -1,7 +1,7 @@ // Inferno utils/8a/a.h // http://code.google.com/p/inferno-os/source/browse/utils/8a/a.h // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -50,7 +50,7 @@ typedef struct Ref Ref; typedef struct Gen Gen; typedef struct Io Io; typedef struct Hist Hist; -typedef struct Gen2 Gen2; +typedef struct Gen2 Gen2; #define MAXALIGN 7 #define FPCHIP 1 @@ -162,6 +162,7 @@ EXTERN int pass; EXTERN char* pathname; EXTERN int32 pc; EXTERN int peekc; +EXTERN int32 stmtline; EXTERN int sym; EXTERN char* symb; EXTERN int thechar; @@ -169,9 +170,9 @@ EXTERN char* thestring; EXTERN int32 thunk; EXTERN Biobuf obuf; -void* alloc(int32); +void* alloc(int32); void* allocn(void*, int32, int32); -void ensuresymb(int32); +void ensuresymb(int32); void errorexit(void); void pushio(void); void newio(void); diff --git a/src/cmd/8a/a.y b/src/cmd/8a/a.y index 8bc96cce5..04662f83d 100644 --- a/src/cmd/8a/a.y +++ b/src/cmd/8a/a.y @@ -1,7 +1,7 @@ // Inferno utils/8a/a.y // http://code.google.com/p/inferno-os/source/browse/utils/8a/a.y // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -64,7 +64,11 @@ %type spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 %% prog: -| prog line +| prog + { + stmtline = lineno; + } + line line: LLAB ':' diff --git a/src/cmd/8a/lex.c b/src/cmd/8a/lex.c index c8127bde9..bf298b266 100644 --- a/src/cmd/8a/lex.c +++ b/src/cmd/8a/lex.c @@ -1,7 +1,7 @@ // Inferno utils/8a/lex.c // http://code.google.com/p/inferno-os/source/browse/utils/8a/lex.c // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -925,10 +925,10 @@ jackpot: } Bputc(&obuf, a); Bputc(&obuf, a>>8); - Bputc(&obuf, lineno); - Bputc(&obuf, lineno>>8); - Bputc(&obuf, lineno>>16); - Bputc(&obuf, lineno>>24); + Bputc(&obuf, stmtline); + Bputc(&obuf, stmtline>>8); + Bputc(&obuf, stmtline>>16); + Bputc(&obuf, stmtline>>24); zaddr(&g2->from, sf); zaddr(&g2->to, st); diff --git a/src/cmd/8c/Makefile b/src/cmd/8c/Makefile index 85ea3013b..60f46d3c9 100644 --- a/src/cmd/8c/Makefile +++ b/src/cmd/8c/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 8c\ +TARG=8c HFILES=\ gc.h\ @@ -29,18 +29,9 @@ OFILES=\ ../8l/enam.$O\ LIB=\ - ../cc/cc.a$O + ../cc/cc.a\ -$(TARG): $(OFILES) $(LIB) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) $(LIB) -lm -lbio -l9 - -$(OFILES): $(HFILES) - -clean: - rm -f *.$O $(TARG) *.8 8.out a.out - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +include ../../Make.ccmd %.$O: ../cc/%.c - $(CC) $(CFLAGS) -c -I. -o $@ ../cc/$*.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../cc/$*.c diff --git a/src/cmd/8c/cgen64.c b/src/cmd/8c/cgen64.c index ce1512c51..3424f762c 100644 --- a/src/cmd/8c/cgen64.c +++ b/src/cmd/8c/cgen64.c @@ -57,7 +57,7 @@ vaddr(Node *n, int a) int32 hi64v(Node *n) { - if(align(0, types[TCHAR], Aarg1)) /* isbigendian */ + if(align(0, types[TCHAR], Aarg1, nil)) /* isbigendian */ return (int32)(n->vconst) & ~0L; else return (int32)((uvlong)n->vconst>>32) & ~0L; @@ -66,7 +66,7 @@ hi64v(Node *n) int32 lo64v(Node *n) { - if(align(0, types[TCHAR], Aarg1)) /* isbigendian */ + if(align(0, types[TCHAR], Aarg1, nil)) /* isbigendian */ return (int32)((uvlong)n->vconst>>32) & ~0L; else return (int32)(n->vconst) & ~0L; diff --git a/src/cmd/8c/div.c b/src/cmd/8c/div.c index 538e3522c..14945052e 100644 --- a/src/cmd/8c/div.c +++ b/src/cmd/8c/div.c @@ -120,7 +120,7 @@ sdivgen(Node *l, Node *r, Node *ax, Node *dx) if(c < 0) c = -c; a = sdiv(c, &m, &s); -//print("a=%d i=%ld s=%d m=%lux\n", a, (int32)r->vconst, s, m); +//print("a=%d i=%d s=%d m=%ux\n", a, (int32)r->vconst, s, m); gins(AMOVL, nodconst(m), ax); gins(AIMULL, l, Z); gins(AMOVL, l, ax); @@ -141,7 +141,7 @@ udivgen(Node *l, Node *r, Node *ax, Node *dx) Node nod; a = udiv(r->vconst, &m, &s, &t); -//print("a=%ud i=%ld p=%d s=%d m=%lux\n", a, (int32)r->vconst, t, s, m); +//print("a=%ud i=%d p=%d s=%d m=%ux\n", a, (int32)r->vconst, t, s, m); if(t != 0) { gins(AMOVL, l, ax); gins(ASHRL, nodconst(t), ax); diff --git a/src/cmd/8c/list.c b/src/cmd/8c/list.c index 6caafd258..c422905cd 100644 --- a/src/cmd/8c/list.c +++ b/src/cmd/8c/list.c @@ -76,15 +76,27 @@ Pconv(Fmt *fp) Prog *p; p = va_arg(fp->args, Prog*); - if(p->as == ADATA) - sprint(str, " %A %D/%d,%D", - p->as, &p->from, p->from.scale, &p->to); - else if(p->as == ATEXT) - sprint(str, " %A %D,%d,%D", - p->as, &p->from, p->from.scale, &p->to); - else - sprint(str, " %A %D,%D", - p->as, &p->from, &p->to); + switch(p->as) { + case ADATA: + sprint(str, "(%L) %A %D/%d,%D", + p->lineno, p->as, &p->from, p->from.scale, &p->to); + break; + + case ATEXT: + if(p->from.scale) { + sprint(str, "(%L) %A %D,%d,%lD", + p->lineno, p->as, &p->from, p->from.scale, &p->to); + break; + } + sprint(str, "(%L) %A %D,%lD", + p->lineno, p->as, &p->from, &p->to); + break; + + default: + sprint(str, "(%L) %A %D,%lD", + p->lineno, p->as, &p->from, &p->to); + break; + } return fmtstrcpy(fp, str); } diff --git a/src/cmd/8c/mul.c b/src/cmd/8c/mul.c index 2b7332d54..a0742807e 100644 --- a/src/cmd/8c/mul.c +++ b/src/cmd/8c/mul.c @@ -355,7 +355,7 @@ mulgen1(uint32 v, Node *n) mulparam(v, p); found: -// print("v=%.lx a=%d n=%d s=%d g=%d o=%d \n", p->value, p->alg, p->neg, p->shift, p->arg, p->off); +// print("v=%.x a=%d n=%d s=%d g=%d o=%d \n", p->value, p->alg, p->neg, p->shift, p->arg, p->off); if(p->alg < 0) return 0; diff --git a/src/cmd/8c/reg.c b/src/cmd/8c/reg.c index 837da2d04..6ba07bed2 100644 --- a/src/cmd/8c/reg.c +++ b/src/cmd/8c/reg.c @@ -382,7 +382,7 @@ regopt(Prog *p) if(debug['R'] && debug['v']) { print("\nlooping structure:\n"); for(r = firstr; r != R; r = r->link) { - print("%ld:%P", r->loop, r->prog); + print("%d:%P", r->loop, r->prog); for(z=0; zuse1.b[z] | r->use2.b[z] | @@ -1031,7 +1031,7 @@ paint1(Reg *r, int bn) if(LOAD(r) & ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])) & bb) { change -= CLOAD * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tld %B $%d\n", r->loop, + print("%d%P\td %B $%d\n", r->loop, r->prog, blsh(bn), change); } for(;;) { @@ -1044,7 +1044,7 @@ paint1(Reg *r, int bn) if(BtoR(bb) != D_F0) change = -CINF; if(debug['R'] && debug['v']) - print("%ld%P\tu1 %B $%d\n", r->loop, + print("%d%P\tu1 %B $%d\n", r->loop, p, blsh(bn), change); } @@ -1054,7 +1054,7 @@ paint1(Reg *r, int bn) if(BtoR(bb) != D_F0) change = -CINF; if(debug['R'] && debug['v']) - print("%ld%P\tu2 %B $%d\n", r->loop, + print("%d%P\tu2 %B $%d\n", r->loop, p, blsh(bn), change); } @@ -1064,7 +1064,7 @@ paint1(Reg *r, int bn) if(BtoR(bb) != D_F0) change = -CINF; if(debug['R'] && debug['v']) - print("%ld%P\tst %B $%d\n", r->loop, + print("%d%P\tst %B $%d\n", r->loop, p, blsh(bn), change); } diff --git a/src/cmd/8c/swt.c b/src/cmd/8c/swt.c index 1c502f5ff..be48885f8 100644 --- a/src/cmd/8c/swt.c +++ b/src/cmd/8c/swt.c @@ -40,7 +40,7 @@ swit1(C1 *q, int nc, int32 def, Node *n) if(nc < 5) { for(i=0; ival); + print("case = %.8ux\n", q->val); gopcode(OEQ, n->type, n, nodconst(q->val)); patch(p, q->label); q++; @@ -52,7 +52,7 @@ swit1(C1 *q, int nc, int32 def, Node *n) i = nc / 2; r = q+i; if(debug['W']) - print("case > %.8lux\n", r->val); + print("case > %.8ux\n", r->val); gopcode(OGT, n->type, n, nodconst(r->val)); sp = p; gbranch(OGOTO); @@ -61,7 +61,7 @@ swit1(C1 *q, int nc, int32 def, Node *n) swit1(q, i, def, n); if(debug['W']) - print("case < %.8lux\n", r->val); + print("case < %.8ux\n", r->val); patch(sp, pc); swit1(r+1, nc-i-1, def, n); } @@ -501,7 +501,7 @@ zaddr(Biobuf *b, Adr *a, int s) } int32 -align(int32 i, Type *t, int op) +align(int32 i, Type *t, int op, int32 *maxalign) { int32 o; Type *v; @@ -515,7 +515,9 @@ align(int32 i, Type *t, int op) break; case Asu2: /* padding at end of a struct */ - w = SZ_LONG; + w = *maxalign; + if(w < 1) + w = 1; if(packflg) w = packflg; break; @@ -523,10 +525,16 @@ align(int32 i, Type *t, int op) case Ael1: /* initial align of struct element */ for(v=t; v->etype==TARRAY; v=v->link) ; - w = ewidth[v->etype]; - if(w <= 0 || w >= SZ_LONG) - w = SZ_LONG; - if(packflg) + if(v->etype == TSTRUCT || v->etype == TUNION) + w = v->align; + else { + w = ewidth[v->etype]; + if(w == 8) + w = 4; + } + if(w < 1 || w > SZ_LONG) + fatal(Z, "align"); + if(packflg) w = packflg; break; @@ -536,8 +544,8 @@ align(int32 i, Type *t, int op) case Aarg0: /* initial passbyptr argument in arg list */ if(typesuv[t->etype]) { - o = align(o, types[TIND], Aarg1); - o = align(o, types[TIND], Aarg2); + o = align(o, types[TIND], Aarg1, nil); + o = align(o, types[TIND], Aarg2, nil); } break; @@ -558,13 +566,15 @@ align(int32 i, Type *t, int op) break; case Aaut3: /* total align of automatic */ - o = align(o, t, Ael1); - o = align(o, t, Ael2); + o = align(o, t, Ael1, nil); + o = align(o, t, Ael2, nil); break; } o = xround(o, w); + if(maxalign && *maxalign < w) + *maxalign = w; if(debug['A']) - print("align %s %ld %T = %ld\n", bnames[op], i, t, o); + print("align %s %d %T = %d\n", bnames[op], i, t, o); return o; } diff --git a/src/cmd/8c/txt.c b/src/cmd/8c/txt.c index 194599c3a..0dd387d11 100644 --- a/src/cmd/8c/txt.c +++ b/src/cmd/8c/txt.c @@ -385,7 +385,7 @@ err: void regsalloc(Node *n, Node *nn) { - cursafe = align(cursafe, nn->type, Aaut3); + cursafe = align(cursafe, nn->type, Aaut3, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); *n = *nodsafe; n->xoffset = -(stkoff + cursafe); @@ -399,22 +399,22 @@ regaalloc1(Node *n, Node *nn) { nodreg(n, nn, REGARG); reg[REGARG]++; - curarg = align(curarg, nn->type, Aarg1); - curarg = align(curarg, nn->type, Aarg2); + curarg = align(curarg, nn->type, Aarg1, nil); + curarg = align(curarg, nn->type, Aarg2, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); } void regaalloc(Node *n, Node *nn) { - curarg = align(curarg, nn->type, Aarg1); + curarg = align(curarg, nn->type, Aarg1, nil); *n = *nn; n->op = OINDREG; n->reg = REGSP; n->xoffset = curarg; n->complex = 0; n->addable = 20; - curarg = align(curarg, nn->type, Aarg2); + curarg = align(curarg, nn->type, Aarg2, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); } @@ -1403,7 +1403,6 @@ exreg(Type *t) return o+1; // +1 to avoid 0 == failure; naddr case OEXREG will -1. } - USED(t); return 0; } diff --git a/src/cmd/8g/Makefile b/src/cmd/8g/Makefile index d2431182f..09cf8d4e3 100644 --- a/src/cmd/8g/Makefile +++ b/src/cmd/8g/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 8g +TARG=8g HFILES=\ ../gc/go.h\ @@ -27,18 +27,9 @@ OFILES=\ reg.$O\ LIB=\ - ../gc/gc.a$O + ../gc/gc.a\ -$(TARG): $(OFILES) $(LIB) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) $(LIB) -lbio -l9 -lm - -$(OFILES): $(HFILES) - -clean: - rm -f *.$O $(TARG) *.8 enam.c 8.out a.out - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +include ../../Make.ccmd %.$O: ../gc/%.c - $(CC) $(CFLAGS) -c -I. -o $@ ../gc/$*.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../gc/$*.c diff --git a/src/cmd/8g/cgen.c b/src/cmd/8g/cgen.c index 8fbdc6ee7..875d434fa 100644 --- a/src/cmd/8g/cgen.c +++ b/src/cmd/8g/cgen.c @@ -175,7 +175,7 @@ cgen(Node *n, Node *res) case OREAL: case OIMAG: case OCMPLX: - // TODO compile complex + fatal("unexpected complex"); return; // these call bgen to get a bool value @@ -230,8 +230,8 @@ cgen(Node *n, Node *res) cgen(nl, res); break; } - mgen(nl, &n1, res); tempname(&n2, n->type); + mgen(nl, &n1, res); gmove(&n1, &n2); gmove(&n2, res); mfree(&n1); @@ -392,23 +392,16 @@ uop: // unary gmove(&n1, res); return; -flt: // floating-point. 387 (not SSE2) to interoperate with 6c +flt: // floating-point. 387 (not SSE2) to interoperate with 8c nodreg(&f0, nl->type, D_F0); nodreg(&f1, n->type, D_F0+1); if(nr != N) goto flt2; - if(n->op == OMINUS) { - nr = nodintconst(-1); - convlit(&nr, n->type); - n->op = OMUL; - goto flt2; - } - // unary cgen(nl, &f0); if(n->op != OCONV && n->op != OPLUS) - gins(foptoas(n->op, n->type, 0), &f0, &f0); + gins(foptoas(n->op, n->type, 0), N, N); gmove(&f0, res); return; @@ -525,9 +518,11 @@ agen(Node *n, Node *res) p2 = nil; // to be patched to panicindex. w = n->type->width; if(nr->addable) { - agenr(nl, &n3, res); - if(!isconst(nr, CTINT)) { + if(!isconst(nr, CTINT)) tempname(&tmp, types[TINT32]); + if(!isconst(nl, CTSTR)) + agenr(nl, &n3, res); + if(!isconst(nr, CTINT)) { p2 = cgenindex(nr, &tmp); regalloc(&n1, tmp.type, N); gmove(&tmp, &n1); @@ -539,13 +534,16 @@ agen(Node *n, Node *res) regalloc(&n1, tmp.type, N); gmove(&tmp, &n1); } - regalloc(&n3, types[tptr], res); - agen(nl, &n3); + if(!isconst(nl, CTSTR)) { + regalloc(&n3, types[tptr], res); + agen(nl, &n3); + } } else { tempname(&tmp, types[TINT32]); p2 = cgenindex(nr, &tmp); nr = &tmp; - agenr(nl, &n3, res); + if(!isconst(nl, CTSTR)) + agenr(nl, &n3, res); regalloc(&n1, tmp.type, N); gins(optoas(OAS, tmp.type), &tmp, &n1); } @@ -556,7 +554,7 @@ agen(Node *n, Node *res) // explicit check for nil if array is large enough // that we might derive too big a pointer. - if(!isslice(nl->type) && nl->type->width >= unmappedzero) { + if(isfixedarray(nl->type) && nl->type->width >= unmappedzero) { regalloc(&n4, types[tptr], &n3); gmove(&n3, &n4); n4.op = OINDREG; @@ -571,9 +569,10 @@ agen(Node *n, Node *res) // constant index if(isconst(nr, CTINT)) { + if(isconst(nl, CTSTR)) + fatal("constant string constant index"); v = mpgetfix(nr->val.u.xval); - if(isslice(nl->type)) { - + if(isslice(nl->type) || nl->type->etype == TSTRING) { if(!debug['B'] && !n->etype) { n1 = n3; n1.op = OINDREG; @@ -591,13 +590,6 @@ agen(Node *n, Node *res) n1.type = types[tptr]; n1.xoffset = Array_array; gmove(&n1, &n3); - } else - if(!debug['B'] && !n->etype) { - if(v < 0) - yyerror("out of bounds on array"); - else - if(v >= nl->type->bound) - yyerror("out of bounds on array"); } nodconst(&n2, types[tptr], v*w); @@ -614,7 +606,9 @@ agen(Node *n, Node *res) if(!debug['B'] && !n->etype) { // check bounds - if(isslice(nl->type)) { + if(isconst(nl, CTSTR)) + nodconst(&n1, types[TUINT32], nl->val.u.sval->len); + else if(isslice(nl->type) || nl->type->etype == TSTRING) { n1 = n3; n1.op = OINDREG; n1.type = types[tptr]; @@ -628,8 +622,17 @@ agen(Node *n, Node *res) ginscall(panicindex, 0); patch(p1, pc); } + + if(isconst(nl, CTSTR)) { + regalloc(&n3, types[tptr], res); + p1 = gins(ALEAL, N, &n3); + datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from); + p1->from.scale = 1; + p1->from.index = n2.val.u.reg; + goto indexdone; + } - if(isslice(nl->type)) { + if(isslice(nl->type) || nl->type->etype == TSTRING) { n1 = n3; n1.op = OINDREG; n1.type = types[tptr]; @@ -649,6 +652,7 @@ agen(Node *n, Node *res) gmove(&n3, res); } + indexdone: gmove(&n3, res); regfree(&n2); regfree(&n3); @@ -724,8 +728,14 @@ igen(Node *n, Node *a, Node *res) { Node n1; + // release register for now, to avoid + // confusing tempname. + if(res != N && res->op == OREGISTER) + reg[res->val.u.reg]--; tempname(&n1, types[tptr]); agen(n, &n1); + if(res != N && res->op == OREGISTER) + reg[res->val.u.reg]++; regalloc(a, types[tptr], res); gmove(&n1, a); a->op = OINDREG; @@ -768,6 +778,9 @@ bgen(Node *n, int true, Prog *to) if(n == N) n = nodbool(1); + if(n->ninit != nil) + genlist(n->ninit); + nl = n->left; nr = n->right; diff --git a/src/cmd/8g/galign.c b/src/cmd/8g/galign.c index 346647205..1c14dfe47 100644 --- a/src/cmd/8g/galign.c +++ b/src/cmd/8g/galign.c @@ -25,7 +25,6 @@ Typedef typedefs[] = void betypeinit(void) { - maxround = 4; widthptr = 4; zprog.link = P; diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c index 8a55ffd59..4dcbd4489 100644 --- a/src/cmd/8g/ggen.c +++ b/src/cmd/8g/ggen.c @@ -8,7 +8,6 @@ #include "opt.h" static Prog *pret; -static Node *naclnop; void compile(Node *fn) @@ -24,7 +23,6 @@ compile(Node *fn) newproc = sysfunc("newproc"); deferproc = sysfunc("deferproc"); deferreturn = sysfunc("deferreturn"); - naclnop = sysfunc("naclnop"); panicindex = sysfunc("panicindex"); panicslice = sysfunc("panicslice"); throwreturn = sysfunc("throwreturn"); @@ -64,6 +62,8 @@ compile(Node *fn) pl = newplist(); pl->name = curfn->nname; + setlineno(curfn); + nodconst(&nod1, types[TINT32], 0); ptxt = gins(ATEXT, curfn->nname, &nod1); afunclit(&ptxt->from); @@ -85,6 +85,8 @@ compile(Node *fn) checklabels(); if(nerrors != 0) goto ret; + if(curfn->endlineno) + lineno = curfn->endlineno; if(curfn->type->outtuple != 0) ginscall(throwreturn, 0); @@ -92,21 +94,13 @@ compile(Node *fn) if(pret) patch(pret, pc); ginit(); + if(hasdefer) + ginscall(deferreturn, 0); if(curfn->exit) genlist(curfn->exit); gclean(); if(nerrors != 0) goto ret; - if(hasdefer) { - // On Native client, insert call to no-op function - // to force alignment immediately before call to deferreturn, - // so that when jmpdefer subtracts 5 from the second CALL's - // return address and then the return masks off the low bits, - // we'll back up to the NOPs immediately after the dummy CALL. - if(strcmp(getgoos(), "nacl") == 0) - ginscall(naclnop, 0); - ginscall(deferreturn, 0); - } pc->as = ARET; // overwrite AEND pc->lineno = lineno; @@ -114,12 +108,12 @@ compile(Node *fn) regopt(ptxt); } // fill in argument size - ptxt->to.offset2 = rnd(curfn->type->argwid, maxround); + ptxt->to.offset2 = rnd(curfn->type->argwid, widthptr); // fill in final stack size if(stksize > maxstksize) maxstksize = stksize; - ptxt->to.offset = rnd(maxstksize+maxarg, maxround); + ptxt->to.offset = rnd(maxstksize+maxarg, widthptr); maxstksize = 0; if(debug['f']) @@ -789,25 +783,58 @@ regcmp(const void *va, const void *vb) static Prog* throwpc; +// We're only going to bother inlining if we can +// convert all the arguments to 32 bits safely. Can we? +static int +fix64(NodeList *nn, int n) +{ + NodeList *l; + Node *r; + int i; + + l = nn; + for(i=0; in->right; + if(is64(r->type) && !smallintconst(r)) { + if(r->op == OCONV) + r = r->left; + if(is64(r->type)) + return 0; + } + l = l->next; + } + return 1; +} + void getargs(NodeList *nn, Node *reg, int n) { NodeList *l; + Node *r; int i; throwpc = nil; l = nn; for(i=0; in->right) && !isslice(l->n->right->type)) { + r = l->n->right; + if(is64(r->type)) { + if(r->op == OCONV) + r = r->left; + else if(smallintconst(r)) + r->type = types[TUINT32]; + if(is64(r->type)) + fatal("getargs"); + } + if(!smallintconst(r) && !isslice(r->type)) { if(i < 3) // AX CX DX - nodreg(reg+i, l->n->right->type, D_AX+i); + nodreg(reg+i, r->type, D_AX+i); else reg[i].op = OXXX; - regalloc(reg+i, l->n->right->type, reg+i); - cgen(l->n->right, reg+i); + regalloc(reg+i, r->type, reg+i); + cgen(r, reg+i); } else - reg[i] = *l->n->right; + reg[i] = *r; if(reg[i].local != 0) yyerror("local used"); reg[i].local = l->n->left->xoffset; @@ -821,44 +848,53 @@ getargs(NodeList *nn, Node *reg, int n) void cmpandthrow(Node *nl, Node *nr) { - vlong cl, cr; + vlong cl; Prog *p1; int op; - Node *c; + Node *c, n1; + Type *t; op = OLE; if(smallintconst(nl)) { cl = mpgetfix(nl->val.u.xval); if(cl == 0) return; - if(smallintconst(nr)) { - cr = mpgetfix(nr->val.u.xval); - if(cl > cr) { - if(throwpc == nil) { - throwpc = pc; - ginscall(panicslice, 0); - } else - patch(gbranch(AJMP, T), throwpc); - } + if(smallintconst(nr)) return; - } - // put the constant on the right op = brrev(op); c = nl; nl = nr; nr = c; } - - gins(optoas(OCMP, types[TUINT32]), nl, nr); + + // Arguments are known not to be 64-bit, + // but they might be smaller than 32 bits. + // Check if we need to use a temporary. + // At least one of the arguments is 32 bits + // (the len or cap) so one temporary suffices. + n1.op = OXXX; + t = types[TUINT32]; + if(nl->type->width != t->width) { + regalloc(&n1, t, nl); + gmove(nl, &n1); + nl = &n1; + } else if(nr->type->width != t->width) { + regalloc(&n1, t, nr); + gmove(nr, &n1); + nr = &n1; + } + gins(optoas(OCMP, t), nl, nr); + if(n1.op != OXXX) + regfree(&n1); if(throwpc == nil) { - p1 = gbranch(optoas(op, types[TUINT32]), T); + p1 = gbranch(optoas(op, t), T); throwpc = pc; ginscall(panicslice, 0); patch(p1, pc); } else { op = brcom(op); - p1 = gbranch(optoas(op, types[TUINT32]), T); + p1 = gbranch(optoas(op, t), T); patch(p1, throwpc); } } @@ -889,6 +925,8 @@ cgen_inline(Node *n, Node *res) goto no; if(!n->left->addable) goto no; + if(n->left->sym == S) + goto no; if(n->left->sym->pkg != runtimepkg) goto no; if(strcmp(n->left->sym->name, "slicearray") == 0) @@ -906,6 +944,8 @@ cgen_inline(Node *n, Node *res) slicearray: if(!sleasy(res)) goto no; + if(!fix64(n->list, 5)) + goto no; getargs(n->list, nodes, 5); // if(hb[3] > nel[1]) goto throw @@ -988,6 +1028,8 @@ slicearray: return 1; sliceslice: + if(!fix64(n->list, narg)) + goto no; ntemp.op = OXXX; if(!sleasy(n->list->n->right)) { Node *n0; @@ -1016,6 +1058,7 @@ sliceslice: // if(lb[1] > old.nel[0]) goto throw; n2 = nodes[0]; n2.xoffset += Array_nel; + n2.type = types[TUINT32]; cmpandthrow(&nodes[1], &n2); // ret.nel = old.nel[0]-lb[1]; @@ -1035,6 +1078,7 @@ sliceslice: // if(hb[2] > old.cap[0]) goto throw; n2 = nodes[0]; n2.xoffset += Array_cap; + n2.type = types[TUINT32]; cmpandthrow(&nodes[2], &n2); // if(lb[1] > hb[2]) goto throw; diff --git a/src/cmd/8g/gobj.c b/src/cmd/8g/gobj.c index 1f4b106f7..e48ad529b 100644 --- a/src/cmd/8g/gobj.c +++ b/src/cmd/8g/gobj.c @@ -642,7 +642,7 @@ dsymptr(Sym *s, int off, Sym *x, int xoff) } void -genembedtramp(Type *rcvr, Type *method, Sym *newnam) +genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface) { Sym *e; int c, d, o, mov, add, loaded; @@ -739,7 +739,7 @@ out: p = pc; gins(AJMP, N, N); p->to.type = D_EXTERN; - p->to.sym = methodsym(method->sym, ptrto(f->type)); + p->to.sym = methodsym(method->sym, ptrto(f->type), 0); //print("6. %P\n", p); pc->as = ARET; // overwrite AEND diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c index 6890c683e..8ed7e5564 100644 --- a/src/cmd/8g/gsubr.c +++ b/src/cmd/8g/gsubr.c @@ -149,6 +149,8 @@ ggloblnod(Node *nam, int32 width) p->to.sym = S; p->to.type = D_CONST; p->to.offset = width; + if(nam->readonly) + p->from.scale = RODATA; } void @@ -165,6 +167,7 @@ ggloblsym(Sym *s, int32 width, int dupok) p->to.offset = width; if(dupok) p->from.scale = DUPOK; + p->from.scale |= RODATA; } int @@ -661,6 +664,11 @@ foptoas(int op, Type *t, int flg) return AFCOMDP; case FCASE(OCMP, TFLOAT64, Fpop2): return AFCOMDPP; + + case FCASE(OMINUS, TFLOAT32, 0): + return AFCHS; + case FCASE(OMINUS, TFLOAT64, 0): + return AFCHS; } fatal("foptoas %O %T %#x", op, t, flg); @@ -707,7 +715,7 @@ gclean(void) for(i=D_AL; i<=D_DI; i++) if(reg[i]) - yyerror("reg %R left allocated at %lux", i, regpc[i]); + yyerror("reg %R left allocated at %ux", i, regpc[i]); } int32 @@ -764,7 +772,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%#lux\n", i, regpc[i]); + fprint(2, "\t%R\t%#ux\n", i, regpc[i]); yyerror("out of fixed registers"); goto err; @@ -1651,7 +1659,7 @@ checkoffset(Addr *a, int canemitcode) if(a->offset < unmappedzero) return; if(!canemitcode) - fatal("checkoffset %#llx, cannot emit code", a->offset); + fatal("checkoffset %#x, cannot emit code", a->offset); // cannot rely on unmapped nil page at 0 to catch // reference with large offset. instead, emit explicit diff --git a/src/cmd/8g/list.c b/src/cmd/8g/list.c index 9b3622a6d..edb1ece84 100644 --- a/src/cmd/8g/list.c +++ b/src/cmd/8g/list.c @@ -47,24 +47,28 @@ Pconv(Fmt *fp) { char str[STRINGSZ]; Prog *p; + char scale[40]; p = va_arg(fp->args, Prog*); sconsize = 8; + scale[0] = '\0'; + if(p->from.scale != 0 && (p->as == AGLOBL || p->as == ATEXT)) + snprint(scale, sizeof scale, "%d,", p->from.scale); switch(p->as) { default: - snprint(str, sizeof(str), "%.4ld (%L) %-7A %D,%D", - p->loc, p->lineno, p->as, &p->from, &p->to); + snprint(str, sizeof(str), "%.4d (%L) %-7A %D,%s%D", + p->loc, p->lineno, p->as, &p->from, scale, &p->to); break; case ADATA: sconsize = p->from.scale; - snprint(str, sizeof(str), "%.4ld (%L) %-7A %D/%d,%D", + snprint(str, sizeof(str), "%.4d (%L) %-7A %D/%d,%D", p->loc, p->lineno, p->as, &p->from, sconsize, &p->to); break; case ATEXT: - snprint(str, sizeof(str), "%.4ld (%L) %-7A %D,%lD", - p->loc, p->lineno, p->as, &p->from, &p->to); + snprint(str, sizeof(str), "%.4d (%L) %-7A %D,%s%lD", + p->loc, p->lineno, p->as, &p->from, scale, &p->to); break; } return fmtstrcpy(fp, str); diff --git a/src/cmd/8g/reg.c b/src/cmd/8g/reg.c index 3e57916c7..e1dacf55a 100644 --- a/src/cmd/8g/reg.c +++ b/src/cmd/8g/reg.c @@ -612,17 +612,17 @@ brk: print("\nstats\n"); if(ostats.ncvtreg) - print(" %4ld cvtreg\n", ostats.ncvtreg); + print(" %4d cvtreg\n", ostats.ncvtreg); if(ostats.nspill) - print(" %4ld spill\n", ostats.nspill); + print(" %4d spill\n", ostats.nspill); if(ostats.nreload) - print(" %4ld reload\n", ostats.nreload); + print(" %4d reload\n", ostats.nreload); if(ostats.ndelmov) - print(" %4ld delmov\n", ostats.ndelmov); + print(" %4d delmov\n", ostats.ndelmov); if(ostats.nvar) - print(" %4ld delmov\n", ostats.nvar); + print(" %4d delmov\n", ostats.nvar); if(ostats.naddr) - print(" %4ld delmov\n", ostats.naddr); + print(" %4d delmov\n", ostats.naddr); memset(&ostats, 0, sizeof(ostats)); } @@ -789,6 +789,8 @@ mkvar(Reg *r, Adr *a) // if they overlaps, disable both if(overlap(v->offset, v->width, o, w)) { + if(debug['R']) + print("disable %s\n", v->sym->name); v->addr = 1; flag = 1; } @@ -821,7 +823,7 @@ mkvar(Reg *r, Adr *a) v->addr = flag; // funny punning 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 %S %D flag=%d\n", i, et, w, s, a, v->addr); ostats.nvar++; bit = blsh(i); @@ -1378,7 +1380,7 @@ dumpone(Reg *r) int z; Bits bit; - print("%ld:%P", r->loop, r->prog); + print("%d:%P", r->loop, r->prog); for(z=0; zset.b[z] | @@ -1427,14 +1429,14 @@ dumpit(char *str, Reg *r0) if(r1 != R) { print(" pred:"); for(; r1 != R; r1 = r1->p2link) - print(" %.4lud", r1->prog->loc); + print(" %.4ud", r1->prog->loc); print("\n"); } // r1 = r->s1; // if(r1 != R) { // print(" succ:"); // for(; r1 != R; r1 = r1->s1) -// print(" %.4lud", r1->prog->loc); +// print(" %.4ud", r1->prog->loc); // print("\n"); // } } diff --git a/src/cmd/8l/8.out.h b/src/cmd/8l/8.out.h index c17f606e2..0866f05f0 100644 --- a/src/cmd/8l/8.out.h +++ b/src/cmd/8l/8.out.h @@ -33,6 +33,7 @@ #define NOPROF (1<<0) #define DUPOK (1<<1) #define NOSPLIT (1<<2) +#define RODATA (1<<3) enum as { @@ -383,8 +384,8 @@ enum as AEND, - ADYNT, - AINIT, + ADYNT_, + AINIT_, ASIGNAME, @@ -498,6 +499,9 @@ enum D_CONST2 = D_INDIR+D_INDIR, D_SIZE, /* 8l internal */ + D_PCREL, + D_GOTOFF, + D_GOTREL, T_TYPE = 1<<0, T_INDEX = 1<<1, diff --git a/src/cmd/8l/Makefile b/src/cmd/8l/Makefile index 88c7c512b..84976ba18 100644 --- a/src/cmd/8l/Makefile +++ b/src/cmd/8l/Makefile @@ -2,15 +2,20 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 8l\ +TARG=8l OFILES=\ asm.$O\ + data.$O\ + dwarf.$O\ elf.$O\ enam.$O\ + go.$O\ + ldelf.$O\ + ldmacho.$O\ lib.$O\ list.$O\ macho.$O\ @@ -18,30 +23,26 @@ OFILES=\ optab.$O\ pass.$O\ pe.$O\ + prof.$O\ span.$O\ - go.$O\ + symtab.$O\ + HFILES=\ l.h\ ../8l/8.out.h\ + ../ld/dwarf.h\ ../ld/elf.h\ ../ld/macho.h\ ../ld/pe.h\ - -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lbio -l9 - -$(OFILES): $(HFILES) +include ../../Make.ccmd enam.c: 8.out.h sh mkenam -clean: - rm -f *.$O $(TARG) *.8 enam.c 8.out a.out +CLEANFILES+=enam.c -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) %.$O: ../ld/%.c - $(CC) $(CFLAGS) -c -I. ../ld/$*.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. ../ld/$*.c diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c index a7f894aa2..cdb5a33e6 100644 --- a/src/cmd/8l/asm.c +++ b/src/cmd/8l/asm.c @@ -28,9 +28,12 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Writing object files. + #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" +#include "../ld/dwarf.h" #include "../ld/macho.h" #include "../ld/pe.h" @@ -38,7 +41,6 @@ char linuxdynld[] = "/lib/ld-linux.so.2"; char freebsddynld[] = "/usr/libexec/ld-elf.so.1"; -uint32 symdatva = SYMDATVA; int32 entryvalue(void) @@ -52,15 +54,8 @@ entryvalue(void) s = lookup(a, 0); if(s->type == 0) return INITTEXT; - switch(s->type) { - case STEXT: - break; - case SDATA: - if(dlm) - return s->value+INITDAT; - default: + if(s->type != STEXT) diag("entry not text: %s", s->name); - } return s->value; } @@ -103,134 +98,13 @@ vputl(uvlong l) lputl(l); } -void -strnput(char *s, int n) -{ - for(; *s && n > 0; s++) { - cput(*s); - n--; - } - while(n > 0) { - cput(0); - n--; - } -} - -vlong -addstring(Sym *s, char *str) -{ - int n, m; - vlong r; - Prog *p; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - n = strlen(str)+1; - while(n > 0) { - m = n; - if(m > sizeof(p->to.scon)) - m = sizeof(p->to.scon); - p = newdata(s, s->value, m, D_EXTERN); - p->to.type = D_SCONST; - memmove(p->to.scon, str, m); - s->value += m; - str += m; - n -= m; - } - return r; -} - -vlong -adduintxx(Sym *s, uint64 v, int wid) -{ - vlong r; - Prog *p; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, wid, D_EXTERN); - s->value += wid; - p->to.type = D_CONST; - p->to.offset = v; - return r; -} - -vlong -adduint8(Sym *s, uint8 v) -{ - return adduintxx(s, v, 1); -} - -vlong -adduint16(Sym *s, uint16 v) -{ - return adduintxx(s, v, 2); -} - -vlong -adduint32(Sym *s, uint32 v) -{ - return adduintxx(s, v, 4); -} - -vlong -adduint64(Sym *s, uint64 v) -{ - return adduintxx(s, v, 8); -} - -vlong -addaddr(Sym *s, Sym *t) -{ - vlong r; - Prog *p; - enum { Ptrsize = 4 }; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, Ptrsize, D_EXTERN); - s->value += Ptrsize; - p->to.type = D_ADDR; - p->to.index = D_EXTERN; - p->to.offset = 0; - p->to.sym = t; - return r; -} - -vlong -addsize(Sym *s, Sym *t) -{ - vlong r; - Prog *p; - enum { Ptrsize = 4 }; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, Ptrsize, D_EXTERN); - s->value += Ptrsize; - p->to.type = D_SIZE; - p->to.index = D_EXTERN; - p->to.offset = 0; - p->to.sym = t; - return r; -} - vlong datoff(vlong addr) { - if(addr >= INITDAT) { - if(HEADTYPE == 8) - return addr - INITDAT + rnd(HEADR+textsize, 4096); - return addr - INITDAT + rnd(HEADR+textsize, INITRND); - } + if(addr >= segdata.vaddr) + return addr - segdata.vaddr + segdata.fileoff; + if(addr >= segtext.vaddr) + return addr - segtext.vaddr + segtext.fileoff; diag("datoff %#llx", addr); return 0; } @@ -252,6 +126,10 @@ enum { ElfStrGosymtab, ElfStrGopclntab, ElfStrShstrtab, + ElfStrSymtab, + ElfStrStrtab, + ElfStrRelPlt, + ElfStrPlt, NElfStr }; @@ -273,26 +151,424 @@ needlib(char *name) return 0; } +int nelfsym = 1; + +static void addpltsym(Sym*); +static void addgotsym(Sym*); + +void +adddynrel(Sym *s, Reloc *r) +{ + Sym *targ, *rel, *got; + + targ = r->sym; + cursym = s; + + switch(r->type) { + default: + if(r->type >= 256) { + diag("unexpected relocation type %d", r->type); + return; + } + break; + + // Handle relocations found in ELF object files. + case 256 + R_386_PC32: + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected R_386_PC32 relocation for dynamic symbol %s", targ->name); + if(targ->type == 0 || targ->type == SXREF) + diag("unknown symbol %s in pcrel", targ->name); + r->type = D_PCREL; + r->add += 4; + return; + + case 256 + R_386_PLT32: + r->type = D_PCREL; + r->add += 4; + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add += targ->plt; + } + return; + + case 256 + R_386_GOT32: + if(targ->dynimpname == nil || targ->dynexport) { + // have symbol + // turn MOVL of GOT entry into LEAL of symbol itself + if(r->off < 2 || s->p[r->off-2] != 0x8b) { + diag("unexpected GOT reloc for non-dynamic symbol %s", targ->name); + return; + } + s->p[r->off-2] = 0x8d; + r->type = D_GOTOFF; + return; + } + addgotsym(targ); + r->type = D_CONST; // write r->add during relocsym + r->sym = S; + r->add += targ->got; + return; + + case 256 + R_386_GOTOFF: + r->type = D_GOTOFF; + return; + + case 256 + R_386_GOTPC: + r->type = D_PCREL; + r->sym = lookup(".got", 0); + r->add += 4; + return; + + case 256 + R_386_32: + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected R_386_32 relocation for dynamic symbol %s", targ->name); + r->type = D_ADDR; + return; + + case 512 + MACHO_GENERIC_RELOC_VANILLA*2 + 0: + r->type = D_ADDR; + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected reloc for dynamic symbol %s", targ->name); + return; + + case 512 + MACHO_GENERIC_RELOC_VANILLA*2 + 1: + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = targ->plt; + r->type = D_PCREL; + return; + } + r->type = D_PCREL; + return; + + case 512 + MACHO_FAKE_GOTPCREL: + if(targ->dynimpname == nil || targ->dynexport) { + // have symbol + // turn MOVL of GOT entry into LEAL of symbol itself + if(r->off < 2 || s->p[r->off-2] != 0x8b) { + diag("unexpected GOT reloc for non-dynamic symbol %s", targ->name); + return; + } + s->p[r->off-2] = 0x8d; + r->type = D_PCREL; + return; + } + addgotsym(targ); + r->sym = lookup(".got", 0); + r->add += targ->got; + r->type = D_PCREL; + return; + } + + // Handle references to ELF symbols from our own object files. + if(targ->dynimpname == nil || targ->dynexport) + return; + + switch(r->type) { + case D_PCREL: + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = targ->plt; + return; + + case D_ADDR: + if(s->type != SDATA) + break; + if(iself) { + adddynsym(targ); + rel = lookup(".rel", 0); + addaddrplus(rel, s, r->off); + adduint32(rel, ELF32_R_INFO(targ->dynid, R_386_32)); + r->type = D_CONST; // write r->add during relocsym + r->sym = S; + return; + } + if(HEADTYPE == 6 && s->size == PtrSize && r->off == 0) { + // Mach-O relocations are a royal pain to lay out. + // They use a compact stateful bytecode representation + // that is too much bother to deal with. + // Instead, interpret the C declaration + // void *_Cvar_stderr = &stderr; + // as making _Cvar_stderr the name of a GOT entry + // for stderr. This is separate from the usual GOT entry, + // just in case the C code assigns to the variable, + // and of course it only works for single pointers, + // but we only need to support cgo and that's all it needs. + adddynsym(targ); + got = lookup(".got", 0); + s->type = got->type | SSUB; + s->outer = got; + s->sub = got->sub; + got->sub = s; + s->value = got->size; + adduint32(got, 0); + adduint32(lookup(".linkedit.got", 0), targ->dynid); + r->type = 256; // ignore during relocsym + return; + } + break; + } + + cursym = s; + diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); +} + +static void +elfsetupplt(void) +{ + Sym *plt, *got; + + plt = lookup(".plt", 0); + got = lookup(".got.plt", 0); + if(plt->size == 0) { + // pushl got+4 + adduint8(plt, 0xff); + adduint8(plt, 0x35); + addaddrplus(plt, got, 4); + + // jmp *got+8 + adduint8(plt, 0xff); + adduint8(plt, 0x25); + addaddrplus(plt, got, 8); + + // zero pad + adduint32(plt, 0); + + // assume got->size == 0 too + addaddrplus(got, lookup(".dynamic", 0), 0); + adduint32(got, 0); + adduint32(got, 0); + } +} + +int +archreloc(Reloc *r, Sym *s, vlong *val) +{ + switch(r->type) { + case D_CONST: + *val = r->add; + return 0; + case D_GOTOFF: + *val = symaddr(r->sym) + r->add - symaddr(lookup(".got", 0)); + return 0; + } + return -1; +} + +static void +addpltsym(Sym *s) +{ + Sym *plt, *got, *rel; + + if(s->plt >= 0) + return; + + adddynsym(s); + + if(iself) { + plt = lookup(".plt", 0); + got = lookup(".got.plt", 0); + rel = lookup(".rel.plt", 0); + if(plt->size == 0) + elfsetupplt(); + + // jmpq *got+size + adduint8(plt, 0xff); + adduint8(plt, 0x25); + addaddrplus(plt, got, got->size); + + // add to got: pointer to current pos in plt + addaddrplus(got, plt, plt->size); + + // pushl $x + adduint8(plt, 0x68); + adduint32(plt, rel->size); + + // jmp .plt + adduint8(plt, 0xe9); + adduint32(plt, -(plt->size+4)); + + // rel + addaddrplus(rel, got, got->size-4); + adduint32(rel, ELF32_R_INFO(s->dynid, R_386_JMP_SLOT)); + + s->plt = plt->size - 16; + } else if(HEADTYPE == 6) { // Mach-O + // Same laziness as in 6l. + + Sym *plt; + + plt = lookup(".plt", 0); + + addgotsym(s); + + adduint32(lookup(".linkedit.plt", 0), s->dynid); + + // jmpq *got+size(IP) + s->plt = plt->size; + + adduint8(plt, 0xff); + adduint8(plt, 0x25); + addaddrplus(plt, lookup(".got", 0), s->got); + } else { + diag("addpltsym: unsupported binary format"); + } +} + +static void +addgotsym(Sym *s) +{ + Sym *got, *rel; + + if(s->got >= 0) + return; + + adddynsym(s); + got = lookup(".got", 0); + s->got = got->size; + adduint32(got, 0); + + if(iself) { + rel = lookup(".rel", 0); + addaddrplus(rel, got, s->got); + adduint32(rel, ELF32_R_INFO(s->dynid, R_386_GLOB_DAT)); + } else if(HEADTYPE == 6) { // Mach-O + adduint32(lookup(".linkedit.got", 0), s->dynid); + } else { + diag("addgotsym: unsupported binary format"); + } +} + +void +adddynsym(Sym *s) +{ + Sym *d, *str; + int t; + char *name; + + if(s->dynid >= 0) + return; + + if(s->dynimpname == nil) + diag("adddynsym: no dynamic name for %s", s->name, *(int32*)0); + + if(iself) { + s->dynid = nelfsym++; + + d = lookup(".dynsym", 0); + + /* name */ + name = s->dynimpname; + if(name == nil) + name = s->name; + adduint32(d, addstring(lookup(".dynstr", 0), name)); + + /* value */ + if(s->type == SDYNIMPORT) + adduint32(d, 0); + else + addaddr(d, s); + + /* size */ + adduint32(d, 0); + + /* type */ + t = STB_GLOBAL << 4; + if(s->dynexport && s->type == STEXT) + t |= STT_FUNC; + else + t |= STT_OBJECT; + adduint8(d, t); + adduint8(d, 0); + + /* shndx */ + if(!s->dynexport && s->dynimpname != nil) + adduint16(d, SHN_UNDEF); + else { + switch(s->type) { + default: + case STEXT: + t = 11; + break; + case SRODATA: + t = 12; + break; + case SDATA: + t = 13; + break; + case SBSS: + t = 14; + break; + } + adduint16(d, t); + } + } else if(HEADTYPE == 6) { + // Mach-O symbol nlist32 + d = lookup(".dynsym", 0); + name = s->dynimpname; + if(name == nil) + name = s->name; + s->dynid = d->size/12; + // darwin still puts _ prefixes on all C symbols + str = lookup(".dynstr", 0); + adduint32(d, str->size); + adduint8(str, '_'); + addstring(str, name); + adduint8(d, 0x01); // type - N_EXT - external symbol + adduint8(d, 0); // section + adduint16(d, 0); // desc + adduint32(d, 0); // value + } else { + diag("adddynsym: unsupported binary format"); + } +} + +void +adddynlib(char *lib) +{ + Sym *s; + + if(!needlib(lib)) + return; + + if(iself) { + s = lookup(".dynstr", 0); + if(s->size == 0) + addstring(s, ""); + elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(s, lib)); + } else if(HEADTYPE == 6) { // Mach-O + machoadddynlib(lib); + } else { + diag("adddynlib: unsupported binary format"); + } +} + void doelf(void) { - Sym *s, *shstrtab, *dynamic, *dynstr, *d; - int h, nsym, t; + Sym *s, *shstrtab, *dynstr; if(!iself) return; /* predefine strings we need for section headers */ shstrtab = lookup(".shstrtab", 0); + shstrtab->type = SELFDATA; shstrtab->reachable = 1; + elfstr[ElfStrEmpty] = addstring(shstrtab, ""); elfstr[ElfStrText] = addstring(shstrtab, ".text"); elfstr[ElfStrData] = addstring(shstrtab, ".data"); elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); + addstring(shstrtab, ".elfdata"); + addstring(shstrtab, ".rodata"); if(!debug['s']) { elfstr[ElfStrGosymcounts] = addstring(shstrtab, ".gosymcounts"); elfstr[ElfStrGosymtab] = addstring(shstrtab, ".gosymtab"); elfstr[ElfStrGopclntab] = addstring(shstrtab, ".gopclntab"); + dwarfaddshstrings(shstrtab); } elfstr[ElfStrShstrtab] = addstring(shstrtab, ".shstrtab"); @@ -305,6 +581,8 @@ doelf(void) elfstr[ElfStrDynsym] = addstring(shstrtab, ".dynsym"); elfstr[ElfStrDynstr] = addstring(shstrtab, ".dynstr"); elfstr[ElfStrRel] = addstring(shstrtab, ".rel"); + elfstr[ElfStrRelPlt] = addstring(shstrtab, ".rel.plt"); + elfstr[ElfStrPlt] = addstring(shstrtab, ".plt"); /* interpreter string */ s = lookup(".interp", 0); @@ -315,13 +593,14 @@ doelf(void) s = lookup(".dynsym", 0); s->type = SELFDATA; s->reachable = 1; - s->value += ELF32SYMSIZE; + s->size += ELF32SYMSIZE; /* dynamic string table */ s = lookup(".dynstr", 0); s->reachable = 1; s->type = SELFDATA; - addstring(s, ""); + if(s->size == 0) + addstring(s, ""); dynstr = s; /* relocation table */ @@ -332,88 +611,36 @@ doelf(void) /* global offset table */ s = lookup(".got", 0); s->reachable = 1; + s->type = SDATA; // writable, so not SELFDATA + + /* hash */ + s = lookup(".hash", 0); + s->reachable = 1; s->type = SELFDATA; - /* got.plt - ??? */ + /* got.plt */ s = lookup(".got.plt", 0); s->reachable = 1; + s->type = SDATA; // writable, so not SELFDATA + + s = lookup(".plt", 0); + s->reachable = 1; s->type = SELFDATA; - /* define dynamic elf table */ - s = lookup(".dynamic", 0); + s = lookup(".rel.plt", 0); s->reachable = 1; s->type = SELFDATA; - dynamic = s; - /* - * relocation entries for dynimport symbols - */ - nsym = 1; // sym 0 is reserved - for(h=0; hlink) { - if(!s->reachable || (s->type != STEXT && s->type != SDATA && s->type != SBSS) || s->dynimpname == nil) - continue; - - if(!s->dynexport) { - d = lookup(".rel", 0); - addaddr(d, s); - adduint32(d, ELF32_R_INFO(nsym, R_386_32)); - } - - nsym++; - - d = lookup(".dynsym", 0); - adduint32(d, addstring(lookup(".dynstr", 0), s->dynimpname)); - /* value */ - if(!s->dynexport) - adduint32(d, 0); - else - addaddr(d, s); - - /* size of object */ - adduint32(d, 0); - - /* type */ - t = STB_GLOBAL << 4; - if(s->dynexport && s->type == STEXT) - t |= STT_FUNC; - else - t |= STT_OBJECT; - adduint8(d, t); - - /* reserved */ - adduint8(d, 0); - - /* section where symbol is defined */ - if(!s->dynexport) - adduint16(d, SHN_UNDEF); - else { - switch(s->type) { - default: - case STEXT: - t = 9; - break; - case SDATA: - t = 10; - break; - case SBSS: - t = 11; - break; - } - adduint16(d, t); - } - - if(!s->dynexport && needlib(s->dynimplib)) - elfwritedynent(dynamic, DT_NEEDED, addstring(dynstr, s->dynimplib)); - } - } + elfsetupplt(); - elfdynhash(nsym); + /* define dynamic elf table */ + s = lookup(".dynamic", 0); + s->reachable = 1; + s->type = SELFDATA; /* * .dynamic table */ - s = dynamic; elfwritedynentsym(s, DT_HASH, lookup(".hash", 0)); elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0)); elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE); @@ -424,6 +651,10 @@ doelf(void) elfwritedynent(s, DT_RELENT, ELF32RELSIZE); if(rpath) elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); + elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0)); + 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_NULL, 0); } } @@ -450,155 +681,52 @@ phsh(Elf64_Phdr *ph, Elf64_Shdr *sh) void asmb(void) { - Prog *p; int32 v, magic; int a, dynsym; uint32 va, fo, w, symo, startva, machlink; - uchar *op1; - ulong expectpc; ElfEhdr *eh; ElfPhdr *ph, *pph; ElfShdr *sh; + Section *sect; if(debug['v']) Bprint(&bso, "%5.2f asmb\n", cputime()); Bflush(&bso); - seek(cout, HEADR, 0); - pc = INITTEXT; - curp = firstp; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; - curp = p; - if(HEADTYPE == 8) { - // native client - expectpc = p->pc; - p->pc = pc; - asmins(p); - if(p->pc != expectpc) { - Bflush(&bso); - diag("phase error %lux sb %lux in %s", p->pc, expectpc, TNAME); - } - while(pc < p->pc) { - cput(0x90); // nop - pc++; - } - } - if(p->pc != pc) { - Bflush(&bso); - if(!debug['a']) - print("%P\n", curp); - diag("phase error %lux sb %lux in %s", p->pc, pc, TNAME); - pc = p->pc; - } - if(HEADTYPE != 8) { - asmins(p); - if(pc != p->pc) { - Bflush(&bso); - diag("asmins changed pc %lux sb %lux in %s", p->pc, pc, TNAME); - } - } - if(cbc < sizeof(and)) - cflush(); - a = (andptr - and); - - if(debug['a']) { - Bprint(&bso, pcstr, pc); - for(op1 = and; op1 < andptr; op1++) - Bprint(&bso, "%.2ux", *op1 & 0xff); - Bprint(&bso, "\t%P\n", curp); - } - if(dlm) { - if(p->as == ATEXT) - reloca = nil; - else if(reloca != nil) - diag("reloc failure: %P", curp); - } - memmove(cbp, and, a); - cbp += a; - pc += a; - cbc -= a; - } - if(HEADTYPE == 8) { - while(pc < INITDAT) { - cput(0xf4); // hlt - pc++; - } - } - cflush(); + sect = segtext.sect; + seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); + codeblk(sect->vaddr, sect->len); - switch(HEADTYPE) { - default: - if(iself) - goto Elfseek; - diag("unknown header type %d", HEADTYPE); - case 0: - seek(cout, rnd(HEADR+textsize, 8192), 0); - break; - case 1: - textsize = rnd(HEADR+textsize, 4096)-HEADR; - seek(cout, textsize+HEADR, 0); - break; - case 2: - seek(cout, HEADR+textsize, 0); - break; - case 3: - case 4: - seek(cout, HEADR+rnd(textsize, INITRND), 0); - break; - case 6: - v = HEADR+textsize; - seek(cout, v, 0); - v = rnd(v, 4096) - v; - while(v > 0) { - cput(0); - v--; - } - cflush(); - break; - case 8: - // Native Client only needs to round - // text segment file address to 4096 bytes, - // but text segment memory address rounds - // to INITRND (65536). - v = rnd(HEADR+textsize, 4096); - seek(cout, v, 0); - break; - Elfseek: - case 10: - v = rnd(HEADR+textsize, INITRND); - seek(cout, v, 0); - break; - } + /* output read-only data in text segment */ + sect = segtext.sect->next; + seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); + datblk(sect->vaddr, sect->len); if(debug['v']) Bprint(&bso, "%5.2f datblk\n", cputime()); Bflush(&bso); - if(dlm){ - char buf[8]; - - write(cout, buf, INITDAT-textsize); - textsize = INITDAT; - } - - for(v = 0; v < datsize; v += sizeof(buf)-Dbufslop) { - if(datsize-v > sizeof(buf)-Dbufslop) - datblk(v, sizeof(buf)-Dbufslop); - else - datblk(v, datsize-v); - } + seek(cout, segdata.fileoff, 0); + datblk(segdata.vaddr, segdata.filelen); machlink = 0; if(HEADTYPE == 6) machlink = domacholink(); + if(iself) { + /* index of elf text section; needed by asmelfsym, double-checked below */ + /* !debug['d'] causes extra sections before the .text section */ + elftextsh = 1; + if(!debug['d']) + elftextsh += 10; + } + symsize = 0; spsize = 0; lcsize = 0; symo = 0; if(!debug['s']) { + // TODO: rationalize if(debug['v']) Bprint(&bso, "%5.2f sym\n", cputime()); Bflush(&bso); @@ -607,53 +735,38 @@ asmb(void) if(iself) goto Elfsym; case 0: - seek(cout, rnd(HEADR+textsize, 8192)+datsize, 0); + seek(cout, rnd(HEADR+segtext.filelen, 8192)+segdata.filelen, 0); break; case 1: - seek(cout, rnd(HEADR+textsize, INITRND)+datsize, 0); + seek(cout, rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen, 0); break; case 2: - seek(cout, HEADR+textsize+datsize, 0); + seek(cout, HEADR+segtext.filelen+segdata.filelen, 0); break; case 3: case 4: debug['s'] = 1; - symo = HEADR+textsize+datsize; + symo = HEADR+segtext.filelen+segdata.filelen; break; case 6: - symo = rnd(HEADR+textsize, INITRND)+rnd(datsize, INITRND)+machlink; + symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(segdata.filelen, INITRND)+machlink; break; Elfsym: - case 10: - symo = rnd(HEADR+textsize, INITRND)+datsize; + symo = rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen; symo = rnd(symo, INITRND); break; + case 10: + // TODO(brainman): not sure what symo meant to be, but it is not used for Windows PE for now anyway + symo = rnd(HEADR+segtext.filelen, PEFILEALIGN)+segdata.filelen; + symo = rnd(symo, PEFILEALIGN); + break; + } + if(HEADTYPE != 10 && !debug['s']) { + seek(cout, symo, 0); + if(debug['v']) + Bprint(&bso, "%5.2f dwarf\n", cputime()); + dwarfemitdebugsections(); } - seek(cout, symo+8, 0); - if(!debug['s']) - asmsym(); - if(debug['v']) - Bprint(&bso, "%5.2f sp\n", cputime()); - Bflush(&bso); - if(debug['v']) - Bprint(&bso, "%5.2f pc\n", cputime()); - Bflush(&bso); - if(!debug['s']) - asmlc(); - if(dlm) - asmdyn(); - if(HEADTYPE == 10 || (iself && !debug['s'])) - strnput("", INITRND-(8+symsize+lcsize)%INITRND); - cflush(); - seek(cout, symo, 0); - lputl(symsize); - lputl(lcsize); - cflush(); - } - else if(dlm){ - seek(cout, HEADR+textsize+datsize, 0); - asmdyn(); - cflush(); } if(debug['v']) Bprint(&bso, "%5.2f headr\n", cputime()); @@ -666,17 +779,17 @@ asmb(void) case 0: /* garbage */ lput(0x160L<<16); /* magic and sections */ lput(0L); /* time and date */ - lput(rnd(HEADR+textsize, 4096)+datsize); + lput(rnd(HEADR+segtext.filelen, 4096)+segdata.filelen); lput(symsize); /* nsyms */ lput((0x38L<<16)|7L); /* size of optional hdr and flags */ lput((0413<<16)|0437L); /* magic and version */ - lput(rnd(HEADR+textsize, 4096)); /* sizes */ - lput(datsize); - lput(bsssize); + lput(rnd(HEADR+segtext.filelen, 4096)); /* sizes */ + lput(segdata.filelen); + lput(segdata.len - segdata.filelen); lput(entryvalue()); /* va of entry */ lput(INITTEXT-HEADR); /* va of base of text */ - lput(INITDAT); /* va of base of data */ - lput(INITDAT+datsize); /* va of base of bss */ + lput(segdata.vaddr); /* va of base of data */ + lput(segdata.vaddr+segdata.filelen); /* va of base of bss */ lput(~0L); /* gp reg mask */ lput(0L); lput(0L); @@ -698,19 +811,19 @@ asmb(void) * a.out header */ lputl(0x10b); /* magic, version stamp */ - lputl(rnd(textsize, INITRND)); /* text sizes */ - lputl(datsize); /* data sizes */ - lputl(bsssize); /* bss sizes */ + lputl(rnd(segtext.filelen, INITRND)); /* text sizes */ + lputl(segdata.filelen); /* data sizes */ + lputl(segdata.len - segdata.filelen); /* bss sizes */ lput(entryvalue()); /* va of entry */ lputl(INITTEXT); /* text start */ - lputl(INITDAT); /* data start */ + lputl(segdata.vaddr); /* data start */ /* * text section header */ s8put(".text"); lputl(HEADR); /* pa */ lputl(HEADR); /* va */ - lputl(textsize); /* text size */ + lputl(segtext.filelen); /* text size */ lputl(HEADR); /* file offset */ lputl(0); /* relocation */ lputl(0); /* line numbers */ @@ -720,10 +833,10 @@ asmb(void) * data section header */ s8put(".data"); - lputl(INITDAT); /* pa */ - lputl(INITDAT); /* va */ - lputl(datsize); /* data size */ - lputl(HEADR+textsize); /* file offset */ + lputl(segdata.vaddr); /* pa */ + lputl(segdata.vaddr); /* va */ + lputl(segdata.filelen); /* data size */ + lputl(HEADR+segtext.filelen); /* file offset */ lputl(0); /* relocation */ lputl(0); /* line numbers */ lputl(0); /* relocation, line numbers */ @@ -732,9 +845,9 @@ asmb(void) * bss section header */ s8put(".bss"); - lputl(INITDAT+datsize); /* pa */ - lputl(INITDAT+datsize); /* va */ - lputl(bsssize); /* bss size */ + lputl(segdata.vaddr+segdata.filelen); /* pa */ + lputl(segdata.vaddr+segdata.filelen); /* va */ + lputl(segdata.len - segdata.filelen); /* bss size */ lputl(0); /* file offset */ lputl(0); /* relocation */ lputl(0); /* line numbers */ @@ -747,20 +860,18 @@ asmb(void) lputl(0); /* pa */ lputl(0); /* va */ lputl(symsize+lcsize); /* comment size */ - lputl(HEADR+textsize+datsize); /* file offset */ - lputl(HEADR+textsize+datsize); /* offset of syms */ - lputl(HEADR+textsize+datsize+symsize);/* offset of line numbers */ + lputl(HEADR+segtext.filelen+segdata.filelen); /* file offset */ + lputl(HEADR+segtext.filelen+segdata.filelen); /* offset of syms */ + lputl(HEADR+segtext.filelen+segdata.filelen+symsize);/* offset of line numbers */ lputl(0); /* relocation, line numbers */ lputl(0x200); /* flags comment only */ break; case 2: /* plan9 */ magic = 4*11*11+7; - if(dlm) - magic |= 0x80000000; lput(magic); /* magic */ - lput(textsize); /* sizes */ - lput(datsize); - lput(bsssize); + lput(segtext.filelen); /* sizes */ + lput(segdata.filelen); + lput(segdata.len - segdata.filelen); lput(symsize); /* nsyms */ lput(entryvalue()); /* va of entry */ lput(spsize); /* sp offsets */ @@ -771,7 +882,7 @@ asmb(void) break; case 4: /* fake MS-DOS .EXE */ - v = rnd(HEADR+textsize, INITRND)+datsize; + v = rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen; wputl(0x5A4D); /* 'MZ' */ wputl(v % 512); /* bytes in last page */ wputl(rnd(v, 512)/512); /* total number of pages */ @@ -793,34 +904,31 @@ asmb(void) break; case 6: - asmbmacho(symdatva, symo); + asmbmacho(); break; Elfput: /* elf 386 */ - if(HEADTYPE == 8 || HEADTYPE == 11) + if(HEADTYPE == 11) debug['d'] = 1; eh = getElfEhdr(); fo = HEADR; startva = INITTEXT - HEADR; va = startva + fo; - w = textsize; + w = segtext.filelen; /* This null SHdr must appear before all others */ sh = newElfShdr(elfstr[ElfStrEmpty]); - /* program header info - but not on native client */ - pph = nil; - if(HEADTYPE != 8) { - pph = newElfPhdr(); - pph->type = PT_PHDR; - pph->flags = PF_R + PF_X; - pph->off = eh->ehsize; - pph->vaddr = INITTEXT - HEADR + pph->off; - pph->paddr = INITTEXT - HEADR + pph->off; - pph->align = INITRND; - } + /* program header info */ + pph = newElfPhdr(); + pph->type = PT_PHDR; + pph->flags = PF_R + PF_X; + pph->off = eh->ehsize; + pph->vaddr = INITTEXT - HEADR + pph->off; + pph->paddr = INITTEXT - HEADR + pph->off; + pph->align = INITRND; if(!debug['d']) { /* interpreter */ @@ -843,51 +951,8 @@ asmb(void) phsh(ph, sh); } - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_X+PF_R; - if(HEADTYPE != 8) { // Include header, but not on Native Client. - va -= fo; - w += fo; - fo = 0; - } - ph->vaddr = va; - ph->paddr = va; - ph->off = fo; - ph->filesz = w; - ph->memsz = INITDAT - va; - ph->align = INITRND; - - // NaCl text segment file address rounds to 4096; - // only memory address rounds to INITRND. - if(HEADTYPE == 8) - fo = rnd(fo+w, 4096); - else - fo = rnd(fo+w, INITRND); - va = INITDAT; - w = datsize; - - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_W+PF_R; - ph->off = fo; - ph->vaddr = va; - ph->paddr = va; - ph->filesz = w; - ph->memsz = w+bsssize; - ph->align = INITRND; - - if(!debug['s'] && HEADTYPE != 8 && HEADTYPE != 11) { - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_R; - ph->off = symo; - ph->vaddr = symdatva; - ph->paddr = symdatva; - ph->filesz = rnd(8+symsize+lcsize, INITRND); - ph->memsz = rnd(8+symsize+lcsize, INITRND); - ph->align = INITRND; - } + elfphload(&segtext); + elfphload(&segdata); /* Dynamic linking sections */ if (!debug['d']) { /* -d suppresses dynamic loader format */ @@ -921,6 +986,22 @@ asmb(void) sh->flags = SHF_ALLOC; sh->addralign = 1; shsym(sh, lookup(".dynstr", 0)); + + sh = newElfShdr(elfstr[ElfStrRelPlt]); + sh->type = SHT_REL; + sh->flags = SHF_ALLOC; + sh->entsize = ELF32RELSIZE; + sh->addralign = 4; + sh->link = dynsym; + sh->info = eh->shnum; // .plt + shsym(sh, lookup(".rel.plt", 0)); + + sh = newElfShdr(elfstr[ElfStrPlt]); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_EXECINSTR; + sh->entsize = 4; + sh->addralign = 4; + shsym(sh, lookup(".plt", 0)); sh = newElfShdr(elfstr[ElfStrHash]); sh->type = SHT_HASH; @@ -968,80 +1049,27 @@ asmb(void) ph->flags = PF_W+PF_R; ph->align = 4; - fo = HEADR; - va = startva + fo; - w = textsize; - - sh = newElfShdr(elfstr[ElfStrText]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_EXECINSTR; - sh->addr = va; - sh->off = fo; - sh->size = w; - sh->addralign = 4; - - // NaCl text segment file address rounds to 4096; - // only memory address rounds to INITRND. - if(HEADTYPE == 8) - fo = rnd(fo+w, 4096); - else - fo = rnd(fo+w, INITRND); - va = rnd(va+w, INITRND); - w = datsize; - - sh = newElfShdr(elfstr[ElfStrData]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_WRITE+SHF_ALLOC; - sh->addr = va + elfdatsize; - sh->off = fo + elfdatsize; - sh->size = w - elfdatsize; - sh->addralign = 4; - - fo += w; - va += w; - w = bsssize; - - sh = newElfShdr(elfstr[ElfStrBss]); - sh->type = SHT_NOBITS; - sh->flags = SHF_WRITE+SHF_ALLOC; - sh->addr = va; - sh->off = fo; - sh->size = w; - sh->addralign = 4; + if(elftextsh != eh->shnum) + diag("elftextsh = %d, want %d", elftextsh, eh->shnum); + for(sect=segtext.sect; sect!=nil; sect=sect->next) + elfshbits(sect); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + elfshbits(sect); if (!debug['s']) { - fo = symo; - w = 8; - - sh = newElfShdr(elfstr[ElfStrGosymcounts]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; - sh->addralign = 1; - sh->addr = symdatva; - - fo += w; - w = symsize; - sh = newElfShdr(elfstr[ElfStrGosymtab]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; sh->addralign = 1; - sh->addr = symdatva + 8; - - fo += w; - w = lcsize; + shsym(sh, lookup("symtab", 0)); sh = newElfShdr(elfstr[ElfStrGopclntab]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; sh->addralign = 1; - sh->addr = symdatva + 8 + symsize; + shsym(sh, lookup("pclntab", 0)); + + dwarfaddelfheaders(); } sh = newElfShstrtab(elfstr[ElfStrShstrtab]); @@ -1058,11 +1086,6 @@ asmb(void) eh->ident[EI_DATA] = ELFDATA2LSB; eh->ident[EI_VERSION] = EV_CURRENT; switch(HEADTYPE) { - case 8: - eh->ident[EI_OSABI] = ELFOSABI_NACL; - eh->ident[EI_ABIVERSION] = 7; - eh->flags = 0x200000; // aligned mod 32 - break; case 9: eh->ident[EI_OSABI] = 9; break; @@ -1113,215 +1136,16 @@ cflush(void) n = sizeof(buf.cbuf) - cbc; if(n) - write(cout, buf.cbuf, n); + ewrite(cout, buf.cbuf, n); cbp = buf.cbuf; cbc = sizeof(buf.cbuf); } -void -datblk(int32 s, int32 n) +/* Current position in file */ +vlong +cpos(void) { - Prog *p; - char *cast; - int32 l, fl, j; - int i, c; - Adr *a; - - memset(buf.dbuf, 0, n+Dbufslop); - for(p = datap; p != P; p = p->link) { - a = &p->from; - - l = a->sym->value + a->offset - s; - if(l >= n) - continue; - - c = a->scale; - i = 0; - if(l < 0) { - if(l+c <= 0) - continue; - i = -l; - l = 0; - } - - curp = p; - if(!a->sym->reachable) - diag("unreachable symbol in datblk - %s", a->sym->name); - if(a->sym->type == SMACHO) - continue; - - if(p->as != AINIT && p->as != ADYNT) { - for(j=l+(c-i)-1; j>=l; j--) - if(buf.dbuf[j]) { - print("%P\n", p); - diag("multiple initialization"); - break; - } - } - switch(p->to.type) { - case D_FCONST: - switch(c) { - default: - case 4: - fl = ieeedtof(&p->to.ieee); - cast = (char*)&fl; - for(; ito.ieee; - for(; ito.scon[i]; - l++; - } - break; - - default: - fl = p->to.offset; - if(p->to.type == D_SIZE) - fl += p->to.sym->size; - if(p->to.type == D_ADDR) { - if(p->to.index != D_STATIC && p->to.index != D_EXTERN) - diag("DADDR type%P", p); - if(p->to.sym) { - if(p->to.sym->type == SUNDEF) - ckoff(p->to.sym, fl); - fl += p->to.sym->value; - if(p->to.sym->type != STEXT && p->to.sym->type != SUNDEF) - fl += INITDAT; - if(dlm) - dynreloc(p->to.sym, l+s+INITDAT, 1); - } - } - cast = (char*)&fl; - switch(c) { - default: - diag("bad nuxi %d %d\n%P", c, i, curp); - break; - case 1: - for(; ilink) { - a = &p->from; - - l = a->sym->value + a->offset - s; - if(l < 0 || l >= n) - continue; - - c = a->scale; - i = 0; - - switch(p->to.type) { - case D_FCONST: - switch(c) { - default: - case 4: - fl = ieeedtof(&p->to.ieee); - cast = (char*)&fl; - Bprint(&bso, pcstr, l+s+INITDAT); - for(j=0; jto.ieee; - Bprint(&bso, pcstr, l+s+INITDAT); - for(j=0; jto.scon[j] & 0xff); - Bprint(&bso, "\t%P\n", curp); - break; - - default: - fl = p->to.offset; - if(p->to.type == D_SIZE) - fl += p->to.sym->size; - if(p->to.type == D_ADDR) { - if(p->to.index != D_STATIC && p->to.index != D_EXTERN) - diag("DADDR type%P", p); - if(p->to.sym) { - if(p->to.sym->type == SUNDEF) - ckoff(p->to.sym, fl); - fl += p->to.sym->value; - if(p->to.sym->type != STEXT && p->to.sym->type != SUNDEF) - fl += INITDAT; - if(dlm) - dynreloc(p->to.sym, l+s+INITDAT, 1); - } - } - cast = (char*)&fl; - switch(c) { - default: - diag("bad nuxi %d %d\n%P", c, i, curp); - break; - case 1: - Bprint(&bso, pcstr, l+s+INITDAT); - for(j=0; jtype == STEXT) + put(s, s->name, 'T', s->value, s->size, s->version, 0); + + for(h=0; hhash) { + switch(s->type&~SSUB) { + case SCONST: + case SRODATA: + case SDATA: + case SELFDATA: + case SMACHO: + case SMACHOGOT: + case SWINDOWS: + if(!s->reachable) + continue; + put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype); + continue; + + case SBSS: + if(!s->reachable) + continue; + put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype); + continue; + + case SFILE: + put(nil, s->name, 'f', s->value, 0, s->version, 0); + continue; + } + } + } + + for(s = textp; s != nil; s = s->next) { + if(s->text == nil) + continue; + + /* filenames first */ + for(a=s->autom; a; a=a->link) + if(a->type == D_FILE) + put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0); + else + if(a->type == D_FILE1) + put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0); + + put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); + + /* frame, auto and param after */ + put(nil, ".frame", 'm', s->text->to.offset+4, 0, 0, 0); + + for(a=s->autom; a; a=a->link) + if(a->type == D_AUTO) + put(nil, a->asym->name, 'a', -a->aoffset, 0, 0, a->gotype); + else + if(a->type == D_PARAM) + put(nil, a->asym->name, 'p', a->aoffset, 0, 0, a->gotype); + } + if(debug['v'] || debug['n']) + Bprint(&bso, "symsize = %ud\n", symsize); + Bflush(&bso); +} diff --git a/src/cmd/8l/doc.go b/src/cmd/8l/doc.go index c8c058684..0bf6f151f 100644 --- a/src/cmd/8l/doc.go +++ b/src/cmd/8l/doc.go @@ -29,8 +29,8 @@ Options new in this version: Write Apple Mach-O binaries (default when $GOOS is darwin) -H7 Write Linux ELF binaries (default when $GOOS is linux) --L dir1,dir2,.. - Search for libraries (package files) in the comma-separated list of directories. +-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. diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h index 495c40d64..daede8879 100644 --- a/src/cmd/8l/l.h +++ b/src/cmd/8l/l.h @@ -44,7 +44,7 @@ enum #define P ((Prog*)0) #define S ((Sym*)0) -#define TNAME (curtext?curtext->from.sym->name:noname) +#define TNAME (cursym?cursym->name:noname) #define cput(c)\ { *cbp++ = c;\ if(--cbc <= 0)\ @@ -55,6 +55,7 @@ typedef struct Prog Prog; typedef struct Sym Sym; typedef struct Auto Auto; typedef struct Optab Optab; +typedef struct Reloc Reloc; struct Adr { @@ -66,11 +67,7 @@ struct Adr Ieee u0ieee; char *u0sbig; } u0; - union - { - Auto* u1autom; - Sym* u1sym; - } u1; + Sym* sym; short type; uchar index; char scale; @@ -83,18 +80,25 @@ struct Adr #define ieee u0.u0ieee #define sbig u0.u0sbig -#define autom u1.u1autom -#define sym u1.u1sym +struct Reloc +{ + int32 off; + uchar siz; + int32 type; + int32 add; + Sym* sym; +}; struct Prog { Adr from; Adr to; - Prog *forwd; + Prog* forwd; + Prog* comefrom; Prog* link; - Prog* dlink; Prog* pcond; /* work on this */ int32 pc; + int32 spadj; int32 line; short as; char width; /* fake for DATA */ @@ -103,8 +107,10 @@ struct Prog uchar mark; /* work on these */ uchar back; uchar bigjmp; - }; +#define datasize from.scale +#define textflag from.scale + struct Auto { Sym* asym; @@ -115,25 +121,39 @@ struct Auto }; struct Sym { - char *name; + char* name; short type; short version; - short become; - short frame; - uchar subtype; uchar dupok; uchar reachable; uchar dynexport; + uchar special; int32 value; int32 size; int32 sig; - Sym* link; - Prog* text; - Prog* data; + int32 dynid; + int32 plt; + int32 got; + Sym* hash; // in hash table + Sym* next; // in text or data list + Sym* sub; // in sub list + Sym* outer; // container of sub Sym* gotype; char* file; char* dynimpname; char* dynimplib; + + // STEXT + Auto* autom; + Prog* text; + + // SDATA, SBSS + uchar* p; + int32 np; + int32 maxp; + Reloc* r; + int32 nr; + int32 maxr; }; struct Optab { @@ -146,23 +166,28 @@ struct Optab enum { Sxxx, - + + /* order here is order in output file */ STEXT, + SELFDATA, + SMACHOPLT, + SRODATA, SDATA, + SMACHO, /* Mach-O __nl_symbol_ptr */ + SMACHOGOT, + SWINDOWS, SBSS, - SDATA1, + SXREF, + SMACHODYNSTR, + SMACHODYNSYM, + SMACHOINDIRECTPLT, + SMACHOINDIRECTGOT, SFILE, SCONST, - SUNDEF, - - SIMPORT, - SEXPORT, + SDYNIMPORT, - SMACHO, /* pointer to mach-o imported symbol */ - - SFIXED, - SELFDATA, + SSUB = 1<<8, /* sub-symbol, linked from parent via ->sub list */ NHASH = 10007, NHUNK = 100000, @@ -240,9 +265,6 @@ enum Pm = 0x0f, /* 2byte opcode escape */ Pq = 0xff, /* both escape */ Pb = 0xfe, /* byte operands */ - - Roffset = 22, /* no. bits for offset in relocation address */ - Rindex = 10, /* no. bits for index in relocation address */ }; EXTERN union @@ -266,12 +288,11 @@ EXTERN union EXTERN int32 HEADR; EXTERN int32 HEADTYPE; -EXTERN int32 INITDAT; EXTERN int32 INITRND; EXTERN int32 INITTEXT; +EXTERN int32 INITDAT; EXTERN char* INITENTRY; /* entry point */ EXTERN Biobuf bso; -EXTERN int32 bsssize; EXTERN int32 casepc; EXTERN int cbc; EXTERN char* cbp; @@ -279,22 +300,17 @@ EXTERN char* pcstr; EXTERN Auto* curauto; EXTERN Auto* curhist; EXTERN Prog* curp; -EXTERN Prog* curtext; -EXTERN Prog* datap; -EXTERN Prog* edatap; -EXTERN int32 datsize; +EXTERN Sym* cursym; +EXTERN Sym* datap; EXTERN int32 elfdatsize; -EXTERN int32 dynptrsize; EXTERN char debug[128]; EXTERN char literal[32]; -EXTERN Prog* etextp; +EXTERN Sym* etextp; EXTERN Prog* firstp; -EXTERN int xrefresolv; EXTERN uchar ycover[Ymax*Ymax]; EXTERN uchar* andptr; EXTERN uchar and[100]; EXTERN char reg[D_NONE]; -EXTERN Prog* lastp; EXTERN int32 lcsize; EXTERN int maxop; EXTERN int nerrors; @@ -304,30 +320,22 @@ EXTERN char* rpath; EXTERN int32 spsize; EXTERN Sym* symlist; EXTERN int32 symsize; -EXTERN Prog* textp; +EXTERN Sym* textp; EXTERN int32 textsize; -EXTERN int32 textpad; EXTERN int version; EXTERN Prog zprg; EXTERN int dtype; EXTERN int tlsoffset; EXTERN Sym* adrgotype; // type symbol on last Adr read EXTERN Sym* fromgotype; // type symbol on last p->from read - -EXTERN Adr* reloca; -EXTERN int doexp, dlm; -EXTERN int imports, nimports; -EXTERN int exports, nexports; -EXTERN char* EXPTAB; -EXTERN Prog undefp; - -#define UP (&undefp) +EXTERN int elftextsh; extern Optab optab[]; extern char* anames[]; int Aconv(Fmt*); int Dconv(Fmt*); +int Iconv(Fmt*); int Pconv(Fmt*); int Rconv(Fmt*); int Sconv(Fmt*); @@ -336,29 +344,23 @@ Prog* appendp(Prog*); void asmb(void); void asmdyn(void); void asmins(Prog*); -void asmlc(void); -void asmsp(void); void asmsym(void); int32 atolwhex(char*); Prog* brchain(Prog*); Prog* brloop(Prog*); void cflush(void); -void ckoff(Sym*, int32); Prog* copyp(Prog*); +vlong cpos(void); double cputime(void); -void datblk(int32, int32); void diag(char*, ...); void dodata(void); void doelf(void); -void doinit(void); void doprof1(void); void doprof2(void); void dostkoff(void); -void dynreloc(Sym*, uint32, int); int32 entryvalue(void); -void export(void); void follow(void); -void import(void); +void instinit(void); void listinit(void); Sym* lookup(char*, int); void lput(int32); @@ -366,26 +368,20 @@ void lputl(int32); void vputl(uvlong); void strnput(char*, int); void main(int, char*[]); -void mkfwd(void); void* mal(uint32); -Prog* newdata(Sym*, int, int, int); -Prog* newtext(Prog*, Sym*); int opsize(Prog*); void patch(void); Prog* prg(void); int relinv(int); -int32 reuse(Prog*, Sym*); int32 rnd(int32, int32); void s8put(char*); void span(void); void undef(void); -int32 vaddr(Adr*); int32 symaddr(Sym*); void wput(ushort); void wputl(ushort); void xdefine(char*, int, int32); -void xfol(Prog*); -void zaddr(Biobuf*, Adr*, Sym*[]); + uint32 machheadr(void); vlong addaddr(Sym *s, Sym *t); vlong addsize(Sym *s, Sym *t); @@ -410,3 +406,9 @@ void deadcode(void); #pragma varargck type "P" Prog* #pragma varargck type "R" int #pragma varargck type "A" int + +/* Used by ../ld/dwarf.c */ +enum +{ + DWARFREGSP = 4 +}; diff --git a/src/cmd/8l/list.c b/src/cmd/8l/list.c index a5dbba7f8..4e199d767 100644 --- a/src/cmd/8l/list.c +++ b/src/cmd/8l/list.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Printing. + #include "l.h" #include "../ld/lib.h" @@ -40,6 +42,7 @@ listinit(void) fmtinstall('D', Dconv); fmtinstall('S', Sconv); fmtinstall('P', Pconv); + fmtinstall('I', Iconv); } static Prog *bigP; @@ -47,7 +50,6 @@ static Prog *bigP; int Pconv(Fmt *fp) { - char str[STRINGSZ]; Prog *p; p = va_arg(fp->args, Prog*); @@ -55,23 +57,23 @@ Pconv(Fmt *fp) switch(p->as) { case ATEXT: if(p->from.scale) { - sprint(str, "(%d) %A %D,%d,%D", + fmtprint(fp, "(%d) %A %D,%d,%D", p->line, p->as, &p->from, p->from.scale, &p->to); break; } default: - sprint(str, "(%d) %A %D,%D", + fmtprint(fp, "(%d) %A %D,%D", p->line, p->as, &p->from, &p->to); break; case ADATA: - case AINIT: - case ADYNT: - sprint(str, "(%d) %A %D/%d,%D", + case AINIT_: + case ADYNT_: + fmtprint(fp, "(%d) %A %D/%d,%D", p->line, p->as, &p->from, p->from.scale, &p->to); break; } bigP = P; - return fmtstrcpy(fp, str); + return 0; } int @@ -102,15 +104,15 @@ Dconv(Fmt *fp) i = a->type; if(i >= D_INDIR && i < 2*D_INDIR) { if(a->offset) - sprint(str, "%ld(%R)", a->offset, i-D_INDIR); + snprint(str, sizeof str, "%d(%R)", a->offset, i-D_INDIR); else - sprint(str, "(%R)", i-D_INDIR); + snprint(str, sizeof str, "(%R)", i-D_INDIR); goto brk; } switch(i) { default: - sprint(str, "%R", i); + snprint(str, sizeof str, "%R", i); break; case D_NONE: @@ -120,54 +122,54 @@ Dconv(Fmt *fp) case D_BRANCH: if(bigP != P && bigP->pcond != P) if(a->sym != S) - sprint(str, "%lux+%s", bigP->pcond->pc, + snprint(str, sizeof str, "%ux+%s", bigP->pcond->pc, a->sym->name); else - sprint(str, "%lux", bigP->pcond->pc); + snprint(str, sizeof str, "%ux", bigP->pcond->pc); else - sprint(str, "%ld(PC)", a->offset); + snprint(str, sizeof str, "%d(PC)", a->offset); break; case D_EXTERN: - sprint(str, "%s+%ld(SB)", xsymname(a->sym), a->offset); + snprint(str, sizeof str, "%s+%d(SB)", xsymname(a->sym), a->offset); break; case D_STATIC: - sprint(str, "%s<%d>+%ld(SB)", xsymname(a->sym), + snprint(str, sizeof str, "%s<%d>+%d(SB)", xsymname(a->sym), a->sym->version, a->offset); break; case D_AUTO: - sprint(str, "%s+%ld(SP)", xsymname(a->sym), a->offset); + snprint(str, sizeof str, "%s+%d(SP)", xsymname(a->sym), a->offset); break; case D_PARAM: if(a->sym) - sprint(str, "%s+%ld(FP)", a->sym->name, a->offset); + snprint(str, sizeof str, "%s+%d(FP)", a->sym->name, a->offset); else - sprint(str, "%ld(FP)", a->offset); + snprint(str, sizeof str, "%d(FP)", a->offset); break; case D_CONST: - sprint(str, "$%ld", a->offset); + snprint(str, sizeof str, "$%d", a->offset); break; case D_CONST2: - sprint(str, "$%ld-%ld", a->offset, a->offset2); + snprint(str, sizeof str, "$%d-%d", a->offset, a->offset2); break; case D_FCONST: - sprint(str, "$(%.8lux,%.8lux)", a->ieee.h, a->ieee.l); + snprint(str, sizeof str, "$(%.8ux,%.8ux)", a->ieee.h, a->ieee.l); break; case D_SCONST: - sprint(str, "$\"%S\"", a->scon); + snprint(str, sizeof str, "$\"%S\"", a->scon); break; case D_ADDR: a->type = a->index; a->index = D_NONE; - sprint(str, "$%D", a); + snprint(str, sizeof str, "$%D", a); a->index = a->type; a->type = D_ADDR; goto conv; @@ -316,19 +318,48 @@ Sconv(Fmt *fp) return fmtstrcpy(fp, str); } +int +Iconv(Fmt *fp) +{ + int i, n; + uchar *p; + char *s; + Fmt fmt; + + n = fp->prec; + fp->prec = 0; + if(!(fp->flags&FmtPrec) || n < 0) + return fmtstrcpy(fp, "%I"); + fp->flags &= ~FmtPrec; + p = va_arg(fp->args, uchar*); + + // format into temporary buffer and + // call fmtstrcpy to handle padding. + fmtstrinit(&fmt); + for(i=0; ifrom.sym != S) - tn = curtext->from.sym->name; + tn = ""; + sep = ""; + if(cursym != S) { + tn = cursym->name; + sep = ": "; + } va_start(arg, fmt); vseprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); - print("%s: %s\n", tn, buf); + print("%s%s%s\n", tn, sep, buf); nerrors++; if(nerrors > 20) { diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c index 1a3ecec1d..18b2112fe 100644 --- a/src/cmd/8l/obj.c +++ b/src/cmd/8l/obj.c @@ -28,11 +28,14 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Reading object files. + #define EXTERN #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" #include "../ld/macho.h" +#include "../ld/dwarf.h" #include "../ld/pe.h" #include @@ -52,32 +55,12 @@ char *thestring = "386"; * -H4 -Tx -Rx is fake MS-DOS .EXE * -H6 -Tx -Rx is Apple Mach-O * -H7 -Tx -Rx is Linux ELF32 - * -H8 -Tx -Rx is Google Native Client + * -H8 -Tx -Rx was Google Native Client * -H9 -Tx -Rx is FreeBSD ELF32 + * -H10 -Tx -Rx is MS Windows PE + * -H11 -Tx -Rx is tiny (os image) */ -static int -isobjfile(char *f) -{ - int n, v; - Biobuf *b; - char buf1[5], buf2[SARMAG]; - - b = Bopen(f, OREAD); - if(b == nil) - return 0; - n = Bread(b, buf1, 5); - if(n == 5 && (buf1[2] == 1 && buf1[3] == '<' || buf1[3] == 1 && buf1[4] == '<')) - v = 1; /* good enough for our purposes */ - else{ - Bseek(b, 0, 0); - n = Bread(b, buf2, SARMAG); - v = n == SARMAG && strncmp(buf2, ARMAG, SARMAG) == 0; - } - Bterm(b); - return v; -} - void usage(void) { @@ -88,7 +71,7 @@ usage(void) void main(int argc, char *argv[]) { - int i, c; + int c; Binit(&bso, 1, OWRITE); cout = -1; @@ -152,9 +135,6 @@ main(int argc, char *argv[]) if(strcmp(goos, "darwin") == 0) HEADTYPE = 6; else - if(strcmp(goos, "nacl") == 0) - HEADTYPE = 8; - else if(strcmp(goos, "freebsd") == 0) HEADTYPE = 9; else @@ -163,6 +143,9 @@ main(int argc, char *argv[]) else if(strcmp(goos, "tiny") == 0) HEADTYPE = 11; + else + if(strcmp(goos, "plan9") == 0) + HEADTYPE = 2; else print("goos is not known: %s\n", goos); } @@ -208,7 +191,7 @@ main(int argc, char *argv[]) if(INITDAT == -1) INITDAT = 0; if(INITRND == -1) - INITRND = 4096; + INITRND = 1; break; case 3: /* MS-DOS .COM */ HEADR = 0; @@ -229,7 +212,7 @@ main(int argc, char *argv[]) INITRND = 4; HEADR += (INITTEXT & 0xFFFF); if(debug['v']) - Bprint(&bso, "HEADR = 0x%ld\n", HEADR); + Bprint(&bso, "HEADR = 0x%d\n", HEADR); break; case 6: /* apple MACH */ /* @@ -249,7 +232,7 @@ main(int argc, char *argv[]) case 7: /* elf32 executable */ case 9: /* - * Linux ELF uses TLS offsets negative from %gs. + * 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. @@ -264,30 +247,15 @@ main(int argc, char *argv[]) if(INITRND == -1) INITRND = 4096; break; - case 8: /* native client elf32 executable */ - elfinit(); - HEADR = 4096; - if(INITTEXT == -1) - INITTEXT = 0x20000; - if(INITDAT == -1) - INITDAT = 0; - if(INITRND == -1) - INITRND = 65536; - - // 512 kB of address space for closures. - // (Doesn't take any space in the binary file.) - // Closures are 64 bytes each, so this is 8,192 closures. - textpad = 512*1024; - break; case 10: /* PE executable */ peinit(); - HEADR = PERESERVE; + HEADR = PEFILEHEADR; if(INITTEXT == -1) - INITTEXT = PEBASE+0x1000; + INITTEXT = PEBASE+PESECTHEADR; if(INITDAT == -1) INITDAT = 0; if(INITRND == -1) - INITRND = PEALIGN; + INITRND = PESECTALIGN; break; case 11: tlsoffset = 0; @@ -302,68 +270,14 @@ main(int argc, char *argv[]) break; } if(INITDAT != 0 && INITRND != 0) - print("warning: -D0x%lux is ignored because of -R0x%lux\n", + print("warning: -D0x%ux is ignored because of -R0x%ux\n", INITDAT, INITRND); if(debug['v']) - Bprint(&bso, "HEADER = -H0x%ld -T0x%lux -D0x%lux -R0x%lux\n", + Bprint(&bso, "HEADER = -H0x%d -T0x%ux -D0x%ux -R0x%ux\n", HEADTYPE, INITTEXT, INITDAT, INITRND); Bflush(&bso); - for(i=1; optab[i].as; i++) - if(i != optab[i].as) { - diag("phase error in optab: %d", i); - errorexit(); - } - maxop = i; - - for(i=0; i= D_AL && i <= D_BH) - reg[i] = (i-D_AL) & 7; - if(i >= D_AX && i <= D_DI) - reg[i] = (i-D_AX) & 7; - if(i >= D_F0 && i <= D_F0+7) - reg[i] = (i-D_F0) & 7; - } + instinit(); zprg.link = P; zprg.pcond = P; zprg.back = 2; @@ -373,49 +287,25 @@ main(int argc, char *argv[]) zprg.from.scale = 1; zprg.to = zprg.from; - pcstr = "%.6lux "; + pcstr = "%.6ux "; nuxiinit(); histgen = 0; - textp = P; - datap = P; - edatap = P; pc = 0; dtype = 4; version = 0; cbp = buf.cbuf; cbc = sizeof(buf.cbuf); - firstp = prg(); - lastp = firstp; addlibpath("command line", "command line", argv[0], "main"); loadlib(); - deadcode(); - - firstp = firstp->link; - if(firstp == P) - errorexit(); - if(doexp || dlm){ - EXPTAB = "_exporttab"; - zerosig(EXPTAB); - zerosig("etext"); - zerosig("edata"); - zerosig("end"); - if(dlm){ - import(); - HEADTYPE = 2; - INITTEXT = INITDAT = 0; - INITRND = 8; - INITENTRY = EXPTAB; - } - export(); - } patch(); follow(); doelf(); if(HEADTYPE == 6) domacho(); - dodata(); + if(HEADTYPE == 10) + dope(); dostkoff(); if(debug['p']) if(debug['1']) @@ -423,14 +313,18 @@ main(int argc, char *argv[]) else doprof2(); span(); - doinit(); - if(HEADTYPE == 10) - dope(); + addexport(); + textaddress(); + pclntab(); + symtab(); + dodata(); + address(); + reloc(); asmb(); undef(); if(debug['v']) { Bprint(&bso, "%5.2f cpu time\n", cputime()); - Bprint(&bso, "%ld symbols\n", nsymbol); + Bprint(&bso, "%d symbols\n", nsymbol); Bprint(&bso, "%d sizeof adr\n", sizeof(Adr)); Bprint(&bso, "%d sizeof prog\n", sizeof(Prog)); } @@ -439,8 +333,19 @@ main(int argc, char *argv[]) errorexit(); } -void -zaddr(Biobuf *f, Adr *a, Sym *h[]) +static Sym* +zsym(char *pn, Biobuf *f, Sym *h[]) +{ + int o; + + o = Bgetc(f); + if(o < 0 || o >= NSYM || h[o] == nil) + mangle(pn); + return h[o]; +} + +static void +zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) { int t; int32 l; @@ -465,7 +370,7 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) } a->sym = S; if(t & T_SYM) - a->sym = h[Bgetc(f)]; + a->sym = zsym(pn, f, h); if(t & T_FCONST) { a->ieee.l = Bget4(f); a->ieee.h = Bget4(f); @@ -479,7 +384,7 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) a->type = Bgetc(f); adrgotype = S; if(t & T_GOTYPE) - adrgotype = h[Bgetc(f)]; + adrgotype = zsym(pn, f, h); t = a->type; if(t == D_INDIR+D_GS) @@ -526,7 +431,7 @@ void ldobj1(Biobuf *f, char *pkg, int64 len, char *pn) { int32 ipc; - Prog *p, *t; + Prog *p; int v, o, r, skip; Sym *h[NSYM], *s, *di; uint32 sig; @@ -534,7 +439,9 @@ ldobj1(Biobuf *f, char *pkg, int64 len, char *pn) int32 eof; char *name, *x; char src[1024]; + Prog *lastp; + lastp = nil; ntext = 0; eof = Boffset(f) + len; di = S; @@ -591,7 +498,7 @@ loop: if(sig != 0){ if(s->sig != 0 && s->sig != sig) diag("incompatible type signatures" - "%lux(%s) and %lux(%s) for %s", + "%ux(%s) and %ux(%s) for %s", s->sig, s->file, sig, pn, s->name); s->sig = sig; s->file = pn; @@ -599,6 +506,8 @@ loop: if(debug['W']) print(" ANAME %s\n", s->name); + if(o < 0 || o >= nelem(h)) + mangle(pn); h[o] = s; if((v == D_EXTERN || v == D_STATIC) && s->type == 0) s->type = SXREF; @@ -613,6 +522,7 @@ loop: histfrogp++; } else collapsefrog(s); + dwarfaddfrag(s->value, s->name); } goto loop; } @@ -623,9 +533,9 @@ loop: p->back = 2; p->ft = 0; p->tt = 0; - zaddr(f, &p->from, h); + zaddr(pn, f, &p->from, h); fromgotype = adrgotype; - zaddr(f, &p->to, h); + zaddr(pn, f, &p->to, h); if(debug['W']) print("%P\n", p); @@ -647,10 +557,10 @@ loop: case AEND: histtoauto(); - if(curtext != P) - curtext->to.autom = curauto; + if(cursym != nil && cursym->text) + cursym->autom = curauto; curauto = 0; - curtext = P; + cursym = nil; if(Boffset(f) == eof) return; goto newloop; @@ -659,89 +569,41 @@ loop: s = p->from.sym; if(s->type == 0 || s->type == SXREF) { s->type = SBSS; - s->value = 0; + s->size = 0; } - if(s->type != SBSS) { + if(s->type != SBSS && !s->dupok) { diag("%s: redefinition: %s in %s", pn, s->name, TNAME); s->type = SBSS; - s->value = 0; + s->size = 0; } - if(p->to.offset > s->value) - s->value = p->to.offset; + if(p->to.offset > s->size) + s->size = p->to.offset; if(p->from.scale & DUPOK) s->dupok = 1; + if(p->from.scale & RODATA) + s->type = SRODATA; goto loop; - case ADYNT: - if(p->to.sym == S) { - diag("DYNT without a sym\n%P", p); - break; - } - di = p->to.sym; - p->from.scale = 4; - if(di->type == SXREF) { - if(debug['z']) - Bprint(&bso, "%P set to %d\n", p, dtype); - di->type = SCONST; - di->value = dtype; - dtype += 4; - } - if(p->from.sym == S) - break; - - p->from.offset = di->value; - p->from.sym->type = SDATA; - if(curtext == P) { - diag("DYNT not in text: %P", p); - break; - } - p->to.sym = curtext->from.sym; - p->to.type = D_ADDR; - p->to.index = D_EXTERN; - goto data; - - case AINIT: - if(p->from.sym == S) { - diag("INIT without a sym\n%P", p); - break; - } - if(di == S) { - diag("INIT without previous DYNT\n%P", p); - break; - } - p->from.offset = di->value; - p->from.sym->type = SDATA; - goto data; - case ADATA: - data: // Assume that AGLOBL comes after ADATA. // If we've seen an AGLOBL that said this sym was DUPOK, // ignore any more ADATA we see, which must be // redefinitions. s = p->from.sym; - if(s != S && s->dupok) { + if(s->dupok) { // if(debug['v']) // Bprint(&bso, "skipping %s in %s: dupok\n", s->name, pn); goto loop; } - if(s != S) { - p->dlink = s->data; - s->data = p; - if(s->file == nil) - s->file = pn; - else if(s->file != pn) { - diag("multiple initialization for %s: in both %s and %s", s->name, s->file, pn); - errorexit(); - } + if(s->file == nil) + s->file = pn; + else if(s->file != pn) { + diag("multiple initialization for %s: in both %s and %s", s->name, s->file, pn); + errorexit(); } - if(edatap == P) - datap = p; - else - edatap->link = p; - edatap = p; - p->link = P; + savedata(s, p); + unmal(p, sizeof *p); goto loop; case AGOK: @@ -751,9 +613,9 @@ loop: case ATEXT: s = p->from.sym; - if(s == S) { - diag("%s: no TEXT symbol: %P", pn, p); - errorexit(); + if(s->text != nil) { + diag("%s: %s: redefinition", pn, s->name); + return; } if(ntext++ == 0 && s->type != 0 && s->type != SXREF) { /* redefinition, so file has probably been seen before */ @@ -761,13 +623,19 @@ loop: diag("skipping: %s: redefinition: %s", pn, s->name); return; } - if(curtext != P) { + if(cursym != nil && cursym->text) { histtoauto(); - curtext->to.autom = curauto; + cursym->autom = curauto; curauto = 0; } skip = 0; - curtext = p; + if(etextp) + etextp->next = s; + else + textp = s; + etextp = s; + s->text = p; + cursym = s; if(s->type != 0 && s->type != SXREF) { if(p->from.scale & DUPOK) { skip = 1; @@ -775,7 +643,10 @@ loop: } diag("%s: redefinition: %s\n%P", pn, s->name, p); } - newtext(p, s); + s->type = STEXT; + s->value = pc; + lastp = p; + p->pc = pc++; goto loop; case AFMOVF: @@ -791,24 +662,12 @@ loop: goto casdef; if(p->from.type == D_FCONST) { /* size sb 9 max */ - sprint(literal, "$%lux", ieeedtof(&p->from.ieee)); + sprint(literal, "$%ux", ieeedtof(&p->from.ieee)); s = lookup(literal, 0); if(s->type == 0) { - s->type = SBSS; - s->value = 4; - t = prg(); - t->as = ADATA; - t->line = p->line; - t->from.type = D_EXTERN; - t->from.sym = s; - t->from.scale = 4; - t->to = p->from; - if(edatap == P) - datap = t; - else - edatap->link = t; - edatap = t; - t->link = P; + s->type = SDATA; + adduint32(s, ieeedtof(&p->from.ieee)); + s->reachable = 0; } p->from.type = D_EXTERN; p->from.sym = s; @@ -829,25 +688,14 @@ loop: goto casdef; if(p->from.type == D_FCONST) { /* size sb 18 max */ - sprint(literal, "$%lux.%lux", + sprint(literal, "$%ux.%ux", p->from.ieee.l, p->from.ieee.h); s = lookup(literal, 0); if(s->type == 0) { - s->type = SBSS; - s->value = 8; - t = prg(); - t->as = ADATA; - t->line = p->line; - t->from.type = D_EXTERN; - t->from.sym = s; - t->from.scale = 8; - t->to = p->from; - if(edatap == P) - datap = t; - else - edatap->link = t; - edatap = t; - t->link = P; + s->type = SDATA; + adduint32(s, p->from.ieee.l); + adduint32(s, p->from.ieee.h); + s->reachable = 0; } p->from.type = D_EXTERN; p->from.sym = s; @@ -859,13 +707,18 @@ loop: default: if(skip) nopout(p); + p->pc = pc; + pc++; if(p->to.type == D_BRANCH) p->to.offset += ipc; + if(lastp == nil) { + if(p->as != ANOP) + diag("unexpected instruction: %P", p); + goto loop; + } lastp->link = p; lastp = p; - p->pc = pc; - pc++; goto loop; } goto loop; @@ -905,153 +758,3 @@ appendp(Prog *q) p->line = q->line; return p; } - -void -doprof1(void) -{ - Sym *s; - int32 n; - Prog *p, *q; - - if(debug['v']) - Bprint(&bso, "%5.2f profile 1\n", cputime()); - Bflush(&bso); - s = lookup("__mcount", 0); - n = 1; - for(p = firstp->link; p != P; p = p->link) { - if(p->as == ATEXT) { - q = prg(); - q->line = p->line; - q->link = datap; - datap = q; - q->as = ADATA; - q->from.type = D_EXTERN; - q->from.offset = n*4; - q->from.sym = s; - q->from.scale = 4; - q->to = p->from; - q->to.type = D_CONST; - - q = prg(); - q->line = p->line; - q->pc = p->pc; - q->link = p->link; - p->link = q; - p = q; - p->as = AADDL; - p->from.type = D_CONST; - p->from.offset = 1; - p->to.type = D_EXTERN; - p->to.sym = s; - p->to.offset = n*4 + 4; - - n += 2; - continue; - } - } - q = prg(); - q->line = 0; - q->link = datap; - datap = q; - - q->as = ADATA; - q->from.type = D_EXTERN; - q->from.sym = s; - q->from.scale = 4; - q->to.type = D_CONST; - q->to.offset = n; - - s->type = SBSS; - s->value = n*4; -} - -void -doprof2(void) -{ - Sym *s2, *s4; - Prog *p, *q, *ps2, *ps4; - - if(debug['v']) - Bprint(&bso, "%5.2f profile 2\n", cputime()); - Bflush(&bso); - - s2 = lookup("_profin", 0); - s4 = lookup("_profout", 0); - if(s2->type != STEXT || s4->type != STEXT) { - diag("_profin/_profout not defined"); - return; - } - - ps2 = P; - ps4 = P; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - if(p->from.sym == s2) { - p->from.scale = 1; - ps2 = p; - } - if(p->from.sym == s4) { - p->from.scale = 1; - ps4 = p; - } - } - } - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - curtext = p; - - if(p->from.scale & NOPROF) { /* dont profile */ - for(;;) { - q = p->link; - if(q == P) - break; - if(q->as == ATEXT) - break; - p = q; - } - continue; - } - - /* - * JMPL profin - */ - q = prg(); - q->line = p->line; - q->pc = p->pc; - q->link = p->link; - p->link = q; - p = q; - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = ps2; - p->to.sym = s2; - - continue; - } - if(p->as == ARET) { - /* - * RET - */ - q = prg(); - q->as = ARET; - q->from = p->from; - q->to = p->to; - q->link = p->link; - p->link = q; - - /* - * JAL profout - */ - p->as = ACALL; - p->from = zprg.from; - p->to = zprg.to; - p->to.type = D_BRANCH; - p->pcond = ps4; - p->to.sym = s4; - - p = q; - - continue; - } - } -} diff --git a/src/cmd/8l/optab.c b/src/cmd/8l/optab.c index 5b7be692e..fceab785d 100644 --- a/src/cmd/8l/optab.c +++ b/src/cmd/8l/optab.c @@ -696,8 +696,8 @@ Optab optab[] = { AFYL2X, ynone, Px, 0xd9, 0xf1 }, { AFYL2XP1, ynone, Px, 0xd9, 0xf9 }, { AEND }, - { ADYNT }, - { AINIT }, + { ADYNT_ }, + { AINIT_ }, { ASIGNAME }, { ACMPXCHGB, yrb_mb, Pm, 0xb0 }, { ACMPXCHGL, yrl_ml, Pm, 0xb1 }, diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c index ace640d22..6e387b0b5 100644 --- a/src/cmd/8l/pass.c +++ b/src/cmd/8l/pass.c @@ -28,9 +28,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Code and data passes. + #include "l.h" #include "../ld/lib.h" +static void xfol(Prog*, Prog**); + // see ../../pkg/runtime/proc.c:/StackGuard enum { @@ -38,138 +42,6 @@ enum StackBig = 4096, }; -void -dodata(void) -{ - int i; - Sym *s; - Prog *p; - int32 t, u; - - if(debug['v']) - Bprint(&bso, "%5.2f dodata\n", cputime()); - Bflush(&bso); - for(p = datap; p != P; p = p->link) { - s = p->from.sym; - if(p->as == ADYNT || p->as == AINIT) - s->value = dtype; - if(s->type == SBSS) - s->type = SDATA; - if(s->type != SDATA && s->type != SELFDATA) - diag("initialize non-data (%d): %s\n%P", - s->type, s->name, p); - t = p->from.offset + p->width; - if(t > s->value) - diag("initialize bounds (%ld): %s\n%P", - s->value, s->name, p); - } - - /* allocate elf guys - must be segregated from real data */ - datsize = 0; - for(i=0; ilink) { - if(!s->reachable) - continue; - if(s->type != SELFDATA) - continue; - t = rnd(s->value, 4); - s->size = t; - s->value = datsize; - datsize += t; - } - elfdatsize = datsize; - - /* allocate small guys */ - for(i=0; ilink) { - if(!s->reachable) - continue; - if(s->type != SDATA) - if(s->type != SBSS) - continue; - t = s->value; - if(t == 0 && s->name[0] != '.') { - diag("%s: no size", s->name); - t = 1; - } - t = rnd(t, 4); - s->size = t; - s->value = t; - if(t > MINSIZ) - continue; - s->value = datsize; - datsize += t; - s->type = SDATA1; - } - - /* allocate the rest of the data */ - for(i=0; ilink) { - if(s->type != SDATA) { - if(s->type == SDATA1) - s->type = SDATA; - continue; - } - t = s->value; - s->size = t; - s->value = datsize; - datsize += t; - } - - if(debug['j']) { - /* - * pad data with bss that fits up to next - * 8k boundary, then push data to 8k - */ - u = rnd(datsize, 8192); - u -= datsize; - for(i=0; ilink) { - if(!s->reachable) - continue; - if(s->type != SBSS) - continue; - t = s->value; - if(t > u) - continue; - u -= t; - s->size = t; - s->value = datsize; - s->type = SDATA; - datsize += t; - } - datsize += u; - } - - if(dynptrsize > 0) { - /* dynamic pointer section between data and bss */ - datsize = rnd(datsize, 4); - } - - /* now the bss */ - bsssize = 0; - for(i=0; ilink) { - if(!s->reachable) - continue; - if(s->type != SBSS) - continue; - t = s->value; - s->size = t; - s->value = bsssize + dynptrsize + datsize; - bsssize += t; - } - - xdefine("data", SBSS, 0); - xdefine("edata", SBSS, datsize); - xdefine("end", SBSS, dynptrsize + bsssize + datsize); - - if(debug['s'] || HEADTYPE == 8) - xdefine("symdat", SFIXED, 0); - else - xdefine("symdat", SFIXED, SYMDATVA); -} - Prog* brchain(Prog *p) { @@ -186,19 +58,53 @@ brchain(Prog *p) void follow(void) { + Prog *firstp, *lastp; if(debug['v']) Bprint(&bso, "%5.2f follow\n", cputime()); Bflush(&bso); - firstp = prg(); - lastp = firstp; - xfol(textp); - lastp->link = P; - firstp = firstp->link; + + for(cursym = textp; cursym != nil; cursym = cursym->next) { + firstp = prg(); + lastp = firstp; + xfol(cursym->text, &lastp); + lastp->link = nil; + cursym->text = firstp->link; + } } -void -xfol(Prog *p) +static int +nofollow(int a) +{ + switch(a) { + case AJMP: + case ARET: + case AIRETL: + case AIRETW: + return 1; + } + return 0; +} + +static int +pushpop(int a) +{ + switch(a) { + case APUSHL: + case APUSHFL: + case APUSHW: + case APUSHFW: + case APOPL: + case APOPFL: + case APOPW: + case APOPFW: + return 1; + } + return 0; +} + +static void +xfol(Prog *p, Prog **last) { Prog *q; int i; @@ -207,46 +113,31 @@ xfol(Prog *p) loop: if(p == P) return; - if(p->as == ATEXT) - curtext = p; - if(!curtext->from.sym->reachable) { - p = p->pcond; - goto loop; - } if(p->as == AJMP) if((q = p->pcond) != P && q->as != ATEXT) { + /* mark instruction as done and continue layout at target of jump */ p->mark = 1; p = q; if(p->mark == 0) goto loop; } if(p->mark) { - /* copy up to 4 instructions to avoid branch */ + /* + * p goes here, but already used it elsewhere. + * copy up to 4 instructions or else branch to other copy. + */ for(i=0,q=p; i<4; i++,q=q->link) { if(q == P) break; - if(q == lastp) + if(q == *last) break; a = q->as; if(a == ANOP) { i--; continue; } - switch(a) { - case AJMP: - case ARET: - case AIRETL: - - case APUSHL: - case APUSHFL: - case APUSHW: - case APUSHFW: - case APOPL: - case APOPFL: - case APOPW: - case APOPFW: - goto brk; - } + if(nofollow(a) || pushpop(a)) + break; // NOTE(rsc): arm does goto copy if(q->pcond == P || q->pcond->mark) continue; if(a == ACALL || a == ALOOP) @@ -259,8 +150,8 @@ loop: q = copyp(p); p = p->link; q->mark = 1; - lastp->link = q; - lastp = q; + (*last)->link = q; + *last = q; if(q->as != a || q->pcond == P || q->pcond->mark) continue; @@ -268,14 +159,13 @@ loop: p = q->pcond; q->pcond = q->link; q->link = p; - xfol(q->link); + xfol(q->link, last); p = q->link; if(p->mark) return; goto loop; } } /* */ - brk:; q = prg(); q->as = AJMP; q->line = p->line; @@ -284,14 +174,22 @@ loop: q->pcond = p; p = q; } + + /* emit p */ p->mark = 1; - lastp->link = p; - lastp = p; + (*last)->link = p; + *last = p; a = p->as; - if(a == AJMP || a == ARET || a == AIRETL) + + /* continue loop with what comes after p */ + if(nofollow(a)) return; - if(p->pcond != P) - if(a != ACALL) { + if(p->pcond != P && a != ACALL) { + /* + * some kind of conditional branch. + * recurse to follow one path. + * continue loop on the other. + */ q = brchain(p->link); if(q != P && q->mark) if(a != ALOOP) { @@ -299,7 +197,7 @@ loop: p->link = p->pcond; p->pcond = q; } - xfol(p->link); + xfol(p->link, last); q = brchain(p->pcond); if(q->mark) { p->pcond = q; @@ -338,28 +236,6 @@ relinv(int a) return a; } -void -doinit(void) -{ - Sym *s; - Prog *p; - int x; - - for(p = datap; p != P; p = p->link) { - x = p->to.type; - if(x != D_EXTERN && x != D_STATIC) - continue; - s = p->to.sym; - if(s->type == 0 || s->type == SXREF) - diag("undefined %s initializer of %s", - s->name, p->from.sym->name); - p->to.offset += s->value; - p->to.type = D_CONST; - if(s->type == SDATA || s->type == SBSS) - p->to.offset += INITDAT; - } -} - void patch(void) { @@ -377,116 +253,106 @@ patch(void) Bflush(&bso); s = lookup("exit", 0); vexit = s->value; - for(p = firstp; p != P; p = p->link) { - if(HEADTYPE == 10) { - // Convert - // op n(GS), reg - // to - // MOVL 0x2C(FS), reg - // op n(reg), reg - // The purpose of this patch is to fix some accesses - // to extern register variables (TLS) on Windows, as - // a different method is used to access them. - if(p->from.type == D_INDIR+D_GS - && p->to.type >= D_AX && p->to.type <= D_DI) { - q = appendp(p); - q->from = p->from; - q->from.type += p->to.type-D_GS; - q->to = p->to; - q->as = p->as; - p->as = AMOVL; - p->from.type = D_INDIR+D_FS; - p->from.offset = 0x2C; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + for(p = cursym->text; p != P; p = p->link) { + if(HEADTYPE == 10) { // Windows + // Convert + // op n(GS), reg + // to + // MOVL 0x2C(FS), reg + // op n(reg), reg + // The purpose of this patch is to fix some accesses + // to extern register variables (TLS) on Windows, as + // a different method is used to access them. + if(p->from.type == D_INDIR+D_GS + && p->to.type >= D_AX && p->to.type <= D_DI) { + q = appendp(p); + q->from = p->from; + q->from.type = D_INDIR + p->to.type; + q->to = p->to; + q->as = p->as; + p->as = AMOVL; + p->from.type = D_INDIR+D_FS; + p->from.offset = 0x2C; + } } - } - if(p->as == ATEXT) - curtext = p; - if(p->as == ACALL || (p->as == AJMP && p->to.type != D_BRANCH)) { - s = p->to.sym; - if(s) { - if(debug['c']) - Bprint(&bso, "%s calls %s\n", TNAME, s->name); - switch(s->type) { - default: - /* diag prints TNAME first */ - diag("undefined: %s", s->name); - s->type = STEXT; - s->value = vexit; - continue; // avoid more error messages - case STEXT: - p->to.offset = s->value; - break; - case SUNDEF: - p->pcond = UP; - p->to.offset = 0; - break; + if(HEADTYPE == 7) { // Linux + // Running binaries under Xen requires using + // MOVL 0(GS), reg + // and then off(reg) instead of saying off(GS) directly + // when the offset is negative. + if(p->from.type == D_INDIR+D_GS && p->from.offset < 0 + && p->to.type >= D_AX && p->to.type <= D_DI) { + q = appendp(p); + q->from = p->from; + q->from.type = D_INDIR + p->to.type; + q->to = p->to; + q->as = p->as; + p->as = AMOVL; + p->from.type = D_INDIR+D_GS; + p->from.offset = 0; } - p->to.type = D_BRANCH; } - } - if(p->to.type != D_BRANCH || p->pcond == UP) - continue; - c = p->to.offset; - for(q = firstp; q != P;) { - if(q->forwd != P) - if(c >= q->forwd->pc) { - q = q->forwd; + if(HEADTYPE == 2) { // Plan 9 + if(p->from.type == D_INDIR+D_GS + && p->to.type >= D_AX && p->to.type <= D_DI) { + p->as = AMOVL; + p->from.type = D_ADDR+D_STATIC; + p->from.offset += 0xdfffefc0; + } + } + if(p->as == ACALL || (p->as == AJMP && p->to.type != D_BRANCH)) { + s = p->to.sym; + if(s) { + if(debug['c']) + Bprint(&bso, "%s calls %s\n", TNAME, s->name); + if((s->type&~SSUB) != STEXT) { + /* diag prints TNAME first */ + diag("undefined: %s", s->name); + s->type = STEXT; + s->value = vexit; + continue; // avoid more error messages + } + if(s->text == nil) + continue; + p->to.type = D_BRANCH; + p->to.offset = s->text->pc; + p->pcond = s->text; + continue; + } + } + if(p->to.type != D_BRANCH) continue; + c = p->to.offset; + for(q = cursym->text; q != P;) { + if(c == q->pc) + break; + if(q->forwd != P && c >= q->forwd->pc) + q = q->forwd; + else + q = q->link; } - if(c == q->pc) - break; - q = q->link; - } - if(q == P) { - diag("branch out of range in %s\n%P", TNAME, p); - p->to.type = D_NONE; + if(q == P) { + diag("branch out of range in %s (%#ux)\n%P [%s]", + TNAME, c, p, p->to.sym ? p->to.sym->name : ""); + p->to.type = D_NONE; + } + p->pcond = q; } - p->pcond = q; } - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; - p->mark = 0; /* initialization for follow */ - if(p->pcond != P && p->pcond != UP) { - p->pcond = brloop(p->pcond); - if(p->pcond != P) - if(p->to.type == D_BRANCH) - p->to.offset = p->pcond->pc; - } - } -} + for(cursym = textp; cursym != nil; cursym = cursym->next) { + if(cursym->text == nil || cursym->p != nil) + continue; -#define LOG 5 -void -mkfwd(void) -{ - Prog *p; - int i; - int32 dwn[LOG], cnt[LOG]; - Prog *lst[LOG]; - - for(i=0; ilink) { - if(p->as == ATEXT) - curtext = p; - i--; - if(i < 0) - i = LOG-1; - p->forwd = P; - dwn[i]--; - if(dwn[i] <= 0) { - dwn[i] = cnt[i]; - if(lst[i] != P) - lst[i]->forwd = p; - lst[i] = p; + for(p = cursym->text; p != P; p = p->link) { + p->mark = 0; /* initialization for follow */ + if(p->pcond != P) { + p->pcond = brloop(p->pcond); + if(p->pcond != P) + if(p->to.type == D_BRANCH) + p->to.offset = p->pcond->pc; + } } } } @@ -513,286 +379,221 @@ dostkoff(void) { Prog *p, *q, *q1; int32 autoffset, deltasp; - int a, f, curframe, curbecome, maxbecome; + int a; Prog *pmorestack; Sym *symmorestack; pmorestack = P; symmorestack = lookup("runtime.morestack", 0); - if(symmorestack->type == STEXT) - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - if(p->from.sym == symmorestack) { - pmorestack = p; - p->from.scale |= NOSPLIT; - break; - } - } - } - if(pmorestack == P) + if(symmorestack->type != STEXT) diag("runtime.morestack not defined"); + else { + pmorestack = symmorestack->text; + symmorestack->text->from.scale |= NOSPLIT; + } - curframe = 0; - curbecome = 0; - maxbecome = 0; - curtext = 0; - for(p = firstp; p != P; p = p->link) { - - /* find out how much arg space is used in this TEXT */ - if(p->to.type == (D_INDIR+D_SP)) - if(p->to.offset > curframe) - curframe = p->to.offset; - - switch(p->as) { - case ATEXT: - if(curtext && curtext->from.sym) { - curtext->from.sym->frame = curframe; - curtext->from.sym->become = curbecome; - if(curbecome > maxbecome) - maxbecome = curbecome; - } - curframe = 0; - curbecome = 0; - - curtext = p; - break; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + if(cursym->text == nil || cursym->text->link == nil) + continue; - case ARET: - /* special form of RET is BECOME */ - if(p->from.type == D_CONST) - if(p->from.offset > curbecome) - curbecome = p->from.offset; - break; - } - } - if(curtext && curtext->from.sym) { - curtext->from.sym->frame = curframe; - curtext->from.sym->become = curbecome; - if(curbecome > maxbecome) - maxbecome = curbecome; - } + p = cursym->text; + autoffset = p->to.offset; + if(autoffset < 0) + autoffset = 0; + + q = P; + q1 = P; + if(pmorestack != P) + if(!(p->from.scale & NOSPLIT)) { + p = appendp(p); // load g into CX + switch(HEADTYPE) { + case 10: // Windows + p->as = AMOVL; + p->from.type = D_INDIR+D_FS; + p->from.offset = 0x2c; + p->to.type = D_CX; - if(debug['b']) - print("max become = %d\n", maxbecome); - xdefine("ALEFbecome", STEXT, maxbecome); + p = appendp(p); + p->as = AMOVL; + p->from.type = D_INDIR+D_CX; + p->from.offset = 0; + p->to.type = D_CX; + break; + + case 7: // Linux + p->as = AMOVL; + p->from.type = D_INDIR+D_GS; + p->from.offset = 0; + p->to.type = D_CX; - curtext = 0; - for(p = firstp; p != P; p = p->link) { - switch(p->as) { - case ATEXT: - curtext = p; - break; - case ACALL: - if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) { - f = maxbecome - curtext->from.sym->frame; - if(f <= 0) - break; - /* calling a become or calling a variable */ - if(p->to.sym == S || p->to.sym->become) { - curtext->to.offset += f; - if(debug['b']) { - curp = p; - print("%D calling %D increase %d\n", - &curtext->from, &p->to, f); - } - } + p = appendp(p); + p->as = AMOVL; + p->from.type = D_INDIR+D_CX; + p->from.offset = tlsoffset + 0; + p->to.type = D_CX; + break; + + case 2: // Plan 9 + p->as = AMOVL; + p->from.type = D_ADDR+D_STATIC; + p->from.offset = 0xdfffefc0; + p->to.type = D_CX; + break; + + default: + p->as = AMOVL; + p->from.type = D_INDIR+D_GS; + p->from.offset = tlsoffset + 0; + p->to.type = D_CX; } - break; - } - } - autoffset = 0; - deltasp = 0; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - curtext = p; - autoffset = p->to.offset; - if(autoffset < 0) - autoffset = 0; - - q = P; - q1 = P; - if(pmorestack != P) - if(!(p->from.scale & NOSPLIT)) { - p = appendp(p); // load g into CX - if(HEADTYPE == 10) { - p->as = AMOVL; - p->from.type = D_INDIR+D_FS; - p->from.offset = 0x2c; - p->to.type = D_CX; + if(debug['K']) { + // 8l -K means check not only for stack + // overflow but stack underflow. + // On underflow, INT 3 (breakpoint). + // Underflow itself is rare but this also + // catches out-of-sync stack guard info. + p = appendp(p); + p->as = ACMPL; + p->from.type = D_INDIR+D_CX; + p->from.offset = 4; + p->to.type = D_SP; - p = appendp(p); - p->as = AMOVL; - p->from.type = D_INDIR+D_CX; - p->from.offset = 0; - p->to.type = D_CX; - } else { - p->as = AMOVL; - p->from.type = D_INDIR+D_GS; - p->from.offset = tlsoffset + 0; - p->to.type = D_CX; - } + p = appendp(p); + p->as = AJCC; + p->to.type = D_BRANCH; + p->to.offset = 4; + q1 = p; - if(debug['K']) { - // 8l -K means check not only for stack - // overflow but stack underflow. - // On underflow, INT 3 (breakpoint). - // Underflow itself is rare but this also - // catches out-of-sync stack guard info. - p = appendp(p); - p->as = ACMPL; - p->from.type = D_INDIR+D_CX; - p->from.offset = 4; - p->to.type = D_SP; + p = appendp(p); + p->as = AINT; + p->from.type = D_CONST; + p->from.offset = 3; + + p = appendp(p); + p->as = ANOP; + q1->pcond = p; + } + if(autoffset < StackBig) { // do we need to call morestack + if(autoffset <= StackSmall) { + // small stack p = appendp(p); - p->as = AJCC; - p->to.type = D_BRANCH; - p->to.offset = 4; - q1 = p; - + p->as = ACMPL; + p->from.type = D_SP; + p->to.type = D_INDIR+D_CX; + } else { + // large stack p = appendp(p); - p->as = AINT; - p->from.type = D_CONST; - p->from.offset = 3; - } - - if(autoffset < StackBig) { // do we need to call morestack - if(autoffset <= StackSmall) { - // small stack - p = appendp(p); - p->as = ACMPL; - p->from.type = D_SP; - p->to.type = D_INDIR+D_CX; - if(q1) { - q1->pcond = p; - q1 = P; - } - } else { - // large stack - p = appendp(p); - p->as = ALEAL; - p->from.type = D_INDIR+D_SP; - p->from.offset = -(autoffset-StackSmall); - p->to.type = D_AX; - if(q1) { - q1->pcond = p; - q1 = P; - } - - p = appendp(p); - p->as = ACMPL; - p->from.type = D_AX; - p->to.type = D_INDIR+D_CX; - } + p->as = ALEAL; + p->from.type = D_INDIR+D_SP; + p->from.offset = -(autoffset-StackSmall); + p->to.type = D_AX; - // common p = appendp(p); - p->as = AJHI; - p->to.type = D_BRANCH; - p->to.offset = 4; - q = p; + p->as = ACMPL; + p->from.type = D_AX; + p->to.type = D_INDIR+D_CX; } - 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 > 4096) - p->from.offset = (autoffset+160) & ~7LL; - - p = appendp(p); // save arg size in AX - p->as = AMOVL; - p->to.type = D_AX; - p->from.type = D_CONST; - p->from.offset = curtext->to.offset2; - + // common p = appendp(p); - p->as = ACALL; + p->as = AJHI; p->to.type = D_BRANCH; - p->pcond = pmorestack; - p->to.sym = symmorestack; - + p->to.offset = 4; + q = p; } - if(q != P) - q->pcond = p->link; + 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 > 4096) + p->from.offset = (autoffset+160) & ~7LL; + + p = appendp(p); // save arg size in AX + p->as = AMOVL; + p->to.type = D_AX; + p->from.type = D_CONST; + p->from.offset = cursym->text->to.offset2; + + p = appendp(p); + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = pmorestack; + p->to.sym = symmorestack; - if(autoffset) { - p = appendp(p); - p->as = AADJSP; - p->from.type = D_CONST; - p->from.offset = autoffset; - if(q != P) - q->pcond = p; - } - deltasp = autoffset; - } - a = p->from.type; - if(a == D_AUTO) - p->from.offset += deltasp; - if(a == D_PARAM) - p->from.offset += deltasp + 4; - a = p->to.type; - if(a == D_AUTO) - p->to.offset += deltasp; - if(a == D_PARAM) - p->to.offset += deltasp + 4; - - switch(p->as) { - default: - continue; - case APUSHL: - case APUSHFL: - deltasp += 4; - continue; - case APUSHW: - case APUSHFW: - deltasp += 2; - continue; - case APOPL: - case APOPFL: - deltasp -= 4; - continue; - case APOPW: - case APOPFW: - deltasp -= 2; - continue; - case ARET: - break; } - if(autoffset != deltasp) - diag("unbalanced PUSH/POP"); - if(p->from.type == D_CONST) - goto become; + if(q != P) + q->pcond = p->link; if(autoffset) { - q = p; p = appendp(p); - p->as = ARET; - - q->as = AADJSP; - q->from.type = D_CONST; - q->from.offset = -autoffset; + p->as = AADJSP; + p->from.type = D_CONST; + p->from.offset = autoffset; + p->spadj = autoffset; + if(q != P) + q->pcond = p; + } + deltasp = autoffset; + + for(; p != P; p = p->link) { + a = p->from.type; + if(a == D_AUTO) + p->from.offset += deltasp; + if(a == D_PARAM) + p->from.offset += deltasp + 4; + a = p->to.type; + if(a == D_AUTO) + p->to.offset += deltasp; + if(a == D_PARAM) + p->to.offset += deltasp + 4; + + switch(p->as) { + default: + continue; + case APUSHL: + case APUSHFL: + deltasp += 4; + p->spadj = 4; + continue; + case APUSHW: + case APUSHFW: + deltasp += 2; + p->spadj = 2; + continue; + case APOPL: + case APOPFL: + deltasp -= 4; + p->spadj = -4; + continue; + case APOPW: + case APOPFW: + deltasp -= 2; + p->spadj = -2; + continue; + case ARET: + break; + } + + if(autoffset != deltasp) + diag("unbalanced PUSH/POP"); + + if(autoffset) { + q = p; + p = appendp(p); + p->as = ARET; + + q->as = AADJSP; + q->from.type = D_CONST; + q->from.offset = -autoffset; + p->spadj = -autoffset; + } } - continue; - - become: - q = p; - p = appendp(p); - p->as = AJMP; - p->to = q->to; - p->pcond = q->pcond; - - q->as = AADJSP; - q->from = zprg.from; - q->from.type = D_CONST; - q->from.offset = -autoffset; - q->to = zprg.to; - continue; } } @@ -843,176 +644,7 @@ undef(void) Sym *s; for(i=0; ilink) + for(s = hash[i]; s != S; s = s->hash) if(s->type == SXREF) - diag("%s: not defined", s->name); -} - -void -import(void) -{ - int i; - Sym *s; - - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type == SXREF && (nimports == 0 || s->subtype == SIMPORT)){ - if(s->value != 0) - diag("value != 0 on SXREF"); - undefsym(s); - Bprint(&bso, "IMPORT: %s sig=%lux v=%ld\n", s->name, s->sig, s->value); - if(debug['S']) - s->sig = 0; - } -} - -void -ckoff(Sym *s, int32 v) -{ - if(v < 0 || v >= 1<name); -} - -Prog* -newdata(Sym *s, int o, int w, int t) -{ - Prog *p; - - p = prg(); - if(edatap == P) - datap = p; - else - edatap->link = p; - edatap = p; - p->as = ADATA; - p->width = w; - p->from.scale = w; - p->from.type = t; - p->from.sym = s; - p->from.offset = o; - p->to.type = D_CONST; - p->dlink = s->data; - s->data = p; - return p; -} - -Prog* -newtext(Prog *p, Sym *s) -{ - if(p == P) { - p = prg(); - p->as = ATEXT; - p->from.sym = s; - } - s->type = STEXT; - s->text = p; - s->value = pc; - lastp->link = p; - lastp = p; - p->pc = pc++; - if(textp == P) - textp = p; - else - etextp->pcond = p; - etextp = p; - return p; -} - -void -export(void) -{ - int i, j, n, off, nb, sv, ne; - Sym *s, *et, *str, **esyms; - Prog *p; - char buf[NSNAME], *t; - - n = 0; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT)) - n++; - esyms = mal(n*sizeof(Sym*)); - ne = n; - n = 0; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT)) - esyms[n++] = s; - for(i = 0; i < ne-1; i++) - for(j = i+1; j < ne; j++) - if(strcmp(esyms[i]->name, esyms[j]->name) > 0){ - s = esyms[i]; - esyms[i] = esyms[j]; - esyms[j] = s; - } - - nb = 0; - off = 0; - et = lookup(EXPTAB, 0); - if(et->type != 0 && et->type != SXREF) - diag("%s already defined", EXPTAB); - et->type = SDATA; - str = lookup(".string", 0); - if(str->type == 0) - str->type = SDATA; - sv = str->value; - for(i = 0; i < ne; i++){ - s = esyms[i]; - if(debug['S']) - s->sig = 0; - /* Bprint(&bso, "EXPORT: %s sig=%lux t=%d\n", s->name, s->sig, s->type); */ - - /* signature */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.offset = s->sig; - - /* address */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.type = D_ADDR; - p->to.index = D_EXTERN; - p->to.sym = s; - - /* string */ - t = s->name; - n = strlen(t)+1; - for(;;){ - buf[nb++] = *t; - sv++; - if(nb >= NSNAME){ - p = newdata(str, sv-NSNAME, NSNAME, D_STATIC); - p->to.type = D_SCONST; - memmove(p->to.scon, buf, NSNAME); - nb = 0; - } - if(*t++ == 0) - break; - } - - /* name */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.type = D_ADDR; - p->to.index = D_STATIC; - p->to.sym = str; - p->to.offset = sv-n; - } - - if(nb > 0){ - p = newdata(str, sv-nb, nb, D_STATIC); - p->to.type = D_SCONST; - memmove(p->to.scon, buf, nb); - } - - for(i = 0; i < 3; i++){ - newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - } - et->value = off; - if(sv == 0) - sv = 1; - str->value = sv; - exports = ne; - free(esyms); + diag("%s(%d): not defined", s->name, s->version); } diff --git a/src/cmd/8l/prof.c b/src/cmd/8l/prof.c new file mode 100644 index 000000000..4e95fad79 --- /dev/null +++ b/src/cmd/8l/prof.c @@ -0,0 +1,173 @@ +// Inferno utils/8l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/8l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Profiling. + +#include "l.h" +#include "../ld/lib.h" + +void +doprof1(void) +{ +#if 0 // TODO(rsc) + Sym *s; + int32 n; + Prog *p, *q; + + if(debug['v']) + Bprint(&bso, "%5.2f profile 1\n", cputime()); + Bflush(&bso); + s = lookup("__mcount", 0); + n = 1; + for(p = firstp->link; p != P; p = p->link) { + if(p->as == ATEXT) { + q = prg(); + q->line = p->line; + q->link = datap; + datap = q; + q->as = ADATA; + q->from.type = D_EXTERN; + q->from.offset = n*4; + q->from.sym = s; + q->from.scale = 4; + q->to = p->from; + q->to.type = D_CONST; + + q = prg(); + q->line = p->line; + q->pc = p->pc; + q->link = p->link; + p->link = q; + p = q; + p->as = AADDL; + p->from.type = D_CONST; + p->from.offset = 1; + p->to.type = D_EXTERN; + p->to.sym = s; + p->to.offset = n*4 + 4; + + n += 2; + continue; + } + } + q = prg(); + q->line = 0; + q->link = datap; + datap = q; + + q->as = ADATA; + q->from.type = D_EXTERN; + q->from.sym = s; + q->from.scale = 4; + q->to.type = D_CONST; + q->to.offset = n; + + s->type = SBSS; + s->size = n*4; +#endif +} + +void +doprof2(void) +{ + Sym *s2, *s4; + Prog *p, *q, *ps2, *ps4; + + if(debug['v']) + Bprint(&bso, "%5.2f profile 2\n", cputime()); + Bflush(&bso); + + s2 = lookup("_profin", 0); + s4 = lookup("_profout", 0); + if(s2->type != STEXT || s4->type != STEXT) { + diag("_profin/_profout not defined"); + return; + } + + ps2 = P; + ps4 = P; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; + if(p->from.sym == s2) { + p->from.scale = 1; + ps2 = p; + } + if(p->from.sym == s4) { + p->from.scale = 1; + ps4 = p; + } + } + for(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; + + if(p->from.scale & NOPROF) /* dont profile */ + continue; + + /* + * JMPL profin + */ + q = prg(); + q->line = p->line; + q->pc = p->pc; + q->link = p->link; + p->link = q; + p = q; + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = ps2; + p->to.sym = s2; + + for(; p; p=p->link) { + if(p->as == ARET) { + /* + * RET + */ + q = prg(); + q->as = ARET; + q->from = p->from; + q->to = p->to; + q->link = p->link; + p->link = q; + + /* + * JAL profout + */ + p->as = ACALL; + p->from = zprg.from; + p->to = zprg.to; + p->to.type = D_BRANCH; + p->pcond = ps4; + p->to.sym = s4; + + p = q; + } + } + } +} diff --git a/src/cmd/8l/span.c b/src/cmd/8l/span.c index 99ba279da..66a843b23 100644 --- a/src/cmd/8l/span.c +++ b/src/cmd/8l/span.c @@ -28,29 +28,28 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Instruction layout. + #include "l.h" #include "../ld/lib.h" +static int32 vaddr(Adr*, Reloc*); + void -span(void) +span1(Sym *s) { Prog *p, *q; - int32 v, c, idat; - int m, n, again; - - xdefine("etext", STEXT, 0L); - idat = INITDAT; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; - n = 0; - if(p->to.type == D_BRANCH) - if(p->pcond == P) - p->pcond = p; - if((q = p->pcond) != P) - if(q->back != 2) - n = 1; - p->back = n; + int32 c, v, loop; + uchar *bp; + int n, m, i; + + cursym = s; + + for(p = s->text; p != P; p = p->link) { + p->back = 2; // use short branches first time through + if((q = p->pcond) != P && (q->back & 2)) + p->back |= 1; // backward jump + if(p->as == AADJSP) { p->to.type = D_SP; v = -p->from.offset; @@ -65,293 +64,186 @@ span(void) p->as = ANOP; } } - + n = 0; -start: - do{ - again = 0; - if(debug['v']) - Bprint(&bso, "%5.2f span %d\n", cputime(), n); - Bflush(&bso); - if(n > 50) { - print("span must be looping - %d\n", textsize); + do { + loop = 0; + memset(s->r, 0, s->nr*sizeof s->r[0]); + s->nr = 0; + s->np = 0; + c = 0; + for(p = s->text; p != P; p = p->link) { + p->pc = c; + + // process forward jumps to p + for(q = p->comefrom; q != P; q = q->forwd) { + v = p->pc - (q->pc + q->mark); + if(q->back & 2) { // short + if(v > 127) { + loop++; + q->back ^= 2; + } + s->p[q->pc+1] = v; + } else { + bp = s->p + q->pc + q->mark - 4; + *bp++ = v; + *bp++ = v>>8; + *bp++ = v>>16; + *bp++ = v>>24; + } + } + p->comefrom = P; + + asmins(p); + p->pc = c; + m = andptr-and; + symgrow(s, p->pc+m); + memmove(s->p+p->pc, and, m); + p->mark = m; + c += m; + } + if(++n > 20) { + diag("span must be looping"); errorexit(); } - c = INITTEXT; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - curtext = p; - if(HEADTYPE == 8) - c = (c+31)&~31; - } - if(p->to.type == D_BRANCH) - if(p->back) - p->pc = c; - if(n == 0 || HEADTYPE == 8 || p->to.type == D_BRANCH) { - if(HEADTYPE == 8) - p->pc = c; - asmins(p); - m = andptr-and; - if(p->mark != m) - again = 1; - p->mark = m; - } - if(HEADTYPE == 8) { - c = p->pc + p->mark; - } else { - p->pc = c; - c += p->mark; - } + } while(loop); + s->size = c; + + if(debug['a'] > 1) { + print("span1 %s %d (%d tries)\n %.6ux", s->name, s->size, n, 0); + for(i=0; inp; i++) { + print(" %.2ux", s->p[i]); + if(i%16 == 15) + print("\n %.6ux", i+1); } - textsize = c; - n++; - }while(again); - - if(INITRND) { - INITDAT = rnd(c+textpad, INITRND); - if(INITDAT != idat) { - idat = INITDAT; - goto start; + if(i%16) + print("\n"); + + for(i=0; inr; i++) { + Reloc *r; + + r = &s->r[i]; + print(" rel %#.4ux/%d %s%+d\n", r->off, r->siz, r->sym->name, r->add); } } - xdefine("etext", STEXT, c); - if(debug['v']) - Bprint(&bso, "etext = %lux\n", c); - Bflush(&bso); - for(p = textp; p != P; p = p->pcond) - p->from.sym->value = p->pc; - textsize = c - INITTEXT; } void -xdefine(char *p, int t, int32 v) +span(void) { - Sym *s; - - s = lookup(p, 0); - if(s->type == 0 || s->type == SXREF) { - s->type = t; - s->value = v; - } - if(s->type == STEXT && s->value == 0) - s->value = v; -} + Prog *p, *q; + int32 v; + int n; -void -putsymb(char *s, int t, int32 v, int ver, Sym *go) -{ - int i, f; - vlong gv; - - if(t == 'f') - s++; - lput(v); - if(ver) - t += 'a' - 'A'; - cput(t+0x80); /* 0x80 is variable length */ - - if(t == 'Z' || t == 'z') { - cput(s[0]); - for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) { - cput(s[i]); - cput(s[i+1]); - } - cput(0); - cput(0); - i++; - } - else { - for(i=0; s[i]; i++) - cput(s[i]); - cput(0); - } - gv = 0; - if(go) { - if(!go->reachable) - sysfatal("unreachable type %s", go->name); - gv = go->value+INITDAT; - } - lput(gv); + if(debug['v']) + Bprint(&bso, "%5.2f span\n", cputime()); - symsize += 4 + 1 + i+1 + 4; + // NOTE(rsc): If we get rid of the globals we should + // be able to parallelize these iterations. + for(cursym = textp; cursym != nil; cursym = cursym->next) { + if(cursym->text == nil || cursym->text->link == nil) + continue; - if(debug['n']) { - if(t == 'z' || t == 'Z') { - Bprint(&bso, "%c %.8lux ", t, v); - for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) { - f = ((s[i]&0xff) << 8) | (s[i+1]&0xff); - Bprint(&bso, "/%x", f); + // TODO: move into span1 + for(p = cursym->text; p != P; p = p->link) { + n = 0; + if(p->to.type == D_BRANCH) + if(p->pcond == P) + p->pcond = p; + if((q = p->pcond) != P) + if(q->back != 2) + n = 1; + p->back = n; + if(p->as == AADJSP) { + p->to.type = D_SP; + v = -p->from.offset; + p->from.offset = v; + p->as = AADDL; + if(v < 0) { + p->as = ASUBL; + v = -v; + p->from.offset = v; + } + if(v == 0) + p->as = ANOP; } - Bprint(&bso, "\n"); - return; } - if(ver) - Bprint(&bso, "%c %.8lux %s<%d> %s (%.8llux)\n", t, v, s, ver, go ? go->name : "", gv); - else - Bprint(&bso, "%c %.8lux %s\n", t, v, s, go ? go->name : "", gv); + span1(cursym); } } void -asmsym(void) +xdefine(char *p, int t, int32 v) { - Prog *p; - Auto *a; Sym *s; - int h; - - s = lookup("etext", 0); - if(s->type == STEXT) - putsymb(s->name, 'T', s->value, s->version, 0); - - for(h=0; hlink) - switch(s->type) { - case SCONST: - if(!s->reachable) - continue; - putsymb(s->name, 'D', s->value, s->version, s->gotype); - continue; - - case SDATA: - case SELFDATA: - if(!s->reachable) - continue; - putsymb(s->name, 'D', s->value+INITDAT, s->version, s->gotype); - continue; - - case SMACHO: - if(!s->reachable) - continue; - putsymb(s->name, 'D', s->value+INITDAT+datsize+bsssize, s->version, s->gotype); - continue; - - case SBSS: - if(!s->reachable) - continue; - putsymb(s->name, 'B', s->value+INITDAT, s->version, s->gotype); - continue; - - case SFIXED: - putsymb(s->name, 'B', s->value, s->version, s->gotype); - continue; - - case SFILE: - putsymb(s->name, 'f', s->value, s->version, 0); - continue; - } - for(p=textp; p!=P; p=p->pcond) { - s = p->from.sym; - if(s->type != STEXT) - continue; - - /* filenames first */ - for(a=p->to.autom; a; a=a->link) - if(a->type == D_FILE) - putsymb(a->asym->name, 'z', a->aoffset, 0, 0); - else - if(a->type == D_FILE1) - putsymb(a->asym->name, 'Z', a->aoffset, 0, 0); - - if(!s->reachable) - continue; - - putsymb(s->name, 'T', s->value, s->version, s->gotype); - - /* frame, auto and param after */ - putsymb(".frame", 'm', p->to.offset+4, 0, 0); - - for(a=p->to.autom; a; a=a->link) - if(a->type == D_AUTO) - putsymb(a->asym->name, 'a', -a->aoffset, 0, a->gotype); - else - if(a->type == D_PARAM) - putsymb(a->asym->name, 'p', a->aoffset, 0, a->gotype); - } - if(debug['v'] || debug['n']) - Bprint(&bso, "symsize = %lud\n", symsize); - Bflush(&bso); + s = lookup(p, 0); + s->type = t; + s->value = v; + s->reachable = 1; + s->special = 1; } void -asmlc(void) +instinit(void) { - int32 oldpc, oldlc; - Prog *p; - int32 v, s; - - oldpc = INITTEXT; - oldlc = 0; - for(p = firstp; p != P; p = p->link) { - if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) { - if(p->as == ATEXT) - curtext = p; - if(debug['L']) - Bprint(&bso, "%6lux %P\n", - p->pc, p); - continue; - } - if(debug['L']) - Bprint(&bso, "\t\t%6ld", lcsize); - v = (p->pc - oldpc) / MINLC; - while(v) { - s = 127; - if(v < 127) - s = v; - cput(s+128); /* 129-255 +pc */ - if(debug['L']) - Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128); - v -= s; - lcsize++; - } - s = p->line - oldlc; - oldlc = p->line; - oldpc = p->pc + MINLC; - if(s > 64 || s < -64) { - cput(0); /* 0 vv +lc */ - cput(s>>24); - cput(s>>16); - cput(s>>8); - cput(s); - if(debug['L']) { - if(s > 0) - Bprint(&bso, " lc+%ld(%d,%ld)\n", - s, 0, s); - else - Bprint(&bso, " lc%ld(%d,%ld)\n", - s, 0, s); - Bprint(&bso, "%6lux %P\n", - p->pc, p); - } - lcsize += 5; - continue; - } - if(s > 0) { - cput(0+s); /* 1-64 +lc */ - if(debug['L']) { - Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s); - Bprint(&bso, "%6lux %P\n", - p->pc, p); - } - } else { - cput(64-s); /* 65-128 -lc */ - if(debug['L']) { - Bprint(&bso, " lc%ld(%ld)\n", s, 64-s); - Bprint(&bso, "%6lux %P\n", - p->pc, p); - } + int i; + + for(i=1; optab[i].as; i++) + if(i != optab[i].as) { + diag("phase error in optab: %d", i); + errorexit(); } - lcsize++; - } - while(lcsize & 1) { - s = 129; - cput(s); - lcsize++; + maxop = i; + + for(i=0; i= D_AL && i <= D_BH) + reg[i] = (i-D_AL) & 7; + if(i >= D_AX && i <= D_DI) + reg[i] = (i-D_AX) & 7; + if(i >= D_F0 && i <= D_F0+7) + reg[i] = (i-D_F0) & 7; } - if(debug['v'] || debug['L']) - Bprint(&bso, "lcsize = %ld\n", lcsize); - Bflush(&bso); } int @@ -506,11 +398,11 @@ oclass(Adr *a) } void -asmidx(Adr *a, int base) +asmidx(int scale, int index, int base) { int i; - switch(a->index) { + switch(index) { default: goto bad; @@ -525,10 +417,10 @@ asmidx(Adr *a, int base) case D_BP: case D_SI: case D_DI: - i = reg[a->index] << 3; + i = reg[index] << 3; break; } - switch(a->scale) { + switch(scale) { default: goto bad; case 1: @@ -564,7 +456,7 @@ bas: *andptr++ = i; return; bad: - diag("asmidx: bad address %D", a); + diag("asmidx: bad address %d,%d,%d", scale, index, base); *andptr++ = 0; return; } @@ -572,10 +464,6 @@ bad: static void put4(int32 v) { - if(dlm && curp != P && reloca != nil){ - dynreloc(reloca->sym, curp->pc + andptr - &and[0], 1); - reloca = nil; - } andptr[0] = v; andptr[1] = v>>8; andptr[2] = v>>16; @@ -583,24 +471,40 @@ put4(int32 v) andptr += 4; } +static void +relput4(Prog *p, Adr *a) +{ + vlong v; + Reloc rel, *r; + + v = vaddr(a, &rel); + if(rel.siz != 0) { + if(rel.siz != 4) + diag("bad reloc"); + r = addrel(cursym); + *r = rel; + r->off = p->pc + andptr - and; + } + put4(v); +} + int32 symaddr(Sym *s) { - Adr a; - - a.type = D_ADDR; - a.index = D_EXTERN; - a.offset = 0; - a.sym = s; - return vaddr(&a); + if(!s->reachable) + diag("unreachable symbol in symaddr - %s", s->name); + return s->value; } -int32 -vaddr(Adr *a) +static int32 +vaddr(Adr *a, Reloc *r) { int t; int32 v; Sym *s; + + if(r != nil) + memset(r, 0, sizeof *r); t = a->type; v = a->offset; @@ -611,30 +515,18 @@ vaddr(Adr *a) case D_EXTERN: s = a->sym; if(s != nil) { - if(dlm && curp != P) - reloca = a; - switch(s->type) { - case SUNDEF: - ckoff(s, v); - case STEXT: - case SCONST: - if(!s->reachable) - sysfatal("unreachable symbol in vaddr - %s", s->name); - v += s->value; - break; - case SMACHO: - if(!s->reachable) - sysfatal("unreachable symbol in vaddr - %s", s->name); - v += INITDAT + datsize + s->value; - break; - case SFIXED: - v += s->value; - break; - default: - if(!s->reachable) - sysfatal("unreachable symbol in vaddr - %s", s->name); - v += INITDAT + s->value; + if(!s->reachable) + sysfatal("unreachable symbol in vaddr - %s", s->name); + if(r == nil) { + diag("need reloc for %D", a); + errorexit(); } + r->type = D_ADDR; + r->siz = 4; + r->off = -1; + r->sym = s; + r->add = v; + v = 0; } } return v; @@ -644,118 +536,127 @@ void asmand(Adr *a, int r) { int32 v; - int t; - Adr aa; + int t, scale; + Reloc rel; v = a->offset; t = a->type; + rel.siz = 0; if(a->index != D_NONE) { - if(t >= D_INDIR && t < 2*D_INDIR) { - t -= D_INDIR; - if(t == D_NONE) { - *andptr++ = (0 << 6) | (4 << 0) | (r << 3); - asmidx(a, t); - put4(v); - return; - } - if(v == 0 && t != D_BP) { - *andptr++ = (0 << 6) | (4 << 0) | (r << 3); - asmidx(a, t); - return; - } - if(v >= -128 && v < 128) { - *andptr++ = (1 << 6) | (4 << 0) | (r << 3); - asmidx(a, t); - *andptr++ = v; - return; + if(t < D_INDIR || t >= 2*D_INDIR) { + switch(t) { + default: + goto bad; + case D_STATIC: + case D_EXTERN: + t = D_NONE; + v = vaddr(a, &rel); + break; + case D_AUTO: + case D_PARAM: + t = D_SP; + break; } - *andptr++ = (2 << 6) | (4 << 0) | (r << 3); - asmidx(a, t); - put4(v); + } else + t -= D_INDIR; + + if(t == D_NONE) { + *andptr++ = (0 << 6) | (4 << 0) | (r << 3); + asmidx(a->scale, a->index, t); + goto putrelv; + } + if(v == 0 && rel.siz == 0 && t != D_BP) { + *andptr++ = (0 << 6) | (4 << 0) | (r << 3); + asmidx(a->scale, a->index, t); + return; + } + if(v >= -128 && v < 128 && rel.siz == 0) { + *andptr++ = (1 << 6) | (4 << 0) | (r << 3); + asmidx(a->scale, a->index, t); + *andptr++ = v; return; } - switch(t) { + *andptr++ = (2 << 6) | (4 << 0) | (r << 3); + asmidx(a->scale, a->index, t); + goto putrelv; + } + if(t >= D_AL && t <= D_F0+7) { + if(v) + goto bad; + *andptr++ = (3 << 6) | (reg[t] << 0) | (r << 3); + return; + } + + scale = a->scale; + if(t < D_INDIR || t >= 2*D_INDIR) { + switch(a->type) { default: goto bad; case D_STATIC: case D_EXTERN: - aa.type = D_NONE+D_INDIR; + t = D_NONE; + v = vaddr(a, &rel); break; case D_AUTO: case D_PARAM: - aa.type = D_SP+D_INDIR; + t = D_SP; break; } - aa.offset = vaddr(a); - aa.index = a->index; - aa.scale = a->scale; - asmand(&aa, r); - return; - } - if(t >= D_AL && t <= D_F0+7) { - if(v) - goto bad; - *andptr++ = (3 << 6) | (reg[t] << 0) | (r << 3); - return; - } - if(t >= D_INDIR && t < 2*D_INDIR) { + scale = 1; + } else t -= D_INDIR; - if(t == D_NONE || (D_CS <= t && t <= D_GS)) { - *andptr++ = (0 << 6) | (5 << 0) | (r << 3); - put4(v); + + if(t == D_NONE || (D_CS <= t && t <= D_GS)) { + *andptr++ = (0 << 6) | (5 << 0) | (r << 3); + goto putrelv; + } + if(t == D_SP) { + if(v == 0 && rel.siz == 0) { + *andptr++ = (0 << 6) | (4 << 0) | (r << 3); + asmidx(scale, D_NONE, t); return; } - if(t == D_SP) { - if(v == 0) { - *andptr++ = (0 << 6) | (4 << 0) | (r << 3); - asmidx(a, D_SP); - return; - } - if(v >= -128 && v < 128) { - *andptr++ = (1 << 6) | (4 << 0) | (r << 3); - asmidx(a, D_SP); - *andptr++ = v; - return; - } - *andptr++ = (2 << 6) | (4 << 0) | (r << 3); - asmidx(a, D_SP); - put4(v); + if(v >= -128 && v < 128 && rel.siz == 0) { + *andptr++ = (1 << 6) | (4 << 0) | (r << 3); + asmidx(scale, D_NONE, t); + *andptr++ = v; return; } - if(t >= D_AX && t <= D_DI) { - if(v == 0 && t != D_BP) { - *andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3); - return; - } - if(v >= -128 && v < 128) { - andptr[0] = (1 << 6) | (reg[t] << 0) | (r << 3); - andptr[1] = v; - andptr += 2; - return; - } - *andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3); - put4(v); + *andptr++ = (2 << 6) | (4 << 0) | (r << 3); + asmidx(scale, D_NONE, t); + goto putrelv; + } + if(t >= D_AX && t <= D_DI) { + if(v == 0 && rel.siz == 0 && t != D_BP) { + *andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3); return; } - goto bad; + if(v >= -128 && v < 128 && rel.siz == 0) { + andptr[0] = (1 << 6) | (reg[t] << 0) | (r << 3); + andptr[1] = v; + andptr += 2; + return; + } + *andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3); + goto putrelv; } - switch(a->type) { - default: - goto bad; - case D_STATIC: - case D_EXTERN: - aa.type = D_NONE+D_INDIR; - break; - case D_AUTO: - case D_PARAM: - aa.type = D_SP+D_INDIR; - break; + goto bad; + +putrelv: + if(rel.siz != 0) { + Reloc *r; + + if(rel.siz != 4) { + diag("bad rel"); + goto bad; + } + r = addrel(cursym); + *r = rel; + r->off = curp->pc + andptr - and; } - aa.index = D_NONE; - aa.scale = 1; - aa.offset = vaddr(a); - asmand(&aa, r); + put4(v); return; + bad: diag("asmand: bad address %D", a); return; @@ -921,22 +822,6 @@ subreg(Prog *p, int from, int to) print("%P\n", p); } -// nacl RET: -// POPL BX -// ANDL BX, $~31 -// JMP BX -uchar naclret[] = { 0x5b, 0x83, 0xe3, ~31, 0xff, 0xe3 }; - -// nacl JMP BX: -// ANDL BX, $~31 -// JMP BX -uchar nacljmpbx[] = { 0x83, 0xe3, ~31, 0xff, 0xe3 }; - -// nacl CALL BX: -// ANDL BX, $~31 -// CALL BX -uchar naclcallbx[] = { 0x83, 0xe3, ~31, 0xff, 0xd3 }; - void doasm(Prog *p) { @@ -945,6 +830,10 @@ doasm(Prog *p) uchar *t; int z, op, ft, tt; int32 v, pre; + Reloc rel, *r; + Adr *a; + + curp = p; // TODO pre = prefixof(&p->from); if(pre) @@ -990,7 +879,7 @@ found: case Pb: /* botch */ break; } - v = vaddr(&p->from); + op = o->op[z]; switch(t[2]) { default: @@ -1001,12 +890,6 @@ found: break; case Zlit: - if(HEADTYPE == 8 && p->as == ARET) { - // native client return. - for(z=0; zop[z]; z++) *andptr++ = op; break; @@ -1040,137 +923,100 @@ found: break; case Zo_m: - if(HEADTYPE == 8) { - Adr a; - - switch(p->as) { - case AJMP: - if(p->to.type < D_AX || p->to.type > D_DI) - diag("indirect jmp must use register in native client"); - // ANDL $~31, REG - *andptr++ = 0x83; - asmand(&p->to, 04); - *andptr++ = ~31; - // JMP REG - *andptr++ = 0xFF; - asmand(&p->to, 04); - return; - - case ACALL: - a = p->to; - // native client indirect call - if(a.type < D_AX || a.type > D_DI) { - // MOVL target into BX - *andptr++ = 0x8b; - asmand(&p->to, reg[D_BX]); - memset(&a, 0, sizeof a); - a.type = D_BX; - } - // ANDL $~31, REG - *andptr++ = 0x83; - asmand(&a, 04); - *andptr++ = ~31; - // CALL REG - *andptr++ = 0xFF; - asmand(&a, 02); - return; - } - } *andptr++ = op; asmand(&p->to, o->op[z+1]); break; case Zm_ibo: - v = vaddr(&p->to); *andptr++ = op; asmand(&p->from, o->op[z+1]); - *andptr++ = v; + *andptr++ = vaddr(&p->to, nil); break; case Zibo_m: *andptr++ = op; asmand(&p->to, o->op[z+1]); - *andptr++ = v; + *andptr++ = vaddr(&p->from, nil); break; case Z_ib: - v = vaddr(&p->to); case Zib_: - if(HEADTYPE == 8 && p->as == AINT && v == 3) { - // native client disallows all INT instructions. - // translate INT $3 to HLT. - *andptr++ = 0xf4; - break; - } + if(t[2] == Zib_) + a = &p->from; + else + a = &p->to; + v = vaddr(a, nil); *andptr++ = op; *andptr++ = v; break; case Zib_rp: *andptr++ = op + reg[p->to.type]; - *andptr++ = v; + *andptr++ = vaddr(&p->from, nil); break; case Zil_rp: *andptr++ = op + reg[p->to.type]; if(o->prefix == Pe) { + v = vaddr(&p->from, nil); *andptr++ = v; *andptr++ = v>>8; } else - put4(v); + relput4(p, &p->from); break; case Zib_rr: *andptr++ = op; asmand(&p->to, reg[p->to.type]); - *andptr++ = v; + *andptr++ = vaddr(&p->from, nil); break; case Z_il: - v = vaddr(&p->to); case Zil_: - *andptr++ = op; - if(o->prefix == Pe) { - *andptr++ = v; - *andptr++ = v>>8; - } + if(t[2] == Zil_) + a = &p->from; else - put4(v); - break; - - case Zm_ilo: - v = vaddr(&p->to); + a = &p->to; *andptr++ = op; - asmand(&p->from, o->op[z+1]); if(o->prefix == Pe) { + v = vaddr(a, nil); *andptr++ = v; *andptr++ = v>>8; } else - put4(v); + relput4(p, a); break; + case Zm_ilo: case Zilo_m: *andptr++ = op; - asmand(&p->to, o->op[z+1]); + if(t[2] == Zilo_m) { + a = &p->from; + asmand(&p->to, o->op[z+1]); + } else { + a = &p->to; + asmand(&p->from, o->op[z+1]); + } if(o->prefix == Pe) { + v = vaddr(a, nil); *andptr++ = v; *andptr++ = v>>8; } else - put4(v); + relput4(p, a); break; case Zil_rr: *andptr++ = op; asmand(&p->to, reg[p->to.type]); if(o->prefix == Pe) { + v = vaddr(&p->from, nil); *andptr++ = v; *andptr++ = v>>8; } else - put4(v); + relput4(p, &p->from); break; case Z_rp: @@ -1185,99 +1031,128 @@ found: *andptr++ = op; asmand(&p->to, reg[p->to.type]); break; - - case Zbr: - q = p->pcond; - if(q) { - v = q->pc - p->pc - 2; - if(q->pc == 0) - v = 0; - if(v >= -128 && v <= 127 && !p->bigjmp) { - *andptr++ = op; - *andptr++ = v; - } else { - p->bigjmp = 1; - v -= 6-2; - *andptr++ = 0x0f; - *andptr++ = o->op[z+1]; - *andptr++ = v; - *andptr++ = v>>8; - *andptr++ = v>>16; - *andptr++ = v>>24; - } - } - break; - + case Zcall: q = p->pcond; - if(q) { - v = q->pc - p->pc - 5; - if(dlm && curp != P && p->to.sym->type == SUNDEF){ - /* v = 0 - p->pc - 5; */ - v = 0; - ckoff(p->to.sym, v); - v += p->to.sym->value; - dynreloc(p->to.sym, p->pc+1, 0); - } - *andptr++ = op; - *andptr++ = v; - *andptr++ = v>>8; - *andptr++ = v>>16; - *andptr++ = v>>24; + if(q == nil) { + diag("call without target"); + errorexit(); + } + if(q->as != ATEXT) { + // Could handle this case by making D_PCREL + // record the Prog* instead of the Sym*, but let's + // wait until the need arises. + diag("call of non-TEXT %P", q); + errorexit(); } - break; - - case Zcallcon: - v = p->to.offset - p->pc - 5; *andptr++ = op; - *andptr++ = v; - *andptr++ = v>>8; - *andptr++ = v>>16; - *andptr++ = v>>24; + r = addrel(cursym); + r->off = p->pc + andptr - and; + r->type = D_PCREL; + r->siz = 4; + r->sym = q->from.sym; + put4(0); break; + case Zbr: case Zjmp: q = p->pcond; - if(q) { - v = q->pc - p->pc - 2; - if(q->pc == 0) - v = 0; - if(v >= -128 && v <= 127 && !p->bigjmp) { + if(q == nil) { + diag("jmp/branch without target"); + errorexit(); + } + if(q->as == ATEXT) { + // jump out of function + if(t[2] == Zbr) { + diag("branch to ATEXT"); + errorexit(); + } + *andptr++ = o->op[z+1]; + r = addrel(cursym); + r->off = p->pc + andptr - and; + r->sym = q->from.sym; + r->type = D_PCREL; + r->siz = 4; + put4(0); + break; + } + + // Assumes q is in this function. + // TODO: Check in input, preserve in brchain. + + // Fill in backward jump now. + if(p->back & 1) { + v = q->pc - (p->pc + 2); + if(v >= -128) { *andptr++ = op; *andptr++ = v; } else { - p->bigjmp = 1; v -= 5-2; + if(t[2] == Zbr) { + *andptr++ = 0x0f; + v--; + } *andptr++ = o->op[z+1]; *andptr++ = v; *andptr++ = v>>8; *andptr++ = v>>16; *andptr++ = v>>24; } + break; + } + + // Annotate target; will fill in later. + p->forwd = q->comefrom; + q->comefrom = p; + if(p->back & 2) { // short + *andptr++ = op; + *andptr++ = 0; + } else { + if(t[2] == Zbr) + *andptr++ = 0x0f; + *andptr++ = o->op[z+1]; + *andptr++ = 0; + *andptr++ = 0; + *andptr++ = 0; + *andptr++ = 0; } break; + case Zcallcon: case Zjmpcon: - v = p->to.offset - p->pc - 5; - *andptr++ = o->op[z+1]; - *andptr++ = v; - *andptr++ = v>>8; - *andptr++ = v>>16; - *andptr++ = v>>24; + if(t[2] == Zcallcon) + *andptr++ = op; + else + *andptr++ = o->op[z+1]; + r = addrel(cursym); + r->off = p->pc + andptr - and; + r->type = D_PCREL; + r->siz = 4; + r->add = p->to.offset; + put4(0); break; case Zloop: q = p->pcond; - if(q) { - v = q->pc - p->pc - 2; - if(v < -128 && v > 127) - diag("loop too far: %P", p); - *andptr++ = op; - *andptr++ = v; + 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) { + rel.siz = op; + r = addrel(cursym); + *r = rel; + r->off = p->pc + andptr - and; + } *andptr++ = v; if(op > 1) { *andptr++ = v>>8; @@ -1342,7 +1217,7 @@ bad: } return; } - diag("doasm: notfound t2=%lux from=%lux to=%lux %P", t[2], p->from.type, p->to.type, p); + diag("doasm: notfound t2=%ux from=%ux to=%ux %P", t[2], p->from.type, p->to.type, p); return; mfound: @@ -1441,204 +1316,10 @@ mfound: void asmins(Prog *p) { - if(HEADTYPE == 8) { - ulong npc; - static Prog *prefix; - - // native client - // - pad indirect jump targets (aka ATEXT) to 32-byte boundary - // - instructions cannot cross 32-byte boundary - // - end of call (return address) must be on 32-byte boundary - if(p->as == ATEXT) - p->pc += 31 & -p->pc; - if(p->as == ACALL) { - // must end on 32-byte boundary. - // doasm to find out how long the CALL encoding is. - andptr = and; - doasm(p); - npc = p->pc + (andptr - and); - p->pc += 31 & -npc; - } - if(p->as == AREP || p->as == AREPN) { - // save prefix for next instruction, - // so that inserted NOPs do not split (e.g.) REP / MOVSL sequence. - prefix = p; - andptr = and; - return; - } - andptr = and; - if(prefix) - doasm(prefix); - doasm(p); - npc = p->pc + (andptr - and); - if(andptr > and && (p->pc&~31) != ((npc-1)&~31)) { - // crossed 32-byte boundary; pad to boundary and try again - p->pc += 31 & -p->pc; - andptr = and; - if(prefix) - doasm(prefix); - doasm(p); - } - prefix = nil; - } else { - andptr = and; - doasm(p); - } + andptr = and; + doasm(p); if(andptr > and+sizeof and) { print("and[] is too short - %d byte instruction\n", andptr - and); errorexit(); } } - -enum{ - ABSD = 0, - ABSU = 1, - RELD = 2, - RELU = 3, -}; - -int modemap[4] = { 0, 1, -1, 2, }; - -typedef struct Reloc Reloc; - -struct Reloc -{ - int n; - int t; - uchar *m; - uint32 *a; -}; - -Reloc rels; - -static void -grow(Reloc *r) -{ - int t; - uchar *m, *nm; - uint32 *a, *na; - - t = r->t; - r->t += 64; - m = r->m; - a = r->a; - r->m = nm = mal(r->t*sizeof(uchar)); - r->a = na = mal(r->t*sizeof(uint32)); - memmove(nm, m, t*sizeof(uchar)); - memmove(na, a, t*sizeof(uint32)); - free(m); - free(a); -} - -void -dynreloc(Sym *s, uint32 v, int abs) -{ - int i, k, n; - uchar *m; - uint32 *a; - Reloc *r; - - if(s->type == SUNDEF) - k = abs ? ABSU : RELU; - else - k = abs ? ABSD : RELD; - /* Bprint(&bso, "R %s a=%ld(%lx) %d\n", s->name, v, v, k); */ - k = modemap[k]; - r = &rels; - n = r->n; - if(n >= r->t) - grow(r); - m = r->m; - a = r->a; - for(i = n; i > 0; i--){ - if(v < a[i-1]){ /* happens occasionally for data */ - m[i] = m[i-1]; - a[i] = a[i-1]; - } - else - break; - } - m[i] = k; - a[i] = v; - r->n++; -} - -static int -sput(char *s) -{ - char *p; - - p = s; - while(*s) - cput(*s++); - cput(0); - return s-p+1; -} - -void -asmdyn() -{ - int i, n, t, c; - Sym *s; - uint32 la, ra, *a; - vlong off; - uchar *m; - Reloc *r; - - cflush(); - off = seek(cout, 0, 1); - lput(0); - t = 0; - lput(imports); - t += 4; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->type == SUNDEF){ - lput(s->sig); - t += 4; - t += sput(s->name); - } - - la = 0; - r = &rels; - n = r->n; - m = r->m; - a = r->a; - lput(n); - t += 4; - for(i = 0; i < n; i++){ - ra = *a-la; - if(*a < la) - diag("bad relocation order"); - if(ra < 256) - c = 0; - else if(ra < 65536) - c = 1; - else - c = 2; - cput((c<<6)|*m++); - t++; - if(c == 0){ - cput(ra); - t++; - } - else if(c == 1){ - wput(ra); - t += 2; - } - else{ - lput(ra); - t += 4; - } - la = *a++; - } - - cflush(); - seek(cout, off, 0); - lput(t); - - if(debug['v']){ - Bprint(&bso, "import table entries = %d\n", imports); - Bprint(&bso, "export table entries = %d\n", exports); - } -} diff --git a/src/cmd/cc/Makefile b/src/cmd/cc/Makefile index 98b89f0a2..71f23383d 100644 --- a/src/cmd/cc/Makefile +++ b/src/cmd/cc/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -LIB=\ - cc.a$O\ +LIB=cc.a HFILES=\ cc.h\ @@ -30,18 +30,7 @@ OFILES=\ dpchk.$O\ omachcap.$O\ -$(LIB): $(OFILES) - ar rsc $(LIB) $(OFILES) - -$(OFILES): $(HFILES) - -y.tab.h: $(YFILES) - bison -y $(YFLAGS) $(YFILES) - -y.tab.c: y.tab.h - test -f y.tab.c && touch y.tab.c - -clean: - rm -f *.$O *.6 enam.c 6.out a.out y.tab.h y.tab.c $(LIB) +NOINSTALL=1 +include ../../Make.clib install: $(LIB) diff --git a/src/cmd/cc/acid.c b/src/cmd/cc/acid.c index eb7968c4f..c6a6722bd 100644 --- a/src/cmd/cc/acid.c +++ b/src/cmd/cc/acid.c @@ -150,7 +150,7 @@ acidmember(Type *t, int32 off, int flag) if(typesu[l->etype]) { s1 = acidsue(l->link); if(s1 != S) { - Bprint(&outbuf, " 'A' %s %ld %s;\n", + Bprint(&outbuf, " 'A' %s %d %s;\n", amap(s1->name), t->offset+off, amap(s->name)); break; @@ -189,7 +189,7 @@ acidmember(Type *t, int32 off, int flag) if(s == S) break; if(flag) { - Bprint(&outbuf, " '%c' %ld %s;\n", + Bprint(&outbuf, " '%c' %d %s;\n", acidchar[t->etype], t->offset+off, amap(s->name)); } else { Bprint(&outbuf, "\tprint(indent, \"%s\t\", addr.%s, \"\\n\");\n", @@ -209,7 +209,7 @@ acidmember(Type *t, int32 off, int flag) acidmember(l, t->offset+off, flag); Bprint(&outbuf, " };\n"); } else { - Bprint(&outbuf, " %s %ld %s;\n", + Bprint(&outbuf, " %s %d %s;\n", amap(s1->name), t->offset+off, amap(s->name)); } @@ -223,7 +223,7 @@ acidmember(Type *t, int32 off, int flag) } else { Bprint(&outbuf, "\tprint(indent, \"%s {\\n\");\n", amap(s1->name)); - Bprint(&outbuf, "\tindent_%s(addr+%ld, indent+\"\\t\");\n", + Bprint(&outbuf, "\tindent_%s(addr+%d, indent+\"\\t\");\n", amap(s1->name), t->offset+off); Bprint(&outbuf, "\tprint(indent, \"}\\n\");\n"); } @@ -263,7 +263,7 @@ acidtype(Type *t) if(debug['s']) goto asmstr; an = amap(s->name); - Bprint(&outbuf, "sizeof%s = %ld;\n", an, t->width); + Bprint(&outbuf, "sizeof%s = %d;\n", an, t->width); Bprint(&outbuf, "aggr %s\n{\n", an); for(l = t->link; l != T; l = l->down) acidmember(l, 0, 1); @@ -280,7 +280,7 @@ acidtype(Type *t) break; for(l = t->link; l != T; l = l->down) if(l->sym != S) - Bprint(&outbuf, "#define\t%s.%s\t%ld\n", + Bprint(&outbuf, "#define\t%s.%s\t%d\n", s->name, l->sym->name, l->offset); diff --git a/src/cmd/cc/cc.h b/src/cmd/cc/cc.h index 69adcccb0..3649bf5f6 100644 --- a/src/cmd/cc/cc.h +++ b/src/cmd/cc/cc.h @@ -166,6 +166,7 @@ struct Type uchar nbits; uchar etype; uchar garb; + uchar align; }; #define T ((Type*)0) @@ -785,7 +786,7 @@ int32 outlstring(ushort*, int32); void sextern(Sym*, Node*, int32, int32); void xcom(Node*); int32 exreg(Type*); -int32 align(int32, Type*, int); +int32 align(int32, Type*, int, int32*); int32 maxround(int32, int32); extern schar ewidth[]; diff --git a/src/cmd/cc/com.c b/src/cmd/cc/com.c index 5cbe8b77c..b1a8a4704 100644 --- a/src/cmd/cc/com.c +++ b/src/cmd/cc/com.c @@ -638,10 +638,10 @@ tcomo(Node *n, int f) n->addable = 1; if(n->class == CEXREG) { n->op = OREGISTER; - // on 386, "extern register" generates + // on 386 or amd64, "extern register" generates // memory references relative to the - // fs segment. - if(thechar == '8') // [sic] + // gs or fs segment. + if(thechar == '8' || thechar == '6') // [sic] n->op = OEXREG; n->reg = n->sym->offset; n->xoffset = 0; diff --git a/src/cmd/cc/dcl.c b/src/cmd/cc/dcl.c index b4d8c4d14..f629925d1 100644 --- a/src/cmd/cc/dcl.c +++ b/src/cmd/cc/dcl.c @@ -195,7 +195,7 @@ doinit(Sym *s, Type *t, int32 o, Node *a) dbgdecl(s); } if(debug['i']) { - print("t = %T; o = %ld; n = %s\n", t, o, s->name); + print("t = %T; o = %d; n = %s\n", t, o, s->name); prtree(a, "doinit value"); } @@ -323,7 +323,7 @@ init1(Sym *s, Type *t, int32 o, int exflag) return Z; if(debug['i']) { - print("t = %T; o = %ld; n = %s\n", t, o, s->name); + print("t = %T; o = %d; n = %s\n", t, o, s->name); prtree(a, "init1 value"); } @@ -479,7 +479,7 @@ init1(Sym *s, Type *t, int32 o, int exflag) e = r->vconst; if(t->width != 0) if(e < 0 || e*w >= t->width) { - diag(a, "initialization index out of range: %ld", e); + diag(a, "initialization index out of range: %d", e); continue; } } @@ -552,9 +552,10 @@ void sualign(Type *t) { Type *l; - int32 o, w; + int32 o, w, maxal; o = 0; + maxal = 0; switch(t->etype) { case TSTRUCT: @@ -577,13 +578,14 @@ sualign(Type *t) l->sym->name); else diag(Z, "incomplete structure element"); - w = align(w, l, Ael1); + w = align(w, l, Ael1, &maxal); l->offset = w; - w = align(w, l, Ael2); + w = align(w, l, Ael2, &maxal); } } - w = align(w, t, Asu2); + w = align(w, t, Asu2, &maxal); t->width = w; + t->align = maxal; acidtype(t); pickletype(t); return; @@ -600,12 +602,13 @@ sualign(Type *t) diag(Z, "incomplete union element"); l->offset = 0; l->shift = 0; - o = align(align(0, l, Ael1), l, Ael2); + o = align(align(0, l, Ael1, &maxal), l, Ael2, &maxal); if(o > w) w = o; } - w = align(w, t, Asu2); + w = align(w, t, Asu2, &maxal); t->width = w; + t->align = maxal; acidtype(t); pickletype(t); return; @@ -663,7 +666,7 @@ argmark(Node *n, int pass) { Type *t; - autoffset = align(0, thisfn->link, Aarg0); + autoffset = align(0, thisfn->link, Aarg0, nil); stkoff = 0; for(; n->left != Z; n = n->left) { if(n->op != OFUNC || n->left->op != ONAME) @@ -745,9 +748,9 @@ loop: firstarg = s; firstargtype = s->type; } - autoffset = align(autoffset, s->type, Aarg1); + autoffset = align(autoffset, s->type, Aarg1, nil); s->offset = autoffset; - autoffset = align(autoffset, s->type, Aarg2); + autoffset = align(autoffset, s->type, Aarg2, nil); } else dodecl(pdecl, CXXX, types[TINT], n); break; @@ -916,7 +919,7 @@ fnproto1(Node *n) void dbgdecl(Sym *s) { - print("decl \"%s\": C=%s [B=%d:O=%ld] T=%T\n", + print("decl \"%s\": C=%s [B=%d:O=%d] T=%T\n", s->name, cnames[s->class], s->block, s->offset, s->type); } @@ -1275,7 +1278,7 @@ adecl(int c, Type *t, Sym *s) } switch(c) { case CAUTO: - autoffset = align(autoffset, t, Aaut3); + autoffset = align(autoffset, t, Aaut3, nil); stkoff = maxround(stkoff, autoffset); s->offset = -autoffset; break; @@ -1285,10 +1288,10 @@ adecl(int c, Type *t, Sym *s) firstarg = s; firstargtype = t; } - autoffset = align(autoffset, t, Aarg1); + autoffset = align(autoffset, t, Aarg1, nil); if(s) s->offset = autoffset; - autoffset = align(autoffset, t, Aarg2); + autoffset = align(autoffset, t, Aarg2, nil); break; } } @@ -1571,7 +1574,7 @@ contig(Sym *s, Node *n, int32 v) Type *zt; if(debug['i']) { - print("contig v = %ld; s = %s\n", v, s->name); + print("contig v = %d; s = %s\n", v, s->name); prtree(n, "doinit value"); } @@ -1587,7 +1590,7 @@ contig(Sym *s, Node *n, int32 v) if(v != 0) diag(n, "automatic adjustable array: %s", s->name); v = s->offset; - autoffset = align(autoffset, s->type, Aaut3); + autoffset = align(autoffset, s->type, Aaut3, nil); s->offset = -autoffset; stkoff = maxround(stkoff, autoffset); symadjust(s, n, v - s->offset); diff --git a/src/cmd/cc/dpchk.c b/src/cmd/cc/dpchk.c index 046c0e4da..6eb5fb409 100644 --- a/src/cmd/cc/dpchk.c +++ b/src/cmd/cc/dpchk.c @@ -369,7 +369,7 @@ checkargs(Node *nn, char *s, int pos) continue; for(l=tprot; l; l=l->link) if(sametype(a->type, l->type)) { -/*print("checking %T/%ulx %T/%ulx\n", a->type, flag.b[0], l->type, l->flag.b[0]);*/ +/*print("checking %T/%ux %T/%ux\n", a->type, flag.b[0], l->type, l->flag.b[0]);*/ if(beq(flag, l->flag)) goto loop; } @@ -400,6 +400,7 @@ dpcheck(Node *n) i = l->param; b = n->right; + a = Z; while(i > 0) { b = nextarg(b, &a); i--; @@ -437,9 +438,9 @@ pragpack(void) ; if(debug['f']) if(packflg) - print("%4ld: pack %d\n", lineno, packflg); + print("%4d: pack %d\n", lineno, packflg); else - print("%4ld: pack off\n", lineno); + print("%4d: pack off\n", lineno); } void @@ -459,9 +460,9 @@ pragfpround(void) ; if(debug['f']) if(fproundflg) - print("%4ld: fproundflg %d\n", lineno, fproundflg); + print("%4d: fproundflg %d\n", lineno, fproundflg); else - print("%4ld: fproundflg off\n", lineno); + print("%4d: fproundflg off\n", lineno); } void @@ -477,7 +478,7 @@ pragtextflag(void) while(getnsc() != '\n') ; if(debug['f']) - print("%4ld: textflag %d\n", lineno, textflag); + print("%4d: textflag %d\n", lineno, textflag); } void diff --git a/src/cmd/cc/lex.c b/src/cmd/cc/lex.c index c9facc667..3b413c246 100644 --- a/src/cmd/cc/lex.c +++ b/src/cmd/cc/lex.c @@ -38,7 +38,7 @@ int systemtype(int sys) { -#ifdef __MINGW32__ +#ifdef _WIN32 return sys&Windows; #else return sys&Plan9; @@ -630,7 +630,7 @@ l1: vv = c; yylval.vval = convvtox(vv, TUCHAR); if(yylval.vval != vv) - yyerror("overflow in character constant: 0x%lx", c); + yyerror("overflow in character constant: 0x%x", c); else if(c & 0x80){ nearln = lineno; @@ -1410,11 +1410,11 @@ Lconv(Fmt *fp) strcat(str, " "); } if(a[i].line) - snprint(s, STRINGSZ, "%s:%ld[%s:%ld]", + snprint(s, STRINGSZ, "%s:%d[%s:%d]", a[i].line->name, l-a[i].ldel+1, a[i].incl->name, l-a[i].idel+1); else - snprint(s, STRINGSZ, "%s:%ld", + snprint(s, STRINGSZ, "%s:%d", a[i].incl->name, l-a[i].idel+1); if(strlen(s)+strlen(str) >= STRINGSZ-10) break; @@ -1463,7 +1463,7 @@ Tconv(Fmt *fp) n = t->width; if(t->link && t->link->width) n /= t->link->width; - sprint(s, "[%ld]", n); + sprint(s, "[%d]", n); if(strlen(str) + strlen(s) < STRINGSZ) strcat(str, s); } diff --git a/src/cmd/cc/macbody b/src/cmd/cc/macbody index ca8a54c0b..35740e985 100644 --- a/src/cmd/cc/macbody +++ b/src/cmd/cc/macbody @@ -63,7 +63,7 @@ getsym(void) if(cp <= symb+NSYMB-4) *cp++ = c; c = getc(); - if(isalnum(c) || c == '_' || c >= 0x80) + if(isalnum(c) || c == '_' || c >= 0x80 || c == '$') continue; unget(c); break; diff --git a/src/cmd/cc/pgen.c b/src/cmd/cc/pgen.c index cd6fffc57..a9d7f1ef4 100644 --- a/src/cmd/cc/pgen.c +++ b/src/cmd/cc/pgen.c @@ -37,7 +37,7 @@ argsize(void) int32 s; //print("t=%T\n", thisfn); - s = align(0, thisfn->link, Aarg0); + s = align(0, thisfn->link, Aarg0, nil); for(t=thisfn->down; t!=T; t=t->down) { switch(t->etype) { case TVOID: @@ -47,8 +47,8 @@ argsize(void) s += 64; break; default: - s = align(s, t, Aarg1); - s = align(s, t, Aarg2); + s = align(s, t, Aarg1, nil); + s = align(s, t, Aarg2, nil); break; } //print(" %d %T\n", s, t); @@ -99,7 +99,7 @@ codgen(Node *n, Node *nn) nod1 = *nodret->left; nod1.sym = firstarg; nod1.type = firstargtype; - nod1.xoffset = align(0, firstargtype, Aarg1); + nod1.xoffset = align(0, firstargtype, Aarg1, nil); nod1.etype = firstargtype->etype; nodreg(&nod, &nod1, REGARG); gmove(&nod, &nod1); diff --git a/src/cmd/cc/pickle.c b/src/cmd/cc/pickle.c index fb7ec585b..82cf5eb05 100644 --- a/src/cmd/cc/pickle.c +++ b/src/cmd/cc/pickle.c @@ -143,7 +143,7 @@ picklemember(Type *t, int32 off) case TIND: if(s == S) Bprint(&outbuf, - "%s\"p\", (char*)addr+%ld+_i*%ld);\n", + "%s\"p\", (char*)addr+%d+_i*%d);\n", picklestr, t->offset+off, t->width); else Bprint(&outbuf, @@ -164,14 +164,14 @@ picklemember(Type *t, int32 off) case TFLOAT: case TDOUBLE: if(s == S) - Bprint(&outbuf, "%s\"%c\", (char*)addr+%ld+_i*%ld);\n", + Bprint(&outbuf, "%s\"%c\", (char*)addr+%d+_i*%d);\n", picklestr, picklechar[t->etype], t->offset+off, t->width); else Bprint(&outbuf, "%s\"%c\", &addr->%s);\n", picklestr, picklechar[t->etype], pmap(s->name)); break; case TARRAY: - Bprint(&outbuf, "\tfor(_i = 0; _i < %ld; _i++) {\n\t", + Bprint(&outbuf, "\tfor(_i = 0; _i < %d; _i++) {\n\t", t->width/t->link->width); picklemember(t->link, t->offset+off); Bprint(&outbuf, "\t}\n\t_i = 0;\n\tUSED(_i);\n"); @@ -183,7 +183,7 @@ picklemember(Type *t, int32 off) if(s1 == S) break; if(s == S) { - Bprint(&outbuf, "\tbp = pickle_%s(bp, ep, un, (%s*)((char*)addr+%ld+_i*%ld));\n", + Bprint(&outbuf, "\tbp = pickle_%s(bp, ep, un, (%s*)((char*)addr+%d+_i*%d));\n", pmap(s1->name), pmap(s1->name), t->offset+off, t->width); } else { Bprint(&outbuf, "\tbp = pickle_%s(bp, ep, un, &addr->%s);\n", @@ -235,7 +235,7 @@ pickletype(Type *t) break; for(l = t->link; l != T; l = l->down) if(l->sym != S) - Bprint(&outbuf, "#define\t%s.%s\t%ld\n", + Bprint(&outbuf, "#define\t%s.%s\t%d\n", s->name, l->sym->name, l->offset); diff --git a/src/cmd/cc/pswt.c b/src/cmd/cc/pswt.c index 02d4e64a0..0e402dea7 100644 --- a/src/cmd/cc/pswt.c +++ b/src/cmd/cc/pswt.c @@ -80,7 +80,7 @@ doswit(Node *n) qsort(iq, nc, sizeof(C1), swcmp); if(debug['W']) for(i=0; i 0) { c = *s++; - if(align(0, types[TCHAR], Aarg1)) { + if(align(0, types[TCHAR], Aarg1, nil)) { buf[0] = c>>8; buf[1] = c; } else { diff --git a/src/cmd/cc/sub.c b/src/cmd/cc/sub.c index 335d30bfb..e0d5df719 100644 --- a/src/cmd/cc/sub.c +++ b/src/cmd/cc/sub.c @@ -92,18 +92,18 @@ prtree1(Node *n, int d, int f) { case ONAME: print(" \"%F\"", n); - print(" %ld", n->xoffset); + print(" %d", n->xoffset); i = 0; break; case OINDREG: - print(" %ld(R%d)", n->xoffset, n->reg); + print(" %d(R%d)", n->xoffset, n->reg); i = 0; break; case OREGISTER: if(n->xoffset) - print(" %ld+R%d", n->xoffset, n->reg); + print(" %d+R%d", n->xoffset, n->reg); else print(" R%d", n->reg); i = 0; @@ -845,7 +845,7 @@ simplifyshift(Node *n) /* if(debug['h']) - print("%.3o %ld %ld %d #%.lux\n", + print("%.3o %d %d %d #%.ux\n", (s1<<3)|s2, c1, c2, topbit(c3), c3); */ diff --git a/src/cmd/cgo/Makefile b/src/cmd/cgo/Makefile index 34ca3dd46..5458c3e4f 100644 --- a/src/cmd/cgo/Makefile +++ b/src/cmd/cgo/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=cgo GOFILES=\ diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go index 580a72a95..8689ac3da 100644 --- a/src/cmd/cgo/ast.go +++ b/src/cmd/cgo/ast.go @@ -12,63 +12,13 @@ import ( "go/doc" "go/parser" "go/scanner" + "go/token" "os" "strings" ) -// A Cref refers to an expression of the form C.xxx in the AST. -type Cref struct { - Name string - Expr *ast.Expr - Context string // "type", "expr", "const", or "call" - TypeName bool // whether xxx is a C type name - Type *Type // the type of xxx - FuncType *FuncType -} - -// A ExpFunc is an exported function, callable from C. -type ExpFunc struct { - Func *ast.FuncDecl - ExpName string // name to use from C -} - -// A Prog collects information about a cgo program. -type Prog struct { - AST *ast.File // parsed AST - Preamble string // C preamble (doc comment on import "C") - PackagePath string - Package string - Crefs []*Cref - Typedef map[string]ast.Expr - Vardef map[string]*Type - Funcdef map[string]*FuncType - Enumdef map[string]int64 - Constdef map[string]string - ExpFuncs []*ExpFunc - PtrSize int64 - GccOptions []string - OutDefs map[string]bool -} - -// A Type collects information about a type in both the C and Go worlds. -type Type struct { - Size int64 - Align int64 - C string - Go ast.Expr - EnumValues map[string]int64 -} - -// A FuncType collects information about a function type in both the C and Go worlds. -type FuncType struct { - Params []*Type - Result *Type - Go *ast.FuncType -} - -func openProg(name string, p *Prog) { - var err os.Error - p.AST, err = parser.ParseFile(name, nil, nil, parser.ParseComments) +func parse(name string, flags uint) *ast.File { + ast1, err := parser.ParseFile(fset, name, nil, flags) if err != nil { if list, ok := err.(scanner.ErrorList); ok { // If err is a scanner.ErrorList, its String will print just @@ -82,25 +32,37 @@ func openProg(name string, p *Prog) { } fatal("parsing %s: %s", name, err) } - p.Package = p.AST.Name.Name() + return ast1 +} - // Find the import "C" line and get any extra C preamble. - // Delete the import "C" line along the way. +// ReadGo populates f with information learned from reading the +// Go source file with the given file name. It gathers the C preamble +// attached to the import "C" comment, a list of references to C.xxx, +// a list of exported functions, and the actual AST, to be rewritten and +// printed. +func (f *File) ReadGo(name string) { + // Two different parses: once with comments, once without. + // The printer is not good enough at printing comments in the + // right place when we start editing the AST behind its back, + // so we use ast1 to look for the doc comments on import "C" + // and on exported functions, and we use ast2 for translating + // and reprinting. + ast1 := parse(name, parser.ParseComments) + ast2 := parse(name, 0) + + f.Package = ast1.Name.Name + f.Name = make(map[string]*Name) + + // In ast1, find the import "C" line and get any extra C preamble. sawC := false - w := 0 - for _, decl := range p.AST.Decls { + for _, decl := range ast1.Decls { d, ok := decl.(*ast.GenDecl) if !ok { - p.AST.Decls[w] = decl - w++ continue } - ws := 0 for _, spec := range d.Specs { s, ok := spec.(*ast.ImportSpec) if !ok || string(s.Path.Value) != `"C"` { - d.Specs[ws] = spec - ws++ continue } sawC = true @@ -108,269 +70,330 @@ func openProg(name string, p *Prog) { error(s.Path.Pos(), `cannot rename import "C"`) } if s.Doc != nil { - p.Preamble += doc.CommentText(s.Doc) + "\n" + f.Preamble += doc.CommentText(s.Doc) + "\n" } else if len(d.Specs) == 1 && d.Doc != nil { - p.Preamble += doc.CommentText(d.Doc) + "\n" + f.Preamble += doc.CommentText(d.Doc) + "\n" + } + } + } + if !sawC { + error(token.NoPos, `cannot find import "C"`) + } + + // In ast2, strip the import "C" line. + w := 0 + for _, decl := range ast2.Decls { + d, ok := decl.(*ast.GenDecl) + if !ok { + ast2.Decls[w] = decl + w++ + continue + } + ws := 0 + for _, spec := range d.Specs { + s, ok := spec.(*ast.ImportSpec) + if !ok || string(s.Path.Value) != `"C"` { + d.Specs[ws] = spec + ws++ } } if ws == 0 { continue } d.Specs = d.Specs[0:ws] - p.AST.Decls[w] = d + ast2.Decls[w] = d w++ } - p.AST.Decls = p.AST.Decls[0:w] - - if !sawC { - error(noPos, `cannot find import "C"`) - } + ast2.Decls = ast2.Decls[0:w] // Accumulate pointers to uses of C.x. - if p.Crefs == nil { - p.Crefs = make([]*Cref, 0, 8) + if f.Ref == nil { + f.Ref = make([]*Ref, 0, 8) } - walk(p.AST, p, "prog") + f.walk(ast2, "prog", (*File).saveRef) + + // Accumulate exported functions. + // The comments are only on ast1 but we need to + // save the function bodies from ast2. + // The first walk fills in ExpFunc, and the + // second walk changes the entries to + // refer to ast2 instead. + f.walk(ast1, "prog", (*File).saveExport) + f.walk(ast2, "prog", (*File).saveExport2) + + f.AST = ast2 } -func walk(x interface{}, p *Prog, context string) { - switch n := x.(type) { - case *ast.Expr: - if sel, ok := (*n).(*ast.SelectorExpr); ok { - // For now, assume that the only instance of capital C is - // when used as the imported package identifier. - // The parser should take care of scoping in the future, - // so that we will be able to distinguish a "top-level C" - // from a local C. - if l, ok := sel.X.(*ast.Ident); ok && l.Name() == "C" { - i := len(p.Crefs) - if i >= cap(p.Crefs) { - new := make([]*Cref, 2*i) - for j, v := range p.Crefs { - new[j] = v - } - p.Crefs = new - } - p.Crefs = p.Crefs[0 : i+1] - p.Crefs[i] = &Cref{ - Name: sel.Sel.Name(), - Expr: n, - Context: context, +// Save references to C.xxx for later processing. +func (f *File) saveRef(x interface{}, context string) { + n, ok := x.(*ast.Expr) + if !ok { + return + } + if sel, ok := (*n).(*ast.SelectorExpr); ok { + // For now, assume that the only instance of capital C is + // when used as the imported package identifier. + // The parser should take care of scoping in the future, + // so that we will be able to distinguish a "top-level C" + // from a local C. + if l, ok := sel.X.(*ast.Ident); ok && l.Name == "C" { + if context == "as2" { + context = "expr" + } + goname := sel.Sel.Name + if goname == "errno" { + error(sel.Pos(), "cannot refer to errno directly; see documentation") + return + } + name := f.Name[goname] + if name == nil { + name = &Name{ + Go: goname, } - break + f.Name[goname] = name } + f.Ref = append(f.Ref, &Ref{ + Name: name, + Expr: n, + Context: context, + }) + return } - walk(*n, p, context) + } +} + +// If a function should be exported add it to ExpFunc. +func (f *File) saveExport(x interface{}, context string) { + n, ok := x.(*ast.FuncDecl) + if !ok { + return + } + + if n.Doc == nil { + return + } + for _, c := range n.Doc.List { + if string(c.Text[0:9]) != "//export " { + continue + } + + name := strings.TrimSpace(string(c.Text[9:])) + if name == "" { + error(c.Pos(), "export missing name") + } + + f.ExpFunc = append(f.ExpFunc, &ExpFunc{ + Func: n, + ExpName: name, + }) + break + } +} + +// Make f.ExpFunc[i] point at the Func from this AST instead of the other one. +func (f *File) saveExport2(x interface{}, context string) { + n, ok := x.(*ast.FuncDecl) + if !ok { + return + } + + for _, exp := range f.ExpFunc { + if exp.Func.Name.Name == n.Name.Name { + exp.Func = n + break + } + } +} + +// walk walks the AST x, calling visit(f, x, context) for each node. +func (f *File) walk(x interface{}, context string, visit func(*File, interface{}, string)) { + visit(f, x, context) + switch n := x.(type) { + case *ast.Expr: + f.walk(*n, context, visit) // everything else just recurs default: - error(noPos, "unexpected type %T in walk", x) + 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: - walk(&n.Type, p, "type") + f.walk(&n.Type, "type", visit) case *ast.FieldList: - for _, f := range n.List { - walk(f, p, context) + for _, field := range n.List { + f.walk(field, context, visit) } case *ast.BadExpr: case *ast.Ident: case *ast.Ellipsis: case *ast.BasicLit: case *ast.FuncLit: - walk(n.Type, p, "type") - walk(n.Body, p, "stmt") + f.walk(n.Type, "type", visit) + f.walk(n.Body, "stmt", visit) case *ast.CompositeLit: - walk(&n.Type, p, "type") - walk(n.Elts, p, "expr") + f.walk(&n.Type, "type", visit) + f.walk(n.Elts, "expr", visit) case *ast.ParenExpr: - walk(&n.X, p, context) + f.walk(&n.X, context, visit) case *ast.SelectorExpr: - walk(&n.X, p, "selector") + f.walk(&n.X, "selector", visit) case *ast.IndexExpr: - walk(&n.X, p, "expr") - walk(&n.Index, p, "expr") + f.walk(&n.X, "expr", visit) + f.walk(&n.Index, "expr", visit) case *ast.SliceExpr: - walk(&n.X, p, "expr") - walk(&n.Index, p, "expr") - if n.End != nil { - walk(&n.End, p, "expr") + f.walk(&n.X, "expr", visit) + if n.Low != nil { + f.walk(&n.Low, "expr", visit) + } + if n.High != nil { + f.walk(&n.High, "expr", visit) } case *ast.TypeAssertExpr: - walk(&n.X, p, "expr") - walk(&n.Type, p, "type") + f.walk(&n.X, "expr", visit) + f.walk(&n.Type, "type", visit) case *ast.CallExpr: - walk(&n.Fun, p, "call") - walk(n.Args, p, "expr") + if context == "as2" { + f.walk(&n.Fun, "call2", visit) + } else { + f.walk(&n.Fun, "call", visit) + } + f.walk(n.Args, "expr", visit) case *ast.StarExpr: - walk(&n.X, p, context) + f.walk(&n.X, context, visit) case *ast.UnaryExpr: - walk(&n.X, p, "expr") + f.walk(&n.X, "expr", visit) case *ast.BinaryExpr: - walk(&n.X, p, "expr") - walk(&n.Y, p, "expr") + f.walk(&n.X, "expr", visit) + f.walk(&n.Y, "expr", visit) case *ast.KeyValueExpr: - walk(&n.Key, p, "expr") - walk(&n.Value, p, "expr") + f.walk(&n.Key, "expr", visit) + f.walk(&n.Value, "expr", visit) case *ast.ArrayType: - walk(&n.Len, p, "expr") - walk(&n.Elt, p, "type") + f.walk(&n.Len, "expr", visit) + f.walk(&n.Elt, "type", visit) case *ast.StructType: - walk(n.Fields, p, "field") + f.walk(n.Fields, "field", visit) case *ast.FuncType: - walk(n.Params, p, "field") + f.walk(n.Params, "field", visit) if n.Results != nil { - walk(n.Results, p, "field") + f.walk(n.Results, "field", visit) } case *ast.InterfaceType: - walk(n.Methods, p, "field") + f.walk(n.Methods, "field", visit) case *ast.MapType: - walk(&n.Key, p, "type") - walk(&n.Value, p, "type") + f.walk(&n.Key, "type", visit) + f.walk(&n.Value, "type", visit) case *ast.ChanType: - walk(&n.Value, p, "type") + f.walk(&n.Value, "type", visit) case *ast.BadStmt: case *ast.DeclStmt: - walk(n.Decl, p, "decl") + f.walk(n.Decl, "decl", visit) case *ast.EmptyStmt: case *ast.LabeledStmt: - walk(n.Stmt, p, "stmt") + f.walk(n.Stmt, "stmt", visit) case *ast.ExprStmt: - walk(&n.X, p, "expr") + f.walk(&n.X, "expr", visit) case *ast.IncDecStmt: - walk(&n.X, p, "expr") + f.walk(&n.X, "expr", visit) case *ast.AssignStmt: - walk(n.Lhs, p, "expr") - walk(n.Rhs, p, "expr") + f.walk(n.Lhs, "expr", visit) + if len(n.Lhs) == 2 && len(n.Rhs) == 1 { + f.walk(n.Rhs, "as2", visit) + } else { + f.walk(n.Rhs, "expr", visit) + } case *ast.GoStmt: - walk(n.Call, p, "expr") + f.walk(n.Call, "expr", visit) case *ast.DeferStmt: - walk(n.Call, p, "expr") + f.walk(n.Call, "expr", visit) case *ast.ReturnStmt: - walk(n.Results, p, "expr") + f.walk(n.Results, "expr", visit) case *ast.BranchStmt: case *ast.BlockStmt: - walk(n.List, p, "stmt") + f.walk(n.List, "stmt", visit) case *ast.IfStmt: - walk(n.Init, p, "stmt") - walk(&n.Cond, p, "expr") - walk(n.Body, p, "stmt") - walk(n.Else, p, "stmt") + f.walk(n.Init, "stmt", visit) + f.walk(&n.Cond, "expr", visit) + f.walk(n.Body, "stmt", visit) + f.walk(n.Else, "stmt", visit) case *ast.CaseClause: - walk(n.Values, p, "expr") - walk(n.Body, p, "stmt") + f.walk(n.Values, "expr", visit) + f.walk(n.Body, "stmt", visit) case *ast.SwitchStmt: - walk(n.Init, p, "stmt") - walk(&n.Tag, p, "expr") - walk(n.Body, p, "stmt") + f.walk(n.Init, "stmt", visit) + f.walk(&n.Tag, "expr", visit) + f.walk(n.Body, "stmt", visit) case *ast.TypeCaseClause: - walk(n.Types, p, "type") - walk(n.Body, p, "stmt") + f.walk(n.Types, "type", visit) + f.walk(n.Body, "stmt", visit) case *ast.TypeSwitchStmt: - walk(n.Init, p, "stmt") - walk(n.Assign, p, "stmt") - walk(n.Body, p, "stmt") + f.walk(n.Init, "stmt", visit) + f.walk(n.Assign, "stmt", visit) + f.walk(n.Body, "stmt", visit) case *ast.CommClause: - walk(n.Lhs, p, "expr") - walk(n.Rhs, p, "expr") - walk(n.Body, p, "stmt") + f.walk(n.Lhs, "expr", visit) + f.walk(n.Rhs, "expr", visit) + f.walk(n.Body, "stmt", visit) case *ast.SelectStmt: - walk(n.Body, p, "stmt") + f.walk(n.Body, "stmt", visit) case *ast.ForStmt: - walk(n.Init, p, "stmt") - walk(&n.Cond, p, "expr") - walk(n.Post, p, "stmt") - walk(n.Body, p, "stmt") + f.walk(n.Init, "stmt", visit) + f.walk(&n.Cond, "expr", visit) + f.walk(n.Post, "stmt", visit) + f.walk(n.Body, "stmt", visit) case *ast.RangeStmt: - walk(&n.Key, p, "expr") - walk(&n.Value, p, "expr") - walk(&n.X, p, "expr") - walk(n.Body, p, "stmt") + f.walk(&n.Key, "expr", visit) + f.walk(&n.Value, "expr", visit) + f.walk(&n.X, "expr", visit) + f.walk(n.Body, "stmt", visit) case *ast.ImportSpec: case *ast.ValueSpec: - walk(&n.Type, p, "type") - walk(n.Values, p, "expr") + f.walk(&n.Type, "type", visit) + f.walk(n.Values, "expr", visit) case *ast.TypeSpec: - walk(&n.Type, p, "type") + f.walk(&n.Type, "type", visit) case *ast.BadDecl: case *ast.GenDecl: - walk(n.Specs, p, "spec") + f.walk(n.Specs, "spec", visit) case *ast.FuncDecl: if n.Recv != nil { - walk(n.Recv, p, "field") + f.walk(n.Recv, "field", visit) } - walk(n.Type, p, "type") + f.walk(n.Type, "type", visit) if n.Body != nil { - walk(n.Body, p, "stmt") + f.walk(n.Body, "stmt", visit) } - checkExpFunc(n, p) - case *ast.File: - walk(n.Decls, p, "decl") + f.walk(n.Decls, "decl", visit) case *ast.Package: - for _, f := range n.Files { - walk(f, p, "file") + for _, file := range n.Files { + f.walk(file, "file", visit) } case []ast.Decl: for _, d := range n { - walk(d, p, context) + f.walk(d, context, visit) } case []ast.Expr: for i := range n { - walk(&n[i], p, context) + f.walk(&n[i], context, visit) } case []ast.Stmt: for _, s := range n { - walk(s, p, context) + f.walk(s, context, visit) } case []ast.Spec: for _, s := range n { - walk(s, p, context) - } - } -} - -// If a function should be exported add it to ExpFuncs. -func checkExpFunc(n *ast.FuncDecl, p *Prog) { - if n.Doc == nil { - return - } - for _, c := range n.Doc.List { - if string(c.Text[0:9]) != "//export " { - continue - } - - name := strings.TrimSpace(string(c.Text[9:])) - if name == "" { - error(c.Position, "export missing name") - } - - if p.ExpFuncs == nil { - p.ExpFuncs = make([]*ExpFunc, 0, 8) + f.walk(s, context, visit) } - i := len(p.ExpFuncs) - if i >= cap(p.ExpFuncs) { - new := make([]*ExpFunc, 2*i) - for j, v := range p.ExpFuncs { - new[j] = v - } - p.ExpFuncs = new - } - p.ExpFuncs = p.ExpFuncs[0 : i+1] - p.ExpFuncs[i] = &ExpFunc{ - Func: n, - ExpName: name, - } - break } } diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index 022a87c15..0f9204d7f 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -23,6 +23,31 @@ the package. For example: // #include import "C" +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. + +The standard C numeric types are available under the names +C.char, C.schar (signed char), C.uchar (unsigned char), +C.short, C.ushort (unsigned short), C.int, C.uint (unsigned int), +C.long, C.ulong (unsigned long), C.longlong (long long), +C.ulonglong (unsigned long long), C.float, C.double. + +To access a struct, union, or enum type directly, prefix it with +struct_, union_, or enum_, as in C.struct_stat. + +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: + + n, err := C.atoi("abc") + +In C, a function argument written as a fixed size array +actually requires a pointer to the first element of the array. +C compilers are aware of this calling convention and adjust +the call accordingly, but Go cannot. In Go, you must pass +the pointer to the first element explicitly: C.f(&x[0]). + 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. diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 5e12a6687..be3b8fe64 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Annotate Crefs in Prog with C types by parsing gcc debug output. +// Annotate Ref in Prog with C types by parsing gcc debug output. // Conversion of debug output to Go types. package main @@ -12,6 +12,8 @@ import ( "debug/dwarf" "debug/elf" "debug/macho" + "debug/pe" + "flag" "fmt" "go/ast" "go/parser" @@ -21,12 +23,64 @@ import ( "strings" ) -func (p *Prog) loadDebugInfo() { +var debugDefine = flag.Bool("debug-define", false, "print relevant #defines") +var debugGcc = flag.Bool("debug-gcc", false, "print gcc invocations") + +var nameToC = map[string]string{ + "schar": "signed char", + "uchar": "unsigned char", + "ushort": "unsigned short", + "uint": "unsigned int", + "ulong": "unsigned long", + "longlong": "long long", + "ulonglong": "unsigned long long", +} + +// cname returns the C name to use for C.s. +// The expansions are listed in nameToC and also +// struct_foo becomes "struct foo", and similarly for +// union and enum. +func cname(s string) string { + if t, ok := nameToC[s]; ok { + return t + } + + if strings.HasPrefix(s, "struct_") { + return "struct " + s[len("struct_"):] + } + if strings.HasPrefix(s, "union_") { + return "union " + s[len("union_"):] + } + if strings.HasPrefix(s, "enum_") { + return "enum " + s[len("enum_"):] + } + return s +} + +// Translate rewrites f.AST, the original Go input, to remove +// references to the imported package C, replacing them with +// references to the equivalent Go types, functions, and variables. +func (p *Package) Translate(f *File) { + for _, cref := range f.Ref { + // Convert C.ulong to C.unsigned long, etc. + cref.Name.C = cname(cref.Name.Go) + } + p.loadDefines(f) + needType := p.guessKinds(f) + if len(needType) > 0 { + p.loadDWARF(f, needType) + } + p.rewriteRef(f) +} + +// loadDefines coerces gcc into spitting out the #defines in use +// in the file f and saves relevant renamings in f.Name[name].Define. +func (p *Package) loadDefines(f *File) { var b bytes.Buffer + b.WriteString(builtinProlog) + b.WriteString(f.Preamble) + stdout := p.gccDefines(b.Bytes()) - b.WriteString(p.Preamble) - stdout := p.gccPostProc(b.Bytes()) - defines := make(map[string]string) for _, line := range strings.Split(stdout, "\n", -1) { if len(line) < 9 || line[0:7] != "#define" { continue @@ -48,70 +102,112 @@ func (p *Prog) loadDebugInfo() { val = strings.TrimSpace(line[tabIndex:]) } - // Only allow string, character, and numeric constants. Ignoring #defines for - // symbols allows those symbols to be referenced in Go, as they will be - // translated by gcc later. - _, err := strconv.Atoi(string(val[0])) - if err == nil || val[0] == '\'' || val[0] == '"' { - defines[key] = val - } - } - - // Construct a slice of unique names from p.Crefs. - m := make(map[string]int) - for _, c := range p.Crefs { - // If we've already found this name as a define, it is not a Cref. - if val, ok := defines[c.Name]; ok { - _, err := parser.ParseExpr("", val, nil) - if err != nil { - fmt.Fprintf(os.Stderr, "The value in C.%s does not parse as a Go expression; cannot use.\n", c.Name) - os.Exit(2) + if n := f.Name[key]; n != nil { + if *debugDefine { + fmt.Fprintf(os.Stderr, "#define %s %s\n", key, val) } - - c.Context = "const" - c.TypeName = false - p.Constdef[c.Name] = val - continue + n.Define = val } - m[c.Name] = -1 - } - names := make([]string, 0, len(m)) - for name, _ := range m { - i := len(names) - names = names[0 : i+1] - names[i] = name - m[name] = i } +} +// guessKinds tricks gcc into revealing the kind of each +// name xxx for the references C.xxx in the Go input. +// The kind is either a constant, type, or variable. +func (p *Package) guessKinds(f *File) []*Name { // Coerce gcc into telling us whether each name is // a type, a value, or undeclared. We compile a function // containing the line: // name; // If name is a type, gcc will print: - // x.c:2: warning: useless type name in empty declaration + // cgo-test:2: warning: useless type name in empty declaration // If name is a value, gcc will print - // x.c:2: warning: statement with no effect + // cgo-test:2: warning: statement with no effect // If name is undeclared, gcc will print - // x.c:2: error: 'name' undeclared (first use in this function) + // cgo-test:2: error: 'name' undeclared (first use in this function) // A line number directive causes the line number to // correspond to the index in the names array. - b.Reset() - b.WriteString(p.Preamble) + // + // The line also has an enum declaration: + // name; enum { _cgo_enum_1 = name }; + // If name is not a constant, gcc will print: + // cgo-test:4: error: enumerator value for '_cgo_enum_4' is not an integer constant + // we assume lines without that error are constants. + + // Make list of names that need sniffing, type lookup. + toSniff := make([]*Name, 0, len(f.Name)) + needType := make([]*Name, 0, len(f.Name)) + + for _, n := range f.Name { + // If we've already found this name as a #define + // and we can translate it as a constant value, do so. + if n.Define != "" { + ok := false + 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 { + ok = true + } + } + if ok { + n.Kind = "const" + n.Const = n.Define + continue + } + + if isName(n.Define) { + n.C = n.Define + } + } + + // If this is a struct, union, or enum type name, + // record the kind but also that we need type information. + if strings.HasPrefix(n.C, "struct ") || strings.HasPrefix(n.C, "union ") || strings.HasPrefix(n.C, "enum ") { + n.Kind = "type" + i := len(needType) + needType = needType[0 : i+1] + needType[i] = n + continue + } + + i := len(toSniff) + toSniff = toSniff[0 : i+1] + toSniff[i] = n + } + + if len(toSniff) == 0 { + return needType + } + + var b bytes.Buffer + b.WriteString(builtinProlog) + b.WriteString(f.Preamble) b.WriteString("void f(void) {\n") b.WriteString("#line 0 \"cgo-test\"\n") - for _, n := range names { - b.WriteString(n) - b.WriteString(";\n") + for i, n := range toSniff { + fmt.Fprintf(&b, "%s; enum { _cgo_enum_%d = %s }; /* cgo-test:%d */\n", n.C, i, n.C, i) } b.WriteString("}\n") - - kind := make(map[string]string) - _, stderr := p.gccDebug(b.Bytes()) + stderr := p.gccErrors(b.Bytes()) if stderr == "" { - fatal("gcc produced no output") + fatal("gcc produced no output\non input:\n%s", b.Bytes()) } + + names := make([]*Name, len(toSniff)) + copy(names, toSniff) + + isConst := make([]bool, len(toSniff)) + for i := range isConst { + isConst[i] = true // until proven otherwise + } + for _, line := range strings.Split(stderr, "\n", -1) { if len(line) < 9 || line[0:9] != "cgo-test:" { + if len(line) > 8 && line[0:8] == ":" { + fatal("gcc produced unexpected output:\n%s\non input:\n%s", line, b.Bytes()) + } continue } line = line[9:] @@ -127,28 +223,52 @@ func (p *Prog) loadDebugInfo() { switch { default: continue - case strings.Index(line, ": useless type name in empty declaration") >= 0: + case strings.Contains(line, ": useless type name in empty declaration"): what = "type" - case strings.Index(line, ": statement with no effect") >= 0: - what = "value" - case strings.Index(line, "undeclared") >= 0: - what = "error" + isConst[i] = false + 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:])) + case strings.Contains(line, "is not an integer constant"): + isConst[i] = false + continue } - if old, ok := kind[names[i]]; ok && old != what { - error(noPos, "inconsistent gcc output about C.%s", names[i]) + n := toSniff[i] + if n == nil { + continue } - kind[names[i]] = what + toSniff[i] = nil + n.Kind = what + + j := len(needType) + needType = needType[0 : j+1] + needType[j] = n } - for _, n := range names { - if _, ok := kind[n]; !ok { - error(noPos, "could not determine kind of name for C.%s", n) + for i, b := range isConst { + if b { + names[i].Kind = "const" } } - + for _, n := range toSniff { + if n == nil { + continue + } + if n.Kind != "" { + continue + } + error(token.NoPos, "could not determine kind of name for C.%s", n.Go) + } if nerrors > 0 { - fatal("failed to interpret gcc output:\n%s", stderr) + fatal("unresolved names") } + return needType +} +// loadDWARF parses the DWARF debug information generated +// by gcc to learn the details of the constants, variables, and types +// being referred to as C.xxx. +func (p *Package) loadDWARF(f *File, names []*Name) { // Extract the types from the DWARF section of an object // from a well-formed C program. Gcc only generates DWARF info // for symbols in the object file, so it is not enough to print the @@ -157,19 +277,24 @@ func (p *Prog) loadDebugInfo() { // typeof(names[i]) *__cgo__i; // for each entry in names and then dereference the type we // learn for __cgo__i. - b.Reset() - b.WriteString(p.Preamble) + var b bytes.Buffer + b.WriteString(builtinProlog) + b.WriteString(f.Preamble) for i, n := range names { - fmt.Fprintf(&b, "typeof(%s) *__cgo__%d;\n", n, i) - } - d, stderr := p.gccDebug(b.Bytes()) - if d == nil { - fatal("gcc failed:\n%s\non input:\n%s", stderr, b.Bytes()) + fmt.Fprintf(&b, "typeof(%s) *__cgo__%d;\n", n.C, i) + if n.Kind == "const" { + fmt.Fprintf(&b, "enum { __cgo_enum__%d = %s };\n", i, n.C) + } } + d := p.gccDebug(b.Bytes()) // Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i. types := make([]dwarf.Type, len(names)) enums := make([]dwarf.Offset, len(names)) + nameToIndex := make(map[*Name]int) + for i, n := range names { + nameToIndex[n] = i + } r := d.Reader() for { e, err := r.Next() @@ -192,9 +317,11 @@ func (p *Prog) loadDebugInfo() { } if e.Tag == dwarf.TagEnumerator { entryName := e.Val(dwarf.AttrName).(string) - i, ok := m[entryName] - if ok { - enums[i] = offset + if strings.HasPrefix(entryName, "__cgo_enum__") { + n, _ := strconv.Atoi(entryName[len("__cgo_enum__"):]) + if 0 <= n && n < len(names) { + enums[n] = offset + } } } } @@ -234,97 +361,221 @@ func (p *Prog) loadDebugInfo() { } } - // Record types and typedef information in Crefs. + // Record types and typedef information. var conv typeConv conv.Init(p.PtrSize) - for _, c := range p.Crefs { - i, ok := m[c.Name] - if !ok { - if _, ok := p.Constdef[c.Name]; !ok { - fatal("Cref %s is no longer around", c.Name) - } - continue - } - c.TypeName = kind[c.Name] == "type" + for i, n := range names { f, fok := types[i].(*dwarf.FuncType) - if c.Context == "call" && !c.TypeName && fok { - c.FuncType = conv.FuncType(f) + if n.Kind != "type" && fok { + n.Kind = "func" + n.FuncType = conv.FuncType(f) } else { - c.Type = conv.Type(types[i]) + n.Type = conv.Type(types[i]) + 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]) + // Remove injected enum to ensure the value will deep-compare + // equally in future loads of the same constant. + n.Type.EnumValues[k] = 0, false + } } } - p.Typedef = conv.typedef } -func concat(a, b []string) []string { - c := make([]string, len(a)+len(b)) - for i, s := range a { - c[i] = s +// 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. +func (p *Package) rewriteRef(f *File) { + // Assign mangled names. + for _, n := range f.Name { + if n.Kind == "not-type" { + n.Kind = "var" + } + if n.Mangle == "" { + n.Mangle = "_C" + n.Kind + "_" + n.Go + } } - for i, s := range b { - c[i+len(a)] = s + + // Now that we have all the name types filled in, + // scan through the Refs to identify the ones that + // are trying to do a ,err call. Also check that + // functions are only used in calls. + for _, r := range f.Ref { + var expr ast.Expr = ast.NewIdent(r.Name.Mangle) // default + switch r.Context { + case "call", "call2": + if r.Name.Kind != "func" { + if r.Name.Kind == "type" { + r.Context = "type" + expr = r.Name.Type.Go + break + } + 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") + } + // Invent new Name for the two-result function. + n := f.Name["2"+r.Name.Go] + if n == nil { + n = new(Name) + *n = *r.Name + n.AddError = true + n.Mangle = "_C2func_" + n.Go + f.Name["2"+r.Name.Go] = n + } + expr = ast.NewIdent(n.Mangle) + r.Name = n + break + } + case "expr": + if r.Name.Kind == "func" { + error(r.Pos(), "must call C.%s", r.Name.Go) + } + if r.Name.Kind == "type" { + // Okay - might be new(T) + expr = r.Name.Type.Go + } + if r.Name.Kind == "var" { + expr = &ast.StarExpr{X: expr} + } + + case "type": + if r.Name.Kind != "type" { + error(r.Pos(), "expression C.%s used as type", r.Name.Go) + } else { + expr = r.Name.Type.Go + } + default: + if r.Name.Kind == "func" { + error(r.Pos(), "must call C.%s", r.Name.Go) + } + } + *r.Expr = expr } - return c } -// gccDebug runs gcc -gdwarf-2 over the C program stdin and -// returns the corresponding DWARF data and any messages -// printed to standard error. -func (p *Prog) gccDebug(stdin []byte) (*dwarf.Data, string) { - machine := "-m32" +// gccName returns the name of the compiler to run. Use $GCC if set in +// the environment, otherwise just "gcc". + +func (p *Package) gccName() (ret string) { + if ret = os.Getenv("GCC"); ret == "" { + ret = "gcc" + } + return +} + +// gccMachine returns the gcc -m flag to use, either "-m32" or "-m64". +func (p *Package) gccMachine() string { if p.PtrSize == 8 { - machine = "-m64" + return "-m64" } + return "-m32" +} - tmp := "_cgo_.o" - base := []string{ - "gcc", - machine, +const gccTmp = "_cgo_.o" + +// gccCmd returns the gcc command line to use for compiling +// the input. +func (p *Package) gccCmd() []string { + return []string{ + p.gccName(), + p.gccMachine(), "-Wall", // many warnings "-Werror", // warnings are errors - "-o" + tmp, // 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 "-xc", // input language is C "-", // read input from standard input } - _, stderr, ok := run(stdin, concat(base, p.GccOptions)) - if !ok { - return nil, string(stderr) - } +} + +// gccDebug runs gcc -gdwarf-2 over the C program stdin and +// returns the corresponding DWARF data and any messages +// printed to standard error. +func (p *Package) gccDebug(stdin []byte) *dwarf.Data { + runGcc(stdin, append(p.gccCmd(), p.GccOptions...)) // Try to parse f as ELF and Mach-O and hope one works. var f interface { DWARF() (*dwarf.Data, os.Error) } var err os.Error - if f, err = elf.Open(tmp); err != nil { - if f, err = macho.Open(tmp); err != nil { - fatal("cannot parse gcc output %s as ELF or Mach-O object", tmp) + if f, err = elf.Open(gccTmp); err != nil { + if f, err = macho.Open(gccTmp); err != nil { + if f, err = pe.Open(gccTmp); err != nil { + fatal("cannot parse gcc output %s as ELF or Mach-O or PE object", gccTmp) + } } } d, err := f.DWARF() if err != nil { - fatal("cannot load DWARF debug information from %s: %s", tmp, err) + fatal("cannot load DWARF debug information from %s: %s", gccTmp, err) } - return d, "" + return d } -func (p *Prog) gccPostProc(stdin []byte) string { - machine := "-m32" - if p.PtrSize == 8 { - machine = "-m64" +// gccDefines runs gcc -E -dM -xc - over the C program stdin +// and returns the corresponding standard output, which is the +// #defines that gcc encountered while processing the input +// and its included files. +func (p *Package) gccDefines(stdin []byte) string { + base := []string{p.gccName(), p.gccMachine(), "-E", "-dM", "-xc", "-"} + stdout, _ := runGcc(stdin, append(base, p.GccOptions...)) + return stdout +} + +// gccErrors runs gcc over the C program stdin and returns +// the errors that gcc prints. That is, this function expects +// gcc to fail. +func (p *Package) gccErrors(stdin []byte) string { + // TODO(rsc): require failure + args := append(p.gccCmd(), p.GccOptions...) + if *debugGcc { + fmt.Fprintf(os.Stderr, "$ %s < Go ident. The Go ident will // be mangled. Any existing identifier that already has the same name on // the C-side will cause the Go-mangled version to be prefixed with _. @@ -783,7 +1038,10 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[f.Name])}, Type: t.Go} off += t.Size - csyntax += t.C + " " + f.Name + "; " + buf.WriteString(t.C) + buf.WriteString(" ") + buf.WriteString(f.Name) + buf.WriteString("; ") if t.Align > align { align = t.Align } @@ -795,7 +1053,8 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s if off != dt.ByteSize { fatal("struct size calculation error") } - csyntax += "}" + buf.WriteString("}") + csyntax = buf.String() expr = &ast.StructType{Fields: &ast.FieldList{List: fld}} return } diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index 070146c9a..942bda5f4 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -11,13 +11,95 @@ package main import ( + "crypto/md5" + "flag" "fmt" "go/ast" + "go/token" + "io" "os" + "reflect" "strings" ) -func usage() { fmt.Fprint(os.Stderr, "usage: cgo [compiler options] file.go ...\n") } +// A Package collects information about the package we're going to write. +type Package struct { + PackageName string // name of package + PackagePath string + PtrSize int64 + GccOptions []string + Written map[string]bool + Name map[string]*Name // accumulated Name from Files + Typedef map[string]ast.Expr // accumulated Typedef from Files + ExpFunc []*ExpFunc // accumulated ExpFunc from Files + Decl []ast.Decl + GoFiles []string // list of Go files + GccFiles []string // list of gcc output files +} + +// A File collects information about a single Go input file. +type File struct { + AST *ast.File // parsed AST + Package string // Package name + Preamble string // C preamble (doc comment on import "C") + Ref []*Ref // all references to C.xxx in AST + ExpFunc []*ExpFunc // exported functions for this file + Name map[string]*Name // map from Go name to Name + Typedef map[string]ast.Expr // translations of all necessary types from C +} + +// A Ref refers to an expression of the form C.xxx in the AST. +type Ref struct { + Name *Name + Expr *ast.Expr + Context string // "type", "expr", "call", or "call2" +} + +func (r *Ref) Pos() token.Pos { + return (*r.Expr).Pos() +} + +// A Name collects information about C.xxx. +type Name struct { + Go string // name used in Go referring to package C + Mangle string // name used in generated Go + C string // name used in C + Define string // #define expansion + Kind string // "const", "type", "var", "func", "not-type" + Type *Type // the type of xxx + FuncType *FuncType + AddError bool + Const string // constant definition +} + +// A ExpFunc is an exported function, callable from C. +// Such functions are identified in the Go input file +// by doc comments containing the line //export ExpName +type ExpFunc struct { + Func *ast.FuncDecl + ExpName string // name to use from C +} + +// A Type collects information about a type in both the C and Go worlds. +type Type struct { + Size int64 + Align int64 + C string + Go ast.Expr + EnumValues map[string]int64 +} + +// A FuncType collects information about a function type in both the C and Go worlds. +type FuncType struct { + Params []*Type + Result *Type + Go *ast.FuncType +} + +func usage() { + fmt.Fprint(os.Stderr, "usage: cgo [compiler options] file.go ...\n") + os.Exit(2) +} var ptrSizeMap = map[string]int64{ "386": 4, @@ -25,43 +107,60 @@ var ptrSizeMap = map[string]int64{ "arm": 4, } -var expandName = map[string]string{ - "schar": "signed char", - "uchar": "unsigned char", - "ushort": "unsigned short", - "uint": "unsigned int", - "ulong": "unsigned long", - "longlong": "long long", - "ulonglong": "unsigned long long", -} +var cPrefix string + +var fset = token.NewFileSet() + +var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file") func main() { - args := os.Args - if len(args) < 2 { + flag.Usage = usage + flag.Parse() + + if *dynobj != "" { + // 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 + // 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 + // symbols and which ones to use. + syms, imports := dynimport(*dynobj) + for _, sym := range syms { + fmt.Printf("#pragma dynimport %s %s %q\n", sym, sym, "") + } + for _, p := range imports { + fmt.Printf("#pragma dynimport %s %s %q\n", "_", "_", p) + } + return + } + + args := flag.Args() + if len(args) < 1 { usage() - os.Exit(2) } // Find first arg that looks like a go file and assume everything before // that are options to pass to gcc. var i int - for i = len(args) - 1; i > 0; i-- { - if !strings.HasSuffix(args[i], ".go") { + for i = len(args); i > 0; i-- { + if !strings.HasSuffix(args[i-1], ".go") { break } } - - i += 1 - - gccOptions, goFiles := args[1:i], args[i:] + if i == len(args) { + usage() + } + gccOptions, goFiles := args[0:i], args[i:] arch := os.Getenv("GOARCH") if arch == "" { fatal("$GOARCH is not set") } - ptrSize, ok := ptrSizeMap[arch] - if !ok { - fatal("unknown architecture %s", arch) + ptrSize := ptrSizeMap[arch] + if ptrSize == 0 { + fatal("unknown $GOARCH %q", arch) } // Clear locale variables so gcc emits English errors [sic]. @@ -69,75 +168,82 @@ func main() { os.Setenv("LC_ALL", "C") os.Setenv("LC_CTYPE", "C") - p := new(Prog) - - p.PtrSize = ptrSize - p.GccOptions = gccOptions - p.Vardef = make(map[string]*Type) - p.Funcdef = make(map[string]*FuncType) - p.Enumdef = make(map[string]int64) - p.Constdef = make(map[string]string) - p.OutDefs = make(map[string]bool) + p := &Package{ + PtrSize: ptrSize, + GccOptions: gccOptions, + Written: make(map[string]bool), + } + // Need a unique prefix for the global C symbols that + // we use to coordinate between gcc and ourselves. + // We already put _cgo_ at the beginning, so the main + // concern is other cgo wrappers for the same functions. + // Use the beginning of the md5 of the input to disambiguate. + h := md5.New() for _, input := range goFiles { - // Reset p.Preamble so that we don't end up with conflicting headers / defines - p.Preamble = builtinProlog - openProg(input, p) - for _, cref := range p.Crefs { - // Convert C.ulong to C.unsigned long, etc. - if expand, ok := expandName[cref.Name]; ok { - cref.Name = expand - } + f, err := os.Open(input, os.O_RDONLY, 0) + if err != nil { + fatal("%s", err) } - p.loadDebugInfo() - for _, cref := range p.Crefs { + io.Copy(h, f) + f.Close() + } + cPrefix = fmt.Sprintf("_%x", h.Sum()[0:6]) + + for _, input := range goFiles { + f := new(File) + // Reset f.Preamble so that we don't end up with conflicting headers / defines + f.Preamble = "" + f.ReadGo(input) + p.Translate(f) + for _, cref := range f.Ref { switch cref.Context { - case "const": - // This came from a #define and we'll output it later. - *cref.Expr = ast.NewIdent(cref.Name) - break - case "call": - if !cref.TypeName { - // Is an actual function call. - pos := (*cref.Expr).Pos() - *cref.Expr = &ast.Ident{Position: pos, Obj: ast.NewObj(ast.Err, pos, "_C_"+cref.Name)} - p.Funcdef[cref.Name] = cref.FuncType - break - } - *cref.Expr = cref.Type.Go - case "expr": - if cref.TypeName { - error((*cref.Expr).Pos(), "type C.%s used as expression", cref.Name) - } - // If the expression refers to an enumerated value, then - // place the identifier for the value and add it to Enumdef so - // it will be declared as a constant in the later stage. - if cref.Type.EnumValues != nil { - *cref.Expr = ast.NewIdent(cref.Name) - p.Enumdef[cref.Name] = cref.Type.EnumValues[cref.Name] + case "call", "call2": + if cref.Name.Kind != "type" { break } - // Reference to C variable. - // We declare a pointer and arrange to have it filled in. - *cref.Expr = &ast.StarExpr{X: ast.NewIdent("_C_" + cref.Name)} - p.Vardef[cref.Name] = cref.Type - case "type": - if !cref.TypeName { - error((*cref.Expr).Pos(), "expression C.%s used as type", cref.Name) - } - *cref.Expr = cref.Type.Go + *cref.Expr = cref.Name.Type.Go } } if nerrors > 0 { os.Exit(2) } - pkg := p.Package + pkg := f.Package if dir := os.Getenv("CGOPKGPATH"); dir != "" { pkg = dir + "/" + pkg } p.PackagePath = pkg - p.writeOutput(input) + p.writeOutput(f, input) + + p.Record(f) } p.writeDefs() + if nerrors > 0 { + os.Exit(2) + } +} + +// 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) + } + + if p.Name == nil { + p.Name = f.Name + } else { + for k, v := range f.Name { + 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) + } + } + } + + p.ExpFunc = append(p.ExpFunc, f.ExpFunc...) + p.Decl = append(p.Decl, f.AST.Decls...) } diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 7cdf483f0..c3f9ae60b 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -5,6 +5,9 @@ package main import ( + "bytes" + "debug/elf" + "debug/macho" "fmt" "go/ast" "go/printer" @@ -13,30 +16,9 @@ import ( "strings" ) -func creat(name string) *os.File { - f, err := os.Open(name, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0666) - if err != nil { - fatal("%s", err) - } - return f -} - -func slashToUnderscore(c int) int { - if c == '/' { - c = '_' - } - return c -} - // 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 *Prog) writeDefs() { - pkgroot := os.Getenv("GOROOT") + "/pkg/" + os.Getenv("GOOS") + "_" + os.Getenv("GOARCH") - path := p.PackagePath - if !strings.HasPrefix(path, "/") { - path = pkgroot + "/" + path - } - +func (p *Package) writeDefs() { // The path for the shared object is slash-free so that ELF loaders // will treat it as a relative path. We rewrite slashes to underscores. sopath := "cgo_" + strings.Map(slashToUnderscore, p.PackagePath) @@ -48,229 +30,305 @@ func (p *Prog) writeDefs() { fgo2 := creat("_cgo_gotypes.go") fc := creat("_cgo_defun.c") + fm := creat("_cgo_main.c") + + // Write C main file for using gcc to resolve imports. + fmt.Fprintf(fm, "int main() { return 0; }\n") + fmt.Fprintf(fm, "int crosscall2;\n\n") // Write second Go output: definitions of _C_xxx. // In a separate file so that the import of "unsafe" does not // pollute the original file. - fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n") - fmt.Fprintf(fgo2, "package %s\n\n", p.Package) + 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, "type _ unsafe.Pointer\n\n") + fmt.Fprintf(fgo2, "func _Cerrno(dst *os.Error, x int) { *dst = os.Errno(x) }\n") - for name, def := range p.Typedef { + for name, def := range typedef { fmt.Fprintf(fgo2, "type %s ", name) - printer.Fprint(fgo2, def) + printer.Fprint(fgo2, fset, def) fmt.Fprintf(fgo2, "\n") } - fmt.Fprintf(fgo2, "type _C_void [0]byte\n") + fmt.Fprintf(fgo2, "type _Ctype_void [0]byte\n") + + fmt.Fprintf(fc, cProlog) + + var cVars []string + for _, n := range p.Name { + if n.Kind != "var" { + continue + } + cVars = append(cVars, n.C) - fmt.Fprintf(fc, cProlog, soprefix, soprefix, soprefix, soprefix, soprefix) + fmt.Fprintf(fm, "extern char %s[];\n", n.C) + fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C) + + fmt.Fprintf(fc, "extern byte *%s;\n", n.C) + fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C) + fmt.Fprintf(fc, "\n") - for name, def := range p.Vardef { - fmt.Fprintf(fc, "#pragma dynimport ·_C_%s %s \"%s%s.so\"\n", name, name, soprefix, sopath) - fmt.Fprintf(fgo2, "var _C_%s ", name) - printer.Fprint(fgo2, &ast.StarExpr{X: def.Go}) + fmt.Fprintf(fgo2, "var %s ", n.Mangle) + printer.Fprint(fgo2, fset, &ast.StarExpr{X: n.Type.Go}) fmt.Fprintf(fgo2, "\n") } fmt.Fprintf(fc, "\n") - for name, value := range p.Constdef { - fmt.Fprintf(fgo2, "const %s = %s\n", name, value) - } - - for name, value := range p.Enumdef { - fmt.Fprintf(fgo2, "const %s = %d\n", name, value) + for _, n := range p.Name { + if n.Const != "" { + fmt.Fprintf(fgo2, "const _Cconst_%s = %s\n", n.Go, n.Const) + } } fmt.Fprintf(fgo2, "\n") - for name, def := range p.Funcdef { - // Go func declaration. - d := &ast.FuncDecl{ - Name: ast.NewIdent("_C_" + name), - Type: def.Go, + for _, n := range p.Name { + if n.FuncType != nil { + p.writeDefsFunc(fc, fgo2, n, soprefix, sopath) } - printer.Fprint(fgo2, d) - fmt.Fprintf(fgo2, "\n") + } - if name == "CString" || name == "GoString" { - // The builtins are already defined in the C prolog. - continue + p.writeExports(fgo2, fc, fm) + + fgo2.Close() + fc.Close() +} + +func dynimport(obj string) (syms, imports []string) { + var f interface { + ImportedLibraries() ([]string, os.Error) + ImportedSymbols() ([]string, os.Error) + } + var isMacho bool + var err1, err2 os.Error + if f, err1 = elf.Open(obj); err1 != nil { + if f, err2 = macho.Open(obj); err2 != nil { + fatal("cannot parse %s as ELF (%v) or Mach-O (%v)", obj, err1, err2) } + isMacho = true + } - // Construct a gcc struct matching the 6c argument frame. - // Assumes that in gcc, char is 1 byte, short 2 bytes, int 4 bytes, long long 8 bytes. - // These assumptions are checked by the gccProlog. - // Also assumes that 6c convention is to word-align the - // input and output parameters. - structType := "struct {\n" - off := int64(0) - npad := 0 - for i, t := range def.Params { - if off%t.Align != 0 { - pad := t.Align - off%t.Align - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ + var err os.Error + syms, err = f.ImportedSymbols() + if err != nil { + fatal("cannot load dynamic symbols: %v", err) + } + if isMacho { + // remove leading _ that OS X insists on + for i, s := range syms { + if len(s) >= 2 && s[0] == '_' { + syms[i] = s[1:] } - structType += fmt.Sprintf("\t\t%s p%d;\n", t.C, i) - off += t.Size } - if off%p.PtrSize != 0 { - pad := p.PtrSize - off%p.PtrSize - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) + } + + imports, err = f.ImportedLibraries() + if err != nil { + fatal("cannot load dynamic imports: %v", err) + } + + return +} + +// Construct a gcc struct matching the 6c argument frame. +// Assumes that in gcc, char is 1 byte, short 2 bytes, int 4 bytes, long long 8 bytes. +// These assumptions are checked by the gccProlog. +// Also assumes that 6c convention is to word-align the +// input and output parameters. +func (p *Package) structType(n *Name) (string, int64) { + var buf bytes.Buffer + fmt.Fprint(&buf, "struct {\n") + off := int64(0) + for i, t := range n.FuncType.Params { + if off%t.Align != 0 { + pad := t.Align - off%t.Align + fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) off += pad - npad++ - } - if t := def.Result; t != nil { - if off%t.Align != 0 { - pad := t.Align - off%t.Align - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ - } - structType += fmt.Sprintf("\t\t%s r;\n", t.C) - off += t.Size } - if off%p.PtrSize != 0 { - pad := p.PtrSize - off%p.PtrSize - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) + fmt.Fprintf(&buf, "\t\t%s p%d;\n", t.C, i) + off += t.Size + } + if off%p.PtrSize != 0 { + pad := p.PtrSize - off%p.PtrSize + fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) + off += pad + } + if t := n.FuncType.Result; t != nil { + if off%t.Align != 0 { + pad := t.Align - off%t.Align + fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) off += pad - npad++ } - if len(def.Params) == 0 && def.Result == nil { - structType += "\t\tchar unused;\n" // avoid empty struct - off++ + qual := "" + if t.C[len(t.C)-1] == '*' { + qual = "const " } - structType += "\t}" - argSize := off + fmt.Fprintf(&buf, "\t\t%s%s r;\n", qual, t.C) + off += t.Size + } + if off%p.PtrSize != 0 { + pad := p.PtrSize - off%p.PtrSize + fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) + off += pad + } + if n.AddError { + fmt.Fprint(&buf, "\t\tvoid *e[2]; /* os.Error */\n") + off += 2 * p.PtrSize + } + if off == 0 { + fmt.Fprintf(&buf, "\t\tchar unused;\n") // avoid empty struct + off++ + } + fmt.Fprintf(&buf, "\t}") + return buf.String(), off +} - // C wrapper calls into gcc, passing a pointer to the argument frame. - // Also emit #pragma to get a pointer to the gcc wrapper. - fmt.Fprintf(fc, "#pragma dynimport _cgo_%s _cgo_%s \"%s%s.so\"\n", name, name, soprefix, sopath) - fmt.Fprintf(fc, "void (*_cgo_%s)(void*);\n", name) - fmt.Fprintf(fc, "\n") - fmt.Fprintf(fc, "void\n") - fmt.Fprintf(fc, "·_C_%s(struct{uint8 x[%d];}p)\n", name, argSize) - fmt.Fprintf(fc, "{\n") - fmt.Fprintf(fc, "\tcgocall(_cgo_%s, &p);\n", name) - fmt.Fprintf(fc, "}\n") - fmt.Fprintf(fc, "\n") +func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name, soprefix, sopath string) { + name := n.Go + gtype := n.FuncType.Go + if n.AddError { + // Add "os.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")} + l := gtype.Results.List + if len(l) == 0 { + l = []*ast.Field{err} + } else { + l = []*ast.Field{l[0], err} + } + t := new(ast.FuncType) + *t = *gtype + t.Results = &ast.FieldList{List: l} + gtype = t } - p.writeExports(fgo2, fc) + // Go func declaration. + d := &ast.FuncDecl{ + Name: ast.NewIdent(n.Mangle), + Type: gtype, + } + printer.Fprint(fgo2, fset, d) + fmt.Fprintf(fgo2, "\n") - fgo2.Close() - fc.Close() + if name == "CString" || name == "GoString" || name == "GoStringN" { + // The builtins are already defined in the C prolog. + return + } + + var argSize int64 + _, argSize = p.structType(n) + + // C wrapper calls into gcc, passing a pointer to the argument frame. + fmt.Fprintf(fc, "void _cgo%s%s(void*);\n", cPrefix, n.Mangle) + fmt.Fprintf(fc, "\n") + fmt.Fprintf(fc, "void\n") + fmt.Fprintf(fc, "·%s(struct{uint8 x[%d];}p)\n", n.Mangle, argSize) + fmt.Fprintf(fc, "{\n") + fmt.Fprintf(fc, "\truntime·cgocall(_cgo%s%s, &p);\n", cPrefix, n.Mangle) + if n.AddError { + // gcc leaves errno in first word of interface at end of p. + // check whether it is zero; if so, turn interface into nil. + // if not, turn interface into errno. + // Go init function initializes ·_Cerrno with an os.Errno + // for us to copy. + fmt.Fprintln(fc, ` { + int32 e; + void **v; + v = (void**)(&p+1) - 2; /* v = final two void* of p */ + e = *(int32*)v; + v[0] = (void*)0xdeadbeef; + v[1] = (void*)0xdeadbeef; + if(e == 0) { + /* nil interface */ + v[0] = 0; + v[1] = 0; + } else { + ·_Cerrno(v, e); /* fill in v as os.Error for errno e */ + } + }`) + } + fmt.Fprintf(fc, "}\n") + fmt.Fprintf(fc, "\n") } // writeOutput creates stubs for a specific source file to be compiled by 6g // (The comments here say 6g and 6c but the code applies to the 8 and 5 tools too.) -func (p *Prog) writeOutput(srcfile string) { +func (p *Package) writeOutput(f *File, srcfile string) { base := srcfile if strings.HasSuffix(base, ".go") { base = base[0 : len(base)-3] } + base = strings.Map(slashToUnderscore, base) fgo1 := creat(base + ".cgo1.go") fgcc := creat(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") + fmt.Fprintf(fgo1, "// Created by cgo - DO NOT EDIT\n\n") fmt.Fprintf(fgo1, "//line %s:1\n", srcfile) - printer.Fprint(fgo1, p.AST) + printer.Fprint(fgo1, fset, f.AST) // While we process the vars and funcs, also write 6c and gcc output. // Gcc output starts with the preamble. - fmt.Fprintf(fgcc, "%s\n", p.Preamble) + fmt.Fprintf(fgcc, "%s\n", f.Preamble) fmt.Fprintf(fgcc, "%s\n", gccProlog) - for name, def := range p.Funcdef { - _, ok := p.OutDefs[name] - if name == "CString" || name == "GoString" || ok { - // The builtins are already defined in the C prolog, and we don't - // want to duplicate function definitions we've already done. - continue - } - p.OutDefs[name] = true - - // Construct a gcc struct matching the 6c argument frame. - // Assumes that in gcc, char is 1 byte, short 2 bytes, int 4 bytes, long long 8 bytes. - // These assumptions are checked by the gccProlog. - // Also assumes that 6c convention is to word-align the - // input and output parameters. - structType := "struct {\n" - off := int64(0) - npad := 0 - for i, t := range def.Params { - if off%t.Align != 0 { - pad := t.Align - off%t.Align - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ - } - structType += fmt.Sprintf("\t\t%s p%d;\n", t.C, i) - off += t.Size + for _, n := range f.Name { + if n.FuncType != nil { + p.writeOutputFunc(fgcc, n) } - if off%p.PtrSize != 0 { - pad := p.PtrSize - off%p.PtrSize - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ - } - if t := def.Result; t != nil { - if off%t.Align != 0 { - pad := t.Align - off%t.Align - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ - } - structType += fmt.Sprintf("\t\t%s r;\n", t.C) - off += t.Size - } - if off%p.PtrSize != 0 { - pad := p.PtrSize - off%p.PtrSize - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ - } - if len(def.Params) == 0 && def.Result == nil { - structType += "\t\tchar unused;\n" // avoid empty struct - off++ - } - structType += "\t}" - - // Gcc wrapper unpacks the C argument struct - // and calls the actual C function. - fmt.Fprintf(fgcc, "void\n") - fmt.Fprintf(fgcc, "_cgo_%s(void *v)\n", name) - fmt.Fprintf(fgcc, "{\n") - fmt.Fprintf(fgcc, "\t%s *a = v;\n", structType) - fmt.Fprintf(fgcc, "\t") - if def.Result != nil { - fmt.Fprintf(fgcc, "a->r = ") - } - fmt.Fprintf(fgcc, "%s(", name) - for i := range def.Params { - if i > 0 { - fmt.Fprintf(fgcc, ", ") - } - fmt.Fprintf(fgcc, "a->p%d", i) - } - fmt.Fprintf(fgcc, ");\n") - fmt.Fprintf(fgcc, "}\n") - fmt.Fprintf(fgcc, "\n") } fgo1.Close() fgcc.Close() } -// Write out the various stubs we need to support functions exported -// from Go so that they are callable from C. -func (p *Prog) writeExports(fgo2, fc *os.File) { - if len(p.ExpFuncs) == 0 { +func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { + name := n.Mangle + if name == "_Cfunc_CString" || name == "_Cfunc_GoString" || name == "_Cfunc_GoStringN" || p.Written[name] { + // The builtins are already defined in the C prolog, and we don't + // want to duplicate function definitions we've already done. return } + p.Written[name] = true + + ctype, _ := p.structType(n) + + // Gcc wrapper unpacks the C argument struct + // and calls the actual C function. + fmt.Fprintf(fgcc, "void\n") + fmt.Fprintf(fgcc, "_cgo%s%s(void *v)\n", cPrefix, n.Mangle) + fmt.Fprintf(fgcc, "{\n") + if n.AddError { + fmt.Fprintf(fgcc, "\tint e;\n") // assuming 32 bit (see comment above structType) + fmt.Fprintf(fgcc, "\terrno = 0;\n") + } + fmt.Fprintf(fgcc, "\t%s *a = v;\n", ctype) + fmt.Fprintf(fgcc, "\t") + if n.FuncType.Result != nil { + fmt.Fprintf(fgcc, "a->r = ") + } + fmt.Fprintf(fgcc, "%s(", n.C) + for i := range n.FuncType.Params { + if i > 0 { + fmt.Fprintf(fgcc, ", ") + } + fmt.Fprintf(fgcc, "a->p%d", i) + } + fmt.Fprintf(fgcc, ");\n") + if n.AddError { + fmt.Fprintf(fgcc, "\t*(int*)(a->e) = errno;\n") + } + fmt.Fprintf(fgcc, "}\n") + fmt.Fprintf(fgcc, "\n") +} +// 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("_cgo_export.c") fgcch := creat("_cgo_export.h") @@ -280,17 +338,17 @@ func (p *Prog) writeExports(fgo2, fc *os.File) { fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n") fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n") - for _, exp := range p.ExpFuncs { + for _, exp := range p.ExpFunc { fn := exp.Func // Construct a gcc struct matching the 6c argument and // result frame. - structType := "struct {\n" + ctype := "struct {\n" off := int64(0) npad := 0 if fn.Recv != nil { t := p.cgoType(fn.Recv.List[0].Type) - structType += fmt.Sprintf("\t\t%s recv;\n", t.C) + ctype += fmt.Sprintf("\t\t%s recv;\n", t.C) off += t.Size } fntype := fn.Type @@ -299,16 +357,16 @@ func (p *Prog) writeExports(fgo2, fc *os.File) { t := p.cgoType(atype) if off%t.Align != 0 { pad := t.Align - off%t.Align - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) + ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) off += pad npad++ } - structType += fmt.Sprintf("\t\t%s p%d;\n", t.C, i) + ctype += fmt.Sprintf("\t\t%s p%d;\n", t.C, i) off += t.Size }) if off%p.PtrSize != 0 { pad := p.PtrSize - off%p.PtrSize - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) + ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) off += pad npad++ } @@ -317,24 +375,24 @@ func (p *Prog) writeExports(fgo2, fc *os.File) { t := p.cgoType(atype) if off%t.Align != 0 { pad := t.Align - off%t.Align - structType += fmt.Sprintf("\t\tchar __pad%d[%d]\n", npad, pad) + ctype += fmt.Sprintf("\t\tchar __pad%d[%d]\n", npad, pad) off += pad npad++ } - structType += fmt.Sprintf("\t\t%s r%d;\n", t.C, i) + ctype += fmt.Sprintf("\t\t%s r%d;\n", t.C, i) off += t.Size }) if off%p.PtrSize != 0 { pad := p.PtrSize - off%p.PtrSize - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) + ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) off += pad npad++ } - if structType == "struct {\n" { - structType += "\t\tchar unused;\n" // avoid empty struct + if ctype == "struct {\n" { + ctype += "\t\tchar unused;\n" // avoid empty struct off++ } - structType += "\t}" + ctype += "\t}" // Get the return type of the wrapper function // compiled by gcc. @@ -370,10 +428,10 @@ func (p *Prog) writeExports(fgo2, fc *os.File) { s += ")" fmt.Fprintf(fgcch, "\nextern %s;\n", s) - fmt.Fprintf(fgcc, "extern _cgoexp_%s(void *, int);\n", exp.ExpName) + fmt.Fprintf(fgcc, "extern _cgoexp%s_%s(void *, int);\n", cPrefix, exp.ExpName) fmt.Fprintf(fgcc, "\n%s\n", s) fmt.Fprintf(fgcc, "{\n") - fmt.Fprintf(fgcc, "\t%s a;\n", structType) + fmt.Fprintf(fgcc, "\t%s a;\n", ctype) if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) { fmt.Fprintf(fgcc, "\t%s r;\n", gccResult) } @@ -384,7 +442,7 @@ func (p *Prog) writeExports(fgo2, fc *os.File) { func(i int, atype ast.Expr) { fmt.Fprintf(fgcc, "\ta.p%d = p%d;\n", i, i) }) - fmt.Fprintf(fgcc, "\tcrosscall2(_cgoexp_%s, &a, (int) sizeof a);\n", exp.ExpName) + fmt.Fprintf(fgcc, "\tcrosscall2(_cgoexp%s_%s, &a, (int) sizeof a);\n", cPrefix, exp.ExpName) if gccResult != "void" { if len(fntype.Results.List) == 1 && len(fntype.Results.List[0].Names) <= 1 { fmt.Fprintf(fgcc, "\treturn a.r0;\n") @@ -399,27 +457,28 @@ func (p *Prog) writeExports(fgo2, fc *os.File) { fmt.Fprintf(fgcc, "}\n") // Build the wrapper function compiled by 6c/8c - goname := exp.Func.Name.Name() + goname := exp.Func.Name.Name if fn.Recv != nil { - goname = "_cgoexpwrap_" + fn.Recv.List[0].Names[0].Name() + "_" + goname + goname = "_cgoexpwrap" + cPrefix + "_" + fn.Recv.List[0].Names[0].Name + "_" + goname } - fmt.Fprintf(fc, "#pragma dynexport _cgoexp_%s _cgoexp_%s\n", exp.ExpName, exp.ExpName) fmt.Fprintf(fc, "extern void ·%s();\n", goname) fmt.Fprintf(fc, "\nvoid\n") - fmt.Fprintf(fc, "_cgoexp_%s(void *a, int32 n)\n", exp.ExpName) + fmt.Fprintf(fc, "_cgoexp%s_%s(void *a, int32 n)\n", cPrefix, exp.ExpName) fmt.Fprintf(fc, "{\n") - fmt.Fprintf(fc, "\tcgocallback(·%s, a, n);\n", goname) + fmt.Fprintf(fc, "\truntime·cgocallback(·%s, a, n);\n", goname) fmt.Fprintf(fc, "}\n") + fmt.Fprintf(fm, "int _cgoexp%s_%s;\n", cPrefix, exp.ExpName) + // Calling a function with a receiver from C requires // a Go wrapper function. if fn.Recv != nil { fmt.Fprintf(fgo2, "func %s(recv ", goname) - printer.Fprint(fgo2, fn.Recv.List[0].Type) + printer.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, atype) + fmt.Fprintf(fgo2, ", p%d ", i) + printer.Fprint(fgo2, fset, atype) }) fmt.Fprintf(fgo2, ")") if gccResult != "void" { @@ -429,7 +488,7 @@ func (p *Prog) writeExports(fgo2, fc *os.File) { if i > 0 { fmt.Fprint(fgo2, ", ") } - printer.Fprint(fgo2, atype) + printer.Fprint(fgo2, fset, atype) }) fmt.Fprint(fgo2, ")") } @@ -493,7 +552,7 @@ var goTypes = map[string]*Type{ } // Map an ast type to a Type. -func (p *Prog) cgoType(e ast.Expr) *Type { +func (p *Package) cgoType(e ast.Expr) *Type { switch t := e.(type) { case *ast.StarExpr: x := p.cgoType(t.X) @@ -515,7 +574,7 @@ func (p *Prog) cgoType(e ast.Expr) *Type { case *ast.Ident: // Look up the type in the top level declarations. // TODO: Handle types defined within a function. - for _, d := range p.AST.Decls { + for _, d := range p.Decl { gd, ok := d.(*ast.GenDecl) if !ok || gd.Tok != token.TYPE { continue @@ -525,30 +584,35 @@ func (p *Prog) cgoType(e ast.Expr) *Type { if !ok { continue } - if ts.Name.Name() == t.Name() { + if ts.Name.Name == t.Name { return p.cgoType(ts.Type) } } } - for name, def := range p.Typedef { - if name == t.Name() { + for name, def := range typedef { + if name == t.Name { return p.cgoType(def) } } - if t.Name() == "uintptr" { + if t.Name == "uintptr" { return &Type{Size: p.PtrSize, Align: p.PtrSize, C: "uintptr"} } - if t.Name() == "string" { + if t.Name == "string" { return &Type{Size: p.PtrSize + 4, Align: p.PtrSize, C: "GoString"} } - if r, ok := goTypes[t.Name()]; ok { + if r, ok := goTypes[t.Name]; ok { if r.Align > p.PtrSize { r.Align = p.PtrSize } return r } + case *ast.SelectorExpr: + id, ok := t.X.(*ast.Ident) + if ok && id.Name == "unsafe" && t.Sel.Name == "Pointer" { + return &Type{Size: p.PtrSize, Align: p.PtrSize, C: "void*"} + } } - error(e.Pos(), "unrecognized Go type %v", e) + error(e.Pos(), "unrecognized Go type %T", e) return &Type{Size: 4, Align: 4, C: "int"} } @@ -568,11 +632,15 @@ typedef long long __cgo_long_long; __cgo_size_assert(__cgo_long_long, 8) __cgo_size_assert(float, 4) __cgo_size_assert(double, 8) + +#include +#include ` const builtinProlog = ` typedef struct { char *p; int n; } _GoString_; _GoString_ GoString(char *p); +_GoString_ GoStringN(char *p, int l); char *CString(_GoString_); ` @@ -580,24 +648,27 @@ const cProlog = ` #include "runtime.h" #include "cgocall.h" -#pragma dynimport initcgo initcgo "%slibcgo.so" -#pragma dynimport libcgo_thread_start libcgo_thread_start "%slibcgo.so" -#pragma dynimport libcgo_set_scheduler libcgo_set_scheduler "%slibcgo.so" -#pragma dynimport _cgo_malloc _cgo_malloc "%slibcgo.so" -#pragma dynimport _cgo_free _cgo_free "%slibcgo.so" +void ·_Cerrno(void*, int32); + +void +·_Cfunc_GoString(int8 *p, String s) +{ + s = runtime·gostring((byte*)p); + FLUSH(&s); +} void -·_C_GoString(int8 *p, String s) +·_Cfunc_GoStringN(int8 *p, int32 l, String s) { - s = gostring((byte*)p); + s = runtime·gostringn((byte*)p, l); FLUSH(&s); } void -·_C_CString(String s, int8 *p) +·_Cfunc_CString(String s, int8 *p) { - p = cmalloc(s.len+1); - mcpy((byte*)p, s.str, s.len); + p = runtime·cmalloc(s.len+1); + runtime·mcpy((byte*)p, s.str, s.len); p[s.len] = 0; FLUSH(&p); } @@ -610,6 +681,7 @@ typedef unsigned char uchar; typedef unsigned short ushort; typedef long long int64; typedef unsigned long long uint64; +typedef __SIZE_TYPE__ uintptr; typedef struct { char *p; int n; } GoString; typedef void *GoMap; diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go index 95067039c..a6f509dc4 100644 --- a/src/cmd/cgo/util.go +++ b/src/cmd/cgo/util.go @@ -12,16 +12,6 @@ import ( "os" ) -// A ByteReaderAt implements io.ReadAt using a slice of bytes. -type ByteReaderAt []byte - -func (r ByteReaderAt) ReadAt(p []byte, off int64) (n int, err os.Error) { - if off >= int64(len(r)) || off < 0 { - return 0, os.EOF - } - return copy(p, r[off:]), nil -} - // run runs the command argv, feeding in stdin on standard input. // It returns the output to standard output and standard error. // ok indicates whether the command exited successfully. @@ -55,9 +45,8 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { w0.Close() c <- true }() - var xstdout []byte // TODO(rsc): delete after 6g can take address of out parameter go func() { - xstdout, _ = ioutil.ReadAll(r1) + stdout, _ = ioutil.ReadAll(r1) r1.Close() c <- true }() @@ -65,7 +54,6 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { r2.Close() <-c <-c - stdout = xstdout w, err := os.Wait(pid, 0) if err != nil { @@ -77,18 +65,45 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { // Die with an error message. func fatal(msg string, args ...interface{}) { - fmt.Fprintf(os.Stderr, msg+"\n", args) + fmt.Fprintf(os.Stderr, msg+"\n", args...) os.Exit(2) } var nerrors int -var noPos token.Position -func error(pos token.Position, msg string, args ...interface{}) { +func error(pos token.Pos, msg string, args ...interface{}) { nerrors++ if pos.IsValid() { - fmt.Fprintf(os.Stderr, "%s: ", pos) + fmt.Fprintf(os.Stderr, "%s: ", fset.Position(pos).String()) } - fmt.Fprintf(os.Stderr, msg, args) + fmt.Fprintf(os.Stderr, msg, args...) fmt.Fprintf(os.Stderr, "\n") } + +// isName returns true if s is a valid C identifier +func isName(s string) bool { + for i, v := range s { + if v != '_' && (v < 'A' || v > 'Z') && (v < 'a' || v > 'z') && (v < '0' || v > '9') { + return false + } + if i == 0 && '0' <= v && v <= '9' { + return false + } + } + return s != "" +} + +func creat(name string) *os.File { + f, err := os.Open(name, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0666) + if err != nil { + fatal("%s", err) + } + return f +} + +func slashToUnderscore(c int) int { + if c == '/' { + c = '_' + } + return c +} diff --git a/src/cmd/clean.bash b/src/cmd/clean.bash index 9317b8ae5..6349919a8 100644 --- a/src/cmd/clean.bash +++ b/src/cmd/clean.bash @@ -3,11 +3,9 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -GOBIN="${GOBIN:-$HOME/bin}" - for i in cc 6l 6a 6c 8l 8a 8c 8g 5l 5a 5c 5g gc 6g gopack nm cgo cov ebnflint godefs godoc gofmt goinstall gotest goyacc hgpatch prof do cd $i - "$GOBIN"/gomake clean + gomake clean cd .. done diff --git a/src/cmd/cov/Makefile b/src/cmd/cov/Makefile index 58cb2302c..fdeb14636 100644 --- a/src/cmd/cov/Makefile +++ b/src/cmd/cov/Makefile @@ -2,7 +2,8 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +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 @@ -16,15 +17,22 @@ OFILES=\ HFILES=\ tree.h\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lmach -lbio -l9 +LIB=\ + ../../../lib/libmach.a\ -clean: - rm -f *.$O $(TARG) +NOINSTALL=1 +include ../../Make.ccmd -install: install-$(shell uname | tr A-Z a-z) +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) @@ -32,5 +40,3 @@ install-darwin: $(TARG) install-default: $(TARG) cp $(TARG) "$(GOBIN)"/$(TARG) - -$(OFILES): $(HFILES) diff --git a/src/cmd/cov/main.c b/src/cmd/cov/main.c index 1b3138a7f..5ff22c00a 100644 --- a/src/cmd/cov/main.c +++ b/src/cmd/cov/main.c @@ -81,7 +81,7 @@ ran(uvlong pc, uvlong epc) key.epc = pc+1; r = treeget(&breakpoints, &key); if(r == nil) - sysfatal("unchecked breakpoint at %#lux+%d", pc, (int)(epc-pc)); + 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. diff --git a/src/cmd/ebnflint/Makefile b/src/cmd/ebnflint/Makefile index 8cb9fd821..8f030aaef 100644 --- a/src/cmd/ebnflint/Makefile +++ b/src/cmd/ebnflint/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=ebnflint GOFILES=\ @@ -11,5 +11,5 @@ GOFILES=\ include ../../Make.cmd test: $(TARG) - $(QUOTED_GOBIN)/$(TARG) -start="SourceFile" "$(GOROOT)"/doc/go_spec.html + $(TARG) -start="SourceFile" "$(GOROOT)"/doc/go_spec.html diff --git a/src/cmd/ebnflint/ebnflint.go b/src/cmd/ebnflint/ebnflint.go index 3dfa71f07..10cb5b387 100644 --- a/src/cmd/ebnflint/ebnflint.go +++ b/src/cmd/ebnflint/ebnflint.go @@ -10,12 +10,14 @@ import ( "flag" "fmt" "go/scanner" + "go/token" "io/ioutil" "os" "path" ) +var fset = token.NewFileSet() var start = flag.String("start", "Start", "name of start production") @@ -92,12 +94,12 @@ func main() { src = extractEBNF(src) } - grammar, err := ebnf.Parse(filename, src) + grammar, err := ebnf.Parse(fset, filename, src) if err != nil { scanner.PrintError(os.Stderr, err) } - if err = ebnf.Verify(grammar, *start); err != nil { + if err = ebnf.Verify(fset, grammar, *start); err != nil { scanner.PrintError(os.Stderr, err) } } diff --git a/src/cmd/gc/Makefile b/src/cmd/gc/Makefile index 46dc6dfbc..dbfd86474 100644 --- a/src/cmd/gc/Makefile +++ b/src/cmd/gc/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -LIB=\ - gc.a$O\ +LIB=gc.a HFILES=\ go.h\ @@ -43,16 +43,10 @@ OFILES=\ walk.$O\ y1.tab.$O\ -$(LIB): $(OFILES) - ar rsc $(LIB) $(OFILES) +NOINSTALL=1 +include ../../Make.clib -$(OFILES): $(HFILES) - -y.tab.h: $(YFILES) - LANG=C LANGUAGE="en_US.UTF8" bison -v -y $(YFLAGS) $(YFILES) - -y.tab.c: y.tab.h - test -f y.tab.c && touch y.tab.c +install: $(LIB) y1.tab.c: y.tab.c # 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/' >y1.tab.c @@ -70,7 +64,4 @@ subr.$O: opnames.h opnames.h: mkopnames go.h ./mkopnames go.h >opnames.h -clean: - rm -f *.[568o] enam.c [568].out a.out y.tab.h y.tab.c y1.tab.c y.output yerr.h $(LIB) mkbuiltin1 builtin.c _builtin.c opnames.h - -install: $(LIB) +CLEANFILES+=*.[568] [568].out y1.tab.c yerr.h mkbuiltin1 builtin.c _builtin.c opnames.h diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c index 1b9112d69..a3785e871 100644 --- a/src/cmd/gc/align.c +++ b/src/cmd/gc/align.c @@ -7,8 +7,8 @@ /* * machine size and rounding * alignment is dictated around - * the size of a pointer, set in belexinit - * (see ../6g/align.c). + * the size of a pointer, set in betypeinit + * (see ../6g/galign.c). */ static int defercalc; @@ -16,15 +16,9 @@ static int defercalc; uint32 rnd(uint32 o, uint32 r) { - if(maxround == 0) + if(r < 1 || r > 8 || (r&(r-1)) != 0) fatal("rnd"); - - if(r > maxround) - r = maxround; - if(r != 0) - while(o%r != 0) - o++; - return o; + return (o+r-1)&~(r-1); } static void @@ -42,30 +36,25 @@ offmod(Type *t) } } -static uint32 -arrayelemwidth(Type *t) -{ - - while(t->etype == TARRAY && t->bound >= 0) - t = t->type; - return t->width; -} - static uint32 widstruct(Type *t, uint32 o, int flag) { Type *f; - int32 w, m; - + int32 w, maxalign; + + maxalign = flag; + if(maxalign < 1) + maxalign = 1; for(f=t->type; f!=T; f=f->down) { if(f->etype != TFIELD) fatal("widstruct: not TFIELD: %lT", f); dowidth(f->type); + if(f->type->align > maxalign) + maxalign = f->type->align; if(f->type->width < 0) fatal("invalid width %lld", f->type->width); w = f->type->width; - m = arrayelemwidth(f->type); - o = rnd(o, m); + o = rnd(o, f->type->align); f->width = o; // really offset for TFIELD if(f->nname != N) { // this same stackparam logic is in addrescapes @@ -82,7 +71,8 @@ widstruct(Type *t, uint32 o, int flag) } // final width is rounded if(flag) - o = rnd(o, maxround); + o = rnd(o, maxalign); + t->align = maxalign; // type width only includes back to first field's offset if(t->type == T) @@ -100,7 +90,7 @@ dowidth(Type *t) int lno; Type *t1; - if(maxround == 0 || widthptr == 0) + if(widthptr == 0) fatal("dowidth without betypeinit"); if(t == T) @@ -124,6 +114,7 @@ dowidth(Type *t) lno = lineno; lineno = t->lineno; t->width = -2; + t->align = 0; et = t->etype; switch(et) { @@ -166,9 +157,11 @@ dowidth(Type *t) case TFLOAT64: case TCOMPLEX64: w = 8; + t->align = widthptr; break; case TCOMPLEX128: w = 16; + t->align = widthptr; break; case TPTR32: w = 4; @@ -180,6 +173,7 @@ dowidth(Type *t) break; case TINTER: // implemented as 2 pointers w = 2*widthptr; + t->align = widthptr; offmod(t); break; case TCHAN: // implemented as pointer @@ -197,6 +191,7 @@ dowidth(Type *t) dowidth(t->type); // just in case if(t1->type->width >= (1<<16)) yyerror("channel element type too large (>64kB)"); + t->width = 1; break; case TMAP: // implemented as pointer w = widthptr; @@ -217,6 +212,7 @@ dowidth(Type *t) if(sizeof_String == 0) fatal("early dowidth string"); w = sizeof_String; + t->align = widthptr; break; case TARRAY: if(t->type == T) @@ -235,11 +231,13 @@ dowidth(Type *t) yyerror("type %lT larger than address space", t); w = t->bound * t->type->width; if(w == 0) - w = maxround; + w = 1; + t->align = t->type->align; } else if(t->bound == -1) { w = sizeof_Array; checkwidth(t->type); + t->align = widthptr; } else if(t->bound == -100) yyerror("use of [...] array outside of array literal"); @@ -252,7 +250,9 @@ dowidth(Type *t) fatal("dowidth fn struct %T", t); w = widstruct(t, 0, 1); if(w == 0) - w = maxround; + w = 1; + //if(t->align < widthptr) + // warn("align %d: %T\n", t->align, t); break; case TFUNC: @@ -271,16 +271,24 @@ dowidth(Type *t) // compute their widths as side-effect. t1 = t->type; w = widstruct(*getthis(t1), 0, 0); - w = widstruct(*getinarg(t1), w, 1); - w = widstruct(*getoutarg(t1), w, 1); + w = widstruct(*getinarg(t1), w, widthptr); + w = widstruct(*getoutarg(t1), w, widthptr); t1->argwid = w; + if(w%widthptr) + warn("bad type %T %d\n", t1, w); + t->align = 1; break; } // catch all for error cases; avoid divide by zero later if(w == 0) - w = maxround; + w = 1; t->width = w; + if(t->align == 0) { + if(w > 8 || (w&(w-1)) != 0) + fatal("invalid alignment for %T", t); + t->align = w; + } lineno = lno; if(defercalc == 1) @@ -351,8 +359,8 @@ void defercheckwidth(void) { // we get out of sync on syntax errors, so don't be pedantic. - // if(defercalc) - // fatal("defercheckwidth"); + if(defercalc && nerrors == 0) + fatal("defercheckwidth"); defercalc = 1; } @@ -596,10 +604,10 @@ typeinit(void) Array_array = rnd(0, widthptr); Array_nel = rnd(Array_array+widthptr, types[TUINT32]->width); Array_cap = rnd(Array_nel+types[TUINT32]->width, types[TUINT32]->width); - sizeof_Array = rnd(Array_cap+types[TUINT32]->width, maxround); + sizeof_Array = rnd(Array_cap+types[TUINT32]->width, widthptr); // string is same as slice wo the cap - sizeof_String = rnd(Array_nel+types[TUINT32]->width, maxround); + sizeof_String = rnd(Array_nel+types[TUINT32]->width, widthptr); dowidth(types[TSTRING]); dowidth(idealstring); diff --git a/src/cmd/gc/builtin.c.boot b/src/cmd/gc/builtin.c.boot index 3e2d98872..380abc642 100644 --- a/src/cmd/gc/builtin.c.boot +++ b/src/cmd/gc/builtin.c.boot @@ -1,6 +1,6 @@ char *runtimeimport = "package runtime\n" - "func \"\".mal (? int32) *any\n" + "func \"\".new (? int32) *any\n" "func \"\".panicindex ()\n" "func \"\".panicslice ()\n" "func \"\".throwreturn ()\n" @@ -19,12 +19,13 @@ char *runtimeimport = "func \"\".printslice (? any)\n" "func \"\".printnl ()\n" "func \"\".printsp ()\n" - "func \"\".printf ()\n" - "func \"\".catstring (? string, ? string) string\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 \"\".indexstring (? string, ? int) uint8\n" "func \"\".intstring (? int64) string\n" "func \"\".slicebytetostring (? []uint8) string\n" "func \"\".sliceinttostring (? []int) string\n" @@ -33,6 +34,7 @@ char *runtimeimport = "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" @@ -75,16 +77,18 @@ char *runtimeimport = "func \"\".selectdefault (sel *uint8) bool\n" "func \"\".selectgo (sel *uint8)\n" "func \"\".makeslice (typ *uint8, nel int64, cap int64) []any\n" - "func \"\".sliceslice1 (old []any, lb int, width int) []any\n" - "func \"\".sliceslice (old []any, lb int, hb int, width int) []any\n" - "func \"\".slicearray (old *any, nel int, lb int, hb int, width int) []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"; diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c index a24a03a49..eb7014366 100644 --- a/src/cmd/gc/closure.c +++ b/src/cmd/gc/closure.c @@ -158,7 +158,7 @@ walkclosure(Node *func, NodeList **init) // create the function xfunc = nod(ODCLFUNC, N, N); - snprint(namebuf, sizeof namebuf, "_func_%.3ld", ++closgen); + snprint(namebuf, sizeof namebuf, "_func_%.3d", ++closgen); xfunc->nname = newname(lookup(namebuf)); xfunc->nname->ntype = xtype; xfunc->nname->defn = xfunc; diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c index cec95359a..72e67a634 100644 --- a/src/cmd/gc/const.c +++ b/src/cmd/gc/const.c @@ -101,7 +101,7 @@ convlit1(Node **np, Type *t, int explicit) break; case OLSH: case ORSH: - convlit1(&n->left, t, explicit); + convlit1(&n->left, t, explicit && isideal(n->left->type)); t = n->left->type; if(t != T && !isint[t->etype]) { yyerror("invalid operation: %#N (shift of type %T)", n, t); @@ -202,8 +202,6 @@ convlit1(Node **np, Type *t, int explicit) goto bad; case CTFLT: case CTINT: - if(explicit) - goto bad; n->val = tocplx(n->val); break; case CTCPLX: @@ -300,7 +298,7 @@ toflt(Val v) f = mal(sizeof(*f)); mpmovefltflt(f, &v.u.cval->real); if(mpcmpfltc(&v.u.cval->imag, 0) != 0) - yyerror("constant %#F truncated to real", v.u.fval); + yyerror("constant %#F%+#Fi truncated to real", &v.u.cval->real, &v.u.cval->imag); v.ctype = CTFLT; v.u.fval = f; break; @@ -324,9 +322,9 @@ toint(Val v) case CTCPLX: i = mal(sizeof(*i)); if(mpmovefltfix(i, &v.u.cval->real) < 0) - yyerror("constant %#F truncated to integer", v.u.fval); + yyerror("constant %#F%+#Fi truncated to integer", &v.u.cval->real, &v.u.cval->imag); if(mpcmpfltc(&v.u.cval->imag, 0) != 0) - yyerror("constant %#F truncated to real", v.u.fval); + yyerror("constant %#F%+#Fi truncated to real", &v.u.cval->real, &v.u.cval->imag); v.ctype = CTINT; v.u.xval = i; break; @@ -536,6 +534,12 @@ evconst(Node *n) v = toflt(v); rv = toflt(rv); } + if(v.ctype != rv.ctype) { + // Use of undefined name as constant? + if((v.ctype == 0 || rv.ctype == 0) && nerrors > 0) + return; + fatal("constant type mismatch %T(%d) %T(%d)", nl->type, v.ctype, nr->type, rv.ctype); + } // run op switch(TUP(n->op, v.ctype)) { @@ -1086,6 +1090,12 @@ smallintconst(Node *n) case TBOOL: case TPTR32: return 1; + case TINT64: + case TUINT64: + if(mpcmpfixfix(n->val.u.xval, minintval[TINT32]) < 0 + || mpcmpfixfix(n->val.u.xval, maxintval[TINT32]) > 0) + break; + return 1; } return 0; } diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c index adb1531c3..a71272aa2 100644 --- a/src/cmd/gc/dcl.c +++ b/src/cmd/gc/dcl.c @@ -729,8 +729,11 @@ stotype(NodeList *l, int et, Type **t) n->right = N; if(n->embedded && n->type != T) { t1 = n->type; - if(t1->sym == S && isptr[t1->etype]) + 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) @@ -841,6 +844,8 @@ dostruct(NodeList *l, int et) t->broke = 1; return t; } + if(et == TINTER) + t = sortinter(t); if(!funarg) checkwidth(t); return t; @@ -1017,11 +1022,12 @@ functype(Node *this, NodeList *in, NodeList *out) } Sym* -methodsym(Sym *nsym, Type *t0) +methodsym(Sym *nsym, Type *t0, int iface) { Sym *s; char *p; Type *t; + char *suffix; t = t0; if(t == T) @@ -1043,7 +1049,13 @@ methodsym(Sym *nsym, Type *t0) if(t != t0 && t0->sym) t0 = ptrto(t); - p = smprint("%#hT·%s", t0, nsym->name); + suffix = ""; + if(iface) { + dowidth(t0); + if(t0->width < types[tptr]->width) + suffix = "·i"; + } + p = smprint("%#hT·%s%s", t0, nsym->name, suffix); s = pkglookup(p, s->pkg); free(p); return s; @@ -1058,7 +1070,7 @@ methodname(Node *n, Type *t) { Sym *s; - s = methodsym(n->sym, t); + s = methodsym(n->sym, t, 0); if(s == S) return n; return newname(s); diff --git a/src/cmd/gc/doc.go b/src/cmd/gc/doc.go index 108a091b2..21e1b103b 100644 --- a/src/cmd/gc/doc.go +++ b/src/cmd/gc/doc.go @@ -25,13 +25,18 @@ other packages. It is therefore not necessary when compiling client C of package P to read the files of P's dependencies, only the compiled output of P. -Usage: 6g [flags] *.go (or 8g or 5g) +Usage: + 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. Flags: -o file - output file, default 6.out for 6g, etc. + output file, default file.6 for 6g, etc. -e normally the compiler quits after 10 errors; -e prints all errors + -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 diff --git a/src/cmd/gc/export.c b/src/cmd/gc/export.c index 52853c454..594509915 100644 --- a/src/cmd/gc/export.c +++ b/src/cmd/gc/export.c @@ -176,7 +176,8 @@ dumpexporttype(Sym *s) yyerror("export of incomplete type %T", t); return; } - Bprint(bout, "type %#T %l#T\n", t, t); + if(Bprint(bout, "type %#T %l#T\n", t, t) < 0) + fatal("Bprint failed for %T", t); } static int diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c index fd8a7f39b..04af5a7bb 100644 --- a/src/cmd/gc/gen.c +++ b/src/cmd/gc/gen.c @@ -58,7 +58,7 @@ allocparams(void) if(w >= MAXWIDTH) fatal("bad width"); stksize += w; - stksize = rnd(stksize, w); + stksize = rnd(stksize, n->type->align); n->xoffset = -stksize; } lineno = lno; @@ -139,8 +139,10 @@ gen(Node *n) Prog *scontin, *sbreak; Prog *p1, *p2, *p3; Label *lab; + int32 wasregalloc; lno = setlineno(n); + wasregalloc = anyregalloc(); if(n == N) goto ret; @@ -246,9 +248,6 @@ gen(Node *n) gen(n->nincr); // contin: incr patch(p1, pc); // test: - if(n->ntest != N) - if(n->ntest->ninit != nil) - genlist(n->ntest->ninit); bgen(n->ntest, 0, breakpc); // if(!test) goto break genlist(n->nbody); // body gjmp(continpc); @@ -261,9 +260,6 @@ gen(Node *n) p1 = gjmp(P); // goto test p2 = gjmp(P); // p2: goto else patch(p1, pc); // test: - if(n->ntest != N) - if(n->ntest->ninit != nil) - genlist(n->ntest->ninit); bgen(n->ntest, 0, p2); // if(!test) goto p2 genlist(n->nbody); // then p3 = gjmp(P); // goto done @@ -342,6 +338,11 @@ gen(Node *n) } ret: + if(anyregalloc() != wasregalloc) { + dump("node", n); + fatal("registers left allocated"); + } + lineno = lno; } @@ -432,7 +433,7 @@ cgen_discard(Node *nr) switch(nr->op) { case ONAME: - if(!(nr->class & PHEAP) && nr->class != PEXTERN && nr->class != PFUNC) + if(!(nr->class & PHEAP) && nr->class != PEXTERN && nr->class != PFUNC && nr->class != PPARAMREF) gused(nr); break; @@ -651,7 +652,6 @@ tempname(Node *n, Type *t) snprint(namebuf, sizeof(namebuf), "autotmp_%.4d", statuniqgen); statuniqgen++; s = lookup(namebuf); - memset(n, 0, sizeof(*n)); n->op = ONAME; n->sym = s; @@ -664,7 +664,7 @@ tempname(Node *n, Type *t) dowidth(t); w = t->width; stksize += w; - stksize = rnd(stksize, w); + stksize = rnd(stksize, t->align); n->xoffset = -stksize; n->pun = anyregalloc(); } diff --git a/src/cmd/gc/go.errors b/src/cmd/gc/go.errors index cdd7578d4..b5af4678c 100644 --- a/src/cmd/gc/go.errors +++ b/src/cmd/gc/go.errors @@ -35,6 +35,15 @@ static struct { % loadsys package imports LTYPE LNAME ';' "unexpected semicolon or newline in type declaration", + % loadsys package imports LCHAN '}' + "unexpected } in channel type", + + % loadsys package imports LCHAN ')' + "unexpected ) in channel type", + + % loadsys package imports LCHAN ',' + "unexpected comma in channel type", + % loadsys package imports LFUNC LNAME '(' ')' '{' if_stmt ';' LELSE "unexpected semicolon or newline before else", diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index 99e369eca..73ea5b976 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -6,6 +6,8 @@ #include #include +#undef OAPPEND + // avoid #undef isblank #define isblank goisblank @@ -38,9 +40,10 @@ enum ASTRING, AINTER, ANILINTER, + AMEMWORD, BADWIDTH = -1000000000, - MAXWIDTH = 1<<30 + MAXWIDTH = 1<<30 }; /* @@ -151,6 +154,7 @@ struct Type uchar deferwidth; uchar broke; uchar isddd; // TFIELD is ... argument + uchar align; Node* nod; // canonical OTYPE node Type* orig; // original type (type literal or predefined type) @@ -209,12 +213,15 @@ struct Node uchar dodata; // compile literal assignment as data statement uchar used; uchar isddd; - uchar pun; // dont registerize variable ONAME + uchar pun; // don't registerize variable ONAME + uchar readonly; + uchar implicit; // don't show in printout // most nodes Node* left; Node* right; Type* type; + Type* realtype; // as determined by typecheck NodeList* list; NodeList* rlist; @@ -261,6 +268,7 @@ struct Node Sym* sym; // various int32 vargen; // unique name for OTYPE/ONAME int32 lineno; + int32 endlineno; vlong xoffset; int32 ostk; int32 iota; @@ -344,6 +352,7 @@ enum OADD, OSUB, OOR, OXOR, OADDSTR, OADDR, OANDAND, + OAPPEND, OARRAY, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, @@ -356,7 +365,7 @@ enum OCLOSURE, OCMPIFACE, OCMPSTR, OCOMPLIT, OMAPLIT, OSTRUCTLIT, OARRAYLIT, - OCONV, OCONVIFACE, OCONVNOP, OCONVSLICE, + OCONV, OCONVIFACE, OCONVNOP, OCOPY, ODCL, ODCLFUNC, ODCLFIELD, ODCLCONST, ODCLTYPE, ODOT, ODOTPTR, ODOTMETH, ODOTINTER, OXDOT, @@ -364,7 +373,7 @@ enum ODOTTYPE2, OEQ, ONE, OLT, OLE, OGE, OGT, OIND, - OINDEX, OINDEXSTR, OINDEXMAP, + OINDEX, OINDEXMAP, OKEY, OPARAM, OLEN, OMAKE, OMAKECHAN, OMAKEMAP, OMAKESLICE, @@ -400,7 +409,6 @@ enum ORETURN, OSELECT, OSWITCH, - OTYPECASE, OTYPESW, // l = r.(type) // types @@ -410,6 +418,7 @@ enum OTINTER, OTFUNC, OTARRAY, + OTPAREN, // misc ODDD, @@ -632,9 +641,9 @@ EXTERN Label* labellist; * * typedef struct * { // must not move anything - * uchar array[8]; // pointer to data - * uchar nel[4]; // number of elements - * uchar cap[4]; // allocated number of elements + * uchar array[8]; // pointer to data + * uchar nel[4]; // number of elements + * uchar cap[4]; // allocated number of elements * } Array; */ EXTERN int Array_array; // runtime offsetof(Array,array) - same for String @@ -649,8 +658,8 @@ EXTERN int sizeof_Array; // runtime sizeof(Array) * * typedef struct * { // must not move anything - * uchar array[8]; // pointer to data - * uchar nel[4]; // number of elements + * uchar array[8]; // pointer to data + * uchar nel[4]; // number of elements * } String; */ EXTERN int sizeof_String; // runtime sizeof(String) @@ -743,7 +752,6 @@ EXTERN int hasdefer; // flag that curfn has defer statetment EXTERN Node* curfn; -EXTERN int maxround; EXTERN int widthptr; EXTERN Node* typesw; @@ -858,7 +866,7 @@ int isifacemethod(Type *f); void markdcl(void); Node* methodname(Node *n, Type *t); Node* methodname1(Node *n, Node *t); -Sym* methodsym(Sym *nsym, Type *t0); +Sym* methodsym(Sym *nsym, Type *t0, int iface); Node* newname(Sym *s); Type* newtype(Sym *s); Node* oldname(Sym *s); @@ -914,6 +922,9 @@ char* lexname(int lex); void mkpackage(char* pkgname); void unimportfile(void); int32 yylex(void); +extern int windows; +extern int yylast; +extern int yyprev; /* * mparith1.c @@ -1003,7 +1014,7 @@ void walkrange(Node *n); * reflect.c */ void dumptypestructs(void); -Type* methodfunc(Type *f, int use_receiver); +Type* methodfunc(Type *f, Type*); Node* typename(Type *t); Sym* typesym(Type *t); @@ -1016,7 +1027,7 @@ void walkselect(Node *sel); /* * sinit.c */ -void anylit(int ctxt, Node *n, Node *var, NodeList **init); +void anylit(int, Node *n, Node *var, NodeList **init); int gen_as_init(Node *n); NodeList* initfix(NodeList *l); int oaslit(Node *n, NodeList **init); @@ -1059,7 +1070,7 @@ 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); +void genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface); Type** getinarg(Type *t); Type* getinargx(Type *t); Type** getoutarg(Type *t); @@ -1095,6 +1106,7 @@ Node* nod(int op, Node *nleft, Node *nright); Node* nodbool(int b); void nodconst(Node *n, Type *t, int64 v); Node* nodintconst(int64 v); +Node* nodfltconst(Mpflt *v); Node* nodnil(void); int parserline(void); Sym* pkglookup(char *name, Pkg *pkg); @@ -1103,13 +1115,13 @@ Type* ptrto(Type *t); void* remal(void *p, int32 on, int32 n); Sym* restrictlookup(char *name, Pkg *pkg); Node* safeexpr(Node *n, NodeList **init); +Node* cheapexpr(Node *n, NodeList **init); int32 setlineno(Node *n); void setmaxarg(Type *t); Type* shallow(Type *t); int simsimtype(Type *t); void smagic(Magic *m); Type* sortinter(Type *t); -Node* staticname(Type *t); uint32 stringhash(char *p); Strlit* strlit(char *s); int structcount(Type *t); @@ -1143,7 +1155,7 @@ void typechecklist(NodeList *l, int top); /* * unsafe.c */ -Node* unsafenmagic(Node *fn, NodeList *args); +Node* unsafenmagic(Node *n); /* * walk.c @@ -1206,7 +1218,7 @@ void dumpfuncs(void); void gdata(Node*, Node*, int); void gdatacomplex(Node*, Mpcplx*); void gdatastring(Node*, Strlit*); -void genembedtramp(Type*, Type*, Sym*); +void genembedtramp(Type*, Type*, Sym*, int iface); void ggloblnod(Node *nam, int32 width); void ggloblsym(Sym *s, int32 width, int dupok); Prog* gjmp(Prog*); diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index c46abaa56..917265758 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -20,6 +20,8 @@ %{ #include /* if we don't, bison will, and go.h re-#defines getc */ #include "go.h" + +static void fixlbrace(int); %} %union { Node* node; @@ -50,7 +52,7 @@ %type stmt ntype %type arg_type %type case caseblock -%type compound_stmt dotname embed expr +%type compound_stmt dotname embed expr complitexpr %type expr_or_type %type fndcl fnliteral %type for_body for_header for_stmt if_header if_stmt non_dcl_stmt @@ -58,7 +60,7 @@ %type name_or_type non_expr_type %type new_name dcl_name oexpr typedclname %type onew_name -%type osimple_stmt pexpr +%type osimple_stmt pexpr pexpr_no_paren %type pseudocall range_stmt select_stmt %type simple_stmt %type switch_stmt uexpr @@ -66,7 +68,7 @@ %type xdcl fnbody fnres switch_body loop_body dcl_name_list %type new_name_list expr_list keyval_list braced_keyval_list expr_or_type_list xdcl_list -%type oexpr_list oexpr_or_type_list_ocomma caseblock_list stmt_list oarg_type_list_ocomma arg_type_list +%type oexpr_list caseblock_list stmt_list oarg_type_list_ocomma arg_type_list %type interfacedcl_list vardcl vardcl_list structdcl structdcl_list %type common_dcl constdcl constdcl1 constdcl_list typedcl_list @@ -459,7 +461,7 @@ case: } break; } -| LCASE name '=' expr ':' +| LCASE expr '=' expr ':' { // will be converted to OCASE // right will point to next case @@ -515,10 +517,33 @@ switch_body: } caseblock: - case stmt_list + case + { + // 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; + } + stmt_list { + 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"); $$ = $1; - $$->nbody = $2; + $$->nbody = $3; } caseblock_list: @@ -648,11 +673,13 @@ select_stmt: LSELECT { markdcl(); + typesw = nod(OXXX, typesw, N); } switch_body { $$ = nod(OSELECT, N, N); $$->list = $3; + typesw = typesw->left; popdcl(); } @@ -783,13 +810,23 @@ uexpr: * can be preceded by 'defer' and 'go' */ pseudocall: - pexpr '(' oexpr_or_type_list_ocomma ')' + pexpr '(' ')' + { + $$ = nod(OCALL, $1, N); + } +| pexpr '(' expr_or_type_list ocomma ')' { $$ = nod(OCALL, $1, N); $$->list = $3; } +| pexpr '(' expr_or_type_list LDDD ocomma ')' + { + $$ = nod(OCALL, $1, N); + $$->list = $3; + $$->isddd = 1; + } -pexpr: +pexpr_no_paren: LLITERAL { $$ = nodlit($1); @@ -806,10 +843,6 @@ pexpr: } $$ = nod(OXDOT, $1, newname($3)); } -| '(' expr_or_type ')' - { - $$ = $2; - } | pexpr '.' '(' expr_or_type ')' { $$ = nod(ODOTTYPE, $1, $4); @@ -824,10 +857,6 @@ pexpr: } | pexpr '[' oexpr ':' oexpr ']' { - if($3 == N) { - yyerror("missing lower bound in slice expression"); - $3 = nodintconst(0); - } $$ = nod(OSLICE, $1, nod(OKEY, $3, $5)); } | pseudocall @@ -842,21 +871,45 @@ pexpr: // composite expression $$ = nod(OCOMPLIT, N, $1); $$->list = $3; - - // 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($2 == LBODY) - loophack = 1; + + fixlbrace($2); } -| pexpr '{' braced_keyval_list '}' +| pexpr_no_paren '{' braced_keyval_list '}' { // composite expression $$ = nod(OCOMPLIT, N, $1); $$->list = $3; } +| '(' expr_or_type ')' '{' braced_keyval_list '}' + { + yyerror("cannot parenthesize type in composite literal"); + // composite expression + $$ = nod(OCOMPLIT, N, $2); + $$->list = $5; + } | fnliteral +keyval: + expr ':' complitexpr + { + $$ = nod(OKEY, $1, $3); + } + +complitexpr: + expr +| '{' braced_keyval_list '}' + { + $$ = nod(OCOMPLIT, N, N); + $$->list = $2; + } + +pexpr: + pexpr_no_paren +| '(' expr_or_type ')' + { + $$ = $2; + } + expr_or_type: expr | non_expr_type %prec PreferToRightParen @@ -939,7 +992,7 @@ ntype: | dotname | '(' ntype ')' { - $$ = $2; + $$ = nod(OTPAREN, $2, N); } non_expr_type: @@ -958,7 +1011,7 @@ non_recvchantype: | dotname | '(' ntype ')' { - $$ = $2; + $$ = nod(OTPAREN, $2, N); } convtype: @@ -1030,34 +1083,31 @@ recvchantype: } structtype: - LSTRUCT '{' structdcl_list osemi '}' + LSTRUCT lbrace structdcl_list osemi '}' { $$ = nod(OTSTRUCT, N, N); $$->list = $3; + fixlbrace($2); } -| LSTRUCT '{' '}' +| LSTRUCT lbrace '}' { $$ = nod(OTSTRUCT, N, N); + fixlbrace($2); } interfacetype: - LINTERFACE '{' interfacedcl_list osemi '}' + LINTERFACE lbrace interfacedcl_list osemi '}' { $$ = nod(OTINTER, N, N); $$->list = $3; + fixlbrace($2); } -| LINTERFACE '{' '}' +| LINTERFACE lbrace '}' { $$ = nod(OTINTER, N, N); + fixlbrace($2); } -keyval: - expr ':' expr - { - $$ = nod(OKEY, $1, $3); - } - - /* * function stuff * all in one place to show how crappy it all is @@ -1069,6 +1119,7 @@ xfndcl: if($$ == N) break; $$->nbody = $3; + $$->endlineno = lineno; funcbody($$); } @@ -1118,6 +1169,8 @@ fndcl: 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"); $$ = nod(ODCLFUNC, N, N); $$->nname = methodname1(name, rcvr->right); @@ -1250,12 +1303,32 @@ structdcl: $1->val = $2; $$ = list1($1); } +| '(' embed ')' oliteral + { + $2->val = $4; + $$ = list1($2); + yyerror("cannot parenthesize embedded type"); + } | '*' embed oliteral { $2->right = nod(OIND, $2->right, N); $2->val = $3; $$ = list1($2); } +| '(' '*' embed ')' oliteral + { + $3->right = nod(OIND, $3->right, N); + $3->val = $5; + $$ = list1($3); + yyerror("cannot parenthesize embedded type"); + } +| '*' '(' embed ')' oliteral + { + $3->right = nod(OIND, $3->right, N); + $3->val = $5; + $$ = list1($3); + yyerror("cannot parenthesize embedded type"); + } packname: LNAME @@ -1296,6 +1369,11 @@ interfacedcl: { $$ = nod(ODCLFIELD, N, oldname($1)); } +| '(' packname ')' + { + $$ = nod(ODCLFIELD, N, oldname($2)); + yyerror("cannot parenthesize embedded type"); + } indcl: '(' oarg_type_list_ocomma ')' fnres @@ -1481,7 +1559,7 @@ keyval_list: { $$ = list1($1); } -| expr +| complitexpr { $$ = list1($1); } @@ -1489,7 +1567,7 @@ keyval_list: { $$ = list($1, $3); } -| keyval_list ',' expr +| keyval_list ',' complitexpr { $$ = list($1, $3); } @@ -1524,12 +1602,6 @@ oexpr_list: } | expr_list -oexpr_or_type_list_ocomma: - { - $$ = nil; - } -| expr_or_type_list ocomma - osimple_stmt: { $$ = N; @@ -1654,7 +1726,6 @@ hidden_type_misc: | LINTERFACE '{' ohidden_interfacedcl_list '}' { $$ = dostruct($3, TINTER); - $$ = sortinter($$); } | '*' hidden_type { @@ -1867,3 +1938,16 @@ hidden_interfacedcl_list: { $$ = list($1, $3); } + +%% + +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/lex.c b/src/cmd/gc/lex.c index 452acfc76..0f1acd2fc 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -14,6 +14,8 @@ extern int yychar; int windows; +int yyprev; +int yylast; static void lexinit(void); static void lexfini(void); @@ -23,9 +25,45 @@ static void ungetc(int); static int32 getr(void); static int escchar(int, int*, vlong*); static void addidir(char*); - +static int getlinepragma(void); static char *goos, *goarch, *goroot; +// Our own isdigit, isspace, isalpha, isalnum that take care +// of EOF and other out of range arguments. +static int +yy_isdigit(int c) +{ + return c >= 0 && c <= 0xFF && isdigit(c); +} + +static int +yy_isspace(int c) +{ + return c >= 0 && c <= 0xFF && isspace(c); +} + +static int +yy_isalpha(int c) +{ + return c >= 0 && c <= 0xFF && isalpha(c); +} + +static int +yy_isalnum(int c) +{ + return c >= 0 && c <= 0xFF && isalnum(c); +} + +// Disallow use of isdigit etc. +#undef isdigit +#undef isspace +#undef isalpha +#undef isalnum +#define isdigit use_yy_isdigit_instead_of_isdigit +#define isspace use_yy_isspace_instead_of_isspace +#define isalpha use_yy_isalpha_instead_of_isalpha +#define isalnum use_yy_isalnum_instead_of_isalnum + #define DBG if(!debug['x']);else print enum { @@ -35,7 +73,7 @@ enum void usage(void) { - print("usage: %cg [flags] file.go...\n"); + print("gc: usage: %cg [flags] file.go...\n", thechar); print("flags:\n"); // -A is allow use of "any" type, for bootstrapping print(" -I DIR search for packages in DIR\n"); @@ -52,12 +90,27 @@ usage(void) exit(0); } +void +fault(int 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 + // the code and try again. + if(nerrors > 0) + errorexit(); + fatal("fault"); +} + int main(int argc, char *argv[]) { int i, c; NodeList *l; char *p; + + signal(SIGBUS, fault); + signal(SIGSEGV, fault); localpkg = mkpkg(strlit("")); localpkg->prefix = "\"\""; @@ -119,7 +172,7 @@ main(int argc, char *argv[]) if(getwd(pathname, 999) == 0) strcpy(pathname, "/???"); - if(isalpha(pathname[0]) && pathname[1] == ':') { + if(yy_isalpha(pathname[0]) && pathname[1] == ':') { // On Windows. windows = 1; @@ -141,7 +194,7 @@ main(int argc, char *argv[]) fmtinstall('F', Fconv); // big float numbers betypeinit(); - if(maxround == 0 || widthptr == 0) + if(widthptr == 0) fatal("betypeinit failed"); lexinit(); @@ -287,7 +340,7 @@ islocalname(Strlit *name) if(!windows && name->len >= 1 && name->s[0] == '/') return 1; if(windows && name->len >= 3 && - isalpha(name->s[0]) && name->s[1] == ':' && name->s[2] == '/') + yy_isalpha(name->s[0]) && name->s[1] == ':' && name->s[2] == '/') return 1; if(name->len >= 2 && strncmp(name->s, "./", 2) == 0) return 1; @@ -300,8 +353,11 @@ static int findpkg(Strlit *name) { Idir *p; + char *q; if(islocalname(name)) { + if(safemode) + return 0; // try .a before .6. important for building libraries: // if there is an array.6 in the array.a library, // want to find all of array.a, not just array.6. @@ -314,6 +370,18 @@ findpkg(Strlit *name) return 0; } + // local imports should be canonicalized already. + // don't want to see "container/../container/vector" + // as different from "container/vector". + q = mal(name->len+1); + memmove(q, name->s, name->len); + q[name->len] = '\0'; + cleanname(q); + if(strlen(q) != name->len || memcmp(q, name->s, name->len) != 0) { + yyerror("non-canonical import path %Z (should be %s)", name, q); + return 0; + } + for(p = idirs; p != nil; p = p->link) { snprint(namebuf, sizeof(namebuf), "%s/%Z.a", p->dir, name); if(access(namebuf, 0) >= 0) @@ -368,7 +436,9 @@ importfile(Val *f, int line) path = f->u.sval; if(islocalname(path)) { cleanbuf = mal(strlen(pathname) + strlen(path->s) + 2); - sprint(cleanbuf, "%s/%s", pathname, path->s); + strcpy(cleanbuf, pathname); + strcat(cleanbuf, "/"); + strcat(cleanbuf, path->s); cleanname(cleanbuf); path = strlit(cleanbuf); } @@ -381,7 +451,7 @@ importfile(Val *f, int line) imp = Bopen(namebuf, OREAD); if(imp == nil) { - yyerror("can't open import: %Z", f->u.sval); + yyerror("can't open import: %Z: %r", f->u.sval); errorexit(); } file = strdup(namebuf); @@ -470,7 +540,7 @@ isfrog(int c) return 0; return 1; } - if(0x80 <= c && c <= 0xa0) // unicode block including unbreakable space. + if(0x7f <= c && c <= 0xa0) // DEL, unicode block including unbreakable space. return 1; return 0; } @@ -496,7 +566,7 @@ _yylex(void) l0: c = getc(); - if(isspace(c)) { + if(yy_isspace(c)) { if(c == '\n' && curio.nlsemi) { ungetc(c); DBG("lex: implicit semi\n"); @@ -514,13 +584,13 @@ l0: goto talph; } - if(isalpha(c)) { + if(yy_isalpha(c)) { cp = lexbuf; ep = lexbuf+sizeof lexbuf; goto talph; } - if(isdigit(c)) + if(yy_isdigit(c)) goto tnum; switch(c) { @@ -536,7 +606,7 @@ l0: case '.': c1 = getc(); - if(isdigit(c1)) { + if(yy_isdigit(c1)) { cp = lexbuf; ep = lexbuf+sizeof lexbuf; *cp++ = c; @@ -656,16 +726,13 @@ l0: } } if(c1 == '/') { + c = getlinepragma(); for(;;) { - c = getr(); - if(c == '\n') { + if(c == '\n' || c == EOF) { ungetc(c); goto l0; } - if(c == EOF) { - yyerror("eof in comment"); - errorexit(); - } + c = getr(); } } if(c1 == '=') { @@ -878,6 +945,10 @@ lx: yyerror("illegal character 0x%ux", c); goto l0; } + if(importpkg == nil && (c == '#' || c == '$' || c == '?' || c == '@' || c == '\\')) { + yyerror("%s: unexpected %c", "syntax error", c); + goto l0; + } return c; asop: @@ -902,7 +973,7 @@ talph: if(!isalpharune(rune) && !isdigitrune(rune) && (importpkg == nil || rune != 0xb7)) yyerror("invalid identifier character 0x%ux", rune); cp += runetochar(cp, &rune); - } else if(!isalnum(c) && c != '_') + } else if(!yy_isalnum(c) && c != '_') break; else *cp++ = c; @@ -940,7 +1011,7 @@ tnum: } *cp++ = c; c = getc(); - if(isdigit(c)) + if(yy_isdigit(c)) continue; goto dc; } @@ -955,7 +1026,7 @@ tnum: } *cp++ = c; c = getc(); - if(isdigit(c)) + if(yy_isdigit(c)) continue; if(c >= 'a' && c <= 'f') continue; @@ -976,7 +1047,7 @@ tnum: yyerror("identifier too long"); errorexit(); } - if(!isdigit(c)) + if(!yy_isdigit(c)) break; if(c < '0' || c > '7') c1 = 1; // not octal @@ -1025,7 +1096,7 @@ casedot: } *cp++ = c; c = getc(); - if(!isdigit(c)) + if(!yy_isdigit(c)) break; } if(c == 'i') @@ -1040,9 +1111,9 @@ casee: *cp++ = c; c = getc(); } - if(!isdigit(c)) + if(!yy_isdigit(c)) yyerror("malformed fp constant exponent"); - while(isdigit(c)) { + while(yy_isdigit(c)) { if(cp+10 >= ep) { yyerror("identifier too long"); errorexit(); @@ -1061,9 +1132,9 @@ casep: *cp++ = c; c = getc(); } - if(!isdigit(c)) + if(!yy_isdigit(c)) yyerror("malformed fp constant exponent"); - while(isdigit(c)) { + while(yy_isdigit(c)) { if(cp+10 >= ep) { yyerror("identifier too long"); errorexit(); @@ -1104,6 +1175,68 @@ caseout: return LLITERAL; } +/* + * read and interpret syntax that looks like + * //line parse.y:15 + * as a discontinuity in sequential line numbers. + * the next line of input comes from parse.y:15 + */ +static int +getlinepragma(void) +{ + int i, c, n; + char *cp, *ep; + Hist *h; + + for(i=0; i<5; i++) { + c = getr(); + if(c != "line "[i]) + goto out; + } + + cp = lexbuf; + ep = lexbuf+sizeof(lexbuf)-5; + for(;;) { + c = getr(); + if(c == '\n' || c == EOF) + goto out; + if(c == ' ') + continue; + if(c == ':') + break; + if(cp < ep) + *cp++ = c; + } + *cp = 0; + + n = 0; + for(;;) { + c = getr(); + if(!yy_isdigit(c)) + break; + n = n*10 + (c-'0'); + if(n > 1e8) { + yyerror("line number out of range"); + errorexit(); + } + } + + if(c != '\n' || n <= 0) + goto out; + + // try to avoid allocating file name over and over + for(h=hist; h!=H; h=h->link) { + if(h->name != nil && strcmp(h->name, lexbuf) == 0) { + linehist(h->name, n, 0); + goto out; + } + } + linehist(strdup(lexbuf), n, 0); + +out: + return c; +} + int32 yylex(void) { @@ -1112,13 +1245,8 @@ yylex(void) lx = _yylex(); if(curio.nlsemi && lx == EOF) { - // if the nlsemi bit is set, we'd be willing to - // insert a ; if we saw a \n, but we didn't. - // that means the final \n is missing. - // complain here, because we can give a - // good message. the syntax error we'd get - // otherwise is inscrutable. - yyerror("missing newline at end of file"); + // Treat EOF as "end of line" for the purposes + // of inserting a semicolon. lx = ';'; } @@ -1140,7 +1268,11 @@ yylex(void) curio.nlsemi = 0; break; } - return lx; + + // Track last two tokens returned by yylex. + yyprev = yylast; + yylast = lx; + return lx; } static int @@ -1395,6 +1527,7 @@ static struct "type", LTYPE, Txxx, OXXX, "var", LVAR, Txxx, OXXX, + "append", LNAME, Txxx, OAPPEND, "cap", LNAME, Txxx, OCAP, "close", LNAME, Txxx, OCLOSE, "closed", LNAME, Txxx, OCLOSED, diff --git a/src/cmd/gc/mkbuiltin b/src/cmd/gc/mkbuiltin index 13309ec32..4dfff1caa 100755 --- a/src/cmd/gc/mkbuiltin +++ b/src/cmd/gc/mkbuiltin @@ -10,11 +10,9 @@ set -e -GOBIN="${GOBIN:-$HOME/bin}" - -. "$GOROOT"/src/Make.$GOARCH +eval $(gomake --no-print-directory -f ../../Make.inc go-env) if [ -z "$GC" ]; then - echo 'missing $GC - maybe no Make.$GOARCH?' 1>&2 + echo 'missing $GC - gomake failed?' 1>&2 exit 1 fi @@ -22,7 +20,7 @@ gcc -o mkbuiltin1 mkbuiltin1.c rm -f _builtin.c for i in runtime unsafe do - "$GOBIN"/$GC -A $i.go + $GC -A $i.go O=$O ./mkbuiltin1 $i >>_builtin.c done diff --git a/src/cmd/gc/mparith1.c b/src/cmd/gc/mparith1.c index 8110e77b9..6cd4e2500 100644 --- a/src/cmd/gc/mparith1.c +++ b/src/cmd/gc/mparith1.c @@ -156,10 +156,11 @@ mpmovefixflt(Mpflt *a, Mpint *b) // convert (truncate) b to a. // return -1 (but still convert) if b was non-integer. -int -mpmovefltfix(Mpint *a, Mpflt *b) +static int +mpexactfltfix(Mpint *a, Mpflt *b) { Mpflt f; + *a = b->val; mpshiftfix(a, b->exp); if(b->exp < 0) { @@ -172,6 +173,35 @@ mpmovefltfix(Mpint *a, Mpflt *b) return 0; } +int +mpmovefltfix(Mpint *a, Mpflt *b) +{ + Mpflt f; + int i; + + if(mpexactfltfix(a, b) == 0) + return 0; + + // try rounding down a little + f = *b; + f.val.a[0] = 0; + if(mpexactfltfix(a, &f) == 0) + return 0; + + // try rounding up a little + for(i=1; i= '0' && c <= '9') { ex = ex*10 + (c-'0'); + if(ex > 1e8) { + yyerror("exponent out of range"); + errorexit(); + } continue; } break; @@ -439,6 +475,8 @@ Fconv(Fmt *fp) // for well in range, convert to double and use print's %g if(-900 < fvp->exp && fvp->exp < 900) { d = mpgetflt(fvp); + if(d >= 0 && (fp->flags & FmtSign)) + fmtprint(fp, "+"); return fmtprint(fp, "%g", d); } // TODO(rsc): for well out of range, print diff --git a/src/cmd/gc/mparith2.c b/src/cmd/gc/mparith2.c index b025917fa..403255005 100644 --- a/src/cmd/gc/mparith2.c +++ b/src/cmd/gc/mparith2.c @@ -319,7 +319,11 @@ mpmulfract(Mpint *a, Mpint *b) s.neg = 0; mpmovecfix(&q, 0); - for(i=0; iovf) { - yyerror("ovf in mpgetfix"); + yyerror("constant overflow"); return 0; } diff --git a/src/cmd/gc/mparith3.c b/src/cmd/gc/mparith3.c index b9cd4ea84..7b7e66668 100644 --- a/src/cmd/gc/mparith3.c +++ b/src/cmd/gc/mparith3.c @@ -27,16 +27,36 @@ sigfig(Mpflt *a) void mpnorm(Mpflt *a) { - int s; + int s, os; + long x; - s = sigfig(a); - if(s == 0) { + os = sigfig(a); + if(os == 0) { // zero a->exp = 0; a->val.neg = 0; return; } - s = (Mpnorm-s) * Mpscale; + + // this will normalize to the nearest word + x = a->val.a[os-1]; + s = (Mpnorm-os) * Mpscale; + + // further normalize to the nearest bit + for(;;) { + x <<= 1; + if(x & Mpbase) + break; + s++; + if(x == 0) { + // this error comes from trying to + // convert an Inf or something + // where the initial x=0x80000000 + s = (Mpnorm-os) * Mpscale; + break; + } + } + mpshiftfix(&a->val, s); a->exp -= s; } @@ -109,7 +129,7 @@ mpmulfltflt(Mpflt *a, Mpflt *b) } mpmulfract(&a->val, &b->val); - a->exp = (a->exp + b->exp) + Mpscale*Mpprec - 1; + a->exp = (a->exp + b->exp) + Mpscale*Mpprec - Mpscale - 1; mpnorm(a); if(Mpdebug) diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c index ae16f2725..0d0d70ac9 100644 --- a/src/cmd/gc/obj.c +++ b/src/cmd/gc/obj.c @@ -73,42 +73,97 @@ Bputname(Biobuf *b, Sym *s) Bwrite(b, s->name, strlen(s->name)+1); } +static void +outzfile(Biobuf *b, char *p) +{ + char *q, *q2; + + while(p) { + q = utfrune(p, '/'); + if(windows) { + q2 = utfrune(p, '\\'); + if(q2 && (!q || q2 < q)) + q = q2; + } + if(!q) { + zfile(b, p, strlen(p)); + return; + } + if(q > p) + zfile(b, p, q-p); + p = q + 1; + } +} + +#define isdelim(c) (c == '/' || c == '\\') + +static void +outwinname(Biobuf *b, Hist *h, char *ds, char *p) +{ + if(isdelim(p[0])) { + // full rooted name + zfile(b, ds, 3); // leading "c:/" + outzfile(b, p+1); + } else { + // relative name + if(h->offset == 0 && pathname && pathname[1] == ':') { + if(tolowerrune(ds[0]) == tolowerrune(pathname[0])) { + // using current drive + zfile(b, pathname, 3); // leading "c:/" + outzfile(b, pathname+3); + } else { + // using drive other then current, + // we don't have any simple way to + // determine current working directory + // there, therefore will output name as is + zfile(b, ds, 2); // leading "c:" + } + } + outzfile(b, p); + } +} + static void outhist(Biobuf *b) { Hist *h; - char *p, *q, *op; - int n; + char *p, ds[] = {'c', ':', '/', 0}; for(h = hist; h != H; h = h->link) { p = h->name; - op = 0; - - if(p && p[0] != '/' && h->offset == 0 && pathname && pathname[0] == '/') { - op = p; - p = pathname; - } - - while(p) { - q = utfrune(p, '/'); - if(q) { - n = q-p; - if(n == 0) - n = 1; // leading "/" - q++; + if(p) { + if(windows) { + // if windows variable is set, then, we know already, + // pathname is started with windows drive specifier + // and all '\' were replaced with '/' (see lex.c) + if(isdelim(p[0]) && isdelim(p[1])) { + // file name has network name in it, + // like \\server\share\dir\file.go + zfile(b, "//", 2); // leading "//" + outzfile(b, p+2); + } else if(p[1] == ':') { + // file name has drive letter in it + ds[0] = p[0]; + outwinname(b, h, ds, p+2); + } else { + // no drive letter in file name + outwinname(b, h, pathname, p); + } } else { - n = strlen(p); - q = 0; - } - if(n) - zfile(b, p, n); - p = q; - if(p == 0 && op) { - p = op; - op = 0; + if(p[0] == '/') { + // full rooted name, like /home/rsc/dir/file.go + zfile(b, "/", 1); // leading "/" + outzfile(b, p+1); + } else { + // relative name, like dir/file.go + if(h->offset == 0 && pathname && pathname[0] == '/') { + zfile(b, "/", 1); // leading "/" + outzfile(b, pathname+1); + } + outzfile(b, p); + } } } - zhist(b, h->line, h->offset); } } diff --git a/src/cmd/gc/print.c b/src/cmd/gc/print.c index 8738eb41b..6bb1f026b 100644 --- a/src/cmd/gc/print.c +++ b/src/cmd/gc/print.c @@ -23,14 +23,21 @@ void exprfmt(Fmt *f, Node *n, int prec) { int nprec; + char *p; nprec = 0; if(n == nil) { fmtprint(f, ""); return; } + + if(n->implicit) { + exprfmt(f, n->left, prec); + return; + } switch(n->op) { + case OAPPEND: case ONAME: case ONONAME: case OPACK: @@ -53,9 +60,11 @@ exprfmt(Fmt *f, Node *n, int prec) case OPRINT: case OPRINTN: case OCALL: + case OCALLMETH: + case OCALLINTER: + case OCALLFUNC: case OCONV: case OCONVNOP: - case OCONVSLICE: case OMAKESLICE: case ORUNESTR: case OADDR: @@ -66,6 +75,9 @@ exprfmt(Fmt *f, Node *n, int prec) case OPLUS: case ORECV: case OCONVIFACE: + case OTPAREN: + case OINDEX: + case OINDEXMAP: nprec = 7; break; @@ -106,6 +118,11 @@ exprfmt(Fmt *f, Node *n, int prec) case OOROR: nprec = 1; break; + + case OTYPE: + if(n->sym != S) + nprec = 7; + break; } if(prec > nprec) @@ -118,6 +135,10 @@ exprfmt(Fmt *f, Node *n, int prec) break; case OLITERAL: + if(n->sym != S) { + fmtprint(f, "%S", n->sym); + break; + } switch(n->val.ctype) { default: goto bad; @@ -154,6 +175,10 @@ exprfmt(Fmt *f, Node *n, int prec) break; case OTYPE: + if(n->type == T && n->sym != S) { + fmtprint(f, "%S", n->sym); + break; + } fmtprint(f, "%T", n->type); break; @@ -161,6 +186,12 @@ exprfmt(Fmt *f, Node *n, int prec) 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["); @@ -178,7 +209,7 @@ exprfmt(Fmt *f, Node *n, int prec) exprfmt(f, n->left, 0); } else { fmtprint(f, " "); - if(n->left->op == OTCHAN && n->left->etype == Crecv) { + if(n->left->op == OTCHAN && n->left->sym == S && n->left->etype == Crecv) { fmtprint(f, "("); exprfmt(f, n->left, 0); fmtprint(f, ")"); @@ -248,6 +279,10 @@ exprfmt(Fmt *f, Node *n, int prec) exprfmt(f, n->left, 0); break; + case OCLOSURE: + fmtprint(f, "func literal"); + break; + case OCOMPLIT: fmtprint(f, "composite literal"); break; @@ -267,6 +302,7 @@ exprfmt(Fmt *f, Node *n, int prec) fmtprint(f, "struct literal"); break; + case OXDOT: case ODOT: case ODOTPTR: case ODOTINTER: @@ -274,8 +310,15 @@ exprfmt(Fmt *f, Node *n, int prec) exprfmt(f, n->left, 7); if(n->right == N || n->right->sym == S) fmtprint(f, "."); - else - fmtprint(f, ".%s", n->right->sym->name); + 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: @@ -291,7 +334,6 @@ exprfmt(Fmt *f, Node *n, int prec) case OINDEX: case OINDEXMAP: - case OINDEXSTR: exprfmt(f, n->left, 7); fmtprint(f, "["); exprfmt(f, n->right, 0); @@ -299,9 +341,12 @@ exprfmt(Fmt *f, Node *n, int prec) break; case OSLICE: + case OSLICESTR: + case OSLICEARR: exprfmt(f, n->left, 7); fmtprint(f, "["); - exprfmt(f, n->right->left, 0); + if(n->right->left != N) + exprfmt(f, n->right->left, 0); fmtprint(f, ":"); if(n->right->right != N) exprfmt(f, n->right->right, 0); @@ -315,6 +360,8 @@ exprfmt(Fmt *f, Node *n, int prec) exprfmt(f, n->left, 7); fmtprint(f, "("); exprlistfmt(f, n->list); + if(n->isddd) + fmtprint(f, "..."); fmtprint(f, ")"); break; @@ -341,7 +388,6 @@ exprfmt(Fmt *f, Node *n, int prec) case OCONV: case OCONVIFACE: case OCONVNOP: - case OCONVSLICE: case OARRAYBYTESTR: case ORUNESTR: if(n->type == T || n->type->sym == S) @@ -355,6 +401,7 @@ exprfmt(Fmt *f, Node *n, int prec) fmtprint(f, ")"); break; + case OAPPEND: case OCAP: case OCLOSE: case OCLOSED: diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c index 09d54b3ee..dca3a5454 100644 --- a/src/cmd/gc/range.c +++ b/src/cmd/gc/range.c @@ -24,6 +24,8 @@ typecheckrange(Node *n) typecheck(&n->right, Erv); if((t = n->right->type) == T) goto out; + if(isptr[t->etype] && isfixedarray(t->type)) + t = t->type; n->type = t; switch(t->etype) { @@ -104,9 +106,6 @@ walkrange(Node *n) a = nod(OCONV, n->right, N); a->type = types[TSTRING]; } - ha = nod(OXXX, N, N); - tempname(ha, a->type); - init = list(init, nod(OAS, ha, a)); v1 = n->list->n; hv1 = N; @@ -116,6 +115,16 @@ walkrange(Node *n) v2 = n->list->next->n; hv2 = N; + if(v2 == N && t->etype == TARRAY) { + // will have just one reference to argument. + // no need to make a potentially expensive copy. + ha = a; + } else { + ha = nod(OXXX, N, N); + tempname(ha, a->type); + init = list(init, nod(OAS, ha, a)); + } + switch(t->etype) { default: fatal("walkrange"); @@ -131,7 +140,7 @@ walkrange(Node *n) init = list(init, nod(OAS, hn, nod(OLEN, ha, N))); if(v2) { hp = nod(OXXX, N, N); - tempname(hp, ptrto(a->type->type)); + tempname(hp, 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))); diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c index 467f3615b..b31eb5154 100644 --- a/src/cmd/gc/reflect.c +++ b/src/cmd/gc/reflect.c @@ -100,16 +100,16 @@ lsort(Sig *l, int(*f)(Sig*, Sig*)) * return function type, receiver as first argument (or not). */ Type* -methodfunc(Type *f, int use_receiver) +methodfunc(Type *f, Type *receiver) { NodeList *in, *out; Node *d; Type *t; in = nil; - if(use_receiver) { + if(receiver) { d = nod(ODCLFIELD, N, N); - d->type = getthisx(f)->type->type; + d->type = receiver; in = list(in, d); } for(t=getinargx(f)->type; t; t=t->down) { @@ -183,14 +183,14 @@ methods(Type *t) a = b; a->name = method->name; - a->isym = methodsym(method, it); - a->tsym = methodsym(method, t); - a->type = methodfunc(f->type, 1); - a->mtype = methodfunc(f->type, 0); + a->isym = methodsym(method, it, 1); + a->tsym = methodsym(method, t, 0); + a->type = methodfunc(f->type, t); + a->mtype = methodfunc(f->type, nil); if(!(a->isym->flags & SymSiggen)) { a->isym->flags |= SymSiggen; - if(!eqtype(this, it)) { + if(!eqtype(this, it) || this->width < types[tptr]->width) { if(oldlist == nil) oldlist = pc; // Is okay to call genwrapper here always, @@ -199,9 +199,9 @@ methods(Type *t) // is a pointer adjustment and a JMP. if(isptr[it->etype] && isptr[this->etype] && f->embedded && !isifacemethod(f->type)) - genembedtramp(it, f, a->isym); + genembedtramp(it, f, a->isym, 1); else - genwrapper(it, f, a->isym); + genwrapper(it, f, a->isym, 1); } } @@ -212,9 +212,9 @@ methods(Type *t) oldlist = pc; if(isptr[t->etype] && isptr[this->etype] && f->embedded && !isifacemethod(f->type)) - genembedtramp(t, f, a->tsym); + genembedtramp(t, f, a->tsym, 0); else - genwrapper(t, f, a->tsym); + genwrapper(t, f, a->tsym, 0); } } } @@ -241,22 +241,27 @@ imethods(Type *t) Sig *a, *all, *last; int o; Type *f; + Sym *method, *isym; + Prog *oldlist; all = nil; last = nil; o = 0; + oldlist = nil; for(f=t->type; f; f=f->down) { if(f->etype != TFIELD) fatal("imethods: not field"); if(f->type->etype != TFUNC || f->sym == nil) continue; + method = f->sym; a = mal(sizeof(*a)); - a->name = f->sym->name; - if(!exportname(f->sym->name)) - a->pkg = f->sym->pkg; + a->name = method->name; + if(!exportname(method->name)) + a->pkg = method->pkg; a->mtype = f->type; a->offset = 0; - a->type = methodfunc(f->type, 0); + a->type = methodfunc(f->type, nil); + if(last && sigcmp(last, a) >= 0) fatal("sigcmp vs sortinter %s %s", last->name, a->name); if(last == nil) @@ -264,7 +269,34 @@ imethods(Type *t) else last->link = a; last = a; + + // Compiler can only refer to wrappers for + // named interface types. + if(t->sym == S) + continue; + + // NOTE(rsc): Perhaps an oversight that + // IfaceType.Method is not in the reflect data. + // Generate the method body, so that compiled + // code can refer to it. + 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; } @@ -545,7 +577,6 @@ dcommontype(Sym *s, int ot, Type *t) { int i; Sym *s1; - Type *elem; char *p; dowidth(t); @@ -566,26 +597,23 @@ dcommontype(Sym *s, int ot, Type *t) // alg uint8; // align uint8; // fieldAlign uint8; + // kind uint8; // string *string; // *nameInfo; // } ot = duintptr(s, ot, t->width); ot = duint32(s, ot, typehash(t)); ot = duint8(s, ot, algtype(t)); - elem = t; - while(elem->etype == TARRAY && elem->bound >= 0) - elem = elem->type; - i = elem->width; - if(i > maxround) - i = maxround; - ot = duint8(s, ot, i); // align - ot = duint8(s, ot, i); // fieldAlign + ot = duint8(s, ot, t->align); // align + ot = duint8(s, ot, t->align); // fieldAlign i = kinds[t->etype]; if(t->etype == TARRAY && t->bound < 0) i = KindSlice; + if(isptr[t->etype] && t->type->etype == TANY) + i = KindUnsafePointer; if(!haspointers(t)) i |= KindNoPointers; - ot = duint8(s, ot, i); + ot = duint8(s, ot, i); // kind longsymnames = 1; p = smprint("%-T", t); longsymnames = 0; @@ -643,11 +671,10 @@ typename(Type *t) static Sym* dtypesym(Type *t) { - int ot, n, isddd; + int ot, n, isddd, dupok; Sym *s, *s1, *s2; Sig *a, *m; - Type *t1; - Sym *tsym; + Type *t1, *tbase; if(isideal(t)) fatal("dtypesym %T", t); @@ -660,30 +687,22 @@ dtypesym(Type *t) // special case (look for runtime below): // when compiling package runtime, // emit the type structures for int, float, etc. - t1 = T; - if(isptr[t->etype]) - t1 = t->type; - tsym = S; - if(t1) - tsym = t1->sym; - else - tsym = t->sym; + tbase = t; + if(isptr[t->etype] && t->sym == S && t->type->sym != S) + tbase = t->type; + dupok = tbase->sym == S; if(compiling_runtime) { - if(t == types[t->etype]) + if(tbase == types[tbase->etype]) // int, float, etc goto ok; - if(t1 && t1 == types[t1->etype]) - goto ok; - if(t1 && t1->etype == tptr && t1->type->etype == TANY) + if(tbase->etype == tptr && tbase->type->etype == TANY) // unsafe.Pointer goto ok; } - // named types from other files are defined in those files - if(t->sym && !t->local) - return s; - if(!t->sym && t1 && t1->sym && !t1->local) + // named types from other files are defined only by those files + if(tbase->sym && !tbase->local) return s; - if(isforw[t->etype] || (t1 && isforw[t1->etype])) + if(isforw[tbase->etype]) return s; ok: @@ -778,6 +797,7 @@ ok: case TPTR32: case TPTR64: if(t->type->etype == TANY) { + // ../../pkg/runtime/type.go:/UnsafePointerType ot = dcommontype(s, ot, t); break; } @@ -818,7 +838,7 @@ ok: break; } - ggloblsym(s, ot, tsym == nil); + ggloblsym(s, ot, dupok); return s; } @@ -846,7 +866,7 @@ dumptypestructs(void) continue; t = n->type; dtypesym(t); - if(t->sym && !isptr[t->etype]) + if(t->sym) dtypesym(ptrto(t)); } diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go index 5783faafd..174bc050e 100644 --- a/src/cmd/gc/runtime.go +++ b/src/cmd/gc/runtime.go @@ -10,7 +10,7 @@ package PACKAGE // emitted by compiler, not referred to by go programs -func mal(int32) *any +func new(int32) *any func panicindex() func panicslice() func throwreturn() @@ -31,13 +31,18 @@ func printeface(any) func printslice(any) func printnl() func printsp() -func printf() +func goprintf() + +// filled in by compiler: int n, string, string, ... +func concatstring() + +// filled in by compiler: Type*, int n, Slice, ... +func append() +func appendslice(typ *byte, x any, y []any) any -func catstring(string, string) string func cmpstring(string, string) int func slicestring(string, int, int) string func slicestring1(string, int) string -func indexstring(string, int) byte func intstring(int64) string func slicebytetostring([]byte) string func sliceinttostring([]int) string @@ -46,6 +51,7 @@ func stringtosliceint(string) []int func stringiter(string, int) int func stringiter2(string, int) (retk int, retv int) func slicecopy(to any, fr any, wid uint32) int +func slicestringcopy(to any, fr any) int // interface conversions func convI2E(elem any) (ret any) @@ -99,9 +105,9 @@ func selectdefault(sel *byte) (selected bool) func selectgo(sel *byte) func makeslice(typ *byte, nel int64, cap int64) (ary []any) -func sliceslice1(old []any, lb int, width int) (ary []any) -func sliceslice(old []any, lb int, hb int, width int) (ary []any) -func slicearray(old *any, nel int, lb int, hb int, width int) (ary []any) +func sliceslice1(old []any, lb uint64, width uint64) (ary []any) +func sliceslice(old []any, lb uint64, hb uint64, width uint64) (ary []any) +func slicearray(old *any, nel uint64, lb uint64, hb uint64, width uint64) (ary []any) func closure() // has args, but compiler fills in @@ -111,6 +117,8 @@ func uint64div(uint64, uint64) uint64 func int64mod(int64, int64) int64 func uint64mod(uint64, uint64) uint64 func float64toint64(float64) int64 +func float64touint64(float64) uint64 func int64tofloat64(int64) float64 +func uint64tofloat64(uint64) float64 func complex128div(num complex128, den complex128) (quo complex128) diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c index 9cba01fa5..1a3771311 100644 --- a/src/cmd/gc/select.c +++ b/src/cmd/gc/select.c @@ -41,11 +41,18 @@ typecheckselect(Node *sel) setlineno(n); switch(n->op) { default: - yyerror("select case must be receive, send or assign recv");; + yyerror("select case must be receive, send or assign recv"); break; case OAS: // convert x = <-c into OSELRECV(x, c) + // assignment might have introduced a + // conversion. throw it away. + // it will come back when the select code + // gets generated, because it always assigns + // through a temporary. + if((n->right->op == OCONVNOP || n->right->op == OCONVIFACE) && n->right->implicit) + n->right = n->right->left; if(n->right->op != ORECV) { yyerror("select assignment must have receive on right hand side"); break; @@ -68,8 +75,6 @@ typecheckselect(Node *sel) typechecklist(ncase->nbody, Etop); } sel->xoffset = count; - if(count == 0) - yyerror("empty select"); lineno = lno; } @@ -91,7 +96,7 @@ walkselect(Node *sel) typecheck(&r, Etop); init = list(init, r); - if(sel->list == nil) + if(sel->list == nil && sel->xoffset != 0) fatal("double walkselect"); // already rewrote // register cases diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c index 5ac14a537..19ee3327b 100644 --- a/src/cmd/gc/sinit.c +++ b/src/cmd/gc/sinit.c @@ -92,15 +92,9 @@ init1(Node *n, NodeList **out) break; case OAS2FUNC: - if(n->defn->initorder) - break; - n->defn->initorder = 1; - for(l=n->defn->rlist; l; l=l->next) - init1(l->n, out); - *out = list(*out, n->defn); - break; - case OAS2MAPR: + case OAS2DOTTYPE: + case OAS2RECV: if(n->defn->initorder) break; n->defn->initorder = 1; @@ -190,6 +184,20 @@ static void arraylit(int ctxt, int pass, Node *n, Node *var, NodeList **init); static void slicelit(int ctxt, Node *n, Node *var, NodeList **init); static void maplit(int ctxt, Node *n, Node *var, NodeList **init); +static Node* +staticname(Type *t, int ctxt) +{ + Node *n; + + snprint(namebuf, sizeof(namebuf), "statictmp_%.4d", statuniqgen); + statuniqgen++; + n = newname(lookup(namebuf)); + if(!ctxt) + n->readonly = 1; + addvar(n, t, PEXTERN); + return n; +} + static int isliteral(Node *n) { @@ -402,12 +410,12 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init) if(ctxt != 0) { // put everything into static array - vstat = staticname(t); + vstat = staticname(t, ctxt); arraylit(ctxt, 1, n, vstat, init); arraylit(ctxt, 2, n, vstat, init); // copy static to slice - a = nod(OADDR, vstat, N); + a = nod(OSLICE, vstat, nod(OKEY, N, N)); a = nod(OAS, var, a); typecheck(&a, Etop); a->dodata = 2; @@ -439,7 +447,7 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init) vstat = N; mode = getdyn(n, 1); if(mode & MODECONST) { - vstat = staticname(t); + vstat = staticname(t, ctxt); arraylit(ctxt, 1, n, vstat, init); } @@ -465,7 +473,7 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init) } // make slice out of heap (5) - a = nod(OAS, var, vauto); + a = nod(OAS, var, nod(OSLICE, vauto, nod(OKEY, N, N))); typecheck(&a, Etop); walkexpr(&a, init); *init = list(*init, a); @@ -514,6 +522,8 @@ maplit(int ctxt, Node *n, Node *var, NodeList **init) Node *vstat, *index, *value; Sym *syma, *symb; +ctxt = 0; + // make the map var nerr = nerrors; @@ -566,7 +576,7 @@ maplit(int ctxt, Node *n, Node *var, NodeList **init) dowidth(t); // make and initialize static array - vstat = staticname(t); + vstat = staticname(t, ctxt); b = 0; for(l=n->list; l; l=l->next) { r = l->n; @@ -675,7 +685,7 @@ anylit(int ctxt, Node *n, Node *var, NodeList **init) if(ctxt == 0) { // lay out static data - vstat = staticname(t); + vstat = staticname(t, ctxt); structlit(1, 1, n, vstat, init); // copy static to var @@ -715,7 +725,7 @@ anylit(int ctxt, Node *n, Node *var, NodeList **init) if(ctxt == 0) { // lay out static data - vstat = staticname(t); + vstat = staticname(t, ctxt); arraylit(1, 1, n, vstat, init); // copy static to automatic @@ -769,8 +779,8 @@ oaslit(Node *n, NodeList **init) // implies generated data executed // exactly once and not subject to races. ctxt = 0; - if(n->dodata == 1) - ctxt = 1; +// if(n->dodata == 1) +// ctxt = 1; switch(n->right->op) { default: @@ -870,8 +880,18 @@ gen_as_init(Node *n) default: goto no; - case OCONVSLICE: - goto slice; + case OCONVNOP: + nr = nr->left; + if(nr == N || nr->op != OSLICEARR) + goto no; + // fall through + + case OSLICEARR: + if(nr->right->op == OKEY && nr->right->left == N && nr->right->right == N) { + nr = nr->left; + goto slice; + } + goto no; case OLITERAL: break; @@ -920,7 +940,7 @@ yes: slice: gused(N); // in case the data is the dest of a goto - nr = n->right->left; + nl = nr; if(nr == N || nr->op != OADDR) goto no; nr = nr->left; @@ -932,7 +952,7 @@ slice: goto no; nam.xoffset += Array_array; - gdata(&nam, n->right->left, types[tptr]->width); + gdata(&nam, nl, types[tptr]->width); nam.xoffset += Array_nel-Array_array; nodconst(&nod1, types[TINT32], nr->type->bound); diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index 65b56dee6..3c4501096 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -228,11 +228,15 @@ linehist(char *file, int32 off, int relative) if(debug['i']) { if(file != nil) { if(off < 0) - print("pragma %s at line %L\n", file, lineno); + print("pragma %s", file); else - print("import %s at line %L\n", file, lineno); + if(off > 0) + print("line %s", file); + else + print("import %s", file); } else - print("end of import at line %L\n", lineno); + print("end of import"); + print(" at line %L\n", lexlineno); } if(off < 0 && file[0] != '/' && !relative) { @@ -267,7 +271,6 @@ setlineno(Node *n) case OTYPE: case OPACK: case OLITERAL: - case ONONAME: break; default: lineno = n->lineno; @@ -360,6 +363,8 @@ importdot(Pkg *opkg, Node *pack) for(s = hash[h]; s != S; s = s->link) { if(s->pkg != opkg) continue; + if(s->def == N) + continue; if(!exportname(s->name) || utfrune(s->name, 0xb7)) // 0xb7 = center dot continue; s1 = lookup(s->name); @@ -473,9 +478,12 @@ algtype(Type *t) int a; if(issimple[t->etype] || isptr[t->etype] || iscomplex[t->etype] || - t->etype == TCHAN || t->etype == TFUNC || t->etype == TMAP) - a = AMEM; // just bytes (int, ptr, etc) - else if(t->etype == TSTRING) + t->etype == TCHAN || t->etype == TFUNC || t->etype == TMAP) { + if(t->width == widthptr) + a = AMEMWORD; + else + a = AMEM; // just bytes (int, ptr, etc) + } else if(t->etype == TSTRING) a = ASTRING; // string else if(isnilinter(t)) a = ANILINTER; // nil interface @@ -584,6 +592,21 @@ nodintconst(int64 v) return c; } +Node* +nodfltconst(Mpflt* v) +{ + Node *c; + + c = nod(OLITERAL, N, N); + c->addable = 1; + c->val.u.fval = mal(sizeof(*c->val.u.fval)); + mpmovefltflt(c->val.u.fval, v); + c->val.ctype = CTFLT; + c->type = types[TIDEAL]; + ullmancalc(c); + return c; +} + void nodconst(Node *n, Type *t, int64 v) { @@ -804,6 +827,7 @@ goopnames[] = [OANDAND] = "&&", [OANDNOT] = "&^", [OAND] = "&", + [OAPPEND] = "append", [OAS] = "=", [OAS2] = "=", [OBREAK] = "break", @@ -894,12 +918,21 @@ Lconv(Fmt *fp) if(lno < h->line) break; if(h->name) { - if(n < HISTSZ) { /* beginning of file */ - a[n].incl = h; - a[n].idel = h->line; - a[n].line = 0; + 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++; } - n++; continue; } n--; @@ -919,14 +952,16 @@ Lconv(Fmt *fp) break; fmtprint(fp, " "); } + if(debug['L']) + fmtprint(fp, "%s/", pathname); if(a[i].line) - fmtprint(fp, "%s:%ld[%s:%ld]", + 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:%ld", + 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 */ + lno = a[i].incl->line - 1; // now print out start of this file } if(n == 0) fmtprint(fp, ""); @@ -1003,10 +1038,10 @@ Jconv(Fmt *fp) fmtprint(fp, " a(%d)", n->addable); if(n->vargen != 0) - fmtprint(fp, " g(%ld)", n->vargen); + fmtprint(fp, " g(%d)", n->vargen); if(n->lineno != 0) - fmtprint(fp, " l(%ld)", n->lineno); + fmtprint(fp, " l(%d)", n->lineno); if(n->xoffset != 0) fmtprint(fp, " x(%lld)", n->xoffset); @@ -1029,6 +1064,9 @@ Jconv(Fmt *fp) if(n->isddd != 0) fmtprint(fp, " isddd(%d)", n->isddd); + if(n->implicit != 0) + fmtprint(fp, " implicit(%d)", n->implicit); + return 0; } @@ -1146,7 +1184,7 @@ Tpretty(Fmt *fp, Type *t) case Csend: return fmtprint(fp, "chan<- %T", t->type); } - if(t->type != T && t->type->etype == TCHAN && t->type->chan == Crecv) + 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); @@ -1256,7 +1294,7 @@ Tpretty(Fmt *fp, Type *t) fmtprint(fp, "...%T", t->type->type); else fmtprint(fp, "%T", t->type); - if(t->note) { + if(t->note) { fmtprint(fp, " "); if(exporting) fmtprint(fp, ":"); @@ -1360,7 +1398,7 @@ Tconv(Fmt *fp) case TARRAY: if(t->bound >= 0) - fmtprint(fp, "[%ld]%T", t->bound, t->type); + fmtprint(fp, "[%d]%T", t->bound, t->type); else fmtprint(fp, "[]%T", t->type); break; @@ -1414,7 +1452,7 @@ Nconv(Fmt *fp) fmtprint(fp, "%O%J", n->op, n); break; } - fmtprint(fp, "%O-%S G%ld%J", n->op, + fmtprint(fp, "%O-%S G%d%J", n->op, n->sym, n->vargen, n); goto ptyp; @@ -1460,7 +1498,7 @@ Nconv(Fmt *fp) break; } if(n->sym != S) - fmtprint(fp, " %S G%ld", n->sym, n->vargen); + fmtprint(fp, " %S G%d", n->sym, n->vargen); ptyp: if(n->type != T) @@ -1909,13 +1947,6 @@ assignop(Type *src, Type *dst, char **why) // 7. Any typed value can be assigned to the blank identifier. if(dst->etype == TBLANK) return OCONVNOP; - - // 8. Array to slice. - // TODO(rsc): Not for long. - if(!src->sym || !dst->sym) - if(isptr[src->etype] && isfixedarray(src->type) && isslice(dst)) - if(eqtype(src->type->type, dst->type)) - return OCONVSLICE; return 0; } @@ -2037,6 +2068,7 @@ assignconv(Node *n, Type *t, char *context) r = nod(op, n, N); r->type = t; r->typecheck = 1; + r->implicit = 1; return r; } @@ -2292,7 +2324,8 @@ ptrto(Type *t) fatal("ptrto: nil"); t1 = typ(tptr); t1->type = t; - t1->width = types[tptr]->width; + t1->width = widthptr; + t1->align = widthptr; return t1; } @@ -2320,7 +2353,7 @@ frame(int context) case ONAME: if(flag) print("--- %s frame ---\n", p); - print("%O %S G%ld %T\n", n->op, n->sym, n->vargen, n->type); + print("%O %S G%d %T\n", n->op, n->sym, n->vargen, n->type); flag = 0; break; @@ -2584,20 +2617,8 @@ brrev(int a) return a; } -Node* -staticname(Type *t) -{ - Node *n; - - snprint(namebuf, sizeof(namebuf), "statictmp_%.4d", statuniqgen); - statuniqgen++; - n = newname(lookup(namebuf)); - addvar(n, t, PEXTERN); - return n; -} - /* - * return side effect-free appending side effects to init. + * return side effect-free n, appending side effects to init. * result is assignable if n is. */ Node* @@ -2654,6 +2675,24 @@ safeexpr(Node *n, NodeList **init) // make a copy; must not be used as an lvalue if(islvalue(n)) fatal("missing lvalue case in safeexpr: %N", n); + return cheapexpr(n, init); +} + +/* + * return side-effect free and cheap n, appending side effects to init. + * result may not be assignable. + */ +Node* +cheapexpr(Node *n, NodeList **init) +{ + Node *a, *l; + + switch(n->op) { + case ONAME: + case OLITERAL: + return n; + } + l = nod(OXXX, N, N); tempname(l, n->type); a = nod(OAS, l, n); @@ -2939,6 +2978,11 @@ expandmeth(Sym *s, Type *t) if(t == T || t->xmethod != nil) return; + // mark top-level method symbols + // so that expand1 doesn't consider them. + for(f=t->method; f != nil; f=f->down) + f->sym->flags |= SymUniq; + // generate all reachable methods slist = nil; expand1(t, nelem(dotlist)-1, 0); @@ -2958,6 +3002,9 @@ expandmeth(Sym *s, Type *t) } } + for(f=t->method; f != nil; f=f->down) + f->sym->flags &= ~SymUniq; + t->xmethod = t->method; for(sl=slist; sl!=nil; sl=sl->link) { if(sl->good) { @@ -2969,7 +3016,6 @@ expandmeth(Sym *s, Type *t) f->embedded = 2; f->down = t->xmethod; t->xmethod = f; - } } } @@ -3031,15 +3077,19 @@ structargs(Type **tl, int mustname) * newnam - the eventual mangled name of this function */ void -genwrapper(Type *rcvr, Type *method, Sym *newnam) +genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) { - Node *this, *fn, *call, *n, *t; + Node *this, *fn, *call, *n, *t, *pad; NodeList *l, *args, *in, *out; + Type *tpad; + int isddd; if(debug['r']) print("genwrapper rcvrtype=%T method=%T newnam=%S\n", rcvr, method, newnam); + lineno = 1; // less confusing than end of input + dclcontext = PEXTERN; markdcl(); @@ -3050,20 +3100,37 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam) fn = nod(ODCLFUNC, N, N); fn->nname = newname(newnam); - t = nod(OTFUNC, this, N); - t->list = in; + t = nod(OTFUNC, N, N); + l = list1(this); + if(iface && rcvr->width < types[tptr]->width) { + // Building method for interface table and receiver + // is smaller than the single pointer-sized word + // that the interface call will pass in. + // Add a dummy padding argument after the + // receiver to make up the difference. + tpad = typ(TARRAY); + tpad->type = types[TUINT8]; + tpad->bound = types[tptr]->width - rcvr->width; + pad = nod(ODCLFIELD, newname(lookup(".pad")), typenod(tpad)); + l = list(l, pad); + } + t->list = concat(l, in); t->rlist = out; fn->nname->ntype = t; funchdr(fn); // arg list args = nil; - for(l=in; l; l=l->next) + isddd = 0; + for(l=in; l; l=l->next) { args = list(args, l->n->left); + isddd = l->n->left->isddd; + } // generate call call = nod(OCALL, adddot(nod(OXDOT, this->left, newname(method->sym))), N); call->list = args; + call->isddd = isddd; fn->nbody = list1(call); if(method->type->outtuple > 0) { n = nod(ORETURN, N, N); @@ -3563,10 +3630,11 @@ umagic(Magic *m) Sym* ngotype(Node *n) { - if(n->sym != S && strncmp(n->sym->name, "autotmp_", 8) != 0) - if(n->type->etype != TFUNC || n->type->thistuple == 0) - if(n->type->etype != TSTRUCT || n->type->funarg == 0) - return typename(n->type)->left->sym; + if(n->sym != S && n->realtype != T) + if(strncmp(n->sym->name, "autotmp_", 8) != 0) + if(strncmp(n->sym->name, "statictmp_", 8) != 0) + return typename(n->realtype)->left->sym; + return S; } @@ -3640,4 +3708,3 @@ strlit(char *s) t->len = strlen(s); return t; } - diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 457b82b4c..ca114d47c 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -17,7 +17,8 @@ static void implicitstar(Node**); static int onearg(Node*, char*, ...); static int twoarg(Node*); static int lookdot(Node*, Type*, int); -static void typecheckaste(int, Type*, NodeList*, char*); +static int looktypedot(Node*, Type*, int); +static void typecheckaste(int, int, Type*, NodeList*, char*); static Type* lookdot1(Sym *s, Type *t, Type *f, int); static int nokeys(NodeList*); static void typecheckcomplit(Node**); @@ -63,7 +64,7 @@ typechecklist(NodeList *l, int top) Node* typecheck(Node **np, int top) { - int et, op, ptr; + int et, aop, op, ptr; Node *n, *l, *r; NodeList *args; int lno, ok, ntop; @@ -79,7 +80,7 @@ typecheck(Node **np, int top) n = *np; if(n == N) return N; - + // Resolve definition of name and value of iota lazily. n = resolve(n); *np = n; @@ -111,6 +112,7 @@ typecheck(Node **np, int top) goto error; } walkdef(n); + n->realtype = n->type; if(n->op == ONONAME) goto error; } @@ -169,6 +171,16 @@ reswitch: goto error; break; + case OTPAREN: + ok |= Etype; + l = typecheck(&n->left, Etype); + if(l->type == T) + goto error; + n->op = OTYPE; + n->type = l->type; + n->left = N; + break; + case OTARRAY: ok |= Etype; t = typ(TARRAY); @@ -251,7 +263,6 @@ reswitch: n->type = dostruct(n->list, TINTER); if(n->type == T) goto error; - n->type = sortinter(n->type); break; case OTFUNC: @@ -340,6 +351,26 @@ reswitch: et = t->etype; if(et == TIDEAL) et = TINT; + 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.) + if(r->type->etype != TBLANK && (aop = assignop(l->type, r->type, nil)) != 0) { + 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) { + r = nod(aop, r, N); + r->type = l->type; + r->typecheck = 1; + n->right = r; + t = r->type; + } + et = t->etype; + } if(t->etype != TIDEAL && !eqtype(l->type, r->type)) { badbinary: defaultlit2(&l, &r, 1); @@ -393,7 +424,7 @@ reswitch: n->right = r; t = r->type; if(!isint[t->etype] || issigned[t->etype]) { - yyerror("invalid operation: %#N (shift count type %T)", n, r->type); + yyerror("invalid operation: %#N (shift count type %T, must be unsigned integer)", n, r->type); goto error; } t = l->type; @@ -467,41 +498,42 @@ reswitch: yyerror("rhs of . must be a name"); // impossible goto error; } - if(isptr[t->etype]) { - t = t->type; - if(t == T) - goto error; - n->op = ODOTPTR; - checkwidth(t); - } sym = n->right->sym; - if(!lookdot(n, t, 0)) { - if(lookdot(n, t, 1)) - yyerror("%#N undefined (cannot refer to unexported field %S)", n, n->right->sym); - else - yyerror("%#N undefined (type %T has no field %S)", n, t, n->right->sym); - goto error; - } if(l->op == OTYPE) { - if(n->type->etype != TFUNC || n->type->thistuple != 1) { - yyerror("type %T has no method %hS", n->left->type, sym); - n->type = T; + if(!looktypedot(n, t, 0)) { + if(looktypedot(n, t, 1)) + 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); goto error; } - if(t->etype == TINTER) { - yyerror("method expression on interface not implemented"); + if(n->type->etype != TFUNC || n->type->thistuple != 1) { + yyerror("type %T has no method %hS", n->left->type, sym); n->type = T; goto error; } n->op = ONAME; - n->sym = methodsym(sym, l->type); - n->type = methodfunc(n->type, 1); + n->sym = methodsym(sym, l->type, 0); + n->type = methodfunc(n->type, l->type); n->xoffset = 0; - getinargx(n->type)->type->type = l->type; // fix up receiver n->class = PFUNC; ok = Erv; goto ret; } + if(isptr[t->etype] && t->type->etype != TINTER) { + t = t->type; + if(t == T) + goto error; + n->op = ODOTPTR; + checkwidth(t); + } + 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); + else + yyerror("%#N undefined (type %T has no field or method %S)", n, t, n->right->sym); + goto error; + } switch(n->op) { case ODOTINTER: case ODOTMETH: @@ -561,7 +593,7 @@ reswitch: goto error; case TARRAY: - defaultlit(&n->right, types[TUINT]); + defaultlit(&n->right, T); if(n->right->type != T && !isint[n->right->type->etype]) yyerror("non-integer array index %#N", n->right); n->type = t->type; @@ -581,7 +613,6 @@ reswitch: if(n->right->type != T && !isint[n->right->type->etype]) yyerror("non-integer string index %#N", n->right); n->type = types[TUINT8]; - n->op = OINDEXSTR; break; } goto ret; @@ -612,6 +643,10 @@ reswitch: l = n->left; if((t = l->type) == T) goto error; + if(t->etype != TCHAN) { + 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); goto error; @@ -620,6 +655,7 @@ reswitch: r = n->right; if((t = r->type) == T) goto error; + r = assignconv(r, l->type->type, "send"); // TODO: more aggressive n->etype = 0; n->type = T; @@ -635,24 +671,19 @@ reswitch: typecheck(&n->right->left, Erv); typecheck(&n->right->right, Erv); defaultlit(&n->left, T); - defaultlit(&n->right->left, types[TUINT]); - defaultlit(&n->right->right, types[TUINT]); + defaultlit(&n->right->left, T); + defaultlit(&n->right->right, T); if(isfixedarray(n->left->type)) { - // Insert explicit & before fixed array - // so that back end knows to move to heap. n->left = nod(OADDR, n->left, N); typecheck(&n->left, top); } - implicitstar(&n->left); - if(n->right->left == N) { - yyerror("missing slice bounds?"); - goto error; - } - if((t = n->right->left->type) == T) - goto error; - if(!isint[t->etype]) { - yyerror("invalid slice index %#N (type %T)", n->right->left, t); - goto error; + if(n->right->left != N) { + if((t = n->right->left->type) == T) + goto error; + if(!isint[t->etype]) { + yyerror("invalid slice index %#N (type %T)", n->right->left, t); + goto error; + } } if(n->right->right != N) { if((t = n->right->right->type) == T) @@ -670,9 +701,9 @@ reswitch: n->op = OSLICESTR; goto ret; } - if(isfixedarray(t)) { + if(isptr[t->etype] && isfixedarray(t->type)) { n->type = typ(TARRAY); - n->type->type = t->type; + n->type->type = t->type->type; n->type->bound = -1; dowidth(n->type); n->op = OSLICEARR; @@ -690,13 +721,17 @@ reswitch: */ case OCALL: l = n->left; - if(l->op == ONAME && (r = unsafenmagic(l, n->list)) != N) { + if(l->op == ONAME && (r = unsafenmagic(n)) != N) { + if(n->isddd) + yyerror("invalid use of ... with builtin %#N", l); n = r; goto reswitch; } typecheck(&n->left, Erv | Etype | Ecall); l = n->left; if(l->op == ONAME && l->etype != 0) { + if(n->isddd && l->etype != OAPPEND) + yyerror("invalid use of ... with builtin %#N", l); // builtin: OLEN, OCAP, etc. n->op = l->etype; n->left = n->right; @@ -706,6 +741,8 @@ reswitch: defaultlit(&n->left, T); l = n->left; if(l->op == OTYPE) { + if(n->isddd) + yyerror("invalid use of ... in type conversion", l); // pick off before type-checking arguments ok |= Erv; // turn CALL(type, arg) into CONV(arg) w/ type @@ -732,7 +769,7 @@ reswitch: case ODOTMETH: n->op = OCALLMETH; - typecheckaste(OCALL, getthisx(t), list1(l->left), "method receiver"); + typecheckaste(OCALL, 0, getthisx(t), list1(l->left), "method receiver"); break; default: @@ -743,7 +780,7 @@ reswitch: } break; } - typecheckaste(OCALL, getinargx(t), n->list, "function argument"); + typecheckaste(OCALL, n->isddd, getinargx(t), n->list, "function argument"); ok |= Etop; if(t->outtuple == 0) goto ret; @@ -792,6 +829,12 @@ reswitch: case OIMAG: if(!iscomplex[t->etype]) goto badcall1; + if(isconst(l, CTCPLX)){ + if(n->op == OREAL) + n = nodfltconst(&l->val.u.cval->real); + else + n = nodfltconst(&l->val.u.cval->imag); + } n->type = types[cplxsubtype(t->etype)]; goto ret; } @@ -802,7 +845,7 @@ reswitch: nodconst(n, types[TINT], l->val.u.sval->len); break; case TARRAY: - if(t->bound >= 0) + if(t->bound >= 0 && l->op == ONAME) nodconst(n, types[TINT], t->bound); break; } @@ -868,6 +911,40 @@ reswitch: ok |= Etop; goto ret; + case OAPPEND: + ok |= Erv; + args = n->list; + if(args == nil) { + yyerror("missing arguments to append"); + goto error; + } + typechecklist(args, Erv); + if((t = args->n->type) == T) + goto error; + n->type = t; + if(!isslice(t)) { + 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"); + goto error; + } + if(args->next->next != nil) { + yyerror("too many arguments to append"); + goto error; + } + args->next->n = assignconv(args->next->n, t->orig, "append"); + goto ret; + } + for(args=args->next; args != nil; args=args->next) { + if(args->n->type == T) + continue; + args->n = assignconv(args->n, t->type, "append"); + } + goto ret; + case OCOPY: ok |= Etop|Erv; args = n->list; @@ -888,13 +965,18 @@ reswitch: goto error; defaultlit(&n->left, T); defaultlit(&n->right, T); + + // copy([]byte, string) + if(isslice(n->left->type) && n->left->type->type == types[TUINT8] && n->right->type->etype == TSTRING) + goto ret; + 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); else if(!isslice(n->left->type)) yyerror("first argument to copy should be slice; have %lT", n->left->type); else - yyerror("second argument to copy should be slice; have %lT", n->right->type); + yyerror("second argument to copy should be slice or string; have %lT", n->right->type); goto error; } if(!eqtype(n->left->type->type, n->right->type->type)) { @@ -1048,7 +1130,7 @@ reswitch: case OPRINT: case OPRINTN: ok |= Etop; - typechecklist(n->list, Erv); + typechecklist(n->list, Erv | Eindir); // Eindir: address does not escape goto ret; case OPANIC: @@ -1056,7 +1138,7 @@ reswitch: if(onearg(n, "panic") < 0) goto error; typecheck(&n->left, Erv); - defaultlit(&n->left, T); + defaultlit(&n->left, types[TINTER]); if(n->left->type == T) goto error; goto ret; @@ -1129,9 +1211,13 @@ reswitch: case ORETURN: ok |= Etop; typechecklist(n->list, Erv | Efnstruct); + if(curfn == N) { + yyerror("return outside function"); + goto error; + } if(curfn->type->outnamed && n->list == nil) goto ret; - typecheckaste(ORETURN, getoutargx(curfn->type), n->list, "return argument"); + typecheckaste(ORETURN, 0, getoutargx(curfn->type), n->list, "return argument"); goto ret; case OSELECT: @@ -1149,11 +1235,6 @@ reswitch: typecheckrange(n); goto ret; - case OTYPECASE: - ok |= Etop | Erv; - typecheck(&n->left, Erv); - goto ret; - case OTYPESW: yyerror("use of .(type) outside type switch"); goto error; @@ -1218,7 +1299,7 @@ ret: goto error; } if((ok & Ecall) && !(top & Ecall)) { - yyerror("must call %#N", n); + yyerror("method %#N is not an expression, must be called", n); goto error; } // TODO(rsc): simplify @@ -1257,7 +1338,7 @@ implicitstar(Node **nn) Type *t; Node *n; - // insert implicit * if needed + // insert implicit * if needed for fixed array n = *nn; t = n->type; if(t == T || !isptr[t->etype]) @@ -1346,6 +1427,57 @@ lookdot1(Sym *s, Type *t, Type *f, int dostrcmp) return r; } +static int +looktypedot(Node *n, Type *t, int dostrcmp) +{ + Type *f1, *f2, *tt; + Sym *s; + + s = n->right->sym; + + if(t->etype == TINTER) { + f1 = lookdot1(s, t, t->type, dostrcmp); + if(f1 == T) + return 0; + + if(f1->width == BADWIDTH) + fatal("lookdot badwidth %T %p", f1, f1); + n->right = methodname(n->right, t); + n->xoffset = f1->width; + n->type = f1->type; + n->op = ODOTINTER; + return 1; + } + + tt = t; + if(t->sym == S && isptr[t->etype]) + tt = t->type; + + f2 = methtype(tt); + if(f2 == T) + return 0; + + expandmeth(f2->sym, f2); + f2 = lookdot1(s, f2, f2->xmethod, dostrcmp); + if(f2 == T) + return 0; + + // disallow T.m if m requires *T receiver + if(isptr[getthisx(f2->type)->type->type->etype] + && !isptr[t->etype] + && f2->embedded != 2 + && !isifacemethod(f2->type)) { + yyerror("invalid method expression %#N (needs pointer receiver: (*%T).%s)", n, t, f2->sym->name); + return 0; + } + + n->right = methodname(n->right, t); + n->xoffset = f2->width; + n->type = f2->type; + n->op = ODOTMETH; + return 1; +} + static int lookdot(Node *n, Type *t, int dostrcmp) { @@ -1359,9 +1491,15 @@ lookdot(Node *n, Type *t, int dostrcmp) if(t->etype == TSTRUCT || t->etype == TINTER) f1 = lookdot1(s, t, t->type, dostrcmp); - f2 = methtype(n->left->type); - if(f2 != T) - f2 = lookdot1(s, f2, f2->method, dostrcmp); + f2 = T; + if(n->left->type == t || n->left->type->sym == S) { + f2 = methtype(t); + 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); + } + } if(f1 != T) { if(f2 != T) @@ -1385,14 +1523,16 @@ lookdot(Node *n, Type *t, int dostrcmp) tt = n->left->type; dowidth(tt); rcvr = getthisx(f2->type)->type->type; - if(n->left->op != OTYPE && !eqtype(rcvr, tt)) { + if(!eqtype(rcvr, tt)) { if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) { checklvalue(n->left, "call pointer method on"); addrescapes(n->left); n->left = nod(OADDR, n->left, N); + n->left->implicit = 1; typecheck(&n->left, Etype|Erv); } else if(tt->etype == tptr && eqtype(tt->type, rcvr)) { n->left = nod(OIND, n->left, N); + n->left->implicit = 1; typecheck(&n->left, Etype|Erv); } else { // method is attached to wrong type? @@ -1422,7 +1562,7 @@ nokeys(NodeList *l) * typecheck assignment: type list = expression list */ static void -typecheckaste(int op, Type *tstruct, NodeList *nl, char *desc) +typecheckaste(int op, int isddd, Type *tstruct, NodeList *nl, char *desc) { Type *t, *tl, *tn; Node *n; @@ -1436,63 +1576,79 @@ typecheckaste(int op, Type *tstruct, NodeList *nl, char *desc) if(nl != nil && nl->next == nil && (n = nl->n)->type != T) if(n->type->etype == TSTRUCT && n->type->funarg) { - setlineno(n); tn = n->type->type; for(tl=tstruct->type; tl; tl=tl->down) { if(tl->isddd) { - for(; tn; tn=tn->down) + for(; tn; tn=tn->down) { + exportassignok(tn->type, desc); if(assignop(tn->type, tl->type->type, &why) == 0) yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type->type, desc, why); + } goto out; } - if(tn == T) { - yyerror("not enough arguments to %#O", op); - goto out; - } + if(tn == T) + goto notenough; + exportassignok(tn->type, desc); if(assignop(tn->type, tl->type, &why) == 0) yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type, desc, why); tn = tn->down; } if(tn != T) - yyerror("too many arguments to %#O", op); + goto toomany; goto out; } for(tl=tstruct->type; tl; tl=tl->down) { t = tl->type; if(tl->isddd) { - if(nl != nil && nl->n->isddd && !eqtype(nl->n->type, t)) { - // TODO(rsc): This is not actually illegal but will - // help catch bugs. - yyerror("cannot pass %+N as %T (... mismatch)", nl->n, tl); + if(nl != nil && nl->n->op == ONAME && nl->n->isddd && !isddd) { + // TODO(rsc): This is not actually illegal, but it will help catch bugs. + yyerror("to pass '%#N' as ...%T, use '%#N...'", nl->n, t->type, nl->n); + isddd = 1; } - if(nl != nil && nl->next == nil && nl->n->isddd && eqtype(nl->n->type, t)) + if(isddd) { + if(nl == nil) + goto notenough; + if(nl->next != nil) + goto toomany; + n = nl->n; + setlineno(n); + if(n->type != T) + nl->n = assignconv(n, t, desc); goto out; + } for(; nl; nl=nl->next) { + n = nl->n; setlineno(nl->n); - defaultlit(&nl->n, t->type); - if(assignop(nl->n->type, t->type, &why) == 0) - yyerror("cannot use %+N as type %T in %s%s", nl->n, t->type, desc, why); + if(n->type != T) + nl->n = assignconv(n, t->type, desc); } goto out; } - if(nl == nil) { - yyerror("not enough arguments to %#O", op); - goto out; - } + if(nl == nil) + goto notenough; n = nl->n; - setlineno(nl->n); + setlineno(n); if(n->type != T) nl->n = assignconv(n, t, desc); nl = nl->next; } - if(nl != nil) { - yyerror("too many arguments to %#O", op); - goto out; - } + if(nl != nil) + goto toomany; + if(isddd) + yyerror("invalid use of ... in %#O", op); out: lineno = lno; + return; + +notenough: + yyerror("not enough arguments to %#O", op); + goto out; + +toomany: + yyerror("too many arguments to %#O", op); + goto out; } /* @@ -1658,23 +1814,103 @@ indexdup(Node *n, Node *hash[], ulong nhash) hash[h] = n; } +static int +prime(ulong h, ulong sr) +{ + ulong n; + + for(n=3; n<=sr; n+=2) + if(h%n == 0) + return 0; + return 1; +} + +static ulong +inithash(Node *n, Node ***hash, Node **autohash, ulong nautohash) +{ + ulong h, sr; + NodeList *ll; + int i; + + // count the number of entries + h = 0; + for(ll=n->list; ll; ll=ll->next) + h++; + + // if the auto hash table is + // large enough use it. + if(h <= nautohash) { + *hash = autohash; + memset(*hash, 0, nautohash * sizeof(**hash)); + return nautohash; + } + + // make hash size odd and 12% larger than entries + h += h/8; + h |= 1; + + // calculate sqrt of h + sr = h/2; + for(i=0; i<5; i++) + sr = (sr + h/sr)/2; + + // check for primeality + while(!prime(h, sr)) + h += 2; + + // build and return a throw-away hash table + *hash = mal(h * sizeof(**hash)); + memset(*hash, 0, h * sizeof(**hash)); + return h; +} + static void typecheckcomplit(Node **np) { int bad, i, len, nerr; - Node *l, *n, *hash[101]; + Node *l, *n, **hash; NodeList *ll; - Type *t, *f; + Type *t, *f, *pushtype; Sym *s; + int32 lno; + ulong nhash; + Node *autohash[101]; n = *np; + lno = lineno; - memset(hash, 0, sizeof hash); + if(n->right == N) { + if(n->list != nil) + setlineno(n->list->n); + yyerror("missing type in composite literal"); + goto error; + } + setlineno(n->right); l = typecheck(&n->right /* sic */, Etype); 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; + } + } + } + switch(t->etype) { default: yyerror("invalid type for composite literal: %T", t); @@ -1682,31 +1918,29 @@ typecheckcomplit(Node **np) break; case TARRAY: + nhash = inithash(n, &hash, autohash, nelem(autohash)); + len = 0; i = 0; for(ll=n->list; ll; ll=ll->next) { l = ll->n; - if(l->op == OKEY) { - typecheck(&l->left, Erv); - evconst(l->left); - i = nonnegconst(l->left); - if(i < 0) { - yyerror("array index must be non-negative integer constant"); - i = -(1<<30); // stay negative for a while - } - typecheck(&l->right, Erv); - defaultlit(&l->right, t->type); - l->right = assignconv(l->right, t->type, "array index"); - } else { - typecheck(&ll->n, Erv); - defaultlit(&ll->n, t->type); - ll->n = assignconv(ll->n, t->type, "array index"); - ll->n = nod(OKEY, nodintconst(i), ll->n); - ll->n->left->type = types[TINT]; - ll->n->left->typecheck = 1; + setlineno(l); + if(l->op != OKEY) { + l = nod(OKEY, nodintconst(i), l); + l->left->type = types[TINT]; + l->left->typecheck = 1; + ll->n = l; + } + + typecheck(&l->left, Erv); + evconst(l->left); + i = nonnegconst(l->left); + if(i < 0) { + yyerror("array index must be non-negative integer constant"); + i = -(1<<30); // stay negative for a while } if(i >= 0) - indexdup(ll->n->left, hash, nelem(hash)); + indexdup(l->left, hash, nhash); i++; if(i > len) { len = i; @@ -1716,6 +1950,12 @@ typecheckcomplit(Node **np) t->bound = -1; // no more errors } } + + 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 index"); } if(t->bound == -100) t->bound = len; @@ -1725,20 +1965,27 @@ typecheckcomplit(Node **np) break; case TMAP: + nhash = inithash(n, &hash, autohash, nelem(autohash)); + for(ll=n->list; ll; ll=ll->next) { l = ll->n; + setlineno(l); if(l->op != OKEY) { typecheck(&ll->n, Erv); yyerror("missing key in map literal"); continue; } + typecheck(&l->left, Erv); - typecheck(&l->right, Erv); defaultlit(&l->left, t->down); - defaultlit(&l->right, t->type); l->left = assignconv(l->left, t->down, "map key"); + 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"); - keydup(l->left, hash, nelem(hash)); } n->op = OMAPLIT; break; @@ -1749,6 +1996,7 @@ typecheckcomplit(Node **np) // simple list of variables f = t->type; for(ll=n->list; ll; ll=ll->next) { + setlineno(ll->n); typecheck(&ll->n, Erv); if(f == nil) { if(!bad++) @@ -1766,9 +2014,12 @@ typecheckcomplit(Node **np) if(f != nil) yyerror("too few values in struct initializer"); } else { + nhash = inithash(n, &hash, autohash, nelem(autohash)); + // keyed list for(ll=n->list; ll; ll=ll->next) { l = ll->n; + setlineno(l); if(l->op != OKEY) { if(!bad++) yyerror("mixture of field:value and value initializers"); @@ -1781,6 +2032,11 @@ typecheckcomplit(Node **np) 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) + s = lookup(s->name); l->left = newname(s); l->left->typecheck = 1; f = lookdot1(s, t, t->type, 0); @@ -1790,7 +2046,7 @@ typecheckcomplit(Node **np) continue; } s = f->sym; - fielddup(newname(s), hash, nelem(hash)); + fielddup(newname(s), hash, nhash); l->right = assignconv(l->right, f->type, "field value"); } } @@ -1802,11 +2058,13 @@ typecheckcomplit(Node **np) n->type = t; *np = n; + lineno = lno; return; error: n->type = T; *np = n; + lineno = lno; } /* @@ -1890,6 +2148,8 @@ islvalue(Node *n) case OINDEX: if(isfixedarray(n->left->type)) return islvalue(n->left); + if(n->left->type != T && n->left->type->etype == TSTRING) + return 0; // fall through case OIND: case ODOTPTR: diff --git a/src/cmd/gc/unsafe.c b/src/cmd/gc/unsafe.c index dbf6f708a..33f375631 100644 --- a/src/cmd/gc/unsafe.c +++ b/src/cmd/gc/unsafe.c @@ -11,13 +11,18 @@ * rewrite with a constant */ Node* -unsafenmagic(Node *fn, NodeList *args) +unsafenmagic(Node *nn) { Node *r, *n; Sym *s; Type *t, *tr; long v; Val val; + Node *fn; + NodeList *args; + + fn = nn->left; + args = nn->list; if(safemode || fn == N || fn->op != ONAME || (s = fn->sym) == S) goto no; @@ -35,13 +40,14 @@ unsafenmagic(Node *fn, NodeList *args) defaultlit(&r, T); tr = r->type; if(tr == T) - goto no; + goto bad; v = tr->width; goto yes; } if(strcmp(s->name, "Offsetof") == 0) { + typecheck(&r, Erv); if(r->op != ODOT && r->op != ODOTPTR) - goto no; + goto bad; typecheck(&r, Erv); v = r->xoffset; goto yes; @@ -51,7 +57,7 @@ unsafenmagic(Node *fn, NodeList *args) defaultlit(&r, T); tr = r->type; if(tr == T) - goto no; + goto bad; // make struct { byte; T; } t = typ(TSTRUCT); @@ -70,9 +76,15 @@ unsafenmagic(Node *fn, NodeList *args) no: return N; +bad: + yyerror("invalid expression %#N", nn); + v = 0; + goto ret; + yes: if(args->next != nil) yyerror("extra arguments for %S", s); +ret: // any side effects disappear; ignore init val.ctype = CTINT; val.u.xval = mal(sizeof(*n->val.u.xval)); diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index 4f59d5598..fa3e5d5e4 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -11,12 +11,14 @@ static Node* makenewvar(Type*, NodeList**, Node**); 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, Type**, NodeList*, int, NodeList**); +static NodeList* ascompatte(int, int, Type**, NodeList*, int, NodeList**); static Node* convas(Node*, NodeList**); static void heapmoves(void); static NodeList* paramstoheap(Type **argin, int out); static NodeList* reorder1(NodeList*); static NodeList* reorder3(NodeList*); +static Node* addstr(Node*, NodeList**); +static Node* append(Node*, NodeList**); static NodeList* walkdefstack; @@ -134,6 +136,10 @@ walkdeftype(Node *n) n->diag = 1; goto ret; } + if(n->type == T) { + n->diag = 1; + goto ret; + } // copy new type and clear fields // that don't come along @@ -257,6 +263,10 @@ walkdef(Node *n) yyerror("const initializer must be constant"); goto ret; } + if(isconst(e, CTNIL)) { + yyerror("const initializer cannot be nil"); + goto ret; + } t = n->type; if(t != T) { convlit(&e, t); @@ -281,6 +291,13 @@ walkdef(Node *n) if(n->defn == N) { if(n->etype != 0) // like OPRINTN break; + if(nerrors > 0) { + // Can have undefined variables in x := foo + // that make x have an n->ndefn == nil. + // If there are other errors anyway, don't + // bother adding to the noise. + break; + } fatal("var without type, init: %S", n->sym); } if(n->defn->op == ONAME) { @@ -292,10 +309,14 @@ walkdef(Node *n) break; case OTYPE: + if(curfn) + defercheckwidth(); n->walkdef = 1; n->type = typ(TFORW); n->type->sym = n->sym; walkdeftype(n); + if(curfn) + resumecheckwidth(); break; case OPACK: @@ -354,7 +375,7 @@ walkstmt(Node **np) NodeList *init; NodeList *ll, *rl; int cl, lno; - Node *n; + Node *n, *f; n = *np; if(n == N) @@ -481,11 +502,19 @@ walkstmt(Node **np) n->list = nil; break; } + if(count(n->list) == 1 && count(rl) > 1) { + // 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); + n->list = concat(list1(f), ascompatet(n->op, rl, &f->type, 0, &n->ninit)); + break; + } ll = ascompatee(n->op, rl, n->list, &n->ninit); n->list = reorder3(ll); break; } - ll = ascompatte(n->op, getoutarg(curfn->type), n->list, 1, &n->ninit); + ll = ascompatte(n->op, 0, getoutarg(curfn->type), n->list, 1, &n->ninit); n->list = ll; break; @@ -542,6 +571,7 @@ walkexpr(Node **np, NodeList **init) NodeList *ll, *lr, *lpost; Type *t; int et; + int64 v, v1, v2, len; int32 lno; Node *n, *fn; char buf[100], *p; @@ -594,8 +624,6 @@ walkexpr(Node **np, NodeList **init) case OMINUS: case OPLUS: case OCOM: - case OLEN: - case OCAP: case OREAL: case OIMAG: case ODOT: @@ -606,13 +634,27 @@ walkexpr(Node **np, NodeList **init) 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; + if(isptr[t->etype]) + t = t->type; + if(isfixedarray(t)) { + safeexpr(n->left, init); + nodconst(n, n->type, t->bound); + n->typecheck = 1; + } + goto ret; + case OLSH: case ORSH: case OAND: case OOR: case OXOR: - case OANDAND: - case OOROR: case OSUB: case OMUL: case OEQ: @@ -626,6 +668,17 @@ walkexpr(Node **np, NodeList **init) walkexpr(&n->left, init); walkexpr(&n->right, init); goto ret; + + case OANDAND: + case OOROR: + walkexpr(&n->left, init); + // cannot put side effects from n->right on init, + // because they cannot run before n->left is checked. + // save elsewhere and store on the eventual n->right. + ll = nil; + walkexpr(&n->right, &ll); + n->right->ninit = concat(n->right->ninit, ll); + goto ret; case OPRINT: case OPRINTN: @@ -656,7 +709,7 @@ walkexpr(Node **np, NodeList **init) goto ret; walkexpr(&n->left, init); walkexprlist(n->list, init); - ll = ascompatte(n->op, getinarg(t), n->list, 0, init); + ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init); n->list = reorder1(ll); goto ret; @@ -666,7 +719,7 @@ walkexpr(Node **np, NodeList **init) goto ret; walkexpr(&n->left, init); walkexprlist(n->list, init); - ll = ascompatte(n->op, getinarg(t), n->list, 0, init); + ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init); n->list = reorder1(ll); if(isselect(n)) { // special prob with selectsend and selectrecv: @@ -676,7 +729,7 @@ walkexpr(Node **np, NodeList **init) Node *b; b = nodbool(0); typecheck(&b, Erv); - lr = ascompatte(n->op, getoutarg(t), list1(b), 0, init); + lr = ascompatte(n->op, 0, getoutarg(t), list1(b), 0, init); n->list = concat(n->list, lr); } goto ret; @@ -687,8 +740,8 @@ walkexpr(Node **np, NodeList **init) goto ret; walkexpr(&n->left, init); walkexprlist(n->list, init); - ll = ascompatte(n->op, getinarg(t), n->list, 0, init); - lr = ascompatte(n->op, getthis(t), list1(n->left->left), 0, 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 = concat(ll, lr); n->left->left = N; ullmancalc(n->left); @@ -882,43 +935,55 @@ walkexpr(Node **np, NodeList **init) case OCONV: case OCONVNOP: if(thechar == '5') { - if(isfloat[n->left->type->etype] && - (n->type->etype == TINT64 || n->type->etype == TUINT64)) { - n = mkcall("float64toint64", n->type, init, conv(n->left, types[TFLOAT64])); - goto ret; + if(isfloat[n->left->type->etype]) { + if(n->type->etype == TINT64) { + n = mkcall("float64toint64", n->type, init, conv(n->left, types[TFLOAT64])); + goto ret; + } + if(n->type->etype == TUINT64) { + n = mkcall("float64touint64", n->type, init, conv(n->left, types[TFLOAT64])); + goto ret; + } } - if((n->left->type->etype == TINT64 || n->left->type->etype == TUINT64) && - isfloat[n->type->etype]) { - n = mkcall("int64tofloat64", n->type, init, conv(n->left, types[TINT64])); - goto ret; + if(isfloat[n->type->etype]) { + if(n->left->type->etype == TINT64) { + n = mkcall("int64tofloat64", n->type, init, conv(n->left, types[TINT64])); + goto ret; + } + if(n->left->type->etype == TUINT64) { + n = mkcall("uint64tofloat64", n->type, init, conv(n->left, types[TUINT64])); + goto ret; + } } } walkexpr(&n->left, init); goto ret; case OASOP: - n->left = safeexpr(n->left, init); - walkexpr(&n->left, init); - l = n->left; - walkexpr(&n->right, init); if(n->etype == OANDNOT) { n->etype = OAND; n->right = nod(OCOM, n->right, N); typecheck(&n->right, Erv); } + n->left = safeexpr(n->left, init); + walkexpr(&n->left, init); + l = n->left; + walkexpr(&n->right, init); /* * on 32-bit arch, rewrite 64-bit ops into l = l op r. * on 386, rewrite float ops into l = l op r. * everywhere, rewrite map ops into l = l op r. * everywhere, rewrite string += into l = l op r. + * everywhere, rewrite complex /= into l = l op r. * TODO(rsc): Maybe this rewrite should be done always? */ et = n->left->type->etype; if((widthptr == 4 && (et == TUINT64 || et == TINT64)) || (thechar == '8' && isfloat[et]) || l->op == OINDEXMAP || - et == TSTRING) { + et == TSTRING || + (iscomplex[et] && n->etype == ODIV)) { l = safeexpr(n->left, init); a = l; if(a->op == OINDEXMAP) { @@ -952,9 +1017,11 @@ walkexpr(Node **np, NodeList **init) */ et = n->left->type->etype; if(iscomplex[et] && n->op == ODIV) { - n = mkcall("complex128div", n->type, init, + t = n->type; + n = mkcall("complex128div", types[TCOMPLEX128], init, conv(n->left, types[TCOMPLEX128]), conv(n->right, types[TCOMPLEX128])); + n = conv(n, t); goto ret; } /* @@ -981,11 +1048,36 @@ walkexpr(Node **np, NodeList **init) // if range of type cannot exceed static array bound, // disable bounds check - if(!isslice(n->left->type)) + if(isfixedarray(n->left->type)) if(n->right->type->width < 4) if((1<<(8*n->right->type->width)) <= n->left->type->bound) n->etype = 1; + if(isconst(n->left, CTSTR)) + if(n->right->type->width < 4) + if((1<<(8*n->right->type->width)) <= n->left->val.u.sval->len) + n->etype = 1; + + // check for static out of bounds + if(isconst(n->right, CTINT) && !n->etype) { + v = mpgetfix(n->right->val.u.xval); + len = 1LL<<60; + t = n->left->type; + if(isconst(n->left, CTSTR)) + len = n->left->val.u.sval->len; + if(t != T && isptr[t->etype]) + t = t->type; + if(isfixedarray(t)) + len = t->bound; + if(v < 0 || v >= (1LL<<31) || v >= len) + yyerror("index out of bounds"); + else if(isconst(n->left, CTSTR)) { + // replace "abc"[2] with 'b'. + // delayed until now because "abc"[2] is not + // an ideal constant. + nodconst(n, n->type, n->left->val.u.sval->s[v]); + } + } goto ret; case OINDEXMAP: @@ -1002,24 +1094,61 @@ walkexpr(Node **np, NodeList **init) goto ret; case OSLICE: + case OSLICEARR: walkexpr(&n->left, init); n->left = safeexpr(n->left, init); walkexpr(&n->right->left, init); n->right->left = safeexpr(n->right->left, init); walkexpr(&n->right->right, init); n->right->right = safeexpr(n->right->right, init); + + len = 1LL<<60; + t = n->left->type; + if(t != T && isptr[t->etype]) + t = t->type; + if(isfixedarray(t)) + len = t->bound; + + // check for static out of bounds + // NOTE: v > len not v >= len. + v1 = -1; + v2 = -1; + if(isconst(n->right->left, CTINT)) { + v1 = mpgetfix(n->right->left->val.u.xval); + if(v1 < 0 || v1 >= (1LL<<31) || v1 > len) { + yyerror("slice index out of bounds"); + v1 = -1; + } + } + if(isconst(n->right->right, CTINT)) { + v2 = mpgetfix(n->right->right->val.u.xval); + if(v2 < 0 || v2 >= (1LL<<31) || v2 > len) { + yyerror("slice index out of bounds"); + v2 = -1; + } + } + if(v1 >= 0 && v2 >= 0 && v1 > v2) + yyerror("inverted slice range"); + + if(n->op == OSLICEARR) + goto slicearray; + // dynamic slice - // sliceslice(old []any, lb int, hb int, width int) (ary []any) - // sliceslice1(old []any, lb int, width int) (ary []any) + // sliceslice(old []any, lb uint64, hb uint64, width uint64) (ary []any) + // sliceslice1(old []any, lb uint64, width uint64) (ary []any) t = n->type; + if(n->right->left == N) + l = nodintconst(0); + else + l = conv(n->right->left, types[TUINT64]); if(n->right->right != N) { fn = syslook("sliceslice", 1); argtype(fn, t->type); // any-1 argtype(fn, t->type); // any-2 n = mkcall1(fn, t, init, n->left, - conv(n->right->left, types[TINT]), - conv(n->right->right, types[TINT]), + l, + conv(n->right->right, types[TUINT64]), nodintconst(t->type->width)); } else { fn = syslook("sliceslice1", 1); @@ -1027,47 +1156,33 @@ walkexpr(Node **np, NodeList **init) argtype(fn, t->type); // any-2 n = mkcall1(fn, t, init, n->left, - conv(n->right->left, types[TINT]), + l, nodintconst(t->type->width)); } goto ret; - case OSLICEARR: - walkexpr(&n->left, init); - n->left = safeexpr(n->left, init); - walkexpr(&n->right->left, init); - n->right->left = safeexpr(n->right->left, init); - walkexpr(&n->right->right, init); - n->right->right = safeexpr(n->right->right, init); + slicearray: // static slice - // slicearray(old *any, nel int, lb int, hb int, width int) (ary []any) + // slicearray(old *any, uint64 nel, lb uint64, hb uint64, width uint64) (ary []any) t = n->type; fn = syslook("slicearray", 1); - argtype(fn, n->left->type); // any-1 + argtype(fn, n->left->type->type); // any-1 argtype(fn, t->type); // any-2 + if(n->right->left == N) + l = nodintconst(0); + else + l = conv(n->right->left, types[TUINT64]); if(n->right->right == N) - r = nodintconst(n->left->type->bound); + r = nodintconst(n->left->type->type->bound); else - r = conv(n->right->right, types[TINT]); + r = conv(n->right->right, types[TUINT64]); n = mkcall1(fn, t, init, - nod(OADDR, n->left, N), nodintconst(n->left->type->bound), - conv(n->right->left, types[TINT]), + n->left, nodintconst(n->left->type->type->bound), + l, r, nodintconst(t->type->width)); goto ret; - case OCONVSLICE: - // slicearray(old *any, nel int, lb int, hb int, width int) (ary []any) - fn = syslook("slicearray", 1); - argtype(fn, n->left->type->type); // any-1 - argtype(fn, n->type->type); // any-2 - n = mkcall1(fn, n->type, init, n->left, - nodintconst(n->left->type->type->bound), - nodintconst(0), - nodintconst(n->left->type->type->bound), - nodintconst(n->type->type->width)); - goto ret; - case OADDR:; Node *nvar, *nstar; @@ -1118,46 +1233,62 @@ walkexpr(Node **np, NodeList **init) goto ret; } + // prepare for rewrite below + if(n->etype == OEQ || n->etype == ONE) { + n->left = cheapexpr(n->left, init); + n->right = cheapexpr(n->right, init); + } + // sys_cmpstring(s1, s2) :: 0 r = mkcall("cmpstring", types[TINT], init, conv(n->left, types[TSTRING]), conv(n->right, types[TSTRING])); r = nod(n->etype, r, nodintconst(0)); + + // quick check of len before full compare for == or != + if(n->etype == OEQ || n->etype == ONE) { + if(n->etype == OEQ) + r = nod(OANDAND, nod(OEQ, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); + else + r = nod(OOROR, nod(ONE, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); + typecheck(&r, Erv); + walkexpr(&r, nil); + } typecheck(&r, Erv); n = r; goto ret; case OADDSTR: - // sys_catstring(s1, s2) - n = mkcall("catstring", n->type, init, - conv(n->left, types[TSTRING]), - conv(n->right, types[TSTRING])); + n = addstr(n, init); goto ret; case OSLICESTR: // sys_slicestring(s, lb, hb) + if(n->right->left == N) + l = nodintconst(0); + else + l = conv(n->right->left, types[TINT]); if(n->right->right) { n = mkcall("slicestring", n->type, init, conv(n->left, types[TSTRING]), - conv(n->right->left, types[TINT]), + l, conv(n->right->right, types[TINT])); } else { n = mkcall("slicestring1", n->type, init, conv(n->left, types[TSTRING]), - conv(n->right->left, types[TINT])); + l); } goto ret; - - case OINDEXSTR: - // TODO(rsc): should be done in back end - // sys_indexstring(s, i) - n = mkcall("indexstring", n->type, init, - conv(n->left, types[TSTRING]), - conv(n->right, types[TINT])); + + case OAPPEND: + n = append(n, init); goto ret; case OCOPY: - fn = syslook("slicecopy", 1); + if(n->right->type->etype == TSTRING) + fn = syslook("slicestringcopy", 1); + else + fn = syslook("slicecopy", 1); argtype(fn, n->left->type); argtype(fn, n->right->type); n = mkcall1(fn, n->type, init, @@ -1441,47 +1572,51 @@ mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init) /* * helpers for shape errors */ -static void +static char* dumptypes(Type **nl, char *what) { int first; Type *l; Iter savel; + Fmt fmt; + fmtstrinit(&fmt); + fmtprint(&fmt, "\t"); l = structfirst(&savel, nl); - print("\t"); first = 1; for(l = structfirst(&savel, nl); l != T; l = structnext(&savel)) { if(first) first = 0; else - print(", "); - print("%T", l); + fmtprint(&fmt, ", "); + fmtprint(&fmt, "%T", l); } if(first) - print("[no arguments %s]", what); - print("\n"); + fmtprint(&fmt, "[no arguments %s]", what); + return fmtstrflush(&fmt); } -static void +static char* dumpnodetypes(NodeList *l, char *what) { int first; Node *r; + Fmt fmt; - print("\t"); + fmtstrinit(&fmt); + fmtprint(&fmt, "\t"); first = 1; for(; l; l=l->next) { r = l->n; if(first) first = 0; else - print(", "); - print("%T", r->type); + fmtprint(&fmt, ", "); + fmtprint(&fmt, "%T", r->type); } if(first) - print("[no arguments %s]", what); - print("\n"); + fmtprint(&fmt, "[no arguments %s]", what); + return fmtstrflush(&fmt); } /* @@ -1491,12 +1626,13 @@ dumpnodetypes(NodeList *l, char *what) * func(expr-list) */ static NodeList* -ascompatte(int op, Type **nl, NodeList *lr, int fp, NodeList **init) +ascompatte(int op, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init) { Type *l, *ll; Node *r, *a; NodeList *nn, *lr0, *alist; Iter savel; + char *l1, *l2; lr0 = lr; l = structfirst(&savel, nl); @@ -1545,7 +1681,7 @@ loop: // only if we are assigning a single ddd // argument to a ddd parameter then it is // passed thru unencapsulated - if(r != N && lr->next == nil && r->isddd && eqtype(l->type, r->type)) { + if(r != N && lr->next == nil && isddd && eqtype(l->type, r->type)) { a = nod(OAS, nodarg(l, fp), r); a = convas(a, init); nn = list(nn, a); @@ -1561,12 +1697,12 @@ loop: if(l == T || r == N) { if(l != T || r != N) { + l1 = dumptypes(nl, "expected"); + l2 = dumpnodetypes(lr0, "given"); if(l != T) - yyerror("not enough arguments to %O", op); + yyerror("not enough arguments to %O\n%s\n%s", op, l1, l2); else - yyerror("too many arguments to %O", op); - dumptypes(nl, "expected"); - dumpnodetypes(lr0, "given"); + yyerror("too many arguments to %O\n%s\n%s", op, l1, l2); } goto ret; } @@ -1740,7 +1876,7 @@ walkprint(Node *nn, NodeList **init, int defer) if(defer) { if(op == OPRINTN) fmtprint(&fmt, "\n"); - on = syslook("printf", 1); + on = syslook("goprintf", 1); on->type = functype(nil, intypes, nil); args->n = nod(OLITERAL, N, N); args->n->val.ctype = CTSTR; @@ -1769,7 +1905,7 @@ callnew(Type *t) Node *fn; dowidth(t); - fn = syslook("mal", 1); + fn = syslook("new", 1); argtype(fn, t); return mkcall1(fn, ptrto(t), nil, nodintconst(t->width)); } @@ -2046,12 +2182,17 @@ static void heapmoves(void) { NodeList *nn; - + int32 lno; + + lno = lineno; + lineno = curfn->lineno; nn = paramstoheap(getthis(curfn->type), 0); nn = concat(nn, paramstoheap(getinarg(curfn->type), 0)); nn = concat(nn, paramstoheap(getoutarg(curfn->type), 1)); curfn->enter = concat(curfn->enter, nn); + lineno = curfn->endlineno; curfn->exit = returnsfromheap(getoutarg(curfn->type)); + lineno = lno; } static Node* @@ -2143,3 +2284,83 @@ mapfn(char *name, Type *t) argtype(fn, t->type); 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 + count++; // r + + // prepare call of runtime.catstring of type int, string, string, string + // with as many strings as we have. + cat = syslook("concatstring", 1); + cat->type = T; + cat->ntype = nod(OTFUNC, N, N); + in = list1(nod(ODCLFIELD, N, typenod(types[TINT]))); // count + typstr = typenod(types[TSTRING]); + for(i=0; intype->list = in; + cat->ntype->rlist = list1(nod(ODCLFIELD, N, typstr)); + + args = nil; + for(r=n; r->op == OADDSTR; r=r->left) + args = concat(list1(conv(r->right, types[TSTRING])), args); + args = concat(list1(conv(r, types[TSTRING])), args); + args = concat(list1(nodintconst(count)), args); + + r = nod(OCALL, cat, N); + r->list = args; + typecheck(&r, Erv); + walkexpr(&r, init); + r->type = n->type; + + return r; +} + +static Node* +append(Node *n, NodeList **init) +{ + int i, j; + Node *f, *r; + NodeList *in, *args; + + if(n->isddd) { + f = syslook("appendslice", 1); + argtype(f, n->type); + argtype(f, n->type->type); + argtype(f, n->type); + r = mkcall1(f, n->type, init, typename(n->type), n->list->n, n->list->next->n); + return r; + } + + j = count(n->list) - 1; + f = syslook("append", 1); + f->type = T; + f->ntype = nod(OTFUNC, N, N); + in = list1(nod(ODCLFIELD, N, typenod(ptrto(types[TUINT8])))); // type + in = list(in, nod(ODCLFIELD, N, typenod(types[TINT]))); // count + in = list(in, nod(ODCLFIELD, N, typenod(n->type))); // slice + for(i=0; itype->type))); + f->ntype->list = in; + f->ntype->rlist = list1(nod(ODCLFIELD, N, typenod(n->type))); + + args = list1(typename(n->type)); + args = list(args, nodintconst(j)); + args = concat(args, n->list); + + r = nod(OCALL, f, N); + r->list = args; + typecheck(&r, Erv); + walkexpr(&r, init); + r->type = n->type; + + return r; +} diff --git a/src/cmd/godefs/Makefile b/src/cmd/godefs/Makefile index 49244f152..b5c76fb0f 100644 --- a/src/cmd/godefs/Makefile +++ b/src/cmd/godefs/Makefile @@ -2,7 +2,8 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) TARG=godefs OFILES=\ @@ -12,13 +13,4 @@ OFILES=\ HFILES=a.h -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lbio -l9 - -clean: - rm -f *.$O $(TARG) - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) - -$(OFILES): $(HFILES) +include ../../Make.ccmd diff --git a/src/cmd/godefs/main.c b/src/cmd/godefs/main.c index 6ff542f48..69ee1be5d 100644 --- a/src/cmd/godefs/main.c +++ b/src/cmd/godefs/main.c @@ -82,7 +82,7 @@ #include "a.h" -#ifdef __MINGW32__ +#ifdef _WIN32 int spawn(char *prog, char **argv) { @@ -133,7 +133,7 @@ Lang go = "type %s struct {\n", "type %s struct {\n", - "\tPad%d [%d]byte;\n", + "\tPad_godefs_%d [%d]byte;\n", "}\n", gotypefmt, @@ -150,7 +150,7 @@ Lang c = "typedef struct %s %s;\nstruct %s {\n", "typedef union %s %s;\nunion %s {\n", - "\tbyte pad%d[%d];\n", + "\tbyte pad_godefs_%d[%d];\n", "};\n", ctypefmt, @@ -373,6 +373,8 @@ Continue: prefix = prefixlen(t); for(j=0; jnf; j++) { f = &t->f[j]; + if(f->type->kind == 0) + continue; // padding if(t->kind == Struct || lang == &go) { if(f->offset%8 != 0 || f->size%8 != 0) { @@ -391,7 +393,7 @@ Continue: if(cutprefix(name)) name += prefix; if(strcmp(name, "") == 0) { - snprint(nambuf, sizeof nambuf, "Pad%d", npad++); + snprint(nambuf, sizeof nambuf, "Pad_godefs_%d", npad++); name = nambuf; } Bprint(bout, "\t%#lT;\n", name, f->type); diff --git a/src/cmd/godefs/stabs.c b/src/cmd/godefs/stabs.c index 8d3be1913..1bc96d4c8 100644 --- a/src/cmd/godefs/stabs.c +++ b/src/cmd/godefs/stabs.c @@ -363,13 +363,22 @@ parsedef(char **pp, char *name) return nil; } + while(f->type->kind == Typedef) + f->type = f->type->type; + 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; + } + // rewrite // uint32 x : 8; // into // uint8 x; // hooray for bitfields. - while(f->type->kind == Typedef) - f->type = f->type->type; while(Int16 <= f->type->kind && f->type->kind <= Uint64 && kindsize[f->type->kind] > f->size) { tt = emalloc(sizeof *tt); *tt = *f->type; diff --git a/src/cmd/godoc/Makefile b/src/cmd/godoc/Makefile index d799219dd..c4e0fd9f9 100644 --- a/src/cmd/godoc/Makefile +++ b/src/cmd/godoc/Makefile @@ -2,16 +2,19 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=godoc GOFILES=\ codewalk.go\ + dirtrees.go\ + format.go\ godoc.go\ index.go\ main.go\ mapping.go\ snippet.go\ spec.go\ + utils.go\ include ../../Make.cmd diff --git a/src/cmd/godoc/codewalk.go b/src/cmd/godoc/codewalk.go index 806849c00..bda63a9f6 100644 --- a/src/cmd/godoc/codewalk.go +++ b/src/cmd/godoc/codewalk.go @@ -31,26 +31,26 @@ import ( // Handler for /doc/codewalk/ and below. -func codewalk(c *http.Conn, r *http.Request) { +func codewalk(w http.ResponseWriter, r *http.Request) { relpath := r.URL.Path[len("/doc/codewalk/"):] abspath := absolutePath(r.URL.Path[1:], *goroot) r.ParseForm() if f := r.FormValue("fileprint"); f != "" { - codewalkFileprint(c, r, f) + codewalkFileprint(w, r, f) return } // If directory exists, serve list of code walks. dir, err := os.Lstat(abspath) if err == nil && dir.IsDirectory() { - codewalkDir(c, r, relpath, abspath) + codewalkDir(w, r, relpath, abspath) return } // If file exists, serve using standard file server. if err == nil { - serveFile(c, r) + serveFile(w, r) return } @@ -58,18 +58,18 @@ func codewalk(c *http.Conn, r *http.Request) { // a codewalk description. cw, err := loadCodewalk(abspath + ".xml") if err != nil { - log.Stderr(err) - serveError(c, r, relpath, err) + log.Print(err) + serveError(w, r, relpath, err) return } // Canonicalize the path and redirect if changed - if redirect(c, r) { + if redirect(w, r) { return } b := applyTemplate(codewalkHTML, "codewalk", cw) - servePage(c, "Codewalk: "+cw.Title, "", "", b) + servePage(w, "Codewalk: "+cw.Title, "", "", b) } @@ -178,7 +178,7 @@ func loadCodewalk(file string) (*Codewalk, os.Error) { // codewalkDir serves the codewalk directory listing. // It scans the directory for subdirectories or files named *.xml // and prepares a table. -func codewalkDir(c *http.Conn, r *http.Request, relpath, abspath string) { +func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string) { type elem struct { Name string Title string @@ -186,8 +186,8 @@ func codewalkDir(c *http.Conn, r *http.Request, relpath, abspath string) { dir, err := ioutil.ReadDir(abspath) if err != nil { - log.Stderr(err) - serveError(c, r, relpath, err) + log.Print(err) + serveError(w, r, relpath, err) return } var v vector.Vector @@ -204,7 +204,7 @@ func codewalkDir(c *http.Conn, r *http.Request, relpath, abspath string) { } b := applyTemplate(codewalkdirHTML, "codewalkdir", v) - servePage(c, "Codewalks", "", "", b) + servePage(w, "Codewalks", "", "", b) } @@ -214,11 +214,12 @@ func codewalkDir(c *http.Conn, r *http.Request, relpath, abspath string) { // in the response. This format is used for the middle window pane // of the codewalk pages. It is a separate iframe and does not get // the usual godoc HTML wrapper. -func codewalkFileprint(c *http.Conn, r *http.Request, f string) { +func codewalkFileprint(w http.ResponseWriter, r *http.Request, f string) { abspath := absolutePath(f, *goroot) data, err := ioutil.ReadFile(abspath) if err != nil { - serveError(c, r, f, err) + log.Print(err) + serveError(w, r, f, err) return } lo, _ := strconv.Atoi(r.FormValue("lo")) @@ -242,17 +243,17 @@ func codewalkFileprint(c *http.Conn, r *http.Request, f string) { } } - io.WriteString(c, `

    `)
    -	template.HTMLEscape(c, data[0:mark])
    -	io.WriteString(c, "")
    -	template.HTMLEscape(c, data[mark:lo])
    +	io.WriteString(w, `
    `)
    +	template.HTMLEscape(w, data[0:mark])
    +	io.WriteString(w, "")
    +	template.HTMLEscape(w, data[mark:lo])
     	if lo < hi {
    -		io.WriteString(c, "
    ") - template.HTMLEscape(c, data[lo:hi]) - io.WriteString(c, "
    ") + io.WriteString(w, "
    ") + template.HTMLEscape(w, data[lo:hi]) + io.WriteString(w, "
    ") } - template.HTMLEscape(c, data[hi:]) - io.WriteString(c, "
    ") + template.HTMLEscape(w, data[hi:]) + io.WriteString(w, "
    ") } @@ -450,13 +451,13 @@ func addrRegexp(data []byte, lo, hi int, dir byte, pattern string) (int, int, os // through file, but that seems like overkill. return 0, 0, os.NewError("reverse search not implemented") } - m := re.Execute(data[hi:]) + m := re.FindIndex(data[hi:]) if len(m) > 0 { m[0] += hi m[1] += hi } else if hi > 0 { // No match. Wrap to beginning of data. - m = re.Execute(data) + m = re.FindIndex(data) } if len(m) == 0 { return 0, 0, os.NewError("no match for " + pattern) diff --git a/src/cmd/godoc/dirtrees.go b/src/cmd/godoc/dirtrees.go new file mode 100644 index 000000000..edb4a169d --- /dev/null +++ b/src/cmd/godoc/dirtrees.go @@ -0,0 +1,341 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains the code dealing with package directory trees. + +package main + +import ( + "bytes" + "go/doc" + "go/parser" + "go/token" + "io/ioutil" + "os" + pathutil "path" + "strings" + "unicode" +) + + +type Directory struct { + Depth int + Path string // includes Name + Name string + Text string // package documentation, if any + Dirs []*Directory // subdirectories +} + + +func isGoFile(f *os.FileInfo) bool { + return f.IsRegular() && + !strings.HasPrefix(f.Name, ".") && // ignore .files + pathutil.Ext(f.Name) == ".go" +} + + +func isPkgFile(f *os.FileInfo) bool { + return isGoFile(f) && + !strings.HasSuffix(f.Name, "_test.go") // ignore test files +} + + +func isPkgDir(f *os.FileInfo) bool { + return f.IsDirectory() && len(f.Name) > 0 && f.Name[0] != '_' +} + + +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 +} + + +func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth int) *Directory { + if b.pathFilter != nil && !b.pathFilter(path) { + return nil + } + + if depth >= b.maxDepth { + // return a dummy directory so that the parent directory + // doesn't get discarded just because we reached the max + // directory depth + return &Directory{depth, path, name, "", nil} + } + + list, _ := ioutil.ReadDir(path) // ignore errors + + // determine number of subdirectories and if there are package files + ndirs := 0 + hasPkgFiles := false + var synopses [4]string // prioritized package documentation (0 == highest priority) + for _, d := range list { + switch { + case isPkgDir(d): + ndirs++ + case isPkgFile(d): + // looks like a package file, but may just be a file ending in ".go"; + // don't just count it yet (otherwise we may end up with hasPkgFiles even + // though the directory doesn't contain any real package files - was bug) + if synopses[0] == "" { + // no "optimal" package synopsis yet; continue to collect synopses + file, err := parser.ParseFile(fset, pathutil.Join(path, d.Name), nil, + parser.ParseComments|parser.PackageClauseOnly) + if err == nil { + hasPkgFiles = true + if file.Doc != nil { + // prioritize documentation + i := -1 + switch file.Name.Name { + case name: + i = 0 // normal case: directory name matches package name + case fakePkgName: + i = 1 // synopses for commands + case "main": + i = 2 // directory contains a main package + default: + i = 3 // none of the above + } + if 0 <= i && i < len(synopses) && synopses[i] == "" { + synopses[i] = firstSentence(doc.CommentText(file.Doc)) + } + } + } + } + } + } + + // create subdirectory tree + var dirs []*Directory + if ndirs > 0 { + dirs = make([]*Directory, ndirs) + i := 0 + for _, d := range list { + if isPkgDir(d) { + dd := b.newDirTree(fset, pathutil.Join(path, d.Name), d.Name, depth+1) + if dd != nil { + dirs[i] = dd + i++ + } + } + } + dirs = dirs[0:i] + } + + // if there are no package files and no subdirectories + // containing package files, ignore the directory + if !hasPkgFiles && len(dirs) == 0 { + return nil + } + + // select the highest-priority synopsis for the directory entry, if any + synopsis := "" + for _, synopsis = range synopses { + if synopsis != "" { + break + } + } + + return &Directory{depth, path, name, synopsis, dirs} +} + + +// newDirectory creates a new package directory tree with at most maxDepth +// levels, anchored at root. The result tree is pruned such that it only +// contains directories that contain package files or that contain +// subdirectories containing package files (transitively). If a non-nil +// pathFilter is provided, directory paths additionally must be accepted +// by the filter (i.e., pathFilter(path) must be true). If a value >= 0 is +// provided for maxDepth, nodes at larger depths are pruned as well; they +// are assumed to contain package files even if their contents are not known +// (i.e., in this case the tree may contain directories w/o any package files). +// +func newDirectory(root string, pathFilter func(string) bool, maxDepth int) *Directory { + d, err := os.Lstat(root) + if err != nil || !isPkgDir(d) { + return nil + } + if maxDepth < 0 { + maxDepth = 1e6 // "infinity" + } + b := treeBuilder{pathFilter, maxDepth} + // the file set provided is only for local parsing, no position + // information escapes and thus we don't need to save the set + return b.newDirTree(token.NewFileSet(), root, d.Name, 0) +} + + +func (dir *Directory) writeLeafs(buf *bytes.Buffer) { + if dir != nil { + if len(dir.Dirs) == 0 { + buf.WriteString(dir.Path) + buf.WriteByte('\n') + return + } + + for _, d := range dir.Dirs { + d.writeLeafs(buf) + } + } +} + + +func (dir *Directory) walk(c chan<- *Directory, skipRoot bool) { + if dir != nil { + if !skipRoot { + c <- dir + } + for _, d := range dir.Dirs { + d.walk(c, false) + } + } +} + + +func (dir *Directory) iter(skipRoot bool) <-chan *Directory { + c := make(chan *Directory) + go func() { + dir.walk(c, skipRoot) + close(c) + }() + return c +} + + +func (dir *Directory) lookupLocal(name string) *Directory { + for _, d := range dir.Dirs { + if d.Name == name { + return d + } + } + return nil +} + + +// lookup looks for the *Directory for a given path, relative to dir. +func (dir *Directory) lookup(path string) *Directory { + d := strings.Split(dir.Path, "/", -1) + p := strings.Split(path, "/", -1) + i := 0 + for i < len(d) { + if i >= len(p) || d[i] != p[i] { + return nil + } + i++ + } + for dir != nil && i < len(p) { + dir = dir.lookupLocal(p[i]) + i++ + } + return dir +} + + +// DirEntry describes a directory entry. The Depth and Height values +// are useful for presenting an entry in an indented fashion. +// +type DirEntry struct { + Depth int // >= 0 + Height int // = DirList.MaxHeight - Depth, > 0 + Path string // includes Name, relative to DirList root + Name string + Synopsis string +} + + +type DirList struct { + MaxHeight int // directory tree height, > 0 + List []DirEntry +} + + +// listing creates a (linear) directory listing from a directory tree. +// If skipRoot is set, the root directory itself is excluded from the list. +// +func (root *Directory) listing(skipRoot bool) *DirList { + if root == nil { + return nil + } + + // determine number of entries n and maximum height + n := 0 + minDepth := 1 << 30 // infinity + maxDepth := 0 + for d := range root.iter(skipRoot) { + n++ + if minDepth > d.Depth { + minDepth = d.Depth + } + if maxDepth < d.Depth { + maxDepth = d.Depth + } + } + maxHeight := maxDepth - minDepth + 1 + + if n == 0 { + return nil + } + + // create list + list := make([]DirEntry, n) + i := 0 + for d := range root.iter(skipRoot) { + p := &list[i] + p.Depth = d.Depth - minDepth + p.Height = maxHeight - p.Depth + // the path is relative to root.Path - remove the root.Path + // prefix (the prefix should always be present but avoid + // crashes and check) + path := d.Path + if strings.HasPrefix(d.Path, root.Path) { + path = d.Path[len(root.Path):] + } + // remove trailing '/' if any - path must be relative + if len(path) > 0 && path[0] == '/' { + path = path[1:] + } + p.Path = path + p.Name = d.Name + p.Synopsis = d.Text + i++ + } + + return &DirList{maxHeight, list} +} diff --git a/src/cmd/godoc/doc.go b/src/cmd/godoc/doc.go index 955ed35bf..02779384c 100644 --- a/src/cmd/godoc/doc.go +++ b/src/cmd/godoc/doc.go @@ -24,7 +24,7 @@ godoc first tries localhost:6060 and then http://golang.org. godoc -q Reader Writer godoc -q math.Sin - godoc -server=:6666 -q sin + godoc -server=:6060 -q sin With the -http flag, it runs as a web server and presents the documentation as a web page. @@ -45,6 +45,10 @@ The flags are: print (exported) source in command-line mode -tabwidth=4 width of tabs in units of spaces + -timestamps=true + show timestamps with directory listings + -fulltext=false + build full text index for regular expression queries -path="" additional package directories (colon-separated) -html @@ -61,6 +65,10 @@ The flags are: 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 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 @@ -76,6 +84,13 @@ 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, it creates a search index from all .go files under -goroot (excluding files starting with .). The index is created at startup and is automatically updated every time the -sync command terminates with exit diff --git a/src/cmd/godoc/format.go b/src/cmd/godoc/format.go new file mode 100644 index 000000000..f68c67b24 --- /dev/null +++ b/src/cmd/godoc/format.go @@ -0,0 +1,342 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements FormatSelections and FormatText. +// FormatText is used to HTML-format Go and non-Go source +// text with line numbers and highlighted sections. It is +// built on top of FormatSelections, a generic formatter +// for "selected" text. + +package main + +import ( + "bytes" + "fmt" + "go/scanner" + "go/token" + "io" + "regexp" + "strconv" + "template" +) + + +// ---------------------------------------------------------------------------- +// Implementation of FormatSelections + +// A Selection is a function returning offset pairs []int{a, b} +// describing consecutive non-overlapping text segments [a, b). +// If there are no more segments, a Selection must return nil. +// +// TODO It's more efficient to return a pair (a, b int) instead +// of creating lots of slices. Need to determine how to +// indicate the end of a Selection. +// +type Selection func() []int + + +// A LinkWriter writes some start or end "tag" to w for the text offset offs. +// It is called by FormatSelections at the start or end of each link segment. +// +type LinkWriter func(w io.Writer, offs int, start bool) + + +// A SegmentWriter formats a text according to selections and writes it to w. +// The selections parameter is a bit set indicating which selections provided +// to FormatSelections overlap with the text segment: If the n'th bit is set +// in selections, the n'th selection provided to FormatSelections is overlapping +// with the text. +// +type SegmentWriter func(w io.Writer, text []byte, selections int) + + +// FormatSelections takes a text and writes it to w using link and segment +// writers lw and sw as follows: lw is invoked for consecutive segment starts +// and ends as specified through the links selection, and sw is invoked for +// consecutive segments of text overlapped by the same selections as specified +// by selections. The link writer lw may be nil, in which case the links +// Selection is ignored. +// +func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, sw SegmentWriter, selections ...Selection) { + if lw != nil { + selections = append(selections, links) + } + // compute the sequence of consecutive segment changes + changes := newMerger(selections) + // The i'th bit in bitset indicates that the text + // at the current offset is covered by selections[i]. + bitset := 0 + lastOffs := 0 + for { + // get the next segment change + index, offs, start := changes.next() + if index < 0 || offs > len(text) { + // no more segment changes or the next change + // is past the end of the text - we're done + break + } + // determine the kind of segment change + if index == len(selections)-1 { + // we have a link segment change: + // format the previous selection segment, write the + // link tag and start a new selection segment + sw(w, text[lastOffs:offs], bitset) + lastOffs = offs + lw(w, offs, start) + } else { + // we have a selection change: + // format the previous selection segment, determine + // the new selection bitset and start a new segment + sw(w, text[lastOffs:offs], bitset) + lastOffs = offs + mask := 1 << uint(index) + if start { + bitset |= mask + } else { + bitset &^= mask + } + } + } + sw(w, text[lastOffs:], bitset) +} + + +// A merger merges a slice of Selections and produces a sequence of +// consecutive segment change events through repeated next() calls. +// +type merger struct { + selections []Selection + segments [][]int // segments[i] is the next segment of selections[i] +} + + +const infinity int = 2e9 + +func newMerger(selections []Selection) *merger { + segments := make([][]int, len(selections)) + for i, sel := range selections { + segments[i] = []int{infinity, infinity} + if sel != nil { + if seg := sel(); seg != nil { + segments[i] = seg + } + } + } + return &merger{selections, segments} +} + + +// next returns the next segment change: index specifies the Selection +// to which the segment belongs, offs is the segment start or end offset +// as determined by the start value. If there are no more segment changes, +// next returns an index value < 0. +// +func (m *merger) next() (index, offs int, start bool) { + // find the next smallest offset where a segment starts or ends + offs = infinity + index = -1 + for i, seg := range m.segments { + switch { + case seg[0] < offs: + offs = seg[0] + index = i + start = true + case seg[1] < offs: + offs = seg[1] + index = i + start = false + } + } + if index < 0 { + // no offset found => all selections merged + return + } + // offset found - it's either the start or end offset but + // either way it is ok to consume the start offset: set it + // to infinity so it won't be considered in the following + // next call + m.segments[index][0] = infinity + if start { + return + } + // end offset found - consume it + m.segments[index][1] = infinity + // advance to the next segment for that selection + seg := m.selections[index]() + if seg == nil { + return + } + m.segments[index] = seg + return +} + + +// ---------------------------------------------------------------------------- +// Implementation of FormatText + +// lineSelection returns the line segments for text as a Selection. +func lineSelection(text []byte) Selection { + i, j := 0, 0 + return func() (seg []int) { + // find next newline, if any + for j < len(text) { + j++ + if text[j-1] == '\n' { + break + } + } + if i < j { + // text[i:j] constitutes a line + seg = []int{i, j} + i = j + } + return + } +} + + +// commentSelection returns the sequence of consecutive comments +// in the Go src text as a Selection. +// +func commentSelection(src []byte) Selection { + var s scanner.Scanner + file := s.Init(token.NewFileSet(), "", src, nil, scanner.ScanComments+scanner.InsertSemis) + return func() (seg []int) { + for { + pos, tok, lit := s.Scan() + if tok == token.EOF { + break + } + offs := file.Offset(pos) + if tok == token.COMMENT { + seg = []int{offs, offs + len(lit)} + break + } + } + return + } +} + + +// makeSelection is a helper function to make a Selection from a slice of pairs. +func makeSelection(matches [][]int) Selection { + return func() (seg []int) { + if len(matches) > 0 { + seg = matches[0] + matches = matches[1:] + } + return + } +} + + +// regexpSelection computes the Selection for the regular expression expr in text. +func regexpSelection(text []byte, expr string) Selection { + var matches [][]int + if rx, err := regexp.Compile(expr); err == nil { + matches = rx.FindAllIndex(text, -1) + } + return makeSelection(matches) +} + + +var selRx = regexp.MustCompile(`^([0-9]+):([0-9]+)`) + +// rangeSelection computes the Selection for a text range described +// by the argument str; the range description must match the selRx +// regular expression. +// +func rangeSelection(str string) Selection { + m := selRx.FindStringSubmatch(str) + if len(m) >= 2 { + from, _ := strconv.Atoi(m[1]) + to, _ := strconv.Atoi(m[2]) + if from < to { + return makeSelection([][]int{[]int{from, to}}) + } + } + return nil +} + + +// Span tags for all the possible selection combinations that may +// be generated by FormatText. Selections are indicated by a bitset, +// and the value of the bitset specifies the tag to be used. +// +// bit 0: comments +// bit 1: highlights +// bit 2: selections +// +var startTags = [][]byte{ + /* 000 */ []byte(``), + /* 001 */ []byte(``), + /* 010 */ []byte(``), + /* 011 */ []byte(``), + /* 100 */ []byte(``), + /* 101 */ []byte(``), + /* 110 */ []byte(``), + /* 111 */ []byte(``), +} + +var endTag = []byte(``) + + +func selectionTag(w io.Writer, text []byte, selections int) { + if len(text) > 0 { + if selections < len(startTags) { + if tag := startTags[selections]; len(tag) > 0 { + w.Write(tag) + template.HTMLEscape(w, text) + w.Write(endTag) + return + } + } + template.HTMLEscape(w, text) + } +} + + +// FormatText HTML-escapes text and returns it wrapped in
     tags.
    +// Conscutive text segments are wrapped in HTML spans (with tags as
    +// defined by startTags and endTag) as follows:
    +//
    +//	- if line >= 0, line numbers are printed before each line, starting
    +//	  with the value of line
    +//	- if the text is Go source, comments get the "comment" span class
    +//	- each occurrence of the regular expression pattern gets the "highlight"
    +//	  span class
    +//	- text segments covered by selection get the "selection" span class
    +//
    +// Comments, highlights, and selections may overlap arbitrarily; the respective
    +// HTML span classes are specified in the startTags variable.
    +//
    +func FormatText(text []byte, line int, goSource bool, pattern string, selection Selection) []byte {
    +	var buf bytes.Buffer
    +	buf.WriteString("
    \n")
    +
    +	var comments, highlights Selection
    +	if goSource {
    +		comments = commentSelection(text)
    +	}
    +	if pattern != "" {
    +		highlights = regexpSelection(text, pattern)
    +	}
    +	if comments != nil || highlights != nil || selection != nil {
    +		var lineTag LinkWriter
    +		if line >= 0 {
    +			lineTag = func(w io.Writer, _ int, start bool) {
    +				if start {
    +					fmt.Fprintf(w, "%5d\t", line, line)
    +					line++
    +				}
    +			}
    +		}
    +		FormatSelections(&buf, text, lineTag, lineSelection(text), selectionTag, comments, highlights, selection)
    +	} else {
    +		template.HTMLEscape(&buf, text)
    +	}
    +
    +	buf.WriteString("
    \n") + return buf.Bytes() +} diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go index 61c53e2c3..d6054ab9d 100644 --- a/src/cmd/godoc/godoc.go +++ b/src/cmd/godoc/godoc.go @@ -21,42 +21,14 @@ import ( pathutil "path" "regexp" "runtime" + "sort" "strings" - "sync" "template" "time" - "unicode" "utf8" ) -// ---------------------------------------------------------------------------- -// Support types - -// An RWValue wraps a value and permits mutually exclusive -// access to it and records the time the value was last set. -type RWValue struct { - mutex sync.RWMutex - value interface{} - timestamp int64 // time of last set(), in seconds since epoch -} - - -func (v *RWValue) set(value interface{}) { - v.mutex.Lock() - v.value = value - v.timestamp = time.Seconds() - v.mutex.Unlock() -} - - -func (v *RWValue) get() (interface{}, int64) { - v.mutex.RLock() - defer v.mutex.RUnlock() - return v.value, v.timestamp -} - - // ---------------------------------------------------------------------------- // Globals @@ -72,6 +44,7 @@ func (dt *delayTime) backoff(max int) { v = max } dt.value = v + // don't change dt.timestamp - calling backoff indicates an error condition dt.mutex.Unlock() } @@ -80,15 +53,24 @@ var ( verbose = flag.Bool("v", false, "verbose mode") // file system roots - goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory") - path = flag.String("path", "", "additional package directories (colon-separated)") + // 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)") + path = 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 // layout control - tabwidth = flag.Int("tabwidth", 4, "tab width") + tabwidth = flag.Int("tabwidth", 4, "tab width") + showTimestamps = flag.Bool("timestamps", true, "show timestamps with directory listings") + fulltextIndex = flag.Bool("fulltext", false, "build full text index for regular expression queries") // file system mapping - fsMap Mapping // user-defined mapping - fsTree RWValue // *Directory tree of packages, updated with each sync + 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 // http handlers fileServer http.Handler // default file server @@ -114,410 +96,179 @@ func registerPublicHandlers(mux *http.ServeMux) { } -// ---------------------------------------------------------------------------- -// Predicates and small utility functions - -func isGoFile(f *os.FileInfo) bool { - return f.IsRegular() && - !strings.HasPrefix(f.Name, ".") && // ignore .files - pathutil.Ext(f.Name) == ".go" -} - - -func isPkgFile(f *os.FileInfo) bool { - return isGoFile(f) && - !strings.HasSuffix(f.Name, "_test.go") // ignore test files +func initFSTree() { + fsTree.set(newDirectory(pathutil.Join(*goroot, *testDir), nil, -1)) + invalidateIndex() } -func isPkgDir(f *os.FileInfo) bool { - return f.IsDirectory() && len(f.Name) > 0 && f.Name[0] != '_' -} - - -func pkgName(filename string) string { - file, err := parser.ParseFile(filename, nil, nil, parser.PackageClauseOnly) - if err != nil || file == nil { - return "" - } - return file.Name.Name() -} - +// ---------------------------------------------------------------------------- +// Directory filters -func htmlEscape(s string) string { - var buf bytes.Buffer - template.HTMLEscape(&buf, []byte(s)) - return buf.String() +// 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 firstSentence(s string) string { - i := -1 // index+1 of first period - j := -1 // index+1 of first period that is followed by white space - prev := 'A' - for k, ch := range s { - k1 := k + 1 - if ch == '.' { - if i < 0 { - i = k1 // first period - } - if k1 < len(s) && s[k1] <= ' ' { - if j < 0 { - j = k1 // first period followed by white space - } - if !unicode.IsUpper(prev) { - j = k1 - break - } - } - } - prev = ch - } - - if j < 0 { - // use the next best period - j = i - if j < 0 { - // no period at all, use the entire string - j = len(s) - } +func setPathFilter(list []string) { + if len(list) == 0 { + pathFilter.set(nil) + return } - return s[0:j] + // 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 absolutePath(path, defaultRoot string) string { - abspath := fsMap.ToAbsolute(path) - if abspath == "" { - // no user-defined mapping found; use default mapping - abspath = pathutil.Join(defaultRoot, path) +func getPathFilter() func(string) bool { + f, _ := pathFilter.get() + if f != nil { + return f.(func(string) bool) } - return abspath + return nil } -func relativePath(path string) string { - relpath := fsMap.ToRelative(path) - if relpath == "" && strings.HasPrefix(path, *goroot+"/") { - // no user-defined mapping found; use default mapping - relpath = path[len(*goroot)+1:] +// 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 := ioutil.ReadFile(filename) + if err != nil { + return nil, err } - // 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 -} - - -// ---------------------------------------------------------------------------- -// Package directories - -type Directory struct { - Depth int - Path string // includes Name - Name string - Text string // package documentation, if any - Dirs []*Directory // subdirectories -} - - -func newDirTree(path, name string, depth, maxDepth int) *Directory { - if depth >= maxDepth { - // return a dummy directory so that the parent directory - // doesn't get discarded just because we reached the max - // directory depth - return &Directory{depth, path, name, "", nil} + // create a sorted list of valid directory names + filter := func(path string) bool { + d, err := os.Lstat(path) + return err == nil && isPkgDir(d) } - - list, _ := ioutil.ReadDir(path) // ignore errors - - // determine number of subdirectories and package files - ndirs := 0 - nfiles := 0 - text := "" - for _, d := range list { - switch { - case isPkgDir(d): - ndirs++ - case isPkgFile(d): - nfiles++ - if text == "" { - // no package documentation yet; take the first found - file, err := parser.ParseFile(pathutil.Join(path, d.Name), nil, nil, - parser.ParseComments|parser.PackageClauseOnly) - if err == nil && - // Also accept fakePkgName, so we get synopses for commmands. - // Note: This may lead to incorrect results if there is a - // (left-over) "documentation" package somewhere in a package - // directory of different name, but this is very unlikely and - // against current conventions. - (file.Name.Name() == name || file.Name.Name() == fakePkgName) && - file.Doc != nil { - // found documentation; extract a synopsys - text = firstSentence(doc.CommentText(file.Doc)) - } - } - } - } - - // create subdirectory tree - var dirs []*Directory - if ndirs > 0 { - dirs = make([]*Directory, ndirs) - i := 0 - for _, d := range list { - if isPkgDir(d) { - dd := newDirTree(pathutil.Join(path, d.Name), d.Name, depth+1, maxDepth) - if dd != nil { - dirs[i] = dd - i++ - } - } + list := canonicalizePaths(strings.Split(string(contents), "\n", -1), filter) + // for each parent path, remove all it's 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++ } - dirs = dirs[0:i] } - - // if there are no package files and no subdirectories - // (with package files), ignore the directory - if nfiles == 0 && len(dirs) == 0 { - return nil - } - - return &Directory{depth, path, name, text, dirs} + return list[0:i], nil } -// newDirectory creates a new package directory tree with at most maxDepth -// levels, anchored at root which is relative to goroot. The result tree -// only contains directories that contain package files or that contain -// subdirectories containing package files (transitively). +// updateMappedDirs computes the directory tree for +// each user-defined file system mapping. If a filter +// is provided, it is used to filter directories. // -func newDirectory(root string, maxDepth int) *Directory { - d, err := os.Lstat(root) - if err != nil || !isPkgDir(d) { - return nil - } - return newDirTree(root, d.Name, 0, maxDepth) -} - - -func (dir *Directory) walk(c chan<- *Directory, skipRoot bool) { - if dir != nil { - if !skipRoot { - c <- dir - } - for _, d := range dir.Dirs { - d.walk(c, false) - } +func 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 (dir *Directory) iter(skipRoot bool) <-chan *Directory { - c := make(chan *Directory) - go func() { - dir.walk(c, skipRoot) - close(c) - }() - return c -} - +func updateFilterFile() { + updateMappedDirs(nil) // no filter for accuracy -func (dir *Directory) lookupLocal(name string) *Directory { - for _, d := range dir.Dirs { - if d.Name == name { - return d + // 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 nil -} + return true + }) - -// lookup looks for the *Directory for a given path, relative to dir. -func (dir *Directory) lookup(path string) *Directory { - d := strings.Split(dir.Path, "/", -1) - p := strings.Split(path, "/", -1) - i := 0 - for i < len(d) { - if i >= len(p) || d[i] != p[i] { - return nil - } - i++ - } - for dir != nil && i < len(p) { - dir = dir.lookupLocal(p[i]) - i++ + // 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 } - return dir -} - - -// DirEntry describes a directory entry. The Depth and Height values -// are useful for presenting an entry in an indented fashion. -// -type DirEntry struct { - Depth int // >= 0 - Height int // = DirList.MaxHeight - Depth, > 0 - Path string // includes Name, relative to DirList root - Name string - Synopsis string } -type DirList struct { - MaxHeight int // directory tree height, > 0 - List []DirEntry -} - - -// listing creates a (linear) directory listing from a directory tree. -// If skipRoot is set, the root directory itself is excluded from the list. -// -func (root *Directory) listing(skipRoot bool) *DirList { - if root == nil { - return nil - } - - // determine number of entries n and maximum height - n := 0 - minDepth := 1 << 30 // infinity - maxDepth := 0 - for d := range root.iter(skipRoot) { - n++ - if minDepth > d.Depth { - minDepth = d.Depth - } - if maxDepth < d.Depth { - maxDepth = d.Depth +func initDirTrees() { + // setup initial path filter + if *filter != "" { + list, err := readDirList(*filter) + if err != nil { + log.Printf("%s", err) + } else if len(list) == 0 { + log.Printf("no directory paths in file %s", *filter) } + setPathFilter(list) } - maxHeight := maxDepth - minDepth + 1 - if n == 0 { - return nil - } + go updateMappedDirs(getPathFilter()) // use filter for speed - // create list - list := make([]DirEntry, n) - i := 0 - for d := range root.iter(skipRoot) { - p := &list[i] - p.Depth = d.Depth - minDepth - p.Height = maxHeight - p.Depth - // the path is relative to root.Path - remove the root.Path - // prefix (the prefix should always be present but avoid - // crashes and check) - path := d.Path - if strings.HasPrefix(d.Path, root.Path) { - path = d.Path[len(root.Path):] - } - // remove trailing '/' if any - path must be relative - if len(path) > 0 && path[0] == '/' { - path = path[1:] - } - p.Path = path - p.Name = d.Name - p.Synopsis = d.Text - i++ + // 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) + } + }() } - - return &DirList{maxHeight, list} } // ---------------------------------------------------------------------------- -// HTML formatting support - -// Styler implements a printer.Styler. -type Styler struct { - linetags bool - highlight string - objmap map[*ast.Object]int - count int -} - - -func newStyler(highlight string) *Styler { - return &Styler{true, highlight, make(map[*ast.Object]int), 0} -} +// Path mapping - -func (s *Styler) id(obj *ast.Object) int { - n, found := s.objmap[obj] - if !found { - n = s.count - s.objmap[obj] = n - s.count++ - } - return n -} - - -func (s *Styler) mapping() []*ast.Object { - if s.objmap == nil { - return nil - } - m := make([]*ast.Object, s.count) - for obj, i := range s.objmap { - m[i] = obj - } - return m -} - - -// Use the defaultStyler when there is no specific styler. -// The defaultStyler does not emit line tags since they may -// interfere with tags emitted by templates. -// TODO(gri): Should emit line tags at the beginning of a line; -// never in the middle of code. -var defaultStyler Styler - - -func (s *Styler) LineTag(line int) (text []byte, tag printer.HTMLTag) { - if s.linetags { - tag = printer.HTMLTag{fmt.Sprintf(``, line), ""} +func absolutePath(path, defaultRoot string) string { + abspath := fsMap.ToAbsolute(path) + if abspath == "" { + // no user-defined mapping found; use default mapping + abspath = pathutil.Join(defaultRoot, path) } - return -} - - -func (s *Styler) Comment(c *ast.Comment, line []byte) (text []byte, tag printer.HTMLTag) { - text = line - // minimal syntax-coloring of comments for now - people will want more - // (don't do anything more until there's a button to turn it on/off) - tag = printer.HTMLTag{``, ""} - return -} - - -func (s *Styler) BasicLit(x *ast.BasicLit) (text []byte, tag printer.HTMLTag) { - text = x.Value - return + return abspath } -func (s *Styler) Ident(id *ast.Ident) (text []byte, tag printer.HTMLTag) { - text = []byte(id.Name()) - var str string - if s.objmap != nil { - str = fmt.Sprintf(` id="%d"`, s.id(id.Obj)) - } - if s.highlight == id.Name() { - str += ` class="highlight"` - } - if str != "" { - tag = printer.HTMLTag{"", ""} +func relativePath(path string) string { + relpath := fsMap.ToRelative(path) + if relpath == "" { + // prefix must end in '/' + prefix := *goroot + if len(prefix) > 0 && prefix[len(prefix)-1] != '/' { + prefix += "/" + } + if strings.HasPrefix(path, prefix) { + // no user-defined mapping found; use default mapping + relpath = path[len(prefix):] + } } - return -} - - -func (s *Styler) Token(tok token.Token) (text []byte, tag printer.HTMLTag) { - text = []byte(tok.String()) - return + // 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 } @@ -597,7 +348,7 @@ func (p *tconv) Write(data []byte) (n int, err os.Error) { // Templates // Write an AST-node to w; optionally html-escaped. -func writeNode(w io.Writer, node interface{}, html bool, styler printer.Styler) { +func writeNode(w io.Writer, fset *token.FileSet, node interface{}, html bool) { mode := printer.TabIndent | printer.UseSpaces if html { mode |= printer.GenHTML @@ -606,7 +357,7 @@ func writeNode(w io.Writer, node interface{}, html bool, styler printer.Styler) // to ensure a good outcome in most browsers (there may still // be tabs in comments and strings, but converting those into // the right number of spaces is much harder) - (&printer.Config{mode, *tabwidth, styler}).Fprint(&tconv{output: w}, node) + (&printer.Config{mode, *tabwidth, nil}).Fprint(&tconv{output: w}, fset, node) } @@ -621,14 +372,14 @@ func writeText(w io.Writer, text []byte, html bool) { // Write anything to w; optionally html-escaped. -func writeAny(w io.Writer, x interface{}, html bool) { +func writeAny(w io.Writer, fset *token.FileSet, html bool, x interface{}) { switch v := x.(type) { case []byte: writeText(w, v, html) case string: writeText(w, []byte(v), html) case ast.Decl, ast.Expr, ast.Stmt, *ast.File: - writeNode(w, x, html, &defaultStyler) + writeNode(w, fset, x, html) default: if html { var buf bytes.Buffer @@ -641,24 +392,34 @@ func writeAny(w io.Writer, x interface{}, html bool) { } +func fileset(x []interface{}) *token.FileSet { + if len(x) > 1 { + if fset, ok := x[1].(*token.FileSet); ok { + return fset + } + } + return nil +} + + // Template formatter for "html" format. -func htmlFmt(w io.Writer, x interface{}, format string) { - writeAny(w, x, true) +func htmlFmt(w io.Writer, format string, x ...interface{}) { + writeAny(w, fileset(x), true, x[0]) } // Template formatter for "html-esc" format. -func htmlEscFmt(w io.Writer, x interface{}, format string) { +func htmlEscFmt(w io.Writer, format string, x ...interface{}) { var buf bytes.Buffer - writeAny(&buf, x, false) + writeAny(&buf, fileset(x), false, x[0]) template.HTMLEscape(w, buf.Bytes()) } // Template formatter for "html-comment" format. -func htmlCommentFmt(w io.Writer, x interface{}, format string) { +func htmlCommentFmt(w io.Writer, format string, x ...interface{}) { var buf bytes.Buffer - writeAny(&buf, x, false) + writeAny(&buf, fileset(x), false, x[0]) // TODO(gri) Provide list of words (e.g. function parameters) // to be emphasized by ToHTML. doc.ToHTML(w, buf.Bytes(), nil) // does html-escaping @@ -666,29 +427,49 @@ func htmlCommentFmt(w io.Writer, x interface{}, format string) { // Template formatter for "" (default) format. -func textFmt(w io.Writer, x interface{}, format string) { - writeAny(w, x, false) +func textFmt(w io.Writer, format string, x ...interface{}) { + writeAny(w, fileset(x), false, x[0]) +} + + +// Template formatter for "urlquery-esc" format. +func urlQueryEscFmt(w io.Writer, format string, x ...interface{}) { + var buf bytes.Buffer + writeAny(&buf, fileset(x), false, x[0]) + template.HTMLEscape(w, []byte(http.URLEscape(string(buf.Bytes())))) } -// Template formatter for the various "url-xxx" formats. -func urlFmt(w io.Writer, x interface{}, format string) { +// Template formatter for the various "url-xxx" formats excluding url-esc. +func urlFmt(w io.Writer, format string, x ...interface{}) { var path string var line int + var low, high int // selection // determine path and position info, if any type positioner interface { - Pos() token.Position + Pos() token.Pos + End() token.Pos } - switch t := x.(type) { + switch t := x[0].(type) { case string: path = t case positioner: - pos := t.Pos() - if pos.IsValid() { + fset := fileset(x) + if p := t.Pos(); p.IsValid() { + pos := fset.Position(p) path = pos.Filename line = pos.Line + low = pos.Offset } + if p := t.End(); p.IsValid() { + high = fset.Position(p).Offset + } + default: + // we should never reach here, but be resilient + // and assume the position is invalid (empty path, + // and line 0) + log.Printf("INTERNAL ERROR: urlFmt(%s) without a string or positioner", format) } // map path @@ -700,7 +481,7 @@ func urlFmt(w io.Writer, x interface{}, format string) { default: // we should never reach here, but be resilient // and assume the url-pkg format instead - log.Stderrf("INTERNAL ERROR: urlFmt(%s)", format) + log.Printf("INTERNAL ERROR: urlFmt(%s)", format) fallthrough case "url-pkg": // because of the irregular mapping under goroot @@ -712,10 +493,22 @@ func urlFmt(w io.Writer, x interface{}, format string) { case "url-src": template.HTMLEscape(w, []byte(relpath)) case "url-pos": + template.HTMLEscape(w, []byte(relpath)) + // selection ranges are of form "s=low:high" + if low < high { + fmt.Fprintf(w, "?s=%d:%d", low, high) + // if we have a selection, position the page + // such that the selection is a bit below the top + line -= 10 + if line < 1 { + line = 1 + } + } // line id's in html-printed source are of the // form "L%d" where %d stands for the line number - template.HTMLEscape(w, []byte(relpath)) - fmt.Fprintf(w, "#L%d", line) + if line > 0 { + fmt.Fprintf(w, "#L%d", line) + } } } @@ -734,14 +527,14 @@ var infoKinds = [nKinds]string{ // Template formatter for "infoKind" format. -func infoKindFmt(w io.Writer, x interface{}, format string) { - fmt.Fprintf(w, infoKinds[x.(SpotKind)]) // infoKind entries are html-escaped +func infoKindFmt(w io.Writer, format string, x ...interface{}) { + fmt.Fprintf(w, infoKinds[x[0].(SpotKind)]) // infoKind entries are html-escaped } // Template formatter for "infoLine" format. -func infoLineFmt(w io.Writer, x interface{}, format string) { - info := x.(SpotInfo) +func infoLineFmt(w io.Writer, format string, x ...interface{}) { + info := x[0].(SpotInfo) line := info.Lori() if info.IsIndex() { index, _ := searchIndex.get() @@ -760,58 +553,52 @@ func infoLineFmt(w io.Writer, x interface{}, format string) { // Template formatter for "infoSnippet" format. -func infoSnippetFmt(w io.Writer, x interface{}, format string) { - info := x.(SpotInfo) - text := `no snippet text available` +func infoSnippetFmt(w io.Writer, format string, x ...interface{}) { + info := x[0].(SpotInfo) + text := []byte(`no snippet text available`) if info.IsIndex() { index, _ := searchIndex.get() // no escaping of snippet text needed; // snippet text is escaped when generated text = index.(*Index).Snippet(info.Lori()).Text } - fmt.Fprint(w, text) + w.Write(text) } // Template formatter for "padding" format. -func paddingFmt(w io.Writer, x interface{}, format string) { - for i := x.(int); i > 0; i-- { +func paddingFmt(w io.Writer, format string, x ...interface{}) { + for i := x[0].(int); i > 0; i-- { fmt.Fprint(w, ``) } } // Template formatter for "time" format. -func timeFmt(w io.Writer, x interface{}, format string) { - template.HTMLEscape(w, []byte(time.SecondsToLocalTime(x.(int64)/1e9).String())) +func timeFmt(w io.Writer, format string, x ...interface{}) { + template.HTMLEscape(w, []byte(time.SecondsToLocalTime(x[0].(int64)/1e9).String())) } // Template formatter for "dir/" format. -func dirslashFmt(w io.Writer, x interface{}, format string) { - if x.(*os.FileInfo).IsDirectory() { +func dirslashFmt(w io.Writer, format string, x ...interface{}) { + if x[0].(*os.FileInfo).IsDirectory() { w.Write([]byte{'/'}) } } // Template formatter for "localname" format. -func localnameFmt(w io.Writer, x interface{}, format string) { - _, localname := pathutil.Split(x.(string)) +func localnameFmt(w io.Writer, format string, x ...interface{}) { + _, localname := pathutil.Split(x[0].(string)) template.HTMLEscape(w, []byte(localname)) } -// Template formatter for "popupInfo" format. -func popupInfoFmt(w io.Writer, x interface{}, format string) { - obj := x.(*ast.Object) - // for now, show object kind and name; eventually - // do something more interesting (show declaration, - // for instance) - if obj.Kind != ast.Err { - fmt.Fprintf(w, "%s ", obj.Kind) - } - template.HTMLEscape(w, []byte(obj.Name)) +// Template formatter for "numlines" format. +func numlinesFmt(w io.Writer, format string, x ...interface{}) { + list := x[0].([]int) + fmt.Fprintf(w, "%d", len(list)) } @@ -820,6 +607,7 @@ var fmap = template.FormatterMap{ "html": htmlFmt, "html-esc": htmlEscFmt, "html-comment": htmlCommentFmt, + "urlquery-esc": urlQueryEscFmt, "url-pkg": urlFmt, "url-src": urlFmt, "url-pos": urlFmt, @@ -830,7 +618,7 @@ var fmap = template.FormatterMap{ "time": timeFmt, "dir/": dirslashFmt, "localname": localnameFmt, - "popupInfo": popupInfoFmt, + "numlines": numlinesFmt, } @@ -857,8 +645,7 @@ var ( packageHTML, packageText, searchHTML, - searchText, - sourceHTML *template.Template + searchText *template.Template ) func readTemplates() { @@ -872,46 +659,40 @@ func readTemplates() { packageText = readTemplate("package.txt") searchHTML = readTemplate("search.html") searchText = readTemplate("search.txt") - sourceHTML = readTemplate("source.html") } // ---------------------------------------------------------------------------- // Generic HTML wrapper -func servePage(c *http.Conn, title, subtitle, query string, content []byte) { - type Data struct { - Title string - Subtitle string - PkgRoots []string - Timestamp int64 - Query string - Version string - Menu []byte - Content []byte - } - - _, ts := fsTree.get() - d := Data{ - Title: title, - Subtitle: subtitle, - PkgRoots: fsMap.PrefixList(), - Timestamp: ts * 1e9, // timestamp in ns - Query: query, - Version: runtime.Version(), - Menu: nil, - Content: content, - } - - if err := godocHTML.Execute(&d, c); err != nil { - log.Stderrf("godocHTML.Execute: %s", err) +func servePage(w http.ResponseWriter, title, subtitle, query string, content []byte) { + d := struct { + Title string + Subtitle string + PkgRoots []string + Query string + Version string + Menu []byte + Content []byte + }{ + title, + subtitle, + fsMap.PrefixList(), + query, + runtime.Version(), + nil, + content, + } + + if err := godocHTML.Execute(&d, w); err != nil { + log.Printf("godocHTML.Execute: %s", err) } } -func serveText(c *http.Conn, text []byte) { - c.SetHeader("Content-Type", "text/plain; charset=utf-8") - c.Write(text) +func serveText(w http.ResponseWriter, text []byte) { + w.SetHeader("Content-Type", "text/plain; charset=utf-8") + w.Write(text) } @@ -926,27 +707,27 @@ var ( func extractString(src []byte, rx *regexp.Regexp) (s string) { - m := rx.Execute(src) - if len(m) >= 4 { - s = strings.TrimSpace(string(src[m[2]:m[3]])) + m := rx.FindSubmatch(src) + if m != nil { + s = strings.TrimSpace(string(m[1])) } return } -func serveHTMLDoc(c *http.Conn, r *http.Request, abspath, relpath string) { +func serveHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath string) { // get HTML body contents src, err := ioutil.ReadFile(abspath) if err != nil { - log.Stderrf("ioutil.ReadFile: %s", err) - serveError(c, r, relpath, err) + log.Printf("ioutil.ReadFile: %s", err) + serveError(w, r, relpath, err) return } // if it begins with "") - template.HTMLEscape(&buf, src) - fmt.Fprintln(&buf, "
    ") - - servePage(c, "Text file "+relpath, "", "", buf.Bytes()) + contents := FormatText(src, 1, pathutil.Ext(abspath) == ".go", r.FormValue("h"), rangeSelection(r.FormValue("s"))) + servePage(w, title+" "+relpath, "", "", contents) } -func serveDirectory(c *http.Conn, r *http.Request, abspath, relpath string) { - if redirect(c, r) { +func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath string) { + if redirect(w, r) { return } list, err := ioutil.ReadDir(abspath) if err != nil { - log.Stderrf("ioutil.ReadDir: %s", err) - serveError(c, r, relpath, err) + log.Printf("ioutil.ReadDir: %s", err) + serveError(w, r, relpath, err) return } @@ -1093,23 +847,23 @@ func serveDirectory(c *http.Conn, r *http.Request, abspath, relpath string) { } contents := applyTemplate(dirlistHTML, "dirlistHTML", list) - servePage(c, "Directory "+relpath, "", "", contents) + servePage(w, "Directory "+relpath, "", "", contents) } -func serveFile(c *http.Conn, r *http.Request) { +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(c, r, pathutil.Join(*goroot, "doc/root.html"), "doc/root.html") + serveHTMLDoc(w, r, pathutil.Join(*goroot, "doc/root.html"), "doc/root.html") return case "/doc/root.html": // hide landing page from its real name - http.Redirect(c, "/", http.StatusMovedPermanently) + http.Redirect(w, r, "/", http.StatusMovedPermanently) return } @@ -1118,42 +872,42 @@ func serveFile(c *http.Conn, r *http.Request) { if strings.HasSuffix(abspath, "/index.html") { // We'll show index.html for the directory. // Use the dir/ version as canonical instead of dir/index.html. - http.Redirect(c, r.URL.Path[0:len(r.URL.Path)-len("index.html")], http.StatusMovedPermanently) + http.Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len("index.html")], http.StatusMovedPermanently) return } - serveHTMLDoc(c, r, abspath, relpath) + serveHTMLDoc(w, r, abspath, relpath) return case ".go": - serveGoSource(c, r, abspath, relpath) + serveTextFile(w, r, abspath, relpath, "Source file") return } dir, err := os.Lstat(abspath) if err != nil { - log.Stderr(err) - serveError(c, r, relpath, err) + log.Print(err) + serveError(w, r, relpath, err) return } if dir != nil && dir.IsDirectory() { - if redirect(c, r) { + if redirect(w, r) { return } if index := abspath + "/index.html"; isTextFile(index) { - serveHTMLDoc(c, r, index, relativePath(index)) + serveHTMLDoc(w, r, index, relativePath(index)) return } - serveDirectory(c, r, abspath, relpath) + serveDirectory(w, r, abspath, relpath) return } if isTextFile(abspath) { - serveTextFile(c, r, abspath, relpath) + serveTextFile(w, r, abspath, relpath, "Text file") return } - fileServer.ServeHTTP(c, r) + fileServer.ServeHTTP(w, r) } @@ -1169,17 +923,19 @@ type PageInfoMode uint const ( exportsOnly PageInfoMode = 1 << iota // only keep exported stuff genDoc // generate documentation - tryMode // don't log errors ) 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 } @@ -1193,10 +949,10 @@ type httpHandler struct { // getPageInfo returns the PageInfo for a package directory abspath. If the // parameter genAST is set, an AST containing only the package exports is // computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc) -// is extracted from the AST. If the parameter try is set, no errors are -// logged if getPageInfo fails. If there is no corresponding package in the -// directory, PageInfo.PDoc and PageInfo.PExp are nil. If there are no sub- -// directories, PageInfo.Dirs is nil. +// is extracted from the AST. If there is no corresponding package in the +// directory, PageInfo.PAst and PageInfo.PDoc are nil. If there are no sub- +// directories, PageInfo.Dirs is nil. If 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 { // filter function to select the desired .go files @@ -1207,10 +963,12 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf } // get package ASTs - pkgs, err := parser.ParseDir(abspath, filter, parser.ParseComments) - if err != nil && mode&tryMode != 0 { - // TODO: errors should be shown instead of an empty directory - log.Stderrf("parser.parseDir: %s", err) + fset := token.NewFileSet() + pkgs, err := parser.ParseDir(fset, abspath, filter, parser.ParseComments) + if err != nil && pkgs == nil { + // only report directory read errors, ignore parse errors + // (may be able to extract partial package information) + return PageInfo{Dirname: abspath, Err: err} } // select package @@ -1258,7 +1016,7 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf // (excluding the selected package, if any). plist = make([]string, len(pkgs)) i := 0 - for name, _ := range pkgs { + for name := range pkgs { if pkg == nil || name != pkg.Name { plist[i] = name i++ @@ -1283,26 +1041,52 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf // get directory information var dir *Directory - if tree, _ := fsTree.get(); tree != nil && tree.(*Directory) != nil { + var timestamp int64 + if tree, ts := fsTree.get(); tree != nil && tree.(*Directory) != nil { // directory tree is present; lookup respective directory // (may still fail if the file system was updated and the // new directory tree has not yet been computed) - // TODO(gri) Need to build directory tree for fsMap entries dir = tree.(*Directory).lookup(abspath) + 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 (either early after startup - // or command-line mode, or we don't build a tree for the - // directory; e.g. google3); compute one level for this page - dir = newDirectory(abspath, 1) + // 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() } - return PageInfo{abspath, plist, past, pdoc, dir.listing(true), h.isPkg} + return PageInfo{abspath, plist, fset, past, pdoc, dir.listing(true), timestamp, h.isPkg, nil} } -func (h *httpHandler) ServeHTTP(c *http.Conn, r *http.Request) { - if redirect(c, r) { +func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if redirect(w, r) { return } @@ -1313,17 +1097,22 @@ func (h *httpHandler) ServeHTTP(c *http.Conn, r *http.Request) { mode |= genDoc } info := h.getPageInfo(abspath, relpath, r.FormValue("p"), mode) + if info.Err != nil { + log.Print(info.Err) + serveError(w, r, relpath, info.Err) + return + } if r.FormValue("f") == "text" { contents := applyTemplate(packageText, "packageText", info) - serveText(c, contents) + serveText(w, contents) return } - var title string + var title, subtitle string switch { case info.PAst != nil: - title = "Package " + info.PAst.Name.Name() + title = "Package " + info.PAst.Name.Name case info.PDoc != nil: switch { case h.isPkg: @@ -1337,10 +1126,13 @@ func (h *httpHandler) ServeHTTP(c *http.Conn, r *http.Request) { } default: title = "Directory " + relativePath(info.Dirname) + if *showTimestamps { + subtitle = "Last update: " + time.SecondsToLocalTime(info.DirTime).String() + } } contents := applyTemplate(packageHTML, "packageHTML", info) - servePage(c, title, "", "", contents) + servePage(w, title, subtitle, "", contents) } @@ -1350,71 +1142,160 @@ func (h *httpHandler) ServeHTTP(c *http.Conn, r *http.Request) { var searchIndex RWValue type SearchResult struct { - Query string - Hit *LookupResult - Alt *AltWords - Illegal bool - Accurate bool + Query string + Alert string // error or warning message + + // identifier matches + Hit *LookupResult // identifier matches of Query + Alt *AltWords // alternative identifiers to look for + + // textual matches + Found int // number of textual occurrences found + Textual []FileLines // textual matches of Query + Complete bool // true if all textual occurrences of Query are reported } func lookup(query string) (result SearchResult) { result.Query = query + + // determine identifier lookup string and full text regexp + lookupStr := "" + lookupRx, err := regexp.Compile(query) + if err != nil { + result.Alert = "Error in query regular expression: " + err.String() + return + } + if prefix, complete := lookupRx.LiteralPrefix(); complete { + // otherwise we lookup "" (with no result) because + // identifier lookup doesn't support regexp search + lookupStr = prefix + } + if index, timestamp := searchIndex.get(); index != nil { - result.Hit, result.Alt, result.Illegal = index.(*Index).Lookup(query) - _, ts := fsTree.get() - result.Accurate = timestamp >= ts + // identifier search + index := index.(*Index) + result.Hit, result.Alt, err = index.Lookup(lookupStr) + if err != nil && !*fulltextIndex { + // ignore the error if there is full text search + // since it accepts that query regular expression + result.Alert = "Error in query string: " + err.String() + return + } + + // textual search + // TODO(gri) should max be a flag? + const max = 10000 // show at most this many fulltext results + result.Found, result.Textual = index.LookupRegexp(lookupRx, max+1) + result.Complete = result.Found <= max + + // is the result accurate? + if _, ts := fsModified.get(); timestamp < ts { + result.Alert = "Indexing in progress: result may be inaccurate" + } } return } -func search(c *http.Conn, r *http.Request) { +func search(w http.ResponseWriter, r *http.Request) { query := strings.TrimSpace(r.FormValue("q")) result := lookup(query) if r.FormValue("f") == "text" { contents := applyTemplate(searchText, "searchText", result) - serveText(c, contents) + serveText(w, contents) return } var title string - if result.Hit != nil { + if result.Hit != nil || len(result.Textual) > 0 { title = fmt.Sprintf(`Results for query %q`, query) } else { title = fmt.Sprintf(`No results found for query %q`, query) } contents := applyTemplate(searchHTML, "searchHTML", result) - servePage(c, title, "", query, contents) + servePage(w, title, "", query, contents) } // ---------------------------------------------------------------------------- // Indexer +// invalidateIndex should be called whenever any of the file systems +// under godoc's observation change so that the indexer is kicked on. +// +func invalidateIndex() { + fsModified.set(nil) +} + + +// indexUpToDate() returns true if the search index is not older +// than any of the file systems under godoc's observation. +// +func indexUpToDate() bool { + _, fsTime := fsModified.get() + _, siTime := searchIndex.get() + return fsTime <= siTime +} + + +// feedDirnames feeds the directory names of all directories +// under the file system given by root to channel c. +// +func feedDirnames(root *RWValue, c chan<- string) { + if dir, _ := root.get(); dir != nil { + for d := range dir.(*Directory).iter(false) { + c <- d.Path + } + } +} + + +// fsDirnames() returns a channel sending all directory names +// of all the file systems under godoc's observation. +// +func fsDirnames() <-chan string { + c := make(chan string, 256) // asynchronous 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 indexer() { for { - _, ts := fsTree.get() - if _, timestamp := searchIndex.get(); timestamp < ts { + if !indexUpToDate() { // index possibly out of date - make a new one - // (could use a channel to send an explicit signal - // from the sync goroutine, but this solution is - // more decoupled, trivial, and works well enough) + if *verbose { + log.Printf("updating index...") + } start := time.Nanoseconds() - index := NewIndex(*goroot) + index := NewIndex(fsDirnames(), *fulltextIndex) stop := time.Nanoseconds() searchIndex.set(index) if *verbose { secs := float64((stop-start)/1e6) / 1e3 - nwords, nspots := index.Size() - log.Stderrf("index updated (%gs, %d unique words, %d spots)", secs, nwords, nspots) + 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.Stderrf("bytes=%d footprint=%d\n", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys) + log.Printf("before GC: bytes = %d footprint = %d", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys) runtime.GC() - log.Stderrf("bytes=%d footprint=%d\n", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys) + log.Printf("after GC: bytes = %d footprint = %d", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys) + } + var delay int64 = 60 * 1e9 // by default, try every 60s + if *testDir != "" { + // in test mode, try once a second for fast startup + delay = 1 * 1e9 } - time.Sleep(1 * 60e9) // try once a minute + time.Sleep(delay) } } diff --git a/src/cmd/godoc/index.go b/src/cmd/godoc/index.go index 8745b8b0a..ba6fe9acd 100644 --- a/src/cmd/godoc/index.go +++ b/src/cmd/godoc/index.go @@ -3,11 +3,11 @@ // license that can be found in the LICENSE file. // This file contains the infrastructure to create an -// (identifier) index for a set of Go files. +// identifier and full-text index for a set of Go files. // -// Basic indexing algorithm: +// Algorithm for identifier index: // - traverse all .go files of the file tree specified by root -// - for each word (identifier) encountered, collect all occurences (spots) +// - for each word (identifier) 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 @@ -21,17 +21,34 @@ // (the line number for spots with snippets is stored in the snippet) // - at the end, create lists of alternative spellings for a given // word +// +// Algorithm for full text index: +// - concatenate all source code in a byte buffer (in memory) +// - add the files to a file set in lockstep as they are added to the byte +// buffer such that a byte buffer offset corresponds to the Pos value for +// that file location +// - create a suffix array from the concatenated sources +// +// String lookup in full text index: +// - use the suffix array to lookup a string's offsets - the offsets +// correspond to the Pos values relative to the file set +// - translate the Pos values back into file and line information and +// sort the result package main import ( + "bytes" "container/vector" "go/ast" "go/parser" "go/token" "go/scanner" + "index/suffixarray" + "io/ioutil" "os" pathutil "path" + "regexp" "sort" "strings" ) @@ -231,7 +248,7 @@ type File struct { } -// A Spot describes a single occurence of a word. +// A Spot describes a single occurrence of a word. type Spot struct { File *File Info SpotInfo @@ -419,7 +436,17 @@ const excludeTestFiles = false type IndexResult struct { Decls RunList // package-level declarations (with snippets) - Others RunList // all other occurences + Others RunList // all other occurrences +} + + +// Statistics provides statistics information for an index. +type Statistics struct { + Bytes int // total size of indexed source files + Files int // number of indexed source files + Lines int // number of lines (all files) + Words int // number of different identifiers + Spots int // number of identifier occurrences } @@ -428,11 +455,14 @@ type IndexResult struct { // interface for walking file trees, and the ast.Visitor interface for // walking Go ASTs. type Indexer struct { + fset *token.FileSet // file set for all indexed files + sources bytes.Buffer // concatenated sources words map[string]*IndexResult // RunLists of Spots snippets vector.Vector // vector of *Snippets, indexed by snippet indices - file *File // current file - decl ast.Decl // current decl - nspots int // number of spots encountered + current *token.File // last file added to file set + file *File // AST for current file + decl ast.Decl // AST for current decl + stats Statistics } @@ -452,24 +482,24 @@ func (x *Indexer) visitComment(c *ast.CommentGroup) { func (x *Indexer) visitIdent(kind SpotKind, id *ast.Ident) { if id != nil { - lists, found := x.words[id.Name()] + lists, found := x.words[id.Name] if !found { lists = new(IndexResult) - x.words[id.Name()] = lists + x.words[id.Name] = lists } if kind == Use || x.decl == nil { // not a declaration or no snippet required - info := makeSpotInfo(kind, id.Pos().Line, false) + info := makeSpotInfo(kind, x.current.Line(id.Pos()), false) lists.Others.Push(Spot{x.file, info}) } else { // a declaration with snippet - index := x.addSnippet(NewSnippet(x.decl, id)) + index := x.addSnippet(NewSnippet(x.fset, x.decl, id)) info := makeSpotInfo(kind, index, true) lists.Decls.Push(Spot{x.file, info}) } - x.nspots++ + x.stats.Spots++ } } @@ -506,7 +536,7 @@ func (x *Indexer) visitSpec(spec ast.Spec, isVarDecl bool) { } -func (x *Indexer) Visit(node interface{}) ast.Visitor { +func (x *Indexer) Visit(node ast.Node) ast.Visitor { // TODO(gri): methods in interface types are categorized as VarDecl switch n := node.(type) { case nil: @@ -578,16 +608,69 @@ func (x *Indexer) Visit(node interface{}) ast.Visitor { } -func (x *Indexer) VisitDir(path string, f *os.FileInfo) bool { - return true +func pkgName(filename string) string { + // use a new file set each time in order to not pollute the indexer's + // file set (which must stay in sync with the concatenated source code) + file, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.PackageClauseOnly) + if err != nil || file == nil { + return "" + } + return file.Name.Name +} + + +func (x *Indexer) addFile(filename string) *ast.File { + // open file + f, err := os.Open(filename, os.O_RDONLY, 0) + if err != nil { + return nil + } + defer f.Close() + + // The file set's base offset and x.sources size must be in lock-step; + // this permits the direct mapping of suffix array lookup results to + // to corresponding Pos values. + // + // When a file is added to the file set, it's offset base increases by + // the size of the file + 1; and the initial base offset is 1. Add an + // extra byte to the sources here. + x.sources.WriteByte(0) + + // If the sources length doesn't match the file set base at this point + // the file set implementation changed or we have another error. + base := x.fset.Base() + if x.sources.Len() != base { + panic("internal error - file base incorrect") + } + + // append file contents to x.sources + if _, err := x.sources.ReadFrom(f); err != nil { + x.sources.Truncate(base) // discard possibly added data + return nil // ignore files with I/O errors + } + + // parse the file and in the process add it to the file set + src := x.sources.Bytes()[base:] // no need to reread the file + file, err := parser.ParseFile(x.fset, filename, src, parser.ParseComments) + if err != nil { + // do not discard the added source code in this case + // because the file has been added to the file set and + // the source size must match the file set base + // TODO(gri): given a FileSet.RemoveFile() one might be + // able to discard the data here (worthwhile?) + return nil // ignore files with (parse) errors + } + + return file } -func (x *Indexer) VisitFile(path string, f *os.FileInfo) { +func (x *Indexer) visitFile(dirname string, f *os.FileInfo) { if !isGoFile(f) { return } + path := pathutil.Join(dirname, f.Name) if excludeTestFiles && (!isPkgFile(f) || strings.HasPrefix(path, "test/")) { return } @@ -596,15 +679,23 @@ func (x *Indexer) VisitFile(path string, f *os.FileInfo) { return } - file, err := parser.ParseFile(path, nil, nil, parser.ParseComments) - if err != nil { - return // ignore files with (parse) errors + file := x.addFile(path) + if file == nil { + return } + // we've got a file to index + x.current = x.fset.File(file.Pos()) // file.Pos is in the current file dir, _ := pathutil.Split(path) - pak := Pak{dir, file.Name.Name()} + pak := Pak{dir, file.Name.Name} x.file = &File{path, pak} ast.Walk(x, file) + + // update statistics + // (count real file size as opposed to using the padded x.sources.Len()) + x.stats.Bytes += x.current.Size() + x.stats.Files++ + x.stats.Lines += x.current.LineCount() } @@ -613,30 +704,54 @@ func (x *Indexer) VisitFile(path string, f *os.FileInfo) { type LookupResult struct { Decls HitList // package-level declarations (with snippets) - Others HitList // all other occurences + Others HitList // all other occurrences } type Index struct { + fset *token.FileSet // file set used during indexing; nil if no textindex + suffixes *suffixarray.Index // suffixes for concatenated sources; nil if no textindex words map[string]*LookupResult // maps words to hit lists alts map[string]*AltWords // maps canonical(words) to lists of alternative spellings snippets []*Snippet // all snippets, indexed by snippet index - nspots int // number of spots indexed (a measure of the index size) + stats Statistics } func canonical(w string) string { return strings.ToLower(w) } -// NewIndex creates a new index for the file tree rooted at root. -func NewIndex(root string) *Index { +// NewIndex creates a new index for the .go files +// in the directories given by dirnames. +// +func NewIndex(dirnames <-chan string, fulltextIndex bool) *Index { var x Indexer // initialize Indexer + x.fset = token.NewFileSet() x.words = make(map[string]*IndexResult) - // collect all Spots - pathutil.Walk(root, &x, nil) + // index all files in the directories given by dirnames + for dirname := range dirnames { + list, err := ioutil.ReadDir(dirname) + if err != nil { + continue // ignore this directory + } + for _, f := range list { + if !f.IsDirectory() { + x.visitFile(dirname, f) + } + } + } + + if !fulltextIndex { + // the file set, the current file, and the sources are + // not needed after indexing if no text index is built - + // help GC and clear them + x.fset = nil + x.sources.Reset() + x.current = nil // contains reference to fset! + } // for each word, reduce the RunLists into a LookupResult; // also collect the word with its canonical spelling in a @@ -652,6 +767,7 @@ func NewIndex(root string) *Index { } wlist.Push(&wordPair{canonical(w), w}) } + x.stats.Words = len(words) // reduce the word list {canonical(w), w} into // a list of AltWords runs {canonical(w), {w}} @@ -670,14 +786,19 @@ func NewIndex(root string) *Index { snippets[i] = x.snippets.At(i).(*Snippet) } - return &Index{words, alts, snippets, x.nspots} + // 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} } -// Size returns the number of different words and -// spots indexed as a measure for the index size. -func (x *Index) Size() (nwords int, nspots int) { - return len(x.words), x.nspots +// Stats() returns index statistics. +func (x *Index) Stats() Statistics { + return x.stats } @@ -696,7 +817,7 @@ func (x *Index) LookupWord(w string) (match *LookupResult, alt *AltWords) { func isIdentifier(s string) bool { var S scanner.Scanner - S.Init("", []byte(s), nil, 0) + S.Init(token.NewFileSet(), "", []byte(s), nil, 0) if _, tok, _ := S.Scan(); tok == token.IDENT { _, tok, _ := S.Scan() return tok == token.EOF @@ -707,14 +828,14 @@ func isIdentifier(s string) bool { // 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, illegal is set. -func (x *Index) Lookup(query string) (match *LookupResult, alt *AltWords, illegal bool) { +// 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) { ss := strings.Split(query, ".", -1) // check query syntax for _, s := range ss { if !isIdentifier(s) { - illegal = true + err = os.NewError("all query parts must be identifiers") return } } @@ -734,7 +855,7 @@ func (x *Index) Lookup(query string) (match *LookupResult, alt *AltWords, illega } default: - illegal = true + err = os.NewError("query is not a (qualified) identifier") } return @@ -748,3 +869,103 @@ func (x *Index) Snippet(i int) *Snippet { } return nil } + + +type positionList []struct { + filename string + line int +} + +func (list positionList) Len() int { return len(list) } +func (list positionList) Less(i, j int) bool { return list[i].filename < list[j].filename } +func (list positionList) Swap(i, j int) { list[i], list[j] = list[j], list[i] } + + +// unique returns the list sorted and with duplicate entries removed +func unique(list []int) []int { + sort.SortInts(list) + var last int + i := 0 + for _, x := range list { + if i == 0 || x != last { + last = x + list[i] = x + i++ + } + } + return list[0:i] +} + + +// A FileLines value specifies a file and line numbers within that file. +type FileLines struct { + Filename string + Lines []int +} + + +// LookupRegexp returns the number of matches and the matches where a regular +// expression r is found in the full text index. At most n matches are +// returned (thus found <= n). +// +func (x *Index) LookupRegexp(r *regexp.Regexp, n int) (found int, result []FileLines) { + if x.suffixes == nil || n <= 0 { + return + } + // n > 0 + + var list positionList + // FindAllIndex may returns matches that span across file boundaries. + // Such matches are unlikely, buf after eliminating them we may end up + // with fewer than n matches. If we don't have enough at the end, redo + // the search with an increased value n1, but only if FindAllIndex + // returned all the requested matches in the first place (if it + // returned fewer than that there cannot be more). + for n1 := n; found < n; n1 += n - found { + found = 0 + matches := x.suffixes.FindAllIndex(r, n1) + // compute files, exclude matches that span file boundaries, + // and map offsets to file-local offsets + list = make(positionList, len(matches)) + for _, m := range matches { + // by construction, an offset corresponds to the Pos value + // for the file set - use it to get the file and line + p := token.Pos(m[0]) + if file := x.fset.File(p); file != nil { + if base := file.Base(); base <= m[1] && m[1] <= base+file.Size() { + // match [m[0], m[1]) is within the file boundaries + list[found].filename = file.Name() + list[found].line = file.Line(p) + found++ + } + } + } + if found == n || len(matches) < n1 { + // found all matches or there's no chance to find more + break + } + } + list = list[0:found] + sort.Sort(list) // sort by filename + + // collect matches belonging to the same file + var last string + var lines []int + addLines := func() { + if len(lines) > 0 { + // remove duplicate lines + result = append(result, FileLines{last, unique(lines)}) + lines = nil + } + } + for _, m := range list { + if m.filename != last { + addLines() + last = m.filename + } + lines = append(lines, m.line) + } + addLines() + + return +} diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go index 7a9279a2f..fe3d22fb9 100644 --- a/src/cmd/godoc/main.go +++ b/src/cmd/godoc/main.go @@ -49,7 +49,7 @@ var ( // 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 delay in minutes; usually syncDelay == syncMin, but delay may back off exponentially + syncDelay delayTime // actual sync interval in minutes; usually syncDelay == syncMin, but syncDelay may back off exponentially // network httpAddr = flag.String("http", "", "HTTP service address (e.g., '"+defaultAddr+"')") @@ -64,29 +64,30 @@ var ( ) -func serveError(c *http.Conn, r *http.Request, relpath string, err os.Error) { +func serveError(w http.ResponseWriter, r *http.Request, relpath string, err os.Error) { contents := applyTemplate(errorHTML, "errorHTML", err) // err may contain an absolute path! - servePage(c, "File "+relpath, "", "", contents) + w.WriteHeader(http.StatusNotFound) + servePage(w, "File "+relpath, "", "", contents) } -func exec(c *http.Conn, args []string) (status int) { +func exec(rw http.ResponseWriter, args []string) (status int) { r, w, err := os.Pipe() if err != nil { - log.Stderrf("os.Pipe(): %v\n", err) + log.Printf("os.Pipe(): %v", err) return 2 } bin := args[0] fds := []*os.File{nil, w, w} if *verbose { - log.Stderrf("executing %v", args) + log.Printf("executing %v", args) } pid, err := os.ForkExec(bin, args, os.Environ(), *goroot, fds) defer r.Close() w.Close() if err != nil { - log.Stderrf("os.ForkExec(%q): %v\n", bin, err) + log.Printf("os.ForkExec(%q): %v", bin, err) return 2 } @@ -95,41 +96,38 @@ func exec(c *http.Conn, args []string) (status int) { wait, err := os.Wait(pid, 0) if err != nil { os.Stderr.Write(buf.Bytes()) - log.Stderrf("os.Wait(%d, 0): %v\n", pid, err) + log.Printf("os.Wait(%d, 0): %v", pid, err) return 2 } status = wait.ExitStatus() if !wait.Exited() || status > 1 { os.Stderr.Write(buf.Bytes()) - log.Stderrf("executing %v failed (exit status = %d)", args, status) + log.Printf("executing %v failed (exit status = %d)", args, status) return } if *verbose { os.Stderr.Write(buf.Bytes()) } - if c != nil { - c.SetHeader("content-type", "text/plain; charset=utf-8") - c.Write(buf.Bytes()) + if rw != nil { + rw.SetHeader("content-type", "text/plain; charset=utf-8") + rw.Write(buf.Bytes()) } return } -// Maximum directory depth, adjust as needed. -const maxDirDepth = 24 - -func dosync(c *http.Conn, r *http.Request) { +func dosync(w http.ResponseWriter, r *http.Request) { args := []string{"/bin/sh", "-c", *syncCmd} - switch exec(c, args) { + 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. - fsTree.set(newDirectory(*goroot, maxDirDepth)) + initFSTree() fallthrough case 1: // sync failed because no files changed; @@ -152,9 +150,9 @@ func usage() { func loggingHandler(h http.Handler) http.Handler { - return http.HandlerFunc(func(c *http.Conn, req *http.Request) { - log.Stderrf("%s\t%s", c.RemoteAddr, req.URL) - h.ServeHTTP(c, req) + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + log.Printf("%s\t%s", w.RemoteAddr(), req.URL) + h.ServeHTTP(w, req) }) } @@ -239,13 +237,16 @@ func main() { // HTTP server mode. var handler http.Handler = http.DefaultServeMux if *verbose { - log.Stderrf("Go Documentation Server\n") - log.Stderrf("version = %s\n", runtime.Version()) - log.Stderrf("address = %s\n", *httpAddr) - log.Stderrf("goroot = %s\n", *goroot) - log.Stderrf("tabwidth = %d\n", *tabwidth) + log.Printf("Go Documentation Server") + log.Printf("version = %s", runtime.Version()) + log.Printf("address = %s", *httpAddr) + log.Printf("goroot = %s", *goroot) + log.Printf("tabwidth = %d", *tabwidth) + if *fulltextIndex { + log.Print("full text index enabled") + } if !fsMap.IsEmpty() { - log.Stderr("user-defined mapping:") + log.Print("user-defined mapping:") fsMap.Fprint(os.Stderr) } handler = loggingHandler(handler) @@ -256,12 +257,12 @@ func main() { http.Handle("/debug/sync", http.HandlerFunc(dosync)) } - // Initialize directory tree with corresponding timestamp. - // Do it in two steps: - // 1) set timestamp right away so that the indexer is kicked on - fsTree.set(nil) - // 2) compute initial directory tree in a goroutine so that launch is quick - go func() { fsTree.set(newDirectory(*goroot, maxDirDepth)) }() + // 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 { @@ -271,7 +272,7 @@ func main() { dosync(nil, nil) delay, _ := syncDelay.get() if *verbose { - log.Stderrf("next sync in %dmin", delay.(int)) + log.Printf("next sync in %dmin", delay.(int)) } time.Sleep(int64(delay.(int)) * 60e9) } @@ -333,15 +334,18 @@ func main() { } // TODO(gri): Provide a mechanism (flag?) to select a package // if there are multiple packages in a directory. - info := pkgHandler.getPageInfo(abspath, relpath, "", mode|tryMode) + info := pkgHandler.getPageInfo(abspath, relpath, "", mode) - if info.PAst == nil && info.PDoc == nil && info.Dirs == nil { + if info.Err != nil || info.PAst == nil && info.PDoc == nil && info.Dirs == nil { // try again, this time assume it's a command if len(path) > 0 && path[0] != '/' { abspath = absolutePath(path, cmdHandler.fsRoot) } info = cmdHandler.getPageInfo(abspath, relpath, "", mode) } + if info.Err != nil { + log.Exitf("%v", info.Err) + } // If we have more than one argument, use the remaining arguments for filtering if flag.NArg() > 1 { @@ -362,7 +366,7 @@ func main() { if i > 0 { fmt.Println() } - writeAny(os.Stdout, d, *html) + writeAny(os.Stdout, info.FSet, *html, d) fmt.Println() } return @@ -373,6 +377,6 @@ func main() { } if err := packageText.Execute(info, os.Stdout); err != nil { - log.Stderrf("packageText.Execute: %s", err) + log.Printf("packageText.Execute: %s", err) } } diff --git a/src/cmd/godoc/mapping.go b/src/cmd/godoc/mapping.go index 400f97e1f..1d87bbc76 100644 --- a/src/cmd/godoc/mapping.go +++ b/src/cmd/godoc/mapping.go @@ -11,6 +11,7 @@ import ( "io" "os" pathutil "path" + "sort" "strings" ) @@ -42,14 +43,19 @@ import ( // // (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 + prefixes []string // lazily computed from list } type mapping struct { prefix, path string + value *RWValue } @@ -75,43 +81,16 @@ type mapping struct { // public -> /home/build/public // func (m *Mapping) Init(paths string) { - cwd, _ := os.Getwd() // ignore errors - - pathlist := strings.Split(paths, ":", -1) - + pathlist := canonicalizePaths(strings.Split(paths, ":", -1), nil) list := make([]mapping, len(pathlist)) - n := 0 // number of mappings - for _, path := range pathlist { - if len(path) == 0 { - // ignore empty paths (don't assume ".") - continue - } - - // len(path) > 0: normalize path - if path[0] != '/' { - path = pathutil.Join(cwd, path) - } else { - path = pathutil.Clean(path) - } - - // check if mapping exists already - var i int - for i = 0; i < n; i++ { - if path == list[i].path { - break - } - } - - // add mapping if it is new - if i >= n { - _, prefix := pathutil.Split(path) - list[n] = mapping{prefix, path} - n++ - } + // create mapping list + for i, path := range pathlist { + _, prefix := pathutil.Split(path) + list[i] = mapping{prefix, path, new(RWValue)} } - m.list = list[0:n] + m.list = list } @@ -134,24 +113,25 @@ func (m *Mapping) PrefixList() []string { // compute the list lazily if m.prefixes == nil { list := make([]string, len(m.list)) - n := 0 // nuber of prefixes - - for _, e := range m.list { - // check if prefix exists already - var i int - for i = 0; i < n; i++ { - if e.prefix == list[i] { - break - } - } - // add prefix if it is new - if i >= n { - list[n] = e.prefix - n++ + // populate list + for i, e := range m.list { + list[i] = e.prefix + } + + // sort the list and remove duplicate entries + sort.SortStrings(list) + i := 0 + prev := "" + for _, path := range list { + if path != prev { + list[i] = path + i++ + prev = path } } - m.prefixes = list[0:n] + + m.prefixes = list[0:i] } return m.prefixes @@ -166,7 +146,7 @@ func (m *Mapping) Fprint(w io.Writer) { } -func split(path string) (head, tail string) { +func splitFirst(path string) (head, tail string) { i := strings.Index(path, "/") if i > 0 { // 0 < i < len(path) @@ -181,7 +161,7 @@ func split(path string) (head, tail string) { // string is returned. // func (m *Mapping) ToAbsolute(path string) string { - prefix, tail := split(path) + prefix, tail := splitFirst(path) for _, e := range m.list { switch { case e.prefix == prefix: @@ -214,3 +194,15 @@ func (m *Mapping) ToRelative(path string) string { } 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/snippet.go b/src/cmd/godoc/snippet.go index d8fb19533..6a12febe1 100755 --- a/src/cmd/godoc/snippet.go +++ b/src/cmd/godoc/snippet.go @@ -12,41 +12,22 @@ package main import ( "bytes" "go/ast" - "go/printer" + "go/token" "fmt" ) type Snippet struct { Line int - Text string + Text []byte } -type snippetStyler struct { - Styler // defined in godoc.go - highlight *ast.Ident // identifier to highlight -} - - -func (s *snippetStyler) LineTag(line int) (text []uint8, tag printer.HTMLTag) { - return // no LineTag for snippets -} - - -func (s *snippetStyler) Ident(id *ast.Ident) (text []byte, tag printer.HTMLTag) { - text = []byte(id.Name()) - if s.highlight == id { - tag = printer.HTMLTag{"", ""} - } - return -} - - -func newSnippet(decl ast.Decl, id *ast.Ident) *Snippet { +func newSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet { + // TODO instead of pretty-printing the node, should use the original source instead var buf bytes.Buffer - writeNode(&buf, decl, true, &snippetStyler{highlight: id}) - return &Snippet{id.Pos().Line, buf.String()} + writeNode(&buf, fset, decl, true) + return &Snippet{fset.Position(id.Pos()).Line, FormatText(buf.Bytes(), -1, true, id.Name, nil)} } @@ -73,20 +54,20 @@ func findSpec(list []ast.Spec, id *ast.Ident) ast.Spec { } -func genSnippet(d *ast.GenDecl, id *ast.Ident) *Snippet { +func genSnippet(fset *token.FileSet, d *ast.GenDecl, id *ast.Ident) *Snippet { s := findSpec(d.Specs, id) if s == nil { return nil // declaration doesn't contain id - exit gracefully } // only use the spec containing the id for the snippet - dd := &ast.GenDecl{d.Doc, d.Position, d.Tok, d.Lparen, []ast.Spec{s}, d.Rparen} + dd := &ast.GenDecl{d.Doc, d.Pos(), d.Tok, d.Lparen, []ast.Spec{s}, d.Rparen} - return newSnippet(dd, id) + return newSnippet(fset, dd, id) } -func funcSnippet(d *ast.FuncDecl, id *ast.Ident) *Snippet { +func funcSnippet(fset *token.FileSet, d *ast.FuncDecl, id *ast.Ident) *Snippet { if d.Name != id { return nil // declaration doesn't contain id - exit gracefully } @@ -94,7 +75,7 @@ func funcSnippet(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} - return newSnippet(dd, id) + return newSnippet(fset, dd, id) } @@ -102,19 +83,21 @@ func funcSnippet(d *ast.FuncDecl, id *ast.Ident) *Snippet { // identifier id. Parts of the declaration not containing the identifier // may be removed for a more compact snippet. // -func NewSnippet(decl ast.Decl, id *ast.Ident) (s *Snippet) { +func NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) (s *Snippet) { switch d := decl.(type) { case *ast.GenDecl: - s = genSnippet(d, id) + s = genSnippet(fset, d, id) case *ast.FuncDecl: - s = funcSnippet(d, id) + s = funcSnippet(fset, d, id) } // handle failure gracefully if s == nil { + var buf bytes.Buffer + fmt.Fprintf(&buf, `could not generate a snippet for %s`, id.Name) s = &Snippet{ - id.Pos().Line, - fmt.Sprintf(`could not generate a snippet for %s`, id.Name()), + fset.Position(id.Pos()).Line, + buf.Bytes(), } } return diff --git a/src/cmd/godoc/spec.go b/src/cmd/godoc/spec.go index 2298fae2c..b1c1a883f 100644 --- a/src/cmd/godoc/spec.go +++ b/src/cmd/godoc/spec.go @@ -20,24 +20,28 @@ import ( type ebnfParser struct { - out io.Writer // parser output - src []byte // parser source + out io.Writer // parser output + src []byte // parser source + file *token.File // for position information scanner scanner.Scanner - prev int // offset of previous token - pos token.Position // token position - tok token.Token // one token look-ahead - lit []byte // token literal + prev int // offset of previous token + pos token.Pos // token position + tok token.Token // one token look-ahead + lit []byte // token literal } func (p *ebnfParser) flush() { - p.out.Write(p.src[p.prev:p.pos.Offset]) - p.prev = p.pos.Offset + offs := p.file.Offset(p.pos) + p.out.Write(p.src[p.prev:offs]) + p.prev = offs } func (p *ebnfParser) next() { - p.flush() + 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? @@ -52,9 +56,9 @@ func (p *ebnfParser) Error(pos token.Position, msg string) { } -func (p *ebnfParser) errorExpected(pos token.Position, msg string) { +func (p *ebnfParser) errorExpected(pos token.Pos, msg string) { msg = "expected " + msg - if pos.Offset == p.pos.Offset { + if pos == p.pos { // the error happened at the current position; // make the error message more specific msg += ", found '" + p.tok.String() + "'" @@ -62,11 +66,11 @@ func (p *ebnfParser) errorExpected(pos token.Position, msg string) { msg += " " + string(p.lit) } } - p.Error(pos, msg) + p.Error(p.file.Position(pos), msg) } -func (p *ebnfParser) expect(tok token.Token) token.Position { +func (p *ebnfParser) expect(tok token.Token) token.Pos { pos := p.pos if p.tok != tok { p.errorExpected(pos, "'"+tok.String()+"'") @@ -148,11 +152,11 @@ func (p *ebnfParser) parseProduction() { } -func (p *ebnfParser) parse(out io.Writer, src []byte) { +func (p *ebnfParser) parse(fset *token.FileSet, out io.Writer, src []byte) { // initialize ebnfParser p.out = out p.src = src - p.scanner.Init("", src, p, 0) + p.file = p.scanner.Init(fset, "", src, p, 0) p.next() // initializes pos, tok, lit // process source @@ -171,6 +175,7 @@ var ( func linkify(out io.Writer, src []byte) { + fset := token.NewFileSet() for len(src) > 0 { n := len(src) @@ -192,7 +197,7 @@ func linkify(out io.Writer, src []byte) { out.Write(src[0:i]) // parse and write EBNF var p ebnfParser - p.parse(out, src[i:j]) + p.parse(fset, out, src[i:j]) // advance src = src[j:n] diff --git a/src/cmd/godoc/utils.go b/src/cmd/godoc/utils.go new file mode 100644 index 000000000..55cf87841 --- /dev/null +++ b/src/cmd/godoc/utils.go @@ -0,0 +1,109 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains support functionality for godoc. + +package main + +import ( + "io" + "io/ioutil" + "os" + pathutil "path" + "sort" + "strings" + "sync" + "time" +) + + +// An RWValue wraps a value and permits mutually exclusive +// access to it and records the time the value was last set. +type RWValue struct { + mutex sync.RWMutex + value interface{} + timestamp int64 // time of last set(), in seconds since epoch +} + + +func (v *RWValue) set(value interface{}) { + v.mutex.Lock() + v.value = value + v.timestamp = time.Seconds() + v.mutex.Unlock() +} + + +func (v *RWValue) get() (interface{}, int64) { + v.mutex.RLock() + defer v.mutex.RUnlock() + return v.value, v.timestamp +} + + +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 path[0] != '/' { + path = pathutil.Join(cwd, path) + } else { + path = pathutil.Clean(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.SortStrings(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 { + f, err := ioutil.TempFile(cwd, 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) +} diff --git a/src/cmd/gofmt/Makefile b/src/cmd/gofmt/Makefile index 2294fd1db..5f2f454e8 100644 --- a/src/cmd/gofmt/Makefile +++ b/src/cmd/gofmt/Makefile @@ -2,12 +2,13 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=gofmt GOFILES=\ gofmt.go\ rewrite.go\ + simplify.go\ include ../../Make.cmd @@ -15,5 +16,4 @@ test: $(TARG) ./test.sh smoketest: $(TARG) - ./test.sh "$(GOROOT)"/src/pkg/go/parser/parser.go - + (cd testdata; ./test.sh) diff --git a/src/cmd/gofmt/doc.go b/src/cmd/gofmt/doc.go index 2e4c40c21..2d2c9ae61 100644 --- a/src/cmd/gofmt/doc.go +++ b/src/cmd/gofmt/doc.go @@ -20,6 +20,8 @@ The flags are: unless -w is also set. -r rule apply the rewrite rule to the source before reformatting. + -s + try to simplify code (after applying the rewrite rule, if any). -w if set, overwrite each input file with its output. -spaces @@ -33,6 +35,8 @@ Debugging flags: -trace print parse trace. + -ast + print AST (before rewrites). -comments=true print comments; if false, all comments are elided from the output. diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index ffec0325f..d7b70c461 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -12,6 +12,7 @@ import ( "go/parser" "go/printer" "go/scanner" + "go/token" "io/ioutil" "os" pathutil "path" @@ -24,11 +25,12 @@ var ( 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(α)] -> α[β:]')") + simplifyAST = flag.Bool("s", false, "simplify code") // debugging support comments = flag.Bool("comments", true, "print comments") - debug = flag.Bool("debug", false, "print debugging information") trace = flag.Bool("trace", false, "print parse trace") + printAST = flag.Bool("ast", false, "print AST (before rewrites)") // layout control tabWidth = flag.Int("tabwidth", 8, "tab width") @@ -38,6 +40,7 @@ var ( var ( + fset = token.NewFileSet() exitCode = 0 rewrite func(*ast.File) *ast.File parserMode uint @@ -92,22 +95,26 @@ func processFile(f *os.File) os.Error { return err } - var scope *ast.Scope - if *debug { - scope = ast.NewScope(nil) - } - file, err := parser.ParseFile(f.Name(), src, scope, parserMode) + file, err := parser.ParseFile(fset, f.Name(), src, parserMode) if err != nil { return err } + if *printAST { + ast.Print(file) + } + if rewrite != nil { file = rewrite(file) } + if *simplifyAST { + simplify(file) + } + var res bytes.Buffer - _, err = (&printer.Config{printerMode, *tabWidth, nil}).Fprint(&res, file) + _, err = (&printer.Config{printerMode, *tabWidth, nil}).Fprint(&res, fset, file) if err != nil { return err } @@ -133,10 +140,10 @@ func processFile(f *os.File) os.Error { } -func processFileByName(filename string) (err os.Error) { +func processFileByName(filename string) os.Error { file, err := os.Open(filename, os.O_RDONLY, 0) if err != nil { - return + return err } defer file.Close() return processFile(file) diff --git a/src/cmd/gofmt/rewrite.go b/src/cmd/gofmt/rewrite.go index a89146ca0..8ea5334e9 100644 --- a/src/cmd/gofmt/rewrite.go +++ b/src/cmd/gofmt/rewrite.go @@ -37,7 +37,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("input", s, nil) + x, err := parser.ParseExpr(fset, "input", s) if err != nil { fmt.Fprintf(os.Stderr, "parsing %s %s: %s\n", what, s, err) os.Exit(2) @@ -66,13 +66,19 @@ func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File { } -var positionType = reflect.Typeof(token.Position{}) -var identType = reflect.Typeof((*ast.Ident)(nil)) - - -func isWildcard(s string) bool { - rune, size := utf8.DecodeRuneInString(s) - return size == len(s) && unicode.IsLower(rune) +// setValue is a wrapper for x.SetValue(y); it protects +// the caller from panics if x cannot be changed to y. +func setValue(x, y reflect.Value) { + defer func() { + if x := recover(); x != nil { + if s, ok := x.(string); ok && strings.HasPrefix(s, "type mismatch") { + // x cannot be set to y - ignore this rewrite + return + } + panic(x) + } + }() + x.SetValue(y) } @@ -86,21 +92,31 @@ func apply(f func(reflect.Value) reflect.Value, val reflect.Value) reflect.Value case *reflect.SliceValue: for i := 0; i < v.Len(); i++ { e := v.Elem(i) - e.SetValue(f(e)) + setValue(e, f(e)) } case *reflect.StructValue: for i := 0; i < v.NumField(); i++ { e := v.Field(i) - e.SetValue(f(e)) + setValue(e, f(e)) } case *reflect.InterfaceValue: e := v.Elem() - v.SetValue(f(e)) + setValue(v, f(e)) } return val } +var positionType = reflect.Typeof(token.NoPos) +var identType = reflect.Typeof((*ast.Ident)(nil)) + + +func isWildcard(s string) bool { + rune, size := utf8.DecodeRuneInString(s) + return size == len(s) && unicode.IsLower(rune) +} + + // match returns true if pattern matches val, // recording wildcard submatches in m. // If m == nil, match checks whether pattern == val. @@ -109,17 +125,20 @@ func match(m map[string]reflect.Value, pattern, val reflect.Value) bool { // times in the pattern, it must match the same expression // each time. if m != nil && pattern.Type() == identType { - name := pattern.Interface().(*ast.Ident).Name() + name := pattern.Interface().(*ast.Ident).Name if isWildcard(name) { - if old, ok := m[name]; ok { - return match(nil, old, val) + // wildcards only match expressions + if _, ok := val.Interface().(ast.Expr); ok { + if old, ok := m[name]; ok { + return match(nil, old, val) + } + m[name] = val + return true } - m[name] = val - return true } } - // Otherwise, the expressions must match recursively. + // Otherwise, pattern and val must match recursively. if pattern == nil || val == nil { return pattern == nil && val == nil } @@ -139,7 +158,7 @@ func match(m map[string]reflect.Value, pattern, val reflect.Value) bool { // of recursing down any further via reflection. p := pattern.Interface().(*ast.Ident) v := val.Interface().(*ast.Ident) - return p == nil && v == nil || p != nil && v != nil && p.Name() == v.Name() + return p == nil && v == nil || p != nil && v != nil && p.Name == v.Name } p := reflect.Indirect(pattern) @@ -194,7 +213,7 @@ func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) // Wildcard gets replaced with map value. if m != nil && pattern.Type() == identType { - name := pattern.Interface().(*ast.Ident).Name() + name := pattern.Interface().(*ast.Ident).Name if isWildcard(name) { if old, ok := m[name]; ok { return subst(nil, old, nil) @@ -203,6 +222,10 @@ func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) } if pos != nil && pattern.Type() == positionType { + // use new position only if old position was valid in the first place + if old := pattern.Interface().(token.Pos); !old.IsValid() { + return pattern + } return pos } diff --git a/src/cmd/gofmt/simplify.go b/src/cmd/gofmt/simplify.go new file mode 100644 index 000000000..bcc67c4a6 --- /dev/null +++ b/src/cmd/gofmt/simplify.go @@ -0,0 +1,67 @@ +// 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 ( + "go/ast" + "reflect" +) + + +type simplifier struct{} + +func (s *simplifier) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.CompositeLit: + // array, slice, and map composite literals may be simplified + outer := n + var eltType ast.Expr + switch typ := outer.Type.(type) { + case *ast.ArrayType: + eltType = typ.Elt + case *ast.MapType: + eltType = typ.Value + } + + if eltType != nil { + typ := reflect.NewValue(eltType) + for _, x := range outer.Elts { + // look at value of indexed/named elements + if t, ok := x.(*ast.KeyValueExpr); ok { + x = t.Value + } + simplify(x) + // if the element is a composite literal and its literal type + // matches the outer literal's element type exactly, the inner + // literal type may be omitted + if inner, ok := x.(*ast.CompositeLit); ok { + if match(nil, typ, reflect.NewValue(inner.Type)) { + inner.Type = nil + } + } + } + + // node was simplified - stop walk (there are no subnodes to simplify) + return nil + } + + case *ast.RangeStmt: + // range of the form: for x, _ = range v {...} + // can be simplified to: for x = range v {...} + if n.Value != nil { + if ident, ok := n.Value.(*ast.Ident); ok && ident.Name == "_" { + n.Value = nil + } + } + } + + return s +} + + +func simplify(node ast.Node) { + var s simplifier + ast.Walk(&s, node) +} diff --git a/src/cmd/gofmt/test.sh b/src/cmd/gofmt/test.sh index bed46532b..b5f4de1e2 100755 --- a/src/cmd/gofmt/test.sh +++ b/src/cmd/gofmt/test.sh @@ -3,7 +3,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -. "$GOROOT"/src/Make.$GOARCH +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 @@ -41,7 +41,8 @@ apply1() { 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 ) return ;; + bug282.go | bug287.go | bug298.go | bug299.go | bug300.go | \ + bug302.go | bug306.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: diff --git a/src/cmd/gofmt/testdata/composites.golden b/src/cmd/gofmt/testdata/composites.golden new file mode 100644 index 000000000..1fd5847c1 --- /dev/null +++ b/src/cmd/gofmt/testdata/composites.golden @@ -0,0 +1,104 @@ +package P + +type T struct { + x, y int +} + +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}, +} + +// from exp/4s/data.go +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 new file mode 100644 index 000000000..15afd9e5c --- /dev/null +++ b/src/cmd/gofmt/testdata/composites.input @@ -0,0 +1,104 @@ +package P + +type T struct { + x, y int +} + +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}, +} + +// from exp/4s/data.go +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/test.sh b/src/cmd/gofmt/testdata/test.sh new file mode 100755 index 000000000..a1d5d823e --- /dev/null +++ b/src/cmd/gofmt/testdata/test.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# Copyright 2010 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +CMD="../gofmt" +TMP=test_tmp.go +COUNT=0 + + +cleanup() { + rm -f $TMP +} + + +error() { + echo $1 + exit 1 +} + + +count() { + #echo $1 + let COUNT=$COUNT+1 + let M=$COUNT%10 + if [ $M == 0 ]; then + echo -n "." + fi +} + + +test() { + count $1 + + # compare against .golden file + cleanup + $CMD -s $1 > $TMP + cmp -s $TMP $2 + if [ $? != 0 ]; then + diff $TMP $2 + error "Error: simplified $1 does not match $2" + fi + + # make sure .golden is idempotent + cleanup + $CMD -s $2 > $TMP + cmp -s $TMP $2 + if [ $? != 0 ]; then + diff $TMP $2 + error "Error: $2 is not idempotent" + fi +} + + +runtests() { + smoketest=../../../pkg/go/parser/parser.go + test $smoketest $smoketest + test composites.input composites.golden + # add more test cases here +} + + +runtests +cleanup +echo "PASSED ($COUNT tests)" diff --git a/src/cmd/goinstall/Makefile b/src/cmd/goinstall/Makefile index cf4728401..6ddb32be7 100644 --- a/src/cmd/goinstall/Makefile +++ b/src/cmd/goinstall/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=goinstall GOFILES=\ diff --git a/src/cmd/goinstall/doc.go b/src/cmd/goinstall/doc.go index 80b30d5ac..17cc06969 100644 --- a/src/cmd/goinstall/doc.go +++ b/src/cmd/goinstall/doc.go @@ -10,14 +10,33 @@ It maintains a list of public Go packages at http://godashboard.appspot.com/pack Usage: goinstall [flags] importpath... + goinstall [flags] -a Flags and default settings: + -a=false install all previously installed packages -dashboard=true tally public packages on godashboard.appspot.com - -update=false update already-downloaded packages + -log=true log installed packages to $GOROOT/goinstall.log for use by -a + -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. +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/. If the import @@ -31,8 +50,8 @@ if necessary. The recognized code hosting sites are: GitHub (Git) - import "github.com/user/project.git" - import "github.com/user/project.git/sub/directory" + import "github.com/user/project" + import "github.com/user/project/sub/directory" Google Code Project Hosting (Mercurial, Subversion) @@ -44,17 +63,17 @@ if necessary. The recognized code hosting sites are: Launchpad - import "launchpad.net/project - import "launchpad.net/project/series - import "launchpad.net/project/series/sub/directory + 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 + 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 -update flag changes this behavior, +attempt to fetch updates. The -u flag changes this behavior, causing goinstall to update all remote packages encountered during the installation. diff --git a/src/cmd/goinstall/download.go b/src/cmd/goinstall/download.go index 3422e8186..889f9d857 100644 --- a/src/cmd/goinstall/download.go +++ b/src/cmd/goinstall/download.go @@ -38,16 +38,16 @@ var launchpad = regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A // download checks out or updates pkg from the remote server. func download(pkg string) (string, os.Error) { - if strings.Index(pkg, "..") >= 0 { + if strings.Contains(pkg, "..") { return "", os.ErrorString("invalid path (contains ..)") } - if m := bitbucket.MatchStrings(pkg); m != nil { + if m := bitbucket.FindStringSubmatch(pkg); m != nil { if err := vcsCheckout(&hg, root+m[1], "http://"+m[1], m[1]); err != nil { return "", err } return root + pkg, nil } - if m := googlecode.MatchStrings(pkg); m != nil { + if m := googlecode.FindStringSubmatch(pkg); m != nil { var v *vcs switch m[2] { case "hg": @@ -58,12 +58,12 @@ func download(pkg string) (string, os.Error) { // regexp only allows hg, svn to get through panic("missing case in download: " + pkg) } - if err := vcsCheckout(v, root+m[1], "http://"+m[1], m[1]); err != nil { + if err := vcsCheckout(v, root+m[1], "https://"+m[1], m[1]); err != nil { return "", err } return root + pkg, nil } - if m := github.MatchStrings(pkg); m != nil { + if m := github.FindStringSubmatch(pkg); m != nil { if strings.HasSuffix(m[1], ".git") { return "", os.ErrorString("repository " + pkg + " should not have .git suffix") } @@ -72,7 +72,7 @@ func download(pkg string) (string, os.Error) { } return root + pkg, nil } - if m := launchpad.MatchStrings(pkg); m != nil { + if m := launchpad.FindStringSubmatch(pkg); m != nil { // Either lp.net/[/[/]] // or lp.net/~//[/] if err := vcsCheckout(&bzr, root+m[1], "https://"+m[1], m[1]); err != nil { @@ -88,6 +88,7 @@ func download(pkg string) (string, os.Error) { type vcs struct { cmd string metadir string + checkout string clone string update string updateReleaseFlag string @@ -101,6 +102,7 @@ type vcs struct { var hg = vcs{ cmd: "hg", metadir: ".hg", + checkout: "checkout", clone: "clone", update: "update", updateReleaseFlag: "release", @@ -113,18 +115,20 @@ var hg = vcs{ var git = vcs{ cmd: "git", metadir: ".git", + checkout: "checkout", clone: "clone", update: "pull", updateReleaseFlag: "release", pull: "fetch", - log: "log", - logLimitFlag: "-n1", + log: "show-ref", + logLimitFlag: "", logReleaseFlag: "release", } var svn = vcs{ cmd: "svn", metadir: ".svn", + checkout: "checkout", clone: "checkout", update: "update", updateReleaseFlag: "release", @@ -136,6 +140,7 @@ var svn = vcs{ var bzr = vcs{ cmd: "bzr", metadir: ".bzr", + checkout: "update", clone: "branch", update: "update", updateReleaseFlag: "-rrelease", @@ -146,6 +151,22 @@ var bzr = vcs{ logReleaseFlag: "-rrelease", } +// Try to detect if a "release" tag exists. If it does, update +// to the tagged version, otherwise just update the current branch. +// NOTE(_nil): svn will always fail because it is trying to get +// the revision history of a file named "release" instead of +// looking for a commit with a release tag +func (v *vcs) updateRepo(dst string) os.Error { + if err := quietRun(dst, nil, v.cmd, v.log, v.logLimitFlag, v.logReleaseFlag); err == nil { + if err := run(dst, nil, v.cmd, v.checkout, v.updateReleaseFlag); err != nil { + return err + } + } else if err := run(dst, nil, v.cmd, v.update); err != nil { + return err + } + return nil +} + // vcsCheckout 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) @@ -164,8 +185,9 @@ func vcsCheckout(vcs *vcs, dst, repo, dashpath string) os.Error { if err := run("/", nil, vcs.cmd, vcs.clone, repo, dst); err != nil { return err } - quietRun(dst, nil, vcs.cmd, vcs.update, vcs.updateReleaseFlag) - + if err := vcs.updateRepo(dst); err != nil { + return err + } // success on first installation - report maybeReportToDashboard(dashpath) } else if *update { @@ -181,19 +203,8 @@ func vcsCheckout(vcs *vcs, dst, repo, dashpath string) os.Error { } } - // Try to detect if a "release" tag exists. If it does, update - // to the tagged version. If no tag is found, then update to the - // tip afterwards. - // NOTE(gustavo@niemeyer.net): What is the expected behavior with - // svn here? "svn log -l1 release" doesn't make sense in this - // context and will probably fail. - if err := quietRun(dst, nil, vcs.cmd, vcs.log, vcs.logLimitFlag, vcs.logReleaseFlag); err == nil { - if err := run(dst, nil, vcs.cmd, vcs.update, vcs.updateReleaseFlag); err != nil { - // The VCS supports tagging, has the "release" tag, but - // something else went wrong. Report. - return err - } - } else if err := run(dst, nil, vcs.cmd, vcs.update); err != nil { + // Update to release or latest revision + if err := vcs.updateRepo(dst); err != nil { return err } } diff --git a/src/cmd/goinstall/main.go b/src/cmd/goinstall/main.go index 60efdf082..b0f08efdf 100644 --- a/src/cmd/goinstall/main.go +++ b/src/cmd/goinstall/main.go @@ -11,28 +11,37 @@ import ( "exec" "flag" "fmt" + "go/token" "io" + "io/ioutil" "os" "path" + "runtime" "strings" ) func usage() { fmt.Fprint(os.Stderr, "usage: goinstall importpath...\n") + fmt.Fprintf(os.Stderr, "\tgoinstall -a\n") flag.PrintDefaults() os.Exit(2) } var ( - argv0 = os.Args[0] - errors = false - gobin = os.Getenv("GOBIN") - parents = make(map[string]string) - root = os.Getenv("GOROOT") - visit = make(map[string]status) + fset = token.NewFileSet() + argv0 = os.Args[0] + errors = false + parents = make(map[string]string) + root = runtime.GOROOT() + visit = make(map[string]status) + logfile = path.Join(root, "goinstall.log") + installedPkgs = make(map[string]bool) + allpkg = flag.Bool("a", false, "install all previously installed packages") reportToDashboard = flag.Bool("dashboard", true, "report public packages at "+dashboardURL) + logPkgs = flag.Bool("log", true, "log installed packages to $GOROOT/goinstall.log for use by -a") update = flag.Bool("u", false, "update already-downloaded packages") + clean = flag.Bool("clean", false, "clean the package directory before installing") verbose = flag.Bool("v", false, "verbose") ) @@ -51,15 +60,30 @@ func main() { os.Exit(1) } root += "/src/pkg/" - if gobin == "" { - gobin = os.Getenv("HOME") + "/bin" - } // special case - "unsafe" is already installed visit["unsafe"] = done - // install command line arguments args := flag.Args() + if *allpkg || *logPkgs { + readPackageList() + } + if *allpkg { + if len(args) != 0 { + usage() // -a and package list both provided + } + // install all packages that were ever installed + if len(installedPkgs) == 0 { + fmt.Fprintf(os.Stderr, "%s: no installed packages\n", argv0) + os.Exit(1) + } + args = make([]string, len(installedPkgs), len(installedPkgs)) + i := 0 + for pkg := range installedPkgs { + args[i] = pkg + i++ + } + } if len(args) == 0 { usage() } @@ -82,6 +106,29 @@ func printDeps(pkg string) { fmt.Fprintf(os.Stderr, "\t%s ->\n", pkg) } +// readPackageList reads the list of installed packages from goinstall.log +func readPackageList() { + pkglistdata, _ := ioutil.ReadFile(logfile) + pkglist := strings.Fields(string(pkglistdata)) + for _, pkg := range pkglist { + installedPkgs[pkg] = true + } +} + +// logPackage logs the named package as installed in goinstall.log, if the package is not found in there +func logPackage(pkg string) { + if installedPkgs[pkg] { + return + } + fout, err := os.Open(logfile, os.O_WRONLY|os.O_APPEND|os.O_CREAT, 0644) + if err != nil { + fmt.Fprintf(os.Stderr, "%s: %s\n", argv0, err) + return + } + fmt.Fprintf(fout, "%s\n", pkg) + fout.Close() +} + // 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. @@ -104,6 +151,11 @@ func install(pkg, parent string) { // If remote, download or update it. var dir string local := false + if strings.HasPrefix(pkg, "http://") { + fmt.Fprintf(os.Stderr, "%s: %s: 'http://' used in remote path, try '%s'\n", argv0, pkg, pkg[7:]) + errors = true + return + } if isLocalPath(pkg) { dir = pkg local = true @@ -122,23 +174,25 @@ func install(pkg, parent string) { } // Install prerequisites. - files, m, pkgname, err := goFiles(dir, parent == "") + dirInfo, err := scanDir(dir, parent == "") if err != nil { fmt.Fprintf(os.Stderr, "%s: %s: %s\n", argv0, pkg, err) errors = true visit[pkg] = done return } - if len(files) == 0 { + if len(dirInfo.goFiles) == 0 { fmt.Fprintf(os.Stderr, "%s: %s: package has no files\n", argv0, pkg) errors = true visit[pkg] = done return } - for p := range m { - install(p, pkg) + for _, p := range dirInfo.imports { + if p != "C" { + install(p, pkg) + } } - if pkgname == "main" { + if dirInfo.pkgName == "main" { if !errors { fmt.Fprintf(os.Stderr, "%s: %s's dependencies are installed.\n", argv0, pkg) } @@ -152,9 +206,11 @@ func install(pkg, parent string) { if err := domake(dir, pkg, local); err != nil { fmt.Fprintf(os.Stderr, "%s: installing %s: %s\n", argv0, pkg, err) errors = true + } else if !local && *logPkgs { + // mark this package as installed in $GOROOT/goinstall.log + logPackage(pkg) } } - visit[pkg] = done } @@ -207,6 +263,9 @@ func genRun(dir string, stdin []byte, cmd []string, quiet bool) os.Error { io.Copy(&buf, p.Stdout) w, err := p.Wait(0) p.Close() + if err != nil { + return err + } if !w.Exited() || w.ExitStatus() != 0 { if !quiet || *verbose { if dir != "" { diff --git a/src/cmd/goinstall/make.go b/src/cmd/goinstall/make.go index c15709b31..93a648b2b 100644 --- a/src/cmd/goinstall/make.go +++ b/src/cmd/goinstall/make.go @@ -17,30 +17,65 @@ import ( // For non-local packages or packages without Makefiles, // domake generates a standard Makefile and passes it // to make on standard input. -func domake(dir, pkg string, local bool) os.Error { +func domake(dir, pkg string, local bool) (err os.Error) { + needMakefile := true if local { _, err := os.Stat(dir + "/Makefile") if err == nil { - return run(dir, nil, gobin+"/gomake", "install") + needMakefile = false } } - makefile, err := makeMakefile(dir, pkg) - if err != nil { - return err + cmd := []string{"gomake"} + var makefile []byte + if needMakefile { + if makefile, err = makeMakefile(dir, pkg); err != nil { + return err + } + cmd = append(cmd, "-f-") + } + if *clean { + cmd = append(cmd, "clean") } - return run(dir, makefile, gobin+"/gomake", "-f-", "install") + 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) ([]byte, os.Error) { - files, _, _, err := goFiles(dir, false) + dirInfo, err := scanDir(dir, false) if err != nil { return nil, err } + + if len(dirInfo.cgoFiles) == 0 && len(dirInfo.cFiles) > 0 { + // When using cgo, .c files are compiled with gcc. Without cgo, + // they may be intended for 6c. Just error out for now. + return nil, os.ErrorString("C files found in non-cgo package") + } + + cgoFiles := dirInfo.cgoFiles + isCgo := make(map[string]bool, len(cgoFiles)) + for _, file := range cgoFiles { + isCgo[file] = true + } + + oFiles := make([]string, 0, len(dirInfo.cFiles)) + for _, file := range dirInfo.cFiles { + oFiles = append(oFiles, file[:len(file)-2]+".o") + } + + goFiles := make([]string, 0, len(dirInfo.goFiles)) + for _, file := range dirInfo.goFiles { + if !isCgo[file] { + goFiles = append(goFiles, file) + } + } + var buf bytes.Buffer - if err := makefileTemplate.Execute(&makedata{pkg, files}, &buf); err != nil { + md := makedata{pkg, goFiles, cgoFiles, oFiles} + if err := makefileTemplate.Execute(&md, &buf); err != nil { return nil, err } return buf.Bytes(), nil @@ -48,19 +83,38 @@ func makeMakefile(dir, pkg string) ([]byte, os.Error) { // makedata is the data type for the makefileTemplate. type makedata struct { - pkg string // package import path - files []string // list of .go files + Pkg string // package import path + GoFiles []string // list of non-cgo .go files + CgoFiles []string // list of cgo .go files + OFiles []string // list of ofiles for cgo } var makefileTemplate = template.MustParse(` -include $(GOROOT)/src/Make.$(GOARCH) +include $(GOROOT)/src/Make.inc + +TARG={Pkg} -TARG={pkg} +{.section GoFiles} GOFILES=\ -{.repeated section files} +{.repeated section GoFiles} + {@}\ +{.end} + +{.end} +{.section CgoFiles} +CGOFILES=\ +{.repeated section CgoFiles} {@}\ {.end} +{.end} +{.section OFiles} +CGO_OFILES=\ +{.repeated section OFiles} + {@}\ +{.end} + +{.end} include $(GOROOT)/src/Make.pkg `, nil) diff --git a/src/cmd/goinstall/parse.go b/src/cmd/goinstall/parse.go index ae391ed9a..679edfabc 100644 --- a/src/cmd/goinstall/parse.go +++ b/src/cmd/goinstall/parse.go @@ -16,36 +16,60 @@ import ( "go/parser" ) -// goFiles returns a list of the *.go source files in dir, excluding -// those in package main (unless allowMain is true) or ending in -// _test.go. It also returns a map giving the packages imported by -// those files, and the package name. -// The map keys are the imported paths. The key's value -// is one file that imports that path. -func goFiles(dir string, allowMain bool) (files []string, imports map[string]string, pkgName string, err os.Error) { + +type dirInfo struct { + goFiles []string // .go files within dir (including cgoFiles) + cgoFiles []string // .go files that import "C" + cFiles []string // .c files within dir + imports []string // All packages imported by goFiles + pkgName string // Name of package within dir +} + +// scanDir returns a structure with details about the Go content found +// in the given directory. The list of files will NOT contain the +// following entries: +// +// - Files in package main (unless allowMain is true) +// - Files ending in _test.go +// - Files starting with _ (temporary) +// - Files containing .cgo in their names +// +// The imports map keys are package paths imported by listed Go files, +// and the values are the Go files importing the respective package paths. +func scanDir(dir string, allowMain bool) (info *dirInfo, err os.Error) { f, err := os.Open(dir, os.O_RDONLY, 0) if err != nil { - return nil, nil, "", err + return nil, err } dirs, err := f.Readdir(-1) f.Close() if err != nil { - return nil, nil, "", err + return nil, err } - files = make([]string, 0, len(dirs)) - imports = make(map[string]string) + goFiles := make([]string, 0, len(dirs)) + cgoFiles := make([]string, 0, len(dirs)) + cFiles := make([]string, 0, len(dirs)) + importsm := make(map[string]bool) + pkgName := "" for i := range dirs { d := &dirs[i] + if strings.HasPrefix(d.Name, "_") || strings.Index(d.Name, ".cgo") != -1 { + continue + } + if strings.HasSuffix(d.Name, ".c") { + cFiles = append(cFiles, d.Name) + continue + } if !strings.HasSuffix(d.Name, ".go") || strings.HasSuffix(d.Name, "_test.go") { continue } filename := path.Join(dir, d.Name) - pf, err := parser.ParseFile(filename, nil, nil, parser.ImportsOnly) + pf, err := parser.ParseFile(fset, filename, nil, parser.ImportsOnly) if err != nil { - return nil, nil, "", err + return nil, err } - s := string(pf.Name.Name()) + s := string(pf.Name.Name) if s == "main" && !allowMain { continue } @@ -56,24 +80,31 @@ func goFiles(dir string, allowMain bool) (files []string, imports map[string]str // do we return pkgName=="main". // A mix of main and another package reverts // to the original (allowMain=false) behaviour. - if allowMain && pkgName == "main" { - return goFiles(dir, false) + if s == "main" || pkgName == "main" { + return scanDir(dir, false) } - return nil, nil, "", os.ErrorString("multiple package names in " + dir) + return nil, os.ErrorString("multiple package names in " + dir) } - n := len(files) - files = files[0 : n+1] - files[n] = filename + goFiles = append(goFiles, d.Name) for _, decl := range pf.Decls { for _, spec := range decl.(*ast.GenDecl).Specs { quoted := string(spec.(*ast.ImportSpec).Path.Value) unquoted, err := strconv.Unquote(quoted) if err != nil { - log.Crashf("%s: parser returned invalid quoted string: <%s>", filename, quoted) + log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted) + } + importsm[unquoted] = true + if unquoted == "C" { + cgoFiles = append(cgoFiles, d.Name) } - imports[unquoted] = filename } } } - return files, imports, pkgName, nil + imports := make([]string, len(importsm)) + i := 0 + for p := range importsm { + imports[i] = p + i++ + } + return &dirInfo{goFiles, cgoFiles, cFiles, imports, pkgName}, nil } diff --git a/src/cmd/gomake/doc.go b/src/cmd/gomake/doc.go new file mode 100644 index 000000000..2f35fd9dd --- /dev/null +++ b/src/cmd/gomake/doc.go @@ -0,0 +1,36 @@ +// 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 index c3c136f42..859809562 100644 --- a/src/cmd/gopack/Makefile +++ b/src/cmd/gopack/Makefile @@ -2,17 +2,14 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) TARG=gopack OFILES=\ ar.$O\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lmach -lbio -l9 +LIB=\ + ../../../lib/libmach.a\ -clean: - rm -f *.$O $(TARG) - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +include ../../Make.ccmd diff --git a/src/cmd/gopack/ar.c b/src/cmd/gopack/ar.c index 377366ec4..063967bd7 100644 --- a/src/cmd/gopack/ar.c +++ b/src/cmd/gopack/ar.c @@ -593,25 +593,43 @@ scanobj(Biobuf *b, Arfile *ap, long size) vlong offset; Dir *d; static int lastobj = -1; + uchar buf[4]; if (!allobj) /* non-object file encountered */ return; offset = Boffset(b); obj = objtype(b, 0); if (obj < 0) { /* not an object file */ + /* maybe a foreign object file */ + Bseek(b, offset, 0); + memset(buf, 0, sizeof buf); + Bread(b, buf, 4); + + /* maybe a foreign object file? that's okay */ + if((buf[0] == 0x7F && buf[1] == 'E' && buf[2] == 'L' && buf[3] == 'F') || // ELF + (buf[0] == 0xFE && buf[1] == 0xED && buf[2] == 0xFA && (buf[3]&~1) == 0xCE) || // Mach-O big-endian + (buf[3] == 0xFE && buf[2] == 0xED && buf[1] == 0xFA && (buf[0]&~1) == 0xCE)) { // Mach-O little-endian + Bseek(b, offset, 0); + return; + } + if (!gflag || strcmp(file, pkgdef) != 0) { /* don't clear allobj if it's pkg defs */ fprint(2, "gopack: non-object file %s\n", file); + errors++; allobj = 0; } d = dirfstat(Bfildes(b)); - if (d != nil && d->length == 0) + if (d != nil && d->length == 0) { fprint(2, "gopack: zero length file %s\n", file); + errors++; + } free(d); Bseek(b, offset, 0); return; } if (lastobj >= 0 && obj != lastobj) { fprint(2, "gopack: inconsistent object file %s\n", file); + errors++; allobj = 0; Bseek(b, offset, 0); return; @@ -619,6 +637,7 @@ scanobj(Biobuf *b, Arfile *ap, long size) lastobj = obj; if (!readar(b, obj, offset+size, 0)) { fprint(2, "gopack: invalid symbol reference in file %s\n", file); + errors++; allobj = 0; Bseek(b, offset, 0); return; @@ -718,8 +737,8 @@ foundstart: first = 1; start = end = 0; for (n=0; n sizeof(pkgbuf)-1) @@ -742,14 +761,19 @@ foundstart: safe = 0; start = Boffset(b); // after package statement first = 0; + free(line); continue; } - if(line[0] == '$' && line[1] == '$') + if(line[0] == '$' && line[1] == '$') { + free(line); goto foundend; + } end = Boffset(b); // before closing $$ + free(line); } bad: fprint(2, "gopack: bad package import section in %s\n", file); + errors++; return; foundend: @@ -795,6 +819,7 @@ objsym(Sym *s, void *p) if(s->type == 'T' && duplicate(as->name, &ofile)) { dupfound = 1; fprint(2, "duplicate text symbol: %s and %s: %s\n", as->file, ofile, as->name); + errors++; free(as->name); free(as); return; diff --git a/src/cmd/gopack/doc.go b/src/cmd/gopack/doc.go index 0d5ccdb6c..74c272fd2 100644 --- a/src/cmd/gopack/doc.go +++ b/src/cmd/gopack/doc.go @@ -4,7 +4,7 @@ /* -Gopack program is a variant of the Plan 9 ar tool. The original is documented at +Gopack is a variant of the Plan 9 ar tool. The original is documented at http://plan9.bell-labs.com/magic/man2html/1/ar diff --git a/src/cmd/gotest/Makefile b/src/cmd/gotest/Makefile index b20b1daff..74054e974 100644 --- a/src/cmd/gotest/Makefile +++ b/src/cmd/gotest/Makefile @@ -1,14 +1,18 @@ -# Copyright 2009 The Go Authors. All rights reserved. +# Copyright 2010 The Go Authors. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc -TARG=gotest +TARG=install clean: @true -install: $(TARG) - ! test -f "$(GOBIN)"/$(TARG) || chmod u+w "$(GOBIN)"/$(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +install: install-gotest install-gotry + +install-%: % + ! test -f "$(GOBIN)"/$* || chmod u+w "$(GOBIN)"/$* + sed 's`@@GOROOT@@`$(GOROOT_FINAL)`' $* >"$(GOBIN)"/$* + chmod +x "$(GOBIN)"/$* + diff --git a/src/cmd/gotest/gotest b/src/cmd/gotest/gotest index fec2b4a4a..7572610d2 100755 --- a/src/cmd/gotest/gotest +++ b/src/cmd/gotest/gotest @@ -3,10 +3,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Using all the test*.go files in the current directory, write out a file +# Using all the *_test.go files in the current directory, write out a file # _testmain.go that runs all its tests. Compile everything and run the # tests. -# If files are named on the command line, use them instead of test*.go. +# If files are named on the command line, use them instead of *_test.go. # Makes egrep,grep work better in general if we put them # in ordinary C mode instead of what the current language is. @@ -14,16 +14,15 @@ unset LANG export LC_ALL=C export LC_CTYPE=C -GOBIN="${GOBIN:-$HOME/bin}" - -_GC=$GC # Make.$GOARCH will overwrite this +_GC=$GC # Make.inc will overwrite this if [ ! -f [Mm]akefile ]; then echo 'please create a Makefile for gotest; see http://golang.org/doc/code.html for details' 1>&2 exit 2 fi -. "$GOROOT"/src/Make.$GOARCH +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 @@ -31,18 +30,12 @@ fi E="$GORUN" -# TODO(kaib): proper emulator strategy -case x"$GOARCH" in -xarm) - export E=${GORUN:-qemu-arm -cpu cortex-a8} -esac - # Allow overrides -GC="$GOBIN/${_GC:-$GC} -I _test" -GL="$GOBIN/${GL:-$LD} -L _test" -AS="$GOBIN/$AS" -CC="$GOBIN/$CC" -LD="$GOBIN/$LD" +GC="${_GC:-$GC} -I _test" +GL="${GL:-$LD} -L _test" +AS="$AS" +CC="$CC" +LD="$LD" export GC GL O AS CC LD gofiles="" @@ -93,14 +86,13 @@ fi set -e -"$GOBIN"/gomake testpackage-clean -"$GOBIN"/gomake testpackage "GOTESTFILES=$gofiles" +gomake testpackage-clean +gomake testpackage "GOTESTFILES=$gofiles" if $havex; then $GC -o $xofile $xgofiles fi # They all compile; now generate the code to call them. -trap "rm -f _testmain.go _testmain.$O" 0 1 2 3 14 15 # Suppress output to stdout on Linux MAKEFLAGS= @@ -116,18 +108,18 @@ nmgrep() { # Figure out pkg. case "$i" in *.a) - pkg=$("$GOBIN"/gopack p $i __.PKGDEF | sed -n 's/^package //p' | sed 's/ .*//' | sed 1q) + pkg=$(gopack p $i __.PKGDEF | sed -n 's/^package //p' | sed 's/ .*//' | sed 1q) ;; *) pkg=$(sed -n 's/^ .* in package "\(.*\)".*/\1/p' $i | sed 1q) ;; esac - "$GOBIN"/6nm -s "$i" | egrep ' T .*\.'"$pat"'$' | + 6nm -s "$i" | egrep ' T .*\.'"$pat"'$' | sed 's/.* //; /\..*\./d; s/""\./'"$pkg"'./g' done } -importpath=$("$GOBIN"/gomake -s importpath) +importpath=$(gomake -s importpath) { # test functions are named TestFoo # the grep -v eliminates methods and other special names @@ -155,30 +147,43 @@ importpath=$("$GOBIN"/gomake -s importpath) echo 'import "./_xtest_"' fi echo 'import "testing"' + echo 'import __regexp__ "regexp"' # rename in case tested package is called regexp # test array echo - echo 'var tests = []testing.Test {' + echo 'var tests = []testing.InternalTest{' for i in $tests do - echo ' testing.Test{ "'$i'", '$i' },' + echo ' {"'$i'", '$i'},' done echo '}' # benchmark array - echo 'var benchmarks = []testing.Benchmark {' - for i in $benchmarks - do - echo ' testing.Benchmark{ "'$i'", '$i' },' - done - echo '}' - + if [ "$benchmarks" = "" ] + then + # keep the empty array gofmt-safe. + # (not an issue for the test array, which is never empty.) + echo 'var benchmarks = []testing.InternalBenchmark{}' + else + echo 'var benchmarks = []testing.InternalBenchmark{' + for i in $benchmarks + do + echo ' {"'$i'", '$i'},' + done + echo '}' + fi # body echo echo 'func main() {' - echo ' testing.Main(tests);' - echo ' testing.RunBenchmarks(benchmarks)' + echo ' testing.Main(__regexp__.MatchString, tests)' + echo ' testing.RunBenchmarks(__regexp__.MatchString, benchmarks)' echo '}' }>_testmain.go $GC _testmain.go $GL _testmain.$O + +# Set dynamic linker library path, no matter what it's called, +# to include the current directory while running $O.out, +# so that cgo libraries can be tested without installation. +LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH \ +DYLD_LIBRARY_PATH=.:$DYLD_LIBRARY_PATH \ $E ./$O.out "$@" diff --git a/src/cmd/gotest/gotry b/src/cmd/gotest/gotry new file mode 100755 index 000000000..52c5d2d58 --- /dev/null +++ b/src/cmd/gotest/gotry @@ -0,0 +1,167 @@ +#!/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 +rm -f /tmp/$USER.try.go +( +cat <<'!' +package main + +import ( + "os" + "try" +! + +if [ "$pkg" != "" ] +then + echo "$T" . '"'$importdir'"' +fi + +cat </tmp/$USER.try.go + +$GC -o /tmp/$USER.try.$O /tmp/$USER.try.go && +$GL -o /tmp/$USER.try /tmp/$USER.try.$O && +/tmp/$USER.try "_$@" +rm -f /tmp/$USER.try /tmp/$USER.try.go /tmp/$USER.try.$O diff --git a/src/cmd/govet/Makefile b/src/cmd/govet/Makefile new file mode 100644 index 000000000..291b27197 --- /dev/null +++ b/src/cmd/govet/Makefile @@ -0,0 +1,11 @@ +# 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 diff --git a/src/cmd/govet/doc.go b/src/cmd/govet/doc.go new file mode 100644 index 000000000..5a2489fca --- /dev/null +++ b/src/cmd/govet/doc.go @@ -0,0 +1,38 @@ +// 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. + +/* + +Govet does simple checking of Go source code. + +It checks for simple errors in calls to functions named + Print Printf Println + Fprint Fprintf Fprintln + Sprint Sprintf Sprintln + Error Errorf + Fatal Fatalf +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 +complains about arguments that look like format descriptor strings. + +Usage: + + govet [flag] [file.go ...] + govet [flag] [directory ...] # Scan all .go files under directory, recursively + +The flags are: + -v + Verbose mode + -printfuncs + A comma-separated list of print-like functions to supplement + the standard list. Each entry is in the form Name:N where N + is the zero-based argument position of the first argument + involved in the print: either the format or the first print + argument for non-formatted prints. For example, + if you have Warn and Warnf functions that take an + io.Writer as their first argument, like Fprintf, + -printfuncs=Warn:1,Warnf:1 + +*/ +package documentation diff --git a/src/cmd/govet/govet.go b/src/cmd/govet/govet.go new file mode 100644 index 000000000..2981891eb --- /dev/null +++ b/src/cmd/govet/govet.go @@ -0,0 +1,325 @@ +// 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. + +// Govet is a simple checker for static errors in Go source code. +// See doc.go for more information. +package main + +import ( + "bytes" + "flag" + "fmt" + "io" + "go/ast" + "go/parser" + "go/token" + "os" + "path" + "strconv" + "strings" +) + +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, ",", -1) { + 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 { + error(`illegal format for "Func:N" argument %q; %s`, name, err) + } + name = name[:colon] + } + 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 { + error("%s: %s", name, err) + return + } + file := &File{fs.File(parsedFile.Pos())} + file.checkFile(name, parsedFile) +} + +// Visitor for path.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 { + error("walk error: %s", e) + } + done <- true + }() + path.Walk(root, V{}, errors) + close(errors) + <-done +} + +// error formats the error to standard error, adding program +// identification and a newline +func error(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 { + // TODO: could return nil for nodes that cannot contain a CallExpr - + // will shortcut traversal. Worthwhile? + switch n := node.(type) { + case *ast.CallExpr: + f.checkCallExpr(n) + } + return f +} + + +// 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. +var printfList = map[string]int{ + "Errorf": 0, + "Fatalf": 0, + "Fprintf": 1, + "Printf": 0, + "Sprintf": 0, +} + +// printList records the unformatted-print functions. The value is the location +// of the first parameter to be printed. +var printList = map[string]int{ + "Error": 0, + "Fatal": 0, + "Fprint": 1, "Fprintln": 1, + "Print": 0, "Println": 0, + "Sprint": 0, "Sprintln": 0, +} + +// checkCall triggers the print-specific checks if the call invokes a print function. +func (f *File) checkCall(call *ast.CallExpr, name string) { + if skip, ok := printfList[name]; ok { + f.checkPrintf(call, name, skip) + return + } + if skip, ok := printList[name]; ok { + f.checkPrint(call, name, skip) + return + } +} + +// checkPrintf checks a call to a formatted print routine such as Printf. +// The skip argument records how many arguments to ignore; that is, +// call.Args[skip] is (well, should be) the format argument. +func (f *File) checkPrintf(call *ast.CallExpr, name string, skip int) { + if len(call.Args) <= skip { + return + } + // Common case: literal is first argument. + arg := call.Args[skip] + lit, ok := arg.(*ast.BasicLit) + if !ok { + // Too hard to check. + if *verbose { + f.Warn(call.Pos(), "can't check args for call to", name) + } + return + } + if lit.Kind == token.STRING { + if bytes.IndexByte(lit.Value, '%') < 0 { + if len(call.Args) > skip+1 { + f.Badf(call.Pos(), "no formatting directive in %s call", name) + } + return + } + } + // Hard part: check formats against args. + // Trivial but useful test: count. + numPercent := 0 + for i := 0; i < len(lit.Value); i++ { + if lit.Value[i] == '%' { + if i+1 < len(lit.Value) && lit.Value[i+1] == '%' { + // %% doesn't count. + i++ + } else { + numPercent++ + } + } + } + expect := len(call.Args) - (skip + 1) + if numPercent != expect { + f.Badf(call.Pos(), "wrong number of formatting directives in %s call: %d percent(s) for %d args", name, numPercent, expect) + } +} + +var terminalNewline = []byte(`\n"`) // \n at end of interpreted string + +// 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") + args := call.Args + if len(args) <= skip { + if *verbose && !isLn { + f.Badf(call.Pos(), "no args in %s call", name) + } + return + } + arg := args[skip] + if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { + if bytes.IndexByte(lit.Value, '%') >= 0 { + f.Badf(call.Pos(), "possible formatting directive in %s call", name) + } + } + if isLn { + // The last item, if a string, should not have a newline. + arg = args[len(call.Args)-1] + if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { + if bytes.HasSuffix(lit.Value, terminalNewline) { + f.Badf(call.Pos(), "%s call ends with newline", name) + } + } + } +} + +// This function never executes, but it serves as a simple test for the program. +// Test with govet -printfuncs="Bad:1,Badf:1,Warn:1,Warnf:1" govet.go +func BadFunctionUsedInTests() { + fmt.Println() // niladic call + fmt.Println("%s", "hi") // % in call to Println + fmt.Printf("%s", "hi", 3) // wrong # percents + fmt.Printf("%s%%%d", "hi", 3) // right # percents + Printf("now is the time", "buddy") // no %s + f := new(File) + f.Warn(0, "%s", "hello", 3) // % in call to added function + f.Warnf(0, "%s", "hello", 3) // wrong # %s in call to added function +} diff --git a/src/cmd/goyacc/Makefile b/src/cmd/goyacc/Makefile index 77ac918bc..54b8f3360 100644 --- a/src/cmd/goyacc/Makefile +++ b/src/cmd/goyacc/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=goyacc GOFILES=\ diff --git a/src/cmd/goyacc/goyacc.go b/src/cmd/goyacc/goyacc.go index a5da5f0a1..c9fa6bfb9 100644 --- a/src/cmd/goyacc/goyacc.go +++ b/src/cmd/goyacc/goyacc.go @@ -299,17 +299,17 @@ type Resrv struct { } var resrv = []Resrv{ - Resrv{"binary", BINARY}, - Resrv{"left", LEFT}, - Resrv{"nonassoc", BINARY}, - Resrv{"prec", PREC}, - Resrv{"right", RIGHT}, - Resrv{"start", START}, - Resrv{"term", TERM}, - Resrv{"token", TERM}, - Resrv{"type", TYPEDEF}, - Resrv{"union", UNION}, - Resrv{"struct", UNION}, + {"binary", BINARY}, + {"left", LEFT}, + {"nonassoc", BINARY}, + {"prec", PREC}, + {"right", RIGHT}, + {"start", START}, + {"term", TERM}, + {"token", TERM}, + {"type", TYPEDEF}, + {"union", UNION}, + {"struct", UNION}, } var zznewstate = 0 @@ -1032,7 +1032,7 @@ func chfind(t int, s string) int { func cpyunion() { if !lflag { - fmt.Fprintf(ftable, "\n//line %v %v\n", lineno, infile) + fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno) } fmt.Fprintf(ftable, "type\tyySymType\tstruct") @@ -1075,7 +1075,7 @@ func cpycode() { lineno++ } if !lflag { - fmt.Fprintf(ftable, "\n//line %v %v\n", lineno, infile) + fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno) } for c != EOF { if c == '%' { @@ -1158,7 +1158,7 @@ func dumpprod(curprod []int, max int) { func cpyact(curprod []int, max int) { if !lflag { - fmt.Fprintf(fcode, "\n//line %v %v\n", lineno, infile) + fmt.Fprintf(fcode, "\n//line %v:%v\n", infile, lineno) } lno := lineno @@ -2066,6 +2066,7 @@ nextk: func output() { var c, u, v int + fmt.Fprintf(ftable, "\n//line yacctab:1\n") fmt.Fprintf(ftable, "var\tyyExca = []int {\n") noset := mkset() @@ -2825,8 +2826,10 @@ func others() { c = getrune(finput) } - parts := strings.Split(yaccpar, "yyrun()", 2) // copy yaccpar + fmt.Fprintf(ftable, "\n//line yaccpar:1\n") + + parts := strings.Split(yaccpar, "yyrun()", 2) fmt.Fprintf(ftable, "%v", parts[0]) ftable.Write(fcode.Bytes()) fmt.Fprintf(ftable, "%v", parts[1]) @@ -3035,7 +3038,7 @@ func open(s string) *bufio.Reader { return bufio.NewReader(fi) } -func create(s string, m int) *bufio.Writer { +func create(s string, m uint32) *bufio.Writer { fo, err := os.Open(s, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, m) if err != nil { error("error opening %v: %v", s, err) @@ -3049,7 +3052,7 @@ func create(s string, m int) *bufio.Writer { // func error(s string, v ...interface{}) { nerrors++ - fmt.Fprintf(stderr, s, v) + fmt.Fprintf(stderr, s, v...) fmt.Fprintf(stderr, ": %v:%v\n", infile, lineno) if fatfl != 0 { summary() @@ -3136,7 +3139,7 @@ out: c = yyTok2[1] /* unknown char */ } if yyDebug >= 3 { - fmt.Printf("lex %.4x %s\n", uint(yychar), yyTokname(c)) + fmt.Printf("lex %U %s\n", uint(yychar), yyTokname(c)) } return c } @@ -3241,7 +3244,7 @@ yydefault: Errflag = 3 /* find a state where "error" is a legal shift action */ - for yyp >= len(YYS) { + for yyp >= 0 { yyn = yyPact[YYS[yyp].yys] + yyErrCode if yyn >= 0 && yyn < yyLast { yystate = yyAct[yyn] /* simulate a shift of "error" */ diff --git a/src/cmd/goyacc/units.y b/src/cmd/goyacc/units.y index bd5517e8b..a7d472fc6 100644 --- a/src/cmd/goyacc/units.y +++ b/src/cmd/goyacc/units.y @@ -215,7 +215,7 @@ expr0: type UnitsLex int -func (_ UnitsLex) Lex(yylval *yySymType) int { +func (UnitsLex) Lex(yylval *yySymType) int { var c, i int c = peekrune @@ -280,7 +280,7 @@ numb: return VAL } -func (_ UnitsLex) Error(s string) { +func (UnitsLex) Error(s string) { Error("syntax error, last name: %v", sym) } @@ -298,7 +298,7 @@ func main() { f, err := os.Open(file, os.O_RDONLY, 0) if err != nil { - fmt.Printf("error opening %v: %v\n", file, err) + fmt.Fprintf(os.Stderr, "error opening %v: %v\n", file, err) os.Exit(1) } fi = bufio.NewReader(f) @@ -390,7 +390,7 @@ func rdigit(c int) bool { func Error(s string, v ...interface{}) { fmt.Printf("%v: %v\n\t", lineno, line) - fmt.Printf(s, v) + fmt.Printf(s, v...) fmt.Printf("\n") nerrors++ diff --git a/src/cmd/hgpatch/Makefile b/src/cmd/hgpatch/Makefile index f7d64bc12..1ef98d7f9 100644 --- a/src/cmd/hgpatch/Makefile +++ b/src/cmd/hgpatch/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=hgpatch GOFILES=\ diff --git a/src/cmd/hgpatch/main.go b/src/cmd/hgpatch/main.go index f6ea36da8..bd4b563f9 100644 --- a/src/cmd/hgpatch/main.go +++ b/src/cmd/hgpatch/main.go @@ -153,7 +153,7 @@ func main() { changed[o.Dst] = 1 } if o.Mode != 0 { - chk(os.Chmod(o.Dst, o.Mode&0755)) + chk(os.Chmod(o.Dst, uint32(o.Mode&0755))) undoRevert(o.Dst) changed[o.Dst] = 1 } @@ -192,7 +192,7 @@ func makeParent(name string) { // Copy of os.MkdirAll but adds to undo log after // creating a directory. -func mkdirAll(path string, perm int) os.Error { +func mkdirAll(path string, perm uint32) os.Error { dir, err := os.Lstat(path) if err == nil { if dir.IsDirectory() { @@ -318,11 +318,9 @@ func hgRename(dst, src string) os.Error { return err } -func copy(a []string) []string { +func dup(a []string) []string { b := make([]string, len(a)) - for i, s := range a { - b[i] = s - } + copy(b, a) return b } @@ -379,7 +377,7 @@ func run(argv []string, input []byte) (out string, err os.Error) { return Error: - err = &runError{copy(argv), err} + err = &runError{dup(argv), err} return } diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c new file mode 100644 index 000000000..210f10ab5 --- /dev/null +++ b/src/cmd/ld/data.c @@ -0,0 +1,914 @@ +// Inferno utils/8l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/8l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Data layout and relocation. + +#include "l.h" +#include "../ld/lib.h" +#include "../ld/elf.h" +#include "../ld/pe.h" + +void dynreloc(void); + +/* + * divide-and-conquer list-link + * sort of Sym* structures. + * Used for the data block. + */ +int +datcmp(Sym *s1, Sym *s2) +{ + if(s1->type != s2->type) + return (int)s1->type - (int)s2->type; + if(s1->size != s2->size) { + if(s1->size < s2->size) + return -1; + return +1; + } + return strcmp(s1->name, s2->name); +} + +Sym* +datsort(Sym *l) +{ + Sym *l1, *l2, *le; + + if(l == 0 || l->next == 0) + return l; + + l1 = l; + l2 = l; + for(;;) { + l2 = l2->next; + if(l2 == 0) + break; + l2 = l2->next; + if(l2 == 0) + break; + l1 = l1->next; + } + + l2 = l1->next; + l1->next = 0; + l1 = datsort(l); + l2 = datsort(l2); + + /* set up lead element */ + if(datcmp(l1, l2) < 0) { + l = l1; + l1 = l1->next; + } else { + l = l2; + l2 = l2->next; + } + le = l; + + for(;;) { + if(l1 == 0) { + while(l2) { + le->next = l2; + le = l2; + l2 = l2->next; + } + le->next = 0; + break; + } + if(l2 == 0) { + while(l1) { + le->next = l1; + le = l1; + l1 = l1->next; + } + break; + } + if(datcmp(l1, l2) < 0) { + le->next = l1; + le = l1; + l1 = l1->next; + } else { + le->next = l2; + le = l2; + l2 = l2->next; + } + } + le->next = 0; + return l; +} + +Reloc* +addrel(Sym *s) +{ + if(s->nr >= s->maxr) { + if(s->maxr == 0) + s->maxr = 4; + else + s->maxr <<= 1; + s->r = realloc(s->r, s->maxr*sizeof s->r[0]); + if(s->r == 0) { + diag("out of memory"); + errorexit(); + } + memset(s->r+s->nr, 0, (s->maxr-s->nr)*sizeof s->r[0]); + } + return &s->r[s->nr++]; +} + +void +relocsym(Sym *s) +{ + Reloc *r; + Prog p; + int32 i, off, siz, fl; + vlong o; + uchar *cast; + + cursym = s; + memset(&p, 0, sizeof p); + for(r=s->r; rr+s->nr; r++) { + off = r->off; + siz = r->siz; + if(off < 0 || off+(siz&~Rbig) > s->np) { + diag("%s: invalid relocation %d+%d not in [%d,%d)", s->name, off, siz&~Rbig, 0, s->np); + continue; + } + if(r->sym != S && (r->sym->type == 0 || r->sym->type == SXREF)) { + diag("%s: not defined", r->sym->name); + continue; + } + if(r->type >= 256) + continue; + + if(r->sym != S && r->sym->type == SDYNIMPORT) + diag("unhandled relocation for %s (type %d rtype %d)", r->sym->name, r->sym->type, r->type); + + if(r->sym != S && !r->sym->reachable) + diag("unreachable sym in relocation: %s %s", s->name, r->sym->name); + + switch(r->type) { + default: + o = 0; + if(archreloc(r, s, &o) < 0) + diag("unknown reloc %d", r->type); + break; + case D_ADDR: + o = symaddr(r->sym) + r->add; + break; + case D_PCREL: + o = symaddr(r->sym) + r->add - (s->value + r->off + r->siz); + break; + case D_SIZE: + o = r->sym->size + r->add; + break; + } +//print("relocate %s %p %s => %p %p %p %p [%p]\n", s->name, s->value+off, r->sym ? r->sym->name : "", (void*)symaddr(r->sym), (void*)s->value, (void*)r->off, (void*)r->siz, (void*)o); + switch(siz) { + default: + cursym = s; + diag("bad reloc size %#ux for %s", siz, r->sym->name); + case 4 + Rbig: + fl = o; + s->p[off] = fl>>24; + s->p[off+1] = fl>>16; + s->p[off+2] = fl>>8; + s->p[off+3] = fl; + break; + case 4 + Rlittle: + fl = o; + s->p[off] = fl; + s->p[off+1] = fl>>8; + s->p[off+2] = fl>>16; + s->p[off+3] = fl>>24; + break; + case 4: + fl = o; + cast = (uchar*)&fl; + for(i=0; i<4; i++) + s->p[off+i] = cast[inuxi4[i]]; + break; + case 8: + cast = (uchar*)&o; + for(i=0; i<8; i++) + s->p[off+i] = cast[inuxi8[i]]; + break; + } + } +} + +void +reloc(void) +{ + Sym *s; + + if(debug['v']) + Bprint(&bso, "%5.2f reloc\n", cputime()); + Bflush(&bso); + + for(s=textp; s!=S; s=s->next) + relocsym(s); + for(s=datap; s!=S; s=s->next) + relocsym(s); +} + +void +dynrelocsym(Sym *s) +{ + Reloc *r; + + for(r=s->r; rr+s->nr; r++) + if(r->sym->type == SDYNIMPORT || r->type >= 256) + adddynrel(s, r); +} + +void +dynreloc(void) +{ + Sym *s; + + if(debug['v']) + Bprint(&bso, "%5.2f reloc\n", cputime()); + Bflush(&bso); + + for(s=textp; s!=S; s=s->next) + dynrelocsym(s); + for(s=datap; s!=S; s=s->next) + dynrelocsym(s); + if(iself) + elfdynhash(); +} + +void +symgrow(Sym *s, int32 siz) +{ + if(s->np >= siz) + return; + + if(s->maxp < siz) { + if(s->maxp == 0) + s->maxp = 8; + while(s->maxp < siz) + s->maxp <<= 1; + s->p = realloc(s->p, s->maxp); + if(s->p == nil) { + diag("out of memory"); + errorexit(); + } + memset(s->p+s->np, 0, s->maxp-s->np); + } + s->np = siz; +} + +void +savedata(Sym *s, Prog *p) +{ + int32 off, siz, i, fl; + uchar *cast; + vlong o; + Reloc *r; + + off = p->from.offset; + siz = p->datasize; + symgrow(s, off+siz); + + switch(p->to.type) { + default: + diag("bad data: %P", p); + break; + + case D_FCONST: + switch(siz) { + default: + case 4: + fl = ieeedtof(&p->to.ieee); + cast = (uchar*)&fl; + for(i=0; i<4; i++) + s->p[off+i] = cast[fnuxi4[i]]; + break; + case 8: + cast = (uchar*)&p->to.ieee; + for(i=0; i<8; i++) + s->p[off+i] = cast[fnuxi8[i]]; + break; + } + break; + + case D_SCONST: + for(i=0; ip[off+i] = p->to.scon[i]; + break; + + case D_CONST: + if(p->to.sym) + goto Addr; + o = p->to.offset; + fl = o; + cast = (uchar*)&fl; + switch(siz) { + default: + diag("bad nuxi %d\n%P", siz, p); + break; + case 1: + s->p[off] = cast[inuxi1[0]]; + break; + case 2: + for(i=0; i<2; i++) + s->p[off+i] = cast[inuxi2[i]]; + break; + case 4: + for(i=0; i<4; i++) + s->p[off+i] = cast[inuxi4[i]]; + break; + case 8: + cast = (uchar*)&o; + for(i=0; i<8; i++) + s->p[off+i] = cast[inuxi8[i]]; + break; + } + break; + + case D_ADDR: + case D_SIZE: + Addr: + r = addrel(s); + r->off = off; + r->siz = siz; + r->sym = p->to.sym; + r->type = p->to.type; + if(r->type != D_SIZE) + r->type = D_ADDR; + r->add = p->to.offset; + break; + } +} + +static void +blk(Sym *allsym, int32 addr, int32 size) +{ + Sym *sym; + int32 eaddr; + uchar *p, *ep; + + for(sym = allsym; sym != nil; sym = sym->next) + if(!(sym->type&SSUB) && sym->value >= addr) + break; + + eaddr = addr+size; + for(; sym != nil; sym = sym->next) { + if(sym->type&SSUB) + continue; + if(sym->value >= eaddr) + break; + if(sym->value < addr) { + diag("phase error: addr=%#llx but sym=%#llx type=%d", (vlong)addr, (vlong)sym->value, sym->type); + errorexit(); + } + cursym = sym; + for(; addr < sym->value; addr++) + cput(0); + p = sym->p; + ep = p + sym->np; + while(p < ep) + cput(*p++); + addr += sym->np; + for(; addr < sym->value+sym->size; addr++) + cput(0); + if(addr != sym->value+sym->size) { + diag("phase error: addr=%#llx value+size=%#llx", (vlong)addr, (vlong)sym->value+sym->size); + errorexit(); + } + } + + for(; addr < eaddr; addr++) + cput(0); + cflush(); +} + +void +codeblk(int32 addr, int32 size) +{ + Sym *sym; + int32 eaddr, n, epc; + Prog *p; + uchar *q; + + if(debug['a']) + Bprint(&bso, "codeblk [%#x,%#x) at offset %#llx\n", addr, addr+size, seek(cout, 0, 1)); + + blk(textp, addr, size); + + /* again for printing */ + if(!debug['a']) + return; + + for(sym = textp; sym != nil; sym = sym->next) { + if(!sym->reachable) + continue; + if(sym->value >= addr) + break; + } + + eaddr = addr + size; + for(; sym != nil; sym = sym->next) { + if(!sym->reachable) + continue; + if(sym->value >= eaddr) + break; + + if(addr < sym->value) { + Bprint(&bso, "%-20s %.8llux|", "_", (vlong)addr); + for(; addr < sym->value; addr++) + Bprint(&bso, " %.2ux", 0); + Bprint(&bso, "\n"); + } + p = sym->text; + if(p == nil) { + Bprint(&bso, "%.6llux\t%-20s | foreign text\n", (vlong)addr, sym->name); + n = sym->size; + q = sym->p; + + while(n >= 16) { + Bprint(&bso, "%.6ux\t%-20.16I\n", addr, q); + addr += 16; + q += 16; + n -= 16; + } + if(n > 0) + Bprint(&bso, "%.6ux\t%-20.*I\n", addr, n, q); + addr += n; + continue; + } + + Bprint(&bso, "%.6llux\t%-20s | %P\n", (vlong)sym->value, sym->name, p); + for(p = p->link; p != P; p = p->link) { + if(p->link != P) + epc = p->link->pc; + else + epc = sym->value + sym->size; + Bprint(&bso, "%.6ux\t", p->pc); + q = sym->p + p->pc - sym->value; + n = epc - p->pc; + Bprint(&bso, "%-20.*I | %P\n", n, q, p); + addr += n; + } + } + + if(addr < eaddr) { + Bprint(&bso, "%-20s %.8llux|", "_", (vlong)addr); + for(; addr < eaddr; addr++) + Bprint(&bso, " %.2ux", 0); + } + Bflush(&bso); +} + +void +datblk(int32 addr, int32 size) +{ + Sym *sym; + int32 eaddr; + uchar *p, *ep; + + if(debug['a']) + Bprint(&bso, "datblk [%#x,%#x) at offset %#llx\n", addr, addr+size, seek(cout, 0, 1)); + + blk(datap, addr, size); + + /* again for printing */ + if(!debug['a']) + return; + + for(sym = datap; sym != nil; sym = sym->next) + if(sym->value >= addr) + break; + + eaddr = addr + size; + for(; sym != nil; sym = sym->next) { + if(sym->value >= eaddr) + break; + if(addr < sym->value) { + Bprint(&bso, "%-20s %.8ux| 00 ...\n", "(pre-pad)", addr); + addr = sym->value; + } + Bprint(&bso, "%-20s %.8ux|", sym->name, addr); + p = sym->p; + ep = p + sym->np; + while(p < ep) + Bprint(&bso, " %.2ux", *p++); + addr += sym->np; + for(; addr < sym->value+sym->size; addr++) + Bprint(&bso, " %.2ux", 0); + Bprint(&bso, "\n"); + } + + if(addr < eaddr) + Bprint(&bso, "%-20s %.8ux| 00 ...\n", "(post-pad)", addr); + Bprint(&bso, "%-20s %.8ux|\n", "", eaddr); +} + +void +strnput(char *s, int n) +{ + for(; *s && n > 0; s++) { + cput(*s); + n--; + } + while(n > 0) { + cput(0); + n--; + } +} + +vlong +addstring(Sym *s, char *str) +{ + int n; + int32 r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + r = s->size; + n = strlen(str)+1; + if(strcmp(s->name, ".shstrtab") == 0) + elfsetstring(str, r); + symgrow(s, r+n); + memmove(s->p+r, str, n); + s->size += n; + return r; +} + +vlong +adduintxx(Sym *s, uint64 v, int wid) +{ + int32 i, r, fl; + vlong o; + uchar *cast; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + r = s->size; + s->size += wid; + symgrow(s, s->size); + assert(r+wid <= s->size); + fl = v; + cast = (uchar*)&fl; + switch(wid) { + case 1: + s->p[r] = cast[inuxi1[0]]; + break; + case 2: + for(i=0; i<2; i++) + s->p[r+i] = cast[inuxi2[i]]; + break; + case 4: + for(i=0; i<4; i++) + s->p[r+i] = cast[inuxi4[i]]; + break; + case 8: + o = v; + cast = (uchar*)&o; + for(i=0; i<8; i++) + s->p[r+i] = cast[inuxi8[i]]; + break; + } + return r; +} + +vlong +adduint8(Sym *s, uint8 v) +{ + return adduintxx(s, v, 1); +} + +vlong +adduint16(Sym *s, uint16 v) +{ + return adduintxx(s, v, 2); +} + +vlong +adduint32(Sym *s, uint32 v) +{ + return adduintxx(s, v, 4); +} + +vlong +adduint64(Sym *s, uint64 v) +{ + return adduintxx(s, v, 8); +} + +vlong +addaddrplus(Sym *s, Sym *t, int32 add) +{ + vlong i; + Reloc *r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + i = s->size; + s->size += PtrSize; + symgrow(s, s->size); + r = addrel(s); + r->sym = t; + r->off = i; + r->siz = PtrSize; + r->type = D_ADDR; + r->add = add; + return i; +} + +vlong +addpcrelplus(Sym *s, Sym *t, int32 add) +{ + vlong i; + Reloc *r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + i = s->size; + s->size += 4; + symgrow(s, s->size); + r = addrel(s); + r->sym = t; + r->off = i; + r->add = add; + r->type = D_PCREL; + r->siz = 4; + return i; +} + +vlong +addaddr(Sym *s, Sym *t) +{ + return addaddrplus(s, t, 0); +} + +vlong +addsize(Sym *s, Sym *t) +{ + vlong i; + Reloc *r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + i = s->size; + s->size += PtrSize; + symgrow(s, s->size); + r = addrel(s); + r->sym = t; + r->off = i; + r->siz = PtrSize; + r->type = D_SIZE; + return i; +} + +void +dodata(void) +{ + int32 h, t, datsize; + Section *sect; + Sym *s, *last, **l; + + if(debug['v']) + Bprint(&bso, "%5.2f dodata\n", cputime()); + Bflush(&bso); + + last = nil; + datap = nil; + for(h=0; hhash){ + if(!s->reachable || s->special) + continue; + if(STEXT < s->type && s->type < SXREF) { + if(last == nil) + datap = s; + else + last->next = s; + s->next = nil; + last = s; + } + } + } + + for(s = datap; s != nil; s = s->next) { + if(s->np > 0 && s->type == SBSS) // TODO: necessary? + 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 + * dynamic relocations. these will grow the relocation + * symbol, which is itself data. + */ + dynreloc(); + + /* some symbols may no longer belong in datap (Mach-O) */ + for(l=&datap; (s=*l) != nil; ) { + if(s->type <= STEXT || SXREF <= s->type) + *l = s->next; + else + l = &s->next; + } + *l = nil; + + datap = datsort(datap); + + /* + * allocate data sections. list is sorted by type, + * so we can just walk it for each piece we want to emit. + */ + + /* read-only data */ + sect = addsection(&segtext, ".rodata", 06); + sect->vaddr = 0; + datsize = 0; + s = datap; + for(; s != nil && s->type < SDATA; s = s->next) { + s->type = SRODATA; + t = rnd(s->size, 4); + s->size = t; + s->value = datsize; + datsize += t; + } + sect->len = datsize - sect->vaddr; + + /* data */ + datsize = 0; + sect = addsection(&segdata, ".data", 06); + sect->vaddr = 0; + for(; s != nil && s->type < SBSS; s = s->next) { + s->type = SDATA; + t = s->size; + if(t == 0 && s->name[0] != '.') { + diag("%s: no size", s->name); + t = 1; + } + if(t & 1) + ; + else if(t & 2) + datsize = rnd(datsize, 2); + else if(t & 4) + datsize = rnd(datsize, 4); + else + datsize = rnd(datsize, 8); + s->value = datsize; + datsize += t; + } + sect->len = datsize - sect->vaddr; + + /* bss */ + sect = addsection(&segdata, ".bss", 06); + sect->vaddr = datsize; + for(; s != nil; s = s->next) { + if(s->type != SBSS) { + cursym = s; + diag("unexpected symbol type %d", s->type); + } + t = s->size; + if(t & 1) + ; + else if(t & 2) + datsize = rnd(datsize, 2); + else if(t & 4) + datsize = rnd(datsize, 4); + else + datsize = rnd(datsize, 8); + s->size = t; + s->value = datsize; + datsize += t; + } + sect->len = datsize - sect->vaddr; +} + +// assign addresses to text +void +textaddress(void) +{ + uvlong va; + Prog *p; + Section *sect; + Sym *sym, *sub; + + addsection(&segtext, ".text", 05); + + // Assign PCs in text segment. + // Could parallelize, by assigning to text + // and then letting threads copy down, but probably not worth it. + sect = segtext.sect; + va = INITTEXT; + sect->vaddr = va; + for(sym = textp; sym != nil; sym = sym->next) { + if(sym->type & SSUB) + continue; + sym->value = 0; + for(sub = sym; sub != S; sub = sub->sub) { + sub->value += va; + for(p = sub->text; p != P; p = p->link) + p->pc += sub->value; + } + if(sym->size == 0 && sym->sub != S) { + cursym = sym; + } + va += sym->size; + } + sect->len = va - sect->vaddr; +} + +// assign addresses +void +address(void) +{ + Section *s, *text, *data, *rodata, *bss; + Sym *sym, *sub; + uvlong va; + + va = INITTEXT; + segtext.rwx = 05; + segtext.vaddr = va; + segtext.fileoff = HEADR; + for(s=segtext.sect; s != nil; s=s->next) { + s->vaddr = va; + va += s->len; + segtext.len = va - INITTEXT; + va = rnd(va, INITRND); + } + segtext.filelen = segtext.len; + + segdata.rwx = 06; + segdata.vaddr = va; + segdata.fileoff = va - segtext.vaddr + segtext.fileoff; + if(thechar == '8' && HEADTYPE == 10) // Windows PE + segdata.fileoff = segtext.fileoff + rnd(segtext.len, PEFILEALIGN); + if(thechar == '8' && HEADTYPE == 2) { // Plan 9 + segdata.vaddr = va = rnd(va, 4096); + segdata.fileoff = segtext.fileoff + segtext.filelen; + } + for(s=segdata.sect; s != nil; s=s->next) { + s->vaddr = va; + va += s->len; + segdata.len = va - segdata.vaddr; + } + segdata.filelen = segdata.sect->len; // assume .data is first + + text = segtext.sect; + rodata = segtext.sect->next; + data = segdata.sect; + bss = segdata.sect->next; + + for(sym = datap; sym != nil; sym = sym->next) { + cursym = sym; + if(sym->type < SDATA) + sym->value += rodata->vaddr; + else + sym->value += data->vaddr; + for(sub = sym->sub; sub != nil; sub = sub->sub) + sub->value += sym->value; + } + + xdefine("text", STEXT, text->vaddr); + xdefine("etext", STEXT, text->vaddr + text->len); + xdefine("rodata", SRODATA, rodata->vaddr); + xdefine("erodata", SRODATA, rodata->vaddr + rodata->len); + xdefine("data", SBSS, data->vaddr); + xdefine("edata", SBSS, data->vaddr + data->len); + xdefine("end", SBSS, segdata.vaddr + segdata.len); + + sym = lookup("pclntab", 0); + xdefine("epclntab", SRODATA, sym->value + sym->size); + sym = lookup("symtab", 0); + xdefine("esymtab", SRODATA, sym->value + sym->size); +} diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c new file mode 100644 index 000000000..506c6e5db --- /dev/null +++ b/src/cmd/ld/dwarf.c @@ -0,0 +1,2547 @@ +// 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. + +// TODO/NICETOHAVE: +// - eliminate DW_CLS_ if not used +// - package info in compilation units +// - assign global variables and types to their packages +// - (upstream) type info for C parts of runtime +// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg +// ptype struct '[]uint8' and qualifiers need to be quoted away +// - lexical scoping is lost, so gdb gets confused as to which 'main.i' you mean. +// - file:line info for variables +// - make strings a typedef so prettyprinters can see the underlying string type +// +#include "l.h" +#include "lib.h" +#include "../ld/dwarf.h" +#include "../ld/dwarf_defs.h" +#include "../ld/elf.h" +#include "../ld/macho.h" + +/* + * Offsets and sizes of the debug_* sections in the cout file. + */ + +static vlong abbrevo; +static vlong abbrevsize; +static vlong lineo; +static vlong linesize; +static vlong infoo; // also the base for DWDie->offs and reference attributes. +static vlong infosize; +static vlong frameo; +static vlong framesize; +static vlong pubnameso; +static vlong pubnamessize; +static vlong pubtypeso; +static vlong pubtypessize; +static vlong arangeso; +static vlong arangessize; +static vlong gdbscripto; +static vlong gdbscriptsize; + +static char gdbscript[1024]; + +/* + * Basic I/O + */ + +static void +addrput(vlong addr) +{ + switch(PtrSize) { + case 4: + LPUT(addr); + break; + case 8: + VPUT(addr); + break; + } +} + +static int +uleb128enc(uvlong v, char* dst) +{ + uint8 c, len; + + len = 0; + do { + c = v & 0x7f; + v >>= 7; + if (v) + c |= 0x80; + if (dst) + *dst++ = c; + len++; + } while (c & 0x80); + return len; +}; + +static int +sleb128enc(vlong v, char *dst) +{ + uint8 c, s, len; + + len = 0; + do { + c = v & 0x7f; + s = v & 0x40; + v >>= 7; + if ((v != -1 || !s) && (v != 0 || s)) + c |= 0x80; + if (dst) + *dst++ = c; + len++; + } while(c & 0x80); + return len; +} + +static void +uleb128put(vlong v) +{ + char buf[10]; + strnput(buf, uleb128enc(v, buf)); +} + +static void +sleb128put(vlong v) +{ + char buf[10]; + strnput(buf, sleb128enc(v, buf)); +} + +/* + * Defining Abbrevs. This is hardcoded, and there will be + * only a handful of them. The DWARF spec places no restriction on + * the ordering of atributes in the Abbrevs and DIEs, and we will + * always write them out in the order of declaration in the abbrev. + * This implementation relies on tag, attr < 127, so they serialize as + * a char. Higher numbered user-defined tags or attributes can be used + * for storing internal data but won't be serialized. + */ +typedef struct DWAttrForm DWAttrForm; +struct DWAttrForm { + uint8 attr; + uint8 form; +}; + +// Index into the abbrevs table below. +// Keep in sync with ispubname() and ispubtype() below. +// ispubtype considers >= NULLTYPE public +enum +{ + DW_ABRV_NULL, + DW_ABRV_COMPUNIT, + DW_ABRV_FUNCTION, + DW_ABRV_VARIABLE, + DW_ABRV_AUTO, + DW_ABRV_PARAM, + DW_ABRV_STRUCTFIELD, + DW_ABRV_FUNCTYPEPARAM, + DW_ABRV_DOTDOTDOT, + DW_ABRV_ARRAYRANGE, + DW_ABRV_NULLTYPE, + DW_ABRV_BASETYPE, + DW_ABRV_ARRAYTYPE, + DW_ABRV_CHANTYPE, + DW_ABRV_FUNCTYPE, + DW_ABRV_IFACETYPE, + DW_ABRV_MAPTYPE, + DW_ABRV_PTRTYPE, + DW_ABRV_SLICETYPE, + DW_ABRV_STRINGTYPE, + DW_ABRV_STRUCTTYPE, + DW_ABRV_TYPEDECL, + DW_NABRV +}; + +typedef struct DWAbbrev DWAbbrev; +static struct DWAbbrev { + uint8 tag; + uint8 children; + DWAttrForm attr[30]; +} abbrevs[DW_NABRV] = { + /* The mandatory DW_ABRV_NULL entry. */ + { 0 }, + /* COMPUNIT */ + { + DW_TAG_compile_unit, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_language, DW_FORM_data1, + DW_AT_low_pc, DW_FORM_addr, + DW_AT_high_pc, DW_FORM_addr, + DW_AT_stmt_list, DW_FORM_data4, + 0, 0 + }, + /* FUNCTION */ + { + DW_TAG_subprogram, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_low_pc, DW_FORM_addr, + DW_AT_high_pc, DW_FORM_addr, + DW_AT_external, DW_FORM_flag, + 0, 0 + }, + /* VARIABLE */ + { + DW_TAG_variable, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_location, DW_FORM_block1, + DW_AT_type, DW_FORM_ref_addr, + DW_AT_external, DW_FORM_flag, + 0, 0 + }, + /* AUTO */ + { + DW_TAG_variable, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_location, DW_FORM_block1, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + /* PARAM */ + { + DW_TAG_formal_parameter, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_location, DW_FORM_block1, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + /* STRUCTFIELD */ + { + DW_TAG_member, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_data_member_location, DW_FORM_block1, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + /* FUNCTYPEPARAM */ + { + DW_TAG_formal_parameter, DW_CHILDREN_no, + // No name! + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* DOTDOTDOT */ + { + DW_TAG_unspecified_parameters, DW_CHILDREN_no, + 0, 0 + }, + /* ARRAYRANGE */ + { + DW_TAG_subrange_type, DW_CHILDREN_no, + // No name! + DW_AT_type, DW_FORM_ref_addr, + DW_AT_upper_bound, DW_FORM_data1, + 0, 0 + }, + + // Below here are the types considered public by ispubtype + /* NULLTYPE */ + { + DW_TAG_unspecified_type, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + 0, 0 + }, + /* BASETYPE */ + { + DW_TAG_base_type, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_encoding, DW_FORM_data1, + DW_AT_byte_size, DW_FORM_data1, + 0, 0 + }, + /* ARRAYTYPE */ + // child is subrange with upper bound + { + DW_TAG_array_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + DW_AT_byte_size, DW_FORM_udata, + 0, 0 + }, + + /* CHANTYPE */ + { + DW_TAG_typedef, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* FUNCTYPE */ + { + DW_TAG_subroutine_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, +// DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* IFACETYPE */ + { + DW_TAG_typedef, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* MAPTYPE */ + { + DW_TAG_typedef, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* PTRTYPE */ + { + DW_TAG_pointer_type, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* SLICETYPE */ + { + DW_TAG_structure_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_byte_size, DW_FORM_udata, + 0, 0 + }, + + /* STRINGTYPE */ + { + DW_TAG_structure_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_byte_size, DW_FORM_udata, + 0, 0 + }, + + /* STRUCTTYPE */ + { + DW_TAG_structure_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_byte_size, DW_FORM_udata, + 0, 0 + }, + + /* TYPEDECL */ + { + DW_TAG_typedef, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, +}; + +static void +writeabbrev(void) +{ + int i, n; + + abbrevo = cpos(); + for (i = 1; i < DW_NABRV; i++) { + // See section 7.5.3 + uleb128put(i); + uleb128put(abbrevs[i].tag); + cput(abbrevs[i].children); + // 0 is not a valid attr or form, and DWAbbrev.attr is + // 0-terminated, so we can treat it as a string + n = strlen((char*)abbrevs[i].attr) / 2; + strnput((char*)abbrevs[i].attr, + (n+1) * sizeof(DWAttrForm)); + } + cput(0); + abbrevsize = cpos() - abbrevo; +} + +/* + * Debugging Information Entries and their attributes. + */ + +enum +{ + HASHSIZE = 107 +}; + +static uint32 +hashstr(char* s) +{ + uint32 h; + + h = 0; + while (*s) + h = h+h+h + *s++; + return h % HASHSIZE; +} + +// For DW_CLS_string and _block, value should contain the length, and +// data the data, for _reference, value is 0 and data is a DWDie* to +// the referenced instance, for all others, value is the whole thing +// and data is null. + +typedef struct DWAttr DWAttr; +struct DWAttr { + DWAttr *link; + uint8 atr; // DW_AT_ + uint8 cls; // DW_CLS_ + vlong value; + char *data; +}; + +typedef struct DWDie DWDie; +struct DWDie { + int abbrev; + DWDie *link; + DWDie *child; + DWAttr *attr; + // offset into .debug_info section, i.e relative to + // infoo. only valid after call to putdie() + vlong offs; + DWDie **hash; // optional index of children by name, enabled by mkindex() + DWDie *hlink; // bucket chain in parent's index +}; + +/* + * Root DIEs for compilation units, types and global variables. + */ + +static DWDie dwroot; +static DWDie dwtypes; +static DWDie dwglobals; + +static DWAttr* +newattr(DWDie *die, uint8 attr, int cls, vlong value, char *data) +{ + DWAttr *a; + + a = mal(sizeof *a); + a->link = die->attr; + die->attr = a; + a->atr = attr; + a->cls = cls; + a->value = value; + a->data = data; + return a; +} + +// Each DIE (except the root ones) has at least 1 attribute: its +// name. getattr moves the desired one to the front so +// frequently searched ones are found faster. +static DWAttr* +getattr(DWDie *die, uint8 attr) +{ + DWAttr *a, *b; + + if (die->attr->atr == attr) + return die->attr; + + a = die->attr; + b = a->link; + while (b != nil) { + if (b->atr == attr) { + a->link = b->link; + b->link = die->attr; + die->attr = b; + return b; + } + a = b; + b = b->link; + } + return nil; +} + +static void +delattr(DWDie *die, uint8 attr) +{ + DWAttr **a; + + a = &die->attr; + while (*a != nil) + if ((*a)->atr == attr) + *a = (*a)->link; + else + a = &((*a)->link); +} + +// Every DIE has at least a DW_AT_name attribute (but it will only be +// written out if it is listed in the abbrev). If its parent is +// keeping an index, the new DIE will be inserted there. +static DWDie* +newdie(DWDie *parent, int abbrev, char *name) +{ + DWDie *die; + int h; + + die = mal(sizeof *die); + die->abbrev = abbrev; + die->link = parent->child; + parent->child = die; + + newattr(die, DW_AT_name, DW_CLS_STRING, strlen(name), name); + + if (parent->hash) { + h = hashstr(name); + die->hlink = parent->hash[h]; + parent->hash[h] = die; + } + + return die; +} + +static void +mkindex(DWDie *die) +{ + die->hash = mal(HASHSIZE * sizeof(DWDie*)); +} + +// Find child by AT_name using hashtable if available or linear scan +// if not. +static DWDie* +find(DWDie *die, char* name) +{ + DWDie *a, *b; + int h; + + if (die->hash == nil) { + for (a = die->child; a != nil; a = a->link) + if (strcmp(name, getattr(a, DW_AT_name)->data) == 0) + return a; + return nil; + } + + h = hashstr(name); + a = die->hash[h]; + + if (a == nil) + return nil; + + + if (strcmp(name, getattr(a, DW_AT_name)->data) == 0) + return a; + + // Move found ones to head of the list. + b = a->hlink; + while (b != nil) { + if (strcmp(name, getattr(b, DW_AT_name)->data) == 0) { + a->hlink = b->hlink; + b->hlink = die->hash[h]; + die->hash[h] = b; + return b; + } + a = b; + b = b->hlink; + } + return nil; +} + +static DWDie* +find_or_diag(DWDie *die, char* name) +{ + DWDie *r; + r = find(die, name); + if (r == nil) + diag("dwarf find: %s has no %s", getattr(die, DW_AT_name)->data, name); + return r; +} + +static DWAttr* +newrefattr(DWDie *die, uint8 attr, DWDie* ref) +{ + if (ref == nil) + return nil; + return newattr(die, attr, DW_CLS_REFERENCE, 0, (char*)ref); +} + +static int fwdcount; + +static void +putattr(int form, int cls, vlong value, char *data) +{ + switch(form) { + case DW_FORM_addr: // address + addrput(value); + break; + + case DW_FORM_block1: // block + value &= 0xff; + cput(value); + while(value--) + cput(*data++); + break; + + case DW_FORM_block2: // block + value &= 0xffff; + WPUT(value); + while(value--) + cput(*data++); + break; + + case DW_FORM_block4: // block + value &= 0xffffffff; + LPUT(value); + while(value--) + cput(*data++); + break; + + case DW_FORM_block: // block + uleb128put(value); + while(value--) + cput(*data++); + break; + + case DW_FORM_data1: // constant + cput(value); + break; + + case DW_FORM_data2: // constant + WPUT(value); + break; + + case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr + LPUT(value); + break; + + case DW_FORM_data8: // constant, {line,loclist,mac,rangelist}ptr + VPUT(value); + break; + + case DW_FORM_sdata: // constant + sleb128put(value); + break; + + case DW_FORM_udata: // constant + uleb128put(value); + break; + + case DW_FORM_string: // string + strnput(data, value+1); + break; + + case DW_FORM_flag: // flag + cput(value?1:0); + break; + + case DW_FORM_ref_addr: // reference to a DIE in the .info section + if (data == nil) { + diag("null dwarf reference"); + LPUT(0); // invalid dwarf, gdb will complain. + } else { + if (((DWDie*)data)->offs == 0) + fwdcount++; + LPUT(((DWDie*)data)->offs); + } + break; + + case DW_FORM_ref1: // reference within the compilation unit + case DW_FORM_ref2: // reference + case DW_FORM_ref4: // reference + case DW_FORM_ref8: // reference + case DW_FORM_ref_udata: // reference + + case DW_FORM_strp: // string + case DW_FORM_indirect: // (see Section 7.5.3) + default: + diag("Unsupported atribute form %d / class %d", form, cls); + errorexit(); + } +} + +// Note that we can (and do) add arbitrary attributes to a DIE, but +// only the ones actually listed in the Abbrev will be written out. +static void +putattrs(int abbrev, DWAttr* attr) +{ + DWAttr *attrs[DW_AT_recursive + 1]; + DWAttrForm* af; + + memset(attrs, 0, sizeof attrs); + for( ; attr; attr = attr->link) + if (attr->atr < nelem(attrs)) + attrs[attr->atr] = attr; + + for(af = abbrevs[abbrev].attr; af->attr; af++) + if (attrs[af->attr]) + putattr(af->form, + attrs[af->attr]->cls, + attrs[af->attr]->value, + attrs[af->attr]->data); + else + putattr(af->form, 0, 0, 0); +} + +static void putdie(DWDie* die); + +static void +putdies(DWDie* die) +{ + for(; die; die = die->link) + putdie(die); +} + +static void +putdie(DWDie* die) +{ + die->offs = cpos() - infoo; + uleb128put(die->abbrev); + putattrs(die->abbrev, die->attr); + if (abbrevs[die->abbrev].children) { + putdies(die->child); + cput(0); + } +} + +static void +reverselist(DWDie** list) +{ + DWDie *curr, *prev; + + curr = *list; + prev = nil; + while(curr != nil) { + DWDie* next = curr->link; + curr->link = prev; + prev = curr; + curr = next; + } + *list = prev; +} + +static void +reversetree(DWDie** list) +{ + DWDie *die; + + reverselist(list); + for (die = *list; die != nil; die = die->link) + if (abbrevs[die->abbrev].children) + reversetree(&die->child); +} + +static void +newmemberoffsetattr(DWDie *die, int32 offs) +{ + char block[10]; + int i; + + i = 0; + if (offs != 0) { + block[i++] = DW_OP_consts; + i += sleb128enc(offs, block+i); + block[i++] = DW_OP_plus; + } + newattr(die, DW_AT_data_member_location, DW_CLS_BLOCK, i, mal(i)); + memmove(die->attr->data, block, i); +} + +// GDB doesn't like DW_FORM_addr for DW_AT_location, so emit a +// location expression that evals to a const. +static void +newabslocexprattr(DWDie *die, vlong addr) +{ + char block[10]; + int i; + + i = 0; + block[i++] = DW_OP_constu; + i += uleb128enc(addr, block+i); + newattr(die, DW_AT_location, DW_CLS_BLOCK, i, mal(i)); + memmove(die->attr->data, block, i); +} + +// Decoding the type.* symbols. This has to be in sync with +// ../../pkg/runtime/type.go, or more specificaly, with what +// ../gc/reflect.c stuffs in these. + +enum { + KindBool = 1, + KindInt, + KindInt8, + KindInt16, + KindInt32, + KindInt64, + KindUint, + KindUint8, + KindUint16, + KindUint32, + KindUint64, + KindUintptr, + KindFloat, + KindFloat32, + KindFloat64, + KindComplex, + KindComplex64, + KindComplex128, + KindArray, + KindChan, + KindFunc, + KindInterface, + KindMap, + KindPtr, + KindSlice, + KindString, + KindStruct, + KindUnsafePointer, + + KindNoPointers = 1<<7, +}; + +static Reloc* +decode_reloc(Sym *s, int32 off) +{ + int i; + + for (i = 0; i < s->nr; i++) + if (s->r[i].off == off) + return s->r + i; + return nil; +} + +static uvlong +decode_inuxi(uchar* p, int sz) +{ + uint64 v; + uint32 l; + uchar *cast, *inuxi; + int i; + + v = l = 0; + cast = nil; + inuxi = nil; + switch (sz) { + case 2: + cast = (uchar*)&l; + inuxi = inuxi2; + break; + case 4: + cast = (uchar*)&l; + inuxi = inuxi4; + break; + case 8: + cast = (uchar*)&v; + inuxi = inuxi8; + break; + default: + diag("decode inuxi %d", sz); + errorexit(); + } + for (i = 0; i < sz; i++) + cast[inuxi[i]] = p[i]; + if (sz == 8) + return v; + return l; +} + +// Type.commonType.kind +static uint8 +decodetype_kind(Sym *s) +{ + return s->p[3*PtrSize + 7] & ~KindNoPointers; // 0x13 / 0x1f +} + +// Type.commonType.size +static vlong +decodetype_size(Sym *s) +{ + return decode_inuxi(s->p + 2*PtrSize, PtrSize); // 0x8 / 0x10 +} + +// Type.ArrayType.elem and Type.SliceType.Elem +static Sym* +decodetype_arrayelem(Sym *s) +{ + return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 +} + +static vlong +decodetype_arraylen(Sym *s) +{ + return decode_inuxi(s->p + 6*PtrSize + 8, PtrSize); +} + +// Type.PtrType.elem +static Sym* +decodetype_ptrelem(Sym *s) +{ + return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 +} + +// Type.MapType.key, elem +static Sym* +decodetype_mapkey(Sym *s) +{ + return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 +} +static Sym* +decodetype_mapvalue(Sym *s) +{ + return decode_reloc(s, 6*PtrSize + 8)->sym; // 0x20 / 0x38 +} + +// Type.ChanType.elem +static Sym* +decodetype_chanelem(Sym *s) +{ + return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 +} + +// Type.FuncType.dotdotdot +static int +decodetype_funcdotdotdot(Sym *s) +{ + return s->p[5*PtrSize + 8]; +} + +// Type.FuncType.in.len +static int +decodetype_funcincount(Sym *s) +{ + return decode_inuxi(s->p + 7*PtrSize + 8, 4); +} + +static int +decodetype_funcoutcount(Sym *s) +{ + return decode_inuxi(s->p + 8*PtrSize + 16, 4); +} + +static Sym* +decodetype_funcintype(Sym *s, int i) +{ + Reloc *r; + + r = decode_reloc(s, 6*PtrSize + 8); + return decode_reloc(r->sym, r->add + i * PtrSize)->sym; +} + +static Sym* +decodetype_funcouttype(Sym *s, int i) +{ + Reloc *r; + + r = decode_reloc(s, 7*PtrSize + 16); + return decode_reloc(r->sym, r->add + i * PtrSize)->sym; +} + +// Type.StructType.fields.Slice::len +static int +decodetype_structfieldcount(Sym *s) +{ + return decode_inuxi(s->p + 6*PtrSize + 8, 4); // 0x20 / 0x38 +} + +// Type.StructType.fields[]-> name, typ and offset. sizeof(structField) = 5*PtrSize +static char* +decodetype_structfieldname(Sym *s, int i) +{ + Reloc* r; + + r = decode_reloc(s, 6*PtrSize + 0x10 + i*5*PtrSize); // go.string."foo" 0x28 / 0x40 + if (r == nil) // embedded structs have a nil name. + return nil; + r = decode_reloc(r->sym, 0); // string."foo" + if (r == nil) // shouldn't happen. + return nil; + return (char*)r->sym->p; // the c-string +} + +static Sym* +decodetype_structfieldtype(Sym *s, int i) +{ + return decode_reloc(s, 8*PtrSize + 0x10 + i*5*PtrSize)->sym; // 0x30 / 0x50 +} + +static vlong +decodetype_structfieldoffs(Sym *s, int i) +{ + return decode_inuxi(s->p + 10*PtrSize + 0x10 + i*5*PtrSize, 4); // 0x38 / 0x60 +} + +// InterfaceTYpe.methods.len +static vlong +decodetype_ifacemethodcount(Sym *s) +{ + return decode_inuxi(s->p + 6*PtrSize + 8, 4); +} + + +// Fake attributes for slices, maps and channel +enum { + DW_AT_internal_elem_type = 250, // channels and slices + DW_AT_internal_key_type = 251, // maps + DW_AT_internal_val_type = 252, // maps + DW_AT_internal_location = 253, // params and locals +}; + +static DWDie* defptrto(DWDie *dwtype); // below + +// Define gotype, for composite ones recurse into constituents. +static DWDie* +defgotype(Sym *gotype) +{ + DWDie *die, *fld; + Sym *s; + char *name, *f; + uint8 kind; + vlong bytesize; + int i, nfields; + + if (gotype == nil) + return find_or_diag(&dwtypes, ""); + + if (strncmp("type.", gotype->name, 5) != 0) { + diag("Type name doesn't start with \".type\": %s", gotype->name); + return find_or_diag(&dwtypes, ""); + } + name = gotype->name + 5; // Altenatively decode from Type.string + + die = find(&dwtypes, name); + if (die != nil) + return die; + + if (0 && debug['v'] > 2) { + print("new type: %s @0x%08x [%d]", gotype->name, gotype->value, gotype->size); + for (i = 0; i < gotype->size; i++) { + if (!(i%8)) print("\n\t%04x ", i); + print("%02x ", gotype->p[i]); + } + print("\n"); + for (i = 0; i < gotype->nr; i++) { + print("\t0x%02x[%x] %d %s[%llx]\n", + gotype->r[i].off, + gotype->r[i].siz, + gotype->r[i].type, + gotype->r[i].sym->name, + (vlong)gotype->r[i].add); + } + } + + kind = decodetype_kind(gotype); + bytesize = decodetype_size(gotype); + + switch (kind) { + case KindBool: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_boolean, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindInt: + case KindInt8: + case KindInt16: + case KindInt32: + case KindInt64: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_signed, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindUint: + case KindUint8: + case KindUint16: + case KindUint32: + case KindUint64: + case KindUintptr: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindFloat: + case KindFloat32: + case KindFloat64: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_float, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindComplex: + case KindComplex64: + case KindComplex128: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_complex_float, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindArray: + die = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + s = decodetype_arrayelem(gotype); + newrefattr(die, DW_AT_type, defgotype(s)); + fld = newdie(die, DW_ABRV_ARRAYRANGE, "range"); + newattr(fld, DW_AT_upper_bound, DW_CLS_CONSTANT, decodetype_arraylen(gotype), 0); + newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr")); + break; + + case KindChan: + die = newdie(&dwtypes, DW_ABRV_CHANTYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + s = decodetype_chanelem(gotype); + newrefattr(die, DW_AT_internal_elem_type, defgotype(s)); + break; + + case KindFunc: + die = newdie(&dwtypes, DW_ABRV_FUNCTYPE, name); + newrefattr(die, DW_AT_type, find_or_diag(&dwtypes, "void")); + nfields = decodetype_funcincount(gotype); + for (i = 0; i < nfields; i++) { + s = decodetype_funcintype(gotype, i); + fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s->name+5); + newrefattr(fld, DW_AT_type, defgotype(s)); + } + if (decodetype_funcdotdotdot(gotype)) + newdie(die, DW_ABRV_DOTDOTDOT, "..."); + nfields = decodetype_funcoutcount(gotype); + for (i = 0; i < nfields; i++) { + s = decodetype_funcouttype(gotype, i); + fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s->name+5); + newrefattr(fld, DW_AT_type, defptrto(defgotype(s))); + } + die = defptrto(die); + break; + + case KindInterface: + die = newdie(&dwtypes, DW_ABRV_IFACETYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + nfields = decodetype_ifacemethodcount(gotype); + if (nfields == 0) + s = lookup("type.runtime.eface", 0); + else + s = lookup("type.runtime.iface", 0); + newrefattr(die, DW_AT_type, defgotype(s)); + break; + + case KindMap: + die = newdie(&dwtypes, DW_ABRV_MAPTYPE, name); + s = decodetype_mapkey(gotype); + newrefattr(die, DW_AT_internal_key_type, defgotype(s)); + s = decodetype_mapvalue(gotype); + newrefattr(die, DW_AT_internal_val_type, defgotype(s)); + break; + + case KindPtr: + die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name); + s = decodetype_ptrelem(gotype); + newrefattr(die, DW_AT_type, defgotype(s)); + break; + + case KindSlice: + die = newdie(&dwtypes, DW_ABRV_SLICETYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + s = decodetype_arrayelem(gotype); + newrefattr(die, DW_AT_internal_elem_type, defgotype(s)); + break; + + case KindString: + die = newdie(&dwtypes, DW_ABRV_STRINGTYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindStruct: + die = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + nfields = decodetype_structfieldcount(gotype); + for (i = 0; i < nfields; i++) { + f = decodetype_structfieldname(gotype, i); + s = decodetype_structfieldtype(gotype, i); + if (f == nil) + f = s->name + 5; // skip "type." + fld = newdie(die, DW_ABRV_STRUCTFIELD, f); + newrefattr(fld, DW_AT_type, defgotype(s)); + newmemberoffsetattr(fld, decodetype_structfieldoffs(gotype, i)); + } + break; + + case KindUnsafePointer: + die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name); + newrefattr(die, DW_AT_type, find(&dwtypes, "void")); + break; + + default: + diag("definition of unknown kind %d: %s", kind, gotype->name); + die = newdie(&dwtypes, DW_ABRV_TYPEDECL, name); + newrefattr(die, DW_AT_type, find_or_diag(&dwtypes, "")); + } + + return die; +} + +// Find or construct *T given T. +static DWDie* +defptrto(DWDie *dwtype) +{ + char ptrname[1024]; + DWDie *die; + + snprint(ptrname, sizeof ptrname, "*%s", getattr(dwtype, DW_AT_name)->data); + die = find(&dwtypes, ptrname); + if (die == nil) { + die = newdie(&dwtypes, DW_ABRV_PTRTYPE, + strcpy(mal(strlen(ptrname)+1), ptrname)); + newrefattr(die, DW_AT_type, dwtype); + } + return die; +} + +// Copies src's children into dst. Copies attributes by value. +// DWAttr.data is copied as pointer only. +static void +copychildren(DWDie *dst, DWDie *src) +{ + DWDie *c; + DWAttr *a; + + for (src = src->child; src != nil; src = src->link) { + c = newdie(dst, src->abbrev, getattr(src, DW_AT_name)->data); + for (a = src->attr; a != nil; a = a->link) + newattr(c, a->atr, a->cls, a->value, a->data); + copychildren(c, src); + } + reverselist(&dst->child); +} + +// Search children (assumed to have DW_TAG_member) for the one named +// field and set it's DW_AT_type to dwtype +static void +substitutetype(DWDie *structdie, char *field, DWDie* dwtype) +{ + DWDie *child; + DWAttr *a; + + child = find_or_diag(structdie, field); + if (child == nil) + return; + + a = getattr(child, DW_AT_type); + if (a != nil) + a->data = (char*) dwtype; + else + newrefattr(child, DW_AT_type, dwtype); +} + +static void +synthesizestringtypes(DWDie* die) +{ + DWDie *prototype; + + prototype = defgotype(lookup("type.runtime.string_", 0)); + if (prototype == nil) + return; + + for (; die != nil; die = die->link) { + if (die->abbrev != DW_ABRV_STRINGTYPE) + continue; + copychildren(die, prototype); + } +} + +static void +synthesizeslicetypes(DWDie *die) +{ + DWDie *prototype, *elem; + + prototype = defgotype(lookup("type.runtime.slice",0)); + if (prototype == nil) + return; + + for (; die != nil; die = die->link) { + if (die->abbrev != DW_ABRV_SLICETYPE) + continue; + copychildren(die, prototype); + elem = (DWDie*) getattr(die, DW_AT_internal_elem_type)->data; + substitutetype(die, "array", defptrto(elem)); + } +} + +static char* +mkinternaltypename(char *base, char *arg1, char *arg2) +{ + char buf[1024]; + char *n; + + if (arg2 == nil) + snprint(buf, sizeof buf, "%s<%s>", base, arg1); + else + snprint(buf, sizeof buf, "%s<%s,%s>", base, arg1, arg2); + n = mal(strlen(buf) + 1); + memmove(n, buf, strlen(buf)); + return n; +} + + +// synthesizemaptypes is way too closely married to runtime/hashmap.c +enum { + MaxValsize = 256 - 64 +}; + +static void +synthesizemaptypes(DWDie *die) +{ + + DWDie *hash, *hash_subtable, *hash_entry, + *dwh, *dwhs, *dwhe, *keytype, *valtype, *fld; + int hashsize, keysize, valsize, datsize, valsize_in_hash, datavo; + DWAttr *a; + + hash = defgotype(lookup("type.runtime.hash",0)); + hash_subtable = defgotype(lookup("type.runtime.hash_subtable",0)); + hash_entry = defgotype(lookup("type.runtime.hash_entry",0)); + + if (hash == nil || hash_subtable == nil || hash_entry == nil) + return; + + dwh = (DWDie*)getattr(find_or_diag(hash_entry, "hash"), DW_AT_type)->data; + if (dwh == nil) + return; + + hashsize = getattr(dwh, DW_AT_byte_size)->value; + + for (; die != nil; die = die->link) { + if (die->abbrev != DW_ABRV_MAPTYPE) + continue; + + keytype = (DWDie*) getattr(die, DW_AT_internal_key_type)->data; + valtype = (DWDie*) getattr(die, DW_AT_internal_val_type)->data; + + a = getattr(keytype, DW_AT_byte_size); + keysize = a ? a->value : PtrSize; // We don't store size with Pointers + + a = getattr(valtype, DW_AT_byte_size); + valsize = a ? a->value : PtrSize; + + // This is what happens in hash_init and makemap_c + valsize_in_hash = valsize; + if (valsize > MaxValsize) + valsize_in_hash = PtrSize; + datavo = keysize; + if (valsize_in_hash >= PtrSize) + datavo = rnd(keysize, PtrSize); + datsize = datavo + valsize_in_hash; + if (datsize < PtrSize) + datsize = PtrSize; + datsize = rnd(datsize, PtrSize); + + // Construct struct hash_entry + dwhe = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("hash_entry", + getattr(keytype, DW_AT_name)->data, + getattr(valtype, DW_AT_name)->data)); + copychildren(dwhe, hash_entry); + substitutetype(dwhe, "key", keytype); + if (valsize > MaxValsize) + valtype = defptrto(valtype); + substitutetype(dwhe, "val", valtype); + fld = find_or_diag(dwhe, "val"); + delattr(fld, DW_AT_data_member_location); + newmemberoffsetattr(fld, hashsize + datavo); + newattr(dwhe, DW_AT_byte_size, DW_CLS_CONSTANT, hashsize + datsize, NULL); + + // Construct hash_subtable> + dwhs = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("hash_subtable", + getattr(keytype, DW_AT_name)->data, + getattr(valtype, DW_AT_name)->data)); + copychildren(dwhs, hash_subtable); + substitutetype(dwhs, "end", defptrto(dwhe)); + substitutetype(dwhs, "entry", dwhe); // todo: []hash_entry with dynamic size + newattr(dwhs, DW_AT_byte_size, DW_CLS_CONSTANT, + getattr(hash_subtable, DW_AT_byte_size)->value, NULL); + + // Construct hash + dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("hash", + getattr(keytype, DW_AT_name)->data, + getattr(valtype, DW_AT_name)->data)); + copychildren(dwh, hash); + substitutetype(dwh, "st", defptrto(dwhs)); + newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, + getattr(hash, DW_AT_byte_size)->value, NULL); + + newrefattr(die, DW_AT_type, defptrto(dwh)); + } +} + +static void +synthesizechantypes(DWDie *die) +{ + DWDie *sudog, *waitq, *link, *hchan, + *dws, *dww, *dwl, *dwh, *elemtype; + DWAttr *a; + int elemsize, linksize, sudogsize; + + sudog = defgotype(lookup("type.runtime.sudoG",0)); + waitq = defgotype(lookup("type.runtime.waitQ",0)); + link = defgotype(lookup("type.runtime.link",0)); + hchan = defgotype(lookup("type.runtime.hChan",0)); + if (sudog == nil || waitq == nil || link == nil || hchan == nil) + return; + + sudogsize = getattr(sudog, DW_AT_byte_size)->value; + linksize = getattr(link, DW_AT_byte_size)->value; + + for (; die != nil; die = die->link) { + if (die->abbrev != DW_ABRV_CHANTYPE) + continue; + elemtype = (DWDie*) getattr(die, DW_AT_internal_elem_type)->data; + a = getattr(elemtype, DW_AT_byte_size); + elemsize = a ? a->value : PtrSize; + + // sudog + dws = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("sudog", + getattr(elemtype, DW_AT_name)->data, NULL)); + copychildren(dws, sudog); + substitutetype(dws, "elem", elemtype); + newattr(dws, DW_AT_byte_size, DW_CLS_CONSTANT, + sudogsize + (elemsize > 8 ? elemsize - 8 : 0), NULL); + + // waitq + dww = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("waitq", getattr(elemtype, DW_AT_name)->data, NULL)); + copychildren(dww, waitq); + substitutetype(dww, "first", defptrto(dws)); + substitutetype(dww, "last", defptrto(dws)); + newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT, + getattr(waitq, DW_AT_byte_size)->value, NULL); + + // link + dwl = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("link", getattr(elemtype, DW_AT_name)->data, NULL)); + copychildren(dwl, link); + substitutetype(dwl, "link", defptrto(dwl)); + substitutetype(dwl, "elem", elemtype); + newattr(dwl, DW_AT_byte_size, DW_CLS_CONSTANT, + linksize + (elemsize > 8 ? elemsize - 8 : 0), NULL); + + // hchan + dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("hchan", getattr(elemtype, DW_AT_name)->data, NULL)); + copychildren(dwh, hchan); + substitutetype(dwh, "senddataq", defptrto(dwl)); + substitutetype(dwh, "recvdataq", defptrto(dwl)); + substitutetype(dwh, "recvq", dww); + substitutetype(dwh, "sendq", dww); + substitutetype(dwh, "free", dws); + newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, + getattr(hchan, DW_AT_byte_size)->value, NULL); + + newrefattr(die, DW_AT_type, defptrto(dwh)); + } +} + +// For use with pass.c::genasmsym +static void +defdwsymb(Sym* sym, char *s, int t, vlong v, vlong size, int ver, Sym *gotype) +{ + DWDie *dv, *dt; + + if (strncmp(s, "go.string.", 10) == 0) + return; + if (strncmp(s, "string.", 7) == 0) + return; + if (strncmp(s, "type._.", 7) == 0) + return; + + if (strncmp(s, "type.", 5) == 0) { + defgotype(sym); + return; + } + + dv = nil; + + switch (t) { + default: + return; + case 'd': + case 'b': + case 'D': + case 'B': + dv = newdie(&dwglobals, DW_ABRV_VARIABLE, s); + newabslocexprattr(dv, v); + if (ver == 0) + newattr(dv, DW_AT_external, DW_CLS_FLAG, 1, 0); + // fallthrough + case 'a': + case 'p': + dt = defgotype(gotype); + } + + if (dv != nil) + newrefattr(dv, DW_AT_type, dt); +} + +// TODO(lvd) For now, just append them all to the first compilation +// unit (that should be main), in the future distribute them to the +// appropriate compilation units. +static void +movetomodule(DWDie *parent) +{ + DWDie *die; + + for (die = dwroot.child->child; die->link != nil; die = die->link) /* nix */; + die->link = parent->child; +} + +/* + * Filename fragments for the line history stack. + */ + +static char **ftab; +static int ftabsize; + +void +dwarfaddfrag(int n, char *frag) +{ + int s; + + if (n >= ftabsize) { + s = ftabsize; + ftabsize = 1 + n + (n >> 2); + ftab = realloc(ftab, ftabsize * sizeof(ftab[0])); + memset(ftab + s, 0, (ftabsize - s) * sizeof(ftab[0])); + } + + if (*frag == '<') + frag++; + ftab[n] = frag; +} + +// Returns a malloc'ed string, piecewise copied from the ftab. +static char * +decodez(char *s) +{ + int len, o; + char *ss, *f; + char *r, *rb, *re; + + len = 0; + ss = s + 1; // first is 0 + while((o = ((uint8)ss[0] << 8) | (uint8)ss[1]) != 0) { + if (o < 0 || o >= ftabsize) { + diag("corrupt z entry"); + return 0; + } + f = ftab[o]; + if (f == nil) { + diag("corrupt z entry"); + return 0; + } + len += strlen(f) + 1; // for the '/' + ss += 2; + } + + if (len == 0) + return 0; + + r = malloc(len + 1); + rb = r; + re = rb + len + 1; + + s++; + while((o = ((uint8)s[0] << 8) | (uint8)s[1]) != 0) { + f = ftab[o]; + if (rb == r || rb[-1] == '/') + rb = seprint(rb, re, "%s", f); + else + rb = seprint(rb, re, "/%s", f); + s += 2; + } + return r; +} + +/* + * The line history itself + */ + +static char **histfile; // [0] holds "", DW_LNS_set_file arguments must be > 0. +static int histfilesize; +static int histfilecap; + +static void +clearhistfile(void) +{ + int i; + + // [0] holds "" + for (i = 1; i < histfilesize; i++) + free(histfile[i]); + histfilesize = 0; +} + +static int +addhistfile(char *zentry) +{ + char *fname; + + if (histfilesize == histfilecap) { + histfilecap = 2 * histfilecap + 2; + histfile = realloc(histfile, histfilecap * sizeof(char*)); + } + if (histfilesize == 0) + histfile[histfilesize++] = ""; + + fname = decodez(zentry); + if (fname == 0) + return -1; + // Don't fill with duplicates (check only top one). + if (strcmp(fname, histfile[histfilesize-1]) == 0) { + free(fname); + return histfilesize - 1; + } + histfile[histfilesize++] = fname; + return histfilesize - 1; +} + +// if the histfile stack contains ..../runtime/runtime_defs.go +// use that to set gdbscript +static void +finddebugruntimepath() +{ + int i, l; + char *c; + + for (i = 1; i < histfilesize; i++) { + if ((c = strstr(histfile[i], "runtime/runtime_defs.go")) != nil) { + l = c - histfile[i]; + memmove(gdbscript, histfile[i], l); + memmove(gdbscript + l, "runtime/runtime-gdb.py", strlen("runtime/runtime-gdb.py") + 1); + break; + } + } +} + +// Go's runtime C sources are sane, and Go sources nest only 1 level, +// so 16 should be plenty. +static struct { + int file; + vlong line; +} includestack[16]; +static int includetop; +static vlong absline; + +typedef struct Linehist Linehist; +struct Linehist { + Linehist *link; + vlong absline; + vlong line; + int file; +}; + +static Linehist *linehist; + +static void +checknesting(void) +{ + int i; + + if (includetop < 0) { + diag("corrupt z stack"); + errorexit(); + } + if (includetop >= nelem(includestack)) { + diag("nesting too deep"); + for (i = 0; i < nelem(includestack); i++) + diag("\t%s", histfile[includestack[i].file]); + errorexit(); + } +} + +/* + * Return false if the a->link chain contains no history, otherwise + * returns true and finds z and Z entries in the Auto list (of a + * Prog), and resets the history stack + */ +static int +inithist(Auto *a) +{ + Linehist *lh; + + for (; a; a = a->link) + if (a->type == D_FILE) + break; + if (a==nil) + return 0; + + // We have a new history. They are guaranteed to come completely + // at the beginning of the compilation unit. + if (a->aoffset != 1) { + diag("stray 'z' with offset %d", a->aoffset); + return 0; + } + + // Clear the history. + clearhistfile(); + includetop = 0; + includestack[includetop].file = 0; + includestack[includetop].line = -1; + absline = 0; + while (linehist != nil) { + lh = linehist->link; + free(linehist); + linehist = lh; + } + + // Construct the new one. + for (; a; a = a->link) { + if (a->type == D_FILE) { // 'z' + int f = addhistfile(a->asym->name); + if (f < 0) { // pop file + includetop--; + checknesting(); + } else if(f != includestack[includetop].file) { // pushed a new file + includestack[includetop].line += a->aoffset - absline; + includetop++; + checknesting(); + includestack[includetop].file = f; + includestack[includetop].line = 1; + } + absline = a->aoffset; + } else if (a->type == D_FILE1) { // 'Z' + // We could just fixup the current + // linehist->line, but there doesn't appear to + // be a guarantee that every 'Z' is preceded + // by it's own 'z', so do the safe thing and + // update the stack and push a new Linehist + // entry + includestack[includetop].line = a->aoffset; + } else + continue; + if (linehist == 0 || linehist->absline != absline) { + Linehist* lh = malloc(sizeof *lh); + lh->link = linehist; + lh->absline = absline; + linehist = lh; + } + linehist->file = includestack[includetop].file; + linehist->line = includestack[includetop].line; + } + return 1; +} + +static Linehist * +searchhist(vlong absline) +{ + Linehist *lh; + + for (lh = linehist; lh; lh = lh->link) + if (lh->absline <= absline) + break; + return lh; +} + +static int +guesslang(char *s) +{ + if(strlen(s) >= 3 && strcmp(s+strlen(s)-3, ".go") == 0) + return DW_LANG_Go; + + return DW_LANG_C; +} + +/* + * Generate short opcodes when possible, long ones when neccesary. + * See section 6.2.5 + */ + +enum { + LINE_BASE = -1, + LINE_RANGE = 4, + OPCODE_BASE = 5 +}; + +static void +putpclcdelta(vlong delta_pc, vlong delta_lc) +{ + if (LINE_BASE <= delta_lc && delta_lc < LINE_BASE+LINE_RANGE) { + vlong opcode = OPCODE_BASE + (delta_lc - LINE_BASE) + (LINE_RANGE * delta_pc); + if (OPCODE_BASE <= opcode && opcode < 256) { + cput(opcode); + return; + } + } + + if (delta_pc) { + cput(DW_LNS_advance_pc); + sleb128put(delta_pc); + } + + cput(DW_LNS_advance_line); + sleb128put(delta_lc); + cput(DW_LNS_copy); +} + +static void +newcfaoffsetattr(DWDie *die, int32 offs) +{ + char block[10]; + int i; + + i = 0; + + block[i++] = DW_OP_call_frame_cfa; + if (offs != 0) { + block[i++] = DW_OP_consts; + i += sleb128enc(offs, block+i); + block[i++] = DW_OP_plus; + } + newattr(die, DW_AT_location, DW_CLS_BLOCK, i, mal(i)); + memmove(die->attr->data, block, i); +} + +static char* +mkvarname(char* name, int da) +{ + char buf[1024]; + char *n; + + snprint(buf, sizeof buf, "%s#%d", name, da); + n = mal(strlen(buf) + 1); + memmove(n, buf, strlen(buf)); + return n; +} + +/* + * Walk prog table, emit line program and build DIE tree. + */ + +// flush previous compilation unit. +static void +flushunit(DWDie *dwinfo, vlong pc, vlong unitstart) +{ + vlong here; + + if (dwinfo != nil && pc != 0) { + newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, pc+1, 0); + } + + if (unitstart >= 0) { + cput(0); // start extended opcode + uleb128put(1); + cput(DW_LNE_end_sequence); + cflush(); + + here = cpos(); + seek(cout, unitstart, 0); + LPUT(here - unitstart - sizeof(int32)); + cflush(); + seek(cout, here, 0); + } +} + +static void +writelines(void) +{ + Prog *q; + Sym *s; + Auto *a; + vlong unitstart, offs; + vlong pc, epc, lc, llc, lline; + int currfile; + int i, lang, da, dt; + Linehist *lh; + DWDie *dwinfo, *dwfunc, *dwvar, **dws; + DWDie *varhash[HASHSIZE]; + char *n, *nn; + + unitstart = -1; + epc = pc = 0; + lc = 1; + llc = 1; + currfile = -1; + lineo = cpos(); + dwinfo = nil; + + for(cursym = textp; cursym != nil; cursym = cursym->next) { + s = cursym; + if(s->text == P) + continue; + + // Look for history stack. If we find one, + // we're entering a new compilation unit + + if (inithist(s->autom)) { + flushunit(dwinfo, epc, unitstart); + unitstart = cpos(); + + if(debug['v'] > 1) { + print("dwarf writelines found %s\n", histfile[1]); + Linehist* lh; + for (lh = linehist; lh; lh = lh->link) + print("\t%8lld: [%4lld]%s\n", + lh->absline, lh->line, histfile[lh->file]); + } + + lang = guesslang(histfile[1]); + finddebugruntimepath(); + + dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, strdup(histfile[1])); + newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT,lang, 0); + newattr(dwinfo, DW_AT_stmt_list, DW_CLS_PTR, unitstart - lineo, 0); + newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, s->text->pc, 0); + + // Write .debug_line Line Number Program Header (sec 6.2.4) + // Fields marked with (*) must be changed for 64-bit dwarf + LPUT(0); // unit_length (*), will be filled in later. + WPUT(3); // dwarf version (appendix F) + LPUT(11); // header_length (*), starting here. + + cput(1); // minimum_instruction_length + cput(1); // default_is_stmt + cput(LINE_BASE); // line_base + cput(LINE_RANGE); // line_range + cput(OPCODE_BASE); // opcode_base (we only use 1..4) + cput(0); // standard_opcode_lengths[1] + cput(1); // standard_opcode_lengths[2] + cput(1); // standard_opcode_lengths[3] + cput(1); // standard_opcode_lengths[4] + cput(0); // include_directories (empty) + cput(0); // file_names (empty) (emitted by DW_LNE's below) + // header_length ends here. + + for (i=1; i < histfilesize; i++) { + cput(0); // start extended opcode + uleb128put(1 + strlen(histfile[i]) + 4); + cput(DW_LNE_define_file); + strnput(histfile[i], strlen(histfile[i]) + 4); + // 4 zeros: the string termination + 3 fields. + } + + epc = pc = s->text->pc; + currfile = 1; + lc = 1; + llc = 1; + + cput(0); // start extended opcode + uleb128put(1 + PtrSize); + cput(DW_LNE_set_address); + addrput(pc); + } + if(s->text == nil) + continue; + + if (unitstart < 0) { + diag("reachable code before seeing any history: %P", s->text); + continue; + } + + dwfunc = newdie(dwinfo, DW_ABRV_FUNCTION, s->name); + newattr(dwfunc, DW_AT_low_pc, DW_CLS_ADDRESS, s->value, 0); + epc = s->value + s->size; + newattr(dwfunc, DW_AT_high_pc, DW_CLS_ADDRESS, epc, 0); + if (s->version == 0) + newattr(dwfunc, DW_AT_external, DW_CLS_FLAG, 1, 0); + + if(s->text->link == nil) + continue; + + for(q = s->text; q != P; q = q->link) { + lh = searchhist(q->line); + if (lh == nil) { + diag("corrupt history or bad absolute line: %P", q); + continue; + } + + if (lh->file < 1) { // 0 is the past-EOF entry. + // diag("instruction with line number past EOF in %s: %P", histfile[1], q); + continue; + } + + lline = lh->line + q->line - lh->absline; + if (debug['v'] > 1) + print("%6llux %s[%lld] %P\n", (vlong)q->pc, histfile[lh->file], lline, q); + + if (q->line == lc) + continue; + if (currfile != lh->file) { + currfile = lh->file; + cput(DW_LNS_set_file); + uleb128put(currfile); + } + putpclcdelta(q->pc - pc, lline - llc); + pc = q->pc; + lc = q->line; + llc = lline; + } + + da = 0; + dwfunc->hash = varhash; // enable indexing of children by name + memset(varhash, 0, sizeof varhash); + for(a = s->autom; a; a = a->link) { + switch (a->type) { + case D_AUTO: + dt = DW_ABRV_AUTO; + offs = a->aoffset - PtrSize; + break; + case D_PARAM: + dt = DW_ABRV_PARAM; + offs = a->aoffset; + break; + default: + continue; + } + if (strstr(a->asym->name, ".autotmp_")) + continue; + if (find(dwfunc, a->asym->name) != nil) + n = mkvarname(a->asym->name, da); + else + n = a->asym->name; + // Drop the package prefix from locals and arguments. + nn = strrchr(n, '.'); + if (nn) + n = nn + 1; + + dwvar = newdie(dwfunc, dt, n); + newcfaoffsetattr(dwvar, offs); + newrefattr(dwvar, DW_AT_type, defgotype(a->gotype)); + + // push dwvar down dwfunc->child to preserve order + newattr(dwvar, DW_AT_internal_location, DW_CLS_CONSTANT, offs, NULL); + dwfunc->child = dwvar->link; // take dwvar out from the top of the list + for (dws = &dwfunc->child; *dws != nil; dws = &(*dws)->link) + if (offs > getattr(*dws, DW_AT_internal_location)->value) + break; + dwvar->link = *dws; + *dws = dwvar; + + da++; + } + + dwfunc->hash = nil; + } + + flushunit(dwinfo, epc, unitstart); + linesize = cpos() - lineo; +} + +/* + * Emit .debug_frame + */ +enum +{ + CIERESERVE = 16, + DATAALIGNMENTFACTOR = -4, // TODO -PtrSize? + FAKERETURNCOLUMN = 16 // TODO gdb6 doesnt like > 15? +}; + +static void +putpccfadelta(vlong deltapc, vlong cfa) +{ + if (deltapc < 0x40) { + cput(DW_CFA_advance_loc + deltapc); + } else if (deltapc < 0x100) { + cput(DW_CFA_advance_loc1); + cput(deltapc); + } else if (deltapc < 0x10000) { + cput(DW_CFA_advance_loc2); + WPUT(deltapc); + } else { + cput(DW_CFA_advance_loc4); + LPUT(deltapc); + } + + cput(DW_CFA_def_cfa_offset_sf); + sleb128put(cfa / DATAALIGNMENTFACTOR); +} + +static void +writeframes(void) +{ + Prog *p, *q; + Sym *s; + vlong fdeo, fdesize, pad, cfa, pc; + + frameo = cpos(); + + // Emit the CIE, Section 6.4.1 + LPUT(CIERESERVE); // initial length, must be multiple of PtrSize + LPUT(0xffffffff); // cid. + cput(3); // dwarf version (appendix F) + cput(0); // augmentation "" + uleb128put(1); // code_alignment_factor + sleb128put(DATAALIGNMENTFACTOR); // guess + uleb128put(FAKERETURNCOLUMN); // return_address_register + + cput(DW_CFA_def_cfa); + uleb128put(DWARFREGSP); // register SP (**ABI-dependent, defined in l.h) + uleb128put(PtrSize); // offset + + cput(DW_CFA_offset + FAKERETURNCOLUMN); // return address + uleb128put(-PtrSize / DATAALIGNMENTFACTOR); // at cfa - x*4 + + // 4 is to exclude the length field. + pad = CIERESERVE + frameo + 4 - cpos(); + if (pad < 0) { + diag("CIERESERVE too small by %lld bytes.", -pad); + errorexit(); + } + strnput("", pad); + + for(cursym = textp; cursym != nil; cursym = cursym->next) { + s = cursym; + if(s->text == nil) + continue; + + fdeo = cpos(); + // Emit a FDE, Section 6.4.1, starting wit a placeholder. + LPUT(0); // length, must be multiple of PtrSize + LPUT(0); // Pointer to the CIE above, at offset 0 + addrput(0); // initial location + addrput(0); // address range + + cfa = PtrSize; // CFA starts at sp+PtrSize + p = s->text; + pc = p->pc; + + for(q = p; q->link != P; q = q->link) { + if (q->spadj == 0) + continue; + cfa += q->spadj; + putpccfadelta(q->link->pc - pc, cfa); + pc = q->link->pc; + } + + fdesize = cpos() - fdeo - 4; // exclude the length field. + pad = rnd(fdesize, PtrSize) - fdesize; + strnput("", pad); + fdesize += pad; + cflush(); + + // Emit the FDE header for real, Section 6.4.1. + seek(cout, fdeo, 0); + LPUT(fdesize); + LPUT(0); + addrput(p->pc); + addrput(s->size); + + cflush(); + seek(cout, fdeo + 4 + fdesize, 0); + } + + cflush(); + framesize = cpos() - frameo; +} + +/* + * Walk DWarfDebugInfoEntries, and emit .debug_info + */ +enum +{ + COMPUNITHEADERSIZE = 4+2+4+1 +}; + +static void +writeinfo(void) +{ + DWDie *compunit; + vlong unitstart, here; + + fwdcount = 0; + + for (compunit = dwroot.child; compunit; compunit = compunit->link) { + unitstart = cpos(); + + // Write .debug_info Compilation Unit Header (sec 7.5.1) + // Fields marked with (*) must be changed for 64-bit dwarf + // This must match COMPUNITHEADERSIZE above. + LPUT(0); // unit_length (*), will be filled in later. + WPUT(3); // dwarf version (appendix F) + LPUT(0); // debug_abbrev_offset (*) + cput(PtrSize); // address_size + + putdie(compunit); + + cflush(); + here = cpos(); + seek(cout, unitstart, 0); + LPUT(here - unitstart - 4); // exclude the length field. + cflush(); + seek(cout, here, 0); + } + +} + +/* + * Emit .debug_pubnames/_types. _info must have been written before, + * because we need die->offs and infoo/infosize; + */ +static int +ispubname(DWDie *die) { + DWAttr *a; + + switch(die->abbrev) { + case DW_ABRV_FUNCTION: + case DW_ABRV_VARIABLE: + a = getattr(die, DW_AT_external); + return a && a->value; + } + return 0; +} + +static int +ispubtype(DWDie *die) { + return die->abbrev >= DW_ABRV_NULLTYPE; +} + +static vlong +writepub(int (*ispub)(DWDie*)) +{ + DWDie *compunit, *die; + DWAttr *dwa; + vlong unitstart, unitend, sectionstart, here; + + sectionstart = cpos(); + + for (compunit = dwroot.child; compunit != nil; compunit = compunit->link) { + unitstart = compunit->offs - COMPUNITHEADERSIZE; + if (compunit->link != nil) + unitend = compunit->link->offs - COMPUNITHEADERSIZE; + else + unitend = infoo + infosize; + + // Write .debug_pubnames/types Header (sec 6.1.1) + LPUT(0); // unit_length (*), will be filled in later. + WPUT(2); // dwarf version (appendix F) + LPUT(unitstart); // debug_info_offset (of the Comp unit Header) + LPUT(unitend - unitstart); // debug_info_length + + for (die = compunit->child; die != nil; die = die->link) { + if (!ispub(die)) continue; + LPUT(die->offs - unitstart); + dwa = getattr(die, DW_AT_name); + strnput(dwa->data, dwa->value + 1); + } + LPUT(0); + + cflush(); + here = cpos(); + seek(cout, sectionstart, 0); + LPUT(here - sectionstart - 4); // exclude the length field. + cflush(); + seek(cout, here, 0); + + } + + return sectionstart; +} + +/* + * emit .debug_aranges. _info must have been written before, + * because we need die->offs of dw_globals. + */ +static vlong +writearanges(void) +{ + DWDie *compunit; + DWAttr *b, *e; + int headersize; + vlong sectionstart; + + sectionstart = cpos(); + headersize = rnd(4+2+4+1+1, PtrSize); // don't count unit_length field itself + + for (compunit = dwroot.child; compunit != nil; compunit = compunit->link) { + b = getattr(compunit, DW_AT_low_pc); + if (b == nil) + continue; + e = getattr(compunit, DW_AT_high_pc); + if (e == nil) + continue; + + // Write .debug_aranges Header + entry (sec 6.1.2) + LPUT(headersize + 4*PtrSize - 4); // unit_length (*) + WPUT(2); // dwarf version (appendix F) + LPUT(compunit->offs - COMPUNITHEADERSIZE); // debug_info_offset + cput(PtrSize); // address_size + cput(0); // segment_size + strnput("", headersize - (4+2+4+1+1)); // align to PtrSize + + addrput(b->value); + addrput(e->value - b->value); + addrput(0); + addrput(0); + } + cflush(); + return sectionstart; +} + +static vlong +writegdbscript(void) +{ + vlong sectionstart; + + sectionstart = cpos(); + + if (gdbscript[0]) { + cput(1); // magic 1 byte? + strnput(gdbscript, strlen(gdbscript)+1); + cflush(); + } + return sectionstart; +} + +/* + * This is the main entry point for generating dwarf. After emitting + * the mandatory debug_abbrev section, it calls writelines() to set up + * the per-compilation unit part of the DIE tree, while simultaneously + * emitting the debug_line section. When the final tree contains + * forward references, it will write the debug_info section in 2 + * passes. + * + */ +void +dwarfemitdebugsections(void) +{ + vlong infoe; + DWDie* die; + + // For diagnostic messages. + newattr(&dwtypes, DW_AT_name, DW_CLS_STRING, strlen("dwtypes"), "dwtypes"); + + mkindex(&dwroot); + mkindex(&dwtypes); + mkindex(&dwglobals); + + // Some types that must exist to define other ones. + newdie(&dwtypes, DW_ABRV_NULLTYPE, ""); + newdie(&dwtypes, DW_ABRV_NULLTYPE, "void"); + newrefattr(newdie(&dwtypes, DW_ABRV_PTRTYPE, "unsafe.Pointer"), + DW_AT_type, find(&dwtypes, "void")); + die = newdie(&dwtypes, DW_ABRV_BASETYPE, "uintptr"); // needed for array size + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, PtrSize, 0); + + // Needed by the prettyprinter code for interface inspection. + defgotype(lookup("type.runtime.commonType",0)); + defgotype(lookup("type.runtime.InterfaceType",0)); + defgotype(lookup("type.runtime.itab",0)); + + genasmsym(defdwsymb); + + writeabbrev(); + writelines(); + writeframes(); + + synthesizestringtypes(dwtypes.child); + synthesizeslicetypes(dwtypes.child); + synthesizemaptypes(dwtypes.child); + synthesizechantypes(dwtypes.child); + + reversetree(&dwroot.child); + reversetree(&dwtypes.child); + reversetree(&dwglobals.child); + + movetomodule(&dwtypes); + movetomodule(&dwglobals); + + infoo = cpos(); + writeinfo(); + gdbscripto = arangeso = pubtypeso = pubnameso = infoe = cpos(); + + if (fwdcount > 0) { + if (debug['v']) + Bprint(&bso, "%5.2f dwarf pass 2.\n", cputime()); + seek(cout, infoo, 0); + writeinfo(); + if (fwdcount > 0) { + diag("unresolved references after first dwarf info pass"); + errorexit(); + } + if (infoe != cpos()) { + diag("inconsistent second dwarf info pass"); + errorexit(); + } + } + infosize = infoe - infoo; + + pubnameso = writepub(ispubname); + pubtypeso = writepub(ispubtype); + arangeso = writearanges(); + gdbscripto = writegdbscript(); + + pubnamessize = pubtypeso - pubnameso; + pubtypessize = arangeso - pubtypeso; + arangessize = gdbscripto - arangeso; + gdbscriptsize = cpos() - gdbscripto; +} + +/* + * Elf. + */ +enum +{ + ElfStrDebugAbbrev, + ElfStrDebugAranges, + ElfStrDebugFrame, + ElfStrDebugInfo, + ElfStrDebugLine, + ElfStrDebugLoc, + ElfStrDebugMacinfo, + ElfStrDebugPubNames, + ElfStrDebugPubTypes, + ElfStrDebugRanges, + ElfStrDebugStr, + ElfStrGDBScripts, + NElfStrDbg +}; + +vlong elfstrdbg[NElfStrDbg]; + +void +dwarfaddshstrings(Sym *shstrtab) +{ + elfstrdbg[ElfStrDebugAbbrev] = addstring(shstrtab, ".debug_abbrev"); + elfstrdbg[ElfStrDebugAranges] = addstring(shstrtab, ".debug_aranges"); + elfstrdbg[ElfStrDebugFrame] = addstring(shstrtab, ".debug_frame"); + elfstrdbg[ElfStrDebugInfo] = addstring(shstrtab, ".debug_info"); + elfstrdbg[ElfStrDebugLine] = addstring(shstrtab, ".debug_line"); + elfstrdbg[ElfStrDebugLoc] = addstring(shstrtab, ".debug_loc"); + elfstrdbg[ElfStrDebugMacinfo] = addstring(shstrtab, ".debug_macinfo"); + elfstrdbg[ElfStrDebugPubNames] = addstring(shstrtab, ".debug_pubnames"); + elfstrdbg[ElfStrDebugPubTypes] = addstring(shstrtab, ".debug_pubtypes"); + elfstrdbg[ElfStrDebugRanges] = addstring(shstrtab, ".debug_ranges"); + elfstrdbg[ElfStrDebugStr] = addstring(shstrtab, ".debug_str"); + elfstrdbg[ElfStrGDBScripts] = addstring(shstrtab, ".debug_gdb_scripts"); +} + +void +dwarfaddelfheaders(void) +{ + ElfShdr *sh; + + sh = newElfShdr(elfstrdbg[ElfStrDebugAbbrev]); + sh->type = SHT_PROGBITS; + sh->off = abbrevo; + sh->size = abbrevsize; + sh->addralign = 1; + + sh = newElfShdr(elfstrdbg[ElfStrDebugLine]); + sh->type = SHT_PROGBITS; + sh->off = lineo; + sh->size = linesize; + sh->addralign = 1; + + sh = newElfShdr(elfstrdbg[ElfStrDebugFrame]); + sh->type = SHT_PROGBITS; + sh->off = frameo; + sh->size = framesize; + sh->addralign = 1; + + sh = newElfShdr(elfstrdbg[ElfStrDebugInfo]); + sh->type = SHT_PROGBITS; + sh->off = infoo; + sh->size = infosize; + sh->addralign = 1; + + if (pubnamessize > 0) { + sh = newElfShdr(elfstrdbg[ElfStrDebugPubNames]); + sh->type = SHT_PROGBITS; + sh->off = pubnameso; + sh->size = pubnamessize; + sh->addralign = 1; + } + + if (pubtypessize > 0) { + sh = newElfShdr(elfstrdbg[ElfStrDebugPubTypes]); + sh->type = SHT_PROGBITS; + sh->off = pubtypeso; + sh->size = pubtypessize; + sh->addralign = 1; + } + + if (arangessize) { + sh = newElfShdr(elfstrdbg[ElfStrDebugAranges]); + sh->type = SHT_PROGBITS; + sh->off = arangeso; + sh->size = arangessize; + sh->addralign = 1; + } + + if (gdbscriptsize) { + sh = newElfShdr(elfstrdbg[ElfStrGDBScripts]); + sh->type = SHT_PROGBITS; + sh->off = gdbscripto; + sh->size = gdbscriptsize; + sh->addralign = 1; + } +} + +/* + * Macho + */ +void +dwarfaddmachoheaders(void) +{ + MachoSect *msect; + MachoSeg *ms; + vlong fakestart; + int nsect; + + // Zero vsize segments won't be loaded in memory, even so they + // have to be page aligned in the file. + fakestart = abbrevo & ~0xfff; + + nsect = 4; + if (pubnamessize > 0) + nsect++; + if (pubtypessize > 0) + nsect++; + if (arangessize > 0) + nsect++; + if (gdbscriptsize > 0) + nsect++; + + ms = newMachoSeg("__DWARF", nsect); + ms->fileoffset = fakestart; + ms->filesize = abbrevo-fakestart; + + msect = newMachoSect(ms, "__debug_abbrev"); + msect->off = abbrevo; + msect->size = abbrevsize; + ms->filesize += msect->size; + + msect = newMachoSect(ms, "__debug_line"); + msect->off = lineo; + msect->size = linesize; + ms->filesize += msect->size; + + msect = newMachoSect(ms, "__debug_frame"); + msect->off = frameo; + msect->size = framesize; + ms->filesize += msect->size; + + msect = newMachoSect(ms, "__debug_info"); + msect->off = infoo; + msect->size = infosize; + ms->filesize += msect->size; + + if (pubnamessize > 0) { + msect = newMachoSect(ms, "__debug_pubnames"); + msect->off = pubnameso; + msect->size = pubnamessize; + ms->filesize += msect->size; + } + + if (pubtypessize > 0) { + msect = newMachoSect(ms, "__debug_pubtypes"); + msect->off = pubtypeso; + msect->size = pubtypessize; + ms->filesize += msect->size; + } + + if (arangessize > 0) { + msect = newMachoSect(ms, "__debug_aranges"); + msect->off = arangeso; + msect->size = arangessize; + ms->filesize += msect->size; + } + + // TODO(lvd) fix gdb/python to load MachO (16 char section name limit) + if (gdbscriptsize > 0) { + msect = newMachoSect(ms, "__debug_gdb_scripts"); + msect->off = gdbscripto; + msect->size = gdbscriptsize; + ms->filesize += msect->size; + } +} diff --git a/src/cmd/ld/dwarf.h b/src/cmd/ld/dwarf.h new file mode 100644 index 000000000..7881213c2 --- /dev/null +++ b/src/cmd/ld/dwarf.h @@ -0,0 +1,29 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + * Register 'f' symbol file fragments. Doing this while parsing the + * .6 input saves a pass over the symbol table later. + */ +void dwarfaddfrag(int n, char* frag); + +/* + * Emit debug_abbrevs, debug_info and debug_line sections to current + * offset in cout. + */ +void dwarfemitdebugsections(void); + +/* + * Add the dwarf section names to the ELF + * s[ection]h[eader]str[ing]tab. Prerequisite for + * dwarfaddelfheaders(). + */ +void dwarfaddshstrings(Sym *shstrtab); + +/* + * Add section headers pointing to the sections emitted in + * dwarfemitdebugsections. + */ +void dwarfaddelfheaders(void); +void dwarfaddmachoheaders(void); diff --git a/src/cmd/ld/dwarf_defs.h b/src/cmd/ld/dwarf_defs.h new file mode 100644 index 000000000..eed143dff --- /dev/null +++ b/src/cmd/ld/dwarf_defs.h @@ -0,0 +1,503 @@ +// 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. + +// Cut, pasted, tr-and-awk'ed from tables in +// http://dwarfstd.org/doc/Dwarf3.pdf + +// Table 18 +enum +{ + DW_TAG_array_type = 0x01, + DW_TAG_class_type = 0x02, + DW_TAG_entry_point = 0x03, + DW_TAG_enumeration_type = 0x04, + DW_TAG_formal_parameter = 0x05, + DW_TAG_imported_declaration = 0x08, + DW_TAG_label = 0x0a, + DW_TAG_lexical_block = 0x0b, + DW_TAG_member = 0x0d, + DW_TAG_pointer_type = 0x0f, + DW_TAG_reference_type = 0x10, + DW_TAG_compile_unit = 0x11, + DW_TAG_string_type = 0x12, + DW_TAG_structure_type = 0x13, + DW_TAG_subroutine_type = 0x15, + DW_TAG_typedef = 0x16, + DW_TAG_union_type = 0x17, + DW_TAG_unspecified_parameters = 0x18, + DW_TAG_variant = 0x19, + DW_TAG_common_block = 0x1a, + DW_TAG_common_inclusion = 0x1b, + DW_TAG_inheritance = 0x1c, + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_module = 0x1e, + DW_TAG_ptr_to_member_type = 0x1f, + DW_TAG_set_type = 0x20, + DW_TAG_subrange_type = 0x21, + DW_TAG_with_stmt = 0x22, + DW_TAG_access_declaration = 0x23, + DW_TAG_base_type = 0x24, + DW_TAG_catch_block = 0x25, + DW_TAG_const_type = 0x26, + DW_TAG_constant = 0x27, + DW_TAG_enumerator = 0x28, + DW_TAG_file_type = 0x29, + DW_TAG_friend = 0x2a, + DW_TAG_namelist = 0x2b, + DW_TAG_namelist_item = 0x2c, + DW_TAG_packed_type = 0x2d, + DW_TAG_subprogram = 0x2e, + DW_TAG_template_type_parameter = 0x2f, + DW_TAG_template_value_parameter = 0x30, + DW_TAG_thrown_type = 0x31, + DW_TAG_try_block = 0x32, + DW_TAG_variant_part = 0x33, + DW_TAG_variable = 0x34, + DW_TAG_volatile_type = 0x35, + // Dwarf3 + DW_TAG_dwarf_procedure = 0x36, + DW_TAG_restrict_type = 0x37, + DW_TAG_interface_type = 0x38, + DW_TAG_namespace = 0x39, + DW_TAG_imported_module = 0x3a, + DW_TAG_unspecified_type = 0x3b, + DW_TAG_partial_unit = 0x3c, + DW_TAG_imported_unit = 0x3d, + DW_TAG_condition = 0x3f, + DW_TAG_shared_type = 0x40, + // Dwarf4 + DW_TAG_type_unit = 0x41, + DW_TAG_rvalue_reference_type = 0x42, + DW_TAG_template_alias = 0x43, + + // User defined + DW_TAG_lo_user = 0x4080, + DW_TAG_hi_user = 0xffff, + +}; + +// Table 19 +enum +{ + DW_CHILDREN_no = 0x00, + DW_CHILDREN_yes = 0x01, +}; + +// Not from the spec, but logicaly belongs here +enum +{ + DW_CLS_ADDRESS = 0x01, + DW_CLS_BLOCK, + DW_CLS_CONSTANT, + DW_CLS_FLAG, + DW_CLS_PTR, // lineptr, loclistptr, macptr, rangelistptr + DW_CLS_REFERENCE, + DW_CLS_STRING +}; + +// Table 20 +enum +{ + DW_AT_sibling = 0x01, // reference + DW_AT_location = 0x02, // block, loclistptr + DW_AT_name = 0x03, // string + DW_AT_ordering = 0x09, // constant + DW_AT_byte_size = 0x0b, // block, constant, reference + DW_AT_bit_offset = 0x0c, // block, constant, reference + DW_AT_bit_size = 0x0d, // block, constant, reference + DW_AT_stmt_list = 0x10, // lineptr + DW_AT_low_pc = 0x11, // address + DW_AT_high_pc = 0x12, // address + DW_AT_language = 0x13, // constant + DW_AT_discr = 0x15, // reference + DW_AT_discr_value = 0x16, // constant + DW_AT_visibility = 0x17, // constant + DW_AT_import = 0x18, // reference + DW_AT_string_length = 0x19, // block, loclistptr + DW_AT_common_reference = 0x1a, // reference + DW_AT_comp_dir = 0x1b, // string + DW_AT_const_value = 0x1c, // block, constant, string + DW_AT_containing_type = 0x1d, // reference + DW_AT_default_value = 0x1e, // reference + DW_AT_inline = 0x20, // constant + DW_AT_is_optional = 0x21, // flag + DW_AT_lower_bound = 0x22, // block, constant, reference + DW_AT_producer = 0x25, // string + DW_AT_prototyped = 0x27, // flag + DW_AT_return_addr = 0x2a, // block, loclistptr + DW_AT_start_scope = 0x2c, // constant + DW_AT_bit_stride = 0x2e, // constant + DW_AT_upper_bound = 0x2f, // block, constant, reference + DW_AT_abstract_origin = 0x31, // reference + DW_AT_accessibility = 0x32, // constant + DW_AT_address_class = 0x33, // constant + DW_AT_artificial = 0x34, // flag + DW_AT_base_types = 0x35, // reference + DW_AT_calling_convention = 0x36, // constant + DW_AT_count = 0x37, // block, constant, reference + DW_AT_data_member_location = 0x38, // block, constant, loclistptr + DW_AT_decl_column = 0x39, // constant + DW_AT_decl_file = 0x3a, // constant + DW_AT_decl_line = 0x3b, // constant + DW_AT_declaration = 0x3c, // flag + DW_AT_discr_list = 0x3d, // block + DW_AT_encoding = 0x3e, // constant + DW_AT_external = 0x3f, // flag + DW_AT_frame_base = 0x40, // block, loclistptr + DW_AT_friend = 0x41, // reference + DW_AT_identifier_case = 0x42, // constant + DW_AT_macro_info = 0x43, // macptr + DW_AT_namelist_item = 0x44, // block + DW_AT_priority = 0x45, // reference + DW_AT_segment = 0x46, // block, loclistptr + DW_AT_specification = 0x47, // reference + DW_AT_static_link = 0x48, // block, loclistptr + DW_AT_type = 0x49, // reference + DW_AT_use_location = 0x4a, // block, loclistptr + DW_AT_variable_parameter = 0x4b, // flag + DW_AT_virtuality = 0x4c, // constant + DW_AT_vtable_elem_location = 0x4d, // block, loclistptr + // Dwarf3 + DW_AT_allocated = 0x4e, // block, constant, reference + DW_AT_associated = 0x4f, // block, constant, reference + DW_AT_data_location = 0x50, // block + DW_AT_byte_stride = 0x51, // block, constant, reference + DW_AT_entry_pc = 0x52, // address + DW_AT_use_UTF8 = 0x53, // flag + DW_AT_extension = 0x54, // reference + DW_AT_ranges = 0x55, // rangelistptr + DW_AT_trampoline = 0x56, // address, flag, reference, string + DW_AT_call_column = 0x57, // constant + DW_AT_call_file = 0x58, // constant + DW_AT_call_line = 0x59, // constant + DW_AT_description = 0x5a, // string + DW_AT_binary_scale = 0x5b, // constant + DW_AT_decimal_scale = 0x5c, // constant + DW_AT_small = 0x5d, // reference + DW_AT_decimal_sign = 0x5e, // constant + DW_AT_digit_count = 0x5f, // constant + DW_AT_picture_string = 0x60, // string + DW_AT_mutable = 0x61, // flag + DW_AT_threads_scaled = 0x62, // flag + DW_AT_explicit = 0x63, // flag + DW_AT_object_pointer = 0x64, // reference + DW_AT_endianity = 0x65, // constant + DW_AT_elemental = 0x66, // flag + DW_AT_pure = 0x67, // flag + DW_AT_recursive = 0x68, // flag + + DW_AT_lo_user = 0x2000, // --- + DW_AT_hi_user = 0x3fff, // --- + +}; + +// Table 21 +enum +{ + DW_FORM_addr = 0x01, // address + DW_FORM_block2 = 0x03, // block + DW_FORM_block4 = 0x04, // block + DW_FORM_data2 = 0x05, // constant + DW_FORM_data4 = 0x06, // constant, lineptr, loclistptr, macptr, rangelistptr + DW_FORM_data8 = 0x07, // constant, lineptr, loclistptr, macptr, rangelistptr + DW_FORM_string = 0x08, // string + DW_FORM_block = 0x09, // block + DW_FORM_block1 = 0x0a, // block + DW_FORM_data1 = 0x0b, // constant + DW_FORM_flag = 0x0c, // flag + DW_FORM_sdata = 0x0d, // constant + DW_FORM_strp = 0x0e, // string + DW_FORM_udata = 0x0f, // constant + DW_FORM_ref_addr = 0x10, // reference + DW_FORM_ref1 = 0x11, // reference + DW_FORM_ref2 = 0x12, // reference + DW_FORM_ref4 = 0x13, // reference + DW_FORM_ref8 = 0x14, // reference + DW_FORM_ref_udata = 0x15, // reference + DW_FORM_indirect = 0x16, // (see Section 7.5.3) +}; + +// Table 24 (#operands, notes) +enum +{ + DW_OP_addr = 0x03, // 1 constant address (size target specific) + DW_OP_deref = 0x06, // 0 + DW_OP_const1u = 0x08, // 1 1-byte constant + DW_OP_const1s = 0x09, // 1 1-byte constant + DW_OP_const2u = 0x0a, // 1 2-byte constant + DW_OP_const2s = 0x0b, // 1 2-byte constant + DW_OP_const4u = 0x0c, // 1 4-byte constant + DW_OP_const4s = 0x0d, // 1 4-byte constant + DW_OP_const8u = 0x0e, // 1 8-byte constant + DW_OP_const8s = 0x0f, // 1 8-byte constant + DW_OP_constu = 0x10, // 1 ULEB128 constant + DW_OP_consts = 0x11, // 1 SLEB128 constant + DW_OP_dup = 0x12, // 0 + DW_OP_drop = 0x13, // 0 + DW_OP_over = 0x14, // 0 + DW_OP_pick = 0x15, // 1 1-byte stack index + DW_OP_swap = 0x16, // 0 + DW_OP_rot = 0x17, // 0 + DW_OP_xderef = 0x18, // 0 + DW_OP_abs = 0x19, // 0 + DW_OP_and = 0x1a, // 0 + DW_OP_div = 0x1b, // 0 + DW_OP_minus = 0x1c, // 0 + DW_OP_mod = 0x1d, // 0 + DW_OP_mul = 0x1e, // 0 + DW_OP_neg = 0x1f, // 0 + DW_OP_not = 0x20, // 0 + DW_OP_or = 0x21, // 0 + DW_OP_plus = 0x22, // 0 + DW_OP_plus_uconst = 0x23, // 1 ULEB128 addend + DW_OP_shl = 0x24, // 0 + DW_OP_shr = 0x25, // 0 + DW_OP_shra = 0x26, // 0 + DW_OP_xor = 0x27, // 0 + DW_OP_skip = 0x2f, // 1 signed 2-byte constant + DW_OP_bra = 0x28, // 1 signed 2-byte constant + DW_OP_eq = 0x29, // 0 + DW_OP_ge = 0x2a, // 0 + DW_OP_gt = 0x2b, // 0 + DW_OP_le = 0x2c, // 0 + DW_OP_lt = 0x2d, // 0 + DW_OP_ne = 0x2e, // 0 + DW_OP_lit0 = 0x30, // 0 ... + DW_OP_lit31 = 0x4f, // 0 literals 0..31 = (DW_OP_lit0 + + // literal) + DW_OP_reg0 = 0x50, // 0 .. + DW_OP_reg31 = 0x6f, // 0 reg 0..31 = (DW_OP_reg0 + regnum) + DW_OP_breg0 = 0x70, // 1 ... + DW_OP_breg31 = 0x8f, // 1 SLEB128 offset base register 0..31 = (DW_OP_breg0 + regnum) + DW_OP_regx = 0x90, // 1 ULEB128 register + DW_OP_fbreg = 0x91, // 1 SLEB128 offset + DW_OP_bregx = 0x92, // 2 ULEB128 register followed by SLEB128 offset + DW_OP_piece = 0x93, // 1 ULEB128 size of piece addressed + DW_OP_deref_size = 0x94, // 1 1-byte size of data retrieved + DW_OP_xderef_size = 0x95, // 1 1-byte size of data retrieved + DW_OP_nop = 0x96, // 0 + DW_OP_push_object_address = 0x97, // 0 + DW_OP_call2 = 0x98, // 1 2-byte offset of DIE + DW_OP_call4 = 0x99, // 1 4-byte offset of DIE + DW_OP_call_ref = 0x9a, // 1 4- or 8-byte offset of DIE + DW_OP_form_tls_address = 0x9b, // 0 + DW_OP_call_frame_cfa = 0x9c, // 0 + DW_OP_bit_piece = 0x9d, // 2 + DW_OP_lo_user = 0xe0, + DW_OP_hi_user = 0xff, +}; + +// Table 25 +enum +{ + DW_ATE_address = 0x01, + DW_ATE_boolean = 0x02, + DW_ATE_complex_float = 0x03, + DW_ATE_float = 0x04, + DW_ATE_signed = 0x05, + DW_ATE_signed_char = 0x06, + DW_ATE_unsigned = 0x07, + DW_ATE_unsigned_char = 0x08, + DW_ATE_imaginary_float = 0x09, + DW_ATE_packed_decimal = 0x0a, + DW_ATE_numeric_string = 0x0b, + DW_ATE_edited = 0x0c, + DW_ATE_signed_fixed = 0x0d, + DW_ATE_unsigned_fixed = 0x0e, + DW_ATE_decimal_float = 0x0f, + DW_ATE_lo_user = 0x80, + DW_ATE_hi_user = 0xff, +}; + +// Table 26 +enum +{ + DW_DS_unsigned = 0x01, + DW_DS_leading_overpunch = 0x02, + DW_DS_trailing_overpunch = 0x03, + DW_DS_leading_separate = 0x04, + DW_DS_trailing_separate = 0x05, +}; + +// Table 27 +enum +{ + DW_END_default = 0x00, + DW_END_big = 0x01, + DW_END_little = 0x02, + DW_END_lo_user = 0x40, + DW_END_hi_user = 0xff, +}; + +// Table 28 +enum +{ + DW_ACCESS_public = 0x01, + DW_ACCESS_protected = 0x02, + DW_ACCESS_private = 0x03, +}; + +// Table 29 +enum +{ + DW_VIS_local = 0x01, + DW_VIS_exported = 0x02, + DW_VIS_qualified = 0x03, +}; + +// Table 30 +enum +{ + DW_VIRTUALITY_none = 0x00, + DW_VIRTUALITY_virtual = 0x01, + DW_VIRTUALITY_pure_virtual = 0x02, +}; + +// Table 31 +enum +{ + DW_LANG_C89 = 0x0001, + DW_LANG_C = 0x0002, + DW_LANG_Ada83 = 0x0003, + DW_LANG_C_plus_plus = 0x0004, + DW_LANG_Cobol74 = 0x0005, + DW_LANG_Cobol85 = 0x0006, + DW_LANG_Fortran77 = 0x0007, + DW_LANG_Fortran90 = 0x0008, + DW_LANG_Pascal83 = 0x0009, + DW_LANG_Modula2 = 0x000a, + // Dwarf3 + DW_LANG_Java = 0x000b, + DW_LANG_C99 = 0x000c, + DW_LANG_Ada95 = 0x000d, + DW_LANG_Fortran95 = 0x000e, + DW_LANG_PLI = 0x000f, + DW_LANG_ObjC = 0x0010, + DW_LANG_ObjC_plus_plus = 0x0011, + DW_LANG_UPC = 0x0012, + DW_LANG_D = 0x0013, + // Dwarf4 + DW_LANG_Python = 0x0014, + // Dwarf5 + DW_LANG_Go = 0x0016, + + DW_LANG_lo_user = 0x8000, + DW_LANG_hi_user = 0xffff, +}; + +// Table 32 +enum +{ + DW_ID_case_sensitive = 0x00, + DW_ID_up_case = 0x01, + DW_ID_down_case = 0x02, + DW_ID_case_insensitive = 0x03, +}; + +// Table 33 +enum +{ + DW_CC_normal = 0x01, + DW_CC_program = 0x02, + DW_CC_nocall = 0x03, + DW_CC_lo_user = 0x40, + DW_CC_hi_user = 0xff, +}; + +// Table 34 +enum +{ + DW_INL_not_inlined = 0x00, + DW_INL_inlined = 0x01, + DW_INL_declared_not_inlined = 0x02, + DW_INL_declared_inlined = 0x03, +}; + +// Table 35 +enum +{ + DW_ORD_row_major = 0x00, + DW_ORD_col_major = 0x01, +}; + +// Table 36 +enum +{ + DW_DSC_label = 0x00, + DW_DSC_range = 0x01, +}; + +// Table 37 +enum +{ + DW_LNS_copy = 0x01, + DW_LNS_advance_pc = 0x02, + DW_LNS_advance_line = 0x03, + DW_LNS_set_file = 0x04, + DW_LNS_set_column = 0x05, + DW_LNS_negate_stmt = 0x06, + DW_LNS_set_basic_block = 0x07, + DW_LNS_const_add_pc = 0x08, + DW_LNS_fixed_advance_pc = 0x09, + // Dwarf3 + DW_LNS_set_prologue_end = 0x0a, + DW_LNS_set_epilogue_begin = 0x0b, + DW_LNS_set_isa = 0x0c, +}; + +// Table 38 +enum +{ + DW_LNE_end_sequence = 0x01, + DW_LNE_set_address = 0x02, + DW_LNE_define_file = 0x03, + DW_LNE_lo_user = 0x80, + DW_LNE_hi_user = 0xff, +}; + +// Table 39 +enum +{ + DW_MACINFO_define = 0x01, + DW_MACINFO_undef = 0x02, + DW_MACINFO_start_file = 0x03, + DW_MACINFO_end_file = 0x04, + DW_MACINFO_vendor_ext = 0xff, +}; + +// Table 40. +enum +{ // operand,... + DW_CFA_nop = 0x00, + DW_CFA_set_loc = 0x01, // address + DW_CFA_advance_loc1 = 0x02, // 1-byte delta + DW_CFA_advance_loc2 = 0x03, // 2-byte delta + DW_CFA_advance_loc4 = 0x04, // 4-byte delta + DW_CFA_offset_extended = 0x05, // ULEB128 register, ULEB128 offset + DW_CFA_restore_extended = 0x06, // ULEB128 register + DW_CFA_undefined = 0x07, // ULEB128 register + DW_CFA_same_value = 0x08, // ULEB128 register + DW_CFA_register = 0x09, // ULEB128 register, ULEB128 register + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, // ULEB128 register, ULEB128 offset + DW_CFA_def_cfa_register = 0x0d, // ULEB128 register + DW_CFA_def_cfa_offset = 0x0e, // ULEB128 offset + DW_CFA_def_cfa_expression = 0x0f, // BLOCK + DW_CFA_expression = 0x10, // ULEB128 register, BLOCK + DW_CFA_offset_extended_sf = 0x11, // ULEB128 register, SLEB128 offset + DW_CFA_def_cfa_sf = 0x12, // ULEB128 register, SLEB128 offset + DW_CFA_def_cfa_offset_sf = 0x13, // SLEB128 offset + DW_CFA_val_offset = 0x14, // ULEB128, ULEB128 + DW_CFA_val_offset_sf = 0x15, // ULEB128, SLEB128 + DW_CFA_val_expression = 0x16, // ULEB128, BLOCK + + DW_CFA_lo_user = 0x1c, + DW_CFA_hi_user = 0x3f, + + // Opcodes that take an addend operand. + DW_CFA_advance_loc = 0x1<<6, // +delta + DW_CFA_offset = 0x2<<6, // +register (ULEB128 offset) + DW_CFA_restore = 0x3<<6, // +register +}; diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c index c5d58576d..d5b0b0311 100644 --- a/src/cmd/ld/elf.c +++ b/src/cmd/ld/elf.c @@ -21,6 +21,16 @@ static ElfPhdr *phdr[NSECT]; static ElfShdr *shdr[NSECT]; static char *interp; +typedef struct Elfstring Elfstring; +struct Elfstring +{ + char *s; + int off; +}; + +static Elfstring elfstr[100]; +static int nelfstr; + /* Initialize the global variable that describes the ELF header. It will be updated as we write section and prog headers. @@ -122,6 +132,18 @@ elfwriteshdrs(void) return hdr.shnum * ELF32SHDRSIZE; } +void +elfsetstring(char *s, int off) +{ + if(nelfstr >= nelem(elfstr)) { + diag("too many elf strings"); + errorexit(); + } + elfstr[nelfstr].s = s; + elfstr[nelfstr].off = off; + nelfstr++; +} + uint32 elfwritephdrs(void) { @@ -142,8 +164,7 @@ newElfPhdr(void) { ElfPhdr *e; - e = malloc(sizeof *e); - memset(e, 0, sizeof *e); + e = mal(sizeof *e); if (hdr.phnum >= NSECT) diag("too many phdrs"); else @@ -167,8 +188,7 @@ newElfShdr(vlong name) { ElfShdr *e; - e = malloc(sizeof *e); - memset(e, 0, sizeof *e); + e = mal(sizeof *e); e->name = name; if (hdr.shnum >= NSECT) { diag("too many shdrs"); @@ -294,7 +314,7 @@ elfwriteinterp(void) n = strlen(interp)+1; seek(cout, ELFRESERVE-n, 0); - write(cout, interp, n); + ewrite(cout, interp, n); return n; } @@ -310,17 +330,25 @@ elfinterp(ElfShdr *sh, uint64 startva, char *p) sh->size = n; } +extern int nelfsym; + void -elfdynhash(int nsym) +elfdynhash(void) { Sym *s, *sy; int i, h, nbucket, b; uchar *pc; uint32 hc, g; uint32 *chain, *buckets; + int nsym; + char *name; + + if(!iself) + return; + nsym = nelfsym; s = lookup(".hash", 0); - s->type = SELFDATA; // TODO: rodata + s->type = SELFDATA; s->reachable = 1; i = nsym; @@ -331,17 +359,24 @@ elfdynhash(int nsym) } chain = malloc(nsym * sizeof(uint32)); - memset(chain, 0, nsym * sizeof(uint32)); buckets = malloc(nbucket * sizeof(uint32)); + if(chain == nil || buckets == nil) { + cursym = nil; + diag("out of memory"); + errorexit(); + } + memset(chain, 0, nsym * sizeof(uint32)); memset(buckets, 0, nbucket * sizeof(uint32)); - i = 1; for(h = 0; hlink) { - if (!sy->reachable || (sy->type != STEXT && sy->type != SDATA && sy->type != SBSS) || sy->dynimpname == nil) + for(sy=hash[h]; sy!=S; sy=sy->hash) { + if (sy->dynid <= 0) continue; hc = 0; - for(pc = (uchar*)sy->dynimpname; *pc; pc++) { + name = sy->dynimpname; + if(name == nil) + name = sy->name; + for(pc = (uchar*)name; *pc; pc++) { hc = (hc<<4) + *pc; g = hc & 0xf0000000; hc ^= g >> 24; @@ -349,9 +384,8 @@ elfdynhash(int nsym) } b = hc % nbucket; - chain[i] = buckets[b]; - buckets[b] = i; - i++; + chain[sy->dynid] = buckets[b]; + buckets[b] = sy->dynid; } } @@ -364,4 +398,64 @@ elfdynhash(int nsym) free(chain); free(buckets); + + elfwritedynent(lookup(".dynamic", 0), DT_NULL, 0); +} + +ElfPhdr* +elfphload(Segment *seg) +{ + ElfPhdr *ph; + + ph = newElfPhdr(); + ph->type = PT_LOAD; + if(seg->rwx & 4) + ph->flags |= PF_R; + if(seg->rwx & 2) + ph->flags |= PF_W; + if(seg->rwx & 1) + ph->flags |= PF_X; + ph->vaddr = seg->vaddr; + ph->paddr = seg->vaddr; + ph->memsz = seg->len; + ph->off = seg->fileoff; + ph->filesz = seg->filelen; + ph->align = INITRND; + + return ph; +} + +ElfShdr* +elfshbits(Section *sect) +{ + int i, off; + ElfShdr *sh; + + for(i=0; iname, elfstr[i].s) == 0) { + off = elfstr[i].off; + goto found; + } + } + diag("cannot find elf name %s", sect->name); + errorexit(); + return nil; + +found: + sh = newElfShdr(off); + if(sect->vaddr < sect->seg->vaddr + sect->seg->filelen) + sh->type = SHT_PROGBITS; + else + sh->type = SHT_NOBITS; + sh->flags = SHF_ALLOC; + if(sect->rwx & 1) + sh->flags |= SHF_EXECINSTR; + if(sect->rwx & 2) + sh->flags |= SHF_WRITE; + sh->addr = sect->vaddr; + sh->addralign = PtrSize; + sh->size = sect->len; + sh->off = sect->seg->fileoff + sect->vaddr - sect->seg->vaddr; + + return sh; } diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h index 9b5fdb17e..df15cb115 100644 --- a/src/cmd/ld/elf.h +++ b/src/cmd/ld/elf.h @@ -964,7 +964,10 @@ extern int numelfshdr; extern int iself; int elfwriteinterp(void); void elfinterp(ElfShdr*, uint64, char*); -void elfdynhash(int); +void elfdynhash(void); +ElfPhdr* elfphload(Segment*); +ElfShdr* elfshbits(Section*); +void elfsetstring(char*, int); /* * Total amount of space to reserve at the start of the file @@ -972,5 +975,4 @@ void elfdynhash(int); * May waste some. * On FreeBSD, cannot be larger than a page. */ -#define ELFRESERVE 2048 - +#define ELFRESERVE 3072 diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c index 015f34db2..8966b2a1f 100644 --- a/src/cmd/ld/go.c +++ b/src/cmd/ld/go.c @@ -66,12 +66,11 @@ ilookup(char *name) } static void loadpkgdata(char*, char*, char*, int); -static void loaddynimport(char*, char*, int); +static void loaddynimport(char*, char*, char*, int); static void loaddynexport(char*, char*, char*, int); static int parsemethod(char**, char*, char**); static int parsepkgdata(char*, char*, char**, char*, char**, char**, char**); -static int ndynexp; static Sym **dynexp; void @@ -194,7 +193,7 @@ ldpkg(Biobuf *f, char *pkg, int64 len, char *filename, int whence) errorexit(); return; } - loaddynimport(filename, p0 + 1, p1 - (p0+1)); + loaddynimport(filename, pkg, p0 + 1, p1 - (p0+1)); } // look for dynexp section @@ -266,6 +265,10 @@ expandpkg(char *t0, char *pkg) // use malloc, not mal, so that caller can free w0 = malloc(strlen(t0) + strlen(pkg)*n); + if(w0 == nil) { + diag("out of memory"); + errorexit(); + } w = w0; for(p=t=t0; (p=strstr(p, "\"\".")) != nil; p=t) { memmove(w, t, p - t); @@ -282,7 +285,7 @@ static int parsepkgdata(char *file, char *pkg, char **pp, char *ep, char **prefixp, char **namep, char **defp) { char *p, *prefix, *name, *def, *edef, *meth; - int n; + int n, inquote; // skip white space p = *pp; @@ -319,8 +322,19 @@ loop: // name: a.b followed by space name = p; - while(p < ep && *p != ' ') + inquote = 0; + while(p < ep) { + if (*p == ' ' && !inquote) + break; + + if(*p == '\\') + p++; + else if(*p == '"') + inquote = !inquote; + p++; + } + if(p >= ep) return -1; *p++ = '\0'; @@ -397,7 +411,7 @@ parsemethod(char **pp, char *ep, char **methp) } static void -loaddynimport(char *file, char *p, int n) +loaddynimport(char *file, char *pkg, char *p, int n) { char *pend, *next, *name, *def, *p0, *lib; Sym *s; @@ -431,10 +445,21 @@ loaddynimport(char *file, char *p, int n) // successful parse: now can edit the line *strchr(name, ' ') = 0; *strchr(def, ' ') = 0; + + if(strcmp(name, "_") == 0 && strcmp(def, "_") == 0) { + // allow #pragma dynimport _ _ "foo.so" + // to force a link of foo.so. + adddynlib(lib); + continue; + } + name = expandpkg(name, pkg); s = lookup(name, 0); - s->dynimplib = lib; - s->dynimpname = def; + if(s->type == 0 || s->type == SXREF) { + s->dynimplib = lib; + s->dynimpname = def; + s->type = SDYNIMPORT; + } } return; @@ -499,38 +524,19 @@ err: static int markdepth; static void -markdata(Prog *p, Sym *s) -{ - markdepth++; - if(p != P && debug['v'] > 1) - Bprint(&bso, "%d markdata %s\n", markdepth, s->name); - for(; p != P; p=p->dlink) - if(p->to.sym) - mark(p->to.sym); - markdepth--; -} - -static void -marktext(Prog *p) +marktext(Sym *s) { Auto *a; + Prog *p; - if(p == P) + if(s == S) return; - if(p->as != ATEXT) { - diag("marktext: %P", p); - return; - } - for(a=p->to.autom; a; a=a->link) - mark(a->gotype); markdepth++; if(debug['v'] > 1) - Bprint(&bso, "%d marktext %s\n", markdepth, p->from.sym->name); - for(a=p->to.autom; a; a=a->link) + Bprint(&bso, "%d marktext %s\n", markdepth, s->name); + for(a=s->autom; a; a=a->link) mark(a->gotype); - for(p=p->link; p != P; p=p->link) { - if(p->as == ATEXT || p->as == ADATA || p->as == AGLOBL) - break; + for(p=s->text; p != P; p=p->link) { if(p->from.sym) mark(p->from.sym); if(p->to.sym) @@ -542,45 +548,21 @@ marktext(Prog *p) void mark(Sym *s) { + int i; + if(s == S || s->reachable) return; s->reachable = 1; if(s->text) - marktext(s->text); - if(s->data) - markdata(s->data, s); + marktext(s); + for(i=0; inr; i++) + mark(s->r[i].sym); if(s->gotype) mark(s->gotype); -} - -static void -sweeplist(Prog **first, Prog **last) -{ - int reachable; - Prog *p, *q; - - reachable = 1; - q = P; - for(p=*first; p != P; p=p->link) { - switch(p->as) { - case ATEXT: - case ADATA: - case AGLOBL: - reachable = p->from.sym->reachable; - } - if(reachable) { - if(q == P) - *first = p; - else - q->link = p; - q = p; - } - } - if(q == P) - *first = P; - else - q->link = P; - *last = q; + if(s->sub) + mark(s->sub); + if(s->outer) + mark(s->outer); } static char* @@ -602,10 +584,43 @@ morename[] = "runtime.morestack48", }; +static int +isz(Auto *a) +{ + for(; a; a=a->link) + if(a->type == D_FILE || a->type == D_FILE1) + return 1; + return 0; +} + +static void +addz(Sym *s, Auto *z) +{ + Auto *a, *last; + + // strip out non-z + last = nil; + for(a = z; a != nil; a = a->link) { + if(a->type == D_FILE || a->type == D_FILE1) { + if(last == nil) + z = a; + else + last->link = a; + last = a; + } + } + if(last) { + last->link = s->autom; + s->autom = z; + } +} + void deadcode(void) { int i; + Sym *s, *last; + Auto *z; if(debug['v']) Bprint(&bso, "%5.2f deadcode\n", cputime()); @@ -616,7 +631,38 @@ deadcode(void) for(i=0; inext) { + if(!s->reachable) { + if(isz(s->autom)) + z = s->autom; + continue; + } + if(last == nil) + textp = s; + else + last->next = s; + last = s; + if(z != nil) { + if(!isz(s->autom)) + addz(s, z); + z = nil; + } + } + if(last == nil) + textp = nil; + else + last->next = nil; +} - // remove dead data - sweeplist(&datap, &edatap); +void +addexport(void) +{ + int i; + + for(i=0; iident, ElfMagic, 4) != 0) + goto bad; + switch(hdr->ident[5]) { + case ElfDataLsb: + e = ≤ + break; + case ElfDataMsb: + e = &be; + break; + default: + goto bad; + } + + // read header + obj = mal(sizeof *obj); + obj->e = e; + obj->f = f; + obj->base = base; + obj->len = len; + obj->name = pn; + + is64 = 0; + if(hdr->ident[4] == ElfClass64) { + ElfHdrBytes64* hdr; + + is64 = 1; + hdr = (ElfHdrBytes64*)hdrbuf; + obj->type = e->e16(hdr->type); + obj->machine = e->e16(hdr->machine); + obj->version = e->e32(hdr->version); + obj->phoff = e->e64(hdr->phoff); + obj->shoff = e->e64(hdr->shoff); + obj->flags = e->e32(hdr->flags); + obj->ehsize = e->e16(hdr->ehsize); + obj->phentsize = e->e16(hdr->phentsize); + obj->phnum = e->e16(hdr->phnum); + obj->shentsize = e->e16(hdr->shentsize); + obj->shnum = e->e16(hdr->shnum); + obj->shstrndx = e->e16(hdr->shstrndx); + } else { + obj->type = e->e16(hdr->type); + obj->machine = e->e16(hdr->machine); + obj->version = e->e32(hdr->version); + obj->entry = e->e32(hdr->entry); + obj->phoff = e->e32(hdr->phoff); + obj->shoff = e->e32(hdr->shoff); + obj->flags = e->e32(hdr->flags); + obj->ehsize = e->e16(hdr->ehsize); + obj->phentsize = e->e16(hdr->phentsize); + obj->phnum = e->e16(hdr->phnum); + obj->shentsize = e->e16(hdr->shentsize); + obj->shnum = e->e16(hdr->shnum); + obj->shstrndx = e->e16(hdr->shstrndx); + } + obj->is64 = is64; + + if(hdr->ident[6] != obj->version) + goto bad; + + if(e->e16(hdr->type) != ElfTypeRelocatable) { + diag("%s: elf but not elf relocatable object"); + return; + } + + switch(thechar) { + default: + diag("%s: elf %s unimplemented", thestring); + return; + case '5': + if(e != &le || obj->machine != ElfMachArm || hdr->ident[4] != ElfClass32) { + diag("%s: elf object but not arm", pn); + return; + } + break; + case '6': + if(e != &le || obj->machine != ElfMachAmd64 || hdr->ident[4] != ElfClass64) { + diag("%s: elf object but not amd64", pn); + return; + } + break; + case '8': + if(e != &le || obj->machine != ElfMach386 || hdr->ident[4] != ElfClass32) { + diag("%s: elf object but not 386", pn); + return; + } + break; + } + + // load section list into memory. + obj->sect = mal(obj->shnum*sizeof obj->sect[0]); + obj->nsect = obj->shnum; + for(i=0; insect; i++) { + if(Bseek(f, base+obj->shoff+i*obj->shentsize, 0) < 0) + goto bad; + sect = &obj->sect[i]; + if(is64) { + ElfSectBytes64 b; + + werrstr("short read"); + if(Bread(f, &b, sizeof b) != sizeof b) + goto bad; + + sect->name = (char*)(uintptr)e->e32(b.name); + sect->type = e->e32(b.type); + sect->flags = e->e64(b.flags); + sect->addr = e->e64(b.addr); + sect->off = e->e64(b.off); + sect->size = e->e64(b.size); + sect->link = e->e32(b.link); + sect->info = e->e32(b.info); + sect->align = e->e64(b.align); + sect->entsize = e->e64(b.entsize); + } else { + ElfSectBytes b; + + werrstr("short read"); + if(Bread(f, &b, sizeof b) != sizeof b) + goto bad; + + sect->name = (char*)(uintptr)e->e32(b.name); + sect->type = e->e32(b.type); + sect->flags = e->e32(b.flags); + sect->addr = e->e32(b.addr); + sect->off = e->e32(b.off); + sect->size = e->e32(b.size); + sect->link = e->e32(b.link); + sect->info = e->e32(b.info); + sect->align = e->e32(b.align); + sect->entsize = e->e32(b.entsize); + } + } + + // read section string table and translate names + if(obj->shstrndx >= obj->nsect) { + werrstr("shstrndx out of range %d >= %d", obj->shstrndx, obj->nsect); + goto bad; + } + sect = &obj->sect[obj->shstrndx]; + if(map(obj, sect) < 0) + goto bad; + for(i=0; insect; i++) + if(obj->sect[i].name != nil) + obj->sect[i].name = (char*)sect->base + (uintptr)obj->sect[i].name; + + // load string table for symbols into memory. + obj->symtab = section(obj, ".symtab"); + if(obj->symtab == nil) { + // our work is done here - no symbols means nothing can refer to this file + return; + } + if(obj->symtab->link <= 0 || obj->symtab->link >= obj->nsect) { + diag("%s: elf object has symbol table with invalid string table link", pn); + return; + } + obj->symstr = &obj->sect[obj->symtab->link]; + if(is64) + obj->nsymtab = obj->symtab->size / sizeof(ElfSymBytes64); + else + obj->nsymtab = obj->symtab->size / sizeof(ElfSymBytes); + + if(map(obj, obj->symtab) < 0) + goto bad; + if(map(obj, obj->symstr) < 0) + goto bad; + + // load text and data segments into memory. + // they are not as small as the section lists, but we'll need + // the memory anyway for the symbol images, so we might + // as well use one large chunk. + + // create symbols for mapped sections + for(i=0; insect; i++) { + sect = &obj->sect[i]; + if((sect->type != ElfSectProgbits && sect->type != ElfSectNobits) || !(sect->flags&ElfSectFlagAlloc)) + continue; + if(sect->type != ElfSectNobits && map(obj, sect) < 0) + goto bad; + + name = smprint("%s(%s)", pn, sect->name); + s = lookup(name, version); + free(name); + switch(sect->flags&(ElfSectFlagAlloc|ElfSectFlagWrite|ElfSectFlagExec)) { + default: + werrstr("unexpected flags for ELF section %s", sect->name); + goto bad; + case ElfSectFlagAlloc: + s->type = SRODATA; + break; + case ElfSectFlagAlloc + ElfSectFlagWrite: + s->type = SDATA; + break; + case ElfSectFlagAlloc + ElfSectFlagExec: + s->type = STEXT; + break; + } + if(sect->type == ElfSectProgbits) { + s->p = sect->base; + s->np = sect->size; + } + s->size = sect->size; + if(s->type == STEXT) { + if(etextp) + etextp->next = s; + else + textp = s; + etextp = s; + } + sect->sym = s; + } + + // load relocations + for(i=0; insect; i++) { + rsect = &obj->sect[i]; + if(rsect->type != ElfSectRela && rsect->type != ElfSectRel) + continue; + if(rsect->info >= obj->nsect || obj->sect[rsect->info].base == nil) + continue; + sect = &obj->sect[rsect->info]; + if(map(obj, rsect) < 0) + goto bad; + rela = rsect->type == ElfSectRela; + n = rsect->size/(4+4*is64)/(2+rela); + r = mal(n*sizeof r[0]); + p = rsect->base; + dp = sect->base; + for(j=0; joff = e->e64(p); + p += 8; + info = e->e64(p); + p += 8; + if(rela) { + add = e->e64(p); + p += 8; + } + } else { + // 32-bit rel/rela + rp->off = e->e32(p); + p += 4; + info = e->e32(p); + info = info>>8<<32 | (info&0xff); // convert to 64-bit info + p += 4; + if(rela) { + add = e->e32(p); + p += 4; + } + } + if(readsym(obj, info>>32, &sym) < 0) + goto bad; + if(sym.sym == nil) { + werrstr("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", + sect->sym->name, j, (int)(info>>32), sym.name, sym.shndx, sym.type); + goto bad; + } + rp->sym = sym.sym; + rp->type = reltype(pn, (uint32)info, &rp->siz); + if(rela) + rp->add = add; + else { + // load addend from image + if(rp->siz == 4) + rp->add = e->e32(sect->base+rp->off); + else if(rp->siz == 8) + rp->add = e->e64(sect->base+rp->off); + else + diag("invalid rela size %d", rp->siz); + } + } + qsort(r, n, sizeof r[0], rbyoff); // just in case + + s = sect->sym; + s->r = r; + s->nr = n; + } + + // enter sub-symbols into symbol table. + // symbol 0 is the null symbol. + for(i=1; insymtab; i++) { + if(readsym(obj, i, &sym) < 0) + goto bad; + if(sym.type != ElfSymTypeFunc && sym.type != ElfSymTypeObject && sym.type != ElfSymTypeNone) + continue; + if(sym.shndx == ElfSymShnCommon) { + s = sym.sym; + if(s->size < sym.size) + s->size = sym.size; + if(s->type == 0 || s->type == SXREF) + s->type = SBSS; + continue; + } + if(sym.shndx >= obj->nsect || sym.shndx == 0) + continue; + sect = obj->sect+sym.shndx; + if(sect->sym == nil) { + diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type); + continue; + } + s = sym.sym; + s->sub = sect->sym->sub; + sect->sym->sub = s; + s->type = sect->sym->type | SSUB; + if(!s->dynexport) { + s->dynimplib = nil; // satisfy dynimport + s->dynimpname = nil; // satisfy dynimport + } + s->value = sym.value; + s->size = sym.size; + s->outer = sect->sym; + if(sect->sym->type == STEXT) { + Prog *p; + + if(s->text != P) + diag("%s: duplicate definition of %s", pn, s->name); + // build a TEXT instruction with a unique pc + // just to make the rest of the linker happy. + p = prg(); + p->as = ATEXT; + p->from.type = D_EXTERN; + p->from.sym = s; + p->textflag = 7; + p->to.type = D_CONST; + p->link = nil; + p->pc = pc++; + s->text = p; + + etextp->next = s; + etextp = s; + } + } + return; + +bad: + diag("%s: malformed elf file: %r", pn); +} + +static ElfSect* +section(ElfObj *obj, char *name) +{ + int i; + + for(i=0; insect; i++) + if(obj->sect[i].name && name && strcmp(obj->sect[i].name, name) == 0) + return &obj->sect[i]; + return nil; +} + +static int +map(ElfObj *obj, ElfSect *sect) +{ + if(sect->base != nil) + return 0; + + if(sect->off+sect->size > obj->len) { + werrstr("elf section past end of file"); + return -1; + } + + sect->base = mal(sect->size); + werrstr("short read"); + if(Bseek(obj->f, obj->base+sect->off, 0) < 0 || Bread(obj->f, sect->base, sect->size) != sect->size) + return -1; + + return 0; +} + +static int +readsym(ElfObj *obj, int i, ElfSym *sym) +{ + Sym *s; + + if(i >= obj->nsymtab || i < 0) { + werrstr("invalid elf symbol index"); + return -1; + } + + if(obj->is64) { + ElfSymBytes64 *b; + + b = (ElfSymBytes64*)(obj->symtab->base + i*sizeof *b); + sym->name = (char*)obj->symstr->base + obj->e->e32(b->name); + sym->value = obj->e->e64(b->value); + sym->size = obj->e->e64(b->size); + sym->shndx = obj->e->e16(b->shndx); + sym->bind = b->info>>4; + sym->type = b->info&0xf; + sym->other = b->other; + } else { + ElfSymBytes *b; + + b = (ElfSymBytes*)(obj->symtab->base + i*sizeof *b); + sym->name = (char*)obj->symstr->base + obj->e->e32(b->name); + sym->value = obj->e->e32(b->value); + sym->size = obj->e->e32(b->size); + sym->shndx = obj->e->e16(b->shndx); + sym->bind = b->info>>4; + sym->type = b->info&0xf; + sym->other = b->other; + } + + s = nil; + if(strcmp(sym->name, "_GLOBAL_OFFSET_TABLE_") == 0) + sym->name = ".got"; + if(strcmp(sym->name, "__stack_chk_fail_local") == 0) + sym->other = 0; // rewrite hidden -> default visibility + switch(sym->type) { + case ElfSymTypeSection: + s = obj->sect[sym->shndx].sym; + break; + case ElfSymTypeObject: + case ElfSymTypeFunc: + case ElfSymTypeNone: + switch(sym->bind) { + case ElfSymBindGlobal: + if(sym->other != 2) { + s = lookup(sym->name, 0); + break; + } + // fall through + case ElfSymBindLocal: + s = lookup(sym->name, version); + break; + default: + werrstr("%s: invalid symbol binding %d", sym->name, sym->bind); + return -1; + } + break; + } + if(s != nil && s->type == 0 && sym->type != ElfSymTypeSection) + s->type = SXREF; + sym->sym = s; + + return 0; +} + +int +rbyoff(const void *va, const void *vb) +{ + Reloc *a, *b; + + a = (Reloc*)va; + b = (Reloc*)vb; + if(a->off < b->off) + return -1; + if(a->off > b->off) + return +1; + return 0; +} + +#define R(x, y) ((x)|((y)<<24)) + +static int +reltype(char *pn, int elftype, uchar *siz) +{ + switch(R(thechar, elftype)) { + default: + diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype); + case R('6', R_X86_64_PC32): + case R('6', R_X86_64_PLT32): + case R('6', R_X86_64_GOTPCREL): + case R('8', R_386_32): + case R('8', R_386_PC32): + case R('8', R_386_GOT32): + case R('8', R_386_PLT32): + case R('8', R_386_GOTOFF): + case R('8', R_386_GOTPC): + *siz = 4; + break; + case R('6', R_X86_64_64): + *siz = 8; + break; + } + + return 256+elftype; +} diff --git a/src/cmd/ld/ldmacho.c b/src/cmd/ld/ldmacho.c new file mode 100644 index 000000000..7e38db0e4 --- /dev/null +++ b/src/cmd/ld/ldmacho.c @@ -0,0 +1,794 @@ +/* +Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c +http://code.swtch.com/plan9port/src/tip/src/libmach/ + + Copyright © 2004 Russ Cox. + Portions Copyright © 2008-2010 Google Inc. + Portions Copyright © 2010 The Go Authors. + +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. +*/ + +#include "l.h" +#include "lib.h" + +enum { + MACHO_FAKE_GOTPCREL = 100, // from macho.h + + N_EXT = 0x01, + N_TYPE = 0x1e, + N_STAB = 0xe0, +}; + +typedef struct MachoObj MachoObj; +typedef struct MachoCmd MachoCmd; +typedef struct MachoSeg MachoSeg; +typedef struct MachoSect MachoSect; +typedef struct MachoRel MachoRel; +typedef struct MachoSymtab MachoSymtab; +typedef struct MachoSym MachoSym; +typedef struct MachoDysymtab MachoDysymtab; + +enum +{ + MachoCpuVax = 1, + MachoCpu68000 = 6, + MachoCpu386 = 7, + MachoCpuAmd64 = 0x1000007, + MachoCpuMips = 8, + MachoCpu98000 = 10, + MachoCpuHppa = 11, + MachoCpuArm = 12, + MachoCpu88000 = 13, + MachoCpuSparc = 14, + MachoCpu860 = 15, + MachoCpuAlpha = 16, + MachoCpuPower = 18, + + MachoCmdSegment = 1, + MachoCmdSymtab = 2, + MachoCmdSymseg = 3, + MachoCmdThread = 4, + MachoCmdDysymtab = 11, + MachoCmdSegment64 = 25, + + MachoFileObject = 1, + MachoFileExecutable = 2, + MachoFileFvmlib = 3, + MachoFileCore = 4, + MachoFilePreload = 5, +}; + +struct MachoSeg +{ + char name[16+1]; + uint64 vmaddr; + uint64 vmsize; + uint32 fileoff; + uint32 filesz; + uint32 maxprot; + uint32 initprot; + uint32 nsect; + uint32 flags; + MachoSect *sect; +}; + +struct MachoSect +{ + char name[16+1]; + char segname[16+1]; + uint64 addr; + uint64 size; + uint32 off; + uint32 align; + uint32 reloff; + uint32 nreloc; + uint32 flags; + uint32 res1; + uint32 res2; + Sym *sym; + + MachoRel *rel; +}; + +struct MachoRel +{ + uint32 addr; + uint32 symnum; + uint8 pcrel; + uint8 length; + uint8 extrn; + uint8 type; + uint8 scattered; + uint32 value; +}; + +struct MachoSymtab +{ + uint32 symoff; + uint32 nsym; + uint32 stroff; + uint32 strsize; + + char *str; + MachoSym *sym; +}; + +struct MachoSym +{ + char *name; + uint8 type; + uint8 sectnum; + uint16 desc; + char kind; + uint64 value; + Sym *sym; +}; + +struct MachoDysymtab +{ + uint32 ilocalsym; + uint32 nlocalsym; + uint32 iextdefsym; + uint32 nextdefsym; + uint32 iundefsym; + uint32 nundefsym; + uint32 tocoff; + uint32 ntoc; + uint32 modtaboff; + uint32 nmodtab; + uint32 extrefsymoff; + uint32 nextrefsyms; + uint32 indirectsymoff; + uint32 nindirectsyms; + uint32 extreloff; + uint32 nextrel; + uint32 locreloff; + uint32 nlocrel; + uint32 *indir; +}; + +struct MachoCmd +{ + int type; + uint32 off; + uint32 size; + MachoSeg seg; + MachoSymtab sym; + MachoDysymtab dsym; +}; + +struct MachoObj +{ + Biobuf *f; + int64 base; // off in f where Mach-O begins + int64 len; // length of Mach-O + int is64; + char *name; + + Endian *e; + uint cputype; + uint subcputype; + uint32 filetype; + uint32 flags; + MachoCmd *cmd; + uint ncmd; +}; + +static int +unpackcmd(uchar *p, MachoObj *m, MachoCmd *c, uint type, uint sz) +{ + uint32 (*e4)(uchar*); + uint64 (*e8)(uchar*); + MachoSect *s; + int i; + + e4 = m->e->e32; + e8 = m->e->e64; + + c->type = type; + c->size = sz; + switch(type){ + default: + return -1; + case MachoCmdSegment: + if(sz < 56) + return -1; + strecpy(c->seg.name, c->seg.name+sizeof c->seg.name, (char*)p+8); + c->seg.vmaddr = e4(p+24); + c->seg.vmsize = e4(p+28); + c->seg.fileoff = e4(p+32); + c->seg.filesz = e4(p+36); + c->seg.maxprot = e4(p+40); + c->seg.initprot = e4(p+44); + c->seg.nsect = e4(p+48); + c->seg.flags = e4(p+52); + c->seg.sect = mal(c->seg.nsect * sizeof c->seg.sect[0]); + if(sz < 56+c->seg.nsect*68) + return -1; + p += 56; + for(i=0; iseg.nsect; i++) { + s = &c->seg.sect[i]; + strecpy(s->name, s->name+sizeof s->name, (char*)p+0); + strecpy(s->segname, s->segname+sizeof s->segname, (char*)p+16); + s->addr = e4(p+32); + s->size = e4(p+36); + s->off = e4(p+40); + s->align = e4(p+44); + s->reloff = e4(p+48); + s->nreloc = e4(p+52); + s->flags = e4(p+56); + s->res1 = e4(p+60); + s->res2 = e4(p+64); + p += 68; + } + break; + case MachoCmdSegment64: + if(sz < 72) + return -1; + strecpy(c->seg.name, c->seg.name+sizeof c->seg.name, (char*)p+8); + c->seg.vmaddr = e8(p+24); + c->seg.vmsize = e8(p+32); + c->seg.fileoff = e8(p+40); + c->seg.filesz = e8(p+48); + c->seg.maxprot = e4(p+56); + c->seg.initprot = e4(p+60); + c->seg.nsect = e4(p+64); + c->seg.flags = e4(p+68); + c->seg.sect = mal(c->seg.nsect * sizeof c->seg.sect[0]); + if(sz < 72+c->seg.nsect*80) + return -1; + p += 72; + for(i=0; iseg.nsect; i++) { + s = &c->seg.sect[i]; + strecpy(s->name, s->name+sizeof s->name, (char*)p+0); + strecpy(s->segname, s->segname+sizeof s->segname, (char*)p+16); + s->addr = e8(p+32); + s->size = e8(p+40); + s->off = e4(p+48); + s->align = e4(p+52); + s->reloff = e4(p+56); + s->nreloc = e4(p+60); + s->flags = e4(p+64); + s->res1 = e4(p+68); + s->res2 = e4(p+72); + // p+76 is reserved + p += 80; + } + break; + case MachoCmdSymtab: + if(sz < 24) + return -1; + c->sym.symoff = e4(p+8); + c->sym.nsym = e4(p+12); + c->sym.stroff = e4(p+16); + c->sym.strsize = e4(p+20); + break; + case MachoCmdDysymtab: + if(sz < 80) + return -1; + c->dsym.ilocalsym = e4(p+8); + c->dsym.nlocalsym = e4(p+12); + c->dsym.iextdefsym = e4(p+16); + c->dsym.nextdefsym = e4(p+20); + c->dsym.iundefsym = e4(p+24); + c->dsym.nundefsym = e4(p+28); + c->dsym.tocoff = e4(p+32); + c->dsym.ntoc = e4(p+36); + c->dsym.modtaboff = e4(p+40); + c->dsym.nmodtab = e4(p+44); + c->dsym.extrefsymoff = e4(p+48); + c->dsym.nextrefsyms = e4(p+52); + c->dsym.indirectsymoff = e4(p+56); + c->dsym.nindirectsyms = e4(p+60); + c->dsym.extreloff = e4(p+64); + c->dsym.nextrel = e4(p+68); + c->dsym.locreloff = e4(p+72); + c->dsym.nlocrel = e4(p+76); + break; + } + return 0; +} + +static int +macholoadrel(MachoObj *m, MachoSect *sect) +{ + MachoRel *rel, *r; + uchar *buf, *p; + int i, n; + uint32 v; + + if(sect->rel != nil || sect->nreloc == 0) + return 0; + rel = mal(sect->nreloc * sizeof r[0]); + n = sect->nreloc * 8; + buf = mal(n); + if(Bseek(m->f, m->base + sect->reloff, 0) < 0 || Bread(m->f, buf, n) != n) + return -1; + for(i=0; inreloc; i++) { + r = &rel[i]; + p = buf+i*8; + r->addr = m->e->e32(p); + + // TODO(rsc): Wrong interpretation for big-endian bitfields? + if(r->addr & 0x80000000) { + // scatterbrained relocation + r->scattered = 1; + v = r->addr >> 24; + r->addr &= 0xFFFFFF; + r->type = v & 0xF; + v >>= 4; + r->length = 1<<(v&3); + v >>= 2; + r->pcrel = v & 1; + r->value = m->e->e32(p+4); + } else { + v = m->e->e32(p+4); + r->symnum = v & 0xFFFFFF; + v >>= 24; + r->pcrel = v&1; + v >>= 1; + r->length = 1<<(v&3); + v >>= 2; + r->extrn = v&1; + v >>= 1; + r->type = v; + } + } + sect->rel = rel; + return 0; +} + +static int +macholoaddsym(MachoObj *m, MachoDysymtab *d) +{ + uchar *p; + int i, n; + + n = d->nindirectsyms; + + p = mal(n*4); + if(Bseek(m->f, m->base + d->indirectsymoff, 0) < 0 || Bread(m->f, p, n*4) != n*4) + return -1; + + d->indir = (uint32*)p; + for(i=0; iindir[i] = m->e->e32(p+4*i); + return 0; +} + +static int +macholoadsym(MachoObj *m, MachoSymtab *symtab) +{ + char *strbuf; + uchar *symbuf, *p; + int i, n, symsize; + MachoSym *sym, *s; + uint32 v; + + if(symtab->sym != nil) + return 0; + + strbuf = mal(symtab->strsize); + if(Bseek(m->f, m->base + symtab->stroff, 0) < 0 || Bread(m->f, strbuf, symtab->strsize) != symtab->strsize) + return -1; + + symsize = 12; + if(m->is64) + symsize = 16; + n = symtab->nsym * symsize; + symbuf = mal(n); + if(Bseek(m->f, m->base + symtab->symoff, 0) < 0 || Bread(m->f, symbuf, n) != n) + return -1; + sym = mal(symtab->nsym * sizeof sym[0]); + p = symbuf; + for(i=0; insym; i++) { + s = &sym[i]; + v = m->e->e32(p); + if(v >= symtab->strsize) + return -1; + s->name = strbuf + v; + s->type = p[4]; + s->sectnum = p[5]; + s->desc = m->e->e16(p+6); + if(m->is64) + s->value = m->e->e64(p+8); + else + s->value = m->e->e32(p+8); + p += symsize; + } + symtab->str = strbuf; + symtab->sym = sym; + return 0; +} + +void +ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) +{ + int i, j, is64; + uchar hdr[7*4], *cmdp; + uchar tmp[4]; + uchar *dat; + ulong ncmd, cmdsz, ty, sz, off; + MachoObj *m; + Endian *e; + int64 base; + MachoSect *sect; + MachoRel *rel; + Sym *s, *outer; + MachoCmd *c; + MachoSymtab *symtab; + MachoDysymtab *dsymtab; + MachoSym *sym; + Reloc *r, *rp; + char *name; + + version++; + base = Boffset(f); + if(Bread(f, hdr, sizeof hdr) != sizeof hdr) + goto bad; + + if((be.e32(hdr)&~1) == 0xFEEDFACE){ + e = &be; + }else if((le.e32(hdr)&~1) == 0xFEEDFACE){ + e = ≤ + }else{ + werrstr("bad magic - not mach-o file"); + goto bad; + } + + is64 = e->e32(hdr) == 0xFEEDFACF; + ncmd = e->e32(hdr+4*4); + cmdsz = e->e32(hdr+5*4); + if(ncmd > 0x10000 || cmdsz >= 0x01000000){ + werrstr("implausible mach-o header ncmd=%lud cmdsz=%lud", ncmd, cmdsz); + goto bad; + } + if(is64) + Bread(f, tmp, 4); // skip reserved word in header + + m = mal(sizeof(*m)+ncmd*sizeof(MachoCmd)+cmdsz); + m->f = f; + m->e = e; + m->cputype = e->e32(hdr+1*4); + m->subcputype = e->e32(hdr+2*4); + m->filetype = e->e32(hdr+3*4); + m->ncmd = ncmd; + m->flags = e->e32(hdr+6*4); + m->is64 = is64; + m->base = base; + m->len = len; + m->name = pn; + + switch(thechar) { + default: + diag("%s: mach-o %s unimplemented", thestring); + return; + case '6': + if(e != &le || m->cputype != MachoCpuAmd64) { + diag("%s: mach-o object but not amd64", pn); + return; + } + break; + case '8': + if(e != &le || m->cputype != MachoCpu386) { + diag("%s: mach-o object but not 386", pn); + return; + } + break; + } + + m->cmd = (MachoCmd*)(m+1); + off = sizeof hdr; + cmdp = (uchar*)(m->cmd+ncmd); + if(Bread(f, cmdp, cmdsz) != cmdsz){ + werrstr("reading cmds: %r"); + goto bad; + } + + // read and parse load commands + c = nil; + symtab = nil; + dsymtab = nil; + for(i=0; ie32(cmdp); + sz = e->e32(cmdp+4); + m->cmd[i].off = off; + unpackcmd(cmdp, m, &m->cmd[i], ty, sz); + cmdp += sz; + off += sz; + if(ty == MachoCmdSymtab) { + if(symtab != nil) { + werrstr("multiple symbol tables"); + goto bad; + } + symtab = &m->cmd[i].sym; + macholoadsym(m, symtab); + } + if(ty == MachoCmdDysymtab) { + dsymtab = &m->cmd[i].dsym; + macholoaddsym(m, dsymtab); + } + if((is64 && ty == MachoCmdSegment64) || (!is64 && ty == MachoCmdSegment)) { + if(c != nil) { + werrstr("multiple load commands"); + goto bad; + } + c = &m->cmd[i]; + } + } + + // load text and data segments into memory. + // they are not as small as the load commands, but we'll need + // the memory anyway for the symbol images, so we might + // as well use one large chunk. + if(c == nil) { + werrstr("no load command"); + goto bad; + } + if(symtab == nil) { + // our work is done here - no symbols means nothing can refer to this file + return; + } + + if(c->seg.fileoff+c->seg.filesz >= len) { + werrstr("load segment out of range"); + goto bad; + } + + dat = mal(c->seg.filesz); + if(Bseek(f, m->base + c->seg.fileoff, 0) < 0 || Bread(f, dat, c->seg.filesz) != c->seg.filesz) { + werrstr("cannot load object data: %r"); + goto bad; + } + + for(i=0; iseg.nsect; i++) { + sect = &c->seg.sect[i]; + if(strcmp(sect->segname, "__TEXT") != 0 && strcmp(sect->segname, "__DATA") != 0) + continue; + if(strcmp(sect->name, "__eh_frame") == 0) + continue; + name = smprint("%s(%s/%s)", pn, sect->segname, sect->name); + s = lookup(name, version); + if(s->type != 0) { + werrstr("duplicate %s/%s", sect->segname, sect->name); + goto bad; + } + free(name); + s->p = dat + sect->addr - c->seg.vmaddr; + s->np = sect->size; + s->size = s->np; + + if(strcmp(sect->segname, "__TEXT") == 0) { + if(strcmp(sect->name, "__text") == 0) + s->type = STEXT; + else + s->type = SRODATA; + } else { + s->type = SDATA; + } + if(s->type == STEXT) { + if(etextp) + etextp->next = s; + else + textp = s; + etextp = s; + } + sect->sym = s; + } + + // enter sub-symbols into symbol table. + // have to guess sizes from next symbol. + for(i=0; insym; i++) { + int v; + sym = &symtab->sym[i]; + if(sym->type&N_STAB) + continue; + // TODO: check sym->type against outer->type. + name = sym->name; + if(name[0] == '_' && name[1] != '\0') + name++; + v = 0; + if(!(sym->type&N_EXT)) + v = version; + s = lookup(name, v); + sym->sym = s; + if(sym->sectnum == 0) // undefined + continue; + if(sym->sectnum > c->seg.nsect) { + werrstr("reference to invalid section %d", sym->sectnum); + goto bad; + } + sect = &c->seg.sect[sym->sectnum-1]; + outer = sect->sym; + if(outer == nil) { + werrstr("reference to invalid section %s/%s", sect->segname, sect->name); + continue; + } + s->type = outer->type | SSUB; + s->sub = outer->sub; + outer->sub = s; + s->outer = outer; + s->value = sym->value - sect->addr; + if(i+1 < symtab->nsym) + s->size = (sym+1)->value - sym->value; + else + s->size = sect->addr + sect->size - sym->value; + if(!s->dynexport) { + s->dynimplib = nil; // satisfy dynimport + s->dynimpname = nil; // satisfy dynimport + } + if(outer->type == STEXT) { + Prog *p; + + if(s->text != P) + diag("%s sym#%d: duplicate definition of %s", pn, i, s->name); + // build a TEXT instruction with a unique pc + // just to make the rest of the linker happy. + // TODO: this is too 6l-specific ? + p = prg(); + p->as = ATEXT; + p->from.type = D_EXTERN; + p->from.sym = s; + p->textflag = 7; + p->to.type = D_CONST; + p->link = nil; + p->pc = pc++; + s->text = p; + + etextp->next = s; + etextp = s; + } + sym->sym = s; + } + + // load relocations + for(i=0; iseg.nsect; i++) { + sect = &c->seg.sect[i]; + if((s = sect->sym) == S) + continue; + macholoadrel(m, sect); + if(sect->rel == nil) + continue; + r = mal(sect->nreloc*sizeof r[0]); + rp = r; + rel = sect->rel; + for(j=0; jnreloc; j++, rel++) { + if(rel->scattered) { + int k; + MachoSect *ks; + + if(thechar != '8') + diag("unexpected scattered relocation"); + + // on 386, rewrite scattered 4/1 relocation into + // the pseudo-pc-relative reference that it is. + // assume that the second in the pair is in this section + // and use that as the pc-relative base. + if(thechar != '8' || rel->type != 4 || j+1 >= sect->nreloc || + !(rel+1)->scattered || (rel+1)->type != 1 || + (rel+1)->value < sect->addr || (rel+1)->value >= sect->addr+sect->size) { + werrstr("unsupported scattered relocation %d/%d", (int)rel->type, (int)(rel+1)->type); + goto bad; + } + rp->siz = rel->length; + rp->off = rel->addr; + + // NOTE(rsc): I haven't worked out why (really when) + // we should ignore the addend on a + // scattered relocation, but it seems that the + // common case is we ignore it. + // It's likely that this is not strictly correct + // and that the math should look something + // like the non-scattered case below. + rp->add = 0; + + // want to make it pc-relative aka relative to rp->off+4 + // but the scatter asks for relative to off = (rel+1)->value - sect->addr. + // adjust rp->add accordingly. + rp->type = D_PCREL; + rp->add += (rp->off+4) - ((rel+1)->value - sect->addr); + + // now consider the desired symbol. + // find the section where it lives. + for(k=0; kseg.nsect; k++) { + ks = &c->seg.sect[k]; + if(ks->addr <= rel->value && rel->value < ks->addr+ks->size) + goto foundk; + } + werrstr("unsupported scattered relocation: invalid address %#ux", rel->addr); + goto bad; + foundk: + if(ks->sym != S) { + rp->sym = ks->sym; + rp->add += rel->value - ks->addr; + } else if(strcmp(ks->segname, "__IMPORT") == 0 && strcmp(ks->name, "__pointers") == 0) { + // handle reference to __IMPORT/__pointers. + // how much worse can this get? + // why are we supporting 386 on the mac anyway? + rp->type = 512 + MACHO_FAKE_GOTPCREL; + // figure out which pointer this is a reference to. + k = ks->res1 + (rel->value - ks->addr) / 4; + // load indirect table for __pointers + // fetch symbol number + if(dsymtab == nil || k < 0 || k >= dsymtab->nindirectsyms || dsymtab->indir == nil) { + werrstr("invalid scattered relocation: indirect symbol reference out of range"); + goto bad; + } + k = dsymtab->indir[k]; + if(k < 0 || k >= symtab->nsym) { + werrstr("invalid scattered relocation: symbol reference out of range"); + goto bad; + } + rp->sym = symtab->sym[k].sym; + } else { + werrstr("unsupported scattered relocation: reference to %s/%s", ks->segname, ks->name); + goto bad; + } + rp++; + // skip #1 of 2 rel; continue skips #2 of 2. + rel++; + j++; + continue; + } + + rp->siz = rel->length; + rp->type = 512 + (rel->type<<1) + rel->pcrel; + rp->off = rel->addr; + + rp->add = e->e32(s->p+rp->off); + // For i386 Mach-O PC-relative, the addend is written such that + // it *is* the PC being subtracted. Use that to make + // it match our version of PC-relative. + if(rel->pcrel && thechar == '8') + rp->add += rp->off+rp->siz; + if(!rel->extrn) { + if(rel->symnum < 1 || rel->symnum > c->seg.nsect) { + werrstr("invalid relocation: section reference out of range %d vs %d", rel->symnum, c->seg.nsect); + goto bad; + } + rp->sym = c->seg.sect[rel->symnum-1].sym; + if(rp->sym == nil) { + werrstr("invalid relocation: %s", c->seg.sect[rel->symnum-1].name); + goto bad; + } + // References to symbols in other sections + // include that information in the addend. + // We only care about the delta from the + // section base. + if(thechar == '8') + rp->add -= c->seg.sect[rel->symnum-1].addr; + } else { + if(rel->symnum >= symtab->nsym) { + werrstr("invalid relocation: symbol reference out of range"); + goto bad; + } + rp->sym = symtab->sym[rel->symnum].sym; + } + rp++; + } + qsort(r, rp - r, sizeof r[0], rbyoff); + s->r = r; + s->nr = rp - r; + } + return; + +bad: + diag("%s: malformed mach-o file: %r", pn); +} diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 1af9f7a41..ae77247c3 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -1,5 +1,6 @@ -// Derived from Inferno utils/6l/obj.c +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c // http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c // // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) @@ -118,7 +119,7 @@ addlib(char *src, char *obj) } for(; iname+1); + snprint(comp, sizeof comp, "%s", histfrog[i]->name+1); for(;;) { p = strstr(comp, "$O"); if(p == 0) @@ -146,6 +147,22 @@ addlib(char *src, char *obj) strcat(name, comp); } cleanname(name); + + // runtime.a -> runtime + p = nil; + if(strlen(name) > 2 && name[strlen(name)-2] == '.') { + p = name+strlen(name)-2; + *p = '\0'; + } + + // already loaded? + for(i=0; i runtime.a for search + if(p != nil) + *p = '.'; if(search) { // try dot, -L "libdir", and then goroot. @@ -159,8 +176,8 @@ addlib(char *src, char *obj) cleanname(pname); /* runtime.a -> runtime */ - if(strlen(name) > 2 && name[strlen(name)-2] == '.') - name[strlen(name)-2] = '\0'; + if(p != nil) + *p = '\0'; if(debug['v']) Bprint(&bso, "%5.2f addlib: %s %s pulls in %s\n", cputime(), obj, src, pname); @@ -186,9 +203,9 @@ addlibpath(char *srcref, char *objref, char *file, char *pkg) if(strcmp(file, library[i].file) == 0) return; - if(debug['v']) + if(debug['v'] > 1) Bprint(&bso, "%5.2f addlibpath: srcref: %s objref: %s file: %s pkg: %s\n", - cputime(), srcref, objref, file, pkg); + cputime(), srcref, objref, file, pkg); if(libraryp == nlibrary){ nlibrary = 50 + 2*libraryp; @@ -219,8 +236,6 @@ loadlib(void) { char pname[1024]; int i, found; - int32 h; - Sym *s; found = 0; for(i=0; ilink) - if(s->type == SXREF) - goto loop; - +/* + * look for the next file in an archive. + * adapted from libmach. + */ +int +nextar(Biobuf *bp, int off, struct ar_hdr *a) +{ + int r; + int32 arsize; + + if (off&01) + off++; + Bseek(bp, off, 0); + r = Bread(bp, a, SAR_HDR); + if(r != SAR_HDR) + return 0; + if(strncmp(a->fmag, ARFMAG, sizeof(a->fmag))) + return -1; + arsize = strtol(a->size, 0, 0); + if (arsize&1) + arsize++; + return arsize + SAR_HDR; } void objfile(char *file, char *pkg) { - int32 off, esym, cnt, l; - int work; + int32 off, l; Biobuf *f; - Sym *s; char magbuf[SARMAG]; - char name[100], pname[150]; + char pname[150]; struct ar_hdr arhdr; - char *e, *start, *stop, *x; pkg = smprint("%i", pkg); - if(file[0] == '-' && file[1] == 'l') { // TODO: fix this - if(debug['9']) - sprint(name, "/%s/lib/lib", thestring); - else - sprint(name, "/usr/%clib/lib", thechar); - strcat(name, file+2); - strcat(name, ".a"); - file = name; - } if(debug['v']) Bprint(&bso, "%5.2f ldobj: %s (%s)\n", cputime(), file, pkg); Bflush(&bso); @@ -291,9 +310,10 @@ objfile(char *file, char *pkg) Bterm(f); return; } - - l = Bread(f, &arhdr, SAR_HDR); - if(l != SAR_HDR) { + + /* skip over __.SYMDEF */ + off = Boffset(f); + if((l = nextar(f, off, &arhdr)) <= 0) { diag("%s: short read on archive file symbol header", file); goto out; } @@ -301,88 +321,52 @@ objfile(char *file, char *pkg) diag("%s: first entry not symbol header", file); goto out; } - - esym = SARMAG + SAR_HDR + atolwhex(arhdr.size); - off = SARMAG + SAR_HDR; - - if(debug['u']) { - struct ar_hdr pkghdr; - int n; - - // Read next ar header to check for package safe bit. - Bseek(f, esym+(esym&1), 0); - l = Bread(f, &pkghdr, SAR_HDR); - if(l != SAR_HDR) { - diag("%s: short read on second archive header", file); - goto out; - } - if(strncmp(pkghdr.name, pkgname, strlen(pkgname))) { - diag("%s: second entry not package header", file); - goto out; - } - n = atolwhex(pkghdr.size); - ldpkg(f, pkg, n, file, Pkgdef); + off += l; + + /* skip over (or process) __.PKGDEF */ + if((l = nextar(f, off, &arhdr)) <= 0) { + diag("%s: short read on archive file symbol header", file); + goto out; } + if(strncmp(arhdr.name, pkgname, strlen(pkgname))) { + diag("%s: second entry not package header", file); + goto out; + } + off += l; + + if(debug['u']) + ldpkg(f, pkg, atolwhex(arhdr.size), file, Pkgdef); /* - * just bang the whole symbol file into memory + * load all the object files from the archive now. + * this gives us sequential file access and keeps us + * from needing to come back later to pick up more + * objects. it breaks the usual C archive model, but + * this is Go, not C. the common case in Go is that + * we need to load all the objects, and then we throw away + * the individual symbols that are unused. + * + * loading every object will also make it possible to + * load foreign objects not referenced by __.SYMDEF. */ - Bseek(f, off, 0); - cnt = esym - off; - start = mal(cnt + 10); - cnt = Bread(f, start, cnt); - if(cnt <= 0){ - Bterm(f); - return; - } - stop = &start[cnt]; - memset(stop, 0, 10); - - work = 1; - while(work) { - if(debug['v']) - Bprint(&bso, "%5.2f library pass: %s\n", cputime(), file); - Bflush(&bso); - work = 0; - for(e = start; e < stop; e = strchr(e+5, 0) + 1) { - x = expandpkg(e+5, pkg); - s = lookup(x, 0); - if(x != e+5) - free(x); - if(s->type != SXREF) - continue; - sprint(pname, "%s(%s)", file, s->name); - if(debug['v']) - Bprint(&bso, "%5.2f library: %s\n", cputime(), pname); - Bflush(&bso); - l = e[1] & 0xff; - l |= (e[2] & 0xff) << 8; - l |= (e[3] & 0xff) << 16; - l |= (e[4] & 0xff) << 24; - Bseek(f, l, 0); - l = Bread(f, &arhdr, SAR_HDR); - if(l != SAR_HDR) - goto bad; - if(strncmp(arhdr.fmag, ARFMAG, sizeof(arhdr.fmag))) - goto bad; - l = SARNAME; - while(l > 0 && arhdr.name[l-1] == ' ') - l--; - sprint(pname, "%s(%.*s)", file, l, arhdr.name); - l = atolwhex(arhdr.size); - ldobj(f, pkg, l, pname, ArchiveObj); - if(s->type == SXREF) { - diag("%s: failed to load: %s", file, s->name); - errorexit(); - } - work = 1; - xrefresolv = 1; + for(;;) { + l = nextar(f, off, &arhdr); + if(l == 0) + break; + if(l < 0) { + diag("%s: malformed archive", file); + goto out; } + off += l; + + l = SARNAME; + while(l > 0 && arhdr.name[l-1] == ' ') + l--; + snprint(pname, sizeof pname, "%s(%.*s)", file, utfnlen(arhdr.name, l), arhdr.name); + l = atolwhex(arhdr.size); + ldobj(f, pkg, l, pname, ArchiveObj); } - return; -bad: - diag("%s: bad or out of date archive", file); out: Bterm(f); } @@ -390,32 +374,38 @@ out: void ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) { - static int files; - static char **filen; - char **nfilen, *line; - int i, n, c1, c2, c3; + char *line; + int n, c1, c2, c3, c4; + uint32 magic; vlong import0, import1, eof; char src[1024]; eof = Boffset(f) + len; src[0] = '\0'; - // don't load individual object more than once. - // happens with import of .6 files because of loop in xresolv. - // doesn't happen with .a because SYMDEF is consulted - // first to decide whether each individual object file is needed. - for(i=0; ilink) + for(s = hash[h]; s != S; s = s->hash) if(s->version == v) if(memcmp(s->name, symb, l) == 0) return s; @@ -487,14 +477,18 @@ lookup(char *symb, int v) if(debug['v'] > 1) Bprint(&bso, "lookup %s\n", symb); + s->dynid = -1; + s->plt = -1; + s->got = -1; s->name = mal(l + 1); memmove(s->name, symb, l); - s->link = hash[h]; + s->hash = hash[h]; s->type = 0; s->version = v; s->value = 0; s->sig = 0; + s->size = 0; hash[h] = s; nsymbol++; return s; @@ -607,8 +601,13 @@ nuxiinit(void) if(i < 1) inuxi1[i] = c; inuxi4[i] = c; - inuxi8[i] = c; - inuxi8[i+4] = c+4; + if(c == i) { + inuxi8[i] = c; + inuxi8[i+4] = c+4; + } else { + inuxi8[i] = c+4; + inuxi8[i+4] = c; + } fnuxi4[i] = c; fnuxi8[i] = c; fnuxi8[i+4] = c+4; @@ -731,21 +730,6 @@ ieeedtod(Ieee *ieeep) return ldexp(fr, exp); } -void -undefsym(Sym *s) -{ - int n; - - n = imports; - if(s->value != 0) - diag("value != 0 on SXREF"); - if(n >= 1<value = n<type = SUNDEF; - imports++; -} - void zerosig(char *sp) { @@ -755,47 +739,6 @@ zerosig(char *sp) s->sig = 0; } -void -readundefs(char *f, int t) -{ - int i, n; - Sym *s; - Biobuf *b; - char *l, buf[256], *fields[64]; - - if(f == nil) - return; - b = Bopen(f, OREAD); - if(b == nil){ - diag("could not open %s: %r", f); - errorexit(); - } - while((l = Brdline(b, '\n')) != nil){ - n = Blinelen(b); - if(n >= sizeof(buf)){ - diag("%s: line too long", f); - errorexit(); - } - memmove(buf, l, n); - buf[n-1] = '\0'; - n = getfields(buf, fields, nelem(fields), 1, " \t\r\n"); - if(n == nelem(fields)){ - diag("%s: bad format", f); - errorexit(); - } - for(i = 0; i < n; i++){ - s = lookup(fields[i], 0); - s->type = SXREF; - s->subtype = t; - if(t == SIMPORT) - nimports++; - else - nexports++; - } - } - Bterm(b); -} - int32 Bget4(Biobuf *f) { @@ -829,15 +772,22 @@ mal(uint32 n) { void *v; - while(n & 7) - n++; + n = (n+7)&~7; if(n > NHUNK) { v = malloc(n); + if(v == nil) { + diag("out of memory"); + errorexit(); + } memset(v, 0, n); return v; } if(n > nhunk) { hunk = malloc(NHUNK); + if(hunk == nil) { + diag("out of memory"); + errorexit(); + } nhunk = NHUNK; } @@ -849,6 +799,16 @@ mal(uint32 n) return v; } +void +unmal(void *v, uint32 n) +{ + n = (n+7)&~7; + if(hunk - n == v) { + hunk -= n; + nhunk += n; + } +} + // Copied from ../gc/subr.c:/^pathtoprefix; must stay in sync. /* * Convert raw string to the prefix that will be used in the symbol table. @@ -901,3 +861,211 @@ iconv(Fmt *fp) return 0; } +void +mangle(char *file) +{ + fprint(2, "%s: mangled input file\n", file); + errorexit(); +} + +Section* +addsection(Segment *seg, char *name, int rwx) +{ + Section **l; + Section *sect; + + for(l=&seg->sect; *l; l=&(*l)->next) + ; + sect = mal(sizeof *sect); + sect->rwx = rwx; + sect->name = name; + sect->seg = seg; + *l = sect; + return sect; +} + +void +ewrite(int fd, void *buf, int n) +{ + if(write(fd, buf, n) < 0) { + diag("write error: %r"); + errorexit(); + } +} + +void +pclntab(void) +{ + vlong oldpc; + Prog *p; + int32 oldlc, v, s; + Sym *sym; + uchar *bp; + + sym = lookup("pclntab", 0); + sym->type = SRODATA; + sym->reachable = 1; + if(debug['s']) + return; + + oldpc = INITTEXT; + oldlc = 0; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + for(p = cursym->text; p != P; p = p->link) { + if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) { + if(debug['O']) + Bprint(&bso, "%6llux %P\n", + (vlong)p->pc, p); + continue; + } + if(debug['O']) + Bprint(&bso, "\t\t%6d", lcsize); + v = (p->pc - oldpc) / MINLC; + while(v) { + s = 127; + if(v < 127) + s = v; + symgrow(sym, lcsize+1); + bp = sym->p + lcsize; + *bp = s+128; /* 129-255 +pc */ + if(debug['O']) + Bprint(&bso, " pc+%d*%d(%d)", s, MINLC, s+128); + v -= s; + lcsize++; + } + s = p->line - oldlc; + oldlc = p->line; + oldpc = p->pc + MINLC; + if(s > 64 || s < -64) { + symgrow(sym, lcsize+5); + bp = sym->p + lcsize; + *bp++ = 0; /* 0 vv +lc */ + *bp++ = s>>24; + *bp++ = s>>16; + *bp++ = s>>8; + *bp = s; + if(debug['O']) { + if(s > 0) + Bprint(&bso, " lc+%d(%d,%d)\n", + s, 0, s); + else + Bprint(&bso, " lc%d(%d,%d)\n", + s, 0, s); + Bprint(&bso, "%6llux %P\n", + (vlong)p->pc, p); + } + lcsize += 5; + continue; + } + symgrow(sym, lcsize+1); + bp = sym->p + lcsize; + if(s > 0) { + *bp = 0+s; /* 1-64 +lc */ + if(debug['O']) { + Bprint(&bso, " lc+%d(%d)\n", s, 0+s); + Bprint(&bso, "%6llux %P\n", + (vlong)p->pc, p); + } + } else { + *bp = 64-s; /* 65-128 -lc */ + if(debug['O']) { + Bprint(&bso, " lc%d(%d)\n", s, 64-s); + Bprint(&bso, "%6llux %P\n", + (vlong)p->pc, p); + } + } + lcsize++; + } + } + if(lcsize & 1) { + symgrow(sym, lcsize+1); + sym->p[lcsize] = 129; + lcsize++; + } + sym->size = lcsize; + lcsize = 0; + + if(debug['v'] || debug['O']) + Bprint(&bso, "lcsize = %d\n", lcsize); + Bflush(&bso); +} + +#define LOG 5 +void +mkfwd(void) +{ + Prog *p; + int i; + int32 dwn[LOG], cnt[LOG]; + Prog *lst[LOG], *last; + + for(i=0; inext) { + for(p = cursym->text; p != P; p = p->link) { + if(p->link == P) { + if(cursym->next) + p->forwd = cursym->next->text; + break; + } + i--; + if(i < 0) + i = LOG-1; + p->forwd = P; + dwn[i]--; + if(dwn[i] <= 0) { + dwn[i] = cnt[i]; + if(lst[i] != P) + lst[i]->forwd = p; + lst[i] = p; + } + } + } +} + +uint16 +le16(uchar *b) +{ + return b[0] | b[1]<<8; +} + +uint32 +le32(uchar *b) +{ + return b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24; +} + +uint64 +le64(uchar *b) +{ + return le32(b) | (uint64)le32(b+4)<<32; +} + +uint16 +be16(uchar *b) +{ + return b[0]<<8 | b[1]; +} + +uint32 +be32(uchar *b) +{ + return b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3]; +} + +uint64 +be64(uchar *b) +{ + return (uvlong)be32(b)<<32 | be32(b+4); +} + +Endian be = { be16, be32, be64 }; +Endian le = { le16, le32, le64 }; diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h index 652d845fb..bcf297116 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -40,6 +40,34 @@ struct Library char *pkg; // import path }; +// Terrible but standard terminology. +// A segment describes a block of file to load into memory. +// A section further describes the pieces of that block for +// use in debuggers and such. + +typedef struct Segment Segment; +typedef struct Section Section; + +struct Segment +{ + uchar rwx; // permission as usual unix bits (5 = r-x etc) + uvlong vaddr; // virtual address + uvlong len; // length in memory + uvlong fileoff; // file offset + uvlong filelen; // length on disk + Section* sect; +}; + +struct Section +{ + uchar rwx; + char *name; + uvlong vaddr; + uvlong len; + Section *next; // in segment list + Segment *seg; +}; + extern char symname[]; extern char *libdir[]; extern int nlibdir; @@ -64,11 +92,18 @@ EXTERN uchar inuxi8[8]; EXTERN char* outfile; EXTERN int32 nsymbol; EXTERN char* thestring; +EXTERN int ndynexp; + +EXTERN Segment segtext; +EXTERN Segment segdata; +EXTERN Segment segsym; void addlib(char *src, char *obj); void addlibpath(char *srcref, char *objref, char *file, char *pkg); +Section* addsection(Segment*, char*, int); void copyhistfrog(char *buf, int nbuf); void addhist(int32 line, int type); +void asmlc(void); void histtoauto(void); void collapsefrog(Sym *s); Sym* lookup(char *symb, int v); @@ -83,20 +118,82 @@ void readundefs(char *f, int t); int32 Bget4(Biobuf *f); void loadlib(void); void errorexit(void); +void mangle(char*); void objfile(char *file, char *pkg); void libinit(void); +void pclntab(void); +void symtab(void); void Lflag(char *arg); void usage(void); +void adddynrel(Sym*, Reloc*); void ldobj1(Biobuf *f, char*, int64 len, char *pn); void ldobj(Biobuf*, char*, int64, char*, int); +void ldelf(Biobuf*, char*, int64, char*); +void ldmacho(Biobuf*, char*, int64, char*); void ldpkg(Biobuf*, char*, int64, char*, int); void mark(Sym *s); +void mkfwd(void); char* expandpkg(char*, char*); void deadcode(void); +void ewrite(int, void*, int); +Reloc* addrel(Sym*); +void codeblk(int32, int32); +void datblk(int32, int32); +Sym* datsort(Sym*); +void reloc(void); +void relocsym(Sym*); +void savedata(Sym*, Prog*); +void symgrow(Sym*, int32); +vlong addstring(Sym*, char*); +vlong adduint32(Sym*, uint32); +vlong adduint64(Sym*, uint64); +vlong addaddr(Sym*, Sym*); +vlong addaddrplus(Sym*, Sym*, int32); +vlong addpcrelplus(Sym*, Sym*, int32); +vlong addsize(Sym*, Sym*); +vlong adduint8(Sym*, uint8); +vlong adduint16(Sym*, uint16); +void asmsym(void); +void asmelfsym64(void); +void strnput(char*, int); +void dodata(void); +void address(void); +void textaddress(void); +void genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)); +vlong datoff(vlong); +void adddynlib(char*); +int archreloc(Reloc*, Sym*, vlong*); +void adddynsym(Sym*); +void addexport(void); int pathchar(void); void* mal(uint32); +void unmal(void*, uint32); void mywhatsys(void); +int rbyoff(const void*, const void*); + +uint16 le16(uchar*); +uint32 le32(uchar*); +uint64 le64(uchar*); +uint16 be16(uchar*); +uint32 be32(uchar*); +uint64 be64(uchar*); + +typedef struct Endian Endian; +struct Endian +{ + uint16 (*e16)(uchar*); + uint32 (*e32)(uchar*); + uint64 (*e64)(uchar*); +}; + +extern Endian be, le; + +// relocation size bits +enum { + Rbig = 128, + Rlittle = 64, +}; /* set by call to mywhatsys() */ extern char* goroot; diff --git a/src/cmd/ld/macho.c b/src/cmd/ld/macho.c index 24400cf14..402e0ec63 100644 --- a/src/cmd/ld/macho.c +++ b/src/cmd/ld/macho.c @@ -6,6 +6,7 @@ // http://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html #include "l.h" +#include "../ld/dwarf.h" #include "../ld/lib.h" #include "../ld/macho.h" @@ -46,6 +47,10 @@ newMachoLoad(uint32 type, uint32 ndata) diag("too many loads"); errorexit(); } + + if(macho64 && (ndata & 1)) + ndata++; + l = &load[nload++]; l->type = type; l->ndata = ndata; @@ -97,22 +102,6 @@ newMachoDebug(void) // Generic linking code. -static uchar *linkdata; -static uint32 nlinkdata; -static uint32 mlinkdata; - -static uchar *strtab; -static uint32 nstrtab; -static uint32 mstrtab; - -struct Expsym -{ - int off; - Sym* s; -} *expsym; -static int nexpsym; -static int nimpsym; - static char **dylib; static int ndylib; @@ -129,7 +118,7 @@ machowrite(void) MachoDebug *d; MachoLoad *l; - o1 = Boffset(&bso); + o1 = cpos(); loadsize = 4*4*ndebug; for(i=0; ireloc); LPUT(t->nreloc); LPUT(t->flag); - LPUT(0); /* reserved */ - LPUT(0); /* reserved */ + LPUT(t->res1); /* reserved */ + LPUT(t->res2); /* reserved */ LPUT(0); /* reserved */ } else { strnput(t->name, 16); @@ -207,8 +196,8 @@ machowrite(void) LPUT(t->reloc); LPUT(t->nreloc); LPUT(t->flag); - LPUT(0); /* reserved */ - LPUT(0); /* reserved */ + LPUT(t->res1); /* reserved */ + LPUT(t->res2); /* reserved */ } } } @@ -229,224 +218,71 @@ machowrite(void) LPUT(d->filesize); } - return Boffset(&bso) - o1; -} - -static void* -grow(uchar **dat, uint32 *ndat, uint32 *mdat, uint32 n) -{ - uchar *p; - uint32 old; - - if(*ndat+n > *mdat) { - old = *mdat; - *mdat = (*ndat+n)*2 + 128; - *dat = realloc(*dat, *mdat); - if(*dat == 0) { - diag("out of memory"); - errorexit(); - } - memset(*dat+old, 0, *mdat-old); - } - p = *dat + *ndat; - *ndat += n; - return p; -} - -static int -needlib(char *name) -{ - char *p; - Sym *s; - - /* reuse hash code in symbol table */ - p = smprint(".machoload.%s", name); - s = lookup(p, 0); - if(s->type == 0) { - s->type = 100; // avoid SDATA, etc. - return 1; - } - return 0; + return cpos() - o1; } void domacho(void) { - int h, ptrsize, t; - char *p; - uchar *dat; - uint32 x; Sym *s; - Sym **impsym; - ptrsize = 4; - if(macho64) - ptrsize = 8; + if(debug['d']) + return; // empirically, string table must begin with " \x00". - if(!debug['d']) - *(char*)grow(&strtab, &nstrtab, &mstrtab, 2) = ' '; - - impsym = nil; - for(h=0; hlink) { - if(!s->reachable || (s->type != STEXT && s->type != SDATA && s->type != SBSS) || s->dynimpname == nil) - continue; - if(debug['d']) { - diag("cannot use dynamic loading and -d"); - errorexit(); - } - if(!s->dynexport) { - if(nimpsym%32 == 0) { - impsym = realloc(impsym, (nimpsym+32)*sizeof impsym[0]); - if(impsym == nil) { - diag("out of memory"); - errorexit(); - } - } - impsym[nimpsym++] = s; - continue; - } - - /* symbol table entry - darwin still puts _ prefixes on all C symbols */ - x = nstrtab; - p = grow(&strtab, &nstrtab, &mstrtab, 1+strlen(s->dynimpname)+1); - *p++ = '_'; - strcpy(p, s->dynimpname); - - dat = grow(&linkdata, &nlinkdata, &mlinkdata, 8+ptrsize); - dat[0] = x; - dat[1] = x>>8; - dat[2] = x>>16; - dat[3] = x>>24; - - dat[4] = 0x0f; // type: N_SECT | N_EXT - external, defined in sect - switch(s->type) { - default: - case STEXT: - t = 1; - break; - case SDATA: - t = 2; - break; - case SBSS: - t = 4; - break; - } - dat[5] = t; // sect: section number - - if (nexpsym%32 == 0) { - expsym = realloc(expsym, (nexpsym+32)*sizeof expsym[0]); - if (expsym == nil) { - diag("out of memory"); - errorexit(); - } - } - expsym[nexpsym].off = nlinkdata - ptrsize; - expsym[nexpsym++].s = s; - } - } - - for(h=0; htype = SMACHO; - s->value = (nexpsym+h) * ptrsize; - - /* symbol table entry - darwin still puts _ prefixes on all C symbols */ - x = nstrtab; - p = grow(&strtab, &nstrtab, &mstrtab, 1+strlen(s->dynimpname)+1); - *p++ = '_'; - strcpy(p, s->dynimpname); - - dat = grow(&linkdata, &nlinkdata, &mlinkdata, 8+ptrsize); - dat[0] = x; - dat[1] = x>>8; - dat[2] = x>>16; - dat[3] = x>>24; - - dat[4] = 0x01; // type: N_EXT - external symbol - - if(needlib(s->dynimplib)) { - if(ndylib%32 == 0) { - dylib = realloc(dylib, (ndylib+32)*sizeof dylib[0]); - if(dylib == nil) { - diag("out of memory"); - errorexit(); - } - } - dylib[ndylib++] = s->dynimplib; - } - } - free(impsym); - - /* - * list of symbol table indexes. - * we don't take advantage of the opportunity - * to order the symbol table differently from - * this list, so it is boring: 0 1 2 3 4 ... - */ - for(x=0; x>8; - dat[2] = x>>16; - dat[3] = x>>24; - } - - dynptrsize = (nexpsym+nimpsym) * ptrsize; + s = lookup(".dynstr", 0); + s->type = SMACHODYNSTR; + s->reachable = 1; + adduint8(s, ' '); + adduint8(s, '\0'); + + s = lookup(".dynsym", 0); + s->type = SMACHODYNSYM; + s->reachable = 1; + + s = lookup(".plt", 0); // will be __symbol_stub + s->type = SMACHOPLT; + s->reachable = 1; + + s = lookup(".got", 0); // will be __nl_symbol_ptr + s->type = SMACHOGOT; + s->reachable = 1; + + s = lookup(".linkedit.plt", 0); // indirect table for .plt + s->type = SMACHOINDIRECTPLT; + s->reachable = 1; + + s = lookup(".linkedit.got", 0); // indirect table for .got + s->type = SMACHOINDIRECTGOT; + s->reachable = 1; } -vlong -domacholink(void) +void +machoadddynlib(char *lib) { - int i; - uchar *p; - Sym *s; - uint64 val; - - linkoff = 0; - if(nlinkdata > 0) { - linkoff = rnd(HEADR+textsize, INITRND) + rnd(datsize, INITRND); - seek(cout, linkoff, 0); - - for(i = 0; ivalue; - if(s->type == SUNDEF) - diag("export of undefined symbol %s", s->name); - if (s->type != STEXT) - val += INITDAT; - p = linkdata+expsym[i].off; - p[0] = val; - p[1] = val >> 8; - p[2] = val >> 16; - p[3] = val >> 24; - if (macho64) { - p[4] = val >> 32; - p[5] = val >> 40; - p[6] = val >> 48; - p[7] = val >> 56; - } + if(ndylib%32 == 0) { + dylib = realloc(dylib, (ndylib+32)*sizeof dylib[0]); + if(dylib == nil) { + diag("out of memory"); + errorexit(); } - - write(cout, linkdata, nlinkdata); - write(cout, strtab, nstrtab); } - return rnd(nlinkdata+nstrtab, INITRND); + dylib[ndylib++] = lib; } void -asmbmacho(vlong symdatva, vlong symo) +asmbmacho(void) { vlong v, w; vlong va; - int a, i, ptrsize; + int a, i; char *pkgroot; MachoHdr *mh; MachoSect *msect; MachoSeg *ms; MachoDebug *md; MachoLoad *ml; + Sym *s; /* apple MACH */ va = INITTEXT - HEADR; @@ -458,12 +294,10 @@ asmbmacho(vlong symdatva, vlong symo) case '6': mh->cpu = MACHO_CPU_AMD64; mh->subcpu = MACHO_SUBCPU_X86; - ptrsize = 8; break; case '8': mh->cpu = MACHO_CPU_386; mh->subcpu = MACHO_SUBCPU_X86; - ptrsize = 4; break; } @@ -472,8 +306,8 @@ asmbmacho(vlong symdatva, vlong symo) ms->vsize = va; /* text */ - v = rnd(HEADR+textsize, INITRND); - ms = newMachoSeg("__TEXT", 1); + v = rnd(HEADR+segtext.len, INITRND); + ms = newMachoSeg("__TEXT", 2); ms->vaddr = va; ms->vsize = v; ms->filesize = v; @@ -482,45 +316,50 @@ asmbmacho(vlong symdatva, vlong symo) msect = newMachoSect(ms, "__text"); msect->addr = INITTEXT; - msect->size = textsize; + msect->size = segtext.sect->len; msect->off = INITTEXT - va; msect->flag = 0x400; /* flag - some instructions */ + + s = lookup(".plt", 0); + if(s->size > 0) { + msect = newMachoSect(ms, "__symbol_stub1"); + msect->addr = symaddr(s); + msect->size = s->size; + msect->off = ms->fileoffset + msect->addr - ms->vaddr; + msect->flag = 0x80000408; /* flag */ + msect->res1 = 0; /* index into indirect symbol table */ + msect->res2 = 6; /* size of stubs */ + } /* data */ - w = datsize+dynptrsize+bsssize; - ms = newMachoSeg("__DATA", 2+(dynptrsize>0)); + w = segdata.len; + ms = newMachoSeg("__DATA", 3); ms->vaddr = va+v; ms->vsize = w; ms->fileoffset = v; - ms->filesize = datsize; + ms->filesize = segdata.filelen; ms->prot1 = 7; ms->prot2 = 3; msect = newMachoSect(ms, "__data"); msect->addr = va+v; - msect->size = datsize; + msect->size = symaddr(lookup(".got", 0)) - msect->addr; msect->off = v; - if(dynptrsize > 0) { + s = lookup(".got", 0); + if(s->size > 0) { msect = newMachoSect(ms, "__nl_symbol_ptr"); - msect->addr = va+v+datsize; - msect->size = dynptrsize; + msect->addr = symaddr(s); + msect->size = s->size; + msect->off = datoff(msect->addr); msect->align = 2; msect->flag = 6; /* section with nonlazy symbol pointers */ - /* - * The reserved1 field is supposed to be the index of - * the first entry in the list of symbol table indexes - * in isymtab for the symbols we need. We only use - * pointers, so we need the entire list, so the index - * here should be 0, which luckily is what the Mach-O - * writing code emits by default for this not really reserved field. - msect->reserved1 = 0; - first indirect symbol table entry we need - */ + msect->res1 = lookup(".linkedit.plt", 0)->size / 4; /* offset into indirect symbol table */ } msect = newMachoSect(ms, "__bss"); - msect->addr = va+v+datsize+dynptrsize; - msect->size = bsssize; + msect->addr = va+v+segdata.filelen; + msect->size = segdata.len - segdata.filelen; msect->flag = 1; /* flag - zero fill */ switch(thechar) { @@ -532,7 +371,7 @@ asmbmacho(vlong symdatva, vlong symo) ml->data[0] = 4; /* thread type */ ml->data[1] = 42; /* word count */ ml->data[2+32] = entryvalue(); /* start pc */ - ml->data[2+32+1] = entryvalue()>>32; + ml->data[2+32+1] = entryvalue()>>16>>16; // hide >>32 for 8l break; case '8': ml = newMachoLoad(5, 16+2); /* unix thread */ @@ -543,39 +382,43 @@ asmbmacho(vlong symdatva, vlong symo) } if(!debug['d']) { - int nsym; + Sym *s1, *s2, *s3, *s4; - nsym = dynptrsize/ptrsize; + // must match domacholink below + s1 = lookup(".dynsym", 0); + s2 = lookup(".dynstr", 0); + s3 = lookup(".linkedit.plt", 0); + s4 = lookup(".linkedit.got", 0); ms = newMachoSeg("__LINKEDIT", 0); - ms->vaddr = va+v+rnd(datsize+dynptrsize+bsssize, INITRND); - ms->vsize = nlinkdata+nstrtab; + ms->vaddr = va+v+rnd(segdata.len, INITRND); + ms->vsize = s1->size + s2->size + s3->size + s4->size; ms->fileoffset = linkoff; - ms->filesize = nlinkdata+nstrtab; + ms->filesize = ms->vsize; ms->prot1 = 7; ms->prot2 = 3; ml = newMachoLoad(2, 4); /* LC_SYMTAB */ ml->data[0] = linkoff; /* symoff */ - ml->data[1] = nsym; /* nsyms */ - ml->data[2] = linkoff + nlinkdata; /* stroff */ - ml->data[3] = nstrtab; /* strsize */ + ml->data[1] = s1->size / (macho64 ? 16 : 12); /* nsyms */ + ml->data[2] = linkoff + s1->size; /* stroff */ + ml->data[3] = s2->size; /* strsize */ ml = newMachoLoad(11, 18); /* LC_DYSYMTAB */ ml->data[0] = 0; /* ilocalsym */ ml->data[1] = 0; /* nlocalsym */ ml->data[2] = 0; /* iextdefsym */ - ml->data[3] = nexpsym; /* nextdefsym */ - ml->data[4] = nexpsym; /* iundefsym */ - ml->data[5] = nimpsym; /* nundefsym */ + ml->data[3] = ndynexp; /* nextdefsym */ + ml->data[4] = ndynexp; /* iundefsym */ + ml->data[5] = (s1->size / (macho64 ? 16 : 12)) - ndynexp; /* nundefsym */ ml->data[6] = 0; /* tocoffset */ ml->data[7] = 0; /* ntoc */ ml->data[8] = 0; /* modtaboff */ ml->data[9] = 0; /* nmodtab */ ml->data[10] = 0; /* extrefsymoff */ ml->data[11] = 0; /* nextrefsyms */ - ml->data[12] = linkoff + nlinkdata - nsym*4; /* indirectsymoff */ - ml->data[13] = nsym; /* nindirectsyms */ + ml->data[12] = linkoff + s1->size + s2->size; /* indirectsymoff */ + ml->data[13] = (s3->size + s4->size) / 4; /* nindirectsyms */ ml->data[14] = 0; /* extreloff */ ml->data[15] = 0; /* nextrel */ ml->data[16] = 0; /* locreloff */ @@ -602,24 +445,53 @@ asmbmacho(vlong symdatva, vlong symo) } if(!debug['s']) { - ms = newMachoSeg("__SYMDAT", 1); - ms->vaddr = symdatva; - ms->vsize = 8+symsize+lcsize; - ms->fileoffset = symo; - ms->filesize = 8+symsize+lcsize; - ms->prot1 = 7; - ms->prot2 = 5; + Sym *s; md = newMachoDebug(); - md->fileoffset = symo+8; - md->filesize = symsize; + s = lookup("symtab", 0); + md->fileoffset = datoff(s->value); + md->filesize = s->size; md = newMachoDebug(); - md->fileoffset = symo+8+symsize; - md->filesize = lcsize; + s = lookup("pclntab", 0); + md->fileoffset = datoff(s->value); + md->filesize = s->size; + + dwarfaddmachoheaders(); } a = machowrite(); if(a > MACHORESERVE) diag("MACHORESERVE too small: %d > %d", a, MACHORESERVE); } + +vlong +domacholink(void) +{ + int size; + Sym *s1, *s2, *s3, *s4; + + // 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); + + size = s1->size + s2->size + s3->size + s4->size; + + if(size > 0) { + linkoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND); + seek(cout, linkoff, 0); + + ewrite(cout, s1->p, s1->size); + ewrite(cout, s2->p, s2->size); + ewrite(cout, s3->p, s3->size); + ewrite(cout, s4->p, s4->size); + } + + return rnd(size, INITRND); +} diff --git a/src/cmd/ld/macho.h b/src/cmd/ld/macho.h index a96b2a383..4cc7edc80 100644 --- a/src/cmd/ld/macho.h +++ b/src/cmd/ld/macho.h @@ -18,6 +18,8 @@ struct MachoSect { uint32 reloc; uint32 nreloc; uint32 flag; + uint32 res1; + uint32 res2; }; typedef struct MachoSeg MachoSeg; @@ -70,8 +72,23 @@ enum { MACHO32SYMSIZE = 12, MACHO64SYMSIZE = 16, + + MACHO_X86_64_RELOC_UNSIGNED = 0, + MACHO_X86_64_RELOC_SIGNED = 1, + MACHO_X86_64_RELOC_BRANCH = 2, + MACHO_X86_64_RELOC_GOT_LOAD = 3, + MACHO_X86_64_RELOC_GOT = 4, + MACHO_X86_64_RELOC_SUBTRACTOR = 5, + MACHO_X86_64_RELOC_SIGNED_1 = 6, + MACHO_X86_64_RELOC_SIGNED_2 = 7, + MACHO_X86_64_RELOC_SIGNED_4 = 8, + + MACHO_GENERIC_RELOC_VANILLA = 0, + + MACHO_FAKE_GOTPCREL = 100, }; void domacho(void); vlong domacholink(void); -void asmbmacho(vlong, vlong); +void asmbmacho(void); +void machoadddynlib(char*); diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c index d3439abfb..82c6941f2 100644 --- a/src/cmd/ld/pe.c +++ b/src/cmd/ld/pe.c @@ -33,18 +33,39 @@ static char dosstub[] = 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +int32 PESECTHEADR; +int32 PEFILEHEADR; + static int pe64; static int nsect; -static int sect_virt_begin; -static int sect_raw_begin = PERESERVE; +static int nextsectoff; +static int nextfileoff; static IMAGE_FILE_HEADER fh; static IMAGE_OPTIONAL_HEADER oh; static IMAGE_SECTION_HEADER sh[16]; -static IMAGE_SECTION_HEADER *textsect, *datsect, *bsssect; + +typedef struct Imp Imp; +struct Imp { + Sym* s; + long va; + long vb; + Imp* next; +}; + +typedef struct Dll Dll; +struct Dll { + char* name; + int count; + Imp* ms; + Dll* next; +}; + +static Dll* dr; +static int ndll, nimp, nsize; static IMAGE_SECTION_HEADER* -new_section(char *name, int size, int noraw) +addpesection(char *name, int sectsize, int filesize, Segment *s) { IMAGE_SECTION_HEADER *h; @@ -54,15 +75,23 @@ new_section(char *name, int size, int noraw) } h = &sh[nsect++]; strncpy((char*)h->Name, name, sizeof(h->Name)); - h->VirtualSize = size; - if(!sect_virt_begin) - sect_virt_begin = 0x1000; - h->VirtualAddress = sect_virt_begin; - sect_virt_begin = rnd(sect_virt_begin+size, 0x1000); - if(!noraw) { - h->SizeOfRawData = rnd(size, PEALIGN); - h->PointerToRawData = sect_raw_begin; - sect_raw_begin += h->SizeOfRawData; + h->VirtualSize = sectsize; + h->VirtualAddress = nextsectoff; + nextsectoff = rnd(nextsectoff+sectsize, PESECTALIGN); + h->PointerToRawData = nextfileoff; + if(filesize > 0) { + h->SizeOfRawData = rnd(filesize, PEFILEALIGN); + nextfileoff += h->SizeOfRawData; + } + if(s) { + if(s->vaddr-PEBASE != h->VirtualAddress) { + diag("%s.VirtualAddress = %#llux, want %#llux", name, (vlong)h->VirtualAddress, (vlong)(s->vaddr-PEBASE)); + errorexit(); + } + if(s->fileoff != h->PointerToRawData) { + diag("%s.PointerToRawData = %#llux, want %#llux", name, (vlong)h->PointerToRawData, (vlong)(s->fileoff)); + errorexit(); + } } return h; } @@ -79,6 +108,11 @@ peinit(void) default: break; } + + PEFILEHEADR = rnd(sizeof(dosstub)+sizeof(fh)+sizeof(oh)+sizeof(sh), PEFILEALIGN); + PESECTHEADR = rnd(PEFILEHEADR, PESECTALIGN); + nextsectoff = PESECTHEADR; + nextfileoff = PEFILEHEADR; } static void @@ -86,7 +120,8 @@ pewrite(void) { int i, j; - write(cout, dosstub, sizeof dosstub); + seek(cout, 0, 0); + ewrite(cout, dosstub, sizeof dosstub); strnput("PE", 4); for (i=0; iCharacteristics = IMAGE_SCN_CNT_CODE| - IMAGE_SCN_CNT_INITIALIZED_DATA| - IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ; - - datsect = new_section(".data", datsize, 0); - datsect->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA| - IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; - INITDAT = PEBASE+datsect->VirtualAddress; - - bsssect = new_section(".bss", bsssize, 1); - bsssect->Characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA| - IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; -} - static void strput(char *s) { @@ -124,73 +141,167 @@ strput(char *s) cput('\0'); } -static void -add_import_table(void) +static Dll* +initdynimport(void) { - IMAGE_IMPORT_DESCRIPTOR ds[2], *d; - char *dllname = "kernel32.dll"; - struct { - char *name; - uint32 thunk; - } *f, fs[] = { - { "GetProcAddress", 0 }, - { "LoadLibraryExA", 0 }, - { 0, 0 } - }; - - uint32 size = 0; - memset(ds, 0, sizeof(ds)); - size += sizeof(ds); - ds[0].Name = size; - size += strlen(dllname) + 1; - for(f=fs; f->name; f++) { - f->thunk = size; - size += sizeof(uint16) + strlen(f->name) + 1; + Imp *m; + Dll *d; + Sym *s; + int i; + Sym *dynamic; + + dr = nil; + ndll = 0; + nimp = 0; + nsize = 0; + + for(i=0; ihash) { + if(!s->reachable || !s->dynimpname) + continue; + nimp++; + for(d = dr; d != nil; d = d->next) { + if(strcmp(d->name,s->dynimplib) == 0) { + m = mal(sizeof *m); + m->s = s; + m->next = d->ms; + d->ms = m; + d->count++; + nsize += strlen(s->dynimpname)+2+1; + break; + } + } + if(d == nil) { + d = mal(sizeof *d); + d->name = s->dynimplib; + d->count = 1; + d->next = dr; + dr = d; + m = mal(sizeof *m); + m->s = s; + m->next = 0; + d->ms = m; + ndll++; + nsize += strlen(s->dynimpname)+2+1; + nsize += strlen(s->dynimplib)+1; + } + } + + nsize += 20*ndll + 20; + nsize += 4*nimp + 4*ndll; + + dynamic = lookup(".windynamic", 0); + dynamic->reachable = 1; + dynamic->type = SWINDOWS; + for(d = dr; d != nil; d = d->next) { + for(m = d->ms; m != nil; m = m->next) { + m->s->type = SWINDOWS | SSUB; + m->s->sub = dynamic->sub; + dynamic->sub = m->s; + m->s->value = dynamic->size; + dynamic->size += 4; + } + dynamic->size += 4; } - ds[0].FirstThunk = size; - for(f=fs; f->name; f++) - size += sizeof(fs[0].thunk); + + return dr; +} +static void +addimports(vlong fileoff, IMAGE_SECTION_HEADER *datsect) +{ IMAGE_SECTION_HEADER *isect; - isect = new_section(".idata", size, 0); + uint32 va; + int noff, aoff, o, last_fn, last_name_off, iat_off; + Imp *m; + Dll *d; + Sym* dynamic; + + isect = addpesection(".idata", nsize, nsize, 0); isect->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA| IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; - - uint32 va = isect->VirtualAddress; + va = isect->VirtualAddress; oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = va; oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect->VirtualSize; - ds[0].Name += va; - ds[0].FirstThunk += va; - for(f=fs; f->name; f++) - f->thunk += va; + seek(cout, fileoff, 0); - vlong off = seek(cout, 0, 1); - seek(cout, 0, 2); - for(d=ds; ; d++) { - lputl(d->OriginalFirstThunk); - lputl(d->TimeDateStamp); - lputl(d->ForwarderChain); - lputl(d->Name); - lputl(d->FirstThunk); - if(!d->Name) - break; + dynamic = lookup(".windynamic", 0); + iat_off = dynamic->value - PEBASE; // FirstThunk allocated in .data + oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = iat_off; + oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = dynamic->size; + + noff = va + 20*ndll + 20; + aoff = noff + 4*nimp + 4*ndll; + last_fn = 0; + last_name_off = aoff; + for(d = dr; d != nil; d = d->next) { + lputl(noff); + lputl(0); + lputl(0); + lputl(last_name_off); + lputl(iat_off); + last_fn = d->count; + noff += 4*last_fn + 4; + aoff += 4*last_fn + 4; + iat_off += 4*last_fn + 4; + last_name_off += strlen(d->name)+1; } - strput(dllname); - for(f=fs; f->name; f++) { - wputl(0); - strput(f->name); + lputl(0); //end + lputl(0); + lputl(0); + lputl(0); + lputl(0); + + // put OriginalFirstThunk + o = last_name_off; + for(d = dr; d != nil; d = d->next) { + for(m = d->ms; m != nil; m = m->next) { + lputl(o); + o += 2 + strlen(m->s->dynimpname) + 1; + } + lputl(0); + } + // put names + for(d = dr; d != nil; d = d->next) { + strput(d->name); + } + // put hint+name + for(d = dr; d != nil; d = d->next) { + for(m = d->ms; m != nil; m = m->next) { + wputl(0); + strput(m->s->dynimpname); + } + } + + strnput("", isect->SizeOfRawData - nsize); + cflush(); + + // put FirstThunk + o = last_name_off; + seek(cout, datsect->PointerToRawData + dynamic->value - PEBASE - datsect->VirtualAddress, 0); + for(d = dr; d != nil; d = d->next) { + for(m = d->ms; m != nil; m = m->next) { + lputl(o); + o += 2 + strlen(m->s->dynimpname) + 1; + } + lputl(0); } - for(f=fs; f->name; f++) - lputl(f->thunk); - strnput("", isect->SizeOfRawData - size); cflush(); - seek(cout, off, 0); + seek(cout, 0, 2); +} + +void +dope(void) +{ + initdynimport(); } void asmbpe(void) { + IMAGE_SECTION_HEADER *t, *d; + switch(thechar) { default: diag("unknown PE architecture"); @@ -203,14 +314,16 @@ asmbpe(void) break; } - if(!debug['s']) { - IMAGE_SECTION_HEADER *symsect; - symsect = new_section(".symdat", 8+symsize+lcsize, 0); - symsect->Characteristics = IMAGE_SCN_MEM_READ| - IMAGE_SCN_CNT_INITIALIZED_DATA; - } + t = addpesection(".text", segtext.len, segtext.len, &segtext); + t->Characteristics = IMAGE_SCN_CNT_CODE| + IMAGE_SCN_CNT_INITIALIZED_DATA| + IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ; + + d = addpesection(".data", segdata.len, segdata.filelen, &segdata); + d->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA| + IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; - add_import_table(); + addimports(nextfileoff, d); fh.NumberOfSections = nsect; fh.TimeDateStamp = time(0); @@ -223,24 +336,24 @@ asmbpe(void) oh.Magic = 0x10b; // PE32 oh.MajorLinkerVersion = 1; oh.MinorLinkerVersion = 0; - oh.SizeOfCode = textsect->SizeOfRawData; - oh.SizeOfInitializedData = datsect->SizeOfRawData; - oh.SizeOfUninitializedData = bsssect->SizeOfRawData; + oh.SizeOfCode = t->SizeOfRawData; + oh.SizeOfInitializedData = d->SizeOfRawData; + oh.SizeOfUninitializedData = 0; oh.AddressOfEntryPoint = entryvalue()-PEBASE; - oh.BaseOfCode = textsect->VirtualAddress; - oh.BaseOfData = datsect->VirtualAddress; + oh.BaseOfCode = t->VirtualAddress; + oh.BaseOfData = d->VirtualAddress; oh.ImageBase = PEBASE; - oh.SectionAlignment = 0x00001000; - oh.FileAlignment = PEALIGN; + oh.SectionAlignment = PESECTALIGN; + oh.FileAlignment = PEFILEALIGN; oh.MajorOperatingSystemVersion = 4; oh.MinorOperatingSystemVersion = 0; oh.MajorImageVersion = 1; oh.MinorImageVersion = 0; oh.MajorSubsystemVersion = 4; oh.MinorSubsystemVersion = 0; - oh.SizeOfImage = sect_virt_begin; - oh.SizeOfHeaders = PERESERVE; + oh.SizeOfImage = nextsectoff; + oh.SizeOfHeaders = PEFILEHEADR; oh.Subsystem = 3; // WINDOWS_CUI oh.SizeOfStackReserve = 0x00200000; oh.SizeOfStackCommit = 0x00001000; diff --git a/src/cmd/ld/pe.h b/src/cmd/ld/pe.h index b64dd97c0..f8161cc4a 100644 --- a/src/cmd/ld/pe.h +++ b/src/cmd/ld/pe.h @@ -72,9 +72,16 @@ typedef struct { uint32 FirstThunk; } IMAGE_IMPORT_DESCRIPTOR; -#define PERESERVE 0x400 -#define PEALIGN 0x200 #define PEBASE 0x00400000 +// SectionAlignment must be greater than or equal to FileAlignment. +// The default is the page size for the architecture. +#define PESECTALIGN 0x1000 +// FileAlignment should be a power of 2 between 512 and 64 K, inclusive. +// The default is 512. If the SectionAlignment is less than +// the architecture's page size, then FileAlignment must match SectionAlignment. +#define PEFILEALIGN (2<<8) +extern int32 PESECTHEADR; +extern int32 PEFILEHEADR; enum { IMAGE_FILE_MACHINE_I386 = 0x14c, @@ -112,5 +119,6 @@ enum { }; void peinit(void); -void dope(void); void asmbpe(void); +void dope(void); + diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c new file mode 100644 index 000000000..26e4def64 --- /dev/null +++ b/src/cmd/ld/symtab.c @@ -0,0 +1,259 @@ +// Inferno utils/6l/span.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Symbol table. + +#include "l.h" +#include "../ld/lib.h" +#include "../ld/elf.h" + +char *elfstrdat; +int elfstrsize; +int maxelfstr; +int elftextsh; + +int +putelfstr(char *s) +{ + int off, n; + + if(elfstrsize == 0 && s[0] != 0) { + // first entry must be empty string + putelfstr(""); + } + + n = strlen(s)+1; + if(elfstrsize+n > maxelfstr) { + maxelfstr = 2*(elfstrsize+n+(1<<20)); + elfstrdat = realloc(elfstrdat, maxelfstr); + } + off = elfstrsize; + elfstrsize += n; + memmove(elfstrdat+off, s, n); + return off; +} + +void +putelfsym64(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) +{ + int bind, type, shndx, stroff; + + bind = STB_GLOBAL; + switch(t) { + default: + return; + case 'T': + type = STT_FUNC; + shndx = elftextsh + 0; + break; + case 'D': + type = STT_OBJECT; + shndx = elftextsh + 1; + break; + case 'B': + type = STT_OBJECT; + shndx = elftextsh + 2; + break; + } + + stroff = putelfstr(s); + LPUT(stroff); // string + cput((bind<<4)|(type&0xF)); + cput(0); + WPUT(shndx); + VPUT(addr); + VPUT(size); +} + +void +asmelfsym64(void) +{ + genasmsym(putelfsym64); +} + +void +putelfsym32(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) +{ + int bind, type, shndx, stroff; + + bind = STB_GLOBAL; + switch(t) { + default: + return; + case 'T': + type = STT_FUNC; + shndx = elftextsh + 0; + break; + case 'D': + type = STT_OBJECT; + shndx = elftextsh + 1; + break; + case 'B': + type = STT_OBJECT; + shndx = elftextsh + 2; + break; + } + + stroff = putelfstr(s); + LPUT(stroff); // string + LPUT(addr); + LPUT(size); + cput((bind<<4)|(type&0xF)); + cput(0); + WPUT(shndx); +} + +void +asmelfsym32(void) +{ + genasmsym(putelfsym32); +} + + +static Sym *symt; + +static void +scput(int b) +{ + uchar *p; + + symgrow(symt, symt->size+1); + p = symt->p + symt->size; + *p = b; + symt->size++; +} + +static void +slputb(int32 v) +{ + uchar *p; + + symgrow(symt, symt->size+4); + p = symt->p + symt->size; + *p++ = v>>24; + *p++ = v>>16; + *p++ = v>>8; + *p = v; + symt->size += 4; +} + +void +putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) +{ + int i, f, l; + Reloc *rel; + + if(t == 'f') + name++; + l = 4; +// if(!debug['8']) +// l = 8; + if(s != nil) { + rel = addrel(symt); + rel->siz = l + Rbig; + rel->sym = s; + rel->type = D_ADDR; + rel->off = symt->size; + v = 0; + } + if(l == 8) + slputb(v>>32); + slputb(v); + if(ver) + t += 'a' - 'A'; + scput(t+0x80); /* 0x80 is variable length */ + + if(t == 'Z' || t == 'z') { + scput(name[0]); + for(i=1; name[i] != 0 || name[i+1] != 0; i += 2) { + scput(name[i]); + scput(name[i+1]); + } + scput(0); + scput(0); + i++; + } + else { + for(i=0; name[i]; i++) + scput(name[i]); + scput(0); + } + if(typ) { + if(!typ->reachable) + diag("unreachable type %s", typ->name); + rel = addrel(symt); + rel->siz = l; + rel->sym = typ; + rel->type = D_ADDR; + rel->off = symt->size; + } + if(l == 8) + slputb(0); + slputb(0); + + if(debug['n']) { + if(t == 'z' || t == 'Z') { + Bprint(&bso, "%c %.8llux ", t, v); + for(i=1; name[i] != 0 || name[i+1] != 0; i+=2) { + f = ((name[i]&0xff) << 8) | (name[i+1]&0xff); + Bprint(&bso, "/%x", f); + } + Bprint(&bso, "\n"); + return; + } + if(ver) + Bprint(&bso, "%c %.8llux %s<%d> %s\n", t, v, s, ver, typ ? typ->name : ""); + else + Bprint(&bso, "%c %.8llux %s %s\n", t, v, s, typ ? typ->name : ""); + } +} + +void +symtab(void) +{ + // 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("end", SBSS, 0); + xdefine("epclntab", SRODATA, 0); + xdefine("esymtab", SRODATA, 0); + + symt = lookup("symtab", 0); + symt->type = SRODATA; + symt->size = 0; + symt->reachable = 1; + + genasmsym(putsymb); +} diff --git a/src/cmd/make.bash b/src/cmd/make.bash index d0fda7d18..63da74625 100755 --- a/src/cmd/make.bash +++ b/src/cmd/make.bash @@ -7,9 +7,7 @@ set -e bash clean.bash -GOBIN="${GOBIN:-$HOME/bin}" - -. "$GOROOT"/src/Make.$GOARCH +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 @@ -17,16 +15,16 @@ fi cd ${O}l bash mkenam -"$GOBIN"/gomake enam.o +gomake enam.o cd .. # Note: commands written in Go are not listed here. -# They are in ../make.bash so that they can be built +# They are in ../pkg/Makefile so that they can be built # after the Go libraries on which they depend. for i in cc ${O}l ${O}a ${O}c gc ${O}g cov godefs gopack gotest nm prof do echo; echo; echo %%%% making $i %%%%; echo cd $i - "$GOBIN"/gomake install + gomake install cd .. done diff --git a/src/cmd/nm/Makefile b/src/cmd/nm/Makefile index bb1545122..383dbd973 100644 --- a/src/cmd/nm/Makefile +++ b/src/cmd/nm/Makefile @@ -2,23 +2,17 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +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 and because this binary -# is linked only with amd64 and x86 support. +# We call the binary 6nm to avoid confusion with the host nm. TARG=6nm OFILES=\ nm.$O\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lmach -lbio -l9 +LIB=\ + ../../../lib/libmach.a\ -clean: - rm -f *.$O $(TARG) - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) - -$(OFILES): $(HFILES) +include ../../Make.ccmd diff --git a/src/cmd/nm/nm.c b/src/cmd/nm/nm.c index 978218bff..845b6c773 100644 --- a/src/cmd/nm/nm.c +++ b/src/cmd/nm/nm.c @@ -126,31 +126,34 @@ void doar(Biobuf *bp) { int offset, size, obj; - char membername[SARNAME]; + char name[SARNAME]; multifile = 1; for (offset = Boffset(bp);;offset += size) { - size = nextar(bp, offset, membername); + size = nextar(bp, offset, name); if (size < 0) { - error("phase error on ar header %ld", offset); + error("phase error on ar header %d", offset); return; } if (size == 0) return; - if (strcmp(membername, symname) == 0) + if (strcmp(name, symname) == 0) continue; obj = objtype(bp, 0); if (obj < 0) { + // perhaps foreign object + if(strlen(name) > 2 && strcmp(name+strlen(name)-2, ".o") == 0) + return; error("inconsistent file %s in %s", - membername, filename); + name, filename); return; } if (!readar(bp, obj, offset+size, 1)) { error("invalid symbol reference in file %s", - membername); + name); return; } - filename = membername; + filename = name; nsym=0; objtraverse(psym, 0); printsyms(symptr, nsym); diff --git a/src/cmd/prof/Makefile b/src/cmd/prof/Makefile index 602c07da6..e643f267c 100644 --- a/src/cmd/prof/Makefile +++ b/src/cmd/prof/Makefile @@ -2,7 +2,8 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +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 @@ -12,19 +13,22 @@ TARG=6prof OFILES=\ main.$O\ -#HFILES=\ -# defs.h\ -# fns.h\ +LIB=\ + ../../../lib/libmach.a\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lmach -lbio -l9 +NOINSTALL=1 +include ../../Make.ccmd -clean: - rm -f *.$O $(TARG) +ifeq ($(GOOS),windows) +NAME=windows +else +NAME=$(shell uname | tr A-Z a-z) +endif -install: install-$(shell uname | tr A-Z a-z) install-pprof +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) @@ -33,7 +37,5 @@ install-darwin: $(TARG) install-default: $(TARG) cp $(TARG) "$(GOBIN)"/$(TARG) -$(OFILES): $(HFILES) - install-pprof: gopprof cp gopprof "$(GOBIN)"/gopprof diff --git a/src/cmd/prof/gopprof b/src/cmd/prof/gopprof index dffeeffa1..4bcfa5800 100755 --- a/src/cmd/prof/gopprof +++ b/src/cmd/prof/gopprof @@ -2736,6 +2736,7 @@ sub IsSymbolizedProfileFile { sub CheckSymbolPage { my $url = SymbolPageURL(); +print STDERR "Read $url\n"; open(SYMBOL, "$CURL -s '$url' |"); my $line = ; $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines @@ -2816,7 +2817,7 @@ sub ResolveRedirectionForCurl { # $main::prog to have the correct program name. sub ReadSymbols { my $in = shift; - my $map = {}; + 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. @@ -2858,20 +2859,30 @@ sub FetchSymbols { my @pcs = grep { !$seen{$_}++ } keys(%$pcset); # uniq if (!defined($symbol_map)) { - my $post_data = join("+", sort((map {"0x" . "$_"} @pcs))); - - 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); - $symbol_map = ReadSymbols(*SYMBOL{IO}); - close(SYMBOL); + $symbol_map = {}; + my @toask = @pcs; + while (@toask > 0) { + my $n = @toask; + if ($n > 49) { $n = 49; } + my @thisround = @toask[0..$n]; +my $t = @toask; +print STDERR "$n $t\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); + +print STDERR "SYMBL!\n"; + 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 = {}; diff --git a/src/cmd/prof/main.c b/src/cmd/prof/main.c index 2bb67f596..f36759cd3 100644 --- a/src/cmd/prof/main.c +++ b/src/cmd/prof/main.c @@ -53,9 +53,10 @@ Map *map[32]; // thread maps void Usage(void) { - fprint(2, "Usage: prof -p pid [-t total_secs] [-d delta_msec] [6.out args ...]\n"); + 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-c file.prof: write [c]pprof output to file.prof\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"); @@ -192,22 +193,22 @@ amd64_ppword(uvlong w) void x86_regprint(void) { - fprint(2, "ax\t0x%llux\n", ureg_x86.ax); - fprint(2, "bx\t0x%llux\n", ureg_x86.bx); - fprint(2, "cx\t0x%llux\n", ureg_x86.cx); - fprint(2, "dx\t0x%llux\n", ureg_x86.dx); - fprint(2, "si\t0x%llux\n", ureg_x86.si); - fprint(2, "di\t0x%llux\n", ureg_x86.di); - fprint(2, "bp\t0x%llux\n", ureg_x86.bp); - fprint(2, "ds\t0x%llux\n", ureg_x86.ds); - fprint(2, "es\t0x%llux\n", ureg_x86.es); - fprint(2, "fs\t0x%llux\n", ureg_x86.fs); - fprint(2, "gs\t0x%llux\n", ureg_x86.gs); - fprint(2, "cs\t0x%llux\n", ureg_x86.cs); - fprint(2, "flags\t0x%llux\n", ureg_x86.flags); - fprint(2, "pc\t0x%llux\n", ureg_x86.pc); - fprint(2, "sp\t0x%llux\n", ureg_x86.sp); - fprint(2, "ss\t0x%llux\n", ureg_x86.ss); + 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 diff --git a/src/env.bash b/src/env.bash index 2a63e6480..2518c4233 100644 --- a/src/env.bash +++ b/src/env.bash @@ -3,24 +3,11 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -if test -z "$GOBIN"; then - if ! test -d "$HOME"/bin; then - echo '$GOBIN is not set and $HOME/bin is not a directory or does not exist.' 1>&2 - echo 'mkdir $HOME/bin or set $GOBIN to a directory where binaries should' 1>&2 - echo 'be installed.' 1>&2 - exit 1 - fi - GOBIN="$HOME/bin" -elif ! test -d "$GOBIN"; then - echo '$GOBIN is not a directory or does not exist' 1>&2 - echo 'create it or set $GOBIN differently' 1>&2 - exit 1 -fi +export GOROOT=${GOROOT:-$(cd ..; pwd)} -GOROOT=${GOROOT:-$(cd ..; pwd)} if ! test -f "$GOROOT"/include/u.h then - echo '$GOROOT is not set correctly or not exported' 1>&2 + echo '$GOROOT is not set correctly or not exported: '$GOROOT 1>&2 exit 1 fi @@ -28,28 +15,35 @@ fi # Various aspects of the build cd into $GOROOT-rooted paths, # making it easy to jump to a different tree and get confused. DIR1=$(cd ..; pwd) -DIR2=$(cd $GOROOT; pwd) +DIR2=$(cd "$GOROOT"; pwd) if [ "$DIR1" != "$DIR2" ]; then - echo 'Suspicious $GOROOT: does not match current directory.' 1>&2 + echo 'Suspicious $GOROOT '"$GOROOT"': does not match current directory.' 1>&2 exit 1 fi -GOARCH=${GOARCH:-$(uname -m | sed 's/^..86$/386/; s/^.86$/386/; s/x86_64/amd64/')} -case "$GOARCH" in -amd64 | 386 | arm) - ;; -*) - echo '$GOARCH is set to <'$GOARCH'>, must be amd64, 386, or arm' 1>&2 - exit 1 -esac - -GOOS=${GOOS:-$(uname | tr A-Z a-z)} -case "$GOOS" in -darwin | freebsd | linux | windows | nacl) - ;; -*) - echo '$GOOS is set to <'$GOOS'>, must be darwin, freebsd, linux, windows, or nacl' 1>&2 +export GOBIN=${GOBIN:-"$GOROOT/bin"} +if [ ! -d "$GOBIN" -a "$GOBIN" != "$GOROOT/bin" ]; then + echo '$GOBIN is not a directory or does not exist' 1>&2 + echo 'create it or set $GOBIN differently' 1>&2 exit 1 -esac +fi + +export OLDPATH=$PATH +export PATH=/bin:/usr/bin:"$GOBIN":$PATH + +MAKE=make +if ! make --version 2>/dev/null | grep 'GNU Make' >/dev/null; then + MAKE=gmake +fi -export GOBIN GOROOT GOARCH GOOS +# Tried to use . <($MAKE ...) here, but it cannot set environment +# variables in the version of bash that ships with OS X. Amazing. +eval $($MAKE --no-print-directory -f Make.inc go-env | egrep 'GOARCH|GOOS|GO_ENV') + +# Shell doesn't tell us whether make succeeded, +# so Make.inc generates a fake variable name. +if [ "$MAKE_GO_ENV_WORKED" != 1 ]; then + echo 'Did not find Go environment variables.' 1>&2 + exit 1 +fi +unset MAKE_GO_ENV_WORKED diff --git a/src/lib9/Makefile b/src/lib9/Makefile index ccaf41a0f..a10d7730a 100644 --- a/src/lib9/Makefile +++ b/src/lib9/Makefile @@ -2,7 +2,8 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../Make.conf +include ../Make.inc +O:=$(HOST_O) LIB=lib9.a @@ -101,38 +102,19 @@ OFILES=\ $(UTFOFILES)\ HFILES=\ - "$(GOROOT)"/include/u.h\ - "$(GOROOT)"/include/libc.h\ + $(QUOTED_GOROOT)/include/u.h\ + $(QUOTED_GOROOT)/include/libc.h\ -install: $(LIB) - cp $(LIB) "$(GOROOT)/lib" +include ../Make.clib -$(LIB): $(OFILES) - ar rsc $(LIB) $(OFILES) +GOROOT_FINAL?=$(GOROOT) %.$O: fmt/%.c - $(CC) -c $(CFLAGS) -DPLAN9PORT -Ifmt $< + $(HOST_CC) -c $(HOST_CFLAGS) -DPLAN9PORT -Ifmt $< %.$O: utf/%.c - $(CC) -c $(CFLAGS) $< + $(HOST_CC) -c $(HOST_CFLAGS) $< goos.$O: goos.c - $(CC) -c $(CFLAGS) -DGOOS='"$(GOOS)"' -DGOARCH='"$(GOARCH)"' -DGOROOT='"$(GOROOT)"' -DGOVERSION='"'"$$(../version.bash)"'"' $< - -clean: - rm -f *.$O *.6 6.out $(LIB) - -nuke: clean - rm -f "$(GOROOT)"/lib/$(LIB) - -#XLIB=$PLAN9/lib/$LIB - -#testfmt: testfmt.$O $XLIB -# $LD -o $target testfmt.$O - -#testfltfmt: testfltfmt.$O $XLIB -# $LD -o $target testfltfmt.$O - -#testprint: testprint.$O $XLIB -# $LD -o $target testprint.$O + $(HOST_CC) -c $(HOST_CFLAGS) -DGOOS='"$(GOOS)"' -DGOARCH='"$(GOARCH)"' -DGOROOT='"$(GOROOT_FINAL)"' -DGOVERSION='"'"$$(../version.bash)"'"' $< diff --git a/src/lib9/dirfwstat.c b/src/lib9/dirfwstat.c index 15f1c1252..fe9153b9b 100644 --- a/src/lib9/dirfwstat.c +++ b/src/lib9/dirfwstat.c @@ -61,7 +61,7 @@ dirfwstat(int fd, Dir *dir) struct timeval tv[2]; ret = 0; -#ifndef __MINGW32__ +#ifndef _WIN32 if(~dir->mode != 0){ if(fchmod(fd, dir->mode) < 0) ret = -1; diff --git a/src/lib9/dirstat.c b/src/lib9/dirstat.c index 6c476753b..6d804ca7c 100644 --- a/src/lib9/dirstat.c +++ b/src/lib9/dirstat.c @@ -39,7 +39,7 @@ dirstat(char *file) Dir *d; char *str; -#ifdef __MINGW32__ +#ifdef _WIN32 if(stat(file, &st) < 0) return nil; lst = st; diff --git a/src/lib9/time.c b/src/lib9/time.c index 720dd702e..7394e9e60 100644 --- a/src/lib9/time.c +++ b/src/lib9/time.c @@ -25,7 +25,7 @@ THE SOFTWARE. #include #include #include -#ifndef __MINGW32__ +#ifndef _WIN32 #include #endif #define NOPLAN9DEFINES @@ -34,7 +34,7 @@ THE SOFTWARE. long p9times(long *t) { -#ifdef __MINGW32__ +#ifdef _WIN32 memset(t, 0, 4*sizeof(long)); #else struct rusage ru, cru; diff --git a/src/lib9/utf/Makefile b/src/lib9/utf/Makefile new file mode 100644 index 000000000..bd15f9eab --- /dev/null +++ b/src/lib9/utf/Makefile @@ -0,0 +1,16 @@ +# 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 library is built by the Makefile in the parent directory. +# This Makefile only builds mkrunetype. + +include ../../Make.inc +O:=$(HOST_O) + +TARG=mkrunetype + +OFILES=\ + mkrunetype.$O\ + +include ../../Make.ccmd diff --git a/src/lib9/utf/mkrunetype.c b/src/lib9/utf/mkrunetype.c index f1a9f8a77..848056451 100644 --- a/src/lib9/utf/mkrunetype.c +++ b/src/lib9/utf/mkrunetype.c @@ -32,11 +32,9 @@ * isdigitrune is true iff c is a numeric-digit category. */ +#include +#include #include -#include -#include -#include -#include #include "utf.h" #include "utfdef.h" @@ -149,8 +147,8 @@ main(int argc, char *argv[]){ last = -1; while(getunicodeline(in, fields, buf)){ code = getcode(fields[FIELD_CODE]); - if (code >= NRUNES) - fatal("code-point value too big: %x", code); + if (code >= NRUNES) + fatal("code-point value too big: %x", code); if(code <= last) fatal("bad code sequence: %x then %x", last, code); last = code; @@ -588,8 +586,7 @@ mkisronly(const char* label, char* prop) { static void mktables(char *src, int usepairs) { - printf("/* generated automatically by mkrunetype.c from %s */\n\n", - basename(src)); + printf("/* generated automatically by mkrunetype.c from %s */\n\n", src); /* * we special case the space and digit tables, since they are assumed @@ -703,8 +700,8 @@ getcode(char *s) int i, code; code = 0; - i = 0; - /* Parse a hex number */ + i = 0; + /* Parse a hex number */ while(s[i]) { code <<= 4; if(s[i] >= '0' && s[i] <= '9') @@ -713,7 +710,7 @@ getcode(char *s) code += s[i] - 'A' + 10; else fatal("bad code char '%c'", s[i]); - i++; + i++; } return code; } diff --git a/src/lib9/utf/runetype.c b/src/lib9/utf/runetype.c index ff2dbb539..19cf433e5 100644 --- a/src/lib9/utf/runetype.c +++ b/src/lib9/utf/runetype.c @@ -46,14 +46,27 @@ rbsearch(Rune c, Rune *t, int n, int ne) * chars as ideographic as well: 20000..2a6d6, and 2f800..2Fa1d. */ static Rune __isideographicr[] = { - 0x3006, 0x3007, /* 3006 not in Unicode 2, in 2.1 */ + 0x3006, 0x3007, /* 0x3006 added in 2.0.14 */ 0x3021, 0x3029, - 0x3038, 0x303a, /* not in Unicode 2 or 2.1 */ - 0x3400, 0x4db5, /* not in Unicode 2 or 2.1 */ - 0x4e00, 0x9fbb, /* 0x9FA6..0x9FBB added for 4.1.0? */ + 0x3038, 0x303a, /* added in 3.0.0 */ + 0x3400, 0x4db5, /* added in 3.0.0 */ + + /* consecutive */ + 0x4e00, 0x9fa5, + 0x9fa6, 0x9fbb, /* added in 4.1.0 */ + 0x9fbc, 0x9fc3, /* added in 5.1.0 */ + 0x9fc4, 0x9fcb, /* added in 5.2.0 */ + 0xf900, 0xfa2d, - 0x20000, 0x2A6D6, - 0x2F800, 0x2FA1D, + + /* consecutive */ + 0xfa30, 0xfa6a, /* added in 5.1.0 */ + 0xfa6b, 0xfa6d, /* added in 5.2.0 */ + + 0xfa70, 0xfad9, /* added in 4.1.0 */ + 0x20000, 0x2a6d6, /* added in 3.1.0 */ + 0x2a700, 0x2b734, /* added in 5.2.0 */ + 0x2f800, 0x2fa1d, /* added in 3.1.0 */ }; int @@ -67,4 +80,4 @@ isideographicrune(Rune c) return 0; } -#include "runetypebody-5.0.0.c" +#include "runetypebody-5.2.0.c" diff --git a/src/lib9/utf/runetypebody-5.2.0.c b/src/lib9/utf/runetypebody-5.2.0.c new file mode 100644 index 000000000..4ff66b9d9 --- /dev/null +++ b/src/lib9/utf/runetypebody-5.2.0.c @@ -0,0 +1,1541 @@ +/* generated automatically by mkrunetype.c from UnicodeData-5.2.0.txt */ + +static Rune __isspacer[] = { + 0x0009, 0x000d, + 0x0020, 0x0020, + 0x0085, 0x0085, + 0x00a0, 0x00a0, + 0x1680, 0x1680, + 0x180e, 0x180e, + 0x2000, 0x200a, + 0x2028, 0x2029, + 0x202f, 0x202f, + 0x205f, 0x205f, + 0x3000, 0x3000, + 0xfeff, 0xfeff, +}; + +int +isspacerune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __isspacer, nelem(__isspacer)/2, 2); + if(p && c >= p[0] && c <= p[1]) + return 1; + return 0; +} + +static Rune __isdigitr[] = { + 0x0030, 0x0039, + 0x0660, 0x0669, + 0x06f0, 0x06f9, + 0x07c0, 0x07c9, + 0x0966, 0x096f, + 0x09e6, 0x09ef, + 0x0a66, 0x0a6f, + 0x0ae6, 0x0aef, + 0x0b66, 0x0b6f, + 0x0be6, 0x0bef, + 0x0c66, 0x0c6f, + 0x0ce6, 0x0cef, + 0x0d66, 0x0d6f, + 0x0e50, 0x0e59, + 0x0ed0, 0x0ed9, + 0x0f20, 0x0f29, + 0x1040, 0x1049, + 0x1090, 0x1099, + 0x17e0, 0x17e9, + 0x1810, 0x1819, + 0x1946, 0x194f, + 0x19d0, 0x19da, + 0x1a80, 0x1a89, + 0x1a90, 0x1a99, + 0x1b50, 0x1b59, + 0x1bb0, 0x1bb9, + 0x1c40, 0x1c49, + 0x1c50, 0x1c59, + 0xa620, 0xa629, + 0xa8d0, 0xa8d9, + 0xa900, 0xa909, + 0xa9d0, 0xa9d9, + 0xaa50, 0xaa59, + 0xabf0, 0xabf9, + 0xff10, 0xff19, + 0x104a0, 0x104a9, + 0x1d7ce, 0x1d7ff, +}; + +int +isdigitrune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __isdigitr, nelem(__isdigitr)/2, 2); + if(p && c >= p[0] && c <= p[1]) + return 1; + return 0; +} + +static Rune __isalphar[] = { + 0x0041, 0x005a, + 0x0061, 0x007a, + 0x00c0, 0x00d6, + 0x00d8, 0x00f6, + 0x00f8, 0x02c1, + 0x02c6, 0x02d1, + 0x02e0, 0x02e4, + 0x0370, 0x0374, + 0x0376, 0x0377, + 0x037a, 0x037d, + 0x0388, 0x038a, + 0x038e, 0x03a1, + 0x03a3, 0x03f5, + 0x03f7, 0x0481, + 0x048a, 0x0525, + 0x0531, 0x0556, + 0x0561, 0x0587, + 0x05d0, 0x05ea, + 0x05f0, 0x05f2, + 0x0621, 0x064a, + 0x066e, 0x066f, + 0x0671, 0x06d3, + 0x06e5, 0x06e6, + 0x06ee, 0x06ef, + 0x06fa, 0x06fc, + 0x0712, 0x072f, + 0x074d, 0x07a5, + 0x07ca, 0x07ea, + 0x07f4, 0x07f5, + 0x0800, 0x0815, + 0x0904, 0x0939, + 0x0958, 0x0961, + 0x0971, 0x0972, + 0x0979, 0x097f, + 0x0985, 0x098c, + 0x098f, 0x0990, + 0x0993, 0x09a8, + 0x09aa, 0x09b0, + 0x09b6, 0x09b9, + 0x09dc, 0x09dd, + 0x09df, 0x09e1, + 0x09f0, 0x09f1, + 0x0a05, 0x0a0a, + 0x0a0f, 0x0a10, + 0x0a13, 0x0a28, + 0x0a2a, 0x0a30, + 0x0a32, 0x0a33, + 0x0a35, 0x0a36, + 0x0a38, 0x0a39, + 0x0a59, 0x0a5c, + 0x0a72, 0x0a74, + 0x0a85, 0x0a8d, + 0x0a8f, 0x0a91, + 0x0a93, 0x0aa8, + 0x0aaa, 0x0ab0, + 0x0ab2, 0x0ab3, + 0x0ab5, 0x0ab9, + 0x0ae0, 0x0ae1, + 0x0b05, 0x0b0c, + 0x0b0f, 0x0b10, + 0x0b13, 0x0b28, + 0x0b2a, 0x0b30, + 0x0b32, 0x0b33, + 0x0b35, 0x0b39, + 0x0b5c, 0x0b5d, + 0x0b5f, 0x0b61, + 0x0b85, 0x0b8a, + 0x0b8e, 0x0b90, + 0x0b92, 0x0b95, + 0x0b99, 0x0b9a, + 0x0b9e, 0x0b9f, + 0x0ba3, 0x0ba4, + 0x0ba8, 0x0baa, + 0x0bae, 0x0bb9, + 0x0c05, 0x0c0c, + 0x0c0e, 0x0c10, + 0x0c12, 0x0c28, + 0x0c2a, 0x0c33, + 0x0c35, 0x0c39, + 0x0c58, 0x0c59, + 0x0c60, 0x0c61, + 0x0c85, 0x0c8c, + 0x0c8e, 0x0c90, + 0x0c92, 0x0ca8, + 0x0caa, 0x0cb3, + 0x0cb5, 0x0cb9, + 0x0ce0, 0x0ce1, + 0x0d05, 0x0d0c, + 0x0d0e, 0x0d10, + 0x0d12, 0x0d28, + 0x0d2a, 0x0d39, + 0x0d60, 0x0d61, + 0x0d7a, 0x0d7f, + 0x0d85, 0x0d96, + 0x0d9a, 0x0db1, + 0x0db3, 0x0dbb, + 0x0dc0, 0x0dc6, + 0x0e01, 0x0e30, + 0x0e32, 0x0e33, + 0x0e40, 0x0e46, + 0x0e81, 0x0e82, + 0x0e87, 0x0e88, + 0x0e94, 0x0e97, + 0x0e99, 0x0e9f, + 0x0ea1, 0x0ea3, + 0x0eaa, 0x0eab, + 0x0ead, 0x0eb0, + 0x0eb2, 0x0eb3, + 0x0ec0, 0x0ec4, + 0x0edc, 0x0edd, + 0x0f40, 0x0f47, + 0x0f49, 0x0f6c, + 0x0f88, 0x0f8b, + 0x1000, 0x102a, + 0x1050, 0x1055, + 0x105a, 0x105d, + 0x1065, 0x1066, + 0x106e, 0x1070, + 0x1075, 0x1081, + 0x10a0, 0x10c5, + 0x10d0, 0x10fa, + 0x1100, 0x1248, + 0x124a, 0x124d, + 0x1250, 0x1256, + 0x125a, 0x125d, + 0x1260, 0x1288, + 0x128a, 0x128d, + 0x1290, 0x12b0, + 0x12b2, 0x12b5, + 0x12b8, 0x12be, + 0x12c2, 0x12c5, + 0x12c8, 0x12d6, + 0x12d8, 0x1310, + 0x1312, 0x1315, + 0x1318, 0x135a, + 0x1380, 0x138f, + 0x13a0, 0x13f4, + 0x1401, 0x166c, + 0x166f, 0x167f, + 0x1681, 0x169a, + 0x16a0, 0x16ea, + 0x1700, 0x170c, + 0x170e, 0x1711, + 0x1720, 0x1731, + 0x1740, 0x1751, + 0x1760, 0x176c, + 0x176e, 0x1770, + 0x1780, 0x17b3, + 0x1820, 0x1877, + 0x1880, 0x18a8, + 0x18b0, 0x18f5, + 0x1900, 0x191c, + 0x1950, 0x196d, + 0x1970, 0x1974, + 0x1980, 0x19ab, + 0x19c1, 0x19c7, + 0x1a00, 0x1a16, + 0x1a20, 0x1a54, + 0x1b05, 0x1b33, + 0x1b45, 0x1b4b, + 0x1b83, 0x1ba0, + 0x1bae, 0x1baf, + 0x1c00, 0x1c23, + 0x1c4d, 0x1c4f, + 0x1c5a, 0x1c7d, + 0x1ce9, 0x1cec, + 0x1cee, 0x1cf1, + 0x1d00, 0x1dbf, + 0x1e00, 0x1f15, + 0x1f18, 0x1f1d, + 0x1f20, 0x1f45, + 0x1f48, 0x1f4d, + 0x1f50, 0x1f57, + 0x1f5f, 0x1f7d, + 0x1f80, 0x1fb4, + 0x1fb6, 0x1fbc, + 0x1fc2, 0x1fc4, + 0x1fc6, 0x1fcc, + 0x1fd0, 0x1fd3, + 0x1fd6, 0x1fdb, + 0x1fe0, 0x1fec, + 0x1ff2, 0x1ff4, + 0x1ff6, 0x1ffc, + 0x2090, 0x2094, + 0x210a, 0x2113, + 0x2119, 0x211d, + 0x212a, 0x212d, + 0x212f, 0x2139, + 0x213c, 0x213f, + 0x2145, 0x2149, + 0x2183, 0x2184, + 0x2c00, 0x2c2e, + 0x2c30, 0x2c5e, + 0x2c60, 0x2ce4, + 0x2ceb, 0x2cee, + 0x2d00, 0x2d25, + 0x2d30, 0x2d65, + 0x2d80, 0x2d96, + 0x2da0, 0x2da6, + 0x2da8, 0x2dae, + 0x2db0, 0x2db6, + 0x2db8, 0x2dbe, + 0x2dc0, 0x2dc6, + 0x2dc8, 0x2dce, + 0x2dd0, 0x2dd6, + 0x2dd8, 0x2dde, + 0x3005, 0x3006, + 0x3031, 0x3035, + 0x303b, 0x303c, + 0x3041, 0x3096, + 0x309d, 0x309f, + 0x30a1, 0x30fa, + 0x30fc, 0x30ff, + 0x3105, 0x312d, + 0x3131, 0x318e, + 0x31a0, 0x31b7, + 0x31f0, 0x31ff, + 0x3400, 0x4db5, + 0x4e00, 0x9fcb, + 0xa000, 0xa48c, + 0xa4d0, 0xa4fd, + 0xa500, 0xa60c, + 0xa610, 0xa61f, + 0xa62a, 0xa62b, + 0xa640, 0xa65f, + 0xa662, 0xa66e, + 0xa67f, 0xa697, + 0xa6a0, 0xa6e5, + 0xa717, 0xa71f, + 0xa722, 0xa788, + 0xa78b, 0xa78c, + 0xa7fb, 0xa801, + 0xa803, 0xa805, + 0xa807, 0xa80a, + 0xa80c, 0xa822, + 0xa840, 0xa873, + 0xa882, 0xa8b3, + 0xa8f2, 0xa8f7, + 0xa90a, 0xa925, + 0xa930, 0xa946, + 0xa960, 0xa97c, + 0xa984, 0xa9b2, + 0xaa00, 0xaa28, + 0xaa40, 0xaa42, + 0xaa44, 0xaa4b, + 0xaa60, 0xaa76, + 0xaa80, 0xaaaf, + 0xaab5, 0xaab6, + 0xaab9, 0xaabd, + 0xaadb, 0xaadd, + 0xabc0, 0xabe2, + 0xac00, 0xd7a3, + 0xd7b0, 0xd7c6, + 0xd7cb, 0xd7fb, + 0xf900, 0xfa2d, + 0xfa30, 0xfa6d, + 0xfa70, 0xfad9, + 0xfb00, 0xfb06, + 0xfb13, 0xfb17, + 0xfb1f, 0xfb28, + 0xfb2a, 0xfb36, + 0xfb38, 0xfb3c, + 0xfb40, 0xfb41, + 0xfb43, 0xfb44, + 0xfb46, 0xfbb1, + 0xfbd3, 0xfd3d, + 0xfd50, 0xfd8f, + 0xfd92, 0xfdc7, + 0xfdf0, 0xfdfb, + 0xfe70, 0xfe74, + 0xfe76, 0xfefc, + 0xff21, 0xff3a, + 0xff41, 0xff5a, + 0xff66, 0xffbe, + 0xffc2, 0xffc7, + 0xffca, 0xffcf, + 0xffd2, 0xffd7, + 0xffda, 0xffdc, + 0x10000, 0x1000b, + 0x1000d, 0x10026, + 0x10028, 0x1003a, + 0x1003c, 0x1003d, + 0x1003f, 0x1004d, + 0x10050, 0x1005d, + 0x10080, 0x100fa, + 0x10280, 0x1029c, + 0x102a0, 0x102d0, + 0x10300, 0x1031e, + 0x10330, 0x10340, + 0x10342, 0x10349, + 0x10380, 0x1039d, + 0x103a0, 0x103c3, + 0x103c8, 0x103cf, + 0x10400, 0x1049d, + 0x10800, 0x10805, + 0x1080a, 0x10835, + 0x10837, 0x10838, + 0x1083f, 0x10855, + 0x10900, 0x10915, + 0x10920, 0x10939, + 0x10a10, 0x10a13, + 0x10a15, 0x10a17, + 0x10a19, 0x10a33, + 0x10a60, 0x10a7c, + 0x10b00, 0x10b35, + 0x10b40, 0x10b55, + 0x10b60, 0x10b72, + 0x10c00, 0x10c48, + 0x11083, 0x110af, + 0x12000, 0x1236e, + 0x13000, 0x1342e, + 0x1d400, 0x1d454, + 0x1d456, 0x1d49c, + 0x1d49e, 0x1d49f, + 0x1d4a5, 0x1d4a6, + 0x1d4a9, 0x1d4ac, + 0x1d4ae, 0x1d4b9, + 0x1d4bd, 0x1d4c3, + 0x1d4c5, 0x1d505, + 0x1d507, 0x1d50a, + 0x1d50d, 0x1d514, + 0x1d516, 0x1d51c, + 0x1d51e, 0x1d539, + 0x1d53b, 0x1d53e, + 0x1d540, 0x1d544, + 0x1d54a, 0x1d550, + 0x1d552, 0x1d6a5, + 0x1d6a8, 0x1d6c0, + 0x1d6c2, 0x1d6da, + 0x1d6dc, 0x1d6fa, + 0x1d6fc, 0x1d714, + 0x1d716, 0x1d734, + 0x1d736, 0x1d74e, + 0x1d750, 0x1d76e, + 0x1d770, 0x1d788, + 0x1d78a, 0x1d7a8, + 0x1d7aa, 0x1d7c2, + 0x1d7c4, 0x1d7cb, + 0x20000, 0x2a6d6, + 0x2a700, 0x2b734, + 0x2f800, 0x2fa1d, +}; + +static Rune __isalphas[] = { + 0x00aa, + 0x00b5, + 0x00ba, + 0x02ec, + 0x02ee, + 0x0386, + 0x038c, + 0x0559, + 0x06d5, + 0x06ff, + 0x0710, + 0x07b1, + 0x07fa, + 0x081a, + 0x0824, + 0x0828, + 0x093d, + 0x0950, + 0x09b2, + 0x09bd, + 0x09ce, + 0x0a5e, + 0x0abd, + 0x0ad0, + 0x0b3d, + 0x0b71, + 0x0b83, + 0x0b9c, + 0x0bd0, + 0x0c3d, + 0x0cbd, + 0x0cde, + 0x0d3d, + 0x0dbd, + 0x0e84, + 0x0e8a, + 0x0e8d, + 0x0ea5, + 0x0ea7, + 0x0ebd, + 0x0ec6, + 0x0f00, + 0x103f, + 0x1061, + 0x108e, + 0x10fc, + 0x1258, + 0x12c0, + 0x17d7, + 0x17dc, + 0x18aa, + 0x1aa7, + 0x1f59, + 0x1f5b, + 0x1f5d, + 0x1fbe, + 0x2071, + 0x207f, + 0x2102, + 0x2107, + 0x2115, + 0x2124, + 0x2126, + 0x2128, + 0x214e, + 0x2d6f, + 0x2e2f, + 0xa8fb, + 0xa9cf, + 0xaa7a, + 0xaab1, + 0xaac0, + 0xaac2, + 0xfb1d, + 0xfb3e, + 0x10808, + 0x1083c, + 0x10a00, + 0x1d4a2, + 0x1d4bb, + 0x1d546, +}; + +int +isalpharune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __isalphar, nelem(__isalphar)/2, 2); + if(p && c >= p[0] && c <= p[1]) + return 1; + p = rbsearch(c, __isalphas, nelem(__isalphas), 1); + if(p && c == p[0]) + return 1; + return 0; +} + +static Rune __isupperr[] = { + 0x0041, 0x005a, + 0x00c0, 0x00d6, + 0x00d8, 0x00de, + 0x0178, 0x0179, + 0x0181, 0x0182, + 0x0186, 0x0187, + 0x0189, 0x018b, + 0x018e, 0x0191, + 0x0193, 0x0194, + 0x0196, 0x0198, + 0x019c, 0x019d, + 0x019f, 0x01a0, + 0x01a6, 0x01a7, + 0x01ae, 0x01af, + 0x01b1, 0x01b3, + 0x01b7, 0x01b8, + 0x01f6, 0x01f8, + 0x023a, 0x023b, + 0x023d, 0x023e, + 0x0243, 0x0246, + 0x0388, 0x038a, + 0x038e, 0x038f, + 0x0391, 0x03a1, + 0x03a3, 0x03ab, + 0x03d2, 0x03d4, + 0x03f9, 0x03fa, + 0x03fd, 0x042f, + 0x04c0, 0x04c1, + 0x0531, 0x0556, + 0x10a0, 0x10c5, + 0x1f08, 0x1f0f, + 0x1f18, 0x1f1d, + 0x1f28, 0x1f2f, + 0x1f38, 0x1f3f, + 0x1f48, 0x1f4d, + 0x1f68, 0x1f6f, + 0x1f88, 0x1f8f, + 0x1f98, 0x1f9f, + 0x1fa8, 0x1faf, + 0x1fb8, 0x1fbc, + 0x1fc8, 0x1fcc, + 0x1fd8, 0x1fdb, + 0x1fe8, 0x1fec, + 0x1ff8, 0x1ffc, + 0x210b, 0x210d, + 0x2110, 0x2112, + 0x2119, 0x211d, + 0x212a, 0x212d, + 0x2130, 0x2133, + 0x213e, 0x213f, + 0x2160, 0x216f, + 0x24b6, 0x24cf, + 0x2c00, 0x2c2e, + 0x2c62, 0x2c64, + 0x2c6d, 0x2c70, + 0x2c7e, 0x2c80, + 0xa77d, 0xa77e, + 0xff21, 0xff3a, + 0x10400, 0x10427, + 0x1d400, 0x1d419, + 0x1d434, 0x1d44d, + 0x1d468, 0x1d481, + 0x1d49e, 0x1d49f, + 0x1d4a5, 0x1d4a6, + 0x1d4a9, 0x1d4ac, + 0x1d4ae, 0x1d4b5, + 0x1d4d0, 0x1d4e9, + 0x1d504, 0x1d505, + 0x1d507, 0x1d50a, + 0x1d50d, 0x1d514, + 0x1d516, 0x1d51c, + 0x1d538, 0x1d539, + 0x1d53b, 0x1d53e, + 0x1d540, 0x1d544, + 0x1d54a, 0x1d550, + 0x1d56c, 0x1d585, + 0x1d5a0, 0x1d5b9, + 0x1d5d4, 0x1d5ed, + 0x1d608, 0x1d621, + 0x1d63c, 0x1d655, + 0x1d670, 0x1d689, + 0x1d6a8, 0x1d6c0, + 0x1d6e2, 0x1d6fa, + 0x1d71c, 0x1d734, + 0x1d756, 0x1d76e, + 0x1d790, 0x1d7a8, +}; + +static Rune __isupperp[] = { + 0x0100, 0x0136, + 0x0139, 0x0147, + 0x014a, 0x0176, + 0x017b, 0x017d, + 0x01a2, 0x01a4, + 0x01cd, 0x01db, + 0x01de, 0x01ee, + 0x01fa, 0x0232, + 0x0248, 0x024e, + 0x0370, 0x0372, + 0x03d8, 0x03ee, + 0x0460, 0x0480, + 0x048a, 0x04be, + 0x04c3, 0x04cd, + 0x04d0, 0x0524, + 0x1e00, 0x1e94, + 0x1e9e, 0x1efe, + 0x1f59, 0x1f5f, + 0x2124, 0x2128, + 0x2c67, 0x2c6b, + 0x2c82, 0x2ce2, + 0x2ceb, 0x2ced, + 0xa640, 0xa65e, + 0xa662, 0xa66c, + 0xa680, 0xa696, + 0xa722, 0xa72e, + 0xa732, 0xa76e, + 0xa779, 0xa77b, + 0xa780, 0xa786, +}; + +static Rune __isuppers[] = { + 0x0184, + 0x01a9, + 0x01ac, + 0x01b5, + 0x01bc, + 0x01c4, + 0x01c7, + 0x01ca, + 0x01f1, + 0x01f4, + 0x0241, + 0x0376, + 0x0386, + 0x038c, + 0x03cf, + 0x03f4, + 0x03f7, + 0x2102, + 0x2107, + 0x2115, + 0x2145, + 0x2183, + 0x2c60, + 0x2c72, + 0x2c75, + 0xa78b, + 0x1d49c, + 0x1d4a2, + 0x1d546, + 0x1d7ca, +}; + +int +isupperrune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __isupperr, nelem(__isupperr)/2, 2); + if(p && c >= p[0] && c <= p[1]) + return 1; + p = rbsearch(c, __isupperp, nelem(__isupperp)/2, 2); + if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) + return 1; + p = rbsearch(c, __isuppers, nelem(__isuppers), 1); + if(p && c == p[0]) + return 1; + return 0; +} + +static Rune __islowerr[] = { + 0x0061, 0x007a, + 0x00df, 0x00f6, + 0x00f8, 0x00ff, + 0x0137, 0x0138, + 0x0148, 0x0149, + 0x017e, 0x0180, + 0x018c, 0x018d, + 0x0199, 0x019b, + 0x01aa, 0x01ab, + 0x01b9, 0x01ba, + 0x01bd, 0x01bf, + 0x01dc, 0x01dd, + 0x01ef, 0x01f0, + 0x0233, 0x0239, + 0x023f, 0x0240, + 0x024f, 0x0293, + 0x0295, 0x02af, + 0x037b, 0x037d, + 0x03ac, 0x03ce, + 0x03d0, 0x03d1, + 0x03d5, 0x03d7, + 0x03ef, 0x03f3, + 0x03fb, 0x03fc, + 0x0430, 0x045f, + 0x04ce, 0x04cf, + 0x0561, 0x0587, + 0x1d00, 0x1d2b, + 0x1d62, 0x1d77, + 0x1d79, 0x1d9a, + 0x1e95, 0x1e9d, + 0x1eff, 0x1f07, + 0x1f10, 0x1f15, + 0x1f20, 0x1f27, + 0x1f30, 0x1f37, + 0x1f40, 0x1f45, + 0x1f50, 0x1f57, + 0x1f60, 0x1f67, + 0x1f70, 0x1f7d, + 0x1f80, 0x1f87, + 0x1f90, 0x1f97, + 0x1fa0, 0x1fa7, + 0x1fb0, 0x1fb4, + 0x1fb6, 0x1fb7, + 0x1fc2, 0x1fc4, + 0x1fc6, 0x1fc7, + 0x1fd0, 0x1fd3, + 0x1fd6, 0x1fd7, + 0x1fe0, 0x1fe7, + 0x1ff2, 0x1ff4, + 0x1ff6, 0x1ff7, + 0x210e, 0x210f, + 0x213c, 0x213d, + 0x2146, 0x2149, + 0x2170, 0x217f, + 0x24d0, 0x24e9, + 0x2c30, 0x2c5e, + 0x2c65, 0x2c66, + 0x2c73, 0x2c74, + 0x2c76, 0x2c7c, + 0x2ce3, 0x2ce4, + 0x2d00, 0x2d25, + 0xa72f, 0xa731, + 0xa771, 0xa778, + 0xfb00, 0xfb06, + 0xfb13, 0xfb17, + 0xff41, 0xff5a, + 0x10428, 0x1044f, + 0x1d41a, 0x1d433, + 0x1d44e, 0x1d454, + 0x1d456, 0x1d467, + 0x1d482, 0x1d49b, + 0x1d4b6, 0x1d4b9, + 0x1d4bd, 0x1d4c3, + 0x1d4c5, 0x1d4cf, + 0x1d4ea, 0x1d503, + 0x1d51e, 0x1d537, + 0x1d552, 0x1d56b, + 0x1d586, 0x1d59f, + 0x1d5ba, 0x1d5d3, + 0x1d5ee, 0x1d607, + 0x1d622, 0x1d63b, + 0x1d656, 0x1d66f, + 0x1d68a, 0x1d6a5, + 0x1d6c2, 0x1d6da, + 0x1d6dc, 0x1d6e1, + 0x1d6fc, 0x1d714, + 0x1d716, 0x1d71b, + 0x1d736, 0x1d74e, + 0x1d750, 0x1d755, + 0x1d770, 0x1d788, + 0x1d78a, 0x1d78f, + 0x1d7aa, 0x1d7c2, + 0x1d7c4, 0x1d7c9, +}; + +static Rune __islowerp[] = { + 0x0101, 0x0135, + 0x013a, 0x0146, + 0x014b, 0x0177, + 0x017a, 0x017c, + 0x0183, 0x0185, + 0x01a1, 0x01a5, + 0x01b4, 0x01b6, + 0x01cc, 0x01da, + 0x01df, 0x01ed, + 0x01f3, 0x01f5, + 0x01f9, 0x0231, + 0x0247, 0x024d, + 0x0371, 0x0373, + 0x03d9, 0x03ed, + 0x0461, 0x0481, + 0x048b, 0x04bf, + 0x04c2, 0x04cc, + 0x04d1, 0x0525, + 0x1e01, 0x1e93, + 0x1e9f, 0x1efd, + 0x2c68, 0x2c6c, + 0x2c81, 0x2ce1, + 0x2cec, 0x2cee, + 0xa641, 0xa65f, + 0xa663, 0xa66d, + 0xa681, 0xa697, + 0xa723, 0xa72d, + 0xa733, 0xa76f, + 0xa77a, 0xa77c, + 0xa77f, 0xa787, +}; + +static Rune __islowers[] = { + 0x00aa, + 0x00b5, + 0x00ba, + 0x0188, + 0x0192, + 0x0195, + 0x019e, + 0x01a8, + 0x01ad, + 0x01b0, + 0x01c6, + 0x01c9, + 0x023c, + 0x0242, + 0x0377, + 0x0390, + 0x03f5, + 0x03f8, + 0x1fbe, + 0x210a, + 0x2113, + 0x212f, + 0x2134, + 0x2139, + 0x214e, + 0x2184, + 0x2c61, + 0x2c71, + 0xa78c, + 0x1d4bb, + 0x1d7cb, +}; + +int +islowerrune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __islowerr, nelem(__islowerr)/2, 2); + if(p && c >= p[0] && c <= p[1]) + return 1; + p = rbsearch(c, __islowerp, nelem(__islowerp)/2, 2); + if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) + return 1; + p = rbsearch(c, __islowers, nelem(__islowers), 1); + if(p && c == p[0]) + return 1; + return 0; +} + +static Rune __istitler[] = { + 0x0041, 0x005a, + 0x00c0, 0x00d6, + 0x00d8, 0x00de, + 0x0178, 0x0179, + 0x0181, 0x0182, + 0x0186, 0x0187, + 0x0189, 0x018b, + 0x018e, 0x0191, + 0x0193, 0x0194, + 0x0196, 0x0198, + 0x019c, 0x019d, + 0x019f, 0x01a0, + 0x01a6, 0x01a7, + 0x01ae, 0x01af, + 0x01b1, 0x01b3, + 0x01b7, 0x01b8, + 0x01f6, 0x01f8, + 0x023a, 0x023b, + 0x023d, 0x023e, + 0x0243, 0x0246, + 0x0388, 0x038a, + 0x038e, 0x038f, + 0x0391, 0x03a1, + 0x03a3, 0x03ab, + 0x03f9, 0x03fa, + 0x03fd, 0x042f, + 0x04c0, 0x04c1, + 0x0531, 0x0556, + 0x10a0, 0x10c5, + 0x1f08, 0x1f0f, + 0x1f18, 0x1f1d, + 0x1f28, 0x1f2f, + 0x1f38, 0x1f3f, + 0x1f48, 0x1f4d, + 0x1f68, 0x1f6f, + 0x1f88, 0x1f8f, + 0x1f98, 0x1f9f, + 0x1fa8, 0x1faf, + 0x1fb8, 0x1fbc, + 0x1fc8, 0x1fcc, + 0x1fd8, 0x1fdb, + 0x1fe8, 0x1fec, + 0x1ff8, 0x1ffc, + 0x2160, 0x216f, + 0x24b6, 0x24cf, + 0x2c00, 0x2c2e, + 0x2c62, 0x2c64, + 0x2c6d, 0x2c70, + 0x2c7e, 0x2c80, + 0xa77d, 0xa77e, + 0xff21, 0xff3a, + 0x10400, 0x10427, +}; + +static Rune __istitlep[] = { + 0x0100, 0x012e, + 0x0132, 0x0136, + 0x0139, 0x0147, + 0x014a, 0x0176, + 0x017b, 0x017d, + 0x01a2, 0x01a4, + 0x01cb, 0x01db, + 0x01de, 0x01ee, + 0x01f2, 0x01f4, + 0x01fa, 0x0232, + 0x0248, 0x024e, + 0x0370, 0x0372, + 0x03d8, 0x03ee, + 0x0460, 0x0480, + 0x048a, 0x04be, + 0x04c3, 0x04cd, + 0x04d0, 0x0524, + 0x1e00, 0x1e94, + 0x1ea0, 0x1efe, + 0x1f59, 0x1f5f, + 0x2c67, 0x2c6b, + 0x2c82, 0x2ce2, + 0x2ceb, 0x2ced, + 0xa640, 0xa65e, + 0xa662, 0xa66c, + 0xa680, 0xa696, + 0xa722, 0xa72e, + 0xa732, 0xa76e, + 0xa779, 0xa77b, + 0xa780, 0xa786, +}; + +static Rune __istitles[] = { + 0x0184, + 0x01a9, + 0x01ac, + 0x01b5, + 0x01bc, + 0x01c5, + 0x01c8, + 0x0241, + 0x0376, + 0x0386, + 0x038c, + 0x03cf, + 0x03f7, + 0x2132, + 0x2183, + 0x2c60, + 0x2c72, + 0x2c75, + 0xa78b, +}; + +int +istitlerune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __istitler, nelem(__istitler)/2, 2); + if(p && c >= p[0] && c <= p[1]) + return 1; + p = rbsearch(c, __istitlep, nelem(__istitlep)/2, 2); + if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) + return 1; + p = rbsearch(c, __istitles, nelem(__istitles), 1); + if(p && c == p[0]) + return 1; + return 0; +} + +static Rune __toupperr[] = { + 0x0061, 0x007a, 1048544, + 0x00e0, 0x00f6, 1048544, + 0x00f8, 0x00fe, 1048544, + 0x023f, 0x0240, 1059391, + 0x0256, 0x0257, 1048371, + 0x028a, 0x028b, 1048359, + 0x037b, 0x037d, 1048706, + 0x03ad, 0x03af, 1048539, + 0x03b1, 0x03c1, 1048544, + 0x03c3, 0x03cb, 1048544, + 0x03cd, 0x03ce, 1048513, + 0x0430, 0x044f, 1048544, + 0x0450, 0x045f, 1048496, + 0x0561, 0x0586, 1048528, + 0x1f00, 0x1f07, 1048584, + 0x1f10, 0x1f15, 1048584, + 0x1f20, 0x1f27, 1048584, + 0x1f30, 0x1f37, 1048584, + 0x1f40, 0x1f45, 1048584, + 0x1f60, 0x1f67, 1048584, + 0x1f70, 0x1f71, 1048650, + 0x1f72, 0x1f75, 1048662, + 0x1f76, 0x1f77, 1048676, + 0x1f78, 0x1f79, 1048704, + 0x1f7a, 0x1f7b, 1048688, + 0x1f7c, 0x1f7d, 1048702, + 0x1f80, 0x1f87, 1048584, + 0x1f90, 0x1f97, 1048584, + 0x1fa0, 0x1fa7, 1048584, + 0x1fb0, 0x1fb1, 1048584, + 0x1fd0, 0x1fd1, 1048584, + 0x1fe0, 0x1fe1, 1048584, + 0x2170, 0x217f, 1048560, + 0x24d0, 0x24e9, 1048550, + 0x2c30, 0x2c5e, 1048528, + 0x2d00, 0x2d25, 1041312, + 0xff41, 0xff5a, 1048544, + 0x10428, 0x1044f, 1048536, +}; + +static Rune __toupperp[] = { + 0x0101, 0x012f, 1048575, + 0x0133, 0x0137, 1048575, + 0x013a, 0x0148, 1048575, + 0x014b, 0x0177, 1048575, + 0x017a, 0x017e, 1048575, + 0x0183, 0x0185, 1048575, + 0x01a1, 0x01a5, 1048575, + 0x01b4, 0x01b6, 1048575, + 0x01ce, 0x01dc, 1048575, + 0x01df, 0x01ef, 1048575, + 0x01f9, 0x021f, 1048575, + 0x0223, 0x0233, 1048575, + 0x0247, 0x024f, 1048575, + 0x0371, 0x0373, 1048575, + 0x03d9, 0x03ef, 1048575, + 0x0461, 0x0481, 1048575, + 0x048b, 0x04bf, 1048575, + 0x04c2, 0x04ce, 1048575, + 0x04d1, 0x0525, 1048575, + 0x1e01, 0x1e95, 1048575, + 0x1ea1, 0x1eff, 1048575, + 0x1f51, 0x1f57, 1048584, + 0x2c68, 0x2c6c, 1048575, + 0x2c81, 0x2ce3, 1048575, + 0x2cec, 0x2cee, 1048575, + 0xa641, 0xa65f, 1048575, + 0xa663, 0xa66d, 1048575, + 0xa681, 0xa697, 1048575, + 0xa723, 0xa72f, 1048575, + 0xa733, 0xa76f, 1048575, + 0xa77a, 0xa77c, 1048575, + 0xa77f, 0xa787, 1048575, +}; + +static Rune __touppers[] = { + 0x00b5, 1049319, + 0x00ff, 1048697, + 0x0131, 1048344, + 0x017f, 1048276, + 0x0180, 1048771, + 0x0188, 1048575, + 0x018c, 1048575, + 0x0192, 1048575, + 0x0195, 1048673, + 0x0199, 1048575, + 0x019a, 1048739, + 0x019e, 1048706, + 0x01a8, 1048575, + 0x01ad, 1048575, + 0x01b0, 1048575, + 0x01b9, 1048575, + 0x01bd, 1048575, + 0x01bf, 1048632, + 0x01c5, 1048575, + 0x01c6, 1048574, + 0x01c8, 1048575, + 0x01c9, 1048574, + 0x01cb, 1048575, + 0x01cc, 1048574, + 0x01dd, 1048497, + 0x01f2, 1048575, + 0x01f3, 1048574, + 0x01f5, 1048575, + 0x023c, 1048575, + 0x0242, 1048575, + 0x0250, 1059359, + 0x0251, 1059356, + 0x0252, 1059358, + 0x0253, 1048366, + 0x0254, 1048370, + 0x0259, 1048374, + 0x025b, 1048373, + 0x0260, 1048371, + 0x0263, 1048369, + 0x0268, 1048367, + 0x0269, 1048365, + 0x026b, 1059319, + 0x026f, 1048365, + 0x0271, 1059325, + 0x0272, 1048363, + 0x0275, 1048362, + 0x027d, 1059303, + 0x0280, 1048358, + 0x0283, 1048358, + 0x0288, 1048358, + 0x0289, 1048507, + 0x028c, 1048505, + 0x0292, 1048357, + 0x0345, 1048660, + 0x0377, 1048575, + 0x03ac, 1048538, + 0x03c2, 1048545, + 0x03cc, 1048512, + 0x03d0, 1048514, + 0x03d1, 1048519, + 0x03d5, 1048529, + 0x03d6, 1048522, + 0x03d7, 1048568, + 0x03f0, 1048490, + 0x03f1, 1048496, + 0x03f2, 1048583, + 0x03f5, 1048480, + 0x03f8, 1048575, + 0x03fb, 1048575, + 0x04cf, 1048561, + 0x1d79, 1083908, + 0x1d7d, 1052390, + 0x1e9b, 1048517, + 0x1fb3, 1048585, + 0x1fbe, 1041371, + 0x1fc3, 1048585, + 0x1fe5, 1048583, + 0x1ff3, 1048585, + 0x214e, 1048548, + 0x2184, 1048575, + 0x2c61, 1048575, + 0x2c65, 1037781, + 0x2c66, 1037784, + 0x2c73, 1048575, + 0x2c76, 1048575, + 0xa78c, 1048575, +}; + +Rune +toupperrune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __toupperr, nelem(__toupperr)/3, 3); + if(p && c >= p[0] && c <= p[1]) + return c + p[2] - 1048576; + p = rbsearch(c, __toupperp, nelem(__toupperp)/3, 3); + if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) + return c + p[2] - 1048576; + p = rbsearch(c, __touppers, nelem(__touppers)/2, 2); + if(p && c == p[0]) + return c + p[1] - 1048576; + return c; +} + +static Rune __tolowerr[] = { + 0x0041, 0x005a, 1048608, + 0x00c0, 0x00d6, 1048608, + 0x00d8, 0x00de, 1048608, + 0x0189, 0x018a, 1048781, + 0x01b1, 0x01b2, 1048793, + 0x0388, 0x038a, 1048613, + 0x038e, 0x038f, 1048639, + 0x0391, 0x03a1, 1048608, + 0x03a3, 0x03ab, 1048608, + 0x03fd, 0x03ff, 1048446, + 0x0400, 0x040f, 1048656, + 0x0410, 0x042f, 1048608, + 0x0531, 0x0556, 1048624, + 0x10a0, 0x10c5, 1055840, + 0x1f08, 0x1f0f, 1048568, + 0x1f18, 0x1f1d, 1048568, + 0x1f28, 0x1f2f, 1048568, + 0x1f38, 0x1f3f, 1048568, + 0x1f48, 0x1f4d, 1048568, + 0x1f68, 0x1f6f, 1048568, + 0x1f88, 0x1f8f, 1048568, + 0x1f98, 0x1f9f, 1048568, + 0x1fa8, 0x1faf, 1048568, + 0x1fb8, 0x1fb9, 1048568, + 0x1fba, 0x1fbb, 1048502, + 0x1fc8, 0x1fcb, 1048490, + 0x1fd8, 0x1fd9, 1048568, + 0x1fda, 0x1fdb, 1048476, + 0x1fe8, 0x1fe9, 1048568, + 0x1fea, 0x1feb, 1048464, + 0x1ff8, 0x1ff9, 1048448, + 0x1ffa, 0x1ffb, 1048450, + 0x2160, 0x216f, 1048592, + 0x24b6, 0x24cf, 1048602, + 0x2c00, 0x2c2e, 1048624, + 0x2c7e, 0x2c7f, 1037761, + 0xff21, 0xff3a, 1048608, + 0x10400, 0x10427, 1048616, +}; + +static Rune __tolowerp[] = { + 0x0100, 0x012e, 1048577, + 0x0132, 0x0136, 1048577, + 0x0139, 0x0147, 1048577, + 0x014a, 0x0176, 1048577, + 0x017b, 0x017d, 1048577, + 0x01a2, 0x01a4, 1048577, + 0x01b3, 0x01b5, 1048577, + 0x01cd, 0x01db, 1048577, + 0x01de, 0x01ee, 1048577, + 0x01f8, 0x021e, 1048577, + 0x0222, 0x0232, 1048577, + 0x0248, 0x024e, 1048577, + 0x0370, 0x0372, 1048577, + 0x03d8, 0x03ee, 1048577, + 0x0460, 0x0480, 1048577, + 0x048a, 0x04be, 1048577, + 0x04c3, 0x04cd, 1048577, + 0x04d0, 0x0524, 1048577, + 0x1e00, 0x1e94, 1048577, + 0x1ea0, 0x1efe, 1048577, + 0x1f59, 0x1f5f, 1048568, + 0x2c67, 0x2c6b, 1048577, + 0x2c80, 0x2ce2, 1048577, + 0x2ceb, 0x2ced, 1048577, + 0xa640, 0xa65e, 1048577, + 0xa662, 0xa66c, 1048577, + 0xa680, 0xa696, 1048577, + 0xa722, 0xa72e, 1048577, + 0xa732, 0xa76e, 1048577, + 0xa779, 0xa77b, 1048577, + 0xa780, 0xa786, 1048577, +}; + +static Rune __tolowers[] = { + 0x0130, 1048377, + 0x0178, 1048455, + 0x0179, 1048577, + 0x0181, 1048786, + 0x0182, 1048577, + 0x0184, 1048577, + 0x0186, 1048782, + 0x0187, 1048577, + 0x018b, 1048577, + 0x018e, 1048655, + 0x018f, 1048778, + 0x0190, 1048779, + 0x0191, 1048577, + 0x0193, 1048781, + 0x0194, 1048783, + 0x0196, 1048787, + 0x0197, 1048785, + 0x0198, 1048577, + 0x019c, 1048787, + 0x019d, 1048789, + 0x019f, 1048790, + 0x01a0, 1048577, + 0x01a6, 1048794, + 0x01a7, 1048577, + 0x01a9, 1048794, + 0x01ac, 1048577, + 0x01ae, 1048794, + 0x01af, 1048577, + 0x01b7, 1048795, + 0x01b8, 1048577, + 0x01bc, 1048577, + 0x01c4, 1048578, + 0x01c5, 1048577, + 0x01c7, 1048578, + 0x01c8, 1048577, + 0x01ca, 1048578, + 0x01cb, 1048577, + 0x01f1, 1048578, + 0x01f2, 1048577, + 0x01f4, 1048577, + 0x01f6, 1048479, + 0x01f7, 1048520, + 0x0220, 1048446, + 0x023a, 1059371, + 0x023b, 1048577, + 0x023d, 1048413, + 0x023e, 1059368, + 0x0241, 1048577, + 0x0243, 1048381, + 0x0244, 1048645, + 0x0245, 1048647, + 0x0246, 1048577, + 0x0376, 1048577, + 0x0386, 1048614, + 0x038c, 1048640, + 0x03cf, 1048584, + 0x03f4, 1048516, + 0x03f7, 1048577, + 0x03f9, 1048569, + 0x03fa, 1048577, + 0x04c0, 1048591, + 0x04c1, 1048577, + 0x1e9e, 1040961, + 0x1fbc, 1048567, + 0x1fcc, 1048567, + 0x1fec, 1048569, + 0x1ffc, 1048567, + 0x2126, 1041059, + 0x212a, 1040193, + 0x212b, 1040314, + 0x2132, 1048604, + 0x2183, 1048577, + 0x2c60, 1048577, + 0x2c62, 1037833, + 0x2c63, 1044762, + 0x2c64, 1037849, + 0x2c6d, 1037796, + 0x2c6e, 1037827, + 0x2c6f, 1037793, + 0x2c70, 1037794, + 0x2c72, 1048577, + 0x2c75, 1048577, + 0xa77d, 1013244, + 0xa77e, 1048577, + 0xa78b, 1048577, +}; + +Rune +tolowerrune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __tolowerr, nelem(__tolowerr)/3, 3); + if(p && c >= p[0] && c <= p[1]) + return c + p[2] - 1048576; + p = rbsearch(c, __tolowerp, nelem(__tolowerp)/3, 3); + if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) + return c + p[2] - 1048576; + p = rbsearch(c, __tolowers, nelem(__tolowers)/2, 2); + if(p && c == p[0]) + return c + p[1] - 1048576; + return c; +} + +static Rune __totitler[] = { + 0x0061, 0x007a, 1048544, + 0x00e0, 0x00f6, 1048544, + 0x00f8, 0x00fe, 1048544, + 0x023f, 0x0240, 1059391, + 0x0256, 0x0257, 1048371, + 0x028a, 0x028b, 1048359, + 0x037b, 0x037d, 1048706, + 0x03ad, 0x03af, 1048539, + 0x03b1, 0x03c1, 1048544, + 0x03c3, 0x03cb, 1048544, + 0x03cd, 0x03ce, 1048513, + 0x0430, 0x044f, 1048544, + 0x0450, 0x045f, 1048496, + 0x0561, 0x0586, 1048528, + 0x1f00, 0x1f07, 1048584, + 0x1f10, 0x1f15, 1048584, + 0x1f20, 0x1f27, 1048584, + 0x1f30, 0x1f37, 1048584, + 0x1f40, 0x1f45, 1048584, + 0x1f60, 0x1f67, 1048584, + 0x1f70, 0x1f71, 1048650, + 0x1f72, 0x1f75, 1048662, + 0x1f76, 0x1f77, 1048676, + 0x1f78, 0x1f79, 1048704, + 0x1f7a, 0x1f7b, 1048688, + 0x1f7c, 0x1f7d, 1048702, + 0x1f80, 0x1f87, 1048584, + 0x1f90, 0x1f97, 1048584, + 0x1fa0, 0x1fa7, 1048584, + 0x1fb0, 0x1fb1, 1048584, + 0x1fd0, 0x1fd1, 1048584, + 0x1fe0, 0x1fe1, 1048584, + 0x2170, 0x217f, 1048560, + 0x24d0, 0x24e9, 1048550, + 0x2c30, 0x2c5e, 1048528, + 0x2d00, 0x2d25, 1041312, + 0xff41, 0xff5a, 1048544, + 0x10428, 0x1044f, 1048536, +}; + +static Rune __totitlep[] = { + 0x0101, 0x012f, 1048575, + 0x0133, 0x0137, 1048575, + 0x013a, 0x0148, 1048575, + 0x014b, 0x0177, 1048575, + 0x017a, 0x017e, 1048575, + 0x0183, 0x0185, 1048575, + 0x01a1, 0x01a5, 1048575, + 0x01b4, 0x01b6, 1048575, + 0x01cc, 0x01dc, 1048575, + 0x01df, 0x01ef, 1048575, + 0x01f3, 0x01f5, 1048575, + 0x01f9, 0x021f, 1048575, + 0x0223, 0x0233, 1048575, + 0x0247, 0x024f, 1048575, + 0x0371, 0x0373, 1048575, + 0x03d9, 0x03ef, 1048575, + 0x0461, 0x0481, 1048575, + 0x048b, 0x04bf, 1048575, + 0x04c2, 0x04ce, 1048575, + 0x04d1, 0x0525, 1048575, + 0x1e01, 0x1e95, 1048575, + 0x1ea1, 0x1eff, 1048575, + 0x1f51, 0x1f57, 1048584, + 0x2c68, 0x2c6c, 1048575, + 0x2c81, 0x2ce3, 1048575, + 0x2cec, 0x2cee, 1048575, + 0xa641, 0xa65f, 1048575, + 0xa663, 0xa66d, 1048575, + 0xa681, 0xa697, 1048575, + 0xa723, 0xa72f, 1048575, + 0xa733, 0xa76f, 1048575, + 0xa77a, 0xa77c, 1048575, + 0xa77f, 0xa787, 1048575, +}; + +static Rune __totitles[] = { + 0x00b5, 1049319, + 0x00ff, 1048697, + 0x0131, 1048344, + 0x017f, 1048276, + 0x0180, 1048771, + 0x0188, 1048575, + 0x018c, 1048575, + 0x0192, 1048575, + 0x0195, 1048673, + 0x0199, 1048575, + 0x019a, 1048739, + 0x019e, 1048706, + 0x01a8, 1048575, + 0x01ad, 1048575, + 0x01b0, 1048575, + 0x01b9, 1048575, + 0x01bd, 1048575, + 0x01bf, 1048632, + 0x01c4, 1048577, + 0x01c6, 1048575, + 0x01c7, 1048577, + 0x01c9, 1048575, + 0x01ca, 1048577, + 0x01dd, 1048497, + 0x01f1, 1048577, + 0x023c, 1048575, + 0x0242, 1048575, + 0x0250, 1059359, + 0x0251, 1059356, + 0x0252, 1059358, + 0x0253, 1048366, + 0x0254, 1048370, + 0x0259, 1048374, + 0x025b, 1048373, + 0x0260, 1048371, + 0x0263, 1048369, + 0x0268, 1048367, + 0x0269, 1048365, + 0x026b, 1059319, + 0x026f, 1048365, + 0x0271, 1059325, + 0x0272, 1048363, + 0x0275, 1048362, + 0x027d, 1059303, + 0x0280, 1048358, + 0x0283, 1048358, + 0x0288, 1048358, + 0x0289, 1048507, + 0x028c, 1048505, + 0x0292, 1048357, + 0x0345, 1048660, + 0x0377, 1048575, + 0x03ac, 1048538, + 0x03c2, 1048545, + 0x03cc, 1048512, + 0x03d0, 1048514, + 0x03d1, 1048519, + 0x03d5, 1048529, + 0x03d6, 1048522, + 0x03d7, 1048568, + 0x03f0, 1048490, + 0x03f1, 1048496, + 0x03f2, 1048583, + 0x03f5, 1048480, + 0x03f8, 1048575, + 0x03fb, 1048575, + 0x04cf, 1048561, + 0x1d79, 1083908, + 0x1d7d, 1052390, + 0x1e9b, 1048517, + 0x1fb3, 1048585, + 0x1fbe, 1041371, + 0x1fc3, 1048585, + 0x1fe5, 1048583, + 0x1ff3, 1048585, + 0x214e, 1048548, + 0x2184, 1048575, + 0x2c61, 1048575, + 0x2c65, 1037781, + 0x2c66, 1037784, + 0x2c73, 1048575, + 0x2c76, 1048575, + 0xa78c, 1048575, +}; + +Rune +totitlerune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __totitler, nelem(__totitler)/3, 3); + if(p && c >= p[0] && c <= p[1]) + return c + p[2] - 1048576; + p = rbsearch(c, __totitlep, nelem(__totitlep)/3, 3); + if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) + return c + p[2] - 1048576; + p = rbsearch(c, __totitles, nelem(__totitles)/2, 2); + if(p && c == p[0]) + return c + p[1] - 1048576; + return c; +} + diff --git a/src/libbio/Makefile b/src/libbio/Makefile index 32fdedd91..4340b0eae 100644 --- a/src/libbio/Makefile +++ b/src/libbio/Makefile @@ -22,7 +22,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -include ../Make.conf +include ../Make.inc +O:=$(HOST_O) LIB=libbio.a @@ -47,19 +48,4 @@ OFILES=\ HFILES=\ ../../include/bio.h -install: $(LIB) - cp $(LIB) ../../lib - -$(LIB): $(OFILES) - ar rsc $(LIB) $(OFILES) - -$(OFILES): $(HFILES) - -y.tab.c: $(YFILES) - yacc $(YFLAGS) $(YFILES) - -clean: - rm -f $(OFILES) *.6 6.out $(LIB) - -nuke: clean - rm -f ../../lib/$(LIB) +include ../Make.clib diff --git a/src/libbio/bprint.c b/src/libbio/bprint.c index 2e3867ae6..b5d3e9ece 100644 --- a/src/libbio/bprint.c +++ b/src/libbio/bprint.c @@ -3,6 +3,7 @@ http://code.google.com/p/inferno-os/source/browse/libbio/bprint.c Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved. + Revisions Copyright © 2010 Google Inc. 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 @@ -30,25 +31,52 @@ THE SOFTWARE. int Bprint(Biobuf *bp, char *fmt, ...) { - va_list ap; - char *ip, *ep, *out; - int n; - - ep = (char*)bp->ebuf; - ip = ep + bp->ocount; - va_start(ap, fmt); - out = vseprint(ip, ep, fmt, ap); - va_end(ap); - if(out == nil || out >= ep-5) { - Bflush(bp); - ip = ep + bp->ocount; - va_start(ap, fmt); - out = vseprint(ip, ep, fmt, ap); - va_end(ap); - if(out >= ep-5) - return Beof; - } - n = out-ip; - bp->ocount += n; - return n; + int n; + va_list arg; + + va_start(arg, fmt); + n = Bvprint(bp, fmt, arg); + va_end(arg); + return n; +} + +static int +bflush(Fmt *f) +{ + Biobuf *bp; + + if(f->stop == nil) + return 0; + + bp = f->farg; + bp->ocount = (char*)f->to - (char*)f->stop; + if(Bflush(bp) < 0) { + f->stop = nil; + f->to = nil; + return 0; + } + f->to = (char*)f->stop + bp->ocount; + + return 1; +} + +int +Bvprint(Biobuf *bp, char *fmt, va_list arg) +{ + int n; + Fmt f; + + memset(&f, 0, sizeof f); + fmtlocaleinit(&f, nil, nil, nil); + f.stop = bp->ebuf; + f.to = (char*)f.stop + bp->ocount; + f.flush = bflush; + f.farg = bp; + + n = fmtvprint(&f, fmt, arg); + + if(f.stop != nil) + bp->ocount = (char*)f.to - (char*)f.stop; + + return n; } diff --git a/src/libbio/bseek.c b/src/libbio/bseek.c index be00ab1a7..291498108 100644 --- a/src/libbio/bseek.c +++ b/src/libbio/bseek.c @@ -33,7 +33,7 @@ Bseek(Biobuf *bp, vlong offset, int base) vlong n, d; int bufsz; -#ifndef __MINGW32__ +#ifndef _WIN32 if(sizeof(offset) != sizeof(off_t)) { fprint(2, "Bseek: libbio compiled with %d-byte offset\n", sizeof(off_t)); abort(); diff --git a/src/libcgo/386.S b/src/libcgo/386.S deleted file mode 100755 index cca79cdd5..000000000 --- a/src/libcgo/386.S +++ /dev/null @@ -1,61 +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. - -/* - * Apple still insists on underscore prefixes for C function names. - */ -#if defined(__APPLE__) || defined(__MINGW32__) -#define EXT(s) _##s -#else -#define EXT(s) s -#endif - -/* - * void crosscall_386(void (*fn)(void)) - * - * Calling into the 8c tool chain, where all registers are caller save. - * Called from standard x86 ABI, where %ebp, %ebx, %esi, - * and %edi are callee-save, so they must be saved explicitly. - */ -.globl EXT(crosscall_386) -EXT(crosscall_386): - pushl %ebp - movl %esp, %ebp - pushl %ebx - pushl %esi - pushl %edi - - movl 8(%ebp), %eax /* fn */ - call *%eax - - popl %edi - popl %esi - popl %ebx - popl %ebp - ret - -/* - * void crosscall2(void (*fn)(void*, int32), void*, int32) - * - * Save registers and call fn with two arguments. - */ -.globl EXT(crosscall2) -EXT(crosscall2): - pushl %ebp - movl %esp, %ebp - pushl %ebx - pushl %esi - pushl %edi - - pushl 16(%ebp) - pushl 12(%ebp) - mov 8(%ebp), %eax - call *%eax - addl $8,%esp - - popl %edi - popl %esi - popl %ebx - popl %ebp - ret diff --git a/src/libcgo/Makefile b/src/libcgo/Makefile deleted file mode 100755 index 0d65af70c..000000000 --- a/src/libcgo/Makefile +++ /dev/null @@ -1,42 +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. - -# ugly hack to deal with whitespaces in $GOROOT -nullstring := -space := $(nullstring) # a space at the end -QUOTED_GOROOT=$(subst $(space),\ ,$(GOROOT)) - -all: libcgo.so - -install: $(QUOTED_GOROOT)/pkg/$(GOOS)_$(GOARCH)/libcgo.so - -OFILES=\ - $(GOOS)_$(GOARCH).o\ - $(GOARCH).o\ - util.o\ - -CFLAGS_386=-m32 -CFLAGS_amd64=-m64 - - -LDFLAGS_linux=-shared -lpthread -lm -LDFLAGS_darwin=-dynamiclib -Wl,-undefined,dynamic_lookup /usr/lib/libpthread.dylib -LDFLAGS_freebsd=-pthread -shared -lm -LDFLAGS_windows=-shared -lm -mthreads - -%.o: %.c - gcc $(CFLAGS_$(GOARCH)) -O2 -fPIC -o $@ -c $*.c - -%.o: %.S - gcc $(CFLAGS_$(GOARCH)) -O2 -fPIC -o $@ -c $*.S - -libcgo.so: $(OFILES) - gcc $(CFLAGS_$(GOARCH)) -o libcgo.so $(OFILES) $(LDFLAGS_$(GOOS)) - -$(QUOTED_GOROOT)/pkg/$(GOOS)_$(GOARCH)/libcgo.so: libcgo.so - cp libcgo.so $(QUOTED_GOROOT)/pkg/$(GOOS)_$(GOARCH) - -clean: - rm -f *.o *.so - diff --git a/src/libcgo/amd64.S b/src/libcgo/amd64.S deleted file mode 100644 index 92ded0ac2..000000000 --- a/src/libcgo/amd64.S +++ /dev/null @@ -1,81 +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. - -/* - * Apple still insists on underscore prefixes for C function names. - */ -#if defined(__APPLE__) || defined(__MINGW64__) -#define EXT(s) _##s -#else -#define EXT(s) s -#endif - -/* - * void crosscall_amd64(M *m, G *g, void (*fn)(void)) - * - * Calling into the 6c tool chain, where all registers are caller save. - * Called from standard x86-64 ABI, where %rbx, %rbp, %r12-%r15 - * are callee-save so they must be saved explicitly. - * The standard x86-64 ABI passes the three arguments m, g, fn - * in %rdi, %rsi, %rdx. - * - * Also need to set %r15 to g and %r14 to m (see ../pkg/runtime/mkasmh.sh) - * during the call. - */ -.globl EXT(crosscall_amd64) -EXT(crosscall_amd64): - pushq %rbx - pushq %rbp - pushq %r12 - pushq %r13 - pushq %r14 - pushq %r15 - - movq %rdi, %r14 /* m */ - movq %rsi, %r15 /* g */ - call *%rdx /* fn */ - - popq %r15 - popq %r14 - popq %r13 - popq %r12 - popq %rbp - popq %rbx - ret - -/* - * void crosscall2(void (*fn)(void*, int32), void *arg, int32 argsize) - * - * Save registers and call fn with two arguments. fn is a Go function - * which takes parameters on the stack rather than in registers. - */ -.globl EXT(crosscall2) -EXT(crosscall2): - subq $0x58, %rsp /* keeps stack pointer 32-byte aligned */ - movq %rbx, 0x10(%rsp) - movq %rbp, 0x18(%rsp) - movq %r12, 0x20(%rsp) - movq %r13, 0x28(%rsp) - movq %r14, 0x30(%rsp) - movq %r15, 0x38(%rsp) - - movq %rdi, %r12 /* fn */ - movq %rsi, 0(%rsp) /* arg */ - movq %rdx, 8(%rsp) /* argsize (includes padding) */ - - leaq 0x40(%rsp), %rdi - call EXT(libcgo_get_scheduler) - movq 0x40(%rsp), %r14 /* m */ - movq 0x48(%rsp), %r15 /* g */ - - call *%r12 - - movq 0x10(%rsp), %rbx - movq 0x18(%rsp), %rbp - movq 0x20(%rsp), %r12 - movq 0x28(%rsp), %r13 - movq 0x30(%rsp), %r14 - movq 0x38(%rsp), %r15 - addq $0x58, %rsp - ret diff --git a/src/libcgo/darwin_386.c b/src/libcgo/darwin_386.c deleted file mode 100644 index 28a428309..000000000 --- a/src/libcgo/darwin_386.c +++ /dev/null @@ -1,144 +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 -#include "libcgo.h" - -static void* threadentry(void*); -static pthread_key_t k1, k2; - -/* gccism: arrange for inittls to be called at dynamic load time */ -static void inittls(void) __attribute__((constructor)); - -static void -inittls(void) -{ - uint32 x, y; - pthread_key_t tofree[16], k; - int i, ntofree; - int havek1, havek2; - - /* - * Allocate thread-local storage slots for m, g. - * The key numbers start at 0x100, and we expect to be - * one of the early calls to pthread_key_create, so we - * should be able to get pretty low numbers. - * - * In Darwin/386 pthreads, %gs points at the thread - * structure, and each key is an index into the thread-local - * storage array that begins at offset 0x48 within in that structure. - * It may happen that we are not quite the first function to try - * to allocate thread-local storage keys, so instead of depending - * on getting 0x100 and 0x101, we try for 0x108 and 0x109, - * allocating keys until we get the ones we want and then freeing - * the ones we didn't want. - * - * Thus the final offsets to use in %gs references are - * 0x48+4*0x108 = 0x468 and 0x48+4*0x109 = 0x46c. - * - * The linker and runtime hard-code these constant offsets - * from %gs where we expect to find m and g. The code - * below verifies that the constants are correct once it has - * obtained the keys. Known to ../cmd/8l/obj.c:/468 - * and to ../pkg/runtime/darwin/386/sys.s:/468 - * - * This is truly disgusting and a bit fragile, but taking care - * of it here protects the rest of the system from damage. - * The alternative would be to use a global variable that - * held the offset and refer to that variable each time we - * need a %gs variable (m or g). That approach would - * require an extra instruction and memory reference in - * every stack growth prolog and would also require - * rewriting the code that 8c generates for extern registers. - */ - havek1 = 0; - havek2 = 0; - ntofree = 0; - while(!havek1 || !havek2) { - if(pthread_key_create(&k, nil) < 0) { - fprintf(stderr, "libcgo: pthread_key_create failed\n"); - abort(); - } - if(k == 0x108) { - havek1 = 1; - k1 = k; - continue; - } - if(k == 0x109) { - havek2 = 1; - k2 = k; - continue; - } - if(ntofree >= nelem(tofree)) { - fprintf(stderr, "libcgo: could not obtain pthread_keys\n"); - fprintf(stderr, "\twanted 0x108 and 0x109\n"); - fprintf(stderr, "\tgot"); - for(i=0; ig->stackguard = size; - pthread_create(&p, &attr, threadentry, ts); -} - -static void* -threadentry(void *v) -{ - ThreadStart ts; - - ts = *(ThreadStart*)v; - free(v); - - ts.g->stackbase = (uintptr)&ts; - - /* - * libcgo_sys_thread_start set stackguard to stack size; - * change to actual guard pointer. - */ - ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; - - pthread_setspecific(k1, (void*)ts.g); - pthread_setspecific(k2, (void*)ts.m); - - crosscall_386(ts.fn); - return nil; -} diff --git a/src/libcgo/darwin_amd64.c b/src/libcgo/darwin_amd64.c deleted file mode 100644 index 2e0e12411..000000000 --- a/src/libcgo/darwin_amd64.c +++ /dev/null @@ -1,78 +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 -#include "libcgo.h" - -static void* threadentry(void*); - -static pthread_key_t km, kg; - -void -initcgo(void) -{ - if(pthread_key_create(&km, nil) < 0) { - fprintf(stderr, "libcgo: pthread_key_create failed\n"); - abort(); - } - if(pthread_key_create(&kg, nil) < 0) { - fprintf(stderr, "libcgo: pthread_key_create failed\n"); - abort(); - } -} - -void -libcgo_sys_thread_start(ThreadStart *ts) -{ - pthread_attr_t attr; - pthread_t p; - size_t size; - - pthread_attr_init(&attr); - pthread_attr_getstacksize(&attr, &size); - ts->g->stackguard = size; - pthread_create(&p, &attr, threadentry, ts); -} - -static void* -threadentry(void *v) -{ - ThreadStart ts; - - ts = *(ThreadStart*)v; - free(v); - - ts.g->stackbase = (uintptr)&ts; - - /* - * libcgo_sys_thread_start set stackguard to stack size; - * change to actual guard pointer. - */ - ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; - - crosscall_amd64(ts.m, ts.g, ts.fn); - return nil; -} - -void -libcgo_set_scheduler(void *m, void *g) -{ - pthread_setspecific(km, m); - pthread_setspecific(kg, g); -} - -struct get_scheduler_args { - void *m; - void *g; -}; - -void libcgo_get_scheduler(struct get_scheduler_args *) - __attribute__ ((visibility("hidden"))); - -void -libcgo_get_scheduler(struct get_scheduler_args *p) -{ - p->m = pthread_getspecific(km); - p->g = pthread_getspecific(kg); -} diff --git a/src/libcgo/freebsd_386.c b/src/libcgo/freebsd_386.c deleted file mode 100644 index b445b940a..000000000 --- a/src/libcgo/freebsd_386.c +++ /dev/null @@ -1,65 +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 -#include "libcgo.h" - -static void* threadentry(void*); - -char *environ[] = { 0 }; -char *__progname; - -static void -inittls(void) -{ -} - -void -initcgo(void) -{ -} - -void -libcgo_sys_thread_start(ThreadStart *ts) -{ - pthread_attr_t attr; - pthread_t p; - size_t size; - - pthread_attr_init(&attr); - pthread_attr_getstacksize(&attr, &size); - ts->g->stackguard = size; - pthread_create(&p, &attr, threadentry, ts); -} - -static void* -threadentry(void *v) -{ - ThreadStart ts; - - ts = *(ThreadStart*)v; - free(v); - - ts.g->stackbase = (uintptr)&ts; - - /* - * libcgo_sys_thread_start set stackguard to stack size; - * change to actual guard pointer. - */ - ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; - - /* - * Set specific keys. On FreeBSD/ELF, the thread local storage - * is just before %gs:0. Our dynamic 8.out's reserve 8 bytes - * for the two words g and m at %gs:-8 and %gs:-4. - */ - asm volatile ( - "movl %0, %%gs:-8\n" // MOVL g, -8(GS) - "movl %1, %%gs:-4\n" // MOVL m, -4(GS) - :: "r"(ts.g), "r"(ts.m) - ); - - crosscall_386(ts.fn); - return nil; -} diff --git a/src/libcgo/freebsd_amd64.c b/src/libcgo/freebsd_amd64.c deleted file mode 100644 index 4baf16ee8..000000000 --- a/src/libcgo/freebsd_amd64.c +++ /dev/null @@ -1,74 +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 -#include "libcgo.h" - -static void* threadentry(void*); - -char *environ[] = { 0 }; -char *__progname; - -void -initcgo(void) -{ -} - -void -libcgo_sys_thread_start(ThreadStart *ts) -{ - pthread_attr_t attr; - pthread_t p; - size_t size; - - pthread_attr_init(&attr); - pthread_attr_getstacksize(&attr, &size); - ts->g->stackguard = size; - pthread_create(&p, &attr, threadentry, ts); -} - -static void* -threadentry(void *v) -{ - ThreadStart ts; - - ts = *(ThreadStart*)v; - free(v); - - ts.g->stackbase = (uintptr)&ts; - - /* - * libcgo_sys_thread_start set stackguard to stack size; - * change to actual guard pointer. - */ - ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; - - crosscall_amd64(ts.m, ts.g, ts.fn); - return nil; -} - -static __thread void *libcgo_m; -static __thread void *libcgo_g; - -void -libcgo_set_scheduler(void *m, void *g) -{ - libcgo_m = m; - libcgo_g = g; -} - -struct get_scheduler_args { - void *m; - void *g; -}; - -void libcgo_get_scheduler(struct get_scheduler_args *) - __attribute__ ((visibility("hidden"))); - -void -libcgo_get_scheduler(struct get_scheduler_args *p) -{ - p->m = libcgo_m; - p->g = libcgo_g; -} diff --git a/src/libcgo/libcgo.h b/src/libcgo/libcgo.h deleted file mode 100644 index b4b25accb..000000000 --- a/src/libcgo/libcgo.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include -#include -#include - -#define nil ((void*)0) -#define nelem(x) (sizeof(x)/sizeof((x)[0])) - -typedef uint32_t uint32; -typedef uintptr_t uintptr; - -/* - * The beginning of the per-goroutine structure, - * as defined in ../pkg/runtime/runtime.h. - * Just enough to edit these two fields. - */ -typedef struct G G; -struct G -{ - uintptr stackguard; - uintptr stackbase; -}; - -/* - * Arguments to the libcgo_thread_start call. - * Also known to ../pkg/runtime/runtime.h. - */ -typedef struct ThreadStart ThreadStart; -struct ThreadStart -{ - uintptr m; - G *g; - void (*fn)(void); -}; - -/* - * Called by 5c/6c/8c world. - * Makes a local copy of the ThreadStart and - * calls libcgo_sys_thread_start(ts). - */ -void libcgo_thread_start(ThreadStart *ts); - -/* - * Creates the new operating system thread (OS, arch dependent). - */ -void libcgo_sys_thread_start(ThreadStart *ts); - -/* - * Call fn in the 6c world, with m and g - * set to the given parameters. - */ -void crosscall_amd64(uintptr m, G *g, void (*fn)(void)); - -/* - * Call fn in the 8c world. - */ -void crosscall_386(void (*fn)(void)); diff --git a/src/libcgo/linux_386.c b/src/libcgo/linux_386.c deleted file mode 100644 index 9d02455cc..000000000 --- a/src/libcgo/linux_386.c +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include -#include "libcgo.h" - -static void *threadentry(void*); - -void -initcgo(void) -{ -} - -void -libcgo_sys_thread_start(ThreadStart *ts) -{ - pthread_attr_t attr; - pthread_t p; - size_t size; - - pthread_attr_init(&attr); - pthread_attr_getstacksize(&attr, &size); - ts->g->stackguard = size; - pthread_create(&p, &attr, threadentry, ts); -} - -static void* -threadentry(void *v) -{ - ThreadStart ts; - - ts = *(ThreadStart*)v; - free(v); - - ts.g->stackbase = (uintptr)&ts; - - /* - * libcgo_sys_thread_start set stackguard to stack size; - * change to actual guard pointer. - */ - ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; - - /* - * Set specific keys. On Linux/ELF, the thread local storage - * is just before %gs:0. Our dynamic 8.out's reserve 8 bytes - * for the two words g and m at %gs:-8 and %gs:-4. - */ - asm volatile ( - "movl %0, %%gs:-8\n" // MOVL g, -8(GS) - "movl %1, %%gs:-4\n" // MOVL m, -4(GS) - :: "r"(ts.g), "r"(ts.m) - ); - - crosscall_386(ts.fn); - return nil; -} diff --git a/src/libcgo/linux_amd64.c b/src/libcgo/linux_amd64.c deleted file mode 100644 index fc4a239fb..000000000 --- a/src/libcgo/linux_amd64.c +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include -#include "libcgo.h" - -static void* threadentry(void*); - -void -initcgo(void) -{ -} - -void -libcgo_sys_thread_start(ThreadStart *ts) -{ - pthread_attr_t attr; - pthread_t p; - size_t size; - - pthread_attr_init(&attr); - pthread_attr_getstacksize(&attr, &size); - ts->g->stackguard = size; - pthread_create(&p, &attr, threadentry, ts); -} - -static void* -threadentry(void *v) -{ - ThreadStart ts; - - ts = *(ThreadStart*)v; - free(v); - - ts.g->stackbase = (uintptr)&ts; - - /* - * libcgo_sys_thread_start set stackguard to stack size; - * change to actual guard pointer. - */ - ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; - - crosscall_amd64(ts.m, ts.g, ts.fn); - return nil; -} - -static __thread void *libcgo_m; -static __thread void *libcgo_g; - -void -libcgo_set_scheduler(void *m, void *g) -{ - libcgo_m = m; - libcgo_g = g; -} - -struct get_scheduler_args { - void *m; - void *g; -}; - -void libcgo_get_scheduler(struct get_scheduler_args *) - __attribute__ ((visibility("hidden"))); - -void -libcgo_get_scheduler(struct get_scheduler_args *p) -{ - p->m = libcgo_m; - p->g = libcgo_g; -} diff --git a/src/libcgo/linux_arm.c b/src/libcgo/linux_arm.c deleted file mode 100644 index 32d862984..000000000 --- a/src/libcgo/linux_arm.c +++ /dev/null @@ -1 +0,0 @@ -/* unimplemented */ diff --git a/src/libcgo/nacl_386.c b/src/libcgo/nacl_386.c deleted file mode 100644 index 32d862984..000000000 --- a/src/libcgo/nacl_386.c +++ /dev/null @@ -1 +0,0 @@ -/* unimplemented */ diff --git a/src/libcgo/util.c b/src/libcgo/util.c deleted file mode 100644 index c296b493d..000000000 --- a/src/libcgo/util.c +++ /dev/null @@ -1,46 +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 "libcgo.h" - -/* Stub for calling malloc from Go */ -void -_cgo_malloc(void *p) -{ - struct a { - long long n; - void *ret; - } *a = p; - - a->ret = malloc(a->n); -} - -/* Stub for calling from Go */ -void -_cgo_free(void *p) -{ - struct a { - void *arg; - } *a = p; - - free(a->arg); -} - -/* Stub for creating a new thread */ -void -libcgo_thread_start(ThreadStart *arg) -{ - ThreadStart *ts; - - /* Make our own copy that can persist after we return. */ - ts = malloc(sizeof *ts); - if(ts == nil) { - fprintf(stderr, "libcgo: out of memory in thread_start\n"); - abort(); - } - *ts = *arg; - - libcgo_sys_thread_start(ts); /* OS-dependent half */ -} - diff --git a/src/libcgo/windows_386.c b/src/libcgo/windows_386.c deleted file mode 100755 index 62be9303e..000000000 --- a/src/libcgo/windows_386.c +++ /dev/null @@ -1,45 +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. - -#define WIN32_LEAN_AND_MEAN -#include -#include "libcgo.h" - -static void *threadentry(void*); - -/* From what I've read 1MB is default for 32-bit Linux. - Allocation granularity on Windows is typically 64 KB. */ -#define STACKSIZE (1*1024*1024) - -void -initcgo(void) -{ -} - -void -libcgo_sys_thread_start(ThreadStart *ts) -{ - ts->g->stackguard = STACKSIZE; - _beginthread(threadentry, STACKSIZE, ts); -} - -static void* -threadentry(void *v) -{ - ThreadStart ts; - - ts = *(ThreadStart*)v; - free(v); - - ts.g->stackbase = (uintptr)&ts; - - /* - * libcgo_sys_thread_start set stackguard to stack size; - * change to actual guard pointer. - */ - ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; - - crosscall_386(ts.fn); - return nil; -} diff --git a/src/libcgo/windows_amd64.c b/src/libcgo/windows_amd64.c deleted file mode 100755 index 56417e178..000000000 --- a/src/libcgo/windows_amd64.c +++ /dev/null @@ -1,45 +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. - -#define WIN64_LEAN_AND_MEAN -#include -#include "libcgo.h" - -static void *threadentry(void*); - -/* From what I've read 2MB is default for 64-bit Linux. - Allocation granularity on Windows is typically 64 KB. */ -#define STACKSIZE (2*1024*1024) - -void -initcgo(void) -{ -} - -void -libcgo_sys_thread_start(ThreadStart *ts) -{ - ts->g->stackguard = STACKSIZE; - _beginthread(threadentry, STACKSIZE, ts); -} - -static void* -threadentry(void *v) -{ - ThreadStart ts; - - ts = *(ThreadStart*)v; - free(v); - - ts.g->stackbase = (uintptr)&ts; - - /* - * libcgo_sys_thread_start set stackguard to stack size; - * change to actual guard pointer. - */ - ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; - - crosscall_386(ts.fn); - return nil; -} diff --git a/src/libmach/5obj.c b/src/libmach/5obj.c index 034deea2c..e539362b0 100644 --- a/src/libmach/5obj.c +++ b/src/libmach/5obj.c @@ -123,6 +123,9 @@ addr(Biobuf *bp) case D_PSR: case D_FPCR: break; + case D_REGREG: + Bgetc(bp); + break; case D_CONST2: Bgetc(bp); Bgetc(bp); diff --git a/src/libmach/8db.c b/src/libmach/8db.c index 92e4c7694..80aa4fe69 100644 --- a/src/libmach/8db.c +++ b/src/libmach/8db.c @@ -329,8 +329,8 @@ struct Instr uchar mod; /* bits 6-7 of mod r/m field */ uchar reg; /* bits 3-5 of mod r/m field */ char ss; /* bits 6-7 of SIB */ - char index; /* bits 3-5 of SIB */ - char base; /* bits 0-2 of SIB */ + schar index; /* bits 3-5 of SIB */ + schar base; /* bits 0-2 of SIB */ char rip; /* RIP-relative in amd64 mode */ uchar opre; /* f2/f3 could introduce media */ short seg; /* segment of far address */ @@ -355,14 +355,15 @@ enum{ DI, /* amd64 */ - R8, - R9, - R10, - R11, - R12, - R13, - R14, - R15 + /* be careful: some unix system headers #define R8, R9, etc */ + AMD64_R8, + AMD64_R9, + AMD64_R10, + AMD64_R11, + AMD64_R12, + AMD64_R13, + AMD64_R14, + AMD64_R15 }; /* amd64 rex extension byte */ @@ -416,8 +417,8 @@ enum { RMOPB, /* Byte R/M field with op code (/digit) */ RMR, /* R/M register only (mod = 11) */ RMM, /* R/M memory only (mod = 0/1/2) */ - R0, /* Base reg of Mod R/M is literal 0x00 */ - R1, /* Base reg of Mod R/M is literal 0x01 */ + Op_R0, /* Base reg of Mod R/M is literal 0x00 */ + Op_R1, /* Base reg of Mod R/M is literal 0x01 */ FRMOP, /* Floating point R/M field with opcode */ FRMEX, /* Extended floating point R/M field with opcode */ JUMP, /* Jump or Call flag - no operand */ @@ -1006,7 +1007,7 @@ static Optable optabDA[8+8] = [0x09] 0,0, "FCMOVEQ %f,F0", [0x0a] 0,0, "FCMOVLS %f,F0", [0x0b] 0,0, "FCMOVUN %f,F0", -[0x0d] R1,0, "FUCOMPP", +[0x0d] Op_R1,0, "FUCOMPP", }; static Optable optabDB[8+64] = @@ -1071,7 +1072,7 @@ static Optable optabDE[8+8] = [0x07] 0,0, "FDIVRW %e,F0", [0x08] 0,0, "FADDDP F0,%f", [0x09] 0,0, "FMULDP F0,%f", -[0x0b] R1,0, "FCOMPDP", +[0x0b] Op_R1,0, "FCOMPDP", [0x0c] 0,0, "FSUBRDP F0,%f", [0x0d] 0,0, "FSUBDP F0,%f", [0x0e] 0,0, "FDIVRDP F0,%f", @@ -1087,7 +1088,7 @@ static Optable optabDF[8+8] = [0x05] 0,0, "FMOVL %e,F0", [0x06] 0,0, "FBSTP %e", [0x07] 0,0, "FMOVLP F0,%e", -[0x0c] R0,0, "FSTSW %OAX", +[0x0c] Op_R0,0, "FSTSW %OAX", [0x0d] 0,0, "FUCOMIP F0,%f", [0x0e] 0,0, "FCOMIP F0,%f", }; @@ -1713,11 +1714,11 @@ badop: if (c != 0x0a) goto badop; break; - case R0: /* base register must be R0 */ + case Op_R0: /* base register must be R0 */ if (ip->base != 0) goto badop; break; - case R1: /* base register must be R1 */ + case Op_R1: /* base register must be R1 */ if (ip->base != 1) goto badop; break; @@ -1903,14 +1904,14 @@ static char *reg[] = { [DI] "DI", /* amd64 */ -[R8] "R8", -[R9] "R9", -[R10] "R10", -[R11] "R11", -[R12] "R12", -[R13] "R13", -[R14] "R14", -[R15] "R15", +[AMD64_R8] "R8", +[AMD64_R9] "R9", +[AMD64_R10] "R10", +[AMD64_R11] "R11", +[AMD64_R12] "R12", +[AMD64_R13] "R13", +[AMD64_R14] "R14", +[AMD64_R15] "R15", }; static char *breg[] = { "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH" }; diff --git a/src/libmach/Makefile b/src/libmach/Makefile index 900d27861..5d7e87d86 100644 --- a/src/libmach/Makefile +++ b/src/libmach/Makefile @@ -26,7 +26,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -include ../Make.conf +include ../Make.inc +O:=$(HOST_O) LIB=libmach.a OFILES=\ @@ -52,21 +53,12 @@ ifneq ($(GOOS),windows) OFILES+=\ $(shell uname | tr A-Z a-z).$O\ +else +OFILES+=\ + windows.$O\ + endif HFILES=../../include/mach.h elf.h macho.h obj.h -install: $(LIB) - cp $(LIB) ../../lib - -$(LIB): $(OFILES) - ar rsc $(LIB) $(OFILES) - -$(OFILES): $(HFILES) - -clean: - rm -f *.$O $(LIB) - -nuke: clean - rm -f "$(GOROOT)"/lib/$(LIB) - +include ../Make.clib diff --git a/src/libmach/darwin.c b/src/libmach/darwin.c index feb49c059..7ee6f7ace 100644 --- a/src/libmach/darwin.c +++ b/src/libmach/darwin.c @@ -807,8 +807,10 @@ ctlproc(int id, char *msg) // Find Mach thread for pid and suspend it. t = addpid(id, 1); - if(t == nil) + if(t == nil) { + fprint(2, "ctlproc attached: addpid: %r\n"); return -1; + } if(me(thread_suspend(t->thread)) < 0){ fprint(2, "ctlproc attached: thread_suspend: %r\n"); return -1; @@ -816,7 +818,12 @@ ctlproc(int id, char *msg) // Let ptrace tell the process to keep going: // then ptrace is out of the way and we're back in Mach land. - return ptrace(PT_CONTINUE, id, (caddr_t)1, 0); + if(ptrace(PT_CONTINUE, id, (caddr_t)1, 0) < 0) { + fprint(2, "ctlproc attached: ptrace continue: %r\n"); + return -1; + } + + return 0; } // All the other control messages require a Thread structure. diff --git a/src/libmach/executable.c b/src/libmach/executable.c index 34da72151..33000ed07 100644 --- a/src/libmach/executable.c +++ b/src/libmach/executable.c @@ -66,7 +66,7 @@ static int adotout(int, Fhdr*, ExecHdr*); static int elfdotout(int, Fhdr*, ExecHdr*); static int machdotout(int, Fhdr*, ExecHdr*); static int armdotout(int, Fhdr*, ExecHdr*); -static void setsym(Fhdr*, int32, int32, int32, vlong); +static void setsym(Fhdr*, vlong, int32, vlong, int32, vlong, int32); static void setdata(Fhdr*, uvlong, int32, vlong, int32); static void settext(Fhdr*, uvlong, uvlong, int32, vlong); static void hswal(void*, int, uint32(*)(uint32)); @@ -427,7 +427,7 @@ adotout(int fd, Fhdr *fp, ExecHdr *hp) hp->e.exechdr.text, sizeof(Exec)); setdata(fp, _round(pgsize+fp->txtsz+sizeof(Exec), pgsize), hp->e.exechdr.data, fp->txtsz+sizeof(Exec), hp->e.exechdr.bss); - setsym(fp, hp->e.exechdr.syms, hp->e.exechdr.spsz, hp->e.exechdr.pcsz, fp->datoff+fp->datsz); + setsym(fp, fp->datoff+fp->datsz, hp->e.exechdr.syms, 0, hp->e.exechdr.spsz, 0, hp->e.exechdr.pcsz); return 1; } @@ -525,7 +525,7 @@ commonllp64(int unused, Fhdr *fp, ExecHdr *hp) settext(fp, entry, pgsize+fp->hdrsz, hp->e.exechdr.text, fp->hdrsz); setdata(fp, _round(pgsize+fp->txtsz+fp->hdrsz, pgsize), hp->e.exechdr.data, fp->txtsz+fp->hdrsz, hp->e.exechdr.bss); - setsym(fp, hp->e.exechdr.syms, hp->e.exechdr.spsz, hp->e.exechdr.pcsz, fp->datoff+fp->datsz); + setsym(fp, fp->datoff+fp->datsz, hp->e.exechdr.syms, 0, hp->e.exechdr.spsz, 0, hp->e.exechdr.pcsz); if(hp->e.exechdr.magic & DYN_MAGIC) { fp->txtaddr = 0; @@ -758,13 +758,12 @@ elf64dotout(int fd, Fhdr *fp, ExecHdr *hp) bsssz = ph[0].memsz - ph[0].filesz; settext(fp, ep->elfentry | 0x80000000, txtaddr, txtsz, ph[0].offset); setdata(fp, dataddr, ph[0].paddr, ph[0].offset + txtsz, bsssz); - setsym(fp, ph[1].filesz, 0, ph[1].memsz, ph[1].offset); + setsym(fp, ph[1].offset, ph[1].filesz, 0, 0, 0, ph[1].memsz); free(ph); return 1; } werrstr("No TEXT or DATA sections"); -error: free(ph); free(sh); return 0; @@ -773,12 +772,13 @@ error: settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset); setdata(fp, ph[id].vaddr, ph[id].filesz, ph[id].offset, ph[id].memsz - ph[id].filesz); if(is != -1) - setsym(fp, ph[is].filesz, 0, ph[is].memsz, ph[is].offset); + setsym(fp, ph[is].offset, ph[is].filesz, 0, 0, 0, ph[is].memsz); else if(sh != 0){ char *buf; uvlong symsize = 0; uvlong symoff = 0; uvlong pclnsz = 0; + uvlong pclnoff = 0; /* load shstrtab names */ buf = malloc(sh[ep->shstrndx].size); @@ -786,7 +786,8 @@ error: goto done; memset(buf, 0, sizeof buf); seek(fd, sh[ep->shstrndx].offset, 0); - read(fd, buf, sh[ep->shstrndx].size); + i = read(fd, buf, sh[ep->shstrndx].size); + USED(i); // shut up ubuntu gcc for(i = 0; i < ep->shnum; i++) { if (strcmp(&buf[sh[i].name], ".gosymtab") == 0) { @@ -794,15 +795,11 @@ error: symoff = sh[i].offset; } if (strcmp(&buf[sh[i].name], ".gopclntab") == 0) { - if (sh[i].offset != symoff+symsize) { - werrstr("pc line table not contiguous with symbol table"); - free(buf); - goto error; - } pclnsz = sh[i].size; + pclnoff = sh[i].offset; } } - setsym(fp, symsize, 0, pclnsz, symoff); + setsym(fp, symoff, symsize, 0, 0, pclnoff, pclnsz); free(buf); } done: @@ -939,13 +936,12 @@ elfdotout(int fd, Fhdr *fp, ExecHdr *hp) bsssz = ph[0].memsz - ph[0].filesz; settext(fp, ep->elfentry | 0x80000000, txtaddr, txtsz, ph[0].offset); setdata(fp, dataddr, ph[0].paddr, ph[0].offset + txtsz, bsssz); - setsym(fp, ph[1].filesz, 0, ph[1].memsz, ph[1].offset); + setsym(fp, ph[1].offset, ph[1].filesz, 0, 0, 0, ph[1].memsz); free(ph); return 1; } werrstr("No TEXT or DATA sections"); -error: free(sh); free(ph); return 0; @@ -954,12 +950,13 @@ error: settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset); setdata(fp, ph[id].vaddr, ph[id].filesz, ph[id].offset, ph[id].memsz - ph[id].filesz); if(is != -1) - setsym(fp, ph[is].filesz, 0, ph[is].memsz, ph[is].offset); + setsym(fp, ph[is].offset, ph[is].filesz, 0, 0, 0, ph[is].memsz); else if(sh != 0){ char *buf; uvlong symsize = 0; uvlong symoff = 0; - uvlong pclnsz = 0; + uvlong pclnsize = 0; + uvlong pclnoff = 0; /* load shstrtab names */ buf = malloc(sh[ep->shstrndx].size); @@ -967,7 +964,8 @@ error: goto done; memset(buf, 0, sizeof buf); seek(fd, sh[ep->shstrndx].offset, 0); - read(fd, buf, sh[ep->shstrndx].size); + i = read(fd, buf, sh[ep->shstrndx].size); + USED(i); // shut up ubuntu gcc for(i = 0; i < ep->shnum; i++) { if (strcmp(&buf[sh[i].name], ".gosymtab") == 0) { @@ -975,15 +973,11 @@ error: symoff = sh[i].offset; } if (strcmp(&buf[sh[i].name], ".gopclntab") == 0) { - if (sh[i].offset != symoff+symsize) { - werrstr("pc line table not contiguous with symbol table"); - free(buf); - goto error; - } - pclnsz = sh[i].size; + pclnsize = sh[i].size; + pclnoff = sh[i].offset; } } - setsym(fp, symsize, 0, pclnsz, symoff); + setsym(fp, symoff, symsize, 0, 0, pclnoff, pclnsize); free(buf); } done: @@ -1207,7 +1201,7 @@ machdotout(int fd, Fhdr *fp, ExecHdr *hp) settext(fp, textva+sizeof(Machhdr) + mp->sizeofcmds, textva, textsize, textoff); setdata(fp, datava, datasize, dataoff, bsssize); if(symtab != 0) - setsym(fp, symtab->filesize, 0, pclntab? pclntab->filesize : 0, symtab->fileoff); + setsym(fp, symtab->fileoff, symtab->filesize, 0, 0, 0, pclntab? pclntab->filesize : 0); free(cmd); free(cmdbuf); return 1; @@ -1228,7 +1222,7 @@ armdotout(int fd, Fhdr *fp, ExecHdr *hp) USED(fd); settext(fp, hp->e.exechdr.entry, sizeof(Exec), hp->e.exechdr.text, sizeof(Exec)); setdata(fp, fp->txtsz, hp->e.exechdr.data, fp->txtsz, hp->e.exechdr.bss); - setsym(fp, hp->e.exechdr.syms, hp->e.exechdr.spsz, hp->e.exechdr.pcsz, fp->datoff+fp->datsz); + setsym(fp, fp->datoff+fp->datsz, hp->e.exechdr.syms, 0, hp->e.exechdr.spsz, 0, hp->e.exechdr.pcsz); kbase = 0xF0000000; if ((fp->entry & kbase) == kbase) { /* Boot image */ @@ -1259,14 +1253,20 @@ setdata(Fhdr *fp, uvlong a, int32 s, vlong off, int32 bss) } static void -setsym(Fhdr *fp, int32 symsz, int32 sppcsz, int32 lnpcsz, vlong symoff) +setsym(Fhdr *fp, vlong symoff, int32 symsz, vlong sppcoff, int32 sppcsz, vlong lnpcoff, int32 lnpcsz) { - fp->symsz = symsz; fp->symoff = symoff; + fp->symsz = symsz; + + if(sppcoff == 0) + sppcoff = symoff+symsz; + fp->sppcoff = symoff; fp->sppcsz = sppcsz; - fp->sppcoff = fp->symoff+fp->symsz; + + if(lnpcoff == 0) + lnpcoff = sppcoff + sppcsz; + fp->lnpcoff = lnpcoff; fp->lnpcsz = lnpcsz; - fp->lnpcoff = fp->sppcoff+fp->sppcsz; } diff --git a/src/libmach/linux.c b/src/libmach/linux.c index 46724b87e..30b4da240 100644 --- a/src/libmach/linux.c +++ b/src/libmach/linux.c @@ -199,9 +199,11 @@ attachthread(int pid, int tid, int *new, int newstate) t = malloc(sizeof *t); if(t == nil) return nil; - memset(t, 0, sizeof *t); + memset(t, 0, sizeof *t); thr[nthr++] = t; + if(pid == 0 && nthr > 0) + pid = thr[0]->pid; t->pid = pid; t->tid = tid; t->state = newstate; @@ -296,7 +298,9 @@ wait1(int nohang) if(nohang != 0) nohang = WNOHANG; + status = 0; tid = waitpid(-1, &status, __WALL|WUNTRACED|WSTOPPED|WCONTINUED|nohang); + if(tid < 0) return -1; if(tid == 0) @@ -305,11 +309,15 @@ wait1(int nohang) if(trace > 0 && status != NormalStop) fprint(2, "TID %d: %#x\n", tid, status); - // If we've not heard of this tid, something is wrong. t = findthread(tid); if(t == nil) { - fprint(2, "ptrace waitpid: unexpected new tid %d status %#x\n", tid, status); - return -1; + // Sometimes the kernel tells us about new threads + // before we see the parent clone. + t = attachthread(0, tid, &new, Stopped); + if(t == nil) { + fprint(2, "failed to attach to new thread %d\n", tid); + return -1; + } } if(WIFSTOPPED(status)) { @@ -339,8 +347,6 @@ wait1(int nohang) } t->child = data; attachthread(t->pid, t->child, &new, Running); - if(!new) - fprint(2, "ptrace child: not new\n"); break; case PTRACE_EVENT_EXEC: @@ -759,9 +765,9 @@ ptracerw(int type, int xtype, int isr, int pid, uvlong addr, void *v, uint n) if(errno) goto ptraceerr; if(n-i >= sizeof(uintptr)) - *(uintptr*)((char*)v+i) = u; + memmove((char*)v+i, &u, sizeof(uintptr)); else{ - *(uintptr*)buf = u; + memmove(buf, &u, sizeof u); memmove((char*)v+i, buf, n-i); } }else{ @@ -772,9 +778,9 @@ ptracerw(int type, int xtype, int isr, int pid, uvlong addr, void *v, uint n) u = ptrace(xtype, pid, addr+i, 0); if(errno) return -1; - *(uintptr*)buf = u; + memmove(buf, &u, sizeof u); memmove(buf, (char*)v+i, n-i); - u = *(uintptr*)buf; + memmove(&u, buf, sizeof u); } if(ptrace(type, pid, addr+i, u) < 0) goto ptraceerr; @@ -810,9 +816,22 @@ ptracesegrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr) // Go 32-bit is // DI SI BP NSP BX DX CX AX GS FS ES DS TRAP ECODE PC CS EFLAGS SP SS -// uint go32tolinux32tab[] = { -// 4, 3, 5, 15, 0, 2, 1, 6, 10, 9, 8, 7, -1, -1, 12, 13, 14, 15, 16 -// }; +uint go32tolinux32tab[] = { + 4, 3, 5, 15, 0, 2, 1, 6, 10, 9, 8, 7, -1, -1, 12, 13, 14, 15, 16 +}; +static int +go32tolinux32(uvlong addr) +{ + int r; + + if(addr%4 || addr/4 >= nelem(go32tolinux32tab)) + return -1; + r = go32tolinux32tab[addr/4]; + if(r < 0) + return -1; + return r*4; +} + uint go32tolinux64tab[] = { 14, 13, 4, 19, 5, 12, 11, 10, 26, 25, 24, 23, -1, -1, 16, 17, 18, 19, 20 }; @@ -830,15 +849,24 @@ go32tolinux64(uvlong addr) } extern Mach mi386; +extern Mach mamd64; static int go2linux(uvlong addr) { - // TODO(rsc): If this file is being compiled in 32-bit mode, - // need to use the go32tolinux32 table instead. + if(sizeof(void*) == 4) { + if(mach == &mi386) + return go32tolinux32(addr); + werrstr("unsupported architecture"); + return -1; + } if(mach == &mi386) return go32tolinux64(addr); + if(mach != &mamd64) { + werrstr("unsupported architecture"); + return -1; + } switch(addr){ case offsetof(Ureg64, ax): diff --git a/src/libmach/windows.c b/src/libmach/windows.c new file mode 100644 index 000000000..391761c18 --- /dev/null +++ b/src/libmach/windows.c @@ -0,0 +1,59 @@ +// This is stubbed out for the moment. Will revisit when the time comes. +#include +#include +#include +#include + +int +ctlproc(int pid, char *msg) +{ + sysfatal("ctlproc unimplemented in Windows"); +} + +char* +proctextfile(int pid) +{ + sysfatal("proctextfile unimplemented in Windows"); +} + +char* +procstatus(int pid) +{ + sysfatal("procstatus unimplemented in Windows"); +} + +Map* +attachproc(int pid, Fhdr *fp) +{ + sysfatal("attachproc unimplemented in Windows"); +} + +void +detachproc(Map *m) +{ + sysfatal("detachproc unimplemented in Windows"); +} + +int +procthreadpids(int pid, int *p, int np) +{ + sysfatal("procthreadpids unimplemented in Windows"); +} + +int +pread(int fd, void *buf, int count, int offset) +{ + sysfatal("pread unimplemented in Windows"); +} + +int +pwrite(int fd, void *buf, int count, int offset) +{ + sysfatal("pwrite unimplemented in Windows"); +} + +int +nanosleep(const struct timespec *rqtp, struct timespec *rmtp) +{ + sysfatal("nanosleep unimplemented in Windows"); +} diff --git a/src/make.bash b/src/make.bash index b718bb956..0d0dae61f 100755 --- a/src/make.bash +++ b/src/make.bash @@ -4,23 +4,36 @@ # license that can be found in the LICENSE file. set -e +if [ ! -f env.bash ]; then + echo 'make.bash must be run from $GOROOT/src' 1>&2 + exit 1 +fi . ./env.bash +# Create target directories +if [ "$GOBIN" = "$GOROOT/bin" ]; then + mkdir -p "$GOROOT/bin" +fi +mkdir -p "$GOROOT/pkg" + +GOROOT_FINAL=${GOROOT_FINAL:-$GOROOT} + MAKEFLAGS=${MAKEFLAGS:-"-j4"} export MAKEFLAGS unset CDPATH # in case user has it set rm -f "$GOBIN"/quietgcc CC=${CC:-gcc} +export CC sed -e "s|@CC@|$CC|" < "$GOROOT"/src/quietgcc.bash > "$GOBIN"/quietgcc chmod +x "$GOBIN"/quietgcc rm -f "$GOBIN"/gomake -MAKE=make -if ! make --version 2>/dev/null | grep 'GNU Make' >/dev/null; then - MAKE=gmake -fi -(echo '#!/bin/sh'; echo 'exec '$MAKE' "$@"') >"$GOBIN"/gomake +( + echo '#!/bin/sh' + echo 'export GOROOT=${GOROOT:-'$GOROOT_FINAL'}' + echo 'exec '$MAKE' "$@"' +) >"$GOBIN"/gomake chmod +x "$GOBIN"/gomake if [ -d /selinux -a -f /selinux/booleans/allow_execstack ] ; then @@ -45,10 +58,11 @@ fi ) bash "$GOROOT"/src/clean.bash -for i in lib9 libbio libmach cmd pkg libcgo cmd/cgo cmd/ebnflint cmd/godoc cmd/gofmt cmd/goinstall cmd/goyacc cmd/hgpatch +# pkg builds libcgo and the Go programs in cmd. +for i in lib9 libbio libmach cmd pkg do case "$i-$GOOS-$GOARCH" in - libcgo-nacl-* | cmd/*-nacl-* | libcgo-linux-arm) + cmd/*-nacl-*) ;; *) # The ( ) here are to preserve the current directory @@ -63,18 +77,41 @@ do bash make.bash ;; pkg) - "$GOBIN"/gomake install + gomake install ;; *) - "$GOBIN"/gomake install + gomake install esac ) || exit 1 esac done -case "`uname`" in -Darwin) - echo; - echo %%% run sudo.bash to install debuggers +# Print post-install messages. +# Implemented as a function so that all.bash can repeat the output +# after run.bash finishes running all the tests. +installed() { + eval $(gomake --no-print-directory -f Make.inc go-env) echo -esac + echo --- + echo Installed Go for $GOOS/$GOARCH in "$GOROOT". + echo Installed commands in "$GOBIN". + case "$OLDPATH" in + "$GOBIN:"* | *":$GOBIN" | *":$GOBIN:"*) + ;; + *) + echo '***' "You need to add $GOBIN to your "'$PATH.' '***' + esac + echo The compiler is $GC. + if [ "$(uname)" = "Darwin" ]; then + echo + echo On OS X the debuggers must be installed setgrp procmod. + echo Read and run ./sudo.bash to install the debuggers. + fi + if [ "$GOROOT_FINAL" != "$GOROOT" ]; then + echo + echo The binaries expect "$GOROOT" to be copied or moved to "$GOROOT_FINAL". + fi +} + +(installed) # run in sub-shell to avoid polluting environment + diff --git a/src/pkg/Makefile b/src/pkg/Makefile index e489b71d4..05e2a26d1 100644 --- a/src/pkg/Makefile +++ b/src/pkg/Makefile @@ -9,18 +9,13 @@ # # to rebuild the dependency information in Make.deps. -nullstring := -space := $(nullstring) # a space at the end -ifndef GOBIN -QUOTED_HOME=$(subst $(space),\ ,$(HOME)) -GOBIN=$(QUOTED_HOME)/bin -endif -QUOTED_GOBIN=$(subst $(space),\ ,$(GOBIN)) +include ../Make.inc all: install DIRS=\ archive/tar\ + archive/zip\ asn1\ big\ bufio\ @@ -36,9 +31,13 @@ DIRS=\ crypto/aes\ crypto/block\ crypto/blowfish\ + crypto/cast5\ + crypto/cipher\ + crypto/elliptic\ crypto/hmac\ crypto/md4\ crypto/md5\ + crypto/ocsp\ crypto/rand\ crypto/rc4\ crypto/ripemd160\ @@ -48,12 +47,14 @@ DIRS=\ crypto/sha512\ crypto/subtle\ crypto/tls\ + crypto/twofish\ crypto/x509\ crypto/xtea\ debug/dwarf\ debug/macho\ debug/elf\ debug/gosym\ + debug/pe\ debug/proc\ ebnf\ encoding/ascii85\ @@ -61,14 +62,13 @@ DIRS=\ encoding/binary\ encoding/git85\ encoding/hex\ + encoding/line\ encoding/pem\ exec\ - exp/bignum\ exp/datafmt\ exp/draw\ exp/draw/x11\ exp/eval\ - exp/iterable\ expvar\ flag\ fmt\ @@ -78,26 +78,30 @@ DIRS=\ go/printer\ go/scanner\ go/token\ + go/typechecker\ gob\ hash\ hash/adler32\ hash/crc32\ hash/crc64\ + html\ http\ http/pprof\ image\ image/jpeg\ image/png\ + index/suffixarray\ io\ io/ioutil\ json\ log\ math\ mime\ + mime/multipart\ net\ + net/dict\ + net/textproto\ netchan\ - nntp\ - once\ os\ os/signal\ patch\ @@ -108,8 +112,10 @@ DIRS=\ rpc\ rpc/jsonrpc\ runtime\ + runtime/cgo\ runtime/pprof\ scanner\ + smtp\ sort\ strconv\ strings\ @@ -123,11 +129,30 @@ DIRS=\ testing/quick\ testing/script\ time\ + try\ unicode\ utf16\ utf8\ websocket\ xml\ + ../cmd/cgo\ + ../cmd/ebnflint\ + ../cmd/godoc\ + ../cmd/gofmt\ + ../cmd/goinstall\ + ../cmd/govet\ + ../cmd/goyacc\ + ../cmd/hgpatch\ + +ifeq ($(GOOS),linux) +DIRS+=\ + os/inotify\ + +endif + +ifeq ($(GOOS),windows) +DIRS:=$(filter-out runtime/cgo,$(DIRS)) +endif NOTEST=\ debug/proc\ @@ -139,11 +164,22 @@ NOTEST=\ http/pprof\ image\ image/jpeg\ + net/dict\ rand\ - runtime\ + runtime/cgo\ runtime/pprof\ syscall\ + testing\ testing/iotest\ + try\ + ../cmd/cgo\ + ../cmd/ebnflint\ + ../cmd/godoc\ + ../cmd/gofmt\ + ../cmd/goinstall\ + ../cmd/govet\ + ../cmd/goyacc\ + ../cmd/hgpatch\ NOBENCH=\ container/vector\ @@ -153,24 +189,12 @@ ifeq ($(DISABLE_NET_TESTS),1) NOTEST+=http net endif -# Disable tests that NaCl cannot run yet. -ifeq ($(GOOS),nacl) -NOTEST+=archive/tar # no pipe -NOTEST+=debug/dwarf # no pread -NOTEST+=debug/macho # no pread -NOTEST+=debug/elf # no pread -NOTEST+=exec # no pipe -NOTEST+=http # no network -NOTEST+=log # no runtime.Caller -NOTEST+=net # no network -NOTEST+=netchan # no network -NOTEST+=os # many things unimplemented +# Disable tests that windows cannot run yet. +ifeq ($(GOOS),windows) NOTEST+=os/signal # no signals NOTEST+=path # tree walking does not work -NOTEST+=rpc # no network NOTEST+=syslog # no network NOTEST+=time # no syscall.Kill, syscall.SIGCHLD for sleep tests -NOTEST+=websocket # no network endif TEST=\ @@ -186,19 +210,19 @@ test.dirs: $(addsuffix .test, $(TEST)) bench.dirs: $(addsuffix .bench, $(BENCH)) %.clean: - +cd $* && $(QUOTED_GOBIN)/gomake clean + +cd $* && gomake clean %.install: - +cd $* && $(QUOTED_GOBIN)/gomake install + +cd $* && gomake install %.nuke: - +cd $* && $(QUOTED_GOBIN)/gomake nuke + +cd $* && gomake nuke %.test: - +cd $* && $(QUOTED_GOBIN)/gomake test + +cd $* && gomake test %.bench: - +cd $* && $(QUOTED_GOBIN)/gomake bench + +cd $* && gomake bench clean: clean.dirs @@ -214,4 +238,9 @@ nuke: nuke.dirs deps: ./deps.bash +echo-dirs: + @echo $(DIRS) + -include Make.deps + +runtime/cgo.install: ../cmd/cgo.install diff --git a/src/pkg/archive/tar/Makefile b/src/pkg/archive/tar/Makefile index 6a29de8f7..8897e883e 100644 --- a/src/pkg/archive/tar/Makefile +++ b/src/pkg/archive/tar/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=archive/tar GOFILES=\ diff --git a/src/pkg/archive/tar/reader_test.go b/src/pkg/archive/tar/reader_test.go index cfc258507..aa4c797fb 100644 --- a/src/pkg/archive/tar/reader_test.go +++ b/src/pkg/archive/tar/reader_test.go @@ -136,7 +136,7 @@ testLoop: break } if hdr != nil || err != nil { - t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, err) + t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err) } f.Close() } diff --git a/src/pkg/archive/tar/writer.go b/src/pkg/archive/tar/writer.go index 1f2656d32..8673bad31 100644 --- a/src/pkg/archive/tar/writer.go +++ b/src/pkg/archive/tar/writer.go @@ -71,9 +71,7 @@ func (tw *Writer) cString(b []byte, s string) { } return } - for i, ch := range []byte(s) { - b[i] = ch - } + copy(b, s) if len(s) < len(b) { b[len(s)] = 0 } diff --git a/src/pkg/archive/tar/writer_test.go b/src/pkg/archive/tar/writer_test.go index 24db9b821..48b891140 100644 --- a/src/pkg/archive/tar/writer_test.go +++ b/src/pkg/archive/tar/writer_test.go @@ -141,7 +141,7 @@ testLoop: } } if err := tw.Close(); err != nil { - t.Errorf("test %d: Failed closing archive: %v", err) + t.Errorf("test %d: Failed closing archive: %v", i, err) continue testLoop } diff --git a/src/pkg/archive/zip/Makefile b/src/pkg/archive/zip/Makefile new file mode 100644 index 000000000..32a543133 --- /dev/null +++ b/src/pkg/archive/zip/Makefile @@ -0,0 +1,12 @@ +# 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=archive/zip +GOFILES=\ + reader.go\ + struct.go\ + +include ../../../Make.pkg diff --git a/src/pkg/archive/zip/reader.go b/src/pkg/archive/zip/reader.go new file mode 100644 index 000000000..579ba1602 --- /dev/null +++ b/src/pkg/archive/zip/reader.go @@ -0,0 +1,278 @@ +// 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 zip package provides support for reading ZIP archives. + +See: http://www.pkware.com/documents/casestudies/APPNOTE.TXT + +This package does not support ZIP64 or disk spanning. +*/ +package zip + +import ( + "bufio" + "bytes" + "compress/flate" + "hash" + "hash/crc32" + "encoding/binary" + "io" + "os" +) + +var ( + FormatError = os.NewError("not a valid zip file") + UnsupportedMethod = os.NewError("unsupported compression algorithm") + ChecksumError = os.NewError("checksum error") +) + +type Reader struct { + r io.ReaderAt + File []*File + Comment string +} + +type File struct { + FileHeader + zipr io.ReaderAt + zipsize int64 + headerOffset uint32 + bodyOffset int64 +} + +// OpenReader will open the Zip file specified by name and return a Reader. +func OpenReader(name string) (*Reader, os.Error) { + f, err := os.Open(name, os.O_RDONLY, 0644) + if err != nil { + return nil, err + } + fi, err := f.Stat() + if err != nil { + return nil, err + } + return NewReader(f, fi.Size) +} + +// NewReader returns a new Reader reading from r, which is assumed to +// have the given size in bytes. +func NewReader(r io.ReaderAt, size int64) (*Reader, os.Error) { + end, err := readDirectoryEnd(r, size) + if err != nil { + return nil, err + } + z := &Reader{ + r: r, + File: make([]*File, end.directoryRecords), + Comment: end.comment, + } + rs := io.NewSectionReader(r, 0, size) + if _, err = rs.Seek(int64(end.directoryOffset), 0); err != nil { + return nil, err + } + buf := bufio.NewReader(rs) + for i := range z.File { + z.File[i] = &File{zipr: r, zipsize: size} + if err := readDirectoryHeader(z.File[i], buf); err != nil { + return nil, err + } + } + return z, nil +} + +// Open returns a ReadCloser that provides access to the File's contents. +func (f *File) Open() (rc io.ReadCloser, err os.Error) { + off := int64(f.headerOffset) + if f.bodyOffset == 0 { + r := io.NewSectionReader(f.zipr, off, f.zipsize-off) + if err = readFileHeader(f, r); err != nil { + return + } + if f.bodyOffset, err = r.Seek(0, 1); err != nil { + return + } + } + r := io.NewSectionReader(f.zipr, off+f.bodyOffset, int64(f.CompressedSize)) + switch f.Method { + case 0: // store (no compression) + rc = nopCloser{r} + case 8: // DEFLATE + rc = flate.NewReader(r) + default: + err = UnsupportedMethod + } + if rc != nil { + rc = &checksumReader{rc, crc32.NewIEEE(), f.CRC32} + } + return +} + +type checksumReader struct { + rc io.ReadCloser + hash hash.Hash32 + sum uint32 +} + +func (r *checksumReader) Read(b []byte) (n int, err os.Error) { + n, err = r.rc.Read(b) + r.hash.Write(b[:n]) + if err != os.EOF { + return + } + if r.hash.Sum32() != r.sum { + err = ChecksumError + } + return +} + +func (r *checksumReader) Close() os.Error { return r.rc.Close() } + +type nopCloser struct { + io.Reader +} + +func (f nopCloser) Close() os.Error { return nil } + +func readFileHeader(f *File, r io.Reader) (err os.Error) { + defer func() { + if rerr, ok := recover().(os.Error); ok { + err = rerr + } + }() + var ( + signature uint32 + filenameLength uint16 + extraLength uint16 + ) + read(r, &signature) + if signature != fileHeaderSignature { + return FormatError + } + read(r, &f.ReaderVersion) + read(r, &f.Flags) + read(r, &f.Method) + read(r, &f.ModifiedTime) + read(r, &f.ModifiedDate) + read(r, &f.CRC32) + read(r, &f.CompressedSize) + read(r, &f.UncompressedSize) + read(r, &filenameLength) + read(r, &extraLength) + f.Name = string(readByteSlice(r, filenameLength)) + f.Extra = readByteSlice(r, extraLength) + return +} + +func readDirectoryHeader(f *File, r io.Reader) (err os.Error) { + defer func() { + if rerr, ok := recover().(os.Error); ok { + err = rerr + } + }() + var ( + signature uint32 + filenameLength uint16 + extraLength uint16 + commentLength uint16 + startDiskNumber uint16 // unused + internalAttributes uint16 // unused + externalAttributes uint32 // unused + ) + read(r, &signature) + if signature != directoryHeaderSignature { + return FormatError + } + read(r, &f.CreatorVersion) + read(r, &f.ReaderVersion) + read(r, &f.Flags) + read(r, &f.Method) + read(r, &f.ModifiedTime) + read(r, &f.ModifiedDate) + read(r, &f.CRC32) + read(r, &f.CompressedSize) + read(r, &f.UncompressedSize) + read(r, &filenameLength) + read(r, &extraLength) + read(r, &commentLength) + read(r, &startDiskNumber) + read(r, &internalAttributes) + read(r, &externalAttributes) + read(r, &f.headerOffset) + f.Name = string(readByteSlice(r, filenameLength)) + f.Extra = readByteSlice(r, extraLength) + f.Comment = string(readByteSlice(r, commentLength)) + return +} + +func readDirectoryEnd(r io.ReaderAt, size int64) (d *directoryEnd, err os.Error) { + // look for directoryEndSignature in the last 1k, then in the last 65k + var b []byte + for i, bLen := range []int64{1024, 65 * 1024} { + if bLen > size { + bLen = size + } + b = make([]byte, int(bLen)) + if _, err := r.ReadAt(b, size-bLen); err != nil && err != os.EOF { + return nil, err + } + if p := findSignatureInBlock(b); p >= 0 { + b = b[p:] + break + } + if i == 1 || bLen == size { + return nil, FormatError + } + } + + // read header into struct + defer func() { + if rerr, ok := recover().(os.Error); ok { + err = rerr + d = nil + } + }() + br := bytes.NewBuffer(b[4:]) // skip over signature + d = new(directoryEnd) + read(br, &d.diskNbr) + read(br, &d.dirDiskNbr) + read(br, &d.dirRecordsThisDisk) + read(br, &d.directoryRecords) + read(br, &d.directorySize) + read(br, &d.directoryOffset) + read(br, &d.commentLen) + d.comment = string(readByteSlice(br, d.commentLen)) + return d, nil +} + +func findSignatureInBlock(b []byte) int { + const minSize = 4 + 2 + 2 + 2 + 2 + 4 + 4 + 2 // fixed part of header + for i := len(b) - minSize; i >= 0; i-- { + // defined from directoryEndSignature in struct.go + if b[i] == 'P' && b[i+1] == 'K' && b[i+2] == 0x05 && b[i+3] == 0x06 { + // n is length of comment + n := int(b[i+minSize-2]) | int(b[i+minSize-1])<<8 + if n+minSize+i == len(b) { + return i + } + } + } + return -1 +} + +func read(r io.Reader, data interface{}) { + if err := binary.Read(r, binary.LittleEndian, data); err != nil { + panic(err) + } +} + +func readByteSlice(r io.Reader, l uint16) []byte { + b := make([]byte, l) + if l == 0 { + return b + } + if _, err := io.ReadFull(r, b); err != nil { + panic(err) + } + return b +} diff --git a/src/pkg/archive/zip/reader_test.go b/src/pkg/archive/zip/reader_test.go new file mode 100644 index 000000000..3c24f1467 --- /dev/null +++ b/src/pkg/archive/zip/reader_test.go @@ -0,0 +1,180 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package zip + +import ( + "bytes" + "encoding/binary" + "io" + "io/ioutil" + "os" + "testing" +) + +type ZipTest struct { + Name string + Comment string + File []ZipTestFile + Error os.Error // the error that Opening this file should return +} + +type ZipTestFile struct { + Name string + Content []byte // if blank, will attempt to compare against File + File string // name of file to compare to (relative to testdata/) +} + +var tests = []ZipTest{ + { + Name: "test.zip", + Comment: "This is a zipfile comment.", + File: []ZipTestFile{ + { + Name: "test.txt", + Content: []byte("This is a test text file.\n"), + }, + { + Name: "gophercolor16x16.png", + File: "gophercolor16x16.png", + }, + }, + }, + { + Name: "r.zip", + File: []ZipTestFile{ + { + Name: "r/r.zip", + File: "r.zip", + }, + }, + }, + {Name: "readme.zip"}, + {Name: "readme.notzip", Error: FormatError}, +} + +func TestReader(t *testing.T) { + for _, zt := range tests { + readTestZip(t, zt) + } +} + +func readTestZip(t *testing.T, zt ZipTest) { + z, err := OpenReader("testdata/" + zt.Name) + if err != zt.Error { + t.Errorf("error=%v, want %v", err, zt.Error) + return + } + + // bail here if no Files expected to be tested + // (there may actually be files in the zip, but we don't care) + if zt.File == nil { + return + } + + if z.Comment != zt.Comment { + t.Errorf("%s: comment=%q, want %q", zt.Name, z.Comment, zt.Comment) + } + if len(z.File) != len(zt.File) { + t.Errorf("%s: file count=%d, want %d", zt.Name, len(z.File), len(zt.File)) + } + + // test read of each file + for i, ft := range zt.File { + readTestFile(t, ft, z.File[i]) + } + + // test simultaneous reads + n := 0 + done := make(chan bool) + for i := 0; i < 5; i++ { + for j, ft := range zt.File { + go func() { + readTestFile(t, ft, z.File[j]) + done <- true + }() + n++ + } + } + for ; n > 0; n-- { + <-done + } + + // test invalid checksum + z.File[0].CRC32++ // invalidate + r, err := z.File[0].Open() + if err != nil { + t.Error(err) + return + } + var b bytes.Buffer + _, err = io.Copy(&b, r) + if err != ChecksumError { + t.Errorf("%s: copy error=%v, want %v", z.File[0].Name, err, ChecksumError) + } +} + +func readTestFile(t *testing.T, ft ZipTestFile, f *File) { + if f.Name != ft.Name { + t.Errorf("name=%q, want %q", f.Name, ft.Name) + } + var b bytes.Buffer + r, err := f.Open() + if err != nil { + t.Error(err) + return + } + _, err = io.Copy(&b, r) + if err != nil { + t.Error(err) + return + } + r.Close() + var c []byte + if len(ft.Content) != 0 { + c = ft.Content + } else if c, err = ioutil.ReadFile("testdata/" + ft.File); err != nil { + t.Error(err) + return + } + if b.Len() != len(c) { + t.Errorf("%s: len=%d, want %d", f.Name, b.Len(), len(c)) + return + } + for i, b := range b.Bytes() { + if b != c[i] { + t.Errorf("%s: content[%d]=%q want %q", f.Name, i, b, c[i]) + return + } + } +} + +func TestInvalidFiles(t *testing.T) { + const size = 1024 * 70 // 70kb + b := make([]byte, size) + + // zeroes + _, err := NewReader(sliceReaderAt(b), size) + if err != FormatError { + t.Errorf("zeroes: error=%v, want %v", err, FormatError) + } + + // repeated directoryEndSignatures + sig := make([]byte, 4) + binary.LittleEndian.PutUint32(sig, directoryEndSignature) + for i := 0; i < size-4; i += 4 { + copy(b[i:i+4], sig) + } + _, err = NewReader(sliceReaderAt(b), size) + if err != FormatError { + t.Errorf("sigs: error=%v, want %v", err, FormatError) + } +} + +type sliceReaderAt []byte + +func (r sliceReaderAt) ReadAt(b []byte, off int64) (int, os.Error) { + copy(b, r[int(off):int(off)+len(b)]) + return len(b), nil +} diff --git a/src/pkg/archive/zip/struct.go b/src/pkg/archive/zip/struct.go new file mode 100644 index 000000000..8a8c727d4 --- /dev/null +++ b/src/pkg/archive/zip/struct.go @@ -0,0 +1,33 @@ +package zip + +const ( + fileHeaderSignature = 0x04034b50 + directoryHeaderSignature = 0x02014b50 + directoryEndSignature = 0x06054b50 +) + +type FileHeader struct { + Name string + CreatorVersion uint16 + ReaderVersion uint16 + Flags uint16 + Method uint16 + ModifiedTime uint16 + ModifiedDate uint16 + CRC32 uint32 + CompressedSize uint32 + UncompressedSize uint32 + Extra []byte + Comment string +} + +type directoryEnd struct { + diskNbr uint16 // unused + dirDiskNbr uint16 // unused + dirRecordsThisDisk uint16 // unused + directoryRecords uint16 + directorySize uint32 + directoryOffset uint32 // relative to file + commentLen uint16 + comment string +} diff --git a/src/pkg/archive/zip/testdata/gophercolor16x16.png b/src/pkg/archive/zip/testdata/gophercolor16x16.png new file mode 100644 index 000000000..48854ff3b Binary files /dev/null and b/src/pkg/archive/zip/testdata/gophercolor16x16.png differ diff --git a/src/pkg/archive/zip/testdata/r.zip b/src/pkg/archive/zip/testdata/r.zip new file mode 100644 index 000000000..ea0fa2ffc Binary files /dev/null and b/src/pkg/archive/zip/testdata/r.zip differ diff --git a/src/pkg/archive/zip/testdata/readme.notzip b/src/pkg/archive/zip/testdata/readme.notzip new file mode 100644 index 000000000..06668c4c1 Binary files /dev/null and b/src/pkg/archive/zip/testdata/readme.notzip differ diff --git a/src/pkg/archive/zip/testdata/readme.zip b/src/pkg/archive/zip/testdata/readme.zip new file mode 100644 index 000000000..db3bb900e Binary files /dev/null and b/src/pkg/archive/zip/testdata/readme.zip differ diff --git a/src/pkg/archive/zip/testdata/test.zip b/src/pkg/archive/zip/testdata/test.zip new file mode 100644 index 000000000..03890c05d Binary files /dev/null and b/src/pkg/archive/zip/testdata/test.zip differ diff --git a/src/pkg/asn1/Makefile b/src/pkg/asn1/Makefile index 40b76b849..6b7770e82 100644 --- a/src/pkg/asn1/Makefile +++ b/src/pkg/asn1/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=asn1 GOFILES=\ diff --git a/src/pkg/asn1/asn1.go b/src/pkg/asn1/asn1.go index bba8a0fe2..d06b1d4d7 100644 --- a/src/pkg/asn1/asn1.go +++ b/src/pkg/asn1/asn1.go @@ -150,6 +150,20 @@ func parseBitString(bytes []byte) (ret BitString, err os.Error) { // An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER. type ObjectIdentifier []int +// Equal returns true iff oi and other represent the same identifier. +func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool { + if len(oi) != len(other) { + return false + } + for i := 0; i < len(oi); i++ { + if oi[i] != other[i] { + return false + } + } + + return true +} + // parseObjectIdentifier parses an OBJECT IDENTIFER from the given bytes and // returns it. An object identifer is a sequence of variable length integers // that are assigned in a hierarachy. @@ -179,6 +193,17 @@ func parseObjectIdentifier(bytes []byte) (s []int, err os.Error) { return } +// ENUMERATED + +// An Enumerated is represented as a plain int. +type Enumerated int + + +// FLAG + +// A Flag accepts any data and is set to true if present. +type Flag bool + // parseBase128Int parses a base-128 encoded int from the given offset in the // given byte array. It returns the value and the new offset. func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err os.Error) { @@ -202,101 +227,20 @@ func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err os.Erro // UTCTime -func isDigit(b byte) bool { return '0' <= b && b <= '9' } - -// twoDigits returns the value of two, base 10 digits. -func twoDigits(bytes []byte, max int) (int, bool) { - for i := 0; i < 2; i++ { - if !isDigit(bytes[i]) { - return 0, false - } - } - value := (int(bytes[0])-'0')*10 + int(bytes[1]-'0') - if value > max { - return 0, false - } - return value, true -} - -// parseUTCTime parses the UTCTime from the given byte array and returns the -// resulting time. func parseUTCTime(bytes []byte) (ret *time.Time, err os.Error) { - // A UTCTime can take the following formats: - // - // 1111111 - // 01234567890123456 - // - // YYMMDDhhmmZ - // YYMMDDhhmm+hhmm - // YYMMDDhhmm-hhmm - // YYMMDDhhmmssZ - // YYMMDDhhmmss+hhmm - // YYMMDDhhmmss-hhmm - if len(bytes) < 11 { - err = SyntaxError{"UTCTime too short"} - return - } - ret = new(time.Time) - - var ok1, ok2, ok3, ok4, ok5 bool - year, ok1 := twoDigits(bytes[0:2], 99) - // RFC 5280, section 5.1.2.4 says that years 2050 or later use another date - // scheme. - if year >= 50 { - ret.Year = 1900 + int64(year) - } else { - ret.Year = 2000 + int64(year) - } - ret.Month, ok2 = twoDigits(bytes[2:4], 12) - ret.Day, ok3 = twoDigits(bytes[4:6], 31) - ret.Hour, ok4 = twoDigits(bytes[6:8], 23) - ret.Minute, ok5 = twoDigits(bytes[8:10], 59) - if !ok1 || !ok2 || !ok3 || !ok4 || !ok5 { - goto Error - } - bytes = bytes[10:] - switch bytes[0] { - case '0', '1', '2', '3', '4', '5', '6': - if len(bytes) < 3 { - goto Error - } - ret.Second, ok1 = twoDigits(bytes[0:2], 60) // 60, not 59, because of leap seconds. - if !ok1 { - goto Error - } - bytes = bytes[2:] - } - if len(bytes) == 0 { - goto Error - } - switch bytes[0] { - case 'Z': - if len(bytes) != 1 { - goto Error - } + s := string(bytes) + ret, err = time.Parse("0601021504Z0700", s) + if err == nil { return - case '-', '+': - if len(bytes) != 5 { - goto Error - } - hours, ok1 := twoDigits(bytes[1:3], 12) - minutes, ok2 := twoDigits(bytes[3:5], 59) - if !ok1 || !ok2 { - goto Error - } - sign := 1 - if bytes[0] == '-' { - sign = -1 - } - ret.ZoneOffset = sign * (60 * (hours*60 + minutes)) - default: - goto Error } + ret, err = time.Parse("060102150405Z0700", s) return +} -Error: - err = SyntaxError{"invalid UTCTime"} - return +// parseGeneralizedTime parses the GeneralizedTime from the given byte array +// and returns the resulting time. +func parseGeneralizedTime(bytes []byte) (ret *time.Time, err os.Error) { + return time.Parse("20060102150405Z0700", string(bytes)) } // PrintableString @@ -346,11 +290,20 @@ func parseIA5String(bytes []byte) (ret string, err os.Error) { return } +// T61String + +// parseT61String parses a ASN.1 T61String (8-bit clean string) from the given +// byte array and returns it. +func parseT61String(bytes []byte) (ret string, err os.Error) { + return string(bytes), nil +} + // A RawValue represents an undecoded ASN.1 object. type RawValue struct { Class, Tag int IsCompound bool Bytes []byte + FullBytes []byte // includes the tag and length } // RawContent is used to signal that the undecoded, DER data needs to be @@ -462,6 +415,8 @@ func parseSequenceOf(bytes []byte, sliceType *reflect.SliceType, elemType reflec var ( bitStringType = reflect.Typeof(BitString{}) objectIdentifierType = reflect.Typeof(ObjectIdentifier{}) + enumeratedType = reflect.Typeof(Enumerated(0)) + flagType = reflect.Typeof(Flag(false)) timeType = reflect.Typeof(&time.Time{}) rawValueType = reflect.Typeof(RawValue{}) rawContentsType = reflect.Typeof(RawContent(nil)) @@ -499,7 +454,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam err = SyntaxError{"data truncated"} return } - result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length]} + result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length], bytes[initOffset : offset+t.length]} offset += t.length v.(*reflect.StructValue).Set(reflect.NewValue(result).(*reflect.StructValue)) return @@ -525,6 +480,8 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam result, err = parsePrintableString(innerBytes) case tagIA5String: result, err = parseIA5String(innerBytes) + case tagT61String: + result, err = parseT61String(innerBytes) case tagInteger: result, err = parseInt64(innerBytes) case tagBitString: @@ -559,9 +516,20 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam return } if params.explicit { - if t.class == classContextSpecific && t.tag == *params.tag && t.isCompound { - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { + if t.class == classContextSpecific && t.tag == *params.tag && (t.length == 0 || t.isCompound) { + if t.length > 0 { + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + } else { + if fieldType != flagType { + err = StructuralError{"Zero length explicit tag was not an asn1.Flag"} + return + } + + flagValue := v.(*reflect.BoolValue) + flagValue.Set(true) return } } else { @@ -584,6 +552,12 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam universalTag = tagIA5String } + // Special case for time: UTCTime and GeneralizedTime both map to the + // Go type time.Time. + if universalTag == tagUTCTime && t.tag == tagGeneralizedTime { + universalTag = tagGeneralizedTime + } + expectedClass := classUniversal expectedTag := universalTag @@ -617,7 +591,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam sliceValue := v.(*reflect.SliceValue) sliceValue.Set(reflect.MakeSlice(sliceValue.Type().(*reflect.SliceType), len(newSlice), len(newSlice))) if err1 == nil { - reflect.ArrayCopy(sliceValue, reflect.NewValue(newSlice).(reflect.ArrayOrSliceValue)) + reflect.Copy(sliceValue, reflect.NewValue(newSlice).(reflect.ArrayOrSliceValue)) } err = err1 return @@ -631,12 +605,30 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam return case timeType: ptrValue := v.(*reflect.PtrValue) - time, err1 := parseUTCTime(innerBytes) + var time *time.Time + var err1 os.Error + if universalTag == tagUTCTime { + time, err1 = parseUTCTime(innerBytes) + } else { + time, err1 = parseGeneralizedTime(innerBytes) + } if err1 == nil { ptrValue.Set(reflect.NewValue(time).(*reflect.PtrValue)) } err = err1 return + case enumeratedType: + parsedInt, err1 := parseInt(innerBytes) + enumValue := v.(*reflect.IntValue) + if err1 == nil { + enumValue.Set(int64(parsedInt)) + } + err = err1 + return + case flagType: + flagValue := v.(*reflect.BoolValue) + flagValue.Set(true) + return } switch val := v.(type) { case *reflect.BoolValue: @@ -691,7 +683,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam sliceType := fieldType.(*reflect.SliceType) if sliceType.Elem().Kind() == reflect.Uint8 { val.Set(reflect.MakeSlice(sliceType, len(innerBytes), len(innerBytes))) - reflect.ArrayCopy(val, reflect.NewValue(innerBytes).(reflect.ArrayOrSliceValue)) + reflect.Copy(val, reflect.NewValue(innerBytes).(reflect.ArrayOrSliceValue)) return } newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem()) @@ -707,6 +699,8 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam v, err = parsePrintableString(innerBytes) case tagIA5String: v, err = parseIA5String(innerBytes) + case tagT61String: + v, err = parseT61String(innerBytes) default: err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)} } @@ -753,6 +747,10 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { // An ASN.1 OBJECT IDENTIFIER can be written to an // ObjectIdentifier. // +// An ASN.1 ENUMERATED can be written to an Enumerated. +// +// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a *time.Time. +// // An ASN.1 PrintableString or IA5String can be written to a string. // // Any of the above ASN.1 values can be written to an interface{}. @@ -777,7 +775,7 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { // // Other ASN.1 types are not supported; if it encounters them, // Unmarshal returns a parse error. -func Unmarshal(val interface{}, b []byte) (rest []byte, err os.Error) { +func Unmarshal(b []byte, val interface{}) (rest []byte, err os.Error) { v := reflect.NewValue(val).(*reflect.PtrValue).Elem() offset, err := parseField(v, b, 0, fieldParameters{}) if err != nil { diff --git a/src/pkg/asn1/asn1_test.go b/src/pkg/asn1/asn1_test.go index b5bce93b7..34b5f1ecd 100644 --- a/src/pkg/asn1/asn1_test.go +++ b/src/pkg/asn1/asn1_test.go @@ -18,16 +18,16 @@ type int64Test struct { } var int64TestData = []int64Test{ - int64Test{[]byte{0x00}, true, 0}, - int64Test{[]byte{0x7f}, true, 127}, - int64Test{[]byte{0x00, 0x80}, true, 128}, - int64Test{[]byte{0x01, 0x00}, true, 256}, - int64Test{[]byte{0x80}, true, -128}, - int64Test{[]byte{0xff, 0x7f}, true, -129}, - int64Test{[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, true, -1}, - int64Test{[]byte{0xff}, true, -1}, - int64Test{[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, true, -9223372036854775808}, - int64Test{[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, false, 0}, + {[]byte{0x00}, true, 0}, + {[]byte{0x7f}, true, 127}, + {[]byte{0x00, 0x80}, true, 128}, + {[]byte{0x01, 0x00}, true, 256}, + {[]byte{0x80}, true, -128}, + {[]byte{0xff, 0x7f}, true, -129}, + {[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, true, -1}, + {[]byte{0xff}, true, -1}, + {[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, true, -9223372036854775808}, + {[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, false, 0}, } func TestParseInt64(t *testing.T) { @@ -50,12 +50,12 @@ type bitStringTest struct { } var bitStringTestData = []bitStringTest{ - bitStringTest{[]byte{}, false, []byte{}, 0}, - bitStringTest{[]byte{0x00}, true, []byte{}, 0}, - bitStringTest{[]byte{0x07, 0x00}, true, []byte{0x00}, 1}, - bitStringTest{[]byte{0x07, 0x01}, false, []byte{}, 0}, - bitStringTest{[]byte{0x07, 0x40}, false, []byte{}, 0}, - bitStringTest{[]byte{0x08, 0x00}, false, []byte{}, 0}, + {[]byte{}, false, []byte{}, 0}, + {[]byte{0x00}, true, []byte{}, 0}, + {[]byte{0x07, 0x00}, true, []byte{0x00}, 1}, + {[]byte{0x07, 0x01}, false, []byte{}, 0}, + {[]byte{0x07, 0x40}, false, []byte{}, 0}, + {[]byte{0x08, 0x00}, false, []byte{}, 0}, } func TestBitString(t *testing.T) { @@ -95,12 +95,12 @@ type bitStringRightAlignTest struct { } var bitStringRightAlignTests = []bitStringRightAlignTest{ - bitStringRightAlignTest{[]byte{0x80}, 1, []byte{0x01}}, - bitStringRightAlignTest{[]byte{0x80, 0x80}, 9, []byte{0x01, 0x01}}, - bitStringRightAlignTest{[]byte{}, 0, []byte{}}, - bitStringRightAlignTest{[]byte{0xce}, 8, []byte{0xce}}, - bitStringRightAlignTest{[]byte{0xce, 0x47}, 16, []byte{0xce, 0x47}}, - bitStringRightAlignTest{[]byte{0x34, 0x50}, 12, []byte{0x03, 0x45}}, + {[]byte{0x80}, 1, []byte{0x01}}, + {[]byte{0x80, 0x80}, 9, []byte{0x01, 0x01}}, + {[]byte{}, 0, []byte{}}, + {[]byte{0xce}, 8, []byte{0xce}}, + {[]byte{0xce, 0x47}, 16, []byte{0xce, 0x47}}, + {[]byte{0x34, 0x50}, 12, []byte{0x03, 0x45}}, } func TestBitStringRightAlign(t *testing.T) { @@ -120,11 +120,11 @@ type objectIdentifierTest struct { } var objectIdentifierTestData = []objectIdentifierTest{ - objectIdentifierTest{[]byte{}, false, []int{}}, - objectIdentifierTest{[]byte{85}, true, []int{2, 5}}, - objectIdentifierTest{[]byte{85, 0x02}, true, []int{2, 5, 2}}, - objectIdentifierTest{[]byte{85, 0x02, 0xc0, 0x00}, true, []int{2, 5, 2, 0x2000}}, - objectIdentifierTest{[]byte{85, 0x02, 0xc0, 0x80, 0x80, 0x80, 0x80}, false, []int{}}, + {[]byte{}, false, []int{}}, + {[]byte{85}, true, []int{2, 5}}, + {[]byte{85, 0x02}, true, []int{2, 5, 2}}, + {[]byte{85, 0x02, 0xc0, 0x00}, true, []int{2, 5, 2, 0x2000}}, + {[]byte{85, 0x02, 0xc0, 0x80, 0x80, 0x80, 0x80}, false, []int{}}, } func TestObjectIdentifier(t *testing.T) { @@ -147,23 +147,23 @@ type timeTest struct { out *time.Time } -var timeTestData = []timeTest{ - timeTest{"910506164540-0700", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, -7 * 60 * 60, ""}}, - timeTest{"910506164540+0730", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 7*60*60 + 30*60, ""}}, - timeTest{"910506234540Z", true, &time.Time{1991, 05, 06, 23, 45, 40, 0, 0, ""}}, - timeTest{"9105062345Z", true, &time.Time{1991, 05, 06, 23, 45, 0, 0, 0, ""}}, - timeTest{"a10506234540Z", false, nil}, - timeTest{"91a506234540Z", false, nil}, - timeTest{"9105a6234540Z", false, nil}, - timeTest{"910506a34540Z", false, nil}, - timeTest{"910506334a40Z", false, nil}, - timeTest{"91050633444aZ", false, nil}, - timeTest{"910506334461Z", false, nil}, - timeTest{"910506334400Za", false, nil}, +var utcTestData = []timeTest{ + {"910506164540-0700", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, -7 * 60 * 60, ""}}, + {"910506164540+0730", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 7*60*60 + 30*60, ""}}, + {"910506234540Z", true, &time.Time{1991, 05, 06, 23, 45, 40, 0, 0, "UTC"}}, + {"9105062345Z", true, &time.Time{1991, 05, 06, 23, 45, 0, 0, 0, "UTC"}}, + {"a10506234540Z", false, nil}, + {"91a506234540Z", false, nil}, + {"9105a6234540Z", false, nil}, + {"910506a34540Z", false, nil}, + {"910506334a40Z", false, nil}, + {"91050633444aZ", false, nil}, + {"910506334461Z", false, nil}, + {"910506334400Za", false, nil}, } -func TestTime(t *testing.T) { - for i, test := range timeTestData { +func TestUTCTime(t *testing.T) { + for i, test := range utcTestData { ret, err := parseUTCTime([]byte(test.in)) if (err == nil) != test.ok { t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) @@ -176,6 +176,27 @@ func TestTime(t *testing.T) { } } +var generalizedTimeTestData = []timeTest{ + {"20100102030405Z", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, "UTC"}}, + {"20100102030405", false, nil}, + {"20100102030405+0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 6*60*60 + 7*60, ""}}, + {"20100102030405-0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, -6*60*60 - 7*60, ""}}, +} + +func TestGeneralizedTime(t *testing.T) { + for i, test := range generalizedTimeTestData { + ret, err := parseGeneralizedTime([]byte(test.in)) + if (err == nil) != test.ok { + t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) + } + if err == nil { + if !reflect.DeepEqual(test.out, ret) { + t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) + } + } + } +} + type tagAndLengthTest struct { in []byte ok bool @@ -183,18 +204,18 @@ type tagAndLengthTest struct { } var tagAndLengthData = []tagAndLengthTest{ - tagAndLengthTest{[]byte{0x80, 0x01}, true, tagAndLength{2, 0, 1, false}}, - tagAndLengthTest{[]byte{0xa0, 0x01}, true, tagAndLength{2, 0, 1, true}}, - tagAndLengthTest{[]byte{0x02, 0x00}, true, tagAndLength{0, 2, 0, false}}, - tagAndLengthTest{[]byte{0xfe, 0x00}, true, tagAndLength{3, 30, 0, true}}, - tagAndLengthTest{[]byte{0x1f, 0x01, 0x00}, true, tagAndLength{0, 1, 0, false}}, - tagAndLengthTest{[]byte{0x1f, 0x81, 0x00, 0x00}, true, tagAndLength{0, 128, 0, false}}, - tagAndLengthTest{[]byte{0x1f, 0x81, 0x80, 0x01, 0x00}, true, tagAndLength{0, 0x4001, 0, false}}, - tagAndLengthTest{[]byte{0x00, 0x81, 0x01}, true, tagAndLength{0, 0, 1, false}}, - tagAndLengthTest{[]byte{0x00, 0x82, 0x01, 0x00}, true, tagAndLength{0, 0, 256, false}}, - tagAndLengthTest{[]byte{0x00, 0x83, 0x01, 0x00}, false, tagAndLength{}}, - tagAndLengthTest{[]byte{0x1f, 0x85}, false, tagAndLength{}}, - tagAndLengthTest{[]byte{0x30, 0x80}, false, tagAndLength{}}, + {[]byte{0x80, 0x01}, true, tagAndLength{2, 0, 1, false}}, + {[]byte{0xa0, 0x01}, true, tagAndLength{2, 0, 1, true}}, + {[]byte{0x02, 0x00}, true, tagAndLength{0, 2, 0, false}}, + {[]byte{0xfe, 0x00}, true, tagAndLength{3, 30, 0, true}}, + {[]byte{0x1f, 0x01, 0x00}, true, tagAndLength{0, 1, 0, false}}, + {[]byte{0x1f, 0x81, 0x00, 0x00}, true, tagAndLength{0, 128, 0, false}}, + {[]byte{0x1f, 0x81, 0x80, 0x01, 0x00}, true, tagAndLength{0, 0x4001, 0, false}}, + {[]byte{0x00, 0x81, 0x01}, true, tagAndLength{0, 0, 1, false}}, + {[]byte{0x00, 0x82, 0x01, 0x00}, true, tagAndLength{0, 0, 256, false}}, + {[]byte{0x00, 0x83, 0x01, 0x00}, false, tagAndLength{}}, + {[]byte{0x1f, 0x85}, false, tagAndLength{}}, + {[]byte{0x30, 0x80}, false, tagAndLength{}}, } func TestParseTagAndLength(t *testing.T) { @@ -223,17 +244,17 @@ func newString(s string) *string { return &s } func newBool(b bool) *bool { return &b } var parseFieldParametersTestData []parseFieldParametersTest = []parseFieldParametersTest{ - parseFieldParametersTest{"", fieldParameters{}}, - parseFieldParametersTest{"ia5", fieldParameters{stringType: tagIA5String}}, - parseFieldParametersTest{"printable", fieldParameters{stringType: tagPrintableString}}, - parseFieldParametersTest{"optional", fieldParameters{optional: true}}, - parseFieldParametersTest{"explicit", fieldParameters{explicit: true, tag: new(int)}}, - parseFieldParametersTest{"optional,explicit", fieldParameters{optional: true, explicit: true, tag: new(int)}}, - parseFieldParametersTest{"default:42", fieldParameters{defaultValue: newInt64(42)}}, - parseFieldParametersTest{"tag:17", fieldParameters{tag: newInt(17)}}, - parseFieldParametersTest{"optional,explicit,default:42,tag:17", fieldParameters{optional: true, explicit: true, defaultValue: newInt64(42), tag: newInt(17)}}, - parseFieldParametersTest{"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, newInt64(42), newInt(17), 0, false}}, - parseFieldParametersTest{"set", fieldParameters{set: true}}, + {"", fieldParameters{}}, + {"ia5", fieldParameters{stringType: tagIA5String}}, + {"printable", fieldParameters{stringType: tagPrintableString}}, + {"optional", fieldParameters{optional: true}}, + {"explicit", fieldParameters{explicit: true, tag: new(int)}}, + {"optional,explicit", fieldParameters{optional: true, explicit: true, tag: new(int)}}, + {"default:42", fieldParameters{defaultValue: newInt64(42)}}, + {"tag:17", fieldParameters{tag: newInt(17)}}, + {"optional,explicit,default:42,tag:17", fieldParameters{optional: true, explicit: true, defaultValue: newInt64(42), tag: newInt(17)}}, + {"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, newInt64(42), newInt(17), 0, false}}, + {"set", fieldParameters{set: true}}, } func TestParseFieldParameters(t *testing.T) { @@ -269,20 +290,20 @@ type TestElementsAfterString struct { } var unmarshalTestData []unmarshalTest = []unmarshalTest{ - unmarshalTest{[]byte{0x02, 0x01, 0x42}, newInt(0x42)}, - unmarshalTest{[]byte{0x30, 0x08, 0x06, 0x06, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d}, &TestObjectIdentifierStruct{[]int{1, 2, 840, 113549}}}, - unmarshalTest{[]byte{0x03, 0x04, 0x06, 0x6e, 0x5d, 0xc0}, &BitString{[]byte{110, 93, 192}, 18}}, - unmarshalTest{[]byte{0x30, 0x09, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03}, &[]int{1, 2, 3}}, - unmarshalTest{[]byte{0x02, 0x01, 0x10}, newInt(16)}, - unmarshalTest{[]byte{0x13, 0x04, 't', 'e', 's', 't'}, newString("test")}, - unmarshalTest{[]byte{0x16, 0x04, 't', 'e', 's', 't'}, newString("test")}, - unmarshalTest{[]byte{0x16, 0x04, 't', 'e', 's', 't'}, &RawValue{0, 22, false, []byte("test")}}, - unmarshalTest{[]byte{0x04, 0x04, 1, 2, 3, 4}, &RawValue{0, 4, false, []byte{1, 2, 3, 4}}}, - unmarshalTest{[]byte{0x30, 0x03, 0x81, 0x01, 0x01}, &TestContextSpecificTags{1}}, - unmarshalTest{[]byte{0x30, 0x08, 0xa1, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}, &TestContextSpecificTags2{1, 2}}, - unmarshalTest{[]byte{0x01, 0x01, 0x00}, newBool(false)}, - unmarshalTest{[]byte{0x01, 0x01, 0x01}, newBool(true)}, - unmarshalTest{[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}}, + {[]byte{0x02, 0x01, 0x42}, newInt(0x42)}, + {[]byte{0x30, 0x08, 0x06, 0x06, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d}, &TestObjectIdentifierStruct{[]int{1, 2, 840, 113549}}}, + {[]byte{0x03, 0x04, 0x06, 0x6e, 0x5d, 0xc0}, &BitString{[]byte{110, 93, 192}, 18}}, + {[]byte{0x30, 0x09, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03}, &[]int{1, 2, 3}}, + {[]byte{0x02, 0x01, 0x10}, newInt(16)}, + {[]byte{0x13, 0x04, 't', 'e', 's', 't'}, newString("test")}, + {[]byte{0x16, 0x04, 't', 'e', 's', 't'}, newString("test")}, + {[]byte{0x16, 0x04, 't', 'e', 's', 't'}, &RawValue{0, 22, false, []byte("test"), []byte("\x16\x04test")}}, + {[]byte{0x04, 0x04, 1, 2, 3, 4}, &RawValue{0, 4, false, []byte{1, 2, 3, 4}, []byte{4, 4, 1, 2, 3, 4}}}, + {[]byte{0x30, 0x03, 0x81, 0x01, 0x01}, &TestContextSpecificTags{1}}, + {[]byte{0x30, 0x08, 0xa1, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}, &TestContextSpecificTags2{1, 2}}, + {[]byte{0x01, 0x01, 0x00}, newBool(false)}, + {[]byte{0x01, 0x01, 0x01}, newBool(true)}, + {[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}}, } func TestUnmarshal(t *testing.T) { @@ -291,7 +312,7 @@ func TestUnmarshal(t *testing.T) { zv := reflect.MakeZero(pv.Type().(*reflect.PtrType).Elem()) pv.(*reflect.PtrValue).PointTo(zv) val := pv.Interface() - _, err := Unmarshal(val, test.in) + _, err := Unmarshal(test.in, val) if err != nil { t.Errorf("Unmarshal failed at index %d %v", i, err) } @@ -342,11 +363,11 @@ type PublicKeyInfo struct { func TestCertificate(t *testing.T) { // This is a minimal, self-signed certificate that should parse correctly. var cert Certificate - if _, err := Unmarshal(&cert, derEncodedSelfSignedCertBytes); err != nil { + if _, err := Unmarshal(derEncodedSelfSignedCertBytes, &cert); err != nil { t.Errorf("Unmarshal failed: %v", err) } if !reflect.DeepEqual(cert, derEncodedSelfSignedCert) { - t.Errorf("Bad result:\ngot: %+v\nwant: %+v\n", cert, derEncodedSelfSignedCert) + t.Errorf("Bad result:\ngot: %+v\nwant: %+v", cert, derEncodedSelfSignedCert) } } @@ -355,7 +376,7 @@ func TestCertificateWithNUL(t *testing.T) { // NUL isn't a permitted character in a PrintableString. var cert Certificate - if _, err := Unmarshal(&cert, derEncodedPaypalNULCertBytes); err == nil { + if _, err := Unmarshal(derEncodedPaypalNULCertBytes, &cert); err == nil { t.Error("Unmarshal succeeded, should not have") } } @@ -369,7 +390,7 @@ func TestRawStructs(t *testing.T) { var s rawStructTest input := []byte{0x30, 0x03, 0x02, 0x01, 0x50} - rest, err := Unmarshal(&s, input) + rest, err := Unmarshal(input, &s) if len(rest) != 0 { t.Errorf("incomplete parse: %x", rest) return @@ -389,7 +410,7 @@ func TestRawStructs(t *testing.T) { var derEncodedSelfSignedCert = Certificate{ TBSCertificate: TBSCertificate{ Version: 0, - SerialNumber: RawValue{Class: 0, Tag: 2, IsCompound: false, Bytes: []uint8{0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}}, + SerialNumber: RawValue{Class: 0, Tag: 2, IsCompound: false, Bytes: []uint8{0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}, FullBytes: []byte{2, 9, 0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}}, SignatureAlgorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}}, Issuer: RDNSequence{ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}}, @@ -399,7 +420,7 @@ var derEncodedSelfSignedCert = Certificate{ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}}, RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}}, }, - Validity: Validity{NotBefore: &time.Time{Year: 2009, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: ""}, NotAfter: &time.Time{Year: 2010, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: ""}}, + Validity: Validity{NotBefore: &time.Time{Year: 2009, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}, NotAfter: &time.Time{Year: 2010, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}}, Subject: RDNSequence{ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}}, RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}}, diff --git a/src/pkg/asn1/common.go b/src/pkg/asn1/common.go index 14fa30426..4a5eca145 100644 --- a/src/pkg/asn1/common.go +++ b/src/pkg/asn1/common.go @@ -24,11 +24,14 @@ const ( tagBitString = 3 tagOctetString = 4 tagOID = 6 + tagEnum = 10 tagSequence = 16 tagSet = 17 tagPrintableString = 19 + tagT61String = 20 tagIA5String = 22 tagUTCTime = 23 + tagGeneralizedTime = 24 ) const ( @@ -121,6 +124,8 @@ func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) { return tagBitString, false, true case timeType: return tagUTCTime, false, true + case enumeratedType: + return tagEnum, false, true } switch t := t.(type) { case *reflect.BoolType: diff --git a/src/pkg/asn1/marshal.go b/src/pkg/asn1/marshal.go index d4f8f782d..24548714b 100644 --- a/src/pkg/asn1/marshal.go +++ b/src/pkg/asn1/marshal.go @@ -96,19 +96,6 @@ func marshalBase128Int(out *forkableWriter, n int64) (err os.Error) { return nil } -func base128Length(i int) (numBytes int) { - if i == 0 { - return 1 - } - - for i > 0 { - numBytes++ - i >>= 7 - } - - return -} - func marshalInt64(out *forkableWriter, i int64) (err os.Error) { n := int64Length(i) @@ -123,11 +110,14 @@ func marshalInt64(out *forkableWriter, i int64) (err os.Error) { } func int64Length(i int64) (numBytes int) { - if i == 0 { - return 1 + numBytes = 1 + + for i > 127 { + numBytes++ + i >>= 8 } - for i > 0 { + for i < -128 { numBytes++ i >>= 8 } @@ -478,25 +468,15 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) return nil } -// Marshal serialises val as an ASN.1 structure and writes the result to out. -// In the case of an error, no output is produced. -func Marshal(out io.Writer, val interface{}) os.Error { +// Marshal returns the ASN.1 encoding of val. +func Marshal(val interface{}) ([]byte, os.Error) { + var out bytes.Buffer v := reflect.NewValue(val) f := newForkableWriter() err := marshalField(f, v, fieldParameters{}) if err != nil { - return err - } - _, err = f.writeTo(out) - return err -} - -// MarshalToMemory performs the same actions as Marshal, but returns the result -// as a byte slice. -func MarshalToMemory(val interface{}) ([]byte, os.Error) { - var out bytes.Buffer - if err := Marshal(&out, val); err != nil { return nil, err } + _, err = f.writeTo(&out) return out.Bytes(), nil } diff --git a/src/pkg/asn1/marshal_test.go b/src/pkg/asn1/marshal_test.go index 67878f9bb..85eafc9e4 100644 --- a/src/pkg/asn1/marshal_test.go +++ b/src/pkg/asn1/marshal_test.go @@ -58,40 +58,43 @@ type marshalTest struct { } var marshalTests = []marshalTest{ - marshalTest{10, "02010a"}, - marshalTest{intStruct{64}, "3003020140"}, - marshalTest{twoIntStruct{64, 65}, "3006020140020141"}, - marshalTest{nestedStruct{intStruct{127}}, "3005300302017f"}, - marshalTest{[]byte{1, 2, 3}, "0403010203"}, - marshalTest{implicitTagTest{64}, "3003850140"}, - marshalTest{explicitTagTest{64}, "3005a503020140"}, - marshalTest{time.SecondsToUTC(0), "170d3730303130313030303030305a"}, - marshalTest{time.SecondsToUTC(1258325776), "170d3039313131353232353631365a"}, - marshalTest{setPST(time.SecondsToUTC(1258325776)), "17113039313131353232353631362d30383030"}, - marshalTest{BitString{[]byte{0x80}, 1}, "03020780"}, - marshalTest{BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"}, - marshalTest{ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"}, - marshalTest{ObjectIdentifier([]int{1, 2, 840, 133549, 1, 1, 5}), "06092a864888932d010105"}, - marshalTest{"test", "130474657374"}, - marshalTest{ia5StringTest{"test"}, "3006160474657374"}, - marshalTest{printableStringTest{"test"}, "3006130474657374"}, - marshalTest{printableStringTest{"test*"}, "30071305746573742a"}, - marshalTest{rawContentsStruct{nil, 64}, "3003020140"}, - marshalTest{rawContentsStruct{[]byte{0x30, 3, 1, 2, 3}, 64}, "3003010203"}, - marshalTest{RawValue{Tag: 1, Class: 2, IsCompound: false, Bytes: []byte{1, 2, 3}}, "8103010203"}, - marshalTest{testSET([]int{10}), "310302010a"}, + {10, "02010a"}, + {127, "02017f"}, + {128, "02020080"}, + {-128, "020180"}, + {-129, "0202ff7f"}, + {intStruct{64}, "3003020140"}, + {twoIntStruct{64, 65}, "3006020140020141"}, + {nestedStruct{intStruct{127}}, "3005300302017f"}, + {[]byte{1, 2, 3}, "0403010203"}, + {implicitTagTest{64}, "3003850140"}, + {explicitTagTest{64}, "3005a503020140"}, + {time.SecondsToUTC(0), "170d3730303130313030303030305a"}, + {time.SecondsToUTC(1258325776), "170d3039313131353232353631365a"}, + {setPST(time.SecondsToUTC(1258325776)), "17113039313131353232353631362d30383030"}, + {BitString{[]byte{0x80}, 1}, "03020780"}, + {BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"}, + {ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"}, + {ObjectIdentifier([]int{1, 2, 840, 133549, 1, 1, 5}), "06092a864888932d010105"}, + {"test", "130474657374"}, + {ia5StringTest{"test"}, "3006160474657374"}, + {printableStringTest{"test"}, "3006130474657374"}, + {printableStringTest{"test*"}, "30071305746573742a"}, + {rawContentsStruct{nil, 64}, "3003020140"}, + {rawContentsStruct{[]byte{0x30, 3, 1, 2, 3}, 64}, "3003010203"}, + {RawValue{Tag: 1, Class: 2, IsCompound: false, Bytes: []byte{1, 2, 3}}, "8103010203"}, + {testSET([]int{10}), "310302010a"}, } func TestMarshal(t *testing.T) { for i, test := range marshalTests { - buf := bytes.NewBuffer(nil) - err := Marshal(buf, test.in) + data, err := Marshal(test.in) if err != nil { t.Errorf("#%d failed: %s", i, err) } out, _ := hex.DecodeString(test.out) - if bytes.Compare(out, buf.Bytes()) != 0 { - t.Errorf("#%d got: %x want %x", i, buf.Bytes(), out) + if bytes.Compare(out, data) != 0 { + t.Errorf("#%d got: %x want %x", i, data, out) } } } diff --git a/src/pkg/big/Makefile b/src/pkg/big/Makefile index 7a4311dca..3d4b56d78 100644 --- a/src/pkg/big/Makefile +++ b/src/pkg/big/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=big GOFILES=\ diff --git a/src/pkg/big/arith.go b/src/pkg/big/arith.go index 29966c7bc..df3808f5e 100644 --- a/src/pkg/big/arith.go +++ b/src/pkg/big/arith.go @@ -56,161 +56,29 @@ func subWW_g(x, y, c Word) (z1, z0 Word) { // z1<<_W + z0 = x*y +// Adapted from Warren, Hacker's Delight, p. 132. func mulWW_g(x, y Word) (z1, z0 Word) { - // Split x and y into 2 halfWords each, multiply - // the halfWords separately while avoiding overflow, - // and return the product as 2 Words. - - if x < y { - x, y = y, x - } - - if x < _B2 { - // y < _B2 because y <= x - // sub-digits of x and y are (0, x) and (0, y) - // z = z[0] = x*y - z0 = x * y - return - } - - if y < _B2 { - // sub-digits of x and y are (x1, x0) and (0, y) - // x = (x1*_B2 + x0) - // y = (y1*_B2 + y0) - x1, x0 := x>>_W2, x&_M2 - - // x*y = t2*_B2*_B2 + t1*_B2 + t0 - t0 := x0 * y - t1 := x1 * y - - // compute result digits but avoid overflow - // z = z[1]*_B + z[0] = x*y - z0 = t1<<_W2 + t0 - z1 = (t1 + t0>>_W2) >> _W2 - return - } - - // general case - // sub-digits of x and y are (x1, x0) and (y1, y0) - // x = (x1*_B2 + x0) - // y = (y1*_B2 + y0) - x1, x0 := x>>_W2, x&_M2 - y1, y0 := y>>_W2, y&_M2 - - // x*y = t2*_B2*_B2 + t1*_B2 + t0 - t0 := x0 * y0 - // t1 := x1*y0 + x0*y1; - var c Word - t1 := x1 * y0 - t1a := t1 - t1 += x0 * y1 - if t1 < t1a { - c++ - } - t2 := x1*y1 + c*_B2 - - // compute result digits but avoid overflow - // z = z[1]*_B + z[0] = x*y - // This may overflow, but that's ok because we also sum t1 and t0 above - // and we take care of the overflow there. - z0 = t1<<_W2 + t0 - - // z1 = t2 + (t1 + t0>>_W2)>>_W2; - var c3 Word - z1 = t1 + t0>>_W2 - if z1 < t1 { - c3++ - } - z1 >>= _W2 - z1 += c3 * _B2 - z1 += t2 + x0 := x & _M2 + x1 := x >> _W2 + y0 := y & _M2 + y1 := y >> _W2 + w0 := x0 * y0 + t := x1*y0 + w0>>_W2 + w1 := t & _M2 + w2 := t >> _W2 + w1 += x0 * y1 + z1 = x1*y1 + w2 + w1>>_W2 + z0 = x * y return } // z1<<_W + z0 = x*y + c func mulAddWWW_g(x, y, c Word) (z1, z0 Word) { - // Split x and y into 2 halfWords each, multiply - // the halfWords separately while avoiding overflow, - // and return the product as 2 Words. - - // TODO(gri) Should implement special cases for faster execution. - - // general case - // sub-digits of x, y, and c are (x1, x0), (y1, y0), (c1, c0) - // x = (x1*_B2 + x0) - // y = (y1*_B2 + y0) - x1, x0 := x>>_W2, x&_M2 - y1, y0 := y>>_W2, y&_M2 - c1, c0 := c>>_W2, c&_M2 - - // x*y + c = t2*_B2*_B2 + t1*_B2 + t0 - // (1<<32-1)^2 == 1<<64 - 1<<33 + 1, so there's space to add c0 in here. - t0 := x0*y0 + c0 - - // t1 := x1*y0 + x0*y1 + c1; - var c2 Word // extra carry - t1 := x1*y0 + c1 - t1a := t1 - t1 += x0 * y1 - if t1 < t1a { // If the number got smaller then we overflowed. - c2++ + z1, zz0 := mulWW(x, y) + if z0 = zz0 + c; z0 < zz0 { + z1++ } - - t2 := x1*y1 + c2*_B2 - - // compute result digits but avoid overflow - // z = z[1]*_B + z[0] = x*y - // z0 = t1<<_W2 + t0; - // This may overflow, but that's ok because we also sum t1 and t0 below - // and we take care of the overflow there. - z0 = t1<<_W2 + t0 - - var c3 Word - z1 = t1 + t0>>_W2 - if z1 < t1 { - c3++ - } - z1 >>= _W2 - z1 += t2 + c3*_B2 - - return -} - - -// q = (x1<<_W + x0 - r)/y -// The most significant bit of y must be 1. -func divStep(x1, x0, y Word) (q, r Word) { - d1, d0 := y>>_W2, y&_M2 - q1, r1 := x1/d1, x1%d1 - m := q1 * d0 - r1 = r1*_B2 | x0>>_W2 - if r1 < m { - q1-- - r1 += y - if r1 >= y && r1 < m { - q1-- - r1 += y - } - } - r1 -= m - - r0 := r1 % d1 - q0 := r1 / d1 - m = q0 * d0 - r0 = r0*_B2 | x0&_M2 - if r0 < m { - q0-- - r0 += y - if r0 >= y && r0 < m { - q0-- - r0 += y - } - } - r0 -= m - - q = q1*_B2 | q0 - r = r0 return } @@ -241,46 +109,48 @@ func leadingZeros(x Word) uint { } -// q = (x1<<_W + x0 - r)/y -func divWW_g(x1, x0, y Word) (q, r Word) { - if x1 == 0 { - q, r = x0/y, x0%y - return +// q = (u1<<_W + u0 - r)/y +// Adapted from Warren, Hacker's Delight, p. 152. +func divWW_g(u1, u0, v Word) (q, r Word) { + if u1 >= v { + return 1<<_W - 1, 1<<_W - 1 } - var q0, q1 Word - z := leadingZeros(y) - if y > x1 { - if z != 0 { - y <<= z - x1 = (x1 << z) | (x0 >> (_W - z)) - x0 <<= z - } - q0, x0 = divStep(x1, x0, y) - q1 = 0 - } else { - if z == 0 { - x1 -= y - q1 = 1 - } else { - z1 := _W - z - y <<= z - x2 := x1 >> z1 - x1 = (x1 << z) | (x0 >> z1) - x0 <<= z - q1, x1 = divStep(x2, x1, y) - } + s := leadingZeros(v) + v <<= s + + vn1 := v >> _W2 + vn0 := v & _M2 + un32 := u1<>(_W-s) + un10 := u0 << s + un1 := un10 >> _W2 + un0 := un10 & _M2 + q1 := un32 / vn1 + rhat := un32 - q1*vn1 - q0, x0 = divStep(x1, x0, y) +again1: + if q1 >= _B2 || q1*vn0 > _B2*rhat+un1 { + q1-- + rhat += vn1 + if rhat < _B2 { + goto again1 + } } - r = x0 >> z + un21 := un32*_B2 + un1 - q1*v + q0 := un21 / vn1 + rhat = un21 - q0*vn1 - if q1 != 0 { - panic("div out of range") +again2: + if q0 >= _B2 || q0*vn0 > _B2*rhat+un0 { + q0-- + rhat += vn1 + if rhat < _B2 { + goto again2 + } } - return q0, r + return q1*_B2 + q0, (un21*_B2 + un0 - q0*v) >> s } diff --git a/src/pkg/big/arith_arm.s b/src/pkg/big/arith_arm.s index c8a45efc4..e4a9a962c 100644 --- a/src/pkg/big/arith_arm.s +++ b/src/pkg/big/arith_arm.s @@ -5,31 +5,302 @@ // This file provides fast assembly versions for the elementary // arithmetic operations on vectors implemented in arith.go. -// TODO(gri) Implement these routines. +#define CFLAG 29 // bit position of carry flag + +// func addVV(z, x, y []Word) (c Word) TEXT ·addVV(SB),7,$0 - B ·addVV_g(SB) + MOVW $0, R0 + MOVW z+0(FP), R1 + MOVW x+12(FP), R2 + MOVW y+24(FP), R3 + MOVW n+4(FP), R4 + MOVW R4<<2, R4 + ADD R1, R4 + B E1 +L1: + MOVW.P 4(R2), R5 + MOVW.P 4(R3), R6 + MOVW R0, CPSR + ADC.S R6, R5 + MOVW.P R5, 4(R1) + MOVW CPSR, R0 +E1: + CMP R1, R4 + BNE L1 + + MOVW R0>>CFLAG, R0 + AND $1, R0 + MOVW R0, c+36(FP) + RET + +// func subVV(z, x, y []Word) (c Word) +// (same as addVV except for SBC instead of ADC and label names) TEXT ·subVV(SB),7,$0 - B ·subVV_g(SB) + MOVW $(1<>CFLAG, R0 + AND $1, R0 + EOR $1, R0 + MOVW R0, c+36(FP) + RET + +// func addVW(z, x []Word, y Word) (c Word) TEXT ·addVW(SB),7,$0 - B ·addVW_g(SB) + MOVW z+0(FP), R1 + MOVW x+12(FP), R2 + MOVW y+24(FP), R3 + MOVW n+4(FP), R4 + MOVW R4<<2, R4 + ADD R1, R4 + CMP R1, R4 + BNE L3a + MOVW R3, c+28(FP) + RET +L3a: + MOVW.P 4(R2), R5 + ADD.S R3, R5 + MOVW.P R5, 4(R1) + MOVW CPSR, R0 + B E3 +L3: + MOVW.P 4(R2), R5 + MOVW R0, CPSR + ADC.S $0, R5 + MOVW.P R5, 4(R1) + MOVW CPSR, R0 +E3: + CMP R1, R4 + BNE L3 + + MOVW R0>>CFLAG, R0 + AND $1, R0 + MOVW R0, c+28(FP) + RET + TEXT ·subVW(SB),7,$0 - B ·subVW_g(SB) + MOVW z+0(FP), R1 + MOVW x+12(FP), R2 + MOVW y+24(FP), R3 + MOVW n+4(FP), R4 + MOVW R4<<2, R4 + ADD R1, R4 + CMP R1, R4 + BNE L4a + MOVW R3, c+28(FP) + RET +L4a: + MOVW.P 4(R2), R5 + SUB.S R3, R5 + MOVW.P R5, 4(R1) + MOVW CPSR, R0 + B E4 +L4: + MOVW.P 4(R2), R5 + MOVW R0, CPSR + SBC.S $0, R5 + MOVW.P R5, 4(R1) + MOVW CPSR, R0 +E4: + CMP R1, R4 + BNE L4 + + MOVW R0>>CFLAG, R0 + AND $1, R0 + EOR $1, R0 + MOVW R0, c+28(FP) + RET + +// func shlVW(z, x []Word, s Word) (c Word) TEXT ·shlVW(SB),7,$0 - B ·shlVW_g(SB) + MOVW n+4(FP), R5 + CMP $0, R5 + BEQ X7 + + MOVW z+0(FP), R1 + MOVW x+12(FP), R2 + MOVW R5<<2, R5 + ADD R5, R2 + ADD R1, R5 + MOVW s+24(FP), R3 + CMP $0, R3 // shift 0 is special + BEQ Y7 + ADD $4, R1 // stop one word early + MOVW $32, R4 + SUB R3, R4 + MOVW $0, R7 + + MOVW.W -4(R2), R6 + MOVW R6<>R4, R6 + MOVW R6, c+28(FP) + B E7 + +L7: + MOVW.W -4(R2), R6 + ORR R6>>R4, R7 + MOVW.W R7, -4(R5) + MOVW R6<>R3, R7 + MOVW R6<>R3, R7 +E6: + CMP R1, R5 + BNE L6 + + MOVW R7, 0(R1) + RET + +Y6: // copy loop, because shift 0 == shift 32 + MOVW.P 4(R2), R6 + MOVW.P R6, 4(R1) + CMP R1, R5 + BNE Y6 + +X6: + MOVW $0, R1 + MOVW R1, c+28(FP) + RET + TEXT ·mulAddVWW(SB),7,$0 - B ·mulAddVWW_g(SB) + MOVW $0, R0 + MOVW z+0(FP), R1 + MOVW x+12(FP), R2 + MOVW y+24(FP), R3 + MOVW r+28(FP), R4 + MOVW n+4(FP), R5 + MOVW R5<<2, R5 + ADD R1, R5 + B E8 + + // word loop +L8: + MOVW.P 4(R2), R6 + MULLU R6, R3, (R7, R6) + ADD.S R4, R6 + ADC R0, R7 + MOVW.P R6, 4(R1) + MOVW R7, R4 +E8: + CMP R1, R5 + BNE L8 + + MOVW R4, c+32(FP) + RET + TEXT ·addMulVVW(SB),7,$0 - B ·addMulVVW_g(SB) + MOVW $0, R0 + MOVW z+0(FP), R1 + MOVW x+12(FP), R2 + MOVW y+24(FP), R3 + MOVW n+4(FP), R5 + MOVW R5<<2, R5 + ADD R1, R5 + MOVW $0, R4 + B E9 + + // word loop +L9: + MOVW.P 4(R2), R6 + MULLU R6, R3, (R7, R6) + ADD.S R4, R6 + ADC R0, R7 + MOVW 0(R1), R4 + ADD.S R4, R6 + ADC R0, R7 + MOVW.P R6, 4(R1) + MOVW R7, R4 +E9: + CMP R1, R5 + BNE L9 + + MOVW R4, c+28(FP) + RET + TEXT ·divWVW(SB),7,$0 + // ARM has no multiword division, so use portable code. B ·divWVW_g(SB) + +TEXT ·divWW(SB),7,$0 + // ARM has no multiword division, so use portable code. + B ·divWW_g(SB) + + +// func mulWW(x, y Word) (z1, z0 Word) +TEXT ·mulWW(SB),7,$0 + MOVW x+0(FP), R1 + MOVW y+4(FP), R2 + MULLU R1, R2, (R4, R3) + MOVW R4, z1+8(FP) + MOVW R3, z0+12(FP) + RET diff --git a/src/pkg/big/arith_test.go b/src/pkg/big/arith_test.go index efdb65123..934b302df 100644 --- a/src/pkg/big/arith_test.go +++ b/src/pkg/big/arith_test.go @@ -13,17 +13,17 @@ type argWW struct { } var sumWW = []argWW{ - argWW{0, 0, 0, 0, 0}, - argWW{0, 1, 0, 0, 1}, - argWW{0, 0, 1, 0, 1}, - argWW{0, 1, 1, 0, 2}, - argWW{12345, 67890, 0, 0, 80235}, - argWW{12345, 67890, 1, 0, 80236}, - argWW{_M, 1, 0, 1, 0}, - argWW{_M, 0, 1, 1, 0}, - argWW{_M, 1, 1, 1, 1}, - argWW{_M, _M, 0, 1, _M - 1}, - argWW{_M, _M, 1, 1, _M}, + {0, 0, 0, 0, 0}, + {0, 1, 0, 0, 1}, + {0, 0, 1, 0, 1}, + {0, 1, 1, 0, 2}, + {12345, 67890, 0, 0, 80235}, + {12345, 67890, 1, 0, 80236}, + {_M, 1, 0, 1, 0}, + {_M, 0, 1, 1, 0}, + {_M, 1, 1, 1, 1}, + {_M, _M, 0, 1, _M - 1}, + {_M, _M, 1, 1, _M}, } @@ -59,15 +59,15 @@ type argVV struct { } var sumVV = []argVV{ - argVV{}, - argVV{nat{0}, nat{0}, nat{0}, 0}, - argVV{nat{1}, nat{1}, nat{0}, 0}, - argVV{nat{0}, nat{_M}, nat{1}, 1}, - argVV{nat{80235}, nat{12345}, nat{67890}, 0}, - argVV{nat{_M - 1}, nat{_M}, nat{_M}, 1}, - argVV{nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, nat{1, 0, 0, 0}, 1}, - argVV{nat{0, 0, 0, _M}, nat{_M, _M, _M, _M - 1}, nat{1, 0, 0, 0}, 0}, - argVV{nat{0, 0, 0, 0}, nat{_M, 0, _M, 0}, nat{1, _M, 0, _M}, 1}, + {}, + {nat{0}, nat{0}, nat{0}, 0}, + {nat{1}, nat{1}, nat{0}, 0}, + {nat{0}, nat{_M}, nat{1}, 1}, + {nat{80235}, nat{12345}, nat{67890}, 0}, + {nat{_M - 1}, nat{_M}, nat{_M}, 1}, + {nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, nat{1, 0, 0, 0}, 1}, + {nat{0, 0, 0, _M}, nat{_M, _M, _M, _M - 1}, nat{1, 0, 0, 0}, 0}, + {nat{0, 0, 0, 0}, nat{_M, 0, _M, 0}, nat{1, _M, 0, _M}, 1}, } @@ -115,57 +115,58 @@ type argVW struct { } var sumVW = []argVW{ - argVW{}, - argVW{nat{0}, nat{0}, 0, 0}, - argVW{nat{1}, nat{0}, 1, 0}, - argVW{nat{1}, nat{1}, 0, 0}, - argVW{nat{0}, nat{_M}, 1, 1}, - argVW{nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, 1, 1}, + {}, + {nil, nil, 2, 2}, + {nat{0}, nat{0}, 0, 0}, + {nat{1}, nat{0}, 1, 0}, + {nat{1}, nat{1}, 0, 0}, + {nat{0}, nat{_M}, 1, 1}, + {nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, 1, 1}, } var prodVW = []argVW{ - argVW{}, - argVW{nat{0}, nat{0}, 0, 0}, - argVW{nat{0}, nat{_M}, 0, 0}, - argVW{nat{0}, nat{0}, _M, 0}, - argVW{nat{1}, nat{1}, 1, 0}, - argVW{nat{22793}, nat{991}, 23, 0}, - argVW{nat{0, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 0}, - argVW{nat{0, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 0}, - argVW{nat{0, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 0}, - argVW{nat{_M << 1 & _M}, nat{_M}, 1 << 1, _M >> (_W - 1)}, - argVW{nat{_M << 7 & _M}, nat{_M}, 1 << 7, _M >> (_W - 7)}, - argVW{nat{_M << 7 & _M, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, _M >> (_W - 7)}, + {}, + {nat{0}, nat{0}, 0, 0}, + {nat{0}, nat{_M}, 0, 0}, + {nat{0}, nat{0}, _M, 0}, + {nat{1}, nat{1}, 1, 0}, + {nat{22793}, nat{991}, 23, 0}, + {nat{0, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 0}, + {nat{0, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 0}, + {nat{0, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 0}, + {nat{_M << 1 & _M}, nat{_M}, 1 << 1, _M >> (_W - 1)}, + {nat{_M << 7 & _M}, nat{_M}, 1 << 7, _M >> (_W - 7)}, + {nat{_M << 7 & _M, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, _M >> (_W - 7)}, } var lshVW = []argVW{ - argVW{}, - argVW{nat{0}, nat{0}, 0, 0}, - argVW{nat{0}, nat{0}, 1, 0}, - argVW{nat{0}, nat{0}, 20, 0}, - - argVW{nat{_M}, nat{_M}, 0, 0}, - argVW{nat{_M << 1 & _M}, nat{_M}, 1, 1}, - argVW{nat{_M << 20 & _M}, nat{_M}, 20, _M >> (_W - 20)}, - - argVW{nat{_M, _M, _M}, nat{_M, _M, _M}, 0, 0}, - argVW{nat{_M << 1 & _M, _M, _M}, nat{_M, _M, _M}, 1, 1}, - argVW{nat{_M << 20 & _M, _M, _M}, nat{_M, _M, _M}, 20, _M >> (_W - 20)}, + {}, + {nat{0}, nat{0}, 0, 0}, + {nat{0}, nat{0}, 1, 0}, + {nat{0}, nat{0}, 20, 0}, + + {nat{_M}, nat{_M}, 0, 0}, + {nat{_M << 1 & _M}, nat{_M}, 1, 1}, + {nat{_M << 20 & _M}, nat{_M}, 20, _M >> (_W - 20)}, + + {nat{_M, _M, _M}, nat{_M, _M, _M}, 0, 0}, + {nat{_M << 1 & _M, _M, _M}, nat{_M, _M, _M}, 1, 1}, + {nat{_M << 20 & _M, _M, _M}, nat{_M, _M, _M}, 20, _M >> (_W - 20)}, } var rshVW = []argVW{ - argVW{}, - argVW{nat{0}, nat{0}, 0, 0}, - argVW{nat{0}, nat{0}, 1, 0}, - argVW{nat{0}, nat{0}, 20, 0}, - - argVW{nat{_M}, nat{_M}, 0, 0}, - argVW{nat{_M >> 1}, nat{_M}, 1, _M << (_W - 1) & _M}, - argVW{nat{_M >> 20}, nat{_M}, 20, _M << (_W - 20) & _M}, - - argVW{nat{_M, _M, _M}, nat{_M, _M, _M}, 0, 0}, - argVW{nat{_M, _M, _M >> 1}, nat{_M, _M, _M}, 1, _M << (_W - 1) & _M}, - argVW{nat{_M, _M, _M >> 20}, nat{_M, _M, _M}, 20, _M << (_W - 20) & _M}, + {}, + {nat{0}, nat{0}, 0, 0}, + {nat{0}, nat{0}, 1, 0}, + {nat{0}, nat{0}, 20, 0}, + + {nat{_M}, nat{_M}, 0, 0}, + {nat{_M >> 1}, nat{_M}, 1, _M << (_W - 1) & _M}, + {nat{_M >> 20}, nat{_M}, 20, _M << (_W - 20) & _M}, + + {nat{_M, _M, _M}, nat{_M, _M, _M}, 0, 0}, + {nat{_M, _M, _M >> 1}, nat{_M, _M, _M}, 1, _M << (_W - 1) & _M}, + {nat{_M, _M, _M >> 20}, nat{_M, _M, _M}, 20, _M << (_W - 20) & _M}, } @@ -217,29 +218,29 @@ type argVWW struct { } var prodVWW = []argVWW{ - argVWW{}, - argVWW{nat{0}, nat{0}, 0, 0, 0}, - argVWW{nat{991}, nat{0}, 0, 991, 0}, - argVWW{nat{0}, nat{_M}, 0, 0, 0}, - argVWW{nat{991}, nat{_M}, 0, 991, 0}, - argVWW{nat{0}, nat{0}, _M, 0, 0}, - argVWW{nat{991}, nat{0}, _M, 991, 0}, - argVWW{nat{1}, nat{1}, 1, 0, 0}, - argVWW{nat{992}, nat{1}, 1, 991, 0}, - argVWW{nat{22793}, nat{991}, 23, 0, 0}, - argVWW{nat{22800}, nat{991}, 23, 7, 0}, - argVWW{nat{0, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 0, 0}, - argVWW{nat{7, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 7, 0}, - argVWW{nat{0, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 0, 0}, - argVWW{nat{991, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 991, 0}, - argVWW{nat{0, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 0, 0}, - argVWW{nat{991, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 991, 0}, - argVWW{nat{_M << 1 & _M}, nat{_M}, 1 << 1, 0, _M >> (_W - 1)}, - argVWW{nat{_M<<1&_M + 1}, nat{_M}, 1 << 1, 1, _M >> (_W - 1)}, - argVWW{nat{_M << 7 & _M}, nat{_M}, 1 << 7, 0, _M >> (_W - 7)}, - argVWW{nat{_M<<7&_M + 1<<6}, nat{_M}, 1 << 7, 1 << 6, _M >> (_W - 7)}, - argVWW{nat{_M << 7 & _M, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, 0, _M >> (_W - 7)}, - argVWW{nat{_M<<7&_M + 1<<6, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, 1 << 6, _M >> (_W - 7)}, + {}, + {nat{0}, nat{0}, 0, 0, 0}, + {nat{991}, nat{0}, 0, 991, 0}, + {nat{0}, nat{_M}, 0, 0, 0}, + {nat{991}, nat{_M}, 0, 991, 0}, + {nat{0}, nat{0}, _M, 0, 0}, + {nat{991}, nat{0}, _M, 991, 0}, + {nat{1}, nat{1}, 1, 0, 0}, + {nat{992}, nat{1}, 1, 991, 0}, + {nat{22793}, nat{991}, 23, 0, 0}, + {nat{22800}, nat{991}, 23, 7, 0}, + {nat{0, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 0, 0}, + {nat{7, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 7, 0}, + {nat{0, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 0, 0}, + {nat{991, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 991, 0}, + {nat{0, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 0, 0}, + {nat{991, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 991, 0}, + {nat{_M << 1 & _M}, nat{_M}, 1 << 1, 0, _M >> (_W - 1)}, + {nat{_M<<1&_M + 1}, nat{_M}, 1 << 1, 1, _M >> (_W - 1)}, + {nat{_M << 7 & _M}, nat{_M}, 1 << 7, 0, _M >> (_W - 7)}, + {nat{_M<<7&_M + 1<<6}, nat{_M}, 1 << 7, 1 << 6, _M >> (_W - 7)}, + {nat{_M << 7 & _M, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, 0, _M >> (_W - 7)}, + {nat{_M<<7&_M + 1<<6, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, 1 << 6, _M >> (_W - 7)}, } @@ -300,15 +301,12 @@ func TestFunVWW(t *testing.T) { } -type mulWWTest struct { +var mulWWTests = []struct { x, y Word q, r Word -} - - -var mulWWTests = []mulWWTest{ - mulWWTest{_M, _M, _M - 1, 1}, - // 32 bit only: mulWWTest{0xc47dfa8c, 50911, 0x98a4, 0x998587f4}, +}{ + {_M, _M, _M - 1, 1}, + // 32 bit only: {0xc47dfa8c, 50911, 0x98a4, 0x998587f4}, } @@ -322,18 +320,15 @@ func TestMulWW(t *testing.T) { } -type mulAddWWWTest struct { +var mulAddWWWTests = []struct { x, y, c Word q, r Word -} - - -var mulAddWWWTests = []mulAddWWWTest{ +}{ // TODO(agl): These will only work on 64-bit platforms. - // mulAddWWWTest{15064310297182388543, 0xe7df04d2d35d5d80, 13537600649892366549, 13644450054494335067, 10832252001440893781}, - // mulAddWWWTest{15064310297182388543, 0xdab2f18048baa68d, 13644450054494335067, 12869334219691522700, 14233854684711418382}, - mulAddWWWTest{_M, _M, 0, _M - 1, 1}, - mulAddWWWTest{_M, _M, _M, _M, 0}, + // {15064310297182388543, 0xe7df04d2d35d5d80, 13537600649892366549, 13644450054494335067, 10832252001440893781}, + // {15064310297182388543, 0xdab2f18048baa68d, 13644450054494335067, 12869334219691522700, 14233854684711418382}, + {_M, _M, 0, _M - 1, 1}, + {_M, _M, _M, _M, 0}, } diff --git a/src/pkg/big/int.go b/src/pkg/big/int.go index 873d5b50c..46e008734 100755 --- a/src/pkg/big/int.go +++ b/src/pkg/big/int.go @@ -6,8 +6,10 @@ package big -import "fmt" - +import ( + "fmt" + "rand" +) // An Int represents a signed multi-precision integer. // The zero value for an Int represents the value 0. @@ -20,6 +22,23 @@ type Int struct { var intOne = &Int{false, natOne} +// Sign returns: +// +// -1 if x < 0 +// 0 if x == 0 +// +1 if x > 0 +// +func (x *Int) Sign() int { + if len(x.abs) == 0 { + return 0 + } + if x.neg { + return -1 + } + return 1 +} + + // SetInt64 sets z to x and returns z. func (z *Int) SetInt64(x int64) *Int { neg := false @@ -47,6 +66,14 @@ func (z *Int) Set(x *Int) *Int { } +// Abs sets z to |x| (the absolute value of x) and returns z. +func (z *Int) Abs(x *Int) *Int { + z.abs = z.abs.set(x.abs) + z.neg = false + return z +} + + // Neg sets z to -x and returns z. func (z *Int) Neg(x *Int) *Int { z.abs = z.abs.set(x.abs) @@ -347,10 +374,12 @@ func (z *Int) SetString(s string, base int) (*Int, bool) { return z, false } - neg := false - if s[0] == '-' { - neg = true + neg := s[0] == '-' + if neg || s[0] == '+' { s = s[1:] + if len(s) == 0 { + return z, false + } } var scanned int @@ -518,6 +547,18 @@ func ProbablyPrime(z *Int, n int) bool { } +// Rand sets z to a pseudo-random number in [0, n) and returns z. +func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int { + z.neg = false + if n.neg == true || len(n.abs) == 0 { + z.abs = nil + return z + } + z.abs = z.abs.random(rnd, n.abs, n.abs.bitLen()) + return z +} + + // ModInverse sets z to the multiplicative inverse of g in the group ℤ/pℤ (where // p is a prime) and returns z. func (z *Int) ModInverse(g, p *Int) *Int { @@ -563,7 +604,7 @@ func (z *Int) And(x, y *Int) *Int { if x.neg { // (-x) & (-y) == ^(x-1) & ^(y-1) == ^((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1) x1 := nat{}.sub(x.abs, natOne) - y1 := z.abs.sub(y.abs, natOne) + y1 := nat{}.sub(y.abs, natOne) z.abs = z.abs.add(z.abs.or(x1, y1), natOne) z.neg = true // z cannot be zero if x and y are negative return z @@ -581,7 +622,7 @@ func (z *Int) And(x, y *Int) *Int { } // x & (-y) == x & ^(y-1) == x &^ (y-1) - y1 := z.abs.sub(y.abs, natOne) + y1 := nat{}.sub(y.abs, natOne) z.abs = z.abs.andNot(x.abs, y1) z.neg = false return z @@ -594,7 +635,7 @@ func (z *Int) AndNot(x, y *Int) *Int { if x.neg { // (-x) &^ (-y) == ^(x-1) &^ ^(y-1) == ^(x-1) & (y-1) == (y-1) &^ (x-1) x1 := nat{}.sub(x.abs, natOne) - y1 := z.abs.sub(y.abs, natOne) + y1 := nat{}.sub(y.abs, natOne) z.abs = z.abs.andNot(y1, x1) z.neg = false return z @@ -608,14 +649,14 @@ func (z *Int) AndNot(x, y *Int) *Int { if x.neg { // (-x) &^ y == ^(x-1) &^ y == ^(x-1) & ^y == ^((x-1) | y) == -(((x-1) | y) + 1) - x1 := z.abs.sub(x.abs, natOne) + x1 := nat{}.sub(x.abs, natOne) z.abs = z.abs.add(z.abs.or(x1, y.abs), natOne) z.neg = true // z cannot be zero if x is negative and y is positive return z } // x &^ (-y) == x &^ ^(y-1) == x & (y-1) - y1 := z.abs.add(y.abs, natOne) + y1 := nat{}.add(y.abs, natOne) z.abs = z.abs.and(x.abs, y1) z.neg = false return z @@ -628,7 +669,7 @@ func (z *Int) Or(x, y *Int) *Int { if x.neg { // (-x) | (-y) == ^(x-1) | ^(y-1) == ^((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1) x1 := nat{}.sub(x.abs, natOne) - y1 := z.abs.sub(y.abs, natOne) + y1 := nat{}.sub(y.abs, natOne) z.abs = z.abs.add(z.abs.and(x1, y1), natOne) z.neg = true // z cannot be zero if x and y are negative return z @@ -646,7 +687,7 @@ func (z *Int) Or(x, y *Int) *Int { } // x | (-y) == x | ^(y-1) == ^((y-1) &^ x) == -(^((y-1) &^ x) + 1) - y1 := z.abs.sub(y.abs, natOne) + y1 := nat{}.sub(y.abs, natOne) z.abs = z.abs.add(z.abs.andNot(y1, x.abs), natOne) z.neg = true // z cannot be zero if one of x or y is negative return z @@ -659,7 +700,7 @@ func (z *Int) Xor(x, y *Int) *Int { if x.neg { // (-x) ^ (-y) == ^(x-1) ^ ^(y-1) == (x-1) ^ (y-1) x1 := nat{}.sub(x.abs, natOne) - y1 := z.abs.sub(y.abs, natOne) + y1 := nat{}.sub(y.abs, natOne) z.abs = z.abs.xor(x1, y1) z.neg = false return z @@ -677,7 +718,7 @@ func (z *Int) Xor(x, y *Int) *Int { } // x ^ (-y) == x ^ ^(y-1) == ^(x ^ (y-1)) == -((x ^ (y-1)) + 1) - y1 := z.abs.sub(y.abs, natOne) + y1 := nat{}.sub(y.abs, natOne) z.abs = z.abs.add(z.abs.xor(x.abs, y1), natOne) z.neg = true // z cannot be zero if only one of x or y is negative return z diff --git a/src/pkg/big/int_test.go b/src/pkg/big/int_test.go index 269c814d4..fc981e1da 100755 --- a/src/pkg/big/int_test.go +++ b/src/pkg/big/int_test.go @@ -29,24 +29,36 @@ type argZZ struct { var sumZZ = []argZZ{ - argZZ{NewInt(0), NewInt(0), NewInt(0)}, - argZZ{NewInt(1), NewInt(1), NewInt(0)}, - argZZ{NewInt(1111111110), NewInt(123456789), NewInt(987654321)}, - argZZ{NewInt(-1), NewInt(-1), NewInt(0)}, - argZZ{NewInt(864197532), NewInt(-123456789), NewInt(987654321)}, - argZZ{NewInt(-1111111110), NewInt(-123456789), NewInt(-987654321)}, + {NewInt(0), NewInt(0), NewInt(0)}, + {NewInt(1), NewInt(1), NewInt(0)}, + {NewInt(1111111110), NewInt(123456789), NewInt(987654321)}, + {NewInt(-1), NewInt(-1), NewInt(0)}, + {NewInt(864197532), NewInt(-123456789), NewInt(987654321)}, + {NewInt(-1111111110), NewInt(-123456789), NewInt(-987654321)}, } var prodZZ = []argZZ{ - argZZ{NewInt(0), NewInt(0), NewInt(0)}, - argZZ{NewInt(0), NewInt(1), NewInt(0)}, - argZZ{NewInt(1), NewInt(1), NewInt(1)}, - argZZ{NewInt(-991 * 991), NewInt(991), NewInt(-991)}, + {NewInt(0), NewInt(0), NewInt(0)}, + {NewInt(0), NewInt(1), NewInt(0)}, + {NewInt(1), NewInt(1), NewInt(1)}, + {NewInt(-991 * 991), NewInt(991), NewInt(-991)}, // TODO(gri) add larger products } +func TestSignZ(t *testing.T) { + var zero Int + for _, a := range sumZZ { + s := a.z.Sign() + e := a.z.Cmp(&zero) + if s != e { + t.Errorf("got %d; want %d for z = %v", s, e, a.z) + } + } +} + + func TestSetZ(t *testing.T) { for _, a := range sumZZ { var z Int @@ -61,11 +73,28 @@ func TestSetZ(t *testing.T) { } +func TestAbsZ(t *testing.T) { + var zero Int + for _, a := range sumZZ { + var z Int + z.Abs(a.z) + var e Int + e.Set(a.z) + if e.Cmp(&zero) < 0 { + e.Sub(&zero, &e) + } + if z.Cmp(&e) != 0 { + t.Errorf("got z = %v; want %v", z, e) + } + } +} + + func testFunZZ(t *testing.T, msg string, f funZZ, a argZZ) { var z Int f(&z, a.x, a.y) if !isNormalized(&z) { - t.Errorf("msg: %v is not normalized", z, msg) + t.Errorf("%s%v is not normalized", z, msg) } if (&z).Cmp(a.z) != 0 { t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, &z, a.z) @@ -157,28 +186,25 @@ func TestMul(t *testing.T) { } -type mulRangeZ struct { +var mulRangesZ = []struct { a, b int64 prod string -} - - -var mulRangesZ = []mulRangeZ{ +}{ // entirely positive ranges are covered by mulRangesN - mulRangeZ{-1, 1, "0"}, - mulRangeZ{-2, -1, "2"}, - mulRangeZ{-3, -2, "6"}, - mulRangeZ{-3, -1, "-6"}, - mulRangeZ{1, 3, "6"}, - mulRangeZ{-10, -10, "-10"}, - mulRangeZ{0, -1, "1"}, // empty range - mulRangeZ{-1, -100, "1"}, // empty range - mulRangeZ{-1, 1, "0"}, // range includes 0 - mulRangeZ{-1e9, 0, "0"}, // range includes 0 - mulRangeZ{-1e9, 1e9, "0"}, // range includes 0 - mulRangeZ{-10, -1, "3628800"}, // 10! - mulRangeZ{-20, -2, "-2432902008176640000"}, // -20! - mulRangeZ{-99, -1, + {-1, 1, "0"}, + {-2, -1, "2"}, + {-3, -2, "6"}, + {-3, -1, "-6"}, + {1, 3, "6"}, + {-10, -10, "-10"}, + {0, -1, "1"}, // empty range + {-1, -100, "1"}, // empty range + {-1, 1, "0"}, // range includes 0 + {-1e9, 0, "0"}, // range includes 0 + {-1e9, 1e9, "0"}, // range includes 0 + {-10, -1, "3628800"}, // 10! + {-20, -2, "-2432902008176640000"}, // -20! + {-99, -1, "-933262154439441526816992388562667004907159682643816214685929" + "638952175999932299156089414639761565182862536979208272237582" + "511852109168640000000000000000000000", // -99! @@ -205,50 +231,52 @@ func TestMulRangeZ(t *testing.T) { } -type stringTest struct { +var stringTests = []struct { in string out string base int val int64 ok bool -} - - -var stringTests = []stringTest{ - stringTest{in: "", ok: false}, - stringTest{in: "a", ok: false}, - stringTest{in: "z", ok: false}, - stringTest{in: "+", ok: false}, - stringTest{in: "0b", ok: false}, - stringTest{in: "0x", ok: false}, - stringTest{in: "2", base: 2, ok: false}, - stringTest{in: "0b2", base: 0, ok: false}, - stringTest{in: "08", ok: false}, - stringTest{in: "8", base: 8, ok: false}, - stringTest{in: "0xg", base: 0, ok: false}, - stringTest{in: "g", base: 16, ok: false}, - stringTest{"0", "0", 0, 0, true}, - stringTest{"0", "0", 10, 0, true}, - stringTest{"0", "0", 16, 0, true}, - stringTest{"10", "10", 0, 10, true}, - stringTest{"10", "10", 10, 10, true}, - stringTest{"10", "10", 16, 16, true}, - stringTest{"-10", "-10", 16, -16, true}, - stringTest{"0x10", "16", 0, 16, true}, - stringTest{in: "0x10", base: 16, ok: false}, - stringTest{"-0x10", "-16", 0, -16, true}, - stringTest{"00", "0", 0, 0, true}, - stringTest{"0", "0", 8, 0, true}, - stringTest{"07", "7", 0, 7, true}, - stringTest{"7", "7", 8, 7, true}, - stringTest{"023", "19", 0, 19, true}, - stringTest{"23", "23", 8, 19, true}, - stringTest{"cafebabe", "cafebabe", 16, 0xcafebabe, true}, - stringTest{"0b0", "0", 0, 0, true}, - stringTest{"-111", "-111", 2, -7, true}, - stringTest{"-0b111", "-7", 0, -7, true}, - stringTest{"0b1001010111", "599", 0, 0x257, true}, - stringTest{"1001010111", "1001010111", 2, 0x257, true}, +}{ + {in: "", ok: false}, + {in: "a", ok: false}, + {in: "z", ok: false}, + {in: "+", ok: false}, + {in: "-", ok: false}, + {in: "0b", ok: false}, + {in: "0x", ok: false}, + {in: "2", base: 2, ok: false}, + {in: "0b2", base: 0, ok: false}, + {in: "08", ok: false}, + {in: "8", base: 8, ok: false}, + {in: "0xg", base: 0, ok: false}, + {in: "g", base: 16, ok: false}, + {"0", "0", 0, 0, true}, + {"0", "0", 10, 0, true}, + {"0", "0", 16, 0, true}, + {"+0", "0", 0, 0, true}, + {"-0", "0", 0, 0, true}, + {"10", "10", 0, 10, true}, + {"10", "10", 10, 10, true}, + {"10", "10", 16, 16, true}, + {"-10", "-10", 16, -16, true}, + {"+10", "10", 16, 16, true}, + {"0x10", "16", 0, 16, true}, + {in: "0x10", base: 16, ok: false}, + {"-0x10", "-16", 0, -16, true}, + {"+0x10", "16", 0, 16, true}, + {"00", "0", 0, 0, true}, + {"0", "0", 8, 0, true}, + {"07", "7", 0, 7, true}, + {"7", "7", 8, 7, true}, + {"023", "19", 0, 19, true}, + {"23", "23", 8, 19, true}, + {"cafebabe", "cafebabe", 16, 0xcafebabe, true}, + {"0b0", "0", 0, 0, true}, + {"-111", "-111", 2, -7, true}, + {"-0b111", "-7", 0, -7, true}, + {"0b1001010111", "599", 0, 0x257, true}, + {"1001010111", "1001010111", 2, 0x257, true}, } @@ -276,13 +304,13 @@ func TestGetString(t *testing.T) { if test.base == 10 { s := z.String() if s != test.out { - t.Errorf("#%da got %s; want %s\n", i, s, test.out) + t.Errorf("#%da got %s; want %s", i, s, test.out) } } s := fmt.Sprintf(format(test.base), z) if s != test.out { - t.Errorf("#%db got %s; want %s\n", i, s, test.out) + t.Errorf("#%db got %s; want %s", i, s, test.out) } } } @@ -310,30 +338,27 @@ func TestSetString(t *testing.T) { } if n1.Cmp(expected) != 0 { - t.Errorf("#%d (input '%s') got: %s want: %d\n", i, test.in, n1, test.val) + t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n1, test.val) } if n2.Cmp(expected) != 0 { - t.Errorf("#%d (input '%s') got: %s want: %d\n", i, test.in, n2, test.val) + t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n2, test.val) } } } -type divisionSignsTest struct { +// Examples from the Go Language Spec, section "Arithmetic operators" +var divisionSignsTests = []struct { x, y int64 q, r int64 // T-division d, m int64 // Euclidian division -} - - -// Examples from the Go Language Spec, section "Arithmetic operators" -var divisionSignsTests = []divisionSignsTest{ - divisionSignsTest{5, 3, 1, 2, 1, 2}, - divisionSignsTest{-5, 3, -1, -2, -2, 1}, - divisionSignsTest{5, -3, -1, 2, -1, 2}, - divisionSignsTest{-5, -3, 1, -2, 2, 1}, - divisionSignsTest{1, 2, 0, 1, 0, 1}, - divisionSignsTest{8, 4, 2, 0, 2, 0}, +}{ + {5, 3, 1, 2, 1, 2}, + {-5, 3, -1, -2, -2, 1}, + {5, -3, -1, 2, -1, 2}, + {-5, -3, 1, -2, 2, 1}, + {1, 2, 0, 1, 0, 1}, + {8, 4, 2, 0, 2, 0}, } @@ -454,20 +479,17 @@ func checkQuo(x, y []byte) bool { } -type quoTest struct { +var quoTests = []struct { x, y string q, r string -} - - -var quoTests = []quoTest{ - quoTest{ +}{ + { "476217953993950760840509444250624797097991362735329973741718102894495832294430498335824897858659711275234906400899559094370964723884706254265559534144986498357", "9353930466774385905609975137998169297361893554149986716853295022578535724979483772383667534691121982974895531435241089241440253066816724367338287092081996", "50911", "1", }, - quoTest{ + { "11510768301994997771168", "1328165573307167369775", "8", @@ -517,25 +539,22 @@ func TestQuoStepD6(t *testing.T) { } -type bitLenTest struct { +var bitLenTests = []struct { in string out int -} - - -var bitLenTests = []bitLenTest{ - bitLenTest{"-1", 1}, - bitLenTest{"0", 0}, - bitLenTest{"1", 1}, - bitLenTest{"2", 2}, - bitLenTest{"4", 3}, - bitLenTest{"0xabc", 12}, - bitLenTest{"0x8000", 16}, - bitLenTest{"0x80000000", 32}, - bitLenTest{"0x800000000000", 48}, - bitLenTest{"0x8000000000000000", 64}, - bitLenTest{"0x80000000000000000000", 80}, - bitLenTest{"-0x4000000000000000000000", 87}, +}{ + {"-1", 1}, + {"0", 0}, + {"1", 1}, + {"2", 2}, + {"4", 3}, + {"0xabc", 12}, + {"0x8000", 16}, + {"0x80000000", 32}, + {"0x800000000000", 48}, + {"0x8000000000000000", 64}, + {"0x80000000000000000000", 80}, + {"-0x4000000000000000000000", 87}, } @@ -548,32 +567,29 @@ func TestBitLen(t *testing.T) { } if n := x.BitLen(); n != test.out { - t.Errorf("#%d got %d want %d\n", i, n, test.out) + t.Errorf("#%d got %d want %d", i, n, test.out) } } } -type expTest struct { +var expTests = []struct { x, y, m string out string -} - - -var expTests = []expTest{ - expTest{"5", "0", "", "1"}, - expTest{"-5", "0", "", "-1"}, - expTest{"5", "1", "", "5"}, - expTest{"-5", "1", "", "-5"}, - expTest{"-2", "3", "2", "0"}, - expTest{"5", "2", "", "25"}, - expTest{"1", "65537", "2", "1"}, - expTest{"0x8000000000000000", "2", "", "0x40000000000000000000000000000000"}, - expTest{"0x8000000000000000", "2", "6719", "4944"}, - expTest{"0x8000000000000000", "3", "6719", "5447"}, - expTest{"0x8000000000000000", "1000", "6719", "1603"}, - expTest{"0x8000000000000000", "1000000", "6719", "3199"}, - expTest{ +}{ + {"5", "0", "", "1"}, + {"-5", "0", "", "-1"}, + {"5", "1", "", "5"}, + {"-5", "1", "", "-5"}, + {"-2", "3", "2", "0"}, + {"5", "2", "", "25"}, + {"1", "65537", "2", "1"}, + {"0x8000000000000000", "2", "", "0x40000000000000000000000000000000"}, + {"0x8000000000000000", "2", "6719", "4944"}, + {"0x8000000000000000", "3", "6719", "5447"}, + {"0x8000000000000000", "1000", "6719", "1603"}, + {"0x8000000000000000", "1000000", "6719", "3199"}, + { "2938462938472983472983659726349017249287491026512746239764525612965293865296239471239874193284792387498274256129746192347", "298472983472983471903246121093472394872319615612417471234712061", "29834729834729834729347290846729561262544958723956495615629569234729836259263598127342374289365912465901365498236492183464", @@ -630,14 +646,11 @@ func checkGcd(aBytes, bBytes []byte) bool { } -type gcdTest struct { +var gcdTests = []struct { a, b int64 d, x, y int64 -} - - -var gcdTests = []gcdTest{ - gcdTest{120, 23, 1, -9, 47}, +}{ + {120, 23, 1, -9, 47}, } @@ -726,30 +739,30 @@ type intShiftTest struct { var rshTests = []intShiftTest{ - intShiftTest{"0", 0, "0"}, - intShiftTest{"-0", 0, "0"}, - intShiftTest{"0", 1, "0"}, - intShiftTest{"0", 2, "0"}, - intShiftTest{"1", 0, "1"}, - intShiftTest{"1", 1, "0"}, - intShiftTest{"1", 2, "0"}, - intShiftTest{"2", 0, "2"}, - intShiftTest{"2", 1, "1"}, - intShiftTest{"-1", 0, "-1"}, - intShiftTest{"-1", 1, "-1"}, - intShiftTest{"-1", 10, "-1"}, - intShiftTest{"-100", 2, "-25"}, - intShiftTest{"-100", 3, "-13"}, - intShiftTest{"-100", 100, "-1"}, - intShiftTest{"4294967296", 0, "4294967296"}, - intShiftTest{"4294967296", 1, "2147483648"}, - intShiftTest{"4294967296", 2, "1073741824"}, - intShiftTest{"18446744073709551616", 0, "18446744073709551616"}, - intShiftTest{"18446744073709551616", 1, "9223372036854775808"}, - intShiftTest{"18446744073709551616", 2, "4611686018427387904"}, - intShiftTest{"18446744073709551616", 64, "1"}, - intShiftTest{"340282366920938463463374607431768211456", 64, "18446744073709551616"}, - intShiftTest{"340282366920938463463374607431768211456", 128, "1"}, + {"0", 0, "0"}, + {"-0", 0, "0"}, + {"0", 1, "0"}, + {"0", 2, "0"}, + {"1", 0, "1"}, + {"1", 1, "0"}, + {"1", 2, "0"}, + {"2", 0, "2"}, + {"2", 1, "1"}, + {"-1", 0, "-1"}, + {"-1", 1, "-1"}, + {"-1", 10, "-1"}, + {"-100", 2, "-25"}, + {"-100", 3, "-13"}, + {"-100", 100, "-1"}, + {"4294967296", 0, "4294967296"}, + {"4294967296", 1, "2147483648"}, + {"4294967296", 2, "1073741824"}, + {"18446744073709551616", 0, "18446744073709551616"}, + {"18446744073709551616", 1, "9223372036854775808"}, + {"18446744073709551616", 2, "4611686018427387904"}, + {"18446744073709551616", 64, "1"}, + {"340282366920938463463374607431768211456", 64, "18446744073709551616"}, + {"340282366920938463463374607431768211456", 128, "1"}, } @@ -786,25 +799,25 @@ func TestRshSelf(t *testing.T) { var lshTests = []intShiftTest{ - intShiftTest{"0", 0, "0"}, - intShiftTest{"0", 1, "0"}, - intShiftTest{"0", 2, "0"}, - intShiftTest{"1", 0, "1"}, - intShiftTest{"1", 1, "2"}, - intShiftTest{"1", 2, "4"}, - intShiftTest{"2", 0, "2"}, - intShiftTest{"2", 1, "4"}, - intShiftTest{"2", 2, "8"}, - intShiftTest{"-87", 1, "-174"}, - intShiftTest{"4294967296", 0, "4294967296"}, - intShiftTest{"4294967296", 1, "8589934592"}, - intShiftTest{"4294967296", 2, "17179869184"}, - intShiftTest{"18446744073709551616", 0, "18446744073709551616"}, - intShiftTest{"9223372036854775808", 1, "18446744073709551616"}, - intShiftTest{"4611686018427387904", 2, "18446744073709551616"}, - intShiftTest{"1", 64, "18446744073709551616"}, - intShiftTest{"18446744073709551616", 64, "340282366920938463463374607431768211456"}, - intShiftTest{"1", 128, "340282366920938463463374607431768211456"}, + {"0", 0, "0"}, + {"0", 1, "0"}, + {"0", 2, "0"}, + {"1", 0, "1"}, + {"1", 1, "2"}, + {"1", 2, "4"}, + {"2", 0, "2"}, + {"2", 1, "4"}, + {"2", 2, "8"}, + {"-87", 1, "-174"}, + {"4294967296", 0, "4294967296"}, + {"4294967296", 1, "8589934592"}, + {"4294967296", 2, "17179869184"}, + {"18446744073709551616", 0, "18446744073709551616"}, + {"9223372036854775808", 1, "18446744073709551616"}, + {"4611686018427387904", 2, "18446744073709551616"}, + {"1", 64, "18446744073709551616"}, + {"18446744073709551616", 64, "340282366920938463463374607431768211456"}, + {"1", 128, "340282366920938463463374607431768211456"}, } @@ -894,26 +907,24 @@ func TestInt64(t *testing.T) { } -type bitwiseTest struct { +var bitwiseTests = []struct { x, y string and, or, xor, andNot string -} - -var bitwiseTests = []bitwiseTest{ - bitwiseTest{"0x00", "0x00", "0x00", "0x00", "0x00", "0x00"}, - bitwiseTest{"0x00", "0x01", "0x00", "0x01", "0x01", "0x00"}, - bitwiseTest{"0x01", "0x00", "0x00", "0x01", "0x01", "0x01"}, - bitwiseTest{"-0x01", "0x00", "0x00", "-0x01", "-0x01", "-0x01"}, - bitwiseTest{"-0xAF", "-0x50", "0x00", "-0xFF", "-0x01", "-0x01"}, - bitwiseTest{"0x00", "-0x01", "0x00", "-0x01", "-0x01", "0x00"}, - bitwiseTest{"0x01", "0x01", "0x01", "0x01", "0x00", "0x00"}, - bitwiseTest{"-0x01", "-0x01", "-0x01", "-0x01", "0x00", "0x00"}, - bitwiseTest{"0x07", "0x08", "0x00", "0x0f", "0x0f", "0x07"}, - bitwiseTest{"0x05", "0x0f", "0x05", "0x0f", "0x0a", "0x00"}, - bitwiseTest{"0x013ff6", "0x9a4e", "0x1a46", "0x01bffe", "0x01a5b8", "0x0125b0"}, - bitwiseTest{"-0x013ff6", "0x9a4e", "0x800a", "-0x0125b2", "-0x01a5bc", "-0x01c000"}, - bitwiseTest{"-0x013ff6", "-0x9a4e", "-0x01bffe", "-0x1a46", "0x01a5b8", "0x8008"}, - bitwiseTest{ +}{ + {"0x00", "0x00", "0x00", "0x00", "0x00", "0x00"}, + {"0x00", "0x01", "0x00", "0x01", "0x01", "0x00"}, + {"0x01", "0x00", "0x00", "0x01", "0x01", "0x01"}, + {"-0x01", "0x00", "0x00", "-0x01", "-0x01", "-0x01"}, + {"-0xaf", "-0x50", "-0xf0", "-0x0f", "0xe1", "0x41"}, + {"0x00", "-0x01", "0x00", "-0x01", "-0x01", "0x00"}, + {"0x01", "0x01", "0x01", "0x01", "0x00", "0x00"}, + {"-0x01", "-0x01", "-0x01", "-0x01", "0x00", "0x00"}, + {"0x07", "0x08", "0x00", "0x0f", "0x0f", "0x07"}, + {"0x05", "0x0f", "0x05", "0x0f", "0x0a", "0x00"}, + {"0x013ff6", "0x9a4e", "0x1a46", "0x01bffe", "0x01a5b8", "0x0125b0"}, + {"-0x013ff6", "0x9a4e", "0x800a", "-0x0125b2", "-0x01a5bc", "-0x01c000"}, + {"-0x013ff6", "-0x9a4e", "-0x01bffe", "-0x1a46", "0x01a5b8", "0x8008"}, + { "0x1000009dc6e3d9822cba04129bcbe3401", "0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", "0x1000001186210100001000009048c2001", @@ -921,7 +932,7 @@ var bitwiseTests = []bitwiseTest{ "0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fc", "0x8c40c2d8822caa04120b8321400", }, - bitwiseTest{ + { "0x1000009dc6e3d9822cba04129bcbe3401", "-0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", "0x8c40c2d8822caa04120b8321401", @@ -929,7 +940,7 @@ var bitwiseTests = []bitwiseTest{ "-0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fe", "0x1000001186210100001000009048c2000", }, - bitwiseTest{ + { "-0x1000009dc6e3d9822cba04129bcbe3401", "-0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", "-0xb9bd7d543685789d57cb918e8bfeff7fddb2ebe87dfbbdfe35fd", @@ -944,24 +955,24 @@ type bitFun func(z, x, y *Int) *Int func testBitFun(t *testing.T, msg string, f bitFun, x, y *Int, exp string) { expected := new(Int) - expected.SetString(exp, 16) + expected.SetString(exp, 0) out := f(new(Int), x, y) if out.Cmp(expected) != 0 { - println("Test failed") t.Errorf("%s: got %s want %s", msg, out, expected) } } func testBitFunSelf(t *testing.T, msg string, f bitFun, x, y *Int, exp string) { + self := new(Int) + self.Set(x) expected := new(Int) - expected.SetString(exp, 16) + expected.SetString(exp, 0) - x = f(x, x, y) - if x.Cmp(expected) != 0 { - println("Test failed") - t.Errorf("%s: got %s want %s", msg, x, expected) + self = f(self, self, y) + if self.Cmp(expected) != 0 { + t.Errorf("%s: got %s want %s", msg, self, expected) } } @@ -970,8 +981,8 @@ func TestBitwise(t *testing.T) { x := new(Int) y := new(Int) for _, test := range bitwiseTests { - x.SetString(test.x, 16) - y.SetString(test.y, 16) + x.SetString(test.x, 0) + y.SetString(test.y, 0) testBitFun(t, "and", (*Int).And, x, y, test.and) testBitFunSelf(t, "and", (*Int).And, x, y, test.and) @@ -985,18 +996,16 @@ func TestBitwise(t *testing.T) { } -type notTest struct { +var notTests = []struct { in string out string -} - -var notTests = []notTest{ - notTest{"0", "-1"}, - notTest{"1", "-2"}, - notTest{"7", "-8"}, - notTest{"0", "-1"}, - notTest{"-81910", "81909"}, - notTest{ +}{ + {"0", "-1"}, + {"1", "-2"}, + {"7", "-8"}, + {"0", "-1"}, + {"-81910", "81909"}, + { "298472983472983471903246121093472394872319615612417471234712061", "-298472983472983471903246121093472394872319615612417471234712062", }, @@ -1021,15 +1030,13 @@ func TestNot(t *testing.T) { } -type modInverseTest struct { +var modInverseTests = []struct { element string prime string -} - -var modInverseTests = []modInverseTest{ - modInverseTest{"1", "7"}, - modInverseTest{"1", "13"}, - modInverseTest{"239487239847", "2410312426921032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919"}, +}{ + {"1", "7"}, + {"1", "13"}, + {"239487239847", "2410312426921032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919"}, } func TestModInverse(t *testing.T) { diff --git a/src/pkg/big/nat.go b/src/pkg/big/nat.go index dc2e6be28..a308f69e8 100755 --- a/src/pkg/big/nat.go +++ b/src/pkg/big/nat.go @@ -103,9 +103,7 @@ func (z nat) setUint64(x uint64) nat { func (z nat) set(x nat) nat { z = z.make(len(x)) - for i, d := range x { - z[i] = d - } + copy(z, x) return z } @@ -666,7 +664,7 @@ func (z nat) scan(s string, base int) (nat, int, int) { } } - return z, base, i + return z.norm(), base, i } @@ -818,13 +816,13 @@ func (z nat) or(x, y nat) nat { n, m = m, n s = y } - // n >= m + // m >= n - z = z.make(n) - for i := 0; i < m; i++ { + z = z.make(m) + for i := 0; i < n; i++ { z[i] = x[i] | y[i] } - copy(z[m:n], s[m:n]) + copy(z[n:m], s[n:m]) return z.norm() } @@ -834,17 +832,17 @@ func (z nat) xor(x, y nat) nat { m := len(x) n := len(y) s := x - if n < m { + if m < n { n, m = m, n s = y } - // n >= m + // m >= n - z = z.make(n) - for i := 0; i < m; i++ { + z = z.make(m) + for i := 0; i < n; i++ { z[i] = x[i] ^ y[i] } - copy(z[m:n], s[m:n]) + copy(z[n:m], s[n:m]) return z.norm() } diff --git a/src/pkg/big/nat_test.go b/src/pkg/big/nat_test.go index 8545981c0..0bcb94554 100755 --- a/src/pkg/big/nat_test.go +++ b/src/pkg/big/nat_test.go @@ -6,27 +6,24 @@ package big import "testing" -type cmpTest struct { +var cmpTests = []struct { x, y nat r int -} - - -var cmpTests = []cmpTest{ - cmpTest{nil, nil, 0}, - cmpTest{nil, nat{}, 0}, - cmpTest{nat{}, nil, 0}, - cmpTest{nat{}, nat{}, 0}, - cmpTest{nat{0}, nat{0}, 0}, - cmpTest{nat{0}, nat{1}, -1}, - cmpTest{nat{1}, nat{0}, 1}, - cmpTest{nat{1}, nat{1}, 0}, - cmpTest{nat{0, _M}, nat{1}, 1}, - cmpTest{nat{1}, nat{0, _M}, -1}, - cmpTest{nat{1, _M}, nat{0, _M}, 1}, - cmpTest{nat{0, _M}, nat{1, _M}, -1}, - cmpTest{nat{16, 571956, 8794, 68}, nat{837, 9146, 1, 754489}, -1}, - cmpTest{nat{34986, 41, 105, 1957}, nat{56, 7458, 104, 1957}, 1}, +}{ + {nil, nil, 0}, + {nil, nat{}, 0}, + {nat{}, nil, 0}, + {nat{}, nat{}, 0}, + {nat{0}, nat{0}, 0}, + {nat{0}, nat{1}, -1}, + {nat{1}, nat{0}, 1}, + {nat{1}, nat{1}, 0}, + {nat{0, _M}, nat{1}, 1}, + {nat{1}, nat{0, _M}, -1}, + {nat{1, _M}, nat{0, _M}, 1}, + {nat{0, _M}, nat{1, _M}, -1}, + {nat{16, 571956, 8794, 68}, nat{837, 9146, 1, 754489}, -1}, + {nat{34986, 41, 105, 1957}, nat{56, 7458, 104, 1957}, 1}, } @@ -47,24 +44,24 @@ type argNN struct { var sumNN = []argNN{ - argNN{}, - argNN{nat{1}, nil, nat{1}}, - argNN{nat{1111111110}, nat{123456789}, nat{987654321}}, - argNN{nat{0, 0, 0, 1}, nil, nat{0, 0, 0, 1}}, - argNN{nat{0, 0, 0, 1111111110}, nat{0, 0, 0, 123456789}, nat{0, 0, 0, 987654321}}, - argNN{nat{0, 0, 0, 1}, nat{0, 0, _M}, nat{0, 0, 1}}, + {}, + {nat{1}, nil, nat{1}}, + {nat{1111111110}, nat{123456789}, nat{987654321}}, + {nat{0, 0, 0, 1}, nil, nat{0, 0, 0, 1}}, + {nat{0, 0, 0, 1111111110}, nat{0, 0, 0, 123456789}, nat{0, 0, 0, 987654321}}, + {nat{0, 0, 0, 1}, nat{0, 0, _M}, nat{0, 0, 1}}, } var prodNN = []argNN{ - argNN{}, - argNN{nil, nil, nil}, - argNN{nil, nat{991}, nil}, - argNN{nat{991}, nat{991}, nat{1}}, - argNN{nat{991 * 991}, nat{991}, nat{991}}, - argNN{nat{0, 0, 991 * 991}, nat{0, 991}, nat{0, 991}}, - argNN{nat{1 * 991, 2 * 991, 3 * 991, 4 * 991}, nat{1, 2, 3, 4}, nat{991}}, - argNN{nat{4, 11, 20, 30, 20, 11, 4}, nat{1, 2, 3, 4}, nat{4, 3, 2, 1}}, + {}, + {nil, nil, nil}, + {nil, nat{991}, nil}, + {nat{991}, nat{991}, nat{1}}, + {nat{991 * 991}, nat{991}, nat{991}}, + {nat{0, 0, 991 * 991}, nat{0, 991}, nat{0, 991}}, + {nat{1 * 991, 2 * 991, 3 * 991, 4 * 991}, nat{1, 2, 3, 4}, nat{991}}, + {nat{4, 11, 20, 30, 20, 11, 4}, nat{1, 2, 3, 4}, nat{4, 3, 2, 1}}, } @@ -111,25 +108,22 @@ func TestFunNN(t *testing.T) { } -type mulRangeN struct { +var mulRangesN = []struct { a, b uint64 prod string -} - - -var mulRangesN = []mulRangeN{ - mulRangeN{0, 0, "0"}, - mulRangeN{1, 1, "1"}, - mulRangeN{1, 2, "2"}, - mulRangeN{1, 3, "6"}, - mulRangeN{10, 10, "10"}, - mulRangeN{0, 100, "0"}, - mulRangeN{0, 1e9, "0"}, - mulRangeN{1, 0, "1"}, // empty range - mulRangeN{100, 1, "1"}, // empty range - mulRangeN{1, 10, "3628800"}, // 10! - mulRangeN{1, 20, "2432902008176640000"}, // 20! - mulRangeN{1, 100, +}{ + {0, 0, "0"}, + {1, 1, "1"}, + {1, 2, "2"}, + {1, 3, "6"}, + {10, 10, "10"}, + {0, 100, "0"}, + {0, 1e9, "0"}, + {1, 0, "1"}, // empty range + {100, 1, "1"}, // empty range + {1, 10, "3628800"}, // 10! + {1, 20, "2432902008176640000"}, // 20! + {1, 100, "933262154439441526816992388562667004907159682643816214685929" + "638952175999932299156089414639761565182862536979208272237582" + "51185210916864000000000000000000000000", // 100! @@ -173,18 +167,15 @@ func BenchmarkMul(b *testing.B) { } -type str struct { +var tab = []struct { x nat b int s string -} - - -var tab = []str{ - str{nil, 10, "0"}, - str{nat{1}, 10, "1"}, - str{nat{10}, 10, "10"}, - str{nat{1234567890}, 10, "1234567890"}, +}{ + {nil, 10, "0"}, + {nat{1}, 10, "1"}, + {nat{10}, 10, "10"}, + {nat{1234567890}, 10, "1234567890"}, } @@ -228,12 +219,12 @@ type shiftTest struct { var leftShiftTests = []shiftTest{ - shiftTest{nil, 0, nil}, - shiftTest{nil, 1, nil}, - shiftTest{natOne, 0, natOne}, - shiftTest{natOne, 1, natTwo}, - shiftTest{nat{1 << (_W - 1)}, 1, nat{0}}, - shiftTest{nat{1 << (_W - 1), 0}, 1, nat{0, 1}}, + {nil, 0, nil}, + {nil, 1, nil}, + {natOne, 0, natOne}, + {natOne, 1, natTwo}, + {nat{1 << (_W - 1)}, 1, nat{0}}, + {nat{1 << (_W - 1), 0}, 1, nat{0, 1}}, } @@ -252,13 +243,13 @@ func TestShiftLeft(t *testing.T) { var rightShiftTests = []shiftTest{ - shiftTest{nil, 0, nil}, - shiftTest{nil, 1, nil}, - shiftTest{natOne, 0, natOne}, - shiftTest{natOne, 1, nil}, - shiftTest{natTwo, 1, natOne}, - shiftTest{nat{0, 1}, 1, nat{1 << (_W - 1)}}, - shiftTest{nat{2, 1, 1}, 1, nat{1<<(_W-1) + 1, 1 << (_W - 1)}}, + {nil, 0, nil}, + {nil, 1, nil}, + {natOne, 0, natOne}, + {natOne, 1, nil}, + {natTwo, 1, natOne}, + {nat{0, 1}, 1, nat{1 << (_W - 1)}}, + {nat{2, 1, 1}, 1, nat{1<<(_W-1) + 1, 1 << (_W - 1)}}, } @@ -284,12 +275,12 @@ type modWTest struct { var modWTests32 = []modWTest{ - modWTest{"23492635982634928349238759823742", "252341", "220170"}, + {"23492635982634928349238759823742", "252341", "220170"}, } var modWTests64 = []modWTest{ - modWTest{"6527895462947293856291561095690465243862946", "524326975699234", "375066989628668"}, + {"6527895462947293856291561095690465243862946", "524326975699234", "375066989628668"}, } @@ -301,7 +292,7 @@ func runModWTests(t *testing.T, tests []modWTest) { r := in.abs.modW(d.abs[0]) if r != out.abs[0] { - t.Errorf("#%d failed: got %s want %s\n", i, r, out) + t.Errorf("#%d failed: got %s want %s", i, r, out) } } } @@ -322,26 +313,23 @@ func TestTrailingZeroBits(t *testing.T) { x-- for i := 0; i < _W; i++ { if trailingZeroBits(x) != i { - t.Errorf("Failed at step %d: x: %x got: %d\n", i, x, trailingZeroBits(x)) + t.Errorf("Failed at step %d: x: %x got: %d", i, x, trailingZeroBits(x)) } x <<= 1 } } -type expNNTest struct { +var expNNTests = []struct { x, y, m string out string -} - - -var expNNTests = []expNNTest{ - expNNTest{"0x8000000000000000", "2", "", "0x40000000000000000000000000000000"}, - expNNTest{"0x8000000000000000", "2", "6719", "4944"}, - expNNTest{"0x8000000000000000", "3", "6719", "5447"}, - expNNTest{"0x8000000000000000", "1000", "6719", "1603"}, - expNNTest{"0x8000000000000000", "1000000", "6719", "3199"}, - expNNTest{ +}{ + {"0x8000000000000000", "2", "", "0x40000000000000000000000000000000"}, + {"0x8000000000000000", "2", "6719", "4944"}, + {"0x8000000000000000", "3", "6719", "5447"}, + {"0x8000000000000000", "1000", "6719", "1603"}, + {"0x8000000000000000", "1000000", "6719", "3199"}, + { "2938462938472983472983659726349017249287491026512746239764525612965293865296239471239874193284792387498274256129746192347", "298472983472983471903246121093472394872319615612417471234712061", "29834729834729834729347290846729561262544958723956495615629569234729836259263598127342374289365912465901365498236492183464", diff --git a/src/pkg/big/rat.go b/src/pkg/big/rat.go index ddd858d5c..e70673a1c 100644 --- a/src/pkg/big/rat.go +++ b/src/pkg/big/rat.go @@ -35,9 +35,8 @@ func (z *Rat) SetFrac(a, b *Int) *Rat { func (z *Rat) SetFrac64(a, b int64) *Rat { z.a.SetInt64(a) if b < 0 { - z.b.setUint64(uint64(-b)) + b = -b z.a.neg = !z.a.neg - return z.norm() } z.b = z.b.setUint64(uint64(b)) return z.norm() @@ -60,6 +59,23 @@ func (z *Rat) SetInt64(x int64) *Rat { } +// Sign returns: +// +// -1 if x < 0 +// 0 if x == 0 +// +1 if x > 0 +// +func (x *Rat) Sign() int { + return x.a.Sign() +} + + +// IsInt returns true if the denominator of x is 1. +func (x *Rat) IsInt() bool { + return len(x.b) == 1 && x.b[0] == 1 +} + + // Num returns the numerator of z; it may be <= 0. // The result is a reference to z's numerator; it // may change if a new value is assigned to z. @@ -126,6 +142,14 @@ func (x *Rat) Cmp(y *Rat) (r int) { } +// Abs sets z to |x| (the absolute value of x) and returns z. +func (z *Rat) Abs(x *Rat) *Rat { + z.a.Abs(&x.a) + z.b = z.b.set(x.b) + return z +} + + // Add sets z to the sum x+y and returns z. func (z *Rat) Add(x, y *Rat) *Rat { a1 := mulNat(&x.a, y.b) @@ -186,26 +210,17 @@ func (z *Rat) Set(x *Rat) *Rat { // SetString sets z to the value of s and returns z and a boolean indicating -// success. s can be given as a fraction "a/b" or as a decimal number "a.b". -// If the operation failed, the value of z is undefined. +// success. s can be given as a fraction "a/b" or as a floating-point number +// optionally followed by an exponent. If the operation failed, the value of z +// is undefined. func (z *Rat) SetString(s string) (*Rat, bool) { if len(s) == 0 { return z, false } - // Check for a decimal point - sep := strings.Index(s, ".") - if sep < 0 { - // Check for a quotient - sep = strings.Index(s, "/") - if sep < 0 { - // Just read in the string as an integer - if _, ok := z.a.SetString(s, 10); !ok { - return z, false - } - z.b = z.b.setWord(1) - return z, true - } + // check for a quotient + sep := strings.Index(s, "/") + if sep >= 0 { if _, ok := z.a.SetString(s[0:sep], 10); !ok { return z, false } @@ -214,59 +229,98 @@ func (z *Rat) SetString(s string) (*Rat, bool) { if z.b, _, n = z.b.scan(s, 10); n != len(s) { return z, false } - return z.norm(), true } - s = s[0:sep] + s[sep+1:] + // check for a decimal point + sep = strings.Index(s, ".") + // check for an exponent + e := strings.IndexAny(s, "eE") + var exp Int + if e >= 0 { + if e < sep { + // The E must come after the decimal point. + return z, false + } + if _, ok := exp.SetString(s[e+1:], 10); !ok { + return z, false + } + s = s[0:e] + } + if sep >= 0 { + s = s[0:sep] + s[sep+1:] + exp.Sub(&exp, NewInt(int64(len(s)-sep))) + } + if _, ok := z.a.SetString(s, 10); !ok { return z, false } - z.b = z.b.expNN(natTen, nat{Word(len(s) - sep)}, nil) + powTen := nat{}.expNN(natTen, exp.abs, nil) + if exp.neg { + z.b = powTen + z.norm() + } else { + z.a.abs = z.a.abs.mul(z.a.abs, powTen) + z.b = z.b.setWord(1) + } - return z.norm(), true + return z, true } -// String returns a string representation of z in the form "a/b". +// String returns a string representation of z in the form "a/b" (even if b == 1). func (z *Rat) String() string { - s := z.a.String() - if len(z.b) == 1 && z.b[0] == 1 { - return s + return z.a.String() + "/" + z.b.string(10) +} + + +// RatString returns a string representation of z in the form "a/b" if b != 1, +// and in the form "a" if b == 1. +func (z *Rat) RatString() string { + if z.IsInt() { + return z.a.String() } - return s + "/" + z.b.string(10) + return z.String() } // FloatString returns a string representation of z in decimal form with prec // digits of precision after the decimal point and the last digit rounded. func (z *Rat) FloatString(prec int) string { - q, r := nat{}.div(nat{}, z.a.abs, z.b) - - s := "" - if z.a.neg { - s = "-" + if z.IsInt() { + return z.a.String() } - s += q.string(10) - if len(z.b) == 1 && z.b[0] == 1 { - return s + q, r := nat{}.div(nat{}, z.a.abs, z.b) + + p := natOne + if prec > 0 { + p = nat{}.expNN(natTen, nat{}.setUint64(uint64(prec)), nil) } - p := nat{}.expNN(natTen, nat{Word(prec)}, nil) r = r.mul(r, p) r, r2 := r.div(nat{}, r, z.b) - // See if we need to round up - r2 = r2.mul(r2, natTwo) + // see if we need to round up + r2 = r2.add(r2, r2) if z.b.cmp(r2) <= 0 { r = r.add(r, natOne) + if r.cmp(p) >= 0 { + q = nat{}.add(q, natOne) + r = nat{}.sub(r, p) + } } - rs := r.string(10) - leadingZeros := prec - len(rs) - s += "." + strings.Repeat("0", leadingZeros) + rs - s = strings.TrimRight(s, "0") + s := q.string(10) + if z.a.neg { + s = "-" + s + } + + if prec > 0 { + rs := r.string(10) + leadingZeros := prec - len(rs) + s += "." + strings.Repeat("0", leadingZeros) + rs + } return s } diff --git a/src/pkg/big/rat_test.go b/src/pkg/big/rat_test.go index 2379cc0d5..8f42949b0 100644 --- a/src/pkg/big/rat_test.go +++ b/src/pkg/big/rat_test.go @@ -7,57 +7,71 @@ package big import "testing" -type setStringTest struct { +var setStringTests = []struct { in, out string ok bool -} - -var setStringTests = []setStringTest{ - setStringTest{"0", "0", true}, - setStringTest{"-0", "0", true}, - setStringTest{"1", "1", true}, - setStringTest{"-1", "-1", true}, - setStringTest{in: "r", ok: false}, - setStringTest{in: "a/b", ok: false}, - setStringTest{in: "a.b", ok: false}, - setStringTest{"-0.1", "-1/10", true}, - setStringTest{"-.1", "-1/10", true}, - setStringTest{"2/4", "1/2", true}, - setStringTest{".25", "1/4", true}, - setStringTest{"-1/5", "-1/5", true}, - setStringTest{"884243222337379604041632732738665534", "884243222337379604041632732738665534", true}, - setStringTest{"53/70893980658822810696", "53/70893980658822810696", true}, - setStringTest{"106/141787961317645621392", "53/70893980658822810696", true}, - setStringTest{"204211327800791583.81095", "4084226556015831676219/20000", true}, +}{ + {"0", "0", true}, + {"-0", "0", true}, + {"1", "1", true}, + {"-1", "-1", true}, + {"1.", "1", true}, + {"1e0", "1", true}, + {"1.e1", "10", true}, + {in: "1e", ok: false}, + {in: "1.e", ok: false}, + {in: "1e+14e-5", ok: false}, + {in: "1e4.5", ok: false}, + {in: "r", ok: false}, + {in: "a/b", ok: false}, + {in: "a.b", ok: false}, + {"-0.1", "-1/10", true}, + {"-.1", "-1/10", true}, + {"2/4", "1/2", true}, + {".25", "1/4", true}, + {"-1/5", "-1/5", true}, + {"8129567.7690E14", "812956776900000000000", true}, + {"78189e+4", "781890000", true}, + {"553019.8935e+8", "55301989350000", true}, + {"98765432109876543210987654321e-10", "98765432109876543210987654321/10000000000", true}, + {"9877861857500000E-7", "3951144743/4", true}, + {"2169378.417e-3", "2169378417/1000000", true}, + {"884243222337379604041632732738665534", "884243222337379604041632732738665534", true}, + {"53/70893980658822810696", "53/70893980658822810696", true}, + {"106/141787961317645621392", "53/70893980658822810696", true}, + {"204211327800791583.81095", "4084226556015831676219/20000", true}, } func TestRatSetString(t *testing.T) { for i, test := range setStringTests { x, ok := new(Rat).SetString(test.in) - if ok != test.ok || ok && x.String() != test.out { - t.Errorf("#%d got %s want %s", i, x.String(), test.out) + if ok != test.ok || ok && x.RatString() != test.out { + t.Errorf("#%d got %s want %s", i, x.RatString(), test.out) } } } -type floatStringTest struct { +var floatStringTests = []struct { in string prec int out string -} - -var floatStringTests = []floatStringTest{ - floatStringTest{"0", 0, "0"}, - floatStringTest{"0", 4, "0"}, - floatStringTest{"1", 0, "1"}, - floatStringTest{"1", 2, "1"}, - floatStringTest{"-1", 0, "-1"}, - floatStringTest{".25", 2, "0.25"}, - floatStringTest{".25", 1, "0.3"}, - floatStringTest{"-1/3", 3, "-0.333"}, - floatStringTest{"-2/3", 4, "-0.6667"}, +}{ + {"0", 0, "0"}, + {"0", 4, "0"}, + {"1", 0, "1"}, + {"1", 2, "1"}, + {"-1", 0, "-1"}, + {".25", 2, "0.25"}, + {".25", 1, "0.3"}, + {"-1/3", 3, "-0.333"}, + {"-2/3", 4, "-0.6667"}, + {"0.96", 1, "1.0"}, + {"0.999", 2, "1.00"}, + {"0.9", 0, "1"}, + {".25", -1, "0"}, + {".55", -1, "1"}, } func TestFloatString(t *testing.T) { @@ -71,21 +85,33 @@ func TestFloatString(t *testing.T) { } -type ratCmpTest struct { - rat1, rat2 string - out int +func TestRatSign(t *testing.T) { + zero := NewRat(0, 1) + for _, a := range setStringTests { + var x Rat + x.SetString(a.in) + s := x.Sign() + e := x.Cmp(zero) + if s != e { + t.Errorf("got %d; want %d for z = %v", s, e, &x) + } + } } -var ratCmpTests = []ratCmpTest{ - ratCmpTest{"0", "0/1", 0}, - ratCmpTest{"1/1", "1", 0}, - ratCmpTest{"-1", "-2/2", 0}, - ratCmpTest{"1", "0", 1}, - ratCmpTest{"0/1", "1/1", -1}, - ratCmpTest{"-5/1434770811533343057144", "-5/1434770811533343057145", -1}, - ratCmpTest{"49832350382626108453/8964749413", "49832350382626108454/8964749413", -1}, - ratCmpTest{"-37414950961700930/7204075375675961", "37414950961700930/7204075375675961", -1}, - ratCmpTest{"37414950961700930/7204075375675961", "74829901923401860/14408150751351922", 0}, + +var ratCmpTests = []struct { + rat1, rat2 string + out int +}{ + {"0", "0/1", 0}, + {"1/1", "1", 0}, + {"-1", "-2/2", 0}, + {"1", "0", 1}, + {"0/1", "1/1", -1}, + {"-5/1434770811533343057144", "-5/1434770811533343057145", -1}, + {"49832350382626108453/8964749413", "49832350382626108454/8964749413", -1}, + {"-37414950961700930/7204075375675961", "37414950961700930/7204075375675961", -1}, + {"37414950961700930/7204075375675961", "74829901923401860/14408150751351922", 0}, } func TestRatCmp(t *testing.T) { @@ -101,6 +127,38 @@ func TestRatCmp(t *testing.T) { } +func TestIsInt(t *testing.T) { + one := NewInt(1) + for _, a := range setStringTests { + var x Rat + x.SetString(a.in) + i := x.IsInt() + e := x.Denom().Cmp(one) == 0 + if i != e { + t.Errorf("got %v; want %v for z = %v", i, e, &x) + } + } +} + + +func TestRatAbs(t *testing.T) { + zero := NewRat(0, 1) + for _, a := range setStringTests { + var z Rat + z.SetString(a.in) + var e Rat + e.Set(&z) + if e.Cmp(zero) < 0 { + e.Sub(zero, &e) + } + z.Abs(&z) + if z.Cmp(&e) != 0 { + t.Errorf("got z = %v; want %v", &z, &e) + } + } +} + + type ratBinFun func(z, x, y *Rat) *Rat type ratBinArg struct { x, y, z string @@ -118,30 +176,28 @@ func testRatBin(t *testing.T, i int, name string, f ratBinFun, a ratBinArg) { } -type ratBinTest struct { +var ratBinTests = []struct { x, y string sum, prod string -} - -var ratBinTests = []ratBinTest{ - ratBinTest{"0", "0", "0", "0"}, - ratBinTest{"0", "1", "1", "0"}, - ratBinTest{"-1", "0", "-1", "0"}, - ratBinTest{"-1", "1", "0", "-1"}, - ratBinTest{"1", "1", "2", "1"}, - ratBinTest{"1/2", "1/2", "1", "1/4"}, - ratBinTest{"1/4", "1/3", "7/12", "1/12"}, - ratBinTest{"2/5", "-14/3", "-64/15", "-28/15"}, - ratBinTest{"4707/49292519774798173060", "-3367/70976135186689855734", "84058377121001851123459/1749296273614329067191168098769082663020", "-1760941/388732505247628681598037355282018369560"}, - ratBinTest{"-61204110018146728334/3", "-31052192278051565633/2", "-215564796870448153567/6", "950260896245257153059642991192710872711/3"}, - ratBinTest{"-854857841473707320655/4237645934602118692642972629634714039", "-18/31750379913563777419", "-27/133467566250814981", "15387441146526731771790/134546868362786310073779084329032722548987800600710485341"}, - ratBinTest{"618575745270541348005638912139/19198433543745179392300736", "-19948846211000086/637313996471", "27674141753240653/30123979153216", "-6169936206128396568797607742807090270137721977/6117715203873571641674006593837351328"}, - ratBinTest{"-3/26206484091896184128", "5/2848423294177090248", "15310893822118706237/9330894968229805033368778458685147968", "-5/24882386581946146755650075889827061248"}, - ratBinTest{"26946729/330400702820", "41563965/225583428284", "1238218672302860271/4658307703098666660055", "224002580204097/14906584649915733312176"}, - ratBinTest{"-8259900599013409474/7", "-84829337473700364773/56707961321161574960", "-468402123685491748914621885145127724451/396955729248131024720", "350340947706464153265156004876107029701/198477864624065512360"}, - ratBinTest{"575775209696864/1320203974639986246357", "29/712593081308", "410331716733912717985762465/940768218243776489278275419794956", "808/45524274987585732633"}, - ratBinTest{"1786597389946320496771/2066653520653241", "6269770/1992362624741777", "3559549865190272133656109052308126637/4117523232840525481453983149257", "8967230/3296219033"}, - ratBinTest{"-36459180403360509753/32150500941194292113930", "9381566963714/9633539", "301622077145533298008420642898530153/309723104686531919656937098270", "-3784609207827/3426986245"}, +}{ + {"0", "0", "0", "0"}, + {"0", "1", "1", "0"}, + {"-1", "0", "-1", "0"}, + {"-1", "1", "0", "-1"}, + {"1", "1", "2", "1"}, + {"1/2", "1/2", "1", "1/4"}, + {"1/4", "1/3", "7/12", "1/12"}, + {"2/5", "-14/3", "-64/15", "-28/15"}, + {"4707/49292519774798173060", "-3367/70976135186689855734", "84058377121001851123459/1749296273614329067191168098769082663020", "-1760941/388732505247628681598037355282018369560"}, + {"-61204110018146728334/3", "-31052192278051565633/2", "-215564796870448153567/6", "950260896245257153059642991192710872711/3"}, + {"-854857841473707320655/4237645934602118692642972629634714039", "-18/31750379913563777419", "-27/133467566250814981", "15387441146526731771790/134546868362786310073779084329032722548987800600710485341"}, + {"618575745270541348005638912139/19198433543745179392300736", "-19948846211000086/637313996471", "27674141753240653/30123979153216", "-6169936206128396568797607742807090270137721977/6117715203873571641674006593837351328"}, + {"-3/26206484091896184128", "5/2848423294177090248", "15310893822118706237/9330894968229805033368778458685147968", "-5/24882386581946146755650075889827061248"}, + {"26946729/330400702820", "41563965/225583428284", "1238218672302860271/4658307703098666660055", "224002580204097/14906584649915733312176"}, + {"-8259900599013409474/7", "-84829337473700364773/56707961321161574960", "-468402123685491748914621885145127724451/396955729248131024720", "350340947706464153265156004876107029701/198477864624065512360"}, + {"575775209696864/1320203974639986246357", "29/712593081308", "410331716733912717985762465/940768218243776489278275419794956", "808/45524274987585732633"}, + {"1786597389946320496771/2066653520653241", "6269770/1992362624741777", "3559549865190272133656109052308126637/4117523232840525481453983149257", "8967230/3296219033"}, + {"-36459180403360509753/32150500941194292113930", "9381566963714/9633539", "301622077145533298008420642898530153/309723104686531919656937098270", "-3784609207827/3426986245"}, } func TestRatBin(t *testing.T) { @@ -201,3 +257,26 @@ func TestIssue820(t *testing.T) { t.Errorf("got %s want %s", z, q) } } + + +var setFrac64Tests = []struct { + a, b int64 + out string +}{ + {0, 1, "0"}, + {0, -1, "0"}, + {1, 1, "1"}, + {-1, 1, "-1"}, + {1, -1, "-1"}, + {-1, -1, "1"}, + {-9223372036854775808, -9223372036854775808, "1"}, +} + +func TestRatSetFrac64Rat(t *testing.T) { + for i, test := range setFrac64Tests { + x := new(Rat).SetFrac64(test.a, test.b) + if x.RatString() != test.out { + t.Errorf("#%d got %s want %s", i, x.RatString(), test.out) + } + } +} diff --git a/src/pkg/bufio/Makefile b/src/pkg/bufio/Makefile index 1f5fc349b..85430e8e8 100644 --- a/src/pkg/bufio/Makefile +++ b/src/pkg/bufio/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=bufio GOFILES=\ diff --git a/src/pkg/bufio/bufio.go b/src/pkg/bufio/bufio.go index 6a73c41ef..c13456a63 100644 --- a/src/pkg/bufio/bufio.go +++ b/src/pkg/bufio/bufio.go @@ -27,7 +27,9 @@ type Error struct { var ( ErrInvalidUnreadByte os.Error = &Error{"bufio: invalid use of UnreadByte"} + ErrInvalidUnreadRune os.Error = &Error{"bufio: invalid use of UnreadRune"} ErrBufferFull os.Error = &Error{"bufio: buffer full"} + ErrNegativeCount os.Error = &Error{"bufio: negative count"} errInternal os.Error = &Error{"bufio: internal error"} ) @@ -43,11 +45,12 @@ func (b BufSizeError) String() string { // Reader implements buffering for an io.Reader object. type Reader struct { - buf []byte - rd io.Reader - r, w int - err os.Error - lastbyte int + buf []byte + rd io.Reader + r, w int + err os.Error + lastByte int + lastRuneSize int } // NewReaderSize creates a new Reader whose buffer has the specified size, @@ -66,7 +69,8 @@ func NewReaderSize(rd io.Reader, size int) (*Reader, os.Error) { b = new(Reader) b.buf = make([]byte, size) b.rd = rd - b.lastbyte = -1 + b.lastByte = -1 + b.lastRuneSize = -1 return b, nil } @@ -83,13 +87,11 @@ func NewReader(rd io.Reader) *Reader { // fill reads a new chunk into the buffer. func (b *Reader) fill() { // Slide existing data to beginning. - if b.w > b.r { - copy(b.buf[0:b.w-b.r], b.buf[b.r:b.w]) + if b.r > 0 { + copy(b.buf, b.buf[b.r:b.w]) b.w -= b.r - } else { - b.w = 0 + b.r = 0 } - b.r = 0 // Read new data. n, e := b.rd.Read(b.buf[b.w:]) @@ -99,48 +101,75 @@ func (b *Reader) fill() { } } +// Peek returns the next n bytes without advancing the reader. The bytes stop +// being valid at the next read call. If Peek returns fewer than n bytes, it +// also returns an error explaining why the read is short. The error is +// ErrBufferFull if n is larger than b's buffer size. +func (b *Reader) Peek(n int) ([]byte, os.Error) { + if n < 0 { + return nil, ErrNegativeCount + } + if n > len(b.buf) { + return nil, ErrBufferFull + } + for b.w-b.r < n && b.err == nil { + b.fill() + } + m := b.w - b.r + if m > n { + m = n + } + err := b.err + if m < n && err == nil { + err = ErrBufferFull + } + return b.buf[b.r : b.r+m], err +} + // Read reads data into p. // It returns the number of bytes read into p. -// If nn < len(p), also returns an error explaining -// why the read is short. At EOF, the count will be -// zero and err will be os.EOF. -func (b *Reader) Read(p []byte) (nn int, err os.Error) { - nn = 0 - for len(p) > 0 { - n := len(p) - if b.w == b.r { - if b.err != nil { - return nn, b.err - } - if len(p) >= len(b.buf) { - // Large read, empty buffer. - // Read directly into p to avoid copy. - n, b.err = b.rd.Read(p) - if n > 0 { - b.lastbyte = int(p[n-1]) - } - p = p[n:] - nn += n - continue +// It calls Read at most once on the underlying Reader, +// hence n may be less than len(p). +// At EOF, the count will be zero and err will be os.EOF. +func (b *Reader) Read(p []byte) (n int, err os.Error) { + n = len(p) + if n == 0 { + return 0, b.err + } + if b.w == b.r { + if b.err != nil { + return 0, b.err + } + if len(p) >= len(b.buf) { + // Large read, empty buffer. + // Read directly into p to avoid copy. + n, b.err = b.rd.Read(p) + if n > 0 { + b.lastByte = int(p[n-1]) + b.lastRuneSize = -1 } - b.fill() - continue + return n, b.err } - if n > b.w-b.r { - n = b.w - b.r + b.fill() + if b.w == b.r { + return 0, b.err } - copy(p[0:n], b.buf[b.r:b.r+n]) - p = p[n:] - b.r += n - b.lastbyte = int(b.buf[b.r-1]) - nn += n } - return nn, nil + + if n > b.w-b.r { + n = b.w - b.r + } + copy(p[0:n], b.buf[b.r:]) + b.r += n + b.lastByte = int(b.buf[b.r-1]) + b.lastRuneSize = -1 + return n, nil } // ReadByte reads and returns a single byte. // If no byte is available, returns an error. func (b *Reader) ReadByte() (c byte, err os.Error) { + b.lastRuneSize = -1 for b.w == b.r { if b.err != nil { return 0, b.err @@ -149,24 +178,25 @@ func (b *Reader) ReadByte() (c byte, err os.Error) { } c = b.buf[b.r] b.r++ - b.lastbyte = int(c) + b.lastByte = int(c) return c, nil } // UnreadByte unreads the last byte. Only the most recently read byte can be unread. func (b *Reader) UnreadByte() os.Error { - if b.r == b.w && b.lastbyte >= 0 { + b.lastRuneSize = -1 + if b.r == b.w && b.lastByte >= 0 { b.w = 1 b.r = 0 - b.buf[0] = byte(b.lastbyte) - b.lastbyte = -1 + b.buf[0] = byte(b.lastByte) + b.lastByte = -1 return nil } if b.r <= 0 { return ErrInvalidUnreadByte } b.r-- - b.lastbyte = -1 + b.lastByte = -1 return nil } @@ -176,6 +206,7 @@ func (b *Reader) ReadRune() (rune int, size int, err os.Error) { for b.r+utf8.UTFMax > b.w && !utf8.FullRune(b.buf[b.r:b.w]) && b.err == nil { b.fill() } + b.lastRuneSize = -1 if b.r == b.w { return 0, 0, b.err } @@ -184,10 +215,25 @@ func (b *Reader) ReadRune() (rune int, size int, err os.Error) { rune, size = utf8.DecodeRune(b.buf[b.r:b.w]) } b.r += size - b.lastbyte = int(b.buf[b.r-1]) + b.lastByte = int(b.buf[b.r-1]) + b.lastRuneSize = size return rune, size, nil } +// UnreadRune unreads the last rune. If the most recent read operation on +// the buffer was not a ReadRune, UnreadRune returns an error. (In this +// regard it is stricter than UnreadByte, which will unread the last byte +// from any read operation.) +func (b *Reader) UnreadRune() os.Error { + if b.lastRuneSize < 0 || b.r == 0 { + return ErrInvalidUnreadRune + } + b.r -= b.lastRuneSize + b.lastByte = -1 + b.lastRuneSize = -1 + return nil +} + // Buffered returns the number of bytes that can be read from the current buffer. func (b *Reader) Buffered() int { return b.w - b.r } @@ -237,7 +283,7 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err os.Error) { } // ReadBytes reads until the first occurrence of delim in the input, -// returning a string containing the data up to and including the delimiter. +// returning a slice containing the data up to and including the delimiter. // If ReadBytes encounters an error before finding a delimiter, // it returns the data read before the error and the error itself (often os.EOF). // ReadBytes returns err != nil if and only if line does not end in delim. @@ -246,7 +292,6 @@ func (b *Reader) ReadBytes(delim byte) (line []byte, err os.Error) { // accumulating full buffers. var frag []byte var full [][]byte - nfull := 0 err = nil for { @@ -263,26 +308,12 @@ func (b *Reader) ReadBytes(delim byte) (line []byte, err os.Error) { // Make a copy of the buffer. buf := make([]byte, len(frag)) copy(buf, frag) - - // Grow list if needed. - if full == nil { - full = make([][]byte, 16) - } else if nfull >= len(full) { - newfull := make([][]byte, len(full)*2) - for i := 0; i < len(full); i++ { - newfull[i] = full[i] - } - full = newfull - } - - // Save buffer - full[nfull] = buf - nfull++ + full = append(full, buf) } // Allocate new buffer to hold the full pieces and the fragment. n := 0 - for i := 0; i < nfull; i++ { + for i := range full { n += len(full[i]) } n += len(frag) @@ -290,11 +321,10 @@ func (b *Reader) ReadBytes(delim byte) (line []byte, err os.Error) { // Copy full pieces and fragment in. buf := make([]byte, n) n = 0 - for i := 0; i < nfull; i++ { - copy(buf[n:n+len(full[i])], full[i]) - n += len(full[i]) + for i := range full { + n += copy(buf[n:], full[i]) } - copy(buf[n:n+len(frag)], frag) + copy(buf[n:], frag) return buf, err } @@ -392,7 +422,7 @@ func (b *Writer) Write(p []byte) (nn int, err os.Error) { } n = b.Available() } - if b.Available() == 0 && len(p) >= len(b.buf) { + if b.Buffered() == 0 && len(p) >= len(b.buf) { // Large write, empty buffer. // Write directly from p to avoid copy. n, b.err = b.wr.Write(p) @@ -451,7 +481,7 @@ func (b *Writer) WriteRune(rune int) (size int, err os.Error) { return b.WriteString(string(rune)) } } - size = utf8.EncodeRune(rune, b.buf[b.n:]) + size = utf8.EncodeRune(b.buf[b.n:], rune) b.n += size return size, nil } diff --git a/src/pkg/bufio/bufio_test.go b/src/pkg/bufio/bufio_test.go index 2279fe3b1..059ca6dd2 100644 --- a/src/pkg/bufio/bufio_test.go +++ b/src/pkg/bufio/bufio_test.go @@ -69,7 +69,7 @@ func TestReaderSimple(t *testing.T) { b = NewReader(newRot13Reader(bytes.NewBufferString(data))) if s := readBytes(b); s != "uryyb jbeyq" { - t.Error("rot13 hello world test failed: got %q", s) + t.Errorf("rot13 hello world test failed: got %q", s) } } @@ -80,10 +80,10 @@ type readMaker struct { } var readMakers = []readMaker{ - readMaker{"full", func(r io.Reader) io.Reader { return r }}, - readMaker{"byte", iotest.OneByteReader}, - readMaker{"half", iotest.HalfReader}, - readMaker{"data+err", iotest.DataErrReader}, + {"full", func(r io.Reader) io.Reader { return r }}, + {"byte", iotest.OneByteReader}, + {"half", iotest.HalfReader}, + {"data+err", iotest.DataErrReader}, } // Call ReadString (which ends up calling everything else) @@ -123,14 +123,14 @@ type bufReader struct { } var bufreaders = []bufReader{ - bufReader{"1", func(b *Reader) string { return reads(b, 1) }}, - bufReader{"2", func(b *Reader) string { return reads(b, 2) }}, - bufReader{"3", func(b *Reader) string { return reads(b, 3) }}, - bufReader{"4", func(b *Reader) string { return reads(b, 4) }}, - bufReader{"5", func(b *Reader) string { return reads(b, 5) }}, - bufReader{"7", func(b *Reader) string { return reads(b, 7) }}, - bufReader{"bytes", readBytes}, - bufReader{"lines", readLines}, + {"1", func(b *Reader) string { return reads(b, 1) }}, + {"2", func(b *Reader) string { return reads(b, 2) }}, + {"3", func(b *Reader) string { return reads(b, 3) }}, + {"4", func(b *Reader) string { return reads(b, 4) }}, + {"5", func(b *Reader) string { return reads(b, 5) }}, + {"7", func(b *Reader) string { return reads(b, 7) }}, + {"bytes", readBytes}, + {"lines", readLines}, } var bufsizes = []int{ @@ -179,10 +179,7 @@ type StringReader struct { func (r *StringReader) Read(p []byte) (n int, err os.Error) { if r.step < len(r.data) { s := r.data[r.step] - for i := 0; i < len(s); i++ { - p[i] = s[i] - } - n = len(s) + n = copy(p, s) r.step++ } else { err = os.EOF @@ -210,14 +207,14 @@ func readRuneSegments(t *testing.T, segments []string) { } var segmentList = [][]string{ - []string{}, - []string{""}, - []string{"日", "本語"}, - []string{"\u65e5", "\u672c", "\u8a9e"}, - []string{"\U000065e5", "\U0000672c", "\U00008a9e"}, - []string{"\xe6", "\x97\xa5\xe6", "\x9c\xac\xe8\xaa\x9e"}, - []string{"Hello", ", ", "World", "!"}, - []string{"Hello", ", ", "", "World", "!"}, + {}, + {""}, + {"日", "本語"}, + {"\u65e5", "\u672c", "\u8a9e"}, + {"\U000065e5", "\U0000672c", "\U00008a9e"}, + {"\xe6", "\x97\xa5\xe6", "\x9c\xac\xe8\xaa\x9e"}, + {"Hello", ", ", "World", "!"}, + {"Hello", ", ", "", "World", "!"}, } func TestReadRune(t *testing.T) { @@ -226,6 +223,113 @@ func TestReadRune(t *testing.T) { } } +func TestUnreadRune(t *testing.T) { + got := "" + segments := []string{"Hello, world:", "日本語"} + data := strings.Join(segments, "") + r := NewReader(&StringReader{data: segments}) + // Normal execution. + for { + rune, _, err := r.ReadRune() + if err != nil { + if err != os.EOF { + t.Error("unexpected EOF") + } + break + } + got += string(rune) + // Put it back and read it again + if err = r.UnreadRune(); err != nil { + t.Error("unexpected error on UnreadRune:", err) + } + rune1, _, err := r.ReadRune() + if err != nil { + t.Error("unexpected error reading after unreading:", err) + } + if rune != rune1 { + t.Errorf("incorrect rune after unread: got %c wanted %c", rune1, rune) + } + } + if got != data { + t.Errorf("want=%q got=%q", data, got) + } +} + +// Test that UnreadRune fails if the preceding operation was not a ReadRune. +func TestUnreadRuneError(t *testing.T) { + buf := make([]byte, 3) // All runes in this test are 3 bytes long + r := NewReader(&StringReader{data: []string{"日本語日本語日本語"}}) + if r.UnreadRune() == nil { + t.Error("expected error on UnreadRune from fresh buffer") + } + _, _, err := r.ReadRune() + if err != nil { + t.Error("unexpected error on ReadRune (1):", err) + } + if err = r.UnreadRune(); err != nil { + t.Error("unexpected error on UnreadRune (1):", err) + } + if r.UnreadRune() == nil { + t.Error("expected error after UnreadRune (1)") + } + // Test error after Read. + _, _, err = r.ReadRune() // reset state + if err != nil { + t.Error("unexpected error on ReadRune (2):", err) + } + _, err = r.Read(buf) + if err != nil { + t.Error("unexpected error on Read (2):", err) + } + if r.UnreadRune() == nil { + t.Error("expected error after Read (2)") + } + // Test error after ReadByte. + _, _, err = r.ReadRune() // reset state + if err != nil { + t.Error("unexpected error on ReadRune (2):", err) + } + for _ = range buf { + _, err = r.ReadByte() + if err != nil { + t.Error("unexpected error on ReadByte (2):", err) + } + } + if r.UnreadRune() == nil { + t.Error("expected error after ReadByte") + } + // Test error after UnreadByte. + _, _, err = r.ReadRune() // reset state + if err != nil { + t.Error("unexpected error on ReadRune (3):", err) + } + _, err = r.ReadByte() + if err != nil { + t.Error("unexpected error on ReadByte (3):", err) + } + err = r.UnreadByte() + if err != nil { + t.Error("unexpected error on UnreadByte (3):", err) + } + if r.UnreadRune() == nil { + t.Error("expected error after UnreadByte (3)") + } +} + +func TestUnreadRuneAtEOF(t *testing.T) { + // UnreadRune/ReadRune should error at EOF (was a bug; used to panic) + r := NewReader(strings.NewReader("x")) + r.ReadRune() + r.ReadRune() + r.UnreadRune() + _, _, err := r.ReadRune() + if err == nil { + t.Error("expected error at EOF") + } else if err != os.EOF { + t.Error("expected EOF; got", err) + } +} + func TestReadWriteRune(t *testing.T) { const NRune = 1000 byteBuf := new(bytes.Buffer) @@ -233,7 +337,7 @@ func TestReadWriteRune(t *testing.T) { // Write the runes out using WriteRune buf := make([]byte, utf8.UTFMax) for rune := 0; rune < NRune; rune++ { - size := utf8.EncodeRune(rune, buf) + size := utf8.EncodeRune(buf, rune) nbytes, err := w.WriteRune(rune) if err != nil { t.Fatalf("WriteRune(0x%x) error: %s", rune, err) @@ -247,7 +351,7 @@ func TestReadWriteRune(t *testing.T) { r := NewReader(byteBuf) // Read them back with ReadRune for rune := 0; rune < NRune; rune++ { - size := utf8.EncodeRune(rune, buf) + size := utf8.EncodeRune(buf, rune) nr, nbytes, err := r.ReadRune() if nr != rune || nbytes != size || err != nil { t.Fatalf("ReadRune(0x%x) got 0x%x,%d not 0x%x,%d (err=%s)", r, nr, nbytes, r, size, err) @@ -293,9 +397,9 @@ func TestWriter(t *testing.T) { } for l := 0; l < len(written); l++ { if written[i] != data[i] { - t.Errorf("%s: wrong bytes written") - t.Errorf("want=%s", data[0:len(written)]) - t.Errorf("have=%s", written) + t.Errorf("wrong bytes written") + t.Errorf("want=%q", data[0:len(written)]) + t.Errorf("have=%q", written) } } } @@ -315,12 +419,12 @@ func (w errorWriterTest) Write(p []byte) (int, os.Error) { } var errorWriterTests = []errorWriterTest{ - errorWriterTest{0, 1, nil, io.ErrShortWrite}, - errorWriterTest{1, 2, nil, io.ErrShortWrite}, - errorWriterTest{1, 1, nil, nil}, - errorWriterTest{0, 1, os.EPIPE, os.EPIPE}, - errorWriterTest{1, 2, os.EPIPE, os.EPIPE}, - errorWriterTest{1, 1, os.EPIPE, os.EPIPE}, + {0, 1, nil, io.ErrShortWrite}, + {1, 2, nil, io.ErrShortWrite}, + {1, 1, nil, nil}, + {0, 1, os.EPIPE, os.EPIPE}, + {1, 2, os.EPIPE, os.EPIPE}, + {1, 1, os.EPIPE, os.EPIPE}, } func TestWriteErrors(t *testing.T) { @@ -419,3 +523,50 @@ func TestBufferFull(t *testing.T) { t.Errorf("second ReadSlice(,) = %q, %v", line, err) } } + +func TestPeek(t *testing.T) { + p := make([]byte, 10) + buf, _ := NewReaderSize(strings.NewReader("abcdefghij"), 4) + if s, err := buf.Peek(1); string(s) != "a" || err != nil { + t.Fatalf("want %q got %q, err=%v", "a", string(s), err) + } + if s, err := buf.Peek(4); string(s) != "abcd" || err != nil { + t.Fatalf("want %q got %q, err=%v", "abcd", string(s), err) + } + if _, err := buf.Peek(5); err != ErrBufferFull { + t.Fatalf("want ErrBufFull got %v", err) + } + if _, err := buf.Read(p[0:3]); string(p[0:3]) != "abc" || err != nil { + t.Fatalf("want %q got %q, err=%v", "abc", string(p[0:3]), err) + } + if s, err := buf.Peek(1); string(s) != "d" || err != nil { + t.Fatalf("want %q got %q, err=%v", "d", string(s), err) + } + if s, err := buf.Peek(2); string(s) != "de" || err != nil { + t.Fatalf("want %q got %q, err=%v", "de", string(s), err) + } + if _, err := buf.Read(p[0:3]); string(p[0:3]) != "def" || err != nil { + t.Fatalf("want %q got %q, err=%v", "def", string(p[0:3]), err) + } + if s, err := buf.Peek(4); string(s) != "ghij" || err != nil { + t.Fatalf("want %q got %q, err=%v", "ghij", string(s), err) + } + if _, err := buf.Read(p[0:4]); string(p[0:4]) != "ghij" || err != nil { + t.Fatalf("want %q got %q, err=%v", "ghij", string(p[0:3]), err) + } + if s, err := buf.Peek(0); string(s) != "" || err != nil { + t.Fatalf("want %q got %q, err=%v", "", string(s), err) + } + if _, err := buf.Peek(1); err != os.EOF { + t.Fatalf("want EOF got %v", err) + } +} + +func TestPeekThenUnreadRune(t *testing.T) { + // This sequence used to cause a crash. + r := NewReader(strings.NewReader("x")) + r.ReadRune() + r.Peek(1) + r.UnreadRune() + r.ReadRune() // Used to panic here +} diff --git a/src/pkg/bytes/Makefile b/src/pkg/bytes/Makefile index d50e624d6..03395c7a4 100644 --- a/src/pkg/bytes/Makefile +++ b/src/pkg/bytes/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=bytes GOFILES=\ diff --git a/src/pkg/bytes/asm_amd64.s b/src/pkg/bytes/asm_amd64.s index 7e78700ec..c6793cbdc 100644 --- a/src/pkg/bytes/asm_amd64.s +++ b/src/pkg/bytes/asm_amd64.s @@ -3,15 +3,90 @@ // license that can be found in the LICENSE file. TEXT ·IndexByte(SB),7,$0 - MOVQ p+0(FP), SI - MOVL len+8(FP), CX - MOVB b+16(FP), AL - MOVQ SI, DI + MOVQ p+0(FP), SI + MOVL len+8(FP), BX + MOVB b+16(FP), AL + MOVQ SI, DI + + CMPL BX, $16 + JLT small + + // round up to first 16-byte boundary + TESTQ $15, SI + JZ aligned + MOVQ SI, CX + ANDQ $~15, CX + ADDQ $16, CX + + // search the beginning + SUBQ SI, CX + REPN; SCASB + JZ success + +// DI is 16-byte aligned; get ready to search using SSE instructions +aligned: + // round down to last 16-byte boundary + MOVQ BX, R11 + ADDQ SI, R11 + ANDQ $~15, R11 + + // shuffle X0 around so that each byte contains c + MOVD AX, X0 + PUNPCKLBW X0, X0 + PUNPCKLBW X0, X0 + PSHUFL $0, X0, X0 + JMP condition + +sse: + // move the next 16-byte chunk of the buffer into X1 + MOVO (DI), X1 + // compare bytes in X0 to X1 + PCMPEQB X0, X1 + // take the top bit of each byte in X1 and put the result in DX + PMOVMSKB X1, DX + TESTL DX, DX + JNZ ssesuccess + ADDQ $16, DI + +condition: + CMPQ DI, R11 + JLT sse + + // search the end + MOVQ SI, CX + ADDQ BX, CX + SUBQ R11, CX + // if CX == 0, the zero flag will be set and we'll end up + // returning a false success + JZ failure REPN; SCASB - JZ 3(PC) - MOVL $-1, ret+24(FP) + JZ success + +failure: + MOVL $-1, ret+24(FP) + RET + +// handle for lengths < 16 +small: + MOVL BX, CX + REPN; SCASB + JZ success + MOVL $-1, ret+24(FP) RET - SUBQ SI, DI - SUBL $1, DI - MOVL DI, ret+24(FP) + +// we've found the chunk containing the byte +// now just figure out which specific byte it is +ssesuccess: + // get the index of the least significant set bit + BSFW DX, DX + SUBQ SI, DI + ADDQ DI, DX + MOVL DX, ret+24(FP) + RET + +success: + SUBQ SI, DI + SUBL $1, DI + MOVL DI, ret+24(FP) RET + diff --git a/src/pkg/bytes/buffer.go b/src/pkg/bytes/buffer.go index 01e6aef67..2574b4f43 100644 --- a/src/pkg/bytes/buffer.go +++ b/src/pkg/bytes/buffer.go @@ -12,14 +12,6 @@ import ( "utf8" ) -// Copy from string to byte array at offset doff. Assume there's room. -func copyString(dst []byte, doff int, str string) { - for soff := 0; soff < len(str); soff++ { - dst[doff] = str[soff] - doff++ - } -} - // A Buffer is a variable-sized buffer of bytes with Read and Write methods. // The zero value for Buffer is an empty buffer ready to use. type Buffer struct { @@ -27,8 +19,20 @@ type Buffer struct { off int // read at &buf[off], write at &buf[len(buf)] runeBytes [utf8.UTFMax]byte // avoid allocation of slice on each WriteByte or Rune bootstrap [64]byte // memory to hold first slice; helps small buffers (Printf) avoid allocation. + lastRead readOp // last read operation, so that Unread* can work correctly. } +// The readOp constants describe the last action performed on +// the buffer, so that UnreadRune and UnreadByte can +// check for invalid usage. +type readOp int + +const ( + opInvalid readOp = iota // Non-read operation. + opReadRune // Read rune. + opRead // Any other read operation. +) + // Bytes returns a slice of the contents of the unread portion of the buffer; // len(b.Bytes()) == b.Len(). If the caller changes the contents of the // returned slice, the contents of the buffer will change provided there @@ -52,6 +56,7 @@ func (b *Buffer) Len() int { return len(b.buf) - b.off } // Truncate discards all but the first n unread bytes from the buffer. // It is an error to call b.Truncate(n) with n > b.Len(). func (b *Buffer) Truncate(n int) { + b.lastRead = opInvalid if n == 0 { // Reuse buffer space. b.off = 0 @@ -90,6 +95,7 @@ func (b *Buffer) grow(n int) int { // Write appends the contents of p to the buffer. The return // value n is the length of p; err is always nil. func (b *Buffer) Write(p []byte) (n int, err os.Error) { + b.lastRead = opInvalid m := b.grow(len(p)) copy(b.buf[m:], p) return len(p), nil @@ -98,9 +104,9 @@ func (b *Buffer) Write(p []byte) (n int, err os.Error) { // WriteString appends the contents of s to the buffer. The return // value n is the length of s; err is always nil. func (b *Buffer) WriteString(s string) (n int, err os.Error) { + b.lastRead = opInvalid m := b.grow(len(s)) - copyString(b.buf, m, s) - return len(s), nil + return copy(b.buf[m:], s), nil } // MinRead is the minimum slice size passed to a Read call by @@ -114,6 +120,7 @@ const MinRead = 512 // Any error except os.EOF encountered during the read // is also returned. func (b *Buffer) ReadFrom(r io.Reader) (n int64, err os.Error) { + b.lastRead = opInvalid // If buffer is empty, reset to recover space. if b.off >= len(b.buf) { b.Truncate(0) @@ -150,6 +157,7 @@ func (b *Buffer) ReadFrom(r io.Reader) (n int64, err os.Error) { // occurs. The return value n is the number of bytes written. // Any error encountered during the write is also returned. func (b *Buffer) WriteTo(w io.Writer) (n int64, err os.Error) { + b.lastRead = opInvalid for b.off < len(b.buf) { m, e := w.Write(b.buf[b.off:]) n += int64(m) @@ -167,6 +175,7 @@ func (b *Buffer) WriteTo(w io.Writer) (n int64, err os.Error) { // The returned error is always nil, but is included // to match bufio.Writer's WriteByte. func (b *Buffer) WriteByte(c byte) os.Error { + b.lastRead = opInvalid m := b.grow(1) b.buf[m] = c return nil @@ -181,7 +190,7 @@ func (b *Buffer) WriteRune(r int) (n int, err os.Error) { b.WriteByte(byte(r)) return 1, nil } - n = utf8.EncodeRune(r, b.runeBytes[0:]) + n = utf8.EncodeRune(b.runeBytes[0:], r) b.Write(b.runeBytes[0:n]) return n, nil } @@ -191,6 +200,7 @@ func (b *Buffer) WriteRune(r int) (n int, err os.Error) { // buffer has no data to return, err is os.EOF even if len(p) is zero; // otherwise it is nil. func (b *Buffer) Read(p []byte) (n int, err os.Error) { + b.lastRead = opInvalid if b.off >= len(b.buf) { // Buffer is empty, reset to recover space. b.Truncate(0) @@ -198,6 +208,9 @@ func (b *Buffer) Read(p []byte) (n int, err os.Error) { } n = copy(p, b.buf[b.off:]) b.off += n + if n > 0 { + b.lastRead = opRead + } return } @@ -206,18 +219,23 @@ func (b *Buffer) Read(p []byte) (n int, err os.Error) { // If there are fewer than n bytes in the buffer, Next returns the entire buffer. // The slice is only valid until the next call to a read or write method. func (b *Buffer) Next(n int) []byte { + b.lastRead = opInvalid m := b.Len() if n > m { n = m } data := b.buf[b.off : b.off+n] b.off += n + if n > 0 { + b.lastRead = opRead + } return data } // ReadByte reads and returns the next byte from the buffer. // If no byte is available, it returns error os.EOF. func (b *Buffer) ReadByte() (c byte, err os.Error) { + b.lastRead = opInvalid if b.off >= len(b.buf) { // Buffer is empty, reset to recover space. b.Truncate(0) @@ -225,6 +243,7 @@ func (b *Buffer) ReadByte() (c byte, err os.Error) { } c = b.buf[b.off] b.off++ + b.lastRead = opRead return c, nil } @@ -234,11 +253,13 @@ func (b *Buffer) ReadByte() (c byte, err os.Error) { // If the bytes are an erroneous UTF-8 encoding, it // consumes one byte and returns U+FFFD, 1. func (b *Buffer) ReadRune() (r int, size int, err os.Error) { + b.lastRead = opInvalid if b.off >= len(b.buf) { // Buffer is empty, reset to recover space. b.Truncate(0) return 0, 0, os.EOF } + b.lastRead = opReadRune c := b.buf[b.off] if c < utf8.RuneSelf { b.off++ @@ -249,6 +270,37 @@ func (b *Buffer) ReadRune() (r int, size int, err os.Error) { return r, n, nil } +// UnreadRune unreads the last rune returned by ReadRune. +// If the most recent read or write operation on the buffer was +// not a ReadRune, UnreadRune returns an error. (In this regard +// it is stricter than UnreadByte, which will unread the last byte +// from any read operation.) +func (b *Buffer) UnreadRune() os.Error { + if b.lastRead != opReadRune { + return os.ErrorString("bytes.Buffer: UnreadRune: previous operation was not ReadRune") + } + b.lastRead = opInvalid + if b.off > 0 { + _, n := utf8.DecodeLastRune(b.buf[0:b.off]) + b.off -= n + } + return nil +} + +// UnreadByte unreads the last byte returned by the most recent +// read operation. If write has happened since the last read, UnreadByte +// returns an error. +func (b *Buffer) UnreadByte() os.Error { + if b.lastRead != opReadRune && b.lastRead != opRead { + return os.ErrorString("bytes.Buffer: UnreadByte: previous operation was not a read") + } + b.lastRead = opInvalid + if b.off > 0 { + b.off-- + } + return nil +} + // NewBuffer creates and initializes a new Buffer using buf as its initial // contents. It is intended to prepare a Buffer to read existing data. It // can also be used to to size the internal buffer for writing. To do that, @@ -259,7 +311,5 @@ func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} } // initial contents. It is intended to prepare a buffer to read an existing // string. func NewBufferString(s string) *Buffer { - buf := make([]byte, len(s)) - copyString(buf, 0, s) - return &Buffer{buf: buf} + return &Buffer{buf: []byte(s)} } diff --git a/src/pkg/bytes/buffer_test.go b/src/pkg/bytes/buffer_test.go index bc696f4b5..509793d24 100644 --- a/src/pkg/bytes/buffer_test.go +++ b/src/pkg/bytes/buffer_test.go @@ -30,19 +30,19 @@ func check(t *testing.T, testname string, buf *Buffer, s string) { bytes := buf.Bytes() str := buf.String() if buf.Len() != len(bytes) { - t.Errorf("%s: buf.Len() == %d, len(buf.Bytes()) == %d\n", testname, buf.Len(), len(bytes)) + t.Errorf("%s: buf.Len() == %d, len(buf.Bytes()) == %d", testname, buf.Len(), len(bytes)) } if buf.Len() != len(str) { - t.Errorf("%s: buf.Len() == %d, len(buf.String()) == %d\n", testname, buf.Len(), len(str)) + t.Errorf("%s: buf.Len() == %d, len(buf.String()) == %d", testname, buf.Len(), len(str)) } if buf.Len() != len(s) { - t.Errorf("%s: buf.Len() == %d, len(s) == %d\n", testname, buf.Len(), len(s)) + t.Errorf("%s: buf.Len() == %d, len(s) == %d", testname, buf.Len(), len(s)) } if string(bytes) != s { - t.Errorf("%s: string(buf.Bytes()) == %q, s == %q\n", testname, string(bytes), s) + t.Errorf("%s: string(buf.Bytes()) == %q, s == %q", testname, string(bytes), s) } } @@ -55,10 +55,10 @@ func fillString(t *testing.T, testname string, buf *Buffer, s string, n int, fus for ; n > 0; n-- { m, err := buf.WriteString(fus) if m != len(fus) { - t.Errorf(testname+" (fill 2): m == %d, expected %d\n", m, len(fus)) + t.Errorf(testname+" (fill 2): m == %d, expected %d", m, len(fus)) } if err != nil { - t.Errorf(testname+" (fill 3): err should always be nil, found err == %s\n", err) + t.Errorf(testname+" (fill 3): err should always be nil, found err == %s", err) } s += fus check(t, testname+" (fill 4)", buf, s) @@ -75,10 +75,10 @@ func fillBytes(t *testing.T, testname string, buf *Buffer, s string, n int, fub for ; n > 0; n-- { m, err := buf.Write(fub) if m != len(fub) { - t.Errorf(testname+" (fill 2): m == %d, expected %d\n", m, len(fub)) + t.Errorf(testname+" (fill 2): m == %d, expected %d", m, len(fub)) } if err != nil { - t.Errorf(testname+" (fill 3): err should always be nil, found err == %s\n", err) + t.Errorf(testname+" (fill 3): err should always be nil, found err == %s", err) } s += string(fub) check(t, testname+" (fill 4)", buf, s) @@ -110,7 +110,7 @@ func empty(t *testing.T, testname string, buf *Buffer, s string, fub []byte) { break } if err != nil { - t.Errorf(testname+" (empty 2): err should always be nil, found err == %s\n", err) + t.Errorf(testname+" (empty 2): err should always be nil, found err == %s", err) } s = s[n:] check(t, testname+" (empty 3)", buf, s) @@ -132,21 +132,21 @@ func TestBasicOperations(t *testing.T) { buf.Truncate(0) check(t, "TestBasicOperations (3)", &buf, "") - n, err := buf.Write(Bytes(data[0:1])) + n, err := buf.Write([]byte(data[0:1])) if n != 1 { - t.Errorf("wrote 1 byte, but n == %d\n", n) + t.Errorf("wrote 1 byte, but n == %d", n) } if err != nil { - t.Errorf("err should always be nil, but err == %s\n", err) + t.Errorf("err should always be nil, but err == %s", err) } check(t, "TestBasicOperations (4)", &buf, "a") buf.WriteByte(data[1]) check(t, "TestBasicOperations (5)", &buf, "ab") - n, err = buf.Write(Bytes(data[2:26])) + n, err = buf.Write([]byte(data[2:26])) if n != 24 { - t.Errorf("wrote 25 bytes, but n == %d\n", n) + t.Errorf("wrote 25 bytes, but n == %d", n) } check(t, "TestBasicOperations (6)", &buf, string(data[0:26])) @@ -162,14 +162,14 @@ func TestBasicOperations(t *testing.T) { buf.WriteByte(data[1]) c, err := buf.ReadByte() if err != nil { - t.Errorf("ReadByte unexpected eof\n") + t.Error("ReadByte unexpected eof") } if c != data[1] { - t.Errorf("ReadByte wrong value c=%v\n", c) + t.Errorf("ReadByte wrong value c=%v", c) } c, err = buf.ReadByte() if err == nil { - t.Errorf("ReadByte unexpected not eof\n") + t.Error("ReadByte unexpected not eof") } } } @@ -238,7 +238,7 @@ func TestMixedReadsAndWrites(t *testing.T) { func TestNil(t *testing.T) { var b *Buffer if b.String() != "" { - t.Error("expcted ; got %q", b.String()) + t.Errorf("expcted ; got %q", b.String()) } } @@ -272,13 +272,13 @@ func TestRuneIO(t *testing.T) { var buf Buffer n := 0 for r := 0; r < NRune; r++ { - size := utf8.EncodeRune(r, b[n:]) + size := utf8.EncodeRune(b[n:], r) nbytes, err := buf.WriteRune(r) if err != nil { - t.Fatalf("WriteRune(0x%x) error: %s", r, err) + t.Fatalf("WriteRune(%U) error: %s", r, err) } if nbytes != size { - t.Fatalf("WriteRune(0x%x) expected %d, got %d", r, size, nbytes) + t.Fatalf("WriteRune(%U) expected %d, got %d", r, size, nbytes) } n += size } @@ -289,12 +289,27 @@ func TestRuneIO(t *testing.T) { t.Fatalf("incorrect result from WriteRune: %q not %q", buf.Bytes(), b) } + p := make([]byte, utf8.UTFMax) // Read it back with ReadRune for r := 0; r < NRune; r++ { - size := utf8.EncodeRune(r, b) + size := utf8.EncodeRune(p, r) nr, nbytes, err := buf.ReadRune() if nr != r || nbytes != size || err != nil { - t.Fatalf("ReadRune(0x%x) got 0x%x,%d not 0x%x,%d (err=%s)", r, nr, nbytes, r, size, err) + t.Fatalf("ReadRune(%U) got %U,%d not %U,%d (err=%s)", r, nr, nbytes, r, size, err) + } + } + + // Check that UnreadRune works + buf.Reset() + buf.Write(b) + for r := 0; r < NRune; r++ { + r1, size, _ := buf.ReadRune() + if err := buf.UnreadRune(); err != nil { + t.Fatalf("UnreadRune(%U) got error %q", r, err) + } + r2, nbytes, err := buf.ReadRune() + if r1 != r2 || r1 != r || nbytes != size || err != nil { + t.Fatalf("ReadRune(%U) after UnreadRune got %U,%d not %U,%d (err=%s)", r, r2, nbytes, r, size, err) } } } diff --git a/src/pkg/bytes/bytes.go b/src/pkg/bytes/bytes.go index bcf7b8609..bfe2ef39d 100644 --- a/src/pkg/bytes/bytes.go +++ b/src/pkg/bytes/bytes.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // The bytes package implements functions for the manipulation of byte slices. -// Analagous to the facilities of the strings package. +// Analogous to the facilities of the strings package. package bytes import ( @@ -127,7 +127,21 @@ func LastIndex(s, sep []byte) int { return -1 } -// IndexAny interprets s as a sequence of UTF-8 encoded Unicode code points. +// IndexRune interprets s as a sequence of UTF-8-encoded Unicode code points. +// It returns the byte index of the first occurrence in s of the given rune. +// It returns -1 if rune is not present in s. +func IndexRune(s []byte, rune int) int { + for i := 0; i < len(s); { + r, size := utf8.DecodeRune(s[i:]) + if r == rune { + return i + } + i += size + } + return -1 +} + +// IndexAny interprets s as a sequence of UTF-8-encoded Unicode code points. // It returns the byte index of the first occurrence in s of any of the Unicode // code points in chars. It returns -1 if chars is empty or if there is no code // point in common. @@ -151,6 +165,25 @@ func IndexAny(s []byte, chars string) int { return -1 } +// LastIndexAny interprets s as a sequence of UTF-8-encoded Unicode code +// points. It returns the byte index of the last occurrence in s of any of +// the Unicode code points in chars. It returns -1 if chars is empty or if +// there is no code point in common. +func LastIndexAny(s []byte, chars string) int { + if len(chars) > 0 { + for i := len(s); i > 0; { + rune, size := utf8.DecodeLastRune(s[0:i]) + i -= size + for _, m := range chars { + if rune == m { + return i + } + } + } + } + return -1 +} + // Generic split: splits after each instance of sep, // including sepSave bytes of sep in the subarrays. func genSplit(s, sep []byte, sepSave, n int) [][]byte { @@ -179,17 +212,22 @@ func genSplit(s, sep []byte, sepSave, n int) [][]byte { return a[0 : na+1] } -// Split splits the array s around each instance of sep, returning an array of subarrays of s. -// If sep is empty, Split splits s after each UTF-8 sequence. -// If n >= 0, Split splits s into at most n subarrays; the last subarray will contain an unsplit remainder. -// Thus if n == 0, the result will ne nil. +// Split slices s into subslices separated by sep and returns a slice of +// the subslices between those separators. +// If sep is empty, Split splits after each UTF-8 sequence. +// The count determines the number of subslices to return: +// n > 0: at most n subslices; the last subslice will be the unsplit remainder. +// n == 0: the result is nil (zero subslices) +// n < 0: all subslices func Split(s, sep []byte, n int) [][]byte { return genSplit(s, sep, 0, n) } -// SplitAfter splits the array s after each instance of sep, returning an array of subarrays of s. -// If sep is empty, SplitAfter splits s after each UTF-8 sequence. -// If n >= 0, SplitAfter splits s into at most n subarrays; the last subarray will contain an -// unsplit remainder. -// Thus if n == 0, the result will ne nil. +// SplitAfter slices s into subslices after each instance of sep and +// returns a slice of those subslices. +// If sep is empty, Split splits after each UTF-8 sequence. +// The count determines the number of subslices to return: +// n > 0: at most n subslices; the last subslice will be the unsplit remainder. +// n == 0: the result is nil (zero subslices) +// n < 0: all subslices func SplitAfter(s, sep []byte, n int) [][]byte { return genSplit(s, sep, len(sep), n) } @@ -197,12 +235,20 @@ func SplitAfter(s, sep []byte, n int) [][]byte { // Fields splits the array s around each instance of one or more consecutive white space // characters, returning a slice of subarrays of s or an empty list if s contains only white space. func Fields(s []byte) [][]byte { + return FieldsFunc(s, unicode.IsSpace) +} + +// FieldsFunc interprets s as a sequence of UTF-8-encoded Unicode code points. +// It splits the array s at each run of code points c satisfying f(c) and +// returns a slice of subarrays of s. If no code points in s satisfy f(c), an +// empty slice is returned. +func FieldsFunc(s []byte, f func(int) bool) [][]byte { n := 0 inField := false for i := 0; i < len(s); { rune, size := utf8.DecodeRune(s[i:]) wasInField := inField - inField = !unicode.IsSpace(rune) + inField = !f(rune) if inField && !wasInField { n++ } @@ -214,12 +260,12 @@ func Fields(s []byte) [][]byte { fieldStart := -1 for i := 0; i <= len(s) && na < n; { rune, size := utf8.DecodeRune(s[i:]) - if fieldStart < 0 && size > 0 && !unicode.IsSpace(rune) { + if fieldStart < 0 && size > 0 && !f(rune) { fieldStart = i i += size continue } - if fieldStart >= 0 && (size == 0 || unicode.IsSpace(rune)) { + if fieldStart >= 0 && (size == 0 || f(rune)) { a[na] = s[fieldStart:i] na++ fieldStart = -1 @@ -278,7 +324,7 @@ func HasSuffix(s, suffix []byte) bool { // Map returns a copy of the byte array s with all its characters modified // according to the mapping function. If mapping returns a negative value, the character is // dropped from the string with no replacement. The characters in s and the -// output are interpreted as UTF-8 encoded Unicode code points. +// output are interpreted as UTF-8-encoded Unicode code points. func Map(mapping func(rune int) int, s []byte) []byte { // In the worst case, the array can grow when mapped, making // things unpleasant. But it's so rare we barge in assuming it's @@ -298,12 +344,10 @@ func Map(mapping func(rune int) int, s []byte) []byte { // Grow the buffer. maxbytes = maxbytes*2 + utf8.UTFMax nb := make([]byte, maxbytes) - for i, c := range b[0:nbytes] { - nb[i] = c - } + copy(nb, b[0:nbytes]) b = nb } - nbytes += utf8.EncodeRune(rune, b[nbytes:maxbytes]) + nbytes += utf8.EncodeRune(b[nbytes:maxbytes], rune) } i += wid } @@ -332,52 +376,147 @@ func ToLower(s []byte) []byte { return Map(unicode.ToLower, s) } // ToTitle returns a copy of the byte array s with all Unicode letters mapped to their title case. func ToTitle(s []byte) []byte { return Map(unicode.ToTitle, s) } -// TrimLeftFunc returns a subslice of s by slicing off all leading UTF-8 encoded +// ToUpperSpecial returns a copy of the byte array s with all Unicode letters mapped to their +// upper case, giving priority to the special casing rules. +func ToUpperSpecial(_case unicode.SpecialCase, s []byte) []byte { + return Map(func(r int) int { return _case.ToUpper(r) }, s) +} + +// ToLowerSpecial returns a copy of the byte array s with all Unicode letters mapped to their +// lower case, giving priority to the special casing rules. +func ToLowerSpecial(_case unicode.SpecialCase, s []byte) []byte { + return Map(func(r int) int { return _case.ToLower(r) }, s) +} + +// ToTitleSpecial returns a copy of the byte array s with all Unicode letters mapped to their +// title case, giving priority to the special casing rules. +func ToTitleSpecial(_case unicode.SpecialCase, s []byte) []byte { + return Map(func(r int) int { return _case.ToTitle(r) }, s) +} + + +// isSeparator reports whether the rune could mark a word boundary. +// TODO: update when package unicode captures more of the properties. +func isSeparator(rune int) bool { + // ASCII alphanumerics and underscore are not separators + if rune <= 0x7F { + switch { + case '0' <= rune && rune <= '9': + return false + case 'a' <= rune && rune <= 'z': + return false + case 'A' <= rune && rune <= 'Z': + return false + case rune == '_': + return false + } + return true + } + // Letters and digits are not separators + if unicode.IsLetter(rune) || unicode.IsDigit(rune) { + return false + } + // Otherwise, all we can do for now is treat spaces as separators. + return unicode.IsSpace(rune) +} + +// BUG(r): The rule Title uses for word boundaries does not handle Unicode punctuation properly. + +// Title returns a copy of s with all Unicode letters that begin words +// mapped to their title case. +func Title(s []byte) []byte { + // Use a closure here to remember state. + // Hackish but effective. Depends on Map scanning in order and calling + // the closure once per rune. + prev := ' ' + return Map( + func(r int) int { + if isSeparator(prev) { + prev = r + return unicode.ToTitle(r) + } + prev = r + return r + }, + s) +} + +// TrimLeftFunc returns a subslice of s by slicing off all leading UTF-8-encoded // Unicode code points c that satisfy f(c). func TrimLeftFunc(s []byte, f func(r int) bool) []byte { - var start, wid int - for start = 0; start < len(s); start += wid { - wid = 1 - rune := int(s[start]) - if rune >= utf8.RuneSelf { - rune, wid = utf8.DecodeRune(s[start:]) - } - if !f(rune) { - break - } + i := indexFunc(s, f, false) + if i == -1 { + return nil } - return s[start:] + return s[i:] } // TrimRightFunc returns a subslice of s by slicing off all trailing UTF-8 // encoded Unicode code points c that satisfy f(c). func TrimRightFunc(s []byte, f func(r int) bool) []byte { - var end, wid int - for end = len(s); end > 0; end -= wid { - wid = 1 - rune := int(s[end-wid]) - if rune >= utf8.RuneSelf { - // Back up & look for beginning of rune. Mustn't pass start. - for wid = 2; end-wid >= 0 && !utf8.RuneStart(s[end-wid]); wid++ { - } - if end-wid < 0 { // invalid UTF-8 sequence; stop processing - break - } - rune, wid = utf8.DecodeRune(s[end-wid : end]) - } - if !f(rune) { - break - } + i := lastIndexFunc(s, f, false) + if i >= 0 && s[i] >= utf8.RuneSelf { + _, wid := utf8.DecodeRune(s[i:]) + i += wid + } else { + i++ } - return s[0:end] + return s[0:i] } // TrimFunc returns a subslice of s by slicing off all leading and trailing -// UTF-8 encoded Unicode code points c that satisfy f(c). +// UTF-8-encoded Unicode code points c that satisfy f(c). func TrimFunc(s []byte, f func(r int) bool) []byte { return TrimRightFunc(TrimLeftFunc(s, f), f) } +// IndexFunc interprets s as a sequence of UTF-8-encoded Unicode code points. +// It returns the byte index in s of the first Unicode +// code point satisfying f(c), or -1 if none do. +func IndexFunc(s []byte, f func(r int) bool) int { + return indexFunc(s, f, true) +} + +// LastIndexFunc interprets s as a sequence of UTF-8-encoded Unicode code points. +// It returns the byte index in s of the last Unicode +// code point satisfying f(c), or -1 if none do. +func LastIndexFunc(s []byte, f func(r int) bool) int { + return lastIndexFunc(s, f, true) +} + +// indexFunc is the same as IndexFunc except that if +// truth==false, the sense of the predicate function is +// inverted. +func indexFunc(s []byte, f func(r int) bool, truth bool) int { + start := 0 + for start < len(s) { + wid := 1 + rune := int(s[start]) + if rune >= utf8.RuneSelf { + rune, wid = utf8.DecodeRune(s[start:]) + } + if f(rune) == truth { + return start + } + start += wid + } + return -1 +} + +// lastIndexFunc is the same as LastIndexFunc except that if +// truth==false, the sense of the predicate function is +// inverted. +func lastIndexFunc(s []byte, f func(r int) bool, truth bool) int { + for i := len(s); i > 0; { + rune, size := utf8.DecodeLastRune(s[0:i]) + i -= size + if f(rune) == truth { + return i + } + } + return -1 +} + func makeCutsetFunc(cutset string) func(rune int) bool { return func(rune int) bool { for _, c := range cutset { @@ -390,71 +529,29 @@ func makeCutsetFunc(cutset string) func(rune int) bool { } // Trim returns a subslice of s by slicing off all leading and -// trailing UTF-8 encoded Unicode code points contained in cutset. +// trailing UTF-8-encoded Unicode code points contained in cutset. func Trim(s []byte, cutset string) []byte { return TrimFunc(s, makeCutsetFunc(cutset)) } // TrimLeft returns a subslice of s by slicing off all leading -// UTF-8 encoded Unicode code points contained in cutset. +// UTF-8-encoded Unicode code points contained in cutset. func TrimLeft(s []byte, cutset string) []byte { return TrimLeftFunc(s, makeCutsetFunc(cutset)) } // TrimRight returns a subslice of s by slicing off all trailing -// UTF-8 encoded Unicode code points that are contained in cutset. +// UTF-8-encoded Unicode code points that are contained in cutset. func TrimRight(s []byte, cutset string) []byte { return TrimRightFunc(s, makeCutsetFunc(cutset)) } // TrimSpace returns a subslice of s by slicing off all leading and -// trailing white space, as as defined by Unicode. +// trailing white space, as defined by Unicode. func TrimSpace(s []byte) []byte { return TrimFunc(s, unicode.IsSpace) } -// How big to make a byte array when growing. -// Heuristic: Scale by 50% to give n log n time. -func resize(n int) int { - if n < 16 { - n = 16 - } - return n + n/2 -} - -// Add appends the contents of t to the end of s and returns the result. -// If s has enough capacity, it is extended in place; otherwise a -// new array is allocated and returned. -func Add(s, t []byte) []byte { - lens := len(s) - lent := len(t) - if lens+lent <= cap(s) { - s = s[0 : lens+lent] - } else { - news := make([]byte, lens+lent, resize(lens+lent)) - copy(news, s) - s = news - } - copy(s[lens:lens+lent], t) - return s -} - -// AddByte appends byte b to the end of s and returns the result. -// If s has enough capacity, it is extended in place; otherwise a -// new array is allocated and returned. -func AddByte(s []byte, t byte) []byte { - lens := len(s) - if lens+1 <= cap(s) { - s = s[0 : lens+1] - } else { - news := make([]byte, lens+1, resize(lens+1)) - copy(news, s) - s = news - } - s[lens] = t - return s -} - // Runes returns a slice of runes (Unicode code points) equivalent to s. func Runes(s []byte) []int { t := make([]int, utf8.RuneCount(s)) diff --git a/src/pkg/bytes/bytes_test.go b/src/pkg/bytes/bytes_test.go index 8197543dc..063686ec5 100644 --- a/src/pkg/bytes/bytes_test.go +++ b/src/pkg/bytes/bytes_test.go @@ -8,6 +8,7 @@ import ( . "bytes" "testing" "unicode" + "utf8" ) func eq(a, b []string) bool { @@ -45,16 +46,16 @@ type BinOpTest struct { } var comparetests = []BinOpTest{ - BinOpTest{"", "", 0}, - BinOpTest{"a", "", 1}, - BinOpTest{"", "a", -1}, - BinOpTest{"abc", "abc", 0}, - BinOpTest{"ab", "abc", -1}, - BinOpTest{"abc", "ab", 1}, - BinOpTest{"x", "ab", 1}, - BinOpTest{"ab", "x", -1}, - BinOpTest{"x", "a", 1}, - BinOpTest{"b", "x", -1}, + {"", "", 0}, + {"a", "", 1}, + {"", "a", -1}, + {"abc", "abc", 0}, + {"ab", "abc", -1}, + {"abc", "ab", 1}, + {"x", "ab", 1}, + {"ab", "x", -1}, + {"x", "a", 1}, + {"b", "x", -1}, } func TestCompare(t *testing.T) { @@ -73,55 +74,81 @@ func TestCompare(t *testing.T) { } var indexTests = []BinOpTest{ - BinOpTest{"", "", 0}, - BinOpTest{"", "a", -1}, - BinOpTest{"", "foo", -1}, - BinOpTest{"fo", "foo", -1}, - BinOpTest{"foo", "foo", 0}, - BinOpTest{"oofofoofooo", "f", 2}, - BinOpTest{"oofofoofooo", "foo", 4}, - BinOpTest{"barfoobarfoo", "foo", 3}, - BinOpTest{"foo", "", 0}, - BinOpTest{"foo", "o", 1}, - BinOpTest{"abcABCabc", "A", 3}, + {"", "", 0}, + {"", "a", -1}, + {"", "foo", -1}, + {"fo", "foo", -1}, + {"foo", "foo", 0}, + {"oofofoofooo", "f", 2}, + {"oofofoofooo", "foo", 4}, + {"barfoobarfoo", "foo", 3}, + {"foo", "", 0}, + {"foo", "o", 1}, + {"abcABCabc", "A", 3}, // cases with one byte strings - test IndexByte and special case in Index() - BinOpTest{"", "a", -1}, - BinOpTest{"x", "a", -1}, - BinOpTest{"x", "x", 0}, - BinOpTest{"abc", "a", 0}, - BinOpTest{"abc", "b", 1}, - BinOpTest{"abc", "c", 2}, - BinOpTest{"abc", "x", -1}, + {"", "a", -1}, + {"x", "a", -1}, + {"x", "x", 0}, + {"abc", "a", 0}, + {"abc", "b", 1}, + {"abc", "c", 2}, + {"abc", "x", -1}, + {"barfoobarfooyyyzzzyyyzzzyyyzzzyyyxxxzzzyyy", "x", 33}, + {"foofyfoobarfoobar", "y", 4}, + {"oooooooooooooooooooooo", "r", -1}, } var lastIndexTests = []BinOpTest{ - BinOpTest{"", "", 0}, - BinOpTest{"", "a", -1}, - BinOpTest{"", "foo", -1}, - BinOpTest{"fo", "foo", -1}, - BinOpTest{"foo", "foo", 0}, - BinOpTest{"foo", "f", 0}, - BinOpTest{"oofofoofooo", "f", 7}, - BinOpTest{"oofofoofooo", "foo", 7}, - BinOpTest{"barfoobarfoo", "foo", 9}, - BinOpTest{"foo", "", 3}, - BinOpTest{"foo", "o", 2}, - BinOpTest{"abcABCabc", "A", 3}, - BinOpTest{"abcABCabc", "a", 6}, + {"", "", 0}, + {"", "a", -1}, + {"", "foo", -1}, + {"fo", "foo", -1}, + {"foo", "foo", 0}, + {"foo", "f", 0}, + {"oofofoofooo", "f", 7}, + {"oofofoofooo", "foo", 7}, + {"barfoobarfoo", "foo", 9}, + {"foo", "", 3}, + {"foo", "o", 2}, + {"abcABCabc", "A", 3}, + {"abcABCabc", "a", 6}, } var indexAnyTests = []BinOpTest{ - BinOpTest{"", "", -1}, - BinOpTest{"", "a", -1}, - BinOpTest{"", "abc", -1}, - BinOpTest{"a", "", -1}, - BinOpTest{"a", "a", 0}, - BinOpTest{"aaa", "a", 0}, - BinOpTest{"abc", "xyz", -1}, - BinOpTest{"abc", "xcz", 2}, - BinOpTest{"ab☺c", "x☺yz", 2}, - BinOpTest{"aRegExp*", ".(|)*+?^$[]", 7}, - BinOpTest{dots + dots + dots, " ", -1}, + {"", "", -1}, + {"", "a", -1}, + {"", "abc", -1}, + {"a", "", -1}, + {"a", "a", 0}, + {"aaa", "a", 0}, + {"abc", "xyz", -1}, + {"abc", "xcz", 2}, + {"ab☺c", "x☺yz", 2}, + {"aRegExp*", ".(|)*+?^$[]", 7}, + {dots + dots + dots, " ", -1}, +} + +var lastIndexAnyTests = []BinOpTest{ + {"", "", -1}, + {"", "a", -1}, + {"", "abc", -1}, + {"a", "", -1}, + {"a", "a", 0}, + {"aaa", "a", 2}, + {"abc", "xyz", -1}, + {"abc", "ab", 1}, + {"a☺b☻c☹d", "uvw☻xyz", 2 + len("☺")}, + {"a.RegExp*", ".(|)*+?^$[]", 8}, + {dots + dots + dots, " ", -1}, +} + +var indexRuneTests = []BinOpTest{ + {"", "a", -1}, + {"", "☺", -1}, + {"foo", "☹", -1}, + {"foo", "o", 1}, + {"foo☺bar", "☺", 3}, + {"foo☺☻☹bar", "☹", 9}, } // Execute f on each test case. funcName should be the name of f; it's used @@ -137,18 +164,23 @@ func runIndexTests(t *testing.T, f func(s, sep []byte) int, funcName string, tes } } -func TestIndex(t *testing.T) { runIndexTests(t, Index, "Index", indexTests) } -func TestLastIndex(t *testing.T) { runIndexTests(t, LastIndex, "LastIndex", lastIndexTests) } -func TestIndexAny(t *testing.T) { - for _, test := range indexAnyTests { +func runIndexAnyTests(t *testing.T, f func(s []byte, chars string) int, funcName string, testCases []BinOpTest) { + for _, test := range testCases { a := []byte(test.a) - actual := IndexAny(a, test.b) + actual := f(a, test.b) if actual != test.i { - t.Errorf("IndexAny(%q,%q) = %v; want %v", a, test.b, actual, test.i) + t.Errorf("%s(%q,%q) = %v; want %v", funcName, a, test.b, actual, test.i) } } } +func TestIndex(t *testing.T) { runIndexTests(t, Index, "Index", indexTests) } +func TestLastIndex(t *testing.T) { runIndexTests(t, LastIndex, "LastIndex", lastIndexTests) } +func TestIndexAny(t *testing.T) { runIndexAnyTests(t, IndexAny, "IndexAny", indexAnyTests) } +func TestLastIndexAny(t *testing.T) { + runIndexAnyTests(t, LastIndexAny, "LastIndexAny", lastIndexAnyTests) +} + func TestIndexByte(t *testing.T) { for _, tt := range indexTests { if len(tt.b) != 1 { @@ -167,6 +199,67 @@ func TestIndexByte(t *testing.T) { } } +// test a larger buffer with different sizes and alignments +func TestIndexByteBig(t *testing.T) { + const n = 1024 + b := make([]byte, n) + for i := 0; i < n; i++ { + // different start alignments + b1 := b[i:] + for j := 0; j < len(b1); j++ { + b1[j] = 'x' + pos := IndexByte(b1, 'x') + if pos != j { + t.Errorf("IndexByte(%q, 'x') = %v", b1, pos) + } + b1[j] = 0 + pos = IndexByte(b1, 'x') + if pos != -1 { + t.Errorf("IndexByte(%q, 'x') = %v", b1, pos) + } + } + // different end alignments + b1 = b[:i] + for j := 0; j < len(b1); j++ { + b1[j] = 'x' + pos := IndexByte(b1, 'x') + if pos != j { + t.Errorf("IndexByte(%q, 'x') = %v", b1, pos) + } + b1[j] = 0 + pos = IndexByte(b1, 'x') + if pos != -1 { + t.Errorf("IndexByte(%q, 'x') = %v", b1, pos) + } + } + // different start and end alignments + b1 = b[i/2 : n-(i+1)/2] + for j := 0; j < len(b1); j++ { + b1[j] = 'x' + pos := IndexByte(b1, 'x') + if pos != j { + t.Errorf("IndexByte(%q, 'x') = %v", b1, pos) + } + b1[j] = 0 + pos = IndexByte(b1, 'x') + if pos != -1 { + t.Errorf("IndexByte(%q, 'x') = %v", b1, pos) + } + } + } +} + +func TestIndexRune(t *testing.T) { + for _, tt := range indexRuneTests { + a := []byte(tt.a) + r, _ := utf8.DecodeRuneInString(tt.b) + pos := IndexRune(a, r) + if pos != tt.i { + t.Errorf(`IndexRune(%q, '%c') = %v`, tt.a, r, pos) + } + } +} + func BenchmarkIndexByte4K(b *testing.B) { bmIndex(b, IndexByte, 4<<10) } func BenchmarkIndexByte4M(b *testing.B) { bmIndex(b, IndexByte, 4<<20) } @@ -211,9 +304,10 @@ type ExplodeTest struct { } var explodetests = []ExplodeTest{ - ExplodeTest{abcd, -1, []string{"a", "b", "c", "d"}}, - ExplodeTest{faces, -1, []string{"☺", "☻", "☹"}}, - ExplodeTest{abcd, 2, []string{"a", "bcd"}}, + {"", -1, []string{}}, + {abcd, -1, []string{"a", "b", "c", "d"}}, + {faces, -1, []string{"☺", "☻", "☹"}}, + {abcd, 2, []string{"a", "bcd"}}, } func TestExplode(t *testing.T) { @@ -240,19 +334,19 @@ type SplitTest struct { } var splittests = []SplitTest{ - SplitTest{abcd, "a", 0, nil}, - SplitTest{abcd, "a", -1, []string{"", "bcd"}}, - SplitTest{abcd, "z", -1, []string{"abcd"}}, - SplitTest{abcd, "", -1, []string{"a", "b", "c", "d"}}, - SplitTest{commas, ",", -1, []string{"1", "2", "3", "4"}}, - SplitTest{dots, "...", -1, []string{"1", ".2", ".3", ".4"}}, - SplitTest{faces, "☹", -1, []string{"☺☻", ""}}, - SplitTest{faces, "~", -1, []string{faces}}, - SplitTest{faces, "", -1, []string{"☺", "☻", "☹"}}, - SplitTest{"1 2 3 4", " ", 3, []string{"1", "2", "3 4"}}, - SplitTest{"1 2", " ", 3, []string{"1", "2"}}, - SplitTest{"123", "", 2, []string{"1", "23"}}, - SplitTest{"123", "", 17, []string{"1", "2", "3"}}, + {abcd, "a", 0, nil}, + {abcd, "a", -1, []string{"", "bcd"}}, + {abcd, "z", -1, []string{"abcd"}}, + {abcd, "", -1, []string{"a", "b", "c", "d"}}, + {commas, ",", -1, []string{"1", "2", "3", "4"}}, + {dots, "...", -1, []string{"1", ".2", ".3", ".4"}}, + {faces, "☹", -1, []string{"☺☻", ""}}, + {faces, "~", -1, []string{faces}}, + {faces, "", -1, []string{"☺", "☻", "☹"}}, + {"1 2 3 4", " ", 3, []string{"1", "2", "3 4"}}, + {"1 2", " ", 3, []string{"1", "2"}}, + {"123", "", 2, []string{"1", "23"}}, + {"123", "", 17, []string{"1", "2", "3"}}, } func TestSplit(t *testing.T) { @@ -274,19 +368,19 @@ func TestSplit(t *testing.T) { } var splitaftertests = []SplitTest{ - SplitTest{abcd, "a", -1, []string{"a", "bcd"}}, - SplitTest{abcd, "z", -1, []string{"abcd"}}, - SplitTest{abcd, "", -1, []string{"a", "b", "c", "d"}}, - SplitTest{commas, ",", -1, []string{"1,", "2,", "3,", "4"}}, - SplitTest{dots, "...", -1, []string{"1...", ".2...", ".3...", ".4"}}, - SplitTest{faces, "☹", -1, []string{"☺☻☹", ""}}, - SplitTest{faces, "~", -1, []string{faces}}, - SplitTest{faces, "", -1, []string{"☺", "☻", "☹"}}, - SplitTest{"1 2 3 4", " ", 3, []string{"1 ", "2 ", "3 4"}}, - SplitTest{"1 2 3", " ", 3, []string{"1 ", "2 ", "3"}}, - SplitTest{"1 2", " ", 3, []string{"1 ", "2"}}, - SplitTest{"123", "", 2, []string{"1", "23"}}, - SplitTest{"123", "", 17, []string{"1", "2", "3"}}, + {abcd, "a", -1, []string{"a", "bcd"}}, + {abcd, "z", -1, []string{"abcd"}}, + {abcd, "", -1, []string{"a", "b", "c", "d"}}, + {commas, ",", -1, []string{"1,", "2,", "3,", "4"}}, + {dots, "...", -1, []string{"1...", ".2...", ".3...", ".4"}}, + {faces, "☹", -1, []string{"☺☻☹", ""}}, + {faces, "~", -1, []string{faces}}, + {faces, "", -1, []string{"☺", "☻", "☹"}}, + {"1 2 3 4", " ", 3, []string{"1 ", "2 ", "3 4"}}, + {"1 2 3", " ", 3, []string{"1 ", "2 ", "3"}}, + {"1 2", " ", 3, []string{"1 ", "2"}}, + {"123", "", 2, []string{"1", "23"}}, + {"123", "", 17, []string{"1", "2", "3"}}, } func TestSplitAfter(t *testing.T) { @@ -310,17 +404,17 @@ type FieldsTest struct { } var fieldstests = []FieldsTest{ - FieldsTest{"", []string{}}, - FieldsTest{" ", []string{}}, - FieldsTest{" \t ", []string{}}, - FieldsTest{" abc ", []string{"abc"}}, - FieldsTest{"1 2 3 4", []string{"1", "2", "3", "4"}}, - FieldsTest{"1 2 3 4", []string{"1", "2", "3", "4"}}, - FieldsTest{"1\t\t2\t\t3\t4", []string{"1", "2", "3", "4"}}, - FieldsTest{"1\u20002\u20013\u20024", []string{"1", "2", "3", "4"}}, - FieldsTest{"\u2000\u2001\u2002", []string{}}, - FieldsTest{"\n™\t™\n", []string{"™", "™"}}, - FieldsTest{faces, []string{faces}}, + {"", []string{}}, + {" ", []string{}}, + {" \t ", []string{}}, + {" abc ", []string{"abc"}}, + {"1 2 3 4", []string{"1", "2", "3", "4"}}, + {"1 2 3 4", []string{"1", "2", "3", "4"}}, + {"1\t\t2\t\t3\t4", []string{"1", "2", "3", "4"}}, + {"1\u20002\u20013\u20024", []string{"1", "2", "3", "4"}}, + {"\u2000\u2001\u2002", []string{}}, + {"\n™\t™\n", []string{"™", "™"}}, + {faces, []string{faces}}, } func TestFields(t *testing.T) { @@ -334,6 +428,23 @@ func TestFields(t *testing.T) { } } +func TestFieldsFunc(t *testing.T) { + pred := func(c int) bool { return c == 'X' } + var fieldsFuncTests = []FieldsTest{ + {"", []string{}}, + {"XX", []string{}}, + {"XXhiXXX", []string{"hi"}}, + {"aXXbXXXcX", []string{"a", "b", "c"}}, + } + for _, tt := range fieldsFuncTests { + a := FieldsFunc([]byte(tt.s), pred) + result := arrayOfString(a) + if !eq(result, tt.a) { + t.Errorf("FieldsFunc(%q) = %v, want %v", tt.s, a, tt.a) + } + } +} + // Test case for any function which accepts and returns a byte array. // For ease of creation, we write the byte arrays as strings. type StringTest struct { @@ -341,51 +452,47 @@ type StringTest struct { } var upperTests = []StringTest{ - StringTest{"", ""}, - StringTest{"abc", "ABC"}, - StringTest{"AbC123", "ABC123"}, - StringTest{"azAZ09_", "AZAZ09_"}, - StringTest{"\u0250\u0250\u0250\u0250\u0250", "\u2C6F\u2C6F\u2C6F\u2C6F\u2C6F"}, // grows one byte per char + {"", ""}, + {"abc", "ABC"}, + {"AbC123", "ABC123"}, + {"azAZ09_", "AZAZ09_"}, + {"\u0250\u0250\u0250\u0250\u0250", "\u2C6F\u2C6F\u2C6F\u2C6F\u2C6F"}, // grows one byte per char } var lowerTests = []StringTest{ - StringTest{"", ""}, - StringTest{"abc", "abc"}, - StringTest{"AbC123", "abc123"}, - StringTest{"azAZ09_", "azaz09_"}, - StringTest{"\u2C6D\u2C6D\u2C6D\u2C6D\u2C6D", "\u0251\u0251\u0251\u0251\u0251"}, // shrinks one byte per char + {"", ""}, + {"abc", "abc"}, + {"AbC123", "abc123"}, + {"azAZ09_", "azaz09_"}, + {"\u2C6D\u2C6D\u2C6D\u2C6D\u2C6D", "\u0251\u0251\u0251\u0251\u0251"}, // shrinks one byte per char } const space = "\t\v\r\f\n\u0085\u00a0\u2000\u3000" var trimSpaceTests = []StringTest{ - StringTest{"", ""}, - StringTest{"abc", "abc"}, - StringTest{space + "abc" + space, "abc"}, - StringTest{" ", ""}, - StringTest{" \t\r\n \t\t\r\r\n\n ", ""}, - StringTest{" \t\r\n x\t\t\r\r\n\n ", "x"}, - StringTest{" \u2000\t\r\n x\t\t\r\r\ny\n \u3000", "x\t\t\r\r\ny"}, - StringTest{"1 \t\r\n2", "1 \t\r\n2"}, - StringTest{" x\x80", "x\x80"}, // invalid UTF-8 on end - StringTest{" x\xc0", "x\xc0"}, // invalid UTF-8 on end -} - -// Bytes returns a new slice containing the bytes in s. -// Borrowed from strings to avoid dependency. -func Bytes(s string) []byte { - b := make([]byte, len(s)) - for i := 0; i < len(s); i++ { - b[i] = s[i] - } - return b + {"", ""}, + {"abc", "abc"}, + {space + "abc" + space, "abc"}, + {" ", ""}, + {" \t\r\n \t\t\r\r\n\n ", ""}, + {" \t\r\n x\t\t\r\r\n\n ", "x"}, + {" \u2000\t\r\n x\t\t\r\r\ny\n \u3000", "x\t\t\r\r\ny"}, + {"1 \t\r\n2", "1 \t\r\n2"}, + {" x\x80", "x\x80"}, + {" x\xc0", "x\xc0"}, + {"x \xc0\xc0 ", "x \xc0\xc0"}, + {"x \xc0", "x \xc0"}, + {"x \xc0 ", "x \xc0"}, + {"x \xc0\xc0 ", "x \xc0\xc0"}, + {"x ☺\xc0\xc0 ", "x ☺\xc0\xc0"}, + {"x ☺ ", "x ☺"}, } // Execute f on each test case. funcName should be the name of f; it's used // in failure reports. func runStringTests(t *testing.T, f func([]byte) []byte, funcName string, testCases []StringTest) { for _, tc := range testCases { - actual := string(f(Bytes(tc.in))) + actual := string(f([]byte(tc.in))) if actual != tc.out { t.Errorf("%s(%q) = %q; want %q", funcName, tc.in, actual, tc.out) } @@ -418,7 +525,7 @@ func TestMap(t *testing.T) { // 1. Grow. This triggers two reallocations in Map. maxRune := func(rune int) int { return unicode.MaxRune } - m := Map(maxRune, Bytes(a)) + m := Map(maxRune, []byte(a)) expect := tenRunes(unicode.MaxRune) if string(m) != expect { t.Errorf("growing: expected %q got %q", expect, m) @@ -426,21 +533,21 @@ func TestMap(t *testing.T) { // 2. Shrink minRune := func(rune int) int { return 'a' } - m = Map(minRune, Bytes(tenRunes(unicode.MaxRune))) + m = Map(minRune, []byte(tenRunes(unicode.MaxRune))) expect = a if string(m) != expect { t.Errorf("shrinking: expected %q got %q", expect, m) } // 3. Rot13 - m = Map(rot13, Bytes("a to zed")) + m = Map(rot13, []byte("a to zed")) expect = "n gb mrq" if string(m) != expect { t.Errorf("rot13: expected %q got %q", expect, m) } // 4. Rot13^2 - m = Map(rot13, Map(rot13, Bytes("a to zed"))) + m = Map(rot13, Map(rot13, []byte("a to zed"))) expect = "a to zed" if string(m) != expect { t.Errorf("rot13: expected %q got %q", expect, m) @@ -453,7 +560,7 @@ func TestMap(t *testing.T) { } return -1 } - m = Map(dropNotLatin, Bytes("Hello, 세계")) + m = Map(dropNotLatin, []byte("Hello, 세계")) expect = "Hello" if string(m) != expect { t.Errorf("drop: expected %q got %q", expect, m) @@ -466,60 +573,19 @@ func TestToLower(t *testing.T) { runStringTests(t, ToLower, "ToLower", lowerTest func TestTrimSpace(t *testing.T) { runStringTests(t, TrimSpace, "TrimSpace", trimSpaceTests) } -type AddTest struct { - s, t string - cap int -} - -var addtests = []AddTest{ - AddTest{"", "", 0}, - AddTest{"a", "", 1}, - AddTest{"a", "b", 1}, - AddTest{"abc", "def", 100}, -} - -func TestAdd(t *testing.T) { - for _, test := range addtests { - b := make([]byte, len(test.s), test.cap) - for i := 0; i < len(test.s); i++ { - b[i] = test.s[i] - } - b = Add(b, []byte(test.t)) - if string(b) != test.s+test.t { - t.Errorf("Add(%q,%q) = %q", test.s, test.t, string(b)) - } - } -} - -func TestAddByte(t *testing.T) { - const N = 2e5 - b := make([]byte, 0) - for i := 0; i < N; i++ { - b = AddByte(b, byte(i)) - } - if len(b) != N { - t.Errorf("AddByte: too small; expected %d got %d", N, len(b)) - } - for i, c := range b { - if c != byte(i) { - t.Fatalf("AddByte: b[%d] should be %d is %d", i, c, byte(i)) - } - } -} - type RepeatTest struct { in, out string count int } var RepeatTests = []RepeatTest{ - RepeatTest{"", "", 0}, - RepeatTest{"", "", 1}, - RepeatTest{"", "", 2}, - RepeatTest{"-", "", 0}, - RepeatTest{"-", "-", 1}, - RepeatTest{"-", "----------", 10}, - RepeatTest{"abc ", "abc abc abc ", 3}, + {"", "", 0}, + {"", "", 1}, + {"", "", 2}, + {"-", "", 0}, + {"-", "-", 1}, + {"-", "----------", 10}, + {"abc ", "abc abc abc ", 3}, } func TestRepeat(t *testing.T) { @@ -553,13 +619,13 @@ type RunesTest struct { } var RunesTests = []RunesTest{ - RunesTest{"", []int{}, false}, - RunesTest{" ", []int{32}, false}, - RunesTest{"ABC", []int{65, 66, 67}, false}, - RunesTest{"abc", []int{97, 98, 99}, false}, - RunesTest{"\u65e5\u672c\u8a9e", []int{26085, 26412, 35486}, false}, - RunesTest{"ab\x80c", []int{97, 98, 0xFFFD, 99}, true}, - RunesTest{"ab\xc0c", []int{97, 98, 0xFFFD, 99}, true}, + {"", []int{}, false}, + {" ", []int{32}, false}, + {"ABC", []int{65, 66, 67}, false}, + {"abc", []int{97, 98, 99}, false}, + {"\u65e5\u672c\u8a9e", []int{26085, 26412, 35486}, false}, + {"ab\x80c", []int{97, 98, 0xFFFD, 99}, true}, + {"ab\xc0c", []int{97, 98, 0xFFFD, 99}, true}, } func TestRunes(t *testing.T) { @@ -587,26 +653,27 @@ type TrimTest struct { } var trimTests = []TrimTest{ - TrimTest{Trim, "abba", "a", "bb"}, - TrimTest{Trim, "abba", "ab", ""}, - TrimTest{TrimLeft, "abba", "ab", ""}, - TrimTest{TrimRight, "abba", "ab", ""}, - TrimTest{TrimLeft, "abba", "a", "bba"}, - TrimTest{TrimRight, "abba", "a", "abb"}, - TrimTest{Trim, "", "<>", "tag"}, - TrimTest{Trim, "* listitem", " *", "listitem"}, - TrimTest{Trim, `"quote"`, `"`, "quote"}, - TrimTest{Trim, "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"}, + {Trim, "abba", "a", "bb"}, + {Trim, "abba", "ab", ""}, + {TrimLeft, "abba", "ab", ""}, + {TrimRight, "abba", "ab", ""}, + {TrimLeft, "abba", "a", "bba"}, + {TrimRight, "abba", "a", "abb"}, + {Trim, "", "<>", "tag"}, + {Trim, "* listitem", " *", "listitem"}, + {Trim, `"quote"`, `"`, "quote"}, + {Trim, "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"}, //empty string tests - TrimTest{Trim, "abba", "", "abba"}, - TrimTest{Trim, "", "123", ""}, - TrimTest{Trim, "", "", ""}, - TrimTest{TrimLeft, "abba", "", "abba"}, - TrimTest{TrimLeft, "", "123", ""}, - TrimTest{TrimLeft, "", "", ""}, - TrimTest{TrimRight, "abba", "", "abba"}, - TrimTest{TrimRight, "", "123", ""}, - TrimTest{TrimRight, "", "", ""}, + {Trim, "abba", "", "abba"}, + {Trim, "", "123", ""}, + {Trim, "", "", ""}, + {TrimLeft, "abba", "", "abba"}, + {TrimLeft, "", "123", ""}, + {TrimLeft, "", "", ""}, + {TrimRight, "abba", "", "abba"}, + {TrimRight, "", "123", ""}, + {TrimRight, "", "", ""}, + {TrimRight, "☺\xc0", "☺", "☺\xc0"}, } func TestTrim(t *testing.T) { @@ -629,22 +696,90 @@ func TestTrim(t *testing.T) { } } +type predicate struct { + f func(r int) bool + name string +} + +var isSpace = predicate{unicode.IsSpace, "IsSpace"} +var isDigit = predicate{unicode.IsDigit, "IsDigit"} +var isUpper = predicate{unicode.IsUpper, "IsUpper"} +var isValidRune = predicate{ + func(r int) bool { + return r != utf8.RuneError + }, + "IsValidRune", +} + type TrimFuncTest struct { - f func(r int) bool - name, in, out string + f predicate + in, out string +} + +func not(p predicate) predicate { + return predicate{ + func(r int) bool { + return !p.f(r) + }, + "not " + p.name, + } } var trimFuncTests = []TrimFuncTest{ - TrimFuncTest{unicode.IsSpace, "IsSpace", space + " hello " + space, "hello"}, - TrimFuncTest{unicode.IsDigit, "IsDigit", "\u0e50\u0e5212hello34\u0e50\u0e51", "hello"}, - TrimFuncTest{unicode.IsUpper, "IsUpper", "\u2C6F\u2C6F\u2C6F\u2C6FABCDhelloEF\u2C6F\u2C6FGH\u2C6F\u2C6F", "hello"}, + {isSpace, space + " hello " + space, "hello"}, + {isDigit, "\u0e50\u0e5212hello34\u0e50\u0e51", "hello"}, + {isUpper, "\u2C6F\u2C6F\u2C6F\u2C6FABCDhelloEF\u2C6F\u2C6FGH\u2C6F\u2C6F", "hello"}, + {not(isSpace), "hello" + space + "hello", space}, + {not(isDigit), "hello\u0e50\u0e521234\u0e50\u0e51helo", "\u0e50\u0e521234\u0e50\u0e51"}, + {isValidRune, "ab\xc0a\xc0cd", "\xc0a\xc0"}, + {not(isValidRune), "\xc0a\xc0", "a"}, } func TestTrimFunc(t *testing.T) { for _, tc := range trimFuncTests { - actual := string(TrimFunc([]byte(tc.in), tc.f)) + actual := string(TrimFunc([]byte(tc.in), tc.f.f)) if actual != tc.out { - t.Errorf("TrimFunc(%q, %q) = %q; want %q", tc.in, tc.name, actual, tc.out) + t.Errorf("TrimFunc(%q, %q) = %q; want %q", tc.in, tc.f.name, actual, tc.out) + } + } +} + +type IndexFuncTest struct { + in string + f predicate + first, last int +} + +var indexFuncTests = []IndexFuncTest{ + {"", isValidRune, -1, -1}, + {"abc", isDigit, -1, -1}, + {"0123", isDigit, 0, 3}, + {"a1b", isDigit, 1, 1}, + {space, isSpace, 0, len(space) - 3}, // last rune in space is 3 bytes + {"\u0e50\u0e5212hello34\u0e50\u0e51", isDigit, 0, 18}, + {"\u2C6F\u2C6F\u2C6F\u2C6FABCDhelloEF\u2C6F\u2C6FGH\u2C6F\u2C6F", isUpper, 0, 34}, + {"12\u0e50\u0e52hello34\u0e50\u0e51", not(isDigit), 8, 12}, + + // tests of invalid UTF-8 + {"\x801", isDigit, 1, 1}, + {"\x80abc", isDigit, -1, -1}, + {"\xc0a\xc0", isValidRune, 1, 1}, + {"\xc0a\xc0", not(isValidRune), 0, 2}, + {"\xc0☺\xc0", not(isValidRune), 0, 4}, + {"\xc0☺\xc0\xc0", not(isValidRune), 0, 5}, + {"ab\xc0a\xc0cd", not(isValidRune), 2, 4}, + {"a\xe0\x80cd", not(isValidRune), 1, 2}, +} + +func TestIndexFunc(t *testing.T) { + for _, tc := range indexFuncTests { + first := IndexFunc([]byte(tc.in), tc.f.f) + if first != tc.first { + t.Errorf("IndexFunc(%q, %s) = %d; want %d", tc.in, tc.f.name, first, tc.first) + } + last := LastIndexFunc([]byte(tc.in), tc.f.f) + if last != tc.last { + t.Errorf("LastIndexFunc(%q, %s) = %d; want %d", tc.in, tc.f.name, last, tc.last) } } } @@ -657,25 +792,25 @@ type ReplaceTest struct { } var ReplaceTests = []ReplaceTest{ - ReplaceTest{"hello", "l", "L", 0, "hello"}, - ReplaceTest{"hello", "l", "L", -1, "heLLo"}, - ReplaceTest{"hello", "x", "X", -1, "hello"}, - ReplaceTest{"", "x", "X", -1, ""}, - ReplaceTest{"radar", "r", "", -1, "ada"}, - ReplaceTest{"", "", "<>", -1, "<>"}, - ReplaceTest{"banana", "a", "<>", -1, "b<>n<>n<>"}, - ReplaceTest{"banana", "a", "<>", 1, "b<>nana"}, - ReplaceTest{"banana", "a", "<>", 1000, "b<>n<>n<>"}, - ReplaceTest{"banana", "an", "<>", -1, "b<><>a"}, - ReplaceTest{"banana", "ana", "<>", -1, "b<>na"}, - ReplaceTest{"banana", "", "<>", -1, "<>b<>a<>n<>a<>n<>a<>"}, - ReplaceTest{"banana", "", "<>", 10, "<>b<>a<>n<>a<>n<>a<>"}, - ReplaceTest{"banana", "", "<>", 6, "<>b<>a<>n<>a<>n<>a"}, - ReplaceTest{"banana", "", "<>", 5, "<>b<>a<>n<>a<>na"}, - ReplaceTest{"banana", "", "<>", 1, "<>banana"}, - ReplaceTest{"banana", "a", "a", -1, "banana"}, - ReplaceTest{"banana", "a", "a", 1, "banana"}, - ReplaceTest{"☺☻☹", "", "<>", -1, "<>☺<>☻<>☹<>"}, + {"hello", "l", "L", 0, "hello"}, + {"hello", "l", "L", -1, "heLLo"}, + {"hello", "x", "X", -1, "hello"}, + {"", "x", "X", -1, ""}, + {"radar", "r", "", -1, "ada"}, + {"", "", "<>", -1, "<>"}, + {"banana", "a", "<>", -1, "b<>n<>n<>"}, + {"banana", "a", "<>", 1, "b<>nana"}, + {"banana", "a", "<>", 1000, "b<>n<>n<>"}, + {"banana", "an", "<>", -1, "b<><>a"}, + {"banana", "ana", "<>", -1, "b<>na"}, + {"banana", "", "<>", -1, "<>b<>a<>n<>a<>n<>a<>"}, + {"banana", "", "<>", 10, "<>b<>a<>n<>a<>n<>a<>"}, + {"banana", "", "<>", 6, "<>b<>a<>n<>a<>n<>a"}, + {"banana", "", "<>", 5, "<>b<>a<>n<>a<>na"}, + {"banana", "", "<>", 1, "<>banana"}, + {"banana", "a", "a", -1, "banana"}, + {"banana", "a", "a", 1, "banana"}, + {"☺☻☹", "", "<>", -1, "<>☺<>☻<>☹<>"}, } func TestReplace(t *testing.T) { @@ -685,3 +820,25 @@ func TestReplace(t *testing.T) { } } } + +type TitleTest struct { + in, out string +} + +var TitleTests = []TitleTest{ + {"", ""}, + {"a", "A"}, + {" aaa aaa aaa ", " Aaa Aaa Aaa "}, + {" Aaa Aaa Aaa ", " Aaa Aaa Aaa "}, + {"123a456", "123a456"}, + {"double-blind", "Double-Blind"}, + {"ÿøû", "Ÿøû"}, +} + +func TestTitle(t *testing.T) { + for _, tt := range TitleTests { + if s := string(Title([]byte(tt.in))); s != tt.out { + t.Errorf("Title(%q) = %q, want %q", tt.in, s, tt.out) + } + } +} diff --git a/src/pkg/cmath/Makefile b/src/pkg/cmath/Makefile index 1936fbda1..486caace4 100644 --- a/src/pkg/cmath/Makefile +++ b/src/pkg/cmath/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=cmath diff --git a/src/pkg/cmath/cmath_test.go b/src/pkg/cmath/cmath_test.go index 25e4f2254..93fac4e20 100644 --- a/src/pkg/cmath/cmath_test.go +++ b/src/pkg/cmath/cmath_test.go @@ -203,16 +203,16 @@ type ff struct { } var polar = []ff{ - ff{9.2022120669932650313380972e+00, 9.9909115046919291062461269e-01}, - ff{7.7438239742296106616261394e+00, -3.5762575021856971295156489e-02}, - ff{5.0182478202557746902556648e+00, -1.6259990074019058442232221e+00}, - ff{1.0861137372799545160704002e+01, 2.0502936359659111755031062e+00}, - ff{1.0070841084922199607011905e+01, 2.9483213155446756211881774e-01}, - ff{5.9922447613166942183705192e+00, 1.0605860367252556281902109e+00}, - ff{5.8978784056736762299945176e+00, 4.8084556083358307819310911e-01}, - ff{3.2822866700678709020367184e+00, 5.8969634164776659423195222e-01}, - ff{8.8756430028990417290744307e+00, -1.3636647724582455028314573e+00}, - ff{1.0011785496777731986390856e+01, 2.6210913895386013290915234e+00}, + {9.2022120669932650313380972e+00, 9.9909115046919291062461269e-01}, + {7.7438239742296106616261394e+00, -3.5762575021856971295156489e-02}, + {5.0182478202557746902556648e+00, -1.6259990074019058442232221e+00}, + {1.0861137372799545160704002e+01, 2.0502936359659111755031062e+00}, + {1.0070841084922199607011905e+01, 2.9483213155446756211881774e-01}, + {5.9922447613166942183705192e+00, 1.0605860367252556281902109e+00}, + {5.8978784056736762299945176e+00, 4.8084556083358307819310911e-01}, + {3.2822866700678709020367184e+00, 5.8969634164776659423195222e-01}, + {8.8756430028990417290744307e+00, -1.3636647724582455028314573e+00}, + {1.0011785496777731986390856e+01, 2.6210913895386013290915234e+00}, } var pow = []complex128{ (-2.499956739197529585028819e+00 + 1.759751724335650228957144e+00i), @@ -392,10 +392,10 @@ var vcPolarSC = []complex128{ NaN(), } var polarSC = []ff{ - ff{math.NaN(), math.NaN()}, + {math.NaN(), math.NaN()}, } var vcPowSC = [][2]complex128{ - [2]complex128{NaN(), NaN()}, + {NaN(), NaN()}, } var powSC = []complex128{ NaN(), @@ -483,175 +483,175 @@ func cAlike(a, b complex128) bool { func TestAbs(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Abs(vc[i]); !veryclose(abs[i], f) { - t.Errorf("Abs(%g) = %g, want %g\n", vc[i], f, abs[i]) + t.Errorf("Abs(%g) = %g, want %g", vc[i], f, abs[i]) } } for i := 0; i < len(vcAbsSC); i++ { if f := Abs(vcAbsSC[i]); !alike(absSC[i], f) { - t.Errorf("Abs(%g) = %g, want %g\n", vcAbsSC[i], f, absSC[i]) + t.Errorf("Abs(%g) = %g, want %g", vcAbsSC[i], f, absSC[i]) } } } func TestAcos(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Acos(vc[i]); !cSoclose(acos[i], f, 1e-14) { - t.Errorf("Acos(%g) = %g, want %g\n", vc[i], f, acos[i]) + t.Errorf("Acos(%g) = %g, want %g", vc[i], f, acos[i]) } } for i := 0; i < len(vcAcosSC); i++ { if f := Acos(vcAcosSC[i]); !cAlike(acosSC[i], f) { - t.Errorf("Acos(%g) = %g, want %g\n", vcAcosSC[i], f, acosSC[i]) + t.Errorf("Acos(%g) = %g, want %g", vcAcosSC[i], f, acosSC[i]) } } } func TestAcosh(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Acosh(vc[i]); !cSoclose(acosh[i], f, 1e-14) { - t.Errorf("Acosh(%g) = %g, want %g\n", vc[i], f, acosh[i]) + t.Errorf("Acosh(%g) = %g, want %g", vc[i], f, acosh[i]) } } for i := 0; i < len(vcAcoshSC); i++ { if f := Acosh(vcAcoshSC[i]); !cAlike(acoshSC[i], f) { - t.Errorf("Acosh(%g) = %g, want %g\n", vcAcoshSC[i], f, acoshSC[i]) + t.Errorf("Acosh(%g) = %g, want %g", vcAcoshSC[i], f, acoshSC[i]) } } } func TestAsin(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Asin(vc[i]); !cSoclose(asin[i], f, 1e-14) { - t.Errorf("Asin(%g) = %g, want %g\n", vc[i], f, asin[i]) + t.Errorf("Asin(%g) = %g, want %g", vc[i], f, asin[i]) } } for i := 0; i < len(vcAsinSC); i++ { if f := Asin(vcAsinSC[i]); !cAlike(asinSC[i], f) { - t.Errorf("Asin(%g) = %g, want %g\n", vcAsinSC[i], f, asinSC[i]) + t.Errorf("Asin(%g) = %g, want %g", vcAsinSC[i], f, asinSC[i]) } } } func TestAsinh(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Asinh(vc[i]); !cSoclose(asinh[i], f, 4e-15) { - t.Errorf("Asinh(%g) = %g, want %g\n", vc[i], f, asinh[i]) + t.Errorf("Asinh(%g) = %g, want %g", vc[i], f, asinh[i]) } } for i := 0; i < len(vcAsinhSC); i++ { if f := Asinh(vcAsinhSC[i]); !cAlike(asinhSC[i], f) { - t.Errorf("Asinh(%g) = %g, want %g\n", vcAsinhSC[i], f, asinhSC[i]) + t.Errorf("Asinh(%g) = %g, want %g", vcAsinhSC[i], f, asinhSC[i]) } } } func TestAtan(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Atan(vc[i]); !cVeryclose(atan[i], f) { - t.Errorf("Atan(%g) = %g, want %g\n", vc[i], f, atan[i]) + t.Errorf("Atan(%g) = %g, want %g", vc[i], f, atan[i]) } } for i := 0; i < len(vcAtanSC); i++ { if f := Atan(vcAtanSC[i]); !cAlike(atanSC[i], f) { - t.Errorf("Atan(%g) = %g, want %g\n", vcAtanSC[i], f, atanSC[i]) + t.Errorf("Atan(%g) = %g, want %g", vcAtanSC[i], f, atanSC[i]) } } } func TestAtanh(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Atanh(vc[i]); !cVeryclose(atanh[i], f) { - t.Errorf("Atanh(%g) = %g, want %g\n", vc[i], f, atanh[i]) + t.Errorf("Atanh(%g) = %g, want %g", vc[i], f, atanh[i]) } } for i := 0; i < len(vcAtanhSC); i++ { if f := Atanh(vcAtanhSC[i]); !cAlike(atanhSC[i], f) { - t.Errorf("Atanh(%g) = %g, want %g\n", vcAtanhSC[i], f, atanhSC[i]) + t.Errorf("Atanh(%g) = %g, want %g", vcAtanhSC[i], f, atanhSC[i]) } } } func TestConj(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Conj(vc[i]); !cVeryclose(conj[i], f) { - t.Errorf("Conj(%g) = %g, want %g\n", vc[i], f, conj[i]) + t.Errorf("Conj(%g) = %g, want %g", vc[i], f, conj[i]) } } for i := 0; i < len(vcConjSC); i++ { if f := Conj(vcConjSC[i]); !cAlike(conjSC[i], f) { - t.Errorf("Conj(%g) = %g, want %g\n", vcConjSC[i], f, conjSC[i]) + t.Errorf("Conj(%g) = %g, want %g", vcConjSC[i], f, conjSC[i]) } } } func TestCos(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Cos(vc[i]); !cSoclose(cos[i], f, 3e-15) { - t.Errorf("Cos(%g) = %g, want %g\n", vc[i], f, cos[i]) + t.Errorf("Cos(%g) = %g, want %g", vc[i], f, cos[i]) } } for i := 0; i < len(vcCosSC); i++ { if f := Cos(vcCosSC[i]); !cAlike(cosSC[i], f) { - t.Errorf("Cos(%g) = %g, want %g\n", vcCosSC[i], f, cosSC[i]) + t.Errorf("Cos(%g) = %g, want %g", vcCosSC[i], f, cosSC[i]) } } } func TestCosh(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Cosh(vc[i]); !cSoclose(cosh[i], f, 2e-15) { - t.Errorf("Cosh(%g) = %g, want %g\n", vc[i], f, cosh[i]) + t.Errorf("Cosh(%g) = %g, want %g", vc[i], f, cosh[i]) } } for i := 0; i < len(vcCoshSC); i++ { if f := Cosh(vcCoshSC[i]); !cAlike(coshSC[i], f) { - t.Errorf("Cosh(%g) = %g, want %g\n", vcCoshSC[i], f, coshSC[i]) + t.Errorf("Cosh(%g) = %g, want %g", vcCoshSC[i], f, coshSC[i]) } } } func TestExp(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Exp(vc[i]); !cSoclose(exp[i], f, 1e-15) { - t.Errorf("Exp(%g) = %g, want %g\n", vc[i], f, exp[i]) + t.Errorf("Exp(%g) = %g, want %g", vc[i], f, exp[i]) } } for i := 0; i < len(vcExpSC); i++ { if f := Exp(vcExpSC[i]); !cAlike(expSC[i], f) { - t.Errorf("Exp(%g) = %g, want %g\n", vcExpSC[i], f, expSC[i]) + t.Errorf("Exp(%g) = %g, want %g", vcExpSC[i], f, expSC[i]) } } } func TestIsNaN(t *testing.T) { for i := 0; i < len(vcIsNaNSC); i++ { if f := IsNaN(vcIsNaNSC[i]); isNaNSC[i] != f { - t.Errorf("IsNaN(%g) = %g, want %g\n", vcIsNaNSC[i], f, isNaNSC[i]) + t.Errorf("IsNaN(%g) = %g, want %g", vcIsNaNSC[i], f, isNaNSC[i]) } } } func TestLog(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Log(vc[i]); !cVeryclose(log[i], f) { - t.Errorf("Log(%g) = %g, want %g\n", vc[i], f, log[i]) + t.Errorf("Log(%g) = %g, want %g", vc[i], f, log[i]) } } for i := 0; i < len(vcLogSC); i++ { if f := Log(vcLogSC[i]); !cAlike(logSC[i], f) { - t.Errorf("Log(%g) = %g, want %g\n", vcLogSC[i], f, logSC[i]) + t.Errorf("Log(%g) = %g, want %g", vcLogSC[i], f, logSC[i]) } } } func TestLog10(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Log10(vc[i]); !cVeryclose(log10[i], f) { - t.Errorf("Log10(%g) = %g, want %g\n", vc[i], f, log10[i]) + t.Errorf("Log10(%g) = %g, want %g", vc[i], f, log10[i]) } } for i := 0; i < len(vcLog10SC); i++ { if f := Log10(vcLog10SC[i]); !cAlike(log10SC[i], f) { - t.Errorf("Log10(%g) = %g, want %g\n", vcLog10SC[i], f, log10SC[i]) + t.Errorf("Log10(%g) = %g, want %g", vcLog10SC[i], f, log10SC[i]) } } } func TestPolar(t *testing.T) { for i := 0; i < len(vc); i++ { if r, theta := Polar(vc[i]); !veryclose(polar[i].r, r) && !veryclose(polar[i].theta, theta) { - t.Errorf("Polar(%g) = %g, %g want %g, %g\n", vc[i], r, theta, polar[i].r, polar[i].theta) + t.Errorf("Polar(%g) = %g, %g want %g, %g", vc[i], r, theta, polar[i].r, polar[i].theta) } } for i := 0; i < len(vcPolarSC); i++ { if r, theta := Polar(vcPolarSC[i]); !alike(polarSC[i].r, r) && !alike(polarSC[i].theta, theta) { - t.Errorf("Polar(%g) = %g, %g, want %g, %g\n", vcPolarSC[i], r, theta, polarSC[i].r, polarSC[i].theta) + t.Errorf("Polar(%g) = %g, %g, want %g, %g", vcPolarSC[i], r, theta, polarSC[i].r, polarSC[i].theta) } } } @@ -659,84 +659,84 @@ func TestPow(t *testing.T) { var a = cmplx(float64(3), float64(3)) for i := 0; i < len(vc); i++ { if f := Pow(a, vc[i]); !cSoclose(pow[i], f, 4e-15) { - t.Errorf("Pow(%g, %g) = %g, want %g\n", a, vc[i], f, pow[i]) + t.Errorf("Pow(%g, %g) = %g, want %g", a, vc[i], f, pow[i]) } } for i := 0; i < len(vcPowSC); i++ { if f := Pow(vcPowSC[i][0], vcPowSC[i][0]); !cAlike(powSC[i], f) { - t.Errorf("Pow(%g, %g) = %g, want %g\n", vcPowSC[i][0], vcPowSC[i][0], f, powSC[i]) + t.Errorf("Pow(%g, %g) = %g, want %g", vcPowSC[i][0], vcPowSC[i][0], f, powSC[i]) } } } func TestRect(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Rect(polar[i].r, polar[i].theta); !cVeryclose(vc[i], f) { - t.Errorf("Rect(%g, %g) = %g want %g\n", polar[i].r, polar[i].theta, f, vc[i]) + t.Errorf("Rect(%g, %g) = %g want %g", polar[i].r, polar[i].theta, f, vc[i]) } } for i := 0; i < len(vcPolarSC); i++ { if f := Rect(polarSC[i].r, polarSC[i].theta); !cAlike(vcPolarSC[i], f) { - t.Errorf("Rect(%g, %g) = %g, want %g\n", polarSC[i].r, polarSC[i].theta, f, vcPolarSC[i]) + t.Errorf("Rect(%g, %g) = %g, want %g", polarSC[i].r, polarSC[i].theta, f, vcPolarSC[i]) } } } func TestSin(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Sin(vc[i]); !cSoclose(sin[i], f, 2e-15) { - t.Errorf("Sin(%g) = %g, want %g\n", vc[i], f, sin[i]) + t.Errorf("Sin(%g) = %g, want %g", vc[i], f, sin[i]) } } for i := 0; i < len(vcSinSC); i++ { if f := Sin(vcSinSC[i]); !cAlike(sinSC[i], f) { - t.Errorf("Sin(%g) = %g, want %g\n", vcSinSC[i], f, sinSC[i]) + t.Errorf("Sin(%g) = %g, want %g", vcSinSC[i], f, sinSC[i]) } } } func TestSinh(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Sinh(vc[i]); !cSoclose(sinh[i], f, 2e-15) { - t.Errorf("Sinh(%g) = %g, want %g\n", vc[i], f, sinh[i]) + t.Errorf("Sinh(%g) = %g, want %g", vc[i], f, sinh[i]) } } for i := 0; i < len(vcSinhSC); i++ { if f := Sinh(vcSinhSC[i]); !cAlike(sinhSC[i], f) { - t.Errorf("Sinh(%g) = %g, want %g\n", vcSinhSC[i], f, sinhSC[i]) + t.Errorf("Sinh(%g) = %g, want %g", vcSinhSC[i], f, sinhSC[i]) } } } func TestSqrt(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Sqrt(vc[i]); !cVeryclose(sqrt[i], f) { - t.Errorf("Sqrt(%g) = %g, want %g\n", vc[i], f, sqrt[i]) + t.Errorf("Sqrt(%g) = %g, want %g", vc[i], f, sqrt[i]) } } for i := 0; i < len(vcSqrtSC); i++ { if f := Sqrt(vcSqrtSC[i]); !cAlike(sqrtSC[i], f) { - t.Errorf("Sqrt(%g) = %g, want %g\n", vcSqrtSC[i], f, sqrtSC[i]) + t.Errorf("Sqrt(%g) = %g, want %g", vcSqrtSC[i], f, sqrtSC[i]) } } } func TestTan(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Tan(vc[i]); !cSoclose(tan[i], f, 3e-15) { - t.Errorf("Tan(%g) = %g, want %g\n", vc[i], f, tan[i]) + t.Errorf("Tan(%g) = %g, want %g", vc[i], f, tan[i]) } } for i := 0; i < len(vcTanSC); i++ { if f := Tan(vcTanSC[i]); !cAlike(tanSC[i], f) { - t.Errorf("Tan(%g) = %g, want %g\n", vcTanSC[i], f, tanSC[i]) + t.Errorf("Tan(%g) = %g, want %g", vcTanSC[i], f, tanSC[i]) } } } func TestTanh(t *testing.T) { for i := 0; i < len(vc); i++ { if f := Tanh(vc[i]); !cSoclose(tanh[i], f, 2e-15) { - t.Errorf("Tanh(%g) = %g, want %g\n", vc[i], f, tanh[i]) + t.Errorf("Tanh(%g) = %g, want %g", vc[i], f, tanh[i]) } } for i := 0; i < len(vcTanhSC); i++ { if f := Tanh(vcTanhSC[i]); !cAlike(tanhSC[i], f) { - t.Errorf("Tanh(%g) = %g, want %g\n", vcTanhSC[i], f, tanhSC[i]) + t.Errorf("Tanh(%g) = %g, want %g", vcTanhSC[i], f, tanhSC[i]) } } } diff --git a/src/pkg/compress/flate/Makefile b/src/pkg/compress/flate/Makefile index 6f9ee74d4..197828a92 100644 --- a/src/pkg/compress/flate/Makefile +++ b/src/pkg/compress/flate/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=compress/flate GOFILES=\ diff --git a/src/pkg/compress/flate/deflate.go b/src/pkg/compress/flate/deflate.go index 79952e713..591b35c44 100644 --- a/src/pkg/compress/flate/deflate.go +++ b/src/pkg/compress/flate/deflate.go @@ -53,19 +53,19 @@ type compressionLevel struct { } var levels = []compressionLevel{ - compressionLevel{}, // 0 + {}, // 0 // For levels 1-3 we don't bother trying with lazy matches - compressionLevel{3, 0, 8, 4, 4}, - compressionLevel{3, 0, 16, 8, 5}, - compressionLevel{3, 0, 32, 32, 6}, + {3, 0, 8, 4, 4}, + {3, 0, 16, 8, 5}, + {3, 0, 32, 32, 6}, // Levels 4-9 use increasingly more lazy matching // and increasingly stringent conditions for "good enough". - compressionLevel{4, 4, 16, 16, math.MaxInt32}, - compressionLevel{8, 16, 32, 32, math.MaxInt32}, - compressionLevel{8, 16, 128, 128, math.MaxInt32}, - compressionLevel{8, 32, 128, 256, math.MaxInt32}, - compressionLevel{32, 128, 258, 1024, math.MaxInt32}, - compressionLevel{32, 258, 258, 4096, math.MaxInt32}, + {4, 4, 16, 16, math.MaxInt32}, + {8, 16, 32, 32, math.MaxInt32}, + {8, 16, 128, 128, math.MaxInt32}, + {8, 32, 128, 256, math.MaxInt32}, + {32, 128, 258, 1024, math.MaxInt32}, + {32, 258, 258, 4096, math.MaxInt32}, } func (sw *syncPipeWriter) Close() os.Error { @@ -89,6 +89,10 @@ type compressor struct { // (1 << logWindowSize) - 1. windowMask int + eof bool // has eof been reached on input? + sync bool // writer wants to flush + syncChan chan os.Error + // hashHead[hashValue] contains the largest inputIndex with the specified hash value hashHead []int @@ -124,6 +128,9 @@ func (d *compressor) flush() os.Error { } func (d *compressor) fillWindow(index int) (int, os.Error) { + if d.sync { + return index, nil + } wSize := d.windowMask + 1 if index >= wSize+wSize-(minMatchLength+maxMatchLength) { // shift the window by wSize @@ -142,12 +149,14 @@ func (d *compressor) fillWindow(index int) (int, os.Error) { d.hashPrev[i] = max(h-wSize, -1) } } - var count int - var err os.Error - count, err = io.ReadAtLeast(d.r, d.window[d.windowEnd:], 1) + count, err := d.r.Read(d.window[d.windowEnd:]) d.windowEnd += count + if count == 0 && err == nil { + d.sync = true + } if err == os.EOF { - return index, nil + d.eof = true + err = nil } return index, err } @@ -227,10 +236,17 @@ func (d *compressor) storedDeflate() os.Error { buf := make([]byte, maxStoreBlockSize) for { n, err := d.r.Read(buf) - if n > 0 { + if n == 0 && err == nil { + d.sync = true + } + if n > 0 || d.sync { if err := d.writeStoredBlock(buf[0:n]); err != nil { return err } + if d.sync { + d.syncChan <- nil + d.sync = false + } } if err != nil { if err == os.EOF { @@ -275,6 +291,7 @@ func (d *compressor) doDeflate() (err os.Error) { hash = int(d.window[index])< windowEnd { panic("index > windowEnd") @@ -291,7 +308,31 @@ func (d *compressor) doDeflate() (err os.Error) { maxInsertIndex = windowEnd - (minMatchLength - 1) lookahead = windowEnd - index if lookahead == 0 { - break + // Flush current output block if any. + if byteAvailable { + // There is still one pending token that needs to be flushed + tokens[ti] = literalToken(uint32(d.window[index-1]) & 0xFF) + ti++ + byteAvailable = false + } + if ti > 0 { + if err = d.writeBlock(tokens[0:ti], index, false); err != nil { + return + } + ti = 0 + } + if d.sync { + d.w.writeStoredHeader(0, false) + d.w.flush() + d.syncChan <- d.w.err + d.sync = false + } + + // If this was only a sync (not at EOF) keep going. + if !d.eof { + continue + } + break Loop } } if index < maxInsertIndex { @@ -383,23 +424,11 @@ func (d *compressor) doDeflate() (err os.Error) { byteAvailable = true } } - - } - if byteAvailable { - // There is still one pending token that needs to be flushed - tokens[ti] = literalToken(uint32(d.window[index-1]) & 0xFF) - ti++ - } - - if ti > 0 { - if err = d.writeBlock(tokens[0:ti], index, false); err != nil { - return - } } return } -func (d *compressor) compressor(r io.Reader, w io.Writer, level int, logWindowSize uint) (err os.Error) { +func (d *compressor) compress(r io.Reader, w io.Writer, level int, logWindowSize uint) (err os.Error) { d.r = r d.w = newHuffmanBitWriter(w) d.level = level @@ -417,6 +446,10 @@ func (d *compressor) compressor(r io.Reader, w io.Writer, level int, logWindowSi return WrongValueError{"level", 0, 9, int32(level)} } + if d.sync { + d.syncChan <- err + d.sync = false + } if err != nil { return err } @@ -426,16 +459,63 @@ func (d *compressor) compressor(r io.Reader, w io.Writer, level int, logWindowSi return d.flush() } -func newCompressor(w io.Writer, level int, logWindowSize uint) io.WriteCloser { +// NewWriter returns a new Writer compressing +// data at the given level. Following zlib, levels +// range from 1 (BestSpeed) to 9 (BestCompression); +// higher levels typically run slower but compress more. +// Level 0 (NoCompression) does not attempt any +// compression; it only adds the necessary DEFLATE framing. +func NewWriter(w io.Writer, level int) *Writer { + const logWindowSize = logMaxOffsetSize var d compressor + d.syncChan = make(chan os.Error, 1) pr, pw := syncPipe() go func() { - err := d.compressor(pr, w, level, logWindowSize) + err := d.compress(pr, w, level, logWindowSize) pr.CloseWithError(err) }() - return pw + return &Writer{pw, &d} +} + +// A Writer takes data written to it and writes the compressed +// form of that data to an underlying writer (see NewWriter). +type Writer struct { + w *syncPipeWriter + d *compressor +} + +// Write writes data to w, which will eventually write the +// compressed form of data to its underlying writer. +func (w *Writer) Write(data []byte) (n int, err os.Error) { + if len(data) == 0 { + // no point, and nil interferes with sync + return + } + return w.w.Write(data) +} + +// Flush flushes any pending compressed data to the underlying writer. +// It is useful mainly in compressed network protocols, to ensure that +// a remote reader has enough data to reconstruct a packet. +// Flush does not return until the data has been written. +// If the underlying writer returns an error, Flush returns that error. +// +// In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH. +func (w *Writer) Flush() os.Error { + // For more about flushing: + // http://www.bolet.org/~pornin/deflate-flush.html + if w.d.sync { + panic("compress/flate: double Flush") + } + _, err := w.w.Write(nil) + err1 := <-w.d.syncChan + if err == nil { + err = err1 + } + return err } -func NewWriter(w io.Writer, level int) io.WriteCloser { - return newCompressor(w, level, logMaxOffsetSize) +// Close flushes and closes the writer. +func (w *Writer) Close() os.Error { + return w.w.Close() } diff --git a/src/pkg/compress/flate/deflate_test.go b/src/pkg/compress/flate/deflate_test.go index 9718d2f5a..3db955609 100644 --- a/src/pkg/compress/flate/deflate_test.go +++ b/src/pkg/compress/flate/deflate_test.go @@ -7,8 +7,10 @@ package flate import ( "bytes" "fmt" + "io" "io/ioutil" "os" + "sync" "testing" ) @@ -79,7 +81,7 @@ func getLargeDataChunk() []byte { func TestDeflate(t *testing.T) { for _, h := range deflateTests { - buffer := bytes.NewBuffer([]byte{}) + buffer := bytes.NewBuffer(nil) w := NewWriter(buffer, h.level) w.Write(h.in) w.Close() @@ -90,21 +92,144 @@ func TestDeflate(t *testing.T) { } } +type syncBuffer struct { + buf bytes.Buffer + mu sync.RWMutex + closed bool + ready chan bool +} + +func newSyncBuffer() *syncBuffer { + return &syncBuffer{ready: make(chan bool, 1)} +} + +func (b *syncBuffer) Read(p []byte) (n int, err os.Error) { + for { + b.mu.RLock() + n, err = b.buf.Read(p) + b.mu.RUnlock() + if n > 0 || b.closed { + return + } + <-b.ready + } + panic("unreachable") +} + +func (b *syncBuffer) Write(p []byte) (n int, err os.Error) { + n, err = b.buf.Write(p) + _ = b.ready <- true + return +} + +func (b *syncBuffer) WriteMode() { + b.mu.Lock() +} + +func (b *syncBuffer) ReadMode() { + b.mu.Unlock() + _ = b.ready <- true +} + +func (b *syncBuffer) Close() os.Error { + b.closed = true + _ = b.ready <- true + return nil +} + +func testSync(t *testing.T, level int, input []byte, name string) { + if len(input) == 0 { + return + } + + t.Logf("--testSync %d, %d, %s", level, len(input), name) + buf := newSyncBuffer() + buf1 := new(bytes.Buffer) + buf.WriteMode() + w := NewWriter(io.MultiWriter(buf, buf1), level) + r := NewReader(buf) + + // Write half the input and read back. + for i := 0; i < 2; i++ { + var lo, hi int + if i == 0 { + lo, hi = 0, (len(input)+1)/2 + } else { + lo, hi = (len(input)+1)/2, len(input) + } + t.Logf("#%d: write %d-%d", i, lo, hi) + if _, err := w.Write(input[lo:hi]); err != nil { + t.Errorf("testSync: write: %v", err) + return + } + if i == 0 { + if err := w.Flush(); err != nil { + t.Errorf("testSync: flush: %v", err) + return + } + } else { + if err := w.Close(); err != nil { + t.Errorf("testSync: close: %v", err) + } + } + buf.ReadMode() + out := make([]byte, hi-lo+1) + m, err := io.ReadAtLeast(r, out, hi-lo) + t.Logf("#%d: read %d", i, m) + if m != hi-lo || err != nil { + t.Errorf("testSync/%d (%d, %d, %s): read %d: %d, %v (%d left)", i, level, len(input), name, hi-lo, m, err, buf.buf.Len()) + return + } + if !bytes.Equal(input[lo:hi], out[:hi-lo]) { + t.Errorf("testSync/%d: read wrong bytes: %x vs %x", i, input[lo:hi], out[:hi-lo]) + return + } + if i == 0 && buf.buf.Len() != 0 { + t.Errorf("testSync/%d (%d, %d, %s): extra data after %d", i, level, len(input), name, hi-lo) + } + buf.WriteMode() + } + buf.ReadMode() + out := make([]byte, 10) + if n, err := r.Read(out); n > 0 || err != os.EOF { + t.Errorf("testSync (%d, %d, %s): final Read: %d, %v (hex: %x)", level, len(input), name, n, err, out[0:n]) + } + if buf.buf.Len() != 0 { + t.Errorf("testSync (%d, %d, %s): extra data at end", level, len(input), name) + } + r.Close() + + // stream should work for ordinary reader too + r = NewReader(buf1) + out, err := ioutil.ReadAll(r) + if err != nil { + t.Errorf("testSync: read: %s", err) + return + } + r.Close() + if !bytes.Equal(input, out) { + t.Errorf("testSync: decompress(compress(data)) != data: level=%d input=%s", level, name) + } +} + + func testToFromWithLevel(t *testing.T, level int, input []byte, name string) os.Error { - buffer := bytes.NewBuffer([]byte{}) + buffer := bytes.NewBuffer(nil) w := NewWriter(buffer, level) w.Write(input) w.Close() - decompressor := NewReader(buffer) - decompressed, err := ioutil.ReadAll(decompressor) + r := NewReader(buffer) + out, err := ioutil.ReadAll(r) if err != nil { - t.Errorf("reading decompressor: %s", err) + t.Errorf("read: %s", err) return err } - decompressor.Close() - if bytes.Compare(input, decompressed) != 0 { + r.Close() + if !bytes.Equal(input, out) { t.Errorf("decompress(compress(data)) != data: level=%d input=%s", level, name) } + + testSync(t, level, input, name) return nil } diff --git a/src/pkg/compress/flate/huffman_code.go b/src/pkg/compress/flate/huffman_code.go index 38cbf4396..6be605f0a 100644 --- a/src/pkg/compress/flate/huffman_code.go +++ b/src/pkg/compress/flate/huffman_code.go @@ -270,7 +270,6 @@ func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 { } } - // Somethings is wrong if at the end, the top level is null or hasn't used // all of the leaves. if top.lastChain.leafCount != n { diff --git a/src/pkg/compress/flate/inflate.go b/src/pkg/compress/flate/inflate.go index f0bd00531..7dc8cf93b 100644 --- a/src/pkg/compress/flate/inflate.go +++ b/src/pkg/compress/flate/inflate.go @@ -47,7 +47,7 @@ func (e *ReadError) String() string { // A WriteError reports an error encountered while writing output. type WriteError struct { Offset int64 // byte offset where error occurred - Error os.Error // error returned by underlying Read + Error os.Error // error returned by underlying Write } func (e *WriteError) String() string { @@ -102,7 +102,6 @@ func (h *huffmanDecoder) init(bits []int) bool { h.min = min h.max = max - // For each code range, compute // nextcode (first code of that length), // limit (last code of that length), and @@ -218,6 +217,7 @@ type decompressor struct { // Output history, buffer. hist [maxHist]byte hp int // current output position in buffer + hw int // have written hist[0:hw] already hfull bool // buffer has filled at least once // Temporary buffer (avoids repeated allocation). @@ -498,6 +498,11 @@ func (f *decompressor) dataBlock() os.Error { return CorruptInputError(f.roffset) } + if n == 0 { + // 0-length block means sync + return f.flush() + } + // Read len bytes into history, // writing as history fills. for n > 0 { @@ -561,19 +566,23 @@ func (f *decompressor) huffSym(h *huffmanDecoder) (int, os.Error) { // Flush any buffered output to the underlying writer. func (f *decompressor) flush() os.Error { - if f.hp == 0 { + if f.hw == f.hp { return nil } - n, err := f.w.Write(f.hist[0:f.hp]) - if n != f.hp && err == nil { + n, err := f.w.Write(f.hist[f.hw:f.hp]) + if n != f.hp-f.hw && err == nil { err = io.ErrShortWrite } if err != nil { return &WriteError{f.woffset, err} } - f.woffset += int64(f.hp) - f.hp = 0 - f.hfull = true + f.woffset += int64(f.hp - f.hw) + f.hw = f.hp + if f.hp == len(f.hist) { + f.hp = 0 + f.hw = 0 + f.hfull = true + } return nil } @@ -584,9 +593,9 @@ func makeReader(r io.Reader) Reader { return bufio.NewReader(r) } -// Inflate reads DEFLATE-compressed data from r and writes +// decompress reads DEFLATE-compressed data from r and writes // the uncompressed data to w. -func (f *decompressor) decompressor(r io.Reader, w io.Writer) os.Error { +func (f *decompressor) decompress(r io.Reader, w io.Writer) os.Error { f.r = makeReader(r) f.w = w f.woffset = 0 @@ -606,6 +615,6 @@ func (f *decompressor) decompressor(r io.Reader, w io.Writer) os.Error { func NewReader(r io.Reader) io.ReadCloser { var f decompressor pr, pw := io.Pipe() - go func() { pw.CloseWithError(f.decompressor(r, pw)) }() + go func() { pw.CloseWithError(f.decompress(r, pw)) }() return pr } diff --git a/src/pkg/compress/gzip/Makefile b/src/pkg/compress/gzip/Makefile index bb4705a8f..b671fc72c 100644 --- a/src/pkg/compress/gzip/Makefile +++ b/src/pkg/compress/gzip/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=compress/gzip GOFILES=\ diff --git a/src/pkg/compress/gzip/gunzip_test.go b/src/pkg/compress/gzip/gunzip_test.go index d5ac0cc14..1c08c7374 100644 --- a/src/pkg/compress/gzip/gunzip_test.go +++ b/src/pkg/compress/gzip/gunzip_test.go @@ -20,7 +20,7 @@ type gunzipTest struct { } var gunzipTests = []gunzipTest{ - gunzipTest{ // has 1 empty fixed-huffman block + { // has 1 empty fixed-huffman block "empty.txt", "empty.txt", "", @@ -32,7 +32,7 @@ var gunzipTests = []gunzipTest{ }, nil, }, - gunzipTest{ // has 1 non-empty fixed huffman block + { // has 1 non-empty fixed huffman block "hello.txt", "hello.txt", "hello world\n", @@ -46,7 +46,7 @@ var gunzipTests = []gunzipTest{ }, nil, }, - gunzipTest{ // concatenation + { // concatenation "hello.txt", "hello.txt x2", "hello world\n" + @@ -67,7 +67,7 @@ var gunzipTests = []gunzipTest{ }, nil, }, - gunzipTest{ // has a fixed huffman block with some length-distance pairs + { // has a fixed huffman block with some length-distance pairs "shesells.txt", "shesells.txt", "she sells seashells by the seashore\n", @@ -83,7 +83,7 @@ var gunzipTests = []gunzipTest{ }, nil, }, - gunzipTest{ // has dynamic huffman blocks + { // has dynamic huffman blocks "gettysburg", "gettysburg", " Four score and seven years ago our fathers brought forth on\n" + @@ -221,7 +221,7 @@ var gunzipTests = []gunzipTest{ }, nil, }, - gunzipTest{ // has 1 non-empty fixed huffman block then garbage + { // has 1 non-empty fixed huffman block then garbage "hello.txt", "hello.txt + garbage", "hello world\n", @@ -235,7 +235,7 @@ var gunzipTests = []gunzipTest{ }, HeaderError, }, - gunzipTest{ // has 1 non-empty fixed huffman block not enough header + { // has 1 non-empty fixed huffman block not enough header "hello.txt", "hello.txt + garbage", "hello world\n", @@ -249,7 +249,7 @@ var gunzipTests = []gunzipTest{ }, io.ErrUnexpectedEOF, }, - gunzipTest{ // has 1 non-empty fixed huffman block but corrupt checksum + { // has 1 non-empty fixed huffman block but corrupt checksum "hello.txt", "hello.txt + corrupt checksum", "hello world\n", @@ -263,7 +263,7 @@ var gunzipTests = []gunzipTest{ }, ChecksumError, }, - gunzipTest{ // has 1 non-empty fixed huffman block but corrupt size + { // has 1 non-empty fixed huffman block but corrupt size "hello.txt", "hello.txt + corrupt size", "hello world\n", diff --git a/src/pkg/compress/zlib/Makefile b/src/pkg/compress/zlib/Makefile index 4cfda4f1b..791072d34 100644 --- a/src/pkg/compress/zlib/Makefile +++ b/src/pkg/compress/zlib/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=compress/zlib GOFILES=\ diff --git a/src/pkg/compress/zlib/reader_test.go b/src/pkg/compress/zlib/reader_test.go index 8ae8d0070..eaefc3a36 100644 --- a/src/pkg/compress/zlib/reader_test.go +++ b/src/pkg/compress/zlib/reader_test.go @@ -22,13 +22,13 @@ type zlibTest struct { // http://www.zlib.net/zpipe.c var zlibTests = []zlibTest{ - zlibTest{ + { "empty", "", []byte{0x78, 0x9c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01}, nil, }, - zlibTest{ + { "goodbye", "goodbye, world", []byte{ @@ -38,25 +38,25 @@ var zlibTests = []zlibTest{ }, nil, }, - zlibTest{ + { "bad header", "", []byte{0x78, 0x9f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01}, HeaderError, }, - zlibTest{ + { "bad checksum", "", []byte{0x78, 0x9c, 0x03, 0x00, 0x00, 0x00, 0x00, 0xff}, ChecksumError, }, - zlibTest{ + { "not enough data", "", []byte{0x78, 0x9c, 0x03, 0x00, 0x00, 0x00}, io.ErrUnexpectedEOF, }, - zlibTest{ + { "excess data is silently ignored", "", []byte{ diff --git a/src/pkg/container/heap/Makefile b/src/pkg/container/heap/Makefile index 244ebae06..4291d1122 100644 --- a/src/pkg/container/heap/Makefile +++ b/src/pkg/container/heap/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=container/heap GOFILES=\ diff --git a/src/pkg/container/list/Makefile b/src/pkg/container/list/Makefile index 2d5b35715..7fcd5f99a 100644 --- a/src/pkg/container/list/Makefile +++ b/src/pkg/container/list/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=container/list GOFILES=\ diff --git a/src/pkg/container/list/list.go b/src/pkg/container/list/list.go index 40c968099..c1ebcddaa 100644 --- a/src/pkg/container/list/list.go +++ b/src/pkg/container/list/list.go @@ -3,6 +3,12 @@ // license that can be found in the LICENSE file. // The list package implements a doubly linked list. +// +// To iterate over a list (where l is a *List): +// for e := l.Front(); e != nil; e = e.Next() { +// // do something with e.Value +// } +// package list // Element is an element in the linked list. @@ -11,8 +17,8 @@ type Element struct { // The front of the list has prev = nil, and the back has next = nil. next, prev *Element - // A unique ID for the list to which this element belongs. - id *byte + // The list to which this element belongs. + list *List // The contents of this list element. Value interface{} @@ -25,10 +31,10 @@ func (e *Element) Next() *Element { return e.next } func (e *Element) Prev() *Element { return e.prev } // List represents a doubly linked list. +// The zero value for List is an empty list ready to use. type List struct { front, back *Element len int - id *byte } // Init initializes or clears a List. @@ -36,12 +42,11 @@ func (l *List) Init() *List { l.front = nil l.back = nil l.len = 0 - l.id = new(byte) return l } // New returns an initialized list. -func New() *List { return new(List).Init() } +func New() *List { return new(List) } // Front returns the first element in the list. func (l *List) Front() *Element { return l.front } @@ -49,9 +54,19 @@ func (l *List) Front() *Element { return l.front } // Back returns the last element in the list. func (l *List) Back() *Element { return l.back } -// Remove removes the element from the list. -func (l *List) Remove(e *Element) { - if e.id != l.id { +// Remove removes the element from the list +// and returns its Value. +func (l *List) Remove(e *Element) interface{} { + l.remove(e) + e.list = nil // do what remove does not + return e.Value +} + +// remove the element from the list, but do not clear the Element's list field. +// This is so that other List methods may use remove when relocating Elements +// without needing to restore the list field. +func (l *List) remove(e *Element) { + if e.list != l { return } if e.prev == nil { @@ -120,78 +135,59 @@ func (l *List) insertBack(e *Element) { // PushFront inserts the value at the front of the list and returns a new Element containing the value. func (l *List) PushFront(value interface{}) *Element { - if l.id == nil { - l.Init() - } - e := &Element{nil, nil, l.id, value} + e := &Element{nil, nil, l, value} l.insertFront(e) return e } // PushBack inserts the value at the back of the list and returns a new Element containing the value. func (l *List) PushBack(value interface{}) *Element { - if l.id == nil { - l.Init() - } - e := &Element{nil, nil, l.id, value} + e := &Element{nil, nil, l, value} l.insertBack(e) return e } // InsertBefore inserts the value immediately before mark and returns a new Element containing the value. func (l *List) InsertBefore(value interface{}, mark *Element) *Element { - if mark.id != l.id { + if mark.list != l { return nil } - e := &Element{nil, nil, l.id, value} + e := &Element{nil, nil, l, value} l.insertBefore(e, mark) return e } // InsertAfter inserts the value immediately after mark and returns a new Element containing the value. func (l *List) InsertAfter(value interface{}, mark *Element) *Element { - if mark.id != l.id { + if mark.list != l { return nil } - e := &Element{nil, nil, l.id, value} + e := &Element{nil, nil, l, value} l.insertAfter(e, mark) return e } // MoveToFront moves the element to the front of the list. func (l *List) MoveToFront(e *Element) { - if e.id != l.id || l.front == e { + if e.list != l || l.front == e { return } - l.Remove(e) + l.remove(e) l.insertFront(e) } // MoveToBack moves the element to the back of the list. func (l *List) MoveToBack(e *Element) { - if e.id != l.id || l.back == e { + if e.list != l || l.back == e { return } - l.Remove(e) + l.remove(e) l.insertBack(e) } // Len returns the number of elements in the list. func (l *List) Len() int { return l.len } -func (l *List) iterate(c chan<- interface{}) { - for e := l.front; e != nil; e = e.next { - c <- e.Value - } - close(c) -} - -func (l *List) Iter() <-chan interface{} { - c := make(chan interface{}) - go l.iterate(c) - return c -} - // PushBackList inserts each element of ol at the back of the list. func (l *List) PushBackList(ol *List) { last := ol.Back() diff --git a/src/pkg/container/list/list_test.go b/src/pkg/container/list/list_test.go index bf35c9dd9..1d44ff84e 100644 --- a/src/pkg/container/list/list_test.go +++ b/src/pkg/container/list/list_test.go @@ -23,8 +23,7 @@ func checkListPointers(t *testing.T, l *List, es []*Element) { t.Errorf("l.back = %v, want %v", l.back, last) } - for i := 0; i < len(es); i++ { - e := es[i] + for i, e := range es { var e_prev, e_next *Element = nil, nil if i > 0 { e_prev = es[i-1] @@ -116,8 +115,8 @@ func TestList(t *testing.T) { // Check standard iteration. sum := 0 - for e := range l.Iter() { - if i, ok := e.(int); ok { + for e := l.Front(); e != nil; e = e.Next() { + if i, ok := e.Value.(int); ok { sum += i } } @@ -141,7 +140,8 @@ func checkList(t *testing.T, l *List, es []interface{}) { return } i := 0 - for le := range l.Iter() { + for e := l.Front(); e != nil; e = e.Next() { + le := e.Value.(int) if le != es[i] { t.Errorf("elt #%d has value=%v, want %v", i, le, es[i]) } @@ -193,3 +193,17 @@ func TestExtending(t *testing.T) { l1.PushFrontList(l3) checkList(t, l1, []interface{}{1, 2, 3}) } + +func TestRemove(t *testing.T) { + l := New() + e1 := l.PushBack(1) + e2 := l.PushBack(2) + checkListPointers(t, l, []*Element{e1, e2}) + e := l.Front() + l.Remove(e) + checkListPointers(t, l, []*Element{e2}) + checkListLen(t, l, 1) + l.Remove(e) + checkListPointers(t, l, []*Element{e2}) + checkListLen(t, l, 1) +} diff --git a/src/pkg/container/ring/Makefile b/src/pkg/container/ring/Makefile index 4755bab07..fb0900774 100644 --- a/src/pkg/container/ring/Makefile +++ b/src/pkg/container/ring/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=container/ring GOFILES=\ diff --git a/src/pkg/container/vector/Makefile b/src/pkg/container/vector/Makefile index c456c6a6c..f6b50156f 100644 --- a/src/pkg/container/vector/Makefile +++ b/src/pkg/container/vector/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=container/vector GOFILES=\ @@ -42,8 +42,7 @@ generate: vector.go vector_test.go | gofmt -r='make_vector -> make_vectorInt'\ | gofmt -r='TestInsertVector -> TestIntInsertVector'\ | gofmt -r='TestDo -> TestIntDo'\ - | gofmt -r='TestIter -> TestIntIter'\ - | gofmt -r='TestVectorData -> TestIntVectorData'\ + | gofmt -r='TestVectorCopy -> TestIntVectorCopy'\ | gofmt -r='interface{} -> int'\ > intvector_test.go\ @@ -65,7 +64,6 @@ generate: vector.go vector_test.go | gofmt -r='make_vector -> make_vectorStr'\ | gofmt -r='TestInsertVector -> TestStrInsertVector'\ | gofmt -r='TestDo -> TestStrDo'\ - | gofmt -r='TestIter -> TestStrIter'\ - | gofmt -r='TestVectorData -> TestStrVectorData'\ + | gofmt -r='TestVectorCopy -> TestStrVectorCopy'\ | gofmt -r='interface{} -> string'\ > stringvector_test.go diff --git a/src/pkg/container/vector/intvector.go b/src/pkg/container/vector/intvector.go index 6aad358e3..5ad9e294b 100644 --- a/src/pkg/container/vector/intvector.go +++ b/src/pkg/container/vector/intvector.go @@ -104,8 +104,8 @@ func (p *IntVector) Set(i int, x int) { (*p)[i] = x } func (p *IntVector) Last() int { return (*p)[len(*p)-1] } -// Data returns all the elements as a slice. -func (p *IntVector) Data() []int { +// Copy makes a copy of the vector and returns it. +func (p *IntVector) Copy() IntVector { arr := make(IntVector, len(*p)) copy(arr, *p) return arr @@ -199,23 +199,6 @@ func (p *IntVector) Swap(i, j int) { } -// Iterate over all elements; driver for range -func (p *IntVector) iterate(c chan<- int) { - for _, v := range *p { - c <- v - } - close(c) -} - - -// Channel iterator for range. -func (p *IntVector) Iter() <-chan int { - c := make(chan int) - go p.iterate(c) - return c -} - - // Do calls function f for each element of the vector, in order. // The behavior of Do is undefined if f changes *p. func (p *IntVector) Do(f func(elem int)) { diff --git a/src/pkg/container/vector/intvector_test.go b/src/pkg/container/vector/intvector_test.go index c80dd52cc..1e38a1982 100644 --- a/src/pkg/container/vector/intvector_test.go +++ b/src/pkg/container/vector/intvector_test.go @@ -127,59 +127,59 @@ func TestIntInsertDeleteClear(t *testing.T) { for i := 0; i < n; i++ { if a.Len() != i { - t.Errorf("T%: A) wrong Len() %d (expected %d)", a, a.Len(), i) + t.Errorf("%T: A) wrong Len() %d (expected %d)", a, a.Len(), i) } if len(a) != i { - t.Errorf("T%: A) wrong len() %d (expected %d)", a, len(a), i) + t.Errorf("%T: A) wrong len() %d (expected %d)", a, len(a), i) } a.Insert(0, int2IntValue(val(i))) if elem2IntValue(a.Last()) != int2IntValue(val(0)) { - t.Error("T%: B", a) + t.Errorf("%T: B", a) } } for i := n - 1; i >= 0; i-- { if elem2IntValue(a.Last()) != int2IntValue(val(0)) { - t.Error("T%: C", a) + t.Errorf("%T: C", a) } if elem2IntValue(a.At(0)) != int2IntValue(val(i)) { - t.Error("T%: D", a) + t.Errorf("%T: D", a) } if elem2IntValue(a[0]) != int2IntValue(val(i)) { - t.Error("T%: D2", a) + t.Errorf("%T: D2", a) } a.Delete(0) if a.Len() != i { - t.Errorf("T%: E) wrong Len() %d (expected %d)", a, a.Len(), i) + t.Errorf("%T: E) wrong Len() %d (expected %d)", a, a.Len(), i) } if len(a) != i { - t.Errorf("T%: E) wrong len() %d (expected %d)", a, len(a), i) + t.Errorf("%T: E) wrong len() %d (expected %d)", a, len(a), i) } } if a.Len() != 0 { - t.Errorf("T%: F) wrong Len() %d (expected 0)", a, a.Len()) + t.Errorf("%T: F) wrong Len() %d (expected 0)", a, a.Len()) } if len(a) != 0 { - t.Errorf("T%: F) wrong len() %d (expected 0)", a, len(a)) + t.Errorf("%T: F) wrong len() %d (expected 0)", a, len(a)) } for i := 0; i < n; i++ { a.Push(int2IntValue(val(i))) if a.Len() != i+1 { - t.Errorf("T%: G) wrong Len() %d (expected %d)", a, a.Len(), i+1) + t.Errorf("%T: G) wrong Len() %d (expected %d)", a, a.Len(), i+1) } if len(a) != i+1 { - t.Errorf("T%: G) wrong len() %d (expected %d)", a, len(a), i+1) + t.Errorf("%T: G) wrong len() %d (expected %d)", a, len(a), i+1) } if elem2IntValue(a.Last()) != int2IntValue(val(i)) { - t.Error("T%: H", a) + t.Errorf("%T: H", a) } } a.Resize(0, 0) if a.Len() != 0 { - t.Errorf("T%: I wrong Len() %d (expected 0)", a, a.Len()) + t.Errorf("%T: I wrong Len() %d (expected 0)", a, a.Len()) } if len(a) != 0 { - t.Errorf("T%: I wrong len() %d (expected 0)", a, len(a)) + t.Errorf("%T: I wrong len() %d (expected 0)", a, len(a)) } const m = 5 @@ -189,21 +189,21 @@ func TestIntInsertDeleteClear(t *testing.T) { x := val(i) a.Push(int2IntValue(x)) if elem2IntValue(a.Pop()) != int2IntValue(x) { - t.Error("T%: J", a) + t.Errorf("%T: J", a) } if a.Len() != j+1 { - t.Errorf("T%: K) wrong Len() %d (expected %d)", a, a.Len(), j+1) + t.Errorf("%T: K) wrong Len() %d (expected %d)", a, a.Len(), j+1) } if len(a) != j+1 { - t.Errorf("T%: K) wrong len() %d (expected %d)", a, len(a), j+1) + t.Errorf("%T: K) wrong len() %d (expected %d)", a, len(a), j+1) } } } if a.Len() != m { - t.Errorf("T%: L) wrong Len() %d (expected %d)", a, a.Len(), m) + t.Errorf("%T: L) wrong Len() %d (expected %d)", a, a.Len(), m) } if len(a) != m { - t.Errorf("T%: L) wrong len() %d (expected %d)", a, len(a), m) + t.Errorf("%T: L) wrong len() %d (expected %d)", a, len(a), m) } } @@ -211,14 +211,14 @@ func TestIntInsertDeleteClear(t *testing.T) { func verify_sliceInt(t *testing.T, x *IntVector, elt, i, j int) { for k := i; k < j; k++ { if elem2IntValue(x.At(k)) != int2IntValue(elt) { - t.Errorf("T%: M) wrong [%d] element %v (expected %v)", x, k, elem2IntValue(x.At(k)), int2IntValue(elt)) + t.Errorf("%T: M) wrong [%d] element %v (expected %v)", x, k, elem2IntValue(x.At(k)), int2IntValue(elt)) } } s := x.Slice(i, j) for k, n := 0, j-i; k < n; k++ { if elem2IntValue(s.At(k)) != int2IntValue(elt) { - t.Errorf("T%: N) wrong [%d] element %v (expected %v)", x, k, elem2IntValue(x.At(k)), int2IntValue(elt)) + t.Errorf("%T: N) wrong [%d] element %v (expected %v)", x, k, elem2IntValue(x.At(k)), int2IntValue(elt)) } } } @@ -227,10 +227,10 @@ func verify_sliceInt(t *testing.T, x *IntVector, elt, i, j int) { func verify_patternInt(t *testing.T, x *IntVector, a, b, c int) { n := a + b + c if x.Len() != n { - t.Errorf("T%: O) wrong Len() %d (expected %d)", x, x.Len(), n) + t.Errorf("%T: O) wrong Len() %d (expected %d)", x, x.Len(), n) } if len(*x) != n { - t.Errorf("T%: O) wrong len() %d (expected %d)", x, len(*x), n) + t.Errorf("%T: O) wrong len() %d (expected %d)", x, len(*x), n) } verify_sliceInt(t, x, 0, 0, a) verify_sliceInt(t, x, 1, a, a+b) @@ -326,61 +326,14 @@ func TestIntDo(t *testing.T) { } -func TestIntIter(t *testing.T) { - const Len = 100 - x := new(IntVector).Resize(Len, 0) - for i := 0; i < Len; i++ { - x.Set(i, int2IntValue(i*i)) - } - i := 0 - for v := range x.Iter() { - if elem2IntValue(v) != int2IntValue(i*i) { - t.Error(tname(x), "Iter expected", i*i, "got", elem2IntValue(v)) - } - i++ - } - if i != Len { - t.Error(tname(x), "Iter stopped at", i, "not", Len) - } - y := new(IntVector).Resize(Len, 0) - for i := 0; i < Len; i++ { - (*y)[i] = int2IntValue(i * i) - } - i = 0 - for v := range y.Iter() { - if elem2IntValue(v) != int2IntValue(i*i) { - t.Error(tname(y), "y, Iter expected", i*i, "got", elem2IntValue(v)) - } - i++ - } - if i != Len { - t.Error(tname(y), "y, Iter stopped at", i, "not", Len) - } - var z IntVector - z.Resize(Len, 0) - for i := 0; i < Len; i++ { - z[i] = int2IntValue(i * i) - } - i = 0 - for v := range z.Iter() { - if elem2IntValue(v) != int2IntValue(i*i) { - t.Error(tname(z), "z, Iter expected", i*i, "got", elem2IntValue(v)) - } - i++ - } - if i != Len { - t.Error(tname(z), "z, Iter stopped at", i, "not", Len) - } -} - -func TestIntVectorData(t *testing.T) { - // verify Data() returns a slice of a copy, not a slice of the original vector +func TestIntVectorCopy(t *testing.T) { + // verify Copy() returns a copy, not simply a slice of the original vector const Len = 10 var src IntVector for i := 0; i < Len; i++ { src.Push(int2IntValue(i * i)) } - dest := src.Data() + dest := src.Copy() for i := 0; i < Len; i++ { src[i] = int2IntValue(-1) v := elem2IntValue(dest[i]) diff --git a/src/pkg/container/vector/numbers_test.go b/src/pkg/container/vector/numbers_test.go index a44242f67..93335ca60 100644 --- a/src/pkg/container/vector/numbers_test.go +++ b/src/pkg/container/vector/numbers_test.go @@ -20,7 +20,7 @@ func s(n uint64) string { lens := len(str) a := make([]string, (lens+2)/3) start := lens - for i, _ := range a { + for i := range a { start -= 3 if start < 0 { start = 0 diff --git a/src/pkg/container/vector/stringvector.go b/src/pkg/container/vector/stringvector.go index ddc030f81..852685f5a 100644 --- a/src/pkg/container/vector/stringvector.go +++ b/src/pkg/container/vector/stringvector.go @@ -104,8 +104,8 @@ func (p *StringVector) Set(i int, x string) { (*p)[i] = x } func (p *StringVector) Last() string { return (*p)[len(*p)-1] } -// Data returns all the elements as a slice. -func (p *StringVector) Data() []string { +// Copy makes a copy of the vector and returns it. +func (p *StringVector) Copy() StringVector { arr := make(StringVector, len(*p)) copy(arr, *p) return arr @@ -199,23 +199,6 @@ func (p *StringVector) Swap(i, j int) { } -// Iterate over all elements; driver for range -func (p *StringVector) iterate(c chan<- string) { - for _, v := range *p { - c <- v - } - close(c) -} - - -// Channel iterator for range. -func (p *StringVector) Iter() <-chan string { - c := make(chan string) - go p.iterate(c) - return c -} - - // Do calls function f for each element of the vector, in order. // The behavior of Do is undefined if f changes *p. func (p *StringVector) Do(f func(elem string)) { diff --git a/src/pkg/container/vector/stringvector_test.go b/src/pkg/container/vector/stringvector_test.go index 859dac2fd..776ae26de 100644 --- a/src/pkg/container/vector/stringvector_test.go +++ b/src/pkg/container/vector/stringvector_test.go @@ -127,59 +127,59 @@ func TestStrInsertDeleteClear(t *testing.T) { for i := 0; i < n; i++ { if a.Len() != i { - t.Errorf("T%: A) wrong Len() %d (expected %d)", a, a.Len(), i) + t.Errorf("%T: A) wrong Len() %d (expected %d)", a, a.Len(), i) } if len(a) != i { - t.Errorf("T%: A) wrong len() %d (expected %d)", a, len(a), i) + t.Errorf("%T: A) wrong len() %d (expected %d)", a, len(a), i) } a.Insert(0, int2StrValue(val(i))) if elem2StrValue(a.Last()) != int2StrValue(val(0)) { - t.Error("T%: B", a) + t.Errorf("%T: B", a) } } for i := n - 1; i >= 0; i-- { if elem2StrValue(a.Last()) != int2StrValue(val(0)) { - t.Error("T%: C", a) + t.Errorf("%T: C", a) } if elem2StrValue(a.At(0)) != int2StrValue(val(i)) { - t.Error("T%: D", a) + t.Errorf("%T: D", a) } if elem2StrValue(a[0]) != int2StrValue(val(i)) { - t.Error("T%: D2", a) + t.Errorf("%T: D2", a) } a.Delete(0) if a.Len() != i { - t.Errorf("T%: E) wrong Len() %d (expected %d)", a, a.Len(), i) + t.Errorf("%T: E) wrong Len() %d (expected %d)", a, a.Len(), i) } if len(a) != i { - t.Errorf("T%: E) wrong len() %d (expected %d)", a, len(a), i) + t.Errorf("%T: E) wrong len() %d (expected %d)", a, len(a), i) } } if a.Len() != 0 { - t.Errorf("T%: F) wrong Len() %d (expected 0)", a, a.Len()) + t.Errorf("%T: F) wrong Len() %d (expected 0)", a, a.Len()) } if len(a) != 0 { - t.Errorf("T%: F) wrong len() %d (expected 0)", a, len(a)) + t.Errorf("%T: F) wrong len() %d (expected 0)", a, len(a)) } for i := 0; i < n; i++ { a.Push(int2StrValue(val(i))) if a.Len() != i+1 { - t.Errorf("T%: G) wrong Len() %d (expected %d)", a, a.Len(), i+1) + t.Errorf("%T: G) wrong Len() %d (expected %d)", a, a.Len(), i+1) } if len(a) != i+1 { - t.Errorf("T%: G) wrong len() %d (expected %d)", a, len(a), i+1) + t.Errorf("%T: G) wrong len() %d (expected %d)", a, len(a), i+1) } if elem2StrValue(a.Last()) != int2StrValue(val(i)) { - t.Error("T%: H", a) + t.Errorf("%T: H", a) } } a.Resize(0, 0) if a.Len() != 0 { - t.Errorf("T%: I wrong Len() %d (expected 0)", a, a.Len()) + t.Errorf("%T: I wrong Len() %d (expected 0)", a, a.Len()) } if len(a) != 0 { - t.Errorf("T%: I wrong len() %d (expected 0)", a, len(a)) + t.Errorf("%T: I wrong len() %d (expected 0)", a, len(a)) } const m = 5 @@ -189,21 +189,21 @@ func TestStrInsertDeleteClear(t *testing.T) { x := val(i) a.Push(int2StrValue(x)) if elem2StrValue(a.Pop()) != int2StrValue(x) { - t.Error("T%: J", a) + t.Errorf("%T: J", a) } if a.Len() != j+1 { - t.Errorf("T%: K) wrong Len() %d (expected %d)", a, a.Len(), j+1) + t.Errorf("%T: K) wrong Len() %d (expected %d)", a, a.Len(), j+1) } if len(a) != j+1 { - t.Errorf("T%: K) wrong len() %d (expected %d)", a, len(a), j+1) + t.Errorf("%T: K) wrong len() %d (expected %d)", a, len(a), j+1) } } } if a.Len() != m { - t.Errorf("T%: L) wrong Len() %d (expected %d)", a, a.Len(), m) + t.Errorf("%T: L) wrong Len() %d (expected %d)", a, a.Len(), m) } if len(a) != m { - t.Errorf("T%: L) wrong len() %d (expected %d)", a, len(a), m) + t.Errorf("%T: L) wrong len() %d (expected %d)", a, len(a), m) } } @@ -211,14 +211,14 @@ func TestStrInsertDeleteClear(t *testing.T) { func verify_sliceStr(t *testing.T, x *StringVector, elt, i, j int) { for k := i; k < j; k++ { if elem2StrValue(x.At(k)) != int2StrValue(elt) { - t.Errorf("T%: M) wrong [%d] element %v (expected %v)", x, k, elem2StrValue(x.At(k)), int2StrValue(elt)) + t.Errorf("%T: M) wrong [%d] element %v (expected %v)", x, k, elem2StrValue(x.At(k)), int2StrValue(elt)) } } s := x.Slice(i, j) for k, n := 0, j-i; k < n; k++ { if elem2StrValue(s.At(k)) != int2StrValue(elt) { - t.Errorf("T%: N) wrong [%d] element %v (expected %v)", x, k, elem2StrValue(x.At(k)), int2StrValue(elt)) + t.Errorf("%T: N) wrong [%d] element %v (expected %v)", x, k, elem2StrValue(x.At(k)), int2StrValue(elt)) } } } @@ -227,10 +227,10 @@ func verify_sliceStr(t *testing.T, x *StringVector, elt, i, j int) { func verify_patternStr(t *testing.T, x *StringVector, a, b, c int) { n := a + b + c if x.Len() != n { - t.Errorf("T%: O) wrong Len() %d (expected %d)", x, x.Len(), n) + t.Errorf("%T: O) wrong Len() %d (expected %d)", x, x.Len(), n) } if len(*x) != n { - t.Errorf("T%: O) wrong len() %d (expected %d)", x, len(*x), n) + t.Errorf("%T: O) wrong len() %d (expected %d)", x, len(*x), n) } verify_sliceStr(t, x, 0, 0, a) verify_sliceStr(t, x, 1, a, a+b) @@ -326,61 +326,14 @@ func TestStrDo(t *testing.T) { } -func TestStrIter(t *testing.T) { - const Len = 100 - x := new(StringVector).Resize(Len, 0) - for i := 0; i < Len; i++ { - x.Set(i, int2StrValue(i*i)) - } - i := 0 - for v := range x.Iter() { - if elem2StrValue(v) != int2StrValue(i*i) { - t.Error(tname(x), "Iter expected", i*i, "got", elem2StrValue(v)) - } - i++ - } - if i != Len { - t.Error(tname(x), "Iter stopped at", i, "not", Len) - } - y := new(StringVector).Resize(Len, 0) - for i := 0; i < Len; i++ { - (*y)[i] = int2StrValue(i * i) - } - i = 0 - for v := range y.Iter() { - if elem2StrValue(v) != int2StrValue(i*i) { - t.Error(tname(y), "y, Iter expected", i*i, "got", elem2StrValue(v)) - } - i++ - } - if i != Len { - t.Error(tname(y), "y, Iter stopped at", i, "not", Len) - } - var z StringVector - z.Resize(Len, 0) - for i := 0; i < Len; i++ { - z[i] = int2StrValue(i * i) - } - i = 0 - for v := range z.Iter() { - if elem2StrValue(v) != int2StrValue(i*i) { - t.Error(tname(z), "z, Iter expected", i*i, "got", elem2StrValue(v)) - } - i++ - } - if i != Len { - t.Error(tname(z), "z, Iter stopped at", i, "not", Len) - } -} - -func TestStrVectorData(t *testing.T) { - // verify Data() returns a slice of a copy, not a slice of the original vector +func TestStrVectorCopy(t *testing.T) { + // verify Copy() returns a copy, not simply a slice of the original vector const Len = 10 var src StringVector for i := 0; i < Len; i++ { src.Push(int2StrValue(i * i)) } - dest := src.Data() + dest := src.Copy() for i := 0; i < Len; i++ { src[i] = int2StrValue(-1) v := elem2StrValue(dest[i]) diff --git a/src/pkg/container/vector/vector.go b/src/pkg/container/vector/vector.go index 986321b14..f43e4d23c 100644 --- a/src/pkg/container/vector/vector.go +++ b/src/pkg/container/vector/vector.go @@ -104,8 +104,8 @@ func (p *Vector) Set(i int, x interface{}) { (*p)[i] = x } func (p *Vector) Last() interface{} { return (*p)[len(*p)-1] } -// Data returns all the elements as a slice. -func (p *Vector) Data() []interface{} { +// Copy makes a copy of the vector and returns it. +func (p *Vector) Copy() Vector { arr := make(Vector, len(*p)) copy(arr, *p) return arr @@ -199,23 +199,6 @@ func (p *Vector) Swap(i, j int) { } -// Iterate over all elements; driver for range -func (p *Vector) iterate(c chan<- interface{}) { - for _, v := range *p { - c <- v - } - close(c) -} - - -// Channel iterator for range. -func (p *Vector) Iter() <-chan interface{} { - c := make(chan interface{}) - go p.iterate(c) - return c -} - - // Do calls function f for each element of the vector, in order. // The behavior of Do is undefined if f changes *p. func (p *Vector) Do(f func(elem interface{})) { diff --git a/src/pkg/container/vector/vector_test.go b/src/pkg/container/vector/vector_test.go index 158b34479..a9c4ceb55 100644 --- a/src/pkg/container/vector/vector_test.go +++ b/src/pkg/container/vector/vector_test.go @@ -127,59 +127,59 @@ func TestInsertDeleteClear(t *testing.T) { for i := 0; i < n; i++ { if a.Len() != i { - t.Errorf("T%: A) wrong Len() %d (expected %d)", a, a.Len(), i) + t.Errorf("%T: A) wrong Len() %d (expected %d)", a, a.Len(), i) } if len(a) != i { - t.Errorf("T%: A) wrong len() %d (expected %d)", a, len(a), i) + t.Errorf("%T: A) wrong len() %d (expected %d)", a, len(a), i) } a.Insert(0, int2Value(val(i))) if elem2Value(a.Last()) != int2Value(val(0)) { - t.Error("T%: B", a) + t.Errorf("%T: B", a) } } for i := n - 1; i >= 0; i-- { if elem2Value(a.Last()) != int2Value(val(0)) { - t.Error("T%: C", a) + t.Errorf("%T: C", a) } if elem2Value(a.At(0)) != int2Value(val(i)) { - t.Error("T%: D", a) + t.Errorf("%T: D", a) } if elem2Value(a[0]) != int2Value(val(i)) { - t.Error("T%: D2", a) + t.Errorf("%T: D2", a) } a.Delete(0) if a.Len() != i { - t.Errorf("T%: E) wrong Len() %d (expected %d)", a, a.Len(), i) + t.Errorf("%T: E) wrong Len() %d (expected %d)", a, a.Len(), i) } if len(a) != i { - t.Errorf("T%: E) wrong len() %d (expected %d)", a, len(a), i) + t.Errorf("%T: E) wrong len() %d (expected %d)", a, len(a), i) } } if a.Len() != 0 { - t.Errorf("T%: F) wrong Len() %d (expected 0)", a, a.Len()) + t.Errorf("%T: F) wrong Len() %d (expected 0)", a, a.Len()) } if len(a) != 0 { - t.Errorf("T%: F) wrong len() %d (expected 0)", a, len(a)) + t.Errorf("%T: F) wrong len() %d (expected 0)", a, len(a)) } for i := 0; i < n; i++ { a.Push(int2Value(val(i))) if a.Len() != i+1 { - t.Errorf("T%: G) wrong Len() %d (expected %d)", a, a.Len(), i+1) + t.Errorf("%T: G) wrong Len() %d (expected %d)", a, a.Len(), i+1) } if len(a) != i+1 { - t.Errorf("T%: G) wrong len() %d (expected %d)", a, len(a), i+1) + t.Errorf("%T: G) wrong len() %d (expected %d)", a, len(a), i+1) } if elem2Value(a.Last()) != int2Value(val(i)) { - t.Error("T%: H", a) + t.Errorf("%T: H", a) } } a.Resize(0, 0) if a.Len() != 0 { - t.Errorf("T%: I wrong Len() %d (expected 0)", a, a.Len()) + t.Errorf("%T: I wrong Len() %d (expected 0)", a, a.Len()) } if len(a) != 0 { - t.Errorf("T%: I wrong len() %d (expected 0)", a, len(a)) + t.Errorf("%T: I wrong len() %d (expected 0)", a, len(a)) } const m = 5 @@ -189,21 +189,21 @@ func TestInsertDeleteClear(t *testing.T) { x := val(i) a.Push(int2Value(x)) if elem2Value(a.Pop()) != int2Value(x) { - t.Error("T%: J", a) + t.Errorf("%T: J", a) } if a.Len() != j+1 { - t.Errorf("T%: K) wrong Len() %d (expected %d)", a, a.Len(), j+1) + t.Errorf("%T: K) wrong Len() %d (expected %d)", a, a.Len(), j+1) } if len(a) != j+1 { - t.Errorf("T%: K) wrong len() %d (expected %d)", a, len(a), j+1) + t.Errorf("%T: K) wrong len() %d (expected %d)", a, len(a), j+1) } } } if a.Len() != m { - t.Errorf("T%: L) wrong Len() %d (expected %d)", a, a.Len(), m) + t.Errorf("%T: L) wrong Len() %d (expected %d)", a, a.Len(), m) } if len(a) != m { - t.Errorf("T%: L) wrong len() %d (expected %d)", a, len(a), m) + t.Errorf("%T: L) wrong len() %d (expected %d)", a, len(a), m) } } @@ -211,14 +211,14 @@ func TestInsertDeleteClear(t *testing.T) { func verify_slice(t *testing.T, x *Vector, elt, i, j int) { for k := i; k < j; k++ { if elem2Value(x.At(k)) != int2Value(elt) { - t.Errorf("T%: M) wrong [%d] element %v (expected %v)", x, k, elem2Value(x.At(k)), int2Value(elt)) + t.Errorf("%T: M) wrong [%d] element %v (expected %v)", x, k, elem2Value(x.At(k)), int2Value(elt)) } } s := x.Slice(i, j) for k, n := 0, j-i; k < n; k++ { if elem2Value(s.At(k)) != int2Value(elt) { - t.Errorf("T%: N) wrong [%d] element %v (expected %v)", x, k, elem2Value(x.At(k)), int2Value(elt)) + t.Errorf("%T: N) wrong [%d] element %v (expected %v)", x, k, elem2Value(x.At(k)), int2Value(elt)) } } } @@ -227,10 +227,10 @@ func verify_slice(t *testing.T, x *Vector, elt, i, j int) { func verify_pattern(t *testing.T, x *Vector, a, b, c int) { n := a + b + c if x.Len() != n { - t.Errorf("T%: O) wrong Len() %d (expected %d)", x, x.Len(), n) + t.Errorf("%T: O) wrong Len() %d (expected %d)", x, x.Len(), n) } if len(*x) != n { - t.Errorf("T%: O) wrong len() %d (expected %d)", x, len(*x), n) + t.Errorf("%T: O) wrong len() %d (expected %d)", x, len(*x), n) } verify_slice(t, x, 0, 0, a) verify_slice(t, x, 1, a, a+b) @@ -326,61 +326,14 @@ func TestDo(t *testing.T) { } -func TestIter(t *testing.T) { - const Len = 100 - x := new(Vector).Resize(Len, 0) - for i := 0; i < Len; i++ { - x.Set(i, int2Value(i*i)) - } - i := 0 - for v := range x.Iter() { - if elem2Value(v) != int2Value(i*i) { - t.Error(tname(x), "Iter expected", i*i, "got", elem2Value(v)) - } - i++ - } - if i != Len { - t.Error(tname(x), "Iter stopped at", i, "not", Len) - } - y := new(Vector).Resize(Len, 0) - for i := 0; i < Len; i++ { - (*y)[i] = int2Value(i * i) - } - i = 0 - for v := range y.Iter() { - if elem2Value(v) != int2Value(i*i) { - t.Error(tname(y), "y, Iter expected", i*i, "got", elem2Value(v)) - } - i++ - } - if i != Len { - t.Error(tname(y), "y, Iter stopped at", i, "not", Len) - } - var z Vector - z.Resize(Len, 0) - for i := 0; i < Len; i++ { - z[i] = int2Value(i * i) - } - i = 0 - for v := range z.Iter() { - if elem2Value(v) != int2Value(i*i) { - t.Error(tname(z), "z, Iter expected", i*i, "got", elem2Value(v)) - } - i++ - } - if i != Len { - t.Error(tname(z), "z, Iter stopped at", i, "not", Len) - } -} - -func TestVectorData(t *testing.T) { - // verify Data() returns a slice of a copy, not a slice of the original vector +func TestVectorCopy(t *testing.T) { + // verify Copy() returns a copy, not simply a slice of the original vector const Len = 10 var src Vector for i := 0; i < Len; i++ { src.Push(int2Value(i * i)) } - dest := src.Data() + dest := src.Copy() for i := 0; i < Len; i++ { src[i] = int2Value(-1) v := elem2Value(dest[i]) diff --git a/src/pkg/crypto/aes/Makefile b/src/pkg/crypto/aes/Makefile index 07d759b4b..9dc846ee3 100644 --- a/src/pkg/crypto/aes/Makefile +++ b/src/pkg/crypto/aes/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=crypto/aes GOFILES=\ diff --git a/src/pkg/crypto/aes/aes_test.go b/src/pkg/crypto/aes/aes_test.go index 1629a33ed..2136d447d 100644 --- a/src/pkg/crypto/aes/aes_test.go +++ b/src/pkg/crypto/aes/aes_test.go @@ -130,7 +130,7 @@ type KeyTest struct { } var keyTests = []KeyTest{ - KeyTest{ + { // A.1. Expansion of a 128-bit Cipher Key []byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}, []uint32{ @@ -160,7 +160,7 @@ var keyTests = []KeyTest{ 0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x9cf4f3c, }, }, - KeyTest{ + { // A.2. Expansion of a 192-bit Cipher Key []byte{ 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5, @@ -183,7 +183,7 @@ var keyTests = []KeyTest{ }, nil, }, - KeyTest{ + { // A.3. Expansion of a 256-bit Cipher Key []byte{ 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, @@ -245,19 +245,19 @@ type CryptTest struct { } var encryptTests = []CryptTest{ - CryptTest{ + { // Appendix B. []byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}, []byte{0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34}, []byte{0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32}, }, - CryptTest{ + { // Appendix C.1. AES-128 []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, []byte{0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30, 0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a}, }, - CryptTest{ + { // Appendix C.2. AES-192 []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, @@ -265,7 +265,7 @@ var encryptTests = []CryptTest{ []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, []byte{0xdd, 0xa9, 0x7c, 0xa4, 0x86, 0x4c, 0xdf, 0xe0, 0x6e, 0xaf, 0x70, 0xa0, 0xec, 0x0d, 0x71, 0x91}, }, - CryptTest{ + { // Appendix C.3. AES-256 []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, @@ -283,7 +283,7 @@ func TestEncryptBlock(t *testing.T) { dec := make([]uint32, n) expandKey(tt.key, enc, dec) out := make([]byte, len(tt.in)) - encryptBlock(enc, tt.in, out) + encryptBlock(enc, out, tt.in) for j, v := range out { if v != tt.out[j] { t.Errorf("encryptBlock %d: out[%d] = %#x, want %#x", i, j, v, tt.out[j]) @@ -301,7 +301,7 @@ func TestDecryptBlock(t *testing.T) { dec := make([]uint32, n) expandKey(tt.key, enc, dec) plain := make([]byte, len(tt.in)) - decryptBlock(dec, tt.out, plain) + decryptBlock(dec, plain, tt.out) for j, v := range plain { if v != tt.in[j] { t.Errorf("decryptBlock %d: plain[%d] = %#x, want %#x", i, j, v, tt.in[j]) @@ -320,7 +320,7 @@ func TestCipherEncrypt(t *testing.T) { continue } out := make([]byte, len(tt.in)) - c.Encrypt(tt.in, out) + c.Encrypt(out, tt.in) for j, v := range out { if v != tt.out[j] { t.Errorf("Cipher.Encrypt %d: out[%d] = %#x, want %#x", i, j, v, tt.out[j]) @@ -339,7 +339,7 @@ func TestCipherDecrypt(t *testing.T) { continue } plain := make([]byte, len(tt.in)) - c.Decrypt(tt.out, plain) + c.Decrypt(plain, tt.out) for j, v := range plain { if v != tt.in[j] { t.Errorf("decryptBlock %d: plain[%d] = %#x, want %#x", i, j, v, tt.in[j]) diff --git a/src/pkg/crypto/aes/block.go b/src/pkg/crypto/aes/block.go index a502554bd..130cd011c 100644 --- a/src/pkg/crypto/aes/block.go +++ b/src/pkg/crypto/aes/block.go @@ -37,7 +37,7 @@ package aes // Encrypt one block from src into dst, using the expanded key xk. -func encryptBlock(xk []uint32, src, dst []byte) { +func encryptBlock(xk []uint32, dst, src []byte) { var s0, s1, s2, s3, t0, t1, t2, t3 uint32 s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) @@ -82,7 +82,7 @@ func encryptBlock(xk []uint32, src, dst []byte) { } // Decrypt one block from src into dst, using the expanded key xk. -func decryptBlock(xk []uint32, src, dst []byte) { +func decryptBlock(xk []uint32, dst, src []byte) { var s0, s1, s2, s3, t0, t1, t2, t3 uint32 s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) diff --git a/src/pkg/crypto/aes/cipher.go b/src/pkg/crypto/aes/cipher.go index 44e905e01..3a9d02318 100644 --- a/src/pkg/crypto/aes/cipher.go +++ b/src/pkg/crypto/aes/cipher.go @@ -53,11 +53,11 @@ func (c *Cipher) BlockSize() int { return BlockSize } // Note that for amounts of data larger than a block, // it is not safe to just call Encrypt on successive blocks; // instead, use an encryption mode like CBC (see crypto/block/cbc.go). -func (c *Cipher) Encrypt(src, dst []byte) { encryptBlock(c.enc, src, dst) } +func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c.enc, dst, src) } // Decrypt decrypts the 16-byte buffer src using the key k // and stores the result in dst. -func (c *Cipher) Decrypt(src, dst []byte) { decryptBlock(c.dec, src, dst) } +func (c *Cipher) Decrypt(dst, src []byte) { decryptBlock(c.dec, dst, src) } // Reset zeros the key data, so that it will no longer // appear in the process's memory. diff --git a/src/pkg/crypto/aes/const.go b/src/pkg/crypto/aes/const.go index 8ddcaff26..97a5b64ec 100644 --- a/src/pkg/crypto/aes/const.go +++ b/src/pkg/crypto/aes/const.go @@ -81,7 +81,7 @@ var sbox1 = [256]byte{ // These can be recomputed by adapting the tests in aes_test.go. var te = [4][256]uint32{ - [256]uint32{ + { 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, @@ -115,7 +115,7 @@ var te = [4][256]uint32{ 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a, }, - [256]uint32{ + { 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, @@ -149,7 +149,7 @@ var te = [4][256]uint32{ 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616, }, - [256]uint32{ + { 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, @@ -183,7 +183,7 @@ var te = [4][256]uint32{ 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16, }, - [256]uint32{ + { 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, @@ -223,7 +223,7 @@ var te = [4][256]uint32{ // These can be recomputed by adapting the tests in aes_test.go. var td = [4][256]uint32{ - [256]uint32{ + { 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, @@ -257,7 +257,7 @@ var td = [4][256]uint32{ 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742, }, - [256]uint32{ + { 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, @@ -291,7 +291,7 @@ var td = [4][256]uint32{ 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857, }, - [256]uint32{ + { 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, @@ -325,7 +325,7 @@ var td = [4][256]uint32{ 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8, }, - [256]uint32{ + { 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, diff --git a/src/pkg/crypto/block/Makefile b/src/pkg/crypto/block/Makefile index 25c3483ae..71c7aff64 100644 --- a/src/pkg/crypto/block/Makefile +++ b/src/pkg/crypto/block/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=crypto/block GOFILES=\ diff --git a/src/pkg/crypto/block/cbc.go b/src/pkg/crypto/block/cbc.go index 10235f541..23229c09f 100644 --- a/src/pkg/crypto/block/cbc.go +++ b/src/pkg/crypto/block/cbc.go @@ -27,14 +27,14 @@ func newCBC(c Cipher, iv []byte) *cbcCipher { x := new(cbcCipher) x.c = c x.blockSize = n - x.iv = copy(iv) + x.iv = dup(iv) x.tmp = make([]byte, n) return x } func (x *cbcCipher) BlockSize() int { return x.blockSize } -func (x *cbcCipher) Encrypt(src, dst []byte) { +func (x *cbcCipher) Encrypt(dst, src []byte) { for i := 0; i < x.blockSize; i++ { x.iv[i] ^= src[i] } @@ -44,8 +44,8 @@ func (x *cbcCipher) Encrypt(src, dst []byte) { } } -func (x *cbcCipher) Decrypt(src, dst []byte) { - x.c.Decrypt(src, x.tmp) +func (x *cbcCipher) Decrypt(dst, src []byte) { + x.c.Decrypt(x.tmp, src) for i := 0; i < x.blockSize; i++ { x.tmp[i] ^= x.iv[i] x.iv[i] = src[i] diff --git a/src/pkg/crypto/block/cbc_aes_test.go b/src/pkg/crypto/block/cbc_aes_test.go deleted file mode 100644 index 5531f3ab9..000000000 --- a/src/pkg/crypto/block/cbc_aes_test.go +++ /dev/null @@ -1,102 +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. - -// CBC AES test vectors. - -// See U.S. National Institute of Standards and Technology (NIST) -// Special Publication 800-38A, ``Recommendation for Block Cipher -// Modes of Operation,'' 2001 Edition, pp. 24-29. - -package block - -import ( - "bytes" - "crypto/aes" - "io" - "testing" -) - -type cbcTest struct { - name string - key []byte - iv []byte - in []byte - out []byte -} - -var cbcAESTests = []cbcTest{ - // NIST SP 800-38A pp 27-29 - cbcTest{ - "CBC-AES128", - commonKey128, - commonIV, - commonInput, - []byte{ - 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, - 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, - 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, - 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7, - }, - }, - cbcTest{ - "CBC-AES192", - commonKey192, - commonIV, - commonInput, - []byte{ - 0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d, 0x71, 0x78, 0x18, 0x3a, 0x9f, 0xa0, 0x71, 0xe8, - 0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d, 0xed, 0xf4, 0xe5, 0xe7, 0x38, 0x76, 0x3f, 0x69, 0x14, 0x5a, - 0x57, 0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0, 0x7f, 0xa9, 0xba, 0xac, 0x3d, 0xf1, 0x02, 0xe0, - 0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88, 0x81, 0xd9, 0x20, 0xa9, 0xe6, 0x4f, 0x56, 0x15, 0xcd, - }, - }, - cbcTest{ - "CBC-AES256", - commonKey256, - commonIV, - commonInput, - []byte{ - 0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6, - 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d, - 0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, 0xa5, 0x30, 0xe2, 0x63, 0x04, 0x23, 0x14, 0x61, - 0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, 0xda, 0x6c, 0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b, - }, - }, -} - -func TestCBC_AES(t *testing.T) { - for _, tt := range cbcAESTests { - test := tt.name - - c, err := aes.NewCipher(tt.key) - if err != nil { - t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err) - continue - } - - var crypt bytes.Buffer - w := NewCBCEncrypter(c, tt.iv, &crypt) - var r io.Reader = bytes.NewBuffer(tt.in) - n, err := io.Copy(w, r) - if n != int64(len(tt.in)) || err != nil { - t.Errorf("%s: CBCEncrypter io.Copy = %d, %v want %d, nil", test, n, err, len(tt.in)) - } else if d := crypt.Bytes(); !same(tt.out, d) { - t.Errorf("%s: CBCEncrypter\nhave %x\nwant %x", test, d, tt.out) - } - - var plain bytes.Buffer - r = NewCBCDecrypter(c, tt.iv, bytes.NewBuffer(tt.out)) - w = &plain - n, err = io.Copy(w, r) - if n != int64(len(tt.out)) || err != nil { - t.Errorf("%s: CBCDecrypter io.Copy = %d, %v want %d, nil", test, n, err, len(tt.out)) - } else if d := plain.Bytes(); !same(tt.in, d) { - t.Errorf("%s: CBCDecrypter\nhave %x\nwant %x", test, d, tt.in) - } - - if t.Failed() { - break - } - } -} diff --git a/src/pkg/crypto/block/cfb.go b/src/pkg/crypto/block/cfb.go index 177ae939d..f20c0a04f 100644 --- a/src/pkg/crypto/block/cfb.go +++ b/src/pkg/crypto/block/cfb.go @@ -33,16 +33,16 @@ func newCFB(c Cipher, s int, iv []byte) *cfbCipher { x.c = c x.blockSize = s / 8 x.cipherSize = b - x.iv = copy(iv) + x.iv = dup(iv) x.tmp = make([]byte, b) return x } func (x *cfbCipher) BlockSize() int { return x.blockSize } -func (x *cfbCipher) Encrypt(src, dst []byte) { +func (x *cfbCipher) Encrypt(dst, src []byte) { // Encrypt old IV and xor prefix with src to make dst. - x.c.Encrypt(x.iv, x.tmp) + x.c.Encrypt(x.tmp, x.iv) for i := 0; i < x.blockSize; i++ { dst[i] = src[i] ^ x.tmp[i] } @@ -57,9 +57,9 @@ func (x *cfbCipher) Encrypt(src, dst []byte) { } } -func (x *cfbCipher) Decrypt(src, dst []byte) { +func (x *cfbCipher) Decrypt(dst, src []byte) { // Encrypt [sic] old IV and xor prefix with src to make dst. - x.c.Encrypt(x.iv, x.tmp) + x.c.Encrypt(x.tmp, x.iv) for i := 0; i < x.blockSize; i++ { dst[i] = src[i] ^ x.tmp[i] } diff --git a/src/pkg/crypto/block/cfb_aes_test.go b/src/pkg/crypto/block/cfb_aes_test.go index 8a245a2cb..e400c182a 100644 --- a/src/pkg/crypto/block/cfb_aes_test.go +++ b/src/pkg/crypto/block/cfb_aes_test.go @@ -27,7 +27,7 @@ type cfbTest struct { } var cfbAESTests = []cfbTest{ - cfbTest{ + { "CFB1-AES128", 1, commonKey128, @@ -41,7 +41,7 @@ var cfbAESTests = []cfbTest{ 1<<7 | 0<<6 | 1<<5 | 1<<4 | 0<<3 | 0<<2 | 1<<1, }, }, - cfbTest{ + { "CFB1-AES192", 1, commonKey192, @@ -55,7 +55,7 @@ var cfbAESTests = []cfbTest{ 0<<7 | 1<<6 | 0<<5 | 1<<4 | 1<<3 | 0<<2 | 0<<1, }, }, - cfbTest{ + { "CFB1-AES256", 1, commonKey256, @@ -70,7 +70,7 @@ var cfbAESTests = []cfbTest{ }, }, - cfbTest{ + { "CFB8-AES128", 8, commonKey128, @@ -117,7 +117,7 @@ var cfbAESTests = []cfbTest{ }, }, - cfbTest{ + { "CFB8-AES192", 8, commonKey192, @@ -164,7 +164,7 @@ var cfbAESTests = []cfbTest{ }, }, - cfbTest{ + { "CFB8-AES256", 8, commonKey256, @@ -211,7 +211,7 @@ var cfbAESTests = []cfbTest{ }, }, - cfbTest{ + { "CFB128-AES128", 128, commonKey128, @@ -230,7 +230,7 @@ var cfbAESTests = []cfbTest{ }, }, - cfbTest{ + { "CFB128-AES192", 128, commonKey192, @@ -249,7 +249,7 @@ var cfbAESTests = []cfbTest{ }, }, - cfbTest{ + { "CFB128-AES256", 128, commonKey256, diff --git a/src/pkg/crypto/block/cipher.go b/src/pkg/crypto/block/cipher.go index 1b786cca4..e1099e9a1 100644 --- a/src/pkg/crypto/block/cipher.go +++ b/src/pkg/crypto/block/cipher.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// The block package is deprecated, use cipher instead. // The block package implements standard block cipher modes // that can be wrapped around low-level block cipher implementations. // See http://csrc.nist.gov/groups/ST/toolkit/BCM/current_modes.html @@ -18,16 +19,16 @@ type Cipher interface { // Encrypt encrypts the first block in src into dst. // Src and dst may point at the same memory. - Encrypt(src, dst []byte) + Encrypt(dst, src []byte) // Decrypt decrypts the first block in src into dst. // Src and dst may point at the same memory. - Decrypt(src, dst []byte) + Decrypt(dst, src []byte) } // Utility routines -func shift1(src, dst []byte) byte { +func shift1(dst, src []byte) byte { var b byte for i := len(src) - 1; i >= 0; i-- { bb := src[i] >> 7 @@ -49,10 +50,8 @@ func same(p, q []byte) bool { return true } -func copy(p []byte) []byte { +func dup(p []byte) []byte { q := make([]byte, len(p)) - for i, b := range p { - q[i] = b - } + copy(q, p) return q } diff --git a/src/pkg/crypto/block/cmac.go b/src/pkg/crypto/block/cmac.go index 6082299ab..b85cde72e 100644 --- a/src/pkg/crypto/block/cmac.go +++ b/src/pkg/crypto/block/cmac.go @@ -52,7 +52,7 @@ func NewCMAC(c Cipher) hash.Hash { if shift1(d.k1, d.k1) != 0 { d.k1[n-1] ^= r } - if shift1(d.k1, d.k2) != 0 { + if shift1(d.k2, d.k1) != 0 { d.k2[n-1] ^= r } diff --git a/src/pkg/crypto/block/cmac_aes_test.go b/src/pkg/crypto/block/cmac_aes_test.go index a9cbc71a6..0a4a1a418 100644 --- a/src/pkg/crypto/block/cmac_aes_test.go +++ b/src/pkg/crypto/block/cmac_aes_test.go @@ -18,17 +18,17 @@ type cmacAESTest struct { } var cmacAESTests = []cmacAESTest{ - cmacAESTest{ + { commonKey128, nil, []byte{0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46}, }, - cmacAESTest{ + { commonKey128, []byte{0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}, []byte{0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c}, }, - cmacAESTest{ + { commonKey128, []byte{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, @@ -37,7 +37,7 @@ var cmacAESTests = []cmacAESTest{ }, []byte{0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27}, }, - cmacAESTest{ + { commonKey128, []byte{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, @@ -47,17 +47,17 @@ var cmacAESTests = []cmacAESTest{ }, []byte{0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe}, }, - cmacAESTest{ + { commonKey192, nil, []byte{0xd1, 0x7d, 0xdf, 0x46, 0xad, 0xaa, 0xcd, 0xe5, 0x31, 0xca, 0xc4, 0x83, 0xde, 0x7a, 0x93, 0x67}, }, - cmacAESTest{ + { commonKey192, []byte{0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}, []byte{0x9e, 0x99, 0xa7, 0xbf, 0x31, 0xe7, 0x10, 0x90, 0x06, 0x62, 0xf6, 0x5e, 0x61, 0x7c, 0x51, 0x84}, }, - cmacAESTest{ + { commonKey192, []byte{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, @@ -66,7 +66,7 @@ var cmacAESTests = []cmacAESTest{ }, []byte{0x8a, 0x1d, 0xe5, 0xbe, 0x2e, 0xb3, 0x1a, 0xad, 0x08, 0x9a, 0x82, 0xe6, 0xee, 0x90, 0x8b, 0x0e}, }, - cmacAESTest{ + { commonKey192, []byte{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, @@ -76,17 +76,17 @@ var cmacAESTests = []cmacAESTest{ }, []byte{0xa1, 0xd5, 0xdf, 0x0e, 0xed, 0x79, 0x0f, 0x79, 0x4d, 0x77, 0x58, 0x96, 0x59, 0xf3, 0x9a, 0x11}, }, - cmacAESTest{ + { commonKey256, nil, []byte{0x02, 0x89, 0x62, 0xf6, 0x1b, 0x7b, 0xf8, 0x9e, 0xfc, 0x6b, 0x55, 0x1f, 0x46, 0x67, 0xd9, 0x83}, }, - cmacAESTest{ + { commonKey256, []byte{0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}, []byte{0x28, 0xa7, 0x02, 0x3f, 0x45, 0x2e, 0x8f, 0x82, 0xbd, 0x4b, 0xf2, 0x8d, 0x8c, 0x37, 0xc3, 0x5c}, }, - cmacAESTest{ + { commonKey256, []byte{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, @@ -95,7 +95,7 @@ var cmacAESTests = []cmacAESTest{ }, []byte{0xaa, 0xf3, 0xd8, 0xf1, 0xde, 0x56, 0x40, 0xc2, 0x32, 0xf5, 0xb1, 0x69, 0xb9, 0xc9, 0x11, 0xe6}, }, - cmacAESTest{ + { commonKey256, []byte{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, diff --git a/src/pkg/crypto/block/ctr.go b/src/pkg/crypto/block/ctr.go index 085ae05b1..5d65c0c9a 100644 --- a/src/pkg/crypto/block/ctr.go +++ b/src/pkg/crypto/block/ctr.go @@ -25,14 +25,14 @@ type ctrStream struct { func newCTRStream(c Cipher, ctr []byte) *ctrStream { x := new(ctrStream) x.c = c - x.ctr = copy(ctr) + x.ctr = dup(ctr) x.out = make([]byte, len(ctr)) return x } func (x *ctrStream) Next() []byte { // Next block is encryption of counter. - x.c.Encrypt(x.ctr, x.out) + x.c.Encrypt(x.out, x.ctr) // Increment counter for i := len(x.ctr) - 1; i >= 0; i-- { diff --git a/src/pkg/crypto/block/ctr_aes_test.go b/src/pkg/crypto/block/ctr_aes_test.go deleted file mode 100644 index adb996c1d..000000000 --- a/src/pkg/crypto/block/ctr_aes_test.go +++ /dev/null @@ -1,110 +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. - -// CTR AES test vectors. - -// See U.S. National Institute of Standards and Technology (NIST) -// Special Publication 800-38A, ``Recommendation for Block Cipher -// Modes of Operation,'' 2001 Edition, pp. 55-58. - -package block - -import ( - "bytes" - "crypto/aes" - "io" - "testing" -) - -type ctrTest struct { - name string - key []byte - iv []byte - in []byte - out []byte -} - -var commonCounter = []byte{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff} - -var ctrAESTests = []ctrTest{ - // NIST SP 800-38A pp 55-58 - ctrTest{ - "CTR-AES128", - commonKey128, - commonCounter, - commonInput, - []byte{ - 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce, - 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff, 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff, - 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab, - 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1, 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee, - }, - }, - ctrTest{ - "CTR-AES192", - commonKey192, - commonCounter, - commonInput, - []byte{ - 0x1a, 0xbc, 0x93, 0x24, 0x17, 0x52, 0x1c, 0xa2, 0x4f, 0x2b, 0x04, 0x59, 0xfe, 0x7e, 0x6e, 0x0b, - 0x09, 0x03, 0x39, 0xec, 0x0a, 0xa6, 0xfa, 0xef, 0xd5, 0xcc, 0xc2, 0xc6, 0xf4, 0xce, 0x8e, 0x94, - 0x1e, 0x36, 0xb2, 0x6b, 0xd1, 0xeb, 0xc6, 0x70, 0xd1, 0xbd, 0x1d, 0x66, 0x56, 0x20, 0xab, 0xf7, - 0x4f, 0x78, 0xa7, 0xf6, 0xd2, 0x98, 0x09, 0x58, 0x5a, 0x97, 0xda, 0xec, 0x58, 0xc6, 0xb0, 0x50, - }, - }, - ctrTest{ - "CTR-AES256", - commonKey256, - commonCounter, - commonInput, - []byte{ - 0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5, 0xb7, 0xa7, 0xf5, 0x04, 0xbb, 0xf3, 0xd2, 0x28, - 0xf4, 0x43, 0xe3, 0xca, 0x4d, 0x62, 0xb5, 0x9a, 0xca, 0x84, 0xe9, 0x90, 0xca, 0xca, 0xf5, 0xc5, - 0x2b, 0x09, 0x30, 0xda, 0xa2, 0x3d, 0xe9, 0x4c, 0xe8, 0x70, 0x17, 0xba, 0x2d, 0x84, 0x98, 0x8d, - 0xdf, 0xc9, 0xc5, 0x8d, 0xb6, 0x7a, 0xad, 0xa6, 0x13, 0xc2, 0xdd, 0x08, 0x45, 0x79, 0x41, 0xa6, - }, - }, -} - -func TestCTR_AES(t *testing.T) { - for _, tt := range ctrAESTests { - test := tt.name - - c, err := aes.NewCipher(tt.key) - if err != nil { - t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err) - continue - } - - for j := 0; j <= 5; j += 5 { - var crypt bytes.Buffer - in := tt.in[0 : len(tt.in)-j] - w := NewCTRWriter(c, tt.iv, &crypt) - var r io.Reader = bytes.NewBuffer(in) - n, err := io.Copy(w, r) - if n != int64(len(in)) || err != nil { - t.Errorf("%s/%d: CTRWriter io.Copy = %d, %v want %d, nil", test, len(in), n, err, len(in)) - } else if d, out := crypt.Bytes(), tt.out[0:len(in)]; !same(out, d) { - t.Errorf("%s/%d: CTRWriter\ninpt %x\nhave %x\nwant %x", test, len(in), in, d, out) - } - } - - for j := 0; j <= 7; j += 7 { - var plain bytes.Buffer - out := tt.out[0 : len(tt.out)-j] - r := NewCTRReader(c, tt.iv, bytes.NewBuffer(out)) - w := &plain - n, err := io.Copy(w, r) - if n != int64(len(out)) || err != nil { - t.Errorf("%s/%d: CTRReader io.Copy = %d, %v want %d, nil", test, len(out), n, err, len(out)) - } else if d, in := plain.Bytes(), tt.in[0:len(out)]; !same(in, d) { - t.Errorf("%s/%d: CTRReader\nhave %x\nwant %x", test, len(out), d, in) - } - } - - if t.Failed() { - break - } - } -} diff --git a/src/pkg/crypto/block/eax.go b/src/pkg/crypto/block/eax.go index cc3662787..3f3b96431 100644 --- a/src/pkg/crypto/block/eax.go +++ b/src/pkg/crypto/block/eax.go @@ -45,8 +45,8 @@ func setupEAX(c Cipher, iv, hdr []byte, tagBytes int) (ctrIV, tag []byte, cmac h cmac.Write(buf) // 0 cmac.Write(iv) sum := cmac.Sum() - ctrIV = copy(sum) - tag = copy(sum[0:tagBytes]) + ctrIV = dup(sum) + tag = dup(sum[0:tagBytes]) cmac.Reset() buf[n-1] = 1 @@ -237,8 +237,8 @@ func (x *eaxDecrypter) checkTag() os.Error { finishEAX(x.tag, x.cr.cmac) if !same(x.tag, x.cr.tag) { e := new(EAXTagError) - e.Computed = copy(x.tag) - e.Read = copy(x.cr.tag) + e.Computed = dup(x.tag) + e.Read = dup(x.cr.tag) return e } return nil diff --git a/src/pkg/crypto/block/eax_aes_test.go b/src/pkg/crypto/block/eax_aes_test.go index a1a099429..93aa771be 100644 --- a/src/pkg/crypto/block/eax_aes_test.go +++ b/src/pkg/crypto/block/eax_aes_test.go @@ -23,70 +23,70 @@ type eaxAESTest struct { } var eaxAESTests = []eaxAESTest{ - eaxAESTest{ + { []byte{}, []byte{0x23, 0x39, 0x52, 0xDE, 0xE4, 0xD5, 0xED, 0x5F, 0x9B, 0x9C, 0x6D, 0x6F, 0xF8, 0x0F, 0xF4, 0x78}, []byte{0x62, 0xEC, 0x67, 0xF9, 0xC3, 0xA4, 0xA4, 0x07, 0xFC, 0xB2, 0xA8, 0xC4, 0x90, 0x31, 0xA8, 0xB3}, []byte{0x6B, 0xFB, 0x91, 0x4F, 0xD0, 0x7E, 0xAE, 0x6B}, []byte{0xE0, 0x37, 0x83, 0x0E, 0x83, 0x89, 0xF2, 0x7B, 0x02, 0x5A, 0x2D, 0x65, 0x27, 0xE7, 0x9D, 0x01}, }, - eaxAESTest{ + { []byte{0xF7, 0xFB}, []byte{0x91, 0x94, 0x5D, 0x3F, 0x4D, 0xCB, 0xEE, 0x0B, 0xF4, 0x5E, 0xF5, 0x22, 0x55, 0xF0, 0x95, 0xA4}, []byte{0xBE, 0xCA, 0xF0, 0x43, 0xB0, 0xA2, 0x3D, 0x84, 0x31, 0x94, 0xBA, 0x97, 0x2C, 0x66, 0xDE, 0xBD}, []byte{0xFA, 0x3B, 0xFD, 0x48, 0x06, 0xEB, 0x53, 0xFA}, []byte{0x19, 0xDD, 0x5C, 0x4C, 0x93, 0x31, 0x04, 0x9D, 0x0B, 0xDA, 0xB0, 0x27, 0x74, 0x08, 0xF6, 0x79, 0x67, 0xE5}, }, - eaxAESTest{ + { []byte{0x1A, 0x47, 0xCB, 0x49, 0x33}, []byte{0x01, 0xF7, 0x4A, 0xD6, 0x40, 0x77, 0xF2, 0xE7, 0x04, 0xC0, 0xF6, 0x0A, 0xDA, 0x3D, 0xD5, 0x23}, []byte{0x70, 0xC3, 0xDB, 0x4F, 0x0D, 0x26, 0x36, 0x84, 0x00, 0xA1, 0x0E, 0xD0, 0x5D, 0x2B, 0xFF, 0x5E}, []byte{0x23, 0x4A, 0x34, 0x63, 0xC1, 0x26, 0x4A, 0xC6}, []byte{0xD8, 0x51, 0xD5, 0xBA, 0xE0, 0x3A, 0x59, 0xF2, 0x38, 0xA2, 0x3E, 0x39, 0x19, 0x9D, 0xC9, 0x26, 0x66, 0x26, 0xC4, 0x0F, 0x80}, }, - eaxAESTest{ + { []byte{0x48, 0x1C, 0x9E, 0x39, 0xB1}, []byte{0xD0, 0x7C, 0xF6, 0xCB, 0xB7, 0xF3, 0x13, 0xBD, 0xDE, 0x66, 0xB7, 0x27, 0xAF, 0xD3, 0xC5, 0xE8}, []byte{0x84, 0x08, 0xDF, 0xFF, 0x3C, 0x1A, 0x2B, 0x12, 0x92, 0xDC, 0x19, 0x9E, 0x46, 0xB7, 0xD6, 0x17}, []byte{0x33, 0xCC, 0xE2, 0xEA, 0xBF, 0xF5, 0xA7, 0x9D}, []byte{0x63, 0x2A, 0x9D, 0x13, 0x1A, 0xD4, 0xC1, 0x68, 0xA4, 0x22, 0x5D, 0x8E, 0x1F, 0xF7, 0x55, 0x93, 0x99, 0x74, 0xA7, 0xBE, 0xDE}, }, - eaxAESTest{ + { []byte{0x40, 0xD0, 0xC0, 0x7D, 0xA5, 0xE4}, []byte{0x35, 0xB6, 0xD0, 0x58, 0x00, 0x05, 0xBB, 0xC1, 0x2B, 0x05, 0x87, 0x12, 0x45, 0x57, 0xD2, 0xC2}, []byte{0xFD, 0xB6, 0xB0, 0x66, 0x76, 0xEE, 0xDC, 0x5C, 0x61, 0xD7, 0x42, 0x76, 0xE1, 0xF8, 0xE8, 0x16}, []byte{0xAE, 0xB9, 0x6E, 0xAE, 0xBE, 0x29, 0x70, 0xE9}, []byte{0x07, 0x1D, 0xFE, 0x16, 0xC6, 0x75, 0xCB, 0x06, 0x77, 0xE5, 0x36, 0xF7, 0x3A, 0xFE, 0x6A, 0x14, 0xB7, 0x4E, 0xE4, 0x98, 0x44, 0xDD}, }, - eaxAESTest{ + { []byte{0x4D, 0xE3, 0xB3, 0x5C, 0x3F, 0xC0, 0x39, 0x24, 0x5B, 0xD1, 0xFB, 0x7D}, []byte{0xBD, 0x8E, 0x6E, 0x11, 0x47, 0x5E, 0x60, 0xB2, 0x68, 0x78, 0x4C, 0x38, 0xC6, 0x2F, 0xEB, 0x22}, []byte{0x6E, 0xAC, 0x5C, 0x93, 0x07, 0x2D, 0x8E, 0x85, 0x13, 0xF7, 0x50, 0x93, 0x5E, 0x46, 0xDA, 0x1B}, []byte{0xD4, 0x48, 0x2D, 0x1C, 0xA7, 0x8D, 0xCE, 0x0F}, []byte{0x83, 0x5B, 0xB4, 0xF1, 0x5D, 0x74, 0x3E, 0x35, 0x0E, 0x72, 0x84, 0x14, 0xAB, 0xB8, 0x64, 0x4F, 0xD6, 0xCC, 0xB8, 0x69, 0x47, 0xC5, 0xE1, 0x05, 0x90, 0x21, 0x0A, 0x4F}, }, - eaxAESTest{ + { []byte{0x8B, 0x0A, 0x79, 0x30, 0x6C, 0x9C, 0xE7, 0xED, 0x99, 0xDA, 0xE4, 0xF8, 0x7F, 0x8D, 0xD6, 0x16, 0x36}, []byte{0x7C, 0x77, 0xD6, 0xE8, 0x13, 0xBE, 0xD5, 0xAC, 0x98, 0xBA, 0xA4, 0x17, 0x47, 0x7A, 0x2E, 0x7D}, []byte{0x1A, 0x8C, 0x98, 0xDC, 0xD7, 0x3D, 0x38, 0x39, 0x3B, 0x2B, 0xF1, 0x56, 0x9D, 0xEE, 0xFC, 0x19}, []byte{0x65, 0xD2, 0x01, 0x79, 0x90, 0xD6, 0x25, 0x28}, []byte{0x02, 0x08, 0x3E, 0x39, 0x79, 0xDA, 0x01, 0x48, 0x12, 0xF5, 0x9F, 0x11, 0xD5, 0x26, 0x30, 0xDA, 0x30, 0x13, 0x73, 0x27, 0xD1, 0x06, 0x49, 0xB0, 0xAA, 0x6E, 0x1C, 0x18, 0x1D, 0xB6, 0x17, 0xD7, 0xF2}, }, - eaxAESTest{ + { []byte{0x1B, 0xDA, 0x12, 0x2B, 0xCE, 0x8A, 0x8D, 0xBA, 0xF1, 0x87, 0x7D, 0x96, 0x2B, 0x85, 0x92, 0xDD, 0x2D, 0x56}, []byte{0x5F, 0xFF, 0x20, 0xCA, 0xFA, 0xB1, 0x19, 0xCA, 0x2F, 0xC7, 0x35, 0x49, 0xE2, 0x0F, 0x5B, 0x0D}, []byte{0xDD, 0xE5, 0x9B, 0x97, 0xD7, 0x22, 0x15, 0x6D, 0x4D, 0x9A, 0xFF, 0x2B, 0xC7, 0x55, 0x98, 0x26}, []byte{0x54, 0xB9, 0xF0, 0x4E, 0x6A, 0x09, 0x18, 0x9A}, []byte{0x2E, 0xC4, 0x7B, 0x2C, 0x49, 0x54, 0xA4, 0x89, 0xAF, 0xC7, 0xBA, 0x48, 0x97, 0xED, 0xCD, 0xAE, 0x8C, 0xC3, 0x3B, 0x60, 0x45, 0x05, 0x99, 0xBD, 0x02, 0xC9, 0x63, 0x82, 0x90, 0x2A, 0xEF, 0x7F, 0x83, 0x2A}, }, - eaxAESTest{ + { []byte{0x6C, 0xF3, 0x67, 0x20, 0x87, 0x2B, 0x85, 0x13, 0xF6, 0xEA, 0xB1, 0xA8, 0xA4, 0x44, 0x38, 0xD5, 0xEF, 0x11}, []byte{0xA4, 0xA4, 0x78, 0x2B, 0xCF, 0xFD, 0x3E, 0xC5, 0xE7, 0xEF, 0x6D, 0x8C, 0x34, 0xA5, 0x61, 0x23}, []byte{0xB7, 0x81, 0xFC, 0xF2, 0xF7, 0x5F, 0xA5, 0xA8, 0xDE, 0x97, 0xA9, 0xCA, 0x48, 0xE5, 0x22, 0xEC}, []byte{0x89, 0x9A, 0x17, 0x58, 0x97, 0x56, 0x1D, 0x7E}, []byte{0x0D, 0xE1, 0x8F, 0xD0, 0xFD, 0xD9, 0x1E, 0x7A, 0xF1, 0x9F, 0x1D, 0x8E, 0xE8, 0x73, 0x39, 0x38, 0xB1, 0xE8, 0xE7, 0xF6, 0xD2, 0x23, 0x16, 0x18, 0x10, 0x2F, 0xDB, 0x7F, 0xE5, 0x5F, 0xF1, 0x99, 0x17, 0x00}, }, - eaxAESTest{ + { []byte{0xCA, 0x40, 0xD7, 0x44, 0x6E, 0x54, 0x5F, 0xFA, 0xED, 0x3B, 0xD1, 0x2A, 0x74, 0x0A, 0x65, 0x9F, 0xFB, 0xBB, 0x3C, 0xEA, 0xB7}, []byte{0x83, 0x95, 0xFC, 0xF1, 0xE9, 0x5B, 0xEB, 0xD6, 0x97, 0xBD, 0x01, 0x0B, 0xC7, 0x66, 0xAA, 0xC3}, []byte{0x22, 0xE7, 0xAD, 0xD9, 0x3C, 0xFC, 0x63, 0x93, 0xC5, 0x7E, 0xC0, 0xB3, 0xC1, 0x7D, 0x6B, 0x44}, diff --git a/src/pkg/crypto/block/ecb.go b/src/pkg/crypto/block/ecb.go index 73d1d63f7..cf09f7cb3 100644 --- a/src/pkg/crypto/block/ecb.go +++ b/src/pkg/crypto/block/ecb.go @@ -127,9 +127,7 @@ func (x *ecbDecrypter) Read(p []byte) (n int, err os.Error) { // Save it for next time. if i < n { p = p[i:n] - for j, v := range p { - x.buf[j] = v - } + copy(x.buf, p) x.crypt = x.buf[0:len(p)] n = i } @@ -191,11 +189,7 @@ func (x *ecbEncrypter) slidePlain() { if len(x.plain) == 0 { x.plain = x.buf[0:0] } else if cap(x.plain) < cap(x.buf) { - // plain and buf share same data, - // but buf is before plain, so forward loop is correct - for i := 0; i < len(x.plain); i++ { - x.buf[i] = x.plain[i] - } + copy(x.buf, x.plain) x.plain = x.buf[0:len(x.plain)] } } diff --git a/src/pkg/crypto/block/ecb_aes_test.go b/src/pkg/crypto/block/ecb_aes_test.go index db0e085fa..14481d096 100644 --- a/src/pkg/crypto/block/ecb_aes_test.go +++ b/src/pkg/crypto/block/ecb_aes_test.go @@ -47,7 +47,7 @@ var commonIV = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 var ecbAESTests = []ecbTest{ // FIPS 197, Appendix B, C - ecbTest{ + { "FIPS-197 Appendix B", commonKey128, []byte{0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34}, @@ -55,7 +55,7 @@ var ecbAESTests = []ecbTest{ }, // NIST SP 800-38A pp 24-27 - ecbTest{ + { "ECB-AES128", commonKey128, commonInput, @@ -66,7 +66,7 @@ var ecbAESTests = []ecbTest{ 0x7b, 0x0c, 0x78, 0x5e, 0x27, 0xe8, 0xad, 0x3f, 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5d, 0xd4, }, }, - ecbTest{ + { "ECB-AES192", commonKey192, commonInput, @@ -77,7 +77,7 @@ var ecbAESTests = []ecbTest{ 0x9a, 0x4b, 0x41, 0xba, 0x73, 0x8d, 0x6c, 0x72, 0xfb, 0x16, 0x69, 0x16, 0x03, 0xc1, 0x8e, 0x0e, }, }, - ecbTest{ + { "ECB-AES256", commonKey256, commonInput, diff --git a/src/pkg/crypto/block/ecb_test.go b/src/pkg/crypto/block/ecb_test.go index 1e991e1dd..6f79d929a 100644 --- a/src/pkg/crypto/block/ecb_test.go +++ b/src/pkg/crypto/block/ecb_test.go @@ -22,7 +22,7 @@ type IncCipher struct { func (c *IncCipher) BlockSize() int { return c.blockSize } -func (c *IncCipher) Encrypt(src, dst []byte) { +func (c *IncCipher) Encrypt(dst, src []byte) { if !c.encrypting { panic("encrypt: not encrypting") } @@ -35,7 +35,7 @@ func (c *IncCipher) Encrypt(src, dst []byte) { } } -func (c *IncCipher) Decrypt(src, dst []byte) { +func (c *IncCipher) Decrypt(dst, src []byte) { if c.encrypting { panic("decrypt: not decrypting") } diff --git a/src/pkg/crypto/block/ofb.go b/src/pkg/crypto/block/ofb.go index 0cd5e73c4..11aaaa4d7 100644 --- a/src/pkg/crypto/block/ofb.go +++ b/src/pkg/crypto/block/ofb.go @@ -29,7 +29,7 @@ func newOFBStream(c Cipher, iv []byte) *ofbStream { if n != c.BlockSize() { panic(fmt.Sprintln("crypto/block: newOFBStream: invalid iv size", n, "!=", c.BlockSize())) } - x.iv = copy(iv) + x.iv = dup(iv) return x } diff --git a/src/pkg/crypto/block/ofb_aes_test.go b/src/pkg/crypto/block/ofb_aes_test.go index f2faa4432..9c527a6b3 100644 --- a/src/pkg/crypto/block/ofb_aes_test.go +++ b/src/pkg/crypto/block/ofb_aes_test.go @@ -27,7 +27,7 @@ type ofbTest struct { var ofbAESTests = []ofbTest{ // NIST SP 800-38A pp 52-55 - ofbTest{ + { "OFB-AES128", commonKey128, commonIV, @@ -39,7 +39,7 @@ var ofbAESTests = []ofbTest{ 0x30, 0x4c, 0x65, 0x28, 0xf6, 0x59, 0xc7, 0x78, 0x66, 0xa5, 0x10, 0xd9, 0xc1, 0xd6, 0xae, 0x5e, }, }, - ofbTest{ + { "OFB-AES192", commonKey192, commonIV, @@ -51,7 +51,7 @@ var ofbAESTests = []ofbTest{ 0x6d, 0x9f, 0x20, 0x08, 0x57, 0xca, 0x6c, 0x3e, 0x9c, 0xac, 0x52, 0x4b, 0xd9, 0xac, 0xc9, 0x2a, }, }, - ofbTest{ + { "OFB-AES256", commonKey256, commonIV, diff --git a/src/pkg/crypto/blowfish/Makefile b/src/pkg/crypto/blowfish/Makefile index c2999cc1d..f370ab28b 100644 --- a/src/pkg/crypto/blowfish/Makefile +++ b/src/pkg/crypto/blowfish/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=crypto/blowfish GOFILES=\ diff --git a/src/pkg/crypto/blowfish/blowfish_test.go b/src/pkg/crypto/blowfish/blowfish_test.go index 44fed9668..3a7ab6c2a 100644 --- a/src/pkg/crypto/blowfish/blowfish_test.go +++ b/src/pkg/crypto/blowfish/blowfish_test.go @@ -16,140 +16,140 @@ type CryptTest struct { // Test vector values are from http://www.schneier.com/code/vectors.txt. var encryptTests = []CryptTest{ - CryptTest{ + { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}}, - CryptTest{ + { []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, []byte{0x51, 0x86, 0x6F, 0xD5, 0xB8, 0x5E, 0xCB, 0x8A}}, - CryptTest{ + { []byte{0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, []byte{0x7D, 0x85, 0x6F, 0x9A, 0x61, 0x30, 0x63, 0xF2}}, - CryptTest{ + { []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, []byte{0x24, 0x66, 0xDD, 0x87, 0x8B, 0x96, 0x3C, 0x9D}}, - CryptTest{ + { []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, []byte{0x61, 0xF9, 0xC3, 0x80, 0x22, 0x81, 0xB0, 0x96}}, - CryptTest{ + { []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, []byte{0x7D, 0x0C, 0xC6, 0x30, 0xAF, 0xDA, 0x1E, 0xC7}}, - CryptTest{ + { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}}, - CryptTest{ + { []byte{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, []byte{0x0A, 0xCE, 0xAB, 0x0F, 0xC6, 0xA0, 0xA2, 0x8D}}, - CryptTest{ + { []byte{0x7C, 0xA1, 0x10, 0x45, 0x4A, 0x1A, 0x6E, 0x57}, []byte{0x01, 0xA1, 0xD6, 0xD0, 0x39, 0x77, 0x67, 0x42}, []byte{0x59, 0xC6, 0x82, 0x45, 0xEB, 0x05, 0x28, 0x2B}}, - CryptTest{ + { []byte{0x01, 0x31, 0xD9, 0x61, 0x9D, 0xC1, 0x37, 0x6E}, []byte{0x5C, 0xD5, 0x4C, 0xA8, 0x3D, 0xEF, 0x57, 0xDA}, []byte{0xB1, 0xB8, 0xCC, 0x0B, 0x25, 0x0F, 0x09, 0xA0}}, - CryptTest{ + { []byte{0x07, 0xA1, 0x13, 0x3E, 0x4A, 0x0B, 0x26, 0x86}, []byte{0x02, 0x48, 0xD4, 0x38, 0x06, 0xF6, 0x71, 0x72}, []byte{0x17, 0x30, 0xE5, 0x77, 0x8B, 0xEA, 0x1D, 0xA4}}, - CryptTest{ + { []byte{0x38, 0x49, 0x67, 0x4C, 0x26, 0x02, 0x31, 0x9E}, []byte{0x51, 0x45, 0x4B, 0x58, 0x2D, 0xDF, 0x44, 0x0A}, []byte{0xA2, 0x5E, 0x78, 0x56, 0xCF, 0x26, 0x51, 0xEB}}, - CryptTest{ + { []byte{0x04, 0xB9, 0x15, 0xBA, 0x43, 0xFE, 0xB5, 0xB6}, []byte{0x42, 0xFD, 0x44, 0x30, 0x59, 0x57, 0x7F, 0xA2}, []byte{0x35, 0x38, 0x82, 0xB1, 0x09, 0xCE, 0x8F, 0x1A}}, - CryptTest{ + { []byte{0x01, 0x13, 0xB9, 0x70, 0xFD, 0x34, 0xF2, 0xCE}, []byte{0x05, 0x9B, 0x5E, 0x08, 0x51, 0xCF, 0x14, 0x3A}, []byte{0x48, 0xF4, 0xD0, 0x88, 0x4C, 0x37, 0x99, 0x18}}, - CryptTest{ + { []byte{0x01, 0x70, 0xF1, 0x75, 0x46, 0x8F, 0xB5, 0xE6}, []byte{0x07, 0x56, 0xD8, 0xE0, 0x77, 0x47, 0x61, 0xD2}, []byte{0x43, 0x21, 0x93, 0xB7, 0x89, 0x51, 0xFC, 0x98}}, - CryptTest{ + { []byte{0x43, 0x29, 0x7F, 0xAD, 0x38, 0xE3, 0x73, 0xFE}, []byte{0x76, 0x25, 0x14, 0xB8, 0x29, 0xBF, 0x48, 0x6A}, []byte{0x13, 0xF0, 0x41, 0x54, 0xD6, 0x9D, 0x1A, 0xE5}}, - CryptTest{ + { []byte{0x07, 0xA7, 0x13, 0x70, 0x45, 0xDA, 0x2A, 0x16}, []byte{0x3B, 0xDD, 0x11, 0x90, 0x49, 0x37, 0x28, 0x02}, []byte{0x2E, 0xED, 0xDA, 0x93, 0xFF, 0xD3, 0x9C, 0x79}}, - CryptTest{ + { []byte{0x04, 0x68, 0x91, 0x04, 0xC2, 0xFD, 0x3B, 0x2F}, []byte{0x26, 0x95, 0x5F, 0x68, 0x35, 0xAF, 0x60, 0x9A}, []byte{0xD8, 0x87, 0xE0, 0x39, 0x3C, 0x2D, 0xA6, 0xE3}}, - CryptTest{ + { []byte{0x37, 0xD0, 0x6B, 0xB5, 0x16, 0xCB, 0x75, 0x46}, []byte{0x16, 0x4D, 0x5E, 0x40, 0x4F, 0x27, 0x52, 0x32}, []byte{0x5F, 0x99, 0xD0, 0x4F, 0x5B, 0x16, 0x39, 0x69}}, - CryptTest{ + { []byte{0x1F, 0x08, 0x26, 0x0D, 0x1A, 0xC2, 0x46, 0x5E}, []byte{0x6B, 0x05, 0x6E, 0x18, 0x75, 0x9F, 0x5C, 0xCA}, []byte{0x4A, 0x05, 0x7A, 0x3B, 0x24, 0xD3, 0x97, 0x7B}}, - CryptTest{ + { []byte{0x58, 0x40, 0x23, 0x64, 0x1A, 0xBA, 0x61, 0x76}, []byte{0x00, 0x4B, 0xD6, 0xEF, 0x09, 0x17, 0x60, 0x62}, []byte{0x45, 0x20, 0x31, 0xC1, 0xE4, 0xFA, 0xDA, 0x8E}}, - CryptTest{ + { []byte{0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xB0, 0x07}, []byte{0x48, 0x0D, 0x39, 0x00, 0x6E, 0xE7, 0x62, 0xF2}, []byte{0x75, 0x55, 0xAE, 0x39, 0xF5, 0x9B, 0x87, 0xBD}}, - CryptTest{ + { []byte{0x49, 0x79, 0x3E, 0xBC, 0x79, 0xB3, 0x25, 0x8F}, []byte{0x43, 0x75, 0x40, 0xC8, 0x69, 0x8F, 0x3C, 0xFA}, []byte{0x53, 0xC5, 0x5F, 0x9C, 0xB4, 0x9F, 0xC0, 0x19}}, - CryptTest{ + { []byte{0x4F, 0xB0, 0x5E, 0x15, 0x15, 0xAB, 0x73, 0xA7}, []byte{0x07, 0x2D, 0x43, 0xA0, 0x77, 0x07, 0x52, 0x92}, []byte{0x7A, 0x8E, 0x7B, 0xFA, 0x93, 0x7E, 0x89, 0xA3}}, - CryptTest{ + { []byte{0x49, 0xE9, 0x5D, 0x6D, 0x4C, 0xA2, 0x29, 0xBF}, []byte{0x02, 0xFE, 0x55, 0x77, 0x81, 0x17, 0xF1, 0x2A}, []byte{0xCF, 0x9C, 0x5D, 0x7A, 0x49, 0x86, 0xAD, 0xB5}}, - CryptTest{ + { []byte{0x01, 0x83, 0x10, 0xDC, 0x40, 0x9B, 0x26, 0xD6}, []byte{0x1D, 0x9D, 0x5C, 0x50, 0x18, 0xF7, 0x28, 0xC2}, []byte{0xD1, 0xAB, 0xB2, 0x90, 0x65, 0x8B, 0xC7, 0x78}}, - CryptTest{ + { []byte{0x1C, 0x58, 0x7F, 0x1C, 0x13, 0x92, 0x4F, 0xEF}, []byte{0x30, 0x55, 0x32, 0x28, 0x6D, 0x6F, 0x29, 0x5A}, []byte{0x55, 0xCB, 0x37, 0x74, 0xD1, 0x3E, 0xF2, 0x01}}, - CryptTest{ + { []byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, []byte{0xFA, 0x34, 0xEC, 0x48, 0x47, 0xB2, 0x68, 0xB2}}, - CryptTest{ + { []byte{0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E}, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, []byte{0xA7, 0x90, 0x79, 0x51, 0x08, 0xEA, 0x3C, 0xAE}}, - CryptTest{ + { []byte{0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE}, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, []byte{0xC3, 0x9E, 0x07, 0x2D, 0x9F, 0xAC, 0x63, 0x1D}}, - CryptTest{ + { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, []byte{0x01, 0x49, 0x33, 0xE0, 0xCD, 0xAF, 0xF6, 0xE4}}, - CryptTest{ + { []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0xF2, 0x1E, 0x9A, 0x77, 0xB7, 0x1C, 0x49, 0xBC}}, - CryptTest{ + { []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x24, 0x59, 0x46, 0x88, 0x57, 0x54, 0x36, 0x9A}}, - CryptTest{ + { []byte{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, []byte{0x6B, 0x5C, 0x5A, 0x9C, 0x5D, 0x9E, 0x0A, 0x5A}}, @@ -163,7 +163,7 @@ func TestCipherEncrypt(t *testing.T) { continue } ct := make([]byte, len(tt.out)) - c.Encrypt(tt.in, ct) + c.Encrypt(ct, tt.in) for j, v := range ct { if v != tt.out[j] { t.Errorf("Cipher.Encrypt, test vector #%d: cipher-text[%d] = %#x, expected %#x", i, j, v, tt.out[j]) @@ -181,7 +181,7 @@ func TestCipherDecrypt(t *testing.T) { continue } pt := make([]byte, len(tt.in)) - c.Decrypt(tt.out, pt) + c.Decrypt(pt, tt.out) for j, v := range pt { if v != tt.in[j] { t.Errorf("Cipher.Decrypt, test vector #%d: plain-text[%d] = %#x, expected %#x", i, j, v, tt.in[j]) diff --git a/src/pkg/crypto/blowfish/cipher.go b/src/pkg/crypto/blowfish/cipher.go index ee0def85e..947f762d8 100644 --- a/src/pkg/crypto/blowfish/cipher.go +++ b/src/pkg/crypto/blowfish/cipher.go @@ -50,7 +50,7 @@ func (c *Cipher) BlockSize() int { return BlockSize } // Note that for amounts of data larger than a block, // it is not safe to just call Encrypt on successive blocks; // instead, use an encryption mode like CBC (see crypto/block/cbc.go). -func (c *Cipher) Encrypt(src, dst []byte) { +func (c *Cipher) Encrypt(dst, src []byte) { l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) l, r = encryptBlock(l, r, c) @@ -60,7 +60,7 @@ func (c *Cipher) Encrypt(src, dst []byte) { // Decrypt decrypts the 8-byte buffer src using the key k // and stores the result in dst. -func (c *Cipher) Decrypt(src, dst []byte) { +func (c *Cipher) Decrypt(dst, src []byte) { l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) l, r = decryptBlock(l, r, c) diff --git a/src/pkg/crypto/cast5/Makefile b/src/pkg/crypto/cast5/Makefile new file mode 100644 index 000000000..346fadd94 --- /dev/null +++ b/src/pkg/crypto/cast5/Makefile @@ -0,0 +1,11 @@ +# Copyright 2010 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../../Make.inc + +TARG=crypto/cast5 +GOFILES=\ + cast5.go\ + +include ../../../Make.pkg diff --git a/src/pkg/crypto/cast5/cast5.go b/src/pkg/crypto/cast5/cast5.go new file mode 100644 index 000000000..35f3e64b6 --- /dev/null +++ b/src/pkg/crypto/cast5/cast5.go @@ -0,0 +1,536 @@ +// 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 package implements CAST5, as defined in RFC 2144. CAST5 is a common +// OpenPGP cipher. +package cast5 + +import ( + "os" +) + +const BlockSize = 8 +const KeySize = 16 + +type Cipher struct { + masking [16]uint32 + rotate [16]uint8 +} + +func NewCipher(key []byte) (c *Cipher, err os.Error) { + if len(key) != KeySize { + return nil, os.ErrorString("CAST5: keys must be 16 bytes") + } + + c = new(Cipher) + c.keySchedule(key) + return +} + +func (c *Cipher) BlockSize() int { + return BlockSize +} + +// Reset zeros the key material in memory. +func (c *Cipher) Reset() { + for i := 0; i < 16; i++ { + c.masking[i] = 0 + c.rotate[i] = 0 + } +} + +func (c *Cipher) Encrypt(dst, src []byte) { + l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) + r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) + + l, r = r, l^f1(r, c.masking[0], c.rotate[0]) + l, r = r, l^f2(r, c.masking[1], c.rotate[1]) + l, r = r, l^f3(r, c.masking[2], c.rotate[2]) + l, r = r, l^f1(r, c.masking[3], c.rotate[3]) + + l, r = r, l^f2(r, c.masking[4], c.rotate[4]) + l, r = r, l^f3(r, c.masking[5], c.rotate[5]) + l, r = r, l^f1(r, c.masking[6], c.rotate[6]) + l, r = r, l^f2(r, c.masking[7], c.rotate[7]) + + l, r = r, l^f3(r, c.masking[8], c.rotate[8]) + l, r = r, l^f1(r, c.masking[9], c.rotate[9]) + l, r = r, l^f2(r, c.masking[10], c.rotate[10]) + l, r = r, l^f3(r, c.masking[11], c.rotate[11]) + + l, r = r, l^f1(r, c.masking[12], c.rotate[12]) + l, r = r, l^f2(r, c.masking[13], c.rotate[13]) + l, r = r, l^f3(r, c.masking[14], c.rotate[14]) + l, r = r, l^f1(r, c.masking[15], c.rotate[15]) + + dst[0] = uint8(r >> 24) + dst[1] = uint8(r >> 16) + dst[2] = uint8(r >> 8) + dst[3] = uint8(r) + dst[4] = uint8(l >> 24) + dst[5] = uint8(l >> 16) + dst[6] = uint8(l >> 8) + dst[7] = uint8(l) +} + +func (c *Cipher) Decrypt(dst, src []byte) { + l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) + r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) + + l, r = r, l^f1(r, c.masking[15], c.rotate[15]) + l, r = r, l^f3(r, c.masking[14], c.rotate[14]) + l, r = r, l^f2(r, c.masking[13], c.rotate[13]) + l, r = r, l^f1(r, c.masking[12], c.rotate[12]) + + l, r = r, l^f3(r, c.masking[11], c.rotate[11]) + l, r = r, l^f2(r, c.masking[10], c.rotate[10]) + l, r = r, l^f1(r, c.masking[9], c.rotate[9]) + l, r = r, l^f3(r, c.masking[8], c.rotate[8]) + + l, r = r, l^f2(r, c.masking[7], c.rotate[7]) + l, r = r, l^f1(r, c.masking[6], c.rotate[6]) + l, r = r, l^f3(r, c.masking[5], c.rotate[5]) + l, r = r, l^f2(r, c.masking[4], c.rotate[4]) + + l, r = r, l^f1(r, c.masking[3], c.rotate[3]) + l, r = r, l^f3(r, c.masking[2], c.rotate[2]) + l, r = r, l^f2(r, c.masking[1], c.rotate[1]) + l, r = r, l^f1(r, c.masking[0], c.rotate[0]) + + dst[0] = uint8(r >> 24) + dst[1] = uint8(r >> 16) + dst[2] = uint8(r >> 8) + dst[3] = uint8(r) + dst[4] = uint8(l >> 24) + dst[5] = uint8(l >> 16) + dst[6] = uint8(l >> 8) + dst[7] = uint8(l) +} + +type keyScheduleA [4][7]uint8 +type keyScheduleB [4][5]uint8 + +// keyScheduleRound contains the magic values for a round of the key schedule. +// The keyScheduleA deals with the lines like: +// z0z1z2z3 = x0x1x2x3 ^ S5[xD] ^ S6[xF] ^ S7[xC] ^ S8[xE] ^ S7[x8] +// Conceptually, both x and z are in the same array, x first. The first +// element describes which word of this array gets written to and the +// second, which word gets read. So, for the line above, it's "4, 0", because +// it's writing to the first word of z, which, being after x, is word 4, and +// reading from the first word of x: word 0. +// +// Next are the indexes into the S-boxes. Now the array is treated as bytes. So +// "xD" is 0xd. The first byte of z is written as "16 + 0", just to be clear +// that it's z that we're indexing. +// +// keyScheduleB deals with lines like: +// K1 = S5[z8] ^ S6[z9] ^ S7[z7] ^ S8[z6] ^ S5[z2] +// "K1" is ignored because key words are always written in order. So the five +// elements are the S-box indexes. They use the same form as in keyScheduleA, +// above. + +type keyScheduleRound struct{} +type keySchedule []keyScheduleRound + +var schedule = []struct { + a keyScheduleA + b keyScheduleB +}{ + { + keyScheduleA{ + {4, 0, 0xd, 0xf, 0xc, 0xe, 0x8}, + {5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa}, + {6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9}, + {7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb}, + }, + keyScheduleB{ + {16 + 8, 16 + 9, 16 + 7, 16 + 6, 16 + 2}, + {16 + 0xa, 16 + 0xb, 16 + 5, 16 + 4, 16 + 6}, + {16 + 0xc, 16 + 0xd, 16 + 3, 16 + 2, 16 + 9}, + {16 + 0xe, 16 + 0xf, 16 + 1, 16 + 0, 16 + 0xc}, + }, + }, + { + keyScheduleA{ + {0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0}, + {1, 4, 0, 2, 1, 3, 16 + 2}, + {2, 5, 7, 6, 5, 4, 16 + 1}, + {3, 7, 0xa, 9, 0xb, 8, 16 + 3}, + }, + keyScheduleB{ + {3, 2, 0xc, 0xd, 8}, + {1, 0, 0xe, 0xf, 0xd}, + {7, 6, 8, 9, 3}, + {5, 4, 0xa, 0xb, 7}, + }, + }, + { + keyScheduleA{ + {4, 0, 0xd, 0xf, 0xc, 0xe, 8}, + {5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa}, + {6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9}, + {7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb}, + }, + keyScheduleB{ + {16 + 3, 16 + 2, 16 + 0xc, 16 + 0xd, 16 + 9}, + {16 + 1, 16 + 0, 16 + 0xe, 16 + 0xf, 16 + 0xc}, + {16 + 7, 16 + 6, 16 + 8, 16 + 9, 16 + 2}, + {16 + 5, 16 + 4, 16 + 0xa, 16 + 0xb, 16 + 6}, + }, + }, + { + keyScheduleA{ + {0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0}, + {1, 4, 0, 2, 1, 3, 16 + 2}, + {2, 5, 7, 6, 5, 4, 16 + 1}, + {3, 7, 0xa, 9, 0xb, 8, 16 + 3}, + }, + keyScheduleB{ + {8, 9, 7, 6, 3}, + {0xa, 0xb, 5, 4, 7}, + {0xc, 0xd, 3, 2, 8}, + {0xe, 0xf, 1, 0, 0xd}, + }, + }, +} + +func (c *Cipher) keySchedule(in []byte) { + var t [8]uint32 + var k [32]uint32 + + for i := 0; i < 4; i++ { + j := i * 4 + t[i] = uint32(in[j])<<24 | uint32(in[j+1])<<16 | uint32(in[j+2])<<8 | uint32(in[j+3]) + } + + x := []byte{6, 7, 4, 5} + ki := 0 + + for half := 0; half < 2; half++ { + for _, round := range schedule { + for j := 0; j < 4; j++ { + var a [7]uint8 + copy(a[:], round.a[j][:]) + w := t[a[1]] + w ^= sBox[4][(t[a[2]>>2]>>(24-8*(a[2]&3)))&0xff] + w ^= sBox[5][(t[a[3]>>2]>>(24-8*(a[3]&3)))&0xff] + w ^= sBox[6][(t[a[4]>>2]>>(24-8*(a[4]&3)))&0xff] + w ^= sBox[7][(t[a[5]>>2]>>(24-8*(a[5]&3)))&0xff] + w ^= sBox[x[j]][(t[a[6]>>2]>>(24-8*(a[6]&3)))&0xff] + t[a[0]] = w + } + + for j := 0; j < 4; j++ { + var b [5]uint8 + copy(b[:], round.b[j][:]) + w := sBox[4][(t[b[0]>>2]>>(24-8*(b[0]&3)))&0xff] + w ^= sBox[5][(t[b[1]>>2]>>(24-8*(b[1]&3)))&0xff] + w ^= sBox[6][(t[b[2]>>2]>>(24-8*(b[2]&3)))&0xff] + w ^= sBox[7][(t[b[3]>>2]>>(24-8*(b[3]&3)))&0xff] + w ^= sBox[4+j][(t[b[4]>>2]>>(24-8*(b[4]&3)))&0xff] + k[ki] = w + ki++ + } + } + } + + for i := 0; i < 16; i++ { + c.masking[i] = k[i] + c.rotate[i] = uint8(k[16+i] & 0x1f) + } +} + +// These are the three 'f' functions. See RFC 2144, section 2.2. +func f1(d, m uint32, r uint8) uint32 { + t := m + d + I := (t << r) | (t >> (32 - r)) + return ((sBox[0][I>>24] ^ sBox[1][(I>>16)&0xff]) - sBox[2][(I>>8)&0xff]) + sBox[3][I&0xff] +} + +func f2(d, m uint32, r uint8) uint32 { + t := m ^ d + I := (t << r) | (t >> (32 - r)) + return ((sBox[0][I>>24] - sBox[1][(I>>16)&0xff]) + sBox[2][(I>>8)&0xff]) ^ sBox[3][I&0xff] +} + +func f3(d, m uint32, r uint8) uint32 { + t := m - d + I := (t << r) | (t >> (32 - r)) + return ((sBox[0][I>>24] + sBox[1][(I>>16)&0xff]) ^ sBox[2][(I>>8)&0xff]) - sBox[3][I&0xff] +} + +var sBox = [8][256]uint32{ + { + 0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949, + 0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e, + 0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d, + 0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0, + 0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, + 0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935, + 0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d, + 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50, + 0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe, + 0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3, + 0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167, + 0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291, + 0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779, + 0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2, + 0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511, + 0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d, + 0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5, + 0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324, + 0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c, + 0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc, + 0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d, + 0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96, + 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a, + 0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d, + 0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd, + 0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6, + 0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9, + 0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872, + 0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c, + 0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e, + 0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9, + 0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf, + }, + { + 0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651, + 0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3, + 0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb, + 0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806, + 0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b, + 0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359, + 0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b, + 0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c, + 0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34, + 0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb, + 0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd, + 0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860, + 0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b, + 0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304, + 0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b, + 0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf, + 0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c, + 0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13, + 0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f, + 0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6, + 0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6, + 0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58, + 0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906, + 0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d, + 0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6, + 0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4, + 0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6, + 0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f, + 0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249, + 0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa, + 0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9, + 0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1, + }, + { + 0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90, + 0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5, + 0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e, + 0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240, + 0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5, + 0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b, + 0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71, + 0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04, + 0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82, + 0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15, + 0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2, + 0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176, + 0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148, + 0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc, + 0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341, + 0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e, + 0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51, + 0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f, + 0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a, + 0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b, + 0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b, + 0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5, + 0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45, + 0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536, + 0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc, + 0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0, + 0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69, + 0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2, + 0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49, + 0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d, + 0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a, + 0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783, + }, + { + 0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1, + 0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf, + 0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15, + 0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121, + 0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25, + 0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5, + 0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb, + 0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5, + 0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d, + 0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6, + 0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23, + 0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003, + 0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6, + 0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119, + 0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24, + 0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a, + 0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79, + 0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df, + 0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26, + 0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab, + 0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7, + 0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417, + 0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2, + 0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2, + 0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a, + 0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919, + 0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef, + 0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876, + 0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab, + 0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04, + 0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282, + 0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2, + }, + { + 0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f, + 0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a, + 0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff, + 0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02, + 0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a, + 0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7, + 0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9, + 0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981, + 0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774, + 0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655, + 0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2, + 0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910, + 0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1, + 0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da, + 0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049, + 0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f, + 0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba, + 0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be, + 0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3, + 0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840, + 0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4, + 0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2, + 0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7, + 0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5, + 0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e, + 0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e, + 0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801, + 0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad, + 0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0, + 0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20, + 0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8, + 0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4, + }, + { + 0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac, + 0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138, + 0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367, + 0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98, + 0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072, + 0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3, + 0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd, + 0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8, + 0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9, + 0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54, + 0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387, + 0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc, + 0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf, + 0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf, + 0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f, + 0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289, + 0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950, + 0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f, + 0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b, + 0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be, + 0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13, + 0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976, + 0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0, + 0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891, + 0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da, + 0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc, + 0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084, + 0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25, + 0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121, + 0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5, + 0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd, + 0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f, + }, + { + 0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f, + 0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de, + 0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43, + 0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19, + 0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2, + 0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516, + 0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88, + 0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816, + 0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756, + 0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a, + 0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264, + 0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688, + 0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28, + 0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3, + 0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7, + 0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06, + 0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033, + 0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a, + 0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566, + 0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509, + 0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962, + 0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e, + 0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c, + 0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c, + 0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285, + 0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301, + 0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be, + 0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767, + 0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647, + 0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914, + 0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c, + 0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3, + }, + { + 0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5, + 0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc, + 0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd, + 0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d, + 0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2, + 0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862, + 0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc, + 0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c, + 0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e, + 0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039, + 0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8, + 0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42, + 0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5, + 0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472, + 0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225, + 0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c, + 0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb, + 0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054, + 0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70, + 0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc, + 0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c, + 0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3, + 0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4, + 0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101, + 0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f, + 0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e, + 0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a, + 0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c, + 0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384, + 0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c, + 0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82, + 0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e, + }, +} diff --git a/src/pkg/crypto/cast5/cast5_test.go b/src/pkg/crypto/cast5/cast5_test.go new file mode 100644 index 000000000..5f7025ff2 --- /dev/null +++ b/src/pkg/crypto/cast5/cast5_test.go @@ -0,0 +1,104 @@ +// 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 cast5 + +import ( + "bytes" + "encoding/hex" + "testing" +) + +// This test vector is taken from RFC 2144, App B.1. +// Since the other two test vectors are for reduced-round variants, we can't +// use them. +var basicTests = []struct { + key, plainText, cipherText string +}{ + { + "0123456712345678234567893456789a", + "0123456789abcdef", + "238b4fe5847e44b2", + }, +} + +func TestBasic(t *testing.T) { + for i, test := range basicTests { + key, _ := hex.DecodeString(test.key) + plainText, _ := hex.DecodeString(test.plainText) + expected, _ := hex.DecodeString(test.cipherText) + + c, err := NewCipher(key) + if err != nil { + t.Errorf("#%d: failed to create Cipher: %s", i, err) + continue + } + var cipherText [BlockSize]byte + c.Encrypt(cipherText[:], plainText) + if !bytes.Equal(cipherText[:], expected) { + t.Errorf("#%d: got:%x want:%x", i, cipherText, expected) + } + + var plainTextAgain [BlockSize]byte + c.Decrypt(plainTextAgain[:], cipherText[:]) + if !bytes.Equal(plainTextAgain[:], plainText) { + t.Errorf("#%d: got:%x want:%x", i, plainTextAgain, plainText) + } + } +} + +// TestFull performs the test specified in RFC 2144, App B.2. +// However, due to the length of time taken, it's disabled here and a more +// limited version is included, below. +func TestFull(t *testing.T) { + // This is too slow for normal testing + return + + a, b := iterate(1000000) + + const expectedA = "eea9d0a249fd3ba6b3436fb89d6dca92" + const expectedB = "b2c95eb00c31ad7180ac05b8e83d696e" + + if hex.EncodeToString(a) != expectedA { + t.Errorf("a: got:%x want:%s", a, expectedA) + } + if hex.EncodeToString(b) != expectedB { + t.Errorf("b: got:%x want:%s", b, expectedB) + } +} + +func iterate(iterations int) ([]byte, []byte) { + const initValueHex = "0123456712345678234567893456789a" + + initValue, _ := hex.DecodeString(initValueHex) + + var a, b [16]byte + copy(a[:], initValue) + copy(b[:], initValue) + + for i := 0; i < iterations; i++ { + c, _ := NewCipher(b[:]) + c.Encrypt(a[:8], a[:8]) + c.Encrypt(a[8:], a[8:]) + c, _ = NewCipher(a[:]) + c.Encrypt(b[:8], b[:8]) + c.Encrypt(b[8:], b[8:]) + } + + return a[:], b[:] +} + +func TestLimited(t *testing.T) { + a, b := iterate(1000) + + const expectedA = "23f73b14b02a2ad7dfb9f2c35644798d" + const expectedB = "e5bf37eff14c456a40b21ce369370a9f" + + if hex.EncodeToString(a) != expectedA { + t.Errorf("a: got:%x want:%s", a, expectedA) + } + if hex.EncodeToString(b) != expectedB { + t.Errorf("b: got:%x want:%s", b, expectedB) + } +} diff --git a/src/pkg/crypto/cipher/Makefile b/src/pkg/crypto/cipher/Makefile new file mode 100644 index 000000000..d7e8a7a13 --- /dev/null +++ b/src/pkg/crypto/cipher/Makefile @@ -0,0 +1,16 @@ +# Copyright 2010 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../../Make.inc + +TARG=crypto/cipher +GOFILES=\ + cbc.go\ + cipher.go\ + ctr.go\ + io.go\ + ocfb.go\ + cfb.go + +include ../../../Make.pkg diff --git a/src/pkg/crypto/cipher/cbc.go b/src/pkg/crypto/cipher/cbc.go new file mode 100644 index 000000000..4632f882a --- /dev/null +++ b/src/pkg/crypto/cipher/cbc.go @@ -0,0 +1,78 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Cipher block chaining (CBC) mode. + +// CBC provides confidentiality by xoring (chaining) each plaintext block +// with the previous ciphertext block before applying the block cipher. + +// See NIST SP 800-38A, pp 10-11 + +package cipher + +type cbc struct { + b Block + blockSize int + iv []byte + tmp []byte +} + +func newCBC(b Block, iv []byte) *cbc { + return &cbc{ + b: b, + blockSize: b.BlockSize(), + iv: dup(iv), + tmp: make([]byte, b.BlockSize()), + } +} + +type cbcEncrypter cbc + +// NewCBCEncrypter returns a BlockMode which encrypts in cipher block chaining +// mode, using the given Block. The length of iv must be the same as the +// Block's block size. +func NewCBCEncrypter(b Block, iv []byte) BlockMode { + return (*cbcEncrypter)(newCBC(b, iv)) +} + +func (x *cbcEncrypter) BlockSize() int { return x.blockSize } + +func (x *cbcEncrypter) CryptBlocks(dst, src []byte) { + for len(src) > 0 { + for i := 0; i < x.blockSize; i++ { + x.iv[i] ^= src[i] + } + x.b.Encrypt(x.iv, x.iv) + for i := 0; i < x.blockSize; i++ { + dst[i] = x.iv[i] + } + src = src[x.blockSize:] + dst = dst[x.blockSize:] + } +} + +type cbcDecrypter cbc + +// NewCBCDecrypter returns a BlockMode which decrypts in cipher block chaining +// mode, using the given Block. The length of iv must be the same as the +// Block's block size as must match the iv used to encrypt the data. +func NewCBCDecrypter(b Block, iv []byte) BlockMode { + return (*cbcDecrypter)(newCBC(b, iv)) +} + +func (x *cbcDecrypter) BlockSize() int { return x.blockSize } + +func (x *cbcDecrypter) CryptBlocks(dst, src []byte) { + for len(src) > 0 { + x.b.Decrypt(x.tmp, src[:x.blockSize]) + for i := 0; i < x.blockSize; i++ { + x.tmp[i] ^= x.iv[i] + x.iv[i] = src[i] + dst[i] = x.tmp[i] + } + + src = src[x.blockSize:] + dst = dst[x.blockSize:] + } +} diff --git a/src/pkg/crypto/cipher/cbc_aes_test.go b/src/pkg/crypto/cipher/cbc_aes_test.go new file mode 100644 index 000000000..944ca1ba8 --- /dev/null +++ b/src/pkg/crypto/cipher/cbc_aes_test.go @@ -0,0 +1,89 @@ +// 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. + +// CBC AES test vectors. + +// See U.S. National Institute of Standards and Technology (NIST) +// Special Publication 800-38A, ``Recommendation for Block Cipher +// Modes of Operation,'' 2001 Edition, pp. 24-29. + +package cipher + +import ( + "bytes" + "crypto/aes" + "testing" +) + +var cbcAESTests = []struct { + name string + key []byte + iv []byte + in []byte + out []byte +}{ + // NIST SP 800-38A pp 27-29 + { + "CBC-AES128", + commonKey128, + commonIV, + commonInput, + []byte{ + 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, + 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, + 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, + 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7, + }, + }, + { + "CBC-AES192", + commonKey192, + commonIV, + commonInput, + []byte{ + 0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d, 0x71, 0x78, 0x18, 0x3a, 0x9f, 0xa0, 0x71, 0xe8, + 0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d, 0xed, 0xf4, 0xe5, 0xe7, 0x38, 0x76, 0x3f, 0x69, 0x14, 0x5a, + 0x57, 0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0, 0x7f, 0xa9, 0xba, 0xac, 0x3d, 0xf1, 0x02, 0xe0, + 0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88, 0x81, 0xd9, 0x20, 0xa9, 0xe6, 0x4f, 0x56, 0x15, 0xcd, + }, + }, + { + "CBC-AES256", + commonKey256, + commonIV, + commonInput, + []byte{ + 0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6, + 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d, + 0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, 0xa5, 0x30, 0xe2, 0x63, 0x04, 0x23, 0x14, 0x61, + 0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, 0xda, 0x6c, 0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b, + }, + }, +} + +func TestCBC_AES(t *testing.T) { + for _, tt := range cbcAESTests { + test := tt.name + + c, err := aes.NewCipher(tt.key) + if err != nil { + t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err) + continue + } + + encrypter := NewCBCEncrypter(c, tt.iv) + d := make([]byte, len(tt.in)) + encrypter.CryptBlocks(d, tt.in) + if !bytes.Equal(tt.out, d) { + t.Errorf("%s: CBCEncrypter\nhave %x\nwant %x", test, d, tt.out) + } + + decrypter := NewCBCDecrypter(c, tt.iv) + p := make([]byte, len(d)) + decrypter.CryptBlocks(p, d) + if !bytes.Equal(tt.in, p) { + t.Errorf("%s: CBCDecrypter\nhave %x\nwant %x", test, d, tt.in) + } + } +} diff --git a/src/pkg/crypto/cipher/cfb.go b/src/pkg/crypto/cipher/cfb.go new file mode 100644 index 000000000..d14165a86 --- /dev/null +++ b/src/pkg/crypto/cipher/cfb.go @@ -0,0 +1,64 @@ +// 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. + +// CFB (Cipher Feedback) Mode. + +package cipher + +type cfb struct { + b Block + out []byte + outUsed int + decrypt bool +} + +// NewCFBEncrypter returns a Stream which encrypts with cipher feedback mode, +// using the given Block. The iv must be the same length as the Block's block +// size. +func NewCFBEncrypter(block Block, iv []byte) Stream { + return newCFB(block, iv, false) +} + +// NewCFBDecrypter returns a Stream which decrypts with cipher feedback mode, +// using the given Block. The iv must be the same length as the Block's block +// size. +func NewCFBDecrypter(block Block, iv []byte) Stream { + return newCFB(block, iv, true) +} + +func newCFB(block Block, iv []byte, decrypt bool) Stream { + blockSize := block.BlockSize() + if len(iv) != blockSize { + return nil + } + + x := &cfb{ + b: block, + out: make([]byte, blockSize), + outUsed: 0, + decrypt: decrypt, + } + block.Encrypt(x.out, iv) + + return x +} + +func (x *cfb) XORKeyStream(dst, src []byte) { + for i := 0; i < len(src); i++ { + if x.outUsed == len(x.out) { + x.b.Encrypt(x.out, x.out) + x.outUsed = 0 + } + + if x.decrypt { + t := src[i] + dst[i] = src[i] ^ x.out[x.outUsed] + x.out[x.outUsed] = t + } else { + x.out[x.outUsed] ^= src[i] + dst[i] = x.out[x.outUsed] + } + x.outUsed++ + } +} diff --git a/src/pkg/crypto/cipher/cfb_test.go b/src/pkg/crypto/cipher/cfb_test.go new file mode 100644 index 000000000..9547bfceb --- /dev/null +++ b/src/pkg/crypto/cipher/cfb_test.go @@ -0,0 +1,35 @@ +// 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 cipher + +import ( + "bytes" + "crypto/aes" + "crypto/rand" + "testing" +) + +func TestCFB(t *testing.T) { + block, err := aes.NewCipher(commonKey128) + if err != nil { + t.Error(err) + return + } + + plaintext := []byte("this is the plaintext") + iv := make([]byte, block.BlockSize()) + rand.Reader.Read(iv) + cfb := NewCFBEncrypter(block, iv) + ciphertext := make([]byte, len(plaintext)) + cfb.XORKeyStream(ciphertext, plaintext) + + cfbdec := NewCFBDecrypter(block, iv) + plaintextCopy := make([]byte, len(plaintext)) + cfbdec.XORKeyStream(plaintextCopy, ciphertext) + + if !bytes.Equal(plaintextCopy, plaintext) { + t.Errorf("got: %x, want: %x", plaintextCopy, plaintext) + } +} diff --git a/src/pkg/crypto/cipher/cipher.go b/src/pkg/crypto/cipher/cipher.go new file mode 100644 index 000000000..50516b23a --- /dev/null +++ b/src/pkg/crypto/cipher/cipher.go @@ -0,0 +1,63 @@ +// 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 cipher package implements standard block cipher modes +// that can be wrapped around low-level block cipher implementations. +// See http://csrc.nist.gov/groups/ST/toolkit/BCM/current_modes.html +// and NIST Special Publication 800-38A. +package cipher + +// A Block represents an implementation of block cipher +// using a given key. It provides the capability to encrypt +// or decrypt individual blocks. The mode implementations +// extend that capability to streams of blocks. +type Block interface { + // BlockSize returns the cipher's block size. + BlockSize() int + + // Encrypt encrypts the first block in src into dst. + // Dst and src may point at the same memory. + Encrypt(dst, src []byte) + + // Decrypt decrypts the first block in src into dst. + // Dst and src may point at the same memory. + Decrypt(dst, src []byte) +} + +// A Stream represents a stream cipher. +type Stream interface { + // XORKeyStream XORs each byte in the given slice with a byte from the + // cipher's key stream. Dst and src may point to the same memory. + XORKeyStream(dst, src []byte) +} + +// A BlockMode represents a block cipher running in a block-based mode (CBC, +// ECB etc). +type BlockMode interface { + // BlockSize returns the mode's block size. + BlockSize() int + + // CryptBlocks encrypts or decrypts a number of blocks. The length of + // src must be a multiple of the block size. Dst and src may point to + // the same memory. + CryptBlocks(dst, src []byte) +} + +// Utility routines + +func shift1(dst, src []byte) byte { + var b byte + for i := len(src) - 1; i >= 0; i-- { + bb := src[i] >> 7 + dst[i] = src[i]<<1 | b + b = bb + } + return b +} + +func dup(p []byte) []byte { + q := make([]byte, len(p)) + copy(q, p) + return q +} diff --git a/src/pkg/crypto/cipher/common_test.go b/src/pkg/crypto/cipher/common_test.go new file mode 100644 index 000000000..fb755757c --- /dev/null +++ b/src/pkg/crypto/cipher/common_test.go @@ -0,0 +1,28 @@ +// 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 cipher + +// Common values for tests. + +var commonInput = []byte{ + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, +} + +var commonKey128 = []byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c} + +var commonKey192 = []byte{ + 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5, + 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b, +} + +var commonKey256 = []byte{ + 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, + 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4, +} + +var commonIV = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f} diff --git a/src/pkg/crypto/cipher/ctr.go b/src/pkg/crypto/cipher/ctr.go new file mode 100644 index 000000000..04436ec23 --- /dev/null +++ b/src/pkg/crypto/cipher/ctr.go @@ -0,0 +1,51 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Counter (CTR) mode. + +// CTR converts a block cipher into a stream cipher by +// repeatedly encrypting an incrementing counter and +// xoring the resulting stream of data with the input. + +// See NIST SP 800-38A, pp 13-15 + +package cipher + +type ctr struct { + b Block + ctr []byte + out []byte + outUsed int +} + +// NewCTR returns a Stream which encrypts/decrypts using the given Block in +// counter mode. The length of iv must be the same as the Block's block size. +func NewCTR(block Block, iv []byte) Stream { + return &ctr{ + b: block, + ctr: dup(iv), + out: make([]byte, len(iv)), + outUsed: len(iv), + } +} + +func (x *ctr) XORKeyStream(dst, src []byte) { + for i := 0; i < len(src); i++ { + if x.outUsed == len(x.ctr) { + x.b.Encrypt(x.out, x.ctr) + x.outUsed = 0 + + // Increment counter + for i := len(x.ctr) - 1; i >= 0; i-- { + x.ctr[i]++ + if x.ctr[i] != 0 { + break + } + } + } + + dst[i] = src[i] ^ x.out[x.outUsed] + x.outUsed++ + } +} diff --git a/src/pkg/crypto/cipher/ctr_aes_test.go b/src/pkg/crypto/cipher/ctr_aes_test.go new file mode 100644 index 000000000..8dca9968c --- /dev/null +++ b/src/pkg/crypto/cipher/ctr_aes_test.go @@ -0,0 +1,101 @@ +// 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. + +// CTR AES test vectors. + +// See U.S. National Institute of Standards and Technology (NIST) +// Special Publication 800-38A, ``Recommendation for Block Cipher +// Modes of Operation,'' 2001 Edition, pp. 55-58. + +package cipher + +import ( + "bytes" + "crypto/aes" + "testing" +) + +var commonCounter = []byte{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff} + +var ctrAESTests = []struct { + name string + key []byte + iv []byte + in []byte + out []byte +}{ + // NIST SP 800-38A pp 55-58 + { + "CTR-AES128", + commonKey128, + commonCounter, + commonInput, + []byte{ + 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce, + 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff, 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff, + 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab, + 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1, 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee, + }, + }, + { + "CTR-AES192", + commonKey192, + commonCounter, + commonInput, + []byte{ + 0x1a, 0xbc, 0x93, 0x24, 0x17, 0x52, 0x1c, 0xa2, 0x4f, 0x2b, 0x04, 0x59, 0xfe, 0x7e, 0x6e, 0x0b, + 0x09, 0x03, 0x39, 0xec, 0x0a, 0xa6, 0xfa, 0xef, 0xd5, 0xcc, 0xc2, 0xc6, 0xf4, 0xce, 0x8e, 0x94, + 0x1e, 0x36, 0xb2, 0x6b, 0xd1, 0xeb, 0xc6, 0x70, 0xd1, 0xbd, 0x1d, 0x66, 0x56, 0x20, 0xab, 0xf7, + 0x4f, 0x78, 0xa7, 0xf6, 0xd2, 0x98, 0x09, 0x58, 0x5a, 0x97, 0xda, 0xec, 0x58, 0xc6, 0xb0, 0x50, + }, + }, + { + "CTR-AES256", + commonKey256, + commonCounter, + commonInput, + []byte{ + 0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5, 0xb7, 0xa7, 0xf5, 0x04, 0xbb, 0xf3, 0xd2, 0x28, + 0xf4, 0x43, 0xe3, 0xca, 0x4d, 0x62, 0xb5, 0x9a, 0xca, 0x84, 0xe9, 0x90, 0xca, 0xca, 0xf5, 0xc5, + 0x2b, 0x09, 0x30, 0xda, 0xa2, 0x3d, 0xe9, 0x4c, 0xe8, 0x70, 0x17, 0xba, 0x2d, 0x84, 0x98, 0x8d, + 0xdf, 0xc9, 0xc5, 0x8d, 0xb6, 0x7a, 0xad, 0xa6, 0x13, 0xc2, 0xdd, 0x08, 0x45, 0x79, 0x41, 0xa6, + }, + }, +} + +func TestCTR_AES(t *testing.T) { + for _, tt := range ctrAESTests { + test := tt.name + + c, err := aes.NewCipher(tt.key) + if err != nil { + t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err) + continue + } + + for j := 0; j <= 5; j += 5 { + in := tt.in[0 : len(tt.in)-j] + ctr := NewCTR(c, tt.iv) + encrypted := make([]byte, len(in)) + ctr.XORKeyStream(encrypted, in) + if out := tt.out[0:len(in)]; !bytes.Equal(out, encrypted) { + t.Errorf("%s/%d: CTR\ninpt %x\nhave %x\nwant %x", test, len(in), in, encrypted, out) + } + } + + for j := 0; j <= 7; j += 7 { + in := tt.out[0 : len(tt.out)-j] + ctr := NewCTR(c, tt.iv) + plain := make([]byte, len(in)) + ctr.XORKeyStream(plain, in) + if out := tt.in[0:len(in)]; !bytes.Equal(out, plain) { + t.Errorf("%s/%d: CTRReader\nhave %x\nwant %x", test, len(out), plain, out) + } + } + + if t.Failed() { + break + } + } +} diff --git a/src/pkg/crypto/cipher/io.go b/src/pkg/crypto/cipher/io.go new file mode 100644 index 000000000..97f40b8e7 --- /dev/null +++ b/src/pkg/crypto/cipher/io.go @@ -0,0 +1,57 @@ +// 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 cipher + +import ( + "os" + "io" +) + +// The Stream* objects are so simple that all their members are public. Users +// can create them themselves. + +// StreamReader wraps a Stream into an io.Reader. It simply calls XORKeyStream +// to process each slice of data which passes through. +type StreamReader struct { + S Stream + R io.Reader +} + +func (r StreamReader) Read(dst []byte) (n int, err os.Error) { + n, err = r.R.Read(dst) + r.S.XORKeyStream(dst[:n], dst[:n]) + return +} + +// StreamWriter wraps a Stream into an io.Writer. It simply calls XORKeyStream +// to process each slice of data which passes through. If any Write call +// returns short then the StreamWriter is out of sync and must be discarded. +type StreamWriter struct { + S Stream + W io.Writer + Err os.Error +} + +func (w StreamWriter) Write(src []byte) (n int, err os.Error) { + if w.Err != nil { + return 0, w.Err + } + c := make([]byte, len(src)) + w.S.XORKeyStream(c, src) + n, err = w.W.Write(c) + if n != len(src) { + if err == nil { // should never happen + err = io.ErrShortWrite + } + w.Err = err + } + return +} + +func (w StreamWriter) Close() os.Error { + // This saves us from either requiring a WriteCloser or having a + // StreamWriterCloser. + return w.W.(io.Closer).Close() +} diff --git a/src/pkg/crypto/cipher/ocfb.go b/src/pkg/crypto/cipher/ocfb.go new file mode 100644 index 000000000..43cb5a531 --- /dev/null +++ b/src/pkg/crypto/cipher/ocfb.go @@ -0,0 +1,112 @@ +// 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. + +// OpenPGP CFB Mode. http://tools.ietf.org/html/rfc4880#section-13.9 + +package cipher + +type ocfbEncrypter struct { + b Block + fre []byte + outUsed int +} + +// NewOCFBEncrypter returns a Stream which encrypts data with OpenPGP's cipher +// feedback mode using the given Block, and an initial amount of ciphertext. +// randData must be random bytes and be the same length as the Block's block +// size. +func NewOCFBEncrypter(block Block, randData []byte) (Stream, []byte) { + blockSize := block.BlockSize() + if len(randData) != blockSize { + return nil, nil + } + + x := &ocfbEncrypter{ + b: block, + fre: make([]byte, blockSize), + outUsed: 0, + } + prefix := make([]byte, blockSize+2) + + block.Encrypt(x.fre, x.fre) + for i := 0; i < blockSize; i++ { + prefix[i] = randData[i] ^ x.fre[i] + } + + block.Encrypt(x.fre, prefix[:blockSize]) + prefix[blockSize] = x.fre[0] ^ randData[blockSize-2] + prefix[blockSize+1] = x.fre[1] ^ randData[blockSize-1] + + block.Encrypt(x.fre, prefix[2:]) + return x, prefix +} + +func (x *ocfbEncrypter) XORKeyStream(dst, src []byte) { + for i := 0; i < len(src); i++ { + if x.outUsed == len(x.fre) { + x.b.Encrypt(x.fre, x.fre) + x.outUsed = 0 + } + + x.fre[x.outUsed] ^= src[i] + dst[i] = x.fre[x.outUsed] + x.outUsed++ + } +} + +type ocfbDecrypter struct { + b Block + fre []byte + outUsed int +} + +// NewOCFBDecrypter returns a Stream which decrypts data with OpenPGP's cipher +// feedback mode using the given Block. Prefix must be the first blockSize + 2 +// bytes of the ciphertext, where blockSize is the Block's block size. If an +// incorrect key is detected then nil is returned. +func NewOCFBDecrypter(block Block, prefix []byte) Stream { + blockSize := block.BlockSize() + if len(prefix) != blockSize+2 { + return nil + } + + x := &ocfbDecrypter{ + b: block, + fre: make([]byte, blockSize), + outUsed: 0, + } + prefixCopy := make([]byte, len(prefix)) + copy(prefixCopy, prefix) + + block.Encrypt(x.fre, x.fre) + for i := 0; i < blockSize; i++ { + prefixCopy[i] ^= x.fre[i] + } + + block.Encrypt(x.fre, prefix[:blockSize]) + prefixCopy[blockSize] ^= x.fre[0] + prefixCopy[blockSize+1] ^= x.fre[1] + + if prefixCopy[blockSize-2] != prefixCopy[blockSize] || + prefixCopy[blockSize-1] != prefixCopy[blockSize+1] { + return nil + } + + block.Encrypt(x.fre, prefix[2:]) + return x +} + +func (x *ocfbDecrypter) XORKeyStream(dst, src []byte) { + for i := 0; i < len(src); i++ { + if x.outUsed == len(x.fre) { + x.b.Encrypt(x.fre, x.fre) + x.outUsed = 0 + } + + c := src[i] + dst[i] = x.fre[x.outUsed] ^ src[i] + x.fre[x.outUsed] = c + x.outUsed++ + } +} diff --git a/src/pkg/crypto/cipher/ocfb_test.go b/src/pkg/crypto/cipher/ocfb_test.go new file mode 100644 index 000000000..289bb7c91 --- /dev/null +++ b/src/pkg/crypto/cipher/ocfb_test.go @@ -0,0 +1,39 @@ +// 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 cipher + +import ( + "bytes" + "crypto/aes" + "crypto/rand" + "testing" +) + +func TestOCFB(t *testing.T) { + block, err := aes.NewCipher(commonKey128) + if err != nil { + t.Error(err) + return + } + + plaintext := []byte("this is the plaintext") + randData := make([]byte, block.BlockSize()) + rand.Reader.Read(randData) + ocfb, prefix := NewOCFBEncrypter(block, randData) + ciphertext := make([]byte, len(plaintext)) + ocfb.XORKeyStream(ciphertext, plaintext) + + ocfbdec := NewOCFBDecrypter(block, prefix) + if ocfbdec == nil { + t.Error("NewOCFBDecrypter failed") + return + } + plaintextCopy := make([]byte, len(plaintext)) + ocfbdec.XORKeyStream(plaintextCopy, ciphertext) + + if !bytes.Equal(plaintextCopy, plaintext) { + t.Errorf("got: %x, want: %x", plaintextCopy, plaintext) + } +} diff --git a/src/pkg/crypto/elliptic/Makefile b/src/pkg/crypto/elliptic/Makefile new file mode 100644 index 000000000..4db5d7de5 --- /dev/null +++ b/src/pkg/crypto/elliptic/Makefile @@ -0,0 +1,11 @@ +# Copyright 2010 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../../Make.inc + +TARG=crypto/elliptic +GOFILES=\ + elliptic.go\ + +include ../../../Make.pkg diff --git a/src/pkg/crypto/elliptic/elliptic.go b/src/pkg/crypto/elliptic/elliptic.go new file mode 100644 index 000000000..beac45ca0 --- /dev/null +++ b/src/pkg/crypto/elliptic/elliptic.go @@ -0,0 +1,376 @@ +// 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 elliptic package implements several standard elliptic curves over prime +// fields +package elliptic + +// This package operates, internally, on Jacobian coordinates. For a given +// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, z1) +// where x = x1/z1² and y = y1/z1³. The greatest speedups come when the whole +// calculation can be performed within the transform (as in ScalarMult and +// ScalarBaseMult). But even for Add and Double, it's faster to apply and +// reverse the transform than to operate in affine coordinates. + +import ( + "big" + "io" + "os" + "sync" +) + +// A Curve represents a short-form Weierstrass curve with a=-3. +// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html +type Curve struct { + P *big.Int // the order of the underlying field + B *big.Int // the constant of the curve equation + Gx, Gy *big.Int // (x,y) of the base point + BitSize int // the size of the underlying field +} + +// IsOnCurve returns true if the given (x,y) lies on the curve. +func (curve *Curve) IsOnCurve(x, y *big.Int) bool { + // y² = x³ - 3x + b + y2 := new(big.Int).Mul(y, y) + y2.Mod(y2, curve.P) + + x3 := new(big.Int).Mul(x, x) + x3.Mul(x3, x) + + threeX := new(big.Int).Lsh(x, 1) + threeX.Add(threeX, x) + + x3.Sub(x3, threeX) + x3.Add(x3, curve.B) + x3.Mod(x3, curve.P) + + return x3.Cmp(y2) == 0 +} + +// affineFromJacobian reverses the Jacobian transform. See the comment at the +// top of the file. +func (curve *Curve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { + zinv := new(big.Int).ModInverse(z, curve.P) + zinvsq := new(big.Int).Mul(zinv, zinv) + + xOut = new(big.Int).Mul(x, zinvsq) + xOut.Mod(xOut, curve.P) + zinvsq.Mul(zinvsq, zinv) + yOut = new(big.Int).Mul(y, zinvsq) + yOut.Mod(yOut, curve.P) + return +} + +// Add returns the sum of (x1,y1) and (x2,y2) +func (curve *Curve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { + z := new(big.Int).SetInt64(1) + return curve.affineFromJacobian(curve.addJacobian(x1, y1, z, x2, y2, z)) +} + +// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and +// (x2, y2, z2) and returns their sum, also in Jacobian form. +func (curve *Curve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl + z1z1 := new(big.Int).Mul(z1, z1) + z1z1.Mod(z1z1, curve.P) + z2z2 := new(big.Int).Mul(z2, z2) + z2z2.Mod(z2z2, curve.P) + + u1 := new(big.Int).Mul(x1, z2z2) + u1.Mod(u1, curve.P) + u2 := new(big.Int).Mul(x2, z1z1) + u2.Mod(u2, curve.P) + h := new(big.Int).Sub(u2, u1) + if h.Sign() == -1 { + h.Add(h, curve.P) + } + i := new(big.Int).Lsh(h, 1) + i.Mul(i, i) + j := new(big.Int).Mul(h, i) + + s1 := new(big.Int).Mul(y1, z2) + s1.Mul(s1, z2z2) + s1.Mod(s1, curve.P) + s2 := new(big.Int).Mul(y2, z1) + s2.Mul(s2, z1z1) + s2.Mod(s2, curve.P) + r := new(big.Int).Sub(s2, s1) + if r.Sign() == -1 { + r.Add(r, curve.P) + } + r.Lsh(r, 1) + v := new(big.Int).Mul(u1, i) + + x3 := new(big.Int).Set(r) + x3.Mul(x3, x3) + x3.Sub(x3, j) + x3.Sub(x3, v) + x3.Sub(x3, v) + x3.Mod(x3, curve.P) + + y3 := new(big.Int).Set(r) + v.Sub(v, x3) + y3.Mul(y3, v) + s1.Mul(s1, j) + s1.Lsh(s1, 1) + y3.Sub(y3, s1) + y3.Mod(y3, curve.P) + + z3 := new(big.Int).Add(z1, z2) + z3.Mul(z3, z3) + z3.Sub(z3, z1z1) + if z3.Sign() == -1 { + z3.Add(z3, curve.P) + } + z3.Sub(z3, z2z2) + if z3.Sign() == -1 { + z3.Add(z3, curve.P) + } + z3.Mul(z3, h) + z3.Mod(z3, curve.P) + + return x3, y3, z3 +} + +// Double returns 2*(x,y) +func (curve *Curve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { + z1 := new(big.Int).SetInt64(1) + return curve.affineFromJacobian(curve.doubleJacobian(x1, y1, z1)) +} + +// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and +// returns its double, also in Jacobian form. +func (curve *Curve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b + delta := new(big.Int).Mul(z, z) + delta.Mod(delta, curve.P) + gamma := new(big.Int).Mul(y, y) + gamma.Mod(gamma, curve.P) + alpha := new(big.Int).Sub(x, delta) + if alpha.Sign() == -1 { + alpha.Add(alpha, curve.P) + } + alpha2 := new(big.Int).Add(x, delta) + alpha.Mul(alpha, alpha2) + alpha2.Set(alpha) + alpha.Lsh(alpha, 1) + alpha.Add(alpha, alpha2) + + beta := alpha2.Mul(x, gamma) + + x3 := new(big.Int).Mul(alpha, alpha) + beta8 := new(big.Int).Lsh(beta, 3) + x3.Sub(x3, beta8) + for x3.Sign() == -1 { + x3.Add(x3, curve.P) + } + x3.Mod(x3, curve.P) + + z3 := new(big.Int).Add(y, z) + z3.Mul(z3, z3) + z3.Sub(z3, gamma) + if z3.Sign() == -1 { + z3.Add(z3, curve.P) + } + z3.Sub(z3, delta) + if z3.Sign() == -1 { + z3.Add(z3, curve.P) + } + z3.Mod(z3, curve.P) + + beta.Lsh(beta, 2) + beta.Sub(beta, x3) + if beta.Sign() == -1 { + beta.Add(beta, curve.P) + } + y3 := alpha.Mul(alpha, beta) + + gamma.Mul(gamma, gamma) + gamma.Lsh(gamma, 3) + gamma.Mod(gamma, curve.P) + + y3.Sub(y3, gamma) + if y3.Sign() == -1 { + y3.Add(y3, curve.P) + } + y3.Mod(y3, curve.P) + + return x3, y3, z3 +} + +// ScalarMult returns k*(Bx,By) where k is a number in big-endian form. +func (curve *Curve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) { + // We have a slight problem in that the identity of the group (the + // point at infinity) cannot be represented in (x, y) form on a finite + // machine. Thus the standard add/double algorithm has to be tweaked + // slightly: our initial state is not the identity, but x, and we + // ignore the first true bit in |k|. If we don't find any true bits in + // |k|, then we return nil, nil, because we cannot return the identity + // element. + + Bz := new(big.Int).SetInt64(1) + x := Bx + y := By + z := Bz + + seenFirstTrue := false + for _, byte := range k { + for bitNum := 0; bitNum < 8; bitNum++ { + if seenFirstTrue { + x, y, z = curve.doubleJacobian(x, y, z) + } + if byte&0x80 == 0x80 { + if !seenFirstTrue { + seenFirstTrue = true + } else { + x, y, z = curve.addJacobian(Bx, By, Bz, x, y, z) + } + } + byte <<= 1 + } + } + + if !seenFirstTrue { + return nil, nil + } + + return curve.affineFromJacobian(x, y, z) +} + +// ScalarBaseMult returns k*G, where G is the base point of the group and k is +// an integer in big-endian form. +func (curve *Curve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { + return curve.ScalarMult(curve.Gx, curve.Gy, k) +} + +var mask = []byte{0xff, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f} + +// GenerateKey returns a public/private key pair. The private key is generated +// using the given reader, which must return random data. +func (curve *Curve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err os.Error) { + byteLen := (curve.BitSize + 7) >> 3 + priv = make([]byte, byteLen) + + for x == nil { + _, err = io.ReadFull(rand, priv) + if err != nil { + return + } + // We have to mask off any excess bits in the case that the size of the + // underlying field is not a whole number of bytes. + priv[0] &= mask[curve.BitSize%8] + // This is because, in tests, rand will return all zeros and we don't + // want to get the point at infinity and loop forever. + priv[1] ^= 0x42 + x, y = curve.ScalarBaseMult(priv) + } + return +} + +// Marshal converts a point into the form specified in section 4.3.6 of ANSI +// X9.62. +func (curve *Curve) Marshal(x, y *big.Int) []byte { + byteLen := (curve.BitSize + 7) >> 3 + + ret := make([]byte, 1+2*byteLen) + ret[0] = 4 // uncompressed point + + xBytes := x.Bytes() + copy(ret[1+byteLen-len(xBytes):], xBytes) + yBytes := y.Bytes() + copy(ret[1+2*byteLen-len(yBytes):], yBytes) + return ret +} + +// Unmarshal converts a point, serialised by Marshal, into an x, y pair. On +// error, x = nil. +func (curve *Curve) Unmarshal(data []byte) (x, y *big.Int) { + byteLen := (curve.BitSize + 7) >> 3 + if len(data) != 1+2*byteLen { + return + } + if data[0] != 4 { // uncompressed form + return + } + x = new(big.Int).SetBytes(data[1 : 1+byteLen]) + y = new(big.Int).SetBytes(data[1+byteLen:]) + return +} + +var initonce sync.Once +var p224 *Curve +var p256 *Curve +var p384 *Curve +var p521 *Curve + +func initAll() { + initP224() + initP256() + initP384() + initP521() +} + +func initP224() { + // See FIPS 186-3, section D.2.2 + p224 = new(Curve) + p224.P, _ = new(big.Int).SetString("26959946667150639794667015087019630673557916260026308143510066298881", 10) + p224.B, _ = new(big.Int).SetString("b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4", 16) + p224.Gx, _ = new(big.Int).SetString("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", 16) + p224.Gy, _ = new(big.Int).SetString("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34", 16) + p224.BitSize = 224 +} + +func initP256() { + // See FIPS 186-3, section D.2.3 + p256 = new(Curve) + p256.P, _ = new(big.Int).SetString("115792089210356248762697446949407573530086143415290314195533631308867097853951", 10) + p256.B, _ = new(big.Int).SetString("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16) + p256.Gx, _ = new(big.Int).SetString("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16) + p256.Gy, _ = new(big.Int).SetString("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16) + p256.BitSize = 256 +} + +func initP384() { + // See FIPS 186-3, section D.2.4 + p384 = new(Curve) + p384.P, _ = new(big.Int).SetString("39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319", 10) + p384.B, _ = new(big.Int).SetString("b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef", 16) + p384.Gx, _ = new(big.Int).SetString("aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7", 16) + p384.Gy, _ = new(big.Int).SetString("3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f", 16) + p384.BitSize = 384 +} + +func initP521() { + // See FIPS 186-3, section D.2.5 + p521 = new(Curve) + p521.P, _ = new(big.Int).SetString("6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151", 10) + p521.B, _ = new(big.Int).SetString("051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00", 16) + p521.Gx, _ = new(big.Int).SetString("c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", 16) + p521.Gy, _ = new(big.Int).SetString("11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650", 16) + p521.BitSize = 521 +} + +// P224 returns a Curve which implements P-224 (see FIPS 186-3, section D.2.2) +func P224() *Curve { + initonce.Do(initAll) + return p224 +} + +// P256 returns a Curve which implements P-256 (see FIPS 186-3, section D.2.3) +func P256() *Curve { + initonce.Do(initAll) + return p256 +} + +// P384 returns a Curve which implements P-384 (see FIPS 186-3, section D.2.4) +func P384() *Curve { + initonce.Do(initAll) + return p384 +} + +// P256 returns a Curve which implements P-521 (see FIPS 186-3, section D.2.5) +func P521() *Curve { + initonce.Do(initAll) + return p521 +} diff --git a/src/pkg/crypto/elliptic/elliptic_test.go b/src/pkg/crypto/elliptic/elliptic_test.go new file mode 100644 index 000000000..6ae6fb96d --- /dev/null +++ b/src/pkg/crypto/elliptic/elliptic_test.go @@ -0,0 +1,331 @@ +// 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 elliptic + +import ( + "big" + "crypto/rand" + "fmt" + "testing" +) + +func TestOnCurve(t *testing.T) { + p224 := P224() + if !p224.IsOnCurve(p224.Gx, p224.Gy) { + t.Errorf("FAIL") + } +} + +type baseMultTest struct { + k string + x, y string +} + +var p224BaseMultTests = []baseMultTest{ + { + "1", + "b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", + "bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34", + }, + { + "2", + "706a46dc76dcb76798e60e6d89474788d16dc18032d268fd1a704fa6", + "1c2b76a7bc25e7702a704fa986892849fca629487acf3709d2e4e8bb", + }, + { + "3", + "df1b1d66a551d0d31eff822558b9d2cc75c2180279fe0d08fd896d04", + "a3f7f03cadd0be444c0aa56830130ddf77d317344e1af3591981a925", + }, + { + "4", + "ae99feebb5d26945b54892092a8aee02912930fa41cd114e40447301", + "482580a0ec5bc47e88bc8c378632cd196cb3fa058a7114eb03054c9", + }, + { + "5", + "31c49ae75bce7807cdff22055d94ee9021fedbb5ab51c57526f011aa", + "27e8bff1745635ec5ba0c9f1c2ede15414c6507d29ffe37e790a079b", + }, + { + "6", + "1f2483f82572251fca975fea40db821df8ad82a3c002ee6c57112408", + "89faf0ccb750d99b553c574fad7ecfb0438586eb3952af5b4b153c7e", + }, + { + "7", + "db2f6be630e246a5cf7d99b85194b123d487e2d466b94b24a03c3e28", + "f3a30085497f2f611ee2517b163ef8c53b715d18bb4e4808d02b963", + }, + { + "8", + "858e6f9cc6c12c31f5df124aa77767b05c8bc021bd683d2b55571550", + "46dcd3ea5c43898c5c5fc4fdac7db39c2f02ebee4e3541d1e78047a", + }, + { + "9", + "2fdcccfee720a77ef6cb3bfbb447f9383117e3daa4a07e36ed15f78d", + "371732e4f41bf4f7883035e6a79fcedc0e196eb07b48171697517463", + }, + { + "10", + "aea9e17a306517eb89152aa7096d2c381ec813c51aa880e7bee2c0fd", + "39bb30eab337e0a521b6cba1abe4b2b3a3e524c14a3fe3eb116b655f", + }, + { + "11", + "ef53b6294aca431f0f3c22dc82eb9050324f1d88d377e716448e507c", + "20b510004092e96636cfb7e32efded8265c266dfb754fa6d6491a6da", + }, + { + "12", + "6e31ee1dc137f81b056752e4deab1443a481033e9b4c93a3044f4f7a", + "207dddf0385bfdeab6e9acda8da06b3bbef224a93ab1e9e036109d13", + }, + { + "13", + "34e8e17a430e43289793c383fac9774247b40e9ebd3366981fcfaeca", + "252819f71c7fb7fbcb159be337d37d3336d7feb963724fdfb0ecb767", + }, + { + "14", + "a53640c83dc208603ded83e4ecf758f24c357d7cf48088b2ce01e9fa", + "d5814cd724199c4a5b974a43685fbf5b8bac69459c9469bc8f23ccaf", + }, + { + "15", + "baa4d8635511a7d288aebeedd12ce529ff102c91f97f867e21916bf9", + "979a5f4759f80f4fb4ec2e34f5566d595680a11735e7b61046127989", + }, + { + "16", + "b6ec4fe1777382404ef679997ba8d1cc5cd8e85349259f590c4c66d", + "3399d464345906b11b00e363ef429221f2ec720d2f665d7dead5b482", + }, + { + "17", + "b8357c3a6ceef288310e17b8bfeff9200846ca8c1942497c484403bc", + "ff149efa6606a6bd20ef7d1b06bd92f6904639dce5174db6cc554a26", + }, + { + "18", + "c9ff61b040874c0568479216824a15eab1a838a797d189746226e4cc", + "ea98d60e5ffc9b8fcf999fab1df7e7ef7084f20ddb61bb045a6ce002", + }, + { + "19", + "a1e81c04f30ce201c7c9ace785ed44cc33b455a022f2acdbc6cae83c", + "dcf1f6c3db09c70acc25391d492fe25b4a180babd6cea356c04719cd", + }, + { + "20", + "fcc7f2b45df1cd5a3c0c0731ca47a8af75cfb0347e8354eefe782455", + "d5d7110274cba7cdee90e1a8b0d394c376a5573db6be0bf2747f530", + }, + { + "112233445566778899", + "61f077c6f62ed802dad7c2f38f5c67f2cc453601e61bd076bb46179e", + "2272f9e9f5933e70388ee652513443b5e289dd135dcc0d0299b225e4", + }, + { + "112233445566778899112233445566778899", + "29895f0af496bfc62b6ef8d8a65c88c613949b03668aab4f0429e35", + "3ea6e53f9a841f2019ec24bde1a75677aa9b5902e61081c01064de93", + }, + { + "6950511619965839450988900688150712778015737983940691968051900319680", + "ab689930bcae4a4aa5f5cb085e823e8ae30fd365eb1da4aba9cf0379", + "3345a121bbd233548af0d210654eb40bab788a03666419be6fbd34e7", + }, + { + "13479972933410060327035789020509431695094902435494295338570602119423", + "bdb6a8817c1f89da1c2f3dd8e97feb4494f2ed302a4ce2bc7f5f4025", + "4c7020d57c00411889462d77a5438bb4e97d177700bf7243a07f1680", + }, + { + "13479971751745682581351455311314208093898607229429740618390390702079", + "d58b61aa41c32dd5eba462647dba75c5d67c83606c0af2bd928446a9", + "d24ba6a837be0460dd107ae77725696d211446c5609b4595976b16bd", + }, + { + "13479972931865328106486971546324465392952975980343228160962702868479", + "dc9fa77978a005510980e929a1485f63716df695d7a0c18bb518df03", + "ede2b016f2ddffc2a8c015b134928275ce09e5661b7ab14ce0d1d403", + }, + { + "11795773708834916026404142434151065506931607341523388140225443265536", + "499d8b2829cfb879c901f7d85d357045edab55028824d0f05ba279ba", + "bf929537b06e4015919639d94f57838fa33fc3d952598dcdbb44d638", + }, + { + "784254593043826236572847595991346435467177662189391577090", + "8246c999137186632c5f9eddf3b1b0e1764c5e8bd0e0d8a554b9cb77", + "e80ed8660bc1cb17ac7d845be40a7a022d3306f116ae9f81fea65947", + }, + { + "13479767645505654746623887797783387853576174193480695826442858012671", + "6670c20afcceaea672c97f75e2e9dd5c8460e54bb38538ebb4bd30eb", + "f280d8008d07a4caf54271f993527d46ff3ff46fd1190a3f1faa4f74", + }, + { + "205688069665150753842126177372015544874550518966168735589597183", + "eca934247425cfd949b795cb5ce1eff401550386e28d1a4c5a8eb", + "d4c01040dba19628931bc8855370317c722cbd9ca6156985f1c2e9ce", + }, + { + "13479966930919337728895168462090683249159702977113823384618282123295", + "ef353bf5c73cd551b96d596fbc9a67f16d61dd9fe56af19de1fba9cd", + "21771b9cdce3e8430c09b3838be70b48c21e15bc09ee1f2d7945b91f", + }, + { + "50210731791415612487756441341851895584393717453129007497216", + "4036052a3091eb481046ad3289c95d3ac905ca0023de2c03ecd451cf", + "d768165a38a2b96f812586a9d59d4136035d9c853a5bf2e1c86a4993", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368041", + "fcc7f2b45df1cd5a3c0c0731ca47a8af75cfb0347e8354eefe782455", + "f2a28eefd8b345832116f1e574f2c6b2c895aa8c24941f40d8b80ad1", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368042", + "a1e81c04f30ce201c7c9ace785ed44cc33b455a022f2acdbc6cae83c", + "230e093c24f638f533dac6e2b6d01da3b5e7f45429315ca93fb8e634", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368043", + "c9ff61b040874c0568479216824a15eab1a838a797d189746226e4cc", + "156729f1a003647030666054e208180f8f7b0df2249e44fba5931fff", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368044", + "b8357c3a6ceef288310e17b8bfeff9200846ca8c1942497c484403bc", + "eb610599f95942df1082e4f9426d086fb9c6231ae8b24933aab5db", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368045", + "b6ec4fe1777382404ef679997ba8d1cc5cd8e85349259f590c4c66d", + "cc662b9bcba6f94ee4ff1c9c10bd6ddd0d138df2d099a282152a4b7f", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368046", + "baa4d8635511a7d288aebeedd12ce529ff102c91f97f867e21916bf9", + "6865a0b8a607f0b04b13d1cb0aa992a5a97f5ee8ca1849efb9ed8678", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368047", + "a53640c83dc208603ded83e4ecf758f24c357d7cf48088b2ce01e9fa", + "2a7eb328dbe663b5a468b5bc97a040a3745396ba636b964370dc3352", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368048", + "34e8e17a430e43289793c383fac9774247b40e9ebd3366981fcfaeca", + "dad7e608e380480434ea641cc82c82cbc92801469c8db0204f13489a", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368049", + "6e31ee1dc137f81b056752e4deab1443a481033e9b4c93a3044f4f7a", + "df82220fc7a4021549165325725f94c3410ddb56c54e161fc9ef62ee", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368050", + "ef53b6294aca431f0f3c22dc82eb9050324f1d88d377e716448e507c", + "df4aefffbf6d1699c930481cd102127c9a3d992048ab05929b6e5927", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368051", + "aea9e17a306517eb89152aa7096d2c381ec813c51aa880e7bee2c0fd", + "c644cf154cc81f5ade49345e541b4d4b5c1adb3eb5c01c14ee949aa2", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368052", + "2fdcccfee720a77ef6cb3bfbb447f9383117e3daa4a07e36ed15f78d", + "c8e8cd1b0be40b0877cfca1958603122f1e6914f84b7e8e968ae8b9e", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368053", + "858e6f9cc6c12c31f5df124aa77767b05c8bc021bd683d2b55571550", + "fb9232c15a3bc7673a3a03b0253824c53d0fd1411b1cabe2e187fb87", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368054", + "db2f6be630e246a5cf7d99b85194b123d487e2d466b94b24a03c3e28", + "f0c5cff7ab680d09ee11dae84e9c1072ac48ea2e744b1b7f72fd469e", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368055", + "1f2483f82572251fca975fea40db821df8ad82a3c002ee6c57112408", + "76050f3348af2664aac3a8b05281304ebc7a7914c6ad50a4b4eac383", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368056", + "31c49ae75bce7807cdff22055d94ee9021fedbb5ab51c57526f011aa", + "d817400e8ba9ca13a45f360e3d121eaaeb39af82d6001c8186f5f866", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368057", + "ae99feebb5d26945b54892092a8aee02912930fa41cd114e40447301", + "fb7da7f5f13a43b81774373c879cd32d6934c05fa758eeb14fcfab38", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368058", + "df1b1d66a551d0d31eff822558b9d2cc75c2180279fe0d08fd896d04", + "5c080fc3522f41bbb3f55a97cfecf21f882ce8cbb1e50ca6e67e56dc", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368059", + "706a46dc76dcb76798e60e6d89474788d16dc18032d268fd1a704fa6", + "e3d4895843da188fd58fb0567976d7b50359d6b78530c8f62d1b1746", + }, + { + "26959946667150639794667015087019625940457807714424391721682722368060", + "b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", + "42c89c774a08dc04b3dd201932bc8a5ea5f8b89bbb2a7e667aff81cd", + }, +} + +func TestBaseMult(t *testing.T) { + p224 := P224() + for i, e := range p224BaseMultTests { + k, ok := new(big.Int).SetString(e.k, 10) + if !ok { + t.Errorf("%d: bad value for k: %s", i, e.k) + } + x, y := p224.ScalarBaseMult(k.Bytes()) + if fmt.Sprintf("%x", x) != e.x || fmt.Sprintf("%x", y) != e.y { + t.Errorf("%d: bad output for k=%s: got (%x, %s), want (%s, %s)", i, e.k, x, y, e.x, e.y) + } + } +} + +func BenchmarkBaseMult(b *testing.B) { + b.ResetTimer() + p224 := P224() + e := p224BaseMultTests[25] + k, _ := new(big.Int).SetString(e.k, 10) + b.StartTimer() + for i := 0; i < b.N; i++ { + p224.ScalarBaseMult(k.Bytes()) + } +} + +func TestMarshal(t *testing.T) { + p224 := P224() + _, x, y, err := p224.GenerateKey(rand.Reader) + if err != nil { + t.Error(err) + return + } + serialised := p224.Marshal(x, y) + xx, yy := p224.Unmarshal(serialised) + if xx == nil { + t.Error("failed to unmarshal") + return + } + if xx.Cmp(x) != 0 || yy.Cmp(y) != 0 { + t.Error("unmarshal returned different values") + return + } +} diff --git a/src/pkg/crypto/hmac/Makefile b/src/pkg/crypto/hmac/Makefile index d1a6bfc2b..cc69abf60 100644 --- a/src/pkg/crypto/hmac/Makefile +++ b/src/pkg/crypto/hmac/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=crypto/hmac GOFILES=\ diff --git a/src/pkg/crypto/hmac/hmac.go b/src/pkg/crypto/hmac/hmac.go index 38d13738d..298fb2c06 100644 --- a/src/pkg/crypto/hmac/hmac.go +++ b/src/pkg/crypto/hmac/hmac.go @@ -11,6 +11,7 @@ package hmac import ( "crypto/md5" "crypto/sha1" + "crypto/sha256" "hash" "os" ) @@ -34,10 +35,9 @@ const ( ) type hmac struct { - size int - key []byte - tmp []byte - inner hash.Hash + size int + key, tmp []byte + outer, inner hash.Hash } func (h *hmac) tmpPad(xor byte) { @@ -50,14 +50,14 @@ func (h *hmac) tmpPad(xor byte) { } func (h *hmac) Sum() []byte { - h.tmpPad(0x5c) sum := h.inner.Sum() + h.tmpPad(0x5c) for i, b := range sum { h.tmp[padSize+i] = b } - h.inner.Reset() - h.inner.Write(h.tmp) - return h.inner.Sum() + h.outer.Reset() + h.outer.Write(h.tmp) + return h.outer.Sum() } func (h *hmac) Write(p []byte) (n int, err os.Error) { @@ -72,27 +72,29 @@ func (h *hmac) Reset() { h.inner.Write(h.tmp[0:padSize]) } -// New returns a new HMAC hash using the given hash and key. -func New(h hash.Hash, key []byte) hash.Hash { +// New returns a new HMAC hash using the given hash generator and key. +func New(h func() hash.Hash, key []byte) hash.Hash { + hm := new(hmac) + hm.outer = h() + hm.inner = h() + hm.size = hm.inner.Size() + hm.tmp = make([]byte, padSize+hm.size) if len(key) > padSize { // If key is too big, hash it. - h.Write(key) - key = h.Sum() + hm.outer.Write(key) + key = hm.outer.Sum() } - hm := new(hmac) - hm.inner = h - hm.size = h.Size() hm.key = make([]byte, len(key)) - for i, k := range key { - hm.key[i] = k - } - hm.tmp = make([]byte, padSize+hm.size) + copy(hm.key, key) hm.Reset() return hm } // NewMD5 returns a new HMAC-MD5 hash using the given key. -func NewMD5(key []byte) hash.Hash { return New(md5.New(), key) } +func NewMD5(key []byte) hash.Hash { return New(md5.New, key) } // NewSHA1 returns a new HMAC-SHA1 hash using the given key. -func NewSHA1(key []byte) hash.Hash { return New(sha1.New(), key) } +func NewSHA1(key []byte) hash.Hash { return New(sha1.New, key) } + +// NewSHA256 returns a new HMAC-SHA256 hash using the given key. +func NewSHA256(key []byte) hash.Hash { return New(sha256.New, key) } diff --git a/src/pkg/crypto/hmac/hmac_test.go b/src/pkg/crypto/hmac/hmac_test.go index d867c83a9..40adbad04 100644 --- a/src/pkg/crypto/hmac/hmac_test.go +++ b/src/pkg/crypto/hmac/hmac_test.go @@ -17,10 +17,10 @@ type hmacTest struct { out string } -// Tests from US FIPS 198 -// http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf var hmacTests = []hmacTest{ - hmacTest{ + // Tests from US FIPS 198 + // http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf + { NewSHA1, []byte{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -35,7 +35,7 @@ var hmacTests = []hmacTest{ []byte("Sample #1"), "4f4ca3d5d68ba7cc0a1208c9c61e9c5da0403c0a", }, - hmacTest{ + { NewSHA1, []byte{ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, @@ -45,7 +45,7 @@ var hmacTests = []hmacTest{ []byte("Sample #2"), "0922d3405faa3d194f82a45830737d5cc6c75d24", }, - hmacTest{ + { NewSHA1, []byte{ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, @@ -67,12 +67,117 @@ var hmacTests = []hmacTest{ }, // Test from Plan 9. - hmacTest{ + { NewMD5, []byte("Jefe"), []byte("what do ya want for nothing?"), "750c783e6ab0b503eaa86e310a5db738", }, + + // Tests from RFC 4231 + { + NewSHA256, + []byte{ + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, + }, + []byte("Hi There"), + "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7", + }, + { + NewSHA256, + []byte("Jefe"), + []byte("what do ya want for nothing?"), + "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843", + }, + { + NewSHA256, + []byte{ + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, + }, + []byte{ + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, + }, + "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe", + }, + { + NewSHA256, + []byte{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, + }, + []byte{ + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, + }, + "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b", + }, + { + NewSHA256, + []byte{ + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, + }, + []byte("Test Using Larger Than Block-Size Key - Hash Key First"), + "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54", + }, + { + NewSHA256, + []byte{ + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, + }, + []byte("This is a test using a larger than block-size key " + + "and a larger than block-size data. The key needs to " + + "be hashed before being used by the HMAC algorithm."), + "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2", + }, } func TestHMAC(t *testing.T) { @@ -84,9 +189,13 @@ func TestHMAC(t *testing.T) { t.Errorf("test %d.%d: Write(%d) = %d, %v", i, j, len(tt.in), n, err) continue } - sum := fmt.Sprintf("%x", h.Sum()) - if sum != tt.out { - t.Errorf("test %d.%d: have %s want %s\n", i, j, sum, tt.out) + + // Repetive Sum() calls should return the same value + for k := 0; k < 2; k++ { + sum := fmt.Sprintf("%x", h.Sum()) + if sum != tt.out { + t.Errorf("test %d.%d.%d: have %s want %s\n", i, j, k, sum, tt.out) + } } // Second iteration: make sure reset works. diff --git a/src/pkg/crypto/md4/Makefile b/src/pkg/crypto/md4/Makefile index 5fff2dd8f..eef05ab70 100644 --- a/src/pkg/crypto/md4/Makefile +++ b/src/pkg/crypto/md4/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=crypto/md4 GOFILES=\ diff --git a/src/pkg/crypto/md4/md4.go b/src/pkg/crypto/md4/md4.go index adbdf29e7..e13c986e6 100644 --- a/src/pkg/crypto/md4/md4.go +++ b/src/pkg/crypto/md4/md4.go @@ -68,10 +68,7 @@ func (d *digest) Write(p []byte) (nn int, err os.Error) { n := _Block(d, p) p = p[n:] if len(p) > 0 { - for i, x := range p { - d.x[i] = x - } - d.nx = len(p) + d.nx = copy(d.x[:], p) } return } diff --git a/src/pkg/crypto/md4/md4_test.go b/src/pkg/crypto/md4/md4_test.go index b883e6459..721bd4cbc 100644 --- a/src/pkg/crypto/md4/md4_test.go +++ b/src/pkg/crypto/md4/md4_test.go @@ -16,37 +16,37 @@ type md4Test struct { } var golden = []md4Test{ - md4Test{"31d6cfe0d16ae931b73c59d7e0c089c0", ""}, - md4Test{"bde52cb31de33e46245e05fbdbd6fb24", "a"}, - md4Test{"ec388dd78999dfc7cf4632465693b6bf", "ab"}, - md4Test{"a448017aaf21d8525fc10ae87aa6729d", "abc"}, - md4Test{"41decd8f579255c5200f86a4bb3ba740", "abcd"}, - md4Test{"9803f4a34e8eb14f96adba49064a0c41", "abcde"}, - md4Test{"804e7f1c2586e50b49ac65db5b645131", "abcdef"}, - md4Test{"752f4adfe53d1da0241b5bc216d098fc", "abcdefg"}, - md4Test{"ad9daf8d49d81988590a6f0e745d15dd", "abcdefgh"}, - md4Test{"1e4e28b05464316b56402b3815ed2dfd", "abcdefghi"}, - md4Test{"dc959c6f5d6f9e04e4380777cc964b3d", "abcdefghij"}, - md4Test{"1b5701e265778898ef7de5623bbe7cc0", "Discard medicine more than two years old."}, - md4Test{"d7f087e090fe7ad4a01cb59dacc9a572", "He who has a shady past knows that nice guys finish last."}, - md4Test{"a6f8fd6df617c72837592fc3570595c9", "I wouldn't marry him with a ten foot pole."}, - md4Test{"c92a84a9526da8abc240c05d6b1a1ce0", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, - md4Test{"f6013160c4dcb00847069fee3bb09803", "The days of the digital watch are numbered. -Tom Stoppard"}, - md4Test{"2c3bb64f50b9107ed57640fe94bec09f", "Nepal premier won't resign."}, - md4Test{"45b7d8a32c7806f2f7f897332774d6e4", "For every action there is an equal and opposite government program."}, - md4Test{"b5b4f9026b175c62d7654bdc3a1cd438", "His money is twice tainted: 'taint yours and 'taint mine."}, - md4Test{"caf44e80f2c20ce19b5ba1cab766e7bd", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, - md4Test{"191fae6707f496aa54a6bce9f2ecf74d", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, - md4Test{"9ddc753e7a4ccee6081cd1b45b23a834", "size: a.out: bad magic"}, - md4Test{"8d050f55b1cadb9323474564be08a521", "The major problem is with sendmail. -Mark Horton"}, - md4Test{"ad6e2587f74c3e3cc19146f6127fa2e3", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, - md4Test{"1d616d60a5fabe85589c3f1566ca7fca", "If the enemy is within range, then so are you."}, - md4Test{"aec3326a4f496a2ced65a1963f84577f", "It's well we cannot hear the screams/That we create in others' dreams."}, - md4Test{"77b4fd762d6b9245e61c50bf6ebf118b", "You remind me of a TV show, but that's all right: I watch it anyway."}, - md4Test{"e8f48c726bae5e516f6ddb1a4fe62438", "C is as portable as Stonehedge!!"}, - md4Test{"a3a84366e7219e887423b01f9be7166e", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, - md4Test{"a6b7aa35157e984ef5d9b7f32e5fbb52", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, - md4Test{"75661f0545955f8f9abeeb17845f3fd6", "How can you write a big system without C++? -Paul Glick"}, + {"31d6cfe0d16ae931b73c59d7e0c089c0", ""}, + {"bde52cb31de33e46245e05fbdbd6fb24", "a"}, + {"ec388dd78999dfc7cf4632465693b6bf", "ab"}, + {"a448017aaf21d8525fc10ae87aa6729d", "abc"}, + {"41decd8f579255c5200f86a4bb3ba740", "abcd"}, + {"9803f4a34e8eb14f96adba49064a0c41", "abcde"}, + {"804e7f1c2586e50b49ac65db5b645131", "abcdef"}, + {"752f4adfe53d1da0241b5bc216d098fc", "abcdefg"}, + {"ad9daf8d49d81988590a6f0e745d15dd", "abcdefgh"}, + {"1e4e28b05464316b56402b3815ed2dfd", "abcdefghi"}, + {"dc959c6f5d6f9e04e4380777cc964b3d", "abcdefghij"}, + {"1b5701e265778898ef7de5623bbe7cc0", "Discard medicine more than two years old."}, + {"d7f087e090fe7ad4a01cb59dacc9a572", "He who has a shady past knows that nice guys finish last."}, + {"a6f8fd6df617c72837592fc3570595c9", "I wouldn't marry him with a ten foot pole."}, + {"c92a84a9526da8abc240c05d6b1a1ce0", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, + {"f6013160c4dcb00847069fee3bb09803", "The days of the digital watch are numbered. -Tom Stoppard"}, + {"2c3bb64f50b9107ed57640fe94bec09f", "Nepal premier won't resign."}, + {"45b7d8a32c7806f2f7f897332774d6e4", "For every action there is an equal and opposite government program."}, + {"b5b4f9026b175c62d7654bdc3a1cd438", "His money is twice tainted: 'taint yours and 'taint mine."}, + {"caf44e80f2c20ce19b5ba1cab766e7bd", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, + {"191fae6707f496aa54a6bce9f2ecf74d", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, + {"9ddc753e7a4ccee6081cd1b45b23a834", "size: a.out: bad magic"}, + {"8d050f55b1cadb9323474564be08a521", "The major problem is with sendmail. -Mark Horton"}, + {"ad6e2587f74c3e3cc19146f6127fa2e3", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, + {"1d616d60a5fabe85589c3f1566ca7fca", "If the enemy is within range, then so are you."}, + {"aec3326a4f496a2ced65a1963f84577f", "It's well we cannot hear the screams/That we create in others' dreams."}, + {"77b4fd762d6b9245e61c50bf6ebf118b", "You remind me of a TV show, but that's all right: I watch it anyway."}, + {"e8f48c726bae5e516f6ddb1a4fe62438", "C is as portable as Stonehedge!!"}, + {"a3a84366e7219e887423b01f9be7166e", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, + {"a6b7aa35157e984ef5d9b7f32e5fbb52", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, + {"75661f0545955f8f9abeeb17845f3fd6", "How can you write a big system without C++? -Paul Glick"}, } func TestGolden(t *testing.T) { diff --git a/src/pkg/crypto/md5/Makefile b/src/pkg/crypto/md5/Makefile index 7f37f6a33..5cde3e6d6 100644 --- a/src/pkg/crypto/md5/Makefile +++ b/src/pkg/crypto/md5/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=crypto/md5 GOFILES=\ diff --git a/src/pkg/crypto/md5/md5.go b/src/pkg/crypto/md5/md5.go index a83337651..54fddb63b 100644 --- a/src/pkg/crypto/md5/md5.go +++ b/src/pkg/crypto/md5/md5.go @@ -68,10 +68,7 @@ func (d *digest) Write(p []byte) (nn int, err os.Error) { n := _Block(d, p) p = p[n:] if len(p) > 0 { - for i, x := range p { - d.x[i] = x - } - d.nx = len(p) + d.nx = copy(d.x[:], p) } return } diff --git a/src/pkg/crypto/md5/md5_test.go b/src/pkg/crypto/md5/md5_test.go index f6c293837..857002b70 100644 --- a/src/pkg/crypto/md5/md5_test.go +++ b/src/pkg/crypto/md5/md5_test.go @@ -16,37 +16,37 @@ type md5Test struct { } var golden = []md5Test{ - md5Test{"d41d8cd98f00b204e9800998ecf8427e", ""}, - md5Test{"0cc175b9c0f1b6a831c399e269772661", "a"}, - md5Test{"187ef4436122d1cc2f40dc2b92f0eba0", "ab"}, - md5Test{"900150983cd24fb0d6963f7d28e17f72", "abc"}, - md5Test{"e2fc714c4727ee9395f324cd2e7f331f", "abcd"}, - md5Test{"ab56b4d92b40713acc5af89985d4b786", "abcde"}, - md5Test{"e80b5017098950fc58aad83c8c14978e", "abcdef"}, - md5Test{"7ac66c0f148de9519b8bd264312c4d64", "abcdefg"}, - md5Test{"e8dc4081b13434b45189a720b77b6818", "abcdefgh"}, - md5Test{"8aa99b1f439ff71293e95357bac6fd94", "abcdefghi"}, - md5Test{"a925576942e94b2ef57a066101b48876", "abcdefghij"}, - md5Test{"d747fc1719c7eacb84058196cfe56d57", "Discard medicine more than two years old."}, - md5Test{"bff2dcb37ef3a44ba43ab144768ca837", "He who has a shady past knows that nice guys finish last."}, - md5Test{"0441015ecb54a7342d017ed1bcfdbea5", "I wouldn't marry him with a ten foot pole."}, - md5Test{"9e3cac8e9e9757a60c3ea391130d3689", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, - md5Test{"a0f04459b031f916a59a35cc482dc039", "The days of the digital watch are numbered. -Tom Stoppard"}, - md5Test{"e7a48e0fe884faf31475d2a04b1362cc", "Nepal premier won't resign."}, - md5Test{"637d2fe925c07c113800509964fb0e06", "For every action there is an equal and opposite government program."}, - md5Test{"834a8d18d5c6562119cf4c7f5086cb71", "His money is twice tainted: 'taint yours and 'taint mine."}, - md5Test{"de3a4d2fd6c73ec2db2abad23b444281", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, - md5Test{"acf203f997e2cf74ea3aff86985aefaf", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, - md5Test{"e1c1384cb4d2221dfdd7c795a4222c9a", "size: a.out: bad magic"}, - md5Test{"c90f3ddecc54f34228c063d7525bf644", "The major problem is with sendmail. -Mark Horton"}, - md5Test{"cdf7ab6c1fd49bd9933c43f3ea5af185", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, - md5Test{"83bc85234942fc883c063cbd7f0ad5d0", "If the enemy is within range, then so are you."}, - md5Test{"277cbe255686b48dd7e8f389394d9299", "It's well we cannot hear the screams/That we create in others' dreams."}, - md5Test{"fd3fb0a7ffb8af16603f3d3af98f8e1f", "You remind me of a TV show, but that's all right: I watch it anyway."}, - md5Test{"469b13a78ebf297ecda64d4723655154", "C is as portable as Stonehedge!!"}, - md5Test{"63eb3a2f466410104731c4b037600110", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, - md5Test{"72c2ed7592debca1c90fc0100f931a2f", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, - md5Test{"132f7619d33b523b1d9e5bd8e0928355", "How can you write a big system without C++? -Paul Glick"}, + {"d41d8cd98f00b204e9800998ecf8427e", ""}, + {"0cc175b9c0f1b6a831c399e269772661", "a"}, + {"187ef4436122d1cc2f40dc2b92f0eba0", "ab"}, + {"900150983cd24fb0d6963f7d28e17f72", "abc"}, + {"e2fc714c4727ee9395f324cd2e7f331f", "abcd"}, + {"ab56b4d92b40713acc5af89985d4b786", "abcde"}, + {"e80b5017098950fc58aad83c8c14978e", "abcdef"}, + {"7ac66c0f148de9519b8bd264312c4d64", "abcdefg"}, + {"e8dc4081b13434b45189a720b77b6818", "abcdefgh"}, + {"8aa99b1f439ff71293e95357bac6fd94", "abcdefghi"}, + {"a925576942e94b2ef57a066101b48876", "abcdefghij"}, + {"d747fc1719c7eacb84058196cfe56d57", "Discard medicine more than two years old."}, + {"bff2dcb37ef3a44ba43ab144768ca837", "He who has a shady past knows that nice guys finish last."}, + {"0441015ecb54a7342d017ed1bcfdbea5", "I wouldn't marry him with a ten foot pole."}, + {"9e3cac8e9e9757a60c3ea391130d3689", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, + {"a0f04459b031f916a59a35cc482dc039", "The days of the digital watch are numbered. -Tom Stoppard"}, + {"e7a48e0fe884faf31475d2a04b1362cc", "Nepal premier won't resign."}, + {"637d2fe925c07c113800509964fb0e06", "For every action there is an equal and opposite government program."}, + {"834a8d18d5c6562119cf4c7f5086cb71", "His money is twice tainted: 'taint yours and 'taint mine."}, + {"de3a4d2fd6c73ec2db2abad23b444281", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, + {"acf203f997e2cf74ea3aff86985aefaf", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, + {"e1c1384cb4d2221dfdd7c795a4222c9a", "size: a.out: bad magic"}, + {"c90f3ddecc54f34228c063d7525bf644", "The major problem is with sendmail. -Mark Horton"}, + {"cdf7ab6c1fd49bd9933c43f3ea5af185", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, + {"83bc85234942fc883c063cbd7f0ad5d0", "If the enemy is within range, then so are you."}, + {"277cbe255686b48dd7e8f389394d9299", "It's well we cannot hear the screams/That we create in others' dreams."}, + {"fd3fb0a7ffb8af16603f3d3af98f8e1f", "You remind me of a TV show, but that's all right: I watch it anyway."}, + {"469b13a78ebf297ecda64d4723655154", "C is as portable as Stonehedge!!"}, + {"63eb3a2f466410104731c4b037600110", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, + {"72c2ed7592debca1c90fc0100f931a2f", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, + {"132f7619d33b523b1d9e5bd8e0928355", "How can you write a big system without C++? -Paul Glick"}, } func TestGolden(t *testing.T) { diff --git a/src/pkg/crypto/ocsp/Makefile b/src/pkg/crypto/ocsp/Makefile new file mode 100644 index 000000000..6e132ff9b --- /dev/null +++ b/src/pkg/crypto/ocsp/Makefile @@ -0,0 +1,11 @@ +# 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=crypto/ocsp +GOFILES=\ + ocsp.go\ + +include ../../../Make.pkg diff --git a/src/pkg/crypto/ocsp/ocsp.go b/src/pkg/crypto/ocsp/ocsp.go new file mode 100644 index 000000000..f3fa3bc83 --- /dev/null +++ b/src/pkg/crypto/ocsp/ocsp.go @@ -0,0 +1,203 @@ +// 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 package parses OCSP responses as specified in RFC 2560. OCSP responses +// are signed messages attesting to the validity of a certificate for a small +// period of time. This is used to manage revocation for X.509 certificates. +package ocsp + +import ( + "asn1" + "crypto/rsa" + "crypto/sha1" + "crypto/x509" + "os" + "time" +) + +var idPKIXOCSPBasic = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 7, 48, 1, 1}) +var idSHA1WithRSA = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 1, 5}) + +// These are internal structures that reflect the ASN.1 structure of an OCSP +// response. See RFC 2560, section 4.2. + +const ( + ocspSuccess = 0 + ocspMalformed = 1 + ocspInternalError = 2 + ocspTryLater = 3 + ocspSigRequired = 4 + ocspUnauthorized = 5 +) + +type rdnSequence []relativeDistinguishedNameSET + +type relativeDistinguishedNameSET []attributeTypeAndValue + +type attributeTypeAndValue struct { + Type asn1.ObjectIdentifier + Value interface{} +} + +type algorithmIdentifier struct { + Algorithm asn1.ObjectIdentifier +} + +type certID struct { + HashAlgorithm algorithmIdentifier + NameHash []byte + IssuerKeyHash []byte + SerialNumber asn1.RawValue +} + +type responseASN1 struct { + Status asn1.Enumerated + Response responseBytes "explicit,tag:0" +} + +type responseBytes struct { + ResponseType asn1.ObjectIdentifier + Response []byte +} + +type basicResponse struct { + TBSResponseData responseData + SignatureAlgorithm algorithmIdentifier + Signature asn1.BitString + Certificates []asn1.RawValue "explicit,tag:0,optional" +} + +type responseData struct { + Raw asn1.RawContent + Version int "optional,default:1,explicit,tag:0" + RequestorName rdnSequence "optional,explicit,tag:1" + KeyHash []byte "optional,explicit,tag:2" + ProducedAt *time.Time + Responses []singleResponse +} + +type singleResponse struct { + CertID certID + Good asn1.Flag "explicit,tag:0,optional" + Revoked revokedInfo "explicit,tag:1,optional" + Unknown asn1.Flag "explicit,tag:2,optional" + ThisUpdate *time.Time + NextUpdate *time.Time "explicit,tag:0,optional" +} + +type revokedInfo struct { + RevocationTime *time.Time + Reason int "explicit,tag:0,optional" +} + +// This is the exposed reflection of the internal OCSP structures. + +const ( + // Good means that the certificate is valid. + Good = iota + // Revoked means that the certificate has been deliberately revoked. + Revoked = iota + // Unknown means that the OCSP responder doesn't know about the certificate. + Unknown = iota + // ServerFailed means that the OCSP responder failed to process the request. + ServerFailed = iota +) + +// Response represents an OCSP response. See RFC 2560. +type Response struct { + // Status is one of {Good, Revoked, Unknown, ServerFailed} + Status int + SerialNumber []byte + ProducedAt, ThisUpdate, NextUpdate, RevokedAt *time.Time + RevocationReason int + Certificate *x509.Certificate +} + +// ParseError results from an invalid OCSP response. +type ParseError string + +func (p ParseError) String() string { + return string(p) +} + +// ParseResponse parses an OCSP response in DER form. It only supports +// responses for a single certificate and only those using RSA signatures. +// Non-RSA responses will result in an x509.UnsupportedAlgorithmError. +// Signature errors or parse failures will result in a ParseError. +func ParseResponse(bytes []byte) (*Response, os.Error) { + var resp responseASN1 + rest, err := asn1.Unmarshal(bytes, &resp) + if err != nil { + return nil, err + } + if len(rest) > 0 { + return nil, ParseError("trailing data in OCSP response") + } + + ret := new(Response) + if resp.Status != ocspSuccess { + ret.Status = ServerFailed + return ret, nil + } + + if !resp.Response.ResponseType.Equal(idPKIXOCSPBasic) { + return nil, ParseError("bad OCSP response type") + } + + var basicResp basicResponse + rest, err = asn1.Unmarshal(resp.Response.Response, &basicResp) + if err != nil { + return nil, err + } + + if len(basicResp.Certificates) != 1 { + return nil, ParseError("OCSP response contains bad number of certificates") + } + + if len(basicResp.TBSResponseData.Responses) != 1 { + return nil, ParseError("OCSP response contains bad number of responses") + } + + ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes) + if err != nil { + return nil, err + } + + if ret.Certificate.PublicKeyAlgorithm != x509.RSA || !basicResp.SignatureAlgorithm.Algorithm.Equal(idSHA1WithRSA) { + return nil, x509.UnsupportedAlgorithmError{} + } + + h := sha1.New() + hashType := rsa.HashSHA1 + + pub := ret.Certificate.PublicKey.(*rsa.PublicKey) + h.Write(basicResp.TBSResponseData.Raw) + digest := h.Sum() + signature := basicResp.Signature.RightAlign() + + if rsa.VerifyPKCS1v15(pub, hashType, digest, signature) != nil { + return nil, ParseError("bad OCSP signature") + } + + r := basicResp.TBSResponseData.Responses[0] + + ret.SerialNumber = r.CertID.SerialNumber.Bytes + + switch { + case bool(r.Good): + ret.Status = Good + case bool(r.Unknown): + ret.Status = Unknown + default: + ret.Status = Revoked + ret.RevokedAt = r.Revoked.RevocationTime + ret.RevocationReason = r.Revoked.Reason + } + + ret.ProducedAt = basicResp.TBSResponseData.ProducedAt + ret.ThisUpdate = r.ThisUpdate + ret.NextUpdate = r.NextUpdate + + return ret, nil +} diff --git a/src/pkg/crypto/ocsp/ocsp_test.go b/src/pkg/crypto/ocsp/ocsp_test.go new file mode 100644 index 000000000..f9889790f --- /dev/null +++ b/src/pkg/crypto/ocsp/ocsp_test.go @@ -0,0 +1,97 @@ +package ocsp + +import ( + "bytes" + "encoding/hex" + "reflect" + "testing" + "time" +) + +func TestOCSPDecode(t *testing.T) { + responseBytes, _ := hex.DecodeString(ocspResponseHex) + resp, err := ParseResponse(responseBytes) + if err != nil { + t.Error(err) + } + + expected := Response{Status: 0, SerialNumber: []byte{0x1, 0xd0, 0xfa}, RevocationReason: 0, ThisUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 15, Minute: 1, Second: 5, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}, NextUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 18, Minute: 35, Second: 17, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}} + + if !reflect.DeepEqual(resp.ThisUpdate, resp.ThisUpdate) { + t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, expected.ThisUpdate) + } + + if !reflect.DeepEqual(resp.NextUpdate, resp.NextUpdate) { + t.Errorf("resp.NextUpdate: got %d, want %d", resp.NextUpdate, expected.NextUpdate) + } + + if resp.Status != expected.Status { + t.Errorf("resp.Status: got %d, want %d", resp.Status, expected.Status) + } + + if !bytes.Equal(resp.SerialNumber, expected.SerialNumber) { + t.Errorf("resp.SerialNumber: got %x, want %x", resp.SerialNumber, expected.SerialNumber) + } + + if resp.RevocationReason != expected.RevocationReason { + t.Errorf("resp.RevocationReason: got %d, want %d", resp.RevocationReason, expected.RevocationReason) + } +} + +// This OCSP response was taken from Thawte's public OCSP responder. +// To recreate: +// $ openssl s_client -tls1 -showcerts -servername www.google.com -connect www.google.com:443 +// Copy and paste the first certificate into /tmp/cert.crt and the second into +// /tmp/intermediate.crt +// $ openssl ocsp -issuer /tmp/intermediate.crt -cert /tmp/cert.crt -url http://ocsp.thawte.com -resp_text -respout /tmp/ocsp.der +// Then hex encode the result: +// $ python -c 'print file("/tmp/ocsp.der", "r").read().encode("hex")' + +const ocspResponseHex = "308206bc0a0100a08206b5308206b106092b0601050507300101048206a23082069e3081" + + "c9a14e304c310b300906035504061302494c31163014060355040a130d5374617274436f" + + "6d204c74642e312530230603550403131c5374617274436f6d20436c6173732031204f43" + + "5350205369676e6572180f32303130303730373137333531375a30663064303c30090605" + + "2b0e03021a050004146568874f40750f016a3475625e1f5c93e5a26d580414eb4234d098" + + "b0ab9ff41b6b08f7cc642eef0e2c45020301d0fa8000180f323031303037303731353031" + + "30355aa011180f32303130303730373138333531375a300d06092a864886f70d01010505" + + "000382010100ab557ff070d1d7cebbb5f0ec91a15c3fed22eb2e1b8244f1b84545f013a4" + + "fb46214c5e3fbfbebb8a56acc2b9db19f68fd3c3201046b3824d5ba689f99864328710cb" + + "467195eb37d84f539e49f859316b32964dc3e47e36814ce94d6c56dd02733b1d0802f7ff" + + "4eebdbbd2927dcf580f16cbc290f91e81b53cb365e7223f1d6e20a88ea064104875e0145" + + "672b20fc14829d51ca122f5f5d77d3ad6c83889c55c7dc43680ba2fe3cef8b05dbcabdc0" + + "d3e09aaf9725597f8c858c2fa38c0d6aed2e6318194420dd1a1137445d13e1c97ab47896" + + "17a4e08925f46f867b72e3a4dc1f08cb870b2b0717f7207faa0ac512e628a029aba7457a" + + "e63dcf3281e2162d9349a08204ba308204b6308204b23082039aa003020102020101300d" + + "06092a864886f70d010105050030818c310b300906035504061302494c31163014060355" + + "040a130d5374617274436f6d204c74642e312b3029060355040b13225365637572652044" + + "69676974616c204365727469666963617465205369676e696e6731383036060355040313" + + "2f5374617274436f6d20436c6173732031205072696d61727920496e7465726d65646961" + + "746520536572766572204341301e170d3037313032353030323330365a170d3132313032" + + "333030323330365a304c310b300906035504061302494c31163014060355040a130d5374" + + "617274436f6d204c74642e312530230603550403131c5374617274436f6d20436c617373" + + "2031204f435350205369676e657230820122300d06092a864886f70d0101010500038201" + + "0f003082010a0282010100b9561b4c45318717178084e96e178df2255e18ed8d8ecc7c2b" + + "7b51a6c1c2e6bf0aa3603066f132fe10ae97b50e99fa24b83fc53dd2777496387d14e1c3" + + "a9b6a4933e2ac12413d085570a95b8147414a0bc007c7bcf222446ef7f1a156d7ea1c577" + + "fc5f0facdfd42eb0f5974990cb2f5cefebceef4d1bdc7ae5c1075c5a99a93171f2b0845b" + + "4ff0864e973fcfe32f9d7511ff87a3e943410c90a4493a306b6944359340a9ca96f02b66" + + "ce67f028df2980a6aaee8d5d5d452b8b0eb93f923cc1e23fcccbdbe7ffcb114d08fa7a6a" + + "3c404f825d1a0e715935cf623a8c7b59670014ed0622f6089a9447a7a19010f7fe58f841" + + "29a2765ea367824d1c3bb2fda308530203010001a382015c30820158300c0603551d1301" + + "01ff04023000300b0603551d0f0404030203a8301e0603551d250417301506082b060105" + + "0507030906092b0601050507300105301d0603551d0e0416041445e0a36695414c5dd449" + + "bc00e33cdcdbd2343e173081a80603551d230481a030819d8014eb4234d098b0ab9ff41b" + + "6b08f7cc642eef0e2c45a18181a47f307d310b300906035504061302494c311630140603" + + "55040a130d5374617274436f6d204c74642e312b3029060355040b132253656375726520" + + "4469676974616c204365727469666963617465205369676e696e67312930270603550403" + + "13205374617274436f6d2043657274696669636174696f6e20417574686f726974798201" + + "0a30230603551d12041c301a8618687474703a2f2f7777772e737461727473736c2e636f" + + "6d2f302c06096086480186f842010d041f161d5374617274436f6d205265766f63617469" + + "6f6e20417574686f72697479300d06092a864886f70d01010505000382010100182d2215" + + "8f0fc0291324fa8574c49bb8ff2835085adcbf7b7fc4191c397ab6951328253fffe1e5ec" + + "2a7da0d50fca1a404e6968481366939e666c0a6209073eca57973e2fefa9ed1718e8176f" + + "1d85527ff522c08db702e3b2b180f1cbff05d98128252cf0f450f7dd2772f4188047f19d" + + "c85317366f94bc52d60f453a550af58e308aaab00ced33040b62bf37f5b1ab2a4f7f0f80" + + "f763bf4d707bc8841d7ad9385ee2a4244469260b6f2bf085977af9074796048ecc2f9d48" + + "a1d24ce16e41a9941568fec5b42771e118f16c106a54ccc339a4b02166445a167902e75e" + + "6d8620b0825dcd18a069b90fd851d10fa8effd409deec02860d26d8d833f304b10669b42" diff --git a/src/pkg/crypto/openpgp/armor/Makefile b/src/pkg/crypto/openpgp/armor/Makefile new file mode 100644 index 000000000..138e314e9 --- /dev/null +++ b/src/pkg/crypto/openpgp/armor/Makefile @@ -0,0 +1,12 @@ +# Copyright 2010 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../../../Make.inc + +TARG=crypto/openpgp/armor +GOFILES=\ + armor.go\ + encode.go\ + +include ../../../../Make.pkg diff --git a/src/pkg/crypto/openpgp/armor/armor.go b/src/pkg/crypto/openpgp/armor/armor.go new file mode 100644 index 000000000..97080f6c6 --- /dev/null +++ b/src/pkg/crypto/openpgp/armor/armor.go @@ -0,0 +1,220 @@ +// 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 package implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is +// very similar to PEM except that it has an additional CRC checksum. +package armor + +import ( + "bytes" + "crypto/openpgp/error" + "encoding/base64" + "encoding/line" + "io" + "os" +) + +// A Block represents an OpenPGP armored structure. +// +// The encoded form is: +// -----BEGIN Type----- +// Headers +// +// base64-encoded Bytes +// '=' base64 encoded checksum +// -----END Type----- +// where Headers is a possibly empty sequence of Key: Value lines. +// +// Since the armored data can be very large, this package presents a streaming +// interface. +type Block struct { + Type string // The type, taken from the preamble (i.e. "PGP SIGNATURE"). + Header map[string]string // Optional headers. + Body io.Reader // A Reader from which the contents can be read + lReader lineReader + oReader openpgpReader +} + +var ArmorCorrupt os.Error = error.StructuralError("armor invalid") + +const crc24Init = 0xb704ce +const crc24Poly = 0x1864cfb +const crc24Mask = 0xffffff + +// crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1 +func crc24(crc uint32, d []byte) uint32 { + for _, b := range d { + crc ^= uint32(b) << 16 + for i := 0; i < 8; i++ { + crc <<= 1 + if crc&0x1000000 != 0 { + crc ^= crc24Poly + } + } + } + return crc +} + +var armorStart = []byte("-----BEGIN ") +var armorEnd = []byte("-----END ") +var armorEndOfLine = []byte("-----") + +// lineReader wraps a line based reader. It watches for the end of an armor +// block and records the expected CRC value. +type lineReader struct { + in *line.Reader + buf []byte + eof bool + crc uint32 +} + +func (l *lineReader) Read(p []byte) (n int, err os.Error) { + if l.eof { + return 0, os.EOF + } + + if len(l.buf) > 0 { + n = copy(p, l.buf) + l.buf = l.buf[n:] + return + } + + line, isPrefix, err := l.in.ReadLine() + if err != nil { + return + } + if isPrefix { + return 0, ArmorCorrupt + } + + if len(line) == 5 && line[0] == '=' { + // This is the checksum line + var expectedBytes [3]byte + var m int + m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:]) + if m != 3 || err != nil { + return + } + l.crc = uint32(expectedBytes[0])<<16 | + uint32(expectedBytes[1])<<8 | + uint32(expectedBytes[2]) + + line, _, err = l.in.ReadLine() + if err != nil && err != os.EOF { + return + } + if !bytes.HasPrefix(line, armorEnd) { + return 0, ArmorCorrupt + } + + l.eof = true + return 0, os.EOF + } + + if len(line) != 64 { + return 0, ArmorCorrupt + } + + n = copy(p, line) + bytesToSave := len(line) - n + if bytesToSave > 0 { + if cap(l.buf) < bytesToSave { + l.buf = make([]byte, 0, bytesToSave) + } + l.buf = l.buf[0:bytesToSave] + copy(l.buf, line[n:]) + } + + return +} + +// openpgpReader passes Read calls to the underlying base64 decoder, but keeps +// a running CRC of the resulting data and checks the CRC against the value +// found by the lineReader at EOF. +type openpgpReader struct { + lReader *lineReader + b64Reader io.Reader + currentCRC uint32 +} + +func (r *openpgpReader) Read(p []byte) (n int, err os.Error) { + n, err = r.b64Reader.Read(p) + r.currentCRC = crc24(r.currentCRC, p[:n]) + + if err == os.EOF { + if r.lReader.crc != uint32(r.currentCRC&crc24Mask) { + return 0, ArmorCorrupt + } + } + + return +} + +// Decode reads a PGP armored block from the given Reader. It will ignore +// leading garbage. If it doesn't find a block, it will return nil, os.EOF. The +// given Reader is not usable after calling this function: an arbitary amount +// of data may have been read past the end of the block. +func Decode(in io.Reader) (p *Block, err os.Error) { + r := line.NewReader(in, 100) + var line []byte + ignoreNext := false + +TryNextBlock: + p = nil + + // Skip leading garbage + for { + ignoreThis := ignoreNext + line, ignoreNext, err = r.ReadLine() + if err != nil { + return + } + if ignoreNext || ignoreThis { + continue + } + line = bytes.TrimSpace(line) + if len(line) > len(armorStart)+len(armorEndOfLine) && bytes.HasPrefix(line, armorStart) { + break + } + } + + p = new(Block) + p.Type = string(line[len(armorStart) : len(line)-len(armorEndOfLine)]) + p.Header = make(map[string]string) + nextIsContinuation := false + var lastKey string + + // Read headers + for { + isContinuation := nextIsContinuation + line, nextIsContinuation, err = r.ReadLine() + if err != nil { + p = nil + return + } + if isContinuation { + p.Header[lastKey] += string(line) + continue + } + line = bytes.TrimSpace(line) + if len(line) == 0 { + break + } + + i := bytes.Index(line, []byte(": ")) + if i == -1 { + goto TryNextBlock + } + lastKey = string(line[:i]) + p.Header[lastKey] = string(line[i+2:]) + } + + p.lReader.in = r + p.oReader.currentCRC = crc24Init + p.oReader.lReader = &p.lReader + p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader) + p.Body = &p.oReader + + return +} diff --git a/src/pkg/crypto/openpgp/armor/armor_test.go b/src/pkg/crypto/openpgp/armor/armor_test.go new file mode 100644 index 000000000..e4ffd414b --- /dev/null +++ b/src/pkg/crypto/openpgp/armor/armor_test.go @@ -0,0 +1,97 @@ +// 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 armor + +import ( + "bytes" + "hash/adler32" + "io/ioutil" + "testing" +) + +func TestDecodeEncode(t *testing.T) { + buf := bytes.NewBuffer([]byte(armorExample1)) + result, err := Decode(buf) + if err != nil { + t.Error(err) + } + expectedType := "PGP SIGNATURE" + if result.Type != expectedType { + t.Errorf("result.Type: got:%s want:%s", result.Type, expectedType) + } + if len(result.Header) != 1 { + t.Errorf("len(result.Header): got:%d want:1", len(result.Header)) + } + v, ok := result.Header["Version"] + if !ok || v != "GnuPG v1.4.10 (GNU/Linux)" { + t.Errorf("result.Header: got:%#v", result.Header) + } + + contents, err := ioutil.ReadAll(result.Body) + if err != nil { + t.Error(err) + } + + if adler32.Checksum(contents) != 0x789d7f00 { + t.Errorf("contents: got: %x", contents) + } + + buf = bytes.NewBuffer(nil) + w, err := Encode(buf, result.Type, result.Header) + if err != nil { + t.Error(err) + } + _, err = w.Write(contents) + if err != nil { + t.Error(err) + } + w.Close() + + if !bytes.Equal(buf.Bytes(), []byte(armorExample1)) { + t.Errorf("got: %s\nwant: %s", string(buf.Bytes()), armorExample1) + } +} + +func TestLongHeader(t *testing.T) { + buf := bytes.NewBuffer([]byte(armorLongLine)) + result, err := Decode(buf) + if err != nil { + t.Error(err) + return + } + value, ok := result.Header["Version"] + if !ok { + t.Errorf("missing Version header") + } + if value != longValueExpected { + t.Errorf("got: %s want: %s", value, longValueExpected) + } +} + +const armorExample1 = `-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.4.10 (GNU/Linux) + +iQEcBAABAgAGBQJMtFESAAoJEKsQXJGvOPsVj40H/1WW6jaMXv4BW+1ueDSMDwM8 +kx1fLOXbVM5/Kn5LStZNt1jWWnpxdz7eq3uiqeCQjmqUoRde3YbB2EMnnwRbAhpp +cacnAvy9ZQ78OTxUdNW1mhX5bS6q1MTEJnl+DcyigD70HG/yNNQD7sOPMdYQw0TA +byQBwmLwmTsuZsrYqB68QyLHI+DUugn+kX6Hd2WDB62DKa2suoIUIHQQCd/ofwB3 +WfCYInXQKKOSxu2YOg2Eb4kLNhSMc1i9uKUWAH+sdgJh7NBgdoE4MaNtBFkHXRvv +okWuf3+xA9ksp1npSY/mDvgHijmjvtpRDe6iUeqfCn8N9u9CBg8geANgaG8+QA4= +=wfQG +-----END PGP SIGNATURE-----` + +const armorLongLine = `-----BEGIN PGP SIGNATURE----- +Version: 0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz + +iQEcBAABAgAGBQJMtFESAAoJEKsQXJGvOPsVj40H/1WW6jaMXv4BW+1ueDSMDwM8 +kx1fLOXbVM5/Kn5LStZNt1jWWnpxdz7eq3uiqeCQjmqUoRde3YbB2EMnnwRbAhpp +cacnAvy9ZQ78OTxUdNW1mhX5bS6q1MTEJnl+DcyigD70HG/yNNQD7sOPMdYQw0TA +byQBwmLwmTsuZsrYqB68QyLHI+DUugn+kX6Hd2WDB62DKa2suoIUIHQQCd/ofwB3 +WfCYInXQKKOSxu2YOg2Eb4kLNhSMc1i9uKUWAH+sdgJh7NBgdoE4MaNtBFkHXRvv +okWuf3+xA9ksp1npSY/mDvgHijmjvtpRDe6iUeqfCn8N9u9CBg8geANgaG8+QA4= +=wfQG +-----END PGP SIGNATURE-----` + +const longValueExpected = "0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz" diff --git a/src/pkg/crypto/openpgp/armor/encode.go b/src/pkg/crypto/openpgp/armor/encode.go new file mode 100644 index 000000000..410e73460 --- /dev/null +++ b/src/pkg/crypto/openpgp/armor/encode.go @@ -0,0 +1,162 @@ +// 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 armor + +import ( + "encoding/base64" + "io" + "os" +) + +var armorHeaderSep = []byte(": ") +var blockEnd = []byte("\n=") +var newline = []byte("\n") +var armorEndOfLineOut = []byte("-----\n") + +// writeSlices writes its arguments to the given Writer. +func writeSlices(out io.Writer, slices ...[]byte) (err os.Error) { + for _, s := range slices { + _, err := out.Write(s) + if err != nil { + return + } + } + return +} + +// lineBreaker breaks data across several lines, all of the same byte length +// (except possibly the last). Lines are broken with a single '\n'. +type lineBreaker struct { + lineLength int + line []byte + used int + out io.Writer + haveWritten bool +} + +func newLineBreaker(out io.Writer, lineLength int) *lineBreaker { + return &lineBreaker{ + lineLength: lineLength, + line: make([]byte, lineLength), + used: 0, + out: out, + } +} + +func (l *lineBreaker) Write(b []byte) (n int, err os.Error) { + n = len(b) + + if n == 0 { + return + } + + if l.used == 0 && l.haveWritten { + _, err = l.out.Write([]byte{'\n'}) + if err != nil { + return + } + } + + if l.used+len(b) < l.lineLength { + l.used += copy(l.line[l.used:], b) + return + } + + l.haveWritten = true + _, err = l.out.Write(l.line[0:l.used]) + if err != nil { + return + } + excess := l.lineLength - l.used + l.used = 0 + + _, err = l.out.Write(b[0:excess]) + if err != nil { + return + } + + _, err = l.Write(b[excess:]) + return +} + +func (l *lineBreaker) Close() (err os.Error) { + if l.used > 0 { + _, err = l.out.Write(l.line[0:l.used]) + if err != nil { + return + } + } + + return +} + +// encoding keeps track of a running CRC24 over the data which has been written +// to it and outputs a OpenPGP checksum when closed, followed by an armor +// trailer. +// +// It's built into a stack of io.Writers: +// encoding -> base64 encoder -> lineBreaker -> out +type encoding struct { + out io.Writer + breaker *lineBreaker + b64 io.WriteCloser + crc uint32 + blockType []byte +} + +func (e *encoding) Write(data []byte) (n int, err os.Error) { + e.crc = crc24(e.crc, data) + return e.b64.Write(data) +} + +func (e *encoding) Close() (err os.Error) { + err = e.b64.Close() + if err != nil { + return + } + + var checksumBytes [3]byte + checksumBytes[0] = byte(e.crc >> 16) + checksumBytes[1] = byte(e.crc >> 8) + checksumBytes[2] = byte(e.crc) + + var b64ChecksumBytes [4]byte + base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:]) + + return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine) +} + +// Encode returns a WriteCloser which will encode the data written to it in +// OpenPGP armor. +func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err os.Error) { + bType := []byte(blockType) + err = writeSlices(out, armorStart, bType, armorEndOfLineOut) + if err != nil { + return + } + + for k, v := range headers { + err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), newline) + if err != nil { + return + } + } + + if len(headers) > 0 { + _, err := out.Write(newline) + if err != nil { + return + } + } + + e := &encoding{ + out: out, + breaker: newLineBreaker(out, 64), + crc: crc24Init, + blockType: bType, + } + e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker) + return e, nil +} diff --git a/src/pkg/crypto/openpgp/error/Makefile b/src/pkg/crypto/openpgp/error/Makefile new file mode 100644 index 000000000..8c370a089 --- /dev/null +++ b/src/pkg/crypto/openpgp/error/Makefile @@ -0,0 +1,11 @@ +# Copyright 2010 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../../../Make.inc + +TARG=crypto/openpgp/error +GOFILES=\ + error.go\ + +include ../../../../Make.pkg diff --git a/src/pkg/crypto/openpgp/error/error.go b/src/pkg/crypto/openpgp/error/error.go new file mode 100644 index 000000000..2d80ce373 --- /dev/null +++ b/src/pkg/crypto/openpgp/error/error.go @@ -0,0 +1,46 @@ +// 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 package contains common error types for the OpenPGP packages. +package error + +// A StructuralError is returned when OpenPGP data is found to be syntactically +// invalid. +type StructuralError string + +func (s StructuralError) String() string { + return "OpenPGP data invalid: " + string(s) +} + +// UnsupportedError indicates that, although the OpenPGP data is valid, it +// makes use of currently unimplemented features. +type UnsupportedError string + +func (s UnsupportedError) String() string { + return "OpenPGP feature unsupported: " + string(s) +} + +// InvalidArgumentError indicates that the caller is in error and passed an +// incorrect value. +type InvalidArgumentError string + +func (i InvalidArgumentError) String() string { + return "OpenPGP argument invalid: " + string(i) +} + +// SignatureError indicates that a syntactically valid signature failed to +// validate. +type SignatureError string + +func (b SignatureError) String() string { + return "OpenPGP signature invalid: " + string(b) +} + +type keyIncorrect int + +func (ki keyIncorrect) String() string { + return "the given key was incorrect" +} + +var KeyIncorrectError = keyIncorrect(0) diff --git a/src/pkg/crypto/rand/Makefile b/src/pkg/crypto/rand/Makefile index 0e7a5536c..88b6d71e3 100644 --- a/src/pkg/crypto/rand/Makefile +++ b/src/pkg/crypto/rand/Makefile @@ -2,11 +2,25 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=crypto/rand GOFILES=\ rand.go\ +GOFILES_freebsd=\ + rand_unix.go\ + +GOFILES_darwin=\ + rand_unix.go\ + +GOFILES_linux=\ + rand_unix.go\ + +GOFILES_windows=\ + rand_windows.go\ + +GOFILES+=$(GOFILES_$(GOOS)) + include ../../../Make.pkg diff --git a/src/pkg/crypto/rand/rand.go b/src/pkg/crypto/rand/rand.go index 01c30316b..42d9da0ef 100644 --- a/src/pkg/crypto/rand/rand.go +++ b/src/pkg/crypto/rand/rand.go @@ -7,124 +7,15 @@ package rand import ( - "crypto/aes" "io" "os" - "sync" - "time" ) // Reader is a global, shared instance of a cryptographically // strong pseudo-random generator. +// On Unix-like systems, Reader reads from /dev/urandom. +// On Windows systems, Reader uses the CryptGenRandom API. var Reader io.Reader // Read is a helper function that calls Reader.Read. func Read(b []byte) (n int, err os.Error) { return Reader.Read(b) } - -// Easy implementation: read from /dev/urandom. -// This is sufficient on Linux, OS X, and FreeBSD. - -func init() { Reader = &devReader{name: "/dev/urandom"} } - -// A devReader satisfies reads by reading the file named name. -type devReader struct { - name string - f *os.File - mu sync.Mutex -} - -func (r *devReader) Read(b []byte) (n int, err os.Error) { - r.mu.Lock() - if r.f == nil { - f, err := os.Open(r.name, os.O_RDONLY, 0) - if f == nil { - return 0, err - } - r.f = f - } - r.mu.Unlock() - return r.f.Read(b) -} - -// Alternate pseudo-random implementation for use on -// systems without a reliable /dev/urandom. So far we -// haven't needed it. - -// newReader returns a new pseudorandom generator that -// seeds itself by reading from entropy. If entropy == nil, -// the generator seeds itself by reading from the system's -// random number generator, typically /dev/random. -// The Read method on the returned reader always returns -// the full amount asked for, or else it returns an error. -// -// The generator uses the X9.31 algorithm with AES-128, -// reseeding after every 1 MB of generated data. -func newReader(entropy io.Reader) io.Reader { - if entropy == nil { - entropy = &devReader{name: "/dev/random"} - } - return &reader{entropy: entropy} -} - -type reader struct { - mu sync.Mutex - budget int // number of bytes that can be generated - cipher *aes.Cipher - entropy io.Reader - time, seed, dst, key [aes.BlockSize]byte -} - -func (r *reader) Read(b []byte) (n int, err os.Error) { - r.mu.Lock() - defer r.mu.Unlock() - n = len(b) - - for len(b) > 0 { - if r.budget == 0 { - _, err := io.ReadFull(r.entropy, r.seed[0:]) - if err != nil { - return n - len(b), err - } - _, err = io.ReadFull(r.entropy, r.key[0:]) - if err != nil { - return n - len(b), err - } - r.cipher, err = aes.NewCipher(r.key[0:]) - if err != nil { - return n - len(b), err - } - r.budget = 1 << 20 // reseed after generating 1MB - } - r.budget -= aes.BlockSize - - // ANSI X9.31 (== X9.17) algorithm, but using AES in place of 3DES. - // - // single block: - // t = encrypt(time) - // dst = encrypt(t^seed) - // seed = encrypt(t^dst) - ns := time.Nanoseconds() - r.time[0] = byte(ns >> 56) - r.time[1] = byte(ns >> 48) - r.time[2] = byte(ns >> 40) - r.time[3] = byte(ns >> 32) - r.time[4] = byte(ns >> 24) - r.time[5] = byte(ns >> 16) - r.time[6] = byte(ns >> 8) - r.time[7] = byte(ns) - r.cipher.Encrypt(r.time[0:], r.time[0:]) - for i := 0; i < aes.BlockSize; i++ { - r.dst[i] = r.time[i] ^ r.seed[i] - } - r.cipher.Encrypt(r.dst[0:], r.dst[0:]) - for i := 0; i < aes.BlockSize; i++ { - r.seed[i] = r.time[i] ^ r.dst[i] - } - r.cipher.Encrypt(r.seed[0:], r.seed[0:]) - - m := copy(b, r.dst[0:]) - b = b[m:] - } - - return n, nil -} diff --git a/src/pkg/crypto/rand/rand_unix.go b/src/pkg/crypto/rand/rand_unix.go new file mode 100644 index 000000000..ff16f2554 --- /dev/null +++ b/src/pkg/crypto/rand/rand_unix.go @@ -0,0 +1,125 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Unix cryptographically secure pseudorandom number +// generator. + +package rand + +import ( + "crypto/aes" + "io" + "os" + "sync" + "time" +) + +// Easy implementation: read from /dev/urandom. +// This is sufficient on Linux, OS X, and FreeBSD. + +func init() { Reader = &devReader{name: "/dev/urandom"} } + +// A devReader satisfies reads by reading the file named name. +type devReader struct { + name string + f *os.File + mu sync.Mutex +} + +func (r *devReader) Read(b []byte) (n int, err os.Error) { + r.mu.Lock() + if r.f == nil { + f, err := os.Open(r.name, os.O_RDONLY, 0) + if f == nil { + r.mu.Unlock() + return 0, err + } + r.f = f + } + r.mu.Unlock() + return r.f.Read(b) +} + +// Alternate pseudo-random implementation for use on +// systems without a reliable /dev/urandom. So far we +// haven't needed it. + +// newReader returns a new pseudorandom generator that +// seeds itself by reading from entropy. If entropy == nil, +// the generator seeds itself by reading from the system's +// random number generator, typically /dev/random. +// The Read method on the returned reader always returns +// the full amount asked for, or else it returns an error. +// +// The generator uses the X9.31 algorithm with AES-128, +// reseeding after every 1 MB of generated data. +func newReader(entropy io.Reader) io.Reader { + if entropy == nil { + entropy = &devReader{name: "/dev/random"} + } + return &reader{entropy: entropy} +} + +type reader struct { + mu sync.Mutex + budget int // number of bytes that can be generated + cipher *aes.Cipher + entropy io.Reader + time, seed, dst, key [aes.BlockSize]byte +} + +func (r *reader) Read(b []byte) (n int, err os.Error) { + r.mu.Lock() + defer r.mu.Unlock() + n = len(b) + + for len(b) > 0 { + if r.budget == 0 { + _, err := io.ReadFull(r.entropy, r.seed[0:]) + if err != nil { + return n - len(b), err + } + _, err = io.ReadFull(r.entropy, r.key[0:]) + if err != nil { + return n - len(b), err + } + r.cipher, err = aes.NewCipher(r.key[0:]) + if err != nil { + return n - len(b), err + } + r.budget = 1 << 20 // reseed after generating 1MB + } + r.budget -= aes.BlockSize + + // ANSI X9.31 (== X9.17) algorithm, but using AES in place of 3DES. + // + // single block: + // t = encrypt(time) + // dst = encrypt(t^seed) + // seed = encrypt(t^dst) + ns := time.Nanoseconds() + r.time[0] = byte(ns >> 56) + r.time[1] = byte(ns >> 48) + r.time[2] = byte(ns >> 40) + r.time[3] = byte(ns >> 32) + r.time[4] = byte(ns >> 24) + r.time[5] = byte(ns >> 16) + r.time[6] = byte(ns >> 8) + r.time[7] = byte(ns) + r.cipher.Encrypt(r.time[0:], r.time[0:]) + for i := 0; i < aes.BlockSize; i++ { + r.dst[i] = r.time[i] ^ r.seed[i] + } + r.cipher.Encrypt(r.dst[0:], r.dst[0:]) + for i := 0; i < aes.BlockSize; i++ { + r.seed[i] = r.time[i] ^ r.dst[i] + } + r.cipher.Encrypt(r.seed[0:], r.seed[0:]) + + m := copy(b, r.dst[0:]) + b = b[m:] + } + + return n, nil +} diff --git a/src/pkg/crypto/rand/rand_windows.go b/src/pkg/crypto/rand/rand_windows.go new file mode 100755 index 000000000..4b2b7a26f --- /dev/null +++ b/src/pkg/crypto/rand/rand_windows.go @@ -0,0 +1,43 @@ +// 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. + +// Windows cryptographically secure pseudorandom number +// generator. + +package rand + +import ( + "os" + "sync" + "syscall" +) + +// Implemented by using Windows CryptoAPI 2.0. + +func init() { Reader = &rngReader{} } + +// A rngReader satisfies reads by reading from the Windows CryptGenRandom API. +type rngReader struct { + prov uint32 + mu sync.Mutex +} + +func (r *rngReader) Read(b []byte) (n int, err os.Error) { + r.mu.Lock() + if r.prov == 0 { + const provType = syscall.PROV_RSA_FULL + const flags = syscall.CRYPT_VERIFYCONTEXT | syscall.CRYPT_SILENT + ok, errno := syscall.CryptAcquireContext(&r.prov, nil, nil, provType, flags) + if !ok { + r.mu.Unlock() + return 0, os.NewSyscallError("CryptAcquireContext", errno) + } + } + r.mu.Unlock() + ok, errno := syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0]) + if !ok { + return 0, os.NewSyscallError("CryptGenRandom", errno) + } + return len(b), nil +} diff --git a/src/pkg/crypto/rc4/Makefile b/src/pkg/crypto/rc4/Makefile index 7827b0817..50a3b7972 100644 --- a/src/pkg/crypto/rc4/Makefile +++ b/src/pkg/crypto/rc4/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=crypto/rc4 GOFILES=\ diff --git a/src/pkg/crypto/rc4/rc4.go b/src/pkg/crypto/rc4/rc4.go index e47a01513..65fd195f3 100644 --- a/src/pkg/crypto/rc4/rc4.go +++ b/src/pkg/crypto/rc4/rc4.go @@ -45,14 +45,14 @@ func NewCipher(key []byte) (*Cipher, os.Error) { return &c, nil } -// XORKeyStream will XOR each byte of the given buffer with a byte of the -// generated keystream. -func (c *Cipher) XORKeyStream(buf []byte) { - for i := range buf { +// XORKeyStream sets dst to the result of XORing src with the key stream. +// Dst and src may be the same slice but otherwise should not overlap. +func (c *Cipher) XORKeyStream(dst, src []byte) { + for i := range src { c.i += 1 c.j += c.s[c.i] c.s[c.i], c.s[c.j] = c.s[c.j], c.s[c.i] - buf[i] ^= c.s[c.s[c.i]+c.s[c.j]] + dst[i] = src[i] ^ c.s[c.s[c.i]+c.s[c.j]] } } diff --git a/src/pkg/crypto/rc4/rc4_test.go b/src/pkg/crypto/rc4/rc4_test.go index 1d39b2f17..6265d9408 100644 --- a/src/pkg/crypto/rc4/rc4_test.go +++ b/src/pkg/crypto/rc4/rc4_test.go @@ -15,25 +15,25 @@ type rc4Test struct { var golden = []rc4Test{ // Test vectors from the original cypherpunk posting of ARC4: // http://groups.google.com/group/sci.crypt/msg/10a300c9d21afca0?pli=1 - rc4Test{ + { []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, []byte{0x74, 0x94, 0xc2, 0xe7, 0x10, 0x4b, 0x08, 0x79}, }, - rc4Test{ + { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0xde, 0x18, 0x89, 0x41, 0xa3, 0x37, 0x5d, 0x3a}, }, - rc4Test{ + { []byte{0xef, 0x01, 0x23, 0x45}, []byte{0xd6, 0xa1, 0x41, 0xa7, 0xec, 0x3c, 0x38, 0xdf, 0xbd, 0x61}, }, // Test vectors from the Wikipedia page: http://en.wikipedia.org/wiki/RC4 - rc4Test{ + { []byte{0x4b, 0x65, 0x79}, []byte{0xeb, 0x9f, 0x77, 0x81, 0xb7, 0x34, 0xca, 0x72, 0xa7, 0x19}, }, - rc4Test{ + { []byte{0x57, 0x69, 0x6b, 0x69}, []byte{0x60, 0x44, 0xdb, 0x6d, 0x41, 0xb7}, }, @@ -48,7 +48,7 @@ func TestGolden(t *testing.T) { return } keystream := make([]byte, len(g.keystream)) - c.XORKeyStream(keystream) + c.XORKeyStream(keystream, keystream) for j, v := range keystream { if g.keystream[j] != v { t.Errorf("Failed at golden index %d", i) diff --git a/src/pkg/crypto/ripemd160/Makefile b/src/pkg/crypto/ripemd160/Makefile index 109e68eda..7e529457d 100644 --- a/src/pkg/crypto/ripemd160/Makefile +++ b/src/pkg/crypto/ripemd160/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=crypto/ripemd160 GOFILES=\ diff --git a/src/pkg/crypto/ripemd160/ripemd160.go b/src/pkg/crypto/ripemd160/ripemd160.go index 5d5519842..5614f1360 100644 --- a/src/pkg/crypto/ripemd160/ripemd160.go +++ b/src/pkg/crypto/ripemd160/ripemd160.go @@ -72,10 +72,7 @@ func (d *digest) Write(p []byte) (nn int, err os.Error) { n := _Block(d, p) p = p[n:] if len(p) > 0 { - for i, x := range p { - d.x[i] = x - } - d.nx = len(p) + d.nx = copy(d.x[:], p) } return } diff --git a/src/pkg/crypto/ripemd160/ripemd160_test.go b/src/pkg/crypto/ripemd160/ripemd160_test.go index eaa3d78c2..f4135f5cf 100644 --- a/src/pkg/crypto/ripemd160/ripemd160_test.go +++ b/src/pkg/crypto/ripemd160/ripemd160_test.go @@ -19,14 +19,14 @@ type mdTest struct { } var vectors = [...]mdTest{ - mdTest{"9c1185a5c5e9fc54612808977ee8f548b2258d31", ""}, - mdTest{"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe", "a"}, - mdTest{"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc", "abc"}, - mdTest{"5d0689ef49d2fae572b881b123a85ffa21595f36", "message digest"}, - mdTest{"f71c27109c692c1b56bbdceb5b9d2865b3708dbc", "abcdefghijklmnopqrstuvwxyz"}, - mdTest{"12a053384a9c0c88e405a06c27dcf49ada62eb2b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - mdTest{"b0e20b6e3116640286ed3a87a5713079b21f5189", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, - mdTest{"9b752e45573d4b39f4dbd3323cab82bf63326bfb", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + {"9c1185a5c5e9fc54612808977ee8f548b2258d31", ""}, + {"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe", "a"}, + {"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc", "abc"}, + {"5d0689ef49d2fae572b881b123a85ffa21595f36", "message digest"}, + {"f71c27109c692c1b56bbdceb5b9d2865b3708dbc", "abcdefghijklmnopqrstuvwxyz"}, + {"12a053384a9c0c88e405a06c27dcf49ada62eb2b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + {"b0e20b6e3116640286ed3a87a5713079b21f5189", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + {"9b752e45573d4b39f4dbd3323cab82bf63326bfb", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, } func TestVectors(t *testing.T) { diff --git a/src/pkg/crypto/rsa/Makefile b/src/pkg/crypto/rsa/Makefile index e4d81bc52..ff26ca6f2 100644 --- a/src/pkg/crypto/rsa/Makefile +++ b/src/pkg/crypto/rsa/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=crypto/rsa GOFILES=\ diff --git a/src/pkg/crypto/rsa/pkcs1v15.go b/src/pkg/crypto/rsa/pkcs1v15.go index 5fd25d58c..714046250 100644 --- a/src/pkg/crypto/rsa/pkcs1v15.go +++ b/src/pkg/crypto/rsa/pkcs1v15.go @@ -130,6 +130,9 @@ func nonZeroRandomBytes(s []byte, rand io.Reader) (err os.Error) { if err != nil { return } + // In tests, the PRNG may return all zeros so we do + // this to break the loop. + s[i] ^= 0x42 } } @@ -146,6 +149,7 @@ const ( HashSHA256 HashSHA384 HashSHA512 + HashMD5SHA1 // combined MD5 and SHA1 hash used for RSA signing in TLS. ) // These are ASN1 DER structures: @@ -153,20 +157,22 @@ const ( // digestAlgorithm AlgorithmIdentifier, // digest OCTET STRING // } -// For performance, we don't use the generic ASN1 encoding. Rather, we +// For performance, we don't use the generic ASN1 encoder. Rather, we // precompute a prefix of the digest value that makes a valid ASN1 DER string // with the correct contents. var hashPrefixes = [][]byte{ // HashMD5 - []byte{0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10}, + {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10}, // HashSHA1 - []byte{0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14}, + {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14}, // HashSHA256 - []byte{0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}, + {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}, // HashSHA384 - []byte{0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30}, + {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30}, // HashSHA512 - []byte{0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40}, + {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40}, + // HashMD5SHA1 + {}, // A special TLS case which doesn't use an ASN1 prefix. } // SignPKCS1v15 calcuates the signature of hashed using RSASSA-PSS-SIGN from RSA PKCS#1 v1.5. @@ -252,6 +258,8 @@ func pkcs1v15HashInfo(hash PKCS1v15Hash, inLen int) (hashLen int, prefix []byte, hashLen = 48 case HashSHA512: hashLen = 64 + case HashMD5SHA1: + hashLen = 36 default: return 0, nil, os.ErrorString("unknown hash function") } diff --git a/src/pkg/crypto/rsa/pkcs1v15_test.go b/src/pkg/crypto/rsa/pkcs1v15_test.go index bfc12be28..bf6306dc2 100644 --- a/src/pkg/crypto/rsa/pkcs1v15_test.go +++ b/src/pkg/crypto/rsa/pkcs1v15_test.go @@ -7,10 +7,10 @@ package rsa import ( "big" "bytes" + "crypto/rand" "crypto/sha1" "encoding/base64" "encoding/hex" - "os" "io" "testing" "testing/quick" @@ -31,19 +31,19 @@ type DecryptPKCS1v15Test struct { // These test vectors were generated with `openssl rsautl -pkcs -encrypt` var decryptPKCS1v15Tests = []DecryptPKCS1v15Test{ - DecryptPKCS1v15Test{ + { "gIcUIoVkD6ATMBk/u/nlCZCCWRKdkfjCgFdo35VpRXLduiKXhNz1XupLLzTXAybEq15juc+EgY5o0DHv/nt3yg==", "x", }, - DecryptPKCS1v15Test{ + { "Y7TOCSqofGhkRb+jaVRLzK8xw2cSo1IVES19utzv6hwvx+M8kFsoWQm5DzBeJCZTCVDPkTpavUuEbgp8hnUGDw==", "testing.", }, - DecryptPKCS1v15Test{ + { "arReP9DJtEVyV2Dg3dDp4c/PSk1O6lxkoJ8HcFupoRorBZG+7+1fDAwT1olNddFnQMjmkb8vxwmNMoTAT/BFjQ==", "testing.\n", }, - DecryptPKCS1v15Test{ + { "WtaBXIoGC54+vH0NH0CHHE+dRDOsMc/6BrfFu2lEqcKL9+uDuWaf+Xj9mrbQCjjZcpQuX733zyok/jsnqe/Ftw==", "01234567890123456789012345678901234567890123456789012", }, @@ -63,10 +63,7 @@ func TestDecryptPKCS1v15(t *testing.T) { } func TestEncryptPKCS1v15(t *testing.T) { - urandom, err := os.Open("/dev/urandom", os.O_RDONLY, 0) - if err != nil { - t.Errorf("Failed to open /dev/urandom") - } + random := rand.Reader k := (rsaPrivateKey.N.BitLen() + 7) / 8 tryEncryptDecrypt := func(in []byte, blind bool) bool { @@ -74,7 +71,7 @@ func TestEncryptPKCS1v15(t *testing.T) { in = in[0 : k-11] } - ciphertext, err := EncryptPKCS1v15(urandom, &rsaPrivateKey.PublicKey, in) + ciphertext, err := EncryptPKCS1v15(random, &rsaPrivateKey.PublicKey, in) if err != nil { t.Errorf("error encrypting: %s", err) return false @@ -84,7 +81,7 @@ func TestEncryptPKCS1v15(t *testing.T) { if !blind { rand = nil } else { - rand = urandom + rand = random } plaintext, err := DecryptPKCS1v15(rand, rsaPrivateKey, ciphertext) if err != nil { @@ -104,19 +101,19 @@ func TestEncryptPKCS1v15(t *testing.T) { // These test vectors were generated with `openssl rsautl -pkcs -encrypt` var decryptPKCS1v15SessionKeyTests = []DecryptPKCS1v15Test{ - DecryptPKCS1v15Test{ + { "e6ukkae6Gykq0fKzYwULpZehX+UPXYzMoB5mHQUDEiclRbOTqas4Y0E6nwns1BBpdvEJcilhl5zsox/6DtGsYg==", "1234", }, - DecryptPKCS1v15Test{ + { "Dtis4uk/q/LQGGqGk97P59K03hkCIVFMEFZRgVWOAAhxgYpCRG0MX2adptt92l67IqMki6iVQyyt0TtX3IdtEw==", "FAIL", }, - DecryptPKCS1v15Test{ + { "LIyFyCYCptPxrvTxpol8F3M7ZivlMsf53zs0vHRAv+rDIh2YsHS69ePMoPMe3TkOMZ3NupiL3takPxIs1sK+dw==", "abcd", }, - DecryptPKCS1v15Test{ + { "bafnobel46bKy76JzqU/RIVOH0uAYvzUtauKmIidKgM0sMlvobYVAVQPeUQ/oTGjbIZ1v/6Gyi5AO4DtHruGdw==", "FAIL", }, @@ -137,13 +134,10 @@ func TestEncryptPKCS1v15SessionKey(t *testing.T) { } func TestNonZeroRandomBytes(t *testing.T) { - urandom, err := os.Open("/dev/urandom", os.O_RDONLY, 0) - if err != nil { - t.Errorf("Failed to open /dev/urandom") - } + random := rand.Reader b := make([]byte, 512) - err = nonZeroRandomBytes(b, urandom) + err := nonZeroRandomBytes(b, random) if err != nil { t.Errorf("returned error: %s", err) } @@ -162,7 +156,7 @@ type signPKCS1v15Test struct { // These vectors have been tested with // `openssl rsautl -verify -inkey pk -in signature | hexdump -C` var signPKCS1v15Tests = []signPKCS1v15Test{ - signPKCS1v15Test{"Test.\n", "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e336ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae"}, + {"Test.\n", "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e336ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae"}, } func TestSignPKCS1v15(t *testing.T) { diff --git a/src/pkg/crypto/rsa/rsa_test.go b/src/pkg/crypto/rsa/rsa_test.go index 172173900..df1f17f17 100644 --- a/src/pkg/crypto/rsa/rsa_test.go +++ b/src/pkg/crypto/rsa/rsa_test.go @@ -7,18 +7,15 @@ package rsa import ( "big" "bytes" + "crypto/rand" "crypto/sha1" - "os" "testing" ) func TestKeyGeneration(t *testing.T) { - urandom, err := os.Open("/dev/urandom", os.O_RDONLY, 0) - if err != nil { - t.Errorf("failed to open /dev/urandom") - } + random := rand.Reader - priv, err := GenerateKey(urandom, 1024) + priv, err := GenerateKey(random, 1024) if err != nil { t.Errorf("failed to generate key") } @@ -33,7 +30,7 @@ func TestKeyGeneration(t *testing.T) { t.Errorf("got:%v, want:%v (%s)", m2, m, priv) } - m3, err := decrypt(urandom, priv, c) + m3, err := decrypt(random, priv, c) if err != nil { t.Errorf("error while decrypting (blind): %s", err) } @@ -76,10 +73,7 @@ func TestEncryptOAEP(t *testing.T) { } func TestDecryptOAEP(t *testing.T) { - urandom, err := os.Open("/dev/urandom", os.O_RDONLY, 0) - if err != nil { - t.Errorf("Failed to open /dev/urandom") - } + random := rand.Reader sha1 := sha1.New() n := new(big.Int) @@ -98,7 +92,7 @@ func TestDecryptOAEP(t *testing.T) { } // Decrypt with blinding. - out, err = DecryptOAEP(sha1, urandom, &private, message.out, nil) + out, err = DecryptOAEP(sha1, random, &private, message.out, nil) if err != nil { t.Errorf("#%d,%d (blind) error: %s", i, j, err) } else if bytes.Compare(out, message.in) != 0 { @@ -111,12 +105,12 @@ func TestDecryptOAEP(t *testing.T) { // testEncryptOAEPData contains a subset of the vectors from RSA's "Test vectors for RSA-OAEP". var testEncryptOAEPData = []testEncryptOAEPStruct{ // Key 1 - testEncryptOAEPStruct{"a8b3b284af8eb50b387034a860f146c4919f318763cd6c5598c8ae4811a1e0abc4c7e0b082d693a5e7fced675cf4668512772c0cbc64a742c6c630f533c8cc72f62ae833c40bf25842e984bb78bdbf97c0107d55bdb662f5c4e0fab9845cb5148ef7392dd3aaff93ae1e6b667bb3d4247616d4f5ba10d4cfd226de88d39f16fb", + {"a8b3b284af8eb50b387034a860f146c4919f318763cd6c5598c8ae4811a1e0abc4c7e0b082d693a5e7fced675cf4668512772c0cbc64a742c6c630f533c8cc72f62ae833c40bf25842e984bb78bdbf97c0107d55bdb662f5c4e0fab9845cb5148ef7392dd3aaff93ae1e6b667bb3d4247616d4f5ba10d4cfd226de88d39f16fb", 65537, "53339cfdb79fc8466a655c7316aca85c55fd8f6dd898fdaf119517ef4f52e8fd8e258df93fee180fa0e4ab29693cd83b152a553d4ac4d1812b8b9fa5af0e7f55fe7304df41570926f3311f15c4d65a732c483116ee3d3d2d0af3549ad9bf7cbfb78ad884f84d5beb04724dc7369b31def37d0cf539e9cfcdd3de653729ead5d1", []testEncryptOAEPMessage{ // Example 1.1 - testEncryptOAEPMessage{ + { []byte{0x66, 0x28, 0x19, 0x4e, 0x12, 0x07, 0x3d, 0xb0, 0x3b, 0xa9, 0x4c, 0xda, 0x9e, 0xf9, 0x53, 0x23, 0x97, 0xd5, 0x0d, 0xba, 0x79, 0xb9, 0x87, 0x00, 0x4a, 0xfe, @@ -144,7 +138,7 @@ var testEncryptOAEPData = []testEncryptOAEPStruct{ }, }, // Example 1.2 - testEncryptOAEPMessage{ + { []byte{0x75, 0x0c, 0x40, 0x47, 0xf5, 0x47, 0xe8, 0xe4, 0x14, 0x11, 0x85, 0x65, 0x23, 0x29, 0x8a, 0xc9, 0xba, 0xe2, 0x45, 0xef, 0xaf, 0x13, 0x97, 0xfb, 0xe5, 0x6f, @@ -172,7 +166,7 @@ var testEncryptOAEPData = []testEncryptOAEPStruct{ }, }, // Example 1.3 - testEncryptOAEPMessage{ + { []byte{0xd9, 0x4a, 0xe0, 0x83, 0x2e, 0x64, 0x45, 0xce, 0x42, 0x33, 0x1c, 0xb0, 0x6d, 0x53, 0x1a, 0x82, 0xb1, 0xdb, 0x4b, 0xaa, 0xd3, 0x0f, 0x74, 0x6d, 0xc9, 0x16, @@ -205,12 +199,12 @@ var testEncryptOAEPData = []testEncryptOAEPStruct{ }, }, // Key 10 - testEncryptOAEPStruct{"ae45ed5601cec6b8cc05f803935c674ddbe0d75c4c09fd7951fc6b0caec313a8df39970c518bffba5ed68f3f0d7f22a4029d413f1ae07e4ebe9e4177ce23e7f5404b569e4ee1bdcf3c1fb03ef113802d4f855eb9b5134b5a7c8085adcae6fa2fa1417ec3763be171b0c62b760ede23c12ad92b980884c641f5a8fac26bdad4a03381a22fe1b754885094c82506d4019a535a286afeb271bb9ba592de18dcf600c2aeeae56e02f7cf79fc14cf3bdc7cd84febbbf950ca90304b2219a7aa063aefa2c3c1980e560cd64afe779585b6107657b957857efde6010988ab7de417fc88d8f384c4e6e72c3f943e0c31c0c4a5cc36f879d8a3ac9d7d59860eaada6b83bb", + {"ae45ed5601cec6b8cc05f803935c674ddbe0d75c4c09fd7951fc6b0caec313a8df39970c518bffba5ed68f3f0d7f22a4029d413f1ae07e4ebe9e4177ce23e7f5404b569e4ee1bdcf3c1fb03ef113802d4f855eb9b5134b5a7c8085adcae6fa2fa1417ec3763be171b0c62b760ede23c12ad92b980884c641f5a8fac26bdad4a03381a22fe1b754885094c82506d4019a535a286afeb271bb9ba592de18dcf600c2aeeae56e02f7cf79fc14cf3bdc7cd84febbbf950ca90304b2219a7aa063aefa2c3c1980e560cd64afe779585b6107657b957857efde6010988ab7de417fc88d8f384c4e6e72c3f943e0c31c0c4a5cc36f879d8a3ac9d7d59860eaada6b83bb", 65537, "056b04216fe5f354ac77250a4b6b0c8525a85c59b0bd80c56450a22d5f438e596a333aa875e291dd43f48cb88b9d5fc0d499f9fcd1c397f9afc070cd9e398c8d19e61db7c7410a6b2675dfbf5d345b804d201add502d5ce2dfcb091ce9997bbebe57306f383e4d588103f036f7e85d1934d152a323e4a8db451d6f4a5b1b0f102cc150e02feee2b88dea4ad4c1baccb24d84072d14e1d24a6771f7408ee30564fb86d4393a34bcf0b788501d193303f13a2284b001f0f649eaf79328d4ac5c430ab4414920a9460ed1b7bc40ec653e876d09abc509ae45b525190116a0c26101848298509c1c3bf3a483e7274054e15e97075036e989f60932807b5257751e79", []testEncryptOAEPMessage{ // Example 10.1 - testEncryptOAEPMessage{ + { []byte{0x8b, 0xba, 0x6b, 0xf8, 0x2a, 0x6c, 0x0f, 0x86, 0xd5, 0xf1, 0x75, 0x6e, 0x97, 0x95, 0x68, 0x70, 0xb0, 0x89, 0x53, 0xb0, 0x6b, 0x4e, 0xb2, 0x05, 0xbc, 0x16, diff --git a/src/pkg/crypto/sha1/Makefile b/src/pkg/crypto/sha1/Makefile index f3422dd84..81ac38c0b 100644 --- a/src/pkg/crypto/sha1/Makefile +++ b/src/pkg/crypto/sha1/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=crypto/sha1 GOFILES=\ diff --git a/src/pkg/crypto/sha1/sha1.go b/src/pkg/crypto/sha1/sha1.go index 681870a21..8716c3591 100644 --- a/src/pkg/crypto/sha1/sha1.go +++ b/src/pkg/crypto/sha1/sha1.go @@ -70,10 +70,7 @@ func (d *digest) Write(p []byte) (nn int, err os.Error) { n := _Block(d, p) p = p[n:] if len(p) > 0 { - for i, x := range p { - d.x[i] = x - } - d.nx = len(p) + d.nx = copy(d.x[:], p) } return } diff --git a/src/pkg/crypto/sha1/sha1_test.go b/src/pkg/crypto/sha1/sha1_test.go index f18c7b096..2712fe35e 100644 --- a/src/pkg/crypto/sha1/sha1_test.go +++ b/src/pkg/crypto/sha1/sha1_test.go @@ -18,37 +18,37 @@ type sha1Test struct { } var golden = []sha1Test{ - sha1Test{"da39a3ee5e6b4b0d3255bfef95601890afd80709", ""}, - sha1Test{"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", "a"}, - sha1Test{"da23614e02469a0d7c7bd1bdab5c9c474b1904dc", "ab"}, - sha1Test{"a9993e364706816aba3e25717850c26c9cd0d89d", "abc"}, - sha1Test{"81fe8bfe87576c3ecb22426f8e57847382917acf", "abcd"}, - sha1Test{"03de6c570bfe24bfc328ccd7ca46b76eadaf4334", "abcde"}, - sha1Test{"1f8ac10f23c5b5bc1167bda84b833e5c057a77d2", "abcdef"}, - sha1Test{"2fb5e13419fc89246865e7a324f476ec624e8740", "abcdefg"}, - sha1Test{"425af12a0743502b322e93a015bcf868e324d56a", "abcdefgh"}, - sha1Test{"c63b19f1e4c8b5f76b25c49b8b87f57d8e4872a1", "abcdefghi"}, - sha1Test{"d68c19a0a345b7eab78d5e11e991c026ec60db63", "abcdefghij"}, - sha1Test{"ebf81ddcbe5bf13aaabdc4d65354fdf2044f38a7", "Discard medicine more than two years old."}, - sha1Test{"e5dea09392dd886ca63531aaa00571dc07554bb6", "He who has a shady past knows that nice guys finish last."}, - sha1Test{"45988f7234467b94e3e9494434c96ee3609d8f8f", "I wouldn't marry him with a ten foot pole."}, - sha1Test{"55dee037eb7460d5a692d1ce11330b260e40c988", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, - sha1Test{"b7bc5fb91080c7de6b582ea281f8a396d7c0aee8", "The days of the digital watch are numbered. -Tom Stoppard"}, - sha1Test{"c3aed9358f7c77f523afe86135f06b95b3999797", "Nepal premier won't resign."}, - sha1Test{"6e29d302bf6e3a5e4305ff318d983197d6906bb9", "For every action there is an equal and opposite government program."}, - sha1Test{"597f6a540010f94c15d71806a99a2c8710e747bd", "His money is twice tainted: 'taint yours and 'taint mine."}, - sha1Test{"6859733b2590a8a091cecf50086febc5ceef1e80", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, - sha1Test{"514b2630ec089b8aee18795fc0cf1f4860cdacad", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, - sha1Test{"c5ca0d4a7b6676fc7aa72caa41cc3d5df567ed69", "size: a.out: bad magic"}, - sha1Test{"74c51fa9a04eadc8c1bbeaa7fc442f834b90a00a", "The major problem is with sendmail. -Mark Horton"}, - sha1Test{"0b4c4ce5f52c3ad2821852a8dc00217fa18b8b66", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, - sha1Test{"3ae7937dd790315beb0f48330e8642237c61550a", "If the enemy is within range, then so are you."}, - sha1Test{"410a2b296df92b9a47412b13281df8f830a9f44b", "It's well we cannot hear the screams/That we create in others' dreams."}, - sha1Test{"841e7c85ca1adcddbdd0187f1289acb5c642f7f5", "You remind me of a TV show, but that's all right: I watch it anyway."}, - sha1Test{"163173b825d03b952601376b25212df66763e1db", "C is as portable as Stonehedge!!"}, - sha1Test{"32b0377f2687eb88e22106f133c586ab314d5279", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, - sha1Test{"0885aaf99b569542fd165fa44e322718f4a984e0", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, - sha1Test{"6627d6904d71420b0bf3886ab629623538689f45", "How can you write a big system without C++? -Paul Glick"}, + {"da39a3ee5e6b4b0d3255bfef95601890afd80709", ""}, + {"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", "a"}, + {"da23614e02469a0d7c7bd1bdab5c9c474b1904dc", "ab"}, + {"a9993e364706816aba3e25717850c26c9cd0d89d", "abc"}, + {"81fe8bfe87576c3ecb22426f8e57847382917acf", "abcd"}, + {"03de6c570bfe24bfc328ccd7ca46b76eadaf4334", "abcde"}, + {"1f8ac10f23c5b5bc1167bda84b833e5c057a77d2", "abcdef"}, + {"2fb5e13419fc89246865e7a324f476ec624e8740", "abcdefg"}, + {"425af12a0743502b322e93a015bcf868e324d56a", "abcdefgh"}, + {"c63b19f1e4c8b5f76b25c49b8b87f57d8e4872a1", "abcdefghi"}, + {"d68c19a0a345b7eab78d5e11e991c026ec60db63", "abcdefghij"}, + {"ebf81ddcbe5bf13aaabdc4d65354fdf2044f38a7", "Discard medicine more than two years old."}, + {"e5dea09392dd886ca63531aaa00571dc07554bb6", "He who has a shady past knows that nice guys finish last."}, + {"45988f7234467b94e3e9494434c96ee3609d8f8f", "I wouldn't marry him with a ten foot pole."}, + {"55dee037eb7460d5a692d1ce11330b260e40c988", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, + {"b7bc5fb91080c7de6b582ea281f8a396d7c0aee8", "The days of the digital watch are numbered. -Tom Stoppard"}, + {"c3aed9358f7c77f523afe86135f06b95b3999797", "Nepal premier won't resign."}, + {"6e29d302bf6e3a5e4305ff318d983197d6906bb9", "For every action there is an equal and opposite government program."}, + {"597f6a540010f94c15d71806a99a2c8710e747bd", "His money is twice tainted: 'taint yours and 'taint mine."}, + {"6859733b2590a8a091cecf50086febc5ceef1e80", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, + {"514b2630ec089b8aee18795fc0cf1f4860cdacad", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, + {"c5ca0d4a7b6676fc7aa72caa41cc3d5df567ed69", "size: a.out: bad magic"}, + {"74c51fa9a04eadc8c1bbeaa7fc442f834b90a00a", "The major problem is with sendmail. -Mark Horton"}, + {"0b4c4ce5f52c3ad2821852a8dc00217fa18b8b66", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, + {"3ae7937dd790315beb0f48330e8642237c61550a", "If the enemy is within range, then so are you."}, + {"410a2b296df92b9a47412b13281df8f830a9f44b", "It's well we cannot hear the screams/That we create in others' dreams."}, + {"841e7c85ca1adcddbdd0187f1289acb5c642f7f5", "You remind me of a TV show, but that's all right: I watch it anyway."}, + {"163173b825d03b952601376b25212df66763e1db", "C is as portable as Stonehedge!!"}, + {"32b0377f2687eb88e22106f133c586ab314d5279", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, + {"0885aaf99b569542fd165fa44e322718f4a984e0", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, + {"6627d6904d71420b0bf3886ab629623538689f45", "How can you write a big system without C++? -Paul Glick"}, } func TestGolden(t *testing.T) { diff --git a/src/pkg/crypto/sha256/Makefile b/src/pkg/crypto/sha256/Makefile index 9efbc4792..97fe4d8e6 100644 --- a/src/pkg/crypto/sha256/Makefile +++ b/src/pkg/crypto/sha256/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=crypto/sha256 GOFILES=\ diff --git a/src/pkg/crypto/sha256/sha256.go b/src/pkg/crypto/sha256/sha256.go index df00a7298..57a8ffa0d 100644 --- a/src/pkg/crypto/sha256/sha256.go +++ b/src/pkg/crypto/sha256/sha256.go @@ -112,10 +112,7 @@ func (d *digest) Write(p []byte) (nn int, err os.Error) { n := _Block(d, p) p = p[n:] if len(p) > 0 { - for i, x := range p { - d.x[i] = x - } - d.nx = len(p) + d.nx = copy(d.x[:], p) } return } diff --git a/src/pkg/crypto/sha256/sha256_test.go b/src/pkg/crypto/sha256/sha256_test.go index d9b294487..42a3fa7a0 100644 --- a/src/pkg/crypto/sha256/sha256_test.go +++ b/src/pkg/crypto/sha256/sha256_test.go @@ -18,71 +18,71 @@ type sha256Test struct { } var golden = []sha256Test{ - sha256Test{"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""}, - sha256Test{"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", "a"}, - sha256Test{"fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603", "ab"}, - sha256Test{"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc"}, - sha256Test{"88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", "abcd"}, - sha256Test{"36bbe50ed96841d10443bcb670d6554f0a34b761be67ec9c4a8ad2c0c44ca42c", "abcde"}, - sha256Test{"bef57ec7f53a6d40beb640a780a639c83bc29ac8a9816f1fc6c5c6dcd93c4721", "abcdef"}, - sha256Test{"7d1a54127b222502f5b79b5fb0803061152a44f92b37e23c6527baf665d4da9a", "abcdefg"}, - sha256Test{"9c56cc51b374c3ba189210d5b6d4bf57790d351c96c47c02190ecf1e430635ab", "abcdefgh"}, - sha256Test{"19cc02f26df43cc571bc9ed7b0c4d29224a3ec229529221725ef76d021c8326f", "abcdefghi"}, - sha256Test{"72399361da6a7754fec986dca5b7cbaf1c810a28ded4abaf56b2106d06cb78b0", "abcdefghij"}, - sha256Test{"a144061c271f152da4d151034508fed1c138b8c976339de229c3bb6d4bbb4fce", "Discard medicine more than two years old."}, - sha256Test{"6dae5caa713a10ad04b46028bf6dad68837c581616a1589a265a11288d4bb5c4", "He who has a shady past knows that nice guys finish last."}, - sha256Test{"ae7a702a9509039ddbf29f0765e70d0001177914b86459284dab8b348c2dce3f", "I wouldn't marry him with a ten foot pole."}, - sha256Test{"6748450b01c568586715291dfa3ee018da07d36bb7ea6f180c1af6270215c64f", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, - sha256Test{"14b82014ad2b11f661b5ae6a99b75105c2ffac278cd071cd6c05832793635774", "The days of the digital watch are numbered. -Tom Stoppard"}, - sha256Test{"7102cfd76e2e324889eece5d6c41921b1e142a4ac5a2692be78803097f6a48d8", "Nepal premier won't resign."}, - sha256Test{"23b1018cd81db1d67983c5f7417c44da9deb582459e378d7a068552ea649dc9f", "For every action there is an equal and opposite government program."}, - sha256Test{"8001f190dfb527261c4cfcab70c98e8097a7a1922129bc4096950e57c7999a5a", "His money is twice tainted: 'taint yours and 'taint mine."}, - sha256Test{"8c87deb65505c3993eb24b7a150c4155e82eee6960cf0c3a8114ff736d69cad5", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, - sha256Test{"bfb0a67a19cdec3646498b2e0f751bddc41bba4b7f30081b0b932aad214d16d7", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, - sha256Test{"7f9a0b9bf56332e19f5a0ec1ad9c1425a153da1c624868fda44561d6b74daf36", "size: a.out: bad magic"}, - sha256Test{"b13f81b8aad9e3666879af19886140904f7f429ef083286195982a7588858cfc", "The major problem is with sendmail. -Mark Horton"}, - sha256Test{"b26c38d61519e894480c70c8374ea35aa0ad05b2ae3d6674eec5f52a69305ed4", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, - sha256Test{"049d5e26d4f10222cd841a119e38bd8d2e0d1129728688449575d4ff42b842c1", "If the enemy is within range, then so are you."}, - sha256Test{"0e116838e3cc1c1a14cd045397e29b4d087aa11b0853fc69ec82e90330d60949", "It's well we cannot hear the screams/That we create in others' dreams."}, - sha256Test{"4f7d8eb5bcf11de2a56b971021a444aa4eafd6ecd0f307b5109e4e776cd0fe46", "You remind me of a TV show, but that's all right: I watch it anyway."}, - sha256Test{"61c0cc4c4bd8406d5120b3fb4ebc31ce87667c162f29468b3c779675a85aebce", "C is as portable as Stonehedge!!"}, - sha256Test{"1fb2eb3688093c4a3f80cd87a5547e2ce940a4f923243a79a2a1e242220693ac", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, - sha256Test{"395585ce30617b62c80b93e8208ce866d4edc811a177fdb4b82d3911d8696423", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, - sha256Test{"4f9b189a13d030838269dce846b16a1ce9ce81fe63e65de2f636863336a98fe6", "How can you write a big system without C++? -Paul Glick"}, + {"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""}, + {"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", "a"}, + {"fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603", "ab"}, + {"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc"}, + {"88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", "abcd"}, + {"36bbe50ed96841d10443bcb670d6554f0a34b761be67ec9c4a8ad2c0c44ca42c", "abcde"}, + {"bef57ec7f53a6d40beb640a780a639c83bc29ac8a9816f1fc6c5c6dcd93c4721", "abcdef"}, + {"7d1a54127b222502f5b79b5fb0803061152a44f92b37e23c6527baf665d4da9a", "abcdefg"}, + {"9c56cc51b374c3ba189210d5b6d4bf57790d351c96c47c02190ecf1e430635ab", "abcdefgh"}, + {"19cc02f26df43cc571bc9ed7b0c4d29224a3ec229529221725ef76d021c8326f", "abcdefghi"}, + {"72399361da6a7754fec986dca5b7cbaf1c810a28ded4abaf56b2106d06cb78b0", "abcdefghij"}, + {"a144061c271f152da4d151034508fed1c138b8c976339de229c3bb6d4bbb4fce", "Discard medicine more than two years old."}, + {"6dae5caa713a10ad04b46028bf6dad68837c581616a1589a265a11288d4bb5c4", "He who has a shady past knows that nice guys finish last."}, + {"ae7a702a9509039ddbf29f0765e70d0001177914b86459284dab8b348c2dce3f", "I wouldn't marry him with a ten foot pole."}, + {"6748450b01c568586715291dfa3ee018da07d36bb7ea6f180c1af6270215c64f", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, + {"14b82014ad2b11f661b5ae6a99b75105c2ffac278cd071cd6c05832793635774", "The days of the digital watch are numbered. -Tom Stoppard"}, + {"7102cfd76e2e324889eece5d6c41921b1e142a4ac5a2692be78803097f6a48d8", "Nepal premier won't resign."}, + {"23b1018cd81db1d67983c5f7417c44da9deb582459e378d7a068552ea649dc9f", "For every action there is an equal and opposite government program."}, + {"8001f190dfb527261c4cfcab70c98e8097a7a1922129bc4096950e57c7999a5a", "His money is twice tainted: 'taint yours and 'taint mine."}, + {"8c87deb65505c3993eb24b7a150c4155e82eee6960cf0c3a8114ff736d69cad5", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, + {"bfb0a67a19cdec3646498b2e0f751bddc41bba4b7f30081b0b932aad214d16d7", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, + {"7f9a0b9bf56332e19f5a0ec1ad9c1425a153da1c624868fda44561d6b74daf36", "size: a.out: bad magic"}, + {"b13f81b8aad9e3666879af19886140904f7f429ef083286195982a7588858cfc", "The major problem is with sendmail. -Mark Horton"}, + {"b26c38d61519e894480c70c8374ea35aa0ad05b2ae3d6674eec5f52a69305ed4", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, + {"049d5e26d4f10222cd841a119e38bd8d2e0d1129728688449575d4ff42b842c1", "If the enemy is within range, then so are you."}, + {"0e116838e3cc1c1a14cd045397e29b4d087aa11b0853fc69ec82e90330d60949", "It's well we cannot hear the screams/That we create in others' dreams."}, + {"4f7d8eb5bcf11de2a56b971021a444aa4eafd6ecd0f307b5109e4e776cd0fe46", "You remind me of a TV show, but that's all right: I watch it anyway."}, + {"61c0cc4c4bd8406d5120b3fb4ebc31ce87667c162f29468b3c779675a85aebce", "C is as portable as Stonehedge!!"}, + {"1fb2eb3688093c4a3f80cd87a5547e2ce940a4f923243a79a2a1e242220693ac", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, + {"395585ce30617b62c80b93e8208ce866d4edc811a177fdb4b82d3911d8696423", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, + {"4f9b189a13d030838269dce846b16a1ce9ce81fe63e65de2f636863336a98fe6", "How can you write a big system without C++? -Paul Glick"}, } var golden224 = []sha256Test{ - sha256Test{"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", ""}, - sha256Test{"abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5", "a"}, - sha256Test{"db3cda86d4429a1d39c148989566b38f7bda0156296bd364ba2f878b", "ab"}, - sha256Test{"23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc"}, - sha256Test{"a76654d8e3550e9a2d67a0eeb6c67b220e5885eddd3fde135806e601", "abcd"}, - sha256Test{"bdd03d560993e675516ba5a50638b6531ac2ac3d5847c61916cfced6", "abcde"}, - sha256Test{"7043631cb415556a275a4ebecb802c74ee9f6153908e1792a90b6a98", "abcdef"}, - sha256Test{"d1884e711701ad81abe0c77a3b0ea12e19ba9af64077286c72fc602d", "abcdefg"}, - sha256Test{"17eb7d40f0356f8598e89eafad5f6c759b1f822975d9c9b737c8a517", "abcdefgh"}, - sha256Test{"aeb35915346c584db820d2de7af3929ffafef9222a9bcb26516c7334", "abcdefghi"}, - sha256Test{"d35e1e5af29ddb0d7e154357df4ad9842afee527c689ee547f753188", "abcdefghij"}, - sha256Test{"19297f1cef7ddc8a7e947f5c5a341e10f7245045e425db67043988d7", "Discard medicine more than two years old."}, - sha256Test{"0f10c2eb436251f777fbbd125e260d36aecf180411726c7c885f599a", "He who has a shady past knows that nice guys finish last."}, - sha256Test{"4d1842104919f314cad8a3cd20b3cba7e8ed3e7abed62b57441358f6", "I wouldn't marry him with a ten foot pole."}, - sha256Test{"a8ba85c6fe0c48fbffc72bbb2f03fcdbc87ae2dc7a56804d1590fb3b", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, - sha256Test{"5543fbab26e67e8885b1a852d567d1cb8b9bfe42e0899584c50449a9", "The days of the digital watch are numbered. -Tom Stoppard"}, - sha256Test{"65ca107390f5da9efa05d28e57b221657edc7e43a9a18fb15b053ddb", "Nepal premier won't resign."}, - sha256Test{"84953962be366305a9cc9b5cd16ed019edc37ac96c0deb3e12cca116", "For every action there is an equal and opposite government program."}, - sha256Test{"35a189ce987151dfd00b3577583cc6a74b9869eecf894459cb52038d", "His money is twice tainted: 'taint yours and 'taint mine."}, - sha256Test{"2fc333713983edfd4ef2c0da6fb6d6415afb94987c91e4069eb063e6", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, - sha256Test{"cbe32d38d577a1b355960a4bc3c659c2dc4670859a19777a875842c4", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, - sha256Test{"a2dc118ce959e027576413a7b440c875cdc8d40df9141d6ef78a57e1", "size: a.out: bad magic"}, - sha256Test{"d10787e24052bcff26dc484787a54ed819e4e4511c54890ee977bf81", "The major problem is with sendmail. -Mark Horton"}, - sha256Test{"62efcf16ab8a893acdf2f348aaf06b63039ff1bf55508c830532c9fb", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, - sha256Test{"3e9b7e4613c59f58665104c5fa86c272db5d3a2ff30df5bb194a5c99", "If the enemy is within range, then so are you."}, - sha256Test{"5999c208b8bdf6d471bb7c359ac5b829e73a8211dff686143a4e7f18", "It's well we cannot hear the screams/That we create in others' dreams."}, - sha256Test{"3b2d67ff54eabc4ef737b14edf87c64280ef582bcdf2a6d56908b405", "You remind me of a TV show, but that's all right: I watch it anyway."}, - sha256Test{"d0733595d20e4d3d6b5c565a445814d1bbb2fd08b9a3b8ffb97930c6", "C is as portable as Stonehedge!!"}, - sha256Test{"43fb8aeed8a833175c9295c1165415f98c866ef08a4922959d673507", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, - sha256Test{"ec18e66e93afc4fb1604bc2baedbfd20b44c43d76e65c0996d7851c6", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, - sha256Test{"86ed2eaa9c75ba98396e5c9fb2f679ecf0ea2ed1e0ee9ceecb4a9332", "How can you write a big system without C++? -Paul Glick"}, + {"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", ""}, + {"abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5", "a"}, + {"db3cda86d4429a1d39c148989566b38f7bda0156296bd364ba2f878b", "ab"}, + {"23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc"}, + {"a76654d8e3550e9a2d67a0eeb6c67b220e5885eddd3fde135806e601", "abcd"}, + {"bdd03d560993e675516ba5a50638b6531ac2ac3d5847c61916cfced6", "abcde"}, + {"7043631cb415556a275a4ebecb802c74ee9f6153908e1792a90b6a98", "abcdef"}, + {"d1884e711701ad81abe0c77a3b0ea12e19ba9af64077286c72fc602d", "abcdefg"}, + {"17eb7d40f0356f8598e89eafad5f6c759b1f822975d9c9b737c8a517", "abcdefgh"}, + {"aeb35915346c584db820d2de7af3929ffafef9222a9bcb26516c7334", "abcdefghi"}, + {"d35e1e5af29ddb0d7e154357df4ad9842afee527c689ee547f753188", "abcdefghij"}, + {"19297f1cef7ddc8a7e947f5c5a341e10f7245045e425db67043988d7", "Discard medicine more than two years old."}, + {"0f10c2eb436251f777fbbd125e260d36aecf180411726c7c885f599a", "He who has a shady past knows that nice guys finish last."}, + {"4d1842104919f314cad8a3cd20b3cba7e8ed3e7abed62b57441358f6", "I wouldn't marry him with a ten foot pole."}, + {"a8ba85c6fe0c48fbffc72bbb2f03fcdbc87ae2dc7a56804d1590fb3b", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, + {"5543fbab26e67e8885b1a852d567d1cb8b9bfe42e0899584c50449a9", "The days of the digital watch are numbered. -Tom Stoppard"}, + {"65ca107390f5da9efa05d28e57b221657edc7e43a9a18fb15b053ddb", "Nepal premier won't resign."}, + {"84953962be366305a9cc9b5cd16ed019edc37ac96c0deb3e12cca116", "For every action there is an equal and opposite government program."}, + {"35a189ce987151dfd00b3577583cc6a74b9869eecf894459cb52038d", "His money is twice tainted: 'taint yours and 'taint mine."}, + {"2fc333713983edfd4ef2c0da6fb6d6415afb94987c91e4069eb063e6", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, + {"cbe32d38d577a1b355960a4bc3c659c2dc4670859a19777a875842c4", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, + {"a2dc118ce959e027576413a7b440c875cdc8d40df9141d6ef78a57e1", "size: a.out: bad magic"}, + {"d10787e24052bcff26dc484787a54ed819e4e4511c54890ee977bf81", "The major problem is with sendmail. -Mark Horton"}, + {"62efcf16ab8a893acdf2f348aaf06b63039ff1bf55508c830532c9fb", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, + {"3e9b7e4613c59f58665104c5fa86c272db5d3a2ff30df5bb194a5c99", "If the enemy is within range, then so are you."}, + {"5999c208b8bdf6d471bb7c359ac5b829e73a8211dff686143a4e7f18", "It's well we cannot hear the screams/That we create in others' dreams."}, + {"3b2d67ff54eabc4ef737b14edf87c64280ef582bcdf2a6d56908b405", "You remind me of a TV show, but that's all right: I watch it anyway."}, + {"d0733595d20e4d3d6b5c565a445814d1bbb2fd08b9a3b8ffb97930c6", "C is as portable as Stonehedge!!"}, + {"43fb8aeed8a833175c9295c1165415f98c866ef08a4922959d673507", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, + {"ec18e66e93afc4fb1604bc2baedbfd20b44c43d76e65c0996d7851c6", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, + {"86ed2eaa9c75ba98396e5c9fb2f679ecf0ea2ed1e0ee9ceecb4a9332", "How can you write a big system without C++? -Paul Glick"}, } func TestGolden(t *testing.T) { diff --git a/src/pkg/crypto/sha512/Makefile b/src/pkg/crypto/sha512/Makefile index cf52732a4..2f7633fa3 100644 --- a/src/pkg/crypto/sha512/Makefile +++ b/src/pkg/crypto/sha512/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=crypto/sha512 GOFILES=\ diff --git a/src/pkg/crypto/sha512/sha512.go b/src/pkg/crypto/sha512/sha512.go index 21b030563..c3cda97d9 100644 --- a/src/pkg/crypto/sha512/sha512.go +++ b/src/pkg/crypto/sha512/sha512.go @@ -112,10 +112,7 @@ func (d *digest) Write(p []byte) (nn int, err os.Error) { n := _Block(d, p) p = p[n:] if len(p) > 0 { - for i, x := range p { - d.x[i] = x - } - d.nx = len(p) + d.nx = copy(d.x[:], p) } return } diff --git a/src/pkg/crypto/sha512/sha512_test.go b/src/pkg/crypto/sha512/sha512_test.go index 590cf1aec..dd116dc17 100644 --- a/src/pkg/crypto/sha512/sha512_test.go +++ b/src/pkg/crypto/sha512/sha512_test.go @@ -18,71 +18,71 @@ type sha512Test struct { } var golden = []sha512Test{ - sha512Test{"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", ""}, - sha512Test{"1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75", "a"}, - sha512Test{"2d408a0717ec188158278a796c689044361dc6fdde28d6f04973b80896e1823975cdbf12eb63f9e0591328ee235d80e9b5bf1aa6a44f4617ff3caf6400eb172d", "ab"}, - sha512Test{"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", "abc"}, - sha512Test{"d8022f2060ad6efd297ab73dcc5355c9b214054b0d1776a136a669d26a7d3b14f73aa0d0ebff19ee333368f0164b6419a96da49e3e481753e7e96b716bdccb6f", "abcd"}, - sha512Test{"878ae65a92e86cac011a570d4c30a7eaec442b85ce8eca0c2952b5e3cc0628c2e79d889ad4d5c7c626986d452dd86374b6ffaa7cd8b67665bef2289a5c70b0a1", "abcde"}, - sha512Test{"e32ef19623e8ed9d267f657a81944b3d07adbb768518068e88435745564e8d4150a0a703be2a7d88b61e3d390c2bb97e2d4c311fdc69d6b1267f05f59aa920e7", "abcdef"}, - sha512Test{"d716a4188569b68ab1b6dfac178e570114cdf0ea3a1cc0e31486c3e41241bc6a76424e8c37ab26f096fc85ef9886c8cb634187f4fddff645fb099f1ff54c6b8c", "abcdefg"}, - sha512Test{"a3a8c81bc97c2560010d7389bc88aac974a104e0e2381220c6e084c4dccd1d2d17d4f86db31c2a851dc80e6681d74733c55dcd03dd96f6062cdda12a291ae6ce", "abcdefgh"}, - sha512Test{"f22d51d25292ca1d0f68f69aedc7897019308cc9db46efb75a03dd494fc7f126c010e8ade6a00a0c1a5f1b75d81e0ed5a93ce98dc9b833db7839247b1d9c24fe", "abcdefghi"}, - sha512Test{"ef6b97321f34b1fea2169a7db9e1960b471aa13302a988087357c520be957ca119c3ba68e6b4982c019ec89de3865ccf6a3cda1fe11e59f98d99f1502c8b9745", "abcdefghij"}, - sha512Test{"2210d99af9c8bdecda1b4beff822136753d8342505ddce37f1314e2cdbb488c6016bdaa9bd2ffa513dd5de2e4b50f031393d8ab61f773b0e0130d7381e0f8a1d", "Discard medicine more than two years old."}, - sha512Test{"a687a8985b4d8d0a24f115fe272255c6afaf3909225838546159c1ed685c211a203796ae8ecc4c81a5b6315919b3a64f10713da07e341fcdbb08541bf03066ce", "He who has a shady past knows that nice guys finish last."}, - sha512Test{"8ddb0392e818b7d585ab22769a50df660d9f6d559cca3afc5691b8ca91b8451374e42bcdabd64589ed7c91d85f626596228a5c8572677eb98bc6b624befb7af8", "I wouldn't marry him with a ten foot pole."}, - sha512Test{"26ed8f6ca7f8d44b6a8a54ae39640fa8ad5c673f70ee9ce074ba4ef0d483eea00bab2f61d8695d6b34df9c6c48ae36246362200ed820448bdc03a720366a87c6", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, - sha512Test{"e5a14bf044be69615aade89afcf1ab0389d5fc302a884d403579d1386a2400c089b0dbb387ed0f463f9ee342f8244d5a38cfbc0e819da9529fbff78368c9a982", "The days of the digital watch are numbered. -Tom Stoppard"}, - sha512Test{"420a1faa48919e14651bed45725abe0f7a58e0f099424c4e5a49194946e38b46c1f8034b18ef169b2e31050d1648e0b982386595f7df47da4b6fd18e55333015", "Nepal premier won't resign."}, - sha512Test{"d926a863beadb20134db07683535c72007b0e695045876254f341ddcccde132a908c5af57baa6a6a9c63e6649bba0c213dc05fadcf9abccea09f23dcfb637fbe", "For every action there is an equal and opposite government program."}, - sha512Test{"9a98dd9bb67d0da7bf83da5313dff4fd60a4bac0094f1b05633690ffa7f6d61de9a1d4f8617937d560833a9aaa9ccafe3fd24db418d0e728833545cadd3ad92d", "His money is twice tainted: 'taint yours and 'taint mine."}, - sha512Test{"d7fde2d2351efade52f4211d3746a0780a26eec3df9b2ed575368a8a1c09ec452402293a8ea4eceb5a4f60064ea29b13cdd86918cd7a4faf366160b009804107", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, - sha512Test{"b0f35ffa2697359c33a56f5c0cf715c7aeed96da9905ca2698acadb08fbc9e669bf566b6bd5d61a3e86dc22999bcc9f2224e33d1d4f32a228cf9d0349e2db518", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, - sha512Test{"3d2e5f91778c9e66f7e061293aaa8a8fc742dd3b2e4f483772464b1144189b49273e610e5cccd7a81a19ca1fa70f16b10f1a100a4d8c1372336be8484c64b311", "size: a.out: bad magic"}, - sha512Test{"b2f68ff58ac015efb1c94c908b0d8c2bf06f491e4de8e6302c49016f7f8a33eac3e959856c7fddbc464de618701338a4b46f76dbfaf9a1e5262b5f40639771c7", "The major problem is with sendmail. -Mark Horton"}, - sha512Test{"d8c92db5fdf52cf8215e4df3b4909d29203ff4d00e9ad0b64a6a4e04dec5e74f62e7c35c7fb881bd5de95442123df8f57a489b0ae616bd326f84d10021121c57", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, - sha512Test{"19a9f8dc0a233e464e8566ad3ca9b91e459a7b8c4780985b015776e1bf239a19bc233d0556343e2b0a9bc220900b4ebf4f8bdf89ff8efeaf79602d6849e6f72e", "If the enemy is within range, then so are you."}, - sha512Test{"00b4c41f307bde87301cdc5b5ab1ae9a592e8ecbb2021dd7bc4b34e2ace60741cc362560bec566ba35178595a91932b8d5357e2c9cec92d393b0fa7831852476", "It's well we cannot hear the screams/That we create in others' dreams."}, - sha512Test{"91eccc3d5375fd026e4d6787874b1dce201cecd8a27dbded5065728cb2d09c58a3d467bb1faf353bf7ba567e005245d5321b55bc344f7c07b91cb6f26c959be7", "You remind me of a TV show, but that's all right: I watch it anyway."}, - sha512Test{"fabbbe22180f1f137cfdc9556d2570e775d1ae02a597ded43a72a40f9b485d500043b7be128fb9fcd982b83159a0d99aa855a9e7cc4240c00dc01a9bdf8218d7", "C is as portable as Stonehedge!!"}, - sha512Test{"2ecdec235c1fa4fc2a154d8fba1dddb8a72a1ad73838b51d792331d143f8b96a9f6fcb0f34d7caa351fe6d88771c4f105040e0392f06e0621689d33b2f3ba92e", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, - sha512Test{"7ad681f6f96f82f7abfa7ecc0334e8fa16d3dc1cdc45b60b7af43fe4075d2357c0c1d60e98350f1afb1f2fe7a4d7cd2ad55b88e458e06b73c40b437331f5dab4", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, - sha512Test{"833f9248ab4a3b9e5131f745fda1ffd2dd435b30e965957e78291c7ab73605fd1912b0794e5c233ab0a12d205a39778d19b83515d6a47003f19cdee51d98c7e0", "How can you write a big system without C++? -Paul Glick"}, + {"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", ""}, + {"1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75", "a"}, + {"2d408a0717ec188158278a796c689044361dc6fdde28d6f04973b80896e1823975cdbf12eb63f9e0591328ee235d80e9b5bf1aa6a44f4617ff3caf6400eb172d", "ab"}, + {"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", "abc"}, + {"d8022f2060ad6efd297ab73dcc5355c9b214054b0d1776a136a669d26a7d3b14f73aa0d0ebff19ee333368f0164b6419a96da49e3e481753e7e96b716bdccb6f", "abcd"}, + {"878ae65a92e86cac011a570d4c30a7eaec442b85ce8eca0c2952b5e3cc0628c2e79d889ad4d5c7c626986d452dd86374b6ffaa7cd8b67665bef2289a5c70b0a1", "abcde"}, + {"e32ef19623e8ed9d267f657a81944b3d07adbb768518068e88435745564e8d4150a0a703be2a7d88b61e3d390c2bb97e2d4c311fdc69d6b1267f05f59aa920e7", "abcdef"}, + {"d716a4188569b68ab1b6dfac178e570114cdf0ea3a1cc0e31486c3e41241bc6a76424e8c37ab26f096fc85ef9886c8cb634187f4fddff645fb099f1ff54c6b8c", "abcdefg"}, + {"a3a8c81bc97c2560010d7389bc88aac974a104e0e2381220c6e084c4dccd1d2d17d4f86db31c2a851dc80e6681d74733c55dcd03dd96f6062cdda12a291ae6ce", "abcdefgh"}, + {"f22d51d25292ca1d0f68f69aedc7897019308cc9db46efb75a03dd494fc7f126c010e8ade6a00a0c1a5f1b75d81e0ed5a93ce98dc9b833db7839247b1d9c24fe", "abcdefghi"}, + {"ef6b97321f34b1fea2169a7db9e1960b471aa13302a988087357c520be957ca119c3ba68e6b4982c019ec89de3865ccf6a3cda1fe11e59f98d99f1502c8b9745", "abcdefghij"}, + {"2210d99af9c8bdecda1b4beff822136753d8342505ddce37f1314e2cdbb488c6016bdaa9bd2ffa513dd5de2e4b50f031393d8ab61f773b0e0130d7381e0f8a1d", "Discard medicine more than two years old."}, + {"a687a8985b4d8d0a24f115fe272255c6afaf3909225838546159c1ed685c211a203796ae8ecc4c81a5b6315919b3a64f10713da07e341fcdbb08541bf03066ce", "He who has a shady past knows that nice guys finish last."}, + {"8ddb0392e818b7d585ab22769a50df660d9f6d559cca3afc5691b8ca91b8451374e42bcdabd64589ed7c91d85f626596228a5c8572677eb98bc6b624befb7af8", "I wouldn't marry him with a ten foot pole."}, + {"26ed8f6ca7f8d44b6a8a54ae39640fa8ad5c673f70ee9ce074ba4ef0d483eea00bab2f61d8695d6b34df9c6c48ae36246362200ed820448bdc03a720366a87c6", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, + {"e5a14bf044be69615aade89afcf1ab0389d5fc302a884d403579d1386a2400c089b0dbb387ed0f463f9ee342f8244d5a38cfbc0e819da9529fbff78368c9a982", "The days of the digital watch are numbered. -Tom Stoppard"}, + {"420a1faa48919e14651bed45725abe0f7a58e0f099424c4e5a49194946e38b46c1f8034b18ef169b2e31050d1648e0b982386595f7df47da4b6fd18e55333015", "Nepal premier won't resign."}, + {"d926a863beadb20134db07683535c72007b0e695045876254f341ddcccde132a908c5af57baa6a6a9c63e6649bba0c213dc05fadcf9abccea09f23dcfb637fbe", "For every action there is an equal and opposite government program."}, + {"9a98dd9bb67d0da7bf83da5313dff4fd60a4bac0094f1b05633690ffa7f6d61de9a1d4f8617937d560833a9aaa9ccafe3fd24db418d0e728833545cadd3ad92d", "His money is twice tainted: 'taint yours and 'taint mine."}, + {"d7fde2d2351efade52f4211d3746a0780a26eec3df9b2ed575368a8a1c09ec452402293a8ea4eceb5a4f60064ea29b13cdd86918cd7a4faf366160b009804107", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, + {"b0f35ffa2697359c33a56f5c0cf715c7aeed96da9905ca2698acadb08fbc9e669bf566b6bd5d61a3e86dc22999bcc9f2224e33d1d4f32a228cf9d0349e2db518", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, + {"3d2e5f91778c9e66f7e061293aaa8a8fc742dd3b2e4f483772464b1144189b49273e610e5cccd7a81a19ca1fa70f16b10f1a100a4d8c1372336be8484c64b311", "size: a.out: bad magic"}, + {"b2f68ff58ac015efb1c94c908b0d8c2bf06f491e4de8e6302c49016f7f8a33eac3e959856c7fddbc464de618701338a4b46f76dbfaf9a1e5262b5f40639771c7", "The major problem is with sendmail. -Mark Horton"}, + {"d8c92db5fdf52cf8215e4df3b4909d29203ff4d00e9ad0b64a6a4e04dec5e74f62e7c35c7fb881bd5de95442123df8f57a489b0ae616bd326f84d10021121c57", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, + {"19a9f8dc0a233e464e8566ad3ca9b91e459a7b8c4780985b015776e1bf239a19bc233d0556343e2b0a9bc220900b4ebf4f8bdf89ff8efeaf79602d6849e6f72e", "If the enemy is within range, then so are you."}, + {"00b4c41f307bde87301cdc5b5ab1ae9a592e8ecbb2021dd7bc4b34e2ace60741cc362560bec566ba35178595a91932b8d5357e2c9cec92d393b0fa7831852476", "It's well we cannot hear the screams/That we create in others' dreams."}, + {"91eccc3d5375fd026e4d6787874b1dce201cecd8a27dbded5065728cb2d09c58a3d467bb1faf353bf7ba567e005245d5321b55bc344f7c07b91cb6f26c959be7", "You remind me of a TV show, but that's all right: I watch it anyway."}, + {"fabbbe22180f1f137cfdc9556d2570e775d1ae02a597ded43a72a40f9b485d500043b7be128fb9fcd982b83159a0d99aa855a9e7cc4240c00dc01a9bdf8218d7", "C is as portable as Stonehedge!!"}, + {"2ecdec235c1fa4fc2a154d8fba1dddb8a72a1ad73838b51d792331d143f8b96a9f6fcb0f34d7caa351fe6d88771c4f105040e0392f06e0621689d33b2f3ba92e", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, + {"7ad681f6f96f82f7abfa7ecc0334e8fa16d3dc1cdc45b60b7af43fe4075d2357c0c1d60e98350f1afb1f2fe7a4d7cd2ad55b88e458e06b73c40b437331f5dab4", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, + {"833f9248ab4a3b9e5131f745fda1ffd2dd435b30e965957e78291c7ab73605fd1912b0794e5c233ab0a12d205a39778d19b83515d6a47003f19cdee51d98c7e0", "How can you write a big system without C++? -Paul Glick"}, } var golden384 = []sha512Test{ - sha512Test{"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", ""}, - sha512Test{"54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31", "a"}, - sha512Test{"c7be03ba5bcaa384727076db0018e99248e1a6e8bd1b9ef58a9ec9dd4eeebb3f48b836201221175befa74ddc3d35afdd", "ab"}, - sha512Test{"cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7", "abc"}, - sha512Test{"1165b3406ff0b52a3d24721f785462ca2276c9f454a116c2b2ba20171a7905ea5a026682eb659c4d5f115c363aa3c79b", "abcd"}, - sha512Test{"4c525cbeac729eaf4b4665815bc5db0c84fe6300068a727cf74e2813521565abc0ec57a37ee4d8be89d097c0d2ad52f0", "abcde"}, - sha512Test{"c6a4c65b227e7387b9c3e839d44869c4cfca3ef583dea64117859b808c1e3d8ae689e1e314eeef52a6ffe22681aa11f5", "abcdef"}, - sha512Test{"9f11fc131123f844c1226f429b6a0a6af0525d9f40f056c7fc16cdf1b06bda08e302554417a59fa7dcf6247421959d22", "abcdefg"}, - sha512Test{"9000cd7cada59d1d2eb82912f7f24e5e69cc5517f68283b005fa27c285b61e05edf1ad1a8a9bded6fd29eb87d75ad806", "abcdefgh"}, - sha512Test{"ef54915b60cf062b8dd0c29ae3cad69abe6310de63ac081f46ef019c5c90897caefd79b796cfa81139788a260ded52df", "abcdefghi"}, - sha512Test{"a12070030a02d86b0ddacd0d3a5b598344513d0a051e7355053e556a0055489c1555399b03342845c4adde2dc44ff66c", "abcdefghij"}, - sha512Test{"86f58ec2d74d1b7f8eb0c2ff0967316699639e8d4eb129de54bdf34c96cdbabe200d052149f2dd787f43571ba74670d4", "Discard medicine more than two years old."}, - sha512Test{"ae4a2b639ca9bfa04b1855d5a05fe7f230994f790891c6979103e2605f660c4c1262a48142dcbeb57a1914ba5f7c3fa7", "He who has a shady past knows that nice guys finish last."}, - sha512Test{"40ae213df6436eca952aa6841886fcdb82908ef1576a99c8f49bb9dd5023169f7c53035abdda0b54c302f4974e2105e7", "I wouldn't marry him with a ten foot pole."}, - sha512Test{"e7cf8b873c9bc950f06259aa54309f349cefa72c00d597aebf903e6519a50011dfe355afff064a10701c705693848df9", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, - sha512Test{"c3d4f0f4047181c7d39d34703365f7bf70207183caf2c2f6145f04da895ef69124d9cdeb635da636c3a474e61024e29b", "The days of the digital watch are numbered. -Tom Stoppard"}, - sha512Test{"a097aab567e167d5cf93676ed73252a69f9687cb3179bb2d27c9878119e94bf7b7c4b58dc90582edfaf66e11388ed714", "Nepal premier won't resign."}, - sha512Test{"5026ca45c41fc64712eb65065da92f6467541c78f8966d3fe2c8e3fb769a3ec14215f819654b47bd64f7f0eac17184f3", "For every action there is an equal and opposite government program."}, - sha512Test{"ac1cc0f5ac8d5f5514a7b738ac322b7fb52a161b449c3672e9b6a6ad1a5e4b26b001cf3bad24c56598676ca17d4b445a", "His money is twice tainted: 'taint yours and 'taint mine."}, - sha512Test{"722d10c5de371ec0c8c4b5247ac8a5f1d240d68c73f8da13d8b25f0166d6f309bf9561979a111a0049405771d201941a", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, - sha512Test{"dc2d3ea18bfa10549c63bf2b75b39b5167a80c12aff0e05443168ea87ff149fb0eda5e0bd234eb5d48c7d02ffc5807f1", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, - sha512Test{"1d67c969e2a945ae5346d2139760261504d4ba164c522443afe19ef3e29b152a4c52445489cfc9d7215e5a450e8e1e4e", "size: a.out: bad magic"}, - sha512Test{"5ff8e075e465646e7b73ef36d812c6e9f7d60fa6ea0e533e5569b4f73cde53cdd2cc787f33540af57cca3fe467d32fe0", "The major problem is with sendmail. -Mark Horton"}, - sha512Test{"5bd0a997a67c9ae1979a894eb0cde403dde003c9b6f2c03cf21925c42ff4e1176e6df1ca005381612ef18457b9b7ec3b", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, - sha512Test{"1eee6da33e7e54fc5be52ae23b94b16ba4d2a947ae4505c6a3edfc7401151ea5205ac01b669b56f27d8ef7f175ed7762", "If the enemy is within range, then so are you."}, - sha512Test{"76b06e9dea66bfbb1a96029426dc0dfd7830bd297eb447ff5358d94a87cd00c88b59df2493fef56ecbb5231073892ea9", "It's well we cannot hear the screams/That we create in others' dreams."}, - sha512Test{"12acaf21452cff586143e3f5db0bfdf7802c057e1adf2a619031c4e1b0ccc4208cf6cef8fe722bbaa2fb46a30d9135d8", "You remind me of a TV show, but that's all right: I watch it anyway."}, - sha512Test{"0fc23d7f4183efd186f0bc4fc5db867e026e2146b06cb3d52f4bdbd57d1740122caa853b41868b197b2ac759db39df88", "C is as portable as Stonehedge!!"}, - sha512Test{"bc805578a7f85d34a86a32976e1c34fe65cf815186fbef76f46ef99cda10723f971f3f1464d488243f5e29db7488598d", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, - sha512Test{"b23918399a12ebf4431559eec3813eaf7412e875fd7464f16d581e473330842d2e96c6be49a7ce3f9bb0b8bc0fcbe0fe", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, - sha512Test{"1764b700eb1ead52a2fc33cc28975c2180f1b8faa5038d94cffa8d78154aab16e91dd787e7b0303948ebed62561542c8", "How can you write a big system without C++? -Paul Glick"}, + {"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", ""}, + {"54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31", "a"}, + {"c7be03ba5bcaa384727076db0018e99248e1a6e8bd1b9ef58a9ec9dd4eeebb3f48b836201221175befa74ddc3d35afdd", "ab"}, + {"cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7", "abc"}, + {"1165b3406ff0b52a3d24721f785462ca2276c9f454a116c2b2ba20171a7905ea5a026682eb659c4d5f115c363aa3c79b", "abcd"}, + {"4c525cbeac729eaf4b4665815bc5db0c84fe6300068a727cf74e2813521565abc0ec57a37ee4d8be89d097c0d2ad52f0", "abcde"}, + {"c6a4c65b227e7387b9c3e839d44869c4cfca3ef583dea64117859b808c1e3d8ae689e1e314eeef52a6ffe22681aa11f5", "abcdef"}, + {"9f11fc131123f844c1226f429b6a0a6af0525d9f40f056c7fc16cdf1b06bda08e302554417a59fa7dcf6247421959d22", "abcdefg"}, + {"9000cd7cada59d1d2eb82912f7f24e5e69cc5517f68283b005fa27c285b61e05edf1ad1a8a9bded6fd29eb87d75ad806", "abcdefgh"}, + {"ef54915b60cf062b8dd0c29ae3cad69abe6310de63ac081f46ef019c5c90897caefd79b796cfa81139788a260ded52df", "abcdefghi"}, + {"a12070030a02d86b0ddacd0d3a5b598344513d0a051e7355053e556a0055489c1555399b03342845c4adde2dc44ff66c", "abcdefghij"}, + {"86f58ec2d74d1b7f8eb0c2ff0967316699639e8d4eb129de54bdf34c96cdbabe200d052149f2dd787f43571ba74670d4", "Discard medicine more than two years old."}, + {"ae4a2b639ca9bfa04b1855d5a05fe7f230994f790891c6979103e2605f660c4c1262a48142dcbeb57a1914ba5f7c3fa7", "He who has a shady past knows that nice guys finish last."}, + {"40ae213df6436eca952aa6841886fcdb82908ef1576a99c8f49bb9dd5023169f7c53035abdda0b54c302f4974e2105e7", "I wouldn't marry him with a ten foot pole."}, + {"e7cf8b873c9bc950f06259aa54309f349cefa72c00d597aebf903e6519a50011dfe355afff064a10701c705693848df9", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, + {"c3d4f0f4047181c7d39d34703365f7bf70207183caf2c2f6145f04da895ef69124d9cdeb635da636c3a474e61024e29b", "The days of the digital watch are numbered. -Tom Stoppard"}, + {"a097aab567e167d5cf93676ed73252a69f9687cb3179bb2d27c9878119e94bf7b7c4b58dc90582edfaf66e11388ed714", "Nepal premier won't resign."}, + {"5026ca45c41fc64712eb65065da92f6467541c78f8966d3fe2c8e3fb769a3ec14215f819654b47bd64f7f0eac17184f3", "For every action there is an equal and opposite government program."}, + {"ac1cc0f5ac8d5f5514a7b738ac322b7fb52a161b449c3672e9b6a6ad1a5e4b26b001cf3bad24c56598676ca17d4b445a", "His money is twice tainted: 'taint yours and 'taint mine."}, + {"722d10c5de371ec0c8c4b5247ac8a5f1d240d68c73f8da13d8b25f0166d6f309bf9561979a111a0049405771d201941a", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, + {"dc2d3ea18bfa10549c63bf2b75b39b5167a80c12aff0e05443168ea87ff149fb0eda5e0bd234eb5d48c7d02ffc5807f1", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, + {"1d67c969e2a945ae5346d2139760261504d4ba164c522443afe19ef3e29b152a4c52445489cfc9d7215e5a450e8e1e4e", "size: a.out: bad magic"}, + {"5ff8e075e465646e7b73ef36d812c6e9f7d60fa6ea0e533e5569b4f73cde53cdd2cc787f33540af57cca3fe467d32fe0", "The major problem is with sendmail. -Mark Horton"}, + {"5bd0a997a67c9ae1979a894eb0cde403dde003c9b6f2c03cf21925c42ff4e1176e6df1ca005381612ef18457b9b7ec3b", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, + {"1eee6da33e7e54fc5be52ae23b94b16ba4d2a947ae4505c6a3edfc7401151ea5205ac01b669b56f27d8ef7f175ed7762", "If the enemy is within range, then so are you."}, + {"76b06e9dea66bfbb1a96029426dc0dfd7830bd297eb447ff5358d94a87cd00c88b59df2493fef56ecbb5231073892ea9", "It's well we cannot hear the screams/That we create in others' dreams."}, + {"12acaf21452cff586143e3f5db0bfdf7802c057e1adf2a619031c4e1b0ccc4208cf6cef8fe722bbaa2fb46a30d9135d8", "You remind me of a TV show, but that's all right: I watch it anyway."}, + {"0fc23d7f4183efd186f0bc4fc5db867e026e2146b06cb3d52f4bdbd57d1740122caa853b41868b197b2ac759db39df88", "C is as portable as Stonehedge!!"}, + {"bc805578a7f85d34a86a32976e1c34fe65cf815186fbef76f46ef99cda10723f971f3f1464d488243f5e29db7488598d", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, + {"b23918399a12ebf4431559eec3813eaf7412e875fd7464f16d581e473330842d2e96c6be49a7ce3f9bb0b8bc0fcbe0fe", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, + {"1764b700eb1ead52a2fc33cc28975c2180f1b8faa5038d94cffa8d78154aab16e91dd787e7b0303948ebed62561542c8", "How can you write a big system without C++? -Paul Glick"}, } func TestGolden(t *testing.T) { diff --git a/src/pkg/crypto/subtle/Makefile b/src/pkg/crypto/subtle/Makefile index fa5f7ef42..08d8bbfa0 100644 --- a/src/pkg/crypto/subtle/Makefile +++ b/src/pkg/crypto/subtle/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=crypto/subtle GOFILES=\ diff --git a/src/pkg/crypto/subtle/constant_time_test.go b/src/pkg/crypto/subtle/constant_time_test.go index 25962b9ae..b28b73581 100644 --- a/src/pkg/crypto/subtle/constant_time_test.go +++ b/src/pkg/crypto/subtle/constant_time_test.go @@ -15,9 +15,9 @@ type TestConstantTimeCompareStruct struct { } var testConstandTimeCompareData = []TestConstantTimeCompareStruct{ - TestConstantTimeCompareStruct{[]byte{}, []byte{}, 1}, - TestConstantTimeCompareStruct{[]byte{0x11}, []byte{0x11}, 1}, - TestConstantTimeCompareStruct{[]byte{0x12}, []byte{0x11}, 0}, + {[]byte{}, []byte{}, 1}, + {[]byte{0x11}, []byte{0x11}, 1}, + {[]byte{0x12}, []byte{0x11}, 0}, } func TestConstantTimeCompare(t *testing.T) { @@ -34,11 +34,11 @@ type TestConstantTimeByteEqStruct struct { } var testConstandTimeByteEqData = []TestConstantTimeByteEqStruct{ - TestConstantTimeByteEqStruct{0, 0, 1}, - TestConstantTimeByteEqStruct{0, 1, 0}, - TestConstantTimeByteEqStruct{1, 0, 0}, - TestConstantTimeByteEqStruct{0xff, 0xff, 1}, - TestConstantTimeByteEqStruct{0xff, 0xfe, 0}, + {0, 0, 1}, + {0, 1, 0}, + {1, 0, 0}, + {0xff, 0xff, 1}, + {0xff, 0xfe, 0}, } func byteEq(a, b uint8) int { diff --git a/src/pkg/crypto/tls/Makefile b/src/pkg/crypto/tls/Makefile index 5e25bd43a..f8ec1511a 100644 --- a/src/pkg/crypto/tls/Makefile +++ b/src/pkg/crypto/tls/Makefile @@ -2,17 +2,19 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=crypto/tls GOFILES=\ alert.go\ ca_set.go\ + cipher_suites.go\ common.go\ conn.go\ handshake_client.go\ handshake_messages.go\ handshake_server.go\ + key_agreement.go\ prf.go\ tls.go\ diff --git a/src/pkg/crypto/tls/ca_set.go b/src/pkg/crypto/tls/ca_set.go index 7f7566e46..ae00ac558 100644 --- a/src/pkg/crypto/tls/ca_set.go +++ b/src/pkg/crypto/tls/ca_set.go @@ -7,32 +7,57 @@ package tls import ( "crypto/x509" "encoding/pem" + "strings" ) // A CASet is a set of certificates. type CASet struct { - bySubjectKeyId map[string]*x509.Certificate - byName map[string]*x509.Certificate + bySubjectKeyId map[string][]*x509.Certificate + byName map[string][]*x509.Certificate } +// NewCASet returns a new, empty CASet. func NewCASet() *CASet { return &CASet{ - make(map[string]*x509.Certificate), - make(map[string]*x509.Certificate), + make(map[string][]*x509.Certificate), + make(map[string][]*x509.Certificate), } } func nameToKey(name *x509.Name) string { - return name.Country + "/" + name.Organization + "/" + name.OrganizationalUnit + "/" + name.CommonName + return strings.Join(name.Country, ",") + "/" + strings.Join(name.Organization, ",") + "/" + strings.Join(name.OrganizationalUnit, ",") + "/" + name.CommonName } -// FindParent attempts to find the certificate in s which signs the given -// certificate. If no such certificate can be found, it returns nil. -func (s *CASet) FindParent(cert *x509.Certificate) (parent *x509.Certificate) { +// FindVerifiedParent attempts to find the certificate in s which has signed +// the given certificate. If no such certificate can be found or the signature +// doesn't match, it returns nil. +func (s *CASet) FindVerifiedParent(cert *x509.Certificate) (parent *x509.Certificate) { + var candidates []*x509.Certificate + if len(cert.AuthorityKeyId) > 0 { - return s.bySubjectKeyId[string(cert.AuthorityKeyId)] + candidates = s.bySubjectKeyId[string(cert.AuthorityKeyId)] + } + if len(candidates) == 0 { + candidates = s.byName[nameToKey(&cert.Issuer)] + } + + for _, c := range candidates { + if cert.CheckSignatureFrom(c) == nil { + return c + } } - return s.byName[nameToKey(&cert.Issuer)] + + return nil +} + +// AddCert adds a certificate to the set +func (s *CASet) AddCert(cert *x509.Certificate) { + if len(cert.SubjectKeyId) > 0 { + keyId := string(cert.SubjectKeyId) + s.bySubjectKeyId[keyId] = append(s.bySubjectKeyId[keyId], cert) + } + name := nameToKey(&cert.Subject) + s.byName[name] = append(s.byName[name], cert) } // SetFromPEM attempts to parse a series of PEM encoded root certificates. It @@ -56,10 +81,7 @@ func (s *CASet) SetFromPEM(pemCerts []byte) (ok bool) { continue } - if len(cert.SubjectKeyId) > 0 { - s.bySubjectKeyId[string(cert.SubjectKeyId)] = cert - } - s.byName[nameToKey(&cert.Subject)] = cert + s.AddCert(cert) ok = true } diff --git a/src/pkg/crypto/tls/cipher_suites.go b/src/pkg/crypto/tls/cipher_suites.go new file mode 100644 index 000000000..bc7b0d32f --- /dev/null +++ b/src/pkg/crypto/tls/cipher_suites.go @@ -0,0 +1,102 @@ +// 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 tls + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/rc4" + "crypto/x509" + "hash" + "os" +) + +// a keyAgreement implements the client and server side of a TLS key agreement +// protocol by generating and processing key exchange messages. +type keyAgreement interface { + // On the server side, the first two methods are called in order. + + // In the case that the key agreement protocol doesn't use a + // ServerKeyExchange message, generateServerKeyExchange can return nil, + // nil. + generateServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, os.Error) + processClientKeyExchange(*Config, *clientKeyExchangeMsg) ([]byte, os.Error) + + // On the client side, the next two methods are called in order. + + // This method may not be called if the server doesn't send a + // ServerKeyExchange message. + processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) os.Error + generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, os.Error) +} + +// A cipherSuite is a specific combination of key agreement, cipher and MAC +// function. All cipher suites currently assume RSA key agreement. +type cipherSuite struct { + // the lengths, in bytes, of the key material needed for each component. + keyLen int + macLen int + ivLen int + ka func() keyAgreement + // If elliptic is set, a server will only consider this ciphersuite if + // the ClientHello indicated that the client supports an elliptic curve + // and point format that we can handle. + elliptic bool + cipher func(key, iv []byte, isRead bool) interface{} + mac func(macKey []byte) hash.Hash +} + +var cipherSuites = map[uint16]*cipherSuite{ + TLS_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, rsaKA, false, cipherRC4, hmacSHA1}, + TLS_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, rsaKA, false, cipherAES, hmacSHA1}, + TLS_ECDHE_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, ecdheRSAKA, true, cipherRC4, hmacSHA1}, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, ecdheRSAKA, true, cipherAES, hmacSHA1}, +} + +func cipherRC4(key, iv []byte, isRead bool) interface{} { + cipher, _ := rc4.NewCipher(key) + return cipher +} + +func cipherAES(key, iv []byte, isRead bool) interface{} { + block, _ := aes.NewCipher(key) + if isRead { + return cipher.NewCBCDecrypter(block, iv) + } + return cipher.NewCBCEncrypter(block, iv) +} + +func hmacSHA1(key []byte) hash.Hash { + return hmac.NewSHA1(key) +} + +func rsaKA() keyAgreement { + return rsaKeyAgreement{} +} + +func ecdheRSAKA() keyAgreement { + return new(ecdheRSAKeyAgreement) +} + +// mutualCipherSuite returns a cipherSuite and its id given a list of supported +// ciphersuites and the id requested by the peer. +func mutualCipherSuite(have []uint16, want uint16) (suite *cipherSuite, id uint16) { + for _, id := range have { + if id == want { + return cipherSuites[id], id + } + } + return +} + +// A list of the possible cipher suite ids. Taken from +// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml +const ( + TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 + TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f + TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011 + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013 +) diff --git a/src/pkg/crypto/tls/common.go b/src/pkg/crypto/tls/common.go index 56c22cf7d..7135f3d0f 100644 --- a/src/pkg/crypto/tls/common.go +++ b/src/pkg/crypto/tls/common.go @@ -9,7 +9,7 @@ import ( "crypto/rsa" "io" "io/ioutil" - "once" + "sync" "time" ) @@ -20,7 +20,7 @@ const ( maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB) minVersion = 0x0301 // minimum supported version - TLS 1.0 - maxVersion = 0x0302 // maximum supported version - TLS 1.1 + maxVersion = 0x0301 // maximum supported version - TLS 1.0 ) // TLS record types. @@ -35,35 +35,65 @@ const ( // TLS handshake message types. const ( - typeClientHello uint8 = 1 - typeServerHello uint8 = 2 - typeCertificate uint8 = 11 - typeServerHelloDone uint8 = 14 - typeClientKeyExchange uint8 = 16 - typeFinished uint8 = 20 - typeNextProtocol uint8 = 67 // Not IANA assigned + typeClientHello uint8 = 1 + typeServerHello uint8 = 2 + typeCertificate uint8 = 11 + typeServerKeyExchange uint8 = 12 + typeCertificateRequest uint8 = 13 + typeServerHelloDone uint8 = 14 + typeCertificateVerify uint8 = 15 + typeClientKeyExchange uint8 = 16 + typeFinished uint8 = 20 + typeCertificateStatus uint8 = 22 + typeNextProtocol uint8 = 67 // Not IANA assigned ) -// TLS cipher suites. +// TLS compression types. +const ( + compressionNone uint8 = 0 +) + +// TLS extension numbers var ( - TLS_RSA_WITH_RC4_128_SHA uint16 = 5 + extensionServerName uint16 = 0 + extensionStatusRequest uint16 = 5 + extensionSupportedCurves uint16 = 10 + extensionSupportedPoints uint16 = 11 + extensionNextProtoNeg uint16 = 13172 // not IANA assigned ) -// TLS compression types. +// TLS Elliptic Curves +// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8 var ( - compressionNone uint8 = 0 + curveP256 uint16 = 23 + curveP384 uint16 = 24 + curveP521 uint16 = 25 ) -// TLS extension numbers +// TLS Elliptic Curve Point Formats +// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9 var ( - extensionServerName uint16 = 0 - extensionNextProtoNeg uint16 = 13172 // not IANA assigned + pointFormatUncompressed uint8 = 0 +) + +// TLS CertificateStatusType (RFC 3546) +const ( + statusTypeOCSP uint8 = 1 ) +// Certificate types (for certificateRequestMsg) +const ( + certTypeRSASign = 1 // A certificate containing an RSA key + certTypeDSSSign = 2 // A certificate containing a DSA key + certTypeRSAFixedDH = 3 // A certificate containing a static DH key + certTypeDSSFixedDH = 4 // A certficiate containing a static DH key + // Rest of these are reserved by the TLS spec +) + +// ConnectionState records basic TLS details about the connection. type ConnectionState struct { HandshakeComplete bool - CipherSuite string - Error alert + CipherSuite uint16 NegotiatedProtocol string } @@ -71,16 +101,76 @@ type ConnectionState struct { // has been passed to a TLS function it must not be modified. type Config struct { // Rand provides the source of entropy for nonces and RSA blinding. + // If Rand is nil, TLS uses the cryptographic random reader in package + // crypto/rand. Rand io.Reader + // Time returns the current time as the number of seconds since the epoch. - Time func() int64 + // If Time is nil, TLS uses the system time.Seconds. + Time func() int64 + + // Certificates contains one or more certificate chains + // to present to the other side of the connection. + // Server configurations must include at least one certificate. Certificates []Certificate - RootCAs *CASet + + // RootCAs defines the set of root certificate authorities + // that clients use when verifying server certificates. + // If RootCAs is nil, TLS uses the host's root CA set. + RootCAs *CASet + // NextProtos is a list of supported, application level protocols. // Currently only server-side handling is supported. NextProtos []string + + // ServerName is included in the client's handshake to support virtual + // hosting. + ServerName string + + // AuthenticateClient controls whether a server will request a certificate + // from the client. It does not require that the client send a + // certificate nor does it require that the certificate sent be + // anything more than self-signed. + AuthenticateClient bool + + // CipherSuites is a list of supported cipher suites. If CipherSuites + // is nil, TLS uses a list of suites supported by the implementation. + CipherSuites []uint16 +} + +func (c *Config) rand() io.Reader { + r := c.Rand + if r == nil { + return rand.Reader + } + return r +} + +func (c *Config) time() int64 { + t := c.Time + if t == nil { + t = time.Seconds + } + return t() +} + +func (c *Config) rootCAs() *CASet { + s := c.RootCAs + if s == nil { + s = defaultRoots() + } + return s +} + +func (c *Config) cipherSuites() []uint16 { + s := c.CipherSuites + if s == nil { + s = defaultCipherSuites() + } + return s } +// A Certificate is a chain of one or more certificates, leaf first. type Certificate struct { Certificate [][]byte PrivateKey *rsa.PrivateKey @@ -98,11 +188,6 @@ type handshakeMessage interface { unmarshal([]byte) bool } -type encryptor interface { - // XORKeyStream xors the contents of the slice with bytes from the key stream. - XORKeyStream(buf []byte) -} - // mutualVersion returns the protocol version to use given the advertised // version of the peer. func mutualVersion(vers uint16) (uint16, bool) { @@ -115,12 +200,10 @@ func mutualVersion(vers uint16) (uint16, bool) { return vers, true } -// The defaultConfig is used in place of a nil *Config in the TLS server and client. -var varDefaultConfig *Config +var emptyConfig Config func defaultConfig() *Config { - once.Do(initDefaultConfig) - return varDefaultConfig + return &emptyConfig } // Possible certificate files; stop after finding one. @@ -132,7 +215,26 @@ var certFiles = []string{ "/usr/share/curl/curl-ca-bundle.crt", // OS X } -func initDefaultConfig() { +var once sync.Once + +func defaultRoots() *CASet { + once.Do(initDefaults) + return varDefaultRoots +} + +func defaultCipherSuites() []uint16 { + once.Do(initDefaults) + return varDefaultCipherSuites +} + +func initDefaults() { + initDefaultRoots() + initDefaultCipherSuites() +} + +var varDefaultRoots *CASet + +func initDefaultRoots() { roots := NewCASet() for _, file := range certFiles { data, err := ioutil.ReadFile(file) @@ -141,10 +243,16 @@ func initDefaultConfig() { break } } + varDefaultRoots = roots +} + +var varDefaultCipherSuites []uint16 - varDefaultConfig = &Config{ - Rand: rand.Reader, - Time: time.Seconds, - RootCAs: roots, +func initDefaultCipherSuites() { + varDefaultCipherSuites = make([]uint16, len(cipherSuites)) + i := 0 + for id, _ := range cipherSuites { + varDefaultCipherSuites[i] = id + i++ } } diff --git a/src/pkg/crypto/tls/conn.go b/src/pkg/crypto/tls/conn.go index 0798e26f6..d203e8d51 100644 --- a/src/pkg/crypto/tls/conn.go +++ b/src/pkg/crypto/tls/conn.go @@ -1,10 +1,16 @@ +// 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. + // TLS low level connection and record layer package tls import ( "bytes" + "crypto/cipher" "crypto/subtle" + "crypto/x509" "hash" "io" "net" @@ -26,6 +32,8 @@ type Conn struct { config *Config // configuration passed to constructor handshakeComplete bool cipherSuite uint16 + ocspResponse []byte // stapled OCSP response + peerCertificates []*x509.Certificate clientProtocol string @@ -96,31 +104,31 @@ func (c *Conn) SetWriteTimeout(nsec int64) os.Error { // connection, either sending or receiving. type halfConn struct { sync.Mutex - crypt encryptor // encryption state - mac hash.Hash // MAC algorithm - seq [8]byte // 64-bit sequence number - bfree *block // list of free blocks + cipher interface{} // cipher algorithm + mac hash.Hash // MAC algorithm + seq [8]byte // 64-bit sequence number + bfree *block // list of free blocks - nextCrypt encryptor // next encryption state - nextMac hash.Hash // next MAC algorithm + nextCipher interface{} // next encryption state + nextMac hash.Hash // next MAC algorithm } // prepareCipherSpec sets the encryption and MAC states // that a subsequent changeCipherSpec will use. -func (hc *halfConn) prepareCipherSpec(crypt encryptor, mac hash.Hash) { - hc.nextCrypt = crypt +func (hc *halfConn) prepareCipherSpec(cipher interface{}, mac hash.Hash) { + hc.nextCipher = cipher hc.nextMac = mac } // changeCipherSpec changes the encryption and MAC states // to the ones previously passed to prepareCipherSpec. func (hc *halfConn) changeCipherSpec() os.Error { - if hc.nextCrypt == nil { + if hc.nextCipher == nil { return alertInternalError } - hc.crypt = hc.nextCrypt + hc.cipher = hc.nextCipher hc.mac = hc.nextMac - hc.nextCrypt = nil + hc.nextCipher = nil hc.nextMac = nil return nil } @@ -147,27 +155,102 @@ func (hc *halfConn) resetSeq() { } } +// removePadding returns an unpadded slice, in constant time, which is a prefix +// of the input. It also returns a byte which is equal to 255 if the padding +// was valid and 0 otherwise. See RFC 2246, section 6.2.3.2 +func removePadding(payload []byte) ([]byte, byte) { + if len(payload) < 1 { + return payload, 0 + } + + paddingLen := payload[len(payload)-1] + t := uint(len(payload)-1) - uint(paddingLen) + // if len(payload) >= (paddingLen - 1) then the MSB of t is zero + good := byte(int32(^t) >> 31) + + toCheck := 255 // the maximum possible padding length + // The length of the padded data is public, so we can use an if here + if toCheck+1 > len(payload) { + toCheck = len(payload) - 1 + } + + for i := 0; i < toCheck; i++ { + t := uint(paddingLen) - uint(i) + // if i <= paddingLen then the MSB of t is zero + mask := byte(int32(^t) >> 31) + b := payload[len(payload)-1-i] + good &^= mask&paddingLen ^ mask&b + } + + // We AND together the bits of good and replicate the result across + // all the bits. + good &= good << 4 + good &= good << 2 + good &= good << 1 + good = uint8(int8(good) >> 7) + + toRemove := good&paddingLen + 1 + return payload[:len(payload)-int(toRemove)], good +} + +func roundUp(a, b int) int { + return a + (b-a%b)%b +} + // decrypt checks and strips the mac and decrypts the data in b. func (hc *halfConn) decrypt(b *block) (bool, alert) { // pull out payload payload := b.data[recordHeaderLen:] + macSize := 0 + if hc.mac != nil { + macSize = hc.mac.Size() + } + + paddingGood := byte(255) + // decrypt - if hc.crypt != nil { - hc.crypt.XORKeyStream(payload) + if hc.cipher != nil { + switch c := hc.cipher.(type) { + case cipher.Stream: + c.XORKeyStream(payload, payload) + case cipher.BlockMode: + blockSize := c.BlockSize() + + if len(payload)%blockSize != 0 || len(payload) < roundUp(macSize+1, blockSize) { + return false, alertBadRecordMAC + } + + c.CryptBlocks(payload, payload) + payload, paddingGood = removePadding(payload) + b.resize(recordHeaderLen + len(payload)) + + // note that we still have a timing side-channel in the + // MAC check, below. An attacker can align the record + // so that a correct padding will cause one less hash + // block to be calculated. Then they can iteratively + // decrypt a record by breaking each byte. See + // "Password Interception in a SSL/TLS Channel", Brice + // Canvel et al. + // + // However, our behaviour matches OpenSSL, so we leak + // only as much as they do. + default: + panic("unknown cipher type") + } } // check, strip mac if hc.mac != nil { - if len(payload) < hc.mac.Size() { + if len(payload) < macSize { return false, alertBadRecordMAC } // strip mac off payload, b.data - n := len(payload) - hc.mac.Size() + n := len(payload) - macSize b.data[3] = byte(n >> 8) b.data[4] = byte(n) - b.data = b.data[0 : recordHeaderLen+n] + b.resize(recordHeaderLen + n) remoteMAC := payload[n:] hc.mac.Reset() @@ -175,7 +258,7 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) { hc.incSeq() hc.mac.Write(b.data) - if subtle.ConstantTimeCompare(hc.mac.Sum(), remoteMAC) != 1 { + if subtle.ConstantTimeCompare(hc.mac.Sum(), remoteMAC) != 1 || paddingGood != 255 { return false, alertBadRecordMAC } } @@ -183,6 +266,23 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) { return true, 0 } +// padToBlockSize calculates the needed padding block, if any, for a payload. +// On exit, prefix aliases payload and extends to the end of the last full +// block of payload. finalBlock is a fresh slice which contains the contents of +// any suffix of payload as well as the needed padding to make finalBlock a +// full block. +func padToBlockSize(payload []byte, blockSize int) (prefix, finalBlock []byte) { + overrun := len(payload) % blockSize + paddingLen := blockSize - overrun + prefix = payload[:len(payload)-overrun] + finalBlock = make([]byte, blockSize) + copy(finalBlock, payload[len(payload)-overrun:]) + for i := overrun; i < blockSize; i++ { + finalBlock[i] = byte(paddingLen - 1) + } + return +} + // encrypt encrypts and macs the data in b. func (hc *halfConn) encrypt(b *block) (bool, alert) { // mac @@ -195,18 +295,30 @@ func (hc *halfConn) encrypt(b *block) (bool, alert) { n := len(b.data) b.resize(n + len(mac)) copy(b.data[n:], mac) - - // update length to include mac - n = len(b.data) - recordHeaderLen - b.data[3] = byte(n >> 8) - b.data[4] = byte(n) } + payload := b.data[recordHeaderLen:] + // encrypt - if hc.crypt != nil { - hc.crypt.XORKeyStream(b.data[recordHeaderLen:]) + if hc.cipher != nil { + switch c := hc.cipher.(type) { + case cipher.Stream: + c.XORKeyStream(payload, payload) + case cipher.BlockMode: + prefix, finalBlock := padToBlockSize(payload, c.BlockSize()) + b.resize(recordHeaderLen + len(prefix) + len(finalBlock)) + c.CryptBlocks(b.data[recordHeaderLen:], prefix) + c.CryptBlocks(b.data[recordHeaderLen+len(prefix):], finalBlock) + default: + panic("unknown cipher type") + } } + // update length to include MAC and any block padding needed. + n := len(b.data) - recordHeaderLen + b.data[3] = byte(n >> 8) + b.data[4] = byte(n) + return true, 0 } @@ -442,7 +554,11 @@ func (c *Conn) sendAlertLocked(err alert) os.Error { } c.tmp[1] = byte(err) c.writeRecord(recordTypeAlert, c.tmp[0:2]) - return c.setError(&net.OpError{Op: "local error", Error: err}) + // closeNotify is a special case in that it isn't an error: + if err != alertCloseNotify { + return c.setError(&net.OpError{Op: "local error", Error: err}) + } + return nil } // sendAlert sends a TLS alert message. @@ -531,10 +647,18 @@ func (c *Conn) readHandshake() (interface{}, os.Error) { m = new(serverHelloMsg) case typeCertificate: m = new(certificateMsg) + case typeCertificateRequest: + m = new(certificateRequestMsg) + case typeCertificateStatus: + m = new(certificateStatusMsg) + case typeServerKeyExchange: + m = new(serverKeyExchangeMsg) case typeServerHelloDone: m = new(serverHelloDoneMsg) case typeClientKeyExchange: m = new(clientKeyExchangeMsg) + case typeCertificateVerify: + m = new(certificateVerifyMsg) case typeNextProtocol: m = new(nextProtoMsg) case typeFinished: @@ -547,7 +671,7 @@ func (c *Conn) readHandshake() (interface{}, os.Error) { // The handshake message unmarshallers // expect to be able to keep references to data, // so pass in a fresh copy that won't be overwritten. - data = bytes.Add(nil, data) + data = append([]byte(nil), data...) if !m.unmarshal(data) { c.sendAlert(alertUnexpectedMessage) @@ -585,7 +709,10 @@ func (c *Conn) Read(b []byte) (n int, err os.Error) { defer c.in.Unlock() for c.input == nil && c.err == nil { - c.readRecord(recordTypeApplicationData) + if err := c.readRecord(recordTypeApplicationData); err != nil { + // Soft error, like EAGAIN + return 0, err + } } if c.err != nil { return 0, c.err @@ -608,7 +735,7 @@ func (c *Conn) Close() os.Error { // Handshake runs the client or server handshake // protocol if it has not yet been run. -// Most uses of this packge need not call Handshake +// Most uses of this package need not call Handshake // explicitly: the first Read or Write will call it automatically. func (c *Conn) Handshake() os.Error { c.handshakeMutex.Lock() @@ -625,11 +752,50 @@ func (c *Conn) Handshake() os.Error { return c.serverHandshake() } -// If c is a TLS server, ClientConnection returns the protocol -// requested by the client during the TLS handshake. -// Handshake must have been called already. -func (c *Conn) ClientConnection() string { +// ConnectionState returns basic TLS details about the connection. +func (c *Conn) ConnectionState() ConnectionState { + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + + var state ConnectionState + state.HandshakeComplete = c.handshakeComplete + if c.handshakeComplete { + state.NegotiatedProtocol = c.clientProtocol + state.CipherSuite = c.cipherSuite + } + + return state +} + +// OCSPResponse returns the stapled OCSP response from the TLS server, if +// any. (Only valid for client connections.) +func (c *Conn) OCSPResponse() []byte { c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() - return c.clientProtocol + + return c.ocspResponse +} + +// PeerCertificates returns the certificate chain that was presented by the +// other side. +func (c *Conn) PeerCertificates() []*x509.Certificate { + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + + return c.peerCertificates +} + +// VerifyHostname checks that the peer certificate chain is valid for +// connecting to host. If so, it returns nil; if not, it returns an os.Error +// describing the problem. +func (c *Conn) VerifyHostname(host string) os.Error { + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + if !c.isClient { + return os.ErrorString("VerifyHostname called on TLS server connection") + } + if !c.handshakeComplete { + return os.ErrorString("TLS handshake has not yet been performed") + } + return c.peerCertificates[0].VerifyHostname(host) } diff --git a/src/pkg/crypto/tls/conn_test.go b/src/pkg/crypto/tls/conn_test.go new file mode 100644 index 000000000..f44a50bed --- /dev/null +++ b/src/pkg/crypto/tls/conn_test.go @@ -0,0 +1,52 @@ +// 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 tls + +import ( + "testing" +) + +func TestRoundUp(t *testing.T) { + if roundUp(0, 16) != 0 || + roundUp(1, 16) != 16 || + roundUp(15, 16) != 16 || + roundUp(16, 16) != 16 || + roundUp(17, 16) != 32 { + t.Error("roundUp broken") + } +} + +var paddingTests = []struct { + in []byte + good bool + expectedLen int +}{ + {[]byte{1, 2, 3, 4, 0}, true, 4}, + {[]byte{1, 2, 3, 4, 0, 1}, false, 0}, + {[]byte{1, 2, 3, 4, 99, 99}, false, 0}, + {[]byte{1, 2, 3, 4, 1, 1}, true, 4}, + {[]byte{1, 2, 3, 2, 2, 2}, true, 3}, + {[]byte{1, 2, 3, 3, 3, 3}, true, 2}, + {[]byte{1, 2, 3, 4, 3, 3}, false, 0}, + {[]byte{1, 4, 4, 4, 4, 4}, true, 1}, + {[]byte{5, 5, 5, 5, 5, 5}, true, 0}, + {[]byte{6, 6, 6, 6, 6, 6}, false, 0}, +} + +func TestRemovePadding(t *testing.T) { + for i, test := range paddingTests { + payload, good := removePadding(test.in) + expectedGood := byte(255) + if !test.good { + expectedGood = 0 + } + if good != expectedGood { + t.Errorf("#%d: wrong validity, want:%d got:%d", i, expectedGood, good) + } + if good == 255 && len(payload) != test.expectedLen { + t.Errorf("#%d: got %d, want %d", i, len(payload), test.expectedLen) + } + } +} diff --git a/src/pkg/crypto/tls/generate_cert.go b/src/pkg/crypto/tls/generate_cert.go new file mode 100644 index 000000000..3e0c63938 --- /dev/null +++ b/src/pkg/crypto/tls/generate_cert.go @@ -0,0 +1,70 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Generate a self-signed X.509 certificate for a TLS server. Outputs to +// 'cert.pem' and 'key.pem' and will overwrite existing files. + +package main + +import ( + "crypto/rsa" + "crypto/rand" + "crypto/x509" + "encoding/pem" + "flag" + "log" + "os" + "time" +) + +var hostName *string = flag.String("host", "127.0.0.1", "Hostname to generate a certificate for") + +func main() { + flag.Parse() + + priv, err := rsa.GenerateKey(rand.Reader, 1024) + if err != nil { + log.Exitf("failed to generate private key: %s", err) + return + } + + now := time.Seconds() + + template := x509.Certificate{ + SerialNumber: []byte{0}, + Subject: x509.Name{ + CommonName: *hostName, + Organization: []string{"Acme Co"}, + }, + NotBefore: time.SecondsToUTC(now - 300), + NotAfter: time.SecondsToUTC(now + 60*60*24*365), // valid for 1 year. + + SubjectKeyId: []byte{1, 2, 3, 4}, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + log.Exitf("Failed to create certificate: %s", err) + return + } + + certOut, err := os.Open("cert.pem", os.O_WRONLY|os.O_CREAT, 0644) + if err != nil { + log.Exitf("failed to open cert.pem for writing: %s", err) + return + } + pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + certOut.Close() + log.Print("written cert.pem\n") + + keyOut, err := os.Open("key.pem", os.O_WRONLY|os.O_CREAT, 0600) + if err != nil { + log.Print("failed to open key.pem for writing:", err) + return + } + pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) + keyOut.Close() + log.Print("written key.pem\n") +} diff --git a/src/pkg/crypto/tls/handshake_client.go b/src/pkg/crypto/tls/handshake_client.go index dd3009802..1ca33f59d 100644 --- a/src/pkg/crypto/tls/handshake_client.go +++ b/src/pkg/crypto/tls/handshake_client.go @@ -5,10 +5,7 @@ package tls import ( - "crypto/hmac" - "crypto/rc4" "crypto/rsa" - "crypto/sha1" "crypto/subtle" "crypto/x509" "io" @@ -18,23 +15,30 @@ import ( func (c *Conn) clientHandshake() os.Error { finishedHash := newFinishedHash() - config := defaultConfig() + if c.config == nil { + c.config = defaultConfig() + } hello := &clientHelloMsg{ vers: maxVersion, - cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, + cipherSuites: c.config.cipherSuites(), compressionMethods: []uint8{compressionNone}, random: make([]byte, 32), + ocspStapling: true, + serverName: c.config.ServerName, + supportedCurves: []uint16{curveP256, curveP384, curveP521}, + supportedPoints: []uint8{pointFormatUncompressed}, } - t := uint32(config.Time()) + t := uint32(c.config.time()) hello.random[0] = byte(t >> 24) hello.random[1] = byte(t >> 16) hello.random[2] = byte(t >> 8) hello.random[3] = byte(t) - _, err := io.ReadFull(config.Rand, hello.random[4:]) + _, err := io.ReadFull(c.config.rand(), hello.random[4:]) if err != nil { - return c.sendAlert(alertInternalError) + c.sendAlert(alertInternalError) + return os.ErrorString("short read from Rand") } finishedHash.Write(hello.marshal()) @@ -57,11 +61,15 @@ func (c *Conn) clientHandshake() os.Error { c.vers = vers c.haveVers = true - if serverHello.cipherSuite != TLS_RSA_WITH_RC4_128_SHA || - serverHello.compressionMethod != compressionNone { + if serverHello.compressionMethod != compressionNone { return c.sendAlert(alertUnexpectedMessage) } + suite, suiteId := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite) + if suite == nil { + return c.sendAlert(alertHandshakeFailure) + } + msg, err = c.readHandshake() if err != nil { return err @@ -73,71 +81,189 @@ func (c *Conn) clientHandshake() os.Error { finishedHash.Write(certMsg.marshal()) certs := make([]*x509.Certificate, len(certMsg.certificates)) + chain := NewCASet() for i, asn1Data := range certMsg.certificates { cert, err := x509.ParseCertificate(asn1Data) if err != nil { - return c.sendAlert(alertBadCertificate) + c.sendAlert(alertBadCertificate) + return os.ErrorString("failed to parse certificate from server: " + err.String()) } certs[i] = cert + chain.AddCert(cert) } - // TODO(agl): do better validation of certs: max path length, name restrictions etc. - for i := 1; i < len(certs); i++ { - if err := certs[i-1].CheckSignatureFrom(certs[i]); err != nil { - return c.sendAlert(alertBadCertificate) + // If we don't have a root CA set configured then anything is accepted. + // TODO(rsc): Find certificates for OS X 10.6. + for cur := certs[0]; c.config.RootCAs != nil; { + parent := c.config.RootCAs.FindVerifiedParent(cur) + if parent != nil { + break } - } - // TODO(rsc): Find certificates for OS X 10.6. - if false && config.RootCAs != nil { - root := config.RootCAs.FindParent(certs[len(certs)-1]) - if root == nil { - return c.sendAlert(alertBadCertificate) + parent = chain.FindVerifiedParent(cur) + if parent == nil { + c.sendAlert(alertBadCertificate) + return os.ErrorString("could not find root certificate for chain") } - if certs[len(certs)-1].CheckSignatureFrom(root) != nil { - return c.sendAlert(alertBadCertificate) + + if !parent.BasicConstraintsValid || !parent.IsCA { + c.sendAlert(alertBadCertificate) + return os.ErrorString("intermediate certificate does not have CA bit set") } + // KeyUsage status flags are ignored. From Engineering + // Security, Peter Gutmann: A European government CA marked its + // signing certificates as being valid for encryption only, but + // no-one noticed. Another European CA marked its signature + // keys as not being valid for signatures. A different CA + // marked its own trusted root certificate as being invalid for + // certificate signing. Another national CA distributed a + // certificate to be used to encrypt data for the country’s tax + // authority that was marked as only being usable for digital + // signatures but not for encryption. Yet another CA reversed + // the order of the bit flags in the keyUsage due to confusion + // over encoding endianness, essentially setting a random + // keyUsage in certificates that it issued. Another CA created + // a self-invalidating certificate by adding a certificate + // policy statement stipulating that the certificate had to be + // used strictly as specified in the keyUsage, and a keyUsage + // containing a flag indicating that the RSA encryption key + // could only be used for Diffie-Hellman key agreement. + + cur = parent } - pub, ok := certs[0].PublicKey.(*rsa.PublicKey) - if !ok { + if _, ok := certs[0].PublicKey.(*rsa.PublicKey); !ok { return c.sendAlert(alertUnsupportedCertificate) } + c.peerCertificates = certs + + if serverHello.certStatus { + msg, err = c.readHandshake() + if err != nil { + return err + } + cs, ok := msg.(*certificateStatusMsg) + if !ok { + return c.sendAlert(alertUnexpectedMessage) + } + finishedHash.Write(cs.marshal()) + + if cs.statusType == statusTypeOCSP { + c.ocspResponse = cs.response + } + } + msg, err = c.readHandshake() if err != nil { return err } + + keyAgreement := suite.ka() + + skx, ok := msg.(*serverKeyExchangeMsg) + if ok { + finishedHash.Write(skx.marshal()) + err = keyAgreement.processServerKeyExchange(c.config, hello, serverHello, certs[0], skx) + if err != nil { + c.sendAlert(alertUnexpectedMessage) + return err + } + + msg, err = c.readHandshake() + if err != nil { + return err + } + } + + transmitCert := false + certReq, ok := msg.(*certificateRequestMsg) + if ok { + // We only accept certificates with RSA keys. + rsaAvail := false + for _, certType := range certReq.certificateTypes { + if certType == certTypeRSASign { + rsaAvail = true + break + } + } + + // For now, only send a certificate back if the server gives us an + // empty list of certificateAuthorities. + // + // RFC 4346 on the certificateAuthorities field: + // A list of the distinguished names of acceptable certificate + // authorities. These distinguished names may specify a desired + // distinguished name for a root CA or for a subordinate CA; thus, + // this message can be used to describe both known roots and a + // desired authorization space. If the certificate_authorities + // list is empty then the client MAY send any certificate of the + // appropriate ClientCertificateType, unless there is some + // external arrangement to the contrary. + if rsaAvail && len(certReq.certificateAuthorities) == 0 { + transmitCert = true + } + + finishedHash.Write(certReq.marshal()) + + msg, err = c.readHandshake() + if err != nil { + return err + } + } + shd, ok := msg.(*serverHelloDoneMsg) if !ok { return c.sendAlert(alertUnexpectedMessage) } finishedHash.Write(shd.marshal()) - ckx := new(clientKeyExchangeMsg) - preMasterSecret := make([]byte, 48) - preMasterSecret[0] = byte(hello.vers >> 8) - preMasterSecret[1] = byte(hello.vers) - _, err = io.ReadFull(config.Rand, preMasterSecret[2:]) - if err != nil { - return c.sendAlert(alertInternalError) + var cert *x509.Certificate + if transmitCert { + certMsg = new(certificateMsg) + if len(c.config.Certificates) > 0 { + cert, err = x509.ParseCertificate(c.config.Certificates[0].Certificate[0]) + if err == nil && cert.PublicKeyAlgorithm == x509.RSA { + certMsg.certificates = c.config.Certificates[0].Certificate + } else { + cert = nil + } + } + finishedHash.Write(certMsg.marshal()) + c.writeRecord(recordTypeHandshake, certMsg.marshal()) } - ckx.ciphertext, err = rsa.EncryptPKCS1v15(config.Rand, pub, preMasterSecret) + preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hello, certs[0]) if err != nil { - return c.sendAlert(alertInternalError) + c.sendAlert(alertInternalError) + return err + } + if ckx != nil { + finishedHash.Write(ckx.marshal()) + c.writeRecord(recordTypeHandshake, ckx.marshal()) } - finishedHash.Write(ckx.marshal()) - c.writeRecord(recordTypeHandshake, ckx.marshal()) + if cert != nil { + certVerify := new(certificateVerifyMsg) + var digest [36]byte + copy(digest[0:16], finishedHash.serverMD5.Sum()) + copy(digest[16:36], finishedHash.serverSHA1.Sum()) + signed, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey, rsa.HashMD5SHA1, digest[0:]) + if err != nil { + return c.sendAlert(alertInternalError) + } + certVerify.signature = signed - suite := cipherSuites[0] - masterSecret, clientMAC, serverMAC, clientKey, serverKey := - keysFromPreMasterSecret11(preMasterSecret, hello.random, serverHello.random, suite.hashLength, suite.cipherKeyLength) + finishedHash.Write(certVerify.marshal()) + c.writeRecord(recordTypeHandshake, certVerify.marshal()) + } - cipher, _ := rc4.NewCipher(clientKey) + masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := + keysFromPreMasterSecret10(preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen) - c.out.prepareCipherSpec(cipher, hmac.New(sha1.New(), clientMAC)) + clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */ ) + clientHash := suite.mac(clientMAC) + c.out.prepareCipherSpec(clientCipher, clientHash) c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) finished := new(finishedMsg) @@ -145,8 +271,9 @@ func (c *Conn) clientHandshake() os.Error { finishedHash.Write(finished.marshal()) c.writeRecord(recordTypeHandshake, finished.marshal()) - cipher2, _ := rc4.NewCipher(serverKey) - c.in.prepareCipherSpec(cipher2, hmac.New(sha1.New(), serverMAC)) + serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */ ) + serverHash := suite.mac(serverMAC) + c.in.prepareCipherSpec(serverCipher, serverHash) c.readRecord(recordTypeChangeCipherSpec) if c.err != nil { return c.err @@ -168,6 +295,6 @@ func (c *Conn) clientHandshake() os.Error { } c.handshakeComplete = true - c.cipherSuite = TLS_RSA_WITH_RC4_128_SHA + c.cipherSuite = suiteId return nil } diff --git a/src/pkg/crypto/tls/handshake_client_test.go b/src/pkg/crypto/tls/handshake_client_test.go new file mode 100644 index 000000000..e5c9684b9 --- /dev/null +++ b/src/pkg/crypto/tls/handshake_client_test.go @@ -0,0 +1,211 @@ +// 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 tls + +import ( + "bytes" + "flag" + "io" + "net" + "testing" +) + +func testClientScript(t *testing.T, name string, clientScript [][]byte, config *Config) { + c, s := net.Pipe() + cli := Client(c, config) + go func() { + cli.Write([]byte("hello\n")) + cli.Close() + }() + + defer c.Close() + for i, b := range clientScript { + if i%2 == 1 { + s.Write(b) + continue + } + bb := make([]byte, len(b)) + _, err := io.ReadFull(s, bb) + if err != nil { + t.Fatalf("%s #%d: %s", name, i, err) + } + if !bytes.Equal(b, bb) { + t.Fatalf("%s #%d: mismatch on read: got:%x want:%x", name, i, bb, b) + } + } +} + +func TestHandshakeClientRC4(t *testing.T) { + testClientScript(t, "RC4", rc4ClientScript, testConfig) +} + +var connect = flag.Bool("connect", false, "connect to a TLS server on :10443") + +func TestRunClient(t *testing.T) { + if !*connect { + return + } + + testConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA} + + conn, err := Dial("tcp", "", "127.0.0.1:10443", testConfig) + if err != nil { + t.Fatal(err) + } + + conn.Write([]byte("hello\n")) + conn.Close() +} + +// Script of interaction with gnutls implementation. +// The values for this test are obtained by building and running in client mode: +// % gotest -match "TestRunClient" -connect +// and then: +// % gnutls-serv -p 10443 --debug 100 --x509keyfile key.pem --x509certfile cert.pem -a > /tmp/log 2>&1 +// % python parse-gnutls-cli-debug-log.py < /tmp/log +// +// Where key.pem is: +// -----BEGIN RSA PRIVATE KEY----- +// MIIBPAIBAAJBAJ+zw4Qnlf8SMVIPFe9GEcStgOY2Ww/dgNdhjeD8ckUJNP5VZkVD +// TGiXav6ooKXfX3j/7tdkuD8Ey2//Kv7+ue0CAwEAAQJAN6W31vDEP2DjdqhzCDDu +// OA4NACqoiFqyblo7yc2tM4h4xMbC3Yx5UKMN9ZkCtX0gzrz6DyF47bdKcWBzNWCj +// gQIhANEoojVt7hq+SQ6MCN6FTAysGgQf56Q3TYoJMoWvdiXVAiEAw3e3rc+VJpOz +// rHuDo6bgpjUAAXM+v3fcpsfZSNO6V7kCIQCtbVjanpUwvZkMI9by02oUk9taki3b +// PzPfAfNPYAbCJQIhAJXNQDWyqwn/lGmR11cqY2y9nZ1+5w3yHGatLrcDnQHxAiEA +// vnlEGo8K85u+KwIOimM48ZG8oTk7iFdkqLJR1utT3aU= +// -----END RSA PRIVATE KEY----- +// +// and cert.pem is: +// -----BEGIN CERTIFICATE----- +// MIIBoDCCAUoCAQAwDQYJKoZIhvcNAQEEBQAwYzELMAkGA1UEBhMCQVUxEzARBgNV +// BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMSMwIQYD +// VQQDExpTZXJ2ZXIgdGVzdCBjZXJ0ICg1MTIgYml0KTAeFw05NzA5MDkwMzQxMjZa +// Fw05NzEwMDkwMzQxMjZaMF4xCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 +// YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxFzAVBgNVBAMT +// DkVyaWMgdGhlIFlvdW5nMFEwCQYFKw4DAgwFAANEAAJBALVEqPODnpI4rShlY8S7 +// tB713JNvabvn6Gned7zylwLLiXQAo/PAT6mfdWPTyCX9RlId/Aroh1ou893BA32Q +// sggwDQYJKoZIhvcNAQEEBQADQQCU5SSgapJSdRXJoX+CpCvFy+JVh9HpSjCpSNKO +// 19raHv98hKAUJuP9HyM+SUsffO6mAIgitUaqW8/wDMePhEC3 +// -----END CERTIFICATE----- +var rc4ClientScript = [][]byte{ + { + 0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00, + 0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, + 0x01, 0x00, 0x00, 0x1b, 0x00, 0x05, 0x00, 0x05, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00, + 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, + }, + + { + 0x16, 0x03, 0x01, 0x00, 0x4a, 0x02, 0x00, 0x00, + 0x46, 0x03, 0x01, 0x4d, 0x0a, 0x56, 0x16, 0xb5, + 0x91, 0xd1, 0xcb, 0x80, 0x4d, 0xc7, 0x46, 0xf3, + 0x37, 0x0c, 0xef, 0xea, 0x64, 0x11, 0x14, 0x56, + 0x97, 0x9b, 0xc5, 0x67, 0x08, 0xb7, 0x13, 0xea, + 0xf8, 0xc9, 0xb3, 0x20, 0xe2, 0xfc, 0x41, 0xf6, + 0x96, 0x90, 0x9d, 0x43, 0x9b, 0xe9, 0x6e, 0xf8, + 0x41, 0x16, 0xcc, 0xf3, 0xc7, 0xde, 0xda, 0x5a, + 0xa1, 0x33, 0x69, 0xe2, 0xde, 0x5b, 0xaf, 0x2a, + 0x92, 0xe7, 0xd4, 0xa0, 0x00, 0x05, 0x00, 0x16, + 0x03, 0x01, 0x01, 0xf7, 0x0b, 0x00, 0x01, 0xf3, + 0x00, 0x01, 0xf0, 0x00, 0x01, 0xed, 0x30, 0x82, + 0x01, 0xe9, 0x30, 0x82, 0x01, 0x52, 0x02, 0x01, + 0x06, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04, 0x05, 0x00, + 0x30, 0x5b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, + 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, + 0x13, 0x0a, 0x51, 0x75, 0x65, 0x65, 0x6e, 0x73, + 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x1a, 0x30, 0x18, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x43, + 0x72, 0x79, 0x70, 0x74, 0x53, 0x6f, 0x66, 0x74, + 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x12, 0x54, 0x65, 0x73, 0x74, 0x20, + 0x43, 0x41, 0x20, 0x28, 0x31, 0x30, 0x32, 0x34, + 0x20, 0x62, 0x69, 0x74, 0x29, 0x30, 0x1e, 0x17, + 0x0d, 0x30, 0x30, 0x31, 0x30, 0x31, 0x36, 0x32, + 0x32, 0x33, 0x31, 0x30, 0x33, 0x5a, 0x17, 0x0d, + 0x30, 0x33, 0x30, 0x31, 0x31, 0x34, 0x32, 0x32, + 0x33, 0x31, 0x30, 0x33, 0x5a, 0x30, 0x63, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x51, + 0x75, 0x65, 0x65, 0x6e, 0x73, 0x6c, 0x61, 0x6e, + 0x64, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x11, 0x43, 0x72, 0x79, 0x70, + 0x74, 0x53, 0x6f, 0x66, 0x74, 0x20, 0x50, 0x74, + 0x79, 0x20, 0x4c, 0x74, 0x64, 0x31, 0x23, 0x30, + 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x74, + 0x65, 0x73, 0x74, 0x20, 0x63, 0x65, 0x72, 0x74, + 0x20, 0x28, 0x35, 0x31, 0x32, 0x20, 0x62, 0x69, + 0x74, 0x29, 0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30, 0x48, + 0x02, 0x41, 0x00, 0x9f, 0xb3, 0xc3, 0x84, 0x27, + 0x95, 0xff, 0x12, 0x31, 0x52, 0x0f, 0x15, 0xef, + 0x46, 0x11, 0xc4, 0xad, 0x80, 0xe6, 0x36, 0x5b, + 0x0f, 0xdd, 0x80, 0xd7, 0x61, 0x8d, 0xe0, 0xfc, + 0x72, 0x45, 0x09, 0x34, 0xfe, 0x55, 0x66, 0x45, + 0x43, 0x4c, 0x68, 0x97, 0x6a, 0xfe, 0xa8, 0xa0, + 0xa5, 0xdf, 0x5f, 0x78, 0xff, 0xee, 0xd7, 0x64, + 0xb8, 0x3f, 0x04, 0xcb, 0x6f, 0xff, 0x2a, 0xfe, + 0xfe, 0xb9, 0xed, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x04, 0x05, 0x00, 0x03, + 0x81, 0x81, 0x00, 0x93, 0xd2, 0x0a, 0xc5, 0x41, + 0xe6, 0x5a, 0xa9, 0x86, 0xf9, 0x11, 0x87, 0xe4, + 0xdb, 0x45, 0xe2, 0xc5, 0x95, 0x78, 0x1a, 0x6c, + 0x80, 0x6d, 0x73, 0x1f, 0xb4, 0x6d, 0x44, 0xa3, + 0xba, 0x86, 0x88, 0xc8, 0x58, 0xcd, 0x1c, 0x06, + 0x35, 0x6c, 0x44, 0x62, 0x88, 0xdf, 0xe4, 0xf6, + 0x64, 0x61, 0x95, 0xef, 0x4a, 0xa6, 0x7f, 0x65, + 0x71, 0xd7, 0x6b, 0x88, 0x39, 0xf6, 0x32, 0xbf, + 0xac, 0x93, 0x67, 0x69, 0x51, 0x8c, 0x93, 0xec, + 0x48, 0x5f, 0xc9, 0xb1, 0x42, 0xf9, 0x55, 0xd2, + 0x7e, 0x4e, 0xf4, 0xf2, 0x21, 0x6b, 0x90, 0x57, + 0xe6, 0xd7, 0x99, 0x9e, 0x41, 0xca, 0x80, 0xbf, + 0x1a, 0x28, 0xa2, 0xca, 0x5b, 0x50, 0x4a, 0xed, + 0x84, 0xe7, 0x82, 0xc7, 0xd2, 0xcf, 0x36, 0x9e, + 0x6a, 0x67, 0xb9, 0x88, 0xa7, 0xf3, 0x8a, 0xd0, + 0x04, 0xf8, 0xe8, 0xc6, 0x17, 0xe3, 0xc5, 0x29, + 0xbc, 0x17, 0xf1, 0x16, 0x03, 0x01, 0x00, 0x04, + 0x0e, 0x00, 0x00, 0x00, + }, + + { + 0x16, 0x03, 0x01, 0x00, 0x46, 0x10, 0x00, 0x00, + 0x42, 0x00, 0x40, 0x87, 0xa1, 0x1f, 0x14, 0xe1, + 0xfb, 0x91, 0xac, 0x58, 0x2e, 0xf3, 0x71, 0xce, + 0x01, 0x85, 0x2c, 0xc7, 0xfe, 0x84, 0x87, 0x82, + 0xb7, 0x57, 0xdb, 0x37, 0x4d, 0x46, 0x83, 0x67, + 0x52, 0x82, 0x51, 0x01, 0x95, 0x23, 0x68, 0x69, + 0x6b, 0xd0, 0xa7, 0xa7, 0xe5, 0x88, 0xd0, 0x47, + 0x71, 0xb8, 0xd2, 0x03, 0x05, 0x25, 0x56, 0x5c, + 0x10, 0x08, 0xc6, 0x9b, 0xd4, 0x67, 0xcd, 0x28, + 0xbe, 0x9c, 0x48, 0x14, 0x03, 0x01, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xc1, 0xb8, + 0xd3, 0x7f, 0xc5, 0xc2, 0x5a, 0x1d, 0x6d, 0x5b, + 0x2d, 0x5c, 0x82, 0x87, 0xc2, 0x6f, 0x0d, 0x63, + 0x7b, 0x72, 0x2b, 0xda, 0x69, 0xc4, 0xfe, 0x3c, + 0x84, 0xa1, 0x5a, 0x62, 0x38, 0x37, 0xc6, 0x54, + 0x25, 0x2a, + }, + + { + 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x01, 0x00, 0x24, 0xea, 0x88, 0x9c, 0x00, 0xf6, + 0x35, 0xb8, 0x42, 0x7f, 0x15, 0x17, 0x76, 0x5e, + 0x4b, 0x24, 0xcb, 0x7e, 0xa0, 0x7b, 0xc3, 0x70, + 0x52, 0x0a, 0x88, 0x2a, 0x7a, 0x45, 0x59, 0x90, + 0x59, 0xac, 0xc6, 0xb5, 0x56, 0x55, 0x96, + }, +} diff --git a/src/pkg/crypto/tls/handshake_messages.go b/src/pkg/crypto/tls/handshake_messages.go index f0a48c863..e5e856271 100644 --- a/src/pkg/crypto/tls/handshake_messages.go +++ b/src/pkg/crypto/tls/handshake_messages.go @@ -13,6 +13,9 @@ type clientHelloMsg struct { compressionMethods []uint8 nextProtoNeg bool serverName string + ocspStapling bool + supportedCurves []uint16 + supportedPoints []uint8 } func (m *clientHelloMsg) marshal() []byte { @@ -26,10 +29,22 @@ func (m *clientHelloMsg) marshal() []byte { if m.nextProtoNeg { numExtensions++ } + if m.ocspStapling { + extensionsLength += 1 + 2 + 2 + numExtensions++ + } if len(m.serverName) > 0 { extensionsLength += 5 + len(m.serverName) numExtensions++ } + if len(m.supportedCurves) > 0 { + extensionsLength += 2 + 2*len(m.supportedCurves) + numExtensions++ + } + if len(m.supportedPoints) > 0 { + extensionsLength += 1 + len(m.supportedPoints) + numExtensions++ + } if numExtensions > 0 { extensionsLength += 4 * numExtensions length += 2 + extensionsLength @@ -95,12 +110,55 @@ func (m *clientHelloMsg) marshal() []byte { // ServerName server_name_list<1..2^16-1> // } ServerNameList; - z[1] = 1 + z[0] = byte((len(m.serverName) + 3) >> 8) + z[1] = byte(len(m.serverName) + 3) z[3] = byte(len(m.serverName) >> 8) z[4] = byte(len(m.serverName)) copy(z[5:], []byte(m.serverName)) z = z[l:] } + if m.ocspStapling { + // RFC 4366, section 3.6 + z[0] = byte(extensionStatusRequest >> 8) + z[1] = byte(extensionStatusRequest) + z[2] = 0 + z[3] = 5 + z[4] = 1 // OCSP type + // Two zero valued uint16s for the two lengths. + z = z[9:] + } + if len(m.supportedCurves) > 0 { + // http://tools.ietf.org/html/rfc4492#section-5.5.1 + z[0] = byte(extensionSupportedCurves >> 8) + z[1] = byte(extensionSupportedCurves) + l := 2 + 2*len(m.supportedCurves) + z[2] = byte(l >> 8) + z[3] = byte(l) + l -= 2 + z[4] = byte(l >> 8) + z[5] = byte(l) + z = z[6:] + for _, curve := range m.supportedCurves { + z[0] = byte(curve >> 8) + z[1] = byte(curve) + z = z[2:] + } + } + if len(m.supportedPoints) > 0 { + // http://tools.ietf.org/html/rfc4492#section-5.5.2 + z[0] = byte(extensionSupportedPoints >> 8) + z[1] = byte(extensionSupportedPoints) + l := 1 + len(m.supportedPoints) + z[2] = byte(l >> 8) + z[3] = byte(l) + l-- + z[4] = byte(l) + z = z[5:] + for _, pointFormat := range m.supportedPoints { + z[0] = byte(pointFormat) + z = z[1:] + } + } m.raw = x @@ -148,6 +206,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { m.nextProtoNeg = false m.serverName = "" + m.ocspStapling = false if len(data) == 0 { // ClientHello is optionally followed by extension data @@ -202,6 +261,35 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { return false } m.nextProtoNeg = true + case extensionStatusRequest: + m.ocspStapling = length > 0 && data[0] == statusTypeOCSP + case extensionSupportedCurves: + // http://tools.ietf.org/html/rfc4492#section-5.5.1 + if length < 2 { + return false + } + l := int(data[0])<<8 | int(data[1]) + if l%2 == 1 || length != l+2 { + return false + } + numCurves := l / 2 + m.supportedCurves = make([]uint16, numCurves) + d := data[2:] + for i := 0; i < numCurves; i++ { + m.supportedCurves[i] = uint16(d[0])<<8 | uint16(d[1]) + d = d[2:] + } + case extensionSupportedPoints: + // http://tools.ietf.org/html/rfc4492#section-5.5.2 + if length < 1 { + return false + } + l := int(data[0]) + if length != l+1 { + return false + } + m.supportedPoints = make([]uint8, l) + copy(m.supportedPoints, data[1:]) } data = data[length:] } @@ -218,6 +306,7 @@ type serverHelloMsg struct { compressionMethod uint8 nextProtoNeg bool nextProtos []string + certStatus bool } func (m *serverHelloMsg) marshal() []byte { @@ -238,6 +327,9 @@ func (m *serverHelloMsg) marshal() []byte { nextProtoLen += len(m.nextProtos) extensionsLength += nextProtoLen } + if m.certStatus { + numExtensions++ + } if numExtensions > 0 { extensionsLength += 4 * numExtensions length += 2 + extensionsLength @@ -281,25 +373,17 @@ func (m *serverHelloMsg) marshal() []byte { z = z[1+l:] } } + if m.certStatus { + z[0] = byte(extensionStatusRequest >> 8) + z[1] = byte(extensionStatusRequest) + z = z[4:] + } m.raw = x return x } -func append(slice []string, elem string) []string { - if len(slice) < cap(slice) { - slice = slice[0 : len(slice)+1] - slice[len(slice)-1] = elem - return slice - } - - fresh := make([]string, len(slice)+1, cap(slice)*2+1) - copy(fresh, slice) - fresh[len(slice)] = elem - return fresh -} - func (m *serverHelloMsg) unmarshal(data []byte) bool { if len(data) < 42 { return false @@ -322,6 +406,7 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool { m.nextProtoNeg = false m.nextProtos = nil + m.certStatus = false if len(data) == 0 { // ServerHello is optionally followed by extension data @@ -361,6 +446,11 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool { m.nextProtos = append(m.nextProtos, string(d[0:l])) d = d[l:] } + case extensionStatusRequest: + if length > 0 { + return false + } + m.certStatus = true } data = data[length:] } @@ -445,6 +535,91 @@ func (m *certificateMsg) unmarshal(data []byte) bool { return true } +type serverKeyExchangeMsg struct { + raw []byte + key []byte +} + +func (m *serverKeyExchangeMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + length := len(m.key) + x := make([]byte, length+4) + x[0] = typeServerKeyExchange + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + copy(x[4:], m.key) + + m.raw = x + return x +} + +func (m *serverKeyExchangeMsg) unmarshal(data []byte) bool { + m.raw = data + if len(data) < 4 { + return false + } + m.key = data[4:] + return true +} + +type certificateStatusMsg struct { + raw []byte + statusType uint8 + response []byte +} + +func (m *certificateStatusMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + var x []byte + if m.statusType == statusTypeOCSP { + x = make([]byte, 4+4+len(m.response)) + x[0] = typeCertificateStatus + l := len(m.response) + 4 + x[1] = byte(l >> 16) + x[2] = byte(l >> 8) + x[3] = byte(l) + x[4] = statusTypeOCSP + + l -= 4 + x[5] = byte(l >> 16) + x[6] = byte(l >> 8) + x[7] = byte(l) + copy(x[8:], m.response) + } else { + x = []byte{typeCertificateStatus, 0, 0, 1, m.statusType} + } + + m.raw = x + return x +} + +func (m *certificateStatusMsg) unmarshal(data []byte) bool { + m.raw = data + if len(data) < 5 { + return false + } + m.statusType = data[4] + + m.response = nil + if m.statusType == statusTypeOCSP { + if len(data) < 8 { + return false + } + respLen := uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7]) + if uint32(len(data)) != 4+4+respLen { + return false + } + m.response = data[8:] + } + return true +} + type serverHelloDoneMsg struct{} func (m *serverHelloDoneMsg) marshal() []byte { @@ -466,15 +641,13 @@ func (m *clientKeyExchangeMsg) marshal() []byte { if m.raw != nil { return m.raw } - length := len(m.ciphertext) + 2 + length := len(m.ciphertext) x := make([]byte, length+4) x[0] = typeClientKeyExchange x[1] = uint8(length >> 16) x[2] = uint8(length >> 8) x[3] = uint8(length) - x[4] = uint8(len(m.ciphertext) >> 8) - x[5] = uint8(len(m.ciphertext)) - copy(x[6:], m.ciphertext) + copy(x[4:], m.ciphertext) m.raw = x return x @@ -482,14 +655,14 @@ func (m *clientKeyExchangeMsg) marshal() []byte { func (m *clientKeyExchangeMsg) unmarshal(data []byte) bool { m.raw = data - if len(data) < 7 { + if len(data) < 4 { return false } - cipherTextLen := int(data[4])<<8 | int(data[5]) - if len(data) != 6+cipherTextLen { + l := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) + if l != len(data)-4 { return false } - m.ciphertext = data[6:] + m.ciphertext = data[4:] return true } @@ -579,3 +752,153 @@ func (m *nextProtoMsg) unmarshal(data []byte) bool { return true } + +type certificateRequestMsg struct { + raw []byte + certificateTypes []byte + certificateAuthorities [][]byte +} + +func (m *certificateRequestMsg) marshal() (x []byte) { + if m.raw != nil { + return m.raw + } + + // See http://tools.ietf.org/html/rfc4346#section-7.4.4 + length := 1 + len(m.certificateTypes) + 2 + for _, ca := range m.certificateAuthorities { + length += 2 + len(ca) + } + + x = make([]byte, 4+length) + x[0] = typeCertificateRequest + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + + x[4] = uint8(len(m.certificateTypes)) + + copy(x[5:], m.certificateTypes) + y := x[5+len(m.certificateTypes):] + + numCA := len(m.certificateAuthorities) + y[0] = uint8(numCA >> 8) + y[1] = uint8(numCA) + y = y[2:] + for _, ca := range m.certificateAuthorities { + y[0] = uint8(len(ca) >> 8) + y[1] = uint8(len(ca)) + y = y[2:] + copy(y, ca) + y = y[len(ca):] + } + + m.raw = x + + return +} + +func (m *certificateRequestMsg) unmarshal(data []byte) bool { + m.raw = data + + if len(data) < 5 { + return false + } + + length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) + if uint32(len(data))-4 != length { + return false + } + + numCertTypes := int(data[4]) + data = data[5:] + if numCertTypes == 0 || len(data) <= numCertTypes { + return false + } + + m.certificateTypes = make([]byte, numCertTypes) + if copy(m.certificateTypes, data) != numCertTypes { + return false + } + + data = data[numCertTypes:] + if len(data) < 2 { + return false + } + + numCAs := uint16(data[0])<<16 | uint16(data[1]) + data = data[2:] + + m.certificateAuthorities = make([][]byte, numCAs) + for i := uint16(0); i < numCAs; i++ { + if len(data) < 2 { + return false + } + caLen := uint16(data[0])<<16 | uint16(data[1]) + + data = data[2:] + if len(data) < int(caLen) { + return false + } + + ca := make([]byte, caLen) + copy(ca, data) + m.certificateAuthorities[i] = ca + data = data[caLen:] + } + + if len(data) > 0 { + return false + } + + return true +} + +type certificateVerifyMsg struct { + raw []byte + signature []byte +} + +func (m *certificateVerifyMsg) marshal() (x []byte) { + if m.raw != nil { + return m.raw + } + + // See http://tools.ietf.org/html/rfc4346#section-7.4.8 + siglength := len(m.signature) + length := 2 + siglength + x = make([]byte, 4+length) + x[0] = typeCertificateVerify + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + x[4] = uint8(siglength >> 8) + x[5] = uint8(siglength) + copy(x[6:], m.signature) + + m.raw = x + + return +} + +func (m *certificateVerifyMsg) unmarshal(data []byte) bool { + m.raw = data + + if len(data) < 6 { + return false + } + + length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) + if uint32(len(data))-4 != length { + return false + } + + siglength := int(data[4])<<8 + int(data[5]) + if len(data)-6 != siglength { + return false + } + + m.signature = data[6:] + + return true +} diff --git a/src/pkg/crypto/tls/handshake_messages_test.go b/src/pkg/crypto/tls/handshake_messages_test.go index 2e422cc6a..21577dd0b 100644 --- a/src/pkg/crypto/tls/handshake_messages_test.go +++ b/src/pkg/crypto/tls/handshake_messages_test.go @@ -16,6 +16,9 @@ var tests = []interface{}{ &serverHelloMsg{}, &certificateMsg{}, + &certificateRequestMsg{}, + &certificateVerifyMsg{}, + &certificateStatusMsg{}, &clientKeyExchangeMsg{}, &finishedMsg{}, &nextProtoMsg{}, @@ -111,6 +114,12 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value { if rand.Intn(10) > 5 { m.serverName = randomString(rand.Intn(255), rand) } + m.ocspStapling = rand.Intn(10) > 5 + m.supportedPoints = randomBytes(rand.Intn(5)+1, rand) + m.supportedCurves = make([]uint16, rand.Intn(5)+1) + for i, _ := range m.supportedCurves { + m.supportedCurves[i] = uint16(rand.Intn(30000)) + } return reflect.NewValue(m) } @@ -146,6 +155,34 @@ func (*certificateMsg) Generate(rand *rand.Rand, size int) reflect.Value { return reflect.NewValue(m) } +func (*certificateRequestMsg) Generate(rand *rand.Rand, size int) reflect.Value { + m := &certificateRequestMsg{} + m.certificateTypes = randomBytes(rand.Intn(5)+1, rand) + numCAs := rand.Intn(100) + m.certificateAuthorities = make([][]byte, numCAs) + for i := 0; i < numCAs; i++ { + m.certificateAuthorities[i] = randomBytes(rand.Intn(15)+1, rand) + } + return reflect.NewValue(m) +} + +func (*certificateVerifyMsg) Generate(rand *rand.Rand, size int) reflect.Value { + m := &certificateVerifyMsg{} + m.signature = randomBytes(rand.Intn(15)+1, rand) + return reflect.NewValue(m) +} + +func (*certificateStatusMsg) Generate(rand *rand.Rand, size int) reflect.Value { + m := &certificateStatusMsg{} + if rand.Intn(10) > 5 { + m.statusType = statusTypeOCSP + m.response = randomBytes(rand.Intn(10)+1, rand) + } else { + m.statusType = 42 + } + return reflect.NewValue(m) +} + func (*clientKeyExchangeMsg) Generate(rand *rand.Rand, size int) reflect.Value { m := &clientKeyExchangeMsg{} m.ciphertext = randomBytes(rand.Intn(1000)+1, rand) diff --git a/src/pkg/crypto/tls/handshake_server.go b/src/pkg/crypto/tls/handshake_server.go index ebf956763..955811ada 100644 --- a/src/pkg/crypto/tls/handshake_server.go +++ b/src/pkg/crypto/tls/handshake_server.go @@ -4,34 +4,14 @@ package tls -// The handshake goroutine reads handshake messages from the record processor -// and outputs messages to be written on another channel. It updates the record -// processor with the state of the connection via the control channel. In the -// case of handshake messages that need synchronous processing (because they -// affect the handling of the next record) the record processor knows about -// them and either waits for a control message (Finished) or includes a reply -// channel in the message (ChangeCipherSpec). - import ( - "crypto/hmac" - "crypto/rc4" "crypto/rsa" - "crypto/sha1" "crypto/subtle" + "crypto/x509" "io" "os" ) -type cipherSuite struct { - id uint16 // The number of this suite on the wire. - hashLength, cipherKeyLength int - // TODO(agl): need a method to create the cipher and hash interfaces. -} - -var cipherSuites = []cipherSuite{ - cipherSuite{TLS_RSA_WITH_RC4_128_SHA, 20, 16}, -} - func (c *Conn) serverHandshake() os.Error { config := c.config msg, err := c.readHandshake() @@ -54,16 +34,38 @@ func (c *Conn) serverHandshake() os.Error { hello := new(serverHelloMsg) - // We only support a single ciphersuite so we look for it in the list - // of client supported suites. - // - // TODO(agl): Add additional cipher suites. - var suite *cipherSuite + supportedCurve := false +Curves: + for _, curve := range clientHello.supportedCurves { + switch curve { + case curveP256, curveP384, curveP521: + supportedCurve = true + break Curves + } + } + supportedPointFormat := false + for _, pointFormat := range clientHello.supportedPoints { + if pointFormat == pointFormatUncompressed { + supportedPointFormat = true + break + } + } + + ellipticOk := supportedCurve && supportedPointFormat + + var suite *cipherSuite + var suiteId uint16 for _, id := range clientHello.cipherSuites { - for _, supported := range cipherSuites { - if supported.id == id { - suite = &supported + for _, supported := range config.cipherSuites() { + if id == supported { + suite = cipherSuites[id] + // Don't select a ciphersuite which we can't + // support for this client. + if suite.elliptic && !ellipticOk { + continue + } + suiteId = id break } } @@ -83,14 +85,14 @@ func (c *Conn) serverHandshake() os.Error { } hello.vers = vers - hello.cipherSuite = suite.id - t := uint32(config.Time()) + hello.cipherSuite = suiteId + t := uint32(config.time()) hello.random = make([]byte, 32) hello.random[0] = byte(t >> 24) hello.random[1] = byte(t >> 16) hello.random[2] = byte(t >> 8) hello.random[3] = byte(t) - _, err = io.ReadFull(config.Rand, hello.random[4:]) + _, err = io.ReadFull(config.rand(), hello.random[4:]) if err != nil { return c.sendAlert(alertInternalError) } @@ -112,10 +114,76 @@ func (c *Conn) serverHandshake() os.Error { finishedHash.Write(certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal()) + keyAgreement := suite.ka() + + skx, err := keyAgreement.generateServerKeyExchange(config, clientHello, hello) + if err != nil { + c.sendAlert(alertHandshakeFailure) + return err + } + if skx != nil { + finishedHash.Write(skx.marshal()) + c.writeRecord(recordTypeHandshake, skx.marshal()) + } + + if config.AuthenticateClient { + // Request a client certificate + certReq := new(certificateRequestMsg) + certReq.certificateTypes = []byte{certTypeRSASign} + // An empty list of certificateAuthorities signals to + // the client that it may send any certificate in response + // to our request. + + finishedHash.Write(certReq.marshal()) + c.writeRecord(recordTypeHandshake, certReq.marshal()) + } + helloDone := new(serverHelloDoneMsg) finishedHash.Write(helloDone.marshal()) c.writeRecord(recordTypeHandshake, helloDone.marshal()) + var pub *rsa.PublicKey + if config.AuthenticateClient { + // Get client certificate + msg, err = c.readHandshake() + if err != nil { + return err + } + certMsg, ok = msg.(*certificateMsg) + if !ok { + return c.sendAlert(alertUnexpectedMessage) + } + finishedHash.Write(certMsg.marshal()) + + certs := make([]*x509.Certificate, len(certMsg.certificates)) + for i, asn1Data := range certMsg.certificates { + cert, err := x509.ParseCertificate(asn1Data) + if err != nil { + c.sendAlert(alertBadCertificate) + return os.ErrorString("could not parse client's certificate: " + err.String()) + } + certs[i] = cert + } + + // TODO(agl): do better validation of certs: max path length, name restrictions etc. + for i := 1; i < len(certs); i++ { + if err := certs[i-1].CheckSignatureFrom(certs[i]); err != nil { + c.sendAlert(alertBadCertificate) + return os.ErrorString("could not validate certificate signature: " + err.String()) + } + } + + if len(certs) > 0 { + key, ok := certs[0].PublicKey.(*rsa.PublicKey) + if !ok { + return c.sendAlert(alertUnsupportedCertificate) + } + pub = key + c.peerCertificates = certs + } + } + + // Get client key exchange msg, err = c.readHandshake() if err != nil { return err @@ -126,28 +194,46 @@ func (c *Conn) serverHandshake() os.Error { } finishedHash.Write(ckx.marshal()) - preMasterSecret := make([]byte, 48) - _, err = io.ReadFull(config.Rand, preMasterSecret[2:]) - if err != nil { - return c.sendAlert(alertInternalError) + // If we received a client cert in response to our certificate request message, + // the client will send us a certificateVerifyMsg immediately after the + // clientKeyExchangeMsg. This message is a MD5SHA1 digest of all preceeding + // handshake-layer messages that is signed using the private key corresponding + // to the client's certificate. This allows us to verify that the client is in + // posession of the private key of the certificate. + if len(c.peerCertificates) > 0 { + msg, err = c.readHandshake() + if err != nil { + return err + } + certVerify, ok := msg.(*certificateVerifyMsg) + if !ok { + return c.sendAlert(alertUnexpectedMessage) + } + + digest := make([]byte, 36) + copy(digest[0:16], finishedHash.serverMD5.Sum()) + copy(digest[16:36], finishedHash.serverSHA1.Sum()) + err = rsa.VerifyPKCS1v15(pub, rsa.HashMD5SHA1, digest, certVerify.signature) + if err != nil { + c.sendAlert(alertBadCertificate) + return os.ErrorString("could not validate signature of connection nonces: " + err.String()) + } + + finishedHash.Write(certVerify.marshal()) } - err = rsa.DecryptPKCS1v15SessionKey(config.Rand, config.Certificates[0].PrivateKey, ckx.ciphertext, preMasterSecret) + preMasterSecret, err := keyAgreement.processClientKeyExchange(config, ckx) if err != nil { - return c.sendAlert(alertHandshakeFailure) + c.sendAlert(alertHandshakeFailure) + return err } - // We don't check the version number in the premaster secret. For one, - // by checking it, we would leak information about the validity of the - // encrypted pre-master secret. Secondly, it provides only a small - // benefit against a downgrade attack and some implementations send the - // wrong version anyway. See the discussion at the end of section - // 7.4.7.1 of RFC 4346. - masterSecret, clientMAC, serverMAC, clientKey, serverKey := - keysFromPreMasterSecret11(preMasterSecret, clientHello.random, hello.random, suite.hashLength, suite.cipherKeyLength) + masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := + keysFromPreMasterSecret10(preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen) - cipher, _ := rc4.NewCipher(clientKey) - c.in.prepareCipherSpec(cipher, hmac.New(sha1.New(), clientMAC)) + clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */ ) + clientHash := suite.mac(clientMAC) + c.in.prepareCipherSpec(clientCipher, clientHash) c.readRecord(recordTypeChangeCipherSpec) if err := c.error(); err != nil { return err @@ -183,8 +269,9 @@ func (c *Conn) serverHandshake() os.Error { finishedHash.Write(clientFinished.marshal()) - cipher2, _ := rc4.NewCipher(serverKey) - c.out.prepareCipherSpec(cipher2, hmac.New(sha1.New(), serverMAC)) + serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */ ) + serverHash := suite.mac(serverMAC) + c.out.prepareCipherSpec(serverCipher, serverHash) c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) finished := new(finishedMsg) @@ -192,7 +279,7 @@ func (c *Conn) serverHandshake() os.Error { c.writeRecord(recordTypeHandshake, finished.marshal()) c.handshakeComplete = true - c.cipherSuite = TLS_RSA_WITH_RC4_128_SHA + c.cipherSuite = suiteId return nil } diff --git a/src/pkg/crypto/tls/handshake_server_test.go b/src/pkg/crypto/tls/handshake_server_test.go index d31dc497e..5cf3ae049 100644 --- a/src/pkg/crypto/tls/handshake_server_test.go +++ b/src/pkg/crypto/tls/handshake_server_test.go @@ -5,8 +5,8 @@ package tls import ( - // "bytes" "big" + "bytes" "crypto/rsa" "encoding/hex" "flag" @@ -14,7 +14,6 @@ import ( "net" "os" "testing" - // "testing/script" ) type zeroSource struct{} @@ -36,6 +35,7 @@ func init() { testConfig.Certificates = make([]Certificate, 1) testConfig.Certificates[0].Certificate = [][]byte{testCertificate} testConfig.Certificates[0].PrivateKey = testPrivateKey + testConfig.CipherSuites = []uint16{TLS_RSA_WITH_RC4_128_SHA} } func testClientHelloFailure(t *testing.T, m handshakeMessage, expected os.Error) { @@ -71,13 +71,13 @@ func TestRejectBadProtocolVersion(t *testing.T) { } func TestNoSuiteOverlap(t *testing.T) { - clientHello := &clientHelloMsg{nil, 0x0301, nil, nil, []uint16{0xff00}, []uint8{0}, false, ""} + clientHello := &clientHelloMsg{nil, 0x0301, nil, nil, []uint16{0xff00}, []uint8{0}, false, "", false, nil, nil} testClientHelloFailure(t, clientHello, alertHandshakeFailure) } func TestNoCompressionOverlap(t *testing.T) { - clientHello := &clientHelloMsg{nil, 0x0301, nil, nil, []uint16{TLS_RSA_WITH_RC4_128_SHA}, []uint8{0xff}, false, ""} + clientHello := &clientHelloMsg{nil, 0x0301, nil, nil, []uint16{TLS_RSA_WITH_RC4_128_SHA}, []uint8{0xff}, false, "", false, nil, nil} testClientHelloFailure(t, clientHello, alertHandshakeFailure) } @@ -107,9 +107,9 @@ func TestClose(t *testing.T) { } -func TestHandshakeServer(t *testing.T) { +func testServerScript(t *testing.T, name string, serverScript [][]byte, config *Config) { c, s := net.Pipe() - srv := Server(s, testConfig) + srv := Server(s, config) go func() { srv.Write([]byte("hello, world\n")) srv.Close() @@ -124,15 +124,23 @@ func TestHandshakeServer(t *testing.T) { bb := make([]byte, len(b)) _, err := io.ReadFull(c, bb) if err != nil { - t.Fatalf("#%d: %s", i, err) + t.Fatalf("%s #%d: %s", name, i, err) + } + if !bytes.Equal(b, bb) { + t.Fatalf("%s #%d: mismatch on read: got:%x want:%x", name, i, bb, b) } } +} - if !srv.haveVers || srv.vers != 0x0302 { - t.Errorf("server version incorrect: %v %v", srv.haveVers, srv.vers) - } +func TestHandshakeServerRC4(t *testing.T) { + testServerScript(t, "RC4", rc4ServerScript, testConfig) +} - // TODO: check protocol +func TestHandshakeServerAES(t *testing.T) { + aesConfig := new(Config) + *aesConfig = *testConfig + aesConfig.CipherSuites = []uint16{TLS_RSA_WITH_AES_128_CBC_SHA} + testServerScript(t, "AES", aesServerScript, aesConfig) } var serve = flag.Bool("serve", false, "run a TLS server on :10443") @@ -152,7 +160,11 @@ func TestRunServer(t *testing.T) { if err != nil { break } - c.Write([]byte("hello, world\n")) + _, err = c.Write([]byte("hello, world\n")) + if err != nil { + t.Errorf("error from TLS: %s", err) + break + } c.Close() } } @@ -181,113 +193,324 @@ var testPrivateKey = &rsa.PrivateKey{ } // Script of interaction with gnutls implementation. -// The values for this test are obtained by building a test binary (gotest) -// and then running 6.out -serve to start a server and then -// gnutls-cli --insecure --debug 100 -p 10443 localhost -// to dump a session. -var serverScript = [][]byte{ - // Alternate write and read. - []byte{ - 0x16, 0x03, 0x02, 0x00, 0x71, 0x01, 0x00, 0x00, 0x6d, 0x03, 0x02, 0x4b, 0xd4, 0xee, 0x6e, 0xab, - 0x0b, 0xc3, 0x01, 0xd6, 0x8d, 0xe0, 0x72, 0x7e, 0x6c, 0x04, 0xbe, 0x9a, 0x3c, 0xa3, 0xd8, 0x95, - 0x28, 0x00, 0xb2, 0xe8, 0x1f, 0xdd, 0xb0, 0xec, 0xca, 0x46, 0x1f, 0x00, 0x00, 0x28, 0x00, 0x33, - 0x00, 0x39, 0x00, 0x16, 0x00, 0x32, 0x00, 0x38, 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91, - 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8c, - 0x00, 0x8d, 0x00, 0x8b, 0x00, 0x8a, 0x01, 0x00, 0x00, 0x1c, 0x00, 0x09, 0x00, 0x03, 0x02, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x0f, 0x00, 0x00, 0x0c, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, - 0x38, 0x2e, 0x30, 0x2e, 0x31, 0x30, +// The values for this test are obtained by building and running in server mode: +// % gotest -match "TestRunServer" -serve +// and then: +// % gnutls-cli --insecure --debug 100 -p 10443 localhost > /tmp/log 2>&1 +// % python parse-gnutls-cli-debug-log.py < /tmp/log +var rc4ServerScript = [][]byte{ + { + 0x16, 0x03, 0x02, 0x00, 0x7f, 0x01, 0x00, 0x00, + 0x7b, 0x03, 0x02, 0x4d, 0x08, 0x1f, 0x5a, 0x7a, + 0x0a, 0x92, 0x2f, 0xf0, 0x73, 0x16, 0x3a, 0x88, + 0x14, 0x85, 0x4c, 0x98, 0x15, 0x7b, 0x65, 0xe0, + 0x78, 0xd0, 0xed, 0xd0, 0xf3, 0x65, 0x20, 0xeb, + 0x80, 0xd1, 0x0b, 0x00, 0x00, 0x34, 0x00, 0x33, + 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16, + 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87, + 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91, + 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41, + 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05, + 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b, + 0x00, 0x8a, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x09, + 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f, + 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0xff, + 0x01, 0x00, 0x01, 0x00, }, - []byte{ - 0x16, 0x03, 0x02, 0x00, 0x2a, - 0x02, 0x00, 0x00, 0x26, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, - - 0x16, 0x03, 0x02, 0x02, 0xbe, - 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0, 0x30, 0x82, - 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, - 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, - 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, - 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, - 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x34, - 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x34, 0x32, - 0x34, 0x30, 0x39, 0x30, 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, - 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, - 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, - 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, - 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, - 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, - 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79, 0xd6, 0xf5, - 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43, 0x5a, 0xd0, - 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c, 0x78, 0xb8, - 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c, 0xa5, 0x33, - 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26, 0x3f, 0xb5, - 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf, 0xef, 0x42, - 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39, 0xc4, 0xa2, - 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf, 0xb1, 0x1d, - 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03, 0x01, 0x00, - 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, - 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23, 0x69, 0xde, - 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x6e, 0x30, - 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23, 0x69, - 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, 0x0b, 0x30, - 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, - 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, - 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, - 0x74, 0x64, 0x82, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0c, 0x06, - 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, 0x09, 0x2a, - 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x08, 0x6c, - 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7, 0x87, 0x9d, - 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66, 0x1f, 0xeb, - 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13, 0xb1, 0x18, - 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31, 0x59, 0xdb, - 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f, 0x33, 0xc4, - 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f, 0x89, 0x20, - 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70, 0xe8, 0x26, - 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9, - 0x16, 0x03, 0x02, 0x00, 0x04, - 0x0e, 0x00, 0x00, 0x00, + { + 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, + 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, + 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, + 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, + 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, + 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, + 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, + 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, + 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, + 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, + 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, + 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, + 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, + 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, + 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, + 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, + 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, + 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, + 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, + 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, + 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, + 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, + 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, + 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, + 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, + 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, + 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, + 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, + 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, + 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, + 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, + 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, + 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, + 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, + 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, + 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, + 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, + 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, + 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, + 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, + 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, + 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, + 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, + 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, + 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, + 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, + 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, + 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, + 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, + 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, + 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, + 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, + 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, + 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, + 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, + 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, + 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, + 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, + 0x00, 0x00, 0x00, }, - []byte{ - 0x16, 0x03, 0x02, 0x00, 0x86, 0x10, 0x00, 0x00, 0x82, 0x00, 0x80, 0x3b, 0x7a, 0x9b, 0x05, 0xfd, - 0x1b, 0x0d, 0x81, 0xf0, 0xac, 0x59, 0x57, 0x4e, 0xb6, 0xf5, 0x81, 0xed, 0x52, 0x78, 0xc5, 0xff, - 0x36, 0x33, 0x9c, 0x94, 0x31, 0xc3, 0x14, 0x98, 0x5d, 0xa0, 0x49, 0x23, 0x11, 0x67, 0xdf, 0x73, - 0x1b, 0x81, 0x0b, 0xdd, 0x10, 0xda, 0xee, 0xb5, 0x68, 0x61, 0xa9, 0xb6, 0x15, 0xae, 0x1a, 0x11, - 0x31, 0x42, 0x2e, 0xde, 0x01, 0x4b, 0x81, 0x70, 0x03, 0xc8, 0x5b, 0xca, 0x21, 0x88, 0x25, 0xef, - 0x89, 0xf0, 0xb7, 0xff, 0x24, 0x32, 0xd3, 0x14, 0x76, 0xe2, 0x50, 0x5c, 0x2e, 0x75, 0x9d, 0x5c, - 0xa9, 0x80, 0x3d, 0x6f, 0xd5, 0x46, 0xd3, 0xdb, 0x42, 0x6e, 0x55, 0x81, 0x88, 0x42, 0x0e, 0x45, - 0xfe, 0x9e, 0xe4, 0x41, 0x79, 0xcf, 0x71, 0x0e, 0xed, 0x27, 0xa8, 0x20, 0x05, 0xe9, 0x7a, 0x42, - 0x4f, 0x05, 0x10, 0x2e, 0x52, 0x5d, 0x8c, 0x3c, 0x40, 0x49, 0x4c, - - 0x14, 0x03, 0x02, 0x00, 0x01, 0x01, - - 0x16, 0x03, 0x02, 0x00, 0x24, 0x8b, 0x12, 0x24, 0x06, 0xaa, 0x92, 0x74, 0xa1, 0x46, 0x6f, 0xc1, - 0x4e, 0x4a, 0xf7, 0x16, 0xdd, 0xd6, 0xe1, 0x2d, 0x37, 0x0b, 0x44, 0xba, 0xeb, 0xc4, 0x6c, 0xc7, - 0xa0, 0xb7, 0x8c, 0x9d, 0x24, 0xbd, 0x99, 0x33, 0x1e, + { + 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, + 0x82, 0x00, 0x80, 0x3c, 0x13, 0xd7, 0x12, 0xc1, + 0x6a, 0xf0, 0x3f, 0x8c, 0xa1, 0x35, 0x5d, 0xc5, + 0x89, 0x1e, 0x9e, 0xcd, 0x32, 0xc7, 0x9e, 0xe6, + 0xae, 0xd5, 0xf1, 0xbf, 0x70, 0xd7, 0xa9, 0xef, + 0x2c, 0x4c, 0xf4, 0x22, 0xbc, 0x17, 0x17, 0xaa, + 0x05, 0xf3, 0x9f, 0x80, 0xf2, 0xe9, 0x82, 0x2f, + 0x2a, 0x15, 0x54, 0x0d, 0x16, 0x0e, 0x77, 0x4c, + 0x28, 0x3c, 0x03, 0x2d, 0x2d, 0xd7, 0xc8, 0x64, + 0xd9, 0x59, 0x4b, 0x1c, 0xf4, 0xde, 0xff, 0x2f, + 0xbc, 0x94, 0xaf, 0x18, 0x26, 0x37, 0xce, 0x4f, + 0x84, 0x74, 0x2e, 0x45, 0x66, 0x7c, 0x0c, 0x54, + 0x46, 0x36, 0x5f, 0x65, 0x21, 0x7b, 0x83, 0x8c, + 0x6d, 0x76, 0xcd, 0x0d, 0x9f, 0xda, 0x1c, 0xa4, + 0x6e, 0xfe, 0xb1, 0xf7, 0x09, 0x0d, 0xfb, 0x74, + 0x66, 0x34, 0x99, 0x89, 0x7f, 0x5f, 0x77, 0x87, + 0x4a, 0x66, 0x4b, 0xa9, 0x59, 0x57, 0xe3, 0x56, + 0x0d, 0xdd, 0xd8, 0x14, 0x03, 0x01, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xc0, 0x4e, + 0xd3, 0x0f, 0xb5, 0xc0, 0x57, 0xa6, 0x18, 0x80, + 0x80, 0x6b, 0x49, 0xfe, 0xbd, 0x3a, 0x7a, 0x2c, + 0xef, 0x70, 0xb5, 0x1c, 0xd2, 0xdf, 0x5f, 0x78, + 0x5a, 0xd8, 0x4f, 0xa0, 0x95, 0xb4, 0xb3, 0xb5, + 0xaa, 0x3b, }, - []byte{ - 0x14, 0x03, 0x02, 0x00, 0x01, - 0x01, + { + 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x01, 0x00, 0x24, 0x9d, 0xc9, 0xda, 0xdf, 0xeb, + 0xc8, 0xdb, 0xf8, 0x94, 0xa5, 0xef, 0xd5, 0xfc, + 0x89, 0x01, 0x64, 0x30, 0x77, 0x5a, 0x18, 0x4b, + 0x16, 0x79, 0x9c, 0xf6, 0xf5, 0x09, 0x22, 0x12, + 0x4c, 0x3e, 0xa8, 0x8e, 0x91, 0xa5, 0x24, + }, +} - 0x16, 0x03, 0x02, 0x00, 0x24, - 0x6e, 0xd1, 0x3e, 0x49, 0x68, 0xc1, 0xa0, 0xa5, 0xb7, 0xaf, 0xb0, 0x7c, 0x52, 0x1f, 0xf7, 0x2d, - 0x51, 0xf3, 0xa5, 0xb6, 0xf6, 0xd4, 0x18, 0x4b, 0x7a, 0xd5, 0x24, 0x1d, 0x09, 0xb6, 0x41, 0x1c, - 0x1c, 0x98, 0xf6, 0x90, +var aesServerScript = [][]byte{ + { + 0x16, 0x03, 0x02, 0x00, 0x7f, 0x01, 0x00, 0x00, + 0x7b, 0x03, 0x02, 0x4d, 0x08, 0x2d, 0x0b, 0xb3, + 0x57, 0x85, 0x71, 0x4b, 0xfb, 0x34, 0xab, 0x16, + 0xd4, 0x92, 0x50, 0x81, 0x16, 0x95, 0x11, 0x28, + 0x1a, 0xcb, 0xff, 0x09, 0x4d, 0x23, 0xa6, 0xfe, + 0x2e, 0xbb, 0x78, 0x00, 0x00, 0x34, 0x00, 0x33, + 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16, + 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87, + 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91, + 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41, + 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05, + 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b, + 0x00, 0x8a, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x09, + 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f, + 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0xff, + 0x01, 0x00, 0x01, 0x00, + }, - 0x17, 0x03, 0x02, 0x00, 0x21, - 0x50, 0xb7, 0x92, 0x4f, 0xd8, 0x78, 0x29, 0xa2, 0xe7, 0xa5, 0xa6, 0xbd, 0x1a, 0x0c, 0xf1, 0x5a, - 0x6e, 0x6c, 0xeb, 0x38, 0x99, 0x9b, 0x3c, 0xfd, 0xee, 0x53, 0xe8, 0x4d, 0x7b, 0xa5, 0x5b, 0x00, + { + 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, + 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x16, + 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, + 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, + 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, + 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, + 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, + 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, + 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, + 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, + 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, + 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, + 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, + 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, + 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, + 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, + 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, + 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, + 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, + 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, + 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, + 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, + 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, + 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, + 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, + 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, + 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, + 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, + 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, + 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, + 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, + 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, + 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, + 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, + 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, + 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, + 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, + 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, + 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, + 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, + 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, + 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, + 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, + 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, + 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, + 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, + 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, + 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, + 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, + 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, + 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, + 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, + 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, + 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, + 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, + 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, + 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, + 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, + 0x00, 0x00, 0x00, + }, - 0xb9, + { + 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, + 0x82, 0x00, 0x80, 0x71, 0x9c, 0xe7, 0x23, 0xfc, + 0xb9, 0x19, 0x29, 0x82, 0xbf, 0xef, 0x08, 0xf7, + 0x99, 0x36, 0xc3, 0x4c, 0x6f, 0x05, 0xd2, 0x8b, + 0x62, 0x2b, 0x19, 0x9b, 0x7f, 0xc0, 0xcc, 0x48, + 0x30, 0x5f, 0xcd, 0xc3, 0x70, 0x55, 0x53, 0x73, + 0xfa, 0x79, 0x74, 0xf3, 0xa3, 0x76, 0x9f, 0xa1, + 0x7f, 0x98, 0xc2, 0xc0, 0xe3, 0xc5, 0xa0, 0x31, + 0x2f, 0xa6, 0xe8, 0x1e, 0x61, 0x46, 0xb3, 0x9b, + 0x4b, 0x16, 0xf1, 0x2d, 0xc7, 0x63, 0x7f, 0x79, + 0x22, 0x30, 0xd1, 0xf2, 0xfc, 0x77, 0x98, 0x0a, + 0x16, 0x11, 0x63, 0x71, 0x7f, 0x70, 0xef, 0x16, + 0xbb, 0x39, 0x87, 0x34, 0xac, 0x49, 0xbd, 0x07, + 0x67, 0xcb, 0x9c, 0xcc, 0xde, 0xef, 0xb1, 0xe0, + 0xdb, 0x01, 0xb5, 0x35, 0xa9, 0xb3, 0x10, 0x0c, + 0x4b, 0xee, 0xb3, 0x4e, 0xfd, 0xbe, 0x15, 0x27, + 0xf0, 0x46, 0xb2, 0x38, 0xba, 0x5f, 0xcc, 0x89, + 0xec, 0x29, 0x82, 0x14, 0x03, 0x01, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0x3c, 0xfb, + 0xa4, 0x12, 0xcb, 0x00, 0xf9, 0x57, 0x7e, 0x9b, + 0xc9, 0xdc, 0x0c, 0xba, 0x9a, 0x81, 0x62, 0xfb, + 0x26, 0x13, 0x53, 0xfe, 0xaa, 0xcc, 0x82, 0xbb, + 0xb6, 0x67, 0x7f, 0x39, 0xbe, 0x4d, 0xbb, 0xc0, + 0x6c, 0x24, 0x31, 0x83, 0xa5, 0x50, 0x3a, 0x75, + 0x32, 0x64, 0xb5, 0xdb, 0xbe, 0x0a, + }, - 0x15, 0x03, 0x02, 0x00, 0x16, - 0xc7, 0xc9, 0x5a, 0x72, 0xfb, 0x02, 0xa5, 0x93, 0xdd, 0x69, 0xeb, 0x30, 0x68, 0x5e, 0xbc, 0xe0, - 0x44, 0xb9, 0x59, 0x33, 0x68, 0xa9, + { + 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x01, 0x00, 0x30, 0x43, 0x24, 0x42, 0x55, 0x08, + 0xe4, 0xc2, 0x15, 0xc9, 0xdb, 0x71, 0x69, 0xee, + 0x09, 0xc5, 0x1c, 0xfd, 0x46, 0x10, 0xa0, 0x68, + 0x21, 0xf2, 0x48, 0xac, 0x6c, 0xc0, 0x2b, 0x62, + 0x07, 0x8f, 0x48, 0x33, 0x0a, 0x6b, 0x62, 0x28, + 0x2e, 0x2c, 0xad, 0xcb, 0x34, 0x85, 0xca, 0x2e, + 0xcd, 0x84, 0xf0, }, } diff --git a/src/pkg/crypto/tls/key_agreement.go b/src/pkg/crypto/tls/key_agreement.go new file mode 100644 index 000000000..861c64f04 --- /dev/null +++ b/src/pkg/crypto/tls/key_agreement.go @@ -0,0 +1,246 @@ +// 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 tls + +import ( + "big" + "crypto/elliptic" + "crypto/md5" + "crypto/rsa" + "crypto/sha1" + "crypto/x509" + "io" + "os" +) + +// rsaKeyAgreement implements the standard TLS key agreement where the client +// encrypts the pre-master secret to the server's public key. +type rsaKeyAgreement struct{} + +func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, os.Error) { + return nil, nil +} + +func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg) ([]byte, os.Error) { + preMasterSecret := make([]byte, 48) + _, err := io.ReadFull(config.rand(), preMasterSecret[2:]) + if err != nil { + return nil, err + } + + if len(ckx.ciphertext) < 2 { + return nil, os.ErrorString("bad ClientKeyExchange") + } + ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1]) + if ciphertextLen != len(ckx.ciphertext)-2 { + return nil, os.ErrorString("bad ClientKeyExchange") + } + ciphertext := ckx.ciphertext[2:] + + err = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey, ciphertext, preMasterSecret) + if err != nil { + return nil, err + } + // We don't check the version number in the premaster secret. For one, + // by checking it, we would leak information about the validity of the + // encrypted pre-master secret. Secondly, it provides only a small + // benefit against a downgrade attack and some implementations send the + // wrong version anyway. See the discussion at the end of section + // 7.4.7.1 of RFC 4346. + return preMasterSecret, nil +} + +func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) os.Error { + return os.ErrorString("unexpected ServerKeyExchange") +} + +func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, os.Error) { + preMasterSecret := make([]byte, 48) + preMasterSecret[0] = byte(clientHello.vers >> 8) + preMasterSecret[1] = byte(clientHello.vers) + _, err := io.ReadFull(config.rand(), preMasterSecret[2:]) + if err != nil { + return nil, nil, err + } + + encrypted, err := rsa.EncryptPKCS1v15(config.rand(), cert.PublicKey.(*rsa.PublicKey), preMasterSecret) + if err != nil { + return nil, nil, err + } + ckx := new(clientKeyExchangeMsg) + ckx.ciphertext = make([]byte, len(encrypted)+2) + ckx.ciphertext[0] = byte(len(encrypted) >> 8) + ckx.ciphertext[1] = byte(len(encrypted)) + copy(ckx.ciphertext[2:], encrypted) + return preMasterSecret, ckx, nil +} + + +// md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the +// concatenation of an MD5 and SHA1 hash. +func md5SHA1Hash(slices ...[]byte) []byte { + md5sha1 := make([]byte, md5.Size+sha1.Size) + hmd5 := md5.New() + for _, slice := range slices { + hmd5.Write(slice) + } + copy(md5sha1, hmd5.Sum()) + + hsha1 := sha1.New() + for _, slice := range slices { + hsha1.Write(slice) + } + copy(md5sha1[md5.Size:], hsha1.Sum()) + return md5sha1 +} + +// ecdheRSAKeyAgreement implements a TLS key agreement where the server +// generates a ephemeral EC public/private key pair and signs it. The +// pre-master secret is then calculated using ECDH. +type ecdheRSAKeyAgreement struct { + privateKey []byte + curve *elliptic.Curve + x, y *big.Int +} + +func (ka *ecdheRSAKeyAgreement) generateServerKeyExchange(config *Config, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, os.Error) { + var curveid uint16 + +Curve: + for _, c := range clientHello.supportedCurves { + switch c { + case curveP256: + ka.curve = elliptic.P256() + curveid = c + break Curve + case curveP384: + ka.curve = elliptic.P384() + curveid = c + break Curve + case curveP521: + ka.curve = elliptic.P521() + curveid = c + break Curve + } + } + + var x, y *big.Int + var err os.Error + ka.privateKey, x, y, err = ka.curve.GenerateKey(config.rand()) + if err != nil { + return nil, err + } + ecdhePublic := ka.curve.Marshal(x, y) + + // http://tools.ietf.org/html/rfc4492#section-5.4 + serverECDHParams := make([]byte, 1+2+1+len(ecdhePublic)) + serverECDHParams[0] = 3 // named curve + serverECDHParams[1] = byte(curveid >> 8) + serverECDHParams[2] = byte(curveid) + serverECDHParams[3] = byte(len(ecdhePublic)) + copy(serverECDHParams[4:], ecdhePublic) + + md5sha1 := md5SHA1Hash(clientHello.random, hello.random, serverECDHParams) + sig, err := rsa.SignPKCS1v15(config.rand(), config.Certificates[0].PrivateKey, rsa.HashMD5SHA1, md5sha1) + if err != nil { + return nil, os.ErrorString("failed to sign ECDHE parameters: " + err.String()) + } + + skx := new(serverKeyExchangeMsg) + skx.key = make([]byte, len(serverECDHParams)+2+len(sig)) + copy(skx.key, serverECDHParams) + k := skx.key[len(serverECDHParams):] + k[0] = byte(len(sig) >> 8) + k[1] = byte(len(sig)) + copy(k[2:], sig) + + return skx, nil +} + +func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg) ([]byte, os.Error) { + if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 { + return nil, os.ErrorString("bad ClientKeyExchange") + } + x, y := ka.curve.Unmarshal(ckx.ciphertext[1:]) + if x == nil { + return nil, os.ErrorString("bad ClientKeyExchange") + } + x, _ = ka.curve.ScalarMult(x, y, ka.privateKey) + preMasterSecret := make([]byte, (ka.curve.BitSize+7)>>3) + xBytes := x.Bytes() + copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes) + + return preMasterSecret, nil +} + +func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) os.Error { + if len(skx.key) < 4 { + goto Error + } + if skx.key[0] != 3 { // named curve + return os.ErrorString("server selected unsupported curve") + } + curveid := uint16(skx.key[1])<<8 | uint16(skx.key[2]) + + switch curveid { + case curveP256: + ka.curve = elliptic.P256() + case curveP384: + ka.curve = elliptic.P384() + case curveP521: + ka.curve = elliptic.P521() + default: + return os.ErrorString("server selected unsupported curve") + } + + publicLen := int(skx.key[3]) + if publicLen+4 > len(skx.key) { + goto Error + } + ka.x, ka.y = ka.curve.Unmarshal(skx.key[4 : 4+publicLen]) + if ka.x == nil { + goto Error + } + serverECDHParams := skx.key[:4+publicLen] + + sig := skx.key[4+publicLen:] + if len(sig) < 2 { + goto Error + } + sigLen := int(sig[0])<<8 | int(sig[1]) + if sigLen+2 != len(sig) { + goto Error + } + sig = sig[2:] + + md5sha1 := md5SHA1Hash(clientHello.random, serverHello.random, serverECDHParams) + return rsa.VerifyPKCS1v15(cert.PublicKey.(*rsa.PublicKey), rsa.HashMD5SHA1, md5sha1, sig) + +Error: + return os.ErrorString("invalid ServerKeyExchange") +} + +func (ka *ecdheRSAKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, os.Error) { + if ka.curve == nil { + return nil, nil, os.ErrorString("missing ServerKeyExchange message") + } + priv, mx, my, err := ka.curve.GenerateKey(config.rand()) + if err != nil { + return nil, nil, err + } + x, _ := ka.curve.ScalarMult(ka.x, ka.y, priv) + preMasterSecret := make([]byte, (ka.curve.BitSize+7)>>3) + xBytes := x.Bytes() + copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes) + + serialised := ka.curve.Marshal(mx, my) + + ckx := new(clientKeyExchangeMsg) + ckx.ciphertext = make([]byte, 1+len(serialised)) + ckx.ciphertext[0] = byte(len(serialised)) + copy(ckx.ciphertext[1:], serialised) + + return preMasterSecret, ckx, nil +} diff --git a/src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py b/src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py new file mode 100644 index 000000000..c03eaa6ea --- /dev/null +++ b/src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py @@ -0,0 +1,55 @@ +# 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 code is used to parse the debug log from gnutls-cli and generate a +# script of the handshake. This script is included in handshake_server_test.go. +# See the comments there for details. + +import sys + +blocks = [] + +READ = 1 +WRITE = 2 + +currentBlockType = 0 +currentBlock = [] +for line in sys.stdin.readlines(): + line = line[:-1] + if line.startswith("|<7>| WRITE: "): + if currentBlockType != WRITE: + if len(currentBlock) > 0: + blocks.append(currentBlock) + currentBlock = [] + currentBlockType = WRITE + elif line.startswith("|<7>| READ: "): + if currentBlockType != READ: + if len(currentBlock) > 0: + blocks.append(currentBlock) + currentBlock = [] + currentBlockType = READ + elif line.startswith("|<7>| 0"): + line = line[13:] + line = line.strip() + bs = line.split() + for b in bs: + currentBlock.append(int(b, 16)) + +if len(currentBlock) > 0: + blocks.append(currentBlock) + +for block in blocks: + sys.stdout.write("\t{\n") + + i = 0 + for b in block: + if i % 8 == 0: + sys.stdout.write("\t\t") + sys.stdout.write("0x%02x," % b) + if i % 8 == 7: + sys.stdout.write("\n") + else: + sys.stdout.write(" ") + i += 1 + sys.stdout.write("\n\t},\n\n") diff --git a/src/pkg/crypto/tls/prf.go b/src/pkg/crypto/tls/prf.go index ee6cb780b..478cf65f9 100644 --- a/src/pkg/crypto/tls/prf.go +++ b/src/pkg/crypto/tls/prf.go @@ -20,7 +20,7 @@ func splitPreMasterSecret(secret []byte) (s1, s2 []byte) { } // pHash implements the P_hash function, as defined in RFC 4346, section 5. -func pHash(result, secret, seed []byte, hash hash.Hash) { +func pHash(result, secret, seed []byte, hash func() hash.Hash) { h := hmac.New(hash, secret) h.Write(seed) a := h.Sum() @@ -44,10 +44,10 @@ func pHash(result, secret, seed []byte, hash hash.Hash) { } } -// pRF11 implements the TLS 1.1 pseudo-random function, as defined in RFC 4346, section 5. -func pRF11(result, secret, label, seed []byte) { - hashSHA1 := sha1.New() - hashMD5 := md5.New() +// pRF10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, section 5. +func pRF10(result, secret, label, seed []byte) { + hashSHA1 := sha1.New + hashMD5 := md5.New labelAndSeed := make([]byte, len(label)+len(seed)) copy(labelAndSeed, label) @@ -75,25 +75,32 @@ var clientFinishedLabel = []byte("client finished") var serverFinishedLabel = []byte("server finished") // keysFromPreMasterSecret generates the connection keys from the pre master -// secret, given the lengths of the MAC and cipher keys, as defined in RFC -// 4346, section 6.3. -func keysFromPreMasterSecret11(preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey []byte) { +// secret, given the lengths of the MAC key, cipher key and IV, as defined in +// RFC 2246, section 6.3. +func keysFromPreMasterSecret10(preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { var seed [tlsRandomLength * 2]byte copy(seed[0:len(clientRandom)], clientRandom) copy(seed[len(clientRandom):], serverRandom) masterSecret = make([]byte, masterSecretLength) - pRF11(masterSecret, preMasterSecret, masterSecretLabel, seed[0:]) + pRF10(masterSecret, preMasterSecret, masterSecretLabel, seed[0:]) copy(seed[0:len(clientRandom)], serverRandom) copy(seed[len(serverRandom):], clientRandom) - n := 2*macLen + 2*keyLen + n := 2*macLen + 2*keyLen + 2*ivLen keyMaterial := make([]byte, n) - pRF11(keyMaterial, masterSecret, keyExpansionLabel, seed[0:]) - clientMAC = keyMaterial[0:macLen] - serverMAC = keyMaterial[macLen : macLen*2] - clientKey = keyMaterial[macLen*2 : macLen*2+keyLen] - serverKey = keyMaterial[macLen*2+keyLen:] + pRF10(keyMaterial, masterSecret, keyExpansionLabel, seed[0:]) + clientMAC = keyMaterial[:macLen] + keyMaterial = keyMaterial[macLen:] + serverMAC = keyMaterial[:macLen] + keyMaterial = keyMaterial[macLen:] + clientKey = keyMaterial[:keyLen] + keyMaterial = keyMaterial[keyLen:] + serverKey = keyMaterial[:keyLen] + keyMaterial = keyMaterial[keyLen:] + clientIV = keyMaterial[:ivLen] + keyMaterial = keyMaterial[ivLen:] + serverIV = keyMaterial[:ivLen] return } @@ -125,7 +132,7 @@ func finishedSum(md5, sha1, label, masterSecret []byte) []byte { copy(seed, md5) copy(seed[len(md5):], sha1) out := make([]byte, finishedVerifyLength) - pRF11(out, masterSecret, label, seed) + pRF10(out, masterSecret, label, seed) return out } diff --git a/src/pkg/crypto/tls/prf_test.go b/src/pkg/crypto/tls/prf_test.go index 5c23f368d..f8c4acb9d 100644 --- a/src/pkg/crypto/tls/prf_test.go +++ b/src/pkg/crypto/tls/prf_test.go @@ -14,11 +14,11 @@ type testSplitPreMasterSecretTest struct { } var testSplitPreMasterSecretTests = []testSplitPreMasterSecretTest{ - testSplitPreMasterSecretTest{"", "", ""}, - testSplitPreMasterSecretTest{"00", "00", "00"}, - testSplitPreMasterSecretTest{"0011", "00", "11"}, - testSplitPreMasterSecretTest{"001122", "0011", "1122"}, - testSplitPreMasterSecretTest{"00112233", "0011", "2233"}, + {"", "", ""}, + {"00", "00", "00"}, + {"0011", "00", "11"}, + {"001122", "0011", "1122"}, + {"00112233", "0011", "2233"}, } func TestSplitPreMasterSecret(t *testing.T) { @@ -47,7 +47,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) { in, _ := hex.DecodeString(test.preMasterSecret) clientRandom, _ := hex.DecodeString(test.clientRandom) serverRandom, _ := hex.DecodeString(test.serverRandom) - master, clientMAC, serverMAC, clientKey, serverKey := keysFromPreMasterSecret11(in, clientRandom, serverRandom, test.macLen, test.keyLen) + master, clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromPreMasterSecret10(in, clientRandom, serverRandom, test.macLen, test.keyLen, 0) masterString := hex.EncodeToString(master) clientMACString := hex.EncodeToString(clientMAC) serverMACString := hex.EncodeToString(serverMAC) @@ -65,7 +65,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) { // These test vectors were generated from GnuTLS using `gnutls-cli --insecure -d 9 ` var testKeysFromTests = []testKeysFromTest{ - testKeysFromTest{ + { "0302cac83ad4b1db3b9ab49ad05957de2a504a634a386fc600889321e1a971f57479466830ac3e6f468e87f5385fa0c5", "4ae66303755184a3917fcb44880605fcc53baa01912b22ed94473fc69cebd558", "4ae663020ec16e6bb5130be918cfcafd4d765979a3136a5d50c593446e4e44db", @@ -77,7 +77,7 @@ var testKeysFromTests = []testKeysFromTest{ 20, 16, }, - testKeysFromTest{ + { "03023f7527316bc12cbcd69e4b9e8275d62c028f27e65c745cfcddc7ce01bd3570a111378b63848127f1c36e5f9e4890", "4ae66364b5ea56b20ce4e25555aed2d7e67f42788dd03f3fee4adae0459ab106", "4ae66363ab815cbf6a248b87d6b556184e945e9b97fbdf247858b0bdafacfa1c", @@ -89,7 +89,7 @@ var testKeysFromTests = []testKeysFromTest{ 20, 16, }, - testKeysFromTest{ + { "832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1", "4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e", "4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e", diff --git a/src/pkg/crypto/tls/tls.go b/src/pkg/crypto/tls/tls.go index 1a5da3ac4..b11d3225d 100644 --- a/src/pkg/crypto/tls/tls.go +++ b/src/pkg/crypto/tls/tls.go @@ -6,23 +6,40 @@ package tls import ( - "os" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "io/ioutil" "net" + "os" + "strings" ) +// Server returns a new TLS server side connection +// using conn as the underlying transport. +// The configuration config must be non-nil and must have +// at least one certificate. func Server(conn net.Conn, config *Config) *Conn { return &Conn{conn: conn, config: config} } +// Client returns a new TLS client side connection +// using conn as the underlying transport. +// Client interprets a nil configuration as equivalent to +// the zero configuration; see the documentation of Config +// for the defaults. func Client(conn net.Conn, config *Config) *Conn { return &Conn{conn: conn, config: config, isClient: true} } +// A Listener implements a network listener (net.Listener) for TLS connections. type Listener struct { listener net.Listener config *Config } +// Accept waits for and returns the next incoming TLS connection. +// The returned connection c is a *tls.Conn. func (l *Listener) Accept() (c net.Conn, err os.Error) { c, err = l.listener.Accept() if err != nil { @@ -32,8 +49,10 @@ func (l *Listener) Accept() (c net.Conn, err os.Error) { return } +// Close closes the listener. func (l *Listener) Close() os.Error { return l.listener.Close() } +// Addr returns the listener's network address. func (l *Listener) Addr() net.Addr { return l.listener.Addr() } // NewListener creates a Listener which accepts connections from an inner @@ -47,7 +66,11 @@ func NewListener(listener net.Listener, config *Config) (l *Listener) { return } -func Listen(network, laddr string, config *Config) (net.Listener, os.Error) { +// Listen creates a TLS listener accepting connections on the +// given network address using net.Listen. +// The configuration config must be non-nil and must have +// at least one certificate. +func Listen(network, laddr string, config *Config) (*Listener, os.Error) { if config == nil || len(config.Certificates) == 0 { return nil, os.NewError("tls.Listen: no certificates in configuration") } @@ -58,10 +81,87 @@ func Listen(network, laddr string, config *Config) (net.Listener, os.Error) { return NewListener(l, config), nil } -func Dial(network, laddr, raddr string) (net.Conn, os.Error) { +// Dial connects to the given network address using net.Dial +// and then initiates a TLS handshake, returning the resulting +// TLS connection. +// Dial interprets a nil configuration as equivalent to +// the zero configuration; see the documentation of Config +// for the defaults. +func Dial(network, laddr, raddr string, config *Config) (*Conn, os.Error) { c, err := net.Dial(network, laddr, raddr) if err != nil { return nil, err } - return Client(c, nil), nil + + colonPos := strings.LastIndex(raddr, ":") + if colonPos == -1 { + colonPos = len(raddr) + } + hostname := raddr[:colonPos] + + if config == nil { + config = defaultConfig() + } + if config.ServerName != "" { + // Make a copy to avoid polluting argument or default. + c := *config + c.ServerName = hostname + config = &c + } + conn := Client(c, config) + if err = conn.Handshake(); err != nil { + c.Close() + return nil, err + } + return conn, nil +} + +// LoadX509KeyPair reads and parses a public/private key pair from a pair of +// files. The files must contain PEM encoded data. +func LoadX509KeyPair(certFile string, keyFile string) (cert Certificate, err os.Error) { + certPEMBlock, err := ioutil.ReadFile(certFile) + if err != nil { + return + } + + certDERBlock, _ := pem.Decode(certPEMBlock) + if certDERBlock == nil { + err = os.ErrorString("crypto/tls: failed to parse certificate PEM data") + return + } + + cert.Certificate = [][]byte{certDERBlock.Bytes} + + keyPEMBlock, err := ioutil.ReadFile(keyFile) + if err != nil { + return + } + + keyDERBlock, _ := pem.Decode(keyPEMBlock) + if keyDERBlock == nil { + err = os.ErrorString("crypto/tls: failed to parse key PEM data") + return + } + + key, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes) + if err != nil { + err = os.ErrorString("crypto/tls: failed to parse key") + return + } + + cert.PrivateKey = key + + // We don't need to parse the public key for TLS, but we so do anyway + // to check that it looks sane and matches the private key. + x509Cert, err := x509.ParseCertificate(certDERBlock.Bytes) + if err != nil { + return + } + + if x509Cert.PublicKeyAlgorithm != x509.RSA || x509Cert.PublicKey.(*rsa.PublicKey).N.Cmp(key.PublicKey.N) != 0 { + err = os.ErrorString("crypto/tls: private key does not match public key") + return + } + + return } diff --git a/src/pkg/crypto/twofish/Makefile b/src/pkg/crypto/twofish/Makefile new file mode 100644 index 000000000..aec61659d --- /dev/null +++ b/src/pkg/crypto/twofish/Makefile @@ -0,0 +1,11 @@ +# Copyright 2011 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../../Make.inc + +TARG=crypto/twofish +GOFILES=\ + twofish.go\ + +include ../../../Make.pkg diff --git a/src/pkg/crypto/twofish/twofish.go b/src/pkg/crypto/twofish/twofish.go new file mode 100644 index 000000000..b362c44d2 --- /dev/null +++ b/src/pkg/crypto/twofish/twofish.go @@ -0,0 +1,358 @@ +// 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 package implements Bruce Schneier's Twofish encryption algorithm. +package twofish + +// Twofish is defined in http://www.schneier.com/paper-twofish-paper.pdf [TWOFISH] + +// This code is a port of the LibTom C implementation. +// See http://libtom.org/?page=features&newsitems=5&whatfile=crypt. +// LibTomCrypt is free for all purposes under the public domain. +// It was heavily inspired by the go blowfish package. + +import ( + "os" + "strconv" +) + +// BlockSize is the constant block size of Twofish. +const BlockSize = 16 + +const mdsPolynomial = 0x169 // x^8 + x^6 + x^5 + x^3 + 1, see [TWOFISH] 4.2 +const rsPolynomial = 0x14d // x^8 + x^6 + x^3 + x^2 + 1, see [TWOFISH] 4.3 + +// A Cipher is an instance of Twofish encryption using a particular key. +type Cipher struct { + s [4][256]uint32 + k [40]uint32 +} + +type KeySizeError int + +func (k KeySizeError) String() string { + return "crypto/twofish: invalid key size " + strconv.Itoa(int(k)) +} + +// NewCipher creates and returns a Cipher. +// The key argument should be the Twofish key, 16, 24 or 32 bytes. +func NewCipher(key []byte) (*Cipher, os.Error) { + keylen := len(key) + + if keylen != 16 && keylen != 24 && keylen != 32 { + return nil, KeySizeError(keylen) + } + + // k is the number of 64 bit words in key + k := keylen / 8 + + // Create the S[..] words + var S [4 * 4]byte + for i := 0; i < k; i++ { + // Computes [y0 y1 y2 y3] = rs . [x0 x1 x2 x3 x4 x5 x6 x7] + for j := 0; j < 4; j++ { + for k := 0; k < 8; k++ { + S[4*i+j] ^= gfMult(key[8*i+k], rs[j][k], rsPolynomial) + } + } + } + + // Calculate subkeys + c := new(Cipher) + var tmp [4]byte + for i := byte(0); i < 20; i++ { + // A = h(p * 2x, Me) + for j := 0; j < 4; j++ { + tmp[j] = 2 * i + } + A := h(tmp[:], key, 0) + + // B = rolc(h(p * (2x + 1), Mo), 8) + for j := 0; j < 4; j++ { + tmp[j] = 2*i + 1 + } + B := h(tmp[:], key, 1) + B = rol(B, 8) + + c.k[2*i] = A + B + + // K[2i+1] = (A + 2B) <<< 9 + c.k[2*i+1] = rol(2*B+A, 9) + } + + // Calculate sboxes + switch k { + case 2: + for i := 0; i <= 255; i++ { + c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][byte(i)]^S[0]]^S[4]], 0) + c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][byte(i)]^S[1]]^S[5]], 1) + c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][byte(i)]^S[2]]^S[6]], 2) + c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][byte(i)]^S[3]]^S[7]], 3) + } + case 3: + for i := 0; i < 256; i++ { + c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][sbox[1][byte(i)]^S[0]]^S[4]]^S[8]], 0) + c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][sbox[1][byte(i)]^S[1]]^S[5]]^S[9]], 1) + c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][sbox[0][byte(i)]^S[2]]^S[6]]^S[10]], 2) + c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][sbox[0][byte(i)]^S[3]]^S[7]]^S[11]], 3) + } + default: + for i := 0; i < 256; i++ { + c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][sbox[1][sbox[1][byte(i)]^S[0]]^S[4]]^S[8]]^S[12]], 0) + c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][sbox[1][sbox[0][byte(i)]^S[1]]^S[5]]^S[9]]^S[13]], 1) + c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][sbox[0][sbox[0][byte(i)]^S[2]]^S[6]]^S[10]]^S[14]], 2) + c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][sbox[0][sbox[1][byte(i)]^S[3]]^S[7]]^S[11]]^S[15]], 3) + } + } + + return c, nil +} + +// Reset zeros the key data, so that it will no longer appear in the process's +// memory. +func (c *Cipher) Reset() { + for i := 0; i < 40; i++ { + c.k[i] = 0 + } + for i := 0; i < 4; i++ { + for j := 0; j < 265; j++ { + c.s[i][j] = 0 + } + } +} + +// BlockSize returns the Twofish block size, 16 bytes. +func (c *Cipher) BlockSize() int { return BlockSize } + +// store32l stores src in dst in little-endian form. +func store32l(dst []byte, src uint32) { + dst[0] = byte(src) + dst[1] = byte(src >> 8) + dst[2] = byte(src >> 16) + dst[3] = byte(src >> 24) + return +} + +// load32l reads a little-endian uint32 from src. +func load32l(src []byte) uint32 { + return uint32(src[0]) | uint32(src[1])<<8 | uint32(src[2])<<16 | uint32(src[3])<<24 +} + +// rol returns x after a left circular rotation of y bits. +func rol(x, y uint32) uint32 { + return (x << (y & 31)) | (x >> (32 - (y & 31))) +} + +// ror returns x after a right circular rotation of y bits. +func ror(x, y uint32) uint32 { + return (x >> (y & 31)) | (x << (32 - (y & 31))) +} + +// The RS matrix. See [TWOFISH] 4.3 +var rs = [4][8]byte{ + {0x01, 0xA4, 0x55, 0x87, 0x5A, 0x58, 0xDB, 0x9E}, + {0xA4, 0x56, 0x82, 0xF3, 0x1E, 0xC6, 0x68, 0xE5}, + {0x02, 0xA1, 0xFC, 0xC1, 0x47, 0xAE, 0x3D, 0x19}, + {0xA4, 0x55, 0x87, 0x5A, 0x58, 0xDB, 0x9E, 0x03}, +} + +// sbox tables +var sbox = [2][256]byte{ + { + 0xa9, 0x67, 0xb3, 0xe8, 0x04, 0xfd, 0xa3, 0x76, 0x9a, 0x92, 0x80, 0x78, 0xe4, 0xdd, 0xd1, 0x38, + 0x0d, 0xc6, 0x35, 0x98, 0x18, 0xf7, 0xec, 0x6c, 0x43, 0x75, 0x37, 0x26, 0xfa, 0x13, 0x94, 0x48, + 0xf2, 0xd0, 0x8b, 0x30, 0x84, 0x54, 0xdf, 0x23, 0x19, 0x5b, 0x3d, 0x59, 0xf3, 0xae, 0xa2, 0x82, + 0x63, 0x01, 0x83, 0x2e, 0xd9, 0x51, 0x9b, 0x7c, 0xa6, 0xeb, 0xa5, 0xbe, 0x16, 0x0c, 0xe3, 0x61, + 0xc0, 0x8c, 0x3a, 0xf5, 0x73, 0x2c, 0x25, 0x0b, 0xbb, 0x4e, 0x89, 0x6b, 0x53, 0x6a, 0xb4, 0xf1, + 0xe1, 0xe6, 0xbd, 0x45, 0xe2, 0xf4, 0xb6, 0x66, 0xcc, 0x95, 0x03, 0x56, 0xd4, 0x1c, 0x1e, 0xd7, + 0xfb, 0xc3, 0x8e, 0xb5, 0xe9, 0xcf, 0xbf, 0xba, 0xea, 0x77, 0x39, 0xaf, 0x33, 0xc9, 0x62, 0x71, + 0x81, 0x79, 0x09, 0xad, 0x24, 0xcd, 0xf9, 0xd8, 0xe5, 0xc5, 0xb9, 0x4d, 0x44, 0x08, 0x86, 0xe7, + 0xa1, 0x1d, 0xaa, 0xed, 0x06, 0x70, 0xb2, 0xd2, 0x41, 0x7b, 0xa0, 0x11, 0x31, 0xc2, 0x27, 0x90, + 0x20, 0xf6, 0x60, 0xff, 0x96, 0x5c, 0xb1, 0xab, 0x9e, 0x9c, 0x52, 0x1b, 0x5f, 0x93, 0x0a, 0xef, + 0x91, 0x85, 0x49, 0xee, 0x2d, 0x4f, 0x8f, 0x3b, 0x47, 0x87, 0x6d, 0x46, 0xd6, 0x3e, 0x69, 0x64, + 0x2a, 0xce, 0xcb, 0x2f, 0xfc, 0x97, 0x05, 0x7a, 0xac, 0x7f, 0xd5, 0x1a, 0x4b, 0x0e, 0xa7, 0x5a, + 0x28, 0x14, 0x3f, 0x29, 0x88, 0x3c, 0x4c, 0x02, 0xb8, 0xda, 0xb0, 0x17, 0x55, 0x1f, 0x8a, 0x7d, + 0x57, 0xc7, 0x8d, 0x74, 0xb7, 0xc4, 0x9f, 0x72, 0x7e, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, + 0x6e, 0x50, 0xde, 0x68, 0x65, 0xbc, 0xdb, 0xf8, 0xc8, 0xa8, 0x2b, 0x40, 0xdc, 0xfe, 0x32, 0xa4, + 0xca, 0x10, 0x21, 0xf0, 0xd3, 0x5d, 0x0f, 0x00, 0x6f, 0x9d, 0x36, 0x42, 0x4a, 0x5e, 0xc1, 0xe0, + }, + { + 0x75, 0xf3, 0xc6, 0xf4, 0xdb, 0x7b, 0xfb, 0xc8, 0x4a, 0xd3, 0xe6, 0x6b, 0x45, 0x7d, 0xe8, 0x4b, + 0xd6, 0x32, 0xd8, 0xfd, 0x37, 0x71, 0xf1, 0xe1, 0x30, 0x0f, 0xf8, 0x1b, 0x87, 0xfa, 0x06, 0x3f, + 0x5e, 0xba, 0xae, 0x5b, 0x8a, 0x00, 0xbc, 0x9d, 0x6d, 0xc1, 0xb1, 0x0e, 0x80, 0x5d, 0xd2, 0xd5, + 0xa0, 0x84, 0x07, 0x14, 0xb5, 0x90, 0x2c, 0xa3, 0xb2, 0x73, 0x4c, 0x54, 0x92, 0x74, 0x36, 0x51, + 0x38, 0xb0, 0xbd, 0x5a, 0xfc, 0x60, 0x62, 0x96, 0x6c, 0x42, 0xf7, 0x10, 0x7c, 0x28, 0x27, 0x8c, + 0x13, 0x95, 0x9c, 0xc7, 0x24, 0x46, 0x3b, 0x70, 0xca, 0xe3, 0x85, 0xcb, 0x11, 0xd0, 0x93, 0xb8, + 0xa6, 0x83, 0x20, 0xff, 0x9f, 0x77, 0xc3, 0xcc, 0x03, 0x6f, 0x08, 0xbf, 0x40, 0xe7, 0x2b, 0xe2, + 0x79, 0x0c, 0xaa, 0x82, 0x41, 0x3a, 0xea, 0xb9, 0xe4, 0x9a, 0xa4, 0x97, 0x7e, 0xda, 0x7a, 0x17, + 0x66, 0x94, 0xa1, 0x1d, 0x3d, 0xf0, 0xde, 0xb3, 0x0b, 0x72, 0xa7, 0x1c, 0xef, 0xd1, 0x53, 0x3e, + 0x8f, 0x33, 0x26, 0x5f, 0xec, 0x76, 0x2a, 0x49, 0x81, 0x88, 0xee, 0x21, 0xc4, 0x1a, 0xeb, 0xd9, + 0xc5, 0x39, 0x99, 0xcd, 0xad, 0x31, 0x8b, 0x01, 0x18, 0x23, 0xdd, 0x1f, 0x4e, 0x2d, 0xf9, 0x48, + 0x4f, 0xf2, 0x65, 0x8e, 0x78, 0x5c, 0x58, 0x19, 0x8d, 0xe5, 0x98, 0x57, 0x67, 0x7f, 0x05, 0x64, + 0xaf, 0x63, 0xb6, 0xfe, 0xf5, 0xb7, 0x3c, 0xa5, 0xce, 0xe9, 0x68, 0x44, 0xe0, 0x4d, 0x43, 0x69, + 0x29, 0x2e, 0xac, 0x15, 0x59, 0xa8, 0x0a, 0x9e, 0x6e, 0x47, 0xdf, 0x34, 0x35, 0x6a, 0xcf, 0xdc, + 0x22, 0xc9, 0xc0, 0x9b, 0x89, 0xd4, 0xed, 0xab, 0x12, 0xa2, 0x0d, 0x52, 0xbb, 0x02, 0x2f, 0xa9, + 0xd7, 0x61, 0x1e, 0xb4, 0x50, 0x04, 0xf6, 0xc2, 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xbe, 0x91, + }, +} + +// gfMult returns a·b in GF(2^8)/p +func gfMult(a, b byte, p uint32) byte { + B := [2]uint32{0, uint32(b)} + P := [2]uint32{0, p} + var result uint32 + + // branchless GF multiplier + for i := 0; i < 7; i++ { + result ^= B[a&1] + a >>= 1 + B[1] = P[B[1]>>7] ^ (B[1] << 1) + } + result ^= B[a&1] + return byte(result) +} + +// mdsColumnMult calculates y{col} where [y0 y1 y2 y3] = MDS . [x0] +func mdsColumnMult(in byte, col int) uint32 { + mul01 := in + mul5B := gfMult(in, 0x5B, mdsPolynomial) + mulEF := gfMult(in, 0xEF, mdsPolynomial) + + switch col { + case 0: + return uint32(mul01) | uint32(mul5B)<<8 | uint32(mulEF)<<16 | uint32(mulEF)<<24 + case 1: + return uint32(mulEF) | uint32(mulEF)<<8 | uint32(mul5B)<<16 | uint32(mul01)<<24 + case 2: + return uint32(mul5B) | uint32(mulEF)<<8 | uint32(mul01)<<16 | uint32(mulEF)<<24 + case 3: + return uint32(mul5B) | uint32(mul01)<<8 | uint32(mulEF)<<16 | uint32(mul5B)<<24 + } + + panic("unreachable") +} + +// h implements the S-box generation function. See [TWOFISH] 4.3.5 +func h(in, key []byte, offset int) uint32 { + var y [4]byte + for x := 0; x < 4; x++ { + y[x] = in[x] + } + switch len(key) / 8 { + case 4: + y[0] = sbox[1][y[0]] ^ key[4*(6+offset)+0] + y[1] = sbox[0][y[1]] ^ key[4*(6+offset)+1] + y[2] = sbox[0][y[2]] ^ key[4*(6+offset)+2] + y[3] = sbox[1][y[3]] ^ key[4*(6+offset)+3] + fallthrough + case 3: + y[0] = sbox[1][y[0]] ^ key[4*(4+offset)+0] + y[1] = sbox[1][y[1]] ^ key[4*(4+offset)+1] + y[2] = sbox[0][y[2]] ^ key[4*(4+offset)+2] + y[3] = sbox[0][y[3]] ^ key[4*(4+offset)+3] + fallthrough + case 2: + y[0] = sbox[1][sbox[0][sbox[0][y[0]]^key[4*(2+offset)+0]]^key[4*(0+offset)+0]] + y[1] = sbox[0][sbox[0][sbox[1][y[1]]^key[4*(2+offset)+1]]^key[4*(0+offset)+1]] + y[2] = sbox[1][sbox[1][sbox[0][y[2]]^key[4*(2+offset)+2]]^key[4*(0+offset)+2]] + y[3] = sbox[0][sbox[1][sbox[1][y[3]]^key[4*(2+offset)+3]]^key[4*(0+offset)+3]] + } + // [y0 y1 y2 y3] = MDS . [x0 x1 x2 x3] + var mdsMult uint32 + for i := 0; i < 4; i++ { + mdsMult ^= mdsColumnMult(y[i], i) + } + return mdsMult +} + +// Encrypt encrypts a 16-byte block from src to dst, which may overlap. +// Note that for amounts of data larger than a block, +// it is not safe to just call Encrypt on successive blocks; +// instead, use an encryption mode like CBC (see crypto/block/cbc.go). +func (skey *Cipher) Encrypt(dst, src []byte) { + S1 := skey.s[0] + S2 := skey.s[1] + S3 := skey.s[2] + S4 := skey.s[3] + + // Load input + a := load32l(src[0:4]) + b := load32l(src[4:8]) + c := load32l(src[8:12]) + d := load32l(src[12:16]) + + // Pre-whitening + a ^= skey.k[0] + b ^= skey.k[1] + c ^= skey.k[2] + d ^= skey.k[3] + + for i := 0; i < 8; i++ { + k := skey.k[8+i*4 : 12+i*4] + t2 := S2[byte(b)] ^ S3[byte(b>>8)] ^ S4[byte(b>>16)] ^ S1[byte(b>>24)] + t1 := S1[byte(a)] ^ S2[byte(a>>8)] ^ S3[byte(a>>16)] ^ S4[byte(a>>24)] + t2 + c = ror(c^(t1+k[0]), 1) + d = rol(d, 1) ^ (t2 + t1 + k[1]) + + t2 = S2[byte(d)] ^ S3[byte(d>>8)] ^ S4[byte(d>>16)] ^ S1[byte(d>>24)] + t1 = S1[byte(c)] ^ S2[byte(c>>8)] ^ S3[byte(c>>16)] ^ S4[byte(c>>24)] + t2 + a = ror(a^(t1+k[2]), 1) + b = rol(b, 1) ^ (t2 + t1 + k[3]) + } + + // Output with "undo last swap" + ta := c ^ skey.k[4] + tb := d ^ skey.k[5] + tc := a ^ skey.k[6] + td := b ^ skey.k[7] + + store32l(dst[0:4], ta) + store32l(dst[4:8], tb) + store32l(dst[8:12], tc) + store32l(dst[12:16], td) +} + +// Decrypt decrypts a 16-byte block from src to dst, which may overlap. +func (skey *Cipher) Decrypt(dst, src []byte) { + S1 := skey.s[0] + S2 := skey.s[1] + S3 := skey.s[2] + S4 := skey.s[3] + + // Load input + ta := load32l(src[0:4]) + tb := load32l(src[4:8]) + tc := load32l(src[8:12]) + td := load32l(src[12:16]) + + // Undo undo final swap + a := tc ^ skey.k[6] + b := td ^ skey.k[7] + c := ta ^ skey.k[4] + d := tb ^ skey.k[5] + + for i := 8; i > 0; i-- { + k := skey.k[4+i*4 : 8+i*4] + t2 := S2[byte(d)] ^ S3[byte(d>>8)] ^ S4[byte(d>>16)] ^ S1[byte(d>>24)] + t1 := S1[byte(c)] ^ S2[byte(c>>8)] ^ S3[byte(c>>16)] ^ S4[byte(c>>24)] + t2 + a = rol(a, 1) ^ (t1 + k[2]) + b = ror(b^(t2+t1+k[3]), 1) + + t2 = S2[byte(b)] ^ S3[byte(b>>8)] ^ S4[byte(b>>16)] ^ S1[byte(b>>24)] + t1 = S1[byte(a)] ^ S2[byte(a>>8)] ^ S3[byte(a>>16)] ^ S4[byte(a>>24)] + t2 + c = rol(c, 1) ^ (t1 + k[0]) + d = ror(d^(t2+t1+k[1]), 1) + } + + // Undo pre-whitening + a ^= skey.k[0] + b ^= skey.k[1] + c ^= skey.k[2] + d ^= skey.k[3] + + store32l(dst[0:4], a) + store32l(dst[4:8], b) + store32l(dst[8:12], c) + store32l(dst[12:16], d) +} diff --git a/src/pkg/crypto/twofish/twofish_test.go b/src/pkg/crypto/twofish/twofish_test.go new file mode 100644 index 000000000..96ca6797a --- /dev/null +++ b/src/pkg/crypto/twofish/twofish_test.go @@ -0,0 +1,129 @@ +// 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 twofish + +import ( + "bytes" + "testing" +) + +var qbox = [2][4][16]byte{ + { + {0x8, 0x1, 0x7, 0xD, 0x6, 0xF, 0x3, 0x2, 0x0, 0xB, 0x5, 0x9, 0xE, 0xC, 0xA, 0x4}, + {0xE, 0xC, 0xB, 0x8, 0x1, 0x2, 0x3, 0x5, 0xF, 0x4, 0xA, 0x6, 0x7, 0x0, 0x9, 0xD}, + {0xB, 0xA, 0x5, 0xE, 0x6, 0xD, 0x9, 0x0, 0xC, 0x8, 0xF, 0x3, 0x2, 0x4, 0x7, 0x1}, + {0xD, 0x7, 0xF, 0x4, 0x1, 0x2, 0x6, 0xE, 0x9, 0xB, 0x3, 0x0, 0x8, 0x5, 0xC, 0xA}, + }, + { + {0x2, 0x8, 0xB, 0xD, 0xF, 0x7, 0x6, 0xE, 0x3, 0x1, 0x9, 0x4, 0x0, 0xA, 0xC, 0x5}, + {0x1, 0xE, 0x2, 0xB, 0x4, 0xC, 0x3, 0x7, 0x6, 0xD, 0xA, 0x5, 0xF, 0x9, 0x0, 0x8}, + {0x4, 0xC, 0x7, 0x5, 0x1, 0x6, 0x9, 0xA, 0x0, 0xE, 0xD, 0x8, 0x2, 0xB, 0x3, 0xF}, + {0xB, 0x9, 0x5, 0x1, 0xC, 0x3, 0xD, 0xE, 0x6, 0x4, 0x7, 0xF, 0x2, 0x0, 0x8, 0xA}, + }, +} + +// genSbox generates the variable sbox +func genSbox(qi int, x byte) byte { + a0, b0 := x/16, x%16 + for i := 0; i < 2; i++ { + a1 := a0 ^ b0 + b1 := (a0 ^ ((b0 << 3) | (b0 >> 1)) ^ (a0 << 3)) & 15 + a0 = qbox[qi][2*i][a1] + b0 = qbox[qi][2*i+1][b1] + } + return (b0 << 4) + a0 +} + +func TestSbox(t *testing.T) { + for n := 0; n < 2; n++ { + for m := 0; m < 256; m++ { + if genSbox(n, byte(m)) != sbox[n][m] { + t.Errorf("#%d|%d: sbox value = %d want %d", n, m, sbox[n][m], genSbox(n, byte(m))) + } + } + } +} + +var testVectors = []struct { + key []byte + dec []byte + enc []byte +}{ + // These tests are extracted from LibTom + { + []byte{0x9F, 0x58, 0x9F, 0x5C, 0xF6, 0x12, 0x2C, 0x32, 0xB6, 0xBF, 0xEC, 0x2F, 0x2A, 0xE8, 0xC3, 0x5A}, + []byte{0xD4, 0x91, 0xDB, 0x16, 0xE7, 0xB1, 0xC3, 0x9E, 0x86, 0xCB, 0x08, 0x6B, 0x78, 0x9F, 0x54, 0x19}, + []byte{0x01, 0x9F, 0x98, 0x09, 0xDE, 0x17, 0x11, 0x85, 0x8F, 0xAA, 0xC3, 0xA3, 0xBA, 0x20, 0xFB, 0xC3}, + }, + { + []byte{0x88, 0xB2, 0xB2, 0x70, 0x6B, 0x10, 0x5E, 0x36, 0xB4, 0x46, 0xBB, 0x6D, 0x73, 0x1A, 0x1E, 0x88, + 0xEF, 0xA7, 0x1F, 0x78, 0x89, 0x65, 0xBD, 0x44}, + []byte{0x39, 0xDA, 0x69, 0xD6, 0xBA, 0x49, 0x97, 0xD5, 0x85, 0xB6, 0xDC, 0x07, 0x3C, 0xA3, 0x41, 0xB2}, + []byte{0x18, 0x2B, 0x02, 0xD8, 0x14, 0x97, 0xEA, 0x45, 0xF9, 0xDA, 0xAC, 0xDC, 0x29, 0x19, 0x3A, 0x65}, + }, + { + []byte{0xD4, 0x3B, 0xB7, 0x55, 0x6E, 0xA3, 0x2E, 0x46, 0xF2, 0xA2, 0x82, 0xB7, 0xD4, 0x5B, 0x4E, 0x0D, + 0x57, 0xFF, 0x73, 0x9D, 0x4D, 0xC9, 0x2C, 0x1B, 0xD7, 0xFC, 0x01, 0x70, 0x0C, 0xC8, 0x21, 0x6F}, + []byte{0x90, 0xAF, 0xE9, 0x1B, 0xB2, 0x88, 0x54, 0x4F, 0x2C, 0x32, 0xDC, 0x23, 0x9B, 0x26, 0x35, 0xE6}, + []byte{0x6C, 0xB4, 0x56, 0x1C, 0x40, 0xBF, 0x0A, 0x97, 0x05, 0x93, 0x1C, 0xB6, 0xD4, 0x08, 0xE7, 0xFA}, + }, + // These test are derived from http://www.schneier.com/code/ecb_ival.txt + { + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x9F, 0x58, 0x9F, 0x5C, 0xF6, 0x12, 0x2C, 0x32, 0xB6, 0xBF, 0xEC, 0x2F, 0x2A, 0xE8, 0xC3, 0x5A}, + }, + { + []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + }, + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0xCF, 0xD1, 0xD2, 0xE5, 0xA9, 0xBE, 0x9C, 0xDF, 0x50, 0x1F, 0x13, 0xB8, 0x92, 0xBD, 0x22, 0x48}, + }, + { + []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + }, + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{0x37, 0x52, 0x7B, 0xE0, 0x05, 0x23, 0x34, 0xB8, 0x9F, 0x0C, 0xFC, 0xCA, 0xE8, 0x7C, 0xFA, 0x20}, + }, +} + +func TestCipher(t *testing.T) { + for n, tt := range testVectors { + // Test if the plaintext (dec) is encrypts to the given + // ciphertext (enc) using the given key. Test also if enc can + // be decrypted again into dec. + c, err := NewCipher(tt.key) + if err != nil { + t.Errorf("#%d: NewCipher: %v", n, err) + return + } + + buf := make([]byte, 16) + c.Encrypt(buf, tt.dec) + if !bytes.Equal(buf, tt.enc) { + t.Errorf("#%d: encrypt = %x want %x", n, buf, tt.enc) + } + c.Decrypt(buf, tt.enc) + if !bytes.Equal(buf, tt.dec) { + t.Errorf("#%d: decrypt = %x want %x", n, buf, tt.dec) + } + + // Test that 16 zero bytes, encrypted 1000 times then decrypted + // 1000 times results in zero bytes again. + zero := make([]byte, 16) + buf = make([]byte, 16) + for i := 0; i < 1000; i++ { + c.Encrypt(buf, buf) + } + for i := 0; i < 1000; i++ { + c.Decrypt(buf, buf) + } + if !bytes.Equal(buf, zero) { + t.Errorf("#%d: encrypt/decrypt 1000: have %x want %x", n, buf, zero) + } + } +} diff --git a/src/pkg/crypto/x509/Makefile b/src/pkg/crypto/x509/Makefile index b2ecfdc01..329a61b7c 100644 --- a/src/pkg/crypto/x509/Makefile +++ b/src/pkg/crypto/x509/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=crypto/x509 GOFILES=\ diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go index 728116850..6199e8db9 100644 --- a/src/pkg/crypto/x509/x509.go +++ b/src/pkg/crypto/x509/x509.go @@ -36,7 +36,7 @@ func rawValueIsInteger(raw *asn1.RawValue) bool { // ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form. func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) { var priv pkcs1PrivateKey - rest, err := asn1.Unmarshal(&priv, der) + rest, err := asn1.Unmarshal(der, &priv) if len(rest) > 0 { err = asn1.SyntaxError{"trailing data"} return @@ -81,7 +81,7 @@ func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte { Q: asn1.RawValue{Tag: 2, Bytes: key.Q.Bytes()}, } - b, _ := asn1.MarshalToMemory(priv) + b, _ := asn1.Marshal(priv) return b } @@ -162,9 +162,10 @@ const ( // Name represents an X.509 distinguished name. This only includes the common // elements of a DN. Additional elements in the name are ignored. type Name struct { - Country, Organization, OrganizationalUnit string - CommonName, SerialNumber, Locality string - Province, StreetAddress, PostalCode string + Country, Organization, OrganizationalUnit []string + Locality, Province []string + StreetAddress, PostalCode []string + SerialNumber, CommonName string } func (n *Name) fillFromRDNSequence(rdns *rdnSequence) { @@ -186,19 +187,19 @@ func (n *Name) fillFromRDNSequence(rdns *rdnSequence) { case 5: n.SerialNumber = value case 6: - n.Country = value + n.Country = append(n.Country, value) case 7: - n.Locality = value + n.Locality = append(n.Locality, value) case 8: - n.Province = value + n.Province = append(n.Province, value) case 9: - n.StreetAddress = value + n.StreetAddress = append(n.StreetAddress, value) case 10: - n.Organization = value + n.Organization = append(n.Organization, value) case 11: - n.OrganizationalUnit = value + n.OrganizationalUnit = append(n.OrganizationalUnit, value) case 17: - n.PostalCode = value + n.PostalCode = append(n.PostalCode, value) } } } @@ -216,50 +217,40 @@ var ( oidPostalCode = []int{2, 5, 4, 17} ) -func (n Name) toRDNSequence() (ret rdnSequence) { - ret = make([]relativeDistinguishedNameSET, 9 /* maximum number of elements */ ) - i := 0 - if len(n.Country) > 0 { - ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidCountry, n.Country}} - i++ - } - if len(n.Organization) > 0 { - ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidOrganization, n.Organization}} - i++ +// appendRDNs appends a relativeDistinguishedNameSET to the given rdnSequence +// and returns the new value. The relativeDistinguishedNameSET contains an +// attributeTypeAndValue for each of the given values. See RFC 5280, A.1, and +// search for AttributeTypeAndValue. +func appendRDNs(in rdnSequence, values []string, oid asn1.ObjectIdentifier) rdnSequence { + if len(values) == 0 { + return in } - if len(n.OrganizationalUnit) > 0 { - ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidOrganizationalUnit, n.OrganizationalUnit}} - i++ + + s := make([]attributeTypeAndValue, len(values)) + for i, value := range values { + s[i].Type = oid + s[i].Value = value } + + return append(in, s) +} + +func (n Name) toRDNSequence() (ret rdnSequence) { + ret = appendRDNs(ret, n.Country, oidCountry) + ret = appendRDNs(ret, n.Organization, oidOrganization) + ret = appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit) + ret = appendRDNs(ret, n.Locality, oidLocatity) + ret = appendRDNs(ret, n.Province, oidProvince) + ret = appendRDNs(ret, n.StreetAddress, oidStreetAddress) + ret = appendRDNs(ret, n.PostalCode, oidPostalCode) if len(n.CommonName) > 0 { - ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidCommonName, n.CommonName}} - i++ + ret = appendRDNs(ret, []string{n.CommonName}, oidCommonName) } if len(n.SerialNumber) > 0 { - ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidSerialNumber, n.SerialNumber}} - i++ - } - if len(n.Locality) > 0 { - ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidLocatity, n.Locality}} - i++ - } - if len(n.Province) > 0 { - ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidProvince, n.Province}} - i++ - } - if len(n.StreetAddress) > 0 { - ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidStreetAddress, n.StreetAddress}} - i++ - } - if len(n.PostalCode) > 0 { - ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidPostalCode, n.PostalCode}} - i++ + ret = appendRDNs(ret, []string{n.SerialNumber}, oidSerialNumber) } - // Adding another RDN here? Remember to update the maximum number of - // elements in the make() at the top of the function. - - return ret[0:i] + return ret } func getSignatureAlgorithmFromOID(oid []int) SignatureAlgorithm { @@ -338,6 +329,8 @@ type Certificate struct { // Subject Alternate Name values DNSNames []string EmailAddresses []string + + PolicyIdentifiers []asn1.ObjectIdentifier } // UnsupportedAlgorithmError results from attempting to perform an operation @@ -426,19 +419,37 @@ func matchHostnames(pattern, host string) bool { return true } -// IsValidForHost returns true iff c is a valid certificate for the given host. -func (c *Certificate) IsValidForHost(h string) bool { +type HostnameError struct { + Certificate *Certificate + Host string +} + +func (h *HostnameError) String() string { + var valid string + c := h.Certificate + if len(c.DNSNames) > 0 { + valid = strings.Join(c.DNSNames, ", ") + } else { + valid = c.Subject.CommonName + } + return "certificate is valid for " + valid + ", not " + h.Host +} + +// VerifyHostname returns nil if c is a valid certificate for the named host. +// Otherwise it returns an os.Error describing the mismatch. +func (c *Certificate) VerifyHostname(h string) os.Error { if len(c.DNSNames) > 0 { for _, match := range c.DNSNames { if matchHostnames(match, h) { - return true + return nil } } // If Subject Alt Name is given, we ignore the common name. - return false + } else if matchHostnames(c.Subject.CommonName, h) { + return nil } - return matchHostnames(c.Subject.CommonName, h) + return &HostnameError{c, h} } type UnhandledCriticalExtension struct{} @@ -457,11 +468,17 @@ type rsaPublicKey struct { E int } +// RFC 5280 4.2.1.4 +type policyInformation struct { + Policy asn1.ObjectIdentifier + // policyQualifiers omitted +} + func parsePublicKey(algo PublicKeyAlgorithm, asn1Data []byte) (interface{}, os.Error) { switch algo { case RSA: p := new(rsaPublicKey) - _, err := asn1.Unmarshal(p, asn1Data) + _, err := asn1.Unmarshal(asn1Data, p) if err != nil { return nil, err } @@ -482,19 +499,6 @@ func parsePublicKey(algo PublicKeyAlgorithm, asn1Data []byte) (interface{}, os.E panic("unreachable") } -func appendString(in []string, v string) (out []string) { - if cap(in)-len(in) < 1 { - out = make([]string, len(in)+1, len(in)*2+1) - for i, v := range in { - out[i] = v - } - } else { - out = in[0 : len(in)+1] - } - out[len(in)] = v - return out -} - func parseCertificate(in *certificate) (*Certificate, os.Error) { out := new(Certificate) out.Raw = in.TBSCertificate.Raw @@ -511,7 +515,7 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { return nil, err } - out.Version = in.TBSCertificate.Version + out.Version = in.TBSCertificate.Version + 1 out.SerialNumber = in.TBSCertificate.SerialNumber.Bytes out.Issuer.fillFromRDNSequence(&in.TBSCertificate.Issuer) out.Subject.fillFromRDNSequence(&in.TBSCertificate.Subject) @@ -524,7 +528,7 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { case 15: // RFC 5280, 4.2.1.3 var usageBits asn1.BitString - _, err := asn1.Unmarshal(&usageBits, e.Value) + _, err := asn1.Unmarshal(e.Value, &usageBits) if err == nil { var usage int @@ -539,7 +543,7 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { case 19: // RFC 5280, 4.2.1.9 var constriants basicConstraints - _, err := asn1.Unmarshal(&constriants, e.Value) + _, err := asn1.Unmarshal(e.Value, &constriants) if err == nil { out.BasicConstraintsValid = true @@ -565,7 +569,7 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { // iPAddress [7] OCTET STRING, // registeredID [8] OBJECT IDENTIFIER } var seq asn1.RawValue - _, err := asn1.Unmarshal(&seq, e.Value) + _, err := asn1.Unmarshal(e.Value, &seq) if err != nil { return nil, err } @@ -578,16 +582,16 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { rest := seq.Bytes for len(rest) > 0 { var v asn1.RawValue - rest, err = asn1.Unmarshal(&v, rest) + rest, err = asn1.Unmarshal(rest, &v) if err != nil { return nil, err } switch v.Tag { case 1: - out.EmailAddresses = appendString(out.EmailAddresses, string(v.Bytes)) + out.EmailAddresses = append(out.EmailAddresses, string(v.Bytes)) parsedName = true case 2: - out.DNSNames = appendString(out.DNSNames, string(v.Bytes)) + out.DNSNames = append(out.DNSNames, string(v.Bytes)) parsedName = true } } @@ -601,7 +605,7 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { case 35: // RFC 5280, 4.2.1.1 var a authKeyId - _, err = asn1.Unmarshal(&a, e.Value) + _, err = asn1.Unmarshal(e.Value, &a) if err != nil { return nil, err } @@ -610,8 +614,24 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { case 14: // RFC 5280, 4.2.1.2 - out.SubjectKeyId = e.Value + var keyid []byte + _, err = asn1.Unmarshal(e.Value, &keyid) + if err != nil { + return nil, err + } + out.SubjectKeyId = keyid continue + + case 32: + // RFC 5280 4.2.1.4: Certificate Policies + var policies []policyInformation + if _, err = asn1.Unmarshal(e.Value, &policies); err != nil { + return nil, err + } + out.PolicyIdentifiers = make([]asn1.ObjectIdentifier, len(policies)) + for i, policy := range policies { + out.PolicyIdentifiers[i] = policy.Policy + } } } @@ -626,7 +646,7 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { // ParseCertificate parses a single certificate from the given ASN.1 DER data. func ParseCertificate(asn1Data []byte) (*Certificate, os.Error) { var cert certificate - rest, err := asn1.Unmarshal(&cert, asn1Data) + rest, err := asn1.Unmarshal(asn1Data, &cert) if err != nil { return nil, err } @@ -645,7 +665,7 @@ func ParseCertificates(asn1Data []byte) ([]*Certificate, os.Error) { for len(asn1Data) > 0 { cert := new(certificate) var err os.Error - asn1Data, err = asn1.Unmarshal(cert, asn1Data) + asn1Data, err = asn1.Unmarshal(asn1Data, cert) if err != nil { return nil, err } @@ -672,15 +692,16 @@ func reverseBitsInAByte(in byte) byte { } var ( - oidExtensionSubjectKeyId = []int{2, 5, 29, 14} - oidExtensionKeyUsage = []int{2, 5, 29, 15} - oidExtensionAuthorityKeyId = []int{2, 5, 29, 35} - oidExtensionBasicConstraints = []int{2, 5, 29, 19} - oidExtensionSubjectAltName = []int{2, 5, 29, 17} + oidExtensionSubjectKeyId = []int{2, 5, 29, 14} + oidExtensionKeyUsage = []int{2, 5, 29, 15} + oidExtensionAuthorityKeyId = []int{2, 5, 29, 35} + oidExtensionBasicConstraints = []int{2, 5, 29, 19} + oidExtensionSubjectAltName = []int{2, 5, 29, 17} + oidExtensionCertificatePolicies = []int{2, 5, 29, 32} ) func buildExtensions(template *Certificate) (ret []extension, err os.Error) { - ret = make([]extension, 5 /* maximum number of elements. */ ) + ret = make([]extension, 6 /* maximum number of elements. */ ) n := 0 if template.KeyUsage != 0 { @@ -696,7 +717,7 @@ func buildExtensions(template *Certificate) (ret []extension, err os.Error) { l = 2 } - ret[n].Value, err = asn1.MarshalToMemory(asn1.BitString{Bytes: a[0:l], BitLength: l * 8}) + ret[n].Value, err = asn1.Marshal(asn1.BitString{Bytes: a[0:l], BitLength: l * 8}) if err != nil { return } @@ -705,7 +726,7 @@ func buildExtensions(template *Certificate) (ret []extension, err os.Error) { if template.BasicConstraintsValid { ret[n].Id = oidExtensionBasicConstraints - ret[n].Value, err = asn1.MarshalToMemory(basicConstraints{template.IsCA, template.MaxPathLen}) + ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, template.MaxPathLen}) ret[n].Critical = true if err != nil { return @@ -715,7 +736,7 @@ func buildExtensions(template *Certificate) (ret []extension, err os.Error) { if len(template.SubjectKeyId) > 0 { ret[n].Id = oidExtensionSubjectKeyId - ret[n].Value, err = asn1.MarshalToMemory(template.SubjectKeyId) + ret[n].Value, err = asn1.Marshal(template.SubjectKeyId) if err != nil { return } @@ -724,7 +745,7 @@ func buildExtensions(template *Certificate) (ret []extension, err os.Error) { if len(template.AuthorityKeyId) > 0 { ret[n].Id = oidExtensionAuthorityKeyId - ret[n].Value, err = asn1.MarshalToMemory(authKeyId{template.AuthorityKeyId}) + ret[n].Value, err = asn1.Marshal(authKeyId{template.AuthorityKeyId}) if err != nil { return } @@ -737,7 +758,20 @@ func buildExtensions(template *Certificate) (ret []extension, err os.Error) { for i, name := range template.DNSNames { rawValues[i] = asn1.RawValue{Tag: 2, Class: 2, Bytes: []byte(name)} } - ret[n].Value, err = asn1.MarshalToMemory(rawValues) + ret[n].Value, err = asn1.Marshal(rawValues) + if err != nil { + return + } + n++ + } + + if len(template.PolicyIdentifiers) > 0 { + ret[n].Id = oidExtensionCertificatePolicies + policies := make([]policyInformation, len(template.PolicyIdentifiers)) + for i, policy := range template.PolicyIdentifiers { + policies[i].Policy = policy + } + ret[n].Value, err = asn1.Marshal(policies) if err != nil { return } @@ -766,7 +800,7 @@ var ( // // The returned slice is the certificate in DER encoding. func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.PublicKey, priv *rsa.PrivateKey) (cert []byte, err os.Error) { - asn1PublicKey, err := asn1.MarshalToMemory(rsaPublicKey{ + asn1PublicKey, err := asn1.Marshal(rsaPublicKey{ N: asn1.RawValue{Tag: 2, Bytes: pub.N.Bytes()}, E: pub.E, }) @@ -785,7 +819,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey} c := tbsCertificate{ - Version: 3, + Version: 2, SerialNumber: asn1.RawValue{Bytes: template.SerialNumber, Tag: 2}, SignatureAlgorithm: algorithmIdentifier{oidSHA1WithRSA}, Issuer: parent.Subject.toRDNSequence(), @@ -795,7 +829,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P Extensions: extensions, } - tbsCertContents, err := asn1.MarshalToMemory(c) + tbsCertContents, err := asn1.Marshal(c) if err != nil { return } @@ -811,7 +845,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P return } - cert, err = asn1.MarshalToMemory(certificate{ + cert, err = asn1.Marshal(certificate{ c, algorithmIdentifier{oidSHA1WithRSA}, asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, diff --git a/src/pkg/crypto/x509/x509_test.go b/src/pkg/crypto/x509/x509_test.go index 23ce1ad11..2fe47fdbe 100644 --- a/src/pkg/crypto/x509/x509_test.go +++ b/src/pkg/crypto/x509/x509_test.go @@ -5,11 +5,12 @@ package x509 import ( + "asn1" "big" + "crypto/rand" "crypto/rsa" "encoding/hex" "encoding/pem" - "os" "reflect" "testing" "time" @@ -59,16 +60,16 @@ type matchHostnamesTest struct { } var matchHostnamesTests = []matchHostnamesTest{ - matchHostnamesTest{"a.b.c", "a.b.c", true}, - matchHostnamesTest{"a.b.c", "b.b.c", false}, - matchHostnamesTest{"", "b.b.c", false}, - matchHostnamesTest{"a.b.c", "", false}, - matchHostnamesTest{"example.com", "example.com", true}, - matchHostnamesTest{"example.com", "www.example.com", false}, - matchHostnamesTest{"*.example.com", "www.example.com", true}, - matchHostnamesTest{"*.example.com", "xyz.www.example.com", false}, - matchHostnamesTest{"*.*.example.com", "xyz.www.example.com", true}, - matchHostnamesTest{"*.www.*.com", "xyz.www.example.com", true}, + {"a.b.c", "a.b.c", true}, + {"a.b.c", "b.b.c", false}, + {"", "b.b.c", false}, + {"a.b.c", "", false}, + {"example.com", "example.com", true}, + {"example.com", "www.example.com", false}, + {"*.example.com", "www.example.com", true}, + {"*.example.com", "xyz.www.example.com", false}, + {"*.*.example.com", "xyz.www.example.com", true}, + {"*.www.*.com", "xyz.www.example.com", true}, } func TestMatchHostnames(t *testing.T) { @@ -96,8 +97,8 @@ func TestCertificateParse(t *testing.T) { t.Error(err) } - if !certs[0].IsValidForHost("mail.google.com") { - t.Errorf("cert not valid for host") + if err := certs[0].VerifyHostname("mail.google.com"); err != nil { + t.Error(err) } } @@ -145,10 +146,7 @@ var certBytes = "308203223082028ba00302010202106edf0d9499fd4533dd1297fc42a93be13 "36dcd585d6ace53f546f961e05af" func TestCreateSelfSignedCertificate(t *testing.T) { - urandom, err := os.Open("/dev/urandom", os.O_RDONLY, 0) - if err != nil { - t.Errorf("failed to open /dev/urandom") - } + random := rand.Reader block, _ := pem.Decode([]byte(pemPrivateKey)) priv, err := ParsePKCS1PrivateKey(block.Bytes) @@ -161,7 +159,7 @@ func TestCreateSelfSignedCertificate(t *testing.T) { SerialNumber: []byte{1}, Subject: Name{ CommonName: "test.example.com", - Organization: "Acme Co", + Organization: []string{"Acme Co"}, }, NotBefore: time.SecondsToUTC(1000), NotAfter: time.SecondsToUTC(100000), @@ -172,9 +170,11 @@ func TestCreateSelfSignedCertificate(t *testing.T) { BasicConstraintsValid: true, IsCA: true, DNSNames: []string{"test.example.com"}, + + PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}}, } - derBytes, err := CreateCertificate(urandom, &template, &template, &priv.PublicKey, priv) + derBytes, err := CreateCertificate(random, &template, &template, &priv.PublicKey, priv) if err != nil { t.Errorf("Failed to create certificate: %s", err) return @@ -185,6 +185,11 @@ func TestCreateSelfSignedCertificate(t *testing.T) { t.Errorf("Failed to parse certificate: %s", err) return } + + if len(cert.PolicyIdentifiers) != 1 || !cert.PolicyIdentifiers[0].Equal(template.PolicyIdentifiers[0]) { + t.Errorf("Failed to parse policy identifiers: got:%#v want:%#v", cert.PolicyIdentifiers, template.PolicyIdentifiers) + } + err = cert.CheckSignatureFrom(cert) if err != nil { t.Errorf("Signature verification failed: %s", err) diff --git a/src/pkg/crypto/xtea/Makefile b/src/pkg/crypto/xtea/Makefile index 74cc1b0dc..301621168 100644 --- a/src/pkg/crypto/xtea/Makefile +++ b/src/pkg/crypto/xtea/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=crypto/xtea GOFILES=\ diff --git a/src/pkg/crypto/xtea/block.go b/src/pkg/crypto/xtea/block.go index dfb82e1e2..3ac36d038 100644 --- a/src/pkg/crypto/xtea/block.go +++ b/src/pkg/crypto/xtea/block.go @@ -36,7 +36,7 @@ func uint32ToBlock(v0, v1 uint32, dst []byte) { } // encryptBlock encrypts a single 8 byte block using XTEA. -func encryptBlock(c *Cipher, src, dst []byte) { +func encryptBlock(c *Cipher, dst, src []byte) { v0, v1 := blockToUint32(src) // Two rounds of XTEA applied per loop @@ -51,7 +51,7 @@ func encryptBlock(c *Cipher, src, dst []byte) { } // decryptBlock decrypt a single 8 byte block using XTEA. -func decryptBlock(c *Cipher, src, dst []byte) { +func decryptBlock(c *Cipher, dst, src []byte) { v0, v1 := blockToUint32(src) // Two rounds of XTEA applied per loop diff --git a/src/pkg/crypto/xtea/cipher.go b/src/pkg/crypto/xtea/cipher.go index 144fe9434..b0fa2a184 100644 --- a/src/pkg/crypto/xtea/cipher.go +++ b/src/pkg/crypto/xtea/cipher.go @@ -55,10 +55,10 @@ func (c *Cipher) BlockSize() int { return BlockSize } // Note that for amounts of data larger than a block, // it is not safe to just call Encrypt on successive blocks; // instead, use an encryption mode like CBC (see crypto/block/cbc.go). -func (c *Cipher) Encrypt(src, dst []byte) { encryptBlock(c, src, dst) } +func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c, dst, src) } // Decrypt decrypts the 8 byte buffer src using the key k and stores the result in dst. -func (c *Cipher) Decrypt(src, dst []byte) { decryptBlock(c, src, dst) } +func (c *Cipher) Decrypt(dst, src []byte) { decryptBlock(c, dst, src) } // Reset zeros the table, so that it will no longer appear in the process's memory. func (c *Cipher) Reset() { diff --git a/src/pkg/crypto/xtea/xtea_test.go b/src/pkg/crypto/xtea/xtea_test.go index 94756f79f..03934f169 100644 --- a/src/pkg/crypto/xtea/xtea_test.go +++ b/src/pkg/crypto/xtea/xtea_test.go @@ -94,7 +94,7 @@ func TestEncodeDecode(t *testing.T) { } // Encrypt the input block - c.Encrypt(input, output) + c.Encrypt(output, input) // Check that the output does not match the input differs := false @@ -112,7 +112,7 @@ func TestEncodeDecode(t *testing.T) { // Decrypt the block we just encrypted input = output output = make([]byte, BlockSize) - c.Decrypt(input, output) + c.Decrypt(output, input) // Check that the output from decrypt matches our initial input for i := 0; i < len(input); i++ { @@ -132,54 +132,54 @@ type CryptTest struct { var CryptTests = []CryptTest{ // These were sourced from http://www.freemedialibrary.com/index.php/XTEA_test_vectors - CryptTest{ + { []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, []byte{0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}, []byte{0x49, 0x7d, 0xf3, 0xd0, 0x72, 0x61, 0x2c, 0xb5}, }, - CryptTest{ + { []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}, []byte{0xe7, 0x8f, 0x2d, 0x13, 0x74, 0x43, 0x41, 0xd8}, }, - CryptTest{ + { []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, []byte{0x5a, 0x5b, 0x6e, 0x27, 0x89, 0x48, 0xd7, 0x7f}, []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}, }, - CryptTest{ + { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}, []byte{0xa0, 0x39, 0x05, 0x89, 0xf8, 0xb8, 0xef, 0xa5}, }, - CryptTest{ + { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}, []byte{0xed, 0x23, 0x37, 0x5a, 0x82, 0x1a, 0x8c, 0x2d}, }, - CryptTest{ + { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x70, 0xe1, 0x22, 0x5d, 0x6e, 0x4e, 0x76, 0x55}, []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}, }, // These vectors are from http://wiki.secondlife.com/wiki/XTEA_Strong_Encryption_Implementation#Bouncy_Castle_C.23_API - CryptTest{ + { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0xDE, 0xE9, 0xD4, 0xD8, 0xF7, 0x13, 0x1E, 0xD9}, }, - CryptTest{ + { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, []byte{0x06, 0x5C, 0x1B, 0x89, 0x75, 0xC6, 0xA8, 0x16}, }, - CryptTest{ + { []byte{0x01, 0x23, 0x45, 0x67, 0x12, 0x34, 0x56, 0x78, 0x23, 0x45, 0x67, 0x89, 0x34, 0x56, 0x78, 0x9A}, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x1F, 0xF9, 0xA0, 0x26, 0x1A, 0xC6, 0x42, 0x64}, }, - CryptTest{ + { []byte{0x01, 0x23, 0x45, 0x67, 0x12, 0x34, 0x56, 0x78, 0x23, 0x45, 0x67, 0x89, 0x34, 0x56, 0x78, 0x9A}, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, []byte{0x8C, 0x67, 0x15, 0x5B, 0x2E, 0xF9, 0x1E, 0xAD}, @@ -196,7 +196,7 @@ func TestCipherEncrypt(t *testing.T) { } out := make([]byte, len(tt.plainText)) - c.Encrypt(tt.plainText, out) + c.Encrypt(out, tt.plainText) for j := 0; j < len(out); j++ { if out[j] != tt.cipherText[j] { @@ -217,7 +217,7 @@ func TestCipherDecrypt(t *testing.T) { } out := make([]byte, len(tt.cipherText)) - c.Decrypt(tt.cipherText, out) + c.Decrypt(out, tt.cipherText) for j := 0; j < len(out); j++ { if out[j] != tt.plainText[j] { diff --git a/src/pkg/debug/dwarf/Makefile b/src/pkg/debug/dwarf/Makefile index a626513c7..c4203188e 100644 --- a/src/pkg/debug/dwarf/Makefile +++ b/src/pkg/debug/dwarf/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=debug/dwarf GOFILES=\ diff --git a/src/pkg/debug/dwarf/type.go b/src/pkg/debug/dwarf/type.go index 5d4a51653..902a545f8 100644 --- a/src/pkg/debug/dwarf/type.go +++ b/src/pkg/debug/dwarf/type.go @@ -451,16 +451,7 @@ func (d *Data) Type(off Offset) (Type, os.Error) { f.ByteSize, _ = kid.Val(AttrByteSize).(int64) f.BitOffset, _ = kid.Val(AttrBitOffset).(int64) f.BitSize, _ = kid.Val(AttrBitSize).(int64) - n := len(t.Field) - if n >= cap(t.Field) { - fld := make([]*StructField, n, n*2) - for i, f := range t.Field { - fld[i] = f - } - t.Field = fld - } - t.Field = t.Field[0 : n+1] - t.Field[n] = f + t.Field = append(t.Field, f) } } @@ -505,9 +496,7 @@ func (d *Data) Type(off Offset) (Type, os.Error) { n := len(t.Val) if n >= cap(t.Val) { val := make([]*EnumValue, n, n*2) - for i, f := range t.Val { - val[i] = f - } + copy(val, t.Val) t.Val = val } t.Val = t.Val[0 : n+1] @@ -558,16 +547,7 @@ func (d *Data) Type(off Offset) (Type, os.Error) { case TagUnspecifiedParameters: tkid = &DotDotDotType{} } - n := len(t.ParamType) - if n >= cap(t.ParamType) { - param := make([]Type, n, n*2) - for i, t := range t.ParamType { - param[i] = t - } - t.ParamType = param - } - t.ParamType = t.ParamType[0 : n+1] - t.ParamType[n] = tkid + t.ParamType = append(t.ParamType, tkid) } case TagTypedef: diff --git a/src/pkg/debug/elf/Makefile b/src/pkg/debug/elf/Makefile index 931f9d24d..dd431f653 100644 --- a/src/pkg/debug/elf/Makefile +++ b/src/pkg/debug/elf/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=debug/elf GOFILES=\ diff --git a/src/pkg/debug/elf/elf.go b/src/pkg/debug/elf/elf.go index 394a7cc87..74e979986 100644 --- a/src/pkg/debug/elf/elf.go +++ b/src/pkg/debug/elf/elf.go @@ -69,8 +69,8 @@ const ( ) var versionStrings = []intName{ - intName{0, "EV_NONE"}, - intName{1, "EV_CURRENT"}, + {0, "EV_NONE"}, + {1, "EV_CURRENT"}, } func (i Version) String() string { return stringName(uint32(i), versionStrings, false) } @@ -86,9 +86,9 @@ const ( ) var classStrings = []intName{ - intName{0, "ELFCLASSNONE"}, - intName{1, "ELFCLASS32"}, - intName{2, "ELFCLASS64"}, + {0, "ELFCLASSNONE"}, + {1, "ELFCLASS32"}, + {2, "ELFCLASS64"}, } func (i Class) String() string { return stringName(uint32(i), classStrings, false) } @@ -104,9 +104,9 @@ const ( ) var dataStrings = []intName{ - intName{0, "ELFDATANONE"}, - intName{1, "ELFDATA2LSB"}, - intName{2, "ELFDATA2MSB"}, + {0, "ELFDATANONE"}, + {1, "ELFDATA2LSB"}, + {2, "ELFDATA2MSB"}, } func (i Data) String() string { return stringName(uint32(i), dataStrings, false) } @@ -136,23 +136,23 @@ const ( ) var osabiStrings = []intName{ - intName{0, "ELFOSABI_NONE"}, - intName{1, "ELFOSABI_HPUX"}, - intName{2, "ELFOSABI_NETBSD"}, - intName{3, "ELFOSABI_LINUX"}, - intName{4, "ELFOSABI_HURD"}, - intName{5, "ELFOSABI_86OPEN"}, - intName{6, "ELFOSABI_SOLARIS"}, - intName{7, "ELFOSABI_AIX"}, - intName{8, "ELFOSABI_IRIX"}, - intName{9, "ELFOSABI_FREEBSD"}, - intName{10, "ELFOSABI_TRU64"}, - intName{11, "ELFOSABI_MODESTO"}, - intName{12, "ELFOSABI_OPENBSD"}, - intName{13, "ELFOSABI_OPENVMS"}, - intName{14, "ELFOSABI_NSK"}, - intName{97, "ELFOSABI_ARM"}, - intName{255, "ELFOSABI_STANDALONE"}, + {0, "ELFOSABI_NONE"}, + {1, "ELFOSABI_HPUX"}, + {2, "ELFOSABI_NETBSD"}, + {3, "ELFOSABI_LINUX"}, + {4, "ELFOSABI_HURD"}, + {5, "ELFOSABI_86OPEN"}, + {6, "ELFOSABI_SOLARIS"}, + {7, "ELFOSABI_AIX"}, + {8, "ELFOSABI_IRIX"}, + {9, "ELFOSABI_FREEBSD"}, + {10, "ELFOSABI_TRU64"}, + {11, "ELFOSABI_MODESTO"}, + {12, "ELFOSABI_OPENBSD"}, + {13, "ELFOSABI_OPENVMS"}, + {14, "ELFOSABI_NSK"}, + {97, "ELFOSABI_ARM"}, + {255, "ELFOSABI_STANDALONE"}, } func (i OSABI) String() string { return stringName(uint32(i), osabiStrings, false) } @@ -174,15 +174,15 @@ const ( ) var typeStrings = []intName{ - intName{0, "ET_NONE"}, - intName{1, "ET_REL"}, - intName{2, "ET_EXEC"}, - intName{3, "ET_DYN"}, - intName{4, "ET_CORE"}, - intName{0xfe00, "ET_LOOS"}, - intName{0xfeff, "ET_HIOS"}, - intName{0xff00, "ET_LOPROC"}, - intName{0xffff, "ET_HIPROC"}, + {0, "ET_NONE"}, + {1, "ET_REL"}, + {2, "ET_EXEC"}, + {3, "ET_DYN"}, + {4, "ET_CORE"}, + {0xfe00, "ET_LOOS"}, + {0xfeff, "ET_HIOS"}, + {0xff00, "ET_LOPROC"}, + {0xffff, "ET_HIPROC"}, } func (i Type) String() string { return stringName(uint32(i), typeStrings, false) } @@ -244,55 +244,55 @@ const ( ) var machineStrings = []intName{ - intName{0, "EM_NONE"}, - intName{1, "EM_M32"}, - intName{2, "EM_SPARC"}, - intName{3, "EM_386"}, - intName{4, "EM_68K"}, - intName{5, "EM_88K"}, - intName{7, "EM_860"}, - intName{8, "EM_MIPS"}, - intName{9, "EM_S370"}, - intName{10, "EM_MIPS_RS3_LE"}, - intName{15, "EM_PARISC"}, - intName{17, "EM_VPP500"}, - intName{18, "EM_SPARC32PLUS"}, - intName{19, "EM_960"}, - intName{20, "EM_PPC"}, - intName{21, "EM_PPC64"}, - intName{22, "EM_S390"}, - intName{36, "EM_V800"}, - intName{37, "EM_FR20"}, - intName{38, "EM_RH32"}, - intName{39, "EM_RCE"}, - intName{40, "EM_ARM"}, - intName{42, "EM_SH"}, - intName{43, "EM_SPARCV9"}, - intName{44, "EM_TRICORE"}, - intName{45, "EM_ARC"}, - intName{46, "EM_H8_300"}, - intName{47, "EM_H8_300H"}, - intName{48, "EM_H8S"}, - intName{49, "EM_H8_500"}, - intName{50, "EM_IA_64"}, - intName{51, "EM_MIPS_X"}, - intName{52, "EM_COLDFIRE"}, - intName{53, "EM_68HC12"}, - intName{54, "EM_MMA"}, - intName{55, "EM_PCP"}, - intName{56, "EM_NCPU"}, - intName{57, "EM_NDR1"}, - intName{58, "EM_STARCORE"}, - intName{59, "EM_ME16"}, - intName{60, "EM_ST100"}, - intName{61, "EM_TINYJ"}, - intName{62, "EM_X86_64"}, + {0, "EM_NONE"}, + {1, "EM_M32"}, + {2, "EM_SPARC"}, + {3, "EM_386"}, + {4, "EM_68K"}, + {5, "EM_88K"}, + {7, "EM_860"}, + {8, "EM_MIPS"}, + {9, "EM_S370"}, + {10, "EM_MIPS_RS3_LE"}, + {15, "EM_PARISC"}, + {17, "EM_VPP500"}, + {18, "EM_SPARC32PLUS"}, + {19, "EM_960"}, + {20, "EM_PPC"}, + {21, "EM_PPC64"}, + {22, "EM_S390"}, + {36, "EM_V800"}, + {37, "EM_FR20"}, + {38, "EM_RH32"}, + {39, "EM_RCE"}, + {40, "EM_ARM"}, + {42, "EM_SH"}, + {43, "EM_SPARCV9"}, + {44, "EM_TRICORE"}, + {45, "EM_ARC"}, + {46, "EM_H8_300"}, + {47, "EM_H8_300H"}, + {48, "EM_H8S"}, + {49, "EM_H8_500"}, + {50, "EM_IA_64"}, + {51, "EM_MIPS_X"}, + {52, "EM_COLDFIRE"}, + {53, "EM_68HC12"}, + {54, "EM_MMA"}, + {55, "EM_PCP"}, + {56, "EM_NCPU"}, + {57, "EM_NDR1"}, + {58, "EM_STARCORE"}, + {59, "EM_ME16"}, + {60, "EM_ST100"}, + {61, "EM_TINYJ"}, + {62, "EM_X86_64"}, /* Non-standard or deprecated. */ - intName{6, "EM_486"}, - intName{10, "EM_MIPS_RS4_BE"}, - intName{41, "EM_ALPHA_STD"}, - intName{0x9026, "EM_ALPHA"}, + {6, "EM_486"}, + {10, "EM_MIPS_RS4_BE"}, + {41, "EM_ALPHA_STD"}, + {0x9026, "EM_ALPHA"}, } func (i Machine) String() string { return stringName(uint32(i), machineStrings, false) } @@ -315,12 +315,12 @@ const ( ) var shnStrings = []intName{ - intName{0, "SHN_UNDEF"}, - intName{0xff00, "SHN_LOPROC"}, - intName{0xff20, "SHN_LOOS"}, - intName{0xfff1, "SHN_ABS"}, - intName{0xfff2, "SHN_COMMON"}, - intName{0xffff, "SHN_XINDEX"}, + {0, "SHN_UNDEF"}, + {0xff00, "SHN_LOPROC"}, + {0xff20, "SHN_LOOS"}, + {0xfff1, "SHN_ABS"}, + {0xfff2, "SHN_COMMON"}, + {0xffff, "SHN_XINDEX"}, } func (i SectionIndex) String() string { return stringName(uint32(i), shnStrings, false) } @@ -356,29 +356,29 @@ const ( ) var shtStrings = []intName{ - intName{0, "SHT_NULL"}, - intName{1, "SHT_PROGBITS"}, - intName{2, "SHT_SYMTAB"}, - intName{3, "SHT_STRTAB"}, - intName{4, "SHT_RELA"}, - intName{5, "SHT_HASH"}, - intName{6, "SHT_DYNAMIC"}, - intName{7, "SHT_NOTE"}, - intName{8, "SHT_NOBITS"}, - intName{9, "SHT_REL"}, - intName{10, "SHT_SHLIB"}, - intName{11, "SHT_DYNSYM"}, - intName{14, "SHT_INIT_ARRAY"}, - intName{15, "SHT_FINI_ARRAY"}, - intName{16, "SHT_PREINIT_ARRAY"}, - intName{17, "SHT_GROUP"}, - intName{18, "SHT_SYMTAB_SHNDX"}, - intName{0x60000000, "SHT_LOOS"}, - intName{0x6fffffff, "SHT_HIOS"}, - intName{0x70000000, "SHT_LOPROC"}, - intName{0x7fffffff, "SHT_HIPROC"}, - intName{0x80000000, "SHT_LOUSER"}, - intName{0xffffffff, "SHT_HIUSER"}, + {0, "SHT_NULL"}, + {1, "SHT_PROGBITS"}, + {2, "SHT_SYMTAB"}, + {3, "SHT_STRTAB"}, + {4, "SHT_RELA"}, + {5, "SHT_HASH"}, + {6, "SHT_DYNAMIC"}, + {7, "SHT_NOTE"}, + {8, "SHT_NOBITS"}, + {9, "SHT_REL"}, + {10, "SHT_SHLIB"}, + {11, "SHT_DYNSYM"}, + {14, "SHT_INIT_ARRAY"}, + {15, "SHT_FINI_ARRAY"}, + {16, "SHT_PREINIT_ARRAY"}, + {17, "SHT_GROUP"}, + {18, "SHT_SYMTAB_SHNDX"}, + {0x60000000, "SHT_LOOS"}, + {0x6fffffff, "SHT_HIOS"}, + {0x70000000, "SHT_LOPROC"}, + {0x7fffffff, "SHT_HIPROC"}, + {0x80000000, "SHT_LOUSER"}, + {0xffffffff, "SHT_HIUSER"}, } func (i SectionType) String() string { return stringName(uint32(i), shtStrings, false) } @@ -403,16 +403,16 @@ const ( ) var shfStrings = []intName{ - intName{0x1, "SHF_WRITE"}, - intName{0x2, "SHF_ALLOC"}, - intName{0x4, "SHF_EXECINSTR"}, - intName{0x10, "SHF_MERGE"}, - intName{0x20, "SHF_STRINGS"}, - intName{0x40, "SHF_INFO_LINK"}, - intName{0x80, "SHF_LINK_ORDER"}, - intName{0x100, "SHF_OS_NONCONFORMING"}, - intName{0x200, "SHF_GROUP"}, - intName{0x400, "SHF_TLS"}, + {0x1, "SHF_WRITE"}, + {0x2, "SHF_ALLOC"}, + {0x4, "SHF_EXECINSTR"}, + {0x10, "SHF_MERGE"}, + {0x20, "SHF_STRINGS"}, + {0x40, "SHF_INFO_LINK"}, + {0x80, "SHF_LINK_ORDER"}, + {0x100, "SHF_OS_NONCONFORMING"}, + {0x200, "SHF_GROUP"}, + {0x400, "SHF_TLS"}, } func (i SectionFlag) String() string { return flagName(uint32(i), shfStrings, false) } @@ -437,18 +437,18 @@ const ( ) var ptStrings = []intName{ - intName{0, "PT_NULL"}, - intName{1, "PT_LOAD"}, - intName{2, "PT_DYNAMIC"}, - intName{3, "PT_INTERP"}, - intName{4, "PT_NOTE"}, - intName{5, "PT_SHLIB"}, - intName{6, "PT_PHDR"}, - intName{7, "PT_TLS"}, - intName{0x60000000, "PT_LOOS"}, - intName{0x6fffffff, "PT_HIOS"}, - intName{0x70000000, "PT_LOPROC"}, - intName{0x7fffffff, "PT_HIPROC"}, + {0, "PT_NULL"}, + {1, "PT_LOAD"}, + {2, "PT_DYNAMIC"}, + {3, "PT_INTERP"}, + {4, "PT_NOTE"}, + {5, "PT_SHLIB"}, + {6, "PT_PHDR"}, + {7, "PT_TLS"}, + {0x60000000, "PT_LOOS"}, + {0x6fffffff, "PT_HIOS"}, + {0x70000000, "PT_LOPROC"}, + {0x7fffffff, "PT_HIPROC"}, } func (i ProgType) String() string { return stringName(uint32(i), ptStrings, false) } @@ -466,9 +466,9 @@ const ( ) var pfStrings = []intName{ - intName{0x1, "PF_X"}, - intName{0x2, "PF_W"}, - intName{0x4, "PF_R"}, + {0x1, "PF_X"}, + {0x2, "PF_W"}, + {0x4, "PF_R"}, } func (i ProgFlag) String() string { return flagName(uint32(i), pfStrings, false) } @@ -523,44 +523,44 @@ const ( ) var dtStrings = []intName{ - intName{0, "DT_NULL"}, - intName{1, "DT_NEEDED"}, - intName{2, "DT_PLTRELSZ"}, - intName{3, "DT_PLTGOT"}, - intName{4, "DT_HASH"}, - intName{5, "DT_STRTAB"}, - intName{6, "DT_SYMTAB"}, - intName{7, "DT_RELA"}, - intName{8, "DT_RELASZ"}, - intName{9, "DT_RELAENT"}, - intName{10, "DT_STRSZ"}, - intName{11, "DT_SYMENT"}, - intName{12, "DT_INIT"}, - intName{13, "DT_FINI"}, - intName{14, "DT_SONAME"}, - intName{15, "DT_RPATH"}, - intName{16, "DT_SYMBOLIC"}, - intName{17, "DT_REL"}, - intName{18, "DT_RELSZ"}, - intName{19, "DT_RELENT"}, - intName{20, "DT_PLTREL"}, - intName{21, "DT_DEBUG"}, - intName{22, "DT_TEXTREL"}, - intName{23, "DT_JMPREL"}, - intName{24, "DT_BIND_NOW"}, - intName{25, "DT_INIT_ARRAY"}, - intName{26, "DT_FINI_ARRAY"}, - intName{27, "DT_INIT_ARRAYSZ"}, - intName{28, "DT_FINI_ARRAYSZ"}, - intName{29, "DT_RUNPATH"}, - intName{30, "DT_FLAGS"}, - intName{32, "DT_ENCODING"}, - intName{32, "DT_PREINIT_ARRAY"}, - intName{33, "DT_PREINIT_ARRAYSZ"}, - intName{0x6000000d, "DT_LOOS"}, - intName{0x6ffff000, "DT_HIOS"}, - intName{0x70000000, "DT_LOPROC"}, - intName{0x7fffffff, "DT_HIPROC"}, + {0, "DT_NULL"}, + {1, "DT_NEEDED"}, + {2, "DT_PLTRELSZ"}, + {3, "DT_PLTGOT"}, + {4, "DT_HASH"}, + {5, "DT_STRTAB"}, + {6, "DT_SYMTAB"}, + {7, "DT_RELA"}, + {8, "DT_RELASZ"}, + {9, "DT_RELAENT"}, + {10, "DT_STRSZ"}, + {11, "DT_SYMENT"}, + {12, "DT_INIT"}, + {13, "DT_FINI"}, + {14, "DT_SONAME"}, + {15, "DT_RPATH"}, + {16, "DT_SYMBOLIC"}, + {17, "DT_REL"}, + {18, "DT_RELSZ"}, + {19, "DT_RELENT"}, + {20, "DT_PLTREL"}, + {21, "DT_DEBUG"}, + {22, "DT_TEXTREL"}, + {23, "DT_JMPREL"}, + {24, "DT_BIND_NOW"}, + {25, "DT_INIT_ARRAY"}, + {26, "DT_FINI_ARRAY"}, + {27, "DT_INIT_ARRAYSZ"}, + {28, "DT_FINI_ARRAYSZ"}, + {29, "DT_RUNPATH"}, + {30, "DT_FLAGS"}, + {32, "DT_ENCODING"}, + {32, "DT_PREINIT_ARRAY"}, + {33, "DT_PREINIT_ARRAYSZ"}, + {0x6000000d, "DT_LOOS"}, + {0x6ffff000, "DT_HIOS"}, + {0x70000000, "DT_LOPROC"}, + {0x7fffffff, "DT_HIPROC"}, } func (i DynTag) String() string { return stringName(uint32(i), dtStrings, false) } @@ -585,11 +585,11 @@ const ( ) var dflagStrings = []intName{ - intName{0x0001, "DF_ORIGIN"}, - intName{0x0002, "DF_SYMBOLIC"}, - intName{0x0004, "DF_TEXTREL"}, - intName{0x0008, "DF_BIND_NOW"}, - intName{0x0010, "DF_STATIC_TLS"}, + {0x0001, "DF_ORIGIN"}, + {0x0002, "DF_SYMBOLIC"}, + {0x0004, "DF_TEXTREL"}, + {0x0008, "DF_BIND_NOW"}, + {0x0010, "DF_STATIC_TLS"}, } func (i DynFlag) String() string { return flagName(uint32(i), dflagStrings, false) } @@ -605,9 +605,9 @@ const ( ) var ntypeStrings = []intName{ - intName{1, "NT_PRSTATUS"}, - intName{2, "NT_FPREGSET"}, - intName{3, "NT_PRPSINFO"}, + {1, "NT_PRSTATUS"}, + {2, "NT_FPREGSET"}, + {3, "NT_PRPSINFO"}, } func (i NType) String() string { return stringName(uint32(i), ntypeStrings, false) } @@ -627,13 +627,13 @@ const ( ) var stbStrings = []intName{ - intName{0, "STB_LOCAL"}, - intName{1, "STB_GLOBAL"}, - intName{2, "STB_WEAK"}, - intName{10, "STB_LOOS"}, - intName{12, "STB_HIOS"}, - intName{13, "STB_LOPROC"}, - intName{15, "STB_HIPROC"}, + {0, "STB_LOCAL"}, + {1, "STB_GLOBAL"}, + {2, "STB_WEAK"}, + {10, "STB_LOOS"}, + {12, "STB_HIOS"}, + {13, "STB_LOPROC"}, + {15, "STB_HIPROC"}, } func (i SymBind) String() string { return stringName(uint32(i), stbStrings, false) } @@ -657,17 +657,17 @@ const ( ) var sttStrings = []intName{ - intName{0, "STT_NOTYPE"}, - intName{1, "STT_OBJECT"}, - intName{2, "STT_FUNC"}, - intName{3, "STT_SECTION"}, - intName{4, "STT_FILE"}, - intName{5, "STT_COMMON"}, - intName{6, "STT_TLS"}, - intName{10, "STT_LOOS"}, - intName{12, "STT_HIOS"}, - intName{13, "STT_LOPROC"}, - intName{15, "STT_HIPROC"}, + {0, "STT_NOTYPE"}, + {1, "STT_OBJECT"}, + {2, "STT_FUNC"}, + {3, "STT_SECTION"}, + {4, "STT_FILE"}, + {5, "STT_COMMON"}, + {6, "STT_TLS"}, + {10, "STT_LOOS"}, + {12, "STT_HIOS"}, + {13, "STT_LOPROC"}, + {15, "STT_HIPROC"}, } func (i SymType) String() string { return stringName(uint32(i), sttStrings, false) } @@ -684,10 +684,10 @@ const ( ) var stvStrings = []intName{ - intName{0x0, "STV_DEFAULT"}, - intName{0x1, "STV_INTERNAL"}, - intName{0x2, "STV_HIDDEN"}, - intName{0x3, "STV_PROTECTED"}, + {0x0, "STV_DEFAULT"}, + {0x1, "STV_INTERNAL"}, + {0x2, "STV_HIDDEN"}, + {0x3, "STV_PROTECTED"}, } func (i SymVis) String() string { return stringName(uint32(i), stvStrings, false) } @@ -728,30 +728,30 @@ const ( ) var rx86_64Strings = []intName{ - intName{0, "R_X86_64_NONE"}, - intName{1, "R_X86_64_64"}, - intName{2, "R_X86_64_PC32"}, - intName{3, "R_X86_64_GOT32"}, - intName{4, "R_X86_64_PLT32"}, - intName{5, "R_X86_64_COPY"}, - intName{6, "R_X86_64_GLOB_DAT"}, - intName{7, "R_X86_64_JMP_SLOT"}, - intName{8, "R_X86_64_RELATIVE"}, - intName{9, "R_X86_64_GOTPCREL"}, - intName{10, "R_X86_64_32"}, - intName{11, "R_X86_64_32S"}, - intName{12, "R_X86_64_16"}, - intName{13, "R_X86_64_PC16"}, - intName{14, "R_X86_64_8"}, - intName{15, "R_X86_64_PC8"}, - intName{16, "R_X86_64_DTPMOD64"}, - intName{17, "R_X86_64_DTPOFF64"}, - intName{18, "R_X86_64_TPOFF64"}, - intName{19, "R_X86_64_TLSGD"}, - intName{20, "R_X86_64_TLSLD"}, - intName{21, "R_X86_64_DTPOFF32"}, - intName{22, "R_X86_64_GOTTPOFF"}, - intName{23, "R_X86_64_TPOFF32"}, + {0, "R_X86_64_NONE"}, + {1, "R_X86_64_64"}, + {2, "R_X86_64_PC32"}, + {3, "R_X86_64_GOT32"}, + {4, "R_X86_64_PLT32"}, + {5, "R_X86_64_COPY"}, + {6, "R_X86_64_GLOB_DAT"}, + {7, "R_X86_64_JMP_SLOT"}, + {8, "R_X86_64_RELATIVE"}, + {9, "R_X86_64_GOTPCREL"}, + {10, "R_X86_64_32"}, + {11, "R_X86_64_32S"}, + {12, "R_X86_64_16"}, + {13, "R_X86_64_PC16"}, + {14, "R_X86_64_8"}, + {15, "R_X86_64_PC8"}, + {16, "R_X86_64_DTPMOD64"}, + {17, "R_X86_64_DTPOFF64"}, + {18, "R_X86_64_TPOFF64"}, + {19, "R_X86_64_TLSGD"}, + {20, "R_X86_64_TLSLD"}, + {21, "R_X86_64_DTPOFF32"}, + {22, "R_X86_64_GOTTPOFF"}, + {23, "R_X86_64_TPOFF32"}, } func (i R_X86_64) String() string { return stringName(uint32(i), rx86_64Strings, false) } @@ -792,34 +792,34 @@ const ( ) var ralphaStrings = []intName{ - intName{0, "R_ALPHA_NONE"}, - intName{1, "R_ALPHA_REFLONG"}, - intName{2, "R_ALPHA_REFQUAD"}, - intName{3, "R_ALPHA_GPREL32"}, - intName{4, "R_ALPHA_LITERAL"}, - intName{5, "R_ALPHA_LITUSE"}, - intName{6, "R_ALPHA_GPDISP"}, - intName{7, "R_ALPHA_BRADDR"}, - intName{8, "R_ALPHA_HINT"}, - intName{9, "R_ALPHA_SREL16"}, - intName{10, "R_ALPHA_SREL32"}, - intName{11, "R_ALPHA_SREL64"}, - intName{12, "R_ALPHA_OP_PUSH"}, - intName{13, "R_ALPHA_OP_STORE"}, - intName{14, "R_ALPHA_OP_PSUB"}, - intName{15, "R_ALPHA_OP_PRSHIFT"}, - intName{16, "R_ALPHA_GPVALUE"}, - intName{17, "R_ALPHA_GPRELHIGH"}, - intName{18, "R_ALPHA_GPRELLOW"}, - intName{19, "R_ALPHA_IMMED_GP_16"}, - intName{20, "R_ALPHA_IMMED_GP_HI32"}, - intName{21, "R_ALPHA_IMMED_SCN_HI32"}, - intName{22, "R_ALPHA_IMMED_BR_HI32"}, - intName{23, "R_ALPHA_IMMED_LO32"}, - intName{24, "R_ALPHA_COPY"}, - intName{25, "R_ALPHA_GLOB_DAT"}, - intName{26, "R_ALPHA_JMP_SLOT"}, - intName{27, "R_ALPHA_RELATIVE"}, + {0, "R_ALPHA_NONE"}, + {1, "R_ALPHA_REFLONG"}, + {2, "R_ALPHA_REFQUAD"}, + {3, "R_ALPHA_GPREL32"}, + {4, "R_ALPHA_LITERAL"}, + {5, "R_ALPHA_LITUSE"}, + {6, "R_ALPHA_GPDISP"}, + {7, "R_ALPHA_BRADDR"}, + {8, "R_ALPHA_HINT"}, + {9, "R_ALPHA_SREL16"}, + {10, "R_ALPHA_SREL32"}, + {11, "R_ALPHA_SREL64"}, + {12, "R_ALPHA_OP_PUSH"}, + {13, "R_ALPHA_OP_STORE"}, + {14, "R_ALPHA_OP_PSUB"}, + {15, "R_ALPHA_OP_PRSHIFT"}, + {16, "R_ALPHA_GPVALUE"}, + {17, "R_ALPHA_GPRELHIGH"}, + {18, "R_ALPHA_GPRELLOW"}, + {19, "R_ALPHA_IMMED_GP_16"}, + {20, "R_ALPHA_IMMED_GP_HI32"}, + {21, "R_ALPHA_IMMED_SCN_HI32"}, + {22, "R_ALPHA_IMMED_BR_HI32"}, + {23, "R_ALPHA_IMMED_LO32"}, + {24, "R_ALPHA_COPY"}, + {25, "R_ALPHA_GLOB_DAT"}, + {26, "R_ALPHA_JMP_SLOT"}, + {27, "R_ALPHA_RELATIVE"}, } func (i R_ALPHA) String() string { return stringName(uint32(i), ralphaStrings, false) } @@ -865,39 +865,39 @@ const ( ) var rarmStrings = []intName{ - intName{0, "R_ARM_NONE"}, - intName{1, "R_ARM_PC24"}, - intName{2, "R_ARM_ABS32"}, - intName{3, "R_ARM_REL32"}, - intName{4, "R_ARM_PC13"}, - intName{5, "R_ARM_ABS16"}, - intName{6, "R_ARM_ABS12"}, - intName{7, "R_ARM_THM_ABS5"}, - intName{8, "R_ARM_ABS8"}, - intName{9, "R_ARM_SBREL32"}, - intName{10, "R_ARM_THM_PC22"}, - intName{11, "R_ARM_THM_PC8"}, - intName{12, "R_ARM_AMP_VCALL9"}, - intName{13, "R_ARM_SWI24"}, - intName{14, "R_ARM_THM_SWI8"}, - intName{15, "R_ARM_XPC25"}, - intName{16, "R_ARM_THM_XPC22"}, - intName{20, "R_ARM_COPY"}, - intName{21, "R_ARM_GLOB_DAT"}, - intName{22, "R_ARM_JUMP_SLOT"}, - intName{23, "R_ARM_RELATIVE"}, - intName{24, "R_ARM_GOTOFF"}, - intName{25, "R_ARM_GOTPC"}, - intName{26, "R_ARM_GOT32"}, - intName{27, "R_ARM_PLT32"}, - intName{100, "R_ARM_GNU_VTENTRY"}, - intName{101, "R_ARM_GNU_VTINHERIT"}, - intName{250, "R_ARM_RSBREL32"}, - intName{251, "R_ARM_THM_RPC22"}, - intName{252, "R_ARM_RREL32"}, - intName{253, "R_ARM_RABS32"}, - intName{254, "R_ARM_RPC24"}, - intName{255, "R_ARM_RBASE"}, + {0, "R_ARM_NONE"}, + {1, "R_ARM_PC24"}, + {2, "R_ARM_ABS32"}, + {3, "R_ARM_REL32"}, + {4, "R_ARM_PC13"}, + {5, "R_ARM_ABS16"}, + {6, "R_ARM_ABS12"}, + {7, "R_ARM_THM_ABS5"}, + {8, "R_ARM_ABS8"}, + {9, "R_ARM_SBREL32"}, + {10, "R_ARM_THM_PC22"}, + {11, "R_ARM_THM_PC8"}, + {12, "R_ARM_AMP_VCALL9"}, + {13, "R_ARM_SWI24"}, + {14, "R_ARM_THM_SWI8"}, + {15, "R_ARM_XPC25"}, + {16, "R_ARM_THM_XPC22"}, + {20, "R_ARM_COPY"}, + {21, "R_ARM_GLOB_DAT"}, + {22, "R_ARM_JUMP_SLOT"}, + {23, "R_ARM_RELATIVE"}, + {24, "R_ARM_GOTOFF"}, + {25, "R_ARM_GOTPC"}, + {26, "R_ARM_GOT32"}, + {27, "R_ARM_PLT32"}, + {100, "R_ARM_GNU_VTENTRY"}, + {101, "R_ARM_GNU_VTINHERIT"}, + {250, "R_ARM_RSBREL32"}, + {251, "R_ARM_THM_RPC22"}, + {252, "R_ARM_RREL32"}, + {253, "R_ARM_RABS32"}, + {254, "R_ARM_RPC24"}, + {255, "R_ARM_RBASE"}, } func (i R_ARM) String() string { return stringName(uint32(i), rarmStrings, false) } @@ -941,37 +941,37 @@ const ( ) var r386Strings = []intName{ - intName{0, "R_386_NONE"}, - intName{1, "R_386_32"}, - intName{2, "R_386_PC32"}, - intName{3, "R_386_GOT32"}, - intName{4, "R_386_PLT32"}, - intName{5, "R_386_COPY"}, - intName{6, "R_386_GLOB_DAT"}, - intName{7, "R_386_JMP_SLOT"}, - intName{8, "R_386_RELATIVE"}, - intName{9, "R_386_GOTOFF"}, - intName{10, "R_386_GOTPC"}, - intName{14, "R_386_TLS_TPOFF"}, - intName{15, "R_386_TLS_IE"}, - intName{16, "R_386_TLS_GOTIE"}, - intName{17, "R_386_TLS_LE"}, - intName{18, "R_386_TLS_GD"}, - intName{19, "R_386_TLS_LDM"}, - intName{24, "R_386_TLS_GD_32"}, - intName{25, "R_386_TLS_GD_PUSH"}, - intName{26, "R_386_TLS_GD_CALL"}, - intName{27, "R_386_TLS_GD_POP"}, - intName{28, "R_386_TLS_LDM_32"}, - intName{29, "R_386_TLS_LDM_PUSH"}, - intName{30, "R_386_TLS_LDM_CALL"}, - intName{31, "R_386_TLS_LDM_POP"}, - intName{32, "R_386_TLS_LDO_32"}, - intName{33, "R_386_TLS_IE_32"}, - intName{34, "R_386_TLS_LE_32"}, - intName{35, "R_386_TLS_DTPMOD32"}, - intName{36, "R_386_TLS_DTPOFF32"}, - intName{37, "R_386_TLS_TPOFF32"}, + {0, "R_386_NONE"}, + {1, "R_386_32"}, + {2, "R_386_PC32"}, + {3, "R_386_GOT32"}, + {4, "R_386_PLT32"}, + {5, "R_386_COPY"}, + {6, "R_386_GLOB_DAT"}, + {7, "R_386_JMP_SLOT"}, + {8, "R_386_RELATIVE"}, + {9, "R_386_GOTOFF"}, + {10, "R_386_GOTPC"}, + {14, "R_386_TLS_TPOFF"}, + {15, "R_386_TLS_IE"}, + {16, "R_386_TLS_GOTIE"}, + {17, "R_386_TLS_LE"}, + {18, "R_386_TLS_GD"}, + {19, "R_386_TLS_LDM"}, + {24, "R_386_TLS_GD_32"}, + {25, "R_386_TLS_GD_PUSH"}, + {26, "R_386_TLS_GD_CALL"}, + {27, "R_386_TLS_GD_POP"}, + {28, "R_386_TLS_LDM_32"}, + {29, "R_386_TLS_LDM_PUSH"}, + {30, "R_386_TLS_LDM_CALL"}, + {31, "R_386_TLS_LDM_POP"}, + {32, "R_386_TLS_LDO_32"}, + {33, "R_386_TLS_IE_32"}, + {34, "R_386_TLS_LE_32"}, + {35, "R_386_TLS_DTPMOD32"}, + {36, "R_386_TLS_DTPOFF32"}, + {37, "R_386_TLS_TPOFF32"}, } func (i R_386) String() string { return stringName(uint32(i), r386Strings, false) } @@ -1061,85 +1061,85 @@ const ( ) var rppcStrings = []intName{ - intName{0, "R_PPC_NONE"}, - intName{1, "R_PPC_ADDR32"}, - intName{2, "R_PPC_ADDR24"}, - intName{3, "R_PPC_ADDR16"}, - intName{4, "R_PPC_ADDR16_LO"}, - intName{5, "R_PPC_ADDR16_HI"}, - intName{6, "R_PPC_ADDR16_HA"}, - intName{7, "R_PPC_ADDR14"}, - intName{8, "R_PPC_ADDR14_BRTAKEN"}, - intName{9, "R_PPC_ADDR14_BRNTAKEN"}, - intName{10, "R_PPC_REL24"}, - intName{11, "R_PPC_REL14"}, - intName{12, "R_PPC_REL14_BRTAKEN"}, - intName{13, "R_PPC_REL14_BRNTAKEN"}, - intName{14, "R_PPC_GOT16"}, - intName{15, "R_PPC_GOT16_LO"}, - intName{16, "R_PPC_GOT16_HI"}, - intName{17, "R_PPC_GOT16_HA"}, - intName{18, "R_PPC_PLTREL24"}, - intName{19, "R_PPC_COPY"}, - intName{20, "R_PPC_GLOB_DAT"}, - intName{21, "R_PPC_JMP_SLOT"}, - intName{22, "R_PPC_RELATIVE"}, - intName{23, "R_PPC_LOCAL24PC"}, - intName{24, "R_PPC_UADDR32"}, - intName{25, "R_PPC_UADDR16"}, - intName{26, "R_PPC_REL32"}, - intName{27, "R_PPC_PLT32"}, - intName{28, "R_PPC_PLTREL32"}, - intName{29, "R_PPC_PLT16_LO"}, - intName{30, "R_PPC_PLT16_HI"}, - intName{31, "R_PPC_PLT16_HA"}, - intName{32, "R_PPC_SDAREL16"}, - intName{33, "R_PPC_SECTOFF"}, - intName{34, "R_PPC_SECTOFF_LO"}, - intName{35, "R_PPC_SECTOFF_HI"}, - intName{36, "R_PPC_SECTOFF_HA"}, - - intName{67, "R_PPC_TLS"}, - intName{68, "R_PPC_DTPMOD32"}, - intName{69, "R_PPC_TPREL16"}, - intName{70, "R_PPC_TPREL16_LO"}, - intName{71, "R_PPC_TPREL16_HI"}, - intName{72, "R_PPC_TPREL16_HA"}, - intName{73, "R_PPC_TPREL32"}, - intName{74, "R_PPC_DTPREL16"}, - intName{75, "R_PPC_DTPREL16_LO"}, - intName{76, "R_PPC_DTPREL16_HI"}, - intName{77, "R_PPC_DTPREL16_HA"}, - intName{78, "R_PPC_DTPREL32"}, - intName{79, "R_PPC_GOT_TLSGD16"}, - intName{80, "R_PPC_GOT_TLSGD16_LO"}, - intName{81, "R_PPC_GOT_TLSGD16_HI"}, - intName{82, "R_PPC_GOT_TLSGD16_HA"}, - intName{83, "R_PPC_GOT_TLSLD16"}, - intName{84, "R_PPC_GOT_TLSLD16_LO"}, - intName{85, "R_PPC_GOT_TLSLD16_HI"}, - intName{86, "R_PPC_GOT_TLSLD16_HA"}, - intName{87, "R_PPC_GOT_TPREL16"}, - intName{88, "R_PPC_GOT_TPREL16_LO"}, - intName{89, "R_PPC_GOT_TPREL16_HI"}, - intName{90, "R_PPC_GOT_TPREL16_HA"}, - - intName{101, "R_PPC_EMB_NADDR32"}, - intName{102, "R_PPC_EMB_NADDR16"}, - intName{103, "R_PPC_EMB_NADDR16_LO"}, - intName{104, "R_PPC_EMB_NADDR16_HI"}, - intName{105, "R_PPC_EMB_NADDR16_HA"}, - intName{106, "R_PPC_EMB_SDAI16"}, - intName{107, "R_PPC_EMB_SDA2I16"}, - intName{108, "R_PPC_EMB_SDA2REL"}, - intName{109, "R_PPC_EMB_SDA21"}, - intName{110, "R_PPC_EMB_MRKREF"}, - intName{111, "R_PPC_EMB_RELSEC16"}, - intName{112, "R_PPC_EMB_RELST_LO"}, - intName{113, "R_PPC_EMB_RELST_HI"}, - intName{114, "R_PPC_EMB_RELST_HA"}, - intName{115, "R_PPC_EMB_BIT_FLD"}, - intName{116, "R_PPC_EMB_RELSDA"}, + {0, "R_PPC_NONE"}, + {1, "R_PPC_ADDR32"}, + {2, "R_PPC_ADDR24"}, + {3, "R_PPC_ADDR16"}, + {4, "R_PPC_ADDR16_LO"}, + {5, "R_PPC_ADDR16_HI"}, + {6, "R_PPC_ADDR16_HA"}, + {7, "R_PPC_ADDR14"}, + {8, "R_PPC_ADDR14_BRTAKEN"}, + {9, "R_PPC_ADDR14_BRNTAKEN"}, + {10, "R_PPC_REL24"}, + {11, "R_PPC_REL14"}, + {12, "R_PPC_REL14_BRTAKEN"}, + {13, "R_PPC_REL14_BRNTAKEN"}, + {14, "R_PPC_GOT16"}, + {15, "R_PPC_GOT16_LO"}, + {16, "R_PPC_GOT16_HI"}, + {17, "R_PPC_GOT16_HA"}, + {18, "R_PPC_PLTREL24"}, + {19, "R_PPC_COPY"}, + {20, "R_PPC_GLOB_DAT"}, + {21, "R_PPC_JMP_SLOT"}, + {22, "R_PPC_RELATIVE"}, + {23, "R_PPC_LOCAL24PC"}, + {24, "R_PPC_UADDR32"}, + {25, "R_PPC_UADDR16"}, + {26, "R_PPC_REL32"}, + {27, "R_PPC_PLT32"}, + {28, "R_PPC_PLTREL32"}, + {29, "R_PPC_PLT16_LO"}, + {30, "R_PPC_PLT16_HI"}, + {31, "R_PPC_PLT16_HA"}, + {32, "R_PPC_SDAREL16"}, + {33, "R_PPC_SECTOFF"}, + {34, "R_PPC_SECTOFF_LO"}, + {35, "R_PPC_SECTOFF_HI"}, + {36, "R_PPC_SECTOFF_HA"}, + + {67, "R_PPC_TLS"}, + {68, "R_PPC_DTPMOD32"}, + {69, "R_PPC_TPREL16"}, + {70, "R_PPC_TPREL16_LO"}, + {71, "R_PPC_TPREL16_HI"}, + {72, "R_PPC_TPREL16_HA"}, + {73, "R_PPC_TPREL32"}, + {74, "R_PPC_DTPREL16"}, + {75, "R_PPC_DTPREL16_LO"}, + {76, "R_PPC_DTPREL16_HI"}, + {77, "R_PPC_DTPREL16_HA"}, + {78, "R_PPC_DTPREL32"}, + {79, "R_PPC_GOT_TLSGD16"}, + {80, "R_PPC_GOT_TLSGD16_LO"}, + {81, "R_PPC_GOT_TLSGD16_HI"}, + {82, "R_PPC_GOT_TLSGD16_HA"}, + {83, "R_PPC_GOT_TLSLD16"}, + {84, "R_PPC_GOT_TLSLD16_LO"}, + {85, "R_PPC_GOT_TLSLD16_HI"}, + {86, "R_PPC_GOT_TLSLD16_HA"}, + {87, "R_PPC_GOT_TPREL16"}, + {88, "R_PPC_GOT_TPREL16_LO"}, + {89, "R_PPC_GOT_TPREL16_HI"}, + {90, "R_PPC_GOT_TPREL16_HA"}, + + {101, "R_PPC_EMB_NADDR32"}, + {102, "R_PPC_EMB_NADDR16"}, + {103, "R_PPC_EMB_NADDR16_LO"}, + {104, "R_PPC_EMB_NADDR16_HI"}, + {105, "R_PPC_EMB_NADDR16_HA"}, + {106, "R_PPC_EMB_SDAI16"}, + {107, "R_PPC_EMB_SDA2I16"}, + {108, "R_PPC_EMB_SDA2REL"}, + {109, "R_PPC_EMB_SDA21"}, + {110, "R_PPC_EMB_MRKREF"}, + {111, "R_PPC_EMB_RELSEC16"}, + {112, "R_PPC_EMB_RELST_LO"}, + {113, "R_PPC_EMB_RELST_HI"}, + {114, "R_PPC_EMB_RELST_HA"}, + {115, "R_PPC_EMB_BIT_FLD"}, + {116, "R_PPC_EMB_RELSDA"}, } func (i R_PPC) String() string { return stringName(uint32(i), rppcStrings, false) } @@ -1208,62 +1208,62 @@ const ( ) var rsparcStrings = []intName{ - intName{0, "R_SPARC_NONE"}, - intName{1, "R_SPARC_8"}, - intName{2, "R_SPARC_16"}, - intName{3, "R_SPARC_32"}, - intName{4, "R_SPARC_DISP8"}, - intName{5, "R_SPARC_DISP16"}, - intName{6, "R_SPARC_DISP32"}, - intName{7, "R_SPARC_WDISP30"}, - intName{8, "R_SPARC_WDISP22"}, - intName{9, "R_SPARC_HI22"}, - intName{10, "R_SPARC_22"}, - intName{11, "R_SPARC_13"}, - intName{12, "R_SPARC_LO10"}, - intName{13, "R_SPARC_GOT10"}, - intName{14, "R_SPARC_GOT13"}, - intName{15, "R_SPARC_GOT22"}, - intName{16, "R_SPARC_PC10"}, - intName{17, "R_SPARC_PC22"}, - intName{18, "R_SPARC_WPLT30"}, - intName{19, "R_SPARC_COPY"}, - intName{20, "R_SPARC_GLOB_DAT"}, - intName{21, "R_SPARC_JMP_SLOT"}, - intName{22, "R_SPARC_RELATIVE"}, - intName{23, "R_SPARC_UA32"}, - intName{24, "R_SPARC_PLT32"}, - intName{25, "R_SPARC_HIPLT22"}, - intName{26, "R_SPARC_LOPLT10"}, - intName{27, "R_SPARC_PCPLT32"}, - intName{28, "R_SPARC_PCPLT22"}, - intName{29, "R_SPARC_PCPLT10"}, - intName{30, "R_SPARC_10"}, - intName{31, "R_SPARC_11"}, - intName{32, "R_SPARC_64"}, - intName{33, "R_SPARC_OLO10"}, - intName{34, "R_SPARC_HH22"}, - intName{35, "R_SPARC_HM10"}, - intName{36, "R_SPARC_LM22"}, - intName{37, "R_SPARC_PC_HH22"}, - intName{38, "R_SPARC_PC_HM10"}, - intName{39, "R_SPARC_PC_LM22"}, - intName{40, "R_SPARC_WDISP16"}, - intName{41, "R_SPARC_WDISP19"}, - intName{42, "R_SPARC_GLOB_JMP"}, - intName{43, "R_SPARC_7"}, - intName{44, "R_SPARC_5"}, - intName{45, "R_SPARC_6"}, - intName{46, "R_SPARC_DISP64"}, - intName{47, "R_SPARC_PLT64"}, - intName{48, "R_SPARC_HIX22"}, - intName{49, "R_SPARC_LOX10"}, - intName{50, "R_SPARC_H44"}, - intName{51, "R_SPARC_M44"}, - intName{52, "R_SPARC_L44"}, - intName{53, "R_SPARC_REGISTER"}, - intName{54, "R_SPARC_UA64"}, - intName{55, "R_SPARC_UA16"}, + {0, "R_SPARC_NONE"}, + {1, "R_SPARC_8"}, + {2, "R_SPARC_16"}, + {3, "R_SPARC_32"}, + {4, "R_SPARC_DISP8"}, + {5, "R_SPARC_DISP16"}, + {6, "R_SPARC_DISP32"}, + {7, "R_SPARC_WDISP30"}, + {8, "R_SPARC_WDISP22"}, + {9, "R_SPARC_HI22"}, + {10, "R_SPARC_22"}, + {11, "R_SPARC_13"}, + {12, "R_SPARC_LO10"}, + {13, "R_SPARC_GOT10"}, + {14, "R_SPARC_GOT13"}, + {15, "R_SPARC_GOT22"}, + {16, "R_SPARC_PC10"}, + {17, "R_SPARC_PC22"}, + {18, "R_SPARC_WPLT30"}, + {19, "R_SPARC_COPY"}, + {20, "R_SPARC_GLOB_DAT"}, + {21, "R_SPARC_JMP_SLOT"}, + {22, "R_SPARC_RELATIVE"}, + {23, "R_SPARC_UA32"}, + {24, "R_SPARC_PLT32"}, + {25, "R_SPARC_HIPLT22"}, + {26, "R_SPARC_LOPLT10"}, + {27, "R_SPARC_PCPLT32"}, + {28, "R_SPARC_PCPLT22"}, + {29, "R_SPARC_PCPLT10"}, + {30, "R_SPARC_10"}, + {31, "R_SPARC_11"}, + {32, "R_SPARC_64"}, + {33, "R_SPARC_OLO10"}, + {34, "R_SPARC_HH22"}, + {35, "R_SPARC_HM10"}, + {36, "R_SPARC_LM22"}, + {37, "R_SPARC_PC_HH22"}, + {38, "R_SPARC_PC_HM10"}, + {39, "R_SPARC_PC_LM22"}, + {40, "R_SPARC_WDISP16"}, + {41, "R_SPARC_WDISP19"}, + {42, "R_SPARC_GLOB_JMP"}, + {43, "R_SPARC_7"}, + {44, "R_SPARC_5"}, + {45, "R_SPARC_6"}, + {46, "R_SPARC_DISP64"}, + {47, "R_SPARC_PLT64"}, + {48, "R_SPARC_HIX22"}, + {49, "R_SPARC_LOX10"}, + {50, "R_SPARC_H44"}, + {51, "R_SPARC_M44"}, + {52, "R_SPARC_L44"}, + {53, "R_SPARC_REGISTER"}, + {54, "R_SPARC_UA64"}, + {55, "R_SPARC_UA16"}, } func (i R_SPARC) String() string { return stringName(uint32(i), rsparcStrings, false) } @@ -1356,9 +1356,12 @@ type Sym32 struct { const Sym32Size = 16 -func ST_BIND(info uint8) SymBind { return SymBind(info >> 4) } -func ST_TYPE(bind SymBind, typ SymType) uint8 { return uint8(bind)<<4 | uint8(typ)&0xf } -func ST_VISIBILITY(other uint8) SymVis { return SymVis(other & 3) } +func ST_BIND(info uint8) SymBind { return SymBind(info >> 4) } +func ST_TYPE(info uint8) SymType { return SymType(info & 0xF) } +func ST_INFO(bind SymBind, typ SymType) uint8 { + return uint8(bind)<<4 | uint8(typ)&0xf +} +func ST_VISIBILITY(other uint8) SymVis { return SymVis(other & 3) } /* * ELF64 diff --git a/src/pkg/debug/elf/elf_test.go b/src/pkg/debug/elf/elf_test.go index 6f827faf0..67b961b5c 100644 --- a/src/pkg/debug/elf/elf_test.go +++ b/src/pkg/debug/elf/elf_test.go @@ -15,28 +15,28 @@ type nameTest struct { } var nameTests = []nameTest{ - nameTest{ELFOSABI_LINUX, "ELFOSABI_LINUX"}, - nameTest{ET_EXEC, "ET_EXEC"}, - nameTest{EM_860, "EM_860"}, - nameTest{SHN_LOPROC, "SHN_LOPROC"}, - nameTest{SHT_PROGBITS, "SHT_PROGBITS"}, - nameTest{SHF_MERGE + SHF_TLS, "SHF_MERGE+SHF_TLS"}, - nameTest{PT_LOAD, "PT_LOAD"}, - nameTest{PF_W + PF_R + 0x50, "PF_W+PF_R+0x50"}, - nameTest{DT_SYMBOLIC, "DT_SYMBOLIC"}, - nameTest{DF_BIND_NOW, "DF_BIND_NOW"}, - nameTest{NT_FPREGSET, "NT_FPREGSET"}, - nameTest{STB_GLOBAL, "STB_GLOBAL"}, - nameTest{STT_COMMON, "STT_COMMON"}, - nameTest{STV_HIDDEN, "STV_HIDDEN"}, - nameTest{R_X86_64_PC32, "R_X86_64_PC32"}, - nameTest{R_ALPHA_OP_PUSH, "R_ALPHA_OP_PUSH"}, - nameTest{R_ARM_THM_ABS5, "R_ARM_THM_ABS5"}, - nameTest{R_386_GOT32, "R_386_GOT32"}, - nameTest{R_PPC_GOT16_HI, "R_PPC_GOT16_HI"}, - nameTest{R_SPARC_GOT22, "R_SPARC_GOT22"}, - nameTest{ET_LOOS + 5, "ET_LOOS+5"}, - nameTest{ProgFlag(0x50), "0x50"}, + {ELFOSABI_LINUX, "ELFOSABI_LINUX"}, + {ET_EXEC, "ET_EXEC"}, + {EM_860, "EM_860"}, + {SHN_LOPROC, "SHN_LOPROC"}, + {SHT_PROGBITS, "SHT_PROGBITS"}, + {SHF_MERGE + SHF_TLS, "SHF_MERGE+SHF_TLS"}, + {PT_LOAD, "PT_LOAD"}, + {PF_W + PF_R + 0x50, "PF_W+PF_R+0x50"}, + {DT_SYMBOLIC, "DT_SYMBOLIC"}, + {DF_BIND_NOW, "DF_BIND_NOW"}, + {NT_FPREGSET, "NT_FPREGSET"}, + {STB_GLOBAL, "STB_GLOBAL"}, + {STT_COMMON, "STT_COMMON"}, + {STV_HIDDEN, "STV_HIDDEN"}, + {R_X86_64_PC32, "R_X86_64_PC32"}, + {R_ALPHA_OP_PUSH, "R_ALPHA_OP_PUSH"}, + {R_ARM_THM_ABS5, "R_ARM_THM_ABS5"}, + {R_386_GOT32, "R_386_GOT32"}, + {R_PPC_GOT16_HI, "R_PPC_GOT16_HI"}, + {R_SPARC_GOT22, "R_SPARC_GOT22"}, + {ET_LOOS + 5, "ET_LOOS+5"}, + {ProgFlag(0x50), "0x50"}, } func TestNames(t *testing.T) { diff --git a/src/pkg/debug/elf/file.go b/src/pkg/debug/elf/file.go index 568370b85..e69317a75 100644 --- a/src/pkg/debug/elf/file.go +++ b/src/pkg/debug/elf/file.go @@ -75,6 +75,15 @@ func (s *Section) Data() ([]byte, os.Error) { return dat[0:n], err } +// stringTable reads and returns the string table given by the +// specified link value. +func (f *File) stringTable(link uint32) ([]byte, os.Error) { + if link <= 0 || link >= uint32(len(f.Sections)) { + return nil, os.ErrorString("section has invalid string table link") + } + return f.Sections[link].Data() +} + // Open returns a new ReadSeeker reading the ELF section. func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } @@ -108,9 +117,9 @@ func (p *Prog) Open() io.ReadSeeker { return io.NewSectionReader(p.sr, 0, 1<<63- // A Symbol represents an entry in an ELF symbol table section. type Symbol struct { - Name uint32 + Name string Info, Other byte - Section uint32 + Section SectionIndex Value, Size uint64 } @@ -160,6 +169,17 @@ func (f *File) Close() os.Error { return err } +// SectionByType returns the first section in f with the +// given type, or nil if there is no such section. +func (f *File) SectionByType(typ SectionType) *Section { + for _, s := range f.Sections { + if s.Type == typ { + return s + } + } + return nil +} + // NewFile creates a new File for accessing an ELF binary in an underlying reader. // The ELF binary is expected to start at position 0 in the ReaderAt. func NewFile(r io.ReaderAt) (*File, os.Error) { @@ -293,9 +313,8 @@ func NewFile(r io.ReaderAt) (*File, os.Error) { } // Load section header string table. - s := f.Sections[shstrndx] - shstrtab := make([]byte, s.Size) - if _, err := r.ReadAt(shstrtab, int64(s.Offset)); err != nil { + shstrtab, err := f.Sections[shstrndx].Data() + if err != nil { return nil, err } for i, s := range f.Sections { @@ -309,25 +328,65 @@ func NewFile(r io.ReaderAt) (*File, os.Error) { return f, nil } -func (f *File) getSymbols() ([]Symbol, os.Error) { +// getSymbols returns a slice of Symbols from parsing the symbol table +// with the given type. +func (f *File) getSymbols(typ SectionType) ([]Symbol, os.Error) { switch f.Class { case ELFCLASS64: - return f.getSymbols64() + return f.getSymbols64(typ) + + case ELFCLASS32: + return f.getSymbols32(typ) } return nil, os.ErrorString("not implemented") } -// GetSymbols returns a slice of Symbols from parsing the symbol table. -func (f *File) getSymbols64() ([]Symbol, os.Error) { - var symtabSection *Section - for _, section := range f.Sections { - if section.Type == SHT_SYMTAB { - symtabSection = section - break - } +func (f *File) getSymbols32(typ SectionType) ([]Symbol, os.Error) { + symtabSection := f.SectionByType(typ) + if symtabSection == nil { + return nil, os.ErrorString("no symbol section") + } + + data, err := symtabSection.Data() + if err != nil { + return nil, os.ErrorString("cannot load symbol section") + } + symtab := bytes.NewBuffer(data) + if symtab.Len()%Sym32Size != 0 { + return nil, os.ErrorString("length of symbol section is not a multiple of SymSize") + } + + strdata, err := f.stringTable(symtabSection.Link) + if err != nil { + return nil, os.ErrorString("cannot load string table section") + } + + // The first entry is all zeros. + var skip [Sym32Size]byte + symtab.Read(skip[0:]) + + symbols := make([]Symbol, symtab.Len()/Sym32Size) + + i := 0 + var sym Sym32 + for symtab.Len() > 0 { + binary.Read(symtab, f.ByteOrder, &sym) + str, _ := getString(strdata, int(sym.Name)) + symbols[i].Name = str + symbols[i].Info = sym.Info + symbols[i].Other = sym.Other + symbols[i].Section = SectionIndex(sym.Shndx) + symbols[i].Value = uint64(sym.Value) + symbols[i].Size = uint64(sym.Size) + i++ } + return symbols, nil +} + +func (f *File) getSymbols64(typ SectionType) ([]Symbol, os.Error) { + symtabSection := f.SectionByType(typ) if symtabSection == nil { return nil, os.ErrorString("no symbol section") } @@ -341,6 +400,11 @@ func (f *File) getSymbols64() ([]Symbol, os.Error) { return nil, os.ErrorString("length of symbol section is not a multiple of Sym64Size") } + strdata, err := f.stringTable(symtabSection.Link) + if err != nil { + return nil, os.ErrorString("cannot load string table section") + } + // The first entry is all zeros. var skip [Sym64Size]byte symtab.Read(skip[0:]) @@ -351,10 +415,11 @@ func (f *File) getSymbols64() ([]Symbol, os.Error) { var sym Sym64 for symtab.Len() > 0 { binary.Read(symtab, f.ByteOrder, &sym) - symbols[i].Name = sym.Name + str, _ := getString(strdata, int(sym.Name)) + symbols[i].Name = str symbols[i].Info = sym.Info symbols[i].Other = sym.Other - symbols[i].Section = uint32(sym.Shndx) + symbols[i].Section = SectionIndex(sym.Shndx) symbols[i].Value = sym.Value symbols[i].Size = sym.Size i++ @@ -403,7 +468,7 @@ func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) os.Error { return os.ErrorString("length of relocation section is not a multiple of Sym64Size") } - symbols, err := f.getSymbols() + symbols, err := f.getSymbols(SHT_SYMTAB) if err != nil { return err } @@ -478,3 +543,63 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) { abbrev, info, str := dat[0], dat[1], dat[2] return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str) } + +// ImportedSymbols returns the names of all symbols +// referred to by the binary f that are expected to be +// satisfied by other libraries at dynamic load time. +// It does not return weak symbols. +func (f *File) ImportedSymbols() ([]string, os.Error) { + sym, err := f.getSymbols(SHT_DYNSYM) + if err != nil { + return nil, err + } + var all []string + for _, s := range sym { + if ST_BIND(s.Info) == STB_GLOBAL && s.Section == SHN_UNDEF { + all = append(all, s.Name) + } + } + return all, nil +} + +// ImportedLibraries returns the names of all libraries +// referred to by the binary f that are expected to be +// linked with the binary at dynamic link time. +func (f *File) ImportedLibraries() ([]string, os.Error) { + ds := f.SectionByType(SHT_DYNAMIC) + if ds == nil { + // not dynamic, so no libraries + return nil, nil + } + d, err := ds.Data() + if err != nil { + return nil, err + } + str, err := f.stringTable(ds.Link) + if err != nil { + return nil, err + } + var all []string + for len(d) > 0 { + var tag DynTag + var value uint64 + switch f.Class { + case ELFCLASS32: + tag = DynTag(f.ByteOrder.Uint32(d[0:4])) + value = uint64(f.ByteOrder.Uint32(d[4:8])) + d = d[8:] + case ELFCLASS64: + tag = DynTag(f.ByteOrder.Uint64(d[0:8])) + value = f.ByteOrder.Uint64(d[8:16]) + d = d[16:] + } + if tag == DT_NEEDED { + s, ok := getString(str, int(value)) + if ok { + all = append(all, s) + } + } + } + + return all, nil +} diff --git a/src/pkg/debug/elf/file_test.go b/src/pkg/debug/elf/file_test.go index 49adc4dc7..84068ea12 100644 --- a/src/pkg/debug/elf/file_test.go +++ b/src/pkg/debug/elf/file_test.go @@ -18,83 +18,83 @@ type fileTest struct { } var fileTests = []fileTest{ - fileTest{ + { "testdata/gcc-386-freebsd-exec", FileHeader{ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_FREEBSD, 0, binary.LittleEndian, ET_EXEC, EM_386}, []SectionHeader{ - SectionHeader{"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - SectionHeader{".interp", SHT_PROGBITS, SHF_ALLOC, 0x80480d4, 0xd4, 0x15, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".hash", SHT_HASH, SHF_ALLOC, 0x80480ec, 0xec, 0x90, 0x3, 0x0, 0x4, 0x4}, - SectionHeader{".dynsym", SHT_DYNSYM, SHF_ALLOC, 0x804817c, 0x17c, 0x110, 0x4, 0x1, 0x4, 0x10}, - SectionHeader{".dynstr", SHT_STRTAB, SHF_ALLOC, 0x804828c, 0x28c, 0xbb, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".rel.plt", SHT_REL, SHF_ALLOC, 0x8048348, 0x348, 0x20, 0x3, 0x7, 0x4, 0x8}, - SectionHeader{".init", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x8048368, 0x368, 0x11, 0x0, 0x0, 0x4, 0x0}, - SectionHeader{".plt", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x804837c, 0x37c, 0x50, 0x0, 0x0, 0x4, 0x4}, - SectionHeader{".text", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x80483cc, 0x3cc, 0x180, 0x0, 0x0, 0x4, 0x0}, - SectionHeader{".fini", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x804854c, 0x54c, 0xc, 0x0, 0x0, 0x4, 0x0}, - SectionHeader{".rodata", SHT_PROGBITS, SHF_ALLOC, 0x8048558, 0x558, 0xa3, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".data", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80495fc, 0x5fc, 0xc, 0x0, 0x0, 0x4, 0x0}, - SectionHeader{".eh_frame", SHT_PROGBITS, SHF_ALLOC, 0x8049608, 0x608, 0x4, 0x0, 0x0, 0x4, 0x0}, - SectionHeader{".dynamic", SHT_DYNAMIC, SHF_WRITE + SHF_ALLOC, 0x804960c, 0x60c, 0x98, 0x4, 0x0, 0x4, 0x8}, - SectionHeader{".ctors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496a4, 0x6a4, 0x8, 0x0, 0x0, 0x4, 0x0}, - SectionHeader{".dtors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496ac, 0x6ac, 0x8, 0x0, 0x0, 0x4, 0x0}, - SectionHeader{".jcr", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496b4, 0x6b4, 0x4, 0x0, 0x0, 0x4, 0x0}, - SectionHeader{".got", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496b8, 0x6b8, 0x1c, 0x0, 0x0, 0x4, 0x4}, - SectionHeader{".bss", SHT_NOBITS, SHF_WRITE + SHF_ALLOC, 0x80496d4, 0x6d4, 0x20, 0x0, 0x0, 0x4, 0x0}, - SectionHeader{".comment", SHT_PROGBITS, 0x0, 0x0, 0x6d4, 0x12d, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".debug_aranges", SHT_PROGBITS, 0x0, 0x0, 0x801, 0x20, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".debug_pubnames", SHT_PROGBITS, 0x0, 0x0, 0x821, 0x1b, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".debug_info", SHT_PROGBITS, 0x0, 0x0, 0x83c, 0x11d, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".debug_abbrev", SHT_PROGBITS, 0x0, 0x0, 0x959, 0x41, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".debug_line", SHT_PROGBITS, 0x0, 0x0, 0x99a, 0x35, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".debug_frame", SHT_PROGBITS, 0x0, 0x0, 0x9d0, 0x30, 0x0, 0x0, 0x4, 0x0}, - SectionHeader{".debug_str", SHT_PROGBITS, 0x0, 0x0, 0xa00, 0xd, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".shstrtab", SHT_STRTAB, 0x0, 0x0, 0xa0d, 0xf8, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".symtab", SHT_SYMTAB, 0x0, 0x0, 0xfb8, 0x4b0, 0x1d, 0x38, 0x4, 0x10}, - SectionHeader{".strtab", SHT_STRTAB, 0x0, 0x0, 0x1468, 0x206, 0x0, 0x0, 0x1, 0x0}, + {"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {".interp", SHT_PROGBITS, SHF_ALLOC, 0x80480d4, 0xd4, 0x15, 0x0, 0x0, 0x1, 0x0}, + {".hash", SHT_HASH, SHF_ALLOC, 0x80480ec, 0xec, 0x90, 0x3, 0x0, 0x4, 0x4}, + {".dynsym", SHT_DYNSYM, SHF_ALLOC, 0x804817c, 0x17c, 0x110, 0x4, 0x1, 0x4, 0x10}, + {".dynstr", SHT_STRTAB, SHF_ALLOC, 0x804828c, 0x28c, 0xbb, 0x0, 0x0, 0x1, 0x0}, + {".rel.plt", SHT_REL, SHF_ALLOC, 0x8048348, 0x348, 0x20, 0x3, 0x7, 0x4, 0x8}, + {".init", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x8048368, 0x368, 0x11, 0x0, 0x0, 0x4, 0x0}, + {".plt", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x804837c, 0x37c, 0x50, 0x0, 0x0, 0x4, 0x4}, + {".text", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x80483cc, 0x3cc, 0x180, 0x0, 0x0, 0x4, 0x0}, + {".fini", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x804854c, 0x54c, 0xc, 0x0, 0x0, 0x4, 0x0}, + {".rodata", SHT_PROGBITS, SHF_ALLOC, 0x8048558, 0x558, 0xa3, 0x0, 0x0, 0x1, 0x0}, + {".data", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80495fc, 0x5fc, 0xc, 0x0, 0x0, 0x4, 0x0}, + {".eh_frame", SHT_PROGBITS, SHF_ALLOC, 0x8049608, 0x608, 0x4, 0x0, 0x0, 0x4, 0x0}, + {".dynamic", SHT_DYNAMIC, SHF_WRITE + SHF_ALLOC, 0x804960c, 0x60c, 0x98, 0x4, 0x0, 0x4, 0x8}, + {".ctors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496a4, 0x6a4, 0x8, 0x0, 0x0, 0x4, 0x0}, + {".dtors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496ac, 0x6ac, 0x8, 0x0, 0x0, 0x4, 0x0}, + {".jcr", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496b4, 0x6b4, 0x4, 0x0, 0x0, 0x4, 0x0}, + {".got", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496b8, 0x6b8, 0x1c, 0x0, 0x0, 0x4, 0x4}, + {".bss", SHT_NOBITS, SHF_WRITE + SHF_ALLOC, 0x80496d4, 0x6d4, 0x20, 0x0, 0x0, 0x4, 0x0}, + {".comment", SHT_PROGBITS, 0x0, 0x0, 0x6d4, 0x12d, 0x0, 0x0, 0x1, 0x0}, + {".debug_aranges", SHT_PROGBITS, 0x0, 0x0, 0x801, 0x20, 0x0, 0x0, 0x1, 0x0}, + {".debug_pubnames", SHT_PROGBITS, 0x0, 0x0, 0x821, 0x1b, 0x0, 0x0, 0x1, 0x0}, + {".debug_info", SHT_PROGBITS, 0x0, 0x0, 0x83c, 0x11d, 0x0, 0x0, 0x1, 0x0}, + {".debug_abbrev", SHT_PROGBITS, 0x0, 0x0, 0x959, 0x41, 0x0, 0x0, 0x1, 0x0}, + {".debug_line", SHT_PROGBITS, 0x0, 0x0, 0x99a, 0x35, 0x0, 0x0, 0x1, 0x0}, + {".debug_frame", SHT_PROGBITS, 0x0, 0x0, 0x9d0, 0x30, 0x0, 0x0, 0x4, 0x0}, + {".debug_str", SHT_PROGBITS, 0x0, 0x0, 0xa00, 0xd, 0x0, 0x0, 0x1, 0x0}, + {".shstrtab", SHT_STRTAB, 0x0, 0x0, 0xa0d, 0xf8, 0x0, 0x0, 0x1, 0x0}, + {".symtab", SHT_SYMTAB, 0x0, 0x0, 0xfb8, 0x4b0, 0x1d, 0x38, 0x4, 0x10}, + {".strtab", SHT_STRTAB, 0x0, 0x0, 0x1468, 0x206, 0x0, 0x0, 0x1, 0x0}, }, }, - fileTest{ + { "testdata/gcc-amd64-linux-exec", FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0, binary.LittleEndian, ET_EXEC, EM_X86_64}, []SectionHeader{ - SectionHeader{"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - SectionHeader{".interp", SHT_PROGBITS, SHF_ALLOC, 0x400200, 0x200, 0x1c, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".note.ABI-tag", SHT_NOTE, SHF_ALLOC, 0x40021c, 0x21c, 0x20, 0x0, 0x0, 0x4, 0x0}, - SectionHeader{".hash", SHT_HASH, SHF_ALLOC, 0x400240, 0x240, 0x24, 0x5, 0x0, 0x8, 0x4}, - SectionHeader{".gnu.hash", SHT_LOOS + 268435446, SHF_ALLOC, 0x400268, 0x268, 0x1c, 0x5, 0x0, 0x8, 0x0}, - SectionHeader{".dynsym", SHT_DYNSYM, SHF_ALLOC, 0x400288, 0x288, 0x60, 0x6, 0x1, 0x8, 0x18}, - SectionHeader{".dynstr", SHT_STRTAB, SHF_ALLOC, 0x4002e8, 0x2e8, 0x3d, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".gnu.version", SHT_HIOS, SHF_ALLOC, 0x400326, 0x326, 0x8, 0x5, 0x0, 0x2, 0x2}, - SectionHeader{".gnu.version_r", SHT_LOOS + 268435454, SHF_ALLOC, 0x400330, 0x330, 0x20, 0x6, 0x1, 0x8, 0x0}, - SectionHeader{".rela.dyn", SHT_RELA, SHF_ALLOC, 0x400350, 0x350, 0x18, 0x5, 0x0, 0x8, 0x18}, - SectionHeader{".rela.plt", SHT_RELA, SHF_ALLOC, 0x400368, 0x368, 0x30, 0x5, 0xc, 0x8, 0x18}, - SectionHeader{".init", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x400398, 0x398, 0x18, 0x0, 0x0, 0x4, 0x0}, - SectionHeader{".plt", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x4003b0, 0x3b0, 0x30, 0x0, 0x0, 0x4, 0x10}, - SectionHeader{".text", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x4003e0, 0x3e0, 0x1b4, 0x0, 0x0, 0x10, 0x0}, - SectionHeader{".fini", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x400594, 0x594, 0xe, 0x0, 0x0, 0x4, 0x0}, - SectionHeader{".rodata", SHT_PROGBITS, SHF_ALLOC, 0x4005a4, 0x5a4, 0x11, 0x0, 0x0, 0x4, 0x0}, - SectionHeader{".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, 0x4005b8, 0x5b8, 0x24, 0x0, 0x0, 0x4, 0x0}, - SectionHeader{".eh_frame", SHT_PROGBITS, SHF_ALLOC, 0x4005e0, 0x5e0, 0xa4, 0x0, 0x0, 0x8, 0x0}, - SectionHeader{".ctors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600688, 0x688, 0x10, 0x0, 0x0, 0x8, 0x0}, - SectionHeader{".dtors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600698, 0x698, 0x10, 0x0, 0x0, 0x8, 0x0}, - SectionHeader{".jcr", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x6006a8, 0x6a8, 0x8, 0x0, 0x0, 0x8, 0x0}, - SectionHeader{".dynamic", SHT_DYNAMIC, SHF_WRITE + SHF_ALLOC, 0x6006b0, 0x6b0, 0x1a0, 0x6, 0x0, 0x8, 0x10}, - SectionHeader{".got", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600850, 0x850, 0x8, 0x0, 0x0, 0x8, 0x8}, - SectionHeader{".got.plt", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600858, 0x858, 0x28, 0x0, 0x0, 0x8, 0x8}, - SectionHeader{".data", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600880, 0x880, 0x18, 0x0, 0x0, 0x8, 0x0}, - SectionHeader{".bss", SHT_NOBITS, SHF_WRITE + SHF_ALLOC, 0x600898, 0x898, 0x8, 0x0, 0x0, 0x4, 0x0}, - SectionHeader{".comment", SHT_PROGBITS, 0x0, 0x0, 0x898, 0x126, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".debug_aranges", SHT_PROGBITS, 0x0, 0x0, 0x9c0, 0x90, 0x0, 0x0, 0x10, 0x0}, - SectionHeader{".debug_pubnames", SHT_PROGBITS, 0x0, 0x0, 0xa50, 0x25, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".debug_info", SHT_PROGBITS, 0x0, 0x0, 0xa75, 0x1a7, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".debug_abbrev", SHT_PROGBITS, 0x0, 0x0, 0xc1c, 0x6f, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".debug_line", SHT_PROGBITS, 0x0, 0x0, 0xc8b, 0x13f, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".debug_str", SHT_PROGBITS, SHF_MERGE + SHF_STRINGS, 0x0, 0xdca, 0xb1, 0x0, 0x0, 0x1, 0x1}, - SectionHeader{".debug_ranges", SHT_PROGBITS, 0x0, 0x0, 0xe80, 0x90, 0x0, 0x0, 0x10, 0x0}, - SectionHeader{".shstrtab", SHT_STRTAB, 0x0, 0x0, 0xf10, 0x149, 0x0, 0x0, 0x1, 0x0}, - SectionHeader{".symtab", SHT_SYMTAB, 0x0, 0x0, 0x19a0, 0x6f0, 0x24, 0x39, 0x8, 0x18}, - SectionHeader{".strtab", SHT_STRTAB, 0x0, 0x0, 0x2090, 0x1fc, 0x0, 0x0, 0x1, 0x0}, + {"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {".interp", SHT_PROGBITS, SHF_ALLOC, 0x400200, 0x200, 0x1c, 0x0, 0x0, 0x1, 0x0}, + {".note.ABI-tag", SHT_NOTE, SHF_ALLOC, 0x40021c, 0x21c, 0x20, 0x0, 0x0, 0x4, 0x0}, + {".hash", SHT_HASH, SHF_ALLOC, 0x400240, 0x240, 0x24, 0x5, 0x0, 0x8, 0x4}, + {".gnu.hash", SHT_LOOS + 268435446, SHF_ALLOC, 0x400268, 0x268, 0x1c, 0x5, 0x0, 0x8, 0x0}, + {".dynsym", SHT_DYNSYM, SHF_ALLOC, 0x400288, 0x288, 0x60, 0x6, 0x1, 0x8, 0x18}, + {".dynstr", SHT_STRTAB, SHF_ALLOC, 0x4002e8, 0x2e8, 0x3d, 0x0, 0x0, 0x1, 0x0}, + {".gnu.version", SHT_HIOS, SHF_ALLOC, 0x400326, 0x326, 0x8, 0x5, 0x0, 0x2, 0x2}, + {".gnu.version_r", SHT_LOOS + 268435454, SHF_ALLOC, 0x400330, 0x330, 0x20, 0x6, 0x1, 0x8, 0x0}, + {".rela.dyn", SHT_RELA, SHF_ALLOC, 0x400350, 0x350, 0x18, 0x5, 0x0, 0x8, 0x18}, + {".rela.plt", SHT_RELA, SHF_ALLOC, 0x400368, 0x368, 0x30, 0x5, 0xc, 0x8, 0x18}, + {".init", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x400398, 0x398, 0x18, 0x0, 0x0, 0x4, 0x0}, + {".plt", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x4003b0, 0x3b0, 0x30, 0x0, 0x0, 0x4, 0x10}, + {".text", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x4003e0, 0x3e0, 0x1b4, 0x0, 0x0, 0x10, 0x0}, + {".fini", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x400594, 0x594, 0xe, 0x0, 0x0, 0x4, 0x0}, + {".rodata", SHT_PROGBITS, SHF_ALLOC, 0x4005a4, 0x5a4, 0x11, 0x0, 0x0, 0x4, 0x0}, + {".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, 0x4005b8, 0x5b8, 0x24, 0x0, 0x0, 0x4, 0x0}, + {".eh_frame", SHT_PROGBITS, SHF_ALLOC, 0x4005e0, 0x5e0, 0xa4, 0x0, 0x0, 0x8, 0x0}, + {".ctors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600688, 0x688, 0x10, 0x0, 0x0, 0x8, 0x0}, + {".dtors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600698, 0x698, 0x10, 0x0, 0x0, 0x8, 0x0}, + {".jcr", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x6006a8, 0x6a8, 0x8, 0x0, 0x0, 0x8, 0x0}, + {".dynamic", SHT_DYNAMIC, SHF_WRITE + SHF_ALLOC, 0x6006b0, 0x6b0, 0x1a0, 0x6, 0x0, 0x8, 0x10}, + {".got", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600850, 0x850, 0x8, 0x0, 0x0, 0x8, 0x8}, + {".got.plt", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600858, 0x858, 0x28, 0x0, 0x0, 0x8, 0x8}, + {".data", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600880, 0x880, 0x18, 0x0, 0x0, 0x8, 0x0}, + {".bss", SHT_NOBITS, SHF_WRITE + SHF_ALLOC, 0x600898, 0x898, 0x8, 0x0, 0x0, 0x4, 0x0}, + {".comment", SHT_PROGBITS, 0x0, 0x0, 0x898, 0x126, 0x0, 0x0, 0x1, 0x0}, + {".debug_aranges", SHT_PROGBITS, 0x0, 0x0, 0x9c0, 0x90, 0x0, 0x0, 0x10, 0x0}, + {".debug_pubnames", SHT_PROGBITS, 0x0, 0x0, 0xa50, 0x25, 0x0, 0x0, 0x1, 0x0}, + {".debug_info", SHT_PROGBITS, 0x0, 0x0, 0xa75, 0x1a7, 0x0, 0x0, 0x1, 0x0}, + {".debug_abbrev", SHT_PROGBITS, 0x0, 0x0, 0xc1c, 0x6f, 0x0, 0x0, 0x1, 0x0}, + {".debug_line", SHT_PROGBITS, 0x0, 0x0, 0xc8b, 0x13f, 0x0, 0x0, 0x1, 0x0}, + {".debug_str", SHT_PROGBITS, SHF_MERGE + SHF_STRINGS, 0x0, 0xdca, 0xb1, 0x0, 0x0, 0x1, 0x1}, + {".debug_ranges", SHT_PROGBITS, 0x0, 0x0, 0xe80, 0x90, 0x0, 0x0, 0x10, 0x0}, + {".shstrtab", SHT_STRTAB, 0x0, 0x0, 0xf10, 0x149, 0x0, 0x0, 0x1, 0x0}, + {".symtab", SHT_SYMTAB, 0x0, 0x0, 0x19a0, 0x6f0, 0x24, 0x39, 0x8, 0x18}, + {".strtab", SHT_STRTAB, 0x0, 0x0, 0x2090, 0x1fc, 0x0, 0x0, 0x1, 0x0}, }, }, } @@ -135,17 +135,17 @@ type relocationTest struct { } var relocationTests = []relocationTest{ - relocationTest{ + { "testdata/go-relocation-test-gcc441-x86-64.o", - &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(1)}, dwarf.Field{Attr: dwarf.AttrName, Val: "go-relocation-test.c"}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp"}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0)}}}, + &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}, }, - relocationTest{ + { "testdata/go-relocation-test-gcc441-x86.o", - &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(1)}, dwarf.Field{Attr: dwarf.AttrName, Val: "t.c"}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp"}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: uint64(0x5)}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0)}}}, + &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "t.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x5)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}, }, - relocationTest{ + { "testdata/go-relocation-test-gcc424-x86-64.o", - &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(1)}, dwarf.Field{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp"}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0)}}}, + &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}, }, } diff --git a/src/pkg/debug/gosym/Makefile b/src/pkg/debug/gosym/Makefile index ac16b4cee..3c0d8c440 100644 --- a/src/pkg/debug/gosym/Makefile +++ b/src/pkg/debug/gosym/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=debug/gosym GOFILES=\ diff --git a/src/pkg/debug/gosym/pclinetest.s b/src/pkg/debug/gosym/pclinetest.s index 519656b63..6305435b0 100644 --- a/src/pkg/debug/gosym/pclinetest.s +++ b/src/pkg/debug/gosym/pclinetest.s @@ -51,39 +51,8 @@ TEXT main(SB),7,$0 CALL pcfromline(SB) // Keep the linker happy -TEXT runtime·morestack(SB),7,$0 +TEXT main·main(SB),7,$0 RET -TEXT runtime·morestack00(SB),7,$0 +TEXT main·init(SB),7,$0 RET - -TEXT runtime·morestack10(SB),7,$0 - RET - -TEXT runtime·morestack01(SB),7,$0 - RET - -TEXT runtime·morestack11(SB),7,$0 - RET - -TEXT runtime·morestack8(SB),7,$0 - RET - -TEXT runtime·morestack16(SB),7,$0 - RET - -TEXT runtime·morestack24(SB),7,$0 - RET - -TEXT runtime·morestack32(SB),7,$0 - RET - -TEXT runtime·morestack40(SB),7,$0 - RET - -TEXT runtime·morestack48(SB),7,$0 - RET - -TEXT runtime·morestack8(SB),7,$0 - RET - diff --git a/src/pkg/debug/gosym/pclntab_test.go b/src/pkg/debug/gosym/pclntab_test.go index 8752e3c9f..908702173 100644 --- a/src/pkg/debug/gosym/pclntab_test.go +++ b/src/pkg/debug/gosym/pclntab_test.go @@ -143,7 +143,7 @@ func TestLineAline(t *testing.T) { } } -// gotest: if [ "$(uname)-$(uname -m)" = Linux-x86_64 ]; then +// gotest: if [ "$(uname)-$(uname -m)" = Linux-x86_64 -a "$GOARCH" = amd64 ]; then // gotest: mkdir -p _test && $AS pclinetest.s && $LD -E main -o _test/pclinetest pclinetest.$O // gotest: fi func TestPCLine(t *testing.T) { diff --git a/src/pkg/debug/macho/Makefile b/src/pkg/debug/macho/Makefile index d34aacf54..5fbbf1efe 100644 --- a/src/pkg/debug/macho/Makefile +++ b/src/pkg/debug/macho/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=debug/macho GOFILES=\ diff --git a/src/pkg/debug/macho/file.go b/src/pkg/debug/macho/file.go index 246dad8e7..fd8da9449 100644 --- a/src/pkg/debug/macho/file.go +++ b/src/pkg/debug/macho/file.go @@ -24,6 +24,9 @@ type File struct { Loads []Load Sections []*Section + Symtab *Symtab + Dysymtab *Dysymtab + closer io.Closer } @@ -112,6 +115,28 @@ func (s *Section) Data() ([]byte, os.Error) { // Open returns a new ReadSeeker reading the Mach-O section. func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } +// A Dylib represents a Mach-O load dynamic library command. +type Dylib struct { + LoadBytes + Name string + Time uint32 + CurrentVersion uint32 + CompatVersion uint32 +} + +// A Symtab represents a Mach-O symbol table command. +type Symtab struct { + LoadBytes + SymtabCmd + Syms []Symbol +} + +// A Dysymtab represents a Mach-O dynamic symbol table command. +type Dysymtab struct { + LoadBytes + DysymtabCmd + IndirectSyms []uint32 // indices into Symtab.Syms +} /* * Mach-O reader @@ -217,6 +242,71 @@ func NewFile(r io.ReaderAt) (*File, os.Error) { default: f.Loads[i] = LoadBytes(cmddat) + case LoadCmdDylib: + var hdr DylibCmd + b := bytes.NewBuffer(cmddat) + if err := binary.Read(b, bo, &hdr); err != nil { + return nil, err + } + l := new(Dylib) + if hdr.Name >= uint32(len(cmddat)) { + return nil, &FormatError{offset, "invalid name in dynamic library command", hdr.Name} + } + l.Name = cstring(cmddat[hdr.Name:]) + l.Time = hdr.Time + l.CurrentVersion = hdr.CurrentVersion + l.CompatVersion = hdr.CompatVersion + l.LoadBytes = LoadBytes(cmddat) + f.Loads[i] = l + + case LoadCmdSymtab: + var hdr SymtabCmd + b := bytes.NewBuffer(cmddat) + if err := binary.Read(b, bo, &hdr); err != nil { + return nil, err + } + strtab := make([]byte, hdr.Strsize) + if _, err := r.ReadAt(strtab, int64(hdr.Stroff)); err != nil { + return nil, err + } + var symsz int + if f.Magic == Magic64 { + symsz = 16 + } else { + symsz = 12 + } + symdat := make([]byte, int(hdr.Nsyms)*symsz) + if _, err := r.ReadAt(symdat, int64(hdr.Symoff)); err != nil { + return nil, err + } + st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset) + if err != nil { + return nil, err + } + f.Loads[i] = st + f.Symtab = st + + case LoadCmdDysymtab: + var hdr DysymtabCmd + b := bytes.NewBuffer(cmddat) + if err := binary.Read(b, bo, &hdr); err != nil { + return nil, err + } + dat := make([]byte, hdr.Nindirectsyms*4) + if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil { + return nil, err + } + x := make([]uint32, hdr.Nindirectsyms) + if err := binary.Read(bytes.NewBuffer(dat), bo, x); err != nil { + return nil, err + } + st := new(Dysymtab) + st.LoadBytes = LoadBytes(cmddat) + st.DysymtabCmd = hdr + st.IndirectSyms = x + f.Loads[i] = st + f.Dysymtab = st + case LoadCmdSegment: var seg32 Segment32 b := bytes.NewBuffer(cmddat) @@ -301,18 +391,45 @@ func NewFile(r io.ReaderAt) (*File, os.Error) { return f, nil } -func (f *File) pushSection(sh *Section, r io.ReaderAt) { - n := len(f.Sections) - if n >= cap(f.Sections) { - m := (n + 1) * 2 - new := make([]*Section, n, m) - for i, sh := range f.Sections { - new[i] = sh +func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, os.Error) { + bo := f.ByteOrder + symtab := make([]Symbol, hdr.Nsyms) + b := bytes.NewBuffer(symdat) + for i := range symtab { + var n Nlist64 + if f.Magic == Magic64 { + if err := binary.Read(b, bo, &n); err != nil { + return nil, err + } + } else { + var n32 Nlist32 + if err := binary.Read(b, bo, &n32); err != nil { + return nil, err + } + n.Name = n32.Name + n.Type = n32.Type + n.Sect = n32.Sect + n.Desc = n32.Desc + n.Value = uint64(n32.Value) + } + sym := &symtab[i] + if n.Name >= uint32(len(strtab)) { + return nil, &FormatError{offset, "invalid name in symbol table", n.Name} } - f.Sections = new + sym.Name = cstring(strtab[n.Name:]) + sym.Type = n.Type + sym.Sect = n.Sect + sym.Desc = n.Desc + sym.Value = n.Value } - f.Sections = f.Sections[0 : n+1] - f.Sections[n] = sh + st := new(Symtab) + st.LoadBytes = LoadBytes(cmddat) + st.Syms = symtab + return st, nil +} + +func (f *File) pushSection(sh *Section, r io.ReaderAt) { + f.Sections = append(f.Sections, sh) sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size)) sh.ReaderAt = sh.sr } @@ -368,3 +485,33 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) { abbrev, info, str := dat[0], dat[1], dat[2] return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str) } + +// ImportedSymbols returns the names of all symbols +// referred to by the binary f that are expected to be +// satisfied by other libraries at dynamic load time. +func (f *File) ImportedSymbols() ([]string, os.Error) { + if f.Dysymtab == nil || f.Symtab == nil { + return nil, &FormatError{0, "missing symbol table", nil} + } + + st := f.Symtab + dt := f.Dysymtab + var all []string + for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] { + all = append(all, s.Name) + } + return all, nil +} + +// ImportedLibraries returns the paths of all libraries +// referred to by the binary f that are expected to be +// linked with the binary at dynamic link time. +func (f *File) ImportedLibraries() ([]string, os.Error) { + var all []string + for _, l := range f.Loads { + if lib, ok := l.(*Dylib); ok { + all = append(all, lib.Name) + } + } + return all, nil +} diff --git a/src/pkg/debug/macho/file_test.go b/src/pkg/debug/macho/file_test.go index d4f3fc19c..56d8a20be 100644 --- a/src/pkg/debug/macho/file_test.go +++ b/src/pkg/debug/macho/file_test.go @@ -17,7 +17,7 @@ type fileTest struct { } var fileTests = []fileTest{ - fileTest{ + { "testdata/gcc-386-darwin-exec", FileHeader{0xfeedface, Cpu386, 0x3, 0x2, 0xc, 0x3c0, 0x85}, []*SegmentHeader{ @@ -42,7 +42,7 @@ var fileTests = []fileTest{ &SectionHeader{"__jump_table", "__IMPORT", 0x3000, 0xa, 0x2000, 0x6, 0x0, 0x0, 0x4000008}, }, }, - fileTest{ + { "testdata/gcc-amd64-darwin-exec", FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0x2, 0xb, 0x568, 0x85}, []*SegmentHeader{ @@ -69,7 +69,7 @@ var fileTests = []fileTest{ &SectionHeader{"__la_symbol_ptr", "__DATA", 0x100001058, 0x10, 0x1058, 0x2, 0x0, 0x0, 0x7}, }, }, - fileTest{ + { "testdata/gcc-amd64-darwin-exec-debug", FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0xa, 0x4, 0x5a0, 0}, []*SegmentHeader{ diff --git a/src/pkg/debug/macho/macho.go b/src/pkg/debug/macho/macho.go index 41962d562..1386f5acf 100644 --- a/src/pkg/debug/macho/macho.go +++ b/src/pkg/debug/macho/macho.go @@ -47,8 +47,8 @@ const ( ) var cpuStrings = []intName{ - intName{uint32(Cpu386), "Cpu386"}, - intName{uint32(CpuAmd64), "CpuAmd64"}, + {uint32(Cpu386), "Cpu386"}, + {uint32(CpuAmd64), "CpuAmd64"}, } func (i Cpu) String() string { return stringName(uint32(i), cpuStrings, false) } @@ -59,16 +59,21 @@ type LoadCmd uint32 const ( LoadCmdSegment LoadCmd = 1 - LoadCmdSegment64 LoadCmd = 25 + LoadCmdSymtab LoadCmd = 2 LoadCmdThread LoadCmd = 4 LoadCmdUnixThread LoadCmd = 5 // thread+stack + LoadCmdDysymtab LoadCmd = 11 + LoadCmdDylib LoadCmd = 12 + LoadCmdDylinker LoadCmd = 15 + LoadCmdSegment64 LoadCmd = 25 ) var cmdStrings = []intName{ - intName{uint32(LoadCmdSegment), "LoadCmdSegment"}, - intName{uint32(LoadCmdSegment64), "LoadCmdSegment64"}, - intName{uint32(LoadCmdThread), "LoadCmdThread"}, - intName{uint32(LoadCmdUnixThread), "LoadCmdUnixThread"}, + {uint32(LoadCmdSegment), "LoadCmdSegment"}, + {uint32(LoadCmdThread), "LoadCmdThread"}, + {uint32(LoadCmdUnixThread), "LoadCmdUnixThread"}, + {uint32(LoadCmdDylib), "LoadCmdDylib"}, + {uint32(LoadCmdSegment64), "LoadCmdSegment64"}, } func (i LoadCmd) String() string { return stringName(uint32(i), cmdStrings, false) } @@ -104,6 +109,16 @@ type Segment32 struct { Flag uint32 } +// A DylibCmd is a Mach-O load dynamic library command. +type DylibCmd struct { + Cmd LoadCmd + Len uint32 + Name uint32 + Time uint32 + CurrentVersion uint32 + CompatVersion uint32 +} + // A Section32 is a 32-bit Mach-O section header. type Section32 struct { Name [16]byte @@ -135,6 +150,67 @@ type Section64 struct { Reserve3 uint32 } +// A SymtabCmd is a Mach-O symbol table command. +type SymtabCmd struct { + Cmd LoadCmd + Len uint32 + Symoff uint32 + Nsyms uint32 + Stroff uint32 + Strsize uint32 +} + +// A DysymtabCmd is a Mach-O dynamic symbol table command. +type DysymtabCmd struct { + Cmd LoadCmd + Len uint32 + Ilocalsym uint32 + Nlocalsym uint32 + Iextdefsym uint32 + Nextdefsym uint32 + Iundefsym uint32 + Nundefsym uint32 + Tocoffset uint32 + Ntoc uint32 + Modtaboff uint32 + Nmodtab uint32 + Extrefsymoff uint32 + Nextrefsyms uint32 + Indirectsymoff uint32 + Nindirectsyms uint32 + Extreloff uint32 + Nextrel uint32 + Locreloff uint32 + Nlocrel uint32 +} + +// An Nlist32 is a Mach-O 32-bit symbol table entry. +type Nlist32 struct { + Name uint32 + Type uint8 + Sect uint8 + Desc uint16 + Value uint32 +} + +// An Nlist64 is a Mach-O 64-bit symbol table entry. +type Nlist64 struct { + Name uint32 + Type uint8 + Sect uint8 + Desc uint16 + Value uint64 +} + +// A Symbol is a Mach-O 32-bit or 64-bit symbol table entry. +type Symbol struct { + Name string + Type uint8 + Sect uint8 + Desc uint16 + Value uint64 +} + // A Thread is a Mach-O thread state command. type Thread struct { Cmd LoadCmd diff --git a/src/pkg/debug/pe/Makefile b/src/pkg/debug/pe/Makefile new file mode 100644 index 000000000..998e6a418 --- /dev/null +++ b/src/pkg/debug/pe/Makefile @@ -0,0 +1,12 @@ +# 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=debug/pe +GOFILES=\ + pe.go\ + file.go\ + +include ../../../Make.pkg diff --git a/src/pkg/debug/pe/file.go b/src/pkg/debug/pe/file.go new file mode 100644 index 000000000..904d2f863 --- /dev/null +++ b/src/pkg/debug/pe/file.go @@ -0,0 +1,231 @@ +// 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 pe implements access to PE (Microsoft Windows Portable Executable) files. +package pe + +import ( + "debug/dwarf" + "encoding/binary" + "fmt" + "io" + "os" + "strconv" +) + +// A File represents an open PE file. +type File struct { + FileHeader + Sections []*Section + + closer io.Closer +} + +type SectionHeader struct { + Name string + VirtualSize uint32 + VirtualAddress uint32 + Size uint32 + Offset uint32 + PointerToRelocations uint32 + PointerToLineNumbers uint32 + NumberOfRelocations uint16 + NumberOfLineNumbers uint16 + Characteristics uint32 +} + + +type Section struct { + SectionHeader + + // Embed ReaderAt for ReadAt method. + // Do not embed SectionReader directly + // to avoid having Read and Seek. + // If a client wants Read and Seek it must use + // Open() to avoid fighting over the seek offset + // with other clients. + io.ReaderAt + sr *io.SectionReader +} + +// Data reads and returns the contents of the PE section. +func (s *Section) Data() ([]byte, os.Error) { + dat := make([]byte, s.sr.Size()) + n, err := s.sr.ReadAt(dat, 0) + return dat[0:n], err +} + +// Open returns a new ReadSeeker reading the PE section. +func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } + + +type FormatError struct { + off int64 + msg string + val interface{} +} + +func (e *FormatError) String() string { + msg := e.msg + if e.val != nil { + msg += fmt.Sprintf(" '%v'", e.val) + } + msg += fmt.Sprintf(" in record at byte %#x", e.off) + return msg +} + +// Open opens the named file using os.Open and prepares it for use as a PE binary. +func Open(name string) (*File, os.Error) { + f, err := os.Open(name, os.O_RDONLY, 0) + if err != nil { + return nil, err + } + ff, err := NewFile(f) + if err != nil { + f.Close() + return nil, err + } + ff.closer = f + return ff, nil +} + +// Close closes the File. +// If the File was created using NewFile directly instead of Open, +// Close has no effect. +func (f *File) Close() os.Error { + var err os.Error + if f.closer != nil { + err = f.closer.Close() + f.closer = nil + } + return err +} + +// NewFile creates a new File for acecssing a PE binary in an underlying reader. +func NewFile(r io.ReaderAt) (*File, os.Error) { + f := new(File) + sr := io.NewSectionReader(r, 0, 1<<63-1) + + var dosheader [96]byte + if _, err := r.ReadAt(dosheader[0:], 0); err != nil { + return nil, err + } + var base int64 + if dosheader[0] == 'M' && dosheader[1] == 'Z' { + var sign [4]byte + r.ReadAt(sign[0:], int64(dosheader[0x3c])) + if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) { + return nil, os.NewError("Invalid PE File Format.") + } + base = int64(dosheader[0x3c]) + 4 + } else { + base = int64(0) + } + sr.Seek(base, 0) + if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil { + return nil, err + } + if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 { + return nil, os.NewError("Invalid PE File Format.") + } + // get symbol string table + sr.Seek(int64(f.FileHeader.PointerToSymbolTable+18*f.FileHeader.NumberOfSymbols), 0) + var l uint32 + if err := binary.Read(sr, binary.LittleEndian, &l); err != nil { + return nil, err + } + ss := make([]byte, l) + if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+18*f.FileHeader.NumberOfSymbols)); err != nil { + return nil, err + } + sr.Seek(base, 0) + binary.Read(sr, binary.LittleEndian, &f.FileHeader) + sr.Seek(int64(f.FileHeader.SizeOfOptionalHeader), 1) //Skip OptionalHeader + f.Sections = make([]*Section, f.FileHeader.NumberOfSections) + for i := 0; i < int(f.FileHeader.NumberOfSections); i++ { + sh := new(SectionHeader32) + if err := binary.Read(sr, binary.LittleEndian, sh); err != nil { + return nil, err + } + var name string + if sh.Name[0] == '\x2F' { + si, _ := strconv.Atoi(cstring(sh.Name[1:])) + name, _ = getString(ss, si) + } else { + name = cstring(sh.Name[0:]) + } + s := new(Section) + s.SectionHeader = SectionHeader{ + Name: name, + VirtualSize: uint32(sh.VirtualSize), + VirtualAddress: uint32(sh.VirtualAddress), + Size: uint32(sh.SizeOfRawData), + Offset: uint32(sh.PointerToRawData), + PointerToRelocations: uint32(sh.PointerToRelocations), + PointerToLineNumbers: uint32(sh.PointerToLineNumbers), + NumberOfRelocations: uint16(sh.NumberOfRelocations), + NumberOfLineNumbers: uint16(sh.NumberOfLineNumbers), + Characteristics: uint32(sh.Characteristics), + } + s.sr = io.NewSectionReader(r, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size)) + s.ReaderAt = s.sr + f.Sections[i] = s + } + return f, nil +} + +func cstring(b []byte) string { + var i int + for i = 0; i < len(b) && b[i] != 0; i++ { + } + return string(b[0:i]) +} + +// getString extracts a string from symbol string table. +func getString(section []byte, start int) (string, bool) { + if start < 0 || start >= len(section) { + return "", false + } + + for end := start; end < len(section); end++ { + if section[end] == 0 { + return string(section[start:end]), true + } + } + return "", false +} + +// Section returns the first section with the given name, or nil if no such +// section exists. +func (f *File) Section(name string) *Section { + for _, s := range f.Sections { + if s.Name == name { + return s + } + } + return nil +} + +func (f *File) DWARF() (*dwarf.Data, os.Error) { + // There are many other DWARF sections, but these + // are the required ones, and the debug/dwarf package + // does not use the others, so don't bother loading them. + var names = [...]string{"abbrev", "info", "str"} + var dat [len(names)][]byte + for i, name := range names { + name = ".debug_" + name + s := f.Section(name) + if s == nil { + continue + } + b, err := s.Data() + if err != nil && uint32(len(b)) < s.Size { + return nil, err + } + dat[i] = b + } + + abbrev, info, str := dat[0], dat[1], dat[2] + return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str) +} diff --git a/src/pkg/debug/pe/file_test.go b/src/pkg/debug/pe/file_test.go new file mode 100644 index 000000000..2c5c25b8c --- /dev/null +++ b/src/pkg/debug/pe/file_test.go @@ -0,0 +1,99 @@ +// 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 pe + +import ( + "reflect" + "testing" +) + +type fileTest struct { + file string + hdr FileHeader + sections []*SectionHeader +} + +var fileTests = []fileTest{ + { + "testdata/gcc-386-mingw-obj", + FileHeader{0x014c, 0x000c, 0x0, 0x64a, 0x1e, 0x0, 0x104}, + []*SectionHeader{ + &SectionHeader{".text", 0, 0, 36, 500, 1440, 0, 3, 0, 0x60300020}, + &SectionHeader{".data", 0, 0, 0, 0, 0, 0, 0, 0, 3224371264}, + &SectionHeader{".bss", 0, 0, 0, 0, 0, 0, 0, 0, 3224371328}, + &SectionHeader{".debug_abbrev", 0, 0, 137, 536, 0, 0, 0, 0, 0x42100000}, + &SectionHeader{".debug_info", 0, 0, 418, 673, 1470, 0, 7, 0, 1108344832}, + &SectionHeader{".debug_line", 0, 0, 128, 1091, 1540, 0, 1, 0, 1108344832}, + &SectionHeader{".rdata", 0, 0, 16, 1219, 0, 0, 0, 0, 1076887616}, + &SectionHeader{".debug_frame", 0, 0, 52, 1235, 1550, 0, 2, 0, 1110441984}, + &SectionHeader{".debug_loc", 0, 0, 56, 1287, 0, 0, 0, 0, 1108344832}, + &SectionHeader{".debug_pubnames", 0, 0, 27, 1343, 1570, 0, 1, 0, 1108344832}, + &SectionHeader{".debug_pubtypes", 0, 0, 38, 1370, 1580, 0, 1, 0, 1108344832}, + &SectionHeader{".debug_aranges", 0, 0, 32, 1408, 1590, 0, 2, 0, 1108344832}, + }, + }, + { + "testdata/gcc-386-mingw-exec", + FileHeader{0x014c, 0x000f, 0x4c6a1b60, 0x3c00, 0x282, 0xe0, 0x107}, + []*SectionHeader{ + &SectionHeader{Name: ".text", VirtualSize: 0xcd8, VirtualAddress: 0x1000, Size: 0xe00, Offset: 0x400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x60500060}, + &SectionHeader{Name: ".data", VirtualSize: 0x10, VirtualAddress: 0x2000, Size: 0x200, Offset: 0x1200, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, + &SectionHeader{Name: ".rdata", VirtualSize: 0x120, VirtualAddress: 0x3000, Size: 0x200, Offset: 0x1400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x40300040}, + &SectionHeader{Name: ".bss", VirtualSize: 0xdc, VirtualAddress: 0x4000, Size: 0x0, Offset: 0x0, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0400080}, + &SectionHeader{Name: ".idata", VirtualSize: 0x3c8, VirtualAddress: 0x5000, Size: 0x400, Offset: 0x1600, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, + &SectionHeader{Name: ".CRT", VirtualSize: 0x18, VirtualAddress: 0x6000, Size: 0x200, Offset: 0x1a00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, + &SectionHeader{Name: ".tls", VirtualSize: 0x20, VirtualAddress: 0x7000, Size: 0x200, Offset: 0x1c00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, + &SectionHeader{Name: ".debug_aranges", VirtualSize: 0x20, VirtualAddress: 0x8000, Size: 0x200, Offset: 0x1e00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + &SectionHeader{Name: ".debug_pubnames", VirtualSize: 0x51, VirtualAddress: 0x9000, Size: 0x200, Offset: 0x2000, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + &SectionHeader{Name: ".debug_pubtypes", VirtualSize: 0x91, VirtualAddress: 0xa000, Size: 0x200, Offset: 0x2200, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + &SectionHeader{Name: ".debug_info", VirtualSize: 0xe22, VirtualAddress: 0xb000, Size: 0x1000, Offset: 0x2400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + &SectionHeader{Name: ".debug_abbrev", VirtualSize: 0x157, VirtualAddress: 0xc000, Size: 0x200, Offset: 0x3400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + &SectionHeader{Name: ".debug_line", VirtualSize: 0x144, VirtualAddress: 0xd000, Size: 0x200, Offset: 0x3600, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + &SectionHeader{Name: ".debug_frame", VirtualSize: 0x34, VirtualAddress: 0xe000, Size: 0x200, Offset: 0x3800, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42300000}, + &SectionHeader{Name: ".debug_loc", VirtualSize: 0x38, VirtualAddress: 0xf000, Size: 0x200, Offset: 0x3a00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + }, + }, +} + +func TestOpen(t *testing.T) { + for i := range fileTests { + tt := &fileTests[i] + + f, err := Open(tt.file) + if err != nil { + t.Error(err) + continue + } + if !reflect.DeepEqual(f.FileHeader, tt.hdr) { + t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr) + continue + } + + for i, sh := range f.Sections { + if i >= len(tt.sections) { + break + } + have := &sh.SectionHeader + want := tt.sections[i] + if !reflect.DeepEqual(have, want) { + t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want) + } + } + tn := len(tt.sections) + fn := len(f.Sections) + if tn != fn { + t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn) + } + + } +} + +func TestOpenFailure(t *testing.T) { + filename := "file.go" // not a PE file + _, err := Open(filename) // don't crash + if err == nil { + t.Errorf("open %s: succeeded unexpectedly", filename) + } +} diff --git a/src/pkg/debug/pe/pe.go b/src/pkg/debug/pe/pe.go new file mode 100644 index 000000000..b3dab739a --- /dev/null +++ b/src/pkg/debug/pe/pe.go @@ -0,0 +1,51 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pe + +type FileHeader struct { + Machine uint16 + NumberOfSections uint16 + TimeDateStamp uint32 + PointerToSymbolTable uint32 + NumberOfSymbols uint32 + SizeOfOptionalHeader uint16 + Characteristics uint16 +} + +type SectionHeader32 struct { + Name [8]uint8 + VirtualSize uint32 + VirtualAddress uint32 + SizeOfRawData uint32 + PointerToRawData uint32 + PointerToRelocations uint32 + PointerToLineNumbers uint32 + NumberOfRelocations uint16 + NumberOfLineNumbers uint16 + Characteristics uint32 +} + +const ( + IMAGE_FILE_MACHINE_UNKNOWN = 0x0 + IMAGE_FILE_MACHINE_AM33 = 0x1d3 + IMAGE_FILE_MACHINE_AMD64 = 0x8664 + IMAGE_FILE_MACHINE_ARM = 0x1c0 + IMAGE_FILE_MACHINE_EBC = 0xebc + IMAGE_FILE_MACHINE_I386 = 0x14c + IMAGE_FILE_MACHINE_IA64 = 0x200 + IMAGE_FILE_MACHINE_M32R = 0x9041 + IMAGE_FILE_MACHINE_MIPS16 = 0x266 + IMAGE_FILE_MACHINE_MIPSFPU = 0x366 + IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466 + IMAGE_FILE_MACHINE_POWERPC = 0x1f0 + IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1 + IMAGE_FILE_MACHINE_R4000 = 0x166 + IMAGE_FILE_MACHINE_SH3 = 0x1a2 + IMAGE_FILE_MACHINE_SH3DSP = 0x1a3 + IMAGE_FILE_MACHINE_SH4 = 0x1a6 + IMAGE_FILE_MACHINE_SH5 = 0x1a8 + IMAGE_FILE_MACHINE_THUMB = 0x1c2 + IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169 +) diff --git a/src/pkg/debug/pe/testdata/gcc-386-mingw-exec b/src/pkg/debug/pe/testdata/gcc-386-mingw-exec new file mode 100644 index 000000000..4b808d043 Binary files /dev/null and b/src/pkg/debug/pe/testdata/gcc-386-mingw-exec differ diff --git a/src/pkg/debug/pe/testdata/gcc-386-mingw-obj b/src/pkg/debug/pe/testdata/gcc-386-mingw-obj new file mode 100644 index 000000000..0c84d898d Binary files /dev/null and b/src/pkg/debug/pe/testdata/gcc-386-mingw-obj differ diff --git a/src/pkg/debug/pe/testdata/hello.c b/src/pkg/debug/pe/testdata/hello.c new file mode 100644 index 000000000..a689d3644 --- /dev/null +++ b/src/pkg/debug/pe/testdata/hello.c @@ -0,0 +1,8 @@ +#include + +int +main(void) +{ + printf("hello, world\n"); + return 0; +} diff --git a/src/pkg/debug/proc/Makefile b/src/pkg/debug/proc/Makefile index 5444ec0db..c6d879836 100644 --- a/src/pkg/debug/proc/Makefile +++ b/src/pkg/debug/proc/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=debug/proc GOFILES=\ diff --git a/src/pkg/debug/proc/proc_linux.go b/src/pkg/debug/proc/proc_linux.go index 5253ea846..f0cc43a10 100644 --- a/src/pkg/debug/proc/proc_linux.go +++ b/src/pkg/debug/proc/proc_linux.go @@ -153,7 +153,7 @@ type process struct { debugEvents chan *debugEvent debugReqs chan *debugReq stopReq chan os.Error - transitionHandlers *vector.Vector + transitionHandlers vector.Vector err os.Error } @@ -297,7 +297,7 @@ func (t *thread) logTrace(format string, args ...interface{}) { } } fmt.Fprint(os.Stderr, ": ") - fmt.Fprintf(os.Stderr, format, args) + fmt.Fprintf(os.Stderr, format, args...) fmt.Fprint(os.Stderr, "\n") } @@ -305,7 +305,7 @@ func (t *thread) warn(format string, args ...interface{}) { logLock.Lock() defer logLock.Unlock() fmt.Fprintf(os.Stderr, "Thread %d: WARNING ", t.tid) - fmt.Fprintf(os.Stderr, format, args) + fmt.Fprintf(os.Stderr, format, args...) fmt.Fprint(os.Stderr, "\n") } @@ -316,7 +316,7 @@ func (p *process) logTrace(format string, args ...interface{}) { logLock.Lock() defer logLock.Unlock() fmt.Fprintf(os.Stderr, "Process %d: ", p.pid) - fmt.Fprintf(os.Stderr, format, args) + fmt.Fprintf(os.Stderr, format, args...) fmt.Fprint(os.Stderr, "\n") } @@ -472,8 +472,8 @@ func (t *thread) setState(newState threadState) { return } - t.proc.transitionHandlers = new(vector.Vector) - for _, h := range handlers.Data() { + t.proc.transitionHandlers = nil + for _, h := range handlers { h := h.(*transitionHandler) h.handle(t, oldState, newState) } @@ -738,7 +738,7 @@ func (p *process) monitor() { // Abort waiting handlers // TODO(austin) How do I stop the wait threads? - for _, h := range p.transitionHandlers.Data() { + for _, h := range p.transitionHandlers { h := h.(*transitionHandler) h.onErr(err) } @@ -1249,14 +1249,13 @@ func (p *process) attachAllThreads() os.Error { // newProcess creates a new process object and starts its monitor thread. func newProcess(pid int) *process { p := &process{ - pid: pid, - threads: make(map[int]*thread), - breakpoints: make(map[uintptr]*breakpoint), - ready: make(chan bool, 1), - debugEvents: make(chan *debugEvent), - debugReqs: make(chan *debugReq), - stopReq: make(chan os.Error), - transitionHandlers: new(vector.Vector), + pid: pid, + threads: make(map[int]*thread), + breakpoints: make(map[uintptr]*breakpoint), + ready: make(chan bool, 1), + debugEvents: make(chan *debugEvent), + debugReqs: make(chan *debugReq), + stopReq: make(chan os.Error), } go p.monitor() diff --git a/src/pkg/debug/proc/proc_nacl.go b/src/pkg/debug/proc/proc_nacl.go deleted file mode 100644 index be26bbf18..000000000 --- a/src/pkg/debug/proc/proc_nacl.go +++ /dev/null @@ -1,20 +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 proc - -import ( - "os" - "syscall" -) - -// Process tracing is not supported on Native Client. - -func Attach(pid int) (Process, os.Error) { - return nil, os.NewSyscallError("ptrace", syscall.ENACL) -} - -func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*os.File) (Process, os.Error) { - return nil, os.NewSyscallError("fork/exec", syscall.ENACL) -} diff --git a/src/pkg/debug/proc/regs_linux_386.go b/src/pkg/debug/proc/regs_linux_386.go index 7cebfa64a..b4a9769db 100644 --- a/src/pkg/debug/proc/regs_linux_386.go +++ b/src/pkg/debug/proc/regs_linux_386.go @@ -80,17 +80,17 @@ func (r *_386Regs) Get(i int) Word { case 9: return Word(uint32(r.Eflags)) case 10: - return Word(r.Cs) + return Word(r.Xcs) case 11: - return Word(r.Ss) + return Word(r.Xss) case 12: - return Word(r.Ds) + return Word(r.Xds) case 13: - return Word(r.Es) + return Word(r.Xes) case 14: - return Word(r.Fs) + return Word(r.Xfs) case 15: - return Word(r.Gs) + return Word(r.Xgs) } panic("invalid register index " + strconv.Itoa(i)) } @@ -118,17 +118,17 @@ func (r *_386Regs) Set(i int, val Word) os.Error { case 9: r.Eflags = int32(val) case 10: - r.Cs = uint16(val) + r.Xcs = int32(val) case 11: - r.Ss = uint16(val) + r.Xss = int32(val) case 12: - r.Ds = uint16(val) + r.Xds = int32(val) case 13: - r.Es = uint16(val) + r.Xes = int32(val) case 14: - r.Fs = uint16(val) + r.Xfs = int32(val) case 15: - r.Gs = uint16(val) + r.Xgs = int32(val) default: panic("invalid register index " + strconv.Itoa(i)) } diff --git a/src/pkg/debug/proc/regs_linux_amd64.go b/src/pkg/debug/proc/regs_linux_amd64.go index a9f3569d3..381be29b1 100644 --- a/src/pkg/debug/proc/regs_linux_amd64.go +++ b/src/pkg/debug/proc/regs_linux_amd64.go @@ -71,7 +71,7 @@ func (r *amd64Regs) SetSP(val Word) os.Error { return r.setter(&r.PtraceRegs) } -func (r *amd64Regs) Names() []string { return &names } +func (r *amd64Regs) Names() []string { return names[0:] } func (r *amd64Regs) Get(i int) Word { switch i { diff --git a/src/pkg/debug/proc/regs_nacl_386.go b/src/pkg/debug/proc/regs_nacl_386.go deleted file mode 100644 index 60c9ac719..000000000 --- a/src/pkg/debug/proc/regs_nacl_386.go +++ /dev/null @@ -1,5 +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 proc diff --git a/src/pkg/deps.bash b/src/pkg/deps.bash index c8406d39c..a8e3dfc3a 100755 --- a/src/pkg/deps.bash +++ b/src/pkg/deps.bash @@ -3,6 +3,8 @@ # 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) + OUT="Make.deps" TMP="Make.deps.tmp" @@ -12,8 +14,8 @@ if [ -f $OUT ] && ! [ -w $OUT ]; then fi # Get list of directories from Makefile -dirs=$(sed '1,/^DIRS=/d; /^$/,$d; s/\\//g' Makefile) -dirpat=$(echo $dirs | sed 's/ /|/g; s/.*/^(&)$/') +dirs=$(gomake --no-print-directory echo-dirs) +dirpat=$(echo $dirs C | sed 's/ /|/g; s/.*/^(&)$/') for dir in $dirs; do ( cd $dir || exit 1 @@ -31,6 +33,7 @@ for dir in $dirs; do ( egrep "$dirpat" | grep -v "^$dir\$" | sed 's/$/.install/' | + sed 's;^C\.install;runtime/cgo.install;' | sort -u ) diff --git a/src/pkg/ebnf/Makefile b/src/pkg/ebnf/Makefile index 4a75c7432..f5555d272 100644 --- a/src/pkg/ebnf/Makefile +++ b/src/pkg/ebnf/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=ebnf GOFILES=\ diff --git a/src/pkg/ebnf/ebnf.go b/src/pkg/ebnf/ebnf.go index 898a48173..e5aabd582 100644 --- a/src/pkg/ebnf/ebnf.go +++ b/src/pkg/ebnf/ebnf.go @@ -23,7 +23,6 @@ package ebnf import ( - "container/vector" "go/scanner" "go/token" "os" @@ -39,7 +38,7 @@ type ( // An Expression node represents a production expression. Expression interface { // Pos is the position of the first character of the syntactic construct - Pos() token.Position + Pos() token.Pos } // An Alternative node represents a non-empty list of alternative expressions. @@ -50,14 +49,14 @@ type ( // A Name node represents a production name. Name struct { - token.Position - String string + StringPos token.Pos + String string } // A Token node represents a literal. Token struct { - token.Position - String string + StringPos token.Pos + String string } // A List node represents a range of characters. @@ -67,20 +66,20 @@ type ( // A Group node represents a grouped expression. Group struct { - token.Position - Body Expression // (body) + Lparen token.Pos + Body Expression // (body) } // An Option node represents an optional expression. Option struct { - token.Position - Body Expression // [body] + Lbrack token.Pos + Body Expression // [body] } // A Repetition node represents a repeated expression. Repetition struct { - token.Position - Body Expression // {body} + Lbrace token.Pos + Body Expression // {body} } // A Production node represents an EBNF production. @@ -96,20 +95,15 @@ type ( ) -func (x Alternative) Pos() token.Position { - return x[0].Pos() // the parser always generates non-empty Alternative -} - - -func (x Sequence) Pos() token.Position { - return x[0].Pos() // the parser always generates non-empty Sequences -} - - -func (x Range) Pos() token.Position { return x.Begin.Pos() } - - -func (p *Production) Pos() token.Position { return p.Name.Pos() } +func (x Alternative) Pos() token.Pos { return x[0].Pos() } // the parser always generates non-empty Alternative +func (x Sequence) Pos() token.Pos { return x[0].Pos() } // the parser always generates non-empty Sequences +func (x *Name) Pos() token.Pos { return x.StringPos } +func (x *Token) Pos() token.Pos { return x.StringPos } +func (x *Range) Pos() token.Pos { return x.Begin.Pos() } +func (x *Group) Pos() token.Pos { return x.Lparen } +func (x *Option) Pos() token.Pos { return x.Lbrack } +func (x *Repetition) Pos() token.Pos { return x.Lbrace } +func (x *Production) Pos() token.Pos { return x.Name.Pos() } // ---------------------------------------------------------------------------- @@ -122,17 +116,23 @@ func isLexical(name string) bool { type verifier struct { + fset *token.FileSet scanner.ErrorVector - worklist vector.Vector + worklist []*Production reached Grammar // set of productions reached from (and including) the root production grammar Grammar } +func (v *verifier) error(pos token.Pos, msg string) { + v.Error(v.fset.Position(pos), msg) +} + + func (v *verifier) push(prod *Production) { name := prod.Name.String if _, found := v.reached[name]; !found { - v.worklist.Push(prod) + v.worklist = append(v.worklist, prod) v.reached[name] = prod } } @@ -141,7 +141,7 @@ func (v *verifier) push(prod *Production) { func (v *verifier) verifyChar(x *Token) int { s := x.String if utf8.RuneCountInString(s) != 1 { - v.Error(x.Pos(), "single char expected, found "+s) + v.error(x.Pos(), "single char expected, found "+s) return 0 } ch, _ := utf8.DecodeRuneInString(s) @@ -167,12 +167,12 @@ func (v *verifier) verifyExpr(expr Expression, lexical bool) { if prod, found := v.grammar[x.String]; found { v.push(prod) } else { - v.Error(x.Pos(), "missing production "+x.String) + v.error(x.Pos(), "missing production "+x.String) } // within a lexical production references // to non-lexical productions are invalid if lexical && !isLexical(x.String) { - v.Error(x.Pos(), "reference to non-lexical production "+x.String) + v.error(x.Pos(), "reference to non-lexical production "+x.String) } case *Token: // nothing to do for now @@ -180,7 +180,7 @@ func (v *verifier) verifyExpr(expr Expression, lexical bool) { i := v.verifyChar(x.Begin) j := v.verifyChar(x.End) if i >= j { - v.Error(x.Pos(), "decreasing character range") + v.error(x.Pos(), "decreasing character range") } case *Group: v.verifyExpr(x.Body, lexical) @@ -194,25 +194,32 @@ func (v *verifier) verifyExpr(expr Expression, lexical bool) { } -func (v *verifier) verify(grammar Grammar, start string) { +func (v *verifier) verify(fset *token.FileSet, grammar Grammar, start string) { // find root production root, found := grammar[start] if !found { - var noPos token.Position - v.Error(noPos, "no start production "+start) + // token.NoPos doesn't require a file set; + // ok to set v.fset only afterwards + v.error(token.NoPos, "no start production "+start) return } // initialize verifier + v.fset = fset v.ErrorVector.Reset() - v.worklist.Resize(0, 0) + v.worklist = v.worklist[0:0] v.reached = make(Grammar) v.grammar = grammar // work through the worklist v.push(root) - for v.worklist.Len() > 0 { - prod := v.worklist.Pop().(*Production) + for { + n := len(v.worklist) - 1 + if n < 0 { + break + } + prod := v.worklist[n] + v.worklist = v.worklist[0:n] v.verifyExpr(prod.Expr, isLexical(prod.Name.String)) } @@ -220,7 +227,7 @@ func (v *verifier) verify(grammar Grammar, start string) { if len(v.reached) < len(v.grammar) { for name, prod := range v.grammar { if _, found := v.reached[name]; !found { - v.Error(prod.Pos(), name+" is unreachable") + v.error(prod.Pos(), name+" is unreachable") } } } @@ -232,8 +239,10 @@ func (v *verifier) verify(grammar Grammar, start string) { // - all productions defined are used when beginning at start // - lexical productions refer only to other lexical productions // -func Verify(grammar Grammar, start string) os.Error { +// Position information is interpreted relative to the file set fset. +// +func Verify(fset *token.FileSet, grammar Grammar, start string) os.Error { var v verifier - v.verify(grammar, start) + v.verify(fset, grammar, start) return v.GetError(scanner.Sorted) } diff --git a/src/pkg/ebnf/ebnf_test.go b/src/pkg/ebnf/ebnf_test.go index a88d19bed..bbe530c27 100644 --- a/src/pkg/ebnf/ebnf_test.go +++ b/src/pkg/ebnf/ebnf_test.go @@ -5,11 +5,15 @@ package ebnf import ( + "go/token" "io/ioutil" "testing" ) +var fset = token.NewFileSet() + + var grammars = []string{ `Program = . `, @@ -40,11 +44,11 @@ var grammars = []string{ func check(t *testing.T, filename string, src []byte) { - grammar, err := Parse(filename, src) + grammar, err := Parse(fset, filename, src) if err != nil { t.Errorf("Parse(%s) failed: %v", src, err) } - if err = Verify(grammar, "Program"); err != nil { + if err = Verify(fset, grammar, "Program"); err != nil { t.Errorf("Verify(%s) failed: %v", src, err) } } diff --git a/src/pkg/ebnf/parser.go b/src/pkg/ebnf/parser.go index 649587879..ef72d91fd 100644 --- a/src/pkg/ebnf/parser.go +++ b/src/pkg/ebnf/parser.go @@ -5,7 +5,6 @@ package ebnf import ( - "container/vector" "go/scanner" "go/token" "os" @@ -14,11 +13,12 @@ import ( type parser struct { + fset *token.FileSet scanner.ErrorVector scanner scanner.Scanner - pos token.Position // token position - tok token.Token // one token look-ahead - lit []byte // token literal + pos token.Pos // token position + tok token.Token // one token look-ahead + lit []byte // token literal } @@ -32,9 +32,14 @@ func (p *parser) next() { } -func (p *parser) errorExpected(pos token.Position, msg string) { +func (p *parser) error(pos token.Pos, msg string) { + p.Error(p.fset.Position(pos), msg) +} + + +func (p *parser) errorExpected(pos token.Pos, msg string) { msg = "expected " + msg - if pos.Offset == p.pos.Offset { + if pos == p.pos { // the error happened at the current position; // make the error message more specific msg += ", found '" + p.tok.String() + "'" @@ -42,11 +47,11 @@ func (p *parser) errorExpected(pos token.Position, msg string) { msg += " " + string(p.lit) } } - p.Error(pos, msg) + p.error(pos, msg) } -func (p *parser) expect(tok token.Token) token.Position { +func (p *parser) expect(tok token.Token) token.Pos { pos := p.pos if p.tok != tok { p.errorExpected(pos, "'"+tok.String()+"'") @@ -116,36 +121,30 @@ func (p *parser) parseTerm() (x Expression) { func (p *parser) parseSequence() Expression { - var list vector.Vector + var list Sequence for x := p.parseTerm(); x != nil; x = p.parseTerm() { - list.Push(x) + list = append(list, x) } // no need for a sequence if list.Len() < 2 - switch list.Len() { + switch len(list) { case 0: return nil case 1: - return list.At(0).(Expression) + return list[0] } - // convert list into a sequence - seq := make(Sequence, list.Len()) - for i := 0; i < list.Len(); i++ { - seq[i] = list.At(i).(Expression) - } - return seq + return list } func (p *parser) parseExpression() Expression { - var list vector.Vector + var list Alternative for { - x := p.parseSequence() - if x != nil { - list.Push(x) + if x := p.parseSequence(); x != nil { + list = append(list, x) } if p.tok != token.OR { break @@ -154,19 +153,14 @@ func (p *parser) parseExpression() Expression { } // no need for an Alternative node if list.Len() < 2 - switch list.Len() { + switch len(list) { case 0: return nil case 1: - return list.At(0).(Expression) + return list[0] } - // convert list into an Alternative node - alt := make(Alternative, list.Len()) - for i := 0; i < list.Len(); i++ { - alt[i] = list.At(i).(Expression) - } - return alt + return list } @@ -179,10 +173,11 @@ func (p *parser) parseProduction() *Production { } -func (p *parser) parse(filename string, src []byte) Grammar { +func (p *parser) parse(fset *token.FileSet, filename string, src []byte) Grammar { // initialize parser + p.fset = fset p.ErrorVector.Reset() - p.scanner.Init(filename, src, p, 0) + p.scanner.Init(fset, filename, src, p, 0) p.next() // initializes pos, tok, lit grammar := make(Grammar) @@ -192,7 +187,7 @@ func (p *parser) parse(filename string, src []byte) Grammar { if _, found := grammar[name]; !found { grammar[name] = prod } else { - p.Error(prod.Pos(), name+" declared already") + p.error(prod.Pos(), name+" declared already") } } @@ -203,10 +198,11 @@ func (p *parser) parse(filename string, src []byte) Grammar { // Parse parses a set of EBNF productions from source src. // It returns a set of productions. Errors are reported // for incorrect syntax and if a production is declared -// more than once. +// more than once. Position information is recorded relative +// to the file set fset. // -func Parse(filename string, src []byte) (Grammar, os.Error) { +func Parse(fset *token.FileSet, filename string, src []byte) (Grammar, os.Error) { var p parser - grammar := p.parse(filename, src) + grammar := p.parse(fset, filename, src) return grammar, p.GetError(scanner.Sorted) } diff --git a/src/pkg/encoding/ascii85/Makefile b/src/pkg/encoding/ascii85/Makefile index 7ec14bd1a..412383efd 100644 --- a/src/pkg/encoding/ascii85/Makefile +++ b/src/pkg/encoding/ascii85/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=encoding/ascii85 GOFILES=\ diff --git a/src/pkg/encoding/ascii85/ascii85_test.go b/src/pkg/encoding/ascii85/ascii85_test.go index 738e1cc1b..fdfeb889f 100644 --- a/src/pkg/encoding/ascii85/ascii85_test.go +++ b/src/pkg/encoding/ascii85/ascii85_test.go @@ -17,7 +17,7 @@ type testpair struct { var pairs = []testpair{ // Wikipedia example - testpair{ + { "Man is distinguished, not only by his reason, but by this singular passion from " + "other animals, which is a lust of the mind, that by a perseverance of delight in " + "the continued and indefatigable generation of knowledge, exceeds the short " + @@ -34,7 +34,7 @@ var bigtest = pairs[len(pairs)-1] func testEqual(t *testing.T, msg string, args ...interface{}) bool { if args[len(args)-2] != args[len(args)-1] { - t.Errorf(msg, args) + t.Errorf(msg, args...) return false } return true @@ -138,8 +138,8 @@ func TestDecodeCorrupt(t *testing.T) { p int } examples := []corrupt{ - corrupt{"v", 0}, - corrupt{"!z!!!!!!!!!", 1}, + {"v", 0}, + {"!z!!!!!!!!!", 1}, } for _, e := range examples { diff --git a/src/pkg/encoding/base64/Makefile b/src/pkg/encoding/base64/Makefile index 8503b1663..2f54ed839 100644 --- a/src/pkg/encoding/base64/Makefile +++ b/src/pkg/encoding/base64/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=encoding/base64 GOFILES=\ diff --git a/src/pkg/encoding/base64/base64_test.go b/src/pkg/encoding/base64/base64_test.go index c14785f1b..de41e704b 100644 --- a/src/pkg/encoding/base64/base64_test.go +++ b/src/pkg/encoding/base64/base64_test.go @@ -17,28 +17,28 @@ type testpair struct { var pairs = []testpair{ // RFC 3548 examples - testpair{"\x14\xfb\x9c\x03\xd9\x7e", "FPucA9l+"}, - testpair{"\x14\xfb\x9c\x03\xd9", "FPucA9k="}, - testpair{"\x14\xfb\x9c\x03", "FPucAw=="}, + {"\x14\xfb\x9c\x03\xd9\x7e", "FPucA9l+"}, + {"\x14\xfb\x9c\x03\xd9", "FPucA9k="}, + {"\x14\xfb\x9c\x03", "FPucAw=="}, // RFC 4648 examples - testpair{"", ""}, - testpair{"f", "Zg=="}, - testpair{"fo", "Zm8="}, - testpair{"foo", "Zm9v"}, - testpair{"foob", "Zm9vYg=="}, - testpair{"fooba", "Zm9vYmE="}, - testpair{"foobar", "Zm9vYmFy"}, + {"", ""}, + {"f", "Zg=="}, + {"fo", "Zm8="}, + {"foo", "Zm9v"}, + {"foob", "Zm9vYg=="}, + {"fooba", "Zm9vYmE="}, + {"foobar", "Zm9vYmFy"}, // Wikipedia examples - testpair{"sure.", "c3VyZS4="}, - testpair{"sure", "c3VyZQ=="}, - testpair{"sur", "c3Vy"}, - testpair{"su", "c3U="}, - testpair{"leasure.", "bGVhc3VyZS4="}, - testpair{"easure.", "ZWFzdXJlLg=="}, - testpair{"asure.", "YXN1cmUu"}, - testpair{"sure.", "c3VyZS4="}, + {"sure.", "c3VyZS4="}, + {"sure", "c3VyZQ=="}, + {"sur", "c3Vy"}, + {"su", "c3U="}, + {"leasure.", "bGVhc3VyZS4="}, + {"easure.", "ZWFzdXJlLg=="}, + {"asure.", "YXN1cmUu"}, + {"sure.", "c3VyZS4="}, } var bigtest = testpair{ @@ -48,7 +48,7 @@ var bigtest = testpair{ func testEqual(t *testing.T, msg string, args ...interface{}) bool { if args[len(args)-2] != args[len(args)-1] { - t.Errorf(msg, args) + t.Errorf(msg, args...) return false } return true @@ -142,12 +142,12 @@ func TestDecodeCorrupt(t *testing.T) { p int } examples := []corrupt{ - corrupt{"!!!!", 0}, - corrupt{"x===", 1}, - corrupt{"AA=A", 2}, - corrupt{"AAA=AAAA", 3}, - corrupt{"AAAAA", 4}, - corrupt{"AAAAAA", 4}, + {"!!!!", 0}, + {"x===", 1}, + {"AA=A", 2}, + {"AAA=AAAA", 3}, + {"AAAAA", 4}, + {"AAAAAA", 4}, } for _, e := range examples { diff --git a/src/pkg/encoding/binary/Makefile b/src/pkg/encoding/binary/Makefile index 23d4d6d43..dc46abe90 100644 --- a/src/pkg/encoding/binary/Makefile +++ b/src/pkg/encoding/binary/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=encoding/binary GOFILES=\ diff --git a/src/pkg/encoding/binary/binary.go b/src/pkg/encoding/binary/binary.go index 5a92faa21..6bbe7eb89 100644 --- a/src/pkg/encoding/binary/binary.go +++ b/src/pkg/encoding/binary/binary.go @@ -29,8 +29,11 @@ type ByteOrder interface { // allowing, e.g., order == binary.LittleEndian. type unused byte -var LittleEndian ByteOrder = littleEndian(0) -var BigEndian ByteOrder = bigEndian(0) +// LittleEndian is the little-endian implementation of ByteOrder. +var LittleEndian littleEndian + +// BigEndian is the big-endian implementation of ByteOrder. +var BigEndian bigEndian type littleEndian unused @@ -115,11 +118,11 @@ func (bigEndian) GoString() string { return "binary.BigEndian" } // Read reads structured binary data from r into data. // Data must be a pointer to a fixed-size value or a slice // of fixed-size values. -// A fixed-size value is either a fixed-size integer -// (int8, uint8, int16, uint16, ...) or an array or struct -// containing only fixed-size values. Bytes read from -// r are decoded using the specified byte order and written -// to successive fields of the data. +// A fixed-size value is either a fixed-size arithmetic +// type (int8, uint8, int16, float32, complex64, ...) +// or an array or struct containing only fixed-size values. +// Bytes read from r are decoded using the specified byte order +// and written to successive fields of the data. func Read(r io.Reader, order ByteOrder, data interface{}) os.Error { var v reflect.Value switch d := reflect.NewValue(data).(type) { @@ -145,11 +148,11 @@ func Read(r io.Reader, order ByteOrder, data interface{}) os.Error { // Write writes the binary representation of data into w. // Data must be a fixed-size value or a pointer to // a fixed-size value. -// A fixed-size value is either a fixed-size integer -// (int8, uint8, int16, uint16, ...) or an array or struct -// containing only fixed-size values. Bytes written to -// w are encoded using the specified byte order and read -// from successive fields of the data. +// A fixed-size value is either a fixed-size arithmetic +// type (int8, uint8, int16, float32, complex64, ...) +// or an array or struct containing only fixed-size values. +// Bytes written to w are encoded using the specified byte order +// and read from successive fields of the data. func Write(w io.Writer, order ByteOrder, data interface{}) os.Error { v := reflect.Indirect(reflect.NewValue(data)) size := TotalSize(v) @@ -194,7 +197,11 @@ func sizeof(v reflect.Type) int { } return sum - case *reflect.UintType, *reflect.IntType, *reflect.FloatType: + case *reflect.UintType, *reflect.IntType, *reflect.FloatType, *reflect.ComplexType: + switch t := t.Kind(); t { + case reflect.Int, reflect.Uint, reflect.Uintptr, reflect.Float, reflect.Complex: + return -1 + } return int(v.Size()) } return -1 @@ -320,6 +327,20 @@ func (d *decoder) value(v reflect.Value) { case reflect.Float64: v.Set(math.Float64frombits(d.uint64())) } + + case *reflect.ComplexValue: + switch v.Type().Kind() { + case reflect.Complex64: + v.Set(cmplx( + float64(math.Float32frombits(d.uint32())), + float64(math.Float32frombits(d.uint32())), + )) + case reflect.Complex128: + v.Set(cmplx( + math.Float64frombits(d.uint64()), + math.Float64frombits(d.uint64()), + )) + } } } @@ -372,5 +393,17 @@ func (e *encoder) value(v reflect.Value) { case reflect.Float64: e.uint64(math.Float64bits(v.Get())) } + + case *reflect.ComplexValue: + switch v.Type().Kind() { + case reflect.Complex64: + x := v.Get() + e.uint32(math.Float32bits(float32(real(x)))) + e.uint32(math.Float32bits(float32(imag(x)))) + case reflect.Complex128: + x := v.Get() + e.uint64(math.Float64bits(real(x))) + e.uint64(math.Float64bits(imag(x))) + } } } diff --git a/src/pkg/encoding/binary/binary_test.go b/src/pkg/encoding/binary/binary_test.go index 12d192d1e..c378413f1 100644 --- a/src/pkg/encoding/binary/binary_test.go +++ b/src/pkg/encoding/binary/binary_test.go @@ -13,16 +13,28 @@ import ( ) type Struct struct { - Int8 int8 - Int16 int16 - Int32 int32 - Int64 int64 - Uint8 uint8 - Uint16 uint16 - Uint32 uint32 - Uint64 uint64 - Float64 float64 - Array [4]uint8 + Int8 int8 + Int16 int16 + Int32 int32 + Int64 int64 + Uint8 uint8 + Uint16 uint16 + Uint32 uint32 + Uint64 uint64 + Float32 float32 + Float64 float64 + Complex64 complex64 + Complex128 complex128 + Array [4]uint8 +} + +type T struct { + Int int + Uint uint + Float float + Complex complex + Uintptr uintptr + Array [4]int } var s = Struct{ @@ -34,8 +46,19 @@ var s = Struct{ 0x1112, 0x13141516, 0x1718191a1b1c1d1e, - math.Float64frombits(0x1f20212223242526), - [4]uint8{0x27, 0x28, 0x29, 0x2a}, + + math.Float32frombits(0x1f202122), + math.Float64frombits(0x232425262728292a), + cmplx( + math.Float32frombits(0x2b2c2d2e), + math.Float32frombits(0x2f303132), + ), + cmplx( + math.Float64frombits(0x333435363738393a), + math.Float64frombits(0x3b3c3d3e3f404142), + ), + + [4]uint8{0x43, 0x44, 0x45, 0x46}, } var big = []byte{ @@ -47,8 +70,13 @@ var big = []byte{ 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, + + 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + + 67, 68, 69, 70, } var little = []byte{ @@ -60,8 +88,13 @@ var little = []byte{ 18, 17, 22, 21, 20, 19, 30, 29, 28, 27, 26, 25, 24, 23, - 38, 37, 36, 35, 34, 33, 32, 31, - 39, 40, 41, 42, + + 34, 33, 32, 31, + 42, 41, 40, 39, 38, 37, 36, 35, + 46, 45, 44, 43, 50, 49, 48, 47, + 58, 57, 56, 55, 54, 53, 52, 51, 66, 65, 64, 63, 62, 61, 60, 59, + + 67, 68, 69, 70, } var src = []byte{1, 2, 3, 4, 5, 6, 7, 8} @@ -112,3 +145,20 @@ func TestWriteSlice(t *testing.T) { err := Write(buf, BigEndian, res) checkResult(t, "WriteSlice", BigEndian, err, buf.Bytes(), src) } + +func TestWriteT(t *testing.T) { + buf := new(bytes.Buffer) + ts := T{} + err := Write(buf, BigEndian, ts) + if err == nil { + t.Errorf("WriteT: have nil, want non-nil") + } + + tv := reflect.Indirect(reflect.NewValue(ts)).(*reflect.StructValue) + for i, n := 0, tv.NumField(); i < n; i++ { + err = Write(buf, BigEndian, tv.Field(i).Interface()) + if err == nil { + t.Errorf("WriteT.%v: have nil, want non-nil", tv.Field(i).Type()) + } + } +} diff --git a/src/pkg/encoding/git85/Makefile b/src/pkg/encoding/git85/Makefile index 09dd96f32..fbd003454 100644 --- a/src/pkg/encoding/git85/Makefile +++ b/src/pkg/encoding/git85/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=encoding/git85 GOFILES=\ diff --git a/src/pkg/encoding/git85/git_test.go b/src/pkg/encoding/git85/git_test.go index a31f14d3c..c76385c35 100644 --- a/src/pkg/encoding/git85/git_test.go +++ b/src/pkg/encoding/git85/git_test.go @@ -17,7 +17,7 @@ type testpair struct { func testEqual(t *testing.T, msg string, args ...interface{}) bool { if args[len(args)-2] != args[len(args)-1] { - t.Errorf(msg, args) + t.Errorf(msg, args...) return false } return true @@ -40,7 +40,7 @@ func TestGitTable(t *testing.T) { var gitPairs = []testpair{ // Wikipedia example, adapted. - testpair{ + { "Man is distinguished, not only by his reason, but by this singular passion from " + "other animals, which is a lust of the mind, that by a perseverance of delight in " + "the continued and indefatigable generation of knowledge, exceeds the short " + @@ -144,8 +144,8 @@ func TestDecodeCorrupt(t *testing.T) { p int } examples := []corrupt{ - corrupt{"v", 0}, - corrupt{"!z!!!!!!!!!", 0}, + {"v", 0}, + {"!z!!!!!!!!!", 0}, } for _, e := range examples { diff --git a/src/pkg/encoding/hex/Makefile b/src/pkg/encoding/hex/Makefile index d6849d9a5..22049f43b 100644 --- a/src/pkg/encoding/hex/Makefile +++ b/src/pkg/encoding/hex/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=encoding/hex GOFILES=\ diff --git a/src/pkg/encoding/hex/hex.go b/src/pkg/encoding/hex/hex.go index 1c52885e2..292d917eb 100644 --- a/src/pkg/encoding/hex/hex.go +++ b/src/pkg/encoding/hex/hex.go @@ -71,7 +71,7 @@ func Decode(dst, src []byte) (int, os.Error) { // fromHexChar converts a hex character into its value and a success flag. func fromHexChar(c byte) (byte, bool) { switch { - case 0 <= c && c <= '9': + case '0' <= c && c <= '9': return c - '0', true case 'a' <= c && c <= 'f': return c - 'a' + 10, true diff --git a/src/pkg/encoding/hex/hex_test.go b/src/pkg/encoding/hex/hex_test.go index d741e595a..a14c9d4f4 100644 --- a/src/pkg/encoding/hex/hex_test.go +++ b/src/pkg/encoding/hex/hex_test.go @@ -14,26 +14,26 @@ type encodeTest struct { } var encodeTests = []encodeTest{ - encodeTest{[]byte{}, []byte{}}, - encodeTest{[]byte{0x01}, []byte{'0', '1'}}, - encodeTest{[]byte{0xff}, []byte{'f', 'f'}}, - encodeTest{[]byte{0xff, 00}, []byte{'f', 'f', '0', '0'}}, - encodeTest{[]byte{0}, []byte{'0', '0'}}, - encodeTest{[]byte{1}, []byte{'0', '1'}}, - encodeTest{[]byte{2}, []byte{'0', '2'}}, - encodeTest{[]byte{3}, []byte{'0', '3'}}, - encodeTest{[]byte{4}, []byte{'0', '4'}}, - encodeTest{[]byte{5}, []byte{'0', '5'}}, - encodeTest{[]byte{6}, []byte{'0', '6'}}, - encodeTest{[]byte{7}, []byte{'0', '7'}}, - encodeTest{[]byte{8}, []byte{'0', '8'}}, - encodeTest{[]byte{9}, []byte{'0', '9'}}, - encodeTest{[]byte{10}, []byte{'0', 'a'}}, - encodeTest{[]byte{11}, []byte{'0', 'b'}}, - encodeTest{[]byte{12}, []byte{'0', 'c'}}, - encodeTest{[]byte{13}, []byte{'0', 'd'}}, - encodeTest{[]byte{14}, []byte{'0', 'e'}}, - encodeTest{[]byte{15}, []byte{'0', 'f'}}, + {[]byte{}, []byte{}}, + {[]byte{0x01}, []byte{'0', '1'}}, + {[]byte{0xff}, []byte{'f', 'f'}}, + {[]byte{0xff, 00}, []byte{'f', 'f', '0', '0'}}, + {[]byte{0}, []byte{'0', '0'}}, + {[]byte{1}, []byte{'0', '1'}}, + {[]byte{2}, []byte{'0', '2'}}, + {[]byte{3}, []byte{'0', '3'}}, + {[]byte{4}, []byte{'0', '4'}}, + {[]byte{5}, []byte{'0', '5'}}, + {[]byte{6}, []byte{'0', '6'}}, + {[]byte{7}, []byte{'0', '7'}}, + {[]byte{8}, []byte{'0', '8'}}, + {[]byte{9}, []byte{'0', '9'}}, + {[]byte{10}, []byte{'0', 'a'}}, + {[]byte{11}, []byte{'0', 'b'}}, + {[]byte{12}, []byte{'0', 'c'}}, + {[]byte{13}, []byte{'0', 'd'}}, + {[]byte{14}, []byte{'0', 'e'}}, + {[]byte{15}, []byte{'0', 'f'}}, } func TestEncode(t *testing.T) { @@ -55,31 +55,32 @@ type decodeTest struct { } var decodeTests = []decodeTest{ - decodeTest{[]byte{}, []byte{}, true}, - decodeTest{[]byte{'0'}, []byte{}, false}, - decodeTest{[]byte{'0', 'g'}, []byte{}, false}, - decodeTest{[]byte{'0', '0'}, []byte{0}, true}, - decodeTest{[]byte{'0', '1'}, []byte{1}, true}, - decodeTest{[]byte{'0', '2'}, []byte{2}, true}, - decodeTest{[]byte{'0', '3'}, []byte{3}, true}, - decodeTest{[]byte{'0', '4'}, []byte{4}, true}, - decodeTest{[]byte{'0', '5'}, []byte{5}, true}, - decodeTest{[]byte{'0', '6'}, []byte{6}, true}, - decodeTest{[]byte{'0', '7'}, []byte{7}, true}, - decodeTest{[]byte{'0', '8'}, []byte{8}, true}, - decodeTest{[]byte{'0', '9'}, []byte{9}, true}, - decodeTest{[]byte{'0', 'a'}, []byte{10}, true}, - decodeTest{[]byte{'0', 'b'}, []byte{11}, true}, - decodeTest{[]byte{'0', 'c'}, []byte{12}, true}, - decodeTest{[]byte{'0', 'd'}, []byte{13}, true}, - decodeTest{[]byte{'0', 'e'}, []byte{14}, true}, - decodeTest{[]byte{'0', 'f'}, []byte{15}, true}, - decodeTest{[]byte{'0', 'A'}, []byte{10}, true}, - decodeTest{[]byte{'0', 'B'}, []byte{11}, true}, - decodeTest{[]byte{'0', 'C'}, []byte{12}, true}, - decodeTest{[]byte{'0', 'D'}, []byte{13}, true}, - decodeTest{[]byte{'0', 'E'}, []byte{14}, true}, - decodeTest{[]byte{'0', 'F'}, []byte{15}, true}, + {[]byte{}, []byte{}, true}, + {[]byte{'0'}, []byte{}, false}, + {[]byte{'0', 'g'}, []byte{}, false}, + {[]byte{'0', '\x01'}, []byte{}, false}, + {[]byte{'0', '0'}, []byte{0}, true}, + {[]byte{'0', '1'}, []byte{1}, true}, + {[]byte{'0', '2'}, []byte{2}, true}, + {[]byte{'0', '3'}, []byte{3}, true}, + {[]byte{'0', '4'}, []byte{4}, true}, + {[]byte{'0', '5'}, []byte{5}, true}, + {[]byte{'0', '6'}, []byte{6}, true}, + {[]byte{'0', '7'}, []byte{7}, true}, + {[]byte{'0', '8'}, []byte{8}, true}, + {[]byte{'0', '9'}, []byte{9}, true}, + {[]byte{'0', 'a'}, []byte{10}, true}, + {[]byte{'0', 'b'}, []byte{11}, true}, + {[]byte{'0', 'c'}, []byte{12}, true}, + {[]byte{'0', 'd'}, []byte{13}, true}, + {[]byte{'0', 'e'}, []byte{14}, true}, + {[]byte{'0', 'f'}, []byte{15}, true}, + {[]byte{'0', 'A'}, []byte{10}, true}, + {[]byte{'0', 'B'}, []byte{11}, true}, + {[]byte{'0', 'C'}, []byte{12}, true}, + {[]byte{'0', 'D'}, []byte{13}, true}, + {[]byte{'0', 'E'}, []byte{14}, true}, + {[]byte{'0', 'F'}, []byte{15}, true}, } func TestDecode(t *testing.T) { @@ -104,10 +105,10 @@ type encodeStringTest struct { } var encodeStringTests = []encodeStringTest{ - encodeStringTest{[]byte{}, ""}, - encodeStringTest{[]byte{0}, "00"}, - encodeStringTest{[]byte{0, 1}, "0001"}, - encodeStringTest{[]byte{0, 1, 255}, "0001ff"}, + {[]byte{}, ""}, + {[]byte{0}, "00"}, + {[]byte{0, 1}, "0001"}, + {[]byte{0, 1, 255}, "0001ff"}, } func TestEncodeToString(t *testing.T) { @@ -126,12 +127,13 @@ type decodeStringTest struct { } var decodeStringTests = []decodeStringTest{ - decodeStringTest{"", []byte{}, true}, - decodeStringTest{"0", []byte{}, false}, - decodeStringTest{"00", []byte{0}, true}, - decodeStringTest{"0g", []byte{}, false}, - decodeStringTest{"00ff00", []byte{0, 255, 0}, true}, - decodeStringTest{"0000ff", []byte{0, 0, 255}, true}, + {"", []byte{}, true}, + {"0", []byte{}, false}, + {"00", []byte{0}, true}, + {"0\x01", []byte{}, false}, + {"0g", []byte{}, false}, + {"00ff00", []byte{0, 255, 0}, true}, + {"0000ff", []byte{0, 0, 255}, true}, } func TestDecodeString(t *testing.T) { diff --git a/src/pkg/encoding/line/Makefile b/src/pkg/encoding/line/Makefile new file mode 100644 index 000000000..1af355c27 --- /dev/null +++ b/src/pkg/encoding/line/Makefile @@ -0,0 +1,11 @@ +# 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=encoding/line +GOFILES=\ + line.go\ + +include ../../../Make.pkg diff --git a/src/pkg/encoding/line/line.go b/src/pkg/encoding/line/line.go new file mode 100644 index 000000000..92dddcb99 --- /dev/null +++ b/src/pkg/encoding/line/line.go @@ -0,0 +1,95 @@ +// 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 package implements a Reader which handles reading \r and \r\n +// deliminated lines. +package line + +import ( + "io" + "os" +) + +// Reader reads lines from an io.Reader (which may use either '\n' or +// '\r\n'). +type Reader struct { + buf []byte + consumed int + in io.Reader + err os.Error +} + +func NewReader(in io.Reader, maxLineLength int) *Reader { + return &Reader{ + buf: make([]byte, 0, maxLineLength), + consumed: 0, + in: in, + } +} + +// ReadLine tries to return a single line, not including the end-of-line bytes. +// If the line was found to be longer than the maximum length then isPrefix is +// set and the beginning of the line is returned. The rest of the line will be +// returned from future calls. isPrefix will be false when returning the last +// fragment of the line. The returned buffer points into the internal state of +// the Reader and is only valid until the next call to ReadLine. ReadLine +// either returns a non-nil line or it returns an error, never both. +func (l *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) { + if l.consumed > 0 { + n := copy(l.buf, l.buf[l.consumed:]) + l.buf = l.buf[:n] + l.consumed = 0 + } + + if len(l.buf) == 0 && l.err != nil { + err = l.err + return + } + + scannedTo := 0 + + for { + i := scannedTo + for ; i < len(l.buf); i++ { + if l.buf[i] == '\r' && len(l.buf) > i+1 && l.buf[i+1] == '\n' { + line = l.buf[:i] + l.consumed = i + 2 + return + } else if l.buf[i] == '\n' { + line = l.buf[:i] + l.consumed = i + 1 + return + } + } + + if i == cap(l.buf) { + line = l.buf[:i] + l.consumed = i + isPrefix = true + return + } + + if l.err != nil { + line = l.buf + l.consumed = i + return + } + + // We don't want to rescan the input that we just scanned. + // However, we need to back up one byte because the last byte + // could have been a '\r' and we do need to rescan that. + scannedTo = i + if scannedTo > 0 { + scannedTo-- + } + oldLen := len(l.buf) + l.buf = l.buf[:cap(l.buf)] + n, readErr := l.in.Read(l.buf[oldLen:]) + l.buf = l.buf[:oldLen+n] + if readErr != nil { + l.err = readErr + } + } + panic("unreachable") +} diff --git a/src/pkg/encoding/line/line_test.go b/src/pkg/encoding/line/line_test.go new file mode 100644 index 000000000..68d13b586 --- /dev/null +++ b/src/pkg/encoding/line/line_test.go @@ -0,0 +1,89 @@ +// 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 line + +import ( + "bytes" + "os" + "testing" +) + +var testOutput = []byte("0123456789abcdefghijklmnopqrstuvwxy") +var testInput = []byte("012\n345\n678\n9ab\ncde\nfgh\nijk\nlmn\nopq\nrst\nuvw\nxy") +var testInputrn = []byte("012\r\n345\r\n678\r\n9ab\r\ncde\r\nfgh\r\nijk\r\nlmn\r\nopq\r\nrst\r\nuvw\r\nxy\r\n\n\r\n") + +// TestReader wraps a []byte and returns reads of a specific length. +type testReader struct { + data []byte + stride int +} + +func (t *testReader) Read(buf []byte) (n int, err os.Error) { + n = t.stride + if n > len(t.data) { + n = len(t.data) + } + if n > len(buf) { + n = len(buf) + } + copy(buf, t.data) + t.data = t.data[n:] + if len(t.data) == 0 { + err = os.EOF + } + return +} + +func testLineReader(t *testing.T, input []byte) { + for stride := 1; stride < len(input); stride++ { + done := 0 + reader := testReader{input, stride} + l := NewReader(&reader, len(input)+1) + for { + line, isPrefix, err := l.ReadLine() + if len(line) > 0 && err != nil { + t.Errorf("ReadLine returned both data and error: %s", err) + } + if isPrefix { + t.Errorf("ReadLine returned prefix") + } + if err != nil { + if err != os.EOF { + t.Fatalf("Got unknown error: %s", err) + } + break + } + if want := testOutput[done : done+len(line)]; !bytes.Equal(want, line) { + t.Errorf("Bad line at stride %d: want: %x got: %x", stride, want, line) + } + done += len(line) + } + if done != len(testOutput) { + t.Error("ReadLine didn't return everything") + } + } +} + +func TestReader(t *testing.T) { + testLineReader(t, testInput) + testLineReader(t, testInputrn) +} + +func TestLineTooLong(t *testing.T) { + buf := bytes.NewBuffer([]byte("aaabbbcc\n")) + l := NewReader(buf, 3) + line, isPrefix, err := l.ReadLine() + if !isPrefix || !bytes.Equal(line, []byte("aaa")) || err != nil { + t.Errorf("bad result for first line: %x %s", line, err) + } + line, isPrefix, err = l.ReadLine() + if !isPrefix || !bytes.Equal(line, []byte("bbb")) || err != nil { + t.Errorf("bad result for second line: %x", line) + } + line, isPrefix, err = l.ReadLine() + if isPrefix || !bytes.Equal(line, []byte("cc")) || err != nil { + t.Errorf("bad result for third line: %x", line) + } +} diff --git a/src/pkg/encoding/pem/Makefile b/src/pkg/encoding/pem/Makefile index 79490a57d..527670380 100644 --- a/src/pkg/encoding/pem/Makefile +++ b/src/pkg/encoding/pem/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=encoding/pem GOFILES=\ diff --git a/src/pkg/encoding/pem/pem.go b/src/pkg/encoding/pem/pem.go index f39540756..5653aeb77 100644 --- a/src/pkg/encoding/pem/pem.go +++ b/src/pkg/encoding/pem/pem.go @@ -218,14 +218,13 @@ func Encode(out io.Writer, b *Block) (err os.Error) { return } - for k, v := range b.Headers { - _, err = out.Write([]byte(k + ": " + v + "\n")) - if err != nil { - return + if len(b.Headers) > 0 { + for k, v := range b.Headers { + _, err = out.Write([]byte(k + ": " + v + "\n")) + if err != nil { + return + } } - } - - if len(b.Headers) > 1 { _, err = out.Write([]byte{'\n'}) if err != nil { return diff --git a/src/pkg/encoding/pem/pem_test.go b/src/pkg/encoding/pem/pem_test.go index 42bd573d7..11efe5544 100644 --- a/src/pkg/encoding/pem/pem_test.go +++ b/src/pkg/encoding/pem/pem_test.go @@ -15,14 +15,14 @@ type GetLineTest struct { } var getLineTests = []GetLineTest{ - GetLineTest{"abc", "abc", ""}, - GetLineTest{"abc\r", "abc\r", ""}, - GetLineTest{"abc\n", "abc", ""}, - GetLineTest{"abc\r\n", "abc", ""}, - GetLineTest{"abc\nd", "abc", "d"}, - GetLineTest{"abc\r\nd", "abc", "d"}, - GetLineTest{"\nabc", "", "abc"}, - GetLineTest{"\r\nabc", "", "abc"}, + {"abc", "abc", ""}, + {"abc\r", "abc\r", ""}, + {"abc\n", "abc", ""}, + {"abc\r\n", "abc", ""}, + {"abc\nd", "abc", "d"}, + {"abc\r\nd", "abc", "d"}, + {"\nabc", "", "abc"}, + {"\r\nabc", "", "abc"}, } func TestGetLine(t *testing.T) { @@ -63,12 +63,12 @@ type lineBreakerTest struct { const sixtyFourCharString = "0123456789012345678901234567890123456789012345678901234567890123" var lineBreakerTests = []lineBreakerTest{ - lineBreakerTest{"", ""}, - lineBreakerTest{"a", "a\n"}, - lineBreakerTest{"ab", "ab\n"}, - lineBreakerTest{sixtyFourCharString, sixtyFourCharString + "\n"}, - lineBreakerTest{sixtyFourCharString + "X", sixtyFourCharString + "\nX\n"}, - lineBreakerTest{sixtyFourCharString + sixtyFourCharString, sixtyFourCharString + "\n" + sixtyFourCharString + "\n"}, + {"", ""}, + {"a", "a\n"}, + {"ab", "ab\n"}, + {sixtyFourCharString, sixtyFourCharString + "\n"}, + {sixtyFourCharString + "X", sixtyFourCharString + "\nX\n"}, + {sixtyFourCharString + sixtyFourCharString, sixtyFourCharString + "\n" + sixtyFourCharString + "\n"}, } func TestLineBreaker(t *testing.T) { diff --git a/src/pkg/exec/Makefile b/src/pkg/exec/Makefile index 9d88b8d8a..262ecac85 100644 --- a/src/pkg/exec/Makefile +++ b/src/pkg/exec/Makefile @@ -2,10 +2,24 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=exec GOFILES=\ exec.go\ +GOFILES_freebsd=\ + lp_unix.go\ + +GOFILES_darwin=\ + lp_unix.go\ + +GOFILES_linux=\ + lp_unix.go\ + +GOFILES_windows=\ + lp_windows.go\ + +GOFILES+=$(GOFILES_$(GOOS)) + include ../../Make.pkg diff --git a/src/pkg/exec/exec.go b/src/pkg/exec/exec.go index ee3cec686..ba9bd2472 100644 --- a/src/pkg/exec/exec.go +++ b/src/pkg/exec/exec.go @@ -7,7 +7,6 @@ package exec import ( "os" - "strings" ) // Arguments to Run. @@ -39,7 +38,7 @@ func modeToFiles(mode, fd int) (*os.File, *os.File, os.Error) { if fd == 0 { rw = os.O_RDONLY } - f, err := os.Open("/dev/null", rw, 0) + f, err := os.Open(os.DevNull, rw, 0) return f, nil, err case PassThrough: switch fd { @@ -63,7 +62,7 @@ func modeToFiles(mode, fd int) (*os.File, *os.File, os.Error) { return nil, nil, os.EINVAL } -// Run starts the binary prog running with +// Run starts the named binary running with // arguments argv and environment envv. // It returns a pointer to a new Cmd representing // the command or an error. @@ -78,7 +77,7 @@ func modeToFiles(mode, fd int) (*os.File, *os.File, os.Error) { // If a parameter is Pipe, then the corresponding field (Stdin, Stdout, Stderr) // of the returned Cmd is the other end of the pipe. // Otherwise the field in Cmd is nil. -func Run(argv0 string, argv, envv []string, dir string, stdin, stdout, stderr int) (p *Cmd, err os.Error) { +func Run(name string, argv, envv []string, dir string, stdin, stdout, stderr int) (p *Cmd, err os.Error) { p = new(Cmd) var fd [3]*os.File @@ -95,7 +94,7 @@ func Run(argv0 string, argv, envv []string, dir string, stdin, stdout, stderr in } // Run command. - p.Pid, err = os.ForkExec(argv0, argv, envv, dir, fd[0:]) + p.Pid, err = os.ForkExec(name, argv, envv, dir, fd[0:]) if err != nil { goto Error } @@ -182,40 +181,3 @@ func (p *Cmd) Close() os.Error { } return err } - -func canExec(file string) bool { - d, err := os.Stat(file) - if err != nil { - return false - } - return d.IsRegular() && d.Permission()&0111 != 0 -} - -// LookPath searches for an executable binary named file -// in the directories named by the PATH environment variable. -// If file contains a slash, it is tried directly and the PATH is not consulted. -// -// TODO(rsc): Does LookPath belong in os instead? -func LookPath(file string) (string, os.Error) { - // NOTE(rsc): I wish we could use the Plan 9 behavior here - // (only bypass the path if file begins with / or ./ or ../) - // but that would not match all the Unix shells. - - if strings.Index(file, "/") >= 0 { - if canExec(file) { - return file, nil - } - return "", os.ENOENT - } - pathenv := os.Getenv("PATH") - for _, dir := range strings.Split(pathenv, ":", -1) { - if dir == "" { - // Unix shell semantics: path element "" means "." - dir = "." - } - if canExec(dir + "/" + file) { - return dir + "/" + file, nil - } - } - return "", os.ENOENT -} diff --git a/src/pkg/exec/exec_test.go b/src/pkg/exec/exec_test.go index 3e4ab7d78..3a3d3b1a5 100644 --- a/src/pkg/exec/exec_test.go +++ b/src/pkg/exec/exec_test.go @@ -8,11 +8,24 @@ import ( "io" "io/ioutil" "testing" + "os" + "runtime" ) +func run(argv []string, stdin, stdout, stderr int) (p *Cmd, err os.Error) { + if runtime.GOOS == "windows" { + argv = append([]string{"cmd", "/c"}, argv...) + } + exe, err := LookPath(argv[0]) + if err != nil { + return nil, err + } + p, err = Run(exe, argv, nil, "", stdin, stdout, stderr) + return p, err +} + func TestRunCat(t *testing.T) { - cmd, err := Run("/bin/cat", []string{"cat"}, nil, "", - Pipe, Pipe, DevNull) + cmd, err := run([]string{"cat"}, Pipe, Pipe, DevNull) if err != nil { t.Fatal("run:", err) } @@ -31,7 +44,7 @@ func TestRunCat(t *testing.T) { } func TestRunEcho(t *testing.T) { - cmd, err := Run("/bin/echo", []string{"echo", "hello", "world"}, nil, "", + cmd, err := run([]string{"sh", "-c", "echo hello world"}, DevNull, Pipe, DevNull) if err != nil { t.Fatal("run:", err) @@ -49,7 +62,7 @@ func TestRunEcho(t *testing.T) { } func TestStderr(t *testing.T) { - cmd, err := Run("/bin/sh", []string{"sh", "-c", "echo hello world 1>&2"}, nil, "", + cmd, err := run([]string{"sh", "-c", "echo hello world 1>&2"}, DevNull, DevNull, Pipe) if err != nil { t.Fatal("run:", err) @@ -66,9 +79,8 @@ func TestStderr(t *testing.T) { } } - func TestMergeWithStdout(t *testing.T) { - cmd, err := Run("/bin/sh", []string{"sh", "-c", "echo hello world 1>&2"}, nil, "", + cmd, err := run([]string{"sh", "-c", "echo hello world 1>&2"}, DevNull, Pipe, MergeWithStdout) if err != nil { t.Fatal("run:", err) @@ -84,3 +96,25 @@ func TestMergeWithStdout(t *testing.T) { t.Fatal("close:", err) } } + +func TestAddEnvVar(t *testing.T) { + err := os.Setenv("NEWVAR", "hello world") + if err != nil { + t.Fatal("setenv:", err) + } + cmd, err := run([]string{"sh", "-c", "echo $NEWVAR"}, + DevNull, Pipe, DevNull) + if err != nil { + t.Fatal("run:", err) + } + buf, err := ioutil.ReadAll(cmd.Stdout) + if err != nil { + t.Fatal("read:", err) + } + if string(buf) != "hello world\n" { + t.Fatalf("read: got %q", buf) + } + if err = cmd.Close(); err != nil { + t.Fatal("close:", err) + } +} diff --git a/src/pkg/exec/lp_unix.go b/src/pkg/exec/lp_unix.go new file mode 100644 index 000000000..292e24fcc --- /dev/null +++ b/src/pkg/exec/lp_unix.go @@ -0,0 +1,45 @@ +// 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 exec + +import ( + "os" + "strings" +) + +func canExec(file string) bool { + d, err := os.Stat(file) + if err != nil { + return false + } + return d.IsRegular() && d.Permission()&0111 != 0 +} + +// LookPath searches for an executable binary named file +// in the directories named by the PATH environment variable. +// If file contains a slash, it is tried directly and the PATH is not consulted. +func LookPath(file string) (string, os.Error) { + // NOTE(rsc): I wish we could use the Plan 9 behavior here + // (only bypass the path if file begins with / or ./ or ../) + // but that would not match all the Unix shells. + + if strings.Contains(file, "/") { + if canExec(file) { + return file, nil + } + return "", &os.PathError{"lookpath", file, os.ENOENT} + } + pathenv := os.Getenv("PATH") + for _, dir := range strings.Split(pathenv, ":", -1) { + if dir == "" { + // Unix shell semantics: path element "" means "." + dir = "." + } + if canExec(dir + "/" + file) { + return dir + "/" + file, nil + } + } + return "", &os.PathError{"lookpath", file, os.ENOENT} +} diff --git a/src/pkg/exec/lp_windows.go b/src/pkg/exec/lp_windows.go new file mode 100644 index 000000000..7b56afa85 --- /dev/null +++ b/src/pkg/exec/lp_windows.go @@ -0,0 +1,66 @@ +// 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 exec + +import ( + "os" + "strings" +) + +func chkStat(file string) bool { + d, err := os.Stat(file) + if err != nil { + return false + } + return d.IsRegular() +} + +func canExec(file string, exts []string) (string, bool) { + if len(exts) == 0 { + return file, chkStat(file) + } + f := strings.ToLower(file) + for _, e := range exts { + if strings.HasSuffix(f, e) { + return file, chkStat(file) + } + } + for _, e := range exts { + if f := file + e; chkStat(f) { + return f, true + } + } + return ``, false +} + +func LookPath(file string) (string, os.Error) { + exts := []string{} + if x := os.Getenv(`PATHEXT`); x != `` { + exts = strings.Split(strings.ToLower(x), `;`, -1) + for i, e := range exts { + if e == `` || e[0] != '.' { + exts[i] = `.` + e + } + } + } + if strings.Contains(file, `\`) || strings.Contains(file, `/`) { + if f, ok := canExec(file, exts); ok { + return f, nil + } + return ``, &os.PathError{"lookpath", file, os.ENOENT} + } + if pathenv := os.Getenv(`PATH`); pathenv == `` { + if f, ok := canExec(`.\`+file, exts); ok { + return f, nil + } + } else { + for _, dir := range strings.Split(pathenv, `;`, -1) { + if f, ok := canExec(dir+`\`+file, exts); ok { + return f, nil + } + } + } + return ``, &os.PathError{"lookpath", file, os.ENOENT} +} diff --git a/src/pkg/exp/4s/4s.go b/src/pkg/exp/4s/4s.go deleted file mode 100644 index 271af78e2..000000000 --- a/src/pkg/exp/4s/4s.go +++ /dev/null @@ -1,79 +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 is a simple demo of Go running under Native Client. -// It is a tetris clone built on top of the exp/nacl/av and exp/draw -// packages. -// -// See ../nacl/README for how to run it. -package main - -import ( - "exp/nacl/av" - "exp/nacl/srpc" - "log" - "runtime" - "os" -) - -var sndc chan []uint16 - -func main() { - // Native Client requires that some calls are issued - // consistently by the same OS thread. - runtime.LockOSThread() - - if srpc.Enabled() { - go srpc.ServeRuntime() - } - - args := os.Args - p := pieces4 - if len(args) > 1 && args[1] == "-5" { - p = pieces5 - } - dx, dy := 500, 500 - w, err := av.Init(av.SubsystemVideo|av.SubsystemAudio, dx, dy) - if err != nil { - log.Exit(err) - } - - sndc = make(chan []uint16, 10) - go audioServer() - Play(p, w) -} - -func audioServer() { - // Native Client requires that all audio calls - // original from a single OS thread. - runtime.LockOSThread() - - n, err := av.AudioStream(nil) - if err != nil { - log.Exit(err) - } - for { - b := <-sndc - for len(b)*2 >= n { - var a []uint16 - a, b = b[0:n/2], b[n/2:] - n, err = av.AudioStream(a) - if err != nil { - log.Exit(err) - } - println(n, len(b)*2) - } - a := make([]uint16, n/2) - for i := range b { - a[i] = b[i] - } - n, err = av.AudioStream(a) - } -} - -func PlaySound(b []uint16) { sndc <- b } - -var whoosh = []uint16{ -// Insert your favorite sound samples here. -} diff --git a/src/pkg/exp/4s/4s.html b/src/pkg/exp/4s/4s.html deleted file mode 100644 index 924f8b118..000000000 --- a/src/pkg/exp/4s/4s.html +++ /dev/null @@ -1,26 +0,0 @@ - - -

    games/4s

    -
    - - -This is a simple block stacking game, a port of Plan 9's -games/4s -

    -To play using the keyboard: -as the blocks fall, the a, s, d, and f keys -move the block left, rotate the block left, rotate the block right, -anad move the block right, respectively. -To drop a block, type the space key. -You may need to click on the game window to -focus the keyboard on it. -

    -To play using the mouse: -as the blocks fall, moving the mouse horizontally positions -the block; left or right clicks rotate the block left or right. -A middle click drops the block. -(Unfortunately, some environments seem to intercept -the middle click before it gets to Native Client.) -

    -To pause the game, type z, p, or the escape key. -
    diff --git a/src/pkg/exp/4s/5s.go b/src/pkg/exp/4s/5s.go deleted file mode 100644 index efeb6f116..000000000 --- a/src/pkg/exp/4s/5s.go +++ /dev/null @@ -1,9 +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. - -// Hack to produce a binary that defaults to 5s. - -package main - -func init() { pieces4 = pieces5 } diff --git a/src/pkg/exp/4s/5s.html b/src/pkg/exp/4s/5s.html deleted file mode 100644 index 5fa110753..000000000 --- a/src/pkg/exp/4s/5s.html +++ /dev/null @@ -1,26 +0,0 @@ - - -

    games/5s

    -
    - - -This is a simple block stacking game, a port of Plan 9's -games/5s -

    -To play using the keyboard: -as the blocks fall, the a, s, d, and f keys -move the block left, rotate the block left, rotate the block right, -anad move the block right, respectively. -To drop a block, type the space key. -You may need to click on the game window to -focus the keyboard on it. -

    -To play using the mouse: -as the blocks fall, moving the mouse horizontally positions -the block; left or right clicks rotate the block left or right. -A middle click drops the block. -(Unfortunately, some environments seem to intercept -the middle click before it gets to Native Client.) -

    -To pause the game, type z, p, or the escape key. -
    diff --git a/src/pkg/exp/4s/Makefile b/src/pkg/exp/4s/Makefile deleted file mode 100644 index 8ad390591..000000000 --- a/src/pkg/exp/4s/Makefile +++ /dev/null @@ -1,20 +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. - -all: 8.out 8.5s - -4s.8: 4s.go data.go xs.go - 8g 4s.go data.go xs.go - -5s.8: 5s.go 4s.go data.go xs.go - 8g 5s.go 4s.go data.go xs.go - -8.out: 4s.8 - 8l 4s.8 - -8.5s: 5s.8 - 8l -o 8.5s 5s.8 - -clean: - rm -f *.8 8.out diff --git a/src/pkg/exp/4s/data.go b/src/pkg/exp/4s/data.go deleted file mode 100644 index ac30fabf7..000000000 --- a/src/pkg/exp/4s/data.go +++ /dev/null @@ -1,142 +0,0 @@ -// games/4s - a tetris clone -// -// Derived from Plan 9's /sys/src/games/xs.c -// http://plan9.bell-labs.com/sources/plan9/sys/src/games/xs.c -// -// Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights Reserved. -// Portions Copyright 2009 The Go Authors. All Rights Reserved. -// Distributed under the terms of the Lucent Public License Version 1.02 -// See http://plan9.bell-labs.com/plan9/license.html - -package main - -import . "exp/draw" - -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}, - - Piece{0, 3, Point{2, 2}, []Point{Point{0, 1}, Point{1, 0}, Point{0, -1}, Point{-1, 0}}, nil, nil}, - Piece{1, 3, Point{2, 2}, []Point{Point{0, 1}, Point{1, 0}, Point{0, -1}, Point{-1, 0}}, nil, nil}, - Piece{2, 3, Point{2, 2}, []Point{Point{0, 1}, Point{1, 0}, Point{0, -1}, Point{-1, 0}}, nil, nil}, - Piece{3, 3, Point{2, 2}, []Point{Point{0, 1}, Point{1, 0}, Point{0, -1}, Point{-1, 0}}, nil, nil}, - - Piece{0, 1, Point{3, 2}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{0, 1}}, nil, nil}, - Piece{1, 1, Point{2, 3}, []Point{Point{1, 0}, Point{0, 1}, Point{0, 1}, Point{-1, 0}}, nil, nil}, - Piece{2, 1, Point{3, 2}, []Point{Point{0, 0}, Point{0, 1}, Point{1, 0}, Point{1, 0}}, nil, nil}, - Piece{3, 1, Point{2, 3}, []Point{Point{0, 0}, Point{1, 0}, Point{-1, 1}, Point{0, 1}}, nil, nil}, - - Piece{0, 2, Point{3, 2}, []Point{Point{0, 1}, Point{1, 0}, Point{1, 0}, Point{0, -1}}, nil, nil}, - Piece{1, 2, Point{2, 3}, []Point{Point{0, 0}, Point{0, 1}, Point{0, 1}, Point{1, 0}}, nil, nil}, - Piece{2, 2, Point{3, 2}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{-2, 1}}, nil, nil}, - Piece{3, 2, Point{2, 3}, []Point{Point{0, 0}, Point{1, 0}, Point{0, 1}, Point{0, 1}}, nil, nil}, - - Piece{0, 4, Point{3, 2}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{-1, 1}}, nil, nil}, - Piece{1, 4, Point{2, 3}, []Point{Point{1, 0}, Point{-1, 1}, Point{1, 0}, Point{0, 1}}, nil, nil}, - Piece{2, 4, Point{3, 2}, []Point{Point{1, 0}, Point{-1, 1}, Point{1, 0}, Point{1, 0}}, nil, nil}, - Piece{3, 4, Point{2, 3}, []Point{Point{0, 0}, Point{0, 1}, Point{0, 1}, Point{1, -1}}, nil, nil}, - - Piece{0, 5, Point{3, 2}, []Point{Point{0, 0}, Point{1, 0}, Point{0, 1}, Point{1, 0}}, nil, nil}, - Piece{1, 5, Point{2, 3}, []Point{Point{1, 0}, Point{0, 1}, Point{-1, 0}, Point{0, 1}}, nil, nil}, - Piece{2, 5, Point{3, 2}, []Point{Point{0, 0}, Point{1, 0}, Point{0, 1}, Point{1, 0}}, nil, nil}, - Piece{3, 5, Point{2, 3}, []Point{Point{1, 0}, Point{0, 1}, Point{-1, 0}, Point{0, 1}}, nil, nil}, - - Piece{0, 6, Point{3, 2}, []Point{Point{0, 1}, Point{1, 0}, Point{0, -1}, Point{1, 0}}, nil, nil}, - Piece{1, 6, Point{2, 3}, []Point{Point{0, 0}, Point{0, 1}, Point{1, 0}, Point{0, 1}}, nil, nil}, - Piece{2, 6, Point{3, 2}, []Point{Point{0, 1}, Point{1, 0}, Point{0, -1}, Point{1, 0}}, nil, nil}, - Piece{3, 6, Point{2, 3}, []Point{Point{0, 0}, Point{0, 1}, Point{1, 0}, Point{0, 1}}, nil, nil}, -} - -var pieces5 = []Piece{ - Piece{0, 1, Point{5, 1}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{1, 0}, Point{1, 0}}, nil, nil}, - Piece{1, 1, Point{1, 5}, []Point{Point{0, 0}, Point{0, 1}, Point{0, 1}, Point{0, 1}, Point{0, 1}}, nil, nil}, - Piece{2, 1, Point{5, 1}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{1, 0}, Point{1, 0}}, nil, nil}, - Piece{3, 1, Point{1, 5}, []Point{Point{0, 0}, Point{0, 1}, Point{0, 1}, Point{0, 1}, Point{0, 1}}, nil, nil}, - - Piece{0, 0, Point{4, 2}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{1, 0}, Point{0, 1}}, nil, nil}, - Piece{1, 0, Point{2, 4}, []Point{Point{1, 0}, Point{0, 1}, Point{0, 1}, Point{0, 1}, Point{-1, 0}}, nil, nil}, - Piece{2, 0, Point{4, 2}, []Point{Point{0, 0}, Point{0, 1}, Point{1, 0}, Point{1, 0}, Point{1, 0}}, nil, nil}, - Piece{3, 0, Point{2, 4}, []Point{Point{0, 0}, Point{1, 0}, Point{-1, 1}, Point{0, 1}, Point{0, 1}}, nil, nil}, - - Piece{0, 2, Point{4, 2}, []Point{Point{0, 0}, Point{0, 1}, Point{1, -1}, Point{1, 0}, Point{1, 0}}, nil, nil}, - Piece{1, 2, Point{2, 4}, []Point{Point{0, 0}, Point{1, 0}, Point{0, 1}, Point{0, 1}, Point{0, 1}}, nil, nil}, - Piece{2, 2, Point{4, 2}, []Point{Point{0, 1}, Point{1, 0}, Point{1, 0}, Point{1, 0}, Point{0, -1}}, nil, nil}, - Piece{3, 2, Point{2, 4}, []Point{Point{0, 0}, Point{0, 1}, Point{0, 1}, Point{0, 1}, Point{1, 0}}, nil, nil}, - - Piece{0, 7, Point{3, 3}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{0, 1}, Point{0, 1}}, nil, nil}, - Piece{1, 7, Point{3, 3}, []Point{Point{0, 2}, Point{1, 0}, Point{1, 0}, Point{0, -1}, Point{0, -1}}, nil, nil}, - Piece{2, 7, Point{3, 3}, []Point{Point{0, 0}, Point{0, 1}, Point{0, 1}, Point{1, 0}, Point{1, 0}}, nil, nil}, - Piece{3, 7, Point{3, 3}, []Point{Point{0, 2}, Point{0, -1}, Point{0, -1}, Point{1, 0}, Point{1, 0}}, nil, nil}, - - Piece{0, 3, Point{3, 2}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{-2, 1}, Point{1, 0}}, nil, nil}, - Piece{1, 3, Point{2, 3}, []Point{Point{0, 0}, Point{1, 0}, Point{-1, 1}, Point{1, 0}, Point{0, 1}}, nil, nil}, - Piece{2, 3, Point{3, 2}, []Point{Point{1, 0}, Point{1, 0}, Point{-2, 1}, Point{1, 0}, Point{1, 0}}, nil, nil}, - Piece{3, 3, Point{2, 3}, []Point{Point{0, 0}, Point{0, 1}, Point{1, 0}, Point{-1, 1}, Point{1, 0}}, nil, nil}, - - Piece{0, 4, Point{3, 2}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{-1, 1}, Point{1, 0}}, nil, nil}, - Piece{1, 4, Point{2, 3}, []Point{Point{1, 0}, Point{-1, 1}, Point{1, 0}, Point{-1, 1}, Point{1, 0}}, nil, nil}, - Piece{2, 4, Point{3, 2}, []Point{Point{0, 0}, Point{1, 0}, Point{-1, 1}, Point{1, 0}, Point{1, 0}}, nil, nil}, - Piece{3, 4, Point{2, 3}, []Point{Point{0, 0}, Point{1, 0}, Point{-1, 1}, Point{1, 0}, Point{-1, 1}}, nil, nil}, - - Piece{0, 7, Point{3, 2}, []Point{Point{0, 0}, Point{2, 0}, Point{-2, 1}, Point{1, 0}, Point{1, 0}}, nil, nil}, - Piece{1, 7, Point{2, 3}, []Point{Point{0, 0}, Point{1, 0}, Point{-1, 1}, Point{0, 1}, Point{1, 0}}, nil, nil}, - Piece{2, 7, Point{3, 2}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{-2, 1}, Point{2, 0}}, nil, nil}, - Piece{3, 7, Point{2, 3}, []Point{Point{0, 0}, Point{1, 0}, Point{0, 1}, Point{-1, 1}, Point{1, 0}}, nil, nil}, - - Piece{0, 5, Point{3, 3}, []Point{Point{0, 0}, Point{1, 0}, Point{0, 1}, Point{1, 0}, Point{-1, 1}}, nil, nil}, - Piece{1, 5, Point{3, 3}, []Point{Point{2, 0}, Point{-2, 1}, Point{1, 0}, Point{1, 0}, Point{-1, 1}}, nil, nil}, - Piece{2, 5, Point{3, 3}, []Point{Point{1, 0}, Point{-1, 1}, Point{1, 0}, Point{0, 1}, Point{1, 0}}, nil, nil}, - Piece{3, 5, Point{3, 3}, []Point{Point{1, 0}, Point{-1, 1}, Point{1, 0}, Point{1, 0}, Point{-2, 1}}, nil, nil}, - - Piece{0, 6, Point{3, 3}, []Point{Point{1, 0}, Point{1, 0}, Point{-2, 1}, Point{1, 0}, Point{0, 1}}, nil, nil}, - Piece{1, 6, Point{3, 3}, []Point{Point{1, 0}, Point{-1, 1}, Point{1, 0}, Point{1, 0}, Point{0, 1}}, nil, nil}, - Piece{2, 6, Point{3, 3}, []Point{Point{1, 0}, Point{0, 1}, Point{1, 0}, Point{-2, 1}, Point{1, 0}}, nil, nil}, - Piece{3, 6, Point{3, 3}, []Point{Point{0, 0}, Point{0, 1}, Point{1, 0}, Point{1, 0}, Point{-1, 1}}, nil, nil}, - - Piece{0, 0, Point{4, 2}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{1, 0}, Point{-2, 1}}, nil, nil}, - Piece{1, 0, Point{2, 4}, []Point{Point{1, 0}, Point{-1, 1}, Point{1, 0}, Point{0, 1}, Point{0, 1}}, nil, nil}, - Piece{2, 0, Point{4, 2}, []Point{Point{2, 0}, Point{-2, 1}, Point{1, 0}, Point{1, 0}, Point{1, 0}}, nil, nil}, - Piece{3, 0, Point{2, 4}, []Point{Point{0, 0}, Point{0, 1}, Point{0, 1}, Point{1, 0}, Point{-1, 1}}, nil, nil}, - - Piece{0, 2, Point{4, 2}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{1, 0}, Point{-1, 1}}, nil, nil}, - Piece{1, 2, Point{2, 4}, []Point{Point{1, 0}, Point{0, 1}, Point{-1, 1}, Point{1, 0}, Point{0, 1}}, nil, nil}, - Piece{2, 2, Point{4, 2}, []Point{Point{1, 0}, Point{-1, 1}, Point{1, 0}, Point{1, 0}, Point{1, 0}}, nil, nil}, - Piece{3, 2, Point{2, 4}, []Point{Point{0, 0}, Point{0, 1}, Point{1, 0}, Point{-1, 1}, Point{0, 1}}, nil, nil}, - - Piece{0, 1, Point{3, 3}, []Point{Point{0, 0}, Point{1, 0}, Point{0, 1}, Point{1, 0}, Point{0, 1}}, nil, nil}, - Piece{1, 1, Point{3, 3}, []Point{Point{2, 0}, Point{-1, 1}, Point{1, 0}, Point{-2, 1}, Point{1, 0}}, nil, nil}, - Piece{2, 1, Point{3, 3}, []Point{Point{0, 0}, Point{0, 1}, Point{1, 0}, Point{0, 1}, Point{1, 0}}, nil, nil}, - Piece{3, 1, Point{3, 3}, []Point{Point{1, 0}, Point{1, 0}, Point{-2, 1}, Point{1, 0}, Point{-1, 1}}, nil, nil}, - - Piece{0, 3, Point{3, 3}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{-1, 1}, Point{0, 1}}, nil, nil}, - Piece{1, 3, Point{3, 3}, []Point{Point{2, 0}, Point{-2, 1}, Point{1, 0}, Point{1, 0}, Point{0, 1}}, nil, nil}, - Piece{2, 3, Point{3, 3}, []Point{Point{1, 0}, Point{0, 1}, Point{-1, 1}, Point{1, 0}, Point{1, 0}}, nil, nil}, - Piece{3, 3, Point{3, 3}, []Point{Point{0, 0}, Point{0, 1}, Point{1, 0}, Point{1, 0}, Point{-2, 1}}, nil, nil}, - - Piece{0, 4, Point{3, 3}, []Point{Point{1, 0}, Point{-1, 1}, Point{1, 0}, Point{1, 0}, Point{-1, 1}}, nil, nil}, - Piece{1, 4, Point{3, 3}, []Point{Point{1, 0}, Point{-1, 1}, Point{1, 0}, Point{1, 0}, Point{-1, 1}}, nil, nil}, - Piece{2, 4, Point{3, 3}, []Point{Point{1, 0}, Point{-1, 1}, Point{1, 0}, Point{1, 0}, Point{-1, 1}}, nil, nil}, - Piece{3, 4, Point{3, 3}, []Point{Point{1, 0}, Point{-1, 1}, Point{1, 0}, Point{1, 0}, Point{-1, 1}}, nil, nil}, - - Piece{0, 8, Point{4, 2}, []Point{Point{0, 0}, Point{1, 0}, Point{0, 1}, Point{1, 0}, Point{1, 0}}, nil, nil}, - Piece{1, 8, Point{2, 4}, []Point{Point{1, 0}, Point{-1, 1}, Point{1, 0}, Point{-1, 1}, Point{0, 1}}, nil, nil}, - Piece{2, 8, Point{4, 2}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{0, 1}, Point{1, 0}}, nil, nil}, - Piece{3, 8, Point{2, 4}, []Point{Point{1, 0}, Point{0, 1}, Point{-1, 1}, Point{1, 0}, Point{-1, 1}}, nil, nil}, - - Piece{0, 9, Point{4, 2}, []Point{Point{2, 0}, Point{1, 0}, Point{-3, 1}, Point{1, 0}, Point{1, 0}}, nil, nil}, - Piece{1, 9, Point{2, 4}, []Point{Point{0, 0}, Point{0, 1}, Point{0, 1}, Point{1, 0}, Point{0, 1}}, nil, nil}, - Piece{2, 9, Point{4, 2}, []Point{Point{1, 0}, Point{1, 0}, Point{1, 0}, Point{-3, 1}, Point{1, 0}}, nil, nil}, - Piece{3, 9, Point{2, 4}, []Point{Point{0, 0}, Point{0, 1}, Point{1, 0}, Point{0, 1}, Point{0, 1}}, nil, nil}, - - Piece{0, 5, Point{3, 3}, []Point{Point{0, 0}, Point{0, 1}, Point{1, 0}, Point{1, 0}, Point{0, 1}}, nil, nil}, - Piece{1, 5, Point{3, 3}, []Point{Point{1, 0}, Point{1, 0}, Point{-1, 1}, Point{-1, 1}, Point{1, 0}}, nil, nil}, - Piece{2, 5, Point{3, 3}, []Point{Point{0, 0}, Point{0, 1}, Point{1, 0}, Point{1, 0}, Point{0, 1}}, nil, nil}, - Piece{3, 5, Point{3, 3}, []Point{Point{1, 0}, Point{1, 0}, Point{-1, 1}, Point{-1, 1}, Point{1, 0}}, nil, nil}, - - Piece{0, 6, Point{3, 3}, []Point{Point{2, 0}, Point{-2, 1}, Point{1, 0}, Point{1, 0}, Point{-2, 1}}, nil, nil}, - Piece{1, 6, Point{3, 3}, []Point{Point{0, 0}, Point{1, 0}, Point{0, 1}, Point{0, 1}, Point{1, 0}}, nil, nil}, - Piece{2, 6, Point{3, 3}, []Point{Point{2, 0}, Point{-2, 1}, Point{1, 0}, Point{1, 0}, Point{-2, 1}}, nil, nil}, - Piece{3, 6, Point{3, 3}, []Point{Point{0, 0}, Point{1, 0}, Point{0, 1}, Point{0, 1}, Point{1, 0}}, nil, nil}, -} diff --git a/src/pkg/exp/4s/xs.go b/src/pkg/exp/4s/xs.go deleted file mode 100644 index c5493e719..000000000 --- a/src/pkg/exp/4s/xs.go +++ /dev/null @@ -1,742 +0,0 @@ -// games/4s - a tetris clone -// -// Derived from Plan 9's /sys/src/games/xs.c -// http://plan9.bell-labs.com/sources/plan9/sys/src/games/xs.c -// -// Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights Reserved. -// Portions Copyright 2009 The Go Authors. All Rights Reserved. -// Distributed under the terms of the Lucent Public License Version 1.02 -// See http://plan9.bell-labs.com/plan9/license.html - -/* - * engine for 4s, 5s, etc - */ - -package main - -import ( - "exp/draw" - "image" - "log" - "os" - "rand" - "time" -) - -/* -Cursor whitearrow = { - {0, 0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, - 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC, - 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, - 0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, }, - {0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C, - 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x38, 0xC0, 0x1C, - 0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E, 0xDF, 0x1C, - 0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, } -}; -*/ - -const ( - CNone = 0 - CBounds = 1 - CPiece = 2 - NX = 10 - NY = 20 - - NCOL = 10 - - MAXN = 5 -) - -var ( - N int - display draw.Context - screen draw.Image - screenr draw.Rectangle - board [NY][NX]byte - rboard draw.Rectangle - pscore draw.Point - scoresz draw.Point - pcsz = 32 - pos draw.Point - bbr, bb2r draw.Rectangle - bb, bbmask, bb2, bb2mask *image.RGBA - whitemask image.Image - br, br2 draw.Rectangle - points int - dt int - DY int - DMOUSE int - lastmx int - mouse draw.Mouse - newscreen bool - timerc <-chan int64 - suspc chan bool - mousec chan draw.Mouse - resizec <-chan bool - kbdc chan int - suspended bool - tsleep int - piece *Piece - pieces []Piece -) - -type Piece struct { - rot int - tx int - sz draw.Point - d []draw.Point - left *Piece - right *Piece -} - -var txbits = [NCOL][32]byte{ - [32]byte{0xDD, 0xDD, 0xFF, 0xFF, 0x77, 0x77, 0xFF, 0xFF, - 0xDD, 0xDD, 0xFF, 0xFF, 0x77, 0x77, 0xFF, 0xFF, - 0xDD, 0xDD, 0xFF, 0xFF, 0x77, 0x77, 0xFF, 0xFF, - 0xDD, 0xDD, 0xFF, 0xFF, 0x77, 0x77, 0xFF, 0xFF, - }, - [32]byte{0xDD, 0xDD, 0x77, 0x77, 0xDD, 0xDD, 0x77, 0x77, - 0xDD, 0xDD, 0x77, 0x77, 0xDD, 0xDD, 0x77, 0x77, - 0xDD, 0xDD, 0x77, 0x77, 0xDD, 0xDD, 0x77, 0x77, - 0xDD, 0xDD, 0x77, 0x77, 0xDD, 0xDD, 0x77, 0x77, - }, - [32]byte{0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, - 0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, - 0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, - 0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, - }, - [32]byte{0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, - 0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, - 0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, - 0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, - }, - [32]byte{0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88, - 0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88, - 0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88, - 0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88, - }, - [32]byte{0x22, 0x22, 0x00, 0x00, 0x88, 0x88, 0x00, 0x00, - 0x22, 0x22, 0x00, 0x00, 0x88, 0x88, 0x00, 0x00, - 0x22, 0x22, 0x00, 0x00, 0x88, 0x88, 0x00, 0x00, - 0x22, 0x22, 0x00, 0x00, 0x88, 0x88, 0x00, 0x00, - }, - [32]byte{0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - }, - [32]byte{0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - }, - [32]byte{0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, - 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, - 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, - 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, - }, - [32]byte{0xCC, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0x33, 0x33, - 0xCC, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0x33, 0x33, - 0xCC, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0x33, 0x33, - 0xCC, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0x33, 0x33, - }, -} - -var txpix = [NCOL]draw.Color{ - draw.Yellow, /* yellow */ - draw.Cyan, /* cyan */ - draw.Green, /* lime green */ - draw.GreyBlue, /* slate */ - draw.Red, /* red */ - draw.GreyGreen, /* olive green */ - draw.Blue, /* blue */ - draw.Color(0xFF55AAFF), /* pink */ - draw.Color(0xFFAAFFFF), /* lavender */ - draw.Color(0xBB005DFF), /* maroon */ -} - -func movemouse() int { - //mouse.draw.Point = draw.Pt(rboard.Min.X + rboard.Dx()/2, rboard.Min.Y + rboard.Dy()/2); - //moveto(mousectl, mouse.Xy); - return mouse.X -} - -func warp(p draw.Point, x int) int { - if !suspended && piece != nil { - x = pos.X + piece.sz.X*pcsz/2 - if p.Y < rboard.Min.Y { - p.Y = rboard.Min.Y - } - if p.Y >= rboard.Max.Y { - p.Y = rboard.Max.Y - 1 - } - //moveto(mousectl, draw.Pt(x, p.Y)); - } - return x -} - -func initPieces() { - for i := range pieces { - p := &pieces[i] - if p.rot == 3 { - p.right = &pieces[i-3] - } else { - p.right = &pieces[i+1] - } - if p.rot == 0 { - p.left = &pieces[i+3] - } else { - p.left = &pieces[i-1] - } - } -} - -func collide(pt draw.Point, p *Piece) bool { - pt.X = (pt.X - rboard.Min.X) / pcsz - pt.Y = (pt.Y - rboard.Min.Y) / pcsz - for _, q := range p.d { - pt.X += q.X - pt.Y += q.Y - if pt.X < 0 || pt.X >= NX || pt.Y < 0 || pt.Y >= NY { - return true - continue - } - if board[pt.Y][pt.X] != 0 { - return true - } - } - return false -} - -func collider(pt, pmax draw.Point) bool { - pi := (pt.X - rboard.Min.X) / pcsz - pj := (pt.Y - rboard.Min.Y) / pcsz - n := pmax.X / pcsz - m := pmax.Y/pcsz + 1 - for i := pi; i < pi+n && i < NX; i++ { - for j := pj; j < pj+m && j < NY; j++ { - if board[j][i] != 0 { - return true - } - } - } - return false -} - -func setpiece(p *Piece) { - draw.Draw(bb, bbr, draw.White, draw.ZP) - draw.Draw(bbmask, bbr, draw.Transparent, draw.ZP) - br = draw.Rect(0, 0, 0, 0) - br2 = br - piece = p - if p == nil { - return - } - var op draw.Point - var r draw.Rectangle - r.Min = bbr.Min - for i, pt := range p.d { - r.Min.X += pt.X * pcsz - r.Min.Y += pt.Y * pcsz - r.Max.X = r.Min.X + pcsz - r.Max.Y = r.Min.Y + pcsz - if i == 0 { - draw.Draw(bb, r, draw.Black, draw.ZP) - draw.Draw(bb, r.Inset(1), txpix[piece.tx], draw.ZP) - draw.Draw(bbmask, r, draw.Opaque, draw.ZP) - op = r.Min - } else { - draw.Draw(bb, r, bb, op) - draw.Draw(bbmask, r, bbmask, op) - } - if br.Max.X < r.Max.X { - br.Max.X = r.Max.X - } - if br.Max.Y < r.Max.Y { - br.Max.Y = r.Max.Y - } - } - br.Max = br.Max.Sub(bbr.Min) - delta := draw.Pt(0, DY) - br2.Max = br.Max.Add(delta) - r = br.Add(bb2r.Min) - r2 := br2.Add(bb2r.Min) - draw.Draw(bb2, r2, draw.White, draw.ZP) - draw.Draw(bb2, r.Add(delta), bb, bbr.Min) - draw.Draw(bb2mask, r2, draw.Transparent, draw.ZP) - draw.DrawMask(bb2mask, r, draw.Opaque, bbr.Min, bbmask, draw.ZP, draw.Over) - draw.DrawMask(bb2mask, r.Add(delta), draw.Opaque, bbr.Min, bbmask, draw.ZP, draw.Over) -} - -func drawpiece() { - draw.DrawMask(screen, br.Add(pos), bb, bbr.Min, bbmask, draw.ZP, draw.Over) - if suspended { - draw.DrawMask(screen, br.Add(pos), draw.White, draw.ZP, whitemask, draw.ZP, draw.Over) - } -} - -func undrawpiece() { - var mask image.Image - if collider(pos, br.Max) { - mask = bbmask - } - draw.DrawMask(screen, br.Add(pos), draw.White, bbr.Min, mask, bbr.Min, draw.Over) -} - -func rest() { - pt := pos.Sub(rboard.Min).Div(pcsz) - for _, p := range piece.d { - pt.X += p.X - pt.Y += p.Y - board[pt.Y][pt.X] = byte(piece.tx + 16) - } -} - -func canfit(p *Piece) bool { - var dx = [...]int{0, -1, 1, -2, 2, -3, 3, 4, -4} - j := N + 1 - if j >= 4 { - j = p.sz.X - if j < p.sz.Y { - j = p.sz.Y - } - j = 2*j - 1 - } - for i := 0; i < j; i++ { - var z draw.Point - z.X = pos.X + dx[i]*pcsz - z.Y = pos.Y - if !collide(z, p) { - z.Y = pos.Y + pcsz - 1 - if !collide(z, p) { - undrawpiece() - pos.X = z.X - return true - } - } - } - return false -} - -func score(p int) { - points += p - // snprint(buf, sizeof(buf), "%.6ld", points); - // draw.Draw(screen, draw.Rpt(pscore, pscore.Add(scoresz)), draw.White, draw.ZP); - // string(screen, pscore, draw.Black, draw.ZP, font, buf); -} - -func drawsq(b draw.Image, p draw.Point, ptx int) { - var r draw.Rectangle - r.Min = p - r.Max.X = r.Min.X + pcsz - r.Max.Y = r.Min.Y + pcsz - draw.Draw(b, r, draw.Black, draw.ZP) - draw.Draw(b, r.Inset(1), txpix[ptx], draw.ZP) -} - -func drawboard() { - draw.Border(screen, rboard.Inset(-2), 2, draw.Black, draw.ZP) - draw.Draw(screen, draw.Rect(rboard.Min.X, rboard.Min.Y-2, rboard.Max.X, rboard.Min.Y), - draw.White, draw.ZP) - for i := 0; i < NY; i++ { - for j := 0; j < NX; j++ { - if board[i][j] != 0 { - drawsq(screen, draw.Pt(rboard.Min.X+j*pcsz, rboard.Min.Y+i*pcsz), int(board[i][j]-16)) - } - } - } - score(0) - if suspended { - draw.DrawMask(screen, screenr, draw.White, draw.ZP, whitemask, draw.ZP, draw.Over) - } -} - -func choosepiece() { - for { - i := rand.Intn(len(pieces)) - setpiece(&pieces[i]) - pos = rboard.Min - pos.X += rand.Intn(NX) * pcsz - if !collide(draw.Pt(pos.X, pos.Y+pcsz-DY), piece) { - break - } - } - drawpiece() - display.FlushImage() -} - -func movepiece() bool { - var mask image.Image - if collide(draw.Pt(pos.X, pos.Y+pcsz), piece) { - return false - } - if collider(pos, br2.Max) { - mask = bb2mask - } - draw.DrawMask(screen, br2.Add(pos), bb2, bb2r.Min, mask, bb2r.Min, draw.Over) - pos.Y += DY - display.FlushImage() - return true -} - -func suspend(s bool) { - suspended = s - /* - if suspended { - setcursor(mousectl, &whitearrow); - } else { - setcursor(mousectl, nil); - } - */ - if !suspended { - drawpiece() - } - drawboard() - display.FlushImage() -} - -func pause(t int) { - display.FlushImage() - for { - select { - case s := <-suspc: - if !suspended && s { - suspend(true) - } else if suspended && !s { - suspend(false) - lastmx = warp(mouse.Point, lastmx) - } - case <-timerc: - if suspended { - break - } - t -= tsleep - if t < 0 { - return - } - case <-resizec: - //redraw(true); - case mouse = <-mousec: - case <-kbdc: - } - } -} - -func horiz() bool { - var lev [MAXN]int - h := 0 - for i := 0; i < NY; i++ { - for j := 0; board[i][j] != 0; j++ { - if j == NX-1 { - lev[h] = i - h++ - break - } - } - } - if h == 0 { - return false - } - r := rboard - newscreen = false - for j := 0; j < h; j++ { - r.Min.Y = rboard.Min.Y + lev[j]*pcsz - r.Max.Y = r.Min.Y + pcsz - draw.DrawMask(screen, r, draw.White, draw.ZP, whitemask, draw.ZP, draw.Over) - display.FlushImage() - } - PlaySound(whoosh) - for i := 0; i < 3; i++ { - pause(250) - if newscreen { - drawboard() - break - } - for j := 0; j < h; j++ { - r.Min.Y = rboard.Min.Y + lev[j]*pcsz - r.Max.Y = r.Min.Y + pcsz - draw.DrawMask(screen, r, draw.White, draw.ZP, whitemask, draw.ZP, draw.Over) - } - display.FlushImage() - } - r = rboard - for j := 0; j < h; j++ { - i := NY - lev[j] - 1 - score(250 + 10*i*i) - r.Min.Y = rboard.Min.Y - r.Max.Y = rboard.Min.Y + lev[j]*pcsz - draw.Draw(screen, r.Add(draw.Pt(0, pcsz)), screen, r.Min) - r.Max.Y = rboard.Min.Y + pcsz - draw.Draw(screen, r, draw.White, draw.ZP) - for k := lev[j] - 1; k >= 0; k-- { - board[k+1] = board[k] - } - board[0] = [NX]byte{} - } - display.FlushImage() - return true -} - -func mright() { - if !collide(draw.Pt(pos.X+pcsz, pos.Y), piece) && - !collide(draw.Pt(pos.X+pcsz, pos.Y+pcsz-DY), piece) { - undrawpiece() - pos.X += pcsz - drawpiece() - display.FlushImage() - } -} - -func mleft() { - if !collide(draw.Pt(pos.X-pcsz, pos.Y), piece) && - !collide(draw.Pt(pos.X-pcsz, pos.Y+pcsz-DY), piece) { - undrawpiece() - pos.X -= pcsz - drawpiece() - display.FlushImage() - } -} - -func rright() { - if canfit(piece.right) { - setpiece(piece.right) - drawpiece() - display.FlushImage() - } -} - -func rleft() { - if canfit(piece.left) { - setpiece(piece.left) - drawpiece() - display.FlushImage() - } -} - -var fusst = 0 - -func drop(f bool) bool { - if f { - score(5 * (rboard.Max.Y - pos.Y) / pcsz) - for movepiece() { - } - } - fusst = 0 - rest() - if pos.Y == rboard.Min.Y && !horiz() { - return true - } - horiz() - setpiece(nil) - pause(1500) - choosepiece() - lastmx = warp(mouse.Point, lastmx) - return false -} - -func play() { - var om draw.Mouse - dt = 64 - lastmx = -1 - lastmx = movemouse() - choosepiece() - lastmx = warp(mouse.Point, lastmx) - for { - select { - case mouse = <-mousec: - if suspended { - om = mouse - break - } - if lastmx < 0 { - lastmx = mouse.X - } - if mouse.X > lastmx+DMOUSE { - mright() - lastmx = mouse.X - } - if mouse.X < lastmx-DMOUSE { - mleft() - lastmx = mouse.X - } - if mouse.Buttons&^om.Buttons&1 == 1 { - rleft() - } - if mouse.Buttons&^om.Buttons&2 == 2 { - if drop(true) { - return - } - } - if mouse.Buttons&^om.Buttons&4 == 4 { - rright() - } - om = mouse - - case s := <-suspc: - if !suspended && s { - suspend(true) - } else if suspended && !s { - suspend(false) - lastmx = warp(mouse.Point, lastmx) - } - - case <-resizec: - //redraw(true); - - case r := <-kbdc: - if suspended { - break - } - switch r { - case 'f', ';': - mright() - case 'a', 'j': - mleft() - case 'd', 'l': - rright() - case 's', 'k': - rleft() - case ' ': - if drop(true) { - return - } - } - - case <-timerc: - if suspended { - break - } - dt -= tsleep - if dt < 0 { - i := 1 - dt = 16 * (points + rand.Intn(10000) - 5000) / 10000 - if dt >= 32 { - i += (dt - 32) / 16 - dt = 32 - } - dt = 52 - dt - for ; i > 0; i-- { - if movepiece() { - continue - } - fusst++ - if fusst == 40 { - if drop(false) { - return - } - break - } - } - } - } - } -} - -func suspproc() { - mc := display.MouseChan() - kc := display.KeyboardChan() - - s := false - for { - select { - case mouse = <-mc: - mousec <- mouse - case r := <-kc: - switch r { - case 'q', 'Q', 0x04, 0x7F: - os.Exit(0) - default: - if s { - s = false - suspc <- s - break - } - switch r { - case 'z', 'Z', 'p', 'P', 0x1B: - s = true - suspc <- s - default: - kbdc <- r - } - } - } - } -} - -func redraw(new bool) { - // if new && getwindow(display, Refmesg) < 0 { - // sysfatal("can't reattach to window"); - // } - r := draw.Rect(0, 0, screen.Width(), screen.Height()) - pos.X = (pos.X - rboard.Min.X) / pcsz - pos.Y = (pos.Y - rboard.Min.Y) / pcsz - dx := r.Max.X - r.Min.X - dy := r.Max.Y - r.Min.Y - 2*32 - DY = dx / NX - if DY > dy/NY { - DY = dy / NY - } - DY /= 8 - if DY > 4 { - DY = 4 - } - pcsz = DY * 8 - DMOUSE = pcsz / 3 - if pcsz < 8 { - log.Exitf("screen too small: %d", pcsz) - } - rboard = screenr - rboard.Min.X += (dx - pcsz*NX) / 2 - rboard.Min.Y += (dy-pcsz*NY)/2 + 32 - rboard.Max.X = rboard.Min.X + NX*pcsz - rboard.Max.Y = rboard.Min.Y + NY*pcsz - pscore.X = rboard.Min.X + 8 - pscore.Y = rboard.Min.Y - 32 - // scoresz = stringsize(font, "000000"); - pos.X = pos.X*pcsz + rboard.Min.X - pos.Y = pos.Y*pcsz + rboard.Min.Y - bbr = draw.Rect(0, 0, N*pcsz, N*pcsz) - bb = image.NewRGBA(bbr.Max.X, bbr.Max.Y) - bbmask = image.NewRGBA(bbr.Max.X, bbr.Max.Y) // actually just a bitmap - bb2r = draw.Rect(0, 0, N*pcsz, N*pcsz+DY) - bb2 = image.NewRGBA(bb2r.Dx(), bb2r.Dy()) - bb2mask = image.NewRGBA(bb2r.Dx(), bb2r.Dy()) // actually just a bitmap - draw.Draw(screen, screenr, draw.White, draw.ZP) - drawboard() - setpiece(piece) - if piece != nil { - drawpiece() - } - lastmx = movemouse() - newscreen = true - display.FlushImage() -} - -func quitter(c <-chan bool) { - <-c - os.Exit(0) -} - -func Play(pp []Piece, ctxt draw.Context) { - display = ctxt - screen = ctxt.Screen() - screenr = draw.Rect(0, 0, screen.Width(), screen.Height()) - pieces = pp - N = len(pieces[0].d) - initPieces() - rand.Seed(int64(time.Nanoseconds() % (1e9 - 1))) - whitemask = draw.White.SetAlpha(0x7F) - tsleep = 50 - timerc = time.Tick(int64(tsleep/2) * 1e6) - suspc = make(chan bool) - mousec = make(chan draw.Mouse) - resizec = ctxt.ResizeChan() - kbdc = make(chan int) - go quitter(ctxt.QuitChan()) - go suspproc() - points = 0 - redraw(false) - play() -} diff --git a/src/pkg/exp/bignum/Makefile b/src/pkg/exp/bignum/Makefile deleted file mode 100644 index 064cf1eb9..000000000 --- a/src/pkg/exp/bignum/Makefile +++ /dev/null @@ -1,14 +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.$(GOARCH) - -TARG=exp/bignum -GOFILES=\ - arith.go\ - bignum.go\ - integer.go\ - rational.go\ - -include ../../../Make.pkg diff --git a/src/pkg/exp/bignum/arith.go b/src/pkg/exp/bignum/arith.go deleted file mode 100644 index aa65dbd7a..000000000 --- a/src/pkg/exp/bignum/arith.go +++ /dev/null @@ -1,121 +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. - -// Fast versions of the routines in this file are in fast.arith.s. -// Simply replace this file with arith.s (renamed from fast.arith.s) -// and the bignum package will build and run on a platform that -// supports the assembly routines. - -package bignum - -import "unsafe" - -// z1<<64 + z0 = x*y -func Mul128(x, y uint64) (z1, z0 uint64) { - // Split x and y into 2 halfwords each, multiply - // the halfwords separately while avoiding overflow, - // and return the product as 2 words. - - const ( - W = uint(unsafe.Sizeof(x)) * 8 - W2 = W / 2 - B2 = 1 << W2 - M2 = B2 - 1 - ) - - if x < y { - x, y = y, x - } - - if x < B2 { - // y < B2 because y <= x - // sub-digits of x and y are (0, x) and (0, y) - // z = z[0] = x*y - z0 = x * y - return - } - - if y < B2 { - // sub-digits of x and y are (x1, x0) and (0, y) - // x = (x1*B2 + x0) - // y = (y1*B2 + y0) - x1, x0 := x>>W2, x&M2 - - // x*y = t2*B2*B2 + t1*B2 + t0 - t0 := x0 * y - t1 := x1 * y - - // compute result digits but avoid overflow - // z = z[1]*B + z[0] = x*y - z0 = t1<>W2) >> W2 - return - } - - // general case - // sub-digits of x and y are (x1, x0) and (y1, y0) - // x = (x1*B2 + x0) - // y = (y1*B2 + y0) - x1, x0 := x>>W2, x&M2 - y1, y0 := y>>W2, y&M2 - - // x*y = t2*B2*B2 + t1*B2 + t0 - t0 := x0 * y0 - t1 := x1*y0 + x0*y1 - t2 := x1 * y1 - - // compute result digits but avoid overflow - // z = z[1]*B + z[0] = x*y - z0 = t1<>W2)>>W2 - return -} - - -// z1<<64 + z0 = x*y + c -func MulAdd128(x, y, c uint64) (z1, z0 uint64) { - // Split x and y into 2 halfwords each, multiply - // the halfwords separately while avoiding overflow, - // and return the product as 2 words. - - const ( - W = uint(unsafe.Sizeof(x)) * 8 - W2 = W / 2 - B2 = 1 << W2 - M2 = B2 - 1 - ) - - // TODO(gri) Should implement special cases for faster execution. - - // general case - // sub-digits of x, y, and c are (x1, x0), (y1, y0), (c1, c0) - // x = (x1*B2 + x0) - // y = (y1*B2 + y0) - x1, x0 := x>>W2, x&M2 - y1, y0 := y>>W2, y&M2 - c1, c0 := c>>W2, c&M2 - - // x*y + c = t2*B2*B2 + t1*B2 + t0 - t0 := x0*y0 + c0 - t1 := x1*y0 + x0*y1 + c1 - t2 := x1 * y1 - - // compute result digits but avoid overflow - // z = z[1]*B + z[0] = x*y - z0 = t1<>W2)>>W2 - return -} - - -// q = (x1<<64 + x0)/y + r -func Div128(x1, x0, y uint64) (q, r uint64) { - if x1 == 0 { - q, r = x0/y, x0%y - return - } - - // TODO(gri) Implement general case. - panic("Div128 not implemented for x > 1<<64-1") -} diff --git a/src/pkg/exp/bignum/arith_amd64.s b/src/pkg/exp/bignum/arith_amd64.s deleted file mode 100644 index 37d5a30de..000000000 --- a/src/pkg/exp/bignum/arith_amd64.s +++ /dev/null @@ -1,41 +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 provides fast assembly versions -// of the routines in arith.go. - -// func Mul128(x, y uint64) (z1, z0 uint64) -// z1<<64 + z0 = x*y -// -TEXT ·Mul128(SB),7,$0 - MOVQ a+0(FP), AX - MULQ a+8(FP) - MOVQ DX, a+16(FP) - MOVQ AX, a+24(FP) - RET - - -// func MulAdd128(x, y, c uint64) (z1, z0 uint64) -// z1<<64 + z0 = x*y + c -// -TEXT ·MulAdd128(SB),7,$0 - MOVQ a+0(FP), AX - MULQ a+8(FP) - ADDQ a+16(FP), AX - ADCQ $0, DX - MOVQ DX, a+24(FP) - MOVQ AX, a+32(FP) - RET - - -// func Div128(x1, x0, y uint64) (q, r uint64) -// q = (x1<<64 + x0)/y + r -// -TEXT ·Div128(SB),7,$0 - MOVQ a+0(FP), DX - MOVQ a+8(FP), AX - DIVQ a+16(FP) - MOVQ AX, a+24(FP) - MOVQ DX, a+32(FP) - RET diff --git a/src/pkg/exp/bignum/bignum.go b/src/pkg/exp/bignum/bignum.go deleted file mode 100644 index 485583199..000000000 --- a/src/pkg/exp/bignum/bignum.go +++ /dev/null @@ -1,1024 +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. - -// A package for arbitrary precision arithmethic. -// It implements the following numeric types: -// -// - Natural unsigned integers -// - Integer signed integers -// - Rational rational numbers -// -// This package has been designed for ease of use but the functions it provides -// are likely to be quite slow. It may be deprecated eventually. Use package -// big instead, if possible. -// -package bignum - -import ( - "fmt" -) - -// TODO(gri) Complete the set of in-place operations. - -// ---------------------------------------------------------------------------- -// Internal representation -// -// A natural number of the form -// -// x = x[n-1]*B^(n-1) + x[n-2]*B^(n-2) + ... + x[1]*B + x[0] -// -// with 0 <= x[i] < B and 0 <= i < n is stored in a slice of length n, -// with the digits x[i] as the slice elements. -// -// A natural number is normalized if the slice contains no leading 0 digits. -// During arithmetic operations, denormalized values may occur but are -// always normalized before returning the final result. The normalized -// representation of 0 is the empty slice (length = 0). -// -// The operations for all other numeric types are implemented on top of -// the operations for natural numbers. -// -// The base B is chosen as large as possible on a given platform but there -// are a few constraints besides the size of the largest unsigned integer -// type available: -// -// 1) To improve conversion speed between strings and numbers, the base B -// is chosen such that division and multiplication by 10 (for decimal -// string representation) can be done without using extended-precision -// arithmetic. This makes addition, subtraction, and conversion routines -// twice as fast. It requires a ``buffer'' of 4 bits per operand digit. -// That is, the size of B must be 4 bits smaller then the size of the -// type (digit) in which these operations are performed. Having this -// buffer also allows for trivial (single-bit) carry computation in -// addition and subtraction (optimization suggested by Ken Thompson). -// -// 2) Long division requires extended-precision (2-digit) division per digit. -// Instead of sacrificing the largest base type for all other operations, -// for division the operands are unpacked into ``half-digits'', and the -// results are packed again. For faster unpacking/packing, the base size -// in bits must be even. - -type ( - digit uint64 - digit2 uint32 // half-digits for division -) - - -const ( - logW = 64 // word width - logH = 4 // bits for a hex digit (= small number) - logB = logW - logH // largest bit-width available - - // half-digits - _W2 = logB / 2 // width - _B2 = 1 << _W2 // base - _M2 = _B2 - 1 // mask - - // full digits - _W = _W2 * 2 // width - _B = 1 << _W // base - _M = _B - 1 // mask -) - - -// ---------------------------------------------------------------------------- -// Support functions - -func assert(p bool) { - if !p { - panic("assert failed") - } -} - - -func isSmall(x digit) bool { return x < 1<= 0; i-- { - print(" ", x[i]); - } - println(); -} -*/ - - -// ---------------------------------------------------------------------------- -// Natural numbers - -// Natural represents an unsigned integer value of arbitrary precision. -// -type Natural []digit - - -// Nat creates a small natural number with value x. -// -func Nat(x uint64) Natural { - if x == 0 { - return nil // len == 0 - } - - // single-digit values - // (note: cannot re-use preallocated values because - // the in-place operations may overwrite them) - if x < _B { - return Natural{digit(x)} - } - - // compute number of digits required to represent x - // (this is usually 1 or 2, but the algorithm works - // for any base) - n := 0 - for t := x; t > 0; t >>= _W { - n++ - } - - // split x into digits - z := make(Natural, n) - for i := 0; i < n; i++ { - z[i] = digit(x & _M) - x >>= _W - } - - return z -} - - -// Value returns the lowest 64bits of x. -// -func (x Natural) Value() uint64 { - // single-digit values - n := len(x) - switch n { - case 0: - return 0 - case 1: - return uint64(x[0]) - } - - // multi-digit values - // (this is usually 1 or 2, but the algorithm works - // for any base) - z := uint64(0) - s := uint(0) - for i := 0; i < n && s < 64; i++ { - z += uint64(x[i]) << s - s += _W - } - - return z -} - - -// Predicates - -// IsEven returns true iff x is divisible by 2. -// -func (x Natural) IsEven() bool { return len(x) == 0 || x[0]&1 == 0 } - - -// IsOdd returns true iff x is not divisible by 2. -// -func (x Natural) IsOdd() bool { return len(x) > 0 && x[0]&1 != 0 } - - -// IsZero returns true iff x == 0. -// -func (x Natural) IsZero() bool { return len(x) == 0 } - - -// Operations -// -// Naming conventions -// -// c carry -// x, y operands -// z result -// n, m len(x), len(y) - -func normalize(x Natural) Natural { - n := len(x) - for n > 0 && x[n-1] == 0 { - n-- - } - return x[0:n] -} - - -// nalloc returns a Natural of n digits. If z is large -// enough, z is resized and returned. Otherwise, a new -// Natural is allocated. -// -func nalloc(z Natural, n int) Natural { - size := n - if size <= 0 { - size = 4 - } - if size <= cap(z) { - return z[0:n] - } - return make(Natural, n, size) -} - - -// Nadd sets *zp to the sum x + y. -// *zp may be x or y. -// -func Nadd(zp *Natural, x, y Natural) { - n := len(x) - m := len(y) - if n < m { - Nadd(zp, y, x) - return - } - - z := nalloc(*zp, n+1) - c := digit(0) - i := 0 - for i < m { - t := c + x[i] + y[i] - c, z[i] = t>>_W, t&_M - i++ - } - for i < n { - t := c + x[i] - c, z[i] = t>>_W, t&_M - i++ - } - if c != 0 { - z[i] = c - i++ - } - *zp = z[0:i] -} - - -// Add returns the sum z = x + y. -// -func (x Natural) Add(y Natural) Natural { - var z Natural - Nadd(&z, x, y) - return z -} - - -// Nsub sets *zp to the difference x - y for x >= y. -// If x < y, an underflow run-time error occurs (use Cmp to test if x >= y). -// *zp may be x or y. -// -func Nsub(zp *Natural, x, y Natural) { - n := len(x) - m := len(y) - if n < m { - panic("underflow") - } - - z := nalloc(*zp, n) - c := digit(0) - i := 0 - for i < m { - t := c + x[i] - y[i] - c, z[i] = digit(int64(t)>>_W), t&_M // requires arithmetic shift! - i++ - } - for i < n { - t := c + x[i] - c, z[i] = digit(int64(t)>>_W), t&_M // requires arithmetic shift! - i++ - } - if int64(c) < 0 { - panic("underflow") - } - *zp = normalize(z) -} - - -// Sub returns the difference x - y for x >= y. -// If x < y, an underflow run-time error occurs (use Cmp to test if x >= y). -// -func (x Natural) Sub(y Natural) Natural { - var z Natural - Nsub(&z, x, y) - return z -} - - -// Returns z1 = (x*y + c) div B, z0 = (x*y + c) mod B. -// -func muladd11(x, y, c digit) (digit, digit) { - z1, z0 := MulAdd128(uint64(x), uint64(y), uint64(c)) - return digit(z1<<(64-logB) | z0>>logB), digit(z0 & _M) -} - - -func mul1(z, x Natural, y digit) (c digit) { - for i := 0; i < len(x); i++ { - c, z[i] = muladd11(x[i], y, c) - } - return -} - - -// Nscale sets *z to the scaled value (*z) * d. -// -func Nscale(z *Natural, d uint64) { - switch { - case d == 0: - *z = Nat(0) - return - case d == 1: - return - case d >= _B: - *z = z.Mul1(d) - return - } - - c := mul1(*z, *z, digit(d)) - - if c != 0 { - n := len(*z) - if n >= cap(*z) { - zz := make(Natural, n+1) - for i, d := range *z { - zz[i] = d - } - *z = zz - } else { - *z = (*z)[0 : n+1] - } - (*z)[n] = c - } -} - - -// Computes x = x*d + c for small d's. -// -func muladd1(x Natural, d, c digit) Natural { - assert(isSmall(d-1) && isSmall(c)) - n := len(x) - z := make(Natural, n+1) - - for i := 0; i < n; i++ { - t := c + x[i]*d - c, z[i] = t>>_W, t&_M - } - z[n] = c - - return normalize(z) -} - - -// Mul1 returns the product x * d. -// -func (x Natural) Mul1(d uint64) Natural { - switch { - case d == 0: - return Nat(0) - case d == 1: - return x - case isSmall(digit(d)): - muladd1(x, digit(d), 0) - case d >= _B: - return x.Mul(Nat(d)) - } - - z := make(Natural, len(x)+1) - c := mul1(z, x, digit(d)) - z[len(x)] = c - return normalize(z) -} - - -// Mul returns the product x * y. -// -func (x Natural) Mul(y Natural) Natural { - n := len(x) - m := len(y) - if n < m { - return y.Mul(x) - } - - if m == 0 { - return Nat(0) - } - - if m == 1 && y[0] < _B { - return x.Mul1(uint64(y[0])) - } - - z := make(Natural, n+m) - for j := 0; j < m; j++ { - d := y[j] - if d != 0 { - c := digit(0) - for i := 0; i < n; i++ { - c, z[i+j] = muladd11(x[i], d, z[i+j]+c) - } - z[n+j] = c - } - } - - return normalize(z) -} - - -// DivMod needs multi-precision division, which is not available if digit -// is already using the largest uint size. Instead, unpack each operand -// into operands with twice as many digits of half the size (digit2), do -// DivMod, and then pack the results again. - -func unpack(x Natural) []digit2 { - n := len(x) - z := make([]digit2, n*2+1) // add space for extra digit (used by DivMod) - for i := 0; i < n; i++ { - t := x[i] - z[i*2] = digit2(t & _M2) - z[i*2+1] = digit2(t >> _W2 & _M2) - } - - // normalize result - k := 2 * n - for k > 0 && z[k-1] == 0 { - k-- - } - return z[0:k] // trim leading 0's -} - - -func pack(x []digit2) Natural { - n := (len(x) + 1) / 2 - z := make(Natural, n) - if len(x)&1 == 1 { - // handle odd len(x) - n-- - z[n] = digit(x[n*2]) - } - for i := 0; i < n; i++ { - z[i] = digit(x[i*2+1])<<_W2 | digit(x[i*2]) - } - return normalize(z) -} - - -func mul21(z, x []digit2, y digit2) digit2 { - c := digit(0) - f := digit(y) - for i := 0; i < len(x); i++ { - t := c + digit(x[i])*f - c, z[i] = t>>_W2, digit2(t&_M2) - } - return digit2(c) -} - - -func div21(z, x []digit2, y digit2) digit2 { - c := digit(0) - d := digit(y) - for i := len(x) - 1; i >= 0; i-- { - t := c<<_W2 + digit(x[i]) - c, z[i] = t%d, digit2(t/d) - } - return digit2(c) -} - - -// divmod returns q and r with x = y*q + r and 0 <= r < y. -// x and y are destroyed in the process. -// -// The algorithm used here is based on 1). 2) describes the same algorithm -// in C. A discussion and summary of the relevant theorems can be found in -// 3). 3) also describes an easier way to obtain the trial digit - however -// it relies on tripple-precision arithmetic which is why Knuth's method is -// used here. -// -// 1) D. Knuth, The Art of Computer Programming. Volume 2. Seminumerical -// Algorithms. Addison-Wesley, Reading, 1969. -// (Algorithm D, Sec. 4.3.1) -// -// 2) Henry S. Warren, Jr., Hacker's Delight. Addison-Wesley, 2003. -// (9-2 Multiword Division, p.140ff) -// -// 3) P. Brinch Hansen, ``Multiple-length division revisited: A tour of the -// minefield''. Software - Practice and Experience 24, (June 1994), -// 579-601. John Wiley & Sons, Ltd. - -func divmod(x, y []digit2) ([]digit2, []digit2) { - n := len(x) - m := len(y) - if m == 0 { - panic("division by zero") - } - assert(n+1 <= cap(x)) // space for one extra digit - x = x[0 : n+1] - assert(x[n] == 0) - - if m == 1 { - // division by single digit - // result is shifted left by 1 in place! - x[0] = div21(x[1:n+1], x[0:n], y[0]) - - } else if m > n { - // y > x => quotient = 0, remainder = x - // TODO in this case we shouldn't even unpack x and y - m = n - - } else { - // general case - assert(2 <= m && m <= n) - - // normalize x and y - // TODO Instead of multiplying, it would be sufficient to - // shift y such that the normalization condition is - // satisfied (as done in Hacker's Delight). - f := _B2 / (digit(y[m-1]) + 1) - if f != 1 { - mul21(x, x, digit2(f)) - mul21(y, y, digit2(f)) - } - assert(_B2/2 <= y[m-1] && y[m-1] < _B2) // incorrect scaling - - y1, y2 := digit(y[m-1]), digit(y[m-2]) - for i := n - m; i >= 0; i-- { - k := i + m - - // compute trial digit (Knuth) - var q digit - { - x0, x1, x2 := digit(x[k]), digit(x[k-1]), digit(x[k-2]) - if x0 != y1 { - q = (x0<<_W2 + x1) / y1 - } else { - q = _B2 - 1 - } - for y2*q > (x0<<_W2+x1-y1*q)<<_W2+x2 { - q-- - } - } - - // subtract y*q - c := digit(0) - for j := 0; j < m; j++ { - t := c + digit(x[i+j]) - digit(y[j])*q - c, x[i+j] = digit(int64(t)>>_W2), digit2(t&_M2) // requires arithmetic shift! - } - x[k] = digit2((c + digit(x[k])) & _M2) - - // correct if trial digit was too large - if x[k] != 0 { - // add y - c := digit(0) - for j := 0; j < m; j++ { - t := c + digit(x[i+j]) + digit(y[j]) - c, x[i+j] = t>>_W2, digit2(t&_M2) - } - x[k] = digit2((c + digit(x[k])) & _M2) - assert(x[k] == 0) - // correct trial digit - q-- - } - - x[k] = digit2(q) - } - - // undo normalization for remainder - if f != 1 { - c := div21(x[0:m], x[0:m], digit2(f)) - assert(c == 0) - } - } - - return x[m : n+1], x[0:m] -} - - -// Div returns the quotient q = x / y for y > 0, -// with x = y*q + r and 0 <= r < y. -// If y == 0, a division-by-zero run-time error occurs. -// -func (x Natural) Div(y Natural) Natural { - q, _ := divmod(unpack(x), unpack(y)) - return pack(q) -} - - -// Mod returns the modulus r of the division x / y for y > 0, -// with x = y*q + r and 0 <= r < y. -// If y == 0, a division-by-zero run-time error occurs. -// -func (x Natural) Mod(y Natural) Natural { - _, r := divmod(unpack(x), unpack(y)) - return pack(r) -} - - -// DivMod returns the pair (x.Div(y), x.Mod(y)) for y > 0. -// If y == 0, a division-by-zero run-time error occurs. -// -func (x Natural) DivMod(y Natural) (Natural, Natural) { - q, r := divmod(unpack(x), unpack(y)) - return pack(q), pack(r) -} - - -func shl(z, x Natural, s uint) digit { - assert(s <= _W) - n := len(x) - c := digit(0) - for i := 0; i < n; i++ { - c, z[i] = x[i]>>(_W-s), x[i]<= 0; i-- { - c, z[i] = x[i]<<(_W-s)&_M, x[i]>>s|c - } - return c -} - - -// Shr implements ``shift right'' x >> s. It returns x / 2^s. -// -func (x Natural) Shr(s uint) Natural { - n := uint(len(x)) - m := n - s/_W - if m > n { // check for underflow - m = 0 - } - z := make(Natural, m) - - shr(z, x[n-m:n], s%_W) - - return normalize(z) -} - - -// And returns the ``bitwise and'' x & y for the 2's-complement representation of x and y. -// -func (x Natural) And(y Natural) Natural { - n := len(x) - m := len(y) - if n < m { - return y.And(x) - } - - z := make(Natural, m) - for i := 0; i < m; i++ { - z[i] = x[i] & y[i] - } - // upper bits are 0 - - return normalize(z) -} - - -func copy(z, x Natural) { - for i, e := range x { - z[i] = e - } -} - - -// AndNot returns the ``bitwise clear'' x &^ y for the 2's-complement representation of x and y. -// -func (x Natural) AndNot(y Natural) Natural { - n := len(x) - m := len(y) - if n < m { - m = n - } - - z := make(Natural, n) - for i := 0; i < m; i++ { - z[i] = x[i] &^ y[i] - } - copy(z[m:n], x[m:n]) - - return normalize(z) -} - - -// Or returns the ``bitwise or'' x | y for the 2's-complement representation of x and y. -// -func (x Natural) Or(y Natural) Natural { - n := len(x) - m := len(y) - if n < m { - return y.Or(x) - } - - z := make(Natural, n) - for i := 0; i < m; i++ { - z[i] = x[i] | y[i] - } - copy(z[m:n], x[m:n]) - - return z -} - - -// Xor returns the ``bitwise exclusive or'' x ^ y for the 2's-complement representation of x and y. -// -func (x Natural) Xor(y Natural) Natural { - n := len(x) - m := len(y) - if n < m { - return y.Xor(x) - } - - z := make(Natural, n) - for i := 0; i < m; i++ { - z[i] = x[i] ^ y[i] - } - copy(z[m:n], x[m:n]) - - return normalize(z) -} - - -// Cmp compares x and y. The result is an int value -// -// < 0 if x < y -// == 0 if x == y -// > 0 if x > y -// -func (x Natural) Cmp(y Natural) int { - n := len(x) - m := len(y) - - if n != m || n == 0 { - return n - m - } - - i := n - 1 - for i > 0 && x[i] == y[i] { - i-- - } - - d := 0 - switch { - case x[i] < y[i]: - d = -1 - case x[i] > y[i]: - d = 1 - } - - return d -} - - -// log2 computes the binary logarithm of x for x > 0. -// The result is the integer n for which 2^n <= x < 2^(n+1). -// If x == 0 a run-time error occurs. -// -func log2(x uint64) uint { - assert(x > 0) - n := uint(0) - for x > 0 { - x >>= 1 - n++ - } - return n - 1 -} - - -// Log2 computes the binary logarithm of x for x > 0. -// The result is the integer n for which 2^n <= x < 2^(n+1). -// If x == 0 a run-time error occurs. -// -func (x Natural) Log2() uint { - n := len(x) - if n > 0 { - return (uint(n)-1)*_W + log2(uint64(x[n-1])) - } - panic("Log2(0)") -} - - -// Computes x = x div d in place (modifies x) for small d's. -// Returns updated x and x mod d. -// -func divmod1(x Natural, d digit) (Natural, digit) { - assert(0 < d && isSmall(d-1)) - - c := digit(0) - for i := len(x) - 1; i >= 0; i-- { - t := c<<_W + x[i] - c, x[i] = t%d, t/d - } - - return normalize(x), c -} - - -// ToString converts x to a string for a given base, with 2 <= base <= 16. -// -func (x Natural) ToString(base uint) string { - if len(x) == 0 { - return "0" - } - - // allocate buffer for conversion - assert(2 <= base && base <= 16) - n := (x.Log2()+1)/log2(uint64(base)) + 1 // +1: round up - s := make([]byte, n) - - // don't destroy x - t := make(Natural, len(x)) - copy(t, x) - - // convert - i := n - for !t.IsZero() { - i-- - var d digit - t, d = divmod1(t, digit(base)) - s[i] = "0123456789abcdef"[d] - } - - return string(s[i:n]) -} - - -// String converts x to its decimal string representation. -// x.String() is the same as x.ToString(10). -// -func (x Natural) String() string { return x.ToString(10) } - - -func fmtbase(c int) uint { - switch c { - case 'b': - return 2 - case 'o': - return 8 - case 'x': - return 16 - } - return 10 -} - - -// Format is a support routine for fmt.Formatter. It accepts -// the formats 'b' (binary), 'o' (octal), and 'x' (hexadecimal). -// -func (x Natural) Format(h fmt.State, c int) { fmt.Fprintf(h, "%s", x.ToString(fmtbase(c))) } - - -func hexvalue(ch byte) uint { - d := uint(1 << logH) - switch { - case '0' <= ch && ch <= '9': - d = uint(ch - '0') - case 'a' <= ch && ch <= 'f': - d = uint(ch-'a') + 10 - case 'A' <= ch && ch <= 'F': - d = uint(ch-'A') + 10 - } - return d -} - - -// NatFromString returns the natural number corresponding to the -// longest possible prefix of s representing a natural number in a -// given conversion base, the actual conversion base used, and the -// prefix length. The syntax of natural numbers follows the syntax -// of unsigned integer literals in Go. -// -// If the base argument is 0, the string prefix determines the actual -// conversion base. A prefix of ``0x'' or ``0X'' selects base 16; the -// ``0'' prefix selects base 8. Otherwise the selected base is 10. -// -func NatFromString(s string, base uint) (Natural, uint, int) { - // determine base if necessary - i, n := 0, len(s) - if base == 0 { - base = 10 - if n > 0 && s[0] == '0' { - if n > 1 && (s[1] == 'x' || s[1] == 'X') { - base, i = 16, 2 - } else { - base, i = 8, 1 - } - } - } - - // convert string - assert(2 <= base && base <= 16) - x := Nat(0) - for ; i < n; i++ { - d := hexvalue(s[i]) - if d < base { - x = muladd1(x, digit(base), digit(d)) - } else { - break - } - } - - return x, base, i -} - - -// Natural number functions - -func pop1(x digit) uint { - n := uint(0) - for x != 0 { - x &= x - 1 - n++ - } - return n -} - - -// Pop computes the ``population count'' of (the number of 1 bits in) x. -// -func (x Natural) Pop() uint { - n := uint(0) - for i := len(x) - 1; i >= 0; i-- { - n += pop1(x[i]) - } - return n -} - - -// Pow computes x to the power of n. -// -func (xp Natural) Pow(n uint) Natural { - z := Nat(1) - x := xp - for n > 0 { - // z * x^n == x^n0 - if n&1 == 1 { - z = z.Mul(x) - } - x, n = x.Mul(x), n/2 - } - return z -} - - -// MulRange computes the product of all the unsigned integers -// in the range [a, b] inclusively. -// -func MulRange(a, b uint) Natural { - switch { - case a > b: - return Nat(1) - case a == b: - return Nat(uint64(a)) - case a+1 == b: - return Nat(uint64(a)).Mul(Nat(uint64(b))) - } - m := (a + b) >> 1 - assert(a <= m && m < b) - return MulRange(a, m).Mul(MulRange(m+1, b)) -} - - -// Fact computes the factorial of n (Fact(n) == MulRange(2, n)). -// -func Fact(n uint) Natural { - // Using MulRange() instead of the basic for-loop - // lead to faster factorial computation. - return MulRange(2, n) -} - - -// Binomial computes the binomial coefficient of (n, k). -// -func Binomial(n, k uint) Natural { return MulRange(n-k+1, n).Div(MulRange(1, k)) } - - -// Gcd computes the gcd of x and y. -// -func (x Natural) Gcd(y Natural) Natural { - // Euclidean algorithm. - a, b := x, y - for !b.IsZero() { - a, b = b, a.Mod(b) - } - return a -} diff --git a/src/pkg/exp/bignum/bignum_test.go b/src/pkg/exp/bignum/bignum_test.go deleted file mode 100644 index 8db93aa96..000000000 --- a/src/pkg/exp/bignum/bignum_test.go +++ /dev/null @@ -1,681 +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 bignum - -import ( - "fmt" - "testing" -) - -const ( - sa = "991" - sb = "2432902008176640000" // 20! - sc = "933262154439441526816992388562667004907159682643816214685929" + - "638952175999932299156089414639761565182862536979208272237582" + - "51185210916864000000000000000000000000" // 100! - sp = "170141183460469231731687303715884105727" // prime -) - -func natFromString(s string, base uint, slen *int) Natural { - x, _, len := NatFromString(s, base) - if slen != nil { - *slen = len - } - return x -} - - -func intFromString(s string, base uint, slen *int) *Integer { - x, _, len := IntFromString(s, base) - if slen != nil { - *slen = len - } - return x -} - - -func ratFromString(s string, base uint, slen *int) *Rational { - x, _, len := RatFromString(s, base) - if slen != nil { - *slen = len - } - return x -} - - -var ( - nat_zero = Nat(0) - nat_one = Nat(1) - nat_two = Nat(2) - a = natFromString(sa, 10, nil) - b = natFromString(sb, 10, nil) - c = natFromString(sc, 10, nil) - p = natFromString(sp, 10, nil) - int_zero = Int(0) - int_one = Int(1) - int_two = Int(2) - ip = intFromString(sp, 10, nil) - rat_zero = Rat(0, 1) - rat_half = Rat(1, 2) - rat_one = Rat(1, 1) - rat_two = Rat(2, 1) -) - - -var test_msg string -var tester *testing.T - -func test(n uint, b bool) { - if !b { - tester.Fatalf("TEST failed: %s (%d)", test_msg, n) - } -} - - -func nat_eq(n uint, x, y Natural) { - if x.Cmp(y) != 0 { - tester.Fatalf("TEST failed: %s (%d)\nx = %v\ny = %v", test_msg, n, &x, &y) - } -} - - -func int_eq(n uint, x, y *Integer) { - if x.Cmp(y) != 0 { - tester.Fatalf("TEST failed: %s (%d)\nx = %v\ny = %v", test_msg, n, x, y) - } -} - - -func rat_eq(n uint, x, y *Rational) { - if x.Cmp(y) != 0 { - tester.Fatalf("TEST failed: %s (%d)\nx = %v\ny = %v", test_msg, n, x, y) - } -} - - -func TestNatConv(t *testing.T) { - tester = t - test_msg = "NatConvA" - type entry1 struct { - x uint64 - s string - } - tab := []entry1{ - entry1{0, "0"}, - entry1{255, "255"}, - entry1{65535, "65535"}, - entry1{4294967295, "4294967295"}, - entry1{18446744073709551615, "18446744073709551615"}, - } - for i, e := range tab { - test(100+uint(i), Nat(e.x).String() == e.s) - test(200+uint(i), natFromString(e.s, 0, nil).Value() == e.x) - } - - test_msg = "NatConvB" - for i := uint(0); i < 100; i++ { - test(i, Nat(uint64(i)).String() == fmt.Sprintf("%d", i)) - } - - test_msg = "NatConvC" - z := uint64(7) - for i := uint(0); i <= 64; i++ { - test(i, Nat(z).Value() == z) - z <<= 1 - } - - test_msg = "NatConvD" - nat_eq(0, a, Nat(991)) - nat_eq(1, b, Fact(20)) - nat_eq(2, c, Fact(100)) - test(3, a.String() == sa) - test(4, b.String() == sb) - test(5, c.String() == sc) - - test_msg = "NatConvE" - var slen int - nat_eq(10, natFromString("0", 0, nil), nat_zero) - nat_eq(11, natFromString("123", 0, nil), Nat(123)) - nat_eq(12, natFromString("077", 0, nil), Nat(7*8+7)) - nat_eq(13, natFromString("0x1f", 0, nil), Nat(1*16+15)) - nat_eq(14, natFromString("0x1fg", 0, &slen), Nat(1*16+15)) - test(4, slen == 4) - - test_msg = "NatConvF" - tmp := c.Mul(c) - for base := uint(2); base <= 16; base++ { - nat_eq(base, natFromString(tmp.ToString(base), base, nil), tmp) - } - - test_msg = "NatConvG" - x := Nat(100) - y, _, _ := NatFromString(fmt.Sprintf("%b", &x), 2) - nat_eq(100, y, x) -} - - -func abs(x int64) uint64 { - if x < 0 { - x = -x - } - return uint64(x) -} - - -func TestIntConv(t *testing.T) { - tester = t - test_msg = "IntConvA" - type entry2 struct { - x int64 - s string - } - tab := []entry2{ - entry2{0, "0"}, - entry2{-128, "-128"}, - entry2{127, "127"}, - entry2{-32768, "-32768"}, - entry2{32767, "32767"}, - entry2{-2147483648, "-2147483648"}, - entry2{2147483647, "2147483647"}, - entry2{-9223372036854775808, "-9223372036854775808"}, - entry2{9223372036854775807, "9223372036854775807"}, - } - for i, e := range tab { - test(100+uint(i), Int(e.x).String() == e.s) - test(200+uint(i), intFromString(e.s, 0, nil).Value() == e.x) - test(300+uint(i), Int(e.x).Abs().Value() == abs(e.x)) - } - - test_msg = "IntConvB" - var slen int - int_eq(0, intFromString("0", 0, nil), int_zero) - int_eq(1, intFromString("-0", 0, nil), int_zero) - int_eq(2, intFromString("123", 0, nil), Int(123)) - int_eq(3, intFromString("-123", 0, nil), Int(-123)) - int_eq(4, intFromString("077", 0, nil), Int(7*8+7)) - int_eq(5, intFromString("-077", 0, nil), Int(-(7*8 + 7))) - int_eq(6, intFromString("0x1f", 0, nil), Int(1*16+15)) - int_eq(7, intFromString("-0x1f", 0, &slen), Int(-(1*16 + 15))) - test(7, slen == 5) - int_eq(8, intFromString("+0x1f", 0, &slen), Int(+(1*16 + 15))) - test(8, slen == 5) - int_eq(9, intFromString("0x1fg", 0, &slen), Int(1*16+15)) - test(9, slen == 4) - int_eq(10, intFromString("-0x1fg", 0, &slen), Int(-(1*16 + 15))) - test(10, slen == 5) -} - - -func TestRatConv(t *testing.T) { - tester = t - test_msg = "RatConv" - var slen int - rat_eq(0, ratFromString("0", 0, nil), rat_zero) - rat_eq(1, ratFromString("0/1", 0, nil), rat_zero) - rat_eq(2, ratFromString("0/01", 0, nil), rat_zero) - rat_eq(3, ratFromString("0x14/10", 0, &slen), rat_two) - test(4, slen == 7) - rat_eq(5, ratFromString("0.", 0, nil), rat_zero) - rat_eq(6, ratFromString("0.001f", 10, nil), Rat(1, 1000)) - rat_eq(7, ratFromString(".1", 0, nil), Rat(1, 10)) - rat_eq(8, ratFromString("10101.0101", 2, nil), Rat(0x155, 1<<4)) - rat_eq(9, ratFromString("-0003.145926", 10, &slen), Rat(-3145926, 1000000)) - test(10, slen == 12) - rat_eq(11, ratFromString("1e2", 0, nil), Rat(100, 1)) - rat_eq(12, ratFromString("1e-2", 0, nil), Rat(1, 100)) - rat_eq(13, ratFromString("1.1e2", 0, nil), Rat(110, 1)) - rat_eq(14, ratFromString(".1e2x", 0, &slen), Rat(10, 1)) - test(15, slen == 4) -} - - -func add(x, y Natural) Natural { - z1 := x.Add(y) - z2 := y.Add(x) - if z1.Cmp(z2) != 0 { - tester.Fatalf("addition not symmetric:\n\tx = %v\n\ty = %t", x, y) - } - return z1 -} - - -func sum(n uint64, scale Natural) Natural { - s := nat_zero - for ; n > 0; n-- { - s = add(s, Nat(n).Mul(scale)) - } - return s -} - - -func TestNatAdd(t *testing.T) { - tester = t - test_msg = "NatAddA" - nat_eq(0, add(nat_zero, nat_zero), nat_zero) - nat_eq(1, add(nat_zero, c), c) - - test_msg = "NatAddB" - for i := uint64(0); i < 100; i++ { - t := Nat(i) - nat_eq(uint(i), sum(i, c), t.Mul(t).Add(t).Shr(1).Mul(c)) - } -} - - -func mul(x, y Natural) Natural { - z1 := x.Mul(y) - z2 := y.Mul(x) - if z1.Cmp(z2) != 0 { - tester.Fatalf("multiplication not symmetric:\n\tx = %v\n\ty = %t", x, y) - } - if !x.IsZero() && z1.Div(x).Cmp(y) != 0 { - tester.Fatalf("multiplication/division not inverse (A):\n\tx = %v\n\ty = %t", x, y) - } - if !y.IsZero() && z1.Div(y).Cmp(x) != 0 { - tester.Fatalf("multiplication/division not inverse (B):\n\tx = %v\n\ty = %t", x, y) - } - return z1 -} - - -func TestNatSub(t *testing.T) { - tester = t - test_msg = "NatSubA" - nat_eq(0, nat_zero.Sub(nat_zero), nat_zero) - nat_eq(1, c.Sub(nat_zero), c) - - test_msg = "NatSubB" - for i := uint64(0); i < 100; i++ { - t := sum(i, c) - for j := uint64(0); j <= i; j++ { - t = t.Sub(mul(Nat(j), c)) - } - nat_eq(uint(i), t, nat_zero) - } -} - - -func TestNatMul(t *testing.T) { - tester = t - test_msg = "NatMulA" - nat_eq(0, mul(c, nat_zero), nat_zero) - nat_eq(1, mul(c, nat_one), c) - - test_msg = "NatMulB" - nat_eq(0, b.Mul(MulRange(0, 100)), nat_zero) - nat_eq(1, b.Mul(MulRange(21, 100)), c) - - test_msg = "NatMulC" - const n = 100 - p := b.Mul(c).Shl(n) - for i := uint(0); i < n; i++ { - nat_eq(i, mul(b.Shl(i), c.Shl(n-i)), p) - } -} - - -func TestNatDiv(t *testing.T) { - tester = t - test_msg = "NatDivA" - nat_eq(0, c.Div(nat_one), c) - nat_eq(1, c.Div(Nat(100)), Fact(99)) - nat_eq(2, b.Div(c), nat_zero) - nat_eq(4, nat_one.Shl(100).Div(nat_one.Shl(90)), nat_one.Shl(10)) - nat_eq(5, c.Div(b), MulRange(21, 100)) - - test_msg = "NatDivB" - const n = 100 - p := Fact(n) - for i := uint(0); i < n; i++ { - nat_eq(100+i, p.Div(MulRange(1, i)), MulRange(i+1, n)) - } - - // a specific test case that exposed a bug in package big - test_msg = "NatDivC" - x := natFromString("69720375229712477164533808935312303556800", 10, nil) - y := natFromString("3099044504245996706400", 10, nil) - q := natFromString("22497377864108980962", 10, nil) - r := natFromString("0", 10, nil) - qc, rc := x.DivMod(y) - nat_eq(0, q, qc) - nat_eq(1, r, rc) -} - - -func TestIntQuoRem(t *testing.T) { - tester = t - test_msg = "IntQuoRem" - type T struct { - x, y, q, r int64 - } - a := []T{ - T{+8, +3, +2, +2}, - T{+8, -3, -2, +2}, - T{-8, +3, -2, -2}, - T{-8, -3, +2, -2}, - T{+1, +2, 0, +1}, - T{+1, -2, 0, +1}, - T{-1, +2, 0, -1}, - T{-1, -2, 0, -1}, - } - for i := uint(0); i < uint(len(a)); i++ { - e := &a[i] - x, y := Int(e.x).Mul(ip), Int(e.y).Mul(ip) - q, r := Int(e.q), Int(e.r).Mul(ip) - qq, rr := x.QuoRem(y) - int_eq(4*i+0, x.Quo(y), q) - int_eq(4*i+1, x.Rem(y), r) - int_eq(4*i+2, qq, q) - int_eq(4*i+3, rr, r) - } -} - - -func TestIntDivMod(t *testing.T) { - tester = t - test_msg = "IntDivMod" - type T struct { - x, y, q, r int64 - } - a := []T{ - T{+8, +3, +2, +2}, - T{+8, -3, -2, +2}, - T{-8, +3, -3, +1}, - T{-8, -3, +3, +1}, - T{+1, +2, 0, +1}, - T{+1, -2, 0, +1}, - T{-1, +2, -1, +1}, - T{-1, -2, +1, +1}, - } - for i := uint(0); i < uint(len(a)); i++ { - e := &a[i] - x, y := Int(e.x).Mul(ip), Int(e.y).Mul(ip) - q, r := Int(e.q), Int(e.r).Mul(ip) - qq, rr := x.DivMod(y) - int_eq(4*i+0, x.Div(y), q) - int_eq(4*i+1, x.Mod(y), r) - int_eq(4*i+2, qq, q) - int_eq(4*i+3, rr, r) - } -} - - -func TestNatMod(t *testing.T) { - tester = t - test_msg = "NatModA" - for i := uint(0); ; i++ { - d := nat_one.Shl(i) - if d.Cmp(c) < 0 { - nat_eq(i, c.Add(d).Mod(c), d) - } else { - nat_eq(i, c.Add(d).Div(c), nat_two) - nat_eq(i, c.Add(d).Mod(c), d.Sub(c)) - break - } - } -} - - -func TestNatShift(t *testing.T) { - tester = t - test_msg = "NatShift1L" - test(0, b.Shl(0).Cmp(b) == 0) - test(1, c.Shl(1).Cmp(c) > 0) - - test_msg = "NatShift1R" - test(3, b.Shr(0).Cmp(b) == 0) - test(4, c.Shr(1).Cmp(c) < 0) - - test_msg = "NatShift2" - for i := uint(0); i < 100; i++ { - test(i, c.Shl(i).Shr(i).Cmp(c) == 0) - } - - test_msg = "NatShift3L" - { - const m = 3 - p := b - f := Nat(1 << m) - for i := uint(0); i < 100; i++ { - nat_eq(i, b.Shl(i*m), p) - p = mul(p, f) - } - } - - test_msg = "NatShift3R" - { - p := c - for i := uint(0); !p.IsZero(); i++ { - nat_eq(i, c.Shr(i), p) - p = p.Shr(1) - } - } -} - - -func TestIntShift(t *testing.T) { - tester = t - test_msg = "IntShift1L" - test(0, ip.Shl(0).Cmp(ip) == 0) - test(1, ip.Shl(1).Cmp(ip) > 0) - - test_msg = "IntShift1R" - test(0, ip.Shr(0).Cmp(ip) == 0) - test(1, ip.Shr(1).Cmp(ip) < 0) - - test_msg = "IntShift2" - for i := uint(0); i < 100; i++ { - test(i, ip.Shl(i).Shr(i).Cmp(ip) == 0) - } - - test_msg = "IntShift3L" - { - const m = 3 - p := ip - f := Int(1 << m) - for i := uint(0); i < 100; i++ { - int_eq(i, ip.Shl(i*m), p) - p = p.Mul(f) - } - } - - test_msg = "IntShift3R" - { - p := ip - for i := uint(0); p.IsPos(); i++ { - int_eq(i, ip.Shr(i), p) - p = p.Shr(1) - } - } - - test_msg = "IntShift4R" - int_eq(0, Int(-43).Shr(1), Int(-43>>1)) - int_eq(0, Int(-1024).Shr(100), Int(-1)) - int_eq(1, ip.Neg().Shr(10), ip.Neg().Div(Int(1).Shl(10))) -} - - -func TestNatBitOps(t *testing.T) { - tester = t - - x := uint64(0xf08e6f56bd8c3941) - y := uint64(0x3984ef67834bc) - - bx := Nat(x) - by := Nat(y) - - test_msg = "NatAnd" - bz := Nat(x & y) - for i := uint(0); i < 100; i++ { - nat_eq(i, bx.Shl(i).And(by.Shl(i)), bz.Shl(i)) - } - - test_msg = "NatAndNot" - bz = Nat(x &^ y) - for i := uint(0); i < 100; i++ { - nat_eq(i, bx.Shl(i).AndNot(by.Shl(i)), bz.Shl(i)) - } - - test_msg = "NatOr" - bz = Nat(x | y) - for i := uint(0); i < 100; i++ { - nat_eq(i, bx.Shl(i).Or(by.Shl(i)), bz.Shl(i)) - } - - test_msg = "NatXor" - bz = Nat(x ^ y) - for i := uint(0); i < 100; i++ { - nat_eq(i, bx.Shl(i).Xor(by.Shl(i)), bz.Shl(i)) - } -} - - -func TestIntBitOps1(t *testing.T) { - tester = t - test_msg = "IntBitOps1" - type T struct { - x, y int64 - } - a := []T{ - T{+7, +3}, - T{+7, -3}, - T{-7, +3}, - T{-7, -3}, - } - for i := uint(0); i < uint(len(a)); i++ { - e := &a[i] - int_eq(4*i+0, Int(e.x).And(Int(e.y)), Int(e.x&e.y)) - int_eq(4*i+1, Int(e.x).AndNot(Int(e.y)), Int(e.x&^e.y)) - int_eq(4*i+2, Int(e.x).Or(Int(e.y)), Int(e.x|e.y)) - int_eq(4*i+3, Int(e.x).Xor(Int(e.y)), Int(e.x^e.y)) - } -} - - -func TestIntBitOps2(t *testing.T) { - tester = t - - test_msg = "IntNot" - int_eq(0, Int(-2).Not(), Int(1)) - int_eq(0, Int(-1).Not(), Int(0)) - int_eq(0, Int(0).Not(), Int(-1)) - int_eq(0, Int(1).Not(), Int(-2)) - int_eq(0, Int(2).Not(), Int(-3)) - - test_msg = "IntAnd" - for x := int64(-15); x < 5; x++ { - bx := Int(x) - for y := int64(-5); y < 15; y++ { - by := Int(y) - for i := uint(50); i < 70; i++ { // shift across 64bit boundary - int_eq(i, bx.Shl(i).And(by.Shl(i)), Int(x&y).Shl(i)) - } - } - } - - test_msg = "IntAndNot" - for x := int64(-15); x < 5; x++ { - bx := Int(x) - for y := int64(-5); y < 15; y++ { - by := Int(y) - for i := uint(50); i < 70; i++ { // shift across 64bit boundary - int_eq(2*i+0, bx.Shl(i).AndNot(by.Shl(i)), Int(x&^y).Shl(i)) - int_eq(2*i+1, bx.Shl(i).And(by.Shl(i).Not()), Int(x&^y).Shl(i)) - } - } - } - - test_msg = "IntOr" - for x := int64(-15); x < 5; x++ { - bx := Int(x) - for y := int64(-5); y < 15; y++ { - by := Int(y) - for i := uint(50); i < 70; i++ { // shift across 64bit boundary - int_eq(i, bx.Shl(i).Or(by.Shl(i)), Int(x|y).Shl(i)) - } - } - } - - test_msg = "IntXor" - for x := int64(-15); x < 5; x++ { - bx := Int(x) - for y := int64(-5); y < 15; y++ { - by := Int(y) - for i := uint(50); i < 70; i++ { // shift across 64bit boundary - int_eq(i, bx.Shl(i).Xor(by.Shl(i)), Int(x^y).Shl(i)) - } - } - } -} - - -func TestNatCmp(t *testing.T) { - tester = t - test_msg = "NatCmp" - test(0, a.Cmp(a) == 0) - test(1, a.Cmp(b) < 0) - test(2, b.Cmp(a) > 0) - test(3, a.Cmp(c) < 0) - d := c.Add(b) - test(4, c.Cmp(d) < 0) - test(5, d.Cmp(c) > 0) -} - - -func TestNatLog2(t *testing.T) { - tester = t - test_msg = "NatLog2A" - test(0, nat_one.Log2() == 0) - test(1, nat_two.Log2() == 1) - test(2, Nat(3).Log2() == 1) - test(3, Nat(4).Log2() == 2) - - test_msg = "NatLog2B" - for i := uint(0); i < 100; i++ { - test(i, nat_one.Shl(i).Log2() == i) - } -} - - -func TestNatGcd(t *testing.T) { - tester = t - test_msg = "NatGcdA" - f := Nat(99991) - nat_eq(0, b.Mul(f).Gcd(c.Mul(f)), MulRange(1, 20).Mul(f)) -} - - -func TestNatPow(t *testing.T) { - tester = t - test_msg = "NatPowA" - nat_eq(0, nat_two.Pow(0), nat_one) - - test_msg = "NatPowB" - for i := uint(0); i < 100; i++ { - nat_eq(i, nat_two.Pow(i), nat_one.Shl(i)) - } -} - - -func TestNatPop(t *testing.T) { - tester = t - test_msg = "NatPopA" - test(0, nat_zero.Pop() == 0) - test(1, nat_one.Pop() == 1) - test(2, Nat(10).Pop() == 2) - test(3, Nat(30).Pop() == 4) - test(4, Nat(0x1248f).Shl(33).Pop() == 8) - - test_msg = "NatPopB" - for i := uint(0); i < 100; i++ { - test(i, nat_one.Shl(i).Sub(nat_one).Pop() == i) - } -} - - -func TestIssue571(t *testing.T) { - const min_float = "4.940656458412465441765687928682213723651e-324" - RatFromString(min_float, 10) // this must not crash -} diff --git a/src/pkg/exp/bignum/integer.go b/src/pkg/exp/bignum/integer.go deleted file mode 100644 index a8d26829d..000000000 --- a/src/pkg/exp/bignum/integer.go +++ /dev/null @@ -1,520 +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. - -// Integer numbers -// -// Integers are normalized if the mantissa is normalized and the sign is -// false for mant == 0. Use MakeInt to create normalized Integers. - -package bignum - -import ( - "fmt" -) - -// TODO(gri) Complete the set of in-place operations. - -// Integer represents a signed integer value of arbitrary precision. -// -type Integer struct { - sign bool - mant Natural -} - - -// MakeInt makes an integer given a sign and a mantissa. -// The number is positive (>= 0) if sign is false or the -// mantissa is zero; it is negative otherwise. -// -func MakeInt(sign bool, mant Natural) *Integer { - if mant.IsZero() { - sign = false // normalize - } - return &Integer{sign, mant} -} - - -// Int creates a small integer with value x. -// -func Int(x int64) *Integer { - var ux uint64 - if x < 0 { - // For the most negative x, -x == x, and - // the bit pattern has the correct value. - ux = uint64(-x) - } else { - ux = uint64(x) - } - return MakeInt(x < 0, Nat(ux)) -} - - -// Value returns the value of x, if x fits into an int64; -// otherwise the result is undefined. -// -func (x *Integer) Value() int64 { - z := int64(x.mant.Value()) - if x.sign { - z = -z - } - return z -} - - -// Abs returns the absolute value of x. -// -func (x *Integer) Abs() Natural { return x.mant } - - -// Predicates - -// IsEven returns true iff x is divisible by 2. -// -func (x *Integer) IsEven() bool { return x.mant.IsEven() } - - -// IsOdd returns true iff x is not divisible by 2. -// -func (x *Integer) IsOdd() bool { return x.mant.IsOdd() } - - -// IsZero returns true iff x == 0. -// -func (x *Integer) IsZero() bool { return x.mant.IsZero() } - - -// IsNeg returns true iff x < 0. -// -func (x *Integer) IsNeg() bool { return x.sign && !x.mant.IsZero() } - - -// IsPos returns true iff x >= 0. -// -func (x *Integer) IsPos() bool { return !x.sign && !x.mant.IsZero() } - - -// Operations - -// Neg returns the negated value of x. -// -func (x *Integer) Neg() *Integer { return MakeInt(!x.sign, x.mant) } - - -// Iadd sets z to the sum x + y. -// z must exist and may be x or y. -// -func Iadd(z, x, y *Integer) { - if x.sign == y.sign { - // x + y == x + y - // (-x) + (-y) == -(x + y) - z.sign = x.sign - Nadd(&z.mant, x.mant, y.mant) - } else { - // x + (-y) == x - y == -(y - x) - // (-x) + y == y - x == -(x - y) - if x.mant.Cmp(y.mant) >= 0 { - z.sign = x.sign - Nsub(&z.mant, x.mant, y.mant) - } else { - z.sign = !x.sign - Nsub(&z.mant, y.mant, x.mant) - } - } -} - - -// Add returns the sum x + y. -// -func (x *Integer) Add(y *Integer) *Integer { - var z Integer - Iadd(&z, x, y) - return &z -} - - -func Isub(z, x, y *Integer) { - if x.sign != y.sign { - // x - (-y) == x + y - // (-x) - y == -(x + y) - z.sign = x.sign - Nadd(&z.mant, x.mant, y.mant) - } else { - // x - y == x - y == -(y - x) - // (-x) - (-y) == y - x == -(x - y) - if x.mant.Cmp(y.mant) >= 0 { - z.sign = x.sign - Nsub(&z.mant, x.mant, y.mant) - } else { - z.sign = !x.sign - Nsub(&z.mant, y.mant, x.mant) - } - } -} - - -// Sub returns the difference x - y. -// -func (x *Integer) Sub(y *Integer) *Integer { - var z Integer - Isub(&z, x, y) - return &z -} - - -// Nscale sets *z to the scaled value (*z) * d. -// -func Iscale(z *Integer, d int64) { - f := uint64(d) - if d < 0 { - f = uint64(-d) - } - z.sign = z.sign != (d < 0) - Nscale(&z.mant, f) -} - - -// Mul1 returns the product x * d. -// -func (x *Integer) Mul1(d int64) *Integer { - f := uint64(d) - if d < 0 { - f = uint64(-d) - } - return MakeInt(x.sign != (d < 0), x.mant.Mul1(f)) -} - - -// Mul returns the product x * y. -// -func (x *Integer) Mul(y *Integer) *Integer { - // x * y == x * y - // x * (-y) == -(x * y) - // (-x) * y == -(x * y) - // (-x) * (-y) == x * y - return MakeInt(x.sign != y.sign, x.mant.Mul(y.mant)) -} - - -// MulNat returns the product x * y, where y is a (unsigned) natural number. -// -func (x *Integer) MulNat(y Natural) *Integer { - // x * y == x * y - // (-x) * y == -(x * y) - return MakeInt(x.sign, x.mant.Mul(y)) -} - - -// Quo returns the quotient q = x / y for y != 0. -// If y == 0, a division-by-zero run-time error occurs. -// -// Quo and Rem implement T-division and modulus (like C99): -// -// q = x.Quo(y) = trunc(x/y) (truncation towards zero) -// r = x.Rem(y) = x - y*q -// -// (Daan Leijen, ``Division and Modulus for Computer Scientists''.) -// -func (x *Integer) Quo(y *Integer) *Integer { - // x / y == x / y - // x / (-y) == -(x / y) - // (-x) / y == -(x / y) - // (-x) / (-y) == x / y - return MakeInt(x.sign != y.sign, x.mant.Div(y.mant)) -} - - -// Rem returns the remainder r of the division x / y for y != 0, -// with r = x - y*x.Quo(y). Unless r is zero, its sign corresponds -// to the sign of x. -// If y == 0, a division-by-zero run-time error occurs. -// -func (x *Integer) Rem(y *Integer) *Integer { - // x % y == x % y - // x % (-y) == x % y - // (-x) % y == -(x % y) - // (-x) % (-y) == -(x % y) - return MakeInt(x.sign, x.mant.Mod(y.mant)) -} - - -// QuoRem returns the pair (x.Quo(y), x.Rem(y)) for y != 0. -// If y == 0, a division-by-zero run-time error occurs. -// -func (x *Integer) QuoRem(y *Integer) (*Integer, *Integer) { - q, r := x.mant.DivMod(y.mant) - return MakeInt(x.sign != y.sign, q), MakeInt(x.sign, r) -} - - -// Div returns the quotient q = x / y for y != 0. -// If y == 0, a division-by-zero run-time error occurs. -// -// Div and Mod implement Euclidian division and modulus: -// -// q = x.Div(y) -// r = x.Mod(y) with: 0 <= r < |q| and: x = y*q + r -// -// (Raymond T. Boute, ``The Euclidian definition of the functions -// div and mod''. ACM Transactions on Programming Languages and -// Systems (TOPLAS), 14(2):127-144, New York, NY, USA, 4/1992. -// ACM press.) -// -func (x *Integer) Div(y *Integer) *Integer { - q, r := x.QuoRem(y) - if r.IsNeg() { - if y.IsPos() { - q = q.Sub(Int(1)) - } else { - q = q.Add(Int(1)) - } - } - return q -} - - -// Mod returns the modulus r of the division x / y for y != 0, -// with r = x - y*x.Div(y). r is always positive. -// If y == 0, a division-by-zero run-time error occurs. -// -func (x *Integer) Mod(y *Integer) *Integer { - r := x.Rem(y) - if r.IsNeg() { - if y.IsPos() { - r = r.Add(y) - } else { - r = r.Sub(y) - } - } - return r -} - - -// DivMod returns the pair (x.Div(y), x.Mod(y)). -// -func (x *Integer) DivMod(y *Integer) (*Integer, *Integer) { - q, r := x.QuoRem(y) - if r.IsNeg() { - if y.IsPos() { - q = q.Sub(Int(1)) - r = r.Add(y) - } else { - q = q.Add(Int(1)) - r = r.Sub(y) - } - } - return q, r -} - - -// Shl implements ``shift left'' x << s. It returns x * 2^s. -// -func (x *Integer) Shl(s uint) *Integer { return MakeInt(x.sign, x.mant.Shl(s)) } - - -// The bitwise operations on integers are defined on the 2's-complement -// representation of integers. From -// -// -x == ^x + 1 (1) 2's complement representation -// -// follows: -// -// -(x) == ^(x) + 1 -// -(-x) == ^(-x) + 1 -// x-1 == ^(-x) -// ^(x-1) == -x (2) -// -// Using (1) and (2), operations on negative integers of the form -x are -// converted to operations on negated positive integers of the form ~(x-1). - - -// Shr implements ``shift right'' x >> s. It returns x / 2^s. -// -func (x *Integer) Shr(s uint) *Integer { - if x.sign { - // (-x) >> s == ^(x-1) >> s == ^((x-1) >> s) == -(((x-1) >> s) + 1) - return MakeInt(true, x.mant.Sub(Nat(1)).Shr(s).Add(Nat(1))) - } - - return MakeInt(false, x.mant.Shr(s)) -} - - -// Not returns the ``bitwise not'' ^x for the 2's-complement representation of x. -func (x *Integer) Not() *Integer { - if x.sign { - // ^(-x) == ^(^(x-1)) == x-1 - return MakeInt(false, x.mant.Sub(Nat(1))) - } - - // ^x == -x-1 == -(x+1) - return MakeInt(true, x.mant.Add(Nat(1))) -} - - -// And returns the ``bitwise and'' x & y for the 2's-complement representation of x and y. -// -func (x *Integer) And(y *Integer) *Integer { - if x.sign == y.sign { - if x.sign { - // (-x) & (-y) == ^(x-1) & ^(y-1) == ^((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1) - return MakeInt(true, x.mant.Sub(Nat(1)).Or(y.mant.Sub(Nat(1))).Add(Nat(1))) - } - - // x & y == x & y - return MakeInt(false, x.mant.And(y.mant)) - } - - // x.sign != y.sign - if x.sign { - x, y = y, x // & is symmetric - } - - // x & (-y) == x & ^(y-1) == x &^ (y-1) - return MakeInt(false, x.mant.AndNot(y.mant.Sub(Nat(1)))) -} - - -// AndNot returns the ``bitwise clear'' x &^ y for the 2's-complement representation of x and y. -// -func (x *Integer) AndNot(y *Integer) *Integer { - if x.sign == y.sign { - if x.sign { - // (-x) &^ (-y) == ^(x-1) &^ ^(y-1) == ^(x-1) & (y-1) == (y-1) &^ (x-1) - return MakeInt(false, y.mant.Sub(Nat(1)).AndNot(x.mant.Sub(Nat(1)))) - } - - // x &^ y == x &^ y - return MakeInt(false, x.mant.AndNot(y.mant)) - } - - if x.sign { - // (-x) &^ y == ^(x-1) &^ y == ^(x-1) & ^y == ^((x-1) | y) == -(((x-1) | y) + 1) - return MakeInt(true, x.mant.Sub(Nat(1)).Or(y.mant).Add(Nat(1))) - } - - // x &^ (-y) == x &^ ^(y-1) == x & (y-1) - return MakeInt(false, x.mant.And(y.mant.Sub(Nat(1)))) -} - - -// Or returns the ``bitwise or'' x | y for the 2's-complement representation of x and y. -// -func (x *Integer) Or(y *Integer) *Integer { - if x.sign == y.sign { - if x.sign { - // (-x) | (-y) == ^(x-1) | ^(y-1) == ^((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1) - return MakeInt(true, x.mant.Sub(Nat(1)).And(y.mant.Sub(Nat(1))).Add(Nat(1))) - } - - // x | y == x | y - return MakeInt(false, x.mant.Or(y.mant)) - } - - // x.sign != y.sign - if x.sign { - x, y = y, x // | or symmetric - } - - // x | (-y) == x | ^(y-1) == ^((y-1) &^ x) == -(^((y-1) &^ x) + 1) - return MakeInt(true, y.mant.Sub(Nat(1)).AndNot(x.mant).Add(Nat(1))) -} - - -// Xor returns the ``bitwise xor'' x | y for the 2's-complement representation of x and y. -// -func (x *Integer) Xor(y *Integer) *Integer { - if x.sign == y.sign { - if x.sign { - // (-x) ^ (-y) == ^(x-1) ^ ^(y-1) == (x-1) ^ (y-1) - return MakeInt(false, x.mant.Sub(Nat(1)).Xor(y.mant.Sub(Nat(1)))) - } - - // x ^ y == x ^ y - return MakeInt(false, x.mant.Xor(y.mant)) - } - - // x.sign != y.sign - if x.sign { - x, y = y, x // ^ is symmetric - } - - // x ^ (-y) == x ^ ^(y-1) == ^(x ^ (y-1)) == -((x ^ (y-1)) + 1) - return MakeInt(true, x.mant.Xor(y.mant.Sub(Nat(1))).Add(Nat(1))) -} - - -// Cmp compares x and y. The result is an int value that is -// -// < 0 if x < y -// == 0 if x == y -// > 0 if x > y -// -func (x *Integer) Cmp(y *Integer) int { - // x cmp y == x cmp y - // x cmp (-y) == x - // (-x) cmp y == y - // (-x) cmp (-y) == -(x cmp y) - var r int - switch { - case x.sign == y.sign: - r = x.mant.Cmp(y.mant) - if x.sign { - r = -r - } - case x.sign: - r = -1 - case y.sign: - r = 1 - } - return r -} - - -// ToString converts x to a string for a given base, with 2 <= base <= 16. -// -func (x *Integer) ToString(base uint) string { - if x.mant.IsZero() { - return "0" - } - var s string - if x.sign { - s = "-" - } - return s + x.mant.ToString(base) -} - - -// String converts x to its decimal string representation. -// x.String() is the same as x.ToString(10). -// -func (x *Integer) String() string { return x.ToString(10) } - - -// Format is a support routine for fmt.Formatter. It accepts -// the formats 'b' (binary), 'o' (octal), and 'x' (hexadecimal). -// -func (x *Integer) Format(h fmt.State, c int) { fmt.Fprintf(h, "%s", x.ToString(fmtbase(c))) } - - -// IntFromString returns the integer corresponding to the -// longest possible prefix of s representing an integer in a -// given conversion base, the actual conversion base used, and -// the prefix length. The syntax of integers follows the syntax -// of signed integer literals in Go. -// -// If the base argument is 0, the string prefix determines the actual -// conversion base. A prefix of ``0x'' or ``0X'' selects base 16; the -// ``0'' prefix selects base 8. Otherwise the selected base is 10. -// -func IntFromString(s string, base uint) (*Integer, uint, int) { - // skip sign, if any - i0 := 0 - if len(s) > 0 && (s[0] == '-' || s[0] == '+') { - i0 = 1 - } - - mant, base, slen := NatFromString(s[i0:], base) - - return MakeInt(i0 > 0 && s[0] == '-', mant), base, i0 + slen -} diff --git a/src/pkg/exp/bignum/nrdiv_test.go b/src/pkg/exp/bignum/nrdiv_test.go deleted file mode 100644 index 725b1acea..000000000 --- a/src/pkg/exp/bignum/nrdiv_test.go +++ /dev/null @@ -1,188 +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 Newton-Raphson division and uses -// it as an additional test case for bignum. -// -// Division of x/y is achieved by computing r = 1/y to -// obtain the quotient q = x*r = x*(1/y) = x/y. The -// reciprocal r is the solution for f(x) = 1/x - y and -// the solution is approximated through iteration. The -// iteration does not require division. - -package bignum - -import "testing" - - -// An fpNat is a Natural scaled by a power of two -// (an unsigned floating point representation). The -// value of an fpNat x is x.m * 2^x.e . -// -type fpNat struct { - m Natural - e int -} - - -// sub computes x - y. -func (x fpNat) sub(y fpNat) fpNat { - switch d := x.e - y.e; { - case d < 0: - return fpNat{x.m.Sub(y.m.Shl(uint(-d))), x.e} - case d > 0: - return fpNat{x.m.Shl(uint(d)).Sub(y.m), y.e} - } - return fpNat{x.m.Sub(y.m), x.e} -} - - -// mul2 computes x*2. -func (x fpNat) mul2() fpNat { return fpNat{x.m, x.e + 1} } - - -// mul computes x*y. -func (x fpNat) mul(y fpNat) fpNat { return fpNat{x.m.Mul(y.m), x.e + y.e} } - - -// mant computes the (possibly truncated) Natural representation -// of an fpNat x. -// -func (x fpNat) mant() Natural { - switch { - case x.e > 0: - return x.m.Shl(uint(x.e)) - case x.e < 0: - return x.m.Shr(uint(-x.e)) - } - return x.m -} - - -// nrDivEst computes an estimate of the quotient q = x0/y0 and returns q. -// q may be too small (usually by 1). -// -func nrDivEst(x0, y0 Natural) Natural { - if y0.IsZero() { - panic("division by zero") - return nil - } - // y0 > 0 - - if y0.Cmp(Nat(1)) == 0 { - return x0 - } - // y0 > 1 - - switch d := x0.Cmp(y0); { - case d < 0: - return Nat(0) - case d == 0: - return Nat(1) - } - // x0 > y0 > 1 - - // Determine maximum result length. - maxLen := int(x0.Log2() - y0.Log2() + 1) - - // In the following, each number x is represented - // as a mantissa x.m and an exponent x.e such that - // x = xm * 2^x.e. - x := fpNat{x0, 0} - y := fpNat{y0, 0} - - // Determine a scale factor f = 2^e such that - // 0.5 <= y/f == y*(2^-e) < 1.0 - // and scale y accordingly. - e := int(y.m.Log2()) + 1 - y.e -= e - - // t1 - var c = 2.9142 - const n = 14 - t1 := fpNat{Nat(uint64(c * (1 << n))), -n} - - // Compute initial value r0 for the reciprocal of y/f. - // r0 = t1 - 2*y - r := t1.sub(y.mul2()) - two := fpNat{Nat(2), 0} - - // Newton-Raphson iteration - p := Nat(0) - for i := 0; ; i++ { - // check if we are done - // TODO: Need to come up with a better test here - // as it will reduce computation time significantly. - // q = x*r/f - q := x.mul(r) - q.e -= e - res := q.mant() - if res.Cmp(p) == 0 { - return res - } - p = res - - // r' = r*(2 - y*r) - r = r.mul(two.sub(y.mul(r))) - - // reduce mantissa size - // TODO: Find smaller bound as it will reduce - // computation time massively. - d := int(r.m.Log2()+1) - maxLen - if d > 0 { - r = fpNat{r.m.Shr(uint(d)), r.e + d} - } - } - - panic("unreachable") - return nil -} - - -func nrdiv(x, y Natural) (q, r Natural) { - q = nrDivEst(x, y) - r = x.Sub(y.Mul(q)) - // if r is too large, correct q and r - // (usually one iteration) - for r.Cmp(y) >= 0 { - q = q.Add(Nat(1)) - r = r.Sub(y) - } - return -} - - -func div(t *testing.T, x, y Natural) { - q, r := nrdiv(x, y) - qx, rx := x.DivMod(y) - if q.Cmp(qx) != 0 { - t.Errorf("x = %s, y = %s, got q = %s, want q = %s", x, y, q, qx) - } - if r.Cmp(rx) != 0 { - t.Errorf("x = %s, y = %s, got r = %s, want r = %s", x, y, r, rx) - } -} - - -func idiv(t *testing.T, x0, y0 uint64) { div(t, Nat(x0), Nat(y0)) } - - -func TestNRDiv(t *testing.T) { - idiv(t, 17, 18) - idiv(t, 17, 17) - idiv(t, 17, 1) - idiv(t, 17, 16) - idiv(t, 17, 10) - idiv(t, 17, 9) - idiv(t, 17, 8) - idiv(t, 17, 5) - idiv(t, 17, 3) - idiv(t, 1025, 512) - idiv(t, 7489595, 2) - idiv(t, 5404679459, 78495) - idiv(t, 7484890589595, 7484890589594) - div(t, Fact(100), Fact(91)) - div(t, Fact(1000), Fact(991)) - //div(t, Fact(10000), Fact(9991)); // takes too long - disabled for now -} diff --git a/src/pkg/exp/bignum/rational.go b/src/pkg/exp/bignum/rational.go deleted file mode 100644 index 378585e5f..000000000 --- a/src/pkg/exp/bignum/rational.go +++ /dev/null @@ -1,205 +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. - -// Rational numbers - -package bignum - -import "fmt" - - -// Rational represents a quotient a/b of arbitrary precision. -// -type Rational struct { - a *Integer // numerator - b Natural // denominator -} - - -// MakeRat makes a rational number given a numerator a and a denominator b. -// -func MakeRat(a *Integer, b Natural) *Rational { - f := a.mant.Gcd(b) // f > 0 - if f.Cmp(Nat(1)) != 0 { - a = MakeInt(a.sign, a.mant.Div(f)) - b = b.Div(f) - } - return &Rational{a, b} -} - - -// Rat creates a small rational number with value a0/b0. -// -func Rat(a0 int64, b0 int64) *Rational { - a, b := Int(a0), Int(b0) - if b.sign { - a = a.Neg() - } - return MakeRat(a, b.mant) -} - - -// Value returns the numerator and denominator of x. -// -func (x *Rational) Value() (numerator *Integer, denominator Natural) { - return x.a, x.b -} - - -// Predicates - -// IsZero returns true iff x == 0. -// -func (x *Rational) IsZero() bool { return x.a.IsZero() } - - -// IsNeg returns true iff x < 0. -// -func (x *Rational) IsNeg() bool { return x.a.IsNeg() } - - -// IsPos returns true iff x > 0. -// -func (x *Rational) IsPos() bool { return x.a.IsPos() } - - -// IsInt returns true iff x can be written with a denominator 1 -// in the form x == x'/1; i.e., if x is an integer value. -// -func (x *Rational) IsInt() bool { return x.b.Cmp(Nat(1)) == 0 } - - -// Operations - -// Neg returns the negated value of x. -// -func (x *Rational) Neg() *Rational { return MakeRat(x.a.Neg(), x.b) } - - -// Add returns the sum x + y. -// -func (x *Rational) Add(y *Rational) *Rational { - return MakeRat((x.a.MulNat(y.b)).Add(y.a.MulNat(x.b)), x.b.Mul(y.b)) -} - - -// Sub returns the difference x - y. -// -func (x *Rational) Sub(y *Rational) *Rational { - return MakeRat((x.a.MulNat(y.b)).Sub(y.a.MulNat(x.b)), x.b.Mul(y.b)) -} - - -// Mul returns the product x * y. -// -func (x *Rational) Mul(y *Rational) *Rational { return MakeRat(x.a.Mul(y.a), x.b.Mul(y.b)) } - - -// Quo returns the quotient x / y for y != 0. -// If y == 0, a division-by-zero run-time error occurs. -// -func (x *Rational) Quo(y *Rational) *Rational { - a := x.a.MulNat(y.b) - b := y.a.MulNat(x.b) - if b.IsNeg() { - a = a.Neg() - } - return MakeRat(a, b.mant) -} - - -// Cmp compares x and y. The result is an int value -// -// < 0 if x < y -// == 0 if x == y -// > 0 if x > y -// -func (x *Rational) Cmp(y *Rational) int { return (x.a.MulNat(y.b)).Cmp(y.a.MulNat(x.b)) } - - -// ToString converts x to a string for a given base, with 2 <= base <= 16. -// The string representation is of the form "n" if x is an integer; otherwise -// it is of form "n/d". -// -func (x *Rational) ToString(base uint) string { - s := x.a.ToString(base) - if !x.IsInt() { - s += "/" + x.b.ToString(base) - } - return s -} - - -// String converts x to its decimal string representation. -// x.String() is the same as x.ToString(10). -// -func (x *Rational) String() string { return x.ToString(10) } - - -// Format is a support routine for fmt.Formatter. It accepts -// the formats 'b' (binary), 'o' (octal), and 'x' (hexadecimal). -// -func (x *Rational) Format(h fmt.State, c int) { fmt.Fprintf(h, "%s", x.ToString(fmtbase(c))) } - - -// RatFromString returns the rational number corresponding to the -// longest possible prefix of s representing a rational number in a -// given conversion base, the actual conversion base used, and the -// prefix length. The syntax of a rational number is: -// -// rational = mantissa [exponent] . -// mantissa = integer ('/' natural | '.' natural) . -// exponent = ('e'|'E') integer . -// -// If the base argument is 0, the string prefix determines the actual -// conversion base for the mantissa. A prefix of ``0x'' or ``0X'' selects -// base 16; the ``0'' prefix selects base 8. Otherwise the selected base is 10. -// If the mantissa is represented via a division, both the numerator and -// denominator may have different base prefixes; in that case the base of -// of the numerator is returned. If the mantissa contains a decimal point, -// the base for the fractional part is the same as for the part before the -// decimal point and the fractional part does not accept a base prefix. -// The base for the exponent is always 10. -// -func RatFromString(s string, base uint) (*Rational, uint, int) { - // read numerator - a, abase, alen := IntFromString(s, base) - b := Nat(1) - - // read denominator or fraction, if any - var blen int - if alen < len(s) { - ch := s[alen] - if ch == '/' { - alen++ - b, base, blen = NatFromString(s[alen:], base) - } else if ch == '.' { - alen++ - b, base, blen = NatFromString(s[alen:], abase) - assert(base == abase) - f := Nat(uint64(base)).Pow(uint(blen)) - a = MakeInt(a.sign, a.mant.Mul(f).Add(b)) - b = f - } - } - - // read exponent, if any - rlen := alen + blen - if rlen < len(s) { - ch := s[rlen] - if ch == 'e' || ch == 'E' { - rlen++ - e, _, elen := IntFromString(s[rlen:], 10) - rlen += elen - m := Nat(10).Pow(uint(e.mant.Value())) - if e.sign { - b = b.Mul(m) - } else { - a = a.MulNat(m) - } - } - } - - return MakeRat(a, b), base, rlen -} diff --git a/src/pkg/exp/datafmt/Makefile b/src/pkg/exp/datafmt/Makefile index 40543b195..aa9453897 100644 --- a/src/pkg/exp/datafmt/Makefile +++ b/src/pkg/exp/datafmt/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=exp/datafmt GOFILES=\ diff --git a/src/pkg/exp/datafmt/datafmt.go b/src/pkg/exp/datafmt/datafmt.go index e77f445b5..46c412342 100644 --- a/src/pkg/exp/datafmt/datafmt.go +++ b/src/pkg/exp/datafmt/datafmt.go @@ -656,7 +656,7 @@ func (s *State) eval(fexpr expr, value reflect.Value, index int) bool { // Eval formats each argument according to the format // f and returns the resulting []byte and os.Error. If -// an error occured, the []byte contains the partially +// an error occurred, the []byte contains the partially // formatted result. An environment env may be passed // in which is available in custom formatters through // the state parameter. @@ -697,7 +697,7 @@ func (f Format) Eval(env Environment, args ...interface{}) ([]byte, os.Error) { // written and an os.Error, if any. // func (f Format) Fprint(w io.Writer, env Environment, args ...interface{}) (int, os.Error) { - data, err := f.Eval(env, args) + data, err := f.Eval(env, args...) if err != nil { // TODO should we print partial result in case of error? return 0, err @@ -711,7 +711,7 @@ func (f Format) Fprint(w io.Writer, env Environment, args ...interface{}) (int, // number of bytes written and an os.Error, if any. // func (f Format) Print(args ...interface{}) (int, os.Error) { - return f.Fprint(os.Stdout, nil, args) + return f.Fprint(os.Stdout, nil, args...) } @@ -722,7 +722,7 @@ func (f Format) Print(args ...interface{}) (int, os.Error) { // func (f Format) Sprint(args ...interface{}) string { var buf bytes.Buffer - _, err := f.Fprint(&buf, nil, args) + _, err := f.Fprint(&buf, nil, args...) if err != nil { var i interface{} = args fmt.Fprintf(&buf, "--- Sprint(%s) failed: %v", fmt.Sprint(i), err) diff --git a/src/pkg/exp/datafmt/datafmt_test.go b/src/pkg/exp/datafmt/datafmt_test.go index 908894717..f6a09f820 100644 --- a/src/pkg/exp/datafmt/datafmt_test.go +++ b/src/pkg/exp/datafmt/datafmt_test.go @@ -7,11 +7,15 @@ package datafmt import ( "fmt" "testing" + "go/token" ) +var fset = token.NewFileSet() + + func parse(t *testing.T, form string, fmap FormatterMap) Format { - f, err := Parse("", []byte(form), fmap) + f, err := Parse(fset, "", []byte(form), fmap) if err != nil { t.Errorf("Parse(%s): %v", form, err) return nil @@ -24,7 +28,7 @@ func verify(t *testing.T, f Format, expected string, args ...interface{}) { if f == nil { return // allow other tests to run } - result := f.Sprint(args) + result := f.Sprint(args...) if result != expected { t.Errorf( "result : `%s`\nexpected: `%s`\n\n", @@ -97,7 +101,7 @@ func check(t *testing.T, form, expected string, args ...interface{}) { if f == nil { return // allow other tests to run } - result := f.Sprint(args) + result := f.Sprint(args...) if result != expected { t.Errorf( "format : %s\nresult : `%s`\nexpected: `%s`\n\n", diff --git a/src/pkg/exp/datafmt/parser.go b/src/pkg/exp/datafmt/parser.go index de1f1c2a6..a01378ea5 100644 --- a/src/pkg/exp/datafmt/parser.go +++ b/src/pkg/exp/datafmt/parser.go @@ -19,9 +19,10 @@ import ( type parser struct { scanner.ErrorVector scanner scanner.Scanner - pos token.Position // token position - tok token.Token // one token look-ahead - lit []byte // token literal + file *token.File + pos token.Pos // token position + tok token.Token // one token look-ahead + lit []byte // token literal packs map[string]string // PackageName -> ImportPath rules map[string]expr // RuleName -> Expression @@ -39,18 +40,23 @@ func (p *parser) next() { } -func (p *parser) init(filename string, src []byte) { +func (p *parser) init(fset *token.FileSet, filename string, src []byte) { p.ErrorVector.Reset() - p.scanner.Init(filename, src, p, scanner.AllowIllegalChars) // return '@' as token.ILLEGAL w/o error message - p.next() // initializes pos, tok, lit + p.file = p.scanner.Init(fset, filename, src, p, scanner.AllowIllegalChars) // return '@' as token.ILLEGAL w/o error message + p.next() // initializes pos, tok, lit p.packs = make(map[string]string) p.rules = make(map[string]expr) } -func (p *parser) errorExpected(pos token.Position, msg string) { +func (p *parser) error(pos token.Pos, msg string) { + p.Error(p.file.Position(pos), msg) +} + + +func (p *parser) errorExpected(pos token.Pos, msg string) { msg = "expected " + msg - if pos.Offset == p.pos.Offset { + if pos == p.pos { // the error happened at the current position; // make the error message more specific msg += ", found '" + p.tok.String() + "'" @@ -58,11 +64,11 @@ func (p *parser) errorExpected(pos token.Position, msg string) { msg += " " + string(p.lit) } } - p.Error(pos, msg) + p.error(pos, msg) } -func (p *parser) expect(tok token.Token) token.Position { +func (p *parser) expect(tok token.Token) token.Pos { pos := p.pos if p.tok != tok { p.errorExpected(pos, "'"+tok.String()+"'") @@ -87,7 +93,7 @@ func (p *parser) parseTypeName() (string, bool) { if importPath, found := p.packs[name]; found { name = importPath } else { - p.Error(pos, "package not declared: "+name) + p.error(pos, "package not declared: "+name) } p.next() name, isIdent = name+"."+p.parseIdentifier(), false @@ -303,11 +309,11 @@ func (p *parser) parseFormat() { // add package declaration if !isIdent { - p.Error(pos, "illegal package name: "+name) + p.error(pos, "illegal package name: "+name) } else if _, found := p.packs[name]; !found { p.packs[name] = importPath } else { - p.Error(pos, "package already declared: "+name) + p.error(pos, "package already declared: "+name) } case token.ASSIGN: @@ -319,7 +325,7 @@ func (p *parser) parseFormat() { if _, found := p.rules[name]; !found { p.rules[name] = x } else { - p.Error(pos, "format rule already declared: "+name) + p.error(pos, "format rule already declared: "+name) } default: @@ -358,10 +364,10 @@ func remap(p *parser, name string) string { // there are no errors, the result is a Format and the error is nil. // Otherwise the format is nil and a non-empty ErrorList is returned. // -func Parse(filename string, src []byte, fmap FormatterMap) (Format, os.Error) { +func Parse(fset *token.FileSet, filename string, src []byte, fmap FormatterMap) (Format, os.Error) { // parse source var p parser - p.init(filename, src) + p.init(fset, filename, src) p.parseFormat() // add custom formatters, if any diff --git a/src/pkg/exp/draw/Makefile b/src/pkg/exp/draw/Makefile index 7ab574482..6f0f0b2f5 100644 --- a/src/pkg/exp/draw/Makefile +++ b/src/pkg/exp/draw/Makefile @@ -2,12 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=exp/draw GOFILES=\ - arith.go\ - color.go\ draw.go\ event.go\ diff --git a/src/pkg/exp/draw/arith.go b/src/pkg/exp/draw/arith.go deleted file mode 100644 index b72242aaa..000000000 --- a/src/pkg/exp/draw/arith.go +++ /dev/null @@ -1,155 +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 draw - -// A Point is an X, Y coordinate pair. -type Point struct { - X, Y int -} - -// ZP is the zero Point. -var ZP Point - -// A Rectangle contains the Points with Min.X <= X < Max.X, Min.Y <= Y < Max.Y. -type Rectangle struct { - Min, Max Point -} - -// ZR is the zero Rectangle. -var ZR Rectangle - -// Pt is shorthand for Point{X, Y}. -func Pt(X, Y int) Point { return Point{X, Y} } - -// Rect is shorthand for Rectangle{Pt(x0, y0), Pt(x1, y1)}. -func Rect(x0, y0, x1, y1 int) Rectangle { return Rectangle{Point{x0, y0}, Point{x1, y1}} } - -// Rpt is shorthand for Rectangle{min, max}. -func Rpt(min, max Point) Rectangle { return Rectangle{min, max} } - -// Add returns the sum of p and q: Pt(p.X+q.X, p.Y+q.Y). -func (p Point) Add(q Point) Point { return Point{p.X + q.X, p.Y + q.Y} } - -// Sub returns the difference of p and q: Pt(p.X-q.X, p.Y-q.Y). -func (p Point) Sub(q Point) Point { return Point{p.X - q.X, p.Y - q.Y} } - -// Mul returns p scaled by k: Pt(p.X*k p.Y*k). -func (p Point) Mul(k int) Point { return Point{p.X * k, p.Y * k} } - -// Div returns p divided by k: Pt(p.X/k, p.Y/k). -func (p Point) Div(k int) Point { return Point{p.X / k, p.Y / k} } - -// Eq returns true if p and q are equal. -func (p Point) Eq(q Point) bool { return p.X == q.X && p.Y == q.Y } - -// In returns true if p is within r. -func (p Point) In(r Rectangle) bool { - return p.X >= r.Min.X && p.X < r.Max.X && - p.Y >= r.Min.Y && p.Y < r.Max.Y -} - -// Inset returns the rectangle r inset by n: Rect(r.Min.X+n, r.Min.Y+n, r.Max.X-n, r.Max.Y-n). -func (r Rectangle) Inset(n int) Rectangle { - return Rectangle{Point{r.Min.X + n, r.Min.Y + n}, Point{r.Max.X - n, r.Max.Y - n}} -} - -// Add returns the rectangle r translated by p: Rpt(r.Min.Add(p), r.Max.Add(p)). -func (r Rectangle) Add(p Point) Rectangle { return Rectangle{r.Min.Add(p), r.Max.Add(p)} } - -// Sub returns the rectangle r translated by -p: Rpt(r.Min.Sub(p), r.Max.Sub(p)). -func (r Rectangle) Sub(p Point) Rectangle { return Rectangle{r.Min.Sub(p), r.Max.Sub(p)} } - -// Canon returns a canonical version of r: the returned rectangle -// has Min.X <= Max.X and Min.Y <= Max.Y. -func (r Rectangle) Canon() Rectangle { - if r.Max.X < r.Min.X { - r.Min.X, r.Max.X = r.Max.X, r.Min.X - } - if r.Max.Y < r.Min.Y { - r.Min.Y, r.Max.Y = r.Max.Y, r.Min.Y - } - return r -} - -// Overlaps returns true if r and r1 cross; that is, it returns true if they share any point. -func (r Rectangle) Overlaps(r1 Rectangle) bool { - return r.Min.X < r1.Max.X && r1.Min.X < r.Max.X && - r.Min.Y < r1.Max.Y && r1.Min.Y < r.Max.Y -} - -// Empty retruns true if r contains no points. -func (r Rectangle) Empty() bool { return r.Max.X <= r.Min.X || r.Max.Y <= r.Min.Y } - -// InRect returns true if all the points in r are also in r1. -func (r Rectangle) In(r1 Rectangle) bool { - if r.Empty() { - return true - } - if r1.Empty() { - return false - } - return r1.Min.X <= r.Min.X && r.Max.X <= r1.Max.X && - r1.Min.Y <= r.Min.Y && r.Max.Y <= r1.Max.Y -} - -// Combine returns the smallest rectangle containing all points from r and from r1. -func (r Rectangle) Combine(r1 Rectangle) Rectangle { - if r.Empty() { - return r1 - } - if r1.Empty() { - return r - } - if r.Min.X > r1.Min.X { - r.Min.X = r1.Min.X - } - if r.Min.Y > r1.Min.Y { - r.Min.Y = r1.Min.Y - } - if r.Max.X < r1.Max.X { - r.Max.X = r1.Max.X - } - if r.Max.Y < r1.Max.Y { - r.Max.Y = r1.Max.Y - } - return r -} - -// Clip returns the largest rectangle containing only points shared by r and r1. -func (r Rectangle) Clip(r1 Rectangle) Rectangle { - if r.Empty() { - return r - } - if r1.Empty() { - return r1 - } - if !r.Overlaps(r1) { - return Rectangle{r.Min, r.Min} - } - if r.Min.X < r1.Min.X { - r.Min.X = r1.Min.X - } - if r.Min.Y < r1.Min.Y { - r.Min.Y = r1.Min.Y - } - if r.Max.X > r1.Max.X { - r.Max.X = r1.Max.X - } - if r.Max.Y > r1.Max.Y { - r.Max.Y = r1.Max.Y - } - return r -} - -// Dx returns the width of the rectangle r: r.Max.X - r.Min.X. -func (r Rectangle) Dx() int { return r.Max.X - r.Min.X } - -// Dy returns the width of the rectangle r: r.Max.Y - r.Min.Y. -func (r Rectangle) Dy() int { return r.Max.Y - r.Min.Y } - -// Eq returns true if r and r1 are equal. -func (r Rectangle) Eq(r1 Rectangle) bool { - return r.Min.Eq(r1.Min) && r.Max.Eq(r1.Max) -} diff --git a/src/pkg/exp/draw/color.go b/src/pkg/exp/draw/color.go deleted file mode 100644 index 3fe7b4abc..000000000 --- a/src/pkg/exp/draw/color.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package draw - -import "image" - -// A Color represents a color with 8-bit R, G, B, and A values, -// packed into a uint32—0xRRGGBBAA—so that comparison -// is defined on colors. -// Color implements image.Color. -// Color also implements image.Image: it is a -// 10⁹x10⁹-pixel image of uniform color. -type Color uint32 - -// Check that Color implements image.Color and image.Image -var _ image.Color = Black -var _ image.Image = Black - -var ( - Opaque Color = 0xFFFFFFFF - Transparent Color = 0x00000000 - Black Color = 0x000000FF - White Color = 0xFFFFFFFF - Red Color = 0xFF0000FF - Green Color = 0x00FF00FF - Blue Color = 0x0000FFFF - Cyan Color = 0x00FFFFFF - Magenta Color = 0xFF00FFFF - Yellow Color = 0xFFFF00FF - PaleYellow Color = 0xFFFFAAFF - DarkYellow Color = 0xEEEE9EFF - DarkGreen Color = 0x448844FF - PaleGreen Color = 0xAAFFAAFF - MedGreen Color = 0x88CC88FF - DarkBlue Color = 0x000055FF - PaleBlueGreen Color = 0xAAFFFFFF - PaleBlue Color = 0x0000BBFF - BlueGreen Color = 0x008888FF - GreyGreen Color = 0x55AAAAFF - PaleGreyGreen Color = 0x9EEEEEFF - YellowGreen Color = 0x99994CFF - MedBlue Color = 0x000099FF - GreyBlue Color = 0x005DBBFF - PaleGreyBlue Color = 0x4993DDFF - PurpleBlue Color = 0x8888CCFF -) - -func (c Color) RGBA() (r, g, b, a uint32) { - x := uint32(c) - r, g, b, a = x>>24, (x>>16)&0xFF, (x>>8)&0xFF, x&0xFF - r |= r << 8 - g |= g << 8 - b |= b << 8 - a |= a << 8 - return -} - -// SetAlpha returns the color obtained by changing -// c's alpha value to a and scaling r, g, and b appropriately. -func (c Color) SetAlpha(a uint8) Color { - r, g, b, oa := c>>24, (c>>16)&0xFF, (c>>8)&0xFF, c&0xFF - if oa == 0 { - return 0 - } - r = r * Color(a) / oa - if r < 0 { - r = 0 - } - if r > 0xFF { - r = 0xFF - } - g = g * Color(a) / oa - if g < 0 { - g = 0 - } - if g > 0xFF { - g = 0xFF - } - b = b * Color(a) / oa - if b < 0 { - b = 0 - } - if b > 0xFF { - b = 0xFF - } - return r<<24 | g<<16 | b<<8 | Color(a) -} - -func (c Color) Width() int { return 1e9 } - -func (c Color) Height() int { return 1e9 } - -func (c Color) At(x, y int) image.Color { return c } - -func toColor(color image.Color) image.Color { - if c, ok := color.(Color); ok { - return c - } - r, g, b, a := color.RGBA() - return Color(r>>8<<24 | g>>8<<16 | b>>8<<8 | a>>8) -} - -func (c Color) ColorModel() image.ColorModel { return image.ColorModelFunc(toColor) } diff --git a/src/pkg/exp/draw/draw.go b/src/pkg/exp/draw/draw.go index 415dd99ac..1d0729d92 100644 --- a/src/pkg/exp/draw/draw.go +++ b/src/pkg/exp/draw/draw.go @@ -8,8 +8,6 @@ // and the X Render extension. package draw -// BUG(rsc): This is a toy library and not ready for production use. - import "image" // m is the maximum color value returned by image.Color.RGBA. @@ -34,22 +32,22 @@ type Image interface { } // Draw calls DrawMask with a nil mask and an Over op. -func Draw(dst Image, r Rectangle, src image.Image, sp Point) { - DrawMask(dst, r, src, sp, nil, ZP, Over) +func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) { + DrawMask(dst, r, src, sp, nil, image.ZP, Over) } // DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r // in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque. -// The implementation is simple and slow. -// TODO(nigeltao): Optimize this. -func DrawMask(dst Image, r Rectangle, src image.Image, sp Point, mask image.Image, mp Point, op Op) { - dx, dy := src.Width()-sp.X, src.Height()-sp.Y +func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { + sb := src.Bounds() + dx, dy := sb.Max.X-sp.X, sb.Max.Y-sp.Y if mask != nil { - if dx > mask.Width()-mp.X { - dx = mask.Width() - mp.X + mb := mask.Bounds() + if dx > mb.Max.X-mp.X { + dx = mb.Max.X - mp.X } - if dy > mask.Height()-mp.Y { - dy = mask.Height() - mp.Y + if dy > mb.Max.Y-mp.Y { + dy = mb.Max.Y - mp.Y } } if r.Dx() > dx { @@ -58,45 +56,38 @@ func DrawMask(dst Image, r Rectangle, src image.Image, sp Point, mask image.Imag if r.Dy() > dy { r.Max.Y = r.Min.Y + dy } - - // TODO(nigeltao): Clip r to dst's bounding box, and handle the case when sp or mp has negative X or Y. - // TODO(nigeltao): Ensure that r is well formed, i.e. r.Max.X >= r.Min.X and likewise for Y. + r = r.Intersect(dst.Bounds()) + if r.Empty() { + return + } // Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation. if dst0, ok := dst.(*image.RGBA); ok { if op == Over { if mask == nil { - if src0, ok := src.(image.ColorImage); ok { + if src0, ok := src.(*image.ColorImage); ok { drawFillOver(dst0, r, src0) return } if src0, ok := src.(*image.RGBA); ok { - if dst0 == src0 && r.Overlaps(r.Add(sp.Sub(r.Min))) { - // TODO(nigeltao): Implement a fast path for the overlapping case. - } else { - drawCopyOver(dst0, r, src0, sp) - return - } + drawCopyOver(dst0, r, src0, sp) + return } } else if mask0, ok := mask.(*image.Alpha); ok { - if src0, ok := src.(image.ColorImage); ok { + if src0, ok := src.(*image.ColorImage); ok { drawGlyphOver(dst0, r, src0, mask0, mp) return } } } else { if mask == nil { - if src0, ok := src.(image.ColorImage); ok { + if src0, ok := src.(*image.ColorImage); ok { drawFillSrc(dst0, r, src0) return } if src0, ok := src.(*image.RGBA); ok { - if dst0 == src0 && r.Overlaps(r.Add(sp.Sub(r.Min))) { - // TODO(nigeltao): Implement a fast path for the overlapping case. - } else { - drawCopySrc(dst0, r, src0, sp) - return - } + drawCopySrc(dst0, r, src0, sp) + return } } } @@ -158,66 +149,96 @@ func DrawMask(dst Image, r Rectangle, src image.Image, sp Point, mask image.Imag } } -func drawFillOver(dst *image.RGBA, r Rectangle, src image.ColorImage) { +func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.ColorImage) { cr, cg, cb, ca := src.RGBA() // The 0x101 is here for the same reason as in drawRGBA. a := (m - ca) * 0x101 x0, x1 := r.Min.X, r.Max.X y0, y1 := r.Min.Y, r.Max.Y for y := y0; y != y1; y++ { - dpix := dst.Pixel[y] - for x := x0; x != x1; x++ { - rgba := dpix[x] + dbase := y * dst.Stride + dpix := dst.Pix[dbase+x0 : dbase+x1] + for i, rgba := range dpix { dr := (uint32(rgba.R)*a)/m + cr dg := (uint32(rgba.G)*a)/m + cg db := (uint32(rgba.B)*a)/m + cb da := (uint32(rgba.A)*a)/m + ca - dpix[x] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} + dpix[i] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} } } } -func drawCopyOver(dst *image.RGBA, r Rectangle, src *image.RGBA, sp Point) { - x0, x1 := r.Min.X, r.Max.X - y0, y1 := r.Min.Y, r.Max.Y - for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { - dpix := dst.Pixel[y] - spix := src.Pixel[sy] - for x, sx := x0, sp.X; x != x1; x, sx = x+1, sx+1 { - // For unknown reasons, even though both dpix[x] and spix[sx] are +func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) { + dx0, dx1 := r.Min.X, r.Max.X + dy0, dy1 := r.Min.Y, r.Max.Y + nrows := dy1 - dy0 + sx0, sx1 := sp.X, sp.X+dx1-dx0 + d0 := dy0*dst.Stride + dx0 + d1 := dy0*dst.Stride + dx1 + s0 := sp.Y*src.Stride + sx0 + s1 := sp.Y*src.Stride + sx1 + var ( + ddelta, sdelta int + i0, i1, idelta int + ) + if r.Min.Y < sp.Y || r.Min.Y == sp.Y && r.Min.X <= sp.X { + ddelta = dst.Stride + sdelta = src.Stride + i0, i1, idelta = 0, d1-d0, +1 + } else { + // If the source start point is higher than the destination start point, or equal height but to the left, + // then we compose the rows in right-to-left, bottom-up order instead of left-to-right, top-down. + d0 += (nrows - 1) * dst.Stride + d1 += (nrows - 1) * dst.Stride + s0 += (nrows - 1) * src.Stride + s1 += (nrows - 1) * src.Stride + ddelta = -dst.Stride + sdelta = -src.Stride + i0, i1, idelta = d1-d0-1, -1, -1 + } + for ; nrows > 0; nrows-- { + dpix := dst.Pix[d0:d1] + spix := src.Pix[s0:s1] + for i := i0; i != i1; i += idelta { + // For unknown reasons, even though both dpix[i] and spix[i] are // image.RGBAColors, on an x86 CPU it seems fastest to call RGBA // for the source but to do it manually for the destination. - sr, sg, sb, sa := spix[sx].RGBA() - drgba := dpix[x] - dr := uint32(drgba.R) - dg := uint32(drgba.G) - db := uint32(drgba.B) - da := uint32(drgba.A) + sr, sg, sb, sa := spix[i].RGBA() + rgba := dpix[i] + dr := uint32(rgba.R) + dg := uint32(rgba.G) + db := uint32(rgba.B) + da := uint32(rgba.A) // The 0x101 is here for the same reason as in drawRGBA. a := (m - sa) * 0x101 dr = (dr*a)/m + sr dg = (dg*a)/m + sg db = (db*a)/m + sb da = (da*a)/m + sa - dpix[x] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} + dpix[i] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} } + d0 += ddelta + d1 += ddelta + s0 += sdelta + s1 += sdelta } } -func drawGlyphOver(dst *image.RGBA, r Rectangle, src image.ColorImage, mask *image.Alpha, mp Point) { +func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.ColorImage, mask *image.Alpha, mp image.Point) { x0, x1 := r.Min.X, r.Max.X y0, y1 := r.Min.Y, r.Max.Y cr, cg, cb, ca := src.RGBA() for y, my := y0, mp.Y; y != y1; y, my = y+1, my+1 { - dpix := dst.Pixel[y] - mpix := mask.Pixel[my] - for x, mx := x0, mp.X; x != x1; x, mx = x+1, mx+1 { - ma := uint32(mpix[mx].A) + dbase := y * dst.Stride + dpix := dst.Pix[dbase+x0 : dbase+x1] + mbase := my * mask.Stride + mpix := mask.Pix[mbase+mp.X:] + for i, rgba := range dpix { + ma := uint32(mpix[i].A) if ma == 0 { continue } ma |= ma << 8 - rgba := dpix[x] dr := uint32(rgba.R) dg := uint32(rgba.G) db := uint32(rgba.B) @@ -228,12 +249,12 @@ func drawGlyphOver(dst *image.RGBA, r Rectangle, src image.ColorImage, mask *ima dg = (dg*a + cg*ma) / m db = (db*a + cb*ma) / m da = (da*a + ca*ma) / m - dpix[x] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} + dpix[i] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} } } } -func drawFillSrc(dst *image.RGBA, r Rectangle, src image.ColorImage) { +func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.ColorImage) { if r.Dy() < 1 { return } @@ -244,26 +265,53 @@ func drawFillSrc(dst *image.RGBA, r Rectangle, src image.ColorImage) { // then use the first row as the slice source for the remaining rows. dx0, dx1 := r.Min.X, r.Max.X dy0, dy1 := r.Min.Y, r.Max.Y - firstRow := dst.Pixel[dy0] - for x := dx0; x < dx1; x++ { - firstRow[x] = color + dbase := dy0 * dst.Stride + i0, i1 := dbase+dx0, dbase+dx1 + firstRow := dst.Pix[i0:i1] + for i := range firstRow { + firstRow[i] = color } - copySrc := firstRow[dx0:dx1] for y := dy0 + 1; y < dy1; y++ { - copy(dst.Pixel[y][dx0:dx1], copySrc) + i0 += dst.Stride + i1 += dst.Stride + copy(dst.Pix[i0:i1], firstRow) } } -func drawCopySrc(dst *image.RGBA, r Rectangle, src *image.RGBA, sp Point) { +func drawCopySrc(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) { dx0, dx1 := r.Min.X, r.Max.X dy0, dy1 := r.Min.Y, r.Max.Y + nrows := dy1 - dy0 sx0, sx1 := sp.X, sp.X+dx1-dx0 - for y, sy := dy0, sp.Y; y < dy1; y, sy = y+1, sy+1 { - copy(dst.Pixel[y][dx0:dx1], src.Pixel[sy][sx0:sx1]) + d0 := dy0*dst.Stride + dx0 + d1 := dy0*dst.Stride + dx1 + s0 := sp.Y*src.Stride + sx0 + s1 := sp.Y*src.Stride + sx1 + var ddelta, sdelta int + if r.Min.Y <= sp.Y { + ddelta = dst.Stride + sdelta = src.Stride + } else { + // If the source start point is higher than the destination start point, then we compose the rows + // in bottom-up order instead of top-down. Unlike the drawCopyOver function, we don't have to + // check the x co-ordinates because the built-in copy function can handle overlapping slices. + d0 += (nrows - 1) * dst.Stride + d1 += (nrows - 1) * dst.Stride + s0 += (nrows - 1) * src.Stride + s1 += (nrows - 1) * src.Stride + ddelta = -dst.Stride + sdelta = -src.Stride + } + for ; nrows > 0; nrows-- { + copy(dst.Pix[d0:d1], src.Pix[s0:s1]) + d0 += ddelta + d1 += ddelta + s0 += sdelta + s1 += sdelta } } -func drawRGBA(dst *image.RGBA, r Rectangle, src image.Image, sp Point, mask image.Image, mp Point, op Op) { +func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { x0, x1, dx := r.Min.X, r.Max.X, 1 y0, y1, dy := r.Min.Y, r.Max.Y, 1 if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) { @@ -278,7 +326,7 @@ func drawRGBA(dst *image.RGBA, r Rectangle, src image.Image, sp Point, mask imag for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { sx := sp.X + x0 - r.Min.X mx := mp.X + x0 - r.Min.X - dpix := dst.Pixel[y] + dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride] for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx { ma := uint32(m) if mask != nil { @@ -313,26 +361,3 @@ func drawRGBA(dst *image.RGBA, r Rectangle, src image.Image, sp Point, mask imag } } } - -// Border aligns r.Min in dst with sp in src and then replaces pixels -// in a w-pixel border around r in dst with the result of the Porter-Duff compositing -// operation ``src over dst.'' If w is positive, the border extends w pixels inside r. -// If w is negative, the border extends w pixels outside r. -func Border(dst Image, r Rectangle, w int, src image.Image, sp Point) { - i := w - if i > 0 { - // inside r - Draw(dst, Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+i), src, sp) // top - Draw(dst, Rect(r.Min.X, r.Min.Y+i, r.Min.X+i, r.Max.Y-i), src, sp.Add(Pt(0, i))) // left - Draw(dst, Rect(r.Max.X-i, r.Min.Y+i, r.Max.X, r.Max.Y-i), src, sp.Add(Pt(r.Dx()-i, i))) // right - Draw(dst, Rect(r.Min.X, r.Max.Y-i, r.Max.X, r.Max.Y), src, sp.Add(Pt(0, r.Dy()-i))) // bottom - return - } - - // outside r; - i = -i - Draw(dst, Rect(r.Min.X-i, r.Min.Y-i, r.Max.X+i, r.Min.Y), src, sp.Add(Pt(-i, -i))) // top - Draw(dst, Rect(r.Min.X-i, r.Min.Y, r.Min.X, r.Max.Y), src, sp.Add(Pt(-i, 0))) // left - Draw(dst, Rect(r.Max.X, r.Min.Y, r.Max.X+i, r.Max.Y), src, sp.Add(Pt(r.Dx(), 0))) // right - Draw(dst, Rect(r.Min.X-i, r.Max.Y, r.Max.X+i, r.Max.Y+i), src, sp.Add(Pt(-i, 0))) // bottom -} diff --git a/src/pkg/exp/draw/draw_test.go b/src/pkg/exp/draw/draw_test.go index e9fde2535..90c9e823d 100644 --- a/src/pkg/exp/draw/draw_test.go +++ b/src/pkg/exp/draw/draw_test.go @@ -16,11 +16,11 @@ func eq(c0, c1 image.Color) bool { } func fillBlue(alpha int) image.Image { - return image.ColorImage{image.RGBAColor{0, 0, uint8(alpha), uint8(alpha)}} + return image.NewColorImage(image.RGBAColor{0, 0, uint8(alpha), uint8(alpha)}) } func fillAlpha(alpha int) image.Image { - return image.ColorImage{image.AlphaColor{uint8(alpha)}} + return image.NewColorImage(image.AlphaColor{uint8(alpha)}) } func vgradGreen(alpha int) image.Image { @@ -53,6 +53,16 @@ func hgradRed(alpha int) Image { return m } +func gradYellow(alpha int) Image { + m := image.NewRGBA(16, 16) + for y := 0; y < 16; y++ { + for x := 0; x < 16; x++ { + m.Set(x, y, image.RGBAColor{uint8(x * alpha / 15), uint8(y * alpha / 15), 0, uint8(alpha)}) + } + } + return m +} + type drawTest struct { desc string src image.Image @@ -63,54 +73,62 @@ type drawTest struct { var drawTests = []drawTest{ // Uniform mask (0% opaque). - drawTest{"nop", vgradGreen(255), fillAlpha(0), Over, image.RGBAColor{136, 0, 0, 255}}, - drawTest{"clear", vgradGreen(255), fillAlpha(0), Src, image.RGBAColor{0, 0, 0, 0}}, + {"nop", vgradGreen(255), fillAlpha(0), Over, image.RGBAColor{136, 0, 0, 255}}, + {"clear", vgradGreen(255), fillAlpha(0), Src, image.RGBAColor{0, 0, 0, 0}}, // Uniform mask (100%, 75%, nil) and uniform source. // At (x, y) == (8, 8): // The destination pixel is {136, 0, 0, 255}. // The source pixel is {0, 0, 90, 90}. - drawTest{"fill", fillBlue(90), fillAlpha(255), Over, image.RGBAColor{88, 0, 90, 255}}, - drawTest{"fillSrc", fillBlue(90), fillAlpha(255), Src, image.RGBAColor{0, 0, 90, 90}}, - drawTest{"fillAlpha", fillBlue(90), fillAlpha(192), Over, image.RGBAColor{100, 0, 68, 255}}, - drawTest{"fillAlphaSrc", fillBlue(90), fillAlpha(192), Src, image.RGBAColor{0, 0, 68, 68}}, - drawTest{"fillNil", fillBlue(90), nil, Over, image.RGBAColor{88, 0, 90, 255}}, - drawTest{"fillNilSrc", fillBlue(90), nil, Src, image.RGBAColor{0, 0, 90, 90}}, + {"fill", fillBlue(90), fillAlpha(255), Over, image.RGBAColor{88, 0, 90, 255}}, + {"fillSrc", fillBlue(90), fillAlpha(255), Src, image.RGBAColor{0, 0, 90, 90}}, + {"fillAlpha", fillBlue(90), fillAlpha(192), Over, image.RGBAColor{100, 0, 68, 255}}, + {"fillAlphaSrc", fillBlue(90), fillAlpha(192), Src, image.RGBAColor{0, 0, 68, 68}}, + {"fillNil", fillBlue(90), nil, Over, image.RGBAColor{88, 0, 90, 255}}, + {"fillNilSrc", fillBlue(90), nil, Src, image.RGBAColor{0, 0, 90, 90}}, // Uniform mask (100%, 75%, nil) and variable source. // At (x, y) == (8, 8): // The destination pixel is {136, 0, 0, 255}. // The source pixel is {0, 48, 0, 90}. - drawTest{"copy", vgradGreen(90), fillAlpha(255), Over, image.RGBAColor{88, 48, 0, 255}}, - drawTest{"copySrc", vgradGreen(90), fillAlpha(255), Src, image.RGBAColor{0, 48, 0, 90}}, - drawTest{"copyAlpha", vgradGreen(90), fillAlpha(192), Over, image.RGBAColor{100, 36, 0, 255}}, - drawTest{"copyAlphaSrc", vgradGreen(90), fillAlpha(192), Src, image.RGBAColor{0, 36, 0, 68}}, - drawTest{"copyNil", vgradGreen(90), nil, Over, image.RGBAColor{88, 48, 0, 255}}, - drawTest{"copyNilSrc", vgradGreen(90), nil, Src, image.RGBAColor{0, 48, 0, 90}}, + {"copy", vgradGreen(90), fillAlpha(255), Over, image.RGBAColor{88, 48, 0, 255}}, + {"copySrc", vgradGreen(90), fillAlpha(255), Src, image.RGBAColor{0, 48, 0, 90}}, + {"copyAlpha", vgradGreen(90), fillAlpha(192), Over, image.RGBAColor{100, 36, 0, 255}}, + {"copyAlphaSrc", vgradGreen(90), fillAlpha(192), Src, image.RGBAColor{0, 36, 0, 68}}, + {"copyNil", vgradGreen(90), nil, Over, image.RGBAColor{88, 48, 0, 255}}, + {"copyNilSrc", vgradGreen(90), nil, Src, image.RGBAColor{0, 48, 0, 90}}, // Variable mask and variable source. // At (x, y) == (8, 8): // The destination pixel is {136, 0, 0, 255}. // The source pixel is {0, 0, 255, 255}. // The mask pixel's alpha is 102, or 40%. - drawTest{"generic", fillBlue(255), vgradAlpha(192), Over, image.RGBAColor{81, 0, 102, 255}}, - drawTest{"genericSrc", fillBlue(255), vgradAlpha(192), Src, image.RGBAColor{0, 0, 102, 102}}, + {"generic", fillBlue(255), vgradAlpha(192), Over, image.RGBAColor{81, 0, 102, 255}}, + {"genericSrc", fillBlue(255), vgradAlpha(192), Src, image.RGBAColor{0, 0, 102, 102}}, } -func makeGolden(dst image.Image, t drawTest) image.Image { +func makeGolden(dst, src, mask image.Image, op Op) image.Image { // Since golden is a newly allocated image, we don't have to check if the // input source and mask images and the output golden image overlap. - golden := image.NewRGBA(dst.Width(), dst.Height()) - for y := 0; y < golden.Height(); y++ { - my, sy := y, y - for x := 0; x < golden.Width(); x++ { - mx, sx := x, x + b := dst.Bounds() + sx0 := src.Bounds().Min.X - b.Min.X + sy0 := src.Bounds().Min.Y - b.Min.Y + var mx0, my0 int + if mask != nil { + mx0 = mask.Bounds().Min.X - b.Min.X + my0 = mask.Bounds().Min.Y - b.Min.Y + } + golden := image.NewRGBA(b.Max.X, b.Max.Y) + for y := b.Min.Y; y < b.Max.Y; y++ { + my, sy := my0+y, sy0+y + for x := b.Min.X; x < b.Max.X; x++ { + mx, sx := mx0+x, sx0+x const M = 1<<16 - 1 var dr, dg, db, da uint32 - if t.op == Over { + if op == Over { dr, dg, db, da = dst.At(x, y).RGBA() } - sr, sg, sb, sa := t.src.At(sx, sy).RGBA() + sr, sg, sb, sa := src.At(sx, sy).RGBA() ma := uint32(M) - if t.mask != nil { - _, _, _, ma = t.mask.At(mx, my).RGBA() + if mask != nil { + _, _, _, ma = mask.At(mx, my).RGBA() } a := M - (sa * ma / M) golden.Set(x, y, image.RGBA64Color{ @@ -121,6 +139,7 @@ func makeGolden(dst image.Image, t drawTest) image.Image { }) } } + golden.Rect = b return golden } @@ -129,9 +148,14 @@ loop: for _, test := range drawTests { dst := hgradRed(255) // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation. - golden := makeGolden(dst, test) + golden := makeGolden(dst, test.src, test.mask, test.op) + b := dst.Bounds() + if !b.Eq(golden.Bounds()) { + t.Errorf("draw %s: bounds %v versus %v", test.desc, dst.Bounds(), golden.Bounds()) + continue + } // Draw the same combination onto the actual dst using the optimized DrawMask implementation. - DrawMask(dst, Rect(0, 0, dst.Width(), dst.Height()), test.src, ZP, test.mask, ZP, test.op) + DrawMask(dst, b, test.src, image.ZP, test.mask, image.ZP, test.op) // Check that the resultant pixel at (8, 8) matches what we expect // (the expected value can be verified by hand). if !eq(dst.At(8, 8), test.expected) { @@ -139,8 +163,8 @@ loop: continue } // Check that the resultant dst image matches the golden output. - for y := 0; y < golden.Height(); y++ { - for x := 0; x < golden.Width(); x++ { + for y := b.Min.Y; y < b.Max.Y; y++ { + for x := b.Min.X; x < b.Max.X; x++ { if !eq(dst.At(x, y), golden.At(x, y)) { t.Errorf("draw %s: at (%d, %d), %v versus golden %v", test.desc, x, y, dst.At(x, y), golden.At(x, y)) continue loop @@ -150,6 +174,45 @@ loop: } } +func TestDrawOverlap(t *testing.T) { + for _, op := range []Op{Over, Src} { + for yoff := -2; yoff <= 2; yoff++ { + loop: + for xoff := -2; xoff <= 2; xoff++ { + m := gradYellow(127).(*image.RGBA) + dst := &image.RGBA{ + Pix: m.Pix, + Stride: m.Stride, + Rect: image.Rect(5, 5, 10, 10), + } + src := &image.RGBA{ + Pix: m.Pix, + Stride: m.Stride, + Rect: image.Rect(5+xoff, 5+yoff, 10+xoff, 10+yoff), + } + // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation. + golden := makeGolden(dst, src, nil, op) + b := dst.Bounds() + if !b.Eq(golden.Bounds()) { + t.Errorf("drawOverlap xoff=%d,yoff=%d: bounds %v versus %v", xoff, yoff, dst.Bounds(), golden.Bounds()) + continue + } + // Draw the same combination onto the actual dst using the optimized DrawMask implementation. + DrawMask(dst, b, src, src.Bounds().Min, nil, image.ZP, op) + // Check that the resultant dst image matches the golden output. + for y := b.Min.Y; y < b.Max.Y; y++ { + for x := b.Min.X; x < b.Max.X; x++ { + if !eq(dst.At(x, y), golden.At(x, y)) { + t.Errorf("drawOverlap xoff=%d,yoff=%d: at (%d, %d), %v versus golden %v", xoff, yoff, x, y, dst.At(x, y), golden.At(x, y)) + continue loop + } + } + } + } + } + } +} + // TestIssue836 verifies http://code.google.com/p/go/issues/detail?id=836. func TestIssue836(t *testing.T) { a := image.NewRGBA(1, 1) @@ -158,7 +221,7 @@ func TestIssue836(t *testing.T) { b.Set(1, 0, image.RGBAColor{0, 0, 5, 5}) b.Set(0, 1, image.RGBAColor{0, 5, 0, 5}) b.Set(1, 1, image.RGBAColor{5, 0, 0, 5}) - Draw(a, Rect(0, 0, 1, 1), b, Pt(1, 1)) + Draw(a, image.Rect(0, 0, 1, 1), b, image.Pt(1, 1)) if !eq(image.RGBAColor{5, 0, 0, 5}, a.At(0, 0)) { t.Errorf("Issue 836: want %v got %v", image.RGBAColor{5, 0, 0, 5}, a.At(0, 0)) } diff --git a/src/pkg/exp/draw/event.go b/src/pkg/exp/draw/event.go index 155922d1c..b777d912e 100644 --- a/src/pkg/exp/draw/event.go +++ b/src/pkg/exp/draw/event.go @@ -4,43 +4,53 @@ package draw -// A Context represents a single graphics window. -type Context interface { - // Screen returns an editable Image of window. +import ( + "image" + "os" +) + +// A Window represents a single graphics window. +type Window interface { + // Screen returns an editable Image for the window. Screen() Image - // FlushImage flushes changes made to Screen() back to screen. FlushImage() + // EventChan returns a channel carrying UI events such as key presses, + // mouse movements and window resizes. + EventChan() <-chan interface{} + // Close closes the window. + Close() os.Error +} - // KeyboardChan returns a channel carrying keystrokes. - // An event is sent each time a key is pressed or released. +// A KeyEvent is sent for a key press or release. +type KeyEvent struct { // The value k represents key k being pressed. // The value -k represents key k being released. // The specific set of key values is not specified, - // but ordinary character represent themselves. - KeyboardChan() <-chan int - - // MouseChan returns a channel carrying mouse events. - // A new event is sent each time the mouse moves or a - // button is pressed or released. - MouseChan() <-chan Mouse + // but ordinary characters represent themselves. + Key int +} - // ResizeChan returns a channel carrying resize events. - // An event is sent each time the window is resized; - // the client should respond by calling Screen() to obtain - // the new screen image. - // The value sent on the channel is always ``true'' and can be ignored. - ResizeChan() <-chan bool +// A MouseEvent is sent for a button press or release or for a mouse movement. +type MouseEvent struct { + // Buttons is a bit mask of buttons: 1<<0 is left, 1<<1 middle, 1<<2 right. + // It represents button state and not necessarily the state delta: bit 0 + // being on means that the left mouse button is down, but does not imply + // that the same button was up in the previous MouseEvent. + Buttons int + // Loc is the location of the cursor. + Loc image.Point + // Nsec is the event's timestamp. + Nsec int64 +} - // QuitChan returns a channel carrying quit requests. - // After reading a value from the quit channel, the application - // should exit. - QuitChan() <-chan bool +// A ConfigEvent is sent each time the window's color model or size changes. +// The client should respond by calling Window.Screen to obtain a new image. +type ConfigEvent struct { + Config image.Config } -// A Mouse represents the state of the mouse. -type Mouse struct { - Buttons int // bit mask of buttons: 1<<0 is left, 1<<1 middle, 1<<2 right - Point // location of cursor - Nsec int64 // time stamp +// An ErrEvent is sent when an error occurs. +type ErrEvent struct { + Err os.Error } diff --git a/src/pkg/exp/draw/x11/Makefile b/src/pkg/exp/draw/x11/Makefile index d4e65ca73..205b3a65b 100644 --- a/src/pkg/exp/draw/x11/Makefile +++ b/src/pkg/exp/draw/x11/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../../Make.$(GOARCH) +include ../../../../Make.inc TARG=exp/draw/x11 GOFILES=\ diff --git a/src/pkg/exp/draw/x11/conn.go b/src/pkg/exp/draw/x11/conn.go index 979ce2b7d..da2181536 100644 --- a/src/pkg/exp/draw/x11/conn.go +++ b/src/pkg/exp/draw/x11/conn.go @@ -8,17 +8,17 @@ // A summary of the wire format can be found in XCB's xproto.xml. package x11 -// BUG(nigeltao): This is a toy library and not ready for production use. - import ( "bufio" "exp/draw" "image" "io" + "log" "net" "os" "strconv" "strings" + "time" ) type resID uint32 // X resource IDs. @@ -35,9 +35,6 @@ const ( ) type conn struct { - // TODO(nigeltao): Figure out which goroutine should be responsible for closing c, - // or if there is a race condition if one goroutine calls c.Close whilst another one - // is reading from r, or writing to w. c io.Closer r *bufio.Reader w *bufio.Writer @@ -45,11 +42,8 @@ type conn struct { gc, window, root, visual resID img *image.RGBA - kbd chan int - mouse chan draw.Mouse - resize chan bool - quit chan bool - mouseState draw.Mouse + eventc chan interface{} + mouseState draw.MouseEvent buf [256]byte // General purpose scratch buffer. @@ -58,25 +52,24 @@ type conn struct { flushBuf1 [4 * 1024]byte } -// flusher runs in its own goroutine, serving both FlushImage calls directly from the exp/draw client -// and indirectly from X expose events. It paints c.img to the X server via PutImage requests. -func (c *conn) flusher() { - for { - _ = <-c.flush - if closed(c.flush) { - return +// writeSocket runs in its own goroutine, serving both FlushImage calls +// directly from the exp/draw client and indirectly from X expose events. +// It paints c.img to the X server via PutImage requests. +func (c *conn) writeSocket() { + defer c.c.Close() + for _ = range c.flush { + b := c.img.Bounds() + if b.Empty() { + continue } - // Each X request has a 16-bit length (in terms of 4-byte units). To avoid going over // this limit, we send PutImage for each row of the image, rather than trying to paint // the entire image in one X request. This approach could easily be optimized (or the // X protocol may have an escape sequence to delimit very large requests). // TODO(nigeltao): See what XCB's xcb_put_image does in this situation. - w, h := c.img.Width(), c.img.Height() - units := 6 + w - if units > 0xffff || h > 0xffff { - // This window is too large for X. - close(c.flush) + units := 6 + b.Dx() + if units > 0xffff || b.Dy() > 0xffff { + log.Print("x11: window is too large for PutImage") return } @@ -86,19 +79,20 @@ func (c *conn) flusher() { c.flushBuf0[3] = uint8(units >> 8) setU32LE(c.flushBuf0[4:8], uint32(c.window)) setU32LE(c.flushBuf0[8:12], uint32(c.gc)) - setU32LE(c.flushBuf0[12:16], 1<<16|uint32(w)) + setU32LE(c.flushBuf0[12:16], 1<<16|uint32(b.Dx())) c.flushBuf0[21] = 0x18 // depth = 24 bits. - for y := 0; y < h; y++ { + for y := b.Min.Y; y < b.Max.Y; y++ { setU32LE(c.flushBuf0[16:20], uint32(y<<16)) - _, err := c.w.Write(c.flushBuf0[0:24]) - if err != nil { - close(c.flush) + if _, err := c.w.Write(c.flushBuf0[0:24]); err != nil { + if err != os.EOF { + log.Println("x11:", err.String()) + } return } - p := c.img.Pixel[y] - for x := 0; x < w; { - nx := w - x + p := c.img.Pix[y*c.img.Stride : (y+1)*c.img.Stride] + for x := b.Min.X; x < b.Max.X; { + nx := b.Max.X - x if nx > len(c.flushBuf1)/4 { nx = len(c.flushBuf1) / 4 } @@ -108,15 +102,18 @@ func (c *conn) flusher() { c.flushBuf1[4*i+2] = rgba.R } x += nx - _, err := c.w.Write(c.flushBuf1[0 : 4*nx]) - if err != nil { - close(c.flush) + if _, err := c.w.Write(c.flushBuf1[0 : 4*nx]); err != nil { + if err != os.EOF { + log.Println("x11:", err.String()) + } return } } } - if c.w.Flush() != nil { - close(c.flush) + if err := c.w.Flush(); err != nil { + if err != os.EOF { + log.Println("x11:", err.String()) + } return } } @@ -131,32 +128,32 @@ func (c *conn) FlushImage() { _ = c.flush <- false } -func (c *conn) KeyboardChan() <-chan int { return c.kbd } - -func (c *conn) MouseChan() <-chan draw.Mouse { return c.mouse } - -func (c *conn) ResizeChan() <-chan bool { return c.resize } +func (c *conn) Close() os.Error { + // Shut down the writeSocket goroutine. This will close the socket to the + // X11 server, which will cause c.eventc to close. + close(c.flush) + for _ = range c.eventc { + // Drain the channel to allow the readSocket goroutine to shut down. + } + return nil +} -func (c *conn) QuitChan() <-chan bool { return c.quit } +func (c *conn) EventChan() <-chan interface{} { return c.eventc } -// pumper runs in its own goroutine, reading X events and demuxing them over the kbd / mouse / resize / quit chans. -func (c *conn) pumper() { +// readSocket runs in its own goroutine, reading X events and sending draw +// events on c's EventChan. +func (c *conn) readSocket() { var ( keymap [256][]int keysymsPerKeycode int ) - defer close(c.flush) - // TODO(nigeltao): Is this the right place for defer c.c.Close()? - // TODO(nigeltao): Should we explicitly defer close our kbd/mouse/resize/quit chans? + defer close(c.eventc) for { // X events are always 32 bytes long. - _, err := io.ReadFull(c.r, c.buf[0:32]) - if err != nil { - // TODO(nigeltao): should draw.Context expose err? - // TODO(nigeltao): should we do c.quit<-true? Should c.quit be a buffered channel? - // Or is c.quit only for non-exceptional closing (e.g. when the window manager destroys - // our window), and not for e.g. an I/O error? - os.Stderr.Write([]byte(err.String())) + if _, err := io.ReadFull(c.r, c.buf[0:32]); err != nil { + if err != os.EOF { + c.eventc <- draw.ErrEvent{err} + } return } switch c.buf[0] { @@ -165,7 +162,7 @@ func (c *conn) pumper() { if cookie != 1 { // We issued only one request (GetKeyboardMapping) with a cookie of 1, // so we shouldn't get any other reply from the X server. - os.Stderr.Write([]byte("exp/draw/x11: unexpected cookie\n")) + c.eventc <- draw.ErrEvent{os.NewError("x11: unexpected cookie")} return } keysymsPerKeycode = int(c.buf[1]) @@ -178,7 +175,9 @@ func (c *conn) pumper() { for j := range m { u, err := readU32LE(c.r, c.buf[0:4]) if err != nil { - os.Stderr.Write([]byte(err.String())) + if err != os.EOF { + c.eventc <- draw.ErrEvent{err} + } return } m[j] = int(u) @@ -199,14 +198,14 @@ func (c *conn) pumper() { if keysym == 0 { keysym = keymap[keycode][0] } - // TODO(nigeltao): Should we send KeyboardChan ints for Shift/Ctrl/Alt? Should Shift-A send + // TODO(nigeltao): Should we send KeyEvents for Shift/Ctrl/Alt? Should Shift-A send // the same int down the channel as the sent on just the A key? // TODO(nigeltao): How should IME events (e.g. key presses that should generate CJK text) work? Or - // is that outside the scope of the draw.Context interface? + // is that outside the scope of the draw.Window interface? if c.buf[0] == 0x03 { keysym = -keysym } - c.kbd <- keysym + c.eventc <- draw.KeyEvent{keysym} case 0x04, 0x05: // Button press, button release. mask := 1 << (c.buf[1] - 1) if c.buf[0] == 0x04 { @@ -214,22 +213,21 @@ func (c *conn) pumper() { } else { c.mouseState.Buttons &^= mask } - // TODO(nigeltao): update mouseState's timestamp. - c.mouse <- c.mouseState + c.mouseState.Nsec = time.Nanoseconds() + c.eventc <- c.mouseState case 0x06: // Motion notify. - c.mouseState.Point.X = int(c.buf[25])<<8 | int(c.buf[24]) - c.mouseState.Point.Y = int(c.buf[27])<<8 | int(c.buf[26]) - // TODO(nigeltao): update mouseState's timestamp. - c.mouse <- c.mouseState + c.mouseState.Loc.X = int(int16(c.buf[25])<<8 | int16(c.buf[24])) + c.mouseState.Loc.Y = int(int16(c.buf[27])<<8 | int16(c.buf[26])) + c.mouseState.Nsec = time.Nanoseconds() + c.eventc <- c.mouseState case 0x0c: // Expose. // A single user action could trigger multiple expose events (e.g. if moving another - // window with XShape'd rounded corners over our window). In that case, the X server - // will send a count (in bytes 16-17) of the number of additional expose events coming. + // window with XShape'd rounded corners over our window). In that case, the X server will + // send a uint16 count (in bytes 16-17) of the number of additional expose events coming. // We could parse each event for the (x, y, width, height) and maintain a minimal dirty // rectangle, but for now, the simplest approach is to paint the entire window, when // receiving the final event in the series. - count := int(c.buf[17])<<8 | int(c.buf[16]) - if count == 0 { + if c.buf[17] == 0 && c.buf[16] == 0 { // TODO(nigeltao): Should we ignore the very first expose event? A freshly mapped window // will trigger expose, but until the first c.FlushImage call, there's probably nothing to // paint but black. For an 800x600 window, at 4 bytes per pixel, each repaint writes about @@ -491,16 +489,13 @@ func (c *conn) handshake() os.Error { if err != nil { return err } - // Read the vendor length. + // Read the vendor length and round it up to a multiple of 4, + // for X11 protocol alignment reasons. vendorLen, err := readU16LE(c.r, c.buf[0:2]) if err != nil { return err } - if vendorLen != 20 { - // For now, assume the vendor is "The X.Org Foundation". Supporting different - // vendors would require figuring out how much padding we need to read. - return os.NewError("unsupported X vendor") - } + vendorLen = (vendorLen + 3) &^ 3 // Read the maximum request length. maxReqLen, err := readU16LE(c.r, c.buf[0:2]) if err != nil { @@ -519,10 +514,13 @@ func (c *conn) handshake() os.Error { if err != nil { return err } - // Ignore some things that we don't care about (totalling 30 bytes): + // Ignore some things that we don't care about (totalling 10 + vendorLen bytes): // imageByteOrder(1), bitmapFormatBitOrder(1), bitmapFormatScanlineUnit(1) bitmapFormatScanlinePad(1), - // minKeycode(1), maxKeycode(1), padding(4), vendor(20, hard-coded above). - _, err = io.ReadFull(c.r, c.buf[0:30]) + // minKeycode(1), maxKeycode(1), padding(4), vendor (vendorLen). + if 10+int(vendorLen) > cap(c.buf) { + return os.NewError("unsupported X vendor") + } + _, err = io.ReadFull(c.r, c.buf[0:10+int(vendorLen)]) if err != nil { return err } @@ -550,7 +548,7 @@ func (c *conn) handshake() os.Error { } // NewWindow calls NewWindowDisplay with $DISPLAY. -func NewWindow() (draw.Context, os.Error) { +func NewWindow() (draw.Window, os.Error) { display := os.Getenv("DISPLAY") if len(display) == 0 { return nil, os.NewError("$DISPLAY not set") @@ -558,10 +556,10 @@ func NewWindow() (draw.Context, os.Error) { return NewWindowDisplay(display) } -// NewWindowDisplay returns a new draw.Context, backed by a newly created and +// NewWindowDisplay returns a new draw.Window, backed by a newly created and // mapped X11 window. The X server to connect to is specified by the display // string, such as ":1". -func NewWindowDisplay(display string) (draw.Context, os.Error) { +func NewWindowDisplay(display string) (draw.Window, os.Error) { socket, displayStr, err := connect(display) if err != nil { return nil, err @@ -616,13 +614,9 @@ func NewWindowDisplay(display string) (draw.Context, os.Error) { } c.img = image.NewRGBA(windowWidth, windowHeight) - // TODO(nigeltao): Should these channels be buffered? - c.kbd = make(chan int) - c.mouse = make(chan draw.Mouse) - c.resize = make(chan bool) - c.quit = make(chan bool) + c.eventc = make(chan interface{}, 16) c.flush = make(chan bool, 1) - go c.flusher() - go c.pumper() + go c.readSocket() + go c.writeSocket() return c, nil } diff --git a/src/pkg/exp/eval/Makefile b/src/pkg/exp/eval/Makefile index eac844f1e..2b716b14c 100644 --- a/src/pkg/exp/eval/Makefile +++ b/src/pkg/exp/eval/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=exp/eval GOFILES=\ @@ -16,14 +16,22 @@ GOFILES=\ stmt.go\ type.go\ typec.go\ - util.go\ value.go\ world.go\ include ../../../Make.pkg main.$O: main.go $(pkgdir)/$(TARG).a - $(QUOTED_GOBIN)/$(GC) $< + $(GC) $< eval: main.$O - $(QUOTED_GOBIN)/$(LD) -o $@ $< + $(LD) -o $@ $< + +gen.$O: gen.go + $(GC) $< + +generate: gen.$O $(pkgdir)/$(TARG).a + $(LD) -o $@ $<;\ + ./generate > expr1.go;\ + gofmt -w expr1.go + diff --git a/src/pkg/exp/eval/bridge.go b/src/pkg/exp/eval/bridge.go index c53febc8a..3fa498d68 100644 --- a/src/pkg/exp/eval/bridge.go +++ b/src/pkg/exp/eval/bridge.go @@ -29,7 +29,7 @@ func TypeFromNative(t reflect.Type) Type { var nt *NamedType if t.Name() != "" { name := t.PkgPath() + "·" + t.Name() - nt = &NamedType{token.Position{}, name, nil, true, make(map[string]Method)} + nt = &NamedType{token.NoPos, name, nil, true, make(map[string]Method)} evalTypes[t] = nt } @@ -79,7 +79,7 @@ func TypeFromNative(t reflect.Type) Type { case *reflect.ArrayType: et = NewArrayType(int64(t.Len()), TypeFromNative(t.Elem())) case *reflect.ChanType: - log.Crashf("%T not implemented", t) + log.Panicf("%T not implemented", t) case *reflect.FuncType: nin := t.NumIn() // Variadic functions have DotDotDotType at the end @@ -97,9 +97,9 @@ func TypeFromNative(t reflect.Type) Type { } et = NewFuncType(in, variadic, out) case *reflect.InterfaceType: - log.Crashf("%T not implemented", t) + log.Panicf("%T not implemented", t) case *reflect.MapType: - log.Crashf("%T not implemented", t) + log.Panicf("%T not implemented", t) case *reflect.PtrType: et = NewPtrType(TypeFromNative(t.Elem())) case *reflect.SliceType: @@ -116,9 +116,9 @@ func TypeFromNative(t reflect.Type) Type { } et = NewStructType(fields) case *reflect.UnsafePointerType: - log.Crashf("%T not implemented", t) + log.Panicf("%T not implemented", t) default: - log.Crashf("unexpected reflect.Type: %T", t) + log.Panicf("unexpected reflect.Type: %T", t) } if nt != nil { diff --git a/src/pkg/exp/eval/compiler.go b/src/pkg/exp/eval/compiler.go index 3e37bfbaa..9d2923bfc 100644 --- a/src/pkg/exp/eval/compiler.go +++ b/src/pkg/exp/eval/compiler.go @@ -11,24 +11,20 @@ import ( ) -type positioned interface { - Pos() token.Position -} - - // A compiler captures information used throughout an entire // compilation. Currently it includes only the error handler. // // TODO(austin) This might actually represent package level, in which // case it should be package compiler. type compiler struct { + fset *token.FileSet errors scanner.ErrorHandler numErrors int silentErrors int } -func (a *compiler) diagAt(pos positioned, format string, args ...interface{}) { - a.errors.Error(pos.Pos(), fmt.Sprintf(format, args)) +func (a *compiler) diagAt(pos token.Pos, format string, args ...interface{}) { + a.errors.Error(a.fset.Position(pos), fmt.Sprintf(format, args...)) a.numErrors++ } @@ -64,9 +60,9 @@ type label struct { continuePC *uint // The position where this label was resolved. If it has not // been resolved yet, an invalid position. - resolved token.Position + resolved token.Pos // The position where this label was first jumped to. - used token.Position + used token.Pos } // A funcCompiler captures information used throughout the compilation diff --git a/src/pkg/exp/eval/eval_test.go b/src/pkg/exp/eval/eval_test.go index 1dfdfe1fd..6bfe9089d 100644 --- a/src/pkg/exp/eval/eval_test.go +++ b/src/pkg/exp/eval/eval_test.go @@ -5,15 +5,20 @@ package eval import ( - "exp/bignum" + "big" "flag" "fmt" + "go/token" "log" "os" "reflect" + "regexp" "testing" ) +// All tests are done using the same file set. +var fset = token.NewFileSet() + // Print each statement or expression before parsing it var noisy = false @@ -48,7 +53,7 @@ func (a test) run(t *testing.T, name string) { println("code:", src) } - code, err := w.Compile(src) + code, err := w.Compile(fset, src) if err != nil { if j.cerr == "" { t.Errorf("%s: Compile %s: %v", name, src, err) @@ -89,9 +94,9 @@ func (a test) run(t *testing.T, name string) { } func match(t *testing.T, err os.Error, pat string) bool { - ok, errstr := testing.MatchString(pat, err.String()) - if errstr != "" { - t.Fatalf("compile regexp %s: %v", pat, errstr) + ok, err1 := regexp.MatchString(pat, err.String()) + if err1 != nil { + t.Fatalf("compile regexp %s: %v", pat, err1) } return ok } @@ -102,40 +107,40 @@ func match(t *testing.T, err os.Error, pat string) bool { */ // Expression compile error -func CErr(expr string, cerr string) test { return test([]job{job{code: expr, cerr: cerr}}) } +func CErr(expr string, cerr string) test { return test([]job{{code: expr, cerr: cerr}}) } // Expression runtime error -func RErr(expr string, rterr string) test { return test([]job{job{code: expr, rterr: rterr}}) } +func RErr(expr string, rterr string) test { return test([]job{{code: expr, rterr: rterr}}) } // Expression value func Val(expr string, val interface{}) test { - return test([]job{job{code: expr, val: toValue(val)}}) + return test([]job{{code: expr, val: toValue(val)}}) } // Statement runs without error -func Run(stmts string) test { return test([]job{job{code: stmts, noval: true}}) } +func Run(stmts string) test { return test([]job{{code: stmts, noval: true}}) } // Two statements without error. // TODO(rsc): Should be possible with Run but the parser // won't let us do both top-level and non-top-level statements. func Run2(stmt1, stmt2 string) test { - return test([]job{job{code: stmt1, noval: true}, job{code: stmt2, noval: true}}) + return test([]job{{code: stmt1, noval: true}, {code: stmt2, noval: true}}) } // Statement runs and test one expression's value func Val1(stmts string, expr1 string, val1 interface{}) test { return test([]job{ - job{code: stmts, noval: true}, - job{code: expr1, val: toValue(val1)}, + {code: stmts, noval: true}, + {code: expr1, val: toValue(val1)}, }) } // Statement runs and test two expressions' values func Val2(stmts string, expr1 string, val1 interface{}, expr2 string, val2 interface{}) test { return test([]job{ - job{code: stmts, noval: true}, - job{code: expr1, val: toValue(val1)}, - job{code: expr2, val: toValue(val2)}, + {code: stmts, noval: true}, + {code: expr1, val: toValue(val1)}, + {code: expr2, val: toValue(val2)}, }) } @@ -166,12 +171,12 @@ func toValue(val interface{}) Value { case int: r := intV(val) return &r - case *bignum.Integer: + case *big.Int: return &idealIntV{val} case float: r := floatV(val) return &r - case *bignum.Rational: + case *big.Rat: return &idealFloatV{val} case string: r := stringV(val) @@ -195,7 +200,7 @@ func toValue(val interface{}) Value { case Func: return &funcV{val} } - log.Crashf("toValue(%T) not implemented", val) + log.Panicf("toValue(%T) not implemented", val) panic("unreachable") } @@ -205,7 +210,7 @@ func toValue(val interface{}) Value { type testFunc struct{} -func (*testFunc) NewFrame() *Frame { return &Frame{nil, &[2]Value{}} } +func (*testFunc) NewFrame() *Frame { return &Frame{nil, make([]Value, 2)} } func (*testFunc) Call(t *Thread) { n := t.f.Vars[0].(IntValue).Get(t) @@ -217,7 +222,7 @@ func (*testFunc) Call(t *Thread) { type oneTwoFunc struct{} -func (*oneTwoFunc) NewFrame() *Frame { return &Frame{nil, &[2]Value{}} } +func (*oneTwoFunc) NewFrame() *Frame { return &Frame{nil, make([]Value, 2)} } func (*oneTwoFunc) Call(t *Thread) { t.f.Vars[0].(IntValue).Set(t, 1) @@ -235,13 +240,13 @@ func newTestWorld() *World { def := func(name string, t Type, val interface{}) { w.DefineVar(name, t, toValue(val)) } - w.DefineConst("c", IdealIntType, toValue(bignum.Int(1))) + w.DefineConst("c", IdealIntType, toValue(big.NewInt(1))) def("i", IntType, 1) def("i2", IntType, 2) def("u", UintType, uint(1)) def("f", FloatType, 1.0) def("s", StringType, "abc") - def("t", NewStructType([]StructField{StructField{"a", IntType, false}}), vstruct{1}) + def("t", NewStructType([]StructField{{"a", IntType, false}}), vstruct{1}) def("ai", NewArrayType(2, IntType), varray{1, 2}) def("aai", NewArrayType(2, NewArrayType(2, IntType)), varray{varray{1, 2}, varray{3, 4}}) def("aai2", NewArrayType(2, NewArrayType(2, IntType)), varray{varray{5, 6}, varray{7, 8}}) diff --git a/src/pkg/exp/eval/expr.go b/src/pkg/exp/eval/expr.go index ea8117d06..70f63cf2d 100644 --- a/src/pkg/exp/eval/expr.go +++ b/src/pkg/exp/eval/expr.go @@ -5,7 +5,7 @@ package eval import ( - "exp/bignum" + "big" "fmt" "go/ast" "go/token" @@ -15,6 +15,11 @@ import ( "os" ) +var ( + idealZero = big.NewInt(0) + idealOne = big.NewInt(1) +) + // An expr is the result of compiling an expression. It stores the // type of the expression and its evaluator function. type expr struct { @@ -52,7 +57,7 @@ type expr struct { // compiled from it. type exprInfo struct { *compiler - pos token.Position + pos token.Pos } func (a *exprInfo) newExpr(t Type, desc string) *expr { @@ -60,7 +65,7 @@ func (a *exprInfo) newExpr(t Type, desc string) *expr { } func (a *exprInfo) diag(format string, args ...interface{}) { - a.diagAt(&a.pos, format, args) + a.diagAt(a.pos, format, args...) } func (a *exprInfo) diagOpType(op token.Token, vt Type) { @@ -82,10 +87,10 @@ func (a *exprInfo) diagOpTypes(op token.Token, lt Type, rt Type) { // TODO(austin) Rename to resolveIdeal or something? func (a *expr) convertTo(t Type) *expr { if !a.t.isIdeal() { - log.Crashf("attempted to convert from %v, expected ideal", a.t) + log.Panicf("attempted to convert from %v, expected ideal", a.t) } - var rat *bignum.Rational + var rat *big.Rat // XXX(Spec) The spec says "It is erroneous". // @@ -97,24 +102,24 @@ func (a *expr) convertTo(t Type) *expr { case IdealFloatType: rat = a.asIdealFloat()() if t.isInteger() && !rat.IsInt() { - a.diag("constant %v truncated to integer", ratToString(rat)) + a.diag("constant %v truncated to integer", rat.FloatString(6)) return nil } case IdealIntType: i := a.asIdealInt()() - rat = bignum.MakeRat(i, bignum.Nat(1)) + rat = new(big.Rat).SetInt(i) default: - log.Crashf("unexpected ideal type %v", a.t) + log.Panicf("unexpected ideal type %v", a.t) } // Check bounds if t, ok := t.lit().(BoundedType); ok { if rat.Cmp(t.minVal()) < 0 { - a.diag("constant %v underflows %v", ratToString(rat), t) + a.diag("constant %v underflows %v", rat.FloatString(6), t) return nil } if rat.Cmp(t.maxVal()) > 0 { - a.diag("constant %v overflows %v", ratToString(rat), t) + a.diag("constant %v overflows %v", rat.FloatString(6), t) return nil } } @@ -123,27 +128,28 @@ func (a *expr) convertTo(t Type) *expr { res := a.newExpr(t, a.desc) switch t := t.lit().(type) { case *uintType: - n, d := rat.Value() - f := n.Quo(bignum.MakeInt(false, d)) - v := f.Abs().Value() + n, d := rat.Num(), rat.Denom() + f := new(big.Int).Quo(n, d) + f = f.Abs(f) + v := uint64(f.Int64()) res.eval = func(*Thread) uint64 { return v } case *intType: - n, d := rat.Value() - f := n.Quo(bignum.MakeInt(false, d)) - v := f.Value() + n, d := rat.Num(), rat.Denom() + f := new(big.Int).Quo(n, d) + v := f.Int64() res.eval = func(*Thread) int64 { return v } case *idealIntType: - n, d := rat.Value() - f := n.Quo(bignum.MakeInt(false, d)) - res.eval = func() *bignum.Integer { return f } + n, d := rat.Num(), rat.Denom() + f := new(big.Int).Quo(n, d) + res.eval = func() *big.Int { return f } case *floatType: - n, d := rat.Value() - v := float64(n.Value()) / float64(d.Value()) + n, d := rat.Num(), rat.Denom() + v := float64(n.Int64()) / float64(d.Int64()) res.eval = func(*Thread) float64 { return v } case *idealFloatType: - res.eval = func() *bignum.Rational { return rat } + res.eval = func() *big.Rat { return rat } default: - log.Crashf("cannot convert to type %T", t) + log.Panicf("cannot convert to type %T", t) } return res @@ -158,7 +164,7 @@ func (a *expr) convertToInt(max int64, negErr string, errOp string) *expr { switch a.t.lit().(type) { case *idealIntType: val := a.asIdealInt()() - if negErr != "" && val.IsNeg() { + if negErr != "" && val.Sign() < 0 { a.diag("negative %s: %s", negErr, val) return nil } @@ -166,7 +172,7 @@ func (a *expr) convertToInt(max int64, negErr string, errOp string) *expr { if negErr == "slice" { bound++ } - if max != -1 && val.Cmp(bignum.Int(bound)) >= 0 { + if max != -1 && val.Cmp(big.NewInt(bound)) >= 0 { a.diag("index %s exceeds length %d", val, max) return nil } @@ -196,7 +202,7 @@ func (a *expr) derefArray() *expr { if _, ok := pt.Elem.lit().(*ArrayType); ok { deref := a.compileStarExpr(a) if deref == nil { - log.Crashf("failed to dereference *array") + log.Panicf("failed to dereference *array") } return deref } @@ -223,7 +229,7 @@ func (a *expr) derefArray() *expr { // multi-valued type. type assignCompiler struct { *compiler - pos token.Position + pos token.Pos // The RHS expressions. This may include nil's for // expressions that failed to compile. rs []*expr @@ -248,7 +254,7 @@ type assignCompiler struct { // assignCompiler with rmt set, but if type checking fails, slots in // the MultiType may be nil. If rs contains nil's, type checking will // fail and these expressions given a nil type. -func (a *compiler) checkAssign(pos token.Position, rs []*expr, errOp, errPosName string) (*assignCompiler, bool) { +func (a *compiler) checkAssign(pos token.Pos, rs []*expr, errOp, errPosName string) (*assignCompiler, bool) { c := &assignCompiler{ compiler: a, pos: pos, @@ -325,7 +331,7 @@ func (a *assignCompiler) compile(b *block, lt Type) func(Value, *Thread) { pos = a.rs[lcount-1].pos } } - a.diagAt(&pos, "%s %ss for %s\n\t%s\n\t%s", msg, a.errPosName, a.errOp, lt, rmt) + a.diagAt(pos, "%s %ss for %s\n\t%s\n\t%s", msg, a.errPosName, a.errOp, lt, rmt) return nil } @@ -364,7 +370,7 @@ func (a *assignCompiler) compile(b *block, lt Type) func(Value, *Thread) { a.rs = make([]*expr, len(a.rmt.Elems)) for i, t := range a.rmt.Elems { if t.isIdeal() { - log.Crashf("Right side of unpack contains ideal: %s", rmt) + log.Panicf("Right side of unpack contains ideal: %s", rmt) } a.rs[i] = orig.newExpr(t, orig.desc) index := i @@ -447,7 +453,7 @@ func (a *assignCompiler) compile(b *block, lt Type) func(Value, *Thread) { // compileAssign compiles an assignment operation without the full // generality of an assignCompiler. See assignCompiler for a // description of the arguments. -func (a *compiler) compileAssign(pos token.Position, b *block, lt Type, rs []*expr, errOp, errPosName string) func(Value, *Thread) { +func (a *compiler) compileAssign(pos token.Pos, b *block, lt Type, rs []*expr, errOp, errPosName string) func(Value, *Thread) { ac, ok := a.checkAssign(pos, rs, errOp, errPosName) if !ok { return nil @@ -490,7 +496,7 @@ func (a *exprCompiler) compile(x ast.Expr, callCtx bool) *expr { case token.STRING: return ei.compileStringLit(string(x.Value)) default: - log.Crashf("unexpected basic literal type %v", x.Kind) + log.Panicf("unexpected basic literal type %v", x.Kind) } case *ast.CompositeLit: @@ -508,7 +514,7 @@ func (a *exprCompiler) compile(x ast.Expr, callCtx bool) *expr { return nil } if a.constant { - a.diagAt(x, "function literal used in constant expression") + a.diagAt(x.Pos(), "function literal used in constant expression") return nil } return ei.compileFuncLit(decl, fn) @@ -565,12 +571,12 @@ func (a *exprCompiler) compile(x ast.Expr, callCtx bool) *expr { return nil } if a.constant { - a.diagAt(x, "function call in constant context") + a.diagAt(x.Pos(), "function call in constant context") return nil } if l.valType != nil { - a.diagAt(x, "type conversions not implemented") + a.diagAt(x.Pos(), "type conversions not implemented") return nil } else if ft, ok := l.t.(*FuncType); ok && ft.builtin != "" { return ei.compileBuiltinCallExpr(a.block, ft, args) @@ -579,7 +585,7 @@ func (a *exprCompiler) compile(x ast.Expr, callCtx bool) *expr { } case *ast.Ident: - return ei.compileIdent(a.block, a.constant, callCtx, x.Name()) + return ei.compileIdent(a.block, a.constant, callCtx, x.Name) case *ast.IndexExpr: l, r := a.compile(x.X, false), a.compile(x.Index, false) @@ -589,15 +595,21 @@ func (a *exprCompiler) compile(x ast.Expr, callCtx bool) *expr { return ei.compileIndexExpr(l, r) case *ast.SliceExpr: - var hi *expr + var lo, hi *expr arr := a.compile(x.X, false) - lo := a.compile(x.Index, false) - if x.End == nil { + if x.Low == nil { + // beginning was omitted, so we need to provide it + ei := &exprInfo{a.compiler, x.Pos()} + lo = ei.compileIntLit("0") + } else { + lo = a.compile(x.Low, false) + } + if x.High == nil { // End was omitted, so we need to compute len(x.X) ei := &exprInfo{a.compiler, x.Pos()} hi = ei.compileBuiltinCallExpr(a.block, lenType, []*expr{arr}) } else { - hi = a.compile(x.End, false) + hi = a.compile(x.High, false) } if arr == nil || lo == nil || hi == nil { return nil @@ -615,7 +627,7 @@ func (a *exprCompiler) compile(x ast.Expr, callCtx bool) *expr { if v == nil { return nil } - return ei.compileSelectorExpr(v, x.Sel.Name()) + return ei.compileSelectorExpr(v, x.Sel.Name) case *ast.StarExpr: // We pass down our call context because this could be @@ -643,18 +655,18 @@ func (a *exprCompiler) compile(x ast.Expr, callCtx bool) *expr { } return ei.compileUnaryExpr(x.Op, v) } - log.Crashf("unexpected ast node type %T", x) + log.Panicf("unexpected ast node type %T", x) panic("unreachable") typeexpr: if !callCtx { - a.diagAt(x, "type used as expression") + a.diagAt(x.Pos(), "type used as expression") return nil } return ei.exprFromType(a.compileType(a.block, x)) notimpl: - a.diagAt(x, "%T expression node not implemented", x) + a.diagAt(x.Pos(), "%T expression node not implemented", x) return nil } @@ -705,7 +717,7 @@ func (a *exprInfo) compileIdent(b *block, constant bool, callCtx bool, name stri a.diag("type %v used as expression", name) return nil } - log.Crashf("name %s has unknown type %T", name, def) + log.Panicf("name %s has unknown type %T", name, def) panic("unreachable") } @@ -735,14 +747,14 @@ func (a *exprInfo) compileGlobalVariable(v *Variable) *expr { return expr } -func (a *exprInfo) compileIdealInt(i *bignum.Integer, desc string) *expr { +func (a *exprInfo) compileIdealInt(i *big.Int, desc string) *expr { expr := a.newExpr(IdealIntType, desc) - expr.eval = func() *bignum.Integer { return i } + expr.eval = func() *big.Int { return i } return expr } func (a *exprInfo) compileIntLit(lit string) *expr { - i, _, _ := bignum.IntFromString(lit, 0) + i, _ := new(big.Int).SetString(lit, 0) return a.compileIdealInt(i, "integer literal") } @@ -758,16 +770,16 @@ func (a *exprInfo) compileCharLit(lit string) *expr { a.silentErrors++ return nil } - return a.compileIdealInt(bignum.Int(int64(v)), "character literal") + return a.compileIdealInt(big.NewInt(int64(v)), "character literal") } func (a *exprInfo) compileFloatLit(lit string) *expr { - f, _, n := bignum.RatFromString(lit, 10) - if n != len(lit) { - log.Crashf("malformed float literal %s at %v passed parser", lit, a.pos) + f, ok := new(big.Rat).SetString(lit) + if !ok { + log.Panicf("malformed float literal %s at %v passed parser", lit, a.pos) } expr := a.newExpr(IdealFloatType, "float literal") - expr.eval = func() *bignum.Rational { return f } + expr.eval = func() *big.Rat { return f } return expr } @@ -822,7 +834,7 @@ func (a *exprInfo) compileSelectorExpr(v *expr, name string) *expr { ambig = true default: - log.Crashf("Marked field at depth %d, but already found one at depth %d", depth, bestDepth) + log.Panicf("Marked field at depth %d, but already found one at depth %d", depth, bestDepth) } amberr += "\n\t" + pathName[1:] } @@ -864,7 +876,7 @@ func (a *exprInfo) compileSelectorExpr(v *expr, name string) *expr { _, ok := ti.methods[name] if ok { mark(depth, pathName+"."+name) - log.Crash("Methods not implemented") + log.Panic("Methods not implemented") } t = ti.Def } @@ -996,7 +1008,7 @@ func (a *exprInfo) compileSliceExpr(arr, lo, hi *expr) *expr { } default: - log.Crashf("unexpected left operand type %T", arr.t.lit()) + log.Panicf("unexpected left operand type %T", arr.t.lit()) } return expr @@ -1120,7 +1132,7 @@ func (a *exprInfo) compileIndexExpr(l, r *expr) *expr { } default: - log.Crashf("unexpected left operand type %T", l.t.lit()) + log.Panicf("unexpected left operand type %T", l.t.lit()) } return expr @@ -1168,12 +1180,8 @@ func (a *exprInfo) compileCallExpr(b *block, l *expr, as []*expr) *expr { // Gather argument and out types to initialize frame variables vts := make([]Type, nin+nout) - for i, t := range lt.In { - vts[i] = t - } - for i, t := range lt.Out { - vts[i+nin] = t - } + copy(vts, lt.In) + copy(vts[nin:], lt.Out) // Compile lf := l.asFunc() @@ -1233,6 +1241,38 @@ func (a *exprInfo) compileBuiltinCallExpr(b *block, ft *FuncType, as []*expr) *e } return expr + case copyType: + if !checkCount(2, 2) { + return nil + } + src := as[1] + dst := as[0] + if src.t != dst.t { + a.diag("arguments to built-in function 'copy' must have same type\nsrc: %s\ndst: %s\n", src.t, dst.t) + return nil + } + if _, ok := src.t.lit().(*SliceType); !ok { + a.diag("src argument to 'copy' must be a slice (got: %s)", src.t) + return nil + } + if _, ok := dst.t.lit().(*SliceType); !ok { + a.diag("dst argument to 'copy' must be a slice (got: %s)", dst.t) + return nil + } + expr := a.newExpr(IntType, "function call") + srcf := src.asSlice() + dstf := dst.asSlice() + expr.eval = func(t *Thread) int64 { + src, dst := srcf(t), dstf(t) + nelems := src.Len + if nelems > dst.Len { + nelems = dst.Len + } + dst.Base.Sub(0, nelems).Assign(t, src.Base.Sub(0, nelems)) + return nelems + } + return expr + case lenType: if !checkCount(1, 1) { return nil @@ -1425,7 +1465,7 @@ func (a *exprInfo) compileBuiltinCallExpr(b *block, ft *FuncType, as []*expr) *e return expr } - log.Crashf("unexpected built-in function '%s'", ft.builtin) + log.Panicf("unexpected built-in function '%s'", ft.builtin) panic("unreachable") } @@ -1492,10 +1532,10 @@ func (a *exprInfo) compileUnaryExpr(op token.Token, v *expr) *expr { t = NewPtrType(v.t) case token.ARROW: - log.Crashf("Unary op %v not implemented", op) + log.Panicf("Unary op %v not implemented", op) default: - log.Crashf("unknown unary operator %v", op) + log.Panicf("unknown unary operator %v", op) } desc, ok := unaryOpDescs[op] @@ -1526,7 +1566,7 @@ func (a *exprInfo) compileUnaryExpr(op token.Token, v *expr) *expr { expr.eval = func(t *Thread) Value { return vf(t) } default: - log.Crashf("Compilation of unary op %v not implemented", op) + log.Panicf("Compilation of unary op %v not implemented", op) } return expr @@ -1650,7 +1690,7 @@ func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr { if l.t.isIdeal() && !r.t.isInteger() { r = r.convertTo(IdealIntType) if r == nil { - log.Crashf("conversion to uintType succeeded, but conversion to idealIntType failed") + log.Panicf("conversion to uintType succeeded, but conversion to idealIntType failed") } } } else if _, ok := r.t.lit().(*uintType); !ok { @@ -1693,7 +1733,7 @@ func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr { // The operands in channel sends differ in type: one // is always a channel and the other is a variable or // value of the channel's element type. - log.Crash("Binary op <- not implemented") + log.Panic("Binary op <- not implemented") t = BoolType case token.LSS, token.GTR, token.LEQ, token.GEQ: @@ -1761,7 +1801,7 @@ func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr { t = BoolType default: - log.Crashf("unknown binary operator %v", op) + log.Panicf("unknown binary operator %v", op) } desc, ok := binOpDescs[op] @@ -1774,8 +1814,8 @@ func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr { switch op { case token.QUO, token.REM: if r.t.isIdeal() { - if (r.t.isInteger() && r.asIdealInt()().IsZero()) || - (r.t.isFloat() && r.asIdealFloat()().IsZero()) { + if (r.t.isInteger() && r.asIdealInt()().Sign() == 0) || + (r.t.isFloat() && r.asIdealFloat()().Sign() == 0) { a.diag("divide by zero") return nil } @@ -1817,13 +1857,13 @@ func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr { lv := l.asIdealInt()() rv := r.asIdealInt()() const maxShift = 99999 - if rv.Cmp(bignum.Int(maxShift)) > 0 { + if rv.Cmp(big.NewInt(maxShift)) > 0 { a.diag("left shift by %v; exceeds implementation limit of %v", rv, maxShift) expr.t = nil return nil } - val := lv.Shl(uint(rv.Value())) - expr.eval = func() *bignum.Integer { return val } + val := new(big.Int).Lsh(lv, uint(rv.Int64())) + expr.eval = func() *big.Int { return val } } else { expr.genBinOpShl(l, r) } @@ -1832,8 +1872,8 @@ func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr { if l.t.isIdeal() { lv := l.asIdealInt()() rv := r.asIdealInt()() - val := lv.Shr(uint(rv.Value())) - expr.eval = func() *bignum.Integer { return val } + val := new(big.Int).Rsh(lv, uint(rv.Int64())) + expr.eval = func() *big.Int { return val } } else { expr.genBinOpShr(l, r) } @@ -1863,7 +1903,7 @@ func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr { expr.genBinOpLogOr(l, r) default: - log.Crashf("Compilation of binary op %v not implemented", op) + log.Panicf("Compilation of binary op %v not implemented", op) } return expr @@ -1886,7 +1926,7 @@ func (a *compiler) compileArrayLen(b *block, expr ast.Expr) (int64, bool) { } if !lenExpr.t.isInteger() { - a.diagAt(expr, "array size must be an integer") + a.diagAt(expr.Pos(), "array size must be an integer") return 0, false } @@ -1896,7 +1936,7 @@ func (a *compiler) compileArrayLen(b *block, expr ast.Expr) (int64, bool) { case *uintType: return int64(lenExpr.asUint()(nil)), true } - log.Crashf("unexpected integer type %T", lenExpr.t) + log.Panicf("unexpected integer type %T", lenExpr.t) return 0, false } @@ -1905,7 +1945,7 @@ func (a *compiler) compileExpr(b *block, constant bool, expr ast.Expr) *expr { nerr := a.numError() e := ec.compile(expr, false) if e == nil && nerr == a.numError() { - log.Crashf("expression compilation failed without reporting errors") + log.Panicf("expression compilation failed without reporting errors") } return e } @@ -1943,7 +1983,7 @@ func (a *expr) extractEffect(b *block, errOp string) (func(*Thread), *expr) { case tempType.isFloat(): tempType = FloatType default: - log.Crashf("unexpected ideal type %v", tempType) + log.Panicf("unexpected ideal type %v", tempType) } } temp := b.DefineTemp(tempType) @@ -1952,7 +1992,7 @@ func (a *expr) extractEffect(b *block, errOp string) (func(*Thread), *expr) { // Create "temp := rhs" assign := ac.compile(b, tempType) if assign == nil { - log.Crashf("compileAssign type check failed") + log.Panicf("compileAssign type check failed") } effect := func(t *Thread) { diff --git a/src/pkg/exp/eval/expr1.go b/src/pkg/exp/eval/expr1.go old mode 100644 new mode 100755 index f0a78ac4d..ae0cfc723 --- a/src/pkg/exp/eval/expr1.go +++ b/src/pkg/exp/eval/expr1.go @@ -4,13 +4,13 @@ package eval import ( - "exp/bignum" + "big" "log" ) /* - * "As" functions. These retrieve evaluator functions from an - * expr, panicking if the requested evaluator has the wrong type. +* "As" functions. These retrieve evaluator functions from an +* expr, panicking if the requested evaluator has the wrong type. */ func (a *expr) asBool() func(*Thread) bool { return a.eval.(func(*Thread) bool) @@ -21,14 +21,14 @@ func (a *expr) asUint() func(*Thread) uint64 { func (a *expr) asInt() func(*Thread) int64 { return a.eval.(func(*Thread) int64) } -func (a *expr) asIdealInt() func() *bignum.Integer { - return a.eval.(func() *bignum.Integer) +func (a *expr) asIdealInt() func() *big.Int { + return a.eval.(func() *big.Int) } func (a *expr) asFloat() func(*Thread) float64 { return a.eval.(func(*Thread) float64) } -func (a *expr) asIdealFloat() func() *bignum.Rational { - return a.eval.(func() *bignum.Rational) +func (a *expr) asIdealFloat() func() *big.Rat { + return a.eval.(func() *big.Rat) } func (a *expr) asString() func(*Thread) string { return a.eval.(func(*Thread) string) @@ -63,11 +63,11 @@ func (a *expr) asInterface() func(*Thread) interface{} { return func(t *Thread) interface{} { return sf(t) } case func(t *Thread) int64: return func(t *Thread) interface{} { return sf(t) } - case func() *bignum.Integer: + case func() *big.Int: return func(*Thread) interface{} { return sf() } case func(t *Thread) float64: return func(t *Thread) interface{} { return sf(t) } - case func() *bignum.Rational: + case func() *big.Rat: return func(*Thread) interface{} { return sf() } case func(t *Thread) string: return func(t *Thread) interface{} { return sf(t) } @@ -84,13 +84,13 @@ func (a *expr) asInterface() func(*Thread) interface{} { case func(t *Thread) Map: return func(t *Thread) interface{} { return sf(t) } default: - log.Crashf("unexpected expression node type %T at %v", a.eval, a.pos) + log.Panicf("unexpected expression node type %T at %v", a.eval, a.pos) } panic("fail") } /* - * Operator generators. +* Operator generators. */ func (a *expr) genConstant(v Value) { @@ -103,12 +103,12 @@ func (a *expr) genConstant(v Value) { a.eval = func(t *Thread) int64 { return v.(IntValue).Get(t) } case *idealIntType: val := v.(IdealIntValue).Get() - a.eval = func() *bignum.Integer { return val } + a.eval = func() *big.Int { return val } case *floatType: a.eval = func(t *Thread) float64 { return v.(FloatValue).Get(t) } case *idealFloatType: val := v.(IdealFloatValue).Get() - a.eval = func() *bignum.Rational { return val } + a.eval = func() *big.Rat { return val } case *stringType: a.eval = func(t *Thread) string { return v.(StringValue).Get(t) } case *ArrayType: @@ -124,7 +124,7 @@ func (a *expr) genConstant(v Value) { case *MapType: a.eval = func(t *Thread) Map { return v.(MapValue).Get(t) } default: - log.Crashf("unexpected constant type %v at %v", a.t, a.pos) + log.Panicf("unexpected constant type %v at %v", a.t, a.pos) } } @@ -154,7 +154,7 @@ func (a *expr) genIdentOp(level, index int) { case *MapType: a.eval = func(t *Thread) Map { return t.f.Get(level, index).(MapValue).Get(t) } default: - log.Crashf("unexpected identifier type %v at %v", a.t, a.pos) + log.Panicf("unexpected identifier type %v at %v", a.t, a.pos) } } @@ -186,7 +186,7 @@ func (a *expr) genFuncCall(call func(t *Thread) []Value) { case *MultiType: a.eval = func(t *Thread) []Value { return call(t) } default: - log.Crashf("unexpected result type %v at %v", a.t, a.pos) + log.Panicf("unexpected result type %v at %v", a.t, a.pos) } } @@ -216,7 +216,7 @@ func (a *expr) genValue(vf func(*Thread) Value) { case *MapType: a.eval = func(t *Thread) Map { return vf(t).(MapValue).Get(t) } default: - log.Crashf("unexpected result type %v at %v", a.t, a.pos) + log.Panicf("unexpected result type %v at %v", a.t, a.pos) } } @@ -229,18 +229,18 @@ func (a *expr) genUnaryOpNeg(v *expr) { vf := v.asInt() a.eval = func(t *Thread) int64 { v := vf(t); return -v } case *idealIntType: - v := v.asIdealInt()() - val := v.Neg() - a.eval = func() *bignum.Integer { return val } + val := v.asIdealInt()() + val.Neg(val) + a.eval = func() *big.Int { return val } case *floatType: vf := v.asFloat() a.eval = func(t *Thread) float64 { v := vf(t); return -v } case *idealFloatType: - v := v.asIdealFloat()() - val := v.Neg() - a.eval = func() *bignum.Rational { return val } + val := v.asIdealFloat()() + val.Neg(val) + a.eval = func() *big.Rat { return val } default: - log.Crashf("unexpected type %v at %v", a.t, a.pos) + log.Panicf("unexpected type %v at %v", a.t, a.pos) } } @@ -250,7 +250,7 @@ func (a *expr) genUnaryOpNot(v *expr) { vf := v.asBool() a.eval = func(t *Thread) bool { v := vf(t); return !v } default: - log.Crashf("unexpected type %v at %v", a.t, a.pos) + log.Panicf("unexpected type %v at %v", a.t, a.pos) } } @@ -263,11 +263,11 @@ func (a *expr) genUnaryOpXor(v *expr) { vf := v.asInt() a.eval = func(t *Thread) int64 { v := vf(t); return ^v } case *idealIntType: - v := v.asIdealInt()() - val := v.Neg().Sub(bignum.Int(1)) - a.eval = func() *bignum.Integer { return val } + val := v.asIdealInt()() + val.Not(val) + a.eval = func() *big.Int { return val } default: - log.Crashf("unexpected type %v at %v", a.t, a.pos) + log.Panicf("unexpected type %v at %v", a.t, a.pos) } } @@ -325,7 +325,7 @@ func (a *expr) genBinOpAdd(l, r *expr) { return uint64(uint(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *intType: lf := l.asInt() @@ -367,13 +367,13 @@ func (a *expr) genBinOpAdd(l, r *expr) { return int64(int(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *idealIntType: l := l.asIdealInt()() r := r.asIdealInt()() - val := l.Add(r) - a.eval = func() *bignum.Integer { return val } + val := l.Add(l, r) + a.eval = func() *big.Int { return val } case *floatType: lf := l.asFloat() rf := r.asFloat() @@ -400,13 +400,13 @@ func (a *expr) genBinOpAdd(l, r *expr) { return float64(float(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *idealFloatType: l := l.asIdealFloat()() r := r.asIdealFloat()() - val := l.Add(r) - a.eval = func() *bignum.Rational { return val } + val := l.Add(l, r) + a.eval = func() *big.Rat { return val } case *stringType: lf := l.asString() rf := r.asString() @@ -415,7 +415,7 @@ func (a *expr) genBinOpAdd(l, r *expr) { return l + r } default: - log.Crashf("unexpected type %v at %v", l.t, a.pos) + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -461,7 +461,7 @@ func (a *expr) genBinOpSub(l, r *expr) { return uint64(uint(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *intType: lf := l.asInt() @@ -503,13 +503,13 @@ func (a *expr) genBinOpSub(l, r *expr) { return int64(int(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *idealIntType: l := l.asIdealInt()() r := r.asIdealInt()() - val := l.Sub(r) - a.eval = func() *bignum.Integer { return val } + val := l.Sub(l, r) + a.eval = func() *big.Int { return val } case *floatType: lf := l.asFloat() rf := r.asFloat() @@ -536,15 +536,15 @@ func (a *expr) genBinOpSub(l, r *expr) { return float64(float(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *idealFloatType: l := l.asIdealFloat()() r := r.asIdealFloat()() - val := l.Sub(r) - a.eval = func() *bignum.Rational { return val } + val := l.Sub(l, r) + a.eval = func() *big.Rat { return val } default: - log.Crashf("unexpected type %v at %v", l.t, a.pos) + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -590,7 +590,7 @@ func (a *expr) genBinOpMul(l, r *expr) { return uint64(uint(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *intType: lf := l.asInt() @@ -632,13 +632,13 @@ func (a *expr) genBinOpMul(l, r *expr) { return int64(int(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *idealIntType: l := l.asIdealInt()() r := r.asIdealInt()() - val := l.Mul(r) - a.eval = func() *bignum.Integer { return val } + val := l.Mul(l, r) + a.eval = func() *big.Int { return val } case *floatType: lf := l.asFloat() rf := r.asFloat() @@ -665,15 +665,15 @@ func (a *expr) genBinOpMul(l, r *expr) { return float64(float(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *idealFloatType: l := l.asIdealFloat()() r := r.asIdealFloat()() - val := l.Mul(r) - a.eval = func() *bignum.Rational { return val } + val := l.Mul(l, r) + a.eval = func() *big.Rat { return val } default: - log.Crashf("unexpected type %v at %v", l.t, a.pos) + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -734,7 +734,7 @@ func (a *expr) genBinOpQuo(l, r *expr) { return uint64(uint(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *intType: lf := l.asInt() @@ -791,13 +791,13 @@ func (a *expr) genBinOpQuo(l, r *expr) { return int64(int(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *idealIntType: l := l.asIdealInt()() r := r.asIdealInt()() - val := l.Quo(r) - a.eval = func() *bignum.Integer { return val } + val := l.Quo(l, r) + a.eval = func() *big.Int { return val } case *floatType: lf := l.asFloat() rf := r.asFloat() @@ -833,15 +833,15 @@ func (a *expr) genBinOpQuo(l, r *expr) { return float64(float(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *idealFloatType: l := l.asIdealFloat()() r := r.asIdealFloat()() - val := l.Quo(r) - a.eval = func() *bignum.Rational { return val } + val := l.Quo(l, r) + a.eval = func() *big.Rat { return val } default: - log.Crashf("unexpected type %v at %v", l.t, a.pos) + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -902,7 +902,7 @@ func (a *expr) genBinOpRem(l, r *expr) { return uint64(uint(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *intType: lf := l.asInt() @@ -959,15 +959,15 @@ func (a *expr) genBinOpRem(l, r *expr) { return int64(int(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *idealIntType: l := l.asIdealInt()() r := r.asIdealInt()() - val := l.Rem(r) - a.eval = func() *bignum.Integer { return val } + val := l.Rem(l, r) + a.eval = func() *big.Int { return val } default: - log.Crashf("unexpected type %v at %v", l.t, a.pos) + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -1013,7 +1013,7 @@ func (a *expr) genBinOpAnd(l, r *expr) { return uint64(uint(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *intType: lf := l.asInt() @@ -1055,15 +1055,15 @@ func (a *expr) genBinOpAnd(l, r *expr) { return int64(int(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *idealIntType: l := l.asIdealInt()() r := r.asIdealInt()() - val := l.And(r) - a.eval = func() *bignum.Integer { return val } + val := l.And(l, r) + a.eval = func() *big.Int { return val } default: - log.Crashf("unexpected type %v at %v", l.t, a.pos) + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -1109,7 +1109,7 @@ func (a *expr) genBinOpOr(l, r *expr) { return uint64(uint(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *intType: lf := l.asInt() @@ -1151,15 +1151,15 @@ func (a *expr) genBinOpOr(l, r *expr) { return int64(int(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *idealIntType: l := l.asIdealInt()() r := r.asIdealInt()() - val := l.Or(r) - a.eval = func() *bignum.Integer { return val } + val := l.Or(l, r) + a.eval = func() *big.Int { return val } default: - log.Crashf("unexpected type %v at %v", l.t, a.pos) + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -1205,7 +1205,7 @@ func (a *expr) genBinOpXor(l, r *expr) { return uint64(uint(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *intType: lf := l.asInt() @@ -1247,15 +1247,15 @@ func (a *expr) genBinOpXor(l, r *expr) { return int64(int(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *idealIntType: l := l.asIdealInt()() r := r.asIdealInt()() - val := l.Xor(r) - a.eval = func() *bignum.Integer { return val } + val := l.Xor(l, r) + a.eval = func() *big.Int { return val } default: - log.Crashf("unexpected type %v at %v", l.t, a.pos) + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -1301,7 +1301,7 @@ func (a *expr) genBinOpAndNot(l, r *expr) { return uint64(uint(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *intType: lf := l.asInt() @@ -1343,15 +1343,15 @@ func (a *expr) genBinOpAndNot(l, r *expr) { return int64(int(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *idealIntType: l := l.asIdealInt()() r := r.asIdealInt()() - val := l.AndNot(r) - a.eval = func() *bignum.Integer { return val } + val := l.AndNot(l, r) + a.eval = func() *big.Int { return val } default: - log.Crashf("unexpected type %v at %v", l.t, a.pos) + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -1397,7 +1397,7 @@ func (a *expr) genBinOpShl(l, r *expr) { return uint64(uint(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *intType: lf := l.asInt() @@ -1439,10 +1439,10 @@ func (a *expr) genBinOpShl(l, r *expr) { return int64(int(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } default: - log.Crashf("unexpected type %v at %v", l.t, a.pos) + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -1488,7 +1488,7 @@ func (a *expr) genBinOpShr(l, r *expr) { return uint64(uint(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } case *intType: lf := l.asInt() @@ -1530,10 +1530,10 @@ func (a *expr) genBinOpShr(l, r *expr) { return int64(int(ret)) } default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } default: - log.Crashf("unexpected type %v at %v", l.t, a.pos) + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -1578,7 +1578,7 @@ func (a *expr) genBinOpLss(l, r *expr) { return l < r } default: - log.Crashf("unexpected type %v at %v", l.t, a.pos) + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -1623,7 +1623,7 @@ func (a *expr) genBinOpGtr(l, r *expr) { return l > r } default: - log.Crashf("unexpected type %v at %v", l.t, a.pos) + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -1668,7 +1668,7 @@ func (a *expr) genBinOpLeq(l, r *expr) { return l <= r } default: - log.Crashf("unexpected type %v at %v", l.t, a.pos) + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -1713,7 +1713,7 @@ func (a *expr) genBinOpGeq(l, r *expr) { return l >= r } default: - log.Crashf("unexpected type %v at %v", l.t, a.pos) + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -1786,7 +1786,7 @@ func (a *expr) genBinOpEql(l, r *expr) { return l == r } default: - log.Crashf("unexpected type %v at %v", l.t, a.pos) + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -1859,7 +1859,7 @@ func (a *expr) genBinOpNeq(l, r *expr) { return l != r } default: - log.Crashf("unexpected type %v at %v", l.t, a.pos) + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -1899,7 +1899,7 @@ func genAssign(lt Type, r *expr) func(lv Value, t *Thread) { rf := r.asMap() return func(lv Value, t *Thread) { lv.(MapValue).Set(t, rf(t)) } default: - log.Crashf("unexpected left operand type %v at %v", lt, r.pos) + log.Panicf("unexpected left operand type %v at %v", lt, r.pos) } panic("fail") } diff --git a/src/pkg/exp/eval/expr_test.go b/src/pkg/exp/eval/expr_test.go index b36554183..0dbce4315 100644 --- a/src/pkg/exp/eval/expr_test.go +++ b/src/pkg/exp/eval/expr_test.go @@ -5,7 +5,7 @@ package eval import ( - "exp/bignum" + "big" "testing" ) @@ -22,7 +22,7 @@ var implLimit = "implementation limit" var mustBeUnsigned = "must be unsigned" var divByZero = "divide by zero" -var hugeInteger = bignum.Int(1).Shl(64) +var hugeInteger = new(big.Int).Lsh(idealOne, 64) var exprTests = []test{ Val("i", 1), @@ -30,9 +30,9 @@ var exprTests = []test{ // TODO(austin) Test variable in constant context //CErr("t", typeAsExpr), - Val("'a'", bignum.Int('a')), - Val("'\\uffff'", bignum.Int('\uffff')), - Val("'\\n'", bignum.Int('\n')), + Val("'a'", big.NewInt('a')), + Val("'\\uffff'", big.NewInt('\uffff')), + Val("'\\n'", big.NewInt('\n')), CErr("''+x", badCharLit), // Produces two parse errors //CErr("'''", ""), @@ -40,10 +40,10 @@ var exprTests = []test{ CErr("'\\z'", unknownEscape), CErr("'ab'", badCharLit), - Val("1.0", bignum.Rat(1, 1)), - Val("1.", bignum.Rat(1, 1)), - Val(".1", bignum.Rat(1, 10)), - Val("1e2", bignum.Rat(100, 1)), + Val("1.0", big.NewRat(1, 1)), + Val("1.", big.NewRat(1, 1)), + Val(".1", big.NewRat(1, 10)), + Val("1e2", big.NewRat(100, 1)), Val("\"abc\"", "abc"), Val("\"\"", ""), @@ -140,12 +140,12 @@ var exprTests = []test{ CErr("&c", badAddrOf), Val("*(&ai[0])", 1), - Val("+1", bignum.Int(+1)), - Val("+1.0", bignum.Rat(1, 1)), - Val("01.5", bignum.Rat(15, 10)), + Val("+1", big.NewInt(+1)), + Val("+1.0", big.NewRat(1, 1)), + Val("01.5", big.NewRat(15, 10)), CErr("+\"x\"", opTypes), - Val("-42", bignum.Int(-42)), + Val("-42", big.NewInt(-42)), Val("-i", -1), Val("-f", -1.0), // 6g bug? @@ -154,8 +154,8 @@ var exprTests = []test{ // TODO(austin) Test unary ! - Val("^2", bignum.Int(^2)), - Val("^(-2)", bignum.Int(^(-2))), + Val("^2", big.NewInt(^2)), + Val("^(-2)", big.NewInt(^(-2))), CErr("^2.0", opTypes), CErr("^2.5", opTypes), Val("^i", ^1), @@ -165,67 +165,66 @@ var exprTests = []test{ Val("1+i", 2), Val("1+u", uint(2)), Val("3.0+i", 4), - Val("1+1", bignum.Int(2)), + Val("1+1", big.NewInt(2)), Val("f+f", 2.0), Val("1+f", 2.0), - Val("1.0+1", bignum.Rat(2, 1)), + Val("1.0+1", big.NewRat(2, 1)), Val("\"abc\" + \"def\"", "abcdef"), CErr("i+u", opTypes), CErr("-1+u", constantUnderflows), // TODO(austin) Test named types - Val("2-1", bignum.Int(1)), - Val("2.0-1", bignum.Rat(1, 1)), + Val("2-1", big.NewInt(1)), + Val("2.0-1", big.NewRat(1, 1)), Val("f-2", -1.0), - // TOOD(austin) bignum can't do negative 0? - //Val("-0.0", XXX), - Val("2*2", bignum.Int(4)), + Val("-0.0", big.NewRat(0, 1)), + Val("2*2", big.NewInt(4)), Val("2*i", 2), - Val("3/2", bignum.Int(1)), + Val("3/2", big.NewInt(1)), Val("3/i", 3), CErr("1/0", divByZero), CErr("1.0/0", divByZero), RErr("i/0", divByZero), - Val("3%2", bignum.Int(1)), + Val("3%2", big.NewInt(1)), Val("i%2", 1), CErr("3%0", divByZero), CErr("3.0%0", opTypes), RErr("i%0", divByZero), // Examples from "Arithmetic operators" - Val("5/3", bignum.Int(1)), + Val("5/3", big.NewInt(1)), Val("(i+4)/(i+2)", 1), - Val("5%3", bignum.Int(2)), + Val("5%3", big.NewInt(2)), Val("(i+4)%(i+2)", 2), - Val("-5/3", bignum.Int(-1)), + Val("-5/3", big.NewInt(-1)), Val("(i-6)/(i+2)", -1), - Val("-5%3", bignum.Int(-2)), + Val("-5%3", big.NewInt(-2)), Val("(i-6)%(i+2)", -2), - Val("5/-3", bignum.Int(-1)), + Val("5/-3", big.NewInt(-1)), Val("(i+4)/(i-4)", -1), - Val("5%-3", bignum.Int(2)), + Val("5%-3", big.NewInt(2)), Val("(i+4)%(i-4)", 2), - Val("-5/-3", bignum.Int(1)), + Val("-5/-3", big.NewInt(1)), Val("(i-6)/(i-4)", 1), - Val("-5%-3", bignum.Int(-2)), + Val("-5%-3", big.NewInt(-2)), Val("(i-6)%(i-4)", -2), // Examples from "Arithmetic operators" - Val("11/4", bignum.Int(2)), + Val("11/4", big.NewInt(2)), Val("(i+10)/4", 2), - Val("11%4", bignum.Int(3)), + Val("11%4", big.NewInt(3)), Val("(i+10)%4", 3), - Val("11>>2", bignum.Int(2)), + Val("11>>2", big.NewInt(2)), Val("(i+10)>>2", 2), - Val("11&3", bignum.Int(3)), + Val("11&3", big.NewInt(3)), Val("(i+10)&3", 3), - Val("-11/4", bignum.Int(-2)), + Val("-11/4", big.NewInt(-2)), Val("(i-12)/4", -2), - Val("-11%4", bignum.Int(-3)), + Val("-11%4", big.NewInt(-3)), Val("(i-12)%4", -3), - Val("-11>>2", bignum.Int(-3)), + Val("-11>>2", big.NewInt(-3)), Val("(i-12)>>2", -3), - Val("-11&3", bignum.Int(1)), + Val("-11&3", big.NewInt(1)), Val("(i-12)&3", 1), // TODO(austin) Test bit ops @@ -234,29 +233,29 @@ var exprTests = []test{ // ideal int, negative ideal int, big ideal int, ideal // fractional float, ideal non-fractional float, int, uint, // and float. - Val("2<<2", bignum.Int(2<<2)), + Val("2<<2", big.NewInt(2<<2)), CErr("2<<(-1)", constantUnderflows), CErr("2<<0x10000000000000000", constantOverflows), CErr("2<<2.5", constantTruncated), - Val("2<<2.0", bignum.Int(2<<2.0)), + Val("2<<2.0", big.NewInt(2<<2.0)), CErr("2<= cap(b.instrs) { - a := make(code, n, n*2) - for i := range b.instrs { - a[i] = b.instrs[i] - } - b.instrs = a - } - b.instrs = b.instrs[0 : n+1] - b.instrs[n] = instr + b.instrs = append(b.instrs, instr) } func (b *codeBuf) nextPC() uint { return uint(len(b.instrs)) } @@ -60,9 +51,7 @@ func (b *codeBuf) nextPC() uint { return uint(len(b.instrs)) } func (b *codeBuf) get() code { // Freeze this buffer into an array of exactly the right size a := make(code, len(b.instrs)) - for i := range b.instrs { - a[i] = b.instrs[i] - } + copy(a, b.instrs) return code(a) } diff --git a/src/pkg/exp/eval/gen.go b/src/pkg/exp/eval/gen.go index 969d65586..81863dd6f 100644 --- a/src/pkg/exp/eval/gen.go +++ b/src/pkg/exp/eval/gen.go @@ -40,16 +40,16 @@ type Type struct { var ( boolType = &Type{Repr: "*boolType", Value: "BoolValue", Native: "bool", As: "asBool"} uintType = &Type{Repr: "*uintType", Value: "UintValue", Native: "uint64", As: "asUint", - Sizes: []Size{Size{8, "uint8"}, Size{16, "uint16"}, Size{32, "uint32"}, Size{64, "uint64"}, Size{0, "uint"}}, + Sizes: []Size{{8, "uint8"}, {16, "uint16"}, {32, "uint32"}, {64, "uint64"}, {0, "uint"}}, } intType = &Type{Repr: "*intType", Value: "IntValue", Native: "int64", As: "asInt", - Sizes: []Size{Size{8, "int8"}, Size{16, "int16"}, Size{32, "int32"}, Size{64, "int64"}, Size{0, "int"}}, + Sizes: []Size{{8, "int8"}, {16, "int16"}, {32, "int32"}, {64, "int64"}, {0, "int"}}, } - idealIntType = &Type{Repr: "*idealIntType", Value: "IdealIntValue", Native: "*bignum.Integer", As: "asIdealInt", IsIdeal: true} + idealIntType = &Type{Repr: "*idealIntType", Value: "IdealIntValue", Native: "*big.Int", As: "asIdealInt", IsIdeal: true} floatType = &Type{Repr: "*floatType", Value: "FloatValue", Native: "float64", As: "asFloat", - Sizes: []Size{Size{32, "float32"}, Size{64, "float64"}, Size{0, "float"}}, + Sizes: []Size{{32, "float32"}, {64, "float64"}, {0, "float"}}, } - idealFloatType = &Type{Repr: "*idealFloatType", Value: "IdealFloatValue", Native: "*bignum.Rational", As: "asIdealFloat", IsIdeal: true} + idealFloatType = &Type{Repr: "*idealFloatType", Value: "IdealFloatValue", Native: "*big.Rat", As: "asIdealFloat", IsIdeal: true} stringType = &Type{Repr: "*stringType", Value: "StringValue", Native: "string", As: "asString"} arrayType = &Type{Repr: "*ArrayType", Value: "ArrayValue", Native: "ArrayValue", As: "asArray", HasAssign: true} structType = &Type{Repr: "*StructType", Value: "StructValue", Native: "StructValue", As: "asStruct", HasAssign: true} @@ -93,41 +93,41 @@ var ( ) var unOps = []Op{ - Op{Name: "Neg", Expr: "-v", ConstExpr: "v.Neg()", Types: numbers}, - Op{Name: "Not", Expr: "!v", Types: bools}, - Op{Name: "Xor", Expr: "^v", ConstExpr: "v.Neg().Sub(bignum.Int(1))", Types: integers}, + {Name: "Neg", Expr: "-v", ConstExpr: "val.Neg(val)", Types: numbers}, + {Name: "Not", Expr: "!v", Types: bools}, + {Name: "Xor", Expr: "^v", ConstExpr: "val.Not(val)", Types: integers}, } var binOps = []Op{ - Op{Name: "Add", Expr: "l + r", ConstExpr: "l.Add(r)", Types: addable}, - Op{Name: "Sub", Expr: "l - r", ConstExpr: "l.Sub(r)", Types: numbers}, - Op{Name: "Mul", Expr: "l * r", ConstExpr: "l.Mul(r)", Types: numbers}, - Op{Name: "Quo", + {Name: "Add", Expr: "l + r", ConstExpr: "l.Add(l, r)", Types: addable}, + {Name: "Sub", Expr: "l - r", ConstExpr: "l.Sub(l, r)", Types: numbers}, + {Name: "Mul", Expr: "l * r", ConstExpr: "l.Mul(l, r)", Types: numbers}, + {Name: "Quo", Body: "if r == 0 { t.Abort(DivByZeroError{}) }; ret = l / r", - ConstExpr: "l.Quo(r)", + ConstExpr: "l.Quo(l, r)", Types: numbers, }, - Op{Name: "Rem", + {Name: "Rem", Body: "if r == 0 { t.Abort(DivByZeroError{}) }; ret = l % r", - ConstExpr: "l.Rem(r)", + ConstExpr: "l.Rem(l, r)", Types: integers, }, - Op{Name: "And", Expr: "l & r", ConstExpr: "l.And(r)", Types: integers}, - Op{Name: "Or", Expr: "l | r", ConstExpr: "l.Or(r)", Types: integers}, - Op{Name: "Xor", Expr: "l ^ r", ConstExpr: "l.Xor(r)", Types: integers}, - Op{Name: "AndNot", Expr: "l &^ r", ConstExpr: "l.AndNot(r)", Types: integers}, - Op{Name: "Shl", Expr: "l << r", ConstExpr: "l.Shl(uint(r.Value()))", + {Name: "And", Expr: "l & r", ConstExpr: "l.And(l, r)", Types: integers}, + {Name: "Or", Expr: "l | r", ConstExpr: "l.Or(l, r)", Types: integers}, + {Name: "Xor", Expr: "l ^ r", ConstExpr: "l.Xor(l, r)", Types: integers}, + {Name: "AndNot", Expr: "l &^ r", ConstExpr: "l.AndNot(l, r)", Types: integers}, + {Name: "Shl", Expr: "l << r", ConstExpr: "l.Lsh(l, uint(r.Value()))", AsRightName: "asUint", Types: shiftable, }, - Op{Name: "Shr", Expr: "l >> r", ConstExpr: "l.Shr(uint(r.Value()))", + {Name: "Shr", Expr: "l >> r", ConstExpr: "new(big.Int).Rsh(l, uint(r.Value()))", AsRightName: "asUint", Types: shiftable, }, - Op{Name: "Lss", Expr: "l < r", ConstExpr: "l.Cmp(r) < 0", ReturnType: "bool", Types: addable}, - Op{Name: "Gtr", Expr: "l > r", ConstExpr: "l.Cmp(r) > 0", ReturnType: "bool", Types: addable}, - Op{Name: "Leq", Expr: "l <= r", ConstExpr: "l.Cmp(r) <= 0", ReturnType: "bool", Types: addable}, - Op{Name: "Geq", Expr: "l >= r", ConstExpr: "l.Cmp(r) >= 0", ReturnType: "bool", Types: addable}, - Op{Name: "Eql", Expr: "l == r", ConstExpr: "l.Cmp(r) == 0", ReturnType: "bool", Types: cmpable}, - Op{Name: "Neq", Expr: "l != r", ConstExpr: "l.Cmp(r) != 0", ReturnType: "bool", Types: cmpable}, + {Name: "Lss", Expr: "l < r", ConstExpr: "l.Cmp(r) < 0", ReturnType: "bool", Types: addable}, + {Name: "Gtr", Expr: "l > r", ConstExpr: "l.Cmp(r) > 0", ReturnType: "bool", Types: addable}, + {Name: "Leq", Expr: "l <= r", ConstExpr: "l.Cmp(r) <= 0", ReturnType: "bool", Types: addable}, + {Name: "Geq", Expr: "l >= r", ConstExpr: "l.Cmp(r) >= 0", ReturnType: "bool", Types: addable}, + {Name: "Eql", Expr: "l == r", ConstExpr: "l.Cmp(r) == 0", ReturnType: "bool", Types: cmpable}, + {Name: "Neq", Expr: "l != r", ConstExpr: "l.Cmp(r) != 0", ReturnType: "bool", Types: cmpable}, } type Data struct { @@ -149,8 +149,8 @@ const templateStr = ` package eval import ( - "bignum"; - "log"; + "big" + "log" ) /* @@ -184,9 +184,9 @@ func (a *expr) asInterface() (func(*Thread) interface{}) { «.end» «.end» default: - log.Crashf("unexpected expression node type %T at %v", a.eval, a.pos); + log.Panicf("unexpected expression node type %T at %v", a.eval, a.pos) } - panic("fail"); + panic("fail") } /* @@ -198,19 +198,19 @@ func (a *expr) genConstant(v Value) { «.repeated section Types» case «Repr»: «.section IsIdeal» - val := v.(«Value»).Get(); + val := v.(«Value»).Get() a.eval = func() «Native» { return val } «.or» a.eval = func(t *Thread) «Native» { return v.(«Value»).Get(t) } «.end» «.end» default: - log.Crashf("unexpected constant type %v at %v", a.t, a.pos); + log.Panicf("unexpected constant type %v at %v", a.t, a.pos) } } func (a *expr) genIdentOp(level, index int) { - a.evalAddr = func(t *Thread) Value { return t.f.Get(level, index) }; + a.evalAddr = func(t *Thread) Value { return t.f.Get(level, index) } switch a.t.lit().(type) { «.repeated section Types» «.section IsIdeal» @@ -220,12 +220,12 @@ func (a *expr) genIdentOp(level, index int) { «.end» «.end» default: - log.Crashf("unexpected identifier type %v at %v", a.t, a.pos); + log.Panicf("unexpected identifier type %v at %v", a.t, a.pos) } } func (a *expr) genFuncCall(call func(t *Thread) []Value) { - a.exec = func(t *Thread) { call(t)}; + a.exec = func(t *Thread) { call(t)} switch a.t.lit().(type) { «.repeated section Types» «.section IsIdeal» @@ -237,12 +237,12 @@ func (a *expr) genFuncCall(call func(t *Thread) []Value) { case *MultiType: a.eval = func(t *Thread) []Value { return call(t) } default: - log.Crashf("unexpected result type %v at %v", a.t, a.pos); + log.Panicf("unexpected result type %v at %v", a.t, a.pos) } } func (a *expr) genValue(vf func(*Thread) Value) { - a.evalAddr = vf; + a.evalAddr = vf switch a.t.lit().(type) { «.repeated section Types» «.section IsIdeal» @@ -252,7 +252,7 @@ func (a *expr) genValue(vf func(*Thread) Value) { «.end» «.end» default: - log.Crashf("unexpected result type %v at %v", a.t, a.pos); + log.Panicf("unexpected result type %v at %v", a.t, a.pos) } } @@ -262,29 +262,29 @@ func (a *expr) genUnaryOp«Name»(v *expr) { «.repeated section Types» case «Repr»: «.section IsIdeal» - v := v.«As»()(); - val := «ConstExpr»; + val := v.«As»()() + «ConstExpr» a.eval = func() «Native» { return val } «.or» - vf := v.«As»(); + vf := v.«As»() a.eval = func(t *Thread) «Native» { v := vf(t); return «Expr» } «.end» «.end» default: - log.Crashf("unexpected type %v at %v", a.t, a.pos); + log.Panicf("unexpected type %v at %v", a.t, a.pos) } } «.end» func (a *expr) genBinOpLogAnd(l, r *expr) { - lf := l.asBool(); - rf := r.asBool(); + lf := l.asBool() + rf := r.asBool() a.eval = func(t *Thread) bool { return lf(t) && rf(t) } } func (a *expr) genBinOpLogOr(l, r *expr) { - lf := l.asBool(); - rf := r.asBool(); + lf := l.asBool() + rf := r.asBool() a.eval = func(t *Thread) bool { return lf(t) || rf(t) } } @@ -294,20 +294,20 @@ func (a *expr) genBinOp«Name»(l, r *expr) { «.repeated section Types» case «Repr»: «.section IsIdeal» - l := l.«As»()(); - r := r.«As»()(); - val := «ConstExpr»; + l := l.«As»()() + r := r.«As»()() + val := «ConstExpr» «.section ReturnType» a.eval = func(t *Thread) «ReturnType» { return val } «.or» a.eval = func() «Native» { return val } «.end» «.or» - lf := l.«As»(); - rf := r.«.section AsRightName»«@»«.or»«As»«.end»(); + lf := l.«As»() + rf := r.«.section AsRightName»«@»«.or»«As»«.end»() «.section ReturnType» a.eval = func(t *Thread) «@» { - l, r := lf(t), rf(t); + l, r := lf(t), rf(t) return «Expr» } «.or» @@ -316,22 +316,22 @@ func (a *expr) genBinOp«Name»(l, r *expr) { «.repeated section @» case «Bits»: a.eval = func(t *Thread) «Native» { - l, r := lf(t), rf(t); - var ret «Native»; + l, r := lf(t), rf(t) + var ret «Native» «.section Body» - «Body»; + «Body» «.or» - ret = «Expr»; + ret = «Expr» «.end» return «Native»(«Sized»(ret)) } «.end» default: - log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos); + log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) } «.or» a.eval = func(t *Thread) «Native» { - l, r := lf(t), rf(t); + l, r := lf(t), rf(t) return «Expr» } «.end» @@ -339,7 +339,7 @@ func (a *expr) genBinOp«Name»(l, r *expr) { «.end» «.end» default: - log.Crashf("unexpected type %v at %v", l.t, a.pos); + log.Panicf("unexpected type %v at %v", l.t, a.pos) } } @@ -350,14 +350,14 @@ func genAssign(lt Type, r *expr) (func(lv Value, t *Thread)) { «.section IsIdeal» «.or» case «Repr»: - rf := r.«As»(); + rf := r.«As»() return func(lv Value, t *Thread) { «.section HasAssign»lv.Assign(t, rf(t))«.or»lv.(«Value»).Set(t, rf(t))«.end» } «.end» «.end» default: - log.Crashf("unexpected left operand type %v at %v", lt, r.pos); + log.Panicf("unexpected left operand type %v at %v", lt, r.pos) } - panic("fail"); + panic("fail") } ` diff --git a/src/pkg/exp/eval/main.go b/src/pkg/exp/eval/main.go index 6033088a3..d87e8f240 100644 --- a/src/pkg/exp/eval/main.go +++ b/src/pkg/exp/eval/main.go @@ -10,10 +10,12 @@ import ( "flag" "go/parser" "go/scanner" + "go/token" "io/ioutil" "os" ) +var fset = token.NewFileSet() var filename = flag.String("f", "", "file to run") func main() { @@ -25,12 +27,12 @@ func main() { println(err.String()) os.Exit(1) } - file, err := parser.ParseFile(*filename, data, nil, 0) + file, err := parser.ParseFile(fset, *filename, data, 0) if err != nil { println(err.String()) os.Exit(1) } - code, err := w.CompileDeclList(file.Decls) + code, err := w.CompileDeclList(fset, file.Decls) if err != nil { if list, ok := err.(scanner.ErrorList); ok { for _, e := range list { @@ -46,7 +48,7 @@ func main() { println(err.String()) os.Exit(1) } - code, err = w.Compile("init()") + code, err = w.Compile(fset, "init()") if code != nil { _, err := code.Run() if err != nil { @@ -54,7 +56,7 @@ func main() { os.Exit(1) } } - code, err = w.Compile("main()") + code, err = w.Compile(fset, "main()") if err != nil { println(err.String()) os.Exit(1) @@ -74,7 +76,7 @@ func main() { if err != nil { break } - code, err := w.Compile(line) + code, err := w.Compile(fset, line) if err != nil { println(err.String()) continue diff --git a/src/pkg/exp/eval/scope.go b/src/pkg/exp/eval/scope.go index 74ce32b59..66305de25 100644 --- a/src/pkg/exp/eval/scope.go +++ b/src/pkg/exp/eval/scope.go @@ -15,11 +15,11 @@ import ( // A definition can be a *Variable, *Constant, or Type. type Def interface { - Pos() token.Position + Pos() token.Pos } type Variable struct { - token.Position + VarPos token.Pos // Index of this variable in the Frame structure Index int // Static type of this variable @@ -30,10 +30,18 @@ type Variable struct { Init Value } +func (v *Variable) Pos() token.Pos { + return v.VarPos +} + type Constant struct { - token.Position - Type Type - Value Value + ConstPos token.Pos + Type Type + Value Value +} + +func (c *Constant) Pos() token.Pos { + return c.ConstPos } // A block represents a definition block in which a name may not be @@ -74,7 +82,7 @@ type Scope struct { func (b *block) enterChild() *block { if b.inner != nil && b.inner.scope == b.scope { - log.Crash("Failed to exit child block before entering another child") + log.Panic("Failed to exit child block before entering another child") } sub := &block{ outer: b, @@ -88,14 +96,14 @@ func (b *block) enterChild() *block { func (b *block) exit() { if b.outer == nil { - log.Crash("Cannot exit top-level block") + log.Panic("Cannot exit top-level block") } if b.outer.scope == b.scope { if b.outer.inner != b { - log.Crash("Already exited block") + log.Panic("Already exited block") } if b.inner != nil && b.inner.scope == b.scope { - log.Crash("Exit of parent block without exit of child block") + log.Panic("Exit of parent block without exit of child block") } } b.outer.inner = nil @@ -103,7 +111,7 @@ func (b *block) exit() { func (b *block) ChildScope() *Scope { if b.inner != nil && b.inner.scope == b.scope { - log.Crash("Failed to exit child block before entering a child scope") + log.Panic("Failed to exit child block before entering a child scope") } sub := b.enterChild() sub.offset = 0 @@ -111,12 +119,12 @@ func (b *block) ChildScope() *Scope { return sub.scope } -func (b *block) DefineVar(name string, pos token.Position, t Type) (*Variable, Def) { +func (b *block) DefineVar(name string, pos token.Pos, t Type) (*Variable, Def) { if prev, ok := b.defs[name]; ok { return nil, prev } v := b.defineSlot(t, false) - v.Position = pos + v.VarPos = pos b.defs[name] = v return v, nil } @@ -125,7 +133,7 @@ func (b *block) DefineTemp(t Type) *Variable { return b.defineSlot(t, true) } func (b *block) defineSlot(t Type, temp bool) *Variable { if b.inner != nil && b.inner.scope == b.scope { - log.Crash("Failed to exit child block before defining variable") + log.Panic("Failed to exit child block before defining variable") } index := -1 if !b.global || temp { @@ -135,11 +143,11 @@ func (b *block) defineSlot(t Type, temp bool) *Variable { b.scope.maxVars = index + 1 } } - v := &Variable{token.Position{}, index, t, nil} + v := &Variable{token.NoPos, index, t, nil} return v } -func (b *block) DefineConst(name string, pos token.Position, t Type, v Value) (*Constant, Def) { +func (b *block) DefineConst(name string, pos token.Pos, t Type, v Value) (*Constant, Def) { if prev, ok := b.defs[name]; ok { return nil, prev } @@ -148,7 +156,7 @@ func (b *block) DefineConst(name string, pos token.Position, t Type, v Value) (* return c, nil } -func (b *block) DefineType(name string, pos token.Position, t Type) Type { +func (b *block) DefineType(name string, pos token.Pos, t Type) Type { if _, ok := b.defs[name]; ok { return nil } diff --git a/src/pkg/exp/eval/stmt.go b/src/pkg/exp/eval/stmt.go index bcd81f04c..b9ffa94fa 100644 --- a/src/pkg/exp/eval/stmt.go +++ b/src/pkg/exp/eval/stmt.go @@ -5,7 +5,7 @@ package eval import ( - "exp/bignum" + "big" "log" "go/ast" "go/token" @@ -22,13 +22,13 @@ const ( type stmtCompiler struct { *blockCompiler - pos token.Position + pos token.Pos // This statement's label, or nil if it is not labeled. stmtLabel *label } func (a *stmtCompiler) diag(format string, args ...interface{}) { - a.diagAt(&a.pos, format, args) + a.diagAt(a.pos, format, args...) } /* @@ -65,7 +65,7 @@ type flowBuf struct { ents map[uint]*flowEnt // gotos is a map from goto positions to information on the // block at the point of the goto. - gotos map[*token.Position]*flowBlock + gotos map[token.Pos]*flowBlock // labels is a map from label name to information on the block // at the point of the label. labels are tracked by name, // since mutliple labels at the same PC can have different @@ -74,7 +74,7 @@ type flowBuf struct { } func newFlowBuf(cb *codeBuf) *flowBuf { - return &flowBuf{cb, make(map[uint]*flowEnt), make(map[*token.Position]*flowBlock), make(map[string]*flowBlock)} + return &flowBuf{cb, make(map[uint]*flowEnt), make(map[token.Pos]*flowBlock), make(map[string]*flowBlock)} } // put creates a flow control point for the next PC in the code buffer. @@ -82,7 +82,7 @@ func newFlowBuf(cb *codeBuf) *flowBuf { func (f *flowBuf) put(cond bool, term bool, jumps []*uint) { pc := f.cb.nextPC() if ent, ok := f.ents[pc]; ok { - log.Crashf("Flow entry already exists at PC %d: %+v", pc, ent) + log.Panicf("Flow entry already exists at PC %d: %+v", pc, ent) } f.ents[pc] = &flowEnt{cond, term, jumps, false} } @@ -123,8 +123,8 @@ func newFlowBlock(target string, b *block) *flowBlock { // putGoto captures the block at a goto statement. This should be // called in addition to putting a flow control point. -func (f *flowBuf) putGoto(pos token.Position, target string, b *block) { - f.gotos[&pos] = newFlowBlock(target, b) +func (f *flowBuf) putGoto(pos token.Pos, target string, b *block) { + f.gotos[pos] = newFlowBlock(target, b) } // putLabel captures the block at a label. @@ -138,7 +138,7 @@ func (f *flowBuf) putLabel(name string, b *block) { func (f *flowBuf) reachesEnd(pc uint) bool { endPC := f.cb.nextPC() if pc > endPC { - log.Crashf("Reached bad PC %d past end PC %d", pc, endPC) + log.Panicf("Reached bad PC %d past end PC %d", pc, endPC) } for ; pc < endPC; pc++ { @@ -210,15 +210,12 @@ func (f *flowBuf) gotosObeyScopes(a *compiler) { */ func (a *stmtCompiler) defineVar(ident *ast.Ident, t Type) *Variable { - v, prev := a.block.DefineVar(ident.Name(), ident.Pos(), t) + v, prev := a.block.DefineVar(ident.Name, ident.Pos(), t) if prev != nil { - // TODO(austin) It's silly that we have to capture - // Pos() in a variable. - pos := prev.Pos() - if pos.IsValid() { - a.diagAt(ident, "variable %s redeclared in this block\n\tprevious declaration at %s", ident.Name(), &pos) + if prev.Pos().IsValid() { + a.diagAt(ident.Pos(), "variable %s redeclared in this block\n\tprevious declaration at %s", ident.Name, a.fset.Position(prev.Pos())) } else { - a.diagAt(ident, "variable %s redeclared in this block", ident.Name()) + a.diagAt(ident.Pos(), "variable %s redeclared in this block", ident.Name) } return nil } @@ -239,7 +236,7 @@ func (a *stmtCompiler) defineVar(ident *ast.Ident, t Type) *Variable { func (a *stmtCompiler) compile(s ast.Stmt) { if a.block.inner != nil { - log.Crash("Child scope still entered") + log.Panic("Child scope still entered") } notimpl := false @@ -309,7 +306,7 @@ func (a *stmtCompiler) compile(s ast.Stmt) { notimpl = true default: - log.Crashf("unexpected ast node type %T", s) + log.Panicf("unexpected ast node type %T", s) } if notimpl { @@ -317,7 +314,7 @@ func (a *stmtCompiler) compile(s ast.Stmt) { } if a.block.inner != nil { - log.Crash("Forgot to exit child scope") + log.Panic("Forgot to exit child scope") } } @@ -329,16 +326,16 @@ func (a *stmtCompiler) compileDeclStmt(s *ast.DeclStmt) { case *ast.FuncDecl: if !a.block.global { - log.Crash("FuncDecl at statement level") + log.Panic("FuncDecl at statement level") } case *ast.GenDecl: if decl.Tok == token.IMPORT && !a.block.global { - log.Crash("import at statement level") + log.Panic("import at statement level") } default: - log.Crashf("Unexpected Decl type %T", s.Decl) + log.Panicf("Unexpected Decl type %T", s.Decl) } a.compileDecl(s.Decl) } @@ -350,7 +347,7 @@ func (a *stmtCompiler) compileVarDecl(decl *ast.GenDecl) { // Declaration without assignment if spec.Type == nil { // Parser should have caught - log.Crash("Type and Values nil") + log.Panic("Type and Values nil") } t := a.compileType(a.block, spec.Type) // Define placeholders even if type compile failed @@ -381,13 +378,13 @@ func (a *stmtCompiler) compileDecl(decl ast.Decl) { } // Declare and initialize v before compiling func // so that body can refer to itself. - c, prev := a.block.DefineConst(d.Name.Name(), a.pos, decl.Type, decl.Type.Zero()) + c, prev := a.block.DefineConst(d.Name.Name, a.pos, decl.Type, decl.Type.Zero()) if prev != nil { pos := prev.Pos() if pos.IsValid() { - a.diagAt(d.Name, "identifier %s redeclared in this block\n\tprevious declaration at %s", d.Name.Name(), &pos) + a.diagAt(d.Name.Pos(), "identifier %s redeclared in this block\n\tprevious declaration at %s", d.Name.Name, a.fset.Position(pos)) } else { - a.diagAt(d.Name, "identifier %s redeclared in this block", d.Name.Name()) + a.diagAt(d.Name.Pos(), "identifier %s redeclared in this block", d.Name.Name) } } fn := a.compileFunc(a.block, decl, d.Body) @@ -400,9 +397,9 @@ func (a *stmtCompiler) compileDecl(decl ast.Decl) { case *ast.GenDecl: switch d.Tok { case token.IMPORT: - log.Crashf("%v not implemented", d.Tok) + log.Panicf("%v not implemented", d.Tok) case token.CONST: - log.Crashf("%v not implemented", d.Tok) + log.Panicf("%v not implemented", d.Tok) case token.TYPE: a.compileTypeDecl(a.block, d) case token.VAR: @@ -410,20 +407,20 @@ func (a *stmtCompiler) compileDecl(decl ast.Decl) { } default: - log.Crashf("Unexpected Decl type %T", decl) + log.Panicf("Unexpected Decl type %T", decl) } } func (a *stmtCompiler) compileLabeledStmt(s *ast.LabeledStmt) { // Define label - l, ok := a.labels[s.Label.Name()] + l, ok := a.labels[s.Label.Name] if ok { if l.resolved.IsValid() { - a.diag("label %s redeclared in this block\n\tprevious declaration at %s", s.Label.Name(), &l.resolved) + a.diag("label %s redeclared in this block\n\tprevious declaration at %s", s.Label.Name, a.fset.Position(l.resolved)) } } else { pc := badPC - l = &label{name: s.Label.Name(), gotoPC: &pc} + l = &label{name: s.Label.Name, gotoPC: &pc} a.labels[l.name] = l } l.desc = "regular label" @@ -486,14 +483,14 @@ func (a *stmtCompiler) compileIncDecStmt(s *ast.IncDecStmt) { op = token.SUB desc = "decrement statement" default: - log.Crashf("Unexpected IncDec token %v", s.Tok) + log.Panicf("Unexpected IncDec token %v", s.Tok) } effect, l := l.extractEffect(bc.block, desc) one := l.newExpr(IdealIntType, "constant") one.pos = s.Pos() - one.eval = func() *bignum.Integer { return bignum.Int(1) } + one.eval = func() *big.Int { return big.NewInt(1) } binop := l.compileBinaryExpr(op, l, one) if binop == nil { @@ -502,7 +499,7 @@ func (a *stmtCompiler) compileIncDecStmt(s *ast.IncDecStmt) { assign := a.compileAssign(s.Pos(), bc.block, l.t, []*expr{binop}, "", "") if assign == nil { - log.Crashf("compileAssign type check failed") + log.Panicf("compileAssign type check failed") } lf := l.evalAddr @@ -555,14 +552,14 @@ func (a *stmtCompiler) doAssign(lhs []ast.Expr, rhs []ast.Expr, tok token.Token, // Check that it's an identifier ident, ok = le.(*ast.Ident) if !ok { - a.diagAt(le, "left side of := must be a name") + a.diagAt(le.Pos(), "left side of := must be a name") // Suppress new defitions errors nDefs++ continue } // Is this simply an assignment? - if _, ok := a.block.defs[ident.Name()]; ok { + if _, ok := a.block.defs[ident.Name]; ok { ident = nil break } @@ -607,7 +604,7 @@ func (a *stmtCompiler) doAssign(lhs []ast.Expr, rhs []ast.Expr, tok token.Token, case ac.rmt.Elems[i].isFloat(): lt = FloatType default: - log.Crashf("unexpected ideal type %v", rs[i].t) + log.Panicf("unexpected ideal type %v", rs[i].t) } default: @@ -780,7 +777,7 @@ func (a *stmtCompiler) doAssignOp(s *ast.AssignStmt) { assign := a.compileAssign(s.Pos(), bc.block, l.t, []*expr{binop}, "assignment", "value") if assign == nil { - log.Crashf("compileAssign type check failed") + log.Panicf("compileAssign type check failed") } lf := l.evalAddr @@ -861,7 +858,7 @@ func (a *stmtCompiler) findLexicalLabel(name *ast.Ident, pred func(*label) bool, if name == nil && pred(l) { return l } - if name != nil && l.name == name.Name() { + if name != nil && l.name == name.Name { if !pred(l) { a.diag("cannot %s to %s %s", errOp, l.desc, l.name) return nil @@ -872,7 +869,7 @@ func (a *stmtCompiler) findLexicalLabel(name *ast.Ident, pred func(*label) bool, if name == nil { a.diag("%s outside %s", errOp, errCtx) } else { - a.diag("%s label %s not defined", errOp, name.Name()) + a.diag("%s label %s not defined", errOp, name.Name) } return nil } @@ -896,10 +893,10 @@ func (a *stmtCompiler) compileBranchStmt(s *ast.BranchStmt) { pc = l.continuePC case token.GOTO: - l, ok := a.labels[s.Label.Name()] + l, ok := a.labels[s.Label.Name] if !ok { pc := badPC - l = &label{name: s.Label.Name(), desc: "unresolved label", gotoPC: &pc, used: s.Pos()} + l = &label{name: s.Label.Name, desc: "unresolved label", gotoPC: &pc, used: s.Pos()} a.labels[l.name] = l } @@ -911,7 +908,7 @@ func (a *stmtCompiler) compileBranchStmt(s *ast.BranchStmt) { return default: - log.Crash("Unexpected branch token %v", s.Tok) + log.Panic("Unexpected branch token %v", s.Tok) } a.flow.put1(false, pc) @@ -1012,12 +1009,12 @@ func (a *stmtCompiler) compileSwitchStmt(s *ast.SwitchStmt) { for _, c := range s.Body.List { clause, ok := c.(*ast.CaseClause) if !ok { - a.diagAt(clause, "switch statement must contain case clauses") + a.diagAt(clause.Pos(), "switch statement must contain case clauses") continue } if clause.Values == nil { if hasDefault { - a.diagAt(clause, "switch statement contains more than one default case") + a.diagAt(clause.Pos(), "switch statement contains more than one default case") } hasDefault = true } else { @@ -1039,7 +1036,7 @@ func (a *stmtCompiler) compileSwitchStmt(s *ast.SwitchStmt) { case e == nil: // Error reported by compileExpr case cond == nil && !e.t.isBoolean(): - a.diagAt(v, "'case' condition must be boolean") + a.diagAt(v.Pos(), "'case' condition must be boolean") case cond == nil: cases[i] = e.asBool() case cond != nil: @@ -1104,7 +1101,7 @@ func (a *stmtCompiler) compileSwitchStmt(s *ast.SwitchStmt) { // empty blocks to be empty // statements. if _, ok := s2.(*ast.EmptyStmt); !ok { - a.diagAt(s, "fallthrough statement must be final statement in case") + a.diagAt(s.Pos(), "fallthrough statement must be final statement in case") break } } @@ -1235,14 +1232,14 @@ func (a *compiler) compileFunc(b *block, decl *FuncDecl, body *ast.BlockStmt) fu defer bodyScope.exit() for i, t := range decl.Type.In { if decl.InNames[i] != nil { - bodyScope.DefineVar(decl.InNames[i].Name(), decl.InNames[i].Pos(), t) + bodyScope.DefineVar(decl.InNames[i].Name, decl.InNames[i].Pos(), t) } else { bodyScope.DefineTemp(t) } } for i, t := range decl.Type.Out { if decl.OutNames[i] != nil { - bodyScope.DefineVar(decl.OutNames[i].Name(), decl.OutNames[i].Pos(), t) + bodyScope.DefineVar(decl.OutNames[i].Name, decl.OutNames[i].Pos(), t) } else { bodyScope.DefineTemp(t) } @@ -1275,7 +1272,7 @@ func (a *compiler) compileFunc(b *block, decl *FuncDecl, body *ast.BlockStmt) fu // this if there were no errors compiling the body. if len(decl.Type.Out) > 0 && fc.flow.reachesEnd(0) { // XXX(Spec) Not specified. - a.diagAt(&body.Rbrace, "function ends without a return statement") + a.diagAt(body.Rbrace, "function ends without a return statement") return nil } @@ -1290,7 +1287,7 @@ func (a *funcCompiler) checkLabels() { nerr := a.numError() for _, l := range a.labels { if !l.resolved.IsValid() { - a.diagAt(&l.used, "label %s not defined", l.name) + a.diagAt(l.used, "label %s not defined", l.name) } } if nerr != a.numError() { diff --git a/src/pkg/exp/eval/test.bash b/src/pkg/exp/eval/test.bash index 5d9ba1ae7..50b61fd00 100755 --- a/src/pkg/exp/eval/test.bash +++ b/src/pkg/exp/eval/test.bash @@ -10,10 +10,8 @@ set -e -GOBIN="${GOBIN:-$HOME/bin}" - -"$GOBIN"/gomake -"$GOBIN"/6g main.go && "$GOBIN"/6l main.6 +gomake +6g main.go && 6l main.6 ( for i in $(egrep -l '// \$G (\$D/)?\$F\.go \&\& \$L \$F\.\$A && \./\$A\.out' "$GOROOT"/test/*.go "$GOROOT"/test/*/*.go) do diff --git a/src/pkg/exp/eval/type.go b/src/pkg/exp/eval/type.go index b0fbe2156..db77ab198 100644 --- a/src/pkg/exp/eval/type.go +++ b/src/pkg/exp/eval/type.go @@ -5,7 +5,7 @@ package eval import ( - "exp/bignum" + "big" "go/ast" "go/token" "log" @@ -54,18 +54,18 @@ type Type interface { // String returns the string representation of this type. String() string // The position where this type was defined, if any. - Pos() token.Position + Pos() token.Pos } type BoundedType interface { Type // minVal returns the smallest value of this type. - minVal() *bignum.Rational + minVal() *big.Rat // maxVal returns the largest value of this type. - maxVal() *bignum.Rational + maxVal() *big.Rat } -var universePos = token.Position{"", 0, 0, 0} +var universePos = token.NoPos /* * Type array maps. These are used to memoize composite types. @@ -140,7 +140,7 @@ func (commonType) isFloat() bool { return false } func (commonType) isIdeal() bool { return false } -func (commonType) Pos() token.Position { return token.Position{} } +func (commonType) Pos() token.Pos { return token.NoPos } /* * Bool @@ -234,9 +234,9 @@ func (t *uintType) Zero() Value { panic("unexpected uint bit count") } -func (t *uintType) minVal() *bignum.Rational { return bignum.Rat(0, 1) } +func (t *uintType) minVal() *big.Rat { return big.NewRat(0, 1) } -func (t *uintType) maxVal() *bignum.Rational { +func (t *uintType) maxVal() *big.Rat { bits := t.Bits if bits == 0 { if t.Ptr { @@ -245,7 +245,10 @@ func (t *uintType) maxVal() *bignum.Rational { bits = uint(8 * unsafe.Sizeof(uint(0))) } } - return bignum.MakeRat(bignum.Int(1).Shl(bits).Add(bignum.Int(-1)), bignum.Nat(1)) + numer := big.NewInt(1) + numer.Lsh(numer, bits) + numer.Sub(numer, idealOne) + return new(big.Rat).SetInt(numer) } /* @@ -307,20 +310,25 @@ func (t *intType) Zero() Value { panic("unexpected int bit count") } -func (t *intType) minVal() *bignum.Rational { +func (t *intType) minVal() *big.Rat { bits := t.Bits if bits == 0 { bits = uint(8 * unsafe.Sizeof(int(0))) } - return bignum.MakeRat(bignum.Int(-1).Shl(bits-1), bignum.Nat(1)) + numer := big.NewInt(-1) + numer.Lsh(numer, bits-1) + return new(big.Rat).SetInt(numer) } -func (t *intType) maxVal() *bignum.Rational { +func (t *intType) maxVal() *big.Rat { bits := t.Bits if bits == 0 { bits = uint(8 * unsafe.Sizeof(int(0))) } - return bignum.MakeRat(bignum.Int(1).Shl(bits-1).Add(bignum.Int(-1)), bignum.Nat(1)) + numer := big.NewInt(1) + numer.Lsh(numer, bits-1) + numer.Sub(numer, idealOne) + return new(big.Rat).SetInt(numer) } /* @@ -346,7 +354,7 @@ func (t *idealIntType) isIdeal() bool { return true } func (t *idealIntType) String() string { return "ideal integer" } -func (t *idealIntType) Zero() Value { return &idealIntV{bignum.Int(0)} } +func (t *idealIntType) Zero() Value { return &idealIntV{idealZero} } /* * Float @@ -393,12 +401,12 @@ func (t *floatType) Zero() Value { panic("unexpected float bit count") } -var maxFloat32Val = bignum.MakeRat(bignum.Int(0xffffff).Shl(127-23), bignum.Nat(1)) -var maxFloat64Val = bignum.MakeRat(bignum.Int(0x1fffffffffffff).Shl(1023-52), bignum.Nat(1)) -var minFloat32Val = maxFloat32Val.Neg() -var minFloat64Val = maxFloat64Val.Neg() +var maxFloat32Val *big.Rat +var maxFloat64Val *big.Rat +var minFloat32Val *big.Rat +var minFloat64Val *big.Rat -func (t *floatType) minVal() *bignum.Rational { +func (t *floatType) minVal() *big.Rat { bits := t.Bits if bits == 0 { bits = uint(8 * unsafe.Sizeof(float(0))) @@ -409,11 +417,11 @@ func (t *floatType) minVal() *bignum.Rational { case 64: return minFloat64Val } - log.Crashf("unexpected floating point bit count: %d", bits) + log.Panicf("unexpected floating point bit count: %d", bits) panic("unreachable") } -func (t *floatType) maxVal() *bignum.Rational { +func (t *floatType) maxVal() *big.Rat { bits := t.Bits if bits == 0 { bits = uint(8 * unsafe.Sizeof(float(0))) @@ -424,7 +432,7 @@ func (t *floatType) maxVal() *bignum.Rational { case 64: return maxFloat64Val } - log.Crashf("unexpected floating point bit count: %d", bits) + log.Panicf("unexpected floating point bit count: %d", bits) panic("unreachable") } @@ -451,7 +459,7 @@ func (t *idealFloatType) isIdeal() bool { return true } func (t *idealFloatType) String() string { return "ideal float" } -func (t *idealFloatType) Zero() Value { return &idealFloatV{bignum.Rat(1, 0)} } +func (t *idealFloatType) Zero() Value { return &idealFloatV{big.NewRat(0, 1)} } /* * String @@ -704,6 +712,7 @@ var ( panicType = &FuncType{builtin: "panic"} printType = &FuncType{builtin: "print"} printlnType = &FuncType{builtin: "println"} + copyType = &FuncType{builtin: "copy"} ) // Two function types are identical if they have the same number of @@ -763,7 +772,7 @@ func typeListString(ts []Type, ns []*ast.Ident) string { s += ", " } if ns != nil && ns[i] != nil { - s += ns[i].Name() + " " + s += ns[i].Name + " " } if t == nil { // Some places use nil types to represent errors @@ -807,7 +816,7 @@ type FuncDecl struct { func (t *FuncDecl) String() string { s := "func" if t.Name != nil { - s += " " + t.Name.Name() + s += " " + t.Name.Name } s += funcTypeString(t.Type, t.InNames, t.OutNames) return s @@ -861,9 +870,7 @@ func NewInterfaceType(methods []IMethod, embeds []*InterfaceType) *InterfaceType // Combine methods allMethods := make([]IMethod, nMethods) - for i, m := range methods { - allMethods[i] = m - } + copy(allMethods, methods) n := len(methods) for _, e := range embeds { for _, m := range e.methods { @@ -1093,8 +1100,8 @@ type Method struct { } type NamedType struct { - token.Position - Name string + NamePos token.Pos + Name string // Underlying type. If incomplete is true, this will be nil. // If incomplete is false and this is still nil, then this is // a placeholder type representing an error. @@ -1107,12 +1114,16 @@ type NamedType struct { // TODO(austin) This is temporarily needed by the debugger's remote // type parser. This should only be possible with block.DefineType. func NewNamedType(name string) *NamedType { - return &NamedType{token.Position{}, name, nil, true, make(map[string]Method)} + return &NamedType{token.NoPos, name, nil, true, make(map[string]Method)} +} + +func (t *NamedType) Pos() token.Pos { + return t.NamePos } func (t *NamedType) Complete(def Type) { if !t.incomplete { - log.Crashf("cannot complete already completed NamedType %+v", *t) + log.Panicf("cannot complete already completed NamedType %+v", *t) } // We strip the name from def because multiple levels of // naming are useless. @@ -1221,6 +1232,15 @@ func (t *MultiType) Zero() Value { */ func init() { + numer := big.NewInt(0xffffff) + numer.Lsh(numer, 127-23) + maxFloat32Val = new(big.Rat).SetInt(numer) + numer.SetInt64(0x1fffffffffffff) + numer.Lsh(numer, 1023-52) + maxFloat64Val = new(big.Rat).SetInt(numer) + minFloat32Val = new(big.Rat).Neg(maxFloat32Val) + minFloat64Val = new(big.Rat).Neg(maxFloat64Val) + // To avoid portability issues all numeric types are distinct // except byte, which is an alias for uint8. @@ -1232,6 +1252,7 @@ func init() { universe.DefineConst("cap", universePos, capType, nil) universe.DefineConst("close", universePos, closeType, nil) universe.DefineConst("closed", universePos, closedType, nil) + universe.DefineConst("copy", universePos, copyType, nil) universe.DefineConst("len", universePos, lenType, nil) universe.DefineConst("make", universePos, makeType, nil) universe.DefineConst("new", universePos, newType, nil) diff --git a/src/pkg/exp/eval/typec.go b/src/pkg/exp/eval/typec.go index 80ac078a2..de90cf664 100644 --- a/src/pkg/exp/eval/typec.go +++ b/src/pkg/exp/eval/typec.go @@ -26,21 +26,21 @@ type typeCompiler struct { } func (a *typeCompiler) compileIdent(x *ast.Ident, allowRec bool) Type { - _, _, def := a.block.Lookup(x.Name()) + _, _, def := a.block.Lookup(x.Name) if def == nil { - a.diagAt(x, "%s: undefined", x.Name()) + a.diagAt(x.Pos(), "%s: undefined", x.Name) return nil } switch def := def.(type) { case *Constant: - a.diagAt(x, "constant %v used as type", x.Name()) + a.diagAt(x.Pos(), "constant %v used as type", x.Name) return nil case *Variable: - a.diagAt(x, "variable %v used as type", x.Name()) + a.diagAt(x.Pos(), "variable %v used as type", x.Name) return nil case *NamedType: if !allowRec && def.incomplete { - a.diagAt(x, "illegal recursive type") + a.diagAt(x.Pos(), "illegal recursive type") return nil } if !def.incomplete && def.Def == nil { @@ -51,7 +51,7 @@ func (a *typeCompiler) compileIdent(x *ast.Ident, allowRec bool) Type { case Type: return def } - log.Crashf("name %s has unknown type %T", x.Name(), def) + log.Panicf("name %s has unknown type %T", x.Name, def) return nil } @@ -68,7 +68,7 @@ func (a *typeCompiler) compileArrayType(x *ast.ArrayType, allowRec bool) Type { } if _, ok := x.Len.(*ast.Ellipsis); ok { - a.diagAt(x.Len, "... array initailizers not implemented") + a.diagAt(x.Len.Pos(), "... array initailizers not implemented") return nil } l, ok := a.compileArrayLen(a.block, x.Len) @@ -76,7 +76,7 @@ func (a *typeCompiler) compileArrayType(x *ast.ArrayType, allowRec bool) Type { return nil } if l < 0 { - a.diagAt(x.Len, "array length must be non-negative") + a.diagAt(x.Len.Pos(), "array length must be non-negative") return nil } if elem == nil { @@ -86,11 +86,11 @@ func (a *typeCompiler) compileArrayType(x *ast.ArrayType, allowRec bool) Type { return NewArrayType(l, elem) } -func (a *typeCompiler) compileFields(fields *ast.FieldList, allowRec bool) ([]Type, []*ast.Ident, []token.Position, bool) { +func (a *typeCompiler) compileFields(fields *ast.FieldList, allowRec bool) ([]Type, []*ast.Ident, []token.Pos, bool) { n := fields.NumFields() ts := make([]Type, n) ns := make([]*ast.Ident, n) - ps := make([]token.Position, n) + ps := make([]token.Pos, n) bad := false if fields != nil { @@ -132,12 +132,12 @@ func (a *typeCompiler) compileStructType(x *ast.StructType, allowRec bool) Type // uniqueness of field names inherited from anonymous fields // at use time. fields := make([]StructField, len(ts)) - nameSet := make(map[string]token.Position, len(ts)) + nameSet := make(map[string]token.Pos, len(ts)) for i := range fields { // Compute field name and check anonymous fields var name string if names[i] != nil { - name = names[i].Name() + name = names[i].Name } else { if ts[i] == nil { continue @@ -162,7 +162,7 @@ func (a *typeCompiler) compileStructType(x *ast.StructType, allowRec bool) Type // *T, and T itself, may not be a pointer or // interface type. if nt == nil { - a.diagAt(&poss[i], "embedded type must T or *T, where T is a named type") + a.diagAt(poss[i], "embedded type must T or *T, where T is a named type") bad = true continue } @@ -172,7 +172,7 @@ func (a *typeCompiler) compileStructType(x *ast.StructType, allowRec bool) Type lateCheck := a.lateCheck a.lateCheck = func() bool { if _, ok := nt.lit().(*PtrType); ok { - a.diagAt(&poss[i], "embedded type %v is a pointer type", nt) + a.diagAt(poss[i], "embedded type %v is a pointer type", nt) return false } return lateCheck() @@ -181,7 +181,7 @@ func (a *typeCompiler) compileStructType(x *ast.StructType, allowRec bool) Type // Check name uniqueness if prev, ok := nameSet[name]; ok { - a.diagAt(&poss[i], "field %s redeclared\n\tprevious declaration at %s", name, &prev) + a.diagAt(poss[i], "field %s redeclared\n\tprevious declaration at %s", name, a.fset.Position(prev)) bad = true continue } @@ -227,7 +227,7 @@ func (a *typeCompiler) compileInterfaceType(x *ast.InterfaceType, allowRec bool) ts, names, poss, bad := a.compileFields(x.Methods, allowRec) methods := make([]IMethod, len(ts)) - nameSet := make(map[string]token.Position, len(ts)) + nameSet := make(map[string]token.Pos, len(ts)) embeds := make([]*InterfaceType, len(ts)) var nm, ne int @@ -237,12 +237,12 @@ func (a *typeCompiler) compileInterfaceType(x *ast.InterfaceType, allowRec bool) } if names[i] != nil { - name := names[i].Name() + name := names[i].Name methods[nm].Name = name methods[nm].Type = ts[i].(*FuncType) nm++ if prev, ok := nameSet[name]; ok { - a.diagAt(&poss[i], "method %s redeclared\n\tprevious declaration at %s", name, &prev) + a.diagAt(poss[i], "method %s redeclared\n\tprevious declaration at %s", name, a.fset.Position(prev)) bad = true continue } @@ -251,7 +251,7 @@ func (a *typeCompiler) compileInterfaceType(x *ast.InterfaceType, allowRec bool) // Embedded interface it, ok := ts[i].lit().(*InterfaceType) if !ok { - a.diagAt(&poss[i], "embedded type must be an interface") + a.diagAt(poss[i], "embedded type must be an interface") bad = true continue } @@ -259,7 +259,7 @@ func (a *typeCompiler) compileInterfaceType(x *ast.InterfaceType, allowRec bool) ne++ for _, m := range it.methods { if prev, ok := nameSet[m.Name]; ok { - a.diagAt(&poss[i], "method %s redeclared\n\tprevious declaration at %s", m.Name, &prev) + a.diagAt(poss[i], "method %s redeclared\n\tprevious declaration at %s", m.Name, a.fset.Position(prev)) bad = true continue } @@ -288,13 +288,13 @@ func (a *typeCompiler) compileMapType(x *ast.MapType) Type { // that can be map keys except for function types. switch key.lit().(type) { case *StructType: - a.diagAt(x, "map key cannot be a struct type") + a.diagAt(x.Pos(), "map key cannot be a struct type") return nil case *ArrayType: - a.diagAt(x, "map key cannot be an array type") + a.diagAt(x.Pos(), "map key cannot be an array type") return nil case *SliceType: - a.diagAt(x, "map key cannot be a slice type") + a.diagAt(x.Pos(), "map key cannot be a slice type") return nil } return NewMapType(key, val) @@ -339,14 +339,14 @@ func (a *typeCompiler) compileType(x ast.Expr, allowRec bool) Type { return a.compileType(x.X, allowRec) case *ast.Ellipsis: - a.diagAt(x, "illegal use of ellipsis") + a.diagAt(x.Pos(), "illegal use of ellipsis") return nil } - a.diagAt(x, "expression used as type") + a.diagAt(x.Pos(), "expression used as type") return nil notimpl: - a.diagAt(x, "compileType: %T not implemented", x) + a.diagAt(x.Pos(), "compileType: %T not implemented", x) return nil } @@ -370,7 +370,7 @@ func (a *compiler) compileTypeDecl(b *block, decl *ast.GenDecl) bool { for _, spec := range decl.Specs { spec := spec.(*ast.TypeSpec) // Create incomplete type for this type - nt := b.DefineType(spec.Name.Name(), spec.Name.Pos(), nil) + nt := b.DefineType(spec.Name.Name, spec.Name.Pos(), nil) if nt != nil { nt.(*NamedType).incomplete = true } diff --git a/src/pkg/exp/eval/util.go b/src/pkg/exp/eval/util.go deleted file mode 100644 index ffe13e170..000000000 --- a/src/pkg/exp/eval/util.go +++ /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. - -package eval - -import ( - "exp/bignum" -) - -// TODO(austin): Maybe add to bignum in more general form -func ratToString(rat *bignum.Rational) string { - n, dnat := rat.Value() - d := bignum.MakeInt(false, dnat) - w, frac := n.QuoRem(d) - out := w.String() - if frac.IsZero() { - return out - } - - r := frac.Abs() - r = r.Mul(bignum.Nat(1e6)) - dec, tail := r.DivMod(dnat) - // Round last digit - if tail.Cmp(dnat.Div(bignum.Nat(2))) >= 0 { - dec = dec.Add(bignum.Nat(1)) - } - // Strip zeros - ten := bignum.Nat(10) - for !dec.IsZero() { - dec2, r2 := dec.DivMod(ten) - if !r2.IsZero() { - break - } - dec = dec2 - } - out += "." + dec.String() - return out -} diff --git a/src/pkg/exp/eval/value.go b/src/pkg/exp/eval/value.go index dce4bfcf3..cace2fd37 100644 --- a/src/pkg/exp/eval/value.go +++ b/src/pkg/exp/eval/value.go @@ -5,7 +5,7 @@ package eval import ( - "exp/bignum" + "big" "fmt" ) @@ -40,7 +40,7 @@ type IntValue interface { // because ideals are not l-values. type IdealIntValue interface { Value - Get() *bignum.Integer + Get() *big.Int } type FloatValue interface { @@ -51,7 +51,7 @@ type FloatValue interface { type IdealFloatValue interface { Value - Get() *bignum.Rational + Get() *big.Rat } type StringValue interface { @@ -272,7 +272,7 @@ func (v *intV) Set(t *Thread, x int64) { *v = intV(x) } */ type idealIntV struct { - V *bignum.Integer + V *big.Int } func (v *idealIntV) String() string { return v.V.String() } @@ -281,7 +281,7 @@ func (v *idealIntV) Assign(t *Thread, o Value) { v.V = o.(IdealIntValue).Get() } -func (v *idealIntV) Get() *bignum.Integer { return v.V } +func (v *idealIntV) Get() *big.Int { return v.V } /* * Float @@ -322,16 +322,16 @@ func (v *floatV) Set(t *Thread, x float64) { *v = floatV(x) } */ type idealFloatV struct { - V *bignum.Rational + V *big.Rat } -func (v *idealFloatV) String() string { return ratToString(v.V) } +func (v *idealFloatV) String() string { return v.V.FloatString(6) } func (v *idealFloatV) Assign(t *Thread, o Value) { v.V = o.(IdealFloatValue).Get() } -func (v *idealFloatV) Get() *bignum.Rational { return v.V } +func (v *idealFloatV) Get() *big.Rat { return v.V } /* * String @@ -586,8 +586,6 @@ func (v multiV) Assign(t *Thread, o Value) { * Universal constants */ -// TODO(austin) Nothing complains if I accidentally define init with -// arguments. Is this intentional? func init() { s := universe diff --git a/src/pkg/exp/eval/world.go b/src/pkg/exp/eval/world.go index edeb39a5f..02d18bd79 100644 --- a/src/pkg/exp/eval/world.go +++ b/src/pkg/exp/eval/world.go @@ -41,14 +41,14 @@ type stmtCode struct { code code } -func (w *World) CompileStmtList(stmts []ast.Stmt) (Code, os.Error) { +func (w *World) CompileStmtList(fset *token.FileSet, stmts []ast.Stmt) (Code, os.Error) { if len(stmts) == 1 { if s, ok := stmts[0].(*ast.ExprStmt); ok { - return w.CompileExpr(s.X) + return w.CompileExpr(fset, s.X) } } errors := new(scanner.ErrorVector) - cc := &compiler{errors, 0, 0} + cc := &compiler{fset, errors, 0, 0} cb := newCodeBuf() fc := &funcCompiler{ compiler: cc, @@ -73,12 +73,12 @@ func (w *World) CompileStmtList(stmts []ast.Stmt) (Code, os.Error) { return &stmtCode{w, fc.get()}, nil } -func (w *World) CompileDeclList(decls []ast.Decl) (Code, os.Error) { +func (w *World) CompileDeclList(fset *token.FileSet, decls []ast.Decl) (Code, os.Error) { stmts := make([]ast.Stmt, len(decls)) for i, d := range decls { stmts[i] = &ast.DeclStmt{d} } - return w.CompileStmtList(stmts) + return w.CompileStmtList(fset, stmts) } func (s *stmtCode) Type() Type { return nil } @@ -95,9 +95,9 @@ type exprCode struct { eval func(Value, *Thread) } -func (w *World) CompileExpr(e ast.Expr) (Code, os.Error) { +func (w *World) CompileExpr(fset *token.FileSet, e ast.Expr) (Code, os.Error) { errors := new(scanner.ErrorVector) - cc := &compiler{errors, 0, 0} + cc := &compiler{fset, errors, 0, 0} ec := cc.compileExpr(w.scope.block, false, e) if ec == nil { @@ -135,16 +135,16 @@ func (e *exprCode) Run() (Value, os.Error) { return v, err } -func (w *World) Compile(text string) (Code, os.Error) { - stmts, err := parser.ParseStmtList("input", text, nil) +func (w *World) Compile(fset *token.FileSet, text string) (Code, os.Error) { + stmts, err := parser.ParseStmtList(fset, "input", text) if err == nil { - return w.CompileStmtList(stmts) + return w.CompileStmtList(fset, stmts) } // Otherwise try as DeclList. - decls, err1 := parser.ParseDeclList("input", text, nil) + decls, err1 := parser.ParseDeclList(fset, "input", text) if err1 == nil { - return w.CompileDeclList(decls) + return w.CompileDeclList(fset, decls) } // Have to pick an error. @@ -162,13 +162,16 @@ func (e *RedefinitionError) String() string { res := "identifier " + e.Name + " redeclared" pos := e.Prev.Pos() if pos.IsValid() { - res += "; previous declaration at " + pos.String() + // TODO: fix this - currently this code is not reached by the tests + // need to get a file set (fset) from somewhere + //res += "; previous declaration at " + fset.Position(pos).String() + panic(0) } return res } func (w *World) DefineConst(name string, t Type, val Value) os.Error { - _, prev := w.scope.DefineConst(name, token.Position{}, t, val) + _, prev := w.scope.DefineConst(name, token.NoPos, t, val) if prev != nil { return &RedefinitionError{name, prev} } @@ -176,7 +179,7 @@ func (w *World) DefineConst(name string, t Type, val Value) os.Error { } func (w *World) DefineVar(name string, t Type, val Value) os.Error { - v, prev := w.scope.DefineVar(name, token.Position{}, t) + v, prev := w.scope.DefineVar(name, token.NoPos, t) if prev != nil { return &RedefinitionError{name, prev} } diff --git a/src/pkg/exp/iterable/Makefile b/src/pkg/exp/iterable/Makefile deleted file mode 100644 index 9adf714da..000000000 --- a/src/pkg/exp/iterable/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.$(GOARCH) - -TARG=exp/iterable -GOFILES=\ - array.go\ - iterable.go\ - -include ../../../Make.pkg diff --git a/src/pkg/exp/iterable/array.go b/src/pkg/exp/iterable/array.go deleted file mode 100644 index b5c7b5c6e..000000000 --- a/src/pkg/exp/iterable/array.go +++ /dev/null @@ -1,59 +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 iterable - -// This file implements the Iterable interface on some primitive types. - -type ByteArray []byte - -func (a ByteArray) Iter() <-chan interface{} { - ch := make(chan interface{}) - go func() { - for _, e := range a { - ch <- e - } - close(ch) - }() - return ch -} - -type IntArray []int - -func (a IntArray) Iter() <-chan interface{} { - ch := make(chan interface{}) - go func() { - for _, e := range a { - ch <- e - } - close(ch) - }() - return ch -} - -type FloatArray []float - -func (a FloatArray) Iter() <-chan interface{} { - ch := make(chan interface{}) - go func() { - for _, e := range a { - ch <- e - } - close(ch) - }() - return ch -} - -type StringArray []string - -func (a StringArray) Iter() <-chan interface{} { - ch := make(chan interface{}) - go func() { - for _, e := range a { - ch <- e - } - close(ch) - }() - return ch -} diff --git a/src/pkg/exp/iterable/iterable.go b/src/pkg/exp/iterable/iterable.go deleted file mode 100644 index aefff9427..000000000 --- a/src/pkg/exp/iterable/iterable.go +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// The iterable package provides several traversal and searching methods. -// It can be used on anything that satisfies the Iterable interface, -// including vector, though certain functions, such as Map, can also be used on -// something that would produce an infinite amount of data. -package iterable - -import ( - "container/list" - "container/vector" -) - -type Iterable interface { - // Iter should return a fresh channel each time it is called. - Iter() <-chan interface{} -} - -func not(f func(interface{}) bool) func(interface{}) bool { - return func(e interface{}) bool { return !f(e) } -} - -// All tests whether f is true for every element of iter. -func All(iter Iterable, f func(interface{}) bool) bool { - for e := range iter.Iter() { - if !f(e) { - return false - } - } - return true -} - -// Any tests whether f is true for at least one element of iter. -func Any(iter Iterable, f func(interface{}) bool) bool { - return !All(iter, not(f)) -} - -// Data returns a slice containing the elements of iter. -func Data(iter Iterable) []interface{} { - vec := new(vector.Vector) - for e := range iter.Iter() { - vec.Push(e) - } - return vec.Data() -} - -// filteredIterable is a struct that implements Iterable with each element -// passed through a filter. -type filteredIterable struct { - it Iterable - f func(interface{}) bool -} - -func (f *filteredIterable) iterate(out chan<- interface{}) { - for e := range f.it.Iter() { - if f.f(e) { - out <- e - } - } - close(out) -} - -func (f *filteredIterable) Iter() <-chan interface{} { - ch := make(chan interface{}) - go f.iterate(ch) - return ch -} - -// Filter returns an Iterable that returns the elements of iter that satisfy f. -func Filter(iter Iterable, f func(interface{}) bool) Iterable { - return &filteredIterable{iter, f} -} - -// Find returns the first element of iter that satisfies f. -// Returns nil if no such element is found. -func Find(iter Iterable, f func(interface{}) bool) interface{} { - for e := range Filter(iter, f).Iter() { - return e - } - return nil -} - -// Injector is a type representing a function that takes two arguments, -// an accumulated value and an element, and returns the next accumulated value. -// See the Inject function. -type Injector func(interface{}, interface{}) interface{} - -// Inject combines the elements of iter by repeatedly calling f with an -// accumulated value and each element in order. The starting accumulated value -// is initial, and after each call the accumulated value is set to the return -// value of f. For instance, to compute a sum: -// var arr IntArray = []int{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; -// sum := iterable.Inject(arr, 0, -// func(ax interface {}, x interface {}) interface {} { -// return ax.(int) + x.(int) }).(int) -func Inject(iter Iterable, initial interface{}, f Injector) interface{} { - acc := initial - for e := range iter.Iter() { - acc = f(acc, e) - } - return acc -} - -// mappedIterable is a helper struct that implements Iterable, returned by Map. -type mappedIterable struct { - it Iterable - f func(interface{}) interface{} -} - -func (m *mappedIterable) iterate(out chan<- interface{}) { - for e := range m.it.Iter() { - out <- m.f(e) - } - close(out) -} - -func (m *mappedIterable) Iter() <-chan interface{} { - ch := make(chan interface{}) - go m.iterate(ch) - return ch -} - -// Map returns an Iterable that returns the result of applying f to each -// element of iter. -func Map(iter Iterable, f func(interface{}) interface{}) Iterable { - return &mappedIterable{iter, f} -} - -// Partition(iter, f) returns Filter(iter, f) and Filter(iter, !f). -func Partition(iter Iterable, f func(interface{}) bool) (Iterable, Iterable) { - return Filter(iter, f), Filter(iter, not(f)) -} - -// A Func is a function that, when called, sends the -// iterable values on a channel. -type Func func(chan<- interface{}) - -// Iter creates and returns a new channel; it starts a -// goroutine running f to send values to the channel. -func (f Func) Iter() <-chan interface{} { - ch := make(chan interface{}) - go f(ch) - return ch -} - -// Take returns an Iterable that contains the first n elements of iter. -func Take(iter Iterable, n int) Iterable { return Slice(iter, 0, n) } - -// TakeWhile returns an Iterable that contains elements from iter while f is true. -func TakeWhile(iter Iterable, f func(interface{}) bool) Iterable { - return Func(func(ch chan<- interface{}) { - for v := range iter.Iter() { - if !f(v) { - break - } - ch <- v - } - close(ch) - }) -} - -// Drop returns an Iterable that returns each element of iter after the first n elements. -func Drop(iter Iterable, n int) Iterable { - return Func(func(ch chan<- interface{}) { - m := n - for v := range iter.Iter() { - if m > 0 { - m-- - continue - } - ch <- v - } - close(ch) - }) -} - -// DropWhile returns an Iterable that returns each element of iter after the initial sequence for which f returns true. -func DropWhile(iter Iterable, f func(interface{}) bool) Iterable { - return Func(func(ch chan<- interface{}) { - drop := true - for v := range iter.Iter() { - if drop { - if f(v) { - continue - } - drop = false - } - ch <- v - } - close(ch) - }) -} - -// Cycle repeats the values of iter in order infinitely. -func Cycle(iter Iterable) Iterable { - return Func(func(ch chan<- interface{}) { - for { - for v := range iter.Iter() { - ch <- v - } - } - }) -} - -// Chain returns an Iterable that concatentates all values from the specified Iterables. -func Chain(args []Iterable) Iterable { - return Func(func(ch chan<- interface{}) { - for _, e := range args { - for v := range e.Iter() { - ch <- v - } - } - close(ch) - }) -} - -// Zip returns an Iterable of []interface{} consisting of the next element from -// each input Iterable. The length of the returned Iterable is the minimum of -// the lengths of the input Iterables. -func Zip(args []Iterable) Iterable { - return Func(func(ch chan<- interface{}) { - defer close(ch) - if len(args) == 0 { - return - } - iters := make([]<-chan interface{}, len(args)) - for i := 0; i < len(iters); i++ { - iters[i] = args[i].Iter() - } - for { - out := make([]interface{}, len(args)) - for i, v := range iters { - out[i] = <-v - if closed(v) { - return - } - } - ch <- out - } - }) -} - -// ZipWith returns an Iterable containing the result of executing f using arguments read from a and b. -func ZipWith2(f func(c, d interface{}) interface{}, a, b Iterable) Iterable { - return Map(Zip([]Iterable{a, b}), func(a1 interface{}) interface{} { - arr := a1.([]interface{}) - return f(arr[0], arr[1]) - }) -} - -// ZipWith returns an Iterable containing the result of executing f using arguments read from a, b and c. -func ZipWith3(f func(d, e, f interface{}) interface{}, a, b, c Iterable) Iterable { - return Map(Zip([]Iterable{a, b, c}), func(a1 interface{}) interface{} { - arr := a1.([]interface{}) - return f(arr[0], arr[1], arr[2]) - }) -} - -// Slice returns an Iterable that contains the elements from iter -// with indexes in [start, stop). -func Slice(iter Iterable, start, stop int) Iterable { - return Func(func(ch chan<- interface{}) { - defer close(ch) - i := 0 - for v := range iter.Iter() { - switch { - case i >= stop: - return - case i >= start: - ch <- v - } - i++ - } - }) -} - -// Repeat generates an infinite stream of v. -func Repeat(v interface{}) Iterable { - return Func(func(ch chan<- interface{}) { - for { - ch <- v - } - }) -} - -// RepeatTimes generates a stream of n copies of v. -func RepeatTimes(v interface{}, n int) Iterable { - return Func(func(ch chan<- interface{}) { - for i := 0; i < n; i++ { - ch <- v - } - close(ch) - }) -} - -// Group is the type for elements returned by the GroupBy function. -type Group struct { - Key interface{} // key value for matching items - Vals Iterable // Iterable for receiving values in the group -} - -// Key defines the interface required by the GroupBy function. -type Grouper interface { - // Return the key for the given value - Key(interface{}) interface{} - - // Compute equality for the given keys - Equal(a, b interface{}) bool -} - -// GroupBy combines sequences of logically identical values from iter using k -// to generate a key to compare values. Each value emitted by the returned -// Iterable is of type Group, which contains the key used for matching the -// values for the group, and an Iterable for retrieving all the values in the -// group. -func GroupBy(iter Iterable, k Grouper) Iterable { - return Func(func(ch chan<- interface{}) { - var curkey interface{} - var lst *list.List - // Basic strategy is to read one group at a time into a list prior to emitting the Group value - for v := range iter.Iter() { - kv := k.Key(v) - if lst == nil || !k.Equal(curkey, kv) { - if lst != nil { - ch <- Group{curkey, lst} - } - lst = list.New() - curkey = kv - } - lst.PushBack(v) - } - if lst != nil { - ch <- Group{curkey, lst} - } - close(ch) - }) -} - -// Unique removes duplicate values which occur consecutively using id to compute keys. -func Unique(iter Iterable, id Grouper) Iterable { - return Map(GroupBy(iter, id), func(v interface{}) interface{} { return v.(Group).Key }) -} diff --git a/src/pkg/exp/iterable/iterable_test.go b/src/pkg/exp/iterable/iterable_test.go deleted file mode 100644 index 1d60d4b91..000000000 --- a/src/pkg/exp/iterable/iterable_test.go +++ /dev/null @@ -1,387 +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 iterable - -import ( - "container/vector" - "testing" -) - -func TestArrayTypes(t *testing.T) { - // Test that conversion works correctly. - bytes := ByteArray([]byte{1, 2, 3}) - if x := Data(bytes)[1].(byte); x != 2 { - t.Error("Data(bytes)[1].(byte) = %v, want 2", x) - } - ints := IntArray([]int{1, 2, 3}) - if x := Data(ints)[2].(int); x != 3 { - t.Error("Data(ints)[2].(int) = %v, want 3", x) - } - floats := FloatArray([]float{1, 2, 3}) - if x := Data(floats)[0].(float); x != 1 { - t.Error("Data(floats)[0].(float) = %v, want 1", x) - } - strings := StringArray([]string{"a", "b", "c"}) - if x := Data(strings)[1].(string); x != "b" { - t.Error(`Data(strings)[1].(string) = %q, want "b"`, x) - } -} - -var ( - oneToFive = IntArray{1, 2, 3, 4, 5} - sixToTen = IntArray{6, 7, 8, 9, 10} - elevenToTwenty = IntArray{11, 12, 13, 14, 15, 16, 17, 18, 19, 20} -) - -func isNegative(n interface{}) bool { return n.(int) < 0 } -func isPositive(n interface{}) bool { return n.(int) > 0 } -func isAbove3(n interface{}) bool { return n.(int) > 3 } -func isEven(n interface{}) bool { return n.(int)%2 == 0 } -func doubler(n interface{}) interface{} { return n.(int) * 2 } -func addOne(n interface{}) interface{} { return n.(int) + 1 } -func adder(acc interface{}, n interface{}) interface{} { - return acc.(int) + n.(int) -} - -// A stream of the natural numbers: 0, 1, 2, 3, ... -type integerStream struct{} - -func (i integerStream) Iter() <-chan interface{} { - ch := make(chan interface{}) - go func() { - for i := 0; ; i++ { - ch <- i - } - }() - return ch -} - -func TestAll(t *testing.T) { - if !All(oneToFive, isPositive) { - t.Error("All(oneToFive, isPositive) == false") - } - if All(oneToFive, isAbove3) { - t.Error("All(oneToFive, isAbove3) == true") - } -} - -func TestAny(t *testing.T) { - if Any(oneToFive, isNegative) { - t.Error("Any(oneToFive, isNegative) == true") - } - if !Any(oneToFive, isEven) { - t.Error("Any(oneToFive, isEven) == false") - } -} - -func assertArraysAreEqual(t *testing.T, res []interface{}, expected []int) { - if len(res) != len(expected) { - t.Errorf("len(res) = %v, want %v", len(res), len(expected)) - goto missing - } - for i := range res { - if v := res[i].(int); v != expected[i] { - t.Errorf("res[%v] = %v, want %v", i, v, expected[i]) - goto missing - } - } - return -missing: - t.Errorf("res = %v\nwant %v", res, expected) -} - -func TestFilter(t *testing.T) { - ints := integerStream{} - moreInts := Filter(ints, isAbove3).Iter() - res := make([]interface{}, 3) - for i := 0; i < 3; i++ { - res[i] = <-moreInts - } - assertArraysAreEqual(t, res, []int{4, 5, 6}) -} - -func TestFind(t *testing.T) { - ints := integerStream{} - first := Find(ints, isAbove3) - if first.(int) != 4 { - t.Errorf("Find(ints, isAbove3) = %v, want 4", first) - } -} - -func TestInject(t *testing.T) { - res := Inject(oneToFive, 0, adder) - if res.(int) != 15 { - t.Errorf("Inject(oneToFive, 0, adder) = %v, want 15", res) - } -} - -func TestMap(t *testing.T) { - res := Data(Map(Map(oneToFive, doubler), addOne)) - assertArraysAreEqual(t, res, []int{3, 5, 7, 9, 11}) -} - -func TestPartition(t *testing.T) { - ti, fi := Partition(oneToFive, isEven) - assertArraysAreEqual(t, Data(ti), []int{2, 4}) - assertArraysAreEqual(t, Data(fi), []int{1, 3, 5}) -} - -func TestTake(t *testing.T) { - res := Take(oneToFive, 2) - assertArraysAreEqual(t, Data(res), []int{1, 2}) - assertArraysAreEqual(t, Data(res), []int{1, 2}) // second test to ensure that .Iter() returns a new channel - - // take none - res = Take(oneToFive, 0) - assertArraysAreEqual(t, Data(res), []int{}) - - // try to take more than available - res = Take(oneToFive, 20) - assertArraysAreEqual(t, Data(res), oneToFive) -} - -func TestTakeWhile(t *testing.T) { - // take some - res := TakeWhile(oneToFive, func(v interface{}) bool { return v.(int) <= 3 }) - assertArraysAreEqual(t, Data(res), []int{1, 2, 3}) - assertArraysAreEqual(t, Data(res), []int{1, 2, 3}) // second test to ensure that .Iter() returns a new channel - - // take none - res = TakeWhile(oneToFive, func(v interface{}) bool { return v.(int) > 3000 }) - assertArraysAreEqual(t, Data(res), []int{}) - - // take all - res = TakeWhile(oneToFive, func(v interface{}) bool { return v.(int) < 3000 }) - assertArraysAreEqual(t, Data(res), oneToFive) -} - -func TestDrop(t *testing.T) { - // drop none - res := Drop(oneToFive, 0) - assertArraysAreEqual(t, Data(res), oneToFive) - assertArraysAreEqual(t, Data(res), oneToFive) // second test to ensure that .Iter() returns a new channel - - // drop some - res = Drop(oneToFive, 2) - assertArraysAreEqual(t, Data(res), []int{3, 4, 5}) - assertArraysAreEqual(t, Data(res), []int{3, 4, 5}) // second test to ensure that .Iter() returns a new channel - - // drop more than available - res = Drop(oneToFive, 88) - assertArraysAreEqual(t, Data(res), []int{}) -} - -func TestDropWhile(t *testing.T) { - // drop some - res := DropWhile(oneToFive, func(v interface{}) bool { return v.(int) < 3 }) - assertArraysAreEqual(t, Data(res), []int{3, 4, 5}) - assertArraysAreEqual(t, Data(res), []int{3, 4, 5}) // second test to ensure that .Iter() returns a new channel - - // test case where all elements are dropped - res = DropWhile(oneToFive, func(v interface{}) bool { return v.(int) < 100 }) - assertArraysAreEqual(t, Data(res), []int{}) - - // test case where none are dropped - res = DropWhile(oneToFive, func(v interface{}) bool { return v.(int) > 1000 }) - assertArraysAreEqual(t, Data(res), oneToFive) -} - -func TestCycle(t *testing.T) { - res := Cycle(oneToFive) - exp := []int{1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4} - - // read the first nineteen values from the iterable - out := make([]interface{}, 19) - for i, it := 0, res.Iter(); i < 19; i++ { - out[i] = <-it - } - assertArraysAreEqual(t, out, exp) - - res2 := Cycle(sixToTen) - exp2 := []int{6, 7, 8, 9, 10, 6, 7, 8, 9, 10, 6, 7, 8, 9, 10, 6, 7, 8, 9} - for i, it := 0, res2.Iter(); i < 19; i++ { - out[i] = <-it - } - assertArraysAreEqual(t, out, exp2) - - // ensure first iterator was not harmed - for i, it := 0, res.Iter(); i < 19; i++ { - out[i] = <-it - } - assertArraysAreEqual(t, out, exp) -} - -func TestChain(t *testing.T) { - - exp := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} - res := Chain([]Iterable{oneToFive, sixToTen, elevenToTwenty}) - assertArraysAreEqual(t, Data(res), exp) - - // reusing the same iterator should produce the same result again - assertArraysAreEqual(t, Data(res), exp) - - // test short read from Chain - i := 0 - out := make([]interface{}, 4) - for v := range res.Iter() { - out[i] = v - i++ - if i == len(out) { - break - } - } - assertArraysAreEqual(t, out, exp[0:4]) - - // test zero length array - res = Chain([]Iterable{}) - assertArraysAreEqual(t, Data(res), []int{}) -} - -func TestZipWith(t *testing.T) { - exp := []int{7, 9, 11, 13, 15} - - // f with 2 args and 1 return value - f := func(a, b interface{}) interface{} { return a.(int) + b.(int) } - res := ZipWith2(f, oneToFive, sixToTen) - assertArraysAreEqual(t, Data(res), exp) - - // test again to make sure returns new iter each time - assertArraysAreEqual(t, Data(res), exp) - - // test a function with 3 args - f2 := func(a, b, c interface{}) interface{} { return a.(int) + b.(int) + c.(int) } - res = ZipWith3(f2, oneToFive, sixToTen, oneToFive) - exp = []int{8, 11, 14, 17, 20} - assertArraysAreEqual(t, Data(res), exp) - - // test a function with multiple values returned - f3 := func(a, b interface{}) interface{} { return ([]interface{}{a.(int) + 1, b.(int) + 1}) } - res = ZipWith2(f3, oneToFive, sixToTen) - - exp2 := [][]int{[]int{2, 7}, []int{3, 8}, []int{4, 9}, []int{5, 10}, []int{6, 11}} - i := 0 - for v := range res.Iter() { - out := v.([]interface{}) - assertArraysAreEqual(t, out, exp2[i]) - i++ - } - - // test different length iterators--should stop after shortest is exhausted - res = ZipWith2(f, elevenToTwenty, oneToFive) - exp = []int{12, 14, 16, 18, 20} - assertArraysAreEqual(t, Data(res), exp) -} - -func TestSlice(t *testing.T) { - out := Data(Slice(elevenToTwenty, 2, 6)) - exp := []int{13, 14, 15, 16} - assertArraysAreEqual(t, out, exp) - - // entire iterable - out = Data(Slice(elevenToTwenty, 0, len(elevenToTwenty))) - exp = []int{11, 12, 13, 14, 15, 16, 17, 18, 19, 20} - assertArraysAreEqual(t, out, exp) - - // empty slice at offset 0 - exp = []int{} - out = Data(Slice(elevenToTwenty, 0, 0)) - assertArraysAreEqual(t, out, exp) - - // slice upper bound exceeds length of iterable - exp = []int{1, 2, 3, 4, 5} - out = Data(Slice(oneToFive, 0, 88)) - assertArraysAreEqual(t, out, exp) - - // slice upper bounce is lower than lower bound - exp = []int{} - out = Data(Slice(oneToFive, 93, 4)) - assertArraysAreEqual(t, out, exp) - - // slice lower bound is greater than len of iterable - exp = []int{} - out = Data(Slice(oneToFive, 93, 108)) - assertArraysAreEqual(t, out, exp) -} - -func TestRepeat(t *testing.T) { - res := Repeat(42) - i := 0 - for v := range res.Iter() { - if v.(int) != 42 { - t.Fatal("Repeat returned the wrong value") - } - if i == 9 { - break - } - i++ - } -} - -func TestRepeatTimes(t *testing.T) { - res := RepeatTimes(84, 9) - exp := []int{84, 84, 84, 84, 84, 84, 84, 84, 84} - assertArraysAreEqual(t, Data(res), exp) - assertArraysAreEqual(t, Data(res), exp) // second time to ensure new iter is returned - - // 0 repeat - res = RepeatTimes(7, 0) - exp = []int{} - assertArraysAreEqual(t, Data(res), exp) - - // negative repeat - res = RepeatTimes(7, -3) - exp = []int{} - assertArraysAreEqual(t, Data(res), exp) -} - -// a type that implements Key for ints -type intkey struct{} - -func (v intkey) Key(a interface{}) interface{} { - return a -} -func (v intkey) Equal(a, b interface{}) bool { return a.(int) == b.(int) } - -func TestGroupBy(t *testing.T) { - in := IntArray{1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5} - exp := [][]int{[]int{1}, []int{2, 2}, []int{3, 3, 3}, []int{4, 4, 4, 4}, []int{5, 5, 5, 5, 5}} - i := 0 - for x := range GroupBy(in, intkey{}).Iter() { - gr := x.(Group) - if gr.Key.(int) != i+1 { - t.Fatal("group key wrong; expected", i+1, "but got", gr.Key.(int)) - } - vals := Data(gr.Vals) - assertArraysAreEqual(t, vals, exp[i]) - i++ - } - if i != 5 { - t.Fatal("did not return expected number of groups") - } - - // test 0 length Iterable - for _ = range GroupBy(IntArray([]int{}), &intkey{}).Iter() { - t.Fatal("iterator should be empty") - } - - // test case with only uniques - var out vector.Vector - for x := range GroupBy(elevenToTwenty, intkey{}).Iter() { - out.Push(x.(Group).Key) - } - assertArraysAreEqual(t, out.Data(), elevenToTwenty) -} - -func TestUnique(t *testing.T) { - in := IntArray([]int{1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5}) - exp := []int{1, 2, 3, 4, 5} - res := Unique(in, intkey{}) - assertArraysAreEqual(t, Data(res), exp) - assertArraysAreEqual(t, Data(res), exp) // second time to ensure new iter is returned - - // test case with only uniques - res = Unique(elevenToTwenty, intkey{}) - assertArraysAreEqual(t, Data(res), elevenToTwenty) -} diff --git a/src/pkg/exp/nacl/README b/src/pkg/exp/nacl/README deleted file mode 100644 index ec18f1d32..000000000 --- a/src/pkg/exp/nacl/README +++ /dev/null @@ -1,36 +0,0 @@ -Native Client support is still incomplete: -Native Client does not yet allow runtime code generation, -so Go's many uses of closures do not work. - -To try Native Client by running 4s (tetris) or 5s or Spacewar: - -1. Build the Go distribution for your native system. - -2. Download Native Client and install it. - http://nativeclient.googlecode.com/svn/trunk/src/native_client/documentation/getting_started.html - * You can stop after step 4 on those instructions - (the ./scons --prebuilt firefox_install). - -3. (optional) Install "./build/native_client/scons-out/opt-*/staging/sel_ldr" - from the Native Client distribution somewhere in your path as "nacl". - This will let you run binaries using "nacl 8.out". - -4. Build the Go distribution again, this time for Native Client: - cd $GOROOT/src - ./all-nacl.bash - * If you didn't do step 3, the tests at the end will fail, but that's okay. - * If you are on a Mac, your dock will flicker as the "nacl" binary - starts and stops while the tests run. You can stop the tests at any time. - -5. Run "godoc --http=:5103". - * This will run the godoc built for your host OS, not Native Client, - because all-nacl.bash doesn't install a nacl godoc. - * Note that there is a colon before the 5103 in the argument - (shorthand for 0.0.0.0:5103). - * The port must be 5103: that's the only port that Native Client - trusts to run binaries from. - -6. Open Firefox and visit one of: - * http://localhost:5103/src/pkg/exp/4s/4s.html - * http://localhost:5103/src/pkg/exp/4s/5s.html [sic] - * http://localhost:5103/src/pkg/exp/spacewar/spacewar.html diff --git a/src/pkg/exp/nacl/av/Makefile b/src/pkg/exp/nacl/av/Makefile deleted file mode 100644 index d966078ec..000000000 --- a/src/pkg/exp/nacl/av/Makefile +++ /dev/null @@ -1,13 +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.$(GOARCH) - -TARG=exp/nacl/av -GOFILES=\ - av.go\ - event.go\ - image.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/exp/nacl/av/av.go b/src/pkg/exp/nacl/av/av.go deleted file mode 100644 index 5c8728292..000000000 --- a/src/pkg/exp/nacl/av/av.go +++ /dev/null @@ -1,299 +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. - -// Native Client audio/video - -// Package av implements audio and video access for Native Client -// binaries running standalone or embedded in a web browser window. -// -// The C version of the API is documented at -// http://nativeclient.googlecode.com/svn/data/docs_tarball/nacl/googleclient/native_client/scons-out/doc/html/group__audio__video.html -package av - -import ( - "exp/draw" - "exp/nacl/srpc" - "log" - "os" - "syscall" - "unsafe" -) - -var srpcEnabled = srpc.Enabled() - -// native_client/src/trusted/service_runtime/include/sys/audio_video.h - -// Subsystem values for Init. -const ( - SubsystemVideo = 1 << iota - SubsystemAudio - SubsystemEmbed -) -// SubsystemRawEvents; - -// Audio formats. -const ( - AudioFormatStereo44K = iota - AudioFormatStereo48K -) - -// A Window represents a connection to the Native Client window. -// It implements draw.Context. -type Window struct { - Embedded bool // running as part of a web page? - *Image // screen image - - mousec chan draw.Mouse - kbdc chan int - quitc chan bool - resizec chan bool -} - -// *Window implements draw.Context -var _ draw.Context = (*Window)(nil) - -func (w *Window) KeyboardChan() <-chan int { return w.kbdc } - -func (w *Window) MouseChan() <-chan draw.Mouse { - return w.mousec -} - -func (w *Window) QuitChan() <-chan bool { return w.quitc } - -func (w *Window) ResizeChan() <-chan bool { return w.resizec } - -func (w *Window) Screen() draw.Image { return w.Image } - -// Init initializes the Native Client subsystems specified by subsys. -// Init must be called before using any of the other functions -// in this package, and it must be called only once. -// -// If the SubsystemVideo flag is set, Init requests a window of size dx×dy. -// When embedded in a web page, the web page's window specification -// overrides the parameters to Init, so the returned Window may have -// a different size than requested. -// -// If the SubsystemAudio flag is set, Init requests a connection to the -// audio device carrying 44 kHz 16-bit stereo PCM audio samples. -func Init(subsys int, dx, dy int) (*Window, os.Error) { - xsubsys := subsys - if srpcEnabled { - waitBridge() - xsubsys &^= SubsystemVideo | SubsystemEmbed - } - - if xsubsys&SubsystemEmbed != 0 { - return nil, os.NewError("not embedded") - } - - w := new(Window) - err := multimediaInit(xsubsys) - if err != nil { - return nil, err - } - - if subsys&SubsystemVideo != 0 { - if dx, dy, err = videoInit(dx, dy); err != nil { - return nil, err - } - w.Image = newImage(dx, dy, bridge.pixel) - w.resizec = make(chan bool, 64) - w.kbdc = make(chan int, 64) - w.mousec = make(chan draw.Mouse, 64) - w.quitc = make(chan bool) - } - - if subsys&SubsystemAudio != 0 { - var n int - if n, err = audioInit(AudioFormatStereo44K, 2048); err != nil { - return nil, err - } - println("audio", n) - } - - if subsys&SubsystemVideo != 0 { - go w.readEvents() - } - - return w, nil -} - -func (w *Window) FlushImage() { - if w.Image == nil { - return - } - videoUpdate(w.Image.Linear) -} - -func multimediaInit(subsys int) (err os.Error) { - return os.NewSyscallError("multimedia_init", syscall.MultimediaInit(subsys)) -} - -func videoInit(dx, dy int) (ndx, ndy int, err os.Error) { - if srpcEnabled { - bridge.share.ready = 1 - return int(bridge.share.width), int(bridge.share.height), nil - } - if e := syscall.VideoInit(dx, dy); e != 0 { - return 0, 0, os.NewSyscallError("video_init", int(e)) - } - return dx, dy, nil -} - -func videoUpdate(data []Color) (err os.Error) { - if srpcEnabled { - bridge.flushRPC.Call("upcall", nil) - return - } - return os.NewSyscallError("video_update", syscall.VideoUpdate((*uint32)(&data[0]))) -} - -var noEvents = os.NewError("no events") - -func videoPollEvent(ev []byte) (err os.Error) { - if srpcEnabled { - r := bridge.share.eq.ri - if r == bridge.share.eq.wi { - return noEvents - } - copy(ev, bridge.share.eq.event[r][0:]) - bridge.share.eq.ri = (r + 1) % eqsize - return nil - } - return os.NewSyscallError("video_poll_event", syscall.VideoPollEvent(&ev[0])) -} - -func audioInit(fmt int, want int) (got int, err os.Error) { - var x int - e := syscall.AudioInit(fmt, want, &x) - if e == 0 { - return x, nil - } - return 0, os.NewSyscallError("audio_init", e) -} - -var audioSize uintptr - -// AudioStream provides access to the audio device. -// Each call to AudioStream writes the given data, -// which should be a slice of 16-bit stereo PCM audio samples, -// and returns the number of samples required by the next -// call to AudioStream. -// -// To find out the initial number of samples to write, call AudioStream(nil). -// -func AudioStream(data []uint16) (nextSize int, err os.Error) { - if audioSize == 0 { - e := os.NewSyscallError("audio_stream", syscall.AudioStream(nil, &audioSize)) - return int(audioSize), e - } - if data == nil { - return int(audioSize), nil - } - if uintptr(len(data))*2 != audioSize { - log.Stdoutf("invalid audio size want %d got %d", audioSize, len(data)) - } - e := os.NewSyscallError("audio_stream", syscall.AudioStream(&data[0], &audioSize)) - return int(audioSize), e -} - -// Synchronization structure to wait for bridge to become ready. -var bridge struct { - c chan bool - displayFd int - rpcFd int - share *videoShare - pixel []Color - client *srpc.Client - flushRPC *srpc.RPC -} - -// Wait for bridge to become ready. -// When chan is first created, there is nothing in it, -// so this blocks. Once the bridge is ready, multimediaBridge.Run -// will drop a value into the channel. Then any calls -// to waitBridge will finish, taking the value out and immediately putting it back. -func waitBridge() { bridge.c <- <-bridge.c } - -const eqsize = 64 - -// Data structure shared with host via mmap. -type videoShare struct { - revision int32 // definition below is rev 100 unless noted - mapSize int32 - - // event queue - eq struct { - ri uint32 // read index [0,eqsize) - wi uint32 // write index [0,eqsize) - eof int32 - event [eqsize][64]byte - } - - // now unused - _, _, _, _ int32 - - // video backing store information - width, height, _, size int32 - ready int32 // rev 0x101 -} - -// The frame buffer data is videoShareSize bytes after -// the videoShare begins. -const videoShareSize = 16 * 1024 - -type multimediaBridge struct{} - -// If using SRPC, the runtime will call this method to pass in two file descriptors, -// one to mmap to get the display memory, and another to use for SRPCs back -// to the main process. -func (multimediaBridge) Run(arg, ret []interface{}, size []int) srpc.Errno { - bridge.displayFd = arg[0].(int) - bridge.rpcFd = arg[1].(int) - - var st syscall.Stat_t - if errno := syscall.Fstat(bridge.displayFd, &st); errno != 0 { - log.Exitf("mmbridge stat display: %s", os.Errno(errno)) - } - - addr, _, errno := syscall.Syscall6(syscall.SYS_MMAP, - 0, - uintptr(st.Size), - syscall.PROT_READ|syscall.PROT_WRITE, - syscall.MAP_SHARED, - uintptr(bridge.displayFd), - 0) - if errno != 0 { - log.Exitf("mmap display: %s", os.Errno(errno)) - } - - bridge.share = (*videoShare)(unsafe.Pointer(addr)) - - // Overestimate frame buffer size - // (must use a compile-time constant) - // and then reslice. 256 megapixels (1 GB) should be enough. - fb := (*[256 * 1024 * 1024]Color)(unsafe.Pointer(addr + videoShareSize)) - bridge.pixel = fb[0 : (st.Size-videoShareSize)/4] - - // Configure RPC connection back to client. - var err os.Error - bridge.client, err = srpc.NewClient(bridge.rpcFd) - if err != nil { - log.Exitf("NewClient: %s", err) - } - bridge.flushRPC = bridge.client.NewRPC(nil) - - // Notify waiters that the bridge is ready. - println("bridged", bridge.share.revision) - bridge.c <- true - - return srpc.OK -} - -func init() { - bridge.c = make(chan bool, 1) - if srpcEnabled { - srpc.Add("nacl_multimedia_bridge", "hh:", multimediaBridge{}) - } -} diff --git a/src/pkg/exp/nacl/av/event.go b/src/pkg/exp/nacl/av/event.go deleted file mode 100644 index 11405c980..000000000 --- a/src/pkg/exp/nacl/av/event.go +++ /dev/null @@ -1,472 +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. - -// NaCl GUI events. -// Clients do not have raw access to the event stream -// (only filtered through the lens of package draw) -// but perhaps they will. - -package av - -import ( - "encoding/binary" - "exp/draw" - "log" - "os" - "time" -) - -// An eventType identifies the type of a Native Client Event. -type eventType uint8 - -const ( - eventActive = 1 + iota - eventExpose - eventKeyDown - eventKeyUp - eventMouseMotion - eventMouseButtonDown - eventMouseButtonUp - eventQuit - eventUnsupported -) - -// A key represents a key on a keyboard. -type key uint16 - -const ( - keyUnknown = 0 - keyFirst = 0 - keyBackspace = 8 - keyTab = 9 - keyClear = 12 - keyReturn = 13 - keyPause = 19 - keyEscape = 27 - keySpace = 32 - keyExclaim = 33 - keyQuotedbl = 34 - keyHash = 35 - keyDollar = 36 - keyAmpersand = 38 - keyQuote = 39 - keyLeftparen = 40 - keyRightparen = 41 - keyAsterisk = 42 - keyPlus = 43 - keyComma = 44 - keyMinus = 45 - keyPeriod = 46 - keySlash = 47 - key0 = 48 - key1 = 49 - key2 = 50 - key3 = 51 - key4 = 52 - key5 = 53 - key6 = 54 - key7 = 55 - key8 = 56 - key9 = 57 - keyColon = 58 - keySemicolon = 59 - keyLess = 60 - keyEquals = 61 - keyGreater = 62 - keyQuestion = 63 - keyAt = 64 - keyLeftbracket = 91 - keyBackslash = 92 - keyRightbracket = 93 - keyCaret = 94 - keyUnderscore = 95 - keyBackquote = 96 - keyA = 97 - keyB = 98 - keyC = 99 - keyD = 100 - keyE = 101 - keyF = 102 - keyG = 103 - keyH = 104 - keyI = 105 - keyJ = 106 - keyK = 107 - keyL = 108 - keyM = 109 - keyN = 110 - keyO = 111 - keyP = 112 - keyQ = 113 - keyR = 114 - keyS = 115 - keyT = 116 - keyU = 117 - keyV = 118 - keyW = 119 - keyX = 120 - keyY = 121 - keyZ = 122 - keyDelete = 127 - keyWorld0 = 160 - keyWorld1 = 161 - keyWorld2 = 162 - keyWorld3 = 163 - keyWorld4 = 164 - keyWorld5 = 165 - keyWorld6 = 166 - keyWorld7 = 167 - keyWorld8 = 168 - keyWorld9 = 169 - keyWorld10 = 170 - keyWorld11 = 171 - keyWorld12 = 172 - keyWorld13 = 173 - keyWorld14 = 174 - keyWorld15 = 175 - keyWorld16 = 176 - keyWorld17 = 177 - keyWorld18 = 178 - keyWorld19 = 179 - keyWorld20 = 180 - keyWorld21 = 181 - keyWorld22 = 182 - keyWorld23 = 183 - keyWorld24 = 184 - keyWorld25 = 185 - keyWorld26 = 186 - keyWorld27 = 187 - keyWorld28 = 188 - keyWorld29 = 189 - keyWorld30 = 190 - keyWorld31 = 191 - keyWorld32 = 192 - keyWorld33 = 193 - keyWorld34 = 194 - keyWorld35 = 195 - keyWorld36 = 196 - keyWorld37 = 197 - keyWorld38 = 198 - keyWorld39 = 199 - keyWorld40 = 200 - keyWorld41 = 201 - keyWorld42 = 202 - keyWorld43 = 203 - keyWorld44 = 204 - keyWorld45 = 205 - keyWorld46 = 206 - keyWorld47 = 207 - keyWorld48 = 208 - keyWorld49 = 209 - keyWorld50 = 210 - keyWorld51 = 211 - keyWorld52 = 212 - keyWorld53 = 213 - keyWorld54 = 214 - keyWorld55 = 215 - keyWorld56 = 216 - keyWorld57 = 217 - keyWorld58 = 218 - keyWorld59 = 219 - keyWorld60 = 220 - keyWorld61 = 221 - keyWorld62 = 222 - keyWorld63 = 223 - keyWorld64 = 224 - keyWorld65 = 225 - keyWorld66 = 226 - keyWorld67 = 227 - keyWorld68 = 228 - keyWorld69 = 229 - keyWorld70 = 230 - keyWorld71 = 231 - keyWorld72 = 232 - keyWorld73 = 233 - keyWorld74 = 234 - keyWorld75 = 235 - keyWorld76 = 236 - keyWorld77 = 237 - keyWorld78 = 238 - keyWorld79 = 239 - keyWorld80 = 240 - keyWorld81 = 241 - keyWorld82 = 242 - keyWorld83 = 243 - keyWorld84 = 244 - keyWorld85 = 245 - keyWorld86 = 246 - keyWorld87 = 247 - keyWorld88 = 248 - keyWorld89 = 249 - keyWorld90 = 250 - keyWorld91 = 251 - keyWorld92 = 252 - keyWorld93 = 253 - keyWorld94 = 254 - keyWorld95 = 255 - - // Numeric keypad - keyKp0 = 256 - keyKp1 = 257 - keyKp2 = 258 - keyKp3 = 259 - keyKp4 = 260 - keyKp5 = 261 - keyKp6 = 262 - keyKp7 = 263 - keyKp8 = 264 - keyKp9 = 265 - keyKpPeriod = 266 - keyKpDivide = 267 - keyKpMultiply = 268 - keyKpMinus = 269 - keyKpPlus = 270 - keyKpEnter = 271 - keyKpEquals = 272 - - // Arrow & insert/delete pad - keyUp = 273 - keyDown = 274 - keyRight = 275 - keyLeft = 276 - keyInsert = 277 - keyHome = 278 - keyEnd = 279 - keyPageup = 280 - keyPagedown = 281 - - // Function keys - keyF1 = 282 - keyF2 = 283 - keyF3 = 284 - keyF4 = 285 - keyF5 = 286 - keyF6 = 287 - keyF7 = 288 - keyF8 = 289 - keyF9 = 290 - keyF10 = 291 - keyF11 = 292 - keyF12 = 293 - keyF13 = 294 - keyF14 = 295 - keyF15 = 296 - - // Modifier keys - keyNumlock = 300 - keyCapslock = 301 - keyScrollock = 302 - keyRshift = 303 - keyLshift = 304 - keyRctrl = 305 - keyLctrl = 306 - keyRalt = 307 - keyLalt = 308 - keyRmeta = 309 - keyLmeta = 310 - keyLsuper = 311 - keyRsuper = 312 - keyMode = 313 - keyCompose = 314 - - // Misc keys - keyHelp = 315 - keyPrint = 316 - keySysreq = 317 - keyBreak = 318 - keyMenu = 319 - keyPower = 320 - keyEuro = 321 - keyUndo = 322 - - // Add any other keys here - keyLast -) - -// A keymod is a set of bit flags -type keymod uint16 - -const ( - keymodNone = 0x0000 - keymodLshift = 0x0001 - keymodRshift = 0x0002 - keymodLctrl = 0x0040 - keymodRctrl = 0x0080 - keymodLalt = 0x0100 - keymodRalt = 0x0200 - keymodLmeta = 0x0400 - keymodRmeta = 0x0800 - keymodNum = 0x1000 - keymodCaps = 0x2000 - keymodMode = 0x4000 - keymodReserved = 0x8000 -) - -const ( - mouseButtonLeft = 1 - mouseButtonMiddle = 2 - mouseButtonRight = 3 - mouseScrollUp = 4 - mouseScrollDown = 5 -) - -const ( - mouseStateLeftButtonPressed = 1 - mouseStateMiddleButtonPressed = 2 - mouseStateRightButtonPressed = 4 -) - -const ( - activeMouse = 1 // mouse leaving/entering - activeInputFocus = 2 // input focus lost/restored - activeApplication = 4 // application minimized/restored -) - -const maxEventBytes = 64 - -type activeEvent struct { - EventType eventType - Gain uint8 - State uint8 -} - -type exposeEvent struct { - EventType eventType -} - -type keyboardEvent struct { - EventType eventType - Device uint8 - State uint8 - Pad uint8 - ScanCode uint8 - Pad1 uint8 - Key key - Mod keymod - Unicode uint16 -} - -type mouseMotionEvent struct { - EventType eventType - Device uint8 - Buttons uint8 - Pad uint8 - X uint16 - Y uint16 - Xrel int16 - Yrel int16 -} - -type mouseButtonEvent struct { - EventType eventType - Device uint8 - Button uint8 - State uint8 - X uint16 - Y uint16 -} - -type quitEvent struct { - EventType eventType -} - -type syncEvent struct{} - -type event interface{} - -type reader []byte - -func (r *reader) Read(p []byte) (n int, err os.Error) { - b := *r - if len(b) == 0 && len(p) > 0 { - return 0, os.EOF - } - n = copy(p, b) - *r = b[n:] - return -} - -func (w *Window) readEvents() { - buf := make([]byte, maxEventBytes) - clean := false - var ( - ea *activeEvent - ee *exposeEvent - ke *keyboardEvent - mme *mouseMotionEvent - mbe *mouseButtonEvent - qe *quitEvent - ) - var m draw.Mouse - for { - if err := videoPollEvent(buf); err != nil { - if !clean { - clean = w.resizec <- false - } - time.Sleep(10e6) // 10ms - continue - } - clean = false - var e event - switch buf[0] { - default: - log.Stdout("unsupported event type", buf[0]) - continue - case eventActive: - ea = new(activeEvent) - e = ea - case eventExpose: - ee = new(exposeEvent) - e = ee - case eventKeyDown, eventKeyUp: - ke = new(keyboardEvent) - e = ke - case eventMouseMotion: - mme = new(mouseMotionEvent) - e = mme - case eventMouseButtonDown, eventMouseButtonUp: - mbe = new(mouseButtonEvent) - e = mbe - case eventQuit: - qe = new(quitEvent) - e = qe - } - r := reader(buf) - if err := binary.Read(&r, binary.LittleEndian, e); err != nil { - log.Stdout("unpacking %T event: %s", e, err) - continue - } - // log.Stdoutf("%#v\n", e); - switch buf[0] { - case eventExpose: - w.resizec <- true - case eventKeyDown: - w.kbdc <- int(ke.Key) - case eventKeyUp: - w.kbdc <- -int(ke.Key) - case eventMouseMotion: - m.X = int(mme.X) - m.Y = int(mme.Y) - m.Buttons = int(mme.Buttons) - m.Nsec = time.Nanoseconds() - _ = w.mousec <- m - case eventMouseButtonDown: - m.X = int(mbe.X) - m.Y = int(mbe.Y) - // TODO(rsc): Remove uint cast once 8g bug is fixed. - m.Buttons |= 1 << uint(mbe.Button-1) - m.Nsec = time.Nanoseconds() - _ = w.mousec <- m - case eventMouseButtonUp: - m.X = int(mbe.X) - m.Y = int(mbe.Y) - // TODO(rsc): Remove uint cast once 8g bug is fixed. - m.Buttons &^= 1 << uint(mbe.Button-1) - m.Nsec = time.Nanoseconds() - _ = w.mousec <- m - case eventQuit: - w.quitc <- true - } - } -} diff --git a/src/pkg/exp/nacl/av/image.go b/src/pkg/exp/nacl/av/image.go deleted file mode 100644 index 8de531136..000000000 --- a/src/pkg/exp/nacl/av/image.go +++ /dev/null @@ -1,85 +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 av - -import ( - "image" -) - -// Native Client image format: -// a single linear array of 32-bit ARGB as packed uint32s. - -// An Image represents a Native Client frame buffer. -// The pixels in the image can be accessed as a single -// linear slice or as a two-dimensional slice of slices. -// Image implements image.Image. -type Image struct { - Linear []Color - Pixel [][]Color -} - -var _ image.Image = (*Image)(nil) - -func (m *Image) ColorModel() image.ColorModel { return ColorModel } - -func (m *Image) Width() int { - if len(m.Pixel) == 0 { - return 0 - } - return len(m.Pixel[0]) -} - -func (m *Image) Height() int { return len(m.Pixel) } - -func (m *Image) At(x, y int) image.Color { return m.Pixel[y][x] } - -func (m *Image) Set(x, y int, color image.Color) { - if c, ok := color.(Color); ok { - m.Pixel[y][x] = c - } - m.Pixel[y][x] = makeColor(color.RGBA()) -} - -func newImage(dx, dy int, linear []Color) *Image { - if linear == nil { - linear = make([]Color, dx*dy) - } - pix := make([][]Color, dy) - for i := range pix { - pix[i] = linear[dx*i : dx*(i+1)] - } - return &Image{linear, pix} -} - -// A Color represents a Native Client color value, -// a 32-bit R, G, B, A value packed as 0xAARRGGBB. -type Color uint32 - -func (p Color) RGBA() (r, g, b, a uint32) { - x := uint32(p) - a = x >> 24 - a |= a << 8 - r = (x >> 16) & 0xFF - r |= r << 8 - g = (x >> 8) & 0xFF - g |= g << 8 - b = x & 0xFF - b |= b << 8 - return -} - -func makeColor(r, g, b, a uint32) Color { - return Color(a>>24<<24 | r>>24<<16 | g>>24<<8 | b>>24) -} - -func toColor(color image.Color) image.Color { - if c, ok := color.(Color); ok { - return c - } - return makeColor(color.RGBA()) -} - -// ColorModel is the color model corresponding to the Native Client Color. -var ColorModel = image.ColorModelFunc(toColor) diff --git a/src/pkg/exp/nacl/srpc/Makefile b/src/pkg/exp/nacl/srpc/Makefile deleted file mode 100644 index 7dda292f5..000000000 --- a/src/pkg/exp/nacl/srpc/Makefile +++ /dev/null @@ -1,13 +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.$(GOARCH) - -TARG=exp/nacl/srpc -GOFILES=\ - client.go\ - msg.go\ - server.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/exp/nacl/srpc/client.go b/src/pkg/exp/nacl/srpc/client.go deleted file mode 100644 index f45730ffa..000000000 --- a/src/pkg/exp/nacl/srpc/client.go +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This package implements Native Client's simple RPC (SRPC). -package srpc - -import ( - "bytes" - "log" - "os" - "sync" -) - -// A Client represents the client side of an SRPC connection. -type Client struct { - fd int // fd to server - r msgReceiver - s msgSender - service map[string]srv // services by name - out chan *msg // send to out to write to connection - - mu sync.Mutex // protects pending, idGen - pending map[uint64]*RPC - idGen uint64 // generator for request IDs -} - -// A srv is a single method that the server offers. -type srv struct { - num uint32 // method number - fmt string // argument format -} - -// An RPC represents a single RPC issued by a client. -type RPC struct { - Ret []interface{} // Return values - Done chan *RPC // Channel where notification of done arrives - Errno Errno // Status code - c *Client - id uint64 // request id -} - -// NewClient allocates a new client using the file descriptor fd. -func NewClient(fd int) (c *Client, err os.Error) { - c = new(Client) - c.fd = fd - c.r.fd = fd - c.s.fd = fd - c.service = make(map[string]srv) - c.pending = make(map[uint64]*RPC) - - // service discovery request - m := &msg{ - protocol: protocol, - isReq: true, - Ret: []interface{}{[]byte(nil)}, - Size: []int{4000}, - } - m.packRequest() - c.s.send(m) - m, err = c.r.recv() - if err != nil { - return nil, err - } - m.unpackResponse() - if m.status != OK { - log.Stderrf("NewClient service_discovery: %s", m.status) - return nil, m.status - } - for n, line := range bytes.Split(m.Ret[0].([]byte), []byte{'\n'}, -1) { - i := bytes.Index(line, []byte{':'}) - if i < 0 { - continue - } - c.service[string(line[0:i])] = srv{uint32(n), string(line[i+1:])} - } - - c.out = make(chan *msg) - go c.input() - go c.output() - return c, nil -} - -func (c *Client) input() { - for { - m, err := c.r.recv() - if err != nil { - log.Exitf("client recv: %s", err) - } - if m.unpackResponse(); m.status != OK { - log.Stderrf("invalid message: %s", m.status) - continue - } - c.mu.Lock() - rpc, ok := c.pending[m.requestId] - if ok { - c.pending[m.requestId] = nil, false - } - c.mu.Unlock() - if !ok { - log.Stderrf("unexpected response") - continue - } - rpc.Ret = m.Ret - rpc.Done <- rpc - } -} - -func (c *Client) output() { - for m := range c.out { - c.s.send(m) - } -} - -// NewRPC creates a new RPC on the client connection. -func (c *Client) NewRPC(done chan *RPC) *RPC { - if done == nil { - done = make(chan *RPC) - } - c.mu.Lock() - id := c.idGen - c.idGen++ - c.mu.Unlock() - return &RPC{nil, done, OK, c, id} -} - -// Start issues an RPC request for method name with the given arguments. -// The RPC r must not be in use for another pending request. -// To wait for the RPC to finish, receive from r.Done and then -// inspect r.Ret and r.Errno. -func (r *RPC) Start(name string, arg []interface{}) { - var m msg - - r.Errno = OK - r.c.mu.Lock() - srv, ok := r.c.service[name] - if !ok { - r.c.mu.Unlock() - r.Errno = ErrBadRPCNumber - r.Done <- r - return - } - r.c.pending[r.id] = r - r.c.mu.Unlock() - - m.protocol = protocol - m.requestId = r.id - m.isReq = true - m.rpcNumber = srv.num - m.Arg = arg - - // Fill in the return values and sizes to generate - // the right type chars. We'll take most any size. - - // Skip over input arguments. - // We could check them against arg, but the server - // will do that anyway. - i := 0 - for srv.fmt[i] != ':' { - i++ - } - fmt := srv.fmt[i+1:] - - // Now the return prototypes. - m.Ret = make([]interface{}, len(fmt)-i) - m.Size = make([]int, len(fmt)-i) - for i := 0; i < len(fmt); i++ { - switch fmt[i] { - default: - log.Exitf("unexpected service type %c", fmt[i]) - case 'b': - m.Ret[i] = false - case 'C': - m.Ret[i] = []byte(nil) - m.Size[i] = 1 << 30 - case 'd': - m.Ret[i] = float64(0) - case 'D': - m.Ret[i] = []float64(nil) - m.Size[i] = 1 << 30 - case 'h': - m.Ret[i] = int(-1) - case 'i': - m.Ret[i] = int32(0) - case 'I': - m.Ret[i] = []int32(nil) - m.Size[i] = 1 << 30 - case 's': - m.Ret[i] = "" - m.Size[i] = 1 << 30 - } - } - - m.packRequest() - r.c.out <- &m -} - -// Call is a convenient wrapper that starts the RPC request, -// waits for it to finish, and then returns the results. -// Its implementation is: -// -// r.Start(name, arg) -// <-r.Done -// return r.Ret, r.Errno -// -func (r *RPC) Call(name string, arg []interface{}) (ret []interface{}, err Errno) { - r.Start(name, arg) - <-r.Done - return r.Ret, r.Errno -} diff --git a/src/pkg/exp/nacl/srpc/msg.go b/src/pkg/exp/nacl/srpc/msg.go deleted file mode 100644 index fe36dbdeb..000000000 --- a/src/pkg/exp/nacl/srpc/msg.go +++ /dev/null @@ -1,526 +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. - -// SRPC constants, data structures, and parsing. - -package srpc - -import ( - "math" - "os" - "strconv" - "syscall" - "unsafe" -) - -// An Errno is an SRPC status code. -type Errno uint32 - -const ( - OK Errno = 256 + iota - ErrBreak - ErrMessageTruncated - ErrNoMemory - ErrProtocolMismatch - ErrBadRPCNumber - ErrBadArgType - ErrTooFewArgs - ErrTooManyArgs - ErrInArgTypeMismatch - ErrOutArgTypeMismatch - ErrInternalError - ErrAppError -) - -var errstr = [...]string{ - OK - OK: "ok", - ErrBreak - OK: "break", - ErrMessageTruncated - OK: "message truncated", - ErrNoMemory - OK: "out of memory", - ErrProtocolMismatch - OK: "protocol mismatch", - ErrBadRPCNumber - OK: "invalid RPC method number", - ErrBadArgType - OK: "unexpected argument type", - ErrTooFewArgs - OK: "too few arguments", - ErrTooManyArgs - OK: "too many arguments", - ErrInArgTypeMismatch - OK: "input argument type mismatch", - ErrOutArgTypeMismatch - OK: "output argument type mismatch", - ErrInternalError - OK: "internal error", - ErrAppError - OK: "application error", -} - -func (e Errno) String() string { - if e < OK || int(e-OK) >= len(errstr) { - return "Errno(" + strconv.Itoa64(int64(e)) + ")" - } - return errstr[e-OK] -} - -// A *msgHdr is the data argument to the imc_recvmsg -// and imc_sendmsg system calls. Because it contains unchecked -// counts trusted by the system calls, the data structure is unsafe -// to expose to package clients. -type msgHdr struct { - iov *iov - niov int32 - desc *int32 - ndesc int32 - flags uint32 -} - -// A single region for I/O. Just as unsafe as msgHdr. -type iov struct { - base *byte - len int32 -} - -// A msg is the Go representation of a message. -type msg struct { - rdata []byte // data being consumed during message parsing - rdesc []int32 // file descriptors being consumed during message parsing - wdata []byte // data being generated when replying - - // parsed version of message - protocol uint32 - requestId uint64 - isReq bool - rpcNumber uint32 - gotHeader bool - status Errno // error code sent in response - Arg []interface{} // method arguments - Ret []interface{} // method results - Size []int // max sizes for arrays in method results - fmt string // accumulated format string of arg+":"+ret -} - -// A msgReceiver receives messages from a file descriptor. -type msgReceiver struct { - fd int - data [128 * 1024]byte - desc [8]int32 - hdr msgHdr - iov iov -} - -func (r *msgReceiver) recv() (*msg, os.Error) { - // Init pointers to buffers where syscall recvmsg can write. - r.iov.base = &r.data[0] - r.iov.len = int32(len(r.data)) - r.hdr.iov = &r.iov - r.hdr.niov = 1 - r.hdr.desc = &r.desc[0] - r.hdr.ndesc = int32(len(r.desc)) - n, _, e := syscall.Syscall(syscall.SYS_IMC_RECVMSG, uintptr(r.fd), uintptr(unsafe.Pointer(&r.hdr)), 0) - if e != 0 { - return nil, os.NewSyscallError("imc_recvmsg", int(e)) - } - - // Make a copy of the data so that the next recvmsg doesn't - // smash it. The system call did not update r.iov.len. Instead it - // returned the total byte count as n. - m := new(msg) - m.rdata = make([]byte, n) - copy(m.rdata, r.data[0:]) - - // Make a copy of the desc too. - // The system call *did* update r.hdr.ndesc. - if r.hdr.ndesc > 0 { - m.rdesc = make([]int32, r.hdr.ndesc) - for i := range m.rdesc { - m.rdesc[i] = r.desc[i] - } - } - - return m, nil -} - -// A msgSender sends messages on a file descriptor. -type msgSender struct { - fd int - hdr msgHdr - iov iov -} - -func (s *msgSender) send(m *msg) os.Error { - if len(m.wdata) > 0 { - s.iov.base = &m.wdata[0] - } - s.iov.len = int32(len(m.wdata)) - s.hdr.iov = &s.iov - s.hdr.niov = 1 - s.hdr.desc = nil - s.hdr.ndesc = 0 - _, _, e := syscall.Syscall(syscall.SYS_IMC_SENDMSG, uintptr(s.fd), uintptr(unsafe.Pointer(&s.hdr)), 0) - if e != 0 { - return os.NewSyscallError("imc_sendmsg", int(e)) - } - return nil -} - -// Reading from msg.rdata. -func (m *msg) uint8() uint8 { - if m.status != OK { - return 0 - } - if len(m.rdata) < 1 { - m.status = ErrMessageTruncated - return 0 - } - x := m.rdata[0] - m.rdata = m.rdata[1:] - return x -} - -func (m *msg) uint32() uint32 { - if m.status != OK { - return 0 - } - if len(m.rdata) < 4 { - m.status = ErrMessageTruncated - return 0 - } - b := m.rdata[0:4] - x := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 - m.rdata = m.rdata[4:] - return x -} - -func (m *msg) uint64() uint64 { - if m.status != OK { - return 0 - } - if len(m.rdata) < 8 { - m.status = ErrMessageTruncated - return 0 - } - b := m.rdata[0:8] - x := uint64(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24) - x |= uint64(uint32(b[4])|uint32(b[5])<<8|uint32(b[6])<<16|uint32(b[7])<<24) << 32 - m.rdata = m.rdata[8:] - return x -} - -func (m *msg) bytes(n int) []byte { - if m.status != OK { - return nil - } - if len(m.rdata) < n { - m.status = ErrMessageTruncated - return nil - } - x := m.rdata[0:n] - m.rdata = m.rdata[n:] - return x -} - -// Writing to msg.wdata. -func (m *msg) grow(n int) []byte { - i := len(m.wdata) - if i+n > cap(m.wdata) { - a := make([]byte, i, (i+n)*2) - copy(a, m.wdata) - m.wdata = a - } - m.wdata = m.wdata[0 : i+n] - return m.wdata[i : i+n] -} - -func (m *msg) wuint8(x uint8) { m.grow(1)[0] = x } - -func (m *msg) wuint32(x uint32) { - b := m.grow(4) - b[0] = byte(x) - b[1] = byte(x >> 8) - b[2] = byte(x >> 16) - b[3] = byte(x >> 24) -} - -func (m *msg) wuint64(x uint64) { - b := m.grow(8) - lo := uint32(x) - b[0] = byte(lo) - b[1] = byte(lo >> 8) - b[2] = byte(lo >> 16) - b[3] = byte(lo >> 24) - hi := uint32(x >> 32) - b[4] = byte(hi) - b[5] = byte(hi >> 8) - b[6] = byte(hi >> 16) - b[7] = byte(hi >> 24) -} - -func (m *msg) wbytes(p []byte) { copy(m.grow(len(p)), p) } - -func (m *msg) wstring(s string) { - b := m.grow(len(s)) - for i := range b { - b[i] = s[i] - } -} - -// Parsing of RPC header and arguments. -// -// The header format is: -// protocol uint32; -// requestId uint64; -// isReq bool; -// rpcNumber uint32; -// status uint32; // only for response -// -// Then a sequence of values follow, preceded by the length: -// nvalue uint32; -// -// Each value begins with a one-byte type followed by -// type-specific data. -// -// type uint8; -// 'b': x bool; -// 'C': len uint32; x [len]byte; -// 'd': x float64; -// 'D': len uint32; x [len]float64; -// 'h': x int; // handle aka file descriptor -// 'i': x int32; -// 'I': len uint32; x [len]int32; -// 's': len uint32; x [len]byte; -// -// If this is a request, a sequence of pseudo-values follows, -// preceded by its length (nvalue uint32). -// -// Each pseudo-value is a one-byte type as above, -// followed by a maximum length (len uint32) -// for the 'C', 'D', 'I', and 's' types. -// -// In the Go msg, we represent each argument by -// an empty interface containing the type of x in the -// corresponding case. - -// The current protocol number. -const protocol = 0xc0da0002 - -func (m *msg) unpackHeader() { - m.protocol = m.uint32() - m.requestId = m.uint64() - m.isReq = m.uint8() != 0 - m.rpcNumber = m.uint32() - m.gotHeader = m.status == OK // signal that header parsed successfully - if m.gotHeader && !m.isReq { - status := Errno(m.uint32()) - m.gotHeader = m.status == OK // still ok? - if m.gotHeader { - m.status = status - } - } -} - -func (m *msg) packHeader() { - m.wuint32(m.protocol) - m.wuint64(m.requestId) - if m.isReq { - m.wuint8(1) - } else { - m.wuint8(0) - } - m.wuint32(m.rpcNumber) - if !m.isReq { - m.wuint32(uint32(m.status)) - } -} - -func (m *msg) unpackValues(v []interface{}) { - for i := range v { - t := m.uint8() - m.fmt += string(t) - switch t { - default: - if m.status == OK { - m.status = ErrBadArgType - } - return - case 'b': // bool[1] - v[i] = m.uint8() > 0 - case 'C': // char array - v[i] = m.bytes(int(m.uint32())) - case 'd': // double - v[i] = math.Float64frombits(m.uint64()) - case 'D': // double array - a := make([]float64, int(m.uint32())) - for j := range a { - a[j] = math.Float64frombits(m.uint64()) - } - v[i] = a - case 'h': // file descriptor (handle) - if len(m.rdesc) == 0 { - if m.status == OK { - m.status = ErrBadArgType - } - return - } - v[i] = int(m.rdesc[0]) - m.rdesc = m.rdesc[1:] - case 'i': // int - v[i] = int32(m.uint32()) - case 'I': // int array - a := make([]int32, int(m.uint32())) - for j := range a { - a[j] = int32(m.uint32()) - } - v[i] = a - case 's': // string - v[i] = string(m.bytes(int(m.uint32()))) - } - } -} - -func (m *msg) packValues(v []interface{}) { - for i := range v { - switch x := v[i].(type) { - default: - if m.status == OK { - m.status = ErrInternalError - } - return - case bool: - m.wuint8('b') - if x { - m.wuint8(1) - } else { - m.wuint8(0) - } - case []byte: - m.wuint8('C') - m.wuint32(uint32(len(x))) - m.wbytes(x) - case float64: - m.wuint8('d') - m.wuint64(math.Float64bits(x)) - case []float64: - m.wuint8('D') - m.wuint32(uint32(len(x))) - for _, f := range x { - m.wuint64(math.Float64bits(f)) - } - case int32: - m.wuint8('i') - m.wuint32(uint32(x)) - case []int32: - m.wuint8('I') - m.wuint32(uint32(len(x))) - for _, i := range x { - m.wuint32(uint32(i)) - } - case string: - m.wuint8('s') - m.wuint32(uint32(len(x))) - m.wstring(x) - } - } -} - -func (m *msg) unpackRequest() { - m.status = OK - if m.unpackHeader(); m.status != OK { - return - } - if m.protocol != protocol || !m.isReq { - m.status = ErrProtocolMismatch - return - } - - // type-tagged argument values - m.Arg = make([]interface{}, m.uint32()) - m.unpackValues(m.Arg) - if m.status != OK { - return - } - - // type-tagged expected return sizes. - // fill in zero values for each return value - // and save sizes. - m.fmt += ":" - m.Ret = make([]interface{}, m.uint32()) - m.Size = make([]int, len(m.Ret)) - for i := range m.Ret { - t := m.uint8() - m.fmt += string(t) - switch t { - default: - if m.status == OK { - m.status = ErrBadArgType - } - return - case 'b': // bool[1] - m.Ret[i] = false - case 'C': // char array - m.Size[i] = int(m.uint32()) - m.Ret[i] = []byte(nil) - case 'd': // double - m.Ret[i] = float64(0) - case 'D': // double array - m.Size[i] = int(m.uint32()) - m.Ret[i] = []float64(nil) - case 'h': // file descriptor (handle) - m.Ret[i] = int(-1) - case 'i': // int - m.Ret[i] = int32(0) - case 'I': // int array - m.Size[i] = int(m.uint32()) - m.Ret[i] = []int32(nil) - case 's': // string - m.Size[i] = int(m.uint32()) - m.Ret[i] = "" - } - } -} - -func (m *msg) packRequest() { - m.packHeader() - m.wuint32(uint32(len(m.Arg))) - m.packValues(m.Arg) - m.wuint32(uint32(len(m.Ret))) - for i, v := range m.Ret { - switch x := v.(type) { - case bool: - m.wuint8('b') - case []byte: - m.wuint8('C') - m.wuint32(uint32(m.Size[i])) - case float64: - m.wuint8('d') - case []float64: - m.wuint8('D') - m.wuint32(uint32(m.Size[i])) - case int: - m.wuint8('h') - case int32: - m.wuint8('i') - case []int32: - m.wuint8('I') - m.wuint32(uint32(m.Size[i])) - case string: - m.wuint8('s') - m.wuint32(uint32(m.Size[i])) - } - } -} - -func (m *msg) unpackResponse() { - m.status = OK - if m.unpackHeader(); m.status != OK { - return - } - if m.protocol != protocol || m.isReq { - m.status = ErrProtocolMismatch - return - } - - // type-tagged return values - m.fmt = "" - m.Ret = make([]interface{}, m.uint32()) - m.unpackValues(m.Ret) -} - -func (m *msg) packResponse() { - m.packHeader() - m.wuint32(uint32(len(m.Ret))) - m.packValues(m.Ret) -} diff --git a/src/pkg/exp/nacl/srpc/server.go b/src/pkg/exp/nacl/srpc/server.go deleted file mode 100644 index af2b855f5..000000000 --- a/src/pkg/exp/nacl/srpc/server.go +++ /dev/null @@ -1,201 +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. - -// SRPC server - -package srpc - -import ( - "bytes" - "log" - "os" - "syscall" -) - -// TODO(rsc): I'd prefer to make this -// type Handler func(m *msg) Errno -// but NaCl can't use closures. -// The explicit interface is a way to attach state. - -// A Handler is a handler for an SRPC method. -// It reads arguments from arg, checks size for array limits, -// writes return values to ret, and returns an Errno status code. -type Handler interface { - Run(arg, ret []interface{}, size []int) Errno -} - -type method struct { - name string - fmt string - handler Handler -} - -var rpcMethod []method - -// BUG(rsc): Add's format string should be replaced by analyzing the -// type of an arbitrary func passed in an interface{} using reflection. - -// Add registers a handler for the named method. -// Fmt is a Native Client format string, a sequence of -// alphabetic characters representing the types of the parameter values, -// a colon, and then a sequence of alphabetic characters -// representing the types of the returned values. -// The format characters and corresponding dynamic types are: -// -// b bool -// C []byte -// d float64 -// D []float64 -// h int // a file descriptor (aka handle) -// i int32 -// I []int32 -// s string -// -func Add(name, fmt string, handler Handler) { - n := len(rpcMethod) - if n >= cap(rpcMethod) { - a := make([]method, n, (n+4)*2) - for i := range a { - a[i] = rpcMethod[i] - } - rpcMethod = a - } - rpcMethod = rpcMethod[0 : n+1] - rpcMethod[n] = method{name, fmt, handler} -} - -// Serve accepts new SRPC connections from the file descriptor fd -// and answers RPCs issued on those connections. -// It closes fd and returns an error if the imc_accept system call fails. -func Serve(fd int) os.Error { - defer syscall.Close(fd) - - for { - cfd, _, e := syscall.Syscall(syscall.SYS_IMC_ACCEPT, uintptr(fd), 0, 0) - if e != 0 { - return os.NewSyscallError("imc_accept", int(e)) - } - go serveLoop(int(cfd)) - } - panic("unreachable") -} - -func serveLoop(fd int) { - c := make(chan *msg) - go sendLoop(fd, c) - - var r msgReceiver - r.fd = fd - for { - m, err := r.recv() - if err != nil { - break - } - m.unpackRequest() - if !m.gotHeader { - log.Stderrf("cannot unpack header: %s", m.status) - continue - } - // log.Stdoutf("<- %#v", m); - m.isReq = false // set up for response - go serveMsg(m, c) - } - close(c) -} - -func sendLoop(fd int, c <-chan *msg) { - var s msgSender - s.fd = fd - for m := range c { - // log.Stdoutf("-> %#v", m); - m.packResponse() - s.send(m) - } - syscall.Close(fd) -} - -func serveMsg(m *msg, c chan<- *msg) { - if m.status != OK { - c <- m - return - } - if m.rpcNumber >= uint32(len(rpcMethod)) { - m.status = ErrBadRPCNumber - c <- m - return - } - - meth := &rpcMethod[m.rpcNumber] - if meth.fmt != m.fmt { - switch { - case len(m.fmt) < len(meth.fmt): - m.status = ErrTooFewArgs - case len(m.fmt) > len(meth.fmt): - m.status = ErrTooManyArgs - default: - // There's a type mismatch. - // It's an in-arg mismatch if the mismatch happens - // before the colon; otherwise it's an out-arg mismatch. - m.status = ErrInArgTypeMismatch - for i := 0; i < len(m.fmt) && m.fmt[i] == meth.fmt[i]; i++ { - if m.fmt[i] == ':' { - m.status = ErrOutArgTypeMismatch - break - } - } - } - c <- m - return - } - - m.status = meth.handler.Run(m.Arg, m.Ret, m.Size) - c <- m -} - -// ServeRuntime serves RPCs issued by the Native Client embedded runtime. -// This should be called by main once all methods have been registered using Add. -func ServeRuntime() os.Error { - // Call getFd to check that we are running embedded. - if _, err := getFd(); err != nil { - return err - } - - // We are running embedded. - // The fd returned by getFd is a red herring. - // Accept connections on magic fd 3. - return Serve(3) -} - -// getFd runs the srpc_get_fd system call. -func getFd() (fd int, err os.Error) { - r1, _, e := syscall.Syscall(syscall.SYS_SRPC_GET_FD, 0, 0, 0) - return int(r1), os.NewSyscallError("srpc_get_fd", int(e)) -} - -// Enabled returns true if SRPC is enabled in the Native Client runtime. -func Enabled() bool { - _, err := getFd() - return err == nil -} - -// Service #0, service_discovery, returns a list of the other services -// and their argument formats. -type serviceDiscovery struct{} - -func (serviceDiscovery) Run(arg, ret []interface{}, size []int) Errno { - var b bytes.Buffer - for _, m := range rpcMethod { - b.WriteString(m.name) - b.WriteByte(':') - b.WriteString(m.fmt) - b.WriteByte('\n') - } - if b.Len() > size[0] { - return ErrNoMemory - } - ret[0] = b.Bytes() - return OK -} - -func init() { Add("service_discovery", ":C", serviceDiscovery{}) } diff --git a/src/pkg/exp/ogle/Makefile b/src/pkg/exp/ogle/Makefile index b701afd9e..ef65d36c8 100644 --- a/src/pkg/exp/ogle/Makefile +++ b/src/pkg/exp/ogle/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=exp/ogle GOFILES=\ @@ -23,7 +23,7 @@ CLEANFILES+=ogle include ../../../Make.pkg main.$O: main.go package - $(QUOTED_GOBIN)/$(GC) -I_obj $< + $(GC) -I_obj $< ogle: main.$O - $(QUOTED_GOBIN)/$(LD) -L_obj -o $@ $< + $(LD) -L_obj -o $@ $< diff --git a/src/pkg/exp/ogle/cmd.go b/src/pkg/exp/ogle/cmd.go index 2f087b777..ff137b0f8 100644 --- a/src/pkg/exp/ogle/cmd.go +++ b/src/pkg/exp/ogle/cmd.go @@ -18,6 +18,7 @@ import ( "strings" ) +var fset = token.NewFileSet() var world *eval.World var curProc *Process @@ -43,7 +44,7 @@ func Main() { } // Try line as code - code, err := world.Compile(string(line)) + code, err := world.Compile(fset, string(line)) if err != nil { scanner.PrintError(os.Stderr, err) continue @@ -63,8 +64,7 @@ func Main() { func newScanner(input []byte) (*scanner.Scanner, *scanner.ErrorVector) { sc := new(scanner.Scanner) ev := new(scanner.ErrorVector) - sc.Init("input", input, ev, 0) - + sc.Init(fset, "input", input, ev, 0) return sc, ev } @@ -84,8 +84,8 @@ type cmd struct { } var cmds = []cmd{ - cmd{"load", cmdLoad}, - cmd{"bt", cmdBt}, + {"load", cmdLoad}, + {"bt", cmdBt}, } // getCmd attempts to parse an input line as a registered command. If @@ -101,7 +101,7 @@ func getCmd(line []byte) (*cmd, []byte) { slit := string(lit) for i := range cmds { if cmds[i].cmd == slit { - return &cmds[i], line[pos.Offset+len(lit):] + return &cmds[i], line[fset.Position(pos).Offset+len(lit):] } } return nil, nil diff --git a/src/pkg/exp/ogle/process.go b/src/pkg/exp/ogle/process.go index 970a7497a..58e830aa6 100644 --- a/src/pkg/exp/ogle/process.go +++ b/src/pkg/exp/ogle/process.go @@ -390,17 +390,7 @@ func (p *Process) causesToEvents() ([]Event, os.Error) { // postEvent appends an event to the posted queue. These events will // be processed before any currently pending events. func (p *Process) postEvent(ev Event) { - n := len(p.posted) - m := n * 2 - if m == 0 { - m = 4 - } - posted := make([]Event, n+1, m) - for i, p := range p.posted { - posted[i] = p - } - posted[n] = ev - p.posted = posted + p.posted = append(p.posted, ev) } // processEvents processes events in the event queue until no events @@ -452,7 +442,7 @@ func (p *Process) processEvent(ev Event) (EventAction, os.Error) { action, err = p.goroutineExitHook.handle(ev) default: - log.Crashf("Unknown event type %T in queue", p.event) + log.Panicf("Unknown event type %T in queue", p.event) } if err != nil { diff --git a/src/pkg/exp/ogle/rtype.go b/src/pkg/exp/ogle/rtype.go index ce4fdb663..fd77f1bc2 100644 --- a/src/pkg/exp/ogle/rtype.go +++ b/src/pkg/exp/ogle/rtype.go @@ -103,7 +103,7 @@ func newManualType(t eval.Type, arch Arch) *remoteType { rt = &remoteType{t, offset, fieldAlign, mk} default: - log.Crashf("cannot manually construct type %T", t) + log.Panicf("cannot manually construct type %T", t) } typeMap[t] = rt @@ -142,7 +142,7 @@ func parseRemoteType(a aborter, rs remoteStruct) *remoteType { if sym != nil { name = sym.Name } - log.Stderrf("%sParsing type at %#x (%s)", prtIndent, addr, name) + log.Printf("%sParsing type at %#x (%s)", prtIndent, addr, name) prtIndent += " " defer func() { prtIndent = prtIndent[0 : len(prtIndent)-1] }() } diff --git a/src/pkg/exp/ogle/vars.go b/src/pkg/exp/ogle/vars.go index eed60acec..8a3a14791 100644 --- a/src/pkg/exp/ogle/vars.go +++ b/src/pkg/exp/ogle/vars.go @@ -140,7 +140,7 @@ func (p *Process) populateWorld(w *eval.World) os.Error { // Symbol name name := s.BaseName() if _, ok := pkg[name]; ok { - log.Stderrf("Multiple definitions of symbol %s", s.Name) + log.Printf("Multiple definitions of symbol %s", s.Name) continue } @@ -191,7 +191,7 @@ func (p *Process) populateWorld(w *eval.World) os.Error { err := w.DefineConst(pkgName, pkgType, pkgVal) if err != nil { - log.Stderrf("while defining package %s: %v", pkgName, err) + log.Printf("while defining package %s: %v", pkgName, err) } } diff --git a/src/pkg/exp/spacewar/Makefile b/src/pkg/exp/spacewar/Makefile deleted file mode 100644 index a27e1f969..000000000 --- a/src/pkg/exp/spacewar/Makefile +++ /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. - -all: 8.out - -pdp1.8: pdp1.go - 8g pdp1.go - -spacewar.8: spacewar.go code.go pdp1.8 - 8g spacewar.go code.go - -8.out: spacewar.8 - 8l spacewar.8 - -clean: - rm -f *.8 8.out - diff --git a/src/pkg/exp/spacewar/code.go b/src/pkg/exp/spacewar/code.go deleted file mode 100644 index 6391b500a..000000000 --- a/src/pkg/exp/spacewar/code.go +++ /dev/null @@ -1,7556 +0,0 @@ -// This file contains the assembly language and machine code for -// Spacewar!, the original PDP-1 video game. It is downloaded from -// http://spacewar.oversigma.com/sources/sources.zip which has -// the following notice at http://spacewar.oversigma.com/: -// -// Spacewar! was conceived in 1961 by Martin Graetz, Stephen Russell, -// and Wayne Wiitanen. It was first realized on the PDP-1 in 1962 by -// Stephen Russell, Peter Samson, Dan Edwards, and Martin Graetz, -// together with Alan Kotok, Steve Piner, and Robert A Saunders. -// Spacewar! is in the public domain, but this credit paragraph must -// accompany all distributed versions of the program. -// -// This is the original version! Martin Graetz provided us with a -// printed version of the source. We typed in in again - it was about -// 40 pages long - and re-assembled it with a PDP-1 assembler written -// in PERL. The resulting binary runs on a PDP-1 emulator written as -// a Java applet. The code is extremely faithful to the original. There -// are only two changes. 1)The spaceships have been made bigger and -// 2) The overall timing has been special cased to deal with varying -// machine speeds. -// -// The "a", "s", "d", "f" keys control one of the spaceships. The "k", -// "l", ";", "'" keys control the other. The controls are spin one -// way, spin the other, thrust, and fire. -// -// Barry Silverman -// Brian Silverman -// Vadim Gerasimov -// - -package main - -const spacewarCode = ` --/macro fio-dec system, june 1963 - 007652 640500 szm=sza sma-szf - 007652 650500 spq=szm i - 007652 761200 clc=cma+cla-opr -- define senseswitch A -- repeat 3, A=A+A -- szs A -- term -- define init A,B -- law B -- dap A -- term -- define index A,B,C -- idx A -- sas B -- jmp C -- term -- define listen -- cla+cli+clf 1-opr-opr -- szf i 1 -- jmp .-1 -- tyi -- term -- define swap -- rcl 9s -- rcl 9s -- term -- define load A,B -- lio (B -- dio A -- term -- define setup A,B -- law i B -- dac A -- term -- define count A,B -- isp A -- jmp B -- term -- define move A,B -- lio A -- dio B -- term -- define clear A,B -- init .+2, A -- dzm -- index .-1, (dzm B+1, .-1 -- term --/spacewar 3.1 24 sep 62 p1. 1 - 000003 3/ - 000003 600061 jmp sbf / ignore seq. break - 000004 601561 jmp a40 - 000005 601556 jmp a1 / use test word for control, note iot 11 00 --/ interesting and often changed constants --/symb loc usual value (all instructions are executed, --/ and may be replaced by jda or jsp) - 000006 tno, - 000006 6, - 000006 710041 law i 41 / number of torps + 1 - 000007 tvl, - 000007 7, - 000007 675017 sar 4s / torpedo velocity - 000010 rlt, - 000010 10, - 000010 710020 law i 20 / torpedo reload time - 000011 tlf, - 000011 11, - 000011 710140 law i 140 / torpedo life - 000012 foo, - 000012 12, - 000012 757777 -20000 / fuel supply - 000013 maa, - 000013 13, - 000013 000010 10 / spaceship angular acceleration - 000014 sac, - 000014 14, - 000014 675017 sar 4s / spaceship acceleration - 000015 str, - 000015 15, - 000015 000001 1 / star capture radius - 000016 me1, - 000016 16, - 000016 006000 6000 / collision "radius" - 000017 me2, - 000017 17, - 000017 003000 3000 / above/2 - 000020 ddd, - 000020 20, - 000020 777777 777777 / 0 to save space for ddt - 000021 the, - 000021 21, - 000021 675777 sar 9s / amount of torpedo space warpage - 000022 mhs, - 000022 22, - 000022 710010 law i 10 / number of hyperspace shots - 000023 hd1, - 000023 23, - 000023 710040 law i 40 / time in hyperspace before breakout - 000024 hd2, - 000024 24, - 000024 710100 law i 100 / time in hyperspace breakout - 000025 hd3, - 000025 25, - 000025 710200 law i 200 / time to recharge hyperfield generator - 000026 hr1, - 000026 26, - 000026 667777 scl 9s / scale on hyperspatial displacement - 000027 hr2, - 000027 27, - 000027 667017 scl 4s / scale on hyperspatially induced velocity - 000030 hur, - 000030 30, - 000030 040000 40000 / hyperspatial uncertancy - 000031 ran, - 000031 31, - 000031 000000 0 / random number --/ place to build a private control word routine. --/ it should leave the control word in the io as follows. --/ high order 4 bits, rotate ccw, rotate cw, (both mean hyperspace) --/ fire rocket, and fire torpedo. low order 4 bits, same for --/ other ship. routine is entered by jsp cwg. - 000040 40/ - 000040 cwr, - 000040 601672 jmp mg1 / normally iot 11 control - 000061 . 20/ / space --//// --/ routine to flush sequence breaks, if they occur. - 000061 sbf, - 000061 720004 tyi - 000062 220002 lio 2 - 000063 200000 lac 0 - 000064 720054 lsm - 000065 610001 jmp i 1 -- define xincr X,Y,INS -- lac Y -- INS ~ssn -- dac Y -- lac X -- INS ~scn -- dac X -- term -- define yincr X,Y,INS -- lac Y -- INS ~scn -- dac Y -- lac X -- -INS+add+sub ~ssn -- dac X -- term --//// -- define dispatch -- add (a+r -- dap . 1 -- jmp . --a, -- term -- define dispt A,Y,B -- repeat 6, B=B+B -- lio Y -- dpy-A+B -- term -- define scale A,B,C -- lac A -- sar B -- dac C -- term -- define diff V,S,QF -- add i V -- dac i V -- xct QF -- add i S -- dac i S -- term -- define random -- lac ran -- rar 1s -- xor (355760 -- add (355670 -- dac ran -- term -- define ranct S,X,C -- random -- S -- X -- sma -- cma -- dac C -- term --//// --/sine-cosine subroutine. adams associates --/calling sequence= number in ac, jda jda sin or jdacos. --/argument is between q+2 pi, with binary point to right of bit 3. --/anser has binary point to right of bit 0. time = 2.35 ms. -- define mult Z -- jda mpy -- lac Z -- term - 000066 cos, - 000066 000000 0 - 000067 260142 dap csx - 000070 202760 lac (62210 - 000071 400066 add cos - 000072 240074 dac sin - 000073 600077 jmp .+4 - 000074 sin, - 000074 000000 0 - 000075 260142 dap csx - 000076 200074 lac sin - 000077 640200 spa - 000100 si1, - 000100 402761 add (311040 - 000101 422760 sub (62210 - 000102 640400 sma - 000103 600143 jmp si2 - 000104 402760 add (62210 - 000105 si3, - 000105 661003 ral 2s -- mult (242763 -+000106 170171 jda mpy -+000107 202762 lac ZZ11 - 000110 240074 dac sin -- mult sin -+000111 170171 jda mpy -+000112 200074 lac ZZ12 - 000113 240066 dac cos -- mult (756103 -+000114 170171 jda mpy -+000115 202763 lac ZZ13 - 000116 402764 add (121312 -- mult cos -+000117 170171 jda mpy -+000120 200066 lac ZZ14 - 000121 402765 add (532511 -- mult cos -+000122 170171 jda mpy -+000123 200066 lac ZZ15 - 000124 402766 add (144417 -- mult sin -+000125 170171 jda mpy -+000126 200074 lac ZZ16 - 000127 667007 scl 3s - 000130 240066 dac cos - 000131 060074 xor sin - 000132 640400 sma - 000133 600141 jmp csx-1 - 000134 202767 lac (377777 - 000135 220074 lio sin - 000136 642000 spi - 000137 761000 cma - 000140 600142 jmp csx - 000141 200066 lac cos - 000142 csx, - 000142 600142 jmp . - 000143 si2, - 000143 761000 cma - 000144 402760 add (62210 - 000145 640400 sma - 000146 600105 jmp si3 - 000147 402760 add (62210 - 000150 640200 spa - 000151 600154 jmp .+3 - 000152 422760 sub (62210 - 000153 600105 jmp si3 - 000154 422760 sub (62210 - 000155 600100 jmp si1 --//// --/bbn multiply subroutine --/call.. lac one factor, jdy mpy or imp, lac other factor. - 000156 imp, - 000156 000000 0 /returns low 17 bits and sign in ac - 000157 260160 dap im1 - 000160 im1, - 000160 100000 xct - 000161 170171 jda mpy - 000162 200156 lac imp - 000163 440160 idx im1 - 000164 672001 rir 1s - 000165 673777 rcr 9s - 000166 673777 rcr 9s - 000167 610160 jmp i im1 - 000170 mp2, - 000170 000000 0 - 000171 mpy, - 000171 000000 0 /return 34 bits and 2 signs - 000172 260200 dap mp1 - 000173 200171 lac mpy - 000174 640200 spa - 000175 761000 cma - 000176 673777 rcr 9s - 000177 673777 rcr 9s - 000200 mp1, - 000200 100000 xct - 000201 640200 spa - 000202 761000 cma - 000203 240170 dac mp2 - 000204 760200 cla - 000205 540170 mus mp2 -+000206 540170 mus mp2 -+000207 540170 mus mp2 -+000210 540170 mus mp2 -+000211 540170 mus mp2 -+000212 540170 mus mp2 -+000213 540170 mus mp2 -+000214 540170 mus mp2 -+000215 540170 mus mp2 -+000216 540170 mus mp2 -+000217 540170 mus mp2 -+000220 540170 mus mp2 -+000221 540170 mus mp2 -+000222 540170 mus mp2 -+000223 540170 mus mp2 -+000224 540170 mus mp2 -+000225 540170 mus mp2 - 000226 240170 dac mp2 - 000227 100200 xct mp1 - 000230 060171 xor mpy - 000231 640400 sma - 000232 600243 jmp mp3 - 000233 200170 lac mp2 - 000234 761000 cma - 000235 673777 rcr 9s - 000236 673777 rcr 9s - 000237 761000 cma - 000240 673777 rcr 9s - 000241 673777 rcr 9s - 000242 240170 dac mp2 - 000243 mp3, - 000243 440200 idx mp1 - 000244 200170 lac mp2 - 000245 610200 jmp i mp1 --//// --/integer square root --/input in ac, binary point to right of bit 17, jda sqt --/answer in ac with binary point between 8 and 9 --/largest input number = 177777 - 000246 sqt, - 000246 000000 0 - 000247 260260 dap sqx - 000250 710023 law i 23 - 000251 240304 dac sq1 - 000252 340305 dzm sq2 - 000253 220246 lio sqt - 000254 340246 dzm sqt - 000255 sq3, - 000255 460304 isp sq1 - 000256 600261 jmp .+3 - 000257 200305 lac sq2 - 000260 sqx, - 000260 600260 jmp . - 000261 200305 lac sq2 - 000262 665001 sal 1s - 000263 240305 dac sq2 - 000264 200246 lac sqt - 000265 663003 rcl 2s - 000266 650100 sza i - 000267 600255 jmp sq3 - 000270 240246 dac sqt - 000271 200305 lac sq2 - 000272 665001 sal 1s - 000273 402770 add (1 - 000274 420246 sub sqt - 000275 640500 sma+sza-skip - 000276 600255 jmp sq3 - 000277 640200 spa - 000300 761000 cma - 000301 240246 dac sqt - 000302 440305 idx sq2 - 000303 600255 jmp sq3 - 000304 sq1, - 000304 000000 0 - 000305 sq2, - 000305 000000 0 --//// --/bbn divide subroutine --/calling sequence.. lac hi-dividend, lio lo-dividend, jda dvd, lac divisor. --/returns quot in ac, rem in io. - 000306 idv, - 000306 000000 0 /integer divide, dividend in ac. - 000307 260317 dap dv1 - 000310 200306 lac idv - 000311 677777 scr 9s - 000312 677377 scr 8s - 000313 240315 dac dvd - 000314 600317 jmp dv1 - 000315 dvd, - 000315 000000 0 - 000316 260317 dap dv1 - 000317 dv1, - 000317 100000 xct - 000320 640200 spa - 000321 761000 cma - 000322 240306 dac idv - 000323 200315 lac dvd - 000324 640400 sma - 000325 600334 jmp dv2 - 000326 761000 cma - 000327 673777 rcr 9s - 000330 673777 rcr 9s - 000331 761000 cma - 000332 673777 rcr 9s - 000333 673777 rcr 9s - 000334 dv2, - 000334 420306 sub idv - 000335 640400 sma - 000336 600376 jmp dve - 000337 560306 dis idv -+000340 560306 dis idv -+000341 560306 dis idv -+000342 560306 dis idv -+000343 560306 dis idv -+000344 560306 dis idv -+000345 560306 dis idv -+000346 560306 dis idv -+000347 560306 dis idv -+000350 560306 dis idv -+000351 560306 dis idv -+000352 560306 dis idv -+000353 560306 dis idv -+000354 560306 dis idv -+000355 560306 dis idv -+000356 560306 dis idv -+000357 560306 dis idv -+000360 560306 dis idv - 000361 400306 add idv - 000362 320306 dio idv - 000363 764000 cli - 000364 673001 rcr 1s - 000365 220315 lio dvd - 000366 642000 spi - 000367 761000 cma - 000370 240315 dac dvd - 000371 100317 xct dv1 - 000372 060315 xor dvd - 000373 673777 rcr 9s - 000374 673777 rcr 9s - 000375 440317 idx dv1 - 000376 dve, - 000376 440317 idx dv1 - 000377 200306 lac idv - 000400 642000 spi - 000401 761000 cma - 000402 220315 lio dvd - 000403 610317 jmp i dv1 --//// --/outline compiler --/ac=where to compile to, call oc --/ot=address of outline table -- define plinst A -- lac A -- dac i oc -- idx oc -- terminate -- define comtab A, B -- plinst A -- jsp ocs -- lac B -- jmp oce -- terminate - 000404 ocs, - 000404 260411 dap ocz /puts in swap - 000405 330412 dio i oc - 000406 440412 idx oc - 000407 330412 dio i oc - 000410 440412 idx oc - 000411 ocz, - 000411 600411 jmp . - 000412 oc, - 000412 000000 0 - 000413 260554 dap ocx - 000414 210554 lac i ocx - 000415 260434 dap ocg -- plinst (stf 5 -+000416 202771 lac ZZ17 -+000417 250412 dac i oc -+000420 440412 idx oc - 000421 260555 dap ocm - 000422 440554 idx ocx - 000423 ock, -- plinst (lac ~sx1 -+000423 202772 lac ZZ18 -+000424 250412 dac i oc -+000425 440412 idx oc -- plinst (lio ~sy1 -+000426 202773 lac ZZ19 -+000427 250412 dac i oc -+000430 440412 idx oc - 000431 760006 clf 6 - 000432 ocj, -- setup ~occ,6 -+000432 710006 law i ZZ210 -+000433 243112 dac ZZ110 - 000434 ocg, - 000434 220434 lio . - 000435 och, - 000435 760200 cla - 000436 663007 rcl 3s - 000437 323113 dio ~oci - 000440 222774 lio (rcl 9s -- dispatch -+000441 402775 add (a11 -+000442 260443 dap . 1 -+000443 600443 jmp . -+000444 a11, - 000444 760000 opr - 000445 600557 jmp oc1 - 000446 oco, - 000446 600602 jmp oc2 - 000447 ocq, - 000447 600610 jmp oc3 - 000450 ocp, - 000450 600616 jmp oc4 - 000451 ocr, - 000451 600624 jmp oc5 - 000452 600632 jmp oc6 --//// -- plinst (szf 5 //code -+000453 202776 lac ZZ112 -+000454 250412 dac i oc -+000455 440412 idx oc - 000456 402777 add (4 - 000457 260556 dap ocn -- plinst ocn -+000460 200556 lac ZZ113 -+000461 250412 dac i oc -+000462 440412 idx oc -- plinst (dac ~sx1 -+000463 203000 lac ZZ114 -+000464 250412 dac i oc -+000465 440412 idx oc -- plinst (dio ~sy1 -+000466 203001 lac ZZ115 -+000467 250412 dac i oc -+000470 440412 idx oc -- plinst (jmp sq6 -+000471 203002 lac ZZ116 -+000472 250412 dac i oc -+000473 440412 idx oc -- plinst (clf 5 -+000474 203003 lac ZZ117 -+000475 250412 dac i oc -+000476 440412 idx oc -- plinst (lac ~scm -+000477 203004 lac ZZ118 -+000500 250412 dac i oc -+000501 440412 idx oc -- plinst (cma -+000502 203005 lac ZZ119 -+000503 250412 dac i oc -+000504 440412 idx oc -- plinst (dac ~scm -+000505 203006 lac ZZ120 -+000506 250412 dac i oc -+000507 440412 idx oc -- plinst (lac ~ssm -+000510 203007 lac ZZ121 -+000511 250412 dac i oc -+000512 440412 idx oc -- plinst (cma -+000513 203005 lac ZZ122 -+000514 250412 dac i oc -+000515 440412 idx oc -- plinst (dac ~ssm -+000516 203010 lac ZZ123 -+000517 250412 dac i oc -+000520 440412 idx oc -- plinst (lac ~csm -+000521 203011 lac ZZ124 -+000522 250412 dac i oc -+000523 440412 idx oc -- plinst (lio ~ssd -+000524 203012 lac ZZ125 -+000525 250412 dac i oc -+000526 440412 idx oc -- plinst (dac ~ssd -+000527 203013 lac ZZ126 -+000530 250412 dac i oc -+000531 440412 idx oc -- plinst (dio ~csm -+000532 203014 lac ZZ127 -+000533 250412 dac i oc -+000534 440412 idx oc -- plinst (lac ~ssc -+000535 203015 lac ZZ128 -+000536 250412 dac i oc -+000537 440412 idx oc -- plinst (lio ~csn -+000540 203016 lac ZZ129 -+000541 250412 dac i oc -+000542 440412 idx oc -- plinst (dac ~csn -+000543 203017 lac ZZ130 -+000544 250412 dac i oc -+000545 440412 idx oc -- plinst (dio ~ssc -+000546 203020 lac ZZ131 -+000547 250412 dac i oc -+000550 440412 idx oc -- plinst ocm -+000551 200555 lac ZZ132 -+000552 250412 dac i oc -+000553 440412 idx oc - 000554 ocx, - 000554 600554 jmp . - 000555 ocm, - 000555 600555 jmp . - 000556 ocn, - 000556 600556 jmp . - 000557 oc1, -- plinst (add ~ssn -+000557 203021 lac ZZ133 -+000560 250412 dac i oc -+000561 440412 idx oc - 000562 620404 jsp ocs - 000563 203022 lac (sub ~scn - 000564 oce, - 000564 250412 dac i oc - 000565 440412 idx oc - 000566 620404 jsp ocs -- plinst (ioh -+000567 203023 lac ZZ134 -+000570 250412 dac i oc -+000571 440412 idx oc - 000572 203024 lac (dpy-4000 - 000573 ocd, - 000573 250412 dac i oc - 000574 440412 idx oc - 000575 223113 lio ~oci -- count ~occ, och -+000576 463112 isp ZZ135 -+000577 600435 jmp ZZ235 - 000600 440434 idx ocg - 000601 600432 jmp ocj - 000602 oc2, -- comtab (add ~scm, (add ~ssm -- plinst ZZ136 -+000602 203025 lac ZZ137 -+000603 250412 dac i oc -+000604 440412 idx oc -+000605 620404 jsp ocs -+000606 203026 lac ZZ236 -+000607 600564 jmp oce - 000610 oc3, -- comtab (add ~ssc, (sub ~csm -- plinst ZZ138 -+000610 203027 lac ZZ139 -+000611 250412 dac i oc -+000612 440412 idx oc -+000613 620404 jsp ocs -+000614 203030 lac ZZ238 -+000615 600564 jmp oce - 000616 oc4, -- comtab (sub ~scm, (sub ~ssm -- plinst ZZ140 -+000616 203031 lac ZZ141 -+000617 250412 dac i oc -+000620 440412 idx oc -+000621 620404 jsp ocs -+000622 203032 lac ZZ240 -+000623 600564 jmp oce - 000624 oc5, -- comtab (add ~csn, (sub ~ssd -- plinst ZZ142 -+000624 203033 lac ZZ143 -+000625 250412 dac i oc -+000626 440412 idx oc -+000627 620404 jsp ocs -+000630 203034 lac ZZ242 -+000631 600564 jmp oce - 000632 oc6, - 000632 640006 szf 6 - 000633 600642 jmp oc9 - 000634 760016 stf 6 -- plinst (dac ~ssa -+000635 203035 lac ZZ144 -+000636 250412 dac i oc -+000637 440412 idx oc - 000640 203036 lac (dio ~ssi - 000641 600573 jmp ocd - 000642 oc9, - 000642 760006 clf 6 -- plinst (lac ~ssa -+000643 203037 lac ZZ145 -+000644 250412 dac i oc -+000645 440412 idx oc - 000646 203040 lac (lio ~ssi - 000647 600573 jmp ocd --//// --/ display a star -- define starp -- add ~bx -- swap -- add ~by -- swap -- ioh -- dpy-4000 -- terminate -- /star - 000650 blp, - 000650 260675 dap blx - 000651 640060 szs 60 - 000652 600675 jmp blx -- random -+000653 200031 lac ran -+000654 671001 rar 1s -+000655 063041 xor (355760 -+000656 403042 add (355670 -+000657 240031 dac ran - 000660 671777 rar 9s - 000661 023043 and (add 340 - 000662 640200 spa - 000663 062767 xor (377777 - 000664 243116 dac ~bx - 000665 200031 lac ran - 000666 661017 ral 4s - 000667 023043 and (add 340 - 000670 640200 spa - 000671 062767 xor (377777 - 000672 243117 dac ~by - 000673 620676 jsp bpt - 000674 730000 ioh - 000675 blx, - 000675 600675 jmp . - 000676 bpt, - 000676 261117 dap bpx -- random -+000677 200031 lac ran -+000700 671001 rar 1s -+000701 063041 xor (355760 -+000702 403042 add (355670 -+000703 240031 dac ran - 000704 675777 sar 9s - 000705 675037 sar 5s - 000706 640200 spa - 000707 761000 cma - 000710 665007 sal 3s - 000711 403044 add (bds - 000712 260715 dap bjm - 000713 764206 cla cli clf 6-opr-opr - 000714 724007 dpy-4000 - 000715 bjm, - 000715 600715 jmp . - 000716 bds, -- starp -+000716 403116 add ~bx -- swap -+000717 663777 rcl 9s -+000720 663777 rcl 9s -+000721 403117 add ~by -- swap -+000722 663777 rcl 9s -+000723 663777 rcl 9s -+000724 730000 ioh -+000725 724007 dpy-4000 -- starp -+000726 403116 add ~bx -- swap -+000727 663777 rcl 9s -+000730 663777 rcl 9s -+000731 403117 add ~by -- swap -+000732 663777 rcl 9s -+000733 663777 rcl 9s -+000734 730000 ioh -+000735 724007 dpy-4000 -- starp -+000736 403116 add ~bx -- swap -+000737 663777 rcl 9s -+000740 663777 rcl 9s -+000741 403117 add ~by -- swap -+000742 663777 rcl 9s -+000743 663777 rcl 9s -+000744 730000 ioh -+000745 724007 dpy-4000 -- starp -+000746 403116 add ~bx -- swap -+000747 663777 rcl 9s -+000750 663777 rcl 9s -+000751 403117 add ~by -- swap -+000752 663777 rcl 9s -+000753 663777 rcl 9s -+000754 730000 ioh -+000755 724007 dpy-4000 -- starp -+000756 403116 add ~bx -- swap -+000757 663777 rcl 9s -+000760 663777 rcl 9s -+000761 403117 add ~by -- swap -+000762 663777 rcl 9s -+000763 663777 rcl 9s -+000764 730000 ioh -+000765 724007 dpy-4000 -- starp -+000766 403116 add ~bx -- swap -+000767 663777 rcl 9s -+000770 663777 rcl 9s -+000771 403117 add ~by -- swap -+000772 663777 rcl 9s -+000773 663777 rcl 9s -+000774 730000 ioh -+000775 724007 dpy-4000 -- starp -+000776 403116 add ~bx -- swap -+000777 663777 rcl 9s -+001000 663777 rcl 9s -+001001 403117 add ~by -- swap -+001002 663777 rcl 9s -+001003 663777 rcl 9s -+001004 730000 ioh -+001005 724007 dpy-4000 -- starp -+001006 403116 add ~bx -- swap -+001007 663777 rcl 9s -+001010 663777 rcl 9s -+001011 403117 add ~by -- swap -+001012 663777 rcl 9s -+001013 663777 rcl 9s -+001014 730000 ioh -+001015 724007 dpy-4000 -- starp -+001016 403116 add ~bx -- swap -+001017 663777 rcl 9s -+001020 663777 rcl 9s -+001021 403117 add ~by -- swap -+001022 663777 rcl 9s -+001023 663777 rcl 9s -+001024 730000 ioh -+001025 724007 dpy-4000 -- starp -+001026 403116 add ~bx -- swap -+001027 663777 rcl 9s -+001030 663777 rcl 9s -+001031 403117 add ~by -- swap -+001032 663777 rcl 9s -+001033 663777 rcl 9s -+001034 730000 ioh -+001035 724007 dpy-4000 -- starp -+001036 403116 add ~bx -- swap -+001037 663777 rcl 9s -+001040 663777 rcl 9s -+001041 403117 add ~by -- swap -+001042 663777 rcl 9s -+001043 663777 rcl 9s -+001044 730000 ioh -+001045 724007 dpy-4000 -- starp -+001046 403116 add ~bx -- swap -+001047 663777 rcl 9s -+001050 663777 rcl 9s -+001051 403117 add ~by -- swap -+001052 663777 rcl 9s -+001053 663777 rcl 9s -+001054 730000 ioh -+001055 724007 dpy-4000 -- starp -+001056 403116 add ~bx -- swap -+001057 663777 rcl 9s -+001060 663777 rcl 9s -+001061 403117 add ~by -- swap -+001062 663777 rcl 9s -+001063 663777 rcl 9s -+001064 730000 ioh -+001065 724007 dpy-4000 -- starp -+001066 403116 add ~bx -- swap -+001067 663777 rcl 9s -+001070 663777 rcl 9s -+001071 403117 add ~by -- swap -+001072 663777 rcl 9s -+001073 663777 rcl 9s -+001074 730000 ioh -+001075 724007 dpy-4000 -- starp -+001076 403116 add ~bx -- swap -+001077 663777 rcl 9s -+001100 663777 rcl 9s -+001101 403117 add ~by -- swap -+001102 663777 rcl 9s -+001103 663777 rcl 9s -+001104 730000 ioh -+001105 724007 dpy-4000 -- starp -+001106 403116 add ~bx -- swap -+001107 663777 rcl 9s -+001110 663777 rcl 9s -+001111 403117 add ~by -- swap -+001112 663777 rcl 9s -+001113 663777 rcl 9s -+001114 730000 ioh -+001115 724007 dpy-4000 - 001116 640006 szf 6 - 001117 bpx, - 001117 601117 jmp . - 001120 760016 stf 6 - 001121 761000 cma -- swap -+001122 663777 rcl 9s -+001123 663777 rcl 9s - 001124 761000 cma -- swap -+001125 663777 rcl 9s -+001126 663777 rcl 9s - 001127 600715 jmp bjm --//// --/background display . 3/13/62, prs. -- define dislis J, Q, B -- repeat 6, B=B+B -- clf 5 -- lac flo+r -- dap fpo+r --fs, -- dap fin+r -- dap fyn+r -- idx fyn+r --fin, -- lac /lac x -- sub fpr /right margin -- sma -- jmp fgr+r -- add (2000 --frr, -- spq --fou, -- jmp fuu+r --fie, -- sub (1000 -- sal 8s --fyn, -- lio /lio y -- dpy-i+B -- stf 5 --fid, -- idx fyn+r -- sad (lio Q+2 -- jmp flp+r -- sad fpo+r -- jmp fx+r -- dap fin+r -- idx fyn+r -- jmp fin+r --fgr, -- add (2000 -20000 -- jmp frr+r --fuu, -- szf 5 --fx, -- jmp flo+r+1 /return -- idx flo+r -- idx flo+r -- sas (Q+2 -- jmp fid+r -- law J -- dac flo+r -- jmp fid+r --flp, -- lac (lio J -- sad fpo+r -- jmp fx+r -- dap fin+r -- law J+1 -- dap fyn+r -- jmp fin+r --fpo, -- lio --flo, -- J -- terminate --//// -- define background -- jsp bck -- termin - 001130 bck, - 001130 261134 dap bcx - 001131 640040 szs 40 - 001132 601134 jmp bcx - 001133 461441 isp bcc - 001134 bcx, - 001134 601134 jmp . - 001135 710002 law i 2 - 001136 241441 dac bcc -- dislis 1j,1q,3 -+001137 000006 ZZ398=ZZ398+ZZ398 -+001137 000014 ZZ398=ZZ398+ZZ398 -+001137 000030 ZZ398=ZZ398+ZZ398 -+001137 000060 ZZ398=ZZ398+ZZ398 -+001137 000140 ZZ398=ZZ398+ZZ398 -+001137 000300 ZZ398=ZZ398+ZZ398 -+001137 760005 clf 5 -+001140 201214 lac flo98 -+001141 261213 dap fpo98 -+001142 fs98, -+001142 261145 dap fin98 -+001143 261156 dap fyn98 -+001144 441156 idx fyn98 -+001145 fin98, -+001145 200000 lac -+001146 421443 sub fpr -+001147 640400 sma -+001150 601171 jmp fgr98 -+001151 403045 add (2000 -+001152 frr98, -+001152 650500 spq -+001153 fou98, -+001153 601173 jmp fuu98 -+001154 fie98, -+001154 423046 sub (1000 -+001155 665377 sal 8s -+001156 fyn98, -+001156 220000 lio -+001157 720307 dpy-i+ZZ398 -+001160 760015 stf 5 -+001161 fid98, -+001161 441156 idx fyn98 -+001162 503047 sad (lio ZZ298+2 -+001163 601204 jmp flp98 -+001164 501213 sad fpo98 -+001165 601174 jmp fx98 -+001166 261145 dap fin98 -+001167 441156 idx fyn98 -+001170 601145 jmp fin98 -+001171 fgr98, -+001171 403050 add (2000 -20000 -+001172 601152 jmp frr98 -+001173 fuu98, -+001173 640005 szf 5 -+001174 fx98, -+001174 601215 jmp flo98+1 -+001175 441214 idx flo98 -+001176 441214 idx flo98 -+001177 523051 sas (ZZ298+2 -+001200 601161 jmp fid98 -+001201 706000 law ZZ198 -+001202 241214 dac flo98 -+001203 601161 jmp fid98 -+001204 flp98, -+001204 203052 lac (lio ZZ198 -+001205 501213 sad fpo98 -+001206 601174 jmp fx98 -+001207 261145 dap fin98 -+001210 706001 law ZZ198+1 -+001211 261156 dap fyn98 -+001212 601145 jmp fin98 -+001213 fpo98, -+001213 220000 lio -+001214 flo98, -+001214 006000 ZZ198 -- dislis 2j,2q,2 -+001215 000004 ZZ399=ZZ399+ZZ399 -+001215 000010 ZZ399=ZZ399+ZZ399 -+001215 000020 ZZ399=ZZ399+ZZ399 -+001215 000040 ZZ399=ZZ399+ZZ399 -+001215 000100 ZZ399=ZZ399+ZZ399 -+001215 000200 ZZ399=ZZ399+ZZ399 -+001215 760005 clf 5 -+001216 201272 lac flo99 -+001217 261271 dap fpo99 -+001220 fs99, -+001220 261223 dap fin99 -+001221 261234 dap fyn99 -+001222 441234 idx fyn99 -+001223 fin99, -+001223 200000 lac -+001224 421443 sub fpr -+001225 640400 sma -+001226 601247 jmp fgr99 -+001227 403045 add (2000 -+001230 frr99, -+001230 650500 spq -+001231 fou99, -+001231 601251 jmp fuu99 -+001232 fie99, -+001232 423046 sub (1000 -+001233 665377 sal 8s -+001234 fyn99, -+001234 220000 lio -+001235 720207 dpy-i+ZZ399 -+001236 760015 stf 5 -+001237 fid99, -+001237 441234 idx fyn99 -+001240 503053 sad (lio ZZ299+2 -+001241 601262 jmp flp99 -+001242 501271 sad fpo99 -+001243 601252 jmp fx99 -+001244 261223 dap fin99 -+001245 441234 idx fyn99 -+001246 601223 jmp fin99 -+001247 fgr99, -+001247 403050 add (2000 -20000 -+001250 601230 jmp frr99 -+001251 fuu99, -+001251 640005 szf 5 -+001252 fx99, -+001252 601273 jmp flo99+1 -+001253 441272 idx flo99 -+001254 441272 idx flo99 -+001255 523054 sas (ZZ299+2 -+001256 601237 jmp fid99 -+001257 706022 law ZZ199 -+001260 241272 dac flo99 -+001261 601237 jmp fid99 -+001262 flp99, -+001262 203055 lac (lio ZZ199 -+001263 501271 sad fpo99 -+001264 601252 jmp fx99 -+001265 261223 dap fin99 -+001266 706023 law ZZ199+1 -+001267 261234 dap fyn99 -+001270 601223 jmp fin99 -+001271 fpo99, -+001271 220000 lio -+001272 flo99, -+001272 006022 ZZ199 -- dislis 3j,3q,1 -+001273 000002 ZZ3100=ZZ3100+ZZ3100 -+001273 000004 ZZ3100=ZZ3100+ZZ3100 -+001273 000010 ZZ3100=ZZ3100+ZZ3100 -+001273 000020 ZZ3100=ZZ3100+ZZ3100 -+001273 000040 ZZ3100=ZZ3100+ZZ3100 -+001273 000100 ZZ3100=ZZ3100+ZZ3100 -+001273 760005 clf 5 -+001274 201350 lac flo100 -+001275 261347 dap fpo100 -+001276 fs100, -+001276 261301 dap fin100 -+001277 261312 dap fyn100 -+001300 441312 idx fyn100 -+001301 fin100, -+001301 200000 lac -+001302 421443 sub fpr -+001303 640400 sma -+001304 601325 jmp fgr100 -+001305 403045 add (2000 -+001306 frr100, -+001306 650500 spq -+001307 fou100, -+001307 601327 jmp fuu100 -+001310 fie100, -+001310 423046 sub (1000 -+001311 665377 sal 8s -+001312 fyn100, -+001312 220000 lio -+001313 720107 dpy-i+ZZ3100 -+001314 760015 stf 5 -+001315 fid100, -+001315 441312 idx fyn100 -+001316 503056 sad (lio ZZ2100+2 -+001317 601340 jmp flp100 -+001320 501347 sad fpo100 -+001321 601330 jmp fx100 -+001322 261301 dap fin100 -+001323 441312 idx fyn100 -+001324 601301 jmp fin100 -+001325 fgr100, -+001325 403050 add (2000 -20000 -+001326 601306 jmp frr100 -+001327 fuu100, -+001327 640005 szf 5 -+001330 fx100, -+001330 601351 jmp flo100+1 -+001331 441350 idx flo100 -+001332 441350 idx flo100 -+001333 523057 sas (ZZ2100+2 -+001334 601315 jmp fid100 -+001335 706044 law ZZ1100 -+001336 241350 dac flo100 -+001337 601315 jmp fid100 -+001340 flp100, -+001340 203060 lac (lio ZZ1100 -+001341 501347 sad fpo100 -+001342 601330 jmp fx100 -+001343 261301 dap fin100 -+001344 706045 law ZZ1100+1 -+001345 261312 dap fyn100 -+001346 601301 jmp fin100 -+001347 fpo100, -+001347 220000 lio -+001350 flo100, -+001350 006044 ZZ1100 -- dislis 4j,4q,0 -+001351 000000 ZZ3101=ZZ3101+ZZ3101 -+001351 000000 ZZ3101=ZZ3101+ZZ3101 -+001351 000000 ZZ3101=ZZ3101+ZZ3101 -+001351 000000 ZZ3101=ZZ3101+ZZ3101 -+001351 000000 ZZ3101=ZZ3101+ZZ3101 -+001351 000000 ZZ3101=ZZ3101+ZZ3101 -+001351 760005 clf 5 -+001352 201426 lac flo101 -+001353 261425 dap fpo101 -+001354 fs101, -+001354 261357 dap fin101 -+001355 261370 dap fyn101 -+001356 441370 idx fyn101 -+001357 fin101, -+001357 200000 lac -+001360 421443 sub fpr -+001361 640400 sma -+001362 601403 jmp fgr101 -+001363 403045 add (2000 -+001364 frr101, -+001364 650500 spq -+001365 fou101, -+001365 601405 jmp fuu101 -+001366 fie101, -+001366 423046 sub (1000 -+001367 665377 sal 8s -+001370 fyn101, -+001370 220000 lio -+001371 720007 dpy-i+ZZ3101 -+001372 760015 stf 5 -+001373 fid101, -+001373 441370 idx fyn101 -+001374 503061 sad (lio ZZ2101+2 -+001375 601416 jmp flp101 -+001376 501425 sad fpo101 -+001377 601406 jmp fx101 -+001400 261357 dap fin101 -+001401 441370 idx fyn101 -+001402 601357 jmp fin101 -+001403 fgr101, -+001403 403050 add (2000 -20000 -+001404 601364 jmp frr101 -+001405 fuu101, -+001405 640005 szf 5 -+001406 fx101, -+001406 601427 jmp flo101+1 -+001407 441426 idx flo101 -+001410 441426 idx flo101 -+001411 523062 sas (ZZ2101+2 -+001412 601373 jmp fid101 -+001413 706306 law ZZ1101 -+001414 241426 dac flo101 -+001415 601373 jmp fid101 -+001416 flp101, -+001416 203063 lac (lio ZZ1101 -+001417 501425 sad fpo101 -+001420 601406 jmp fx101 -+001421 261357 dap fin101 -+001422 706307 law ZZ1101+1 -+001423 261370 dap fyn101 -+001424 601357 jmp fin101 -+001425 fpo101, -+001425 220000 lio -+001426 flo101, -+001426 006306 ZZ1101 - 001427 461442 isp bkc - 001430 601134 jmp bcx - 001431 710020 law i 20 - 001432 241442 dac bkc - 001433 710001 law i 1 - 001434 401443 add fpr - 001435 640200 spa - 001436 403064 add (20000 - 001437 241443 dac fpr - 001440 601134 jmp bcx - 001441 bcc, - 001441 000000 0 - 001442 bkc, - 001442 000000 0 - 001443 fpr, - 001443 010000 10000 --//// --/spacewar 3.1 24 sep 62 pt. 2 --/main control for spaceships - 001444 000030 nob=30 /total number of colliding objects - 001444 ml0, -- load ~mtc, -4000 /delay for loop -+001444 223065 lio (ZZ2102 -+001445 323120 dio ZZ1102 -- init ml1, mtb /loc of calc routines -+001446 703365 law ZZ2103 -+001447 261703 dap ZZ1103 - 001450 403066 add (nob - 001451 261737 dap mx1 /x - 001452 003415 nx1=mtb nob - 001452 403066 add (nob - 001453 261747 dap my1 /y - 001454 003445 ny1=nx1 nob - 001454 403066 add (nob - 001455 261772 dap ma1 / count for length of explosion or torp - 001456 003475 na1=ny1 nob - 001456 403066 add (nob - 001457 262006 dap mb1 / count of instructions taken by calc routine - 001460 003525 nb1=na1 nob - 001460 403066 add (nob - 001461 243121 dac ~mdx / dx - 001462 003555 ndx=nb1 nob - 001462 403066 add (nob - 001463 243122 dac ~mdy / dy - 001464 003605 ndy=ndx nob - 001464 403066 add (nob - 001465 262327 dap mom /angular velocity - 001466 003635 nom=ndy nob - 001466 403067 add (2 - 001467 262343 dap mth / angle - 001470 003637 nth=nom 2 - 001470 403067 add (2 - 001471 243123 dac ~mfu /fuel - 001472 003641 nfu=nth 2 - 001472 403067 add (2 - 001473 243124 dac ~mtr / no torps remaining - 001474 003643 ntr=nfu 2 - 001474 403067 add (2 - 001475 261732 dap mot / outline of spaceship - 001476 003645 not=ntr 2 - 001476 403067 add (2 - 001477 262577 dap mco / old control word - 001500 003647 nco=not 2 - 001500 403067 add (2 - 001501 243125 dac ~mh1 - 001502 003651 nh1=nco 2 - 001502 403067 add (2 - 001503 243126 dac ~mh2 - 001504 003653 nh2=nh1 2 - 001504 403067 add (2 - 001505 243127 dac ~mh3 - 001506 003655 nh3=nh2 2 - 001506 403067 add (2 - 001507 243130 dac ~mh4 - 001510 003657 nh4=nh3 2 - 001510 003661 nnn=nh4 2 --//// - 001510 702310 law ss1 - 001511 063365 xor mtb - 001512 640100 sza - 001513 601534 jmp mdn - 001514 702314 law ss2 - 001515 063366 xor mtb 1 - 001516 640100 sza - 001517 601534 jmp mdn - 001520 700001 law 1 / test if both ships out of torps - 001521 403643 add ntr - 001522 640200 spa - 001523 601530 jmp md1 - 001524 700001 law 1 - 001525 403644 add ntr 1 - 001526 650200 spa i - 001527 601534 jmp mdn - 001530 md1, - 001530 100011 xct tlf / restart delay is 2x torpedo life - 001531 665001 sal 1s - 001532 243131 dac ~ntd - 001533 601703 jmp ml1 - 001534 mdn, -- count ~ntd,ml1 -+001534 463131 isp ZZ1104 -+001535 601703 jmp ZZ2104 - 001536 760011 stf 1 - 001537 760012 stf 2 - 001540 702310 law ss1 - 001541 063365 xor mtb - 001542 640100 sza - 001543 760001 clf 1 - 001544 650100 sza i - 001545 443132 idx ~1sc - 001546 702314 law ss2 - 001547 063366 xor mtb 1 - 001550 640100 sza - 001551 760002 clf 2 - 001552 650100 sza i - 001553 443133 idx ~2sc - 001554 760002 clf 2 - 001555 601564 jmp a --//// - 001556 a1, - 001556 701676 law mg2 / test word control - 001557 243134 dac ~cwg - 001560 601564 jmp a - 001561 a40, - 001561 700040 law cwr / here from start at 4 - 001562 243134 dac ~cwg - 001563 601613 jmp a6 - 001564 a, - 001564 203135 lac ~gct - 001565 640400 sma - 001566 601576 jmp a5 -- count ~gct, a5 -+001567 463135 isp ZZ1105 -+001570 601576 jmp ZZ2105 - 001571 203132 lac ~1sc - 001572 523133 sas ~2sc - 001573 601602 jmp a4 - 001574 710001 law i 1 - 001575 243135 dac ~gct - 001576 a5, - 001576 762200 lat - 001577 023070 and (40 - 001600 650100 sza i - 001601 601621 jmp a2 - 001602 a4, - 001602 203132 lac ~1sc - 001603 223133 lio ~2sc - 001604 760400 hlt - 001605 762200 lat - 001606 023070 and (40 - 001607 640100 sza - 001610 601621 jmp a2 - 001611 343132 dzm ~1sc - 001612 343133 dzm ~2sc - 001613 a6, - 001613 762200 lat - 001614 671077 rar 6s - 001615 023071 and (37 - 001616 640100 sza - 001617 761000 cma - 001620 243135 dac ~gct - 001621 a2, -- clear mtb, nnn-1 / clear out all tables -- init .+2, ZZ1106 -+001621 703365 law ZZ2107 -+001622 261623 dap ZZ1107 -+001623 340000 dzm -- index .-1, (dzm ZZ2106+1, .-1 -+001624 441623 idx ZZ1108 -+001625 523072 sas ZZ2108 -+001626 601623 jmp ZZ3108 - 001627 702310 law ss1 - 001630 243365 dac mtb - 001631 702314 law ss2 - 001632 243366 dac mtb 1 - 001633 203073 lac (200000 - 001634 243415 dac nx1 - 001635 243445 dac ny1 - 001636 761000 cma - 001637 243416 dac nx1 1 - 001640 243446 dac ny1 1 - 001641 203074 lac (144420 - 001642 243637 dac nth --//// - 001643 703661 law nnn / start of outline problem - 001644 243645 dac not - 001645 220020 lio ddd - 001646 652000 spi i - 001647 601652 jmp a3 - 001650 170412 jda oc - 001651 002735 ot1 - 001652 a3, - 001652 243646 dac not 1 - 001653 170412 jda oc - 001654 002746 ot2 - 001655 100006 xct tno - 001656 243643 dac ntr - 001657 243644 dac ntr 1 - 001660 200012 lac foo - 001661 243641 dac nfu - 001662 243642 dac nfu 1 - 001663 702000 law 2000 - 001664 243525 dac nb1 - 001665 243526 dac nb1 1 - 001666 100022 xct mhs - 001667 243653 dac nh2 - 001670 243654 dac nh2 1 - 001671 601444 jmp ml0 --/ control word get routines - 001672 mg1, - 001672 261675 dap mg3 - 001673 764000 cli - 001674 720011 iot 11 - 001675 mg3, - 001675 601675 jmp . - 001676 mg2, - 001676 261702 dap mg4 - 001677 762200 lat -- swap -+001700 663777 rcl 9s -+001701 663777 rcl 9s - 001702 mg4, - 001702 601702 jmp . --//// - 001703 ml1, - 001703 201703 lac . / 1st control word - 001704 650100 sza i / zero if not active - 001705 602011 jmp mq1 / not active -- swap -+001706 663777 rcl 9s -+001707 663777 rcl 9s - 001710 443136 idx ~moc - 001711 642000 spi - 001712 602003 jmp mq4 - 001713 700001 law 1 - 001714 401703 add ml1 - 001715 261734 dap ml2 - 001716 700001 law 1 - 001717 401737 add mx1 - 001720 261740 dap mx2 - 001721 700001 law 1 - 001722 401747 add my1 - 001723 261750 dap my2 - 001724 700001 law 1 - 001725 401772 add ma1 - 001726 261773 dap ma2 - 001727 700001 law 1 - 001730 402006 add mb1 - 001731 261766 dap mb2 - 001732 mot, - 001732 201732 lac . - 001733 262530 dap sp5 - 001734 ml2, - 001734 201734 lac . / 2nd control word - 001735 650500 spq / can it collide? - 001736 601774 jmp mq2 / no - 001737 mx1, - 001737 201737 lac . / calc if collision - 001740 mx2, - 001740 421740 sub . / delta x - 001741 640200 spa / take abs value - 001742 761000 cma - 001743 243137 dac ~mt1 - 001744 420016 sub me1 / < epsilon ? - 001745 640400 sma - 001746 601774 jmp mq2 / no - 001747 my1, - 001747 201747 lac . - 001750 my2, - 001750 421750 sub . - 001751 640200 spa - 001752 761000 cma - 001753 420016 sub me1 / < epsilon ? - 001754 640400 sma - 001755 601774 jmp mq2 / no - 001756 403137 add ~mt1 - 001757 420017 sub me2 - 001760 640400 sma - 001761 601774 jmp mq2 - 001762 203103 lac (mex 400000 / yes, explode - 001763 251703 dac i ml1 / replace calc routine with explosion - 001764 251734 dac i ml2 - 001765 212006 lac i mb1 / duration of explosion - 001766 mb2, - 001766 401766 add . - 001767 761000 cma - 001770 675377 sar 8s - 001771 402770 add (1 - 001772 ma1, - 001772 241772 dac . - 001773 ma2, - 001773 241773 dac . - 001774 mq2, - 001774 441740 idx mx2 / end of comparion loop - 001775 441750 idx my2 - 001776 441773 idx ma2 - 001777 441766 idx mb2 -- index ml2, (lac mtb nob, ml2 -+002000 441734 idx ZZ1111 -+002001 523075 sas ZZ2111 -+002002 601734 jmp ZZ3111 --//// - 002003 mq4, - 002003 211703 lac i ml1 / routine for calculating spaceship - 002004 262005 dap . 1 / or other object and displaying it - 002005 622005 jsp . - 002006 mb1, - 002006 202006 lac . / alter count of number of instructions - 002007 403120 add ~mtc - 002010 243120 dac ~mtc - 002011 mq1, - 002011 441737 idx mx1 / end of comparison and display loop - 002012 441747 idx my1 - 002013 441772 idx ma1 - 002014 442006 idx mb1 - 002015 443121 idx ~mdx - 002016 443122 idx ~mdy - 002017 442327 idx mom - 002020 442343 idx mth - 002021 443140 idx ~mas - 002022 443123 idx ~mfu - 002023 443124 idx ~mtr - 002024 441732 idx mot - 002025 442577 idx mco - 002026 443125 idx ~mh1 - 002027 443126 idx ~mh2 - 002030 443127 idx ~mh3 - 002031 443130 idx ~mh4 -- index ml1, (lac mtb nob-1, ml1 -+002032 441703 idx ZZ1112 -+002033 523076 sas ZZ2112 -+002034 601703 jmp ZZ3112 - 002035 211703 lac i ml1 / display and compute last point - 002036 650100 sza i / if active - 002037 602045 jmp mq3 - 002040 262041 dap . 1 - 002041 622041 jsp . - 002042 212006 lac i mb1 - 002043 403120 add ~mtc - 002044 243120 dac ~mtc - 002045 mq3, -- background / display stars of the heavens -+002045 621130 jsp bck - 002046 620650 jsp blp / display massive star -- count ~mtc, . / use the rest of time of main loop -+002047 463120 isp ZZ1114 -+002050 602047 jmp ZZ2114 - 002051 601444 jmp ml0 / repeat whole works --//// --/ misc calculation routines -- / explosion - 002052 mex, - 002052 262133 dap mxr - 002053 760200 cla -- diff ~mdx, mx1, (sar 3s -+002054 413121 add i ZZ1115 -+002055 253121 dac i ZZ1115 -+002056 103077 xct ZZ3115 -+002057 411737 add i ZZ2115 -+002060 251737 dac i ZZ2115 - 002061 760200 cla -- diff ~mdy, my1, (sar 3s -+002062 413122 add i ZZ1116 -+002063 253122 dac i ZZ1116 -+002064 103077 xct ZZ3116 -+002065 411747 add i ZZ2116 -+002066 251747 dac i ZZ2116 - 002067 702134 law ms2 - 002070 262117 dap msh - 002071 212006 lac i mb1 / time involved - 002072 765000 cma cli-opr - 002073 675007 sar 3s - 002074 243141 dac ~mxc - 002075 ms1, - 002075 423100 sub (140 - 002076 640400 sma - 002077 442117 idx msh - 002100 mz1, -- random -+002100 200031 lac ran -+002101 671001 rar 1s -+002102 063041 xor (355760 -+002103 403042 add (355670 -+002104 240031 dac ran - 002105 023101 and (777 - 002106 043102 ior (scl - 002107 242120 dac mi1 -- random -+002110 200031 lac ran -+002111 671001 rar 1s -+002112 063041 xor (355760 -+002113 403042 add (355670 -+002114 240031 dac ran - 002115 677777 scr 9s - 002116 676777 sir 9s - 002117 msh, - 002117 102117 xct . - 002120 mi1, - 002120 760400 hlt - 002121 411747 add i my1 -- swap -+002122 663777 rcl 9s -+002123 663777 rcl 9s - 002124 411737 add i mx1 - 002125 720307 dpy-i 300 -- count ~mxc, mz1 -+002126 463141 isp ZZ1120 -+002127 602100 jmp ZZ2120 -- count i ma1, mxr -+002130 471772 isp ZZ1121 -+002131 602133 jmp ZZ2121 - 002132 351703 dzm i ml1 - 002133 mxr, - 002133 602133 jmp . - 002134 ms2, - 002134 677001 scr 1s - 002135 677007 scr 3s --/ torpedo calc routine - 002136 tcr, - 002136 262167 dap trc -- count i ma1, tc1 -+002137 471772 isp ZZ1122 -+002140 602146 jmp ZZ2122 - 002141 203103 lac (mex 400000 - 002142 251703 dac i ml1 - 002143 710002 law i 2 - 002144 251772 dac i ma1 - 002145 602167 jmp trc - 002146 tc1, - 002146 211737 lac i mx1 - 002147 675777 sar 9s - 002150 100021 xct the -- diff ~mdy, my1, (sar 3s -+002151 413122 add i ZZ1123 -+002152 253122 dac i ZZ1123 -+002153 103077 xct ZZ3123 -+002154 411747 add i ZZ2123 -+002155 251747 dac i ZZ2123 - 002156 675777 sar 9s - 002157 100021 xct the -- diff ~mdx, mx1, (sar 3s -+002160 413121 add i ZZ1124 -+002161 253121 dac i ZZ1124 -+002162 103077 xct ZZ3124 -+002163 411737 add i ZZ2124 -+002164 251737 dac i ZZ2124 -- dispt i, i my1, 1 -+002165 000002 ZZ3125=ZZ3125+ZZ3125 -+002165 000004 ZZ3125=ZZ3125+ZZ3125 -+002165 000010 ZZ3125=ZZ3125+ZZ3125 -+002165 000020 ZZ3125=ZZ3125+ZZ3125 -+002165 000040 ZZ3125=ZZ3125+ZZ3125 -+002165 000100 ZZ3125=ZZ3125+ZZ3125 -+002165 231747 lio ZZ2125 -+002166 720107 dpy-ZZ1125+ZZ3125 - 002167 trc, - 002167 602167 jmp . --//// --/ hyperspace routines --/ this routine handles a non-colliding ship invisibly --/ in hyperspace - 002170 hp1, - 002170 262245 dap hp2 -- count i ma1, hp2 -+002171 471772 isp ZZ1126 -+002172 602245 jmp ZZ2126 - 002173 702246 law hp3 / next step - 002174 251703 dac i ml1 - 002175 700007 law 7 - 002176 252006 dac i mb1 -- random -+002177 200031 lac ran -+002200 671001 rar 1s -+002201 063041 xor (355760 -+002202 403042 add (355670 -+002203 240031 dac ran - 002204 677777 scr 9s - 002205 676777 sir 9s - 002206 100026 xct hr1 - 002207 411737 add i mx1 - 002210 251737 dac i mx1 -- swap -+002211 663777 rcl 9s -+002212 663777 rcl 9s - 002213 411747 add i my1 - 002214 251747 dac i my1 -- random -+002215 200031 lac ran -+002216 671001 rar 1s -+002217 063041 xor (355760 -+002220 403042 add (355670 -+002221 240031 dac ran - 002222 677777 scr 9s - 002223 676777 sir 9s - 002224 100027 xct hr2 - 002225 253122 dac i ~mdy - 002226 333121 dio i ~mdx -- setup ~hpt,3 -+002227 710003 law i ZZ2130 -+002230 243142 dac ZZ1130 - 002231 200031 lac ran - 002232 252343 dac i mth - 002233 hp4, - 002233 212343 lac i mth - 002234 640400 sma - 002235 422761 sub (311040 - 002236 640200 spa - 002237 402761 add (311040 - 002240 252343 dac i mth -- count ~hpt,hp4 -+002241 463142 isp ZZ1131 -+002242 602233 jmp ZZ2131 - 002243 100024 xct hd2 - 002244 251772 dac i ma1 - 002245 hp2, - 002245 602245 jmp . --/ this routine handles a ship breaking out of --/ hyperspace - 002246 hp3, - 002246 262307 dap hp5 -- count i ma1,hp6 -+002247 471772 isp ZZ1132 -+002250 602304 jmp ZZ2132 - 002251 213125 lac i ~mh1 - 002252 251703 dac i ml1 - 002253 702000 law 2000 - 002254 252006 dac i mb1 -- count i ~mh2,hp7 -+002255 473126 isp ZZ1133 -+002256 602260 jmp ZZ2133 - 002257 353126 dzm i ~mh2 --//// - 002260 hp7, - 002260 100025 xct hd3 - 002261 253127 dac i ~mh3 - 002262 213130 lac i ~mh4 - 002263 400030 add hur - 002264 253130 dac i ~mh4 -- random -+002265 200031 lac ran -+002266 671001 rar 1s -+002267 063041 xor (355760 -+002270 403042 add (355670 -+002271 240031 dac ran - 002272 043104 ior (400000 - 002273 413130 add i ~mh4 - 002274 640200 spa - 002275 602307 jmp hp5 - 002276 203103 lac (mex 400000 - 002277 251703 dac i ml1 - 002300 710010 law i 10 - 002301 251772 dac i ma1 - 002302 702000 law 2000 - 002303 252006 dac i mb1 - 002304 hp6, - 002304 211737 lac i mx1 -- dispt i, i my1, 2 -+002305 000004 ZZ3135=ZZ3135+ZZ3135 -+002305 000010 ZZ3135=ZZ3135+ZZ3135 -+002305 000020 ZZ3135=ZZ3135+ZZ3135 -+002305 000040 ZZ3135=ZZ3135+ZZ3135 -+002305 000100 ZZ3135=ZZ3135+ZZ3135 -+002305 000200 ZZ3135=ZZ3135+ZZ3135 -+002305 231747 lio ZZ2135 -+002306 720207 dpy-ZZ1135+ZZ3135 - 002307 hp5, - 002307 602307 jmp . --//// --/ spaceship calc - 002310 ss1, - 002310 262713 dap srt / first spaceship - 002311 633134 jsp i ~cwg - 002312 323143 dio ~scw - 002313 602320 jmp sr0 - 002314 ss2, - 002314 262713 dap srt - 002315 633134 jsp i ~cwg - 002316 672017 rir 4s - 002317 323143 dio ~scw - 002320 sr0, - 002320 sc1, - 002320 223143 lio ~scw /control word - 002321 760206 clf 6 cla-opr /update angle - 002322 642000 spi - 002323 400013 add maa - 002324 662001 ril 1s - 002325 642000 spi - 002326 420013 sub maa - 002327 mom, - 002327 402327 add . - 002330 252327 dac i mom - 002331 640010 szs 10 - 002332 602335 jmp sr8 - 002333 352327 dzm i mom - 002334 661177 ral 7s - 002335 sr8, - 002335 662001 ril 1s - 002336 642000 spi - 002337 760016 stf 6 - 002340 233123 lio i ~mfu - 002341 652000 spi i - 002342 760006 clf 6 - 002343 mth, - 002343 402343 add . - 002344 640400 sma - 002345 422761 sub (311040 - 002346 640200 spa - 002347 402761 add (311040 - 002350 252343 dac i mth - 002351 170074 jda sin - 002352 243144 dac ~sn - 002353 343116 dzm ~bx - 002354 343117 dzm ~by - 002355 640060 szs 60 - 002356 602430 jmp bsg - 002357 211737 lac i mx1 - 002360 675777 sar 9s - 002361 675003 sar 2s - 002362 243145 dac ~t1 - 002363 170156 jda imp - 002364 203145 lac ~t1 - 002365 243146 dac ~t2 - 002366 211747 lac i my1 --//// - 002367 675777 sar 9s - 002370 675003 sar 2s - 002371 243145 dac ~t1 - 002372 170156 jda imp - 002373 203145 lac ~t1 - 002374 403146 add ~t2 - 002375 420015 sub str - 002376 650500 sma i sza-skp - 002377 602714 jmp poh - 002400 400015 add str - 002401 243145 dac ~t1 - 002402 170246 jda sqt - 002403 675777 sar 9s - 002404 170171 jda mpy - 002405 203145 lac ~t1 - 002406 677003 scr 2s - 002407 650020 szs i 20 / switch 2 for light star - 002410 677003 scr 2s - 002411 640100 sza - 002412 602430 jmp bsg - 002413 323145 dio ~t1 - 002414 211737 lac i mx1 - 002415 761000 cma - 002416 170306 jda idv - 002417 203145 lac ~t1 - 002420 760000 opr - 002421 243116 dac ~bx - 002422 211747 lac i my1 - 002423 761000 cma - 002424 170306 jda idv - 002425 203145 lac ~t1 - 002426 760000 opr - 002427 243117 dac ~by - 002430 bsg, - 002430 760200 cla - 002431 513123 sad i ~mfu - 002432 760006 clf 6 - 002433 212343 lac i mth - 002434 170066 jda cos - 002435 243147 dac ~cs - 002436 675777 sar 9s - 002437 100014 xct sac - 002440 650006 szf i 6 - 002441 760200 cla - 002442 403117 add ~by -- diff ~mdy, my1, (sar 3s -+002443 413122 add i ZZ1136 -+002444 253122 dac i ZZ1136 -+002445 103077 xct ZZ3136 -+002446 411747 add i ZZ2136 -+002447 251747 dac i ZZ2136 - 002450 203144 lac ~sn - 002451 675777 sar 9s - 002452 100014 xct sac - 002453 761000 cma - 002454 650006 szf i 6 - 002455 760200 cla - 002456 403116 add ~bx -- diff ~mdx, mx1, (sar 3s -+002457 413121 add i ZZ1137 -+002460 253121 dac i ZZ1137 -+002461 103077 xct ZZ3137 -+002462 411737 add i ZZ2137 -+002463 251737 dac i ZZ2137 - 002464 sp1, -- scale ~sn, 5s, ~ssn -+002464 203144 lac ZZ1138 -+002465 675037 sar ZZ2138 -+002466 243150 dac ZZ3138 - 002467 sp2, -- scale ~cs, 5s, ~scn -+002467 203147 lac ZZ1139 -+002470 675037 sar ZZ2139 -+002471 243114 dac ZZ3139 - 002472 211737 lac i mx1 --//// - 002473 423150 sub ~ssn - 002474 243151 dac ~sx1 - 002475 423150 sub ~ssn - 002476 243152 dac ~stx - 002477 211747 lac i my1 - 002500 403114 add ~scn - 002501 243153 dac ~sy1 - 002502 403114 add ~scn - 002503 243154 dac ~sty --/ Modified for Smaller Laptop screens - BDS --// scale ~sn, 9s, ~ssn --// scale ~cs, 9s, ~scn -- scale ~sn, 8s, ~ssn -+002504 203144 lac ZZ1140 -+002505 675377 sar ZZ2140 -+002506 243150 dac ZZ3140 -- scale ~cs, 8s, ~scn -+002507 203147 lac ZZ1141 -+002510 675377 sar ZZ2141 -+002511 243114 dac ZZ3141 - 002512 203150 lac ~ssn - 002513 243155 dac ~ssm - 002514 403114 add ~scn - 002515 243156 dac ~ssc - 002516 243157 dac ~ssd - 002517 203150 lac ~ssn - 002520 423114 sub ~scn - 002521 243160 dac ~csn - 002522 761000 cma - 002523 243161 dac ~csm - 002524 203114 lac ~scn - 002525 243162 dac ~scm - 002526 764200 cla cli-opr - 002527 724007 dpy-4000 - 002530 sp5, - 002530 602530 jmp . - 002531 sq6, - 002531 730000 ioh -- ranct sar 9s, sar 4s, ~src -- random -+002532 200031 lac ran -+002533 671001 rar 1s -+002534 063041 xor (355760 -+002535 403042 add (355670 -+002536 240031 dac ran -+002537 675777 ZZ1142 -+002540 675017 ZZ2142 -+002541 640400 sma -+002542 761000 cma -+002543 243163 dac ZZ3142 - 002544 223143 lio ~scw - 002545 662003 ril 2s - 002546 652000 spi i / not blasting - 002547 602574 jmp sq9 / no tail - 002550 sq7, -- scale ~sn, 8s, ~ssn -+002550 203144 lac ZZ1144 -+002551 675377 sar ZZ2144 -+002552 243150 dac ZZ3144 -- scale ~cs, 8s, ~scn -+002553 203147 lac ZZ1145 -+002554 675377 sar ZZ2145 -+002555 243114 dac ZZ3145 -- count i ~mfu, st2 -+002556 473123 isp ZZ1146 -+002557 602562 jmp ZZ2146 - 002560 353123 dzm i ~mfu - 002561 602574 jmp sq9 - 002562 st2, -- yincr ~sx1, ~sy1, sub -+002562 203153 lac ZZ2147 -+002563 423114 ZZ3147 ~scn -+002564 243153 dac ZZ2147 -+002565 203151 lac ZZ1147 -+002566 403150 -ZZ3147+add+sub ~ssn -+002567 243151 dac ZZ1147 -- dispt i, ~sy1 -+002570 000000 ZZ3148=ZZ3148+ZZ3148 -+002570 000000 ZZ3148=ZZ3148+ZZ3148 -+002570 000000 ZZ3148=ZZ3148+ZZ3148 -+002570 000000 ZZ3148=ZZ3148+ZZ3148 -+002570 000000 ZZ3148=ZZ3148+ZZ3148 -+002570 000000 ZZ3148=ZZ3148+ZZ3148 -+002570 223153 lio ZZ2148 -+002571 720007 dpy-ZZ1148+ZZ3148 -- count ~src,sq7 -+002572 463163 isp ZZ1149 -+002573 602550 jmp ZZ2149 - 002574 sq9, -- count i ma1, sr5 / check if torp tube reloaded -+002574 471772 isp ZZ1150 -+002575 602667 jmp ZZ2150 - 002576 351772 dzm i ma1 / prevent count around - 002577 mco, - 002577 202577 lac . / previous control word - 002600 761000 cma - 002601 650030 szs i 30 - 002602 761200 clc - 002603 023143 and ~scw / present control word - 002604 661007 ral 3s / torpedo bit to bit 0 - 002605 640400 sma - 002606 602667 jmp sr5 / no launch -- count i ~mtr, st1 / check if torpedos exhausted -+002607 473124 isp ZZ1151 -+002610 602613 jmp ZZ2151 - 002611 353124 dzm i ~mtr / prevent count around - 002612 602667 jmp sr5 - 002613 st1, -- init sr1, mtb / search for unused object -+002613 703365 law ZZ2152 -+002614 262615 dap ZZ1152 - 002615 sr1, - 002615 202615 lac . - 002616 650100 sza i / 0 if unused - 002617 602625 jmp sr2 -- index sr1, (lac mtb+nob, sr1 -+002620 442615 idx ZZ1153 -+002621 523105 sas ZZ2153 -+002622 602615 jmp ZZ3153 - 002623 760400 hlt / no space for new objects - 002624 602623 jmp .-1 --//// - 002625 sr2, - 002625 203106 lac (tcr - 002626 252615 dac i sr1 - 002627 700030 law nob - 002630 402615 add sr1 - 002631 262633 dap ss3 - 002632 223152 lio ~stx - 002633 ss3, - 002633 322633 dio . - 002634 403066 add (nob - 002635 262637 dap ss4 - 002636 223154 lio ~sty - 002637 ss4, - 002637 322637 dio . - 002640 403066 add (nob - 002641 262664 dap sr6 - 002642 403066 add (nob - 002643 262666 dap sr7 - 002644 403066 add (nob - 002645 262654 dap sr3 - 002646 403066 add (nob - 002647 262660 dap sr4 - 002650 203144 lac ~sn - 002651 100007 xct tvl - 002652 761000 cma - 002653 413121 add i ~mdx - 002654 sr3, - 002654 242654 dac . - 002655 203147 lac ~cs - 002656 100007 xct tvl - 002657 413122 add i ~mdy - 002660 sr4, - 002660 242660 dac . - 002661 100010 xct rlt - 002662 251772 dac i ma1 / permit torp tubes to cool - 002663 trp, - 002663 100011 xct tlf / life of torpedo - 002664 sr6, - 002664 242664 dac . - 002665 700020 law 20 - 002666 sr7, - 002666 262666 dap . / length of torp calc - 002667 sr5, -- count i ~mh3, st3 / hyperbutton active? -+002667 473127 isp ZZ1154 -+002670 602713 jmp ZZ2154 - 002671 353127 dzm i ~mh3 - 002672 213126 lac i ~mh2 - 002673 650100 sza i - 002674 602713 jmp st3 - 002675 203143 lac ~scw - 002676 761000 cma - 002677 052577 ior i mco - 002700 023107 and (600000 - 002701 640100 sza - 002702 602713 jmp st3 - 002703 211703 lac i ml1 - 002704 253125 dac i ~mh1 - 002705 203110 lac (hp1 400000 - 002706 251703 dac i ml1 - 002707 100023 xct hd1 - 002710 251772 dac i ma1 - 002711 700003 law 3 - 002712 252006 dac i mb1 - 002713 st3, - 002713 srt, - 002713 602713 jmp . --//// --/ here to handle spaceships into star --/ spaceship in star - 002714 poh, - 002714 353121 dzm i ~mdx - 002715 353122 dzm i ~mdy - 002716 640050 szs 50 - 002717 602730 jmp po1 - 002720 202767 lac (377777 - 002721 251737 dac i mx1 - 002722 251747 dac i my1 - 002723 212006 lac i mb1 - 002724 243150 dac ~ssn -- count ~ssn, . -+002725 463150 isp ZZ1155 -+002726 602725 jmp ZZ2155 - 002727 602713 jmp srt - 002730 po1, - 002730 203103 lac (mex 400000 / now go bang - 002731 251703 dac i ml1 - 002732 710010 law i 10 - 002733 251772 dac i ma1 - 002734 602713 jmp srt --//// --/ outlines of spaceships - 002735 ot1, - 002735 111131 111131 - 002736 111111 111111 - 002737 111111 111111 - 002740 111163 111163 - 002741 311111 311111 - 002742 146111 146111 - 002743 111114 111114 - 002744 700000 700000 - 002745 000005 . 5/ - 002746 ot2, - 002746 013113 013113 - 002747 113111 113111 - 002750 116313 116313 - 002751 131111 131111 - 002752 161151 161151 - 002753 111633 111633 - 002754 365114 365114 - 002755 700000 700000 - 002756 000005 . 5/ - 002757 203164 lac ~ssa / To fix assembler bug - ~ssa only referenced in lit - 002760 constants -+002760 062210 62210 -+002761 311040 311040 -+002762 242763 242763 -+002763 756103 756103 -+002764 121312 121312 -+002765 532511 532511 -+002766 144417 144417 -+002767 377777 377777 -+002770 000001 1 -+002771 760015 stf 5 -+002772 203151 lac ~sx1 -+002773 223153 lio ~sy1 -+002774 663777 rcl 9s -+002775 000444 a11 -+002776 640005 szf 5 -+002777 000004 4 -+003000 243151 dac ~sx1 -+003001 323153 dio ~sy1 -+003002 602531 jmp sq6 -+003003 760005 clf 5 -+003004 203162 lac ~scm -+003005 761000 cma -+003006 243162 dac ~scm -+003007 203155 lac ~ssm -+003010 243155 dac ~ssm -+003011 203161 lac ~csm -+003012 223157 lio ~ssd -+003013 243157 dac ~ssd -+003014 323161 dio ~csm -+003015 203156 lac ~ssc -+003016 223160 lio ~csn -+003017 243160 dac ~csn -+003020 323156 dio ~ssc -+003021 403150 add ~ssn -+003022 423114 sub ~scn -+003023 730000 ioh -+003024 724007 dpy-4000 -+003025 403162 add ~scm -+003026 403155 add ~ssm -+003027 403156 add ~ssc -+003030 423161 sub ~csm -+003031 423162 sub ~scm -+003032 423155 sub ~ssm -+003033 403160 add ~csn -+003034 423157 sub ~ssd -+003035 243164 dac ~ssa -+003036 323115 dio ~ssi -+003037 203164 lac ~ssa -+003040 223115 lio ~ssi -+003041 355760 355760 -+003042 355670 355670 -+003043 400340 add 340 -+003044 000716 bds -+003045 002000 2000 -+003046 001000 1000 -+003047 226022 lio ZZ298+2 -+003050 761777 2000 -20000 -+003051 006022 ZZ298+2 -+003052 226000 lio ZZ198 -+003053 226044 lio ZZ299+2 -+003054 006044 ZZ299+2 -+003055 226022 lio ZZ199 -+003056 226306 lio ZZ2100+2 -+003057 006306 ZZ2100+2 -+003060 226044 lio ZZ1100 -+003061 227652 lio ZZ2101+2 -+003062 007652 ZZ2101+2 -+003063 226306 lio ZZ1101 -+003064 020000 20000 -+003065 773777 ZZ2102 -+003066 000030 nob -+003067 000002 2 -+003070 000040 40 -+003071 000037 37 -+003072 343661 dzm ZZ2106+1 -+003073 200000 200000 -+003074 144420 144420 -+003075 203415 lac mtb nob -+003076 203414 lac mtb nob-1 -+003077 675007 sar 3s -+003100 000140 140 -+003101 000777 777 -+003102 667000 scl -+003103 402052 mex 400000 -+003104 400000 400000 -+003105 203415 lac mtb+nob -+003106 002136 tcr -+003107 600000 600000 -+003110 402170 hp1 400000 - 003111 000000 0 - 003112 variables -+003112 000000 occ -+003113 000000 oci -+003114 000000 scn -+003115 000000 ssi -+003116 000000 bx -+003117 000000 by -+003120 000000 mtc -+003121 000000 mdx -+003122 000000 mdy -+003123 000000 mfu -+003124 000000 mtr -+003125 000000 mh1 -+003126 000000 mh2 -+003127 000000 mh3 -+003130 000000 mh4 -+003131 000000 ntd -+003132 000000 1sc -+003133 000000 2sc -+003134 000000 cwg -+003135 000000 gct -+003136 000000 moc -+003137 000000 mt1 -+003140 000000 mas -+003141 000000 mxc -+003142 000000 hpt -+003143 000000 scw -+003144 000000 sn -+003145 000000 t1 -+003146 000000 t2 -+003147 000000 cs -+003150 000000 ssn -+003151 000000 sx1 -+003152 000000 stx -+003153 000000 sy1 -+003154 000000 sty -+003155 000000 ssm -+003156 000000 ssc -+003157 000000 ssd -+003160 000000 csn -+003161 000000 csm -+003162 000000 scm -+003163 000000 src -+003164 000000 ssa - 003165 p, - 003365 . 200/ / space for patches - 003365 mtb, -- / table of objects and their properties - 006000 6000/ --/stars 1 3/13/62 prs. - 006000 decimal -- define mark X, Y -- repeat 10, Y=Y+Y -- 0 8192 -X -- 0 Y -- terminate - 006000 1j, -- mark 1537, 371 /87 taur, aldebaran -+006000 001346 ZZ2156=ZZ2156+ZZ2156 -+006000 002714 ZZ2156=ZZ2156+ZZ2156 -+006000 005630 ZZ2156=ZZ2156+ZZ2156 -+006000 013460 ZZ2156=ZZ2156+ZZ2156 -+006000 027140 ZZ2156=ZZ2156+ZZ2156 -+006000 056300 ZZ2156=ZZ2156+ZZ2156 -+006000 134600 ZZ2156=ZZ2156+ZZ2156 -+006000 271400 ZZ2156=ZZ2156+ZZ2156 -+006000 014777 0 8192 -ZZ1156 -+006001 271400 0 ZZ2156 -- mark 1762, -189 /19 orio, rigel -+006002 777204 ZZ2157=ZZ2157+ZZ2157 -+006002 776410 ZZ2157=ZZ2157+ZZ2157 -+006002 775020 ZZ2157=ZZ2157+ZZ2157 -+006002 772040 ZZ2157=ZZ2157+ZZ2157 -+006002 764100 ZZ2157=ZZ2157+ZZ2157 -+006002 750200 ZZ2157=ZZ2157+ZZ2157 -+006002 720400 ZZ2157=ZZ2157+ZZ2157 -+006002 641000 ZZ2157=ZZ2157+ZZ2157 -+006002 014436 0 8192 -ZZ1157 -+006003 641000 0 ZZ2157 -- mark 1990, 168 /58 orio, betelgeuze -+006004 000520 ZZ2158=ZZ2158+ZZ2158 -+006004 001240 ZZ2158=ZZ2158+ZZ2158 -+006004 002500 ZZ2158=ZZ2158+ZZ2158 -+006004 005200 ZZ2158=ZZ2158+ZZ2158 -+006004 012400 ZZ2158=ZZ2158+ZZ2158 -+006004 025000 ZZ2158=ZZ2158+ZZ2158 -+006004 052000 ZZ2158=ZZ2158+ZZ2158 -+006004 124000 ZZ2158=ZZ2158+ZZ2158 -+006004 014072 0 8192 -ZZ1158 -+006005 124000 0 ZZ2158 -- mark 2280, -377 /9 cmaj, sirius -+006006 776414 ZZ2159=ZZ2159+ZZ2159 -+006006 775030 ZZ2159=ZZ2159+ZZ2159 -+006006 772060 ZZ2159=ZZ2159+ZZ2159 -+006006 764140 ZZ2159=ZZ2159+ZZ2159 -+006006 750300 ZZ2159=ZZ2159+ZZ2159 -+006006 720600 ZZ2159=ZZ2159+ZZ2159 -+006006 641400 ZZ2159=ZZ2159+ZZ2159 -+006006 503000 ZZ2159=ZZ2159+ZZ2159 -+006006 013430 0 8192 -ZZ1159 -+006007 503000 0 ZZ2159 -- mark 2583, 125 /25 cmin, procyon -+006010 000372 ZZ2160=ZZ2160+ZZ2160 -+006010 000764 ZZ2160=ZZ2160+ZZ2160 -+006010 001750 ZZ2160=ZZ2160+ZZ2160 -+006010 003720 ZZ2160=ZZ2160+ZZ2160 -+006010 007640 ZZ2160=ZZ2160+ZZ2160 -+006010 017500 ZZ2160=ZZ2160+ZZ2160 -+006010 037200 ZZ2160=ZZ2160+ZZ2160 -+006010 076400 ZZ2160=ZZ2160+ZZ2160 -+006010 012751 0 8192 -ZZ1160 -+006011 076400 0 ZZ2160 -- mark 3431, 283 /32 leon, regulus -+006012 001066 ZZ2161=ZZ2161+ZZ2161 -+006012 002154 ZZ2161=ZZ2161+ZZ2161 -+006012 004330 ZZ2161=ZZ2161+ZZ2161 -+006012 010660 ZZ2161=ZZ2161+ZZ2161 -+006012 021540 ZZ2161=ZZ2161+ZZ2161 -+006012 043300 ZZ2161=ZZ2161+ZZ2161 -+006012 106600 ZZ2161=ZZ2161+ZZ2161 -+006012 215400 ZZ2161=ZZ2161+ZZ2161 -+006012 011231 0 8192 -ZZ1161 -+006013 215400 0 ZZ2161 -- mark 4551, -242 /67 virg, spica -+006014 777032 ZZ2162=ZZ2162+ZZ2162 -+006014 776064 ZZ2162=ZZ2162+ZZ2162 -+006014 774150 ZZ2162=ZZ2162+ZZ2162 -+006014 770320 ZZ2162=ZZ2162+ZZ2162 -+006014 760640 ZZ2162=ZZ2162+ZZ2162 -+006014 741500 ZZ2162=ZZ2162+ZZ2162 -+006014 703200 ZZ2162=ZZ2162+ZZ2162 -+006014 606400 ZZ2162=ZZ2162+ZZ2162 -+006014 007071 0 8192 -ZZ1162 -+006015 606400 0 ZZ2162 -- mark 4842, 448 /16 boot, arcturus -+006016 001600 ZZ2163=ZZ2163+ZZ2163 -+006016 003400 ZZ2163=ZZ2163+ZZ2163 -+006016 007000 ZZ2163=ZZ2163+ZZ2163 -+006016 016000 ZZ2163=ZZ2163+ZZ2163 -+006016 034000 ZZ2163=ZZ2163+ZZ2163 -+006016 070000 ZZ2163=ZZ2163+ZZ2163 -+006016 160000 ZZ2163=ZZ2163+ZZ2163 -+006016 340000 ZZ2163=ZZ2163+ZZ2163 -+006016 006426 0 8192 -ZZ1163 -+006017 340000 0 ZZ2163 - 006020 1q, -- mark 6747, 196 /53 aqil, altair -+006020 000610 ZZ2164=ZZ2164+ZZ2164 -+006020 001420 ZZ2164=ZZ2164+ZZ2164 -+006020 003040 ZZ2164=ZZ2164+ZZ2164 -+006020 006100 ZZ2164=ZZ2164+ZZ2164 -+006020 014200 ZZ2164=ZZ2164+ZZ2164 -+006020 030400 ZZ2164=ZZ2164+ZZ2164 -+006020 061000 ZZ2164=ZZ2164+ZZ2164 -+006020 142000 ZZ2164=ZZ2164+ZZ2164 -+006020 002645 0 8192 -ZZ1164 -+006021 142000 0 ZZ2164 - 006022 2j, -- mark 1819, 143 /24 orio, bellatrix -+006022 000436 ZZ2165=ZZ2165+ZZ2165 -+006022 001074 ZZ2165=ZZ2165+ZZ2165 -+006022 002170 ZZ2165=ZZ2165+ZZ2165 -+006022 004360 ZZ2165=ZZ2165+ZZ2165 -+006022 010740 ZZ2165=ZZ2165+ZZ2165 -+006022 021700 ZZ2165=ZZ2165+ZZ2165 -+006022 043600 ZZ2165=ZZ2165+ZZ2165 -+006022 107400 ZZ2165=ZZ2165+ZZ2165 -+006022 014345 0 8192 -ZZ1165 -+006023 107400 0 ZZ2165 -- mark 1884, -29 /46 orio -+006024 777704 ZZ2166=ZZ2166+ZZ2166 -+006024 777610 ZZ2166=ZZ2166+ZZ2166 -+006024 777420 ZZ2166=ZZ2166+ZZ2166 -+006024 777040 ZZ2166=ZZ2166+ZZ2166 -+006024 776100 ZZ2166=ZZ2166+ZZ2166 -+006024 774200 ZZ2166=ZZ2166+ZZ2166 -+006024 770400 ZZ2166=ZZ2166+ZZ2166 -+006024 761000 ZZ2166=ZZ2166+ZZ2166 -+006024 014244 0 8192 -ZZ1166 -+006025 761000 0 ZZ2166 -- mark 1910, -46 /50 orio -+006026 777642 ZZ2167=ZZ2167+ZZ2167 -+006026 777504 ZZ2167=ZZ2167+ZZ2167 -+006026 777210 ZZ2167=ZZ2167+ZZ2167 -+006026 776420 ZZ2167=ZZ2167+ZZ2167 -+006026 775040 ZZ2167=ZZ2167+ZZ2167 -+006026 772100 ZZ2167=ZZ2167+ZZ2167 -+006026 764200 ZZ2167=ZZ2167+ZZ2167 -+006026 750400 ZZ2167=ZZ2167+ZZ2167 -+006026 014212 0 8192 -ZZ1167 -+006027 750400 0 ZZ2167 -- mark 1951, -221 /53 orio -+006030 777104 ZZ2168=ZZ2168+ZZ2168 -+006030 776210 ZZ2168=ZZ2168+ZZ2168 -+006030 774420 ZZ2168=ZZ2168+ZZ2168 -+006030 771040 ZZ2168=ZZ2168+ZZ2168 -+006030 762100 ZZ2168=ZZ2168+ZZ2168 -+006030 744200 ZZ2168=ZZ2168+ZZ2168 -+006030 710400 ZZ2168=ZZ2168+ZZ2168 -+006030 621000 ZZ2168=ZZ2168+ZZ2168 -+006030 014141 0 8192 -ZZ1168 -+006031 621000 0 ZZ2168 -- mark 2152, -407 / 2 cmaj -+006032 776320 ZZ2169=ZZ2169+ZZ2169 -+006032 774640 ZZ2169=ZZ2169+ZZ2169 -+006032 771500 ZZ2169=ZZ2169+ZZ2169 -+006032 763200 ZZ2169=ZZ2169+ZZ2169 -+006032 746400 ZZ2169=ZZ2169+ZZ2169 -+006032 715000 ZZ2169=ZZ2169+ZZ2169 -+006032 632000 ZZ2169=ZZ2169+ZZ2169 -+006032 464000 ZZ2169=ZZ2169+ZZ2169 -+006032 013630 0 8192 -ZZ1169 -+006033 464000 0 ZZ2169 -- mark 2230, 375 /24 gemi -+006034 001356 ZZ2170=ZZ2170+ZZ2170 -+006034 002734 ZZ2170=ZZ2170+ZZ2170 -+006034 005670 ZZ2170=ZZ2170+ZZ2170 -+006034 013560 ZZ2170=ZZ2170+ZZ2170 -+006034 027340 ZZ2170=ZZ2170+ZZ2170 -+006034 056700 ZZ2170=ZZ2170+ZZ2170 -+006034 135600 ZZ2170=ZZ2170+ZZ2170 -+006034 273400 ZZ2170=ZZ2170+ZZ2170 -+006034 013512 0 8192 -ZZ1170 -+006035 273400 0 ZZ2170 -- mark 3201, -187 /30 hyda, alphard -+006036 777210 ZZ2171=ZZ2171+ZZ2171 -+006036 776420 ZZ2171=ZZ2171+ZZ2171 -+006036 775040 ZZ2171=ZZ2171+ZZ2171 -+006036 772100 ZZ2171=ZZ2171+ZZ2171 -+006036 764200 ZZ2171=ZZ2171+ZZ2171 -+006036 750400 ZZ2171=ZZ2171+ZZ2171 -+006036 721000 ZZ2171=ZZ2171+ZZ2171 -+006036 642000 ZZ2171=ZZ2171+ZZ2171 -+006036 011577 0 8192 -ZZ1171 -+006037 642000 0 ZZ2171 -- mark 4005, 344 /94 leon, denebola -+006040 001260 ZZ2172=ZZ2172+ZZ2172 -+006040 002540 ZZ2172=ZZ2172+ZZ2172 -+006040 005300 ZZ2172=ZZ2172+ZZ2172 -+006040 012600 ZZ2172=ZZ2172+ZZ2172 -+006040 025400 ZZ2172=ZZ2172+ZZ2172 -+006040 053000 ZZ2172=ZZ2172+ZZ2172 -+006040 126000 ZZ2172=ZZ2172+ZZ2172 -+006040 254000 ZZ2172=ZZ2172+ZZ2172 -+006040 010133 0 8192 -ZZ1172 -+006041 254000 0 ZZ2172 - 006042 2q, -- mark 5975, 288 /55 ophi -+006042 001100 ZZ2173=ZZ2173+ZZ2173 -+006042 002200 ZZ2173=ZZ2173+ZZ2173 -+006042 004400 ZZ2173=ZZ2173+ZZ2173 -+006042 011000 ZZ2173=ZZ2173+ZZ2173 -+006042 022000 ZZ2173=ZZ2173+ZZ2173 -+006042 044000 ZZ2173=ZZ2173+ZZ2173 -+006042 110000 ZZ2173=ZZ2173+ZZ2173 -+006042 220000 ZZ2173=ZZ2173+ZZ2173 -+006042 004251 0 8192 -ZZ1173 -+006043 220000 0 ZZ2173 - 006044 3j, -- mark 46, 333 /88 pegs, algenib -+006044 001232 ZZ2174=ZZ2174+ZZ2174 -+006044 002464 ZZ2174=ZZ2174+ZZ2174 -+006044 005150 ZZ2174=ZZ2174+ZZ2174 -+006044 012320 ZZ2174=ZZ2174+ZZ2174 -+006044 024640 ZZ2174=ZZ2174+ZZ2174 -+006044 051500 ZZ2174=ZZ2174+ZZ2174 -+006044 123200 ZZ2174=ZZ2174+ZZ2174 -+006044 246400 ZZ2174=ZZ2174+ZZ2174 -+006044 017722 0 8192 -ZZ1174 -+006045 246400 0 ZZ2174 -- mark 362, -244 /31 ceti -+006046 777026 ZZ2175=ZZ2175+ZZ2175 -+006046 776054 ZZ2175=ZZ2175+ZZ2175 -+006046 774130 ZZ2175=ZZ2175+ZZ2175 -+006046 770260 ZZ2175=ZZ2175+ZZ2175 -+006046 760540 ZZ2175=ZZ2175+ZZ2175 -+006046 741300 ZZ2175=ZZ2175+ZZ2175 -+006046 702600 ZZ2175=ZZ2175+ZZ2175 -+006046 605400 ZZ2175=ZZ2175+ZZ2175 -+006046 017226 0 8192 -ZZ1175 -+006047 605400 0 ZZ2175 -- mark 490, 338 /99 pisc -+006050 001244 ZZ2176=ZZ2176+ZZ2176 -+006050 002510 ZZ2176=ZZ2176+ZZ2176 -+006050 005220 ZZ2176=ZZ2176+ZZ2176 -+006050 012440 ZZ2176=ZZ2176+ZZ2176 -+006050 025100 ZZ2176=ZZ2176+ZZ2176 -+006050 052200 ZZ2176=ZZ2176+ZZ2176 -+006050 124400 ZZ2176=ZZ2176+ZZ2176 -+006050 251000 ZZ2176=ZZ2176+ZZ2176 -+006050 017026 0 8192 -ZZ1176 -+006051 251000 0 ZZ2176 -- mark 566, -375 /52 ceti -+006052 776420 ZZ2177=ZZ2177+ZZ2177 -+006052 775040 ZZ2177=ZZ2177+ZZ2177 -+006052 772100 ZZ2177=ZZ2177+ZZ2177 -+006052 764200 ZZ2177=ZZ2177+ZZ2177 -+006052 750400 ZZ2177=ZZ2177+ZZ2177 -+006052 721000 ZZ2177=ZZ2177+ZZ2177 -+006052 642000 ZZ2177=ZZ2177+ZZ2177 -+006052 504000 ZZ2177=ZZ2177+ZZ2177 -+006052 016712 0 8192 -ZZ1177 -+006053 504000 0 ZZ2177 -- mark 621, 462 / 6 arie -+006054 001634 ZZ2178=ZZ2178+ZZ2178 -+006054 003470 ZZ2178=ZZ2178+ZZ2178 -+006054 007160 ZZ2178=ZZ2178+ZZ2178 -+006054 016340 ZZ2178=ZZ2178+ZZ2178 -+006054 034700 ZZ2178=ZZ2178+ZZ2178 -+006054 071600 ZZ2178=ZZ2178+ZZ2178 -+006054 163400 ZZ2178=ZZ2178+ZZ2178 -+006054 347000 ZZ2178=ZZ2178+ZZ2178 -+006054 016623 0 8192 -ZZ1178 -+006055 347000 0 ZZ2178 -- mark 764, -78 /68 ceti, mira -+006056 777542 ZZ2179=ZZ2179+ZZ2179 -+006056 777304 ZZ2179=ZZ2179+ZZ2179 -+006056 776610 ZZ2179=ZZ2179+ZZ2179 -+006056 775420 ZZ2179=ZZ2179+ZZ2179 -+006056 773040 ZZ2179=ZZ2179+ZZ2179 -+006056 766100 ZZ2179=ZZ2179+ZZ2179 -+006056 754200 ZZ2179=ZZ2179+ZZ2179 -+006056 730400 ZZ2179=ZZ2179+ZZ2179 -+006056 016404 0 8192 -ZZ1179 -+006057 730400 0 ZZ2179 -- mark 900, 64 /86 ceti -+006060 000200 ZZ2180=ZZ2180+ZZ2180 -+006060 000400 ZZ2180=ZZ2180+ZZ2180 -+006060 001000 ZZ2180=ZZ2180+ZZ2180 -+006060 002000 ZZ2180=ZZ2180+ZZ2180 -+006060 004000 ZZ2180=ZZ2180+ZZ2180 -+006060 010000 ZZ2180=ZZ2180+ZZ2180 -+006060 020000 ZZ2180=ZZ2180+ZZ2180 -+006060 040000 ZZ2180=ZZ2180+ZZ2180 -+006060 016174 0 8192 -ZZ1180 -+006061 040000 0 ZZ2180 -- mark 1007, 84 /92 ceti -+006062 000250 ZZ2181=ZZ2181+ZZ2181 -+006062 000520 ZZ2181=ZZ2181+ZZ2181 -+006062 001240 ZZ2181=ZZ2181+ZZ2181 -+006062 002500 ZZ2181=ZZ2181+ZZ2181 -+006062 005200 ZZ2181=ZZ2181+ZZ2181 -+006062 012400 ZZ2181=ZZ2181+ZZ2181 -+006062 025000 ZZ2181=ZZ2181+ZZ2181 -+006062 052000 ZZ2181=ZZ2181+ZZ2181 -+006062 016021 0 8192 -ZZ1181 -+006063 052000 0 ZZ2181 -- mark 1243, -230 /23 erid -+006064 777062 ZZ2182=ZZ2182+ZZ2182 -+006064 776144 ZZ2182=ZZ2182+ZZ2182 -+006064 774310 ZZ2182=ZZ2182+ZZ2182 -+006064 770620 ZZ2182=ZZ2182+ZZ2182 -+006064 761440 ZZ2182=ZZ2182+ZZ2182 -+006064 743100 ZZ2182=ZZ2182+ZZ2182 -+006064 706200 ZZ2182=ZZ2182+ZZ2182 -+006064 614400 ZZ2182=ZZ2182+ZZ2182 -+006064 015445 0 8192 -ZZ1182 -+006065 614400 0 ZZ2182 -- mark 1328, -314 /34 erid -+006066 776612 ZZ2183=ZZ2183+ZZ2183 -+006066 775424 ZZ2183=ZZ2183+ZZ2183 -+006066 773050 ZZ2183=ZZ2183+ZZ2183 -+006066 766120 ZZ2183=ZZ2183+ZZ2183 -+006066 754240 ZZ2183=ZZ2183+ZZ2183 -+006066 730500 ZZ2183=ZZ2183+ZZ2183 -+006066 661200 ZZ2183=ZZ2183+ZZ2183 -+006066 542400 ZZ2183=ZZ2183+ZZ2183 -+006066 015320 0 8192 -ZZ1183 -+006067 542400 0 ZZ2183 -- mark 1495, 432 /74 taur -+006070 001540 ZZ2184=ZZ2184+ZZ2184 -+006070 003300 ZZ2184=ZZ2184+ZZ2184 -+006070 006600 ZZ2184=ZZ2184+ZZ2184 -+006070 015400 ZZ2184=ZZ2184+ZZ2184 -+006070 033000 ZZ2184=ZZ2184+ZZ2184 -+006070 066000 ZZ2184=ZZ2184+ZZ2184 -+006070 154000 ZZ2184=ZZ2184+ZZ2184 -+006070 330000 ZZ2184=ZZ2184+ZZ2184 -+006070 015051 0 8192 -ZZ1184 -+006071 330000 0 ZZ2184 -- mark 1496, 356 /78 taur -+006072 001310 ZZ2185=ZZ2185+ZZ2185 -+006072 002620 ZZ2185=ZZ2185+ZZ2185 -+006072 005440 ZZ2185=ZZ2185+ZZ2185 -+006072 013100 ZZ2185=ZZ2185+ZZ2185 -+006072 026200 ZZ2185=ZZ2185+ZZ2185 -+006072 054400 ZZ2185=ZZ2185+ZZ2185 -+006072 131000 ZZ2185=ZZ2185+ZZ2185 -+006072 262000 ZZ2185=ZZ2185+ZZ2185 -+006072 015050 0 8192 -ZZ1185 -+006073 262000 0 ZZ2185 -- mark 1618, 154 / 1 orio -+006074 000464 ZZ2186=ZZ2186+ZZ2186 -+006074 001150 ZZ2186=ZZ2186+ZZ2186 -+006074 002320 ZZ2186=ZZ2186+ZZ2186 -+006074 004640 ZZ2186=ZZ2186+ZZ2186 -+006074 011500 ZZ2186=ZZ2186+ZZ2186 -+006074 023200 ZZ2186=ZZ2186+ZZ2186 -+006074 046400 ZZ2186=ZZ2186+ZZ2186 -+006074 115000 ZZ2186=ZZ2186+ZZ2186 -+006074 014656 0 8192 -ZZ1186 -+006075 115000 0 ZZ2186 -- mark 1644, 52 / 8 orio -+006076 000150 ZZ2187=ZZ2187+ZZ2187 -+006076 000320 ZZ2187=ZZ2187+ZZ2187 -+006076 000640 ZZ2187=ZZ2187+ZZ2187 -+006076 001500 ZZ2187=ZZ2187+ZZ2187 -+006076 003200 ZZ2187=ZZ2187+ZZ2187 -+006076 006400 ZZ2187=ZZ2187+ZZ2187 -+006076 015000 ZZ2187=ZZ2187+ZZ2187 -+006076 032000 ZZ2187=ZZ2187+ZZ2187 -+006076 014624 0 8192 -ZZ1187 -+006077 032000 0 ZZ2187 -- mark 1723, -119 /67 erid -+006100 777420 ZZ2188=ZZ2188+ZZ2188 -+006100 777040 ZZ2188=ZZ2188+ZZ2188 -+006100 776100 ZZ2188=ZZ2188+ZZ2188 -+006100 774200 ZZ2188=ZZ2188+ZZ2188 -+006100 770400 ZZ2188=ZZ2188+ZZ2188 -+006100 761000 ZZ2188=ZZ2188+ZZ2188 -+006100 742000 ZZ2188=ZZ2188+ZZ2188 -+006100 704000 ZZ2188=ZZ2188+ZZ2188 -+006100 014505 0 8192 -ZZ1188 -+006101 704000 0 ZZ2188 -- mark 1755, -371 / 5 leps -+006102 776430 ZZ2189=ZZ2189+ZZ2189 -+006102 775060 ZZ2189=ZZ2189+ZZ2189 -+006102 772140 ZZ2189=ZZ2189+ZZ2189 -+006102 764300 ZZ2189=ZZ2189+ZZ2189 -+006102 750600 ZZ2189=ZZ2189+ZZ2189 -+006102 721400 ZZ2189=ZZ2189+ZZ2189 -+006102 643000 ZZ2189=ZZ2189+ZZ2189 -+006102 506000 ZZ2189=ZZ2189+ZZ2189 -+006102 014445 0 8192 -ZZ1189 -+006103 506000 0 ZZ2189 -- mark 1779, -158 /20 orio -+006104 777302 ZZ2190=ZZ2190+ZZ2190 -+006104 776604 ZZ2190=ZZ2190+ZZ2190 -+006104 775410 ZZ2190=ZZ2190+ZZ2190 -+006104 773020 ZZ2190=ZZ2190+ZZ2190 -+006104 766040 ZZ2190=ZZ2190+ZZ2190 -+006104 754100 ZZ2190=ZZ2190+ZZ2190 -+006104 730200 ZZ2190=ZZ2190+ZZ2190 -+006104 660400 ZZ2190=ZZ2190+ZZ2190 -+006104 014415 0 8192 -ZZ1190 -+006105 660400 0 ZZ2190 -- mark 1817, -57 /28 orio -+006106 777614 ZZ2191=ZZ2191+ZZ2191 -+006106 777430 ZZ2191=ZZ2191+ZZ2191 -+006106 777060 ZZ2191=ZZ2191+ZZ2191 -+006106 776140 ZZ2191=ZZ2191+ZZ2191 -+006106 774300 ZZ2191=ZZ2191+ZZ2191 -+006106 770600 ZZ2191=ZZ2191+ZZ2191 -+006106 761400 ZZ2191=ZZ2191+ZZ2191 -+006106 743000 ZZ2191=ZZ2191+ZZ2191 -+006106 014347 0 8192 -ZZ1191 -+006107 743000 0 ZZ2191 -- mark 1843, -474 / 9 leps -+006110 776112 ZZ2192=ZZ2192+ZZ2192 -+006110 774224 ZZ2192=ZZ2192+ZZ2192 -+006110 770450 ZZ2192=ZZ2192+ZZ2192 -+006110 761120 ZZ2192=ZZ2192+ZZ2192 -+006110 742240 ZZ2192=ZZ2192+ZZ2192 -+006110 704500 ZZ2192=ZZ2192+ZZ2192 -+006110 611200 ZZ2192=ZZ2192+ZZ2192 -+006110 422400 ZZ2192=ZZ2192+ZZ2192 -+006110 014315 0 8192 -ZZ1192 -+006111 422400 0 ZZ2192 -- mark 1860, -8 /34 orio -+006112 777756 ZZ2193=ZZ2193+ZZ2193 -+006112 777734 ZZ2193=ZZ2193+ZZ2193 -+006112 777670 ZZ2193=ZZ2193+ZZ2193 -+006112 777560 ZZ2193=ZZ2193+ZZ2193 -+006112 777340 ZZ2193=ZZ2193+ZZ2193 -+006112 776700 ZZ2193=ZZ2193+ZZ2193 -+006112 775600 ZZ2193=ZZ2193+ZZ2193 -+006112 773400 ZZ2193=ZZ2193+ZZ2193 -+006112 014274 0 8192 -ZZ1193 -+006113 773400 0 ZZ2193 -- mark 1868, -407 /11 leps -+006114 776320 ZZ2194=ZZ2194+ZZ2194 -+006114 774640 ZZ2194=ZZ2194+ZZ2194 -+006114 771500 ZZ2194=ZZ2194+ZZ2194 -+006114 763200 ZZ2194=ZZ2194+ZZ2194 -+006114 746400 ZZ2194=ZZ2194+ZZ2194 -+006114 715000 ZZ2194=ZZ2194+ZZ2194 -+006114 632000 ZZ2194=ZZ2194+ZZ2194 -+006114 464000 ZZ2194=ZZ2194+ZZ2194 -+006114 014264 0 8192 -ZZ1194 -+006115 464000 0 ZZ2194 -- mark 1875, 225 /39 orio -+006116 000702 ZZ2195=ZZ2195+ZZ2195 -+006116 001604 ZZ2195=ZZ2195+ZZ2195 -+006116 003410 ZZ2195=ZZ2195+ZZ2195 -+006116 007020 ZZ2195=ZZ2195+ZZ2195 -+006116 016040 ZZ2195=ZZ2195+ZZ2195 -+006116 034100 ZZ2195=ZZ2195+ZZ2195 -+006116 070200 ZZ2195=ZZ2195+ZZ2195 -+006116 160400 ZZ2195=ZZ2195+ZZ2195 -+006116 014255 0 8192 -ZZ1195 -+006117 160400 0 ZZ2195 -- mark 1880, -136 /44 orio -+006120 777356 ZZ2196=ZZ2196+ZZ2196 -+006120 776734 ZZ2196=ZZ2196+ZZ2196 -+006120 775670 ZZ2196=ZZ2196+ZZ2196 -+006120 773560 ZZ2196=ZZ2196+ZZ2196 -+006120 767340 ZZ2196=ZZ2196+ZZ2196 -+006120 756700 ZZ2196=ZZ2196+ZZ2196 -+006120 735600 ZZ2196=ZZ2196+ZZ2196 -+006120 673400 ZZ2196=ZZ2196+ZZ2196 -+006120 014250 0 8192 -ZZ1196 -+006121 673400 0 ZZ2196 -- mark 1887, 480 /123 taur -+006122 001700 ZZ2197=ZZ2197+ZZ2197 -+006122 003600 ZZ2197=ZZ2197+ZZ2197 -+006122 007400 ZZ2197=ZZ2197+ZZ2197 -+006122 017000 ZZ2197=ZZ2197+ZZ2197 -+006122 036000 ZZ2197=ZZ2197+ZZ2197 -+006122 074000 ZZ2197=ZZ2197+ZZ2197 -+006122 170000 ZZ2197=ZZ2197+ZZ2197 -+006122 360000 ZZ2197=ZZ2197+ZZ2197 -+006122 014241 0 8192 -ZZ1197 -+006123 360000 0 ZZ2197 -- mark 1948, -338 /14 leps -+006124 776532 ZZ2198=ZZ2198+ZZ2198 -+006124 775264 ZZ2198=ZZ2198+ZZ2198 -+006124 772550 ZZ2198=ZZ2198+ZZ2198 -+006124 765320 ZZ2198=ZZ2198+ZZ2198 -+006124 752640 ZZ2198=ZZ2198+ZZ2198 -+006124 725500 ZZ2198=ZZ2198+ZZ2198 -+006124 653200 ZZ2198=ZZ2198+ZZ2198 -+006124 526400 ZZ2198=ZZ2198+ZZ2198 -+006124 014144 0 8192 -ZZ1198 -+006125 526400 0 ZZ2198 -- mark 2274, 296 /31 gemi -+006126 001120 ZZ2199=ZZ2199+ZZ2199 -+006126 002240 ZZ2199=ZZ2199+ZZ2199 -+006126 004500 ZZ2199=ZZ2199+ZZ2199 -+006126 011200 ZZ2199=ZZ2199+ZZ2199 -+006126 022400 ZZ2199=ZZ2199+ZZ2199 -+006126 045000 ZZ2199=ZZ2199+ZZ2199 -+006126 112000 ZZ2199=ZZ2199+ZZ2199 -+006126 224000 ZZ2199=ZZ2199+ZZ2199 -+006126 013436 0 8192 -ZZ1199 -+006127 224000 0 ZZ2199 -- mark 2460, 380 /54 gemi -+006130 001370 ZZ2200=ZZ2200+ZZ2200 -+006130 002760 ZZ2200=ZZ2200+ZZ2200 -+006130 005740 ZZ2200=ZZ2200+ZZ2200 -+006130 013700 ZZ2200=ZZ2200+ZZ2200 -+006130 027600 ZZ2200=ZZ2200+ZZ2200 -+006130 057400 ZZ2200=ZZ2200+ZZ2200 -+006130 137000 ZZ2200=ZZ2200+ZZ2200 -+006130 276000 ZZ2200=ZZ2200+ZZ2200 -+006130 013144 0 8192 -ZZ1200 -+006131 276000 0 ZZ2200 -- mark 2470, 504 /55 gemi -+006132 001760 ZZ2201=ZZ2201+ZZ2201 -+006132 003740 ZZ2201=ZZ2201+ZZ2201 -+006132 007700 ZZ2201=ZZ2201+ZZ2201 -+006132 017600 ZZ2201=ZZ2201+ZZ2201 -+006132 037400 ZZ2201=ZZ2201+ZZ2201 -+006132 077000 ZZ2201=ZZ2201+ZZ2201 -+006132 176000 ZZ2201=ZZ2201+ZZ2201 -+006132 374000 ZZ2201=ZZ2201+ZZ2201 -+006132 013132 0 8192 -ZZ1201 -+006133 374000 0 ZZ2201 -- mark 2513, 193 / 3 cmin -+006134 000602 ZZ2202=ZZ2202+ZZ2202 -+006134 001404 ZZ2202=ZZ2202+ZZ2202 -+006134 003010 ZZ2202=ZZ2202+ZZ2202 -+006134 006020 ZZ2202=ZZ2202+ZZ2202 -+006134 014040 ZZ2202=ZZ2202+ZZ2202 -+006134 030100 ZZ2202=ZZ2202+ZZ2202 -+006134 060200 ZZ2202=ZZ2202+ZZ2202 -+006134 140400 ZZ2202=ZZ2202+ZZ2202 -+006134 013057 0 8192 -ZZ1202 -+006135 140400 0 ZZ2202 -- mark 2967, 154 /11 hyda -+006136 000464 ZZ2203=ZZ2203+ZZ2203 -+006136 001150 ZZ2203=ZZ2203+ZZ2203 -+006136 002320 ZZ2203=ZZ2203+ZZ2203 -+006136 004640 ZZ2203=ZZ2203+ZZ2203 -+006136 011500 ZZ2203=ZZ2203+ZZ2203 -+006136 023200 ZZ2203=ZZ2203+ZZ2203 -+006136 046400 ZZ2203=ZZ2203+ZZ2203 -+006136 115000 ZZ2203=ZZ2203+ZZ2203 -+006136 012151 0 8192 -ZZ1203 -+006137 115000 0 ZZ2203 -- mark 3016, 144 /16 hyda -+006140 000440 ZZ2204=ZZ2204+ZZ2204 -+006140 001100 ZZ2204=ZZ2204+ZZ2204 -+006140 002200 ZZ2204=ZZ2204+ZZ2204 -+006140 004400 ZZ2204=ZZ2204+ZZ2204 -+006140 011000 ZZ2204=ZZ2204+ZZ2204 -+006140 022000 ZZ2204=ZZ2204+ZZ2204 -+006140 044000 ZZ2204=ZZ2204+ZZ2204 -+006140 110000 ZZ2204=ZZ2204+ZZ2204 -+006140 012070 0 8192 -ZZ1204 -+006141 110000 0 ZZ2204 -- mark 3424, 393 /30 leon -+006142 001422 ZZ2205=ZZ2205+ZZ2205 -+006142 003044 ZZ2205=ZZ2205+ZZ2205 -+006142 006110 ZZ2205=ZZ2205+ZZ2205 -+006142 014220 ZZ2205=ZZ2205+ZZ2205 -+006142 030440 ZZ2205=ZZ2205+ZZ2205 -+006142 061100 ZZ2205=ZZ2205+ZZ2205 -+006142 142200 ZZ2205=ZZ2205+ZZ2205 -+006142 304400 ZZ2205=ZZ2205+ZZ2205 -+006142 011240 0 8192 -ZZ1205 -+006143 304400 0 ZZ2205 -- mark 3496, 463 /41 leon, algieba -+006144 001636 ZZ2206=ZZ2206+ZZ2206 -+006144 003474 ZZ2206=ZZ2206+ZZ2206 -+006144 007170 ZZ2206=ZZ2206+ZZ2206 -+006144 016360 ZZ2206=ZZ2206+ZZ2206 -+006144 034740 ZZ2206=ZZ2206+ZZ2206 -+006144 071700 ZZ2206=ZZ2206+ZZ2206 -+006144 163600 ZZ2206=ZZ2206+ZZ2206 -+006144 347400 ZZ2206=ZZ2206+ZZ2206 -+006144 011130 0 8192 -ZZ1206 -+006145 347400 0 ZZ2206 -- mark 3668, -357 /nu hyda -+006146 776464 ZZ2207=ZZ2207+ZZ2207 -+006146 775150 ZZ2207=ZZ2207+ZZ2207 -+006146 772320 ZZ2207=ZZ2207+ZZ2207 -+006146 764640 ZZ2207=ZZ2207+ZZ2207 -+006146 751500 ZZ2207=ZZ2207+ZZ2207 -+006146 723200 ZZ2207=ZZ2207+ZZ2207 -+006146 646400 ZZ2207=ZZ2207+ZZ2207 -+006146 515000 ZZ2207=ZZ2207+ZZ2207 -+006146 010654 0 8192 -ZZ1207 -+006147 515000 0 ZZ2207 -- mark 3805, 479 /68 leon -+006150 001676 ZZ2208=ZZ2208+ZZ2208 -+006150 003574 ZZ2208=ZZ2208+ZZ2208 -+006150 007370 ZZ2208=ZZ2208+ZZ2208 -+006150 016760 ZZ2208=ZZ2208+ZZ2208 -+006150 035740 ZZ2208=ZZ2208+ZZ2208 -+006150 073700 ZZ2208=ZZ2208+ZZ2208 -+006150 167600 ZZ2208=ZZ2208+ZZ2208 -+006150 357400 ZZ2208=ZZ2208+ZZ2208 -+006150 010443 0 8192 -ZZ1208 -+006151 357400 0 ZZ2208 -- mark 3806, 364 /10 leon -+006152 001330 ZZ2209=ZZ2209+ZZ2209 -+006152 002660 ZZ2209=ZZ2209+ZZ2209 -+006152 005540 ZZ2209=ZZ2209+ZZ2209 -+006152 013300 ZZ2209=ZZ2209+ZZ2209 -+006152 026600 ZZ2209=ZZ2209+ZZ2209 -+006152 055400 ZZ2209=ZZ2209+ZZ2209 -+006152 133000 ZZ2209=ZZ2209+ZZ2209 -+006152 266000 ZZ2209=ZZ2209+ZZ2209 -+006152 010442 0 8192 -ZZ1209 -+006153 266000 0 ZZ2209 -- mark 4124, -502 / 2 corv -+006154 776022 ZZ2210=ZZ2210+ZZ2210 -+006154 774044 ZZ2210=ZZ2210+ZZ2210 -+006154 770110 ZZ2210=ZZ2210+ZZ2210 -+006154 760220 ZZ2210=ZZ2210+ZZ2210 -+006154 740440 ZZ2210=ZZ2210+ZZ2210 -+006154 701100 ZZ2210=ZZ2210+ZZ2210 -+006154 602200 ZZ2210=ZZ2210+ZZ2210 -+006154 404400 ZZ2210=ZZ2210+ZZ2210 -+006154 007744 0 8192 -ZZ1210 -+006155 404400 0 ZZ2210 -- mark 4157, -387 / 4 corv -+006156 776370 ZZ2211=ZZ2211+ZZ2211 -+006156 774760 ZZ2211=ZZ2211+ZZ2211 -+006156 771740 ZZ2211=ZZ2211+ZZ2211 -+006156 763700 ZZ2211=ZZ2211+ZZ2211 -+006156 747600 ZZ2211=ZZ2211+ZZ2211 -+006156 717400 ZZ2211=ZZ2211+ZZ2211 -+006156 637000 ZZ2211=ZZ2211+ZZ2211 -+006156 476000 ZZ2211=ZZ2211+ZZ2211 -+006156 007703 0 8192 -ZZ1211 -+006157 476000 0 ZZ2211 -- mark 4236, -363 / 7 corv -+006160 776450 ZZ2212=ZZ2212+ZZ2212 -+006160 775120 ZZ2212=ZZ2212+ZZ2212 -+006160 772240 ZZ2212=ZZ2212+ZZ2212 -+006160 764500 ZZ2212=ZZ2212+ZZ2212 -+006160 751200 ZZ2212=ZZ2212+ZZ2212 -+006160 722400 ZZ2212=ZZ2212+ZZ2212 -+006160 645000 ZZ2212=ZZ2212+ZZ2212 -+006160 512000 ZZ2212=ZZ2212+ZZ2212 -+006160 007564 0 8192 -ZZ1212 -+006161 512000 0 ZZ2212 -- mark 4304, -21 /29 virg -+006162 777724 ZZ2213=ZZ2213+ZZ2213 -+006162 777650 ZZ2213=ZZ2213+ZZ2213 -+006162 777520 ZZ2213=ZZ2213+ZZ2213 -+006162 777240 ZZ2213=ZZ2213+ZZ2213 -+006162 776500 ZZ2213=ZZ2213+ZZ2213 -+006162 775200 ZZ2213=ZZ2213+ZZ2213 -+006162 772400 ZZ2213=ZZ2213+ZZ2213 -+006162 765000 ZZ2213=ZZ2213+ZZ2213 -+006162 007460 0 8192 -ZZ1213 -+006163 765000 0 ZZ2213 -- mark 4384, 90 /43 virg -+006164 000264 ZZ2214=ZZ2214+ZZ2214 -+006164 000550 ZZ2214=ZZ2214+ZZ2214 -+006164 001320 ZZ2214=ZZ2214+ZZ2214 -+006164 002640 ZZ2214=ZZ2214+ZZ2214 -+006164 005500 ZZ2214=ZZ2214+ZZ2214 -+006164 013200 ZZ2214=ZZ2214+ZZ2214 -+006164 026400 ZZ2214=ZZ2214+ZZ2214 -+006164 055000 ZZ2214=ZZ2214+ZZ2214 -+006164 007340 0 8192 -ZZ1214 -+006165 055000 0 ZZ2214 -- mark 4421, 262 /47 virg -+006166 001014 ZZ2215=ZZ2215+ZZ2215 -+006166 002030 ZZ2215=ZZ2215+ZZ2215 -+006166 004060 ZZ2215=ZZ2215+ZZ2215 -+006166 010140 ZZ2215=ZZ2215+ZZ2215 -+006166 020300 ZZ2215=ZZ2215+ZZ2215 -+006166 040600 ZZ2215=ZZ2215+ZZ2215 -+006166 101400 ZZ2215=ZZ2215+ZZ2215 -+006166 203000 ZZ2215=ZZ2215+ZZ2215 -+006166 007273 0 8192 -ZZ1215 -+006167 203000 0 ZZ2215 -- mark 4606, -2 /79 virg -+006170 777772 ZZ2216=ZZ2216+ZZ2216 -+006170 777764 ZZ2216=ZZ2216+ZZ2216 -+006170 777750 ZZ2216=ZZ2216+ZZ2216 -+006170 777720 ZZ2216=ZZ2216+ZZ2216 -+006170 777640 ZZ2216=ZZ2216+ZZ2216 -+006170 777500 ZZ2216=ZZ2216+ZZ2216 -+006170 777200 ZZ2216=ZZ2216+ZZ2216 -+006170 776400 ZZ2216=ZZ2216+ZZ2216 -+006170 007002 0 8192 -ZZ1216 -+006171 776400 0 ZZ2216 -- mark 4721, 430 / 8 boot -+006172 001534 ZZ2217=ZZ2217+ZZ2217 -+006172 003270 ZZ2217=ZZ2217+ZZ2217 -+006172 006560 ZZ2217=ZZ2217+ZZ2217 -+006172 015340 ZZ2217=ZZ2217+ZZ2217 -+006172 032700 ZZ2217=ZZ2217+ZZ2217 -+006172 065600 ZZ2217=ZZ2217+ZZ2217 -+006172 153400 ZZ2217=ZZ2217+ZZ2217 -+006172 327000 ZZ2217=ZZ2217+ZZ2217 -+006172 006617 0 8192 -ZZ1217 -+006173 327000 0 ZZ2217 -- mark 5037, -356 / 9 libr -+006174 776466 ZZ2218=ZZ2218+ZZ2218 -+006174 775154 ZZ2218=ZZ2218+ZZ2218 -+006174 772330 ZZ2218=ZZ2218+ZZ2218 -+006174 764660 ZZ2218=ZZ2218+ZZ2218 -+006174 751540 ZZ2218=ZZ2218+ZZ2218 -+006174 723300 ZZ2218=ZZ2218+ZZ2218 -+006174 646600 ZZ2218=ZZ2218+ZZ2218 -+006174 515400 ZZ2218=ZZ2218+ZZ2218 -+006174 006123 0 8192 -ZZ1218 -+006175 515400 0 ZZ2218 -- mark 5186, -205 /27 libr -+006176 777144 ZZ2219=ZZ2219+ZZ2219 -+006176 776310 ZZ2219=ZZ2219+ZZ2219 -+006176 774620 ZZ2219=ZZ2219+ZZ2219 -+006176 771440 ZZ2219=ZZ2219+ZZ2219 -+006176 763100 ZZ2219=ZZ2219+ZZ2219 -+006176 746200 ZZ2219=ZZ2219+ZZ2219 -+006176 714400 ZZ2219=ZZ2219+ZZ2219 -+006176 631000 ZZ2219=ZZ2219+ZZ2219 -+006176 005676 0 8192 -ZZ1219 -+006177 631000 0 ZZ2219 -- mark 5344, 153 /24 serp -+006200 000462 ZZ2220=ZZ2220+ZZ2220 -+006200 001144 ZZ2220=ZZ2220+ZZ2220 -+006200 002310 ZZ2220=ZZ2220+ZZ2220 -+006200 004620 ZZ2220=ZZ2220+ZZ2220 -+006200 011440 ZZ2220=ZZ2220+ZZ2220 -+006200 023100 ZZ2220=ZZ2220+ZZ2220 -+006200 046200 ZZ2220=ZZ2220+ZZ2220 -+006200 114400 ZZ2220=ZZ2220+ZZ2220 -+006200 005440 0 8192 -ZZ1220 -+006201 114400 0 ZZ2220 -- mark 5357, 358 /28 serp -+006202 001314 ZZ2221=ZZ2221+ZZ2221 -+006202 002630 ZZ2221=ZZ2221+ZZ2221 -+006202 005460 ZZ2221=ZZ2221+ZZ2221 -+006202 013140 ZZ2221=ZZ2221+ZZ2221 -+006202 026300 ZZ2221=ZZ2221+ZZ2221 -+006202 054600 ZZ2221=ZZ2221+ZZ2221 -+006202 131400 ZZ2221=ZZ2221+ZZ2221 -+006202 263000 ZZ2221=ZZ2221+ZZ2221 -+006202 005423 0 8192 -ZZ1221 -+006203 263000 0 ZZ2221 -- mark 5373, -71 /32 serp -+006204 777560 ZZ2222=ZZ2222+ZZ2222 -+006204 777340 ZZ2222=ZZ2222+ZZ2222 -+006204 776700 ZZ2222=ZZ2222+ZZ2222 -+006204 775600 ZZ2222=ZZ2222+ZZ2222 -+006204 773400 ZZ2222=ZZ2222+ZZ2222 -+006204 767000 ZZ2222=ZZ2222+ZZ2222 -+006204 756000 ZZ2222=ZZ2222+ZZ2222 -+006204 734000 ZZ2222=ZZ2222+ZZ2222 -+006204 005403 0 8192 -ZZ1222 -+006205 734000 0 ZZ2222 -- mark 5430, -508 / 7 scor -+006206 776006 ZZ2223=ZZ2223+ZZ2223 -+006206 774014 ZZ2223=ZZ2223+ZZ2223 -+006206 770030 ZZ2223=ZZ2223+ZZ2223 -+006206 760060 ZZ2223=ZZ2223+ZZ2223 -+006206 740140 ZZ2223=ZZ2223+ZZ2223 -+006206 700300 ZZ2223=ZZ2223+ZZ2223 -+006206 600600 ZZ2223=ZZ2223+ZZ2223 -+006206 401400 ZZ2223=ZZ2223+ZZ2223 -+006206 005312 0 8192 -ZZ1223 -+006207 401400 0 ZZ2223 -- mark 5459, -445 / 8 scor -+006210 776204 ZZ2224=ZZ2224+ZZ2224 -+006210 774410 ZZ2224=ZZ2224+ZZ2224 -+006210 771020 ZZ2224=ZZ2224+ZZ2224 -+006210 762040 ZZ2224=ZZ2224+ZZ2224 -+006210 744100 ZZ2224=ZZ2224+ZZ2224 -+006210 710200 ZZ2224=ZZ2224+ZZ2224 -+006210 620400 ZZ2224=ZZ2224+ZZ2224 -+006210 441000 ZZ2224=ZZ2224+ZZ2224 -+006210 005255 0 8192 -ZZ1224 -+006211 441000 0 ZZ2224 -- mark 5513, -78 / 1 ophi -+006212 777542 ZZ2225=ZZ2225+ZZ2225 -+006212 777304 ZZ2225=ZZ2225+ZZ2225 -+006212 776610 ZZ2225=ZZ2225+ZZ2225 -+006212 775420 ZZ2225=ZZ2225+ZZ2225 -+006212 773040 ZZ2225=ZZ2225+ZZ2225 -+006212 766100 ZZ2225=ZZ2225+ZZ2225 -+006212 754200 ZZ2225=ZZ2225+ZZ2225 -+006212 730400 ZZ2225=ZZ2225+ZZ2225 -+006212 005167 0 8192 -ZZ1225 -+006213 730400 0 ZZ2225 -- mark 5536, -101 / 2 ophi -+006214 777464 ZZ2226=ZZ2226+ZZ2226 -+006214 777150 ZZ2226=ZZ2226+ZZ2226 -+006214 776320 ZZ2226=ZZ2226+ZZ2226 -+006214 774640 ZZ2226=ZZ2226+ZZ2226 -+006214 771500 ZZ2226=ZZ2226+ZZ2226 -+006214 763200 ZZ2226=ZZ2226+ZZ2226 -+006214 746400 ZZ2226=ZZ2226+ZZ2226 -+006214 715000 ZZ2226=ZZ2226+ZZ2226 -+006214 005140 0 8192 -ZZ1226 -+006215 715000 0 ZZ2226 -- mark 5609, 494 /27 herc -+006216 001734 ZZ2227=ZZ2227+ZZ2227 -+006216 003670 ZZ2227=ZZ2227+ZZ2227 -+006216 007560 ZZ2227=ZZ2227+ZZ2227 -+006216 017340 ZZ2227=ZZ2227+ZZ2227 -+006216 036700 ZZ2227=ZZ2227+ZZ2227 -+006216 075600 ZZ2227=ZZ2227+ZZ2227 -+006216 173400 ZZ2227=ZZ2227+ZZ2227 -+006216 367000 ZZ2227=ZZ2227+ZZ2227 -+006216 005027 0 8192 -ZZ1227 -+006217 367000 0 ZZ2227 -- mark 5641, -236 /13 ophi -+006220 777046 ZZ2228=ZZ2228+ZZ2228 -+006220 776114 ZZ2228=ZZ2228+ZZ2228 -+006220 774230 ZZ2228=ZZ2228+ZZ2228 -+006220 770460 ZZ2228=ZZ2228+ZZ2228 -+006220 761140 ZZ2228=ZZ2228+ZZ2228 -+006220 742300 ZZ2228=ZZ2228+ZZ2228 -+006220 704600 ZZ2228=ZZ2228+ZZ2228 -+006220 611400 ZZ2228=ZZ2228+ZZ2228 -+006220 004767 0 8192 -ZZ1228 -+006221 611400 0 ZZ2228 -- mark 5828, -355 /35 ophi -+006222 776470 ZZ2229=ZZ2229+ZZ2229 -+006222 775160 ZZ2229=ZZ2229+ZZ2229 -+006222 772340 ZZ2229=ZZ2229+ZZ2229 -+006222 764700 ZZ2229=ZZ2229+ZZ2229 -+006222 751600 ZZ2229=ZZ2229+ZZ2229 -+006222 723400 ZZ2229=ZZ2229+ZZ2229 -+006222 647000 ZZ2229=ZZ2229+ZZ2229 -+006222 516000 ZZ2229=ZZ2229+ZZ2229 -+006222 004474 0 8192 -ZZ1229 -+006223 516000 0 ZZ2229 -- mark 5860, 330 /64 herc -+006224 001224 ZZ2230=ZZ2230+ZZ2230 -+006224 002450 ZZ2230=ZZ2230+ZZ2230 -+006224 005120 ZZ2230=ZZ2230+ZZ2230 -+006224 012240 ZZ2230=ZZ2230+ZZ2230 -+006224 024500 ZZ2230=ZZ2230+ZZ2230 -+006224 051200 ZZ2230=ZZ2230+ZZ2230 -+006224 122400 ZZ2230=ZZ2230+ZZ2230 -+006224 245000 ZZ2230=ZZ2230+ZZ2230 -+006224 004434 0 8192 -ZZ1230 -+006225 245000 0 ZZ2230 -- mark 5984, -349 /55 serp -+006226 776504 ZZ2231=ZZ2231+ZZ2231 -+006226 775210 ZZ2231=ZZ2231+ZZ2231 -+006226 772420 ZZ2231=ZZ2231+ZZ2231 -+006226 765040 ZZ2231=ZZ2231+ZZ2231 -+006226 752100 ZZ2231=ZZ2231+ZZ2231 -+006226 724200 ZZ2231=ZZ2231+ZZ2231 -+006226 650400 ZZ2231=ZZ2231+ZZ2231 -+006226 521000 ZZ2231=ZZ2231+ZZ2231 -+006226 004240 0 8192 -ZZ1231 -+006227 521000 0 ZZ2231 -- mark 6047, 63 /62 ophi -+006230 000176 ZZ2232=ZZ2232+ZZ2232 -+006230 000374 ZZ2232=ZZ2232+ZZ2232 -+006230 000770 ZZ2232=ZZ2232+ZZ2232 -+006230 001760 ZZ2232=ZZ2232+ZZ2232 -+006230 003740 ZZ2232=ZZ2232+ZZ2232 -+006230 007700 ZZ2232=ZZ2232+ZZ2232 -+006230 017600 ZZ2232=ZZ2232+ZZ2232 -+006230 037400 ZZ2232=ZZ2232+ZZ2232 -+006230 004141 0 8192 -ZZ1232 -+006231 037400 0 ZZ2232 -- mark 6107, -222 /64 ophi -+006232 777102 ZZ2233=ZZ2233+ZZ2233 -+006232 776204 ZZ2233=ZZ2233+ZZ2233 -+006232 774410 ZZ2233=ZZ2233+ZZ2233 -+006232 771020 ZZ2233=ZZ2233+ZZ2233 -+006232 762040 ZZ2233=ZZ2233+ZZ2233 -+006232 744100 ZZ2233=ZZ2233+ZZ2233 -+006232 710200 ZZ2233=ZZ2233+ZZ2233 -+006232 620400 ZZ2233=ZZ2233+ZZ2233 -+006232 004045 0 8192 -ZZ1233 -+006233 620400 0 ZZ2233 -- mark 6159, 217 /72 ophi -+006234 000662 ZZ2234=ZZ2234+ZZ2234 -+006234 001544 ZZ2234=ZZ2234+ZZ2234 -+006234 003310 ZZ2234=ZZ2234+ZZ2234 -+006234 006620 ZZ2234=ZZ2234+ZZ2234 -+006234 015440 ZZ2234=ZZ2234+ZZ2234 -+006234 033100 ZZ2234=ZZ2234+ZZ2234 -+006234 066200 ZZ2234=ZZ2234+ZZ2234 -+006234 154400 ZZ2234=ZZ2234+ZZ2234 -+006234 003761 0 8192 -ZZ1234 -+006235 154400 0 ZZ2234 -- mark 6236, -66 /58 serp -+006236 777572 ZZ2235=ZZ2235+ZZ2235 -+006236 777364 ZZ2235=ZZ2235+ZZ2235 -+006236 776750 ZZ2235=ZZ2235+ZZ2235 -+006236 775720 ZZ2235=ZZ2235+ZZ2235 -+006236 773640 ZZ2235=ZZ2235+ZZ2235 -+006236 767500 ZZ2235=ZZ2235+ZZ2235 -+006236 757200 ZZ2235=ZZ2235+ZZ2235 -+006236 736400 ZZ2235=ZZ2235+ZZ2235 -+006236 003644 0 8192 -ZZ1235 -+006237 736400 0 ZZ2235 -- mark 6439, -483 /37 sgtr -+006240 776070 ZZ2236=ZZ2236+ZZ2236 -+006240 774160 ZZ2236=ZZ2236+ZZ2236 -+006240 770340 ZZ2236=ZZ2236+ZZ2236 -+006240 760700 ZZ2236=ZZ2236+ZZ2236 -+006240 741600 ZZ2236=ZZ2236+ZZ2236 -+006240 703400 ZZ2236=ZZ2236+ZZ2236 -+006240 607000 ZZ2236=ZZ2236+ZZ2236 -+006240 416000 ZZ2236=ZZ2236+ZZ2236 -+006240 003331 0 8192 -ZZ1236 -+006241 416000 0 ZZ2236 -- mark 6490, 312 /17 aqil -+006242 001160 ZZ2237=ZZ2237+ZZ2237 -+006242 002340 ZZ2237=ZZ2237+ZZ2237 -+006242 004700 ZZ2237=ZZ2237+ZZ2237 -+006242 011600 ZZ2237=ZZ2237+ZZ2237 -+006242 023400 ZZ2237=ZZ2237+ZZ2237 -+006242 047000 ZZ2237=ZZ2237+ZZ2237 -+006242 116000 ZZ2237=ZZ2237+ZZ2237 -+006242 234000 ZZ2237=ZZ2237+ZZ2237 -+006242 003246 0 8192 -ZZ1237 -+006243 234000 0 ZZ2237 -- mark 6491, -115 /16 aqil -+006244 777430 ZZ2238=ZZ2238+ZZ2238 -+006244 777060 ZZ2238=ZZ2238+ZZ2238 -+006244 776140 ZZ2238=ZZ2238+ZZ2238 -+006244 774300 ZZ2238=ZZ2238+ZZ2238 -+006244 770600 ZZ2238=ZZ2238+ZZ2238 -+006244 761400 ZZ2238=ZZ2238+ZZ2238 -+006244 743000 ZZ2238=ZZ2238+ZZ2238 -+006244 706000 ZZ2238=ZZ2238+ZZ2238 -+006244 003245 0 8192 -ZZ1238 -+006245 706000 0 ZZ2238 -- mark 6507, -482 /41 sgtr -+006246 776072 ZZ2239=ZZ2239+ZZ2239 -+006246 774164 ZZ2239=ZZ2239+ZZ2239 -+006246 770350 ZZ2239=ZZ2239+ZZ2239 -+006246 760720 ZZ2239=ZZ2239+ZZ2239 -+006246 741640 ZZ2239=ZZ2239+ZZ2239 -+006246 703500 ZZ2239=ZZ2239+ZZ2239 -+006246 607200 ZZ2239=ZZ2239+ZZ2239 -+006246 416400 ZZ2239=ZZ2239+ZZ2239 -+006246 003225 0 8192 -ZZ1239 -+006247 416400 0 ZZ2239 -- mark 6602, 66 /30 aqil -+006250 000204 ZZ2240=ZZ2240+ZZ2240 -+006250 000410 ZZ2240=ZZ2240+ZZ2240 -+006250 001020 ZZ2240=ZZ2240+ZZ2240 -+006250 002040 ZZ2240=ZZ2240+ZZ2240 -+006250 004100 ZZ2240=ZZ2240+ZZ2240 -+006250 010200 ZZ2240=ZZ2240+ZZ2240 -+006250 020400 ZZ2240=ZZ2240+ZZ2240 -+006250 041000 ZZ2240=ZZ2240+ZZ2240 -+006250 003066 0 8192 -ZZ1240 -+006251 041000 0 ZZ2240 -- mark 6721, 236 /50 aqil -+006252 000730 ZZ2241=ZZ2241+ZZ2241 -+006252 001660 ZZ2241=ZZ2241+ZZ2241 -+006252 003540 ZZ2241=ZZ2241+ZZ2241 -+006252 007300 ZZ2241=ZZ2241+ZZ2241 -+006252 016600 ZZ2241=ZZ2241+ZZ2241 -+006252 035400 ZZ2241=ZZ2241+ZZ2241 -+006252 073000 ZZ2241=ZZ2241+ZZ2241 -+006252 166000 ZZ2241=ZZ2241+ZZ2241 -+006252 002677 0 8192 -ZZ1241 -+006253 166000 0 ZZ2241 -- mark 6794, 437 /12 sgte -+006254 001552 ZZ2242=ZZ2242+ZZ2242 -+006254 003324 ZZ2242=ZZ2242+ZZ2242 -+006254 006650 ZZ2242=ZZ2242+ZZ2242 -+006254 015520 ZZ2242=ZZ2242+ZZ2242 -+006254 033240 ZZ2242=ZZ2242+ZZ2242 -+006254 066500 ZZ2242=ZZ2242+ZZ2242 -+006254 155200 ZZ2242=ZZ2242+ZZ2242 -+006254 332400 ZZ2242=ZZ2242+ZZ2242 -+006254 002566 0 8192 -ZZ1242 -+006255 332400 0 ZZ2242 -- mark 6862, -25 /65 aqil -+006256 777714 ZZ2243=ZZ2243+ZZ2243 -+006256 777630 ZZ2243=ZZ2243+ZZ2243 -+006256 777460 ZZ2243=ZZ2243+ZZ2243 -+006256 777140 ZZ2243=ZZ2243+ZZ2243 -+006256 776300 ZZ2243=ZZ2243+ZZ2243 -+006256 774600 ZZ2243=ZZ2243+ZZ2243 -+006256 771400 ZZ2243=ZZ2243+ZZ2243 -+006256 763000 ZZ2243=ZZ2243+ZZ2243 -+006256 002462 0 8192 -ZZ1243 -+006257 763000 0 ZZ2243 -- mark 6914, -344 / 9 capr -+006260 776516 ZZ2244=ZZ2244+ZZ2244 -+006260 775234 ZZ2244=ZZ2244+ZZ2244 -+006260 772470 ZZ2244=ZZ2244+ZZ2244 -+006260 765160 ZZ2244=ZZ2244+ZZ2244 -+006260 752340 ZZ2244=ZZ2244+ZZ2244 -+006260 724700 ZZ2244=ZZ2244+ZZ2244 -+006260 651600 ZZ2244=ZZ2244+ZZ2244 -+006260 523400 ZZ2244=ZZ2244+ZZ2244 -+006260 002376 0 8192 -ZZ1244 -+006261 523400 0 ZZ2244 -- mark 7014, 324 / 6 dlph -+006262 001210 ZZ2245=ZZ2245+ZZ2245 -+006262 002420 ZZ2245=ZZ2245+ZZ2245 -+006262 005040 ZZ2245=ZZ2245+ZZ2245 -+006262 012100 ZZ2245=ZZ2245+ZZ2245 -+006262 024200 ZZ2245=ZZ2245+ZZ2245 -+006262 050400 ZZ2245=ZZ2245+ZZ2245 -+006262 121000 ZZ2245=ZZ2245+ZZ2245 -+006262 242000 ZZ2245=ZZ2245+ZZ2245 -+006262 002232 0 8192 -ZZ1245 -+006263 242000 0 ZZ2245 -- mark 7318, -137 /22 aqar -+006264 777354 ZZ2246=ZZ2246+ZZ2246 -+006264 776730 ZZ2246=ZZ2246+ZZ2246 -+006264 775660 ZZ2246=ZZ2246+ZZ2246 -+006264 773540 ZZ2246=ZZ2246+ZZ2246 -+006264 767300 ZZ2246=ZZ2246+ZZ2246 -+006264 756600 ZZ2246=ZZ2246+ZZ2246 -+006264 735400 ZZ2246=ZZ2246+ZZ2246 -+006264 673000 ZZ2246=ZZ2246+ZZ2246 -+006264 001552 0 8192 -ZZ1246 -+006265 673000 0 ZZ2246 -- mark 7391, 214 / 8 pegs -+006266 000654 ZZ2247=ZZ2247+ZZ2247 -+006266 001530 ZZ2247=ZZ2247+ZZ2247 -+006266 003260 ZZ2247=ZZ2247+ZZ2247 -+006266 006540 ZZ2247=ZZ2247+ZZ2247 -+006266 015300 ZZ2247=ZZ2247+ZZ2247 -+006266 032600 ZZ2247=ZZ2247+ZZ2247 -+006266 065400 ZZ2247=ZZ2247+ZZ2247 -+006266 153000 ZZ2247=ZZ2247+ZZ2247 -+006266 001441 0 8192 -ZZ1247 -+006267 153000 0 ZZ2247 -- mark 7404, -377 /49 capr -+006270 776414 ZZ2248=ZZ2248+ZZ2248 -+006270 775030 ZZ2248=ZZ2248+ZZ2248 -+006270 772060 ZZ2248=ZZ2248+ZZ2248 -+006270 764140 ZZ2248=ZZ2248+ZZ2248 -+006270 750300 ZZ2248=ZZ2248+ZZ2248 -+006270 720600 ZZ2248=ZZ2248+ZZ2248 -+006270 641400 ZZ2248=ZZ2248+ZZ2248 -+006270 503000 ZZ2248=ZZ2248+ZZ2248 -+006270 001424 0 8192 -ZZ1248 -+006271 503000 0 ZZ2248 -- mark 7513, -18 /34 aqar -+006272 777732 ZZ2249=ZZ2249+ZZ2249 -+006272 777664 ZZ2249=ZZ2249+ZZ2249 -+006272 777550 ZZ2249=ZZ2249+ZZ2249 -+006272 777320 ZZ2249=ZZ2249+ZZ2249 -+006272 776640 ZZ2249=ZZ2249+ZZ2249 -+006272 775500 ZZ2249=ZZ2249+ZZ2249 -+006272 773200 ZZ2249=ZZ2249+ZZ2249 -+006272 766400 ZZ2249=ZZ2249+ZZ2249 -+006272 001247 0 8192 -ZZ1249 -+006273 766400 0 ZZ2249 -- mark 7539, 130 /26 pegs -+006274 000404 ZZ2250=ZZ2250+ZZ2250 -+006274 001010 ZZ2250=ZZ2250+ZZ2250 -+006274 002020 ZZ2250=ZZ2250+ZZ2250 -+006274 004040 ZZ2250=ZZ2250+ZZ2250 -+006274 010100 ZZ2250=ZZ2250+ZZ2250 -+006274 020200 ZZ2250=ZZ2250+ZZ2250 -+006274 040400 ZZ2250=ZZ2250+ZZ2250 -+006274 101000 ZZ2250=ZZ2250+ZZ2250 -+006274 001215 0 8192 -ZZ1250 -+006275 101000 0 ZZ2250 -- mark 7644, -12 /55 aqar -+006276 777746 ZZ2251=ZZ2251+ZZ2251 -+006276 777714 ZZ2251=ZZ2251+ZZ2251 -+006276 777630 ZZ2251=ZZ2251+ZZ2251 -+006276 777460 ZZ2251=ZZ2251+ZZ2251 -+006276 777140 ZZ2251=ZZ2251+ZZ2251 -+006276 776300 ZZ2251=ZZ2251+ZZ2251 -+006276 774600 ZZ2251=ZZ2251+ZZ2251 -+006276 771400 ZZ2251=ZZ2251+ZZ2251 -+006276 001044 0 8192 -ZZ1251 -+006277 771400 0 ZZ2251 -- mark 7717, 235 /42 pegs -+006300 000726 ZZ2252=ZZ2252+ZZ2252 -+006300 001654 ZZ2252=ZZ2252+ZZ2252 -+006300 003530 ZZ2252=ZZ2252+ZZ2252 -+006300 007260 ZZ2252=ZZ2252+ZZ2252 -+006300 016540 ZZ2252=ZZ2252+ZZ2252 -+006300 035300 ZZ2252=ZZ2252+ZZ2252 -+006300 072600 ZZ2252=ZZ2252+ZZ2252 -+006300 165400 ZZ2252=ZZ2252+ZZ2252 -+006300 000733 0 8192 -ZZ1252 -+006301 165400 0 ZZ2252 -- mark 7790, -372 /76 aqar -+006302 776426 ZZ2253=ZZ2253+ZZ2253 -+006302 775054 ZZ2253=ZZ2253+ZZ2253 -+006302 772130 ZZ2253=ZZ2253+ZZ2253 -+006302 764260 ZZ2253=ZZ2253+ZZ2253 -+006302 750540 ZZ2253=ZZ2253+ZZ2253 -+006302 721300 ZZ2253=ZZ2253+ZZ2253 -+006302 642600 ZZ2253=ZZ2253+ZZ2253 -+006302 505400 ZZ2253=ZZ2253+ZZ2253 -+006302 000622 0 8192 -ZZ1253 -+006303 505400 0 ZZ2253 - 006304 3q, -- mark 7849, 334 /54 pegs, markab -+006304 001234 ZZ2254=ZZ2254+ZZ2254 -+006304 002470 ZZ2254=ZZ2254+ZZ2254 -+006304 005160 ZZ2254=ZZ2254+ZZ2254 -+006304 012340 ZZ2254=ZZ2254+ZZ2254 -+006304 024700 ZZ2254=ZZ2254+ZZ2254 -+006304 051600 ZZ2254=ZZ2254+ZZ2254 -+006304 123400 ZZ2254=ZZ2254+ZZ2254 -+006304 247000 ZZ2254=ZZ2254+ZZ2254 -+006304 000527 0 8192 -ZZ1254 -+006305 247000 0 ZZ2254 - 006306 4j, -- mark 1, -143 /33 pisc -+006306 777340 ZZ2255=ZZ2255+ZZ2255 -+006306 776700 ZZ2255=ZZ2255+ZZ2255 -+006306 775600 ZZ2255=ZZ2255+ZZ2255 -+006306 773400 ZZ2255=ZZ2255+ZZ2255 -+006306 767000 ZZ2255=ZZ2255+ZZ2255 -+006306 756000 ZZ2255=ZZ2255+ZZ2255 -+006306 734000 ZZ2255=ZZ2255+ZZ2255 -+006306 670000 ZZ2255=ZZ2255+ZZ2255 -+006306 017777 0 8192 -ZZ1255 -+006307 670000 0 ZZ2255 -- mark 54, 447 /89 pegs -+006310 001576 ZZ2256=ZZ2256+ZZ2256 -+006310 003374 ZZ2256=ZZ2256+ZZ2256 -+006310 006770 ZZ2256=ZZ2256+ZZ2256 -+006310 015760 ZZ2256=ZZ2256+ZZ2256 -+006310 033740 ZZ2256=ZZ2256+ZZ2256 -+006310 067700 ZZ2256=ZZ2256+ZZ2256 -+006310 157600 ZZ2256=ZZ2256+ZZ2256 -+006310 337400 ZZ2256=ZZ2256+ZZ2256 -+006310 017712 0 8192 -ZZ1256 -+006311 337400 0 ZZ2256 -- mark 54, -443 /7 ceti -+006312 776210 ZZ2257=ZZ2257+ZZ2257 -+006312 774420 ZZ2257=ZZ2257+ZZ2257 -+006312 771040 ZZ2257=ZZ2257+ZZ2257 -+006312 762100 ZZ2257=ZZ2257+ZZ2257 -+006312 744200 ZZ2257=ZZ2257+ZZ2257 -+006312 710400 ZZ2257=ZZ2257+ZZ2257 -+006312 621000 ZZ2257=ZZ2257+ZZ2257 -+006312 442000 ZZ2257=ZZ2257+ZZ2257 -+006312 017712 0 8192 -ZZ1257 -+006313 442000 0 ZZ2257 -- mark 82, -214 /8 ceti -+006314 777122 ZZ2258=ZZ2258+ZZ2258 -+006314 776244 ZZ2258=ZZ2258+ZZ2258 -+006314 774510 ZZ2258=ZZ2258+ZZ2258 -+006314 771220 ZZ2258=ZZ2258+ZZ2258 -+006314 762440 ZZ2258=ZZ2258+ZZ2258 -+006314 745100 ZZ2258=ZZ2258+ZZ2258 -+006314 712200 ZZ2258=ZZ2258+ZZ2258 -+006314 624400 ZZ2258=ZZ2258+ZZ2258 -+006314 017656 0 8192 -ZZ1258 -+006315 624400 0 ZZ2258 -- mark 223, -254 /17 ceti -+006316 777002 ZZ2259=ZZ2259+ZZ2259 -+006316 776004 ZZ2259=ZZ2259+ZZ2259 -+006316 774010 ZZ2259=ZZ2259+ZZ2259 -+006316 770020 ZZ2259=ZZ2259+ZZ2259 -+006316 760040 ZZ2259=ZZ2259+ZZ2259 -+006316 740100 ZZ2259=ZZ2259+ZZ2259 -+006316 700200 ZZ2259=ZZ2259+ZZ2259 -+006316 600400 ZZ2259=ZZ2259+ZZ2259 -+006316 017441 0 8192 -ZZ1259 -+006317 600400 0 ZZ2259 -- mark 248, 160 /63 pisc -+006320 000500 ZZ2260=ZZ2260+ZZ2260 -+006320 001200 ZZ2260=ZZ2260+ZZ2260 -+006320 002400 ZZ2260=ZZ2260+ZZ2260 -+006320 005000 ZZ2260=ZZ2260+ZZ2260 -+006320 012000 ZZ2260=ZZ2260+ZZ2260 -+006320 024000 ZZ2260=ZZ2260+ZZ2260 -+006320 050000 ZZ2260=ZZ2260+ZZ2260 -+006320 120000 ZZ2260=ZZ2260+ZZ2260 -+006320 017410 0 8192 -ZZ1260 -+006321 120000 0 ZZ2260 -- mark 273, -38 /20 ceti -+006322 777662 ZZ2261=ZZ2261+ZZ2261 -+006322 777544 ZZ2261=ZZ2261+ZZ2261 -+006322 777310 ZZ2261=ZZ2261+ZZ2261 -+006322 776620 ZZ2261=ZZ2261+ZZ2261 -+006322 775440 ZZ2261=ZZ2261+ZZ2261 -+006322 773100 ZZ2261=ZZ2261+ZZ2261 -+006322 766200 ZZ2261=ZZ2261+ZZ2261 -+006322 754400 ZZ2261=ZZ2261+ZZ2261 -+006322 017357 0 8192 -ZZ1261 -+006323 754400 0 ZZ2261 -- mark 329, 167 /71 pisc -+006324 000516 ZZ2262=ZZ2262+ZZ2262 -+006324 001234 ZZ2262=ZZ2262+ZZ2262 -+006324 002470 ZZ2262=ZZ2262+ZZ2262 -+006324 005160 ZZ2262=ZZ2262+ZZ2262 -+006324 012340 ZZ2262=ZZ2262+ZZ2262 -+006324 024700 ZZ2262=ZZ2262+ZZ2262 -+006324 051600 ZZ2262=ZZ2262+ZZ2262 -+006324 123400 ZZ2262=ZZ2262+ZZ2262 -+006324 017267 0 8192 -ZZ1262 -+006325 123400 0 ZZ2262 -- mark 376, 467 /84 pisc -+006326 001646 ZZ2263=ZZ2263+ZZ2263 -+006326 003514 ZZ2263=ZZ2263+ZZ2263 -+006326 007230 ZZ2263=ZZ2263+ZZ2263 -+006326 016460 ZZ2263=ZZ2263+ZZ2263 -+006326 035140 ZZ2263=ZZ2263+ZZ2263 -+006326 072300 ZZ2263=ZZ2263+ZZ2263 -+006326 164600 ZZ2263=ZZ2263+ZZ2263 -+006326 351400 ZZ2263=ZZ2263+ZZ2263 -+006326 017210 0 8192 -ZZ1263 -+006327 351400 0 ZZ2263 -- mark 450, -198 /45 ceti -+006330 777162 ZZ2264=ZZ2264+ZZ2264 -+006330 776344 ZZ2264=ZZ2264+ZZ2264 -+006330 774710 ZZ2264=ZZ2264+ZZ2264 -+006330 771620 ZZ2264=ZZ2264+ZZ2264 -+006330 763440 ZZ2264=ZZ2264+ZZ2264 -+006330 747100 ZZ2264=ZZ2264+ZZ2264 -+006330 716200 ZZ2264=ZZ2264+ZZ2264 -+006330 634400 ZZ2264=ZZ2264+ZZ2264 -+006330 017076 0 8192 -ZZ1264 -+006331 634400 0 ZZ2264 -- mark 548, 113 /106 pisc -+006332 000342 ZZ2265=ZZ2265+ZZ2265 -+006332 000704 ZZ2265=ZZ2265+ZZ2265 -+006332 001610 ZZ2265=ZZ2265+ZZ2265 -+006332 003420 ZZ2265=ZZ2265+ZZ2265 -+006332 007040 ZZ2265=ZZ2265+ZZ2265 -+006332 016100 ZZ2265=ZZ2265+ZZ2265 -+006332 034200 ZZ2265=ZZ2265+ZZ2265 -+006332 070400 ZZ2265=ZZ2265+ZZ2265 -+006332 016734 0 8192 -ZZ1265 -+006333 070400 0 ZZ2265 -- mark 570, 197 /110 pisc -+006334 000612 ZZ2266=ZZ2266+ZZ2266 -+006334 001424 ZZ2266=ZZ2266+ZZ2266 -+006334 003050 ZZ2266=ZZ2266+ZZ2266 -+006334 006120 ZZ2266=ZZ2266+ZZ2266 -+006334 014240 ZZ2266=ZZ2266+ZZ2266 -+006334 030500 ZZ2266=ZZ2266+ZZ2266 -+006334 061200 ZZ2266=ZZ2266+ZZ2266 -+006334 142400 ZZ2266=ZZ2266+ZZ2266 -+006334 016706 0 8192 -ZZ1266 -+006335 142400 0 ZZ2266 -- mark 595, -255 /53 ceti -+006336 777000 ZZ2267=ZZ2267+ZZ2267 -+006336 776000 ZZ2267=ZZ2267+ZZ2267 -+006336 774000 ZZ2267=ZZ2267+ZZ2267 -+006336 770000 ZZ2267=ZZ2267+ZZ2267 -+006336 760000 ZZ2267=ZZ2267+ZZ2267 -+006336 740000 ZZ2267=ZZ2267+ZZ2267 -+006336 700000 ZZ2267=ZZ2267+ZZ2267 -+006336 600000 ZZ2267=ZZ2267+ZZ2267 -+006336 016655 0 8192 -ZZ1267 -+006337 600000 0 ZZ2267 -- mark 606, -247 /55 ceti -+006340 777020 ZZ2268=ZZ2268+ZZ2268 -+006340 776040 ZZ2268=ZZ2268+ZZ2268 -+006340 774100 ZZ2268=ZZ2268+ZZ2268 -+006340 770200 ZZ2268=ZZ2268+ZZ2268 -+006340 760400 ZZ2268=ZZ2268+ZZ2268 -+006340 741000 ZZ2268=ZZ2268+ZZ2268 -+006340 702000 ZZ2268=ZZ2268+ZZ2268 -+006340 604000 ZZ2268=ZZ2268+ZZ2268 -+006340 016642 0 8192 -ZZ1268 -+006341 604000 0 ZZ2268 -- mark 615, 428 / 5 arie -+006342 001530 ZZ2269=ZZ2269+ZZ2269 -+006342 003260 ZZ2269=ZZ2269+ZZ2269 -+006342 006540 ZZ2269=ZZ2269+ZZ2269 -+006342 015300 ZZ2269=ZZ2269+ZZ2269 -+006342 032600 ZZ2269=ZZ2269+ZZ2269 -+006342 065400 ZZ2269=ZZ2269+ZZ2269 -+006342 153000 ZZ2269=ZZ2269+ZZ2269 -+006342 326000 ZZ2269=ZZ2269+ZZ2269 -+006342 016631 0 8192 -ZZ1269 -+006343 326000 0 ZZ2269 -- mark 617, 61 /14 pisc -+006344 000172 ZZ2270=ZZ2270+ZZ2270 -+006344 000364 ZZ2270=ZZ2270+ZZ2270 -+006344 000750 ZZ2270=ZZ2270+ZZ2270 -+006344 001720 ZZ2270=ZZ2270+ZZ2270 -+006344 003640 ZZ2270=ZZ2270+ZZ2270 -+006344 007500 ZZ2270=ZZ2270+ZZ2270 -+006344 017200 ZZ2270=ZZ2270+ZZ2270 -+006344 036400 ZZ2270=ZZ2270+ZZ2270 -+006344 016627 0 8192 -ZZ1270 -+006345 036400 0 ZZ2270 -- mark 656, -491 /59 ceti -+006346 776050 ZZ2271=ZZ2271+ZZ2271 -+006346 774120 ZZ2271=ZZ2271+ZZ2271 -+006346 770240 ZZ2271=ZZ2271+ZZ2271 -+006346 760500 ZZ2271=ZZ2271+ZZ2271 -+006346 741200 ZZ2271=ZZ2271+ZZ2271 -+006346 702400 ZZ2271=ZZ2271+ZZ2271 -+006346 605000 ZZ2271=ZZ2271+ZZ2271 -+006346 412000 ZZ2271=ZZ2271+ZZ2271 -+006346 016560 0 8192 -ZZ1271 -+006347 412000 0 ZZ2271 -- mark 665, 52 /113 pisc -+006350 000150 ZZ2272=ZZ2272+ZZ2272 -+006350 000320 ZZ2272=ZZ2272+ZZ2272 -+006350 000640 ZZ2272=ZZ2272+ZZ2272 -+006350 001500 ZZ2272=ZZ2272+ZZ2272 -+006350 003200 ZZ2272=ZZ2272+ZZ2272 -+006350 006400 ZZ2272=ZZ2272+ZZ2272 -+006350 015000 ZZ2272=ZZ2272+ZZ2272 -+006350 032000 ZZ2272=ZZ2272+ZZ2272 -+006350 016547 0 8192 -ZZ1272 -+006351 032000 0 ZZ2272 -- mark 727, 191 /65 ceti -+006352 000576 ZZ2273=ZZ2273+ZZ2273 -+006352 001374 ZZ2273=ZZ2273+ZZ2273 -+006352 002770 ZZ2273=ZZ2273+ZZ2273 -+006352 005760 ZZ2273=ZZ2273+ZZ2273 -+006352 013740 ZZ2273=ZZ2273+ZZ2273 -+006352 027700 ZZ2273=ZZ2273+ZZ2273 -+006352 057600 ZZ2273=ZZ2273+ZZ2273 -+006352 137400 ZZ2273=ZZ2273+ZZ2273 -+006352 016451 0 8192 -ZZ1273 -+006353 137400 0 ZZ2273 -- mark 803, -290 /72 ceti -+006354 776672 ZZ2274=ZZ2274+ZZ2274 -+006354 775564 ZZ2274=ZZ2274+ZZ2274 -+006354 773350 ZZ2274=ZZ2274+ZZ2274 -+006354 766720 ZZ2274=ZZ2274+ZZ2274 -+006354 755640 ZZ2274=ZZ2274+ZZ2274 -+006354 733500 ZZ2274=ZZ2274+ZZ2274 -+006354 667200 ZZ2274=ZZ2274+ZZ2274 -+006354 556400 ZZ2274=ZZ2274+ZZ2274 -+006354 016335 0 8192 -ZZ1274 -+006355 556400 0 ZZ2274 -- mark 813, 182 /73 ceti -+006356 000554 ZZ2275=ZZ2275+ZZ2275 -+006356 001330 ZZ2275=ZZ2275+ZZ2275 -+006356 002660 ZZ2275=ZZ2275+ZZ2275 -+006356 005540 ZZ2275=ZZ2275+ZZ2275 -+006356 013300 ZZ2275=ZZ2275+ZZ2275 -+006356 026600 ZZ2275=ZZ2275+ZZ2275 -+006356 055400 ZZ2275=ZZ2275+ZZ2275 -+006356 133000 ZZ2275=ZZ2275+ZZ2275 -+006356 016323 0 8192 -ZZ1275 -+006357 133000 0 ZZ2275 -- mark 838, -357 /76 ceti -+006360 776464 ZZ2276=ZZ2276+ZZ2276 -+006360 775150 ZZ2276=ZZ2276+ZZ2276 -+006360 772320 ZZ2276=ZZ2276+ZZ2276 -+006360 764640 ZZ2276=ZZ2276+ZZ2276 -+006360 751500 ZZ2276=ZZ2276+ZZ2276 -+006360 723200 ZZ2276=ZZ2276+ZZ2276 -+006360 646400 ZZ2276=ZZ2276+ZZ2276 -+006360 515000 ZZ2276=ZZ2276+ZZ2276 -+006360 016272 0 8192 -ZZ1276 -+006361 515000 0 ZZ2276 -- mark 878, -2 /82 ceti -+006362 777772 ZZ2277=ZZ2277+ZZ2277 -+006362 777764 ZZ2277=ZZ2277+ZZ2277 -+006362 777750 ZZ2277=ZZ2277+ZZ2277 -+006362 777720 ZZ2277=ZZ2277+ZZ2277 -+006362 777640 ZZ2277=ZZ2277+ZZ2277 -+006362 777500 ZZ2277=ZZ2277+ZZ2277 -+006362 777200 ZZ2277=ZZ2277+ZZ2277 -+006362 776400 ZZ2277=ZZ2277+ZZ2277 -+006362 016222 0 8192 -ZZ1277 -+006363 776400 0 ZZ2277 -- mark 907, -340 /89 ceti -+006364 776526 ZZ2278=ZZ2278+ZZ2278 -+006364 775254 ZZ2278=ZZ2278+ZZ2278 -+006364 772530 ZZ2278=ZZ2278+ZZ2278 -+006364 765260 ZZ2278=ZZ2278+ZZ2278 -+006364 752540 ZZ2278=ZZ2278+ZZ2278 -+006364 725300 ZZ2278=ZZ2278+ZZ2278 -+006364 652600 ZZ2278=ZZ2278+ZZ2278 -+006364 525400 ZZ2278=ZZ2278+ZZ2278 -+006364 016165 0 8192 -ZZ1278 -+006365 525400 0 ZZ2278 -- mark 908, 221 /87 ceti -+006366 000672 ZZ2279=ZZ2279+ZZ2279 -+006366 001564 ZZ2279=ZZ2279+ZZ2279 -+006366 003350 ZZ2279=ZZ2279+ZZ2279 -+006366 006720 ZZ2279=ZZ2279+ZZ2279 -+006366 015640 ZZ2279=ZZ2279+ZZ2279 -+006366 033500 ZZ2279=ZZ2279+ZZ2279 -+006366 067200 ZZ2279=ZZ2279+ZZ2279 -+006366 156400 ZZ2279=ZZ2279+ZZ2279 -+006366 016164 0 8192 -ZZ1279 -+006367 156400 0 ZZ2279 -- mark 913, -432 / 1 erid -+006370 776236 ZZ2280=ZZ2280+ZZ2280 -+006370 774474 ZZ2280=ZZ2280+ZZ2280 -+006370 771170 ZZ2280=ZZ2280+ZZ2280 -+006370 762360 ZZ2280=ZZ2280+ZZ2280 -+006370 744740 ZZ2280=ZZ2280+ZZ2280 -+006370 711700 ZZ2280=ZZ2280+ZZ2280 -+006370 623600 ZZ2280=ZZ2280+ZZ2280 -+006370 447400 ZZ2280=ZZ2280+ZZ2280 -+006370 016157 0 8192 -ZZ1280 -+006371 447400 0 ZZ2280 -- mark 947, -487 / 2 erid -+006372 776060 ZZ2281=ZZ2281+ZZ2281 -+006372 774140 ZZ2281=ZZ2281+ZZ2281 -+006372 770300 ZZ2281=ZZ2281+ZZ2281 -+006372 760600 ZZ2281=ZZ2281+ZZ2281 -+006372 741400 ZZ2281=ZZ2281+ZZ2281 -+006372 703000 ZZ2281=ZZ2281+ZZ2281 -+006372 606000 ZZ2281=ZZ2281+ZZ2281 -+006372 414000 ZZ2281=ZZ2281+ZZ2281 -+006372 016115 0 8192 -ZZ1281 -+006373 414000 0 ZZ2281 -- mark 976, -212 / 3 erid -+006374 777126 ZZ2282=ZZ2282+ZZ2282 -+006374 776254 ZZ2282=ZZ2282+ZZ2282 -+006374 774530 ZZ2282=ZZ2282+ZZ2282 -+006374 771260 ZZ2282=ZZ2282+ZZ2282 -+006374 762540 ZZ2282=ZZ2282+ZZ2282 -+006374 745300 ZZ2282=ZZ2282+ZZ2282 -+006374 712600 ZZ2282=ZZ2282+ZZ2282 -+006374 625400 ZZ2282=ZZ2282+ZZ2282 -+006374 016060 0 8192 -ZZ1282 -+006375 625400 0 ZZ2282 -- mark 992, 194 /91 ceti -+006376 000604 ZZ2283=ZZ2283+ZZ2283 -+006376 001410 ZZ2283=ZZ2283+ZZ2283 -+006376 003020 ZZ2283=ZZ2283+ZZ2283 -+006376 006040 ZZ2283=ZZ2283+ZZ2283 -+006376 014100 ZZ2283=ZZ2283+ZZ2283 -+006376 030200 ZZ2283=ZZ2283+ZZ2283 -+006376 060400 ZZ2283=ZZ2283+ZZ2283 -+006376 141000 ZZ2283=ZZ2283+ZZ2283 -+006376 016040 0 8192 -ZZ1283 -+006377 141000 0 ZZ2283 -- mark 1058, 440 /57 arie -+006400 001560 ZZ2284=ZZ2284+ZZ2284 -+006400 003340 ZZ2284=ZZ2284+ZZ2284 -+006400 006700 ZZ2284=ZZ2284+ZZ2284 -+006400 015600 ZZ2284=ZZ2284+ZZ2284 -+006400 033400 ZZ2284=ZZ2284+ZZ2284 -+006400 067000 ZZ2284=ZZ2284+ZZ2284 -+006400 156000 ZZ2284=ZZ2284+ZZ2284 -+006400 334000 ZZ2284=ZZ2284+ZZ2284 -+006400 015736 0 8192 -ZZ1284 -+006401 334000 0 ZZ2284 -- mark 1076, 470 /58 arie -+006402 001654 ZZ2285=ZZ2285+ZZ2285 -+006402 003530 ZZ2285=ZZ2285+ZZ2285 -+006402 007260 ZZ2285=ZZ2285+ZZ2285 -+006402 016540 ZZ2285=ZZ2285+ZZ2285 -+006402 035300 ZZ2285=ZZ2285+ZZ2285 -+006402 072600 ZZ2285=ZZ2285+ZZ2285 -+006402 165400 ZZ2285=ZZ2285+ZZ2285 -+006402 353000 ZZ2285=ZZ2285+ZZ2285 -+006402 015714 0 8192 -ZZ1285 -+006403 353000 0 ZZ2285 -- mark 1087, -209 /13 erid -+006404 777134 ZZ2286=ZZ2286+ZZ2286 -+006404 776270 ZZ2286=ZZ2286+ZZ2286 -+006404 774560 ZZ2286=ZZ2286+ZZ2286 -+006404 771340 ZZ2286=ZZ2286+ZZ2286 -+006404 762700 ZZ2286=ZZ2286+ZZ2286 -+006404 745600 ZZ2286=ZZ2286+ZZ2286 -+006404 713400 ZZ2286=ZZ2286+ZZ2286 -+006404 627000 ZZ2286=ZZ2286+ZZ2286 -+006404 015701 0 8192 -ZZ1286 -+006405 627000 0 ZZ2286 -- mark 1104, 68 /96 ceti -+006406 000210 ZZ2287=ZZ2287+ZZ2287 -+006406 000420 ZZ2287=ZZ2287+ZZ2287 -+006406 001040 ZZ2287=ZZ2287+ZZ2287 -+006406 002100 ZZ2287=ZZ2287+ZZ2287 -+006406 004200 ZZ2287=ZZ2287+ZZ2287 -+006406 010400 ZZ2287=ZZ2287+ZZ2287 -+006406 021000 ZZ2287=ZZ2287+ZZ2287 -+006406 042000 ZZ2287=ZZ2287+ZZ2287 -+006406 015660 0 8192 -ZZ1287 -+006407 042000 0 ZZ2287 -- mark 1110, -503 /16 erid -+006410 776020 ZZ2288=ZZ2288+ZZ2288 -+006410 774040 ZZ2288=ZZ2288+ZZ2288 -+006410 770100 ZZ2288=ZZ2288+ZZ2288 -+006410 760200 ZZ2288=ZZ2288+ZZ2288 -+006410 740400 ZZ2288=ZZ2288+ZZ2288 -+006410 701000 ZZ2288=ZZ2288+ZZ2288 -+006410 602000 ZZ2288=ZZ2288+ZZ2288 -+006410 404000 ZZ2288=ZZ2288+ZZ2288 -+006410 015652 0 8192 -ZZ1288 -+006411 404000 0 ZZ2288 -- mark 1135, 198 / 1 taur -+006412 000614 ZZ2289=ZZ2289+ZZ2289 -+006412 001430 ZZ2289=ZZ2289+ZZ2289 -+006412 003060 ZZ2289=ZZ2289+ZZ2289 -+006412 006140 ZZ2289=ZZ2289+ZZ2289 -+006412 014300 ZZ2289=ZZ2289+ZZ2289 -+006412 030600 ZZ2289=ZZ2289+ZZ2289 -+006412 061400 ZZ2289=ZZ2289+ZZ2289 -+006412 143000 ZZ2289=ZZ2289+ZZ2289 -+006412 015621 0 8192 -ZZ1289 -+006413 143000 0 ZZ2289 -- mark 1148, 214 / 2 taur -+006414 000654 ZZ2290=ZZ2290+ZZ2290 -+006414 001530 ZZ2290=ZZ2290+ZZ2290 -+006414 003260 ZZ2290=ZZ2290+ZZ2290 -+006414 006540 ZZ2290=ZZ2290+ZZ2290 -+006414 015300 ZZ2290=ZZ2290+ZZ2290 -+006414 032600 ZZ2290=ZZ2290+ZZ2290 -+006414 065400 ZZ2290=ZZ2290+ZZ2290 -+006414 153000 ZZ2290=ZZ2290+ZZ2290 -+006414 015604 0 8192 -ZZ1290 -+006415 153000 0 ZZ2290 -- mark 1168, 287 / 5 taur -+006416 001076 ZZ2291=ZZ2291+ZZ2291 -+006416 002174 ZZ2291=ZZ2291+ZZ2291 -+006416 004370 ZZ2291=ZZ2291+ZZ2291 -+006416 010760 ZZ2291=ZZ2291+ZZ2291 -+006416 021740 ZZ2291=ZZ2291+ZZ2291 -+006416 043700 ZZ2291=ZZ2291+ZZ2291 -+006416 107600 ZZ2291=ZZ2291+ZZ2291 -+006416 217400 ZZ2291=ZZ2291+ZZ2291 -+006416 015560 0 8192 -ZZ1291 -+006417 217400 0 ZZ2291 -- mark 1170, -123 /17 erid -+006420 777410 ZZ2292=ZZ2292+ZZ2292 -+006420 777020 ZZ2292=ZZ2292+ZZ2292 -+006420 776040 ZZ2292=ZZ2292+ZZ2292 -+006420 774100 ZZ2292=ZZ2292+ZZ2292 -+006420 770200 ZZ2292=ZZ2292+ZZ2292 -+006420 760400 ZZ2292=ZZ2292+ZZ2292 -+006420 741000 ZZ2292=ZZ2292+ZZ2292 -+006420 702000 ZZ2292=ZZ2292+ZZ2292 -+006420 015556 0 8192 -ZZ1292 -+006421 702000 0 ZZ2292 -- mark 1185, -223 /18 erid -+006422 777100 ZZ2293=ZZ2293+ZZ2293 -+006422 776200 ZZ2293=ZZ2293+ZZ2293 -+006422 774400 ZZ2293=ZZ2293+ZZ2293 -+006422 771000 ZZ2293=ZZ2293+ZZ2293 -+006422 762000 ZZ2293=ZZ2293+ZZ2293 -+006422 744000 ZZ2293=ZZ2293+ZZ2293 -+006422 710000 ZZ2293=ZZ2293+ZZ2293 -+006422 620000 ZZ2293=ZZ2293+ZZ2293 -+006422 015537 0 8192 -ZZ1293 -+006423 620000 0 ZZ2293 -- mark 1191, -500 /19 erid -+006424 776026 ZZ2294=ZZ2294+ZZ2294 -+006424 774054 ZZ2294=ZZ2294+ZZ2294 -+006424 770130 ZZ2294=ZZ2294+ZZ2294 -+006424 760260 ZZ2294=ZZ2294+ZZ2294 -+006424 740540 ZZ2294=ZZ2294+ZZ2294 -+006424 701300 ZZ2294=ZZ2294+ZZ2294 -+006424 602600 ZZ2294=ZZ2294+ZZ2294 -+006424 405400 ZZ2294=ZZ2294+ZZ2294 -+006424 015531 0 8192 -ZZ1294 -+006425 405400 0 ZZ2294 -- mark 1205, 2 /10 taur -+006426 000004 ZZ2295=ZZ2295+ZZ2295 -+006426 000010 ZZ2295=ZZ2295+ZZ2295 -+006426 000020 ZZ2295=ZZ2295+ZZ2295 -+006426 000040 ZZ2295=ZZ2295+ZZ2295 -+006426 000100 ZZ2295=ZZ2295+ZZ2295 -+006426 000200 ZZ2295=ZZ2295+ZZ2295 -+006426 000400 ZZ2295=ZZ2295+ZZ2295 -+006426 001000 ZZ2295=ZZ2295+ZZ2295 -+006426 015513 0 8192 -ZZ1295 -+006427 001000 0 ZZ2295 -- mark 1260, -283 /26 erid -+006430 776710 ZZ2296=ZZ2296+ZZ2296 -+006430 775620 ZZ2296=ZZ2296+ZZ2296 -+006430 773440 ZZ2296=ZZ2296+ZZ2296 -+006430 767100 ZZ2296=ZZ2296+ZZ2296 -+006430 756200 ZZ2296=ZZ2296+ZZ2296 -+006430 734400 ZZ2296=ZZ2296+ZZ2296 -+006430 671000 ZZ2296=ZZ2296+ZZ2296 -+006430 562000 ZZ2296=ZZ2296+ZZ2296 -+006430 015424 0 8192 -ZZ1296 -+006431 562000 0 ZZ2296 -- mark 1304, -74 /32 erid -+006432 777552 ZZ2297=ZZ2297+ZZ2297 -+006432 777324 ZZ2297=ZZ2297+ZZ2297 -+006432 776650 ZZ2297=ZZ2297+ZZ2297 -+006432 775520 ZZ2297=ZZ2297+ZZ2297 -+006432 773240 ZZ2297=ZZ2297+ZZ2297 -+006432 766500 ZZ2297=ZZ2297+ZZ2297 -+006432 755200 ZZ2297=ZZ2297+ZZ2297 -+006432 732400 ZZ2297=ZZ2297+ZZ2297 -+006432 015350 0 8192 -ZZ1297 -+006433 732400 0 ZZ2297 -- mark 1338, 278 /35 taur -+006434 001054 ZZ2298=ZZ2298+ZZ2298 -+006434 002130 ZZ2298=ZZ2298+ZZ2298 -+006434 004260 ZZ2298=ZZ2298+ZZ2298 -+006434 010540 ZZ2298=ZZ2298+ZZ2298 -+006434 021300 ZZ2298=ZZ2298+ZZ2298 -+006434 042600 ZZ2298=ZZ2298+ZZ2298 -+006434 105400 ZZ2298=ZZ2298+ZZ2298 -+006434 213000 ZZ2298=ZZ2298+ZZ2298 -+006434 015306 0 8192 -ZZ1298 -+006435 213000 0 ZZ2298 -- mark 1353, 130 /38 taur -+006436 000404 ZZ2299=ZZ2299+ZZ2299 -+006436 001010 ZZ2299=ZZ2299+ZZ2299 -+006436 002020 ZZ2299=ZZ2299+ZZ2299 -+006436 004040 ZZ2299=ZZ2299+ZZ2299 -+006436 010100 ZZ2299=ZZ2299+ZZ2299 -+006436 020200 ZZ2299=ZZ2299+ZZ2299 -+006436 040400 ZZ2299=ZZ2299+ZZ2299 -+006436 101000 ZZ2299=ZZ2299+ZZ2299 -+006436 015267 0 8192 -ZZ1299 -+006437 101000 0 ZZ2299 -- mark 1358, 497 /37 taur -+006440 001742 ZZ2300=ZZ2300+ZZ2300 -+006440 003704 ZZ2300=ZZ2300+ZZ2300 -+006440 007610 ZZ2300=ZZ2300+ZZ2300 -+006440 017420 ZZ2300=ZZ2300+ZZ2300 -+006440 037040 ZZ2300=ZZ2300+ZZ2300 -+006440 076100 ZZ2300=ZZ2300+ZZ2300 -+006440 174200 ZZ2300=ZZ2300+ZZ2300 -+006440 370400 ZZ2300=ZZ2300+ZZ2300 -+006440 015262 0 8192 -ZZ1300 -+006441 370400 0 ZZ2300 -- mark 1405, -162 /38 erid -+006442 777272 ZZ2301=ZZ2301+ZZ2301 -+006442 776564 ZZ2301=ZZ2301+ZZ2301 -+006442 775350 ZZ2301=ZZ2301+ZZ2301 -+006442 772720 ZZ2301=ZZ2301+ZZ2301 -+006442 765640 ZZ2301=ZZ2301+ZZ2301 -+006442 753500 ZZ2301=ZZ2301+ZZ2301 -+006442 727200 ZZ2301=ZZ2301+ZZ2301 -+006442 656400 ZZ2301=ZZ2301+ZZ2301 -+006442 015203 0 8192 -ZZ1301 -+006443 656400 0 ZZ2301 -- mark 1414, 205 /47 taur -+006444 000632 ZZ2302=ZZ2302+ZZ2302 -+006444 001464 ZZ2302=ZZ2302+ZZ2302 -+006444 003150 ZZ2302=ZZ2302+ZZ2302 -+006444 006320 ZZ2302=ZZ2302+ZZ2302 -+006444 014640 ZZ2302=ZZ2302+ZZ2302 -+006444 031500 ZZ2302=ZZ2302+ZZ2302 -+006444 063200 ZZ2302=ZZ2302+ZZ2302 -+006444 146400 ZZ2302=ZZ2302+ZZ2302 -+006444 015172 0 8192 -ZZ1302 -+006445 146400 0 ZZ2302 -- mark 1423, 197 /49 taur -+006446 000612 ZZ2303=ZZ2303+ZZ2303 -+006446 001424 ZZ2303=ZZ2303+ZZ2303 -+006446 003050 ZZ2303=ZZ2303+ZZ2303 -+006446 006120 ZZ2303=ZZ2303+ZZ2303 -+006446 014240 ZZ2303=ZZ2303+ZZ2303 -+006446 030500 ZZ2303=ZZ2303+ZZ2303 -+006446 061200 ZZ2303=ZZ2303+ZZ2303 -+006446 142400 ZZ2303=ZZ2303+ZZ2303 -+006446 015161 0 8192 -ZZ1303 -+006447 142400 0 ZZ2303 -- mark 1426, -178 /40 erid -+006450 777232 ZZ2304=ZZ2304+ZZ2304 -+006450 776464 ZZ2304=ZZ2304+ZZ2304 -+006450 775150 ZZ2304=ZZ2304+ZZ2304 -+006450 772320 ZZ2304=ZZ2304+ZZ2304 -+006450 764640 ZZ2304=ZZ2304+ZZ2304 -+006450 751500 ZZ2304=ZZ2304+ZZ2304 -+006450 723200 ZZ2304=ZZ2304+ZZ2304 -+006450 646400 ZZ2304=ZZ2304+ZZ2304 -+006450 015156 0 8192 -ZZ1304 -+006451 646400 0 ZZ2304 -- mark 1430, 463 /50 taur -+006452 001636 ZZ2305=ZZ2305+ZZ2305 -+006452 003474 ZZ2305=ZZ2305+ZZ2305 -+006452 007170 ZZ2305=ZZ2305+ZZ2305 -+006452 016360 ZZ2305=ZZ2305+ZZ2305 -+006452 034740 ZZ2305=ZZ2305+ZZ2305 -+006452 071700 ZZ2305=ZZ2305+ZZ2305 -+006452 163600 ZZ2305=ZZ2305+ZZ2305 -+006452 347400 ZZ2305=ZZ2305+ZZ2305 -+006452 015152 0 8192 -ZZ1305 -+006453 347400 0 ZZ2305 -- mark 1446, 350 /54 taur -+006454 001274 ZZ2306=ZZ2306+ZZ2306 -+006454 002570 ZZ2306=ZZ2306+ZZ2306 -+006454 005360 ZZ2306=ZZ2306+ZZ2306 -+006454 012740 ZZ2306=ZZ2306+ZZ2306 -+006454 025700 ZZ2306=ZZ2306+ZZ2306 -+006454 053600 ZZ2306=ZZ2306+ZZ2306 -+006454 127400 ZZ2306=ZZ2306+ZZ2306 -+006454 257000 ZZ2306=ZZ2306+ZZ2306 -+006454 015132 0 8192 -ZZ1306 -+006455 257000 0 ZZ2306 -- mark 1463, 394 /61 taur -+006456 001424 ZZ2307=ZZ2307+ZZ2307 -+006456 003050 ZZ2307=ZZ2307+ZZ2307 -+006456 006120 ZZ2307=ZZ2307+ZZ2307 -+006456 014240 ZZ2307=ZZ2307+ZZ2307 -+006456 030500 ZZ2307=ZZ2307+ZZ2307 -+006456 061200 ZZ2307=ZZ2307+ZZ2307 -+006456 142400 ZZ2307=ZZ2307+ZZ2307 -+006456 305000 ZZ2307=ZZ2307+ZZ2307 -+006456 015111 0 8192 -ZZ1307 -+006457 305000 0 ZZ2307 -- mark 1470, 392 /64 taur -+006460 001420 ZZ2308=ZZ2308+ZZ2308 -+006460 003040 ZZ2308=ZZ2308+ZZ2308 -+006460 006100 ZZ2308=ZZ2308+ZZ2308 -+006460 014200 ZZ2308=ZZ2308+ZZ2308 -+006460 030400 ZZ2308=ZZ2308+ZZ2308 -+006460 061000 ZZ2308=ZZ2308+ZZ2308 -+006460 142000 ZZ2308=ZZ2308+ZZ2308 -+006460 304000 ZZ2308=ZZ2308+ZZ2308 -+006460 015102 0 8192 -ZZ1308 -+006461 304000 0 ZZ2308 -- mark 1476, 502 /65 taur -+006462 001754 ZZ2309=ZZ2309+ZZ2309 -+006462 003730 ZZ2309=ZZ2309+ZZ2309 -+006462 007660 ZZ2309=ZZ2309+ZZ2309 -+006462 017540 ZZ2309=ZZ2309+ZZ2309 -+006462 037300 ZZ2309=ZZ2309+ZZ2309 -+006462 076600 ZZ2309=ZZ2309+ZZ2309 -+006462 175400 ZZ2309=ZZ2309+ZZ2309 -+006462 373000 ZZ2309=ZZ2309+ZZ2309 -+006462 015074 0 8192 -ZZ1309 -+006463 373000 0 ZZ2309 -- mark 1477, 403 /68 taur -+006464 001446 ZZ2310=ZZ2310+ZZ2310 -+006464 003114 ZZ2310=ZZ2310+ZZ2310 -+006464 006230 ZZ2310=ZZ2310+ZZ2310 -+006464 014460 ZZ2310=ZZ2310+ZZ2310 -+006464 031140 ZZ2310=ZZ2310+ZZ2310 -+006464 062300 ZZ2310=ZZ2310+ZZ2310 -+006464 144600 ZZ2310=ZZ2310+ZZ2310 -+006464 311400 ZZ2310=ZZ2310+ZZ2310 -+006464 015073 0 8192 -ZZ1310 -+006465 311400 0 ZZ2310 -- mark 1483, 350 /71 taur -+006466 001274 ZZ2311=ZZ2311+ZZ2311 -+006466 002570 ZZ2311=ZZ2311+ZZ2311 -+006466 005360 ZZ2311=ZZ2311+ZZ2311 -+006466 012740 ZZ2311=ZZ2311+ZZ2311 -+006466 025700 ZZ2311=ZZ2311+ZZ2311 -+006466 053600 ZZ2311=ZZ2311+ZZ2311 -+006466 127400 ZZ2311=ZZ2311+ZZ2311 -+006466 257000 ZZ2311=ZZ2311+ZZ2311 -+006466 015065 0 8192 -ZZ1311 -+006467 257000 0 ZZ2311 -- mark 1485, 330 /73 taur -+006470 001224 ZZ2312=ZZ2312+ZZ2312 -+006470 002450 ZZ2312=ZZ2312+ZZ2312 -+006470 005120 ZZ2312=ZZ2312+ZZ2312 -+006470 012240 ZZ2312=ZZ2312+ZZ2312 -+006470 024500 ZZ2312=ZZ2312+ZZ2312 -+006470 051200 ZZ2312=ZZ2312+ZZ2312 -+006470 122400 ZZ2312=ZZ2312+ZZ2312 -+006470 245000 ZZ2312=ZZ2312+ZZ2312 -+006470 015063 0 8192 -ZZ1312 -+006471 245000 0 ZZ2312 -- mark 1495, 358 /77 taur -+006472 001314 ZZ2313=ZZ2313+ZZ2313 -+006472 002630 ZZ2313=ZZ2313+ZZ2313 -+006472 005460 ZZ2313=ZZ2313+ZZ2313 -+006472 013140 ZZ2313=ZZ2313+ZZ2313 -+006472 026300 ZZ2313=ZZ2313+ZZ2313 -+006472 054600 ZZ2313=ZZ2313+ZZ2313 -+006472 131400 ZZ2313=ZZ2313+ZZ2313 -+006472 263000 ZZ2313=ZZ2313+ZZ2313 -+006472 015051 0 8192 -ZZ1313 -+006473 263000 0 ZZ2313 -- mark 1507, 364 / -+006474 001330 ZZ2314=ZZ2314+ZZ2314 -+006474 002660 ZZ2314=ZZ2314+ZZ2314 -+006474 005540 ZZ2314=ZZ2314+ZZ2314 -+006474 013300 ZZ2314=ZZ2314+ZZ2314 -+006474 026600 ZZ2314=ZZ2314+ZZ2314 -+006474 055400 ZZ2314=ZZ2314+ZZ2314 -+006474 133000 ZZ2314=ZZ2314+ZZ2314 -+006474 266000 ZZ2314=ZZ2314+ZZ2314 -+006474 015035 0 8192 -ZZ1314 -+006475 266000 0 ZZ2314 -- mark 1518, -6 /45 erid -+006476 777762 ZZ2315=ZZ2315+ZZ2315 -+006476 777744 ZZ2315=ZZ2315+ZZ2315 -+006476 777710 ZZ2315=ZZ2315+ZZ2315 -+006476 777620 ZZ2315=ZZ2315+ZZ2315 -+006476 777440 ZZ2315=ZZ2315+ZZ2315 -+006476 777100 ZZ2315=ZZ2315+ZZ2315 -+006476 776200 ZZ2315=ZZ2315+ZZ2315 -+006476 774400 ZZ2315=ZZ2315+ZZ2315 -+006476 015022 0 8192 -ZZ1315 -+006477 774400 0 ZZ2315 -- mark 1526, 333 /86 taur -+006500 001232 ZZ2316=ZZ2316+ZZ2316 -+006500 002464 ZZ2316=ZZ2316+ZZ2316 -+006500 005150 ZZ2316=ZZ2316+ZZ2316 -+006500 012320 ZZ2316=ZZ2316+ZZ2316 -+006500 024640 ZZ2316=ZZ2316+ZZ2316 -+006500 051500 ZZ2316=ZZ2316+ZZ2316 -+006500 123200 ZZ2316=ZZ2316+ZZ2316 -+006500 246400 ZZ2316=ZZ2316+ZZ2316 -+006500 015012 0 8192 -ZZ1316 -+006501 246400 0 ZZ2316 -- mark 1537, 226 /88 taur -+006502 000704 ZZ2317=ZZ2317+ZZ2317 -+006502 001610 ZZ2317=ZZ2317+ZZ2317 -+006502 003420 ZZ2317=ZZ2317+ZZ2317 -+006502 007040 ZZ2317=ZZ2317+ZZ2317 -+006502 016100 ZZ2317=ZZ2317+ZZ2317 -+006502 034200 ZZ2317=ZZ2317+ZZ2317 -+006502 070400 ZZ2317=ZZ2317+ZZ2317 -+006502 161000 ZZ2317=ZZ2317+ZZ2317 -+006502 014777 0 8192 -ZZ1317 -+006503 161000 0 ZZ2317 -- mark 1544, -81 /48 erid -+006504 777534 ZZ2318=ZZ2318+ZZ2318 -+006504 777270 ZZ2318=ZZ2318+ZZ2318 -+006504 776560 ZZ2318=ZZ2318+ZZ2318 -+006504 775340 ZZ2318=ZZ2318+ZZ2318 -+006504 772700 ZZ2318=ZZ2318+ZZ2318 -+006504 765600 ZZ2318=ZZ2318+ZZ2318 -+006504 753400 ZZ2318=ZZ2318+ZZ2318 -+006504 727000 ZZ2318=ZZ2318+ZZ2318 -+006504 014770 0 8192 -ZZ1318 -+006505 727000 0 ZZ2318 -- mark 1551, 280 /90 taur -+006506 001060 ZZ2319=ZZ2319+ZZ2319 -+006506 002140 ZZ2319=ZZ2319+ZZ2319 -+006506 004300 ZZ2319=ZZ2319+ZZ2319 -+006506 010600 ZZ2319=ZZ2319+ZZ2319 -+006506 021400 ZZ2319=ZZ2319+ZZ2319 -+006506 043000 ZZ2319=ZZ2319+ZZ2319 -+006506 106000 ZZ2319=ZZ2319+ZZ2319 -+006506 214000 ZZ2319=ZZ2319+ZZ2319 -+006506 014761 0 8192 -ZZ1319 -+006507 214000 0 ZZ2319 -- mark 1556, 358 /92 taur -+006510 001314 ZZ2320=ZZ2320+ZZ2320 -+006510 002630 ZZ2320=ZZ2320+ZZ2320 -+006510 005460 ZZ2320=ZZ2320+ZZ2320 -+006510 013140 ZZ2320=ZZ2320+ZZ2320 -+006510 026300 ZZ2320=ZZ2320+ZZ2320 -+006510 054600 ZZ2320=ZZ2320+ZZ2320 -+006510 131400 ZZ2320=ZZ2320+ZZ2320 -+006510 263000 ZZ2320=ZZ2320+ZZ2320 -+006510 014754 0 8192 -ZZ1320 -+006511 263000 0 ZZ2320 -- mark 1557, -330 /53 erid -+006512 776552 ZZ2321=ZZ2321+ZZ2321 -+006512 775324 ZZ2321=ZZ2321+ZZ2321 -+006512 772650 ZZ2321=ZZ2321+ZZ2321 -+006512 765520 ZZ2321=ZZ2321+ZZ2321 -+006512 753240 ZZ2321=ZZ2321+ZZ2321 -+006512 726500 ZZ2321=ZZ2321+ZZ2321 -+006512 655200 ZZ2321=ZZ2321+ZZ2321 -+006512 532400 ZZ2321=ZZ2321+ZZ2321 -+006512 014753 0 8192 -ZZ1321 -+006513 532400 0 ZZ2321 -- mark 1571, -452 /54 erid -+006514 776166 ZZ2322=ZZ2322+ZZ2322 -+006514 774354 ZZ2322=ZZ2322+ZZ2322 -+006514 770730 ZZ2322=ZZ2322+ZZ2322 -+006514 761660 ZZ2322=ZZ2322+ZZ2322 -+006514 743540 ZZ2322=ZZ2322+ZZ2322 -+006514 707300 ZZ2322=ZZ2322+ZZ2322 -+006514 616600 ZZ2322=ZZ2322+ZZ2322 -+006514 435400 ZZ2322=ZZ2322+ZZ2322 -+006514 014735 0 8192 -ZZ1322 -+006515 435400 0 ZZ2322 -- mark 1596, -78 /57 erid -+006516 777542 ZZ2323=ZZ2323+ZZ2323 -+006516 777304 ZZ2323=ZZ2323+ZZ2323 -+006516 776610 ZZ2323=ZZ2323+ZZ2323 -+006516 775420 ZZ2323=ZZ2323+ZZ2323 -+006516 773040 ZZ2323=ZZ2323+ZZ2323 -+006516 766100 ZZ2323=ZZ2323+ZZ2323 -+006516 754200 ZZ2323=ZZ2323+ZZ2323 -+006516 730400 ZZ2323=ZZ2323+ZZ2323 -+006516 014704 0 8192 -ZZ1323 -+006517 730400 0 ZZ2323 -- mark 1622, 199 / 2 orio -+006520 000616 ZZ2324=ZZ2324+ZZ2324 -+006520 001434 ZZ2324=ZZ2324+ZZ2324 -+006520 003070 ZZ2324=ZZ2324+ZZ2324 -+006520 006160 ZZ2324=ZZ2324+ZZ2324 -+006520 014340 ZZ2324=ZZ2324+ZZ2324 -+006520 030700 ZZ2324=ZZ2324+ZZ2324 -+006520 061600 ZZ2324=ZZ2324+ZZ2324 -+006520 143400 ZZ2324=ZZ2324+ZZ2324 -+006520 014652 0 8192 -ZZ1324 -+006521 143400 0 ZZ2324 -- mark 1626, 124 / 3 orio -+006522 000370 ZZ2325=ZZ2325+ZZ2325 -+006522 000760 ZZ2325=ZZ2325+ZZ2325 -+006522 001740 ZZ2325=ZZ2325+ZZ2325 -+006522 003700 ZZ2325=ZZ2325+ZZ2325 -+006522 007600 ZZ2325=ZZ2325+ZZ2325 -+006522 017400 ZZ2325=ZZ2325+ZZ2325 -+006522 037000 ZZ2325=ZZ2325+ZZ2325 -+006522 076000 ZZ2325=ZZ2325+ZZ2325 -+006522 014646 0 8192 -ZZ1325 -+006523 076000 0 ZZ2325 -- mark 1638, -128 /61 erid -+006524 777376 ZZ2326=ZZ2326+ZZ2326 -+006524 776774 ZZ2326=ZZ2326+ZZ2326 -+006524 775770 ZZ2326=ZZ2326+ZZ2326 -+006524 773760 ZZ2326=ZZ2326+ZZ2326 -+006524 767740 ZZ2326=ZZ2326+ZZ2326 -+006524 757700 ZZ2326=ZZ2326+ZZ2326 -+006524 737600 ZZ2326=ZZ2326+ZZ2326 -+006524 677400 ZZ2326=ZZ2326+ZZ2326 -+006524 014632 0 8192 -ZZ1326 -+006525 677400 0 ZZ2326 -- mark 1646, 228 / 7 orio -+006526 000710 ZZ2327=ZZ2327+ZZ2327 -+006526 001620 ZZ2327=ZZ2327+ZZ2327 -+006526 003440 ZZ2327=ZZ2327+ZZ2327 -+006526 007100 ZZ2327=ZZ2327+ZZ2327 -+006526 016200 ZZ2327=ZZ2327+ZZ2327 -+006526 034400 ZZ2327=ZZ2327+ZZ2327 -+006526 071000 ZZ2327=ZZ2327+ZZ2327 -+006526 162000 ZZ2327=ZZ2327+ZZ2327 -+006526 014622 0 8192 -ZZ1327 -+006527 162000 0 ZZ2327 -- mark 1654, 304 / 9 orio -+006530 001140 ZZ2328=ZZ2328+ZZ2328 -+006530 002300 ZZ2328=ZZ2328+ZZ2328 -+006530 004600 ZZ2328=ZZ2328+ZZ2328 -+006530 011400 ZZ2328=ZZ2328+ZZ2328 -+006530 023000 ZZ2328=ZZ2328+ZZ2328 -+006530 046000 ZZ2328=ZZ2328+ZZ2328 -+006530 114000 ZZ2328=ZZ2328+ZZ2328 -+006530 230000 ZZ2328=ZZ2328+ZZ2328 -+006530 014612 0 8192 -ZZ1328 -+006531 230000 0 ZZ2328 -- mark 1669, 36 /10 orio -+006532 000110 ZZ2329=ZZ2329+ZZ2329 -+006532 000220 ZZ2329=ZZ2329+ZZ2329 -+006532 000440 ZZ2329=ZZ2329+ZZ2329 -+006532 001100 ZZ2329=ZZ2329+ZZ2329 -+006532 002200 ZZ2329=ZZ2329+ZZ2329 -+006532 004400 ZZ2329=ZZ2329+ZZ2329 -+006532 011000 ZZ2329=ZZ2329+ZZ2329 -+006532 022000 ZZ2329=ZZ2329+ZZ2329 -+006532 014573 0 8192 -ZZ1329 -+006533 022000 0 ZZ2329 -- mark 1680, -289 /64 erid -+006534 776674 ZZ2330=ZZ2330+ZZ2330 -+006534 775570 ZZ2330=ZZ2330+ZZ2330 -+006534 773360 ZZ2330=ZZ2330+ZZ2330 -+006534 766740 ZZ2330=ZZ2330+ZZ2330 -+006534 755700 ZZ2330=ZZ2330+ZZ2330 -+006534 733600 ZZ2330=ZZ2330+ZZ2330 -+006534 667400 ZZ2330=ZZ2330+ZZ2330 -+006534 557000 ZZ2330=ZZ2330+ZZ2330 -+006534 014560 0 8192 -ZZ1330 -+006535 557000 0 ZZ2330 -- mark 1687, -167 /65 erid -+006536 777260 ZZ2331=ZZ2331+ZZ2331 -+006536 776540 ZZ2331=ZZ2331+ZZ2331 -+006536 775300 ZZ2331=ZZ2331+ZZ2331 -+006536 772600 ZZ2331=ZZ2331+ZZ2331 -+006536 765400 ZZ2331=ZZ2331+ZZ2331 -+006536 753000 ZZ2331=ZZ2331+ZZ2331 -+006536 726000 ZZ2331=ZZ2331+ZZ2331 -+006536 654000 ZZ2331=ZZ2331+ZZ2331 -+006536 014551 0 8192 -ZZ1331 -+006537 654000 0 ZZ2331 -- mark 1690, -460 / -+006540 776146 ZZ2332=ZZ2332+ZZ2332 -+006540 774314 ZZ2332=ZZ2332+ZZ2332 -+006540 770630 ZZ2332=ZZ2332+ZZ2332 -+006540 761460 ZZ2332=ZZ2332+ZZ2332 -+006540 743140 ZZ2332=ZZ2332+ZZ2332 -+006540 706300 ZZ2332=ZZ2332+ZZ2332 -+006540 614600 ZZ2332=ZZ2332+ZZ2332 -+006540 431400 ZZ2332=ZZ2332+ZZ2332 -+006540 014546 0 8192 -ZZ1332 -+006541 431400 0 ZZ2332 -- mark 1690, 488 /102 taur -+006542 001720 ZZ2333=ZZ2333+ZZ2333 -+006542 003640 ZZ2333=ZZ2333+ZZ2333 -+006542 007500 ZZ2333=ZZ2333+ZZ2333 -+006542 017200 ZZ2333=ZZ2333+ZZ2333 -+006542 036400 ZZ2333=ZZ2333+ZZ2333 -+006542 075000 ZZ2333=ZZ2333+ZZ2333 -+006542 172000 ZZ2333=ZZ2333+ZZ2333 -+006542 364000 ZZ2333=ZZ2333+ZZ2333 -+006542 014546 0 8192 -ZZ1333 -+006543 364000 0 ZZ2333 -- mark 1700, 347 /11 orio -+006544 001266 ZZ2334=ZZ2334+ZZ2334 -+006544 002554 ZZ2334=ZZ2334+ZZ2334 -+006544 005330 ZZ2334=ZZ2334+ZZ2334 -+006544 012660 ZZ2334=ZZ2334+ZZ2334 -+006544 025540 ZZ2334=ZZ2334+ZZ2334 -+006544 053300 ZZ2334=ZZ2334+ZZ2334 -+006544 126600 ZZ2334=ZZ2334+ZZ2334 -+006544 255400 ZZ2334=ZZ2334+ZZ2334 -+006544 014534 0 8192 -ZZ1334 -+006545 255400 0 ZZ2334 -- mark 1729, 352 /15 orio -+006546 001300 ZZ2335=ZZ2335+ZZ2335 -+006546 002600 ZZ2335=ZZ2335+ZZ2335 -+006546 005400 ZZ2335=ZZ2335+ZZ2335 -+006546 013000 ZZ2335=ZZ2335+ZZ2335 -+006546 026000 ZZ2335=ZZ2335+ZZ2335 -+006546 054000 ZZ2335=ZZ2335+ZZ2335 -+006546 130000 ZZ2335=ZZ2335+ZZ2335 -+006546 260000 ZZ2335=ZZ2335+ZZ2335 -+006546 014477 0 8192 -ZZ1335 -+006547 260000 0 ZZ2335 -- mark 1732, -202 /69 erid -+006550 777152 ZZ2336=ZZ2336+ZZ2336 -+006550 776324 ZZ2336=ZZ2336+ZZ2336 -+006550 774650 ZZ2336=ZZ2336+ZZ2336 -+006550 771520 ZZ2336=ZZ2336+ZZ2336 -+006550 763240 ZZ2336=ZZ2336+ZZ2336 -+006550 746500 ZZ2336=ZZ2336+ZZ2336 -+006550 715200 ZZ2336=ZZ2336+ZZ2336 -+006550 632400 ZZ2336=ZZ2336+ZZ2336 -+006550 014474 0 8192 -ZZ1336 -+006551 632400 0 ZZ2336 -- mark 1750, -273 / 3 leps -+006552 776734 ZZ2337=ZZ2337+ZZ2337 -+006552 775670 ZZ2337=ZZ2337+ZZ2337 -+006552 773560 ZZ2337=ZZ2337+ZZ2337 -+006552 767340 ZZ2337=ZZ2337+ZZ2337 -+006552 756700 ZZ2337=ZZ2337+ZZ2337 -+006552 735600 ZZ2337=ZZ2337+ZZ2337 -+006552 673400 ZZ2337=ZZ2337+ZZ2337 -+006552 567000 ZZ2337=ZZ2337+ZZ2337 -+006552 014452 0 8192 -ZZ1337 -+006553 567000 0 ZZ2337 -- mark 1753, 63 /17 orio -+006554 000176 ZZ2338=ZZ2338+ZZ2338 -+006554 000374 ZZ2338=ZZ2338+ZZ2338 -+006554 000770 ZZ2338=ZZ2338+ZZ2338 -+006554 001760 ZZ2338=ZZ2338+ZZ2338 -+006554 003740 ZZ2338=ZZ2338+ZZ2338 -+006554 007700 ZZ2338=ZZ2338+ZZ2338 -+006554 017600 ZZ2338=ZZ2338+ZZ2338 -+006554 037400 ZZ2338=ZZ2338+ZZ2338 -+006554 014447 0 8192 -ZZ1338 -+006555 037400 0 ZZ2338 -- mark 1756, -297 / 4 leps -+006556 776654 ZZ2339=ZZ2339+ZZ2339 -+006556 775530 ZZ2339=ZZ2339+ZZ2339 -+006556 773260 ZZ2339=ZZ2339+ZZ2339 -+006556 766540 ZZ2339=ZZ2339+ZZ2339 -+006556 755300 ZZ2339=ZZ2339+ZZ2339 -+006556 732600 ZZ2339=ZZ2339+ZZ2339 -+006556 665400 ZZ2339=ZZ2339+ZZ2339 -+006556 553000 ZZ2339=ZZ2339+ZZ2339 -+006556 014444 0 8192 -ZZ1339 -+006557 553000 0 ZZ2339 -- mark 1792, -302 / 6 leps -+006560 776642 ZZ2340=ZZ2340+ZZ2340 -+006560 775504 ZZ2340=ZZ2340+ZZ2340 -+006560 773210 ZZ2340=ZZ2340+ZZ2340 -+006560 766420 ZZ2340=ZZ2340+ZZ2340 -+006560 755040 ZZ2340=ZZ2340+ZZ2340 -+006560 732100 ZZ2340=ZZ2340+ZZ2340 -+006560 664200 ZZ2340=ZZ2340+ZZ2340 -+006560 550400 ZZ2340=ZZ2340+ZZ2340 -+006560 014400 0 8192 -ZZ1340 -+006561 550400 0 ZZ2340 -- mark 1799, -486 / -+006562 776062 ZZ2341=ZZ2341+ZZ2341 -+006562 774144 ZZ2341=ZZ2341+ZZ2341 -+006562 770310 ZZ2341=ZZ2341+ZZ2341 -+006562 760620 ZZ2341=ZZ2341+ZZ2341 -+006562 741440 ZZ2341=ZZ2341+ZZ2341 -+006562 703100 ZZ2341=ZZ2341+ZZ2341 -+006562 606200 ZZ2341=ZZ2341+ZZ2341 -+006562 414400 ZZ2341=ZZ2341+ZZ2341 -+006562 014371 0 8192 -ZZ1341 -+006563 414400 0 ZZ2341 -- mark 1801, -11 /22 orio -+006564 777750 ZZ2342=ZZ2342+ZZ2342 -+006564 777720 ZZ2342=ZZ2342+ZZ2342 -+006564 777640 ZZ2342=ZZ2342+ZZ2342 -+006564 777500 ZZ2342=ZZ2342+ZZ2342 -+006564 777200 ZZ2342=ZZ2342+ZZ2342 -+006564 776400 ZZ2342=ZZ2342+ZZ2342 -+006564 775000 ZZ2342=ZZ2342+ZZ2342 -+006564 772000 ZZ2342=ZZ2342+ZZ2342 -+006564 014367 0 8192 -ZZ1342 -+006565 772000 0 ZZ2342 -- mark 1807, 79 /23 orio -+006566 000236 ZZ2343=ZZ2343+ZZ2343 -+006566 000474 ZZ2343=ZZ2343+ZZ2343 -+006566 001170 ZZ2343=ZZ2343+ZZ2343 -+006566 002360 ZZ2343=ZZ2343+ZZ2343 -+006566 004740 ZZ2343=ZZ2343+ZZ2343 -+006566 011700 ZZ2343=ZZ2343+ZZ2343 -+006566 023600 ZZ2343=ZZ2343+ZZ2343 -+006566 047400 ZZ2343=ZZ2343+ZZ2343 -+006566 014361 0 8192 -ZZ1343 -+006567 047400 0 ZZ2343 -- mark 1816, -180 /29 orio -+006570 777226 ZZ2344=ZZ2344+ZZ2344 -+006570 776454 ZZ2344=ZZ2344+ZZ2344 -+006570 775130 ZZ2344=ZZ2344+ZZ2344 -+006570 772260 ZZ2344=ZZ2344+ZZ2344 -+006570 764540 ZZ2344=ZZ2344+ZZ2344 -+006570 751300 ZZ2344=ZZ2344+ZZ2344 -+006570 722600 ZZ2344=ZZ2344+ZZ2344 -+006570 645400 ZZ2344=ZZ2344+ZZ2344 -+006570 014350 0 8192 -ZZ1344 -+006571 645400 0 ZZ2344 -- mark 1818, 40 /25 orio -+006572 000120 ZZ2345=ZZ2345+ZZ2345 -+006572 000240 ZZ2345=ZZ2345+ZZ2345 -+006572 000500 ZZ2345=ZZ2345+ZZ2345 -+006572 001200 ZZ2345=ZZ2345+ZZ2345 -+006572 002400 ZZ2345=ZZ2345+ZZ2345 -+006572 005000 ZZ2345=ZZ2345+ZZ2345 -+006572 012000 ZZ2345=ZZ2345+ZZ2345 -+006572 024000 ZZ2345=ZZ2345+ZZ2345 -+006572 014346 0 8192 -ZZ1345 -+006573 024000 0 ZZ2345 -- mark 1830, 497 /114 taur -+006574 001742 ZZ2346=ZZ2346+ZZ2346 -+006574 003704 ZZ2346=ZZ2346+ZZ2346 -+006574 007610 ZZ2346=ZZ2346+ZZ2346 -+006574 017420 ZZ2346=ZZ2346+ZZ2346 -+006574 037040 ZZ2346=ZZ2346+ZZ2346 -+006574 076100 ZZ2346=ZZ2346+ZZ2346 -+006574 174200 ZZ2346=ZZ2346+ZZ2346 -+006574 370400 ZZ2346=ZZ2346+ZZ2346 -+006574 014332 0 8192 -ZZ1346 -+006575 370400 0 ZZ2346 -- mark 1830, 69 /30 orio -+006576 000212 ZZ2347=ZZ2347+ZZ2347 -+006576 000424 ZZ2347=ZZ2347+ZZ2347 -+006576 001050 ZZ2347=ZZ2347+ZZ2347 -+006576 002120 ZZ2347=ZZ2347+ZZ2347 -+006576 004240 ZZ2347=ZZ2347+ZZ2347 -+006576 010500 ZZ2347=ZZ2347+ZZ2347 -+006576 021200 ZZ2347=ZZ2347+ZZ2347 -+006576 042400 ZZ2347=ZZ2347+ZZ2347 -+006576 014332 0 8192 -ZZ1347 -+006577 042400 0 ZZ2347 -- mark 1851, 134 /32 orio -+006600 000414 ZZ2348=ZZ2348+ZZ2348 -+006600 001030 ZZ2348=ZZ2348+ZZ2348 -+006600 002060 ZZ2348=ZZ2348+ZZ2348 -+006600 004140 ZZ2348=ZZ2348+ZZ2348 -+006600 010300 ZZ2348=ZZ2348+ZZ2348 -+006600 020600 ZZ2348=ZZ2348+ZZ2348 -+006600 041400 ZZ2348=ZZ2348+ZZ2348 -+006600 103000 ZZ2348=ZZ2348+ZZ2348 -+006600 014305 0 8192 -ZZ1348 -+006601 103000 0 ZZ2348 -- mark 1857, 421 /119 taur -+006602 001512 ZZ2349=ZZ2349+ZZ2349 -+006602 003224 ZZ2349=ZZ2349+ZZ2349 -+006602 006450 ZZ2349=ZZ2349+ZZ2349 -+006602 015120 ZZ2349=ZZ2349+ZZ2349 -+006602 032240 ZZ2349=ZZ2349+ZZ2349 -+006602 064500 ZZ2349=ZZ2349+ZZ2349 -+006602 151200 ZZ2349=ZZ2349+ZZ2349 -+006602 322400 ZZ2349=ZZ2349+ZZ2349 -+006602 014277 0 8192 -ZZ1349 -+006603 322400 0 ZZ2349 -- mark 1861, -168 /36 orio -+006604 777256 ZZ2350=ZZ2350+ZZ2350 -+006604 776534 ZZ2350=ZZ2350+ZZ2350 -+006604 775270 ZZ2350=ZZ2350+ZZ2350 -+006604 772560 ZZ2350=ZZ2350+ZZ2350 -+006604 765340 ZZ2350=ZZ2350+ZZ2350 -+006604 752700 ZZ2350=ZZ2350+ZZ2350 -+006604 725600 ZZ2350=ZZ2350+ZZ2350 -+006604 653400 ZZ2350=ZZ2350+ZZ2350 -+006604 014273 0 8192 -ZZ1350 -+006605 653400 0 ZZ2350 -- mark 1874, 214 /37 orio -+006606 000654 ZZ2351=ZZ2351+ZZ2351 -+006606 001530 ZZ2351=ZZ2351+ZZ2351 -+006606 003260 ZZ2351=ZZ2351+ZZ2351 -+006606 006540 ZZ2351=ZZ2351+ZZ2351 -+006606 015300 ZZ2351=ZZ2351+ZZ2351 -+006606 032600 ZZ2351=ZZ2351+ZZ2351 -+006606 065400 ZZ2351=ZZ2351+ZZ2351 -+006606 153000 ZZ2351=ZZ2351+ZZ2351 -+006606 014256 0 8192 -ZZ1351 -+006607 153000 0 ZZ2351 -- mark 1878, -132 / -+006610 777366 ZZ2352=ZZ2352+ZZ2352 -+006610 776754 ZZ2352=ZZ2352+ZZ2352 -+006610 775730 ZZ2352=ZZ2352+ZZ2352 -+006610 773660 ZZ2352=ZZ2352+ZZ2352 -+006610 767540 ZZ2352=ZZ2352+ZZ2352 -+006610 757300 ZZ2352=ZZ2352+ZZ2352 -+006610 736600 ZZ2352=ZZ2352+ZZ2352 -+006610 675400 ZZ2352=ZZ2352+ZZ2352 -+006610 014252 0 8192 -ZZ1352 -+006611 675400 0 ZZ2352 -- mark 1880, -112 /42 orio -+006612 777436 ZZ2353=ZZ2353+ZZ2353 -+006612 777074 ZZ2353=ZZ2353+ZZ2353 -+006612 776170 ZZ2353=ZZ2353+ZZ2353 -+006612 774360 ZZ2353=ZZ2353+ZZ2353 -+006612 770740 ZZ2353=ZZ2353+ZZ2353 -+006612 761700 ZZ2353=ZZ2353+ZZ2353 -+006612 743600 ZZ2353=ZZ2353+ZZ2353 -+006612 707400 ZZ2353=ZZ2353+ZZ2353 -+006612 014250 0 8192 -ZZ1353 -+006613 707400 0 ZZ2353 -- mark 1885, 210 /40 orio -+006614 000644 ZZ2354=ZZ2354+ZZ2354 -+006614 001510 ZZ2354=ZZ2354+ZZ2354 -+006614 003220 ZZ2354=ZZ2354+ZZ2354 -+006614 006440 ZZ2354=ZZ2354+ZZ2354 -+006614 015100 ZZ2354=ZZ2354+ZZ2354 -+006614 032200 ZZ2354=ZZ2354+ZZ2354 -+006614 064400 ZZ2354=ZZ2354+ZZ2354 -+006614 151000 ZZ2354=ZZ2354+ZZ2354 -+006614 014243 0 8192 -ZZ1354 -+006615 151000 0 ZZ2354 -- mark 1899,-60 /48 orio -+006616 777606 ZZ2355=ZZ2355+ZZ2355 -+006616 777414 ZZ2355=ZZ2355+ZZ2355 -+006616 777030 ZZ2355=ZZ2355+ZZ2355 -+006616 776060 ZZ2355=ZZ2355+ZZ2355 -+006616 774140 ZZ2355=ZZ2355+ZZ2355 -+006616 770300 ZZ2355=ZZ2355+ZZ2355 -+006616 760600 ZZ2355=ZZ2355+ZZ2355 -+006616 741400 ZZ2355=ZZ2355+ZZ2355 -+006616 014225 0 8192 -ZZ1355 -+006617 741400 0 ZZ2355 -- mark 1900, 93 /47 orio -+006620 000272 ZZ2356=ZZ2356+ZZ2356 -+006620 000564 ZZ2356=ZZ2356+ZZ2356 -+006620 001350 ZZ2356=ZZ2356+ZZ2356 -+006620 002720 ZZ2356=ZZ2356+ZZ2356 -+006620 005640 ZZ2356=ZZ2356+ZZ2356 -+006620 013500 ZZ2356=ZZ2356+ZZ2356 -+006620 027200 ZZ2356=ZZ2356+ZZ2356 -+006620 056400 ZZ2356=ZZ2356+ZZ2356 -+006620 014224 0 8192 -ZZ1356 -+006621 056400 0 ZZ2356 -- mark 1900, -165 /49 orio -+006622 777264 ZZ2357=ZZ2357+ZZ2357 -+006622 776550 ZZ2357=ZZ2357+ZZ2357 -+006622 775320 ZZ2357=ZZ2357+ZZ2357 -+006622 772640 ZZ2357=ZZ2357+ZZ2357 -+006622 765500 ZZ2357=ZZ2357+ZZ2357 -+006622 753200 ZZ2357=ZZ2357+ZZ2357 -+006622 726400 ZZ2357=ZZ2357+ZZ2357 -+006622 655000 ZZ2357=ZZ2357+ZZ2357 -+006622 014224 0 8192 -ZZ1357 -+006623 655000 0 ZZ2357 -- mark 1909, 375 /126 taur -+006624 001356 ZZ2358=ZZ2358+ZZ2358 -+006624 002734 ZZ2358=ZZ2358+ZZ2358 -+006624 005670 ZZ2358=ZZ2358+ZZ2358 -+006624 013560 ZZ2358=ZZ2358+ZZ2358 -+006624 027340 ZZ2358=ZZ2358+ZZ2358 -+006624 056700 ZZ2358=ZZ2358+ZZ2358 -+006624 135600 ZZ2358=ZZ2358+ZZ2358 -+006624 273400 ZZ2358=ZZ2358+ZZ2358 -+006624 014213 0 8192 -ZZ1358 -+006625 273400 0 ZZ2358 -- mark 1936, -511 /13 leps -+006626 776000 ZZ2359=ZZ2359+ZZ2359 -+006626 774000 ZZ2359=ZZ2359+ZZ2359 -+006626 770000 ZZ2359=ZZ2359+ZZ2359 -+006626 760000 ZZ2359=ZZ2359+ZZ2359 -+006626 740000 ZZ2359=ZZ2359+ZZ2359 -+006626 700000 ZZ2359=ZZ2359+ZZ2359 -+006626 600000 ZZ2359=ZZ2359+ZZ2359 -+006626 400000 ZZ2359=ZZ2359+ZZ2359 -+006626 014160 0 8192 -ZZ1359 -+006627 400000 0 ZZ2359 -- mark 1957, 287 /134 taur -+006630 001076 ZZ2360=ZZ2360+ZZ2360 -+006630 002174 ZZ2360=ZZ2360+ZZ2360 -+006630 004370 ZZ2360=ZZ2360+ZZ2360 -+006630 010760 ZZ2360=ZZ2360+ZZ2360 -+006630 021740 ZZ2360=ZZ2360+ZZ2360 -+006630 043700 ZZ2360=ZZ2360+ZZ2360 -+006630 107600 ZZ2360=ZZ2360+ZZ2360 -+006630 217400 ZZ2360=ZZ2360+ZZ2360 -+006630 014133 0 8192 -ZZ1360 -+006631 217400 0 ZZ2360 -- mark 1974, -475 /15 leps -+006632 776110 ZZ2361=ZZ2361+ZZ2361 -+006632 774220 ZZ2361=ZZ2361+ZZ2361 -+006632 770440 ZZ2361=ZZ2361+ZZ2361 -+006632 761100 ZZ2361=ZZ2361+ZZ2361 -+006632 742200 ZZ2361=ZZ2361+ZZ2361 -+006632 704400 ZZ2361=ZZ2361+ZZ2361 -+006632 611000 ZZ2361=ZZ2361+ZZ2361 -+006632 422000 ZZ2361=ZZ2361+ZZ2361 -+006632 014112 0 8192 -ZZ1361 -+006633 422000 0 ZZ2361 -- mark 1982, 461 /54 orio -+006634 001632 ZZ2362=ZZ2362+ZZ2362 -+006634 003464 ZZ2362=ZZ2362+ZZ2362 -+006634 007150 ZZ2362=ZZ2362+ZZ2362 -+006634 016320 ZZ2362=ZZ2362+ZZ2362 -+006634 034640 ZZ2362=ZZ2362+ZZ2362 -+006634 071500 ZZ2362=ZZ2362+ZZ2362 -+006634 163200 ZZ2362=ZZ2362+ZZ2362 -+006634 346400 ZZ2362=ZZ2362+ZZ2362 -+006634 014102 0 8192 -ZZ1362 -+006635 346400 0 ZZ2362 -- mark 2002, -323 /16 leps -+006636 776570 ZZ2363=ZZ2363+ZZ2363 -+006636 775360 ZZ2363=ZZ2363+ZZ2363 -+006636 772740 ZZ2363=ZZ2363+ZZ2363 -+006636 765700 ZZ2363=ZZ2363+ZZ2363 -+006636 753600 ZZ2363=ZZ2363+ZZ2363 -+006636 727400 ZZ2363=ZZ2363+ZZ2363 -+006636 657000 ZZ2363=ZZ2363+ZZ2363 -+006636 536000 ZZ2363=ZZ2363+ZZ2363 -+006636 014056 0 8192 -ZZ1363 -+006637 536000 0 ZZ2363 -- mark 2020, -70 / -+006640 777562 ZZ2364=ZZ2364+ZZ2364 -+006640 777344 ZZ2364=ZZ2364+ZZ2364 -+006640 776710 ZZ2364=ZZ2364+ZZ2364 -+006640 775620 ZZ2364=ZZ2364+ZZ2364 -+006640 773440 ZZ2364=ZZ2364+ZZ2364 -+006640 767100 ZZ2364=ZZ2364+ZZ2364 -+006640 756200 ZZ2364=ZZ2364+ZZ2364 -+006640 734400 ZZ2364=ZZ2364+ZZ2364 -+006640 014034 0 8192 -ZZ1364 -+006641 734400 0 ZZ2364 -- mark 2030, 220 /61 orio -+006642 000670 ZZ2365=ZZ2365+ZZ2365 -+006642 001560 ZZ2365=ZZ2365+ZZ2365 -+006642 003340 ZZ2365=ZZ2365+ZZ2365 -+006642 006700 ZZ2365=ZZ2365+ZZ2365 -+006642 015600 ZZ2365=ZZ2365+ZZ2365 -+006642 033400 ZZ2365=ZZ2365+ZZ2365 -+006642 067000 ZZ2365=ZZ2365+ZZ2365 -+006642 156000 ZZ2365=ZZ2365+ZZ2365 -+006642 014022 0 8192 -ZZ1365 -+006643 156000 0 ZZ2365 -- mark 2032, -241 / 3 mono -+006644 777034 ZZ2366=ZZ2366+ZZ2366 -+006644 776070 ZZ2366=ZZ2366+ZZ2366 -+006644 774160 ZZ2366=ZZ2366+ZZ2366 -+006644 770340 ZZ2366=ZZ2366+ZZ2366 -+006644 760700 ZZ2366=ZZ2366+ZZ2366 -+006644 741600 ZZ2366=ZZ2366+ZZ2366 -+006644 703400 ZZ2366=ZZ2366+ZZ2366 -+006644 607000 ZZ2366=ZZ2366+ZZ2366 -+006644 014020 0 8192 -ZZ1366 -+006645 607000 0 ZZ2366 -- mark 2037, 458 /62 orio -+006646 001624 ZZ2367=ZZ2367+ZZ2367 -+006646 003450 ZZ2367=ZZ2367+ZZ2367 -+006646 007120 ZZ2367=ZZ2367+ZZ2367 -+006646 016240 ZZ2367=ZZ2367+ZZ2367 -+006646 034500 ZZ2367=ZZ2367+ZZ2367 -+006646 071200 ZZ2367=ZZ2367+ZZ2367 -+006646 162400 ZZ2367=ZZ2367+ZZ2367 -+006646 345000 ZZ2367=ZZ2367+ZZ2367 -+006646 014013 0 8192 -ZZ1367 -+006647 345000 0 ZZ2367 -- mark 2057, -340 /18 leps -+006650 776526 ZZ2368=ZZ2368+ZZ2368 -+006650 775254 ZZ2368=ZZ2368+ZZ2368 -+006650 772530 ZZ2368=ZZ2368+ZZ2368 -+006650 765260 ZZ2368=ZZ2368+ZZ2368 -+006650 752540 ZZ2368=ZZ2368+ZZ2368 -+006650 725300 ZZ2368=ZZ2368+ZZ2368 -+006650 652600 ZZ2368=ZZ2368+ZZ2368 -+006650 525400 ZZ2368=ZZ2368+ZZ2368 -+006650 013767 0 8192 -ZZ1368 -+006651 525400 0 ZZ2368 -- mark 2059, 336 /67 orio -+006652 001240 ZZ2369=ZZ2369+ZZ2369 -+006652 002500 ZZ2369=ZZ2369+ZZ2369 -+006652 005200 ZZ2369=ZZ2369+ZZ2369 -+006652 012400 ZZ2369=ZZ2369+ZZ2369 -+006652 025000 ZZ2369=ZZ2369+ZZ2369 -+006652 052000 ZZ2369=ZZ2369+ZZ2369 -+006652 124000 ZZ2369=ZZ2369+ZZ2369 -+006652 250000 ZZ2369=ZZ2369+ZZ2369 -+006652 013765 0 8192 -ZZ1369 -+006653 250000 0 ZZ2369 -- mark 2084, 368 /69 orio -+006654 001340 ZZ2370=ZZ2370+ZZ2370 -+006654 002700 ZZ2370=ZZ2370+ZZ2370 -+006654 005600 ZZ2370=ZZ2370+ZZ2370 -+006654 013400 ZZ2370=ZZ2370+ZZ2370 -+006654 027000 ZZ2370=ZZ2370+ZZ2370 -+006654 056000 ZZ2370=ZZ2370+ZZ2370 -+006654 134000 ZZ2370=ZZ2370+ZZ2370 -+006654 270000 ZZ2370=ZZ2370+ZZ2370 -+006654 013734 0 8192 -ZZ1370 -+006655 270000 0 ZZ2370 -- mark 2084, 324 /70 orio -+006656 001210 ZZ2371=ZZ2371+ZZ2371 -+006656 002420 ZZ2371=ZZ2371+ZZ2371 -+006656 005040 ZZ2371=ZZ2371+ZZ2371 -+006656 012100 ZZ2371=ZZ2371+ZZ2371 -+006656 024200 ZZ2371=ZZ2371+ZZ2371 -+006656 050400 ZZ2371=ZZ2371+ZZ2371 -+006656 121000 ZZ2371=ZZ2371+ZZ2371 -+006656 242000 ZZ2371=ZZ2371+ZZ2371 -+006656 013734 0 8192 -ZZ1371 -+006657 242000 0 ZZ2371 -- mark 2105, -142 / 5 mono -+006660 777342 ZZ2372=ZZ2372+ZZ2372 -+006660 776704 ZZ2372=ZZ2372+ZZ2372 -+006660 775610 ZZ2372=ZZ2372+ZZ2372 -+006660 773420 ZZ2372=ZZ2372+ZZ2372 -+006660 767040 ZZ2372=ZZ2372+ZZ2372 -+006660 756100 ZZ2372=ZZ2372+ZZ2372 -+006660 734200 ZZ2372=ZZ2372+ZZ2372 -+006660 670400 ZZ2372=ZZ2372+ZZ2372 -+006660 013707 0 8192 -ZZ1372 -+006661 670400 0 ZZ2372 -- mark 2112, -311 / -+006662 776620 ZZ2373=ZZ2373+ZZ2373 -+006662 775440 ZZ2373=ZZ2373+ZZ2373 -+006662 773100 ZZ2373=ZZ2373+ZZ2373 -+006662 766200 ZZ2373=ZZ2373+ZZ2373 -+006662 754400 ZZ2373=ZZ2373+ZZ2373 -+006662 731000 ZZ2373=ZZ2373+ZZ2373 -+006662 662000 ZZ2373=ZZ2373+ZZ2373 -+006662 544000 ZZ2373=ZZ2373+ZZ2373 -+006662 013700 0 8192 -ZZ1373 -+006663 544000 0 ZZ2373 -- mark 2153, 106 / 8 mono -+006664 000324 ZZ2374=ZZ2374+ZZ2374 -+006664 000650 ZZ2374=ZZ2374+ZZ2374 -+006664 001520 ZZ2374=ZZ2374+ZZ2374 -+006664 003240 ZZ2374=ZZ2374+ZZ2374 -+006664 006500 ZZ2374=ZZ2374+ZZ2374 -+006664 015200 ZZ2374=ZZ2374+ZZ2374 -+006664 032400 ZZ2374=ZZ2374+ZZ2374 -+006664 065000 ZZ2374=ZZ2374+ZZ2374 -+006664 013627 0 8192 -ZZ1374 -+006665 065000 0 ZZ2374 -- mark 2179, 462 /18 gemi -+006666 001634 ZZ2375=ZZ2375+ZZ2375 -+006666 003470 ZZ2375=ZZ2375+ZZ2375 -+006666 007160 ZZ2375=ZZ2375+ZZ2375 -+006666 016340 ZZ2375=ZZ2375+ZZ2375 -+006666 034700 ZZ2375=ZZ2375+ZZ2375 -+006666 071600 ZZ2375=ZZ2375+ZZ2375 -+006666 163400 ZZ2375=ZZ2375+ZZ2375 -+006666 347000 ZZ2375=ZZ2375+ZZ2375 -+006666 013575 0 8192 -ZZ1375 -+006667 347000 0 ZZ2375 -- mark 2179, -107 /10 mono -+006670 777450 ZZ2376=ZZ2376+ZZ2376 -+006670 777120 ZZ2376=ZZ2376+ZZ2376 -+006670 776240 ZZ2376=ZZ2376+ZZ2376 -+006670 774500 ZZ2376=ZZ2376+ZZ2376 -+006670 771200 ZZ2376=ZZ2376+ZZ2376 -+006670 762400 ZZ2376=ZZ2376+ZZ2376 -+006670 745000 ZZ2376=ZZ2376+ZZ2376 -+006670 712000 ZZ2376=ZZ2376+ZZ2376 -+006670 013575 0 8192 -ZZ1376 -+006671 712000 0 ZZ2376 -- mark 2184, -159 /11 mono -+006672 777300 ZZ2377=ZZ2377+ZZ2377 -+006672 776600 ZZ2377=ZZ2377+ZZ2377 -+006672 775400 ZZ2377=ZZ2377+ZZ2377 -+006672 773000 ZZ2377=ZZ2377+ZZ2377 -+006672 766000 ZZ2377=ZZ2377+ZZ2377 -+006672 754000 ZZ2377=ZZ2377+ZZ2377 -+006672 730000 ZZ2377=ZZ2377+ZZ2377 -+006672 660000 ZZ2377=ZZ2377+ZZ2377 -+006672 013570 0 8192 -ZZ1377 -+006673 660000 0 ZZ2377 -- mark 2204, 168 /13 mono -+006674 000520 ZZ2378=ZZ2378+ZZ2378 -+006674 001240 ZZ2378=ZZ2378+ZZ2378 -+006674 002500 ZZ2378=ZZ2378+ZZ2378 -+006674 005200 ZZ2378=ZZ2378+ZZ2378 -+006674 012400 ZZ2378=ZZ2378+ZZ2378 -+006674 025000 ZZ2378=ZZ2378+ZZ2378 -+006674 052000 ZZ2378=ZZ2378+ZZ2378 -+006674 124000 ZZ2378=ZZ2378+ZZ2378 -+006674 013544 0 8192 -ZZ1378 -+006675 124000 0 ZZ2378 -- mark 2232, -436 / 7 cmaj -+006676 776226 ZZ2379=ZZ2379+ZZ2379 -+006676 774454 ZZ2379=ZZ2379+ZZ2379 -+006676 771130 ZZ2379=ZZ2379+ZZ2379 -+006676 762260 ZZ2379=ZZ2379+ZZ2379 -+006676 744540 ZZ2379=ZZ2379+ZZ2379 -+006676 711300 ZZ2379=ZZ2379+ZZ2379 -+006676 622600 ZZ2379=ZZ2379+ZZ2379 -+006676 445400 ZZ2379=ZZ2379+ZZ2379 -+006676 013510 0 8192 -ZZ1379 -+006677 445400 0 ZZ2379 -- mark 2239, -413 / 8 cmaj -+006700 776304 ZZ2380=ZZ2380+ZZ2380 -+006700 774610 ZZ2380=ZZ2380+ZZ2380 -+006700 771420 ZZ2380=ZZ2380+ZZ2380 -+006700 763040 ZZ2380=ZZ2380+ZZ2380 -+006700 746100 ZZ2380=ZZ2380+ZZ2380 -+006700 714200 ZZ2380=ZZ2380+ZZ2380 -+006700 630400 ZZ2380=ZZ2380+ZZ2380 -+006700 461000 ZZ2380=ZZ2380+ZZ2380 -+006700 013501 0 8192 -ZZ1380 -+006701 461000 0 ZZ2380 -- mark 2245, -320 / -+006702 776576 ZZ2381=ZZ2381+ZZ2381 -+006702 775374 ZZ2381=ZZ2381+ZZ2381 -+006702 772770 ZZ2381=ZZ2381+ZZ2381 -+006702 765760 ZZ2381=ZZ2381+ZZ2381 -+006702 753740 ZZ2381=ZZ2381+ZZ2381 -+006702 727700 ZZ2381=ZZ2381+ZZ2381 -+006702 657600 ZZ2381=ZZ2381+ZZ2381 -+006702 537400 ZZ2381=ZZ2381+ZZ2381 -+006702 013473 0 8192 -ZZ1381 -+006703 537400 0 ZZ2381 -- mark 2250, 227 /15 mono -+006704 000706 ZZ2382=ZZ2382+ZZ2382 -+006704 001614 ZZ2382=ZZ2382+ZZ2382 -+006704 003430 ZZ2382=ZZ2382+ZZ2382 -+006704 007060 ZZ2382=ZZ2382+ZZ2382 -+006704 016140 ZZ2382=ZZ2382+ZZ2382 -+006704 034300 ZZ2382=ZZ2382+ZZ2382 -+006704 070600 ZZ2382=ZZ2382+ZZ2382 -+006704 161400 ZZ2382=ZZ2382+ZZ2382 -+006704 013466 0 8192 -ZZ1382 -+006705 161400 0 ZZ2382 -- mark 2266, 303 /30 gemi -+006706 001136 ZZ2383=ZZ2383+ZZ2383 -+006706 002274 ZZ2383=ZZ2383+ZZ2383 -+006706 004570 ZZ2383=ZZ2383+ZZ2383 -+006706 011360 ZZ2383=ZZ2383+ZZ2383 -+006706 022740 ZZ2383=ZZ2383+ZZ2383 -+006706 045700 ZZ2383=ZZ2383+ZZ2383 -+006706 113600 ZZ2383=ZZ2383+ZZ2383 -+006706 227400 ZZ2383=ZZ2383+ZZ2383 -+006706 013446 0 8192 -ZZ1383 -+006707 227400 0 ZZ2383 -- mark 2291, 57 /18 mono -+006710 000162 ZZ2384=ZZ2384+ZZ2384 -+006710 000344 ZZ2384=ZZ2384+ZZ2384 -+006710 000710 ZZ2384=ZZ2384+ZZ2384 -+006710 001620 ZZ2384=ZZ2384+ZZ2384 -+006710 003440 ZZ2384=ZZ2384+ZZ2384 -+006710 007100 ZZ2384=ZZ2384+ZZ2384 -+006710 016200 ZZ2384=ZZ2384+ZZ2384 -+006710 034400 ZZ2384=ZZ2384+ZZ2384 -+006710 013415 0 8192 -ZZ1384 -+006711 034400 0 ZZ2384 -- mark 2327, 303 /38 gemi -+006712 001136 ZZ2385=ZZ2385+ZZ2385 -+006712 002274 ZZ2385=ZZ2385+ZZ2385 -+006712 004570 ZZ2385=ZZ2385+ZZ2385 -+006712 011360 ZZ2385=ZZ2385+ZZ2385 -+006712 022740 ZZ2385=ZZ2385+ZZ2385 -+006712 045700 ZZ2385=ZZ2385+ZZ2385 -+006712 113600 ZZ2385=ZZ2385+ZZ2385 -+006712 227400 ZZ2385=ZZ2385+ZZ2385 -+006712 013351 0 8192 -ZZ1385 -+006713 227400 0 ZZ2385 -- mark 2328, -457 /15 cmaj -+006714 776154 ZZ2386=ZZ2386+ZZ2386 -+006714 774330 ZZ2386=ZZ2386+ZZ2386 -+006714 770660 ZZ2386=ZZ2386+ZZ2386 -+006714 761540 ZZ2386=ZZ2386+ZZ2386 -+006714 743300 ZZ2386=ZZ2386+ZZ2386 -+006714 706600 ZZ2386=ZZ2386+ZZ2386 -+006714 615400 ZZ2386=ZZ2386+ZZ2386 -+006714 433000 ZZ2386=ZZ2386+ZZ2386 -+006714 013350 0 8192 -ZZ1386 -+006715 433000 0 ZZ2386 -- mark 2330, -271 /14 cmaj -+006716 776740 ZZ2387=ZZ2387+ZZ2387 -+006716 775700 ZZ2387=ZZ2387+ZZ2387 -+006716 773600 ZZ2387=ZZ2387+ZZ2387 -+006716 767400 ZZ2387=ZZ2387+ZZ2387 -+006716 757000 ZZ2387=ZZ2387+ZZ2387 -+006716 736000 ZZ2387=ZZ2387+ZZ2387 -+006716 674000 ZZ2387=ZZ2387+ZZ2387 -+006716 570000 ZZ2387=ZZ2387+ZZ2387 -+006716 013346 0 8192 -ZZ1387 -+006717 570000 0 ZZ2387 -- mark 2340, -456 /19 cmaj -+006720 776156 ZZ2388=ZZ2388+ZZ2388 -+006720 774334 ZZ2388=ZZ2388+ZZ2388 -+006720 770670 ZZ2388=ZZ2388+ZZ2388 -+006720 761560 ZZ2388=ZZ2388+ZZ2388 -+006720 743340 ZZ2388=ZZ2388+ZZ2388 -+006720 706700 ZZ2388=ZZ2388+ZZ2388 -+006720 615600 ZZ2388=ZZ2388+ZZ2388 -+006720 433400 ZZ2388=ZZ2388+ZZ2388 -+006720 013334 0 8192 -ZZ1388 -+006721 433400 0 ZZ2388 -- mark 2342, -385 /20 cmaj -+006722 776374 ZZ2389=ZZ2389+ZZ2389 -+006722 774770 ZZ2389=ZZ2389+ZZ2389 -+006722 771760 ZZ2389=ZZ2389+ZZ2389 -+006722 763740 ZZ2389=ZZ2389+ZZ2389 -+006722 747700 ZZ2389=ZZ2389+ZZ2389 -+006722 717600 ZZ2389=ZZ2389+ZZ2389 -+006722 637400 ZZ2389=ZZ2389+ZZ2389 -+006722 477000 ZZ2389=ZZ2389+ZZ2389 -+006722 013332 0 8192 -ZZ1389 -+006723 477000 0 ZZ2389 -- mark 2378, -93 /19 mono -+006724 777504 ZZ2390=ZZ2390+ZZ2390 -+006724 777210 ZZ2390=ZZ2390+ZZ2390 -+006724 776420 ZZ2390=ZZ2390+ZZ2390 -+006724 775040 ZZ2390=ZZ2390+ZZ2390 -+006724 772100 ZZ2390=ZZ2390+ZZ2390 -+006724 764200 ZZ2390=ZZ2390+ZZ2390 -+006724 750400 ZZ2390=ZZ2390+ZZ2390 -+006724 721000 ZZ2390=ZZ2390+ZZ2390 -+006724 013266 0 8192 -ZZ1390 -+006725 721000 0 ZZ2390 -- mark 2379, 471 /43 gemi -+006726 001656 ZZ2391=ZZ2391+ZZ2391 -+006726 003534 ZZ2391=ZZ2391+ZZ2391 -+006726 007270 ZZ2391=ZZ2391+ZZ2391 -+006726 016560 ZZ2391=ZZ2391+ZZ2391 -+006726 035340 ZZ2391=ZZ2391+ZZ2391 -+006726 072700 ZZ2391=ZZ2391+ZZ2391 -+006726 165600 ZZ2391=ZZ2391+ZZ2391 -+006726 353400 ZZ2391=ZZ2391+ZZ2391 -+006726 013265 0 8192 -ZZ1391 -+006727 353400 0 ZZ2391 -- mark 2385, -352 /23 cmaj -+006730 776476 ZZ2392=ZZ2392+ZZ2392 -+006730 775174 ZZ2392=ZZ2392+ZZ2392 -+006730 772370 ZZ2392=ZZ2392+ZZ2392 -+006730 764760 ZZ2392=ZZ2392+ZZ2392 -+006730 751740 ZZ2392=ZZ2392+ZZ2392 -+006730 723700 ZZ2392=ZZ2392+ZZ2392 -+006730 647600 ZZ2392=ZZ2392+ZZ2392 -+006730 517400 ZZ2392=ZZ2392+ZZ2392 -+006730 013257 0 8192 -ZZ1392 -+006731 517400 0 ZZ2392 -- mark 2428, -8 /22 mono -+006732 777756 ZZ2393=ZZ2393+ZZ2393 -+006732 777734 ZZ2393=ZZ2393+ZZ2393 -+006732 777670 ZZ2393=ZZ2393+ZZ2393 -+006732 777560 ZZ2393=ZZ2393+ZZ2393 -+006732 777340 ZZ2393=ZZ2393+ZZ2393 -+006732 776700 ZZ2393=ZZ2393+ZZ2393 -+006732 775600 ZZ2393=ZZ2393+ZZ2393 -+006732 773400 ZZ2393=ZZ2393+ZZ2393 -+006732 013204 0 8192 -ZZ1393 -+006733 773400 0 ZZ2393 -- mark 2491, -429 / -+006734 776244 ZZ2394=ZZ2394+ZZ2394 -+006734 774510 ZZ2394=ZZ2394+ZZ2394 -+006734 771220 ZZ2394=ZZ2394+ZZ2394 -+006734 762440 ZZ2394=ZZ2394+ZZ2394 -+006734 745100 ZZ2394=ZZ2394+ZZ2394 -+006734 712200 ZZ2394=ZZ2394+ZZ2394 -+006734 624400 ZZ2394=ZZ2394+ZZ2394 -+006734 451000 ZZ2394=ZZ2394+ZZ2394 -+006734 013105 0 8192 -ZZ1394 -+006735 451000 0 ZZ2394 -- mark 2519, 208 / 4 cmin -+006736 000640 ZZ2395=ZZ2395+ZZ2395 -+006736 001500 ZZ2395=ZZ2395+ZZ2395 -+006736 003200 ZZ2395=ZZ2395+ZZ2395 -+006736 006400 ZZ2395=ZZ2395+ZZ2395 -+006736 015000 ZZ2395=ZZ2395+ZZ2395 -+006736 032000 ZZ2395=ZZ2395+ZZ2395 -+006736 064000 ZZ2395=ZZ2395+ZZ2395 -+006736 150000 ZZ2395=ZZ2395+ZZ2395 -+006736 013051 0 8192 -ZZ1395 -+006737 150000 0 ZZ2395 -- mark 2527, 278 / 6 cmin -+006740 001054 ZZ2396=ZZ2396+ZZ2396 -+006740 002130 ZZ2396=ZZ2396+ZZ2396 -+006740 004260 ZZ2396=ZZ2396+ZZ2396 -+006740 010540 ZZ2396=ZZ2396+ZZ2396 -+006740 021300 ZZ2396=ZZ2396+ZZ2396 -+006740 042600 ZZ2396=ZZ2396+ZZ2396 -+006740 105400 ZZ2396=ZZ2396+ZZ2396 -+006740 213000 ZZ2396=ZZ2396+ZZ2396 -+006740 013041 0 8192 -ZZ1396 -+006741 213000 0 ZZ2396 -- mark 2559, -503 / -+006742 776020 ZZ2397=ZZ2397+ZZ2397 -+006742 774040 ZZ2397=ZZ2397+ZZ2397 -+006742 770100 ZZ2397=ZZ2397+ZZ2397 -+006742 760200 ZZ2397=ZZ2397+ZZ2397 -+006742 740400 ZZ2397=ZZ2397+ZZ2397 -+006742 701000 ZZ2397=ZZ2397+ZZ2397 -+006742 602000 ZZ2397=ZZ2397+ZZ2397 -+006742 404000 ZZ2397=ZZ2397+ZZ2397 -+006742 013001 0 8192 -ZZ1397 -+006743 404000 0 ZZ2397 -- mark 2597, -212 /26 mono -+006744 777126 ZZ2398=ZZ2398+ZZ2398 -+006744 776254 ZZ2398=ZZ2398+ZZ2398 -+006744 774530 ZZ2398=ZZ2398+ZZ2398 -+006744 771260 ZZ2398=ZZ2398+ZZ2398 -+006744 762540 ZZ2398=ZZ2398+ZZ2398 -+006744 745300 ZZ2398=ZZ2398+ZZ2398 -+006744 712600 ZZ2398=ZZ2398+ZZ2398 -+006744 625400 ZZ2398=ZZ2398+ZZ2398 -+006744 012733 0 8192 -ZZ1398 -+006745 625400 0 ZZ2398 -- mark 2704, -412 / -+006746 776306 ZZ2399=ZZ2399+ZZ2399 -+006746 774614 ZZ2399=ZZ2399+ZZ2399 -+006746 771430 ZZ2399=ZZ2399+ZZ2399 -+006746 763060 ZZ2399=ZZ2399+ZZ2399 -+006746 746140 ZZ2399=ZZ2399+ZZ2399 -+006746 714300 ZZ2399=ZZ2399+ZZ2399 -+006746 630600 ZZ2399=ZZ2399+ZZ2399 -+006746 461400 ZZ2399=ZZ2399+ZZ2399 -+006746 012560 0 8192 -ZZ1399 -+006747 461400 0 ZZ2399 -- mark 2709, -25 /28 mono -+006750 777714 ZZ2400=ZZ2400+ZZ2400 -+006750 777630 ZZ2400=ZZ2400+ZZ2400 -+006750 777460 ZZ2400=ZZ2400+ZZ2400 -+006750 777140 ZZ2400=ZZ2400+ZZ2400 -+006750 776300 ZZ2400=ZZ2400+ZZ2400 -+006750 774600 ZZ2400=ZZ2400+ZZ2400 -+006750 771400 ZZ2400=ZZ2400+ZZ2400 -+006750 763000 ZZ2400=ZZ2400+ZZ2400 -+006750 012553 0 8192 -ZZ1400 -+006751 763000 0 ZZ2400 -- mark 2714, 60 / -+006752 000170 ZZ2401=ZZ2401+ZZ2401 -+006752 000360 ZZ2401=ZZ2401+ZZ2401 -+006752 000740 ZZ2401=ZZ2401+ZZ2401 -+006752 001700 ZZ2401=ZZ2401+ZZ2401 -+006752 003600 ZZ2401=ZZ2401+ZZ2401 -+006752 007400 ZZ2401=ZZ2401+ZZ2401 -+006752 017000 ZZ2401=ZZ2401+ZZ2401 -+006752 036000 ZZ2401=ZZ2401+ZZ2401 -+006752 012546 0 8192 -ZZ1401 -+006753 036000 0 ZZ2401 -- mark 2751, -61 /29 mono -+006754 777604 ZZ2402=ZZ2402+ZZ2402 -+006754 777410 ZZ2402=ZZ2402+ZZ2402 -+006754 777020 ZZ2402=ZZ2402+ZZ2402 -+006754 776040 ZZ2402=ZZ2402+ZZ2402 -+006754 774100 ZZ2402=ZZ2402+ZZ2402 -+006754 770200 ZZ2402=ZZ2402+ZZ2402 -+006754 760400 ZZ2402=ZZ2402+ZZ2402 -+006754 741000 ZZ2402=ZZ2402+ZZ2402 -+006754 012501 0 8192 -ZZ1402 -+006755 741000 0 ZZ2402 -- mark 2757, -431 /16 pupp -+006756 776240 ZZ2403=ZZ2403+ZZ2403 -+006756 774500 ZZ2403=ZZ2403+ZZ2403 -+006756 771200 ZZ2403=ZZ2403+ZZ2403 -+006756 762400 ZZ2403=ZZ2403+ZZ2403 -+006756 745000 ZZ2403=ZZ2403+ZZ2403 -+006756 712000 ZZ2403=ZZ2403+ZZ2403 -+006756 624000 ZZ2403=ZZ2403+ZZ2403 -+006756 450000 ZZ2403=ZZ2403+ZZ2403 -+006756 012473 0 8192 -ZZ1403 -+006757 450000 0 ZZ2403 -- mark 2768, -288 /19 pupp -+006760 776676 ZZ2404=ZZ2404+ZZ2404 -+006760 775574 ZZ2404=ZZ2404+ZZ2404 -+006760 773370 ZZ2404=ZZ2404+ZZ2404 -+006760 766760 ZZ2404=ZZ2404+ZZ2404 -+006760 755740 ZZ2404=ZZ2404+ZZ2404 -+006760 733700 ZZ2404=ZZ2404+ZZ2404 -+006760 667600 ZZ2404=ZZ2404+ZZ2404 -+006760 557400 ZZ2404=ZZ2404+ZZ2404 -+006760 012460 0 8192 -ZZ1404 -+006761 557400 0 ZZ2404 -- mark 2794, 216 /17 canc -+006762 000660 ZZ2405=ZZ2405+ZZ2405 -+006762 001540 ZZ2405=ZZ2405+ZZ2405 -+006762 003300 ZZ2405=ZZ2405+ZZ2405 -+006762 006600 ZZ2405=ZZ2405+ZZ2405 -+006762 015400 ZZ2405=ZZ2405+ZZ2405 -+006762 033000 ZZ2405=ZZ2405+ZZ2405 -+006762 066000 ZZ2405=ZZ2405+ZZ2405 -+006762 154000 ZZ2405=ZZ2405+ZZ2405 -+006762 012426 0 8192 -ZZ1405 -+006763 154000 0 ZZ2405 -- mark 2848, -82 / -+006764 777532 ZZ2406=ZZ2406+ZZ2406 -+006764 777264 ZZ2406=ZZ2406+ZZ2406 -+006764 776550 ZZ2406=ZZ2406+ZZ2406 -+006764 775320 ZZ2406=ZZ2406+ZZ2406 -+006764 772640 ZZ2406=ZZ2406+ZZ2406 -+006764 765500 ZZ2406=ZZ2406+ZZ2406 -+006764 753200 ZZ2406=ZZ2406+ZZ2406 -+006764 726400 ZZ2406=ZZ2406+ZZ2406 -+006764 012340 0 8192 -ZZ1406 -+006765 726400 0 ZZ2406 -- mark 2915, 138 / 4 hyda -+006766 000424 ZZ2407=ZZ2407+ZZ2407 -+006766 001050 ZZ2407=ZZ2407+ZZ2407 -+006766 002120 ZZ2407=ZZ2407+ZZ2407 -+006766 004240 ZZ2407=ZZ2407+ZZ2407 -+006766 010500 ZZ2407=ZZ2407+ZZ2407 -+006766 021200 ZZ2407=ZZ2407+ZZ2407 -+006766 042400 ZZ2407=ZZ2407+ZZ2407 -+006766 105000 ZZ2407=ZZ2407+ZZ2407 -+006766 012235 0 8192 -ZZ1407 -+006767 105000 0 ZZ2407 -- mark 2921, 84 / 5 hyda -+006770 000250 ZZ2408=ZZ2408+ZZ2408 -+006770 000520 ZZ2408=ZZ2408+ZZ2408 -+006770 001240 ZZ2408=ZZ2408+ZZ2408 -+006770 002500 ZZ2408=ZZ2408+ZZ2408 -+006770 005200 ZZ2408=ZZ2408+ZZ2408 -+006770 012400 ZZ2408=ZZ2408+ZZ2408 -+006770 025000 ZZ2408=ZZ2408+ZZ2408 -+006770 052000 ZZ2408=ZZ2408+ZZ2408 -+006770 012227 0 8192 -ZZ1408 -+006771 052000 0 ZZ2408 -- mark 2942, -355 / 9 hyda -+006772 776470 ZZ2409=ZZ2409+ZZ2409 -+006772 775160 ZZ2409=ZZ2409+ZZ2409 -+006772 772340 ZZ2409=ZZ2409+ZZ2409 -+006772 764700 ZZ2409=ZZ2409+ZZ2409 -+006772 751600 ZZ2409=ZZ2409+ZZ2409 -+006772 723400 ZZ2409=ZZ2409+ZZ2409 -+006772 647000 ZZ2409=ZZ2409+ZZ2409 -+006772 516000 ZZ2409=ZZ2409+ZZ2409 -+006772 012202 0 8192 -ZZ1409 -+006773 516000 0 ZZ2409 -- mark 2944, 497 /43 canc -+006774 001742 ZZ2410=ZZ2410+ZZ2410 -+006774 003704 ZZ2410=ZZ2410+ZZ2410 -+006774 007610 ZZ2410=ZZ2410+ZZ2410 -+006774 017420 ZZ2410=ZZ2410+ZZ2410 -+006774 037040 ZZ2410=ZZ2410+ZZ2410 -+006774 076100 ZZ2410=ZZ2410+ZZ2410 -+006774 174200 ZZ2410=ZZ2410+ZZ2410 -+006774 370400 ZZ2410=ZZ2410+ZZ2410 -+006774 012200 0 8192 -ZZ1410 -+006775 370400 0 ZZ2410 -- mark 2947, 85 / 7 hyda -+006776 000252 ZZ2411=ZZ2411+ZZ2411 -+006776 000524 ZZ2411=ZZ2411+ZZ2411 -+006776 001250 ZZ2411=ZZ2411+ZZ2411 -+006776 002520 ZZ2411=ZZ2411+ZZ2411 -+006776 005240 ZZ2411=ZZ2411+ZZ2411 -+006776 012500 ZZ2411=ZZ2411+ZZ2411 -+006776 025200 ZZ2411=ZZ2411+ZZ2411 -+006776 052400 ZZ2411=ZZ2411+ZZ2411 -+006776 012175 0 8192 -ZZ1411 -+006777 052400 0 ZZ2411 -- mark 2951, -156 / -+007000 777306 ZZ2412=ZZ2412+ZZ2412 -+007000 776614 ZZ2412=ZZ2412+ZZ2412 -+007000 775430 ZZ2412=ZZ2412+ZZ2412 -+007000 773060 ZZ2412=ZZ2412+ZZ2412 -+007000 766140 ZZ2412=ZZ2412+ZZ2412 -+007000 754300 ZZ2412=ZZ2412+ZZ2412 -+007000 730600 ZZ2412=ZZ2412+ZZ2412 -+007000 661400 ZZ2412=ZZ2412+ZZ2412 -+007000 012171 0 8192 -ZZ1412 -+007001 661400 0 ZZ2412 -- mark 2953, 421 /47 canc -+007002 001512 ZZ2413=ZZ2413+ZZ2413 -+007002 003224 ZZ2413=ZZ2413+ZZ2413 -+007002 006450 ZZ2413=ZZ2413+ZZ2413 -+007002 015120 ZZ2413=ZZ2413+ZZ2413 -+007002 032240 ZZ2413=ZZ2413+ZZ2413 -+007002 064500 ZZ2413=ZZ2413+ZZ2413 -+007002 151200 ZZ2413=ZZ2413+ZZ2413 -+007002 322400 ZZ2413=ZZ2413+ZZ2413 -+007002 012167 0 8192 -ZZ1413 -+007003 322400 0 ZZ2413 -- mark 2968, -300 /12 hyda -+007004 776646 ZZ2414=ZZ2414+ZZ2414 -+007004 775514 ZZ2414=ZZ2414+ZZ2414 -+007004 773230 ZZ2414=ZZ2414+ZZ2414 -+007004 766460 ZZ2414=ZZ2414+ZZ2414 -+007004 755140 ZZ2414=ZZ2414+ZZ2414 -+007004 732300 ZZ2414=ZZ2414+ZZ2414 -+007004 664600 ZZ2414=ZZ2414+ZZ2414 -+007004 551400 ZZ2414=ZZ2414+ZZ2414 -+007004 012150 0 8192 -ZZ1414 -+007005 551400 0 ZZ2414 -- mark 2976, 141 /13 hyda -+007006 000432 ZZ2415=ZZ2415+ZZ2415 -+007006 001064 ZZ2415=ZZ2415+ZZ2415 -+007006 002150 ZZ2415=ZZ2415+ZZ2415 -+007006 004320 ZZ2415=ZZ2415+ZZ2415 -+007006 010640 ZZ2415=ZZ2415+ZZ2415 -+007006 021500 ZZ2415=ZZ2415+ZZ2415 -+007006 043200 ZZ2415=ZZ2415+ZZ2415 -+007006 106400 ZZ2415=ZZ2415+ZZ2415 -+007006 012140 0 8192 -ZZ1415 -+007007 106400 0 ZZ2415 -- mark 3032, 279 /65 canc -+007010 001056 ZZ2416=ZZ2416+ZZ2416 -+007010 002134 ZZ2416=ZZ2416+ZZ2416 -+007010 004270 ZZ2416=ZZ2416+ZZ2416 -+007010 010560 ZZ2416=ZZ2416+ZZ2416 -+007010 021340 ZZ2416=ZZ2416+ZZ2416 -+007010 042700 ZZ2416=ZZ2416+ZZ2416 -+007010 105600 ZZ2416=ZZ2416+ZZ2416 -+007010 213400 ZZ2416=ZZ2416+ZZ2416 -+007010 012050 0 8192 -ZZ1416 -+007011 213400 0 ZZ2416 -- mark 3124, 62 /22 hyda -+007012 000174 ZZ2417=ZZ2417+ZZ2417 -+007012 000370 ZZ2417=ZZ2417+ZZ2417 -+007012 000760 ZZ2417=ZZ2417+ZZ2417 -+007012 001740 ZZ2417=ZZ2417+ZZ2417 -+007012 003700 ZZ2417=ZZ2417+ZZ2417 -+007012 007600 ZZ2417=ZZ2417+ZZ2417 -+007012 017400 ZZ2417=ZZ2417+ZZ2417 -+007012 037000 ZZ2417=ZZ2417+ZZ2417 -+007012 011714 0 8192 -ZZ1417 -+007013 037000 0 ZZ2417 -- mark 3157, -263 /26 hyda -+007014 776760 ZZ2418=ZZ2418+ZZ2418 -+007014 775740 ZZ2418=ZZ2418+ZZ2418 -+007014 773700 ZZ2418=ZZ2418+ZZ2418 -+007014 767600 ZZ2418=ZZ2418+ZZ2418 -+007014 757400 ZZ2418=ZZ2418+ZZ2418 -+007014 737000 ZZ2418=ZZ2418+ZZ2418 -+007014 676000 ZZ2418=ZZ2418+ZZ2418 -+007014 574000 ZZ2418=ZZ2418+ZZ2418 -+007014 011653 0 8192 -ZZ1418 -+007015 574000 0 ZZ2418 -- mark 3161, -208 /27 hyda -+007016 777136 ZZ2419=ZZ2419+ZZ2419 -+007016 776274 ZZ2419=ZZ2419+ZZ2419 -+007016 774570 ZZ2419=ZZ2419+ZZ2419 -+007016 771360 ZZ2419=ZZ2419+ZZ2419 -+007016 762740 ZZ2419=ZZ2419+ZZ2419 -+007016 745700 ZZ2419=ZZ2419+ZZ2419 -+007016 713600 ZZ2419=ZZ2419+ZZ2419 -+007016 627400 ZZ2419=ZZ2419+ZZ2419 -+007016 011647 0 8192 -ZZ1419 -+007017 627400 0 ZZ2419 -- mark 3209, -53 /31 hyda -+007020 777624 ZZ2420=ZZ2420+ZZ2420 -+007020 777450 ZZ2420=ZZ2420+ZZ2420 -+007020 777120 ZZ2420=ZZ2420+ZZ2420 -+007020 776240 ZZ2420=ZZ2420+ZZ2420 -+007020 774500 ZZ2420=ZZ2420+ZZ2420 -+007020 771200 ZZ2420=ZZ2420+ZZ2420 -+007020 762400 ZZ2420=ZZ2420+ZZ2420 -+007020 745000 ZZ2420=ZZ2420+ZZ2420 -+007020 011567 0 8192 -ZZ1420 -+007021 745000 0 ZZ2420 -- mark 3225, -17 /32 hyda -+007022 777734 ZZ2421=ZZ2421+ZZ2421 -+007022 777670 ZZ2421=ZZ2421+ZZ2421 -+007022 777560 ZZ2421=ZZ2421+ZZ2421 -+007022 777340 ZZ2421=ZZ2421+ZZ2421 -+007022 776700 ZZ2421=ZZ2421+ZZ2421 -+007022 775600 ZZ2421=ZZ2421+ZZ2421 -+007022 773400 ZZ2421=ZZ2421+ZZ2421 -+007022 767000 ZZ2421=ZZ2421+ZZ2421 -+007022 011547 0 8192 -ZZ1421 -+007023 767000 0 ZZ2421 -- mark 3261, 116 / -+007024 000350 ZZ2422=ZZ2422+ZZ2422 -+007024 000720 ZZ2422=ZZ2422+ZZ2422 -+007024 001640 ZZ2422=ZZ2422+ZZ2422 -+007024 003500 ZZ2422=ZZ2422+ZZ2422 -+007024 007200 ZZ2422=ZZ2422+ZZ2422 -+007024 016400 ZZ2422=ZZ2422+ZZ2422 -+007024 035000 ZZ2422=ZZ2422+ZZ2422 -+007024 072000 ZZ2422=ZZ2422+ZZ2422 -+007024 011503 0 8192 -ZZ1422 -+007025 072000 0 ZZ2422 -- mark 3270, -16 /35 hyda -+007026 777736 ZZ2423=ZZ2423+ZZ2423 -+007026 777674 ZZ2423=ZZ2423+ZZ2423 -+007026 777570 ZZ2423=ZZ2423+ZZ2423 -+007026 777360 ZZ2423=ZZ2423+ZZ2423 -+007026 776740 ZZ2423=ZZ2423+ZZ2423 -+007026 775700 ZZ2423=ZZ2423+ZZ2423 -+007026 773600 ZZ2423=ZZ2423+ZZ2423 -+007026 767400 ZZ2423=ZZ2423+ZZ2423 -+007026 011472 0 8192 -ZZ1423 -+007027 767400 0 ZZ2423 -- mark 3274, -316 /38 hyda -+007030 776606 ZZ2424=ZZ2424+ZZ2424 -+007030 775414 ZZ2424=ZZ2424+ZZ2424 -+007030 773030 ZZ2424=ZZ2424+ZZ2424 -+007030 766060 ZZ2424=ZZ2424+ZZ2424 -+007030 754140 ZZ2424=ZZ2424+ZZ2424 -+007030 730300 ZZ2424=ZZ2424+ZZ2424 -+007030 660600 ZZ2424=ZZ2424+ZZ2424 -+007030 541400 ZZ2424=ZZ2424+ZZ2424 -+007030 011466 0 8192 -ZZ1424 -+007031 541400 0 ZZ2424 -- mark 3276, 236 /14 leon -+007032 000730 ZZ2425=ZZ2425+ZZ2425 -+007032 001660 ZZ2425=ZZ2425+ZZ2425 -+007032 003540 ZZ2425=ZZ2425+ZZ2425 -+007032 007300 ZZ2425=ZZ2425+ZZ2425 -+007032 016600 ZZ2425=ZZ2425+ZZ2425 -+007032 035400 ZZ2425=ZZ2425+ZZ2425 -+007032 073000 ZZ2425=ZZ2425+ZZ2425 -+007032 166000 ZZ2425=ZZ2425+ZZ2425 -+007032 011464 0 8192 -ZZ1425 -+007033 166000 0 ZZ2425 -- mark 3338, -327 /39 hyda -+007034 776560 ZZ2426=ZZ2426+ZZ2426 -+007034 775340 ZZ2426=ZZ2426+ZZ2426 -+007034 772700 ZZ2426=ZZ2426+ZZ2426 -+007034 765600 ZZ2426=ZZ2426+ZZ2426 -+007034 753400 ZZ2426=ZZ2426+ZZ2426 -+007034 727000 ZZ2426=ZZ2426+ZZ2426 -+007034 656000 ZZ2426=ZZ2426+ZZ2426 -+007034 534000 ZZ2426=ZZ2426+ZZ2426 -+007034 011366 0 8192 -ZZ1426 -+007035 534000 0 ZZ2426 -- mark 3385, 194 /29 leon -+007036 000604 ZZ2427=ZZ2427+ZZ2427 -+007036 001410 ZZ2427=ZZ2427+ZZ2427 -+007036 003020 ZZ2427=ZZ2427+ZZ2427 -+007036 006040 ZZ2427=ZZ2427+ZZ2427 -+007036 014100 ZZ2427=ZZ2427+ZZ2427 -+007036 030200 ZZ2427=ZZ2427+ZZ2427 -+007036 060400 ZZ2427=ZZ2427+ZZ2427 -+007036 141000 ZZ2427=ZZ2427+ZZ2427 -+007036 011307 0 8192 -ZZ1427 -+007037 141000 0 ZZ2427 -- mark 3415, -286 /40 hyda -+007040 776702 ZZ2428=ZZ2428+ZZ2428 -+007040 775604 ZZ2428=ZZ2428+ZZ2428 -+007040 773410 ZZ2428=ZZ2428+ZZ2428 -+007040 767020 ZZ2428=ZZ2428+ZZ2428 -+007040 756040 ZZ2428=ZZ2428+ZZ2428 -+007040 734100 ZZ2428=ZZ2428+ZZ2428 -+007040 670200 ZZ2428=ZZ2428+ZZ2428 -+007040 560400 ZZ2428=ZZ2428+ZZ2428 -+007040 011251 0 8192 -ZZ1428 -+007041 560400 0 ZZ2428 -- mark 3428, 239 /31 leon -+007042 000736 ZZ2429=ZZ2429+ZZ2429 -+007042 001674 ZZ2429=ZZ2429+ZZ2429 -+007042 003570 ZZ2429=ZZ2429+ZZ2429 -+007042 007360 ZZ2429=ZZ2429+ZZ2429 -+007042 016740 ZZ2429=ZZ2429+ZZ2429 -+007042 035700 ZZ2429=ZZ2429+ZZ2429 -+007042 073600 ZZ2429=ZZ2429+ZZ2429 -+007042 167400 ZZ2429=ZZ2429+ZZ2429 -+007042 011234 0 8192 -ZZ1429 -+007043 167400 0 ZZ2429 -- mark 3429, 3 /15 sext -+007044 000006 ZZ2430=ZZ2430+ZZ2430 -+007044 000014 ZZ2430=ZZ2430+ZZ2430 -+007044 000030 ZZ2430=ZZ2430+ZZ2430 -+007044 000060 ZZ2430=ZZ2430+ZZ2430 -+007044 000140 ZZ2430=ZZ2430+ZZ2430 -+007044 000300 ZZ2430=ZZ2430+ZZ2430 -+007044 000600 ZZ2430=ZZ2430+ZZ2430 -+007044 001400 ZZ2430=ZZ2430+ZZ2430 -+007044 011233 0 8192 -ZZ1430 -+007045 001400 0 ZZ2430 -- mark 3446, -270 /41 hyda -+007046 776742 ZZ2431=ZZ2431+ZZ2431 -+007046 775704 ZZ2431=ZZ2431+ZZ2431 -+007046 773610 ZZ2431=ZZ2431+ZZ2431 -+007046 767420 ZZ2431=ZZ2431+ZZ2431 -+007046 757040 ZZ2431=ZZ2431+ZZ2431 -+007046 736100 ZZ2431=ZZ2431+ZZ2431 -+007046 674200 ZZ2431=ZZ2431+ZZ2431 -+007046 570400 ZZ2431=ZZ2431+ZZ2431 -+007046 011212 0 8192 -ZZ1431 -+007047 570400 0 ZZ2431 -- mark 3495, 455 /40 leon -+007050 001616 ZZ2432=ZZ2432+ZZ2432 -+007050 003434 ZZ2432=ZZ2432+ZZ2432 -+007050 007070 ZZ2432=ZZ2432+ZZ2432 -+007050 016160 ZZ2432=ZZ2432+ZZ2432 -+007050 034340 ZZ2432=ZZ2432+ZZ2432 -+007050 070700 ZZ2432=ZZ2432+ZZ2432 -+007050 161600 ZZ2432=ZZ2432+ZZ2432 -+007050 343400 ZZ2432=ZZ2432+ZZ2432 -+007050 011131 0 8192 -ZZ1432 -+007051 343400 0 ZZ2432 -- mark 3534, -372 /42 hyda -+007052 776426 ZZ2433=ZZ2433+ZZ2433 -+007052 775054 ZZ2433=ZZ2433+ZZ2433 -+007052 772130 ZZ2433=ZZ2433+ZZ2433 -+007052 764260 ZZ2433=ZZ2433+ZZ2433 -+007052 750540 ZZ2433=ZZ2433+ZZ2433 -+007052 721300 ZZ2433=ZZ2433+ZZ2433 -+007052 642600 ZZ2433=ZZ2433+ZZ2433 -+007052 505400 ZZ2433=ZZ2433+ZZ2433 -+007052 011062 0 8192 -ZZ1433 -+007053 505400 0 ZZ2433 -- mark 3557, -3 /30 sext -+007054 777770 ZZ2434=ZZ2434+ZZ2434 -+007054 777760 ZZ2434=ZZ2434+ZZ2434 -+007054 777740 ZZ2434=ZZ2434+ZZ2434 -+007054 777700 ZZ2434=ZZ2434+ZZ2434 -+007054 777600 ZZ2434=ZZ2434+ZZ2434 -+007054 777400 ZZ2434=ZZ2434+ZZ2434 -+007054 777000 ZZ2434=ZZ2434+ZZ2434 -+007054 776000 ZZ2434=ZZ2434+ZZ2434 -+007054 011033 0 8192 -ZZ1434 -+007055 776000 0 ZZ2434 -- mark 3570, 223 /47 leon -+007056 000676 ZZ2435=ZZ2435+ZZ2435 -+007056 001574 ZZ2435=ZZ2435+ZZ2435 -+007056 003370 ZZ2435=ZZ2435+ZZ2435 -+007056 006760 ZZ2435=ZZ2435+ZZ2435 -+007056 015740 ZZ2435=ZZ2435+ZZ2435 -+007056 033700 ZZ2435=ZZ2435+ZZ2435 -+007056 067600 ZZ2435=ZZ2435+ZZ2435 -+007056 157400 ZZ2435=ZZ2435+ZZ2435 -+007056 011016 0 8192 -ZZ1435 -+007057 157400 0 ZZ2435 -- mark 3726, -404 /al crat -+007060 776326 ZZ2436=ZZ2436+ZZ2436 -+007060 774654 ZZ2436=ZZ2436+ZZ2436 -+007060 771530 ZZ2436=ZZ2436+ZZ2436 -+007060 763260 ZZ2436=ZZ2436+ZZ2436 -+007060 746540 ZZ2436=ZZ2436+ZZ2436 -+007060 715300 ZZ2436=ZZ2436+ZZ2436 -+007060 632600 ZZ2436=ZZ2436+ZZ2436 -+007060 465400 ZZ2436=ZZ2436+ZZ2436 -+007060 010562 0 8192 -ZZ1436 -+007061 465400 0 ZZ2436 -- mark 3736, -44 /61 leon -+007062 777646 ZZ2437=ZZ2437+ZZ2437 -+007062 777514 ZZ2437=ZZ2437+ZZ2437 -+007062 777230 ZZ2437=ZZ2437+ZZ2437 -+007062 776460 ZZ2437=ZZ2437+ZZ2437 -+007062 775140 ZZ2437=ZZ2437+ZZ2437 -+007062 772300 ZZ2437=ZZ2437+ZZ2437 -+007062 764600 ZZ2437=ZZ2437+ZZ2437 -+007062 751400 ZZ2437=ZZ2437+ZZ2437 -+007062 010550 0 8192 -ZZ1437 -+007063 751400 0 ZZ2437 -- mark 3738, 471 /60 leon -+007064 001656 ZZ2438=ZZ2438+ZZ2438 -+007064 003534 ZZ2438=ZZ2438+ZZ2438 -+007064 007270 ZZ2438=ZZ2438+ZZ2438 -+007064 016560 ZZ2438=ZZ2438+ZZ2438 -+007064 035340 ZZ2438=ZZ2438+ZZ2438 -+007064 072700 ZZ2438=ZZ2438+ZZ2438 -+007064 165600 ZZ2438=ZZ2438+ZZ2438 -+007064 353400 ZZ2438=ZZ2438+ZZ2438 -+007064 010546 0 8192 -ZZ1438 -+007065 353400 0 ZZ2438 -- mark 3754, 179 /63 leon -+007066 000546 ZZ2439=ZZ2439+ZZ2439 -+007066 001314 ZZ2439=ZZ2439+ZZ2439 -+007066 002630 ZZ2439=ZZ2439+ZZ2439 -+007066 005460 ZZ2439=ZZ2439+ZZ2439 -+007066 013140 ZZ2439=ZZ2439+ZZ2439 -+007066 026300 ZZ2439=ZZ2439+ZZ2439 -+007066 054600 ZZ2439=ZZ2439+ZZ2439 -+007066 131400 ZZ2439=ZZ2439+ZZ2439 -+007066 010526 0 8192 -ZZ1439 -+007067 131400 0 ZZ2439 -- mark 3793, -507 /11 crat -+007070 776010 ZZ2440=ZZ2440+ZZ2440 -+007070 774020 ZZ2440=ZZ2440+ZZ2440 -+007070 770040 ZZ2440=ZZ2440+ZZ2440 -+007070 760100 ZZ2440=ZZ2440+ZZ2440 -+007070 740200 ZZ2440=ZZ2440+ZZ2440 -+007070 700400 ZZ2440=ZZ2440+ZZ2440 -+007070 601000 ZZ2440=ZZ2440+ZZ2440 -+007070 402000 ZZ2440=ZZ2440+ZZ2440 -+007070 010457 0 8192 -ZZ1440 -+007071 402000 0 ZZ2440 -- mark 3821, -71 /74 leon -+007072 777560 ZZ2441=ZZ2441+ZZ2441 -+007072 777340 ZZ2441=ZZ2441+ZZ2441 -+007072 776700 ZZ2441=ZZ2441+ZZ2441 -+007072 775600 ZZ2441=ZZ2441+ZZ2441 -+007072 773400 ZZ2441=ZZ2441+ZZ2441 -+007072 767000 ZZ2441=ZZ2441+ZZ2441 -+007072 756000 ZZ2441=ZZ2441+ZZ2441 -+007072 734000 ZZ2441=ZZ2441+ZZ2441 -+007072 010423 0 8192 -ZZ1441 -+007073 734000 0 ZZ2441 -- mark 3836, -324 /12 crat -+007074 776566 ZZ2442=ZZ2442+ZZ2442 -+007074 775354 ZZ2442=ZZ2442+ZZ2442 -+007074 772730 ZZ2442=ZZ2442+ZZ2442 -+007074 765660 ZZ2442=ZZ2442+ZZ2442 -+007074 753540 ZZ2442=ZZ2442+ZZ2442 -+007074 727300 ZZ2442=ZZ2442+ZZ2442 -+007074 656600 ZZ2442=ZZ2442+ZZ2442 -+007074 535400 ZZ2442=ZZ2442+ZZ2442 -+007074 010404 0 8192 -ZZ1442 -+007075 535400 0 ZZ2442 -- mark 3846, 150 /77 leon -+007076 000454 ZZ2443=ZZ2443+ZZ2443 -+007076 001130 ZZ2443=ZZ2443+ZZ2443 -+007076 002260 ZZ2443=ZZ2443+ZZ2443 -+007076 004540 ZZ2443=ZZ2443+ZZ2443 -+007076 011300 ZZ2443=ZZ2443+ZZ2443 -+007076 022600 ZZ2443=ZZ2443+ZZ2443 -+007076 045400 ZZ2443=ZZ2443+ZZ2443 -+007076 113000 ZZ2443=ZZ2443+ZZ2443 -+007076 010372 0 8192 -ZZ1443 -+007077 113000 0 ZZ2443 -- mark 3861, 252 /78 leon -+007100 000770 ZZ2444=ZZ2444+ZZ2444 -+007100 001760 ZZ2444=ZZ2444+ZZ2444 -+007100 003740 ZZ2444=ZZ2444+ZZ2444 -+007100 007700 ZZ2444=ZZ2444+ZZ2444 -+007100 017600 ZZ2444=ZZ2444+ZZ2444 -+007100 037400 ZZ2444=ZZ2444+ZZ2444 -+007100 077000 ZZ2444=ZZ2444+ZZ2444 -+007100 176000 ZZ2444=ZZ2444+ZZ2444 -+007100 010353 0 8192 -ZZ1444 -+007101 176000 0 ZZ2444 -- mark 3868, -390 /15 crat -+007102 776362 ZZ2445=ZZ2445+ZZ2445 -+007102 774744 ZZ2445=ZZ2445+ZZ2445 -+007102 771710 ZZ2445=ZZ2445+ZZ2445 -+007102 763620 ZZ2445=ZZ2445+ZZ2445 -+007102 747440 ZZ2445=ZZ2445+ZZ2445 -+007102 717100 ZZ2445=ZZ2445+ZZ2445 -+007102 636200 ZZ2445=ZZ2445+ZZ2445 -+007102 474400 ZZ2445=ZZ2445+ZZ2445 -+007102 010344 0 8192 -ZZ1445 -+007103 474400 0 ZZ2445 -- mark 3935, -211 /21 crat -+007104 777130 ZZ2446=ZZ2446+ZZ2446 -+007104 776260 ZZ2446=ZZ2446+ZZ2446 -+007104 774540 ZZ2446=ZZ2446+ZZ2446 -+007104 771300 ZZ2446=ZZ2446+ZZ2446 -+007104 762600 ZZ2446=ZZ2446+ZZ2446 -+007104 745400 ZZ2446=ZZ2446+ZZ2446 -+007104 713000 ZZ2446=ZZ2446+ZZ2446 -+007104 626000 ZZ2446=ZZ2446+ZZ2446 -+007104 010241 0 8192 -ZZ1446 -+007105 626000 0 ZZ2446 -- mark 3936, -6 /91 leon -+007106 777762 ZZ2447=ZZ2447+ZZ2447 -+007106 777744 ZZ2447=ZZ2447+ZZ2447 -+007106 777710 ZZ2447=ZZ2447+ZZ2447 -+007106 777620 ZZ2447=ZZ2447+ZZ2447 -+007106 777440 ZZ2447=ZZ2447+ZZ2447 -+007106 777100 ZZ2447=ZZ2447+ZZ2447 -+007106 776200 ZZ2447=ZZ2447+ZZ2447 -+007106 774400 ZZ2447=ZZ2447+ZZ2447 -+007106 010240 0 8192 -ZZ1447 -+007107 774400 0 ZZ2447 -- mark 3981, -405 /27 crat -+007110 776324 ZZ2448=ZZ2448+ZZ2448 -+007110 774650 ZZ2448=ZZ2448+ZZ2448 -+007110 771520 ZZ2448=ZZ2448+ZZ2448 -+007110 763240 ZZ2448=ZZ2448+ZZ2448 -+007110 746500 ZZ2448=ZZ2448+ZZ2448 -+007110 715200 ZZ2448=ZZ2448+ZZ2448 -+007110 632400 ZZ2448=ZZ2448+ZZ2448 -+007110 465000 ZZ2448=ZZ2448+ZZ2448 -+007110 010163 0 8192 -ZZ1448 -+007111 465000 0 ZZ2448 -- mark 3986, 161 / 3 virg -+007112 000502 ZZ2449=ZZ2449+ZZ2449 -+007112 001204 ZZ2449=ZZ2449+ZZ2449 -+007112 002410 ZZ2449=ZZ2449+ZZ2449 -+007112 005020 ZZ2449=ZZ2449+ZZ2449 -+007112 012040 ZZ2449=ZZ2449+ZZ2449 -+007112 024100 ZZ2449=ZZ2449+ZZ2449 -+007112 050200 ZZ2449=ZZ2449+ZZ2449 -+007112 120400 ZZ2449=ZZ2449+ZZ2449 -+007112 010156 0 8192 -ZZ1449 -+007113 120400 0 ZZ2449 -- mark 3998, 473 /93 leon -+007114 001662 ZZ2450=ZZ2450+ZZ2450 -+007114 003544 ZZ2450=ZZ2450+ZZ2450 -+007114 007310 ZZ2450=ZZ2450+ZZ2450 -+007114 016620 ZZ2450=ZZ2450+ZZ2450 -+007114 035440 ZZ2450=ZZ2450+ZZ2450 -+007114 073100 ZZ2450=ZZ2450+ZZ2450 -+007114 166200 ZZ2450=ZZ2450+ZZ2450 -+007114 354400 ZZ2450=ZZ2450+ZZ2450 -+007114 010142 0 8192 -ZZ1450 -+007115 354400 0 ZZ2450 -- mark 4013, 53 / 5 virg -+007116 000152 ZZ2451=ZZ2451+ZZ2451 -+007116 000324 ZZ2451=ZZ2451+ZZ2451 -+007116 000650 ZZ2451=ZZ2451+ZZ2451 -+007116 001520 ZZ2451=ZZ2451+ZZ2451 -+007116 003240 ZZ2451=ZZ2451+ZZ2451 -+007116 006500 ZZ2451=ZZ2451+ZZ2451 -+007116 015200 ZZ2451=ZZ2451+ZZ2451 -+007116 032400 ZZ2451=ZZ2451+ZZ2451 -+007116 010123 0 8192 -ZZ1451 -+007117 032400 0 ZZ2451 -- mark 4072, 163 / 8 virg -+007120 000506 ZZ2452=ZZ2452+ZZ2452 -+007120 001214 ZZ2452=ZZ2452+ZZ2452 -+007120 002430 ZZ2452=ZZ2452+ZZ2452 -+007120 005060 ZZ2452=ZZ2452+ZZ2452 -+007120 012140 ZZ2452=ZZ2452+ZZ2452 -+007120 024300 ZZ2452=ZZ2452+ZZ2452 -+007120 050600 ZZ2452=ZZ2452+ZZ2452 -+007120 121400 ZZ2452=ZZ2452+ZZ2452 -+007120 010030 0 8192 -ZZ1452 -+007121 121400 0 ZZ2452 -- mark 4097, 211 / 9 virg -+007122 000646 ZZ2453=ZZ2453+ZZ2453 -+007122 001514 ZZ2453=ZZ2453+ZZ2453 -+007122 003230 ZZ2453=ZZ2453+ZZ2453 -+007122 006460 ZZ2453=ZZ2453+ZZ2453 -+007122 015140 ZZ2453=ZZ2453+ZZ2453 -+007122 032300 ZZ2453=ZZ2453+ZZ2453 -+007122 064600 ZZ2453=ZZ2453+ZZ2453 -+007122 151400 ZZ2453=ZZ2453+ZZ2453 -+007122 007777 0 8192 -ZZ1453 -+007123 151400 0 ZZ2453 -- mark 4180, -3 /15 virg -+007124 777770 ZZ2454=ZZ2454+ZZ2454 -+007124 777760 ZZ2454=ZZ2454+ZZ2454 -+007124 777740 ZZ2454=ZZ2454+ZZ2454 -+007124 777700 ZZ2454=ZZ2454+ZZ2454 -+007124 777600 ZZ2454=ZZ2454+ZZ2454 -+007124 777400 ZZ2454=ZZ2454+ZZ2454 -+007124 777000 ZZ2454=ZZ2454+ZZ2454 -+007124 776000 ZZ2454=ZZ2454+ZZ2454 -+007124 007654 0 8192 -ZZ1454 -+007125 776000 0 ZZ2454 -- mark 4185, 418 /11 coma -+007126 001504 ZZ2455=ZZ2455+ZZ2455 -+007126 003210 ZZ2455=ZZ2455+ZZ2455 -+007126 006420 ZZ2455=ZZ2455+ZZ2455 -+007126 015040 ZZ2455=ZZ2455+ZZ2455 -+007126 032100 ZZ2455=ZZ2455+ZZ2455 -+007126 064200 ZZ2455=ZZ2455+ZZ2455 -+007126 150400 ZZ2455=ZZ2455+ZZ2455 -+007126 321000 ZZ2455=ZZ2455+ZZ2455 -+007126 007647 0 8192 -ZZ1455 -+007127 321000 0 ZZ2455 -- mark 4249, -356 / 8 corv -+007130 776466 ZZ2456=ZZ2456+ZZ2456 -+007130 775154 ZZ2456=ZZ2456+ZZ2456 -+007130 772330 ZZ2456=ZZ2456+ZZ2456 -+007130 764660 ZZ2456=ZZ2456+ZZ2456 -+007130 751540 ZZ2456=ZZ2456+ZZ2456 -+007130 723300 ZZ2456=ZZ2456+ZZ2456 -+007130 646600 ZZ2456=ZZ2456+ZZ2456 -+007130 515400 ZZ2456=ZZ2456+ZZ2456 -+007130 007547 0 8192 -ZZ1456 -+007131 515400 0 ZZ2456 -- mark 4290, -170 /26 virg -+007132 777252 ZZ2457=ZZ2457+ZZ2457 -+007132 776524 ZZ2457=ZZ2457+ZZ2457 -+007132 775250 ZZ2457=ZZ2457+ZZ2457 -+007132 772520 ZZ2457=ZZ2457+ZZ2457 -+007132 765240 ZZ2457=ZZ2457+ZZ2457 -+007132 752500 ZZ2457=ZZ2457+ZZ2457 -+007132 725200 ZZ2457=ZZ2457+ZZ2457 -+007132 652400 ZZ2457=ZZ2457+ZZ2457 -+007132 007476 0 8192 -ZZ1457 -+007133 652400 0 ZZ2457 -- mark 4305, 245 /30 virg -+007134 000752 ZZ2458=ZZ2458+ZZ2458 -+007134 001724 ZZ2458=ZZ2458+ZZ2458 -+007134 003650 ZZ2458=ZZ2458+ZZ2458 -+007134 007520 ZZ2458=ZZ2458+ZZ2458 -+007134 017240 ZZ2458=ZZ2458+ZZ2458 -+007134 036500 ZZ2458=ZZ2458+ZZ2458 -+007134 075200 ZZ2458=ZZ2458+ZZ2458 -+007134 172400 ZZ2458=ZZ2458+ZZ2458 -+007134 007457 0 8192 -ZZ1458 -+007135 172400 0 ZZ2458 -- mark 4376, -205 /40 virg -+007136 777144 ZZ2459=ZZ2459+ZZ2459 -+007136 776310 ZZ2459=ZZ2459+ZZ2459 -+007136 774620 ZZ2459=ZZ2459+ZZ2459 -+007136 771440 ZZ2459=ZZ2459+ZZ2459 -+007136 763100 ZZ2459=ZZ2459+ZZ2459 -+007136 746200 ZZ2459=ZZ2459+ZZ2459 -+007136 714400 ZZ2459=ZZ2459+ZZ2459 -+007136 631000 ZZ2459=ZZ2459+ZZ2459 -+007136 007350 0 8192 -ZZ1459 -+007137 631000 0 ZZ2459 -- mark 4403, 409 /36 coma -+007140 001462 ZZ2460=ZZ2460+ZZ2460 -+007140 003144 ZZ2460=ZZ2460+ZZ2460 -+007140 006310 ZZ2460=ZZ2460+ZZ2460 -+007140 014620 ZZ2460=ZZ2460+ZZ2460 -+007140 031440 ZZ2460=ZZ2460+ZZ2460 -+007140 063100 ZZ2460=ZZ2460+ZZ2460 -+007140 146200 ZZ2460=ZZ2460+ZZ2460 -+007140 314400 ZZ2460=ZZ2460+ZZ2460 -+007140 007315 0 8192 -ZZ1460 -+007141 314400 0 ZZ2460 -- mark 4465, -114 /51 virg -+007142 777432 ZZ2461=ZZ2461+ZZ2461 -+007142 777064 ZZ2461=ZZ2461+ZZ2461 -+007142 776150 ZZ2461=ZZ2461+ZZ2461 -+007142 774320 ZZ2461=ZZ2461+ZZ2461 -+007142 770640 ZZ2461=ZZ2461+ZZ2461 -+007142 761500 ZZ2461=ZZ2461+ZZ2461 -+007142 743200 ZZ2461=ZZ2461+ZZ2461 -+007142 706400 ZZ2461=ZZ2461+ZZ2461 -+007142 007217 0 8192 -ZZ1461 -+007143 706400 0 ZZ2461 -- mark 4466, 411 /42 coma -+007144 001466 ZZ2462=ZZ2462+ZZ2462 -+007144 003154 ZZ2462=ZZ2462+ZZ2462 -+007144 006330 ZZ2462=ZZ2462+ZZ2462 -+007144 014660 ZZ2462=ZZ2462+ZZ2462 -+007144 031540 ZZ2462=ZZ2462+ZZ2462 -+007144 063300 ZZ2462=ZZ2462+ZZ2462 -+007144 146600 ZZ2462=ZZ2462+ZZ2462 -+007144 315400 ZZ2462=ZZ2462+ZZ2462 -+007144 007216 0 8192 -ZZ1462 -+007145 315400 0 ZZ2462 -- mark 4512, -404 /61 virg -+007146 776326 ZZ2463=ZZ2463+ZZ2463 -+007146 774654 ZZ2463=ZZ2463+ZZ2463 -+007146 771530 ZZ2463=ZZ2463+ZZ2463 -+007146 763260 ZZ2463=ZZ2463+ZZ2463 -+007146 746540 ZZ2463=ZZ2463+ZZ2463 -+007146 715300 ZZ2463=ZZ2463+ZZ2463 -+007146 632600 ZZ2463=ZZ2463+ZZ2463 -+007146 465400 ZZ2463=ZZ2463+ZZ2463 -+007146 007140 0 8192 -ZZ1463 -+007147 465400 0 ZZ2463 -- mark 4563, -352 /69 virg -+007150 776476 ZZ2464=ZZ2464+ZZ2464 -+007150 775174 ZZ2464=ZZ2464+ZZ2464 -+007150 772370 ZZ2464=ZZ2464+ZZ2464 -+007150 764760 ZZ2464=ZZ2464+ZZ2464 -+007150 751740 ZZ2464=ZZ2464+ZZ2464 -+007150 723700 ZZ2464=ZZ2464+ZZ2464 -+007150 647600 ZZ2464=ZZ2464+ZZ2464 -+007150 517400 ZZ2464=ZZ2464+ZZ2464 -+007150 007055 0 8192 -ZZ1464 -+007151 517400 0 ZZ2464 -- mark 4590, -131 /74 virg -+007152 777370 ZZ2465=ZZ2465+ZZ2465 -+007152 776760 ZZ2465=ZZ2465+ZZ2465 -+007152 775740 ZZ2465=ZZ2465+ZZ2465 -+007152 773700 ZZ2465=ZZ2465+ZZ2465 -+007152 767600 ZZ2465=ZZ2465+ZZ2465 -+007152 757400 ZZ2465=ZZ2465+ZZ2465 -+007152 737000 ZZ2465=ZZ2465+ZZ2465 -+007152 676000 ZZ2465=ZZ2465+ZZ2465 -+007152 007022 0 8192 -ZZ1465 -+007153 676000 0 ZZ2465 -- mark 4603, 95 /78 virg -+007154 000276 ZZ2466=ZZ2466+ZZ2466 -+007154 000574 ZZ2466=ZZ2466+ZZ2466 -+007154 001370 ZZ2466=ZZ2466+ZZ2466 -+007154 002760 ZZ2466=ZZ2466+ZZ2466 -+007154 005740 ZZ2466=ZZ2466+ZZ2466 -+007154 013700 ZZ2466=ZZ2466+ZZ2466 -+007154 027600 ZZ2466=ZZ2466+ZZ2466 -+007154 057400 ZZ2466=ZZ2466+ZZ2466 -+007154 007005 0 8192 -ZZ1466 -+007155 057400 0 ZZ2466 -- mark 4679, 409 / 4 boot -+007156 001462 ZZ2467=ZZ2467+ZZ2467 -+007156 003144 ZZ2467=ZZ2467+ZZ2467 -+007156 006310 ZZ2467=ZZ2467+ZZ2467 -+007156 014620 ZZ2467=ZZ2467+ZZ2467 -+007156 031440 ZZ2467=ZZ2467+ZZ2467 -+007156 063100 ZZ2467=ZZ2467+ZZ2467 -+007156 146200 ZZ2467=ZZ2467+ZZ2467 -+007156 314400 ZZ2467=ZZ2467+ZZ2467 -+007156 006671 0 8192 -ZZ1467 -+007157 314400 0 ZZ2467 -- mark 4691, 371 / 5 boot -+007160 001346 ZZ2468=ZZ2468+ZZ2468 -+007160 002714 ZZ2468=ZZ2468+ZZ2468 -+007160 005630 ZZ2468=ZZ2468+ZZ2468 -+007160 013460 ZZ2468=ZZ2468+ZZ2468 -+007160 027140 ZZ2468=ZZ2468+ZZ2468 -+007160 056300 ZZ2468=ZZ2468+ZZ2468 -+007160 134600 ZZ2468=ZZ2468+ZZ2468 -+007160 271400 ZZ2468=ZZ2468+ZZ2468 -+007160 006655 0 8192 -ZZ1468 -+007161 271400 0 ZZ2468 -- mark 4759, 46 /93 virg -+007162 000134 ZZ2469=ZZ2469+ZZ2469 -+007162 000270 ZZ2469=ZZ2469+ZZ2469 -+007162 000560 ZZ2469=ZZ2469+ZZ2469 -+007162 001340 ZZ2469=ZZ2469+ZZ2469 -+007162 002700 ZZ2469=ZZ2469+ZZ2469 -+007162 005600 ZZ2469=ZZ2469+ZZ2469 -+007162 013400 ZZ2469=ZZ2469+ZZ2469 -+007162 027000 ZZ2469=ZZ2469+ZZ2469 -+007162 006551 0 8192 -ZZ1469 -+007163 027000 0 ZZ2469 -- mark 4820, 66 / -+007164 000204 ZZ2470=ZZ2470+ZZ2470 -+007164 000410 ZZ2470=ZZ2470+ZZ2470 -+007164 001020 ZZ2470=ZZ2470+ZZ2470 -+007164 002040 ZZ2470=ZZ2470+ZZ2470 -+007164 004100 ZZ2470=ZZ2470+ZZ2470 -+007164 010200 ZZ2470=ZZ2470+ZZ2470 -+007164 020400 ZZ2470=ZZ2470+ZZ2470 -+007164 041000 ZZ2470=ZZ2470+ZZ2470 -+007164 006454 0 8192 -ZZ1470 -+007165 041000 0 ZZ2470 -- mark 4822, -223 /98 virg -+007166 777100 ZZ2471=ZZ2471+ZZ2471 -+007166 776200 ZZ2471=ZZ2471+ZZ2471 -+007166 774400 ZZ2471=ZZ2471+ZZ2471 -+007166 771000 ZZ2471=ZZ2471+ZZ2471 -+007166 762000 ZZ2471=ZZ2471+ZZ2471 -+007166 744000 ZZ2471=ZZ2471+ZZ2471 -+007166 710000 ZZ2471=ZZ2471+ZZ2471 -+007166 620000 ZZ2471=ZZ2471+ZZ2471 -+007166 006452 0 8192 -ZZ1471 -+007167 620000 0 ZZ2471 -- mark 4840, -126 /99 virg -+007170 777402 ZZ2472=ZZ2472+ZZ2472 -+007170 777004 ZZ2472=ZZ2472+ZZ2472 -+007170 776010 ZZ2472=ZZ2472+ZZ2472 -+007170 774020 ZZ2472=ZZ2472+ZZ2472 -+007170 770040 ZZ2472=ZZ2472+ZZ2472 -+007170 760100 ZZ2472=ZZ2472+ZZ2472 -+007170 740200 ZZ2472=ZZ2472+ZZ2472 -+007170 700400 ZZ2472=ZZ2472+ZZ2472 -+007170 006430 0 8192 -ZZ1472 -+007171 700400 0 ZZ2472 -- mark 4857, -294 /100 virg -+007172 776662 ZZ2473=ZZ2473+ZZ2473 -+007172 775544 ZZ2473=ZZ2473+ZZ2473 -+007172 773310 ZZ2473=ZZ2473+ZZ2473 -+007172 766620 ZZ2473=ZZ2473+ZZ2473 -+007172 755440 ZZ2473=ZZ2473+ZZ2473 -+007172 733100 ZZ2473=ZZ2473+ZZ2473 -+007172 666200 ZZ2473=ZZ2473+ZZ2473 -+007172 554400 ZZ2473=ZZ2473+ZZ2473 -+007172 006407 0 8192 -ZZ1473 -+007173 554400 0 ZZ2473 -- mark 4864, 382 /20 boot -+007174 001374 ZZ2474=ZZ2474+ZZ2474 -+007174 002770 ZZ2474=ZZ2474+ZZ2474 -+007174 005760 ZZ2474=ZZ2474+ZZ2474 -+007174 013740 ZZ2474=ZZ2474+ZZ2474 -+007174 027700 ZZ2474=ZZ2474+ZZ2474 -+007174 057600 ZZ2474=ZZ2474+ZZ2474 -+007174 137400 ZZ2474=ZZ2474+ZZ2474 -+007174 277000 ZZ2474=ZZ2474+ZZ2474 -+007174 006400 0 8192 -ZZ1474 -+007175 277000 0 ZZ2474 -- mark 4910, -41 /105 virg -+007176 777654 ZZ2475=ZZ2475+ZZ2475 -+007176 777530 ZZ2475=ZZ2475+ZZ2475 -+007176 777260 ZZ2475=ZZ2475+ZZ2475 -+007176 776540 ZZ2475=ZZ2475+ZZ2475 -+007176 775300 ZZ2475=ZZ2475+ZZ2475 -+007176 772600 ZZ2475=ZZ2475+ZZ2475 -+007176 765400 ZZ2475=ZZ2475+ZZ2475 -+007176 753000 ZZ2475=ZZ2475+ZZ2475 -+007176 006322 0 8192 -ZZ1475 -+007177 753000 0 ZZ2475 -- mark 4984, 383 /29 boot -+007200 001376 ZZ2476=ZZ2476+ZZ2476 -+007200 002774 ZZ2476=ZZ2476+ZZ2476 -+007200 005770 ZZ2476=ZZ2476+ZZ2476 -+007200 013760 ZZ2476=ZZ2476+ZZ2476 -+007200 027740 ZZ2476=ZZ2476+ZZ2476 -+007200 057700 ZZ2476=ZZ2476+ZZ2476 -+007200 137600 ZZ2476=ZZ2476+ZZ2476 -+007200 277400 ZZ2476=ZZ2476+ZZ2476 -+007200 006210 0 8192 -ZZ1476 -+007201 277400 0 ZZ2476 -- mark 4986, 322 /30 boot -+007202 001204 ZZ2477=ZZ2477+ZZ2477 -+007202 002410 ZZ2477=ZZ2477+ZZ2477 -+007202 005020 ZZ2477=ZZ2477+ZZ2477 -+007202 012040 ZZ2477=ZZ2477+ZZ2477 -+007202 024100 ZZ2477=ZZ2477+ZZ2477 -+007202 050200 ZZ2477=ZZ2477+ZZ2477 -+007202 120400 ZZ2477=ZZ2477+ZZ2477 -+007202 241000 ZZ2477=ZZ2477+ZZ2477 -+007202 006206 0 8192 -ZZ1477 -+007203 241000 0 ZZ2477 -- mark 4994, -119 /107 virg -+007204 777420 ZZ2478=ZZ2478+ZZ2478 -+007204 777040 ZZ2478=ZZ2478+ZZ2478 -+007204 776100 ZZ2478=ZZ2478+ZZ2478 -+007204 774200 ZZ2478=ZZ2478+ZZ2478 -+007204 770400 ZZ2478=ZZ2478+ZZ2478 -+007204 761000 ZZ2478=ZZ2478+ZZ2478 -+007204 742000 ZZ2478=ZZ2478+ZZ2478 -+007204 704000 ZZ2478=ZZ2478+ZZ2478 -+007204 006176 0 8192 -ZZ1478 -+007205 704000 0 ZZ2478 -- mark 5009, 396 /35 boot -+007206 001430 ZZ2479=ZZ2479+ZZ2479 -+007206 003060 ZZ2479=ZZ2479+ZZ2479 -+007206 006140 ZZ2479=ZZ2479+ZZ2479 -+007206 014300 ZZ2479=ZZ2479+ZZ2479 -+007206 030600 ZZ2479=ZZ2479+ZZ2479 -+007206 061400 ZZ2479=ZZ2479+ZZ2479 -+007206 143000 ZZ2479=ZZ2479+ZZ2479 -+007206 306000 ZZ2479=ZZ2479+ZZ2479 -+007206 006157 0 8192 -ZZ1479 -+007207 306000 0 ZZ2479 -- mark 5013, 53 /109 virg -+007210 000152 ZZ2480=ZZ2480+ZZ2480 -+007210 000324 ZZ2480=ZZ2480+ZZ2480 -+007210 000650 ZZ2480=ZZ2480+ZZ2480 -+007210 001520 ZZ2480=ZZ2480+ZZ2480 -+007210 003240 ZZ2480=ZZ2480+ZZ2480 -+007210 006500 ZZ2480=ZZ2480+ZZ2480 -+007210 015200 ZZ2480=ZZ2480+ZZ2480 -+007210 032400 ZZ2480=ZZ2480+ZZ2480 -+007210 006153 0 8192 -ZZ1480 -+007211 032400 0 ZZ2480 -- mark 5045, 444 /37 boot -+007212 001570 ZZ2481=ZZ2481+ZZ2481 -+007212 003360 ZZ2481=ZZ2481+ZZ2481 -+007212 006740 ZZ2481=ZZ2481+ZZ2481 -+007212 015700 ZZ2481=ZZ2481+ZZ2481 -+007212 033600 ZZ2481=ZZ2481+ZZ2481 -+007212 067400 ZZ2481=ZZ2481+ZZ2481 -+007212 157000 ZZ2481=ZZ2481+ZZ2481 -+007212 336000 ZZ2481=ZZ2481+ZZ2481 -+007212 006113 0 8192 -ZZ1481 -+007213 336000 0 ZZ2481 -- mark 5074, -90 /16 libr -+007214 777512 ZZ2482=ZZ2482+ZZ2482 -+007214 777224 ZZ2482=ZZ2482+ZZ2482 -+007214 776450 ZZ2482=ZZ2482+ZZ2482 -+007214 775120 ZZ2482=ZZ2482+ZZ2482 -+007214 772240 ZZ2482=ZZ2482+ZZ2482 -+007214 764500 ZZ2482=ZZ2482+ZZ2482 -+007214 751200 ZZ2482=ZZ2482+ZZ2482 -+007214 722400 ZZ2482=ZZ2482+ZZ2482 -+007214 006056 0 8192 -ZZ1482 -+007215 722400 0 ZZ2482 -- mark 5108, 57 /110 virg -+007216 000162 ZZ2483=ZZ2483+ZZ2483 -+007216 000344 ZZ2483=ZZ2483+ZZ2483 -+007216 000710 ZZ2483=ZZ2483+ZZ2483 -+007216 001620 ZZ2483=ZZ2483+ZZ2483 -+007216 003440 ZZ2483=ZZ2483+ZZ2483 -+007216 007100 ZZ2483=ZZ2483+ZZ2483 -+007216 016200 ZZ2483=ZZ2483+ZZ2483 -+007216 034400 ZZ2483=ZZ2483+ZZ2483 -+007216 006014 0 8192 -ZZ1483 -+007217 034400 0 ZZ2483 -- mark 5157, -442 /24 libr -+007220 776212 ZZ2484=ZZ2484+ZZ2484 -+007220 774424 ZZ2484=ZZ2484+ZZ2484 -+007220 771050 ZZ2484=ZZ2484+ZZ2484 -+007220 762120 ZZ2484=ZZ2484+ZZ2484 -+007220 744240 ZZ2484=ZZ2484+ZZ2484 -+007220 710500 ZZ2484=ZZ2484+ZZ2484 -+007220 621200 ZZ2484=ZZ2484+ZZ2484 -+007220 442400 ZZ2484=ZZ2484+ZZ2484 -+007220 005733 0 8192 -ZZ1484 -+007221 442400 0 ZZ2484 -- mark 5283, -221 /37 libr -+007222 777104 ZZ2485=ZZ2485+ZZ2485 -+007222 776210 ZZ2485=ZZ2485+ZZ2485 -+007222 774420 ZZ2485=ZZ2485+ZZ2485 -+007222 771040 ZZ2485=ZZ2485+ZZ2485 -+007222 762100 ZZ2485=ZZ2485+ZZ2485 -+007222 744200 ZZ2485=ZZ2485+ZZ2485 -+007222 710400 ZZ2485=ZZ2485+ZZ2485 -+007222 621000 ZZ2485=ZZ2485+ZZ2485 -+007222 005535 0 8192 -ZZ1485 -+007223 621000 0 ZZ2485 -- mark 5290, -329 /38 libr -+007224 776554 ZZ2486=ZZ2486+ZZ2486 -+007224 775330 ZZ2486=ZZ2486+ZZ2486 -+007224 772660 ZZ2486=ZZ2486+ZZ2486 -+007224 765540 ZZ2486=ZZ2486+ZZ2486 -+007224 753300 ZZ2486=ZZ2486+ZZ2486 -+007224 726600 ZZ2486=ZZ2486+ZZ2486 -+007224 655400 ZZ2486=ZZ2486+ZZ2486 -+007224 533000 ZZ2486=ZZ2486+ZZ2486 -+007224 005526 0 8192 -ZZ1486 -+007225 533000 0 ZZ2486 -- mark 5291, 247 /13 serp -+007226 000756 ZZ2487=ZZ2487+ZZ2487 -+007226 001734 ZZ2487=ZZ2487+ZZ2487 -+007226 003670 ZZ2487=ZZ2487+ZZ2487 -+007226 007560 ZZ2487=ZZ2487+ZZ2487 -+007226 017340 ZZ2487=ZZ2487+ZZ2487 -+007226 036700 ZZ2487=ZZ2487+ZZ2487 -+007226 075600 ZZ2487=ZZ2487+ZZ2487 -+007226 173400 ZZ2487=ZZ2487+ZZ2487 -+007226 005525 0 8192 -ZZ1487 -+007227 173400 0 ZZ2487 -- mark 5326, -440 /43 libr -+007230 776216 ZZ2488=ZZ2488+ZZ2488 -+007230 774434 ZZ2488=ZZ2488+ZZ2488 -+007230 771070 ZZ2488=ZZ2488+ZZ2488 -+007230 762160 ZZ2488=ZZ2488+ZZ2488 -+007230 744340 ZZ2488=ZZ2488+ZZ2488 -+007230 710700 ZZ2488=ZZ2488+ZZ2488 -+007230 621600 ZZ2488=ZZ2488+ZZ2488 -+007230 443400 ZZ2488=ZZ2488+ZZ2488 -+007230 005462 0 8192 -ZZ1488 -+007231 443400 0 ZZ2488 -- mark 5331, 455 /21 serp -+007232 001616 ZZ2489=ZZ2489+ZZ2489 -+007232 003434 ZZ2489=ZZ2489+ZZ2489 -+007232 007070 ZZ2489=ZZ2489+ZZ2489 -+007232 016160 ZZ2489=ZZ2489+ZZ2489 -+007232 034340 ZZ2489=ZZ2489+ZZ2489 -+007232 070700 ZZ2489=ZZ2489+ZZ2489 -+007232 161600 ZZ2489=ZZ2489+ZZ2489 -+007232 343400 ZZ2489=ZZ2489+ZZ2489 -+007232 005455 0 8192 -ZZ1489 -+007233 343400 0 ZZ2489 -- mark 5357, 175 /27 serp -+007234 000536 ZZ2490=ZZ2490+ZZ2490 -+007234 001274 ZZ2490=ZZ2490+ZZ2490 -+007234 002570 ZZ2490=ZZ2490+ZZ2490 -+007234 005360 ZZ2490=ZZ2490+ZZ2490 -+007234 012740 ZZ2490=ZZ2490+ZZ2490 -+007234 025700 ZZ2490=ZZ2490+ZZ2490 -+007234 053600 ZZ2490=ZZ2490+ZZ2490 -+007234 127400 ZZ2490=ZZ2490+ZZ2490 -+007234 005423 0 8192 -ZZ1490 -+007235 127400 0 ZZ2490 -- mark 5372, 420 /35 serp -+007236 001510 ZZ2491=ZZ2491+ZZ2491 -+007236 003220 ZZ2491=ZZ2491+ZZ2491 -+007236 006440 ZZ2491=ZZ2491+ZZ2491 -+007236 015100 ZZ2491=ZZ2491+ZZ2491 -+007236 032200 ZZ2491=ZZ2491+ZZ2491 -+007236 064400 ZZ2491=ZZ2491+ZZ2491 -+007236 151000 ZZ2491=ZZ2491+ZZ2491 -+007236 322000 ZZ2491=ZZ2491+ZZ2491 -+007236 005404 0 8192 -ZZ1491 -+007237 322000 0 ZZ2491 -- mark 5381, 109 /37 serp -+007240 000332 ZZ2492=ZZ2492+ZZ2492 -+007240 000664 ZZ2492=ZZ2492+ZZ2492 -+007240 001550 ZZ2492=ZZ2492+ZZ2492 -+007240 003320 ZZ2492=ZZ2492+ZZ2492 -+007240 006640 ZZ2492=ZZ2492+ZZ2492 -+007240 015500 ZZ2492=ZZ2492+ZZ2492 -+007240 033200 ZZ2492=ZZ2492+ZZ2492 -+007240 066400 ZZ2492=ZZ2492+ZZ2492 -+007240 005373 0 8192 -ZZ1492 -+007241 066400 0 ZZ2492 -- mark 5387, 484 /38 serp -+007242 001710 ZZ2493=ZZ2493+ZZ2493 -+007242 003620 ZZ2493=ZZ2493+ZZ2493 -+007242 007440 ZZ2493=ZZ2493+ZZ2493 -+007242 017100 ZZ2493=ZZ2493+ZZ2493 -+007242 036200 ZZ2493=ZZ2493+ZZ2493 -+007242 074400 ZZ2493=ZZ2493+ZZ2493 -+007242 171000 ZZ2493=ZZ2493+ZZ2493 -+007242 362000 ZZ2493=ZZ2493+ZZ2493 -+007242 005365 0 8192 -ZZ1493 -+007243 362000 0 ZZ2493 -- mark 5394, -374 /46 libr -+007244 776422 ZZ2494=ZZ2494+ZZ2494 -+007244 775044 ZZ2494=ZZ2494+ZZ2494 -+007244 772110 ZZ2494=ZZ2494+ZZ2494 -+007244 764220 ZZ2494=ZZ2494+ZZ2494 -+007244 750440 ZZ2494=ZZ2494+ZZ2494 -+007244 721100 ZZ2494=ZZ2494+ZZ2494 -+007244 642200 ZZ2494=ZZ2494+ZZ2494 -+007244 504400 ZZ2494=ZZ2494+ZZ2494 -+007244 005356 0 8192 -ZZ1494 -+007245 504400 0 ZZ2494 -- mark 5415, 364 /41 serp -+007246 001330 ZZ2495=ZZ2495+ZZ2495 -+007246 002660 ZZ2495=ZZ2495+ZZ2495 -+007246 005540 ZZ2495=ZZ2495+ZZ2495 -+007246 013300 ZZ2495=ZZ2495+ZZ2495 -+007246 026600 ZZ2495=ZZ2495+ZZ2495 -+007246 055400 ZZ2495=ZZ2495+ZZ2495 -+007246 133000 ZZ2495=ZZ2495+ZZ2495 -+007246 266000 ZZ2495=ZZ2495+ZZ2495 -+007246 005331 0 8192 -ZZ1495 -+007247 266000 0 ZZ2495 -- mark 5419, -318 /48 libr -+007250 776602 ZZ2496=ZZ2496+ZZ2496 -+007250 775404 ZZ2496=ZZ2496+ZZ2496 -+007250 773010 ZZ2496=ZZ2496+ZZ2496 -+007250 766020 ZZ2496=ZZ2496+ZZ2496 -+007250 754040 ZZ2496=ZZ2496+ZZ2496 -+007250 730100 ZZ2496=ZZ2496+ZZ2496 -+007250 660200 ZZ2496=ZZ2496+ZZ2496 -+007250 540400 ZZ2496=ZZ2496+ZZ2496 -+007250 005325 0 8192 -ZZ1496 -+007251 540400 0 ZZ2496 -- mark 5455, -253 /xi scor -+007252 777004 ZZ2497=ZZ2497+ZZ2497 -+007252 776010 ZZ2497=ZZ2497+ZZ2497 -+007252 774020 ZZ2497=ZZ2497+ZZ2497 -+007252 770040 ZZ2497=ZZ2497+ZZ2497 -+007252 760100 ZZ2497=ZZ2497+ZZ2497 -+007252 740200 ZZ2497=ZZ2497+ZZ2497 -+007252 700400 ZZ2497=ZZ2497+ZZ2497 -+007252 601000 ZZ2497=ZZ2497+ZZ2497 -+007252 005261 0 8192 -ZZ1497 -+007253 601000 0 ZZ2497 -- mark 5467, -464 / 9 scor -+007254 776136 ZZ2498=ZZ2498+ZZ2498 -+007254 774274 ZZ2498=ZZ2498+ZZ2498 -+007254 770570 ZZ2498=ZZ2498+ZZ2498 -+007254 761360 ZZ2498=ZZ2498+ZZ2498 -+007254 742740 ZZ2498=ZZ2498+ZZ2498 -+007254 705700 ZZ2498=ZZ2498+ZZ2498 -+007254 613600 ZZ2498=ZZ2498+ZZ2498 -+007254 427400 ZZ2498=ZZ2498+ZZ2498 -+007254 005245 0 8192 -ZZ1498 -+007255 427400 0 ZZ2498 -- mark 5470, -469 /10 scor -+007256 776124 ZZ2499=ZZ2499+ZZ2499 -+007256 774250 ZZ2499=ZZ2499+ZZ2499 -+007256 770520 ZZ2499=ZZ2499+ZZ2499 -+007256 761240 ZZ2499=ZZ2499+ZZ2499 -+007256 742500 ZZ2499=ZZ2499+ZZ2499 -+007256 705200 ZZ2499=ZZ2499+ZZ2499 -+007256 612400 ZZ2499=ZZ2499+ZZ2499 -+007256 425000 ZZ2499=ZZ2499+ZZ2499 -+007256 005242 0 8192 -ZZ1499 -+007257 425000 0 ZZ2499 -- mark 5497, -437 /14 scor -+007260 776224 ZZ2500=ZZ2500+ZZ2500 -+007260 774450 ZZ2500=ZZ2500+ZZ2500 -+007260 771120 ZZ2500=ZZ2500+ZZ2500 -+007260 762240 ZZ2500=ZZ2500+ZZ2500 -+007260 744500 ZZ2500=ZZ2500+ZZ2500 -+007260 711200 ZZ2500=ZZ2500+ZZ2500 -+007260 622400 ZZ2500=ZZ2500+ZZ2500 -+007260 445000 ZZ2500=ZZ2500+ZZ2500 -+007260 005207 0 8192 -ZZ1500 -+007261 445000 0 ZZ2500 -- mark 5499, -223 /15 scor -+007262 777100 ZZ2501=ZZ2501+ZZ2501 -+007262 776200 ZZ2501=ZZ2501+ZZ2501 -+007262 774400 ZZ2501=ZZ2501+ZZ2501 -+007262 771000 ZZ2501=ZZ2501+ZZ2501 -+007262 762000 ZZ2501=ZZ2501+ZZ2501 -+007262 744000 ZZ2501=ZZ2501+ZZ2501 -+007262 710000 ZZ2501=ZZ2501+ZZ2501 -+007262 620000 ZZ2501=ZZ2501+ZZ2501 -+007262 005205 0 8192 -ZZ1501 -+007263 620000 0 ZZ2501 -- mark 5558, 29 /50 serp -+007264 000072 ZZ2502=ZZ2502+ZZ2502 -+007264 000164 ZZ2502=ZZ2502+ZZ2502 -+007264 000350 ZZ2502=ZZ2502+ZZ2502 -+007264 000720 ZZ2502=ZZ2502+ZZ2502 -+007264 001640 ZZ2502=ZZ2502+ZZ2502 -+007264 003500 ZZ2502=ZZ2502+ZZ2502 -+007264 007200 ZZ2502=ZZ2502+ZZ2502 -+007264 016400 ZZ2502=ZZ2502+ZZ2502 -+007264 005112 0 8192 -ZZ1502 -+007265 016400 0 ZZ2502 -- mark 5561, 441 /20 herc -+007266 001562 ZZ2503=ZZ2503+ZZ2503 -+007266 003344 ZZ2503=ZZ2503+ZZ2503 -+007266 006710 ZZ2503=ZZ2503+ZZ2503 -+007266 015620 ZZ2503=ZZ2503+ZZ2503 -+007266 033440 ZZ2503=ZZ2503+ZZ2503 -+007266 067100 ZZ2503=ZZ2503+ZZ2503 -+007266 156200 ZZ2503=ZZ2503+ZZ2503 -+007266 334400 ZZ2503=ZZ2503+ZZ2503 -+007266 005107 0 8192 -ZZ1503 -+007267 334400 0 ZZ2503 -- mark 5565, -451 / 4 ophi -+007270 776170 ZZ2504=ZZ2504+ZZ2504 -+007270 774360 ZZ2504=ZZ2504+ZZ2504 -+007270 770740 ZZ2504=ZZ2504+ZZ2504 -+007270 761700 ZZ2504=ZZ2504+ZZ2504 -+007270 743600 ZZ2504=ZZ2504+ZZ2504 -+007270 707400 ZZ2504=ZZ2504+ZZ2504 -+007270 617000 ZZ2504=ZZ2504+ZZ2504 -+007270 436000 ZZ2504=ZZ2504+ZZ2504 -+007270 005103 0 8192 -ZZ1504 -+007271 436000 0 ZZ2504 -- mark 5580, 325 /24 herc -+007272 001212 ZZ2505=ZZ2505+ZZ2505 -+007272 002424 ZZ2505=ZZ2505+ZZ2505 -+007272 005050 ZZ2505=ZZ2505+ZZ2505 -+007272 012120 ZZ2505=ZZ2505+ZZ2505 -+007272 024240 ZZ2505=ZZ2505+ZZ2505 -+007272 050500 ZZ2505=ZZ2505+ZZ2505 -+007272 121200 ZZ2505=ZZ2505+ZZ2505 -+007272 242400 ZZ2505=ZZ2505+ZZ2505 -+007272 005064 0 8192 -ZZ1505 -+007273 242400 0 ZZ2505 -- mark 5582, -415 / 7 ophi -+007274 776300 ZZ2506=ZZ2506+ZZ2506 -+007274 774600 ZZ2506=ZZ2506+ZZ2506 -+007274 771400 ZZ2506=ZZ2506+ZZ2506 -+007274 763000 ZZ2506=ZZ2506+ZZ2506 -+007274 746000 ZZ2506=ZZ2506+ZZ2506 -+007274 714000 ZZ2506=ZZ2506+ZZ2506 -+007274 630000 ZZ2506=ZZ2506+ZZ2506 -+007274 460000 ZZ2506=ZZ2506+ZZ2506 -+007274 005062 0 8192 -ZZ1506 -+007275 460000 0 ZZ2506 -- mark 5589, -186 / 3 ophi -+007276 777212 ZZ2507=ZZ2507+ZZ2507 -+007276 776424 ZZ2507=ZZ2507+ZZ2507 -+007276 775050 ZZ2507=ZZ2507+ZZ2507 -+007276 772120 ZZ2507=ZZ2507+ZZ2507 -+007276 764240 ZZ2507=ZZ2507+ZZ2507 -+007276 750500 ZZ2507=ZZ2507+ZZ2507 -+007276 721200 ZZ2507=ZZ2507+ZZ2507 -+007276 642400 ZZ2507=ZZ2507+ZZ2507 -+007276 005053 0 8192 -ZZ1507 -+007277 642400 0 ZZ2507 -- mark 5606, -373 / 8 ophi -+007300 776424 ZZ2508=ZZ2508+ZZ2508 -+007300 775050 ZZ2508=ZZ2508+ZZ2508 -+007300 772120 ZZ2508=ZZ2508+ZZ2508 -+007300 764240 ZZ2508=ZZ2508+ZZ2508 -+007300 750500 ZZ2508=ZZ2508+ZZ2508 -+007300 721200 ZZ2508=ZZ2508+ZZ2508 -+007300 642400 ZZ2508=ZZ2508+ZZ2508 -+007300 505000 ZZ2508=ZZ2508+ZZ2508 -+007300 005032 0 8192 -ZZ1508 -+007301 505000 0 ZZ2508 -- mark 5609, 50 /10 ophi -+007302 000144 ZZ2509=ZZ2509+ZZ2509 -+007302 000310 ZZ2509=ZZ2509+ZZ2509 -+007302 000620 ZZ2509=ZZ2509+ZZ2509 -+007302 001440 ZZ2509=ZZ2509+ZZ2509 -+007302 003100 ZZ2509=ZZ2509+ZZ2509 -+007302 006200 ZZ2509=ZZ2509+ZZ2509 -+007302 014400 ZZ2509=ZZ2509+ZZ2509 -+007302 031000 ZZ2509=ZZ2509+ZZ2509 -+007302 005027 0 8192 -ZZ1509 -+007303 031000 0 ZZ2509 -- mark 5610, -484 / 9 ophi -+007304 776066 ZZ2510=ZZ2510+ZZ2510 -+007304 774154 ZZ2510=ZZ2510+ZZ2510 -+007304 770330 ZZ2510=ZZ2510+ZZ2510 -+007304 760660 ZZ2510=ZZ2510+ZZ2510 -+007304 741540 ZZ2510=ZZ2510+ZZ2510 -+007304 703300 ZZ2510=ZZ2510+ZZ2510 -+007304 606600 ZZ2510=ZZ2510+ZZ2510 -+007304 415400 ZZ2510=ZZ2510+ZZ2510 -+007304 005026 0 8192 -ZZ1510 -+007305 415400 0 ZZ2510 -- mark 5620, 266 /29 herc -+007306 001024 ZZ2511=ZZ2511+ZZ2511 -+007306 002050 ZZ2511=ZZ2511+ZZ2511 -+007306 004120 ZZ2511=ZZ2511+ZZ2511 -+007306 010240 ZZ2511=ZZ2511+ZZ2511 -+007306 020500 ZZ2511=ZZ2511+ZZ2511 -+007306 041200 ZZ2511=ZZ2511+ZZ2511 -+007306 102400 ZZ2511=ZZ2511+ZZ2511 -+007306 205000 ZZ2511=ZZ2511+ZZ2511 -+007306 005014 0 8192 -ZZ1511 -+007307 205000 0 ZZ2511 -- mark 5713, -241 /20 ophi -+007310 777034 ZZ2512=ZZ2512+ZZ2512 -+007310 776070 ZZ2512=ZZ2512+ZZ2512 -+007310 774160 ZZ2512=ZZ2512+ZZ2512 -+007310 770340 ZZ2512=ZZ2512+ZZ2512 -+007310 760700 ZZ2512=ZZ2512+ZZ2512 -+007310 741600 ZZ2512=ZZ2512+ZZ2512 -+007310 703400 ZZ2512=ZZ2512+ZZ2512 -+007310 607000 ZZ2512=ZZ2512+ZZ2512 -+007310 004657 0 8192 -ZZ1512 -+007311 607000 0 ZZ2512 -- mark 5742, 235 /25 ophi -+007312 000726 ZZ2513=ZZ2513+ZZ2513 -+007312 001654 ZZ2513=ZZ2513+ZZ2513 -+007312 003530 ZZ2513=ZZ2513+ZZ2513 -+007312 007260 ZZ2513=ZZ2513+ZZ2513 -+007312 016540 ZZ2513=ZZ2513+ZZ2513 -+007312 035300 ZZ2513=ZZ2513+ZZ2513 -+007312 072600 ZZ2513=ZZ2513+ZZ2513 -+007312 165400 ZZ2513=ZZ2513+ZZ2513 -+007312 004622 0 8192 -ZZ1513 -+007313 165400 0 ZZ2513 -- mark 5763, 217 /27 ophi -+007314 000662 ZZ2514=ZZ2514+ZZ2514 -+007314 001544 ZZ2514=ZZ2514+ZZ2514 -+007314 003310 ZZ2514=ZZ2514+ZZ2514 -+007314 006620 ZZ2514=ZZ2514+ZZ2514 -+007314 015440 ZZ2514=ZZ2514+ZZ2514 -+007314 033100 ZZ2514=ZZ2514+ZZ2514 -+007314 066200 ZZ2514=ZZ2514+ZZ2514 -+007314 154400 ZZ2514=ZZ2514+ZZ2514 -+007314 004575 0 8192 -ZZ1514 -+007315 154400 0 ZZ2514 -- mark 5807, 293 /60 herc -+007316 001112 ZZ2515=ZZ2515+ZZ2515 -+007316 002224 ZZ2515=ZZ2515+ZZ2515 -+007316 004450 ZZ2515=ZZ2515+ZZ2515 -+007316 011120 ZZ2515=ZZ2515+ZZ2515 -+007316 022240 ZZ2515=ZZ2515+ZZ2515 -+007316 044500 ZZ2515=ZZ2515+ZZ2515 -+007316 111200 ZZ2515=ZZ2515+ZZ2515 -+007316 222400 ZZ2515=ZZ2515+ZZ2515 -+007316 004521 0 8192 -ZZ1515 -+007317 222400 0 ZZ2515 -- mark 5868, -8 /41 ophi -+007320 777756 ZZ2516=ZZ2516+ZZ2516 -+007320 777734 ZZ2516=ZZ2516+ZZ2516 -+007320 777670 ZZ2516=ZZ2516+ZZ2516 -+007320 777560 ZZ2516=ZZ2516+ZZ2516 -+007320 777340 ZZ2516=ZZ2516+ZZ2516 -+007320 776700 ZZ2516=ZZ2516+ZZ2516 -+007320 775600 ZZ2516=ZZ2516+ZZ2516 -+007320 773400 ZZ2516=ZZ2516+ZZ2516 -+007320 004424 0 8192 -ZZ1516 -+007321 773400 0 ZZ2516 -- mark 5888, -478 /40 ophi -+007322 776102 ZZ2517=ZZ2517+ZZ2517 -+007322 774204 ZZ2517=ZZ2517+ZZ2517 -+007322 770410 ZZ2517=ZZ2517+ZZ2517 -+007322 761020 ZZ2517=ZZ2517+ZZ2517 -+007322 742040 ZZ2517=ZZ2517+ZZ2517 -+007322 704100 ZZ2517=ZZ2517+ZZ2517 -+007322 610200 ZZ2517=ZZ2517+ZZ2517 -+007322 420400 ZZ2517=ZZ2517+ZZ2517 -+007322 004400 0 8192 -ZZ1517 -+007323 420400 0 ZZ2517 -- mark 5889, -290 /53 serp -+007324 776672 ZZ2518=ZZ2518+ZZ2518 -+007324 775564 ZZ2518=ZZ2518+ZZ2518 -+007324 773350 ZZ2518=ZZ2518+ZZ2518 -+007324 766720 ZZ2518=ZZ2518+ZZ2518 -+007324 755640 ZZ2518=ZZ2518+ZZ2518 -+007324 733500 ZZ2518=ZZ2518+ZZ2518 -+007324 667200 ZZ2518=ZZ2518+ZZ2518 -+007324 556400 ZZ2518=ZZ2518+ZZ2518 -+007324 004377 0 8192 -ZZ1518 -+007325 556400 0 ZZ2518 -- mark 5924, -114 / -+007326 777432 ZZ2519=ZZ2519+ZZ2519 -+007326 777064 ZZ2519=ZZ2519+ZZ2519 -+007326 776150 ZZ2519=ZZ2519+ZZ2519 -+007326 774320 ZZ2519=ZZ2519+ZZ2519 -+007326 770640 ZZ2519=ZZ2519+ZZ2519 -+007326 761500 ZZ2519=ZZ2519+ZZ2519 -+007326 743200 ZZ2519=ZZ2519+ZZ2519 -+007326 706400 ZZ2519=ZZ2519+ZZ2519 -+007326 004334 0 8192 -ZZ1519 -+007327 706400 0 ZZ2519 -- mark 5925, 96 /49 ophi -+007330 000300 ZZ2520=ZZ2520+ZZ2520 -+007330 000600 ZZ2520=ZZ2520+ZZ2520 -+007330 001400 ZZ2520=ZZ2520+ZZ2520 -+007330 003000 ZZ2520=ZZ2520+ZZ2520 -+007330 006000 ZZ2520=ZZ2520+ZZ2520 -+007330 014000 ZZ2520=ZZ2520+ZZ2520 -+007330 030000 ZZ2520=ZZ2520+ZZ2520 -+007330 060000 ZZ2520=ZZ2520+ZZ2520 -+007330 004333 0 8192 -ZZ1520 -+007331 060000 0 ZZ2520 -- mark 5987, -183 /57 ophi -+007332 777220 ZZ2521=ZZ2521+ZZ2521 -+007332 776440 ZZ2521=ZZ2521+ZZ2521 -+007332 775100 ZZ2521=ZZ2521+ZZ2521 -+007332 772200 ZZ2521=ZZ2521+ZZ2521 -+007332 764400 ZZ2521=ZZ2521+ZZ2521 -+007332 751000 ZZ2521=ZZ2521+ZZ2521 -+007332 722000 ZZ2521=ZZ2521+ZZ2521 -+007332 644000 ZZ2521=ZZ2521+ZZ2521 -+007332 004235 0 8192 -ZZ1521 -+007333 644000 0 ZZ2521 -- mark 6006, -292 /56 serp -+007334 776666 ZZ2522=ZZ2522+ZZ2522 -+007334 775554 ZZ2522=ZZ2522+ZZ2522 -+007334 773330 ZZ2522=ZZ2522+ZZ2522 -+007334 766660 ZZ2522=ZZ2522+ZZ2522 -+007334 755540 ZZ2522=ZZ2522+ZZ2522 -+007334 733300 ZZ2522=ZZ2522+ZZ2522 -+007334 666600 ZZ2522=ZZ2522+ZZ2522 -+007334 555400 ZZ2522=ZZ2522+ZZ2522 -+007334 004212 0 8192 -ZZ1522 -+007335 555400 0 ZZ2522 -- mark 6016, -492 /58 ophi -+007336 776046 ZZ2523=ZZ2523+ZZ2523 -+007336 774114 ZZ2523=ZZ2523+ZZ2523 -+007336 770230 ZZ2523=ZZ2523+ZZ2523 -+007336 760460 ZZ2523=ZZ2523+ZZ2523 -+007336 741140 ZZ2523=ZZ2523+ZZ2523 -+007336 702300 ZZ2523=ZZ2523+ZZ2523 -+007336 604600 ZZ2523=ZZ2523+ZZ2523 -+007336 411400 ZZ2523=ZZ2523+ZZ2523 -+007336 004200 0 8192 -ZZ1523 -+007337 411400 0 ZZ2523 -- mark 6117, -84 /57 serp -+007340 777526 ZZ2524=ZZ2524+ZZ2524 -+007340 777254 ZZ2524=ZZ2524+ZZ2524 -+007340 776530 ZZ2524=ZZ2524+ZZ2524 -+007340 775260 ZZ2524=ZZ2524+ZZ2524 -+007340 772540 ZZ2524=ZZ2524+ZZ2524 -+007340 765300 ZZ2524=ZZ2524+ZZ2524 -+007340 752600 ZZ2524=ZZ2524+ZZ2524 -+007340 725400 ZZ2524=ZZ2524+ZZ2524 -+007340 004033 0 8192 -ZZ1524 -+007341 725400 0 ZZ2524 -- mark 6117, 99 /66 ophi -+007342 000306 ZZ2525=ZZ2525+ZZ2525 -+007342 000614 ZZ2525=ZZ2525+ZZ2525 -+007342 001430 ZZ2525=ZZ2525+ZZ2525 -+007342 003060 ZZ2525=ZZ2525+ZZ2525 -+007342 006140 ZZ2525=ZZ2525+ZZ2525 -+007342 014300 ZZ2525=ZZ2525+ZZ2525 -+007342 030600 ZZ2525=ZZ2525+ZZ2525 -+007342 061400 ZZ2525=ZZ2525+ZZ2525 -+007342 004033 0 8192 -ZZ1525 -+007343 061400 0 ZZ2525 -- mark 6119, 381 /93 herc -+007344 001372 ZZ2526=ZZ2526+ZZ2526 -+007344 002764 ZZ2526=ZZ2526+ZZ2526 -+007344 005750 ZZ2526=ZZ2526+ZZ2526 -+007344 013720 ZZ2526=ZZ2526+ZZ2526 -+007344 027640 ZZ2526=ZZ2526+ZZ2526 -+007344 057500 ZZ2526=ZZ2526+ZZ2526 -+007344 137200 ZZ2526=ZZ2526+ZZ2526 -+007344 276400 ZZ2526=ZZ2526+ZZ2526 -+007344 004031 0 8192 -ZZ1526 -+007345 276400 0 ZZ2526 -- mark 6119, 67 /67 ophi -+007346 000206 ZZ2527=ZZ2527+ZZ2527 -+007346 000414 ZZ2527=ZZ2527+ZZ2527 -+007346 001030 ZZ2527=ZZ2527+ZZ2527 -+007346 002060 ZZ2527=ZZ2527+ZZ2527 -+007346 004140 ZZ2527=ZZ2527+ZZ2527 -+007346 010300 ZZ2527=ZZ2527+ZZ2527 -+007346 020600 ZZ2527=ZZ2527+ZZ2527 -+007346 041400 ZZ2527=ZZ2527+ZZ2527 -+007346 004031 0 8192 -ZZ1527 -+007347 041400 0 ZZ2527 -- mark 6125, 30 /68 ophi -+007350 000074 ZZ2528=ZZ2528+ZZ2528 -+007350 000170 ZZ2528=ZZ2528+ZZ2528 -+007350 000360 ZZ2528=ZZ2528+ZZ2528 -+007350 000740 ZZ2528=ZZ2528+ZZ2528 -+007350 001700 ZZ2528=ZZ2528+ZZ2528 -+007350 003600 ZZ2528=ZZ2528+ZZ2528 -+007350 007400 ZZ2528=ZZ2528+ZZ2528 -+007350 017000 ZZ2528=ZZ2528+ZZ2528 -+007350 004023 0 8192 -ZZ1528 -+007351 017000 0 ZZ2528 -- mark 6146, 57 /70 ophi -+007352 000162 ZZ2529=ZZ2529+ZZ2529 -+007352 000344 ZZ2529=ZZ2529+ZZ2529 -+007352 000710 ZZ2529=ZZ2529+ZZ2529 -+007352 001620 ZZ2529=ZZ2529+ZZ2529 -+007352 003440 ZZ2529=ZZ2529+ZZ2529 -+007352 007100 ZZ2529=ZZ2529+ZZ2529 -+007352 016200 ZZ2529=ZZ2529+ZZ2529 -+007352 034400 ZZ2529=ZZ2529+ZZ2529 -+007352 003776 0 8192 -ZZ1529 -+007353 034400 0 ZZ2529 -- mark 6158, 198 /71 ophi -+007354 000614 ZZ2530=ZZ2530+ZZ2530 -+007354 001430 ZZ2530=ZZ2530+ZZ2530 -+007354 003060 ZZ2530=ZZ2530+ZZ2530 -+007354 006140 ZZ2530=ZZ2530+ZZ2530 -+007354 014300 ZZ2530=ZZ2530+ZZ2530 -+007354 030600 ZZ2530=ZZ2530+ZZ2530 -+007354 061400 ZZ2530=ZZ2530+ZZ2530 -+007354 143000 ZZ2530=ZZ2530+ZZ2530 -+007354 003762 0 8192 -ZZ1530 -+007355 143000 0 ZZ2530 -- mark 6170, 473 /102 herc -+007356 001662 ZZ2531=ZZ2531+ZZ2531 -+007356 003544 ZZ2531=ZZ2531+ZZ2531 -+007356 007310 ZZ2531=ZZ2531+ZZ2531 -+007356 016620 ZZ2531=ZZ2531+ZZ2531 -+007356 035440 ZZ2531=ZZ2531+ZZ2531 -+007356 073100 ZZ2531=ZZ2531+ZZ2531 -+007356 166200 ZZ2531=ZZ2531+ZZ2531 -+007356 354400 ZZ2531=ZZ2531+ZZ2531 -+007356 003746 0 8192 -ZZ1531 -+007357 354400 0 ZZ2531 -- mark 6188, -480 /13 sgtr -+007360 776076 ZZ2532=ZZ2532+ZZ2532 -+007360 774174 ZZ2532=ZZ2532+ZZ2532 -+007360 770370 ZZ2532=ZZ2532+ZZ2532 -+007360 760760 ZZ2532=ZZ2532+ZZ2532 -+007360 741740 ZZ2532=ZZ2532+ZZ2532 -+007360 703700 ZZ2532=ZZ2532+ZZ2532 -+007360 607600 ZZ2532=ZZ2532+ZZ2532 -+007360 417400 ZZ2532=ZZ2532+ZZ2532 -+007360 003724 0 8192 -ZZ1532 -+007361 417400 0 ZZ2532 -- mark 6234, 76 /74 ophi -+007362 000230 ZZ2533=ZZ2533+ZZ2533 -+007362 000460 ZZ2533=ZZ2533+ZZ2533 -+007362 001140 ZZ2533=ZZ2533+ZZ2533 -+007362 002300 ZZ2533=ZZ2533+ZZ2533 -+007362 004600 ZZ2533=ZZ2533+ZZ2533 -+007362 011400 ZZ2533=ZZ2533+ZZ2533 -+007362 023000 ZZ2533=ZZ2533+ZZ2533 -+007362 046000 ZZ2533=ZZ2533+ZZ2533 -+007362 003646 0 8192 -ZZ1533 -+007363 046000 0 ZZ2533 -- mark 6235, 499 /106 herc -+007364 001746 ZZ2534=ZZ2534+ZZ2534 -+007364 003714 ZZ2534=ZZ2534+ZZ2534 -+007364 007630 ZZ2534=ZZ2534+ZZ2534 -+007364 017460 ZZ2534=ZZ2534+ZZ2534 -+007364 037140 ZZ2534=ZZ2534+ZZ2534 -+007364 076300 ZZ2534=ZZ2534+ZZ2534 -+007364 174600 ZZ2534=ZZ2534+ZZ2534 -+007364 371400 ZZ2534=ZZ2534+ZZ2534 -+007364 003645 0 8192 -ZZ1534 -+007365 371400 0 ZZ2534 -- mark 6247, -204 /xi scut -+007366 777146 ZZ2535=ZZ2535+ZZ2535 -+007366 776314 ZZ2535=ZZ2535+ZZ2535 -+007366 774630 ZZ2535=ZZ2535+ZZ2535 -+007366 771460 ZZ2535=ZZ2535+ZZ2535 -+007366 763140 ZZ2535=ZZ2535+ZZ2535 -+007366 746300 ZZ2535=ZZ2535+ZZ2535 -+007366 714600 ZZ2535=ZZ2535+ZZ2535 -+007366 631400 ZZ2535=ZZ2535+ZZ2535 -+007366 003631 0 8192 -ZZ1535 -+007367 631400 0 ZZ2535 -- mark 6254, -469 /21 sgtr -+007370 776124 ZZ2536=ZZ2536+ZZ2536 -+007370 774250 ZZ2536=ZZ2536+ZZ2536 -+007370 770520 ZZ2536=ZZ2536+ZZ2536 -+007370 761240 ZZ2536=ZZ2536+ZZ2536 -+007370 742500 ZZ2536=ZZ2536+ZZ2536 -+007370 705200 ZZ2536=ZZ2536+ZZ2536 -+007370 612400 ZZ2536=ZZ2536+ZZ2536 -+007370 425000 ZZ2536=ZZ2536+ZZ2536 -+007370 003622 0 8192 -ZZ1536 -+007371 425000 0 ZZ2536 -- mark 6255, 494 /109 herc -+007372 001734 ZZ2537=ZZ2537+ZZ2537 -+007372 003670 ZZ2537=ZZ2537+ZZ2537 -+007372 007560 ZZ2537=ZZ2537+ZZ2537 -+007372 017340 ZZ2537=ZZ2537+ZZ2537 -+007372 036700 ZZ2537=ZZ2537+ZZ2537 -+007372 075600 ZZ2537=ZZ2537+ZZ2537 -+007372 173400 ZZ2537=ZZ2537+ZZ2537 -+007372 367000 ZZ2537=ZZ2537+ZZ2537 -+007372 003621 0 8192 -ZZ1537 -+007373 367000 0 ZZ2537 -- mark 6278, -333 /ga scut -+007374 776544 ZZ2538=ZZ2538+ZZ2538 -+007374 775310 ZZ2538=ZZ2538+ZZ2538 -+007374 772620 ZZ2538=ZZ2538+ZZ2538 -+007374 765440 ZZ2538=ZZ2538+ZZ2538 -+007374 753100 ZZ2538=ZZ2538+ZZ2538 -+007374 726200 ZZ2538=ZZ2538+ZZ2538 -+007374 654400 ZZ2538=ZZ2538+ZZ2538 -+007374 531000 ZZ2538=ZZ2538+ZZ2538 -+007374 003572 0 8192 -ZZ1538 -+007375 531000 0 ZZ2538 -- mark 6313, -189 /al scut -+007376 777204 ZZ2539=ZZ2539+ZZ2539 -+007376 776410 ZZ2539=ZZ2539+ZZ2539 -+007376 775020 ZZ2539=ZZ2539+ZZ2539 -+007376 772040 ZZ2539=ZZ2539+ZZ2539 -+007376 764100 ZZ2539=ZZ2539+ZZ2539 -+007376 750200 ZZ2539=ZZ2539+ZZ2539 -+007376 720400 ZZ2539=ZZ2539+ZZ2539 -+007376 641000 ZZ2539=ZZ2539+ZZ2539 -+007376 003527 0 8192 -ZZ1539 -+007377 641000 0 ZZ2539 -- mark 6379, 465 /110 herc -+007400 001642 ZZ2540=ZZ2540+ZZ2540 -+007400 003504 ZZ2540=ZZ2540+ZZ2540 -+007400 007210 ZZ2540=ZZ2540+ZZ2540 -+007400 016420 ZZ2540=ZZ2540+ZZ2540 -+007400 035040 ZZ2540=ZZ2540+ZZ2540 -+007400 072100 ZZ2540=ZZ2540+ZZ2540 -+007400 164200 ZZ2540=ZZ2540+ZZ2540 -+007400 350400 ZZ2540=ZZ2540+ZZ2540 -+007400 003425 0 8192 -ZZ1540 -+007401 350400 0 ZZ2540 -- mark 6382, -110 /be scut -+007402 777442 ZZ2541=ZZ2541+ZZ2541 -+007402 777104 ZZ2541=ZZ2541+ZZ2541 -+007402 776210 ZZ2541=ZZ2541+ZZ2541 -+007402 774420 ZZ2541=ZZ2541+ZZ2541 -+007402 771040 ZZ2541=ZZ2541+ZZ2541 -+007402 762100 ZZ2541=ZZ2541+ZZ2541 -+007402 744200 ZZ2541=ZZ2541+ZZ2541 -+007402 710400 ZZ2541=ZZ2541+ZZ2541 -+007402 003422 0 8192 -ZZ1541 -+007403 710400 0 ZZ2541 -- mark 6386, 411 /111 herc -+007404 001466 ZZ2542=ZZ2542+ZZ2542 -+007404 003154 ZZ2542=ZZ2542+ZZ2542 -+007404 006330 ZZ2542=ZZ2542+ZZ2542 -+007404 014660 ZZ2542=ZZ2542+ZZ2542 -+007404 031540 ZZ2542=ZZ2542+ZZ2542 -+007404 063300 ZZ2542=ZZ2542+ZZ2542 -+007404 146600 ZZ2542=ZZ2542+ZZ2542 -+007404 315400 ZZ2542=ZZ2542+ZZ2542 -+007404 003416 0 8192 -ZZ1542 -+007405 315400 0 ZZ2542 -- mark 6436, 93 /63 serp -+007406 000272 ZZ2543=ZZ2543+ZZ2543 -+007406 000564 ZZ2543=ZZ2543+ZZ2543 -+007406 001350 ZZ2543=ZZ2543+ZZ2543 -+007406 002720 ZZ2543=ZZ2543+ZZ2543 -+007406 005640 ZZ2543=ZZ2543+ZZ2543 -+007406 013500 ZZ2543=ZZ2543+ZZ2543 -+007406 027200 ZZ2543=ZZ2543+ZZ2543 -+007406 056400 ZZ2543=ZZ2543+ZZ2543 -+007406 003334 0 8192 -ZZ1543 -+007407 056400 0 ZZ2543 -- mark 6457, 340 /13 aqil -+007410 001250 ZZ2544=ZZ2544+ZZ2544 -+007410 002520 ZZ2544=ZZ2544+ZZ2544 -+007410 005240 ZZ2544=ZZ2544+ZZ2544 -+007410 012500 ZZ2544=ZZ2544+ZZ2544 -+007410 025200 ZZ2544=ZZ2544+ZZ2544 -+007410 052400 ZZ2544=ZZ2544+ZZ2544 -+007410 125000 ZZ2544=ZZ2544+ZZ2544 -+007410 252000 ZZ2544=ZZ2544+ZZ2544 -+007410 003307 0 8192 -ZZ1544 -+007411 252000 0 ZZ2544 -- mark 6465, -134 /12 aqil -+007412 777362 ZZ2545=ZZ2545+ZZ2545 -+007412 776744 ZZ2545=ZZ2545+ZZ2545 -+007412 775710 ZZ2545=ZZ2545+ZZ2545 -+007412 773620 ZZ2545=ZZ2545+ZZ2545 -+007412 767440 ZZ2545=ZZ2545+ZZ2545 -+007412 757100 ZZ2545=ZZ2545+ZZ2545 -+007412 736200 ZZ2545=ZZ2545+ZZ2545 -+007412 674400 ZZ2545=ZZ2545+ZZ2545 -+007412 003277 0 8192 -ZZ1545 -+007413 674400 0 ZZ2545 -- mark 6478, -498 /39 sgtr -+007414 776032 ZZ2546=ZZ2546+ZZ2546 -+007414 774064 ZZ2546=ZZ2546+ZZ2546 -+007414 770150 ZZ2546=ZZ2546+ZZ2546 -+007414 760320 ZZ2546=ZZ2546+ZZ2546 -+007414 740640 ZZ2546=ZZ2546+ZZ2546 -+007414 701500 ZZ2546=ZZ2546+ZZ2546 -+007414 603200 ZZ2546=ZZ2546+ZZ2546 -+007414 406400 ZZ2546=ZZ2546+ZZ2546 -+007414 003262 0 8192 -ZZ1546 -+007415 406400 0 ZZ2546 -- mark 6553, 483 / 1 vulp -+007416 001706 ZZ2547=ZZ2547+ZZ2547 -+007416 003614 ZZ2547=ZZ2547+ZZ2547 -+007416 007430 ZZ2547=ZZ2547+ZZ2547 -+007416 017060 ZZ2547=ZZ2547+ZZ2547 -+007416 036140 ZZ2547=ZZ2547+ZZ2547 -+007416 074300 ZZ2547=ZZ2547+ZZ2547 -+007416 170600 ZZ2547=ZZ2547+ZZ2547 -+007416 361400 ZZ2547=ZZ2547+ZZ2547 -+007416 003147 0 8192 -ZZ1547 -+007417 361400 0 ZZ2547 -- mark 6576, -410 /44 sgtr -+007420 776312 ZZ2548=ZZ2548+ZZ2548 -+007420 774624 ZZ2548=ZZ2548+ZZ2548 -+007420 771450 ZZ2548=ZZ2548+ZZ2548 -+007420 763120 ZZ2548=ZZ2548+ZZ2548 -+007420 746240 ZZ2548=ZZ2548+ZZ2548 -+007420 714500 ZZ2548=ZZ2548+ZZ2548 -+007420 631200 ZZ2548=ZZ2548+ZZ2548 -+007420 462400 ZZ2548=ZZ2548+ZZ2548 -+007420 003120 0 8192 -ZZ1548 -+007421 462400 0 ZZ2548 -- mark 6576, -368 /46 sgtr -+007422 776436 ZZ2549=ZZ2549+ZZ2549 -+007422 775074 ZZ2549=ZZ2549+ZZ2549 -+007422 772170 ZZ2549=ZZ2549+ZZ2549 -+007422 764360 ZZ2549=ZZ2549+ZZ2549 -+007422 750740 ZZ2549=ZZ2549+ZZ2549 -+007422 721700 ZZ2549=ZZ2549+ZZ2549 -+007422 643600 ZZ2549=ZZ2549+ZZ2549 -+007422 507400 ZZ2549=ZZ2549+ZZ2549 -+007422 003120 0 8192 -ZZ1549 -+007423 507400 0 ZZ2549 -- mark 6607, 3 /32 aqil -+007424 000006 ZZ2550=ZZ2550+ZZ2550 -+007424 000014 ZZ2550=ZZ2550+ZZ2550 -+007424 000030 ZZ2550=ZZ2550+ZZ2550 -+007424 000060 ZZ2550=ZZ2550+ZZ2550 -+007424 000140 ZZ2550=ZZ2550+ZZ2550 -+007424 000300 ZZ2550=ZZ2550+ZZ2550 -+007424 000600 ZZ2550=ZZ2550+ZZ2550 -+007424 001400 ZZ2550=ZZ2550+ZZ2550 -+007424 003061 0 8192 -ZZ1550 -+007425 001400 0 ZZ2550 -- mark 6651, 163 /38 aqil -+007426 000506 ZZ2551=ZZ2551+ZZ2551 -+007426 001214 ZZ2551=ZZ2551+ZZ2551 -+007426 002430 ZZ2551=ZZ2551+ZZ2551 -+007426 005060 ZZ2551=ZZ2551+ZZ2551 -+007426 012140 ZZ2551=ZZ2551+ZZ2551 -+007426 024300 ZZ2551=ZZ2551+ZZ2551 -+007426 050600 ZZ2551=ZZ2551+ZZ2551 -+007426 121400 ZZ2551=ZZ2551+ZZ2551 -+007426 003005 0 8192 -ZZ1551 -+007427 121400 0 ZZ2551 -- mark 6657, 445 / 9 vulp -+007430 001572 ZZ2552=ZZ2552+ZZ2552 -+007430 003364 ZZ2552=ZZ2552+ZZ2552 -+007430 006750 ZZ2552=ZZ2552+ZZ2552 -+007430 015720 ZZ2552=ZZ2552+ZZ2552 -+007430 033640 ZZ2552=ZZ2552+ZZ2552 -+007430 067500 ZZ2552=ZZ2552+ZZ2552 -+007430 157200 ZZ2552=ZZ2552+ZZ2552 -+007430 336400 ZZ2552=ZZ2552+ZZ2552 -+007430 002777 0 8192 -ZZ1552 -+007431 336400 0 ZZ2552 -- mark 6665, -35 /41 aqil -+007432 777670 ZZ2553=ZZ2553+ZZ2553 -+007432 777560 ZZ2553=ZZ2553+ZZ2553 -+007432 777340 ZZ2553=ZZ2553+ZZ2553 -+007432 776700 ZZ2553=ZZ2553+ZZ2553 -+007432 775600 ZZ2553=ZZ2553+ZZ2553 -+007432 773400 ZZ2553=ZZ2553+ZZ2553 -+007432 767000 ZZ2553=ZZ2553+ZZ2553 -+007432 756000 ZZ2553=ZZ2553+ZZ2553 -+007432 002767 0 8192 -ZZ1553 -+007433 756000 0 ZZ2553 -- mark 6688, 405 / 5 sgte -+007434 001452 ZZ2554=ZZ2554+ZZ2554 -+007434 003124 ZZ2554=ZZ2554+ZZ2554 -+007434 006250 ZZ2554=ZZ2554+ZZ2554 -+007434 014520 ZZ2554=ZZ2554+ZZ2554 -+007434 031240 ZZ2554=ZZ2554+ZZ2554 -+007434 062500 ZZ2554=ZZ2554+ZZ2554 -+007434 145200 ZZ2554=ZZ2554+ZZ2554 -+007434 312400 ZZ2554=ZZ2554+ZZ2554 -+007434 002740 0 8192 -ZZ1554 -+007435 312400 0 ZZ2554 -- mark 6693, 393 / 6 sgte -+007436 001422 ZZ2555=ZZ2555+ZZ2555 -+007436 003044 ZZ2555=ZZ2555+ZZ2555 -+007436 006110 ZZ2555=ZZ2555+ZZ2555 -+007436 014220 ZZ2555=ZZ2555+ZZ2555 -+007436 030440 ZZ2555=ZZ2555+ZZ2555 -+007436 061100 ZZ2555=ZZ2555+ZZ2555 -+007436 142200 ZZ2555=ZZ2555+ZZ2555 -+007436 304400 ZZ2555=ZZ2555+ZZ2555 -+007436 002733 0 8192 -ZZ1555 -+007437 304400 0 ZZ2555 -- mark 6730, 416 / 7 sgte -+007440 001500 ZZ2556=ZZ2556+ZZ2556 -+007440 003200 ZZ2556=ZZ2556+ZZ2556 -+007440 006400 ZZ2556=ZZ2556+ZZ2556 -+007440 015000 ZZ2556=ZZ2556+ZZ2556 -+007440 032000 ZZ2556=ZZ2556+ZZ2556 -+007440 064000 ZZ2556=ZZ2556+ZZ2556 -+007440 150000 ZZ2556=ZZ2556+ZZ2556 -+007440 320000 ZZ2556=ZZ2556+ZZ2556 -+007440 002666 0 8192 -ZZ1556 -+007441 320000 0 ZZ2556 -- mark 6739, 430 / 8 sgte -+007442 001534 ZZ2557=ZZ2557+ZZ2557 -+007442 003270 ZZ2557=ZZ2557+ZZ2557 -+007442 006560 ZZ2557=ZZ2557+ZZ2557 -+007442 015340 ZZ2557=ZZ2557+ZZ2557 -+007442 032700 ZZ2557=ZZ2557+ZZ2557 -+007442 065600 ZZ2557=ZZ2557+ZZ2557 -+007442 153400 ZZ2557=ZZ2557+ZZ2557 -+007442 327000 ZZ2557=ZZ2557+ZZ2557 -+007442 002655 0 8192 -ZZ1557 -+007443 327000 0 ZZ2557 -- mark 6755, 17 /55 aqil -+007444 000042 ZZ2558=ZZ2558+ZZ2558 -+007444 000104 ZZ2558=ZZ2558+ZZ2558 -+007444 000210 ZZ2558=ZZ2558+ZZ2558 -+007444 000420 ZZ2558=ZZ2558+ZZ2558 -+007444 001040 ZZ2558=ZZ2558+ZZ2558 -+007444 002100 ZZ2558=ZZ2558+ZZ2558 -+007444 004200 ZZ2558=ZZ2558+ZZ2558 -+007444 010400 ZZ2558=ZZ2558+ZZ2558 -+007444 002635 0 8192 -ZZ1558 -+007445 010400 0 ZZ2558 -- mark 6766, 187 /59 aqil -+007446 000566 ZZ2559=ZZ2559+ZZ2559 -+007446 001354 ZZ2559=ZZ2559+ZZ2559 -+007446 002730 ZZ2559=ZZ2559+ZZ2559 -+007446 005660 ZZ2559=ZZ2559+ZZ2559 -+007446 013540 ZZ2559=ZZ2559+ZZ2559 -+007446 027300 ZZ2559=ZZ2559+ZZ2559 -+007446 056600 ZZ2559=ZZ2559+ZZ2559 -+007446 135400 ZZ2559=ZZ2559+ZZ2559 -+007446 002622 0 8192 -ZZ1559 -+007447 135400 0 ZZ2559 -- mark 6772, 140 /60 aqil -+007450 000430 ZZ2560=ZZ2560+ZZ2560 -+007450 001060 ZZ2560=ZZ2560+ZZ2560 -+007450 002140 ZZ2560=ZZ2560+ZZ2560 -+007450 004300 ZZ2560=ZZ2560+ZZ2560 -+007450 010600 ZZ2560=ZZ2560+ZZ2560 -+007450 021400 ZZ2560=ZZ2560+ZZ2560 -+007450 043000 ZZ2560=ZZ2560+ZZ2560 -+007450 106000 ZZ2560=ZZ2560+ZZ2560 -+007450 002614 0 8192 -ZZ1560 -+007451 106000 0 ZZ2560 -- mark 6882, 339 /67 aqil -+007452 001246 ZZ2561=ZZ2561+ZZ2561 -+007452 002514 ZZ2561=ZZ2561+ZZ2561 -+007452 005230 ZZ2561=ZZ2561+ZZ2561 -+007452 012460 ZZ2561=ZZ2561+ZZ2561 -+007452 025140 ZZ2561=ZZ2561+ZZ2561 -+007452 052300 ZZ2561=ZZ2561+ZZ2561 -+007452 124600 ZZ2561=ZZ2561+ZZ2561 -+007452 251400 ZZ2561=ZZ2561+ZZ2561 -+007452 002436 0 8192 -ZZ1561 -+007453 251400 0 ZZ2561 -- mark 6896, -292 / 5 capr -+007454 776666 ZZ2562=ZZ2562+ZZ2562 -+007454 775554 ZZ2562=ZZ2562+ZZ2562 -+007454 773330 ZZ2562=ZZ2562+ZZ2562 -+007454 766660 ZZ2562=ZZ2562+ZZ2562 -+007454 755540 ZZ2562=ZZ2562+ZZ2562 -+007454 733300 ZZ2562=ZZ2562+ZZ2562 -+007454 666600 ZZ2562=ZZ2562+ZZ2562 -+007454 555400 ZZ2562=ZZ2562+ZZ2562 -+007454 002420 0 8192 -ZZ1562 -+007455 555400 0 ZZ2562 -- mark 6898, -292 / 6 capr -+007456 776666 ZZ2563=ZZ2563+ZZ2563 -+007456 775554 ZZ2563=ZZ2563+ZZ2563 -+007456 773330 ZZ2563=ZZ2563+ZZ2563 -+007456 766660 ZZ2563=ZZ2563+ZZ2563 -+007456 755540 ZZ2563=ZZ2563+ZZ2563 -+007456 733300 ZZ2563=ZZ2563+ZZ2563 -+007456 666600 ZZ2563=ZZ2563+ZZ2563 -+007456 555400 ZZ2563=ZZ2563+ZZ2563 -+007456 002416 0 8192 -ZZ1563 -+007457 555400 0 ZZ2563 -- mark 6913, -297 / 8 capr -+007460 776654 ZZ2564=ZZ2564+ZZ2564 -+007460 775530 ZZ2564=ZZ2564+ZZ2564 -+007460 773260 ZZ2564=ZZ2564+ZZ2564 -+007460 766540 ZZ2564=ZZ2564+ZZ2564 -+007460 755300 ZZ2564=ZZ2564+ZZ2564 -+007460 732600 ZZ2564=ZZ2564+ZZ2564 -+007460 665400 ZZ2564=ZZ2564+ZZ2564 -+007460 553000 ZZ2564=ZZ2564+ZZ2564 -+007460 002377 0 8192 -ZZ1564 -+007461 553000 0 ZZ2564 -- mark 6958, -413 /11 capr -+007462 776304 ZZ2565=ZZ2565+ZZ2565 -+007462 774610 ZZ2565=ZZ2565+ZZ2565 -+007462 771420 ZZ2565=ZZ2565+ZZ2565 -+007462 763040 ZZ2565=ZZ2565+ZZ2565 -+007462 746100 ZZ2565=ZZ2565+ZZ2565 -+007462 714200 ZZ2565=ZZ2565+ZZ2565 -+007462 630400 ZZ2565=ZZ2565+ZZ2565 -+007462 461000 ZZ2565=ZZ2565+ZZ2565 -+007462 002322 0 8192 -ZZ1565 -+007463 461000 0 ZZ2565 -- mark 6988, 250 / 2 dlph -+007464 000764 ZZ2566=ZZ2566+ZZ2566 -+007464 001750 ZZ2566=ZZ2566+ZZ2566 -+007464 003720 ZZ2566=ZZ2566+ZZ2566 -+007464 007640 ZZ2566=ZZ2566+ZZ2566 -+007464 017500 ZZ2566=ZZ2566+ZZ2566 -+007464 037200 ZZ2566=ZZ2566+ZZ2566 -+007464 076400 ZZ2566=ZZ2566+ZZ2566 -+007464 175000 ZZ2566=ZZ2566+ZZ2566 -+007464 002264 0 8192 -ZZ1566 -+007465 175000 0 ZZ2566 -- mark 7001, 326 / 4 dlph -+007466 001214 ZZ2567=ZZ2567+ZZ2567 -+007466 002430 ZZ2567=ZZ2567+ZZ2567 -+007466 005060 ZZ2567=ZZ2567+ZZ2567 -+007466 012140 ZZ2567=ZZ2567+ZZ2567 -+007466 024300 ZZ2567=ZZ2567+ZZ2567 -+007466 050600 ZZ2567=ZZ2567+ZZ2567 -+007466 121400 ZZ2567=ZZ2567+ZZ2567 -+007466 243000 ZZ2567=ZZ2567+ZZ2567 -+007466 002247 0 8192 -ZZ1567 -+007467 243000 0 ZZ2567 -- mark 7015, -33 /71 aqil -+007470 777674 ZZ2568=ZZ2568+ZZ2568 -+007470 777570 ZZ2568=ZZ2568+ZZ2568 -+007470 777360 ZZ2568=ZZ2568+ZZ2568 -+007470 776740 ZZ2568=ZZ2568+ZZ2568 -+007470 775700 ZZ2568=ZZ2568+ZZ2568 -+007470 773600 ZZ2568=ZZ2568+ZZ2568 -+007470 767400 ZZ2568=ZZ2568+ZZ2568 -+007470 757000 ZZ2568=ZZ2568+ZZ2568 -+007470 002231 0 8192 -ZZ1568 -+007471 757000 0 ZZ2568 -- mark 7020, 475 /29 vulp -+007472 001666 ZZ2569=ZZ2569+ZZ2569 -+007472 003554 ZZ2569=ZZ2569+ZZ2569 -+007472 007330 ZZ2569=ZZ2569+ZZ2569 -+007472 016660 ZZ2569=ZZ2569+ZZ2569 -+007472 035540 ZZ2569=ZZ2569+ZZ2569 -+007472 073300 ZZ2569=ZZ2569+ZZ2569 -+007472 166600 ZZ2569=ZZ2569+ZZ2569 -+007472 355400 ZZ2569=ZZ2569+ZZ2569 -+007472 002224 0 8192 -ZZ1569 -+007473 355400 0 ZZ2569 -- mark 7026, 354 / 9 dlph -+007474 001304 ZZ2570=ZZ2570+ZZ2570 -+007474 002610 ZZ2570=ZZ2570+ZZ2570 -+007474 005420 ZZ2570=ZZ2570+ZZ2570 -+007474 013040 ZZ2570=ZZ2570+ZZ2570 -+007474 026100 ZZ2570=ZZ2570+ZZ2570 -+007474 054200 ZZ2570=ZZ2570+ZZ2570 -+007474 130400 ZZ2570=ZZ2570+ZZ2570 -+007474 261000 ZZ2570=ZZ2570+ZZ2570 -+007474 002216 0 8192 -ZZ1570 -+007475 261000 0 ZZ2570 -- mark 7047, 335 /11 dlph -+007476 001236 ZZ2571=ZZ2571+ZZ2571 -+007476 002474 ZZ2571=ZZ2571+ZZ2571 -+007476 005170 ZZ2571=ZZ2571+ZZ2571 -+007476 012360 ZZ2571=ZZ2571+ZZ2571 -+007476 024740 ZZ2571=ZZ2571+ZZ2571 -+007476 051700 ZZ2571=ZZ2571+ZZ2571 -+007476 123600 ZZ2571=ZZ2571+ZZ2571 -+007476 247400 ZZ2571=ZZ2571+ZZ2571 -+007476 002171 0 8192 -ZZ1571 -+007477 247400 0 ZZ2571 -- mark 7066, 359 /12 dlph -+007500 001316 ZZ2572=ZZ2572+ZZ2572 -+007500 002634 ZZ2572=ZZ2572+ZZ2572 -+007500 005470 ZZ2572=ZZ2572+ZZ2572 -+007500 013160 ZZ2572=ZZ2572+ZZ2572 -+007500 026340 ZZ2572=ZZ2572+ZZ2572 -+007500 054700 ZZ2572=ZZ2572+ZZ2572 -+007500 131600 ZZ2572=ZZ2572+ZZ2572 -+007500 263400 ZZ2572=ZZ2572+ZZ2572 -+007500 002146 0 8192 -ZZ1572 -+007501 263400 0 ZZ2572 -- mark 7067, -225 / 2 aqar -+007502 777074 ZZ2573=ZZ2573+ZZ2573 -+007502 776170 ZZ2573=ZZ2573+ZZ2573 -+007502 774360 ZZ2573=ZZ2573+ZZ2573 -+007502 770740 ZZ2573=ZZ2573+ZZ2573 -+007502 761700 ZZ2573=ZZ2573+ZZ2573 -+007502 743600 ZZ2573=ZZ2573+ZZ2573 -+007502 707400 ZZ2573=ZZ2573+ZZ2573 -+007502 617000 ZZ2573=ZZ2573+ZZ2573 -+007502 002145 0 8192 -ZZ1573 -+007503 617000 0 ZZ2573 -- mark 7068, -123 / 3 aqar -+007504 777410 ZZ2574=ZZ2574+ZZ2574 -+007504 777020 ZZ2574=ZZ2574+ZZ2574 -+007504 776040 ZZ2574=ZZ2574+ZZ2574 -+007504 774100 ZZ2574=ZZ2574+ZZ2574 -+007504 770200 ZZ2574=ZZ2574+ZZ2574 -+007504 760400 ZZ2574=ZZ2574+ZZ2574 -+007504 741000 ZZ2574=ZZ2574+ZZ2574 -+007504 702000 ZZ2574=ZZ2574+ZZ2574 -+007504 002144 0 8192 -ZZ1574 -+007505 702000 0 ZZ2574 -- mark 7096, -213 / 6 aqar -+007506 777124 ZZ2575=ZZ2575+ZZ2575 -+007506 776250 ZZ2575=ZZ2575+ZZ2575 -+007506 774520 ZZ2575=ZZ2575+ZZ2575 -+007506 771240 ZZ2575=ZZ2575+ZZ2575 -+007506 762500 ZZ2575=ZZ2575+ZZ2575 -+007506 745200 ZZ2575=ZZ2575+ZZ2575 -+007506 712400 ZZ2575=ZZ2575+ZZ2575 -+007506 625000 ZZ2575=ZZ2575+ZZ2575 -+007506 002110 0 8192 -ZZ1575 -+007507 625000 0 ZZ2575 -- mark 7161, -461 /22 capr -+007510 776144 ZZ2576=ZZ2576+ZZ2576 -+007510 774310 ZZ2576=ZZ2576+ZZ2576 -+007510 770620 ZZ2576=ZZ2576+ZZ2576 -+007510 761440 ZZ2576=ZZ2576+ZZ2576 -+007510 743100 ZZ2576=ZZ2576+ZZ2576 -+007510 706200 ZZ2576=ZZ2576+ZZ2576 -+007510 614400 ZZ2576=ZZ2576+ZZ2576 -+007510 431000 ZZ2576=ZZ2576+ZZ2576 -+007510 002007 0 8192 -ZZ1576 -+007511 431000 0 ZZ2576 -- mark 7170, -401 /23 capr -+007512 776334 ZZ2577=ZZ2577+ZZ2577 -+007512 774670 ZZ2577=ZZ2577+ZZ2577 -+007512 771560 ZZ2577=ZZ2577+ZZ2577 -+007512 763340 ZZ2577=ZZ2577+ZZ2577 -+007512 746700 ZZ2577=ZZ2577+ZZ2577 -+007512 715600 ZZ2577=ZZ2577+ZZ2577 -+007512 633400 ZZ2577=ZZ2577+ZZ2577 -+007512 467000 ZZ2577=ZZ2577+ZZ2577 -+007512 001776 0 8192 -ZZ1577 -+007513 467000 0 ZZ2577 -- mark 7192, -268 /13 capr -+007514 776746 ZZ2578=ZZ2578+ZZ2578 -+007514 775714 ZZ2578=ZZ2578+ZZ2578 -+007514 773630 ZZ2578=ZZ2578+ZZ2578 -+007514 767460 ZZ2578=ZZ2578+ZZ2578 -+007514 757140 ZZ2578=ZZ2578+ZZ2578 -+007514 736300 ZZ2578=ZZ2578+ZZ2578 -+007514 674600 ZZ2578=ZZ2578+ZZ2578 -+007514 571400 ZZ2578=ZZ2578+ZZ2578 -+007514 001750 0 8192 -ZZ1578 -+007515 571400 0 ZZ2578 -- mark 7199, 222 / 5 equl -+007516 000674 ZZ2579=ZZ2579+ZZ2579 -+007516 001570 ZZ2579=ZZ2579+ZZ2579 -+007516 003360 ZZ2579=ZZ2579+ZZ2579 -+007516 006740 ZZ2579=ZZ2579+ZZ2579 -+007516 015700 ZZ2579=ZZ2579+ZZ2579 -+007516 033600 ZZ2579=ZZ2579+ZZ2579 -+007516 067400 ZZ2579=ZZ2579+ZZ2579 -+007516 157000 ZZ2579=ZZ2579+ZZ2579 -+007516 001741 0 8192 -ZZ1579 -+007517 157000 0 ZZ2579 -- mark 7223, 219 / 7 equl -+007520 000666 ZZ2580=ZZ2580+ZZ2580 -+007520 001554 ZZ2580=ZZ2580+ZZ2580 -+007520 003330 ZZ2580=ZZ2580+ZZ2580 -+007520 006660 ZZ2580=ZZ2580+ZZ2580 -+007520 015540 ZZ2580=ZZ2580+ZZ2580 -+007520 033300 ZZ2580=ZZ2580+ZZ2580 -+007520 066600 ZZ2580=ZZ2580+ZZ2580 -+007520 155400 ZZ2580=ZZ2580+ZZ2580 -+007520 001711 0 8192 -ZZ1580 -+007521 155400 0 ZZ2580 -- mark 7230, 110 / 8 equl -+007522 000334 ZZ2581=ZZ2581+ZZ2581 -+007522 000670 ZZ2581=ZZ2581+ZZ2581 -+007522 001560 ZZ2581=ZZ2581+ZZ2581 -+007522 003340 ZZ2581=ZZ2581+ZZ2581 -+007522 006700 ZZ2581=ZZ2581+ZZ2581 -+007522 015600 ZZ2581=ZZ2581+ZZ2581 -+007522 033400 ZZ2581=ZZ2581+ZZ2581 -+007522 067000 ZZ2581=ZZ2581+ZZ2581 -+007522 001702 0 8192 -ZZ1581 -+007523 067000 0 ZZ2581 -- mark 7263, -393 /32 capr -+007524 776354 ZZ2582=ZZ2582+ZZ2582 -+007524 774730 ZZ2582=ZZ2582+ZZ2582 -+007524 771660 ZZ2582=ZZ2582+ZZ2582 -+007524 763540 ZZ2582=ZZ2582+ZZ2582 -+007524 747300 ZZ2582=ZZ2582+ZZ2582 -+007524 716600 ZZ2582=ZZ2582+ZZ2582 -+007524 635400 ZZ2582=ZZ2582+ZZ2582 -+007524 473000 ZZ2582=ZZ2582+ZZ2582 -+007524 001641 0 8192 -ZZ1582 -+007525 473000 0 ZZ2582 -- mark 7267, 441 / 1 pegs -+007526 001562 ZZ2583=ZZ2583+ZZ2583 -+007526 003344 ZZ2583=ZZ2583+ZZ2583 -+007526 006710 ZZ2583=ZZ2583+ZZ2583 -+007526 015620 ZZ2583=ZZ2583+ZZ2583 -+007526 033440 ZZ2583=ZZ2583+ZZ2583 -+007526 067100 ZZ2583=ZZ2583+ZZ2583 -+007526 156200 ZZ2583=ZZ2583+ZZ2583 -+007526 334400 ZZ2583=ZZ2583+ZZ2583 -+007526 001635 0 8192 -ZZ1583 -+007527 334400 0 ZZ2583 -- mark 7299, -506 /36 capr -+007530 776012 ZZ2584=ZZ2584+ZZ2584 -+007530 774024 ZZ2584=ZZ2584+ZZ2584 -+007530 770050 ZZ2584=ZZ2584+ZZ2584 -+007530 760120 ZZ2584=ZZ2584+ZZ2584 -+007530 740240 ZZ2584=ZZ2584+ZZ2584 -+007530 700500 ZZ2584=ZZ2584+ZZ2584 -+007530 601200 ZZ2584=ZZ2584+ZZ2584 -+007530 402400 ZZ2584=ZZ2584+ZZ2584 -+007530 001575 0 8192 -ZZ1584 -+007531 402400 0 ZZ2584 -- mark 7347, -453 /39 capr -+007532 776164 ZZ2585=ZZ2585+ZZ2585 -+007532 774350 ZZ2585=ZZ2585+ZZ2585 -+007532 770720 ZZ2585=ZZ2585+ZZ2585 -+007532 761640 ZZ2585=ZZ2585+ZZ2585 -+007532 743500 ZZ2585=ZZ2585+ZZ2585 -+007532 707200 ZZ2585=ZZ2585+ZZ2585 -+007532 616400 ZZ2585=ZZ2585+ZZ2585 -+007532 435000 ZZ2585=ZZ2585+ZZ2585 -+007532 001515 0 8192 -ZZ1585 -+007533 435000 0 ZZ2585 -- mark 7353, -189 /23 aqar -+007534 777204 ZZ2586=ZZ2586+ZZ2586 -+007534 776410 ZZ2586=ZZ2586+ZZ2586 -+007534 775020 ZZ2586=ZZ2586+ZZ2586 -+007534 772040 ZZ2586=ZZ2586+ZZ2586 -+007534 764100 ZZ2586=ZZ2586+ZZ2586 -+007534 750200 ZZ2586=ZZ2586+ZZ2586 -+007534 720400 ZZ2586=ZZ2586+ZZ2586 -+007534 641000 ZZ2586=ZZ2586+ZZ2586 -+007534 001507 0 8192 -ZZ1586 -+007535 641000 0 ZZ2586 -- mark 7365, -390 /40 capr -+007536 776362 ZZ2587=ZZ2587+ZZ2587 -+007536 774744 ZZ2587=ZZ2587+ZZ2587 -+007536 771710 ZZ2587=ZZ2587+ZZ2587 -+007536 763620 ZZ2587=ZZ2587+ZZ2587 -+007536 747440 ZZ2587=ZZ2587+ZZ2587 -+007536 717100 ZZ2587=ZZ2587+ZZ2587 -+007536 636200 ZZ2587=ZZ2587+ZZ2587 -+007536 474400 ZZ2587=ZZ2587+ZZ2587 -+007536 001473 0 8192 -ZZ1587 -+007537 474400 0 ZZ2587 -- mark 7379, -440 /43 capr -+007540 776216 ZZ2588=ZZ2588+ZZ2588 -+007540 774434 ZZ2588=ZZ2588+ZZ2588 -+007540 771070 ZZ2588=ZZ2588+ZZ2588 -+007540 762160 ZZ2588=ZZ2588+ZZ2588 -+007540 744340 ZZ2588=ZZ2588+ZZ2588 -+007540 710700 ZZ2588=ZZ2588+ZZ2588 -+007540 621600 ZZ2588=ZZ2588+ZZ2588 -+007540 443400 ZZ2588=ZZ2588+ZZ2588 -+007540 001455 0 8192 -ZZ1588 -+007541 443400 0 ZZ2588 -- mark 7394, 384 / 9 pegs -+007542 001400 ZZ2589=ZZ2589+ZZ2589 -+007542 003000 ZZ2589=ZZ2589+ZZ2589 -+007542 006000 ZZ2589=ZZ2589+ZZ2589 -+007542 014000 ZZ2589=ZZ2589+ZZ2589 -+007542 030000 ZZ2589=ZZ2589+ZZ2589 -+007542 060000 ZZ2589=ZZ2589+ZZ2589 -+007542 140000 ZZ2589=ZZ2589+ZZ2589 -+007542 300000 ZZ2589=ZZ2589+ZZ2589 -+007542 001436 0 8192 -ZZ1589 -+007543 300000 0 ZZ2589 -- mark 7499, -60 /31 aquar -+007544 777606 ZZ2590=ZZ2590+ZZ2590 -+007544 777414 ZZ2590=ZZ2590+ZZ2590 -+007544 777030 ZZ2590=ZZ2590+ZZ2590 -+007544 776060 ZZ2590=ZZ2590+ZZ2590 -+007544 774140 ZZ2590=ZZ2590+ZZ2590 -+007544 770300 ZZ2590=ZZ2590+ZZ2590 -+007544 760600 ZZ2590=ZZ2590+ZZ2590 -+007544 741400 ZZ2590=ZZ2590+ZZ2590 -+007544 001265 0 8192 -ZZ1590 -+007545 741400 0 ZZ2590 -- mark 7513, 104 /22 pegs -+007546 000320 ZZ2591=ZZ2591+ZZ2591 -+007546 000640 ZZ2591=ZZ2591+ZZ2591 -+007546 001500 ZZ2591=ZZ2591+ZZ2591 -+007546 003200 ZZ2591=ZZ2591+ZZ2591 -+007546 006400 ZZ2591=ZZ2591+ZZ2591 -+007546 015000 ZZ2591=ZZ2591+ZZ2591 -+007546 032000 ZZ2591=ZZ2591+ZZ2591 -+007546 064000 ZZ2591=ZZ2591+ZZ2591 -+007546 001247 0 8192 -ZZ1591 -+007547 064000 0 ZZ2591 -- mark 7515, -327 /33 aqar -+007550 776560 ZZ2592=ZZ2592+ZZ2592 -+007550 775340 ZZ2592=ZZ2592+ZZ2592 -+007550 772700 ZZ2592=ZZ2592+ZZ2592 -+007550 765600 ZZ2592=ZZ2592+ZZ2592 -+007550 753400 ZZ2592=ZZ2592+ZZ2592 -+007550 727000 ZZ2592=ZZ2592+ZZ2592 -+007550 656000 ZZ2592=ZZ2592+ZZ2592 -+007550 534000 ZZ2592=ZZ2592+ZZ2592 -+007550 001245 0 8192 -ZZ1592 -+007551 534000 0 ZZ2592 -- mark 7575, -189 /43 aqar -+007552 777204 ZZ2593=ZZ2593+ZZ2593 -+007552 776410 ZZ2593=ZZ2593+ZZ2593 -+007552 775020 ZZ2593=ZZ2593+ZZ2593 -+007552 772040 ZZ2593=ZZ2593+ZZ2593 -+007552 764100 ZZ2593=ZZ2593+ZZ2593 -+007552 750200 ZZ2593=ZZ2593+ZZ2593 -+007552 720400 ZZ2593=ZZ2593+ZZ2593 -+007552 641000 ZZ2593=ZZ2593+ZZ2593 -+007552 001151 0 8192 -ZZ1593 -+007553 641000 0 ZZ2593 -- mark 7603, -43 /48 aqar -+007554 777650 ZZ2594=ZZ2594+ZZ2594 -+007554 777520 ZZ2594=ZZ2594+ZZ2594 -+007554 777240 ZZ2594=ZZ2594+ZZ2594 -+007554 776500 ZZ2594=ZZ2594+ZZ2594 -+007554 775200 ZZ2594=ZZ2594+ZZ2594 -+007554 772400 ZZ2594=ZZ2594+ZZ2594 -+007554 765000 ZZ2594=ZZ2594+ZZ2594 -+007554 752000 ZZ2594=ZZ2594+ZZ2594 -+007554 001115 0 8192 -ZZ1594 -+007555 752000 0 ZZ2594 -- mark 7604, 266 /31 pegs -+007556 001024 ZZ2595=ZZ2595+ZZ2595 -+007556 002050 ZZ2595=ZZ2595+ZZ2595 -+007556 004120 ZZ2595=ZZ2595+ZZ2595 -+007556 010240 ZZ2595=ZZ2595+ZZ2595 -+007556 020500 ZZ2595=ZZ2595+ZZ2595 -+007556 041200 ZZ2595=ZZ2595+ZZ2595 -+007556 102400 ZZ2595=ZZ2595+ZZ2595 -+007556 205000 ZZ2595=ZZ2595+ZZ2595 -+007556 001114 0 8192 -ZZ1595 -+007557 205000 0 ZZ2595 -- mark 7624, 20 /52 aquar -+007560 000050 ZZ2596=ZZ2596+ZZ2596 -+007560 000120 ZZ2596=ZZ2596+ZZ2596 -+007560 000240 ZZ2596=ZZ2596+ZZ2596 -+007560 000500 ZZ2596=ZZ2596+ZZ2596 -+007560 001200 ZZ2596=ZZ2596+ZZ2596 -+007560 002400 ZZ2596=ZZ2596+ZZ2596 -+007560 005000 ZZ2596=ZZ2596+ZZ2596 -+007560 012000 ZZ2596=ZZ2596+ZZ2596 -+007560 001070 0 8192 -ZZ1596 -+007561 012000 0 ZZ2596 -- mark 7639, 96 /35 pegs -+007562 000300 ZZ2597=ZZ2597+ZZ2597 -+007562 000600 ZZ2597=ZZ2597+ZZ2597 -+007562 001400 ZZ2597=ZZ2597+ZZ2597 -+007562 003000 ZZ2597=ZZ2597+ZZ2597 -+007562 006000 ZZ2597=ZZ2597+ZZ2597 -+007562 014000 ZZ2597=ZZ2597+ZZ2597 -+007562 030000 ZZ2597=ZZ2597+ZZ2597 -+007562 060000 ZZ2597=ZZ2597+ZZ2597 -+007562 001051 0 8192 -ZZ1597 -+007563 060000 0 ZZ2597 -- mark 7654, -255 /57 aqar -+007564 777000 ZZ2598=ZZ2598+ZZ2598 -+007564 776000 ZZ2598=ZZ2598+ZZ2598 -+007564 774000 ZZ2598=ZZ2598+ZZ2598 -+007564 770000 ZZ2598=ZZ2598+ZZ2598 -+007564 760000 ZZ2598=ZZ2598+ZZ2598 -+007564 740000 ZZ2598=ZZ2598+ZZ2598 -+007564 700000 ZZ2598=ZZ2598+ZZ2598 -+007564 600000 ZZ2598=ZZ2598+ZZ2598 -+007564 001032 0 8192 -ZZ1598 -+007565 600000 0 ZZ2598 -- mark 7681, -14 /62 aqar -+007566 777742 ZZ2599=ZZ2599+ZZ2599 -+007566 777704 ZZ2599=ZZ2599+ZZ2599 -+007566 777610 ZZ2599=ZZ2599+ZZ2599 -+007566 777420 ZZ2599=ZZ2599+ZZ2599 -+007566 777040 ZZ2599=ZZ2599+ZZ2599 -+007566 776100 ZZ2599=ZZ2599+ZZ2599 -+007566 774200 ZZ2599=ZZ2599+ZZ2599 -+007566 770400 ZZ2599=ZZ2599+ZZ2599 -+007566 000777 0 8192 -ZZ1599 -+007567 770400 0 ZZ2599 -- mark 7727, -440 /66 aqar -+007570 776216 ZZ2600=ZZ2600+ZZ2600 -+007570 774434 ZZ2600=ZZ2600+ZZ2600 -+007570 771070 ZZ2600=ZZ2600+ZZ2600 -+007570 762160 ZZ2600=ZZ2600+ZZ2600 -+007570 744340 ZZ2600=ZZ2600+ZZ2600 -+007570 710700 ZZ2600=ZZ2600+ZZ2600 -+007570 621600 ZZ2600=ZZ2600+ZZ2600 -+007570 443400 ZZ2600=ZZ2600+ZZ2600 -+007570 000721 0 8192 -ZZ1600 -+007571 443400 0 ZZ2600 -- mark 7747, 266 /46 pegs -+007572 001024 ZZ2601=ZZ2601+ZZ2601 -+007572 002050 ZZ2601=ZZ2601+ZZ2601 -+007572 004120 ZZ2601=ZZ2601+ZZ2601 -+007572 010240 ZZ2601=ZZ2601+ZZ2601 -+007572 020500 ZZ2601=ZZ2601+ZZ2601 -+007572 041200 ZZ2601=ZZ2601+ZZ2601 -+007572 102400 ZZ2601=ZZ2601+ZZ2601 -+007572 205000 ZZ2601=ZZ2601+ZZ2601 -+007572 000675 0 8192 -ZZ1601 -+007573 205000 0 ZZ2601 -- mark 7761, -321 /71 aqar -+007574 776574 ZZ2602=ZZ2602+ZZ2602 -+007574 775370 ZZ2602=ZZ2602+ZZ2602 -+007574 772760 ZZ2602=ZZ2602+ZZ2602 -+007574 765740 ZZ2602=ZZ2602+ZZ2602 -+007574 753700 ZZ2602=ZZ2602+ZZ2602 -+007574 727600 ZZ2602=ZZ2602+ZZ2602 -+007574 657400 ZZ2602=ZZ2602+ZZ2602 -+007574 537000 ZZ2602=ZZ2602+ZZ2602 -+007574 000657 0 8192 -ZZ1602 -+007575 537000 0 ZZ2602 -- mark 7779, -185 /73 aqar -+007576 777214 ZZ2603=ZZ2603+ZZ2603 -+007576 776430 ZZ2603=ZZ2603+ZZ2603 -+007576 775060 ZZ2603=ZZ2603+ZZ2603 -+007576 772140 ZZ2603=ZZ2603+ZZ2603 -+007576 764300 ZZ2603=ZZ2603+ZZ2603 -+007576 750600 ZZ2603=ZZ2603+ZZ2603 -+007576 721400 ZZ2603=ZZ2603+ZZ2603 -+007576 643000 ZZ2603=ZZ2603+ZZ2603 -+007576 000635 0 8192 -ZZ1603 -+007577 643000 0 ZZ2603 -- mark 7795, 189 /50 pegs -+007600 000572 ZZ2604=ZZ2604+ZZ2604 -+007600 001364 ZZ2604=ZZ2604+ZZ2604 -+007600 002750 ZZ2604=ZZ2604+ZZ2604 -+007600 005720 ZZ2604=ZZ2604+ZZ2604 -+007600 013640 ZZ2604=ZZ2604+ZZ2604 -+007600 027500 ZZ2604=ZZ2604+ZZ2604 -+007600 057200 ZZ2604=ZZ2604+ZZ2604 -+007600 136400 ZZ2604=ZZ2604+ZZ2604 -+007600 000615 0 8192 -ZZ1604 -+007601 136400 0 ZZ2604 -- mark 7844, 75 / 4 pisc -+007602 000226 ZZ2605=ZZ2605+ZZ2605 -+007602 000454 ZZ2605=ZZ2605+ZZ2605 -+007602 001130 ZZ2605=ZZ2605+ZZ2605 -+007602 002260 ZZ2605=ZZ2605+ZZ2605 -+007602 004540 ZZ2605=ZZ2605+ZZ2605 -+007602 011300 ZZ2605=ZZ2605+ZZ2605 -+007602 022600 ZZ2605=ZZ2605+ZZ2605 -+007602 045400 ZZ2605=ZZ2605+ZZ2605 -+007602 000534 0 8192 -ZZ1605 -+007603 045400 0 ZZ2605 -- mark 7862, 202 /55 pegs -+007604 000624 ZZ2606=ZZ2606+ZZ2606 -+007604 001450 ZZ2606=ZZ2606+ZZ2606 -+007604 003120 ZZ2606=ZZ2606+ZZ2606 -+007604 006240 ZZ2606=ZZ2606+ZZ2606 -+007604 014500 ZZ2606=ZZ2606+ZZ2606 -+007604 031200 ZZ2606=ZZ2606+ZZ2606 -+007604 062400 ZZ2606=ZZ2606+ZZ2606 -+007604 145000 ZZ2606=ZZ2606+ZZ2606 -+007604 000512 0 8192 -ZZ1606 -+007605 145000 0 ZZ2606 -- mark 7874, -494 /88 aqar -+007606 776042 ZZ2607=ZZ2607+ZZ2607 -+007606 774104 ZZ2607=ZZ2607+ZZ2607 -+007606 770210 ZZ2607=ZZ2607+ZZ2607 -+007606 760420 ZZ2607=ZZ2607+ZZ2607 -+007606 741040 ZZ2607=ZZ2607+ZZ2607 -+007606 702100 ZZ2607=ZZ2607+ZZ2607 -+007606 604200 ZZ2607=ZZ2607+ZZ2607 -+007606 410400 ZZ2607=ZZ2607+ZZ2607 -+007606 000476 0 8192 -ZZ1607 -+007607 410400 0 ZZ2607 -- mark 7903, -150 /90 aqar -+007610 777322 ZZ2608=ZZ2608+ZZ2608 -+007610 776644 ZZ2608=ZZ2608+ZZ2608 -+007610 775510 ZZ2608=ZZ2608+ZZ2608 -+007610 773220 ZZ2608=ZZ2608+ZZ2608 -+007610 766440 ZZ2608=ZZ2608+ZZ2608 -+007610 755100 ZZ2608=ZZ2608+ZZ2608 -+007610 732200 ZZ2608=ZZ2608+ZZ2608 -+007610 664400 ZZ2608=ZZ2608+ZZ2608 -+007610 000441 0 8192 -ZZ1608 -+007611 664400 0 ZZ2608 -- mark 7911, -219 /91 aqar -+007612 777110 ZZ2609=ZZ2609+ZZ2609 -+007612 776220 ZZ2609=ZZ2609+ZZ2609 -+007612 774440 ZZ2609=ZZ2609+ZZ2609 -+007612 771100 ZZ2609=ZZ2609+ZZ2609 -+007612 762200 ZZ2609=ZZ2609+ZZ2609 -+007612 744400 ZZ2609=ZZ2609+ZZ2609 -+007612 711000 ZZ2609=ZZ2609+ZZ2609 -+007612 622000 ZZ2609=ZZ2609+ZZ2609 -+007612 000431 0 8192 -ZZ1609 -+007613 622000 0 ZZ2609 -- mark 7919, 62 / 6 pisc -+007614 000174 ZZ2610=ZZ2610+ZZ2610 -+007614 000370 ZZ2610=ZZ2610+ZZ2610 -+007614 000760 ZZ2610=ZZ2610+ZZ2610 -+007614 001740 ZZ2610=ZZ2610+ZZ2610 -+007614 003700 ZZ2610=ZZ2610+ZZ2610 -+007614 007600 ZZ2610=ZZ2610+ZZ2610 -+007614 017400 ZZ2610=ZZ2610+ZZ2610 -+007614 037000 ZZ2610=ZZ2610+ZZ2610 -+007614 000421 0 8192 -ZZ1610 -+007615 037000 0 ZZ2610 -- mark 7923, -222 /93 aqar -+007616 777102 ZZ2611=ZZ2611+ZZ2611 -+007616 776204 ZZ2611=ZZ2611+ZZ2611 -+007616 774410 ZZ2611=ZZ2611+ZZ2611 -+007616 771020 ZZ2611=ZZ2611+ZZ2611 -+007616 762040 ZZ2611=ZZ2611+ZZ2611 -+007616 744100 ZZ2611=ZZ2611+ZZ2611 -+007616 710200 ZZ2611=ZZ2611+ZZ2611 -+007616 620400 ZZ2611=ZZ2611+ZZ2611 -+007616 000415 0 8192 -ZZ1611 -+007617 620400 0 ZZ2611 -- mark 7952, -470 /98 aqar -+007620 776122 ZZ2612=ZZ2612+ZZ2612 -+007620 774244 ZZ2612=ZZ2612+ZZ2612 -+007620 770510 ZZ2612=ZZ2612+ZZ2612 -+007620 761220 ZZ2612=ZZ2612+ZZ2612 -+007620 742440 ZZ2612=ZZ2612+ZZ2612 -+007620 705100 ZZ2612=ZZ2612+ZZ2612 -+007620 612200 ZZ2612=ZZ2612+ZZ2612 -+007620 424400 ZZ2612=ZZ2612+ZZ2612 -+007620 000360 0 8192 -ZZ1612 -+007621 424400 0 ZZ2612 -- mark 7969, -482 /99 aqar -+007622 776072 ZZ2613=ZZ2613+ZZ2613 -+007622 774164 ZZ2613=ZZ2613+ZZ2613 -+007622 770350 ZZ2613=ZZ2613+ZZ2613 -+007622 760720 ZZ2613=ZZ2613+ZZ2613 -+007622 741640 ZZ2613=ZZ2613+ZZ2613 -+007622 703500 ZZ2613=ZZ2613+ZZ2613 -+007622 607200 ZZ2613=ZZ2613+ZZ2613 -+007622 416400 ZZ2613=ZZ2613+ZZ2613 -+007622 000337 0 8192 -ZZ1613 -+007623 416400 0 ZZ2613 -- mark 7975, 16 / 8 pisc -+007624 000040 ZZ2614=ZZ2614+ZZ2614 -+007624 000100 ZZ2614=ZZ2614+ZZ2614 -+007624 000200 ZZ2614=ZZ2614+ZZ2614 -+007624 000400 ZZ2614=ZZ2614+ZZ2614 -+007624 001000 ZZ2614=ZZ2614+ZZ2614 -+007624 002000 ZZ2614=ZZ2614+ZZ2614 -+007624 004000 ZZ2614=ZZ2614+ZZ2614 -+007624 010000 ZZ2614=ZZ2614+ZZ2614 -+007624 000331 0 8192 -ZZ1614 -+007625 010000 0 ZZ2614 -- mark 7981, 133 /10 pisc -+007626 000412 ZZ2615=ZZ2615+ZZ2615 -+007626 001024 ZZ2615=ZZ2615+ZZ2615 -+007626 002050 ZZ2615=ZZ2615+ZZ2615 -+007626 004120 ZZ2615=ZZ2615+ZZ2615 -+007626 010240 ZZ2615=ZZ2615+ZZ2615 -+007626 020500 ZZ2615=ZZ2615+ZZ2615 -+007626 041200 ZZ2615=ZZ2615+ZZ2615 -+007626 102400 ZZ2615=ZZ2615+ZZ2615 -+007626 000323 0 8192 -ZZ1615 -+007627 102400 0 ZZ2615 -- mark 7988, 278 /70 pegs -+007630 001054 ZZ2616=ZZ2616+ZZ2616 -+007630 002130 ZZ2616=ZZ2616+ZZ2616 -+007630 004260 ZZ2616=ZZ2616+ZZ2616 -+007630 010540 ZZ2616=ZZ2616+ZZ2616 -+007630 021300 ZZ2616=ZZ2616+ZZ2616 -+007630 042600 ZZ2616=ZZ2616+ZZ2616 -+007630 105400 ZZ2616=ZZ2616+ZZ2616 -+007630 213000 ZZ2616=ZZ2616+ZZ2616 -+007630 000314 0 8192 -ZZ1616 -+007631 213000 0 ZZ2616 -- mark 8010, -489 /101 aqar -+007632 776054 ZZ2617=ZZ2617+ZZ2617 -+007632 774130 ZZ2617=ZZ2617+ZZ2617 -+007632 770260 ZZ2617=ZZ2617+ZZ2617 -+007632 760540 ZZ2617=ZZ2617+ZZ2617 -+007632 741300 ZZ2617=ZZ2617+ZZ2617 -+007632 702600 ZZ2617=ZZ2617+ZZ2617 -+007632 605400 ZZ2617=ZZ2617+ZZ2617 -+007632 413000 ZZ2617=ZZ2617+ZZ2617 -+007632 000266 0 8192 -ZZ1617 -+007633 413000 0 ZZ2617 -- mark 8049, 116 /17 pisc -+007634 000350 ZZ2618=ZZ2618+ZZ2618 -+007634 000720 ZZ2618=ZZ2618+ZZ2618 -+007634 001640 ZZ2618=ZZ2618+ZZ2618 -+007634 003500 ZZ2618=ZZ2618+ZZ2618 -+007634 007200 ZZ2618=ZZ2618+ZZ2618 -+007634 016400 ZZ2618=ZZ2618+ZZ2618 -+007634 035000 ZZ2618=ZZ2618+ZZ2618 -+007634 072000 ZZ2618=ZZ2618+ZZ2618 -+007634 000217 0 8192 -ZZ1618 -+007635 072000 0 ZZ2618 -- mark 8059, -418 /104 aqar -+007636 776272 ZZ2619=ZZ2619+ZZ2619 -+007636 774564 ZZ2619=ZZ2619+ZZ2619 -+007636 771350 ZZ2619=ZZ2619+ZZ2619 -+007636 762720 ZZ2619=ZZ2619+ZZ2619 -+007636 745640 ZZ2619=ZZ2619+ZZ2619 -+007636 713500 ZZ2619=ZZ2619+ZZ2619 -+007636 627200 ZZ2619=ZZ2619+ZZ2619 -+007636 456400 ZZ2619=ZZ2619+ZZ2619 -+007636 000205 0 8192 -ZZ1619 -+007637 456400 0 ZZ2619 -- mark 8061, 28 /18 pisc -+007640 000070 ZZ2620=ZZ2620+ZZ2620 -+007640 000160 ZZ2620=ZZ2620+ZZ2620 -+007640 000340 ZZ2620=ZZ2620+ZZ2620 -+007640 000700 ZZ2620=ZZ2620+ZZ2620 -+007640 001600 ZZ2620=ZZ2620+ZZ2620 -+007640 003400 ZZ2620=ZZ2620+ZZ2620 -+007640 007000 ZZ2620=ZZ2620+ZZ2620 -+007640 016000 ZZ2620=ZZ2620+ZZ2620 -+007640 000203 0 8192 -ZZ1620 -+007641 016000 0 ZZ2620 -- mark 8064, -344 /105 aqar -+007642 776516 ZZ2621=ZZ2621+ZZ2621 -+007642 775234 ZZ2621=ZZ2621+ZZ2621 -+007642 772470 ZZ2621=ZZ2621+ZZ2621 -+007642 765160 ZZ2621=ZZ2621+ZZ2621 -+007642 752340 ZZ2621=ZZ2621+ZZ2621 -+007642 724700 ZZ2621=ZZ2621+ZZ2621 -+007642 651600 ZZ2621=ZZ2621+ZZ2621 -+007642 523400 ZZ2621=ZZ2621+ZZ2621 -+007642 000200 0 8192 -ZZ1621 -+007643 523400 0 ZZ2621 -- mark 8159, 144 /28 pisc -+007644 000440 ZZ2622=ZZ2622+ZZ2622 -+007644 001100 ZZ2622=ZZ2622+ZZ2622 -+007644 002200 ZZ2622=ZZ2622+ZZ2622 -+007644 004400 ZZ2622=ZZ2622+ZZ2622 -+007644 011000 ZZ2622=ZZ2622+ZZ2622 -+007644 022000 ZZ2622=ZZ2622+ZZ2622 -+007644 044000 ZZ2622=ZZ2622+ZZ2622 -+007644 110000 ZZ2622=ZZ2622+ZZ2622 -+007644 000041 0 8192 -ZZ1622 -+007645 110000 0 ZZ2622 -- mark 8174, -149 /30 pisc -+007646 777324 ZZ2623=ZZ2623+ZZ2623 -+007646 776650 ZZ2623=ZZ2623+ZZ2623 -+007646 775520 ZZ2623=ZZ2623+ZZ2623 -+007646 773240 ZZ2623=ZZ2623+ZZ2623 -+007646 766500 ZZ2623=ZZ2623+ZZ2623 -+007646 755200 ZZ2623=ZZ2623+ZZ2623 -+007646 732400 ZZ2623=ZZ2623+ZZ2623 -+007646 665000 ZZ2623=ZZ2623+ZZ2623 -+007646 000022 0 8192 -ZZ1623 -+007647 665000 0 ZZ2623 - 007650 4q, -- mark 8188, -407 / 2 ceti -+007650 776320 ZZ2624=ZZ2624+ZZ2624 -+007650 774640 ZZ2624=ZZ2624+ZZ2624 -+007650 771500 ZZ2624=ZZ2624+ZZ2624 -+007650 763200 ZZ2624=ZZ2624+ZZ2624 -+007650 746400 ZZ2624=ZZ2624+ZZ2624 -+007650 715000 ZZ2624=ZZ2624+ZZ2624 -+007650 632000 ZZ2624=ZZ2624+ZZ2624 -+007650 464000 ZZ2624=ZZ2624+ZZ2624 -+007650 000004 0 8192 -ZZ1624 -+007651 464000 0 ZZ2624 - 007652 start 4 -` diff --git a/src/pkg/exp/spacewar/pdp1.go b/src/pkg/exp/spacewar/pdp1.go deleted file mode 100644 index e3abd6807..000000000 --- a/src/pkg/exp/spacewar/pdp1.go +++ /dev/null @@ -1,389 +0,0 @@ -// Copyright (c) 1996 Barry Silverman, Brian Silverman, Vadim Gerasimov. -// Portions Copyright (c) 2009 The Go Authors. -// -// 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. - -// This package and spacewar.go implement a simple PDP-1 emulator -// complete enough to run the original PDP-1 video game Spacewar! -// See ../../nacl/README for details on running them. -// -// They are a translation of the Java emulator pdp1.java in -// http://spacewar.oversigma.com/sources/sources.zip. -// -// See also the PDP-1 handbook at http://www.dbit.com/~greeng3/pdp1/pdp1.html -// -// http://spacewar.oversigma.com/readme.html reads: -// -// Spacewar! was conceived in 1961 by Martin Graetz, Stephen Russell, -// and Wayne Wiitanen. It was first realized on the PDP-1 in 1962 by -// Stephen Russell, Peter Samson, Dan Edwards, and Martin Graetz, -// together with Alan Kotok, Steve Piner, and Robert A Saunders. -// Spacewar! is in the public domain, but this credit paragraph must -// accompany all distributed versions of the program. -// -// This is the original version! Martin Graetz provided us with a -// printed version of the source. We typed in in again - it was about -// 40 pages long - and re-assembled it with a PDP-1 assembler written -// in PERL. The resulting binary runs on a PDP-1 emulator written as -// a Java applet. The code is extremely faithful to the original. There -// are only two changes. 1)The spaceships have been made bigger and -// 2) The overall timing has been special cased to deal with varying -// machine speeds. -// -// The "a", "s", "d", "f" keys control one of the spaceships. The "k", -// "l", ";", "'" keys control the other. The controls are spin one -// way, spin the other, thrust, and fire. -// -// Barry Silverman -// Brian Silverman -// Vadim Gerasimov -// -package pdp1 - -import ( - "bufio" - "fmt" - "os" - "io" -) - -type Word uint32 - -const mask = 0777777 -const sign = 0400000 - -const ( - _ = iota // 00 - opAND - opIOR - opXOR - opXCT - _ - _ - opCALJDA - - opLAC // 10 - opLIO - opDAC - opDAP - _ - opDIO - opDZM - _ - - opADD // 20 - opSUB - opIDX - opISP - opSAD - opSAS - opMUS - opDIS - - opJMP // 30 - opJSP - opSKP - opSFT - opLAW - opIOT - _ - opOPR -) - -// A Trapper represents an object with a Trap method. -// The machine calls the Trap method to implement the -// PDP-1 IOT instruction. -type Trapper interface { - Trap(y Word) -} - -// An M represents the machine state of a PDP-1. -// Clients can set Display to install an output device. -type M struct { - AC, IO, PC, OV Word - Mem [010000]Word - Flag [7]bool - Sense [7]bool - Halt bool -} - - -// Step runs a single machine instruction. -func (m *M) Step(t Trapper) os.Error { - inst := m.Mem[m.PC] - m.PC++ - return m.run(inst, t) -} - -// Normalize actual 32-bit integer i to 18-bit ones-complement integer. -// Interpret mod 0777777, because 0777777 == -0 == +0 == 0000000. -func norm(i Word) Word { - i += i >> 18 - i &= mask - if i == mask { - i = 0 - } - return i -} - -type UnknownInstrError struct { - Inst Word - PC Word -} - -func (e UnknownInstrError) String() string { - return fmt.Sprintf("unknown instruction %06o at %06o", e.Inst, e.PC) -} - -type HaltError Word - -func (e HaltError) String() string { - return fmt.Sprintf("executed HLT instruction at %06o", e) -} - -type LoopError Word - -func (e LoopError) String() string { return fmt.Sprintf("indirect load looping at %06o", e) } - -func (m *M) run(inst Word, t Trapper) os.Error { - ib, y := (inst>>12)&1, inst&07777 - op := inst >> 13 - if op < opSKP && op != opCALJDA { - for n := 0; ib != 0; n++ { - if n > 07777 { - return LoopError(m.PC - 1) - } - ib = (m.Mem[y] >> 12) & 1 - y = m.Mem[y] & 07777 - } - } - - switch op { - case opAND: - m.AC &= m.Mem[y] - case opIOR: - m.AC |= m.Mem[y] - case opXOR: - m.AC ^= m.Mem[y] - case opXCT: - m.run(m.Mem[y], t) - case opCALJDA: - a := y - if ib == 0 { - a = 64 - } - m.Mem[a] = m.AC - m.AC = (m.OV << 17) + m.PC - m.PC = a + 1 - case opLAC: - m.AC = m.Mem[y] - case opLIO: - m.IO = m.Mem[y] - case opDAC: - m.Mem[y] = m.AC - case opDAP: - m.Mem[y] = m.Mem[y]&0770000 | m.AC&07777 - case opDIO: - m.Mem[y] = m.IO - case opDZM: - m.Mem[y] = 0 - case opADD: - m.AC += m.Mem[y] - m.OV = m.AC >> 18 - m.AC = norm(m.AC) - case opSUB: - diffSigns := (m.AC^m.Mem[y])>>17 == 1 - m.AC += m.Mem[y] ^ mask - m.AC = norm(m.AC) - if diffSigns && m.Mem[y]>>17 == m.AC>>17 { - m.OV = 1 - } - case opIDX: - m.AC = norm(m.Mem[y] + 1) - m.Mem[y] = m.AC - case opISP: - m.AC = norm(m.Mem[y] + 1) - m.Mem[y] = m.AC - if m.AC&sign == 0 { - m.PC++ - } - case opSAD: - if m.AC != m.Mem[y] { - m.PC++ - } - case opSAS: - if m.AC == m.Mem[y] { - m.PC++ - } - case opMUS: - if m.IO&1 == 1 { - m.AC += m.Mem[y] - m.AC = norm(m.AC) - } - m.IO = (m.IO>>1 | m.AC<<17) & mask - m.AC >>= 1 - case opDIS: - m.AC, m.IO = (m.AC<<1|m.IO>>17)&mask, - ((m.IO<<1|m.AC>>17)&mask)^1 - if m.IO&1 == 1 { - m.AC = m.AC + (m.Mem[y] ^ mask) - } else { - m.AC = m.AC + 1 + m.Mem[y] - } - m.AC = norm(m.AC) - case opJMP: - m.PC = y - case opJSP: - m.AC = (m.OV << 17) + m.PC - m.PC = y - case opSKP: - cond := y&0100 == 0100 && m.AC == 0 || - y&0200 == 0200 && m.AC>>17 == 0 || - y&0400 == 0400 && m.AC>>17 == 1 || - y&01000 == 01000 && m.OV == 0 || - y&02000 == 02000 && m.IO>>17 == 0 || - y&7 != 0 && !m.Flag[y&7] || - y&070 != 0 && !m.Sense[(y&070)>>3] || - y&070 == 010 - if (ib == 0) == cond { - m.PC++ - } - if y&01000 == 01000 { - m.OV = 0 - } - case opSFT: - for count := inst & 0777; count != 0; count >>= 1 { - if count&1 == 0 { - continue - } - switch (inst >> 9) & 017 { - case 001: // rotate AC left - m.AC = (m.AC<<1 | m.AC>>17) & mask - case 002: // rotate IO left - m.IO = (m.IO<<1 | m.IO>>17) & mask - case 003: // rotate AC and IO left. - w := uint64(m.AC)<<18 | uint64(m.IO) - w = w<<1 | w>>35 - m.AC = Word(w>>18) & mask - m.IO = Word(w) & mask - case 005: // shift AC left (excluding sign bit) - m.AC = (m.AC<<1|m.AC>>17)&mask&^sign | m.AC&sign - case 006: // shift IO left (excluding sign bit) - m.IO = (m.IO<<1|m.IO>>17)&mask&^sign | m.IO&sign - case 007: // shift AC and IO left (excluding AC's sign bit) - w := uint64(m.AC)<<18 | uint64(m.IO) - w = w<<1 | w>>35 - m.AC = Word(w>>18)&mask&^sign | m.AC&sign - m.IO = Word(w)&mask&^sign | m.AC&sign - case 011: // rotate AC right - m.AC = (m.AC>>1 | m.AC<<17) & mask - case 012: // rotate IO right - m.IO = (m.IO>>1 | m.IO<<17) & mask - case 013: // rotate AC and IO right - w := uint64(m.AC)<<18 | uint64(m.IO) - w = w>>1 | w<<35 - m.AC = Word(w>>18) & mask - m.IO = Word(w) & mask - case 015: // shift AC right (excluding sign bit) - m.AC = m.AC>>1 | m.AC&sign - case 016: // shift IO right (excluding sign bit) - m.IO = m.IO>>1 | m.IO&sign - case 017: // shift AC and IO right (excluding AC's sign bit) - w := uint64(m.AC)<<18 | uint64(m.IO) - w = w >> 1 - m.AC = Word(w>>18) | m.AC&sign - m.IO = Word(w) & mask - default: - goto Unknown - } - } - case opLAW: - if ib == 0 { - m.AC = y - } else { - m.AC = y ^ mask - } - case opIOT: - t.Trap(y) - case opOPR: - if y&0200 == 0200 { - m.AC = 0 - } - if y&04000 == 04000 { - m.IO = 0 - } - if y&01000 == 01000 { - m.AC ^= mask - } - if y&0400 == 0400 { - m.PC-- - return HaltError(m.PC) - } - switch i, f := y&7, y&010 == 010; { - case i == 7: - for i := 2; i < 7; i++ { - m.Flag[i] = f - } - case i >= 2: - m.Flag[i] = f - } - default: - Unknown: - return UnknownInstrError{inst, m.PC - 1} - } - return nil -} - -// Load loads the machine's memory from a text input file -// listing octal address-value pairs, one per line, matching the -// regular expression ^[ +]([0-7]+)\t([0-7]+). -func (m *M) Load(r io.Reader) os.Error { - b := bufio.NewReader(r) - for { - line, err := b.ReadString('\n') - if err != nil { - if err != os.EOF { - return err - } - break - } - // look for ^[ +]([0-9]+)\t([0-9]+) - if line[0] != ' ' && line[0] != '+' { - continue - } - i := 1 - a := Word(0) - for ; i < len(line) && '0' <= line[i] && line[i] <= '7'; i++ { - a = a*8 + Word(line[i]-'0') - } - if i >= len(line) || line[i] != '\t' || i == 1 { - continue - } - v := Word(0) - j := i - for i++; i < len(line) && '0' <= line[i] && line[i] <= '7'; i++ { - v = v*8 + Word(line[i]-'0') - } - if i == j { - continue - } - m.Mem[a] = v - } - return nil -} diff --git a/src/pkg/exp/spacewar/spacewar.go b/src/pkg/exp/spacewar/spacewar.go deleted file mode 100644 index 7333220ef..000000000 --- a/src/pkg/exp/spacewar/spacewar.go +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright (c) 1996 Barry Silverman, Brian Silverman, Vadim Gerasimov. -// Portions Copyright (c) 2009 The Go Authors. -// -// 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. - -// See ../../nacl/README. - -package main - -import ( - "bytes" - "exp/draw" - "exp/nacl/av" - "exp/nacl/srpc" - "image" - "log" - "os" - "runtime" - "time" - "./pdp1" -) - -func main() { - runtime.LockOSThread() - if srpc.Enabled() { - go srpc.ServeRuntime() - } - - w, err := av.Init(av.SubsystemVideo, 512, 512) - if err != nil { - log.Exitf("av.Init: %s", err) - } - - go quitter(w.QuitChan()) - - var m SpacewarPDP1 - m.Init(w) - m.PC = 4 - f := bytes.NewBuffer([]byte(spacewarCode)) - if err = m.Load(f); err != nil { - log.Exitf("loading %s: %s", "spacewar.lst", err) - } - for err == nil { - //fmt.Printf("step PC=%06o ", m.PC); - //fmt.Printf("inst=%06o AC=%06o IO=%06o OV=%o\n", - // m.Mem[m.PC], m.AC, m.IO, m.OV); - err = m.Step() - } - log.Exitf("step: %s", err) -} - -func quitter(c <-chan bool) { - <-c - os.Exit(0) -} - -// A SpacewarPDP1 is a PDP-1 machine configured to run Spacewar! -// It responds to traps by drawing on the display, and it flushes the -// display and pauses every second time the program counter reaches -// instruction 02051. -type SpacewarPDP1 struct { - pdp1.M - nframe int - frameTime int64 - ctxt draw.Context - dx, dy int - screen draw.Image - ctl pdp1.Word - kc <-chan int - colorModel image.ColorModel - cmap []image.Color - pix [][]uint8 -} - -func min(a, b int) int { - if a < b { - return a - } - return b -} - -func (m *SpacewarPDP1) Init(ctxt draw.Context) { - m.ctxt = ctxt - m.kc = ctxt.KeyboardChan() - m.screen = ctxt.Screen() - m.dx = m.screen.Width() - m.dy = m.screen.Height() - m.colorModel = m.screen.ColorModel() - m.pix = make([][]uint8, m.dy) - for i := range m.pix { - m.pix[i] = make([]uint8, m.dx) - } - m.cmap = make([]image.Color, 256) - for i := range m.cmap { - var r, g, b uint8 - r = uint8(min(0, 255)) - g = uint8(min(i*2, 255)) - b = uint8(min(0, 255)) - m.cmap[i] = m.colorModel.Convert(image.RGBAColor{r, g, b, 0xff}) - } -} - -const ( - frameDelay = 56 * 1e6 // 56 ms -) - -var ctlBits = [...]pdp1.Word{ - 'f': 0000001, - 'd': 0000002, - 'a': 0000004, - 's': 0000010, - '\'': 0040000, - ';': 0100000, - 'k': 0200000, - 'l': 0400000, -} - -func (m *SpacewarPDP1) Step() os.Error { - if m.PC == 02051 { - m.pollInput() - m.nframe++ - if m.nframe&1 == 0 { - m.flush() - t := time.Nanoseconds() - if t >= m.frameTime+3*frameDelay { - m.frameTime = t - } else { - m.frameTime += frameDelay - for t < m.frameTime { - time.Sleep(m.frameTime - t) - t = time.Nanoseconds() - } - } - } - } - return m.M.Step(m) -} - -func (m *SpacewarPDP1) Trap(y pdp1.Word) { - switch y & 077 { - case 7: - x := int(m.AC+0400000) & 0777777 - y := int(m.IO+0400000) & 0777777 - x = x * m.dx / 0777777 - y = y * m.dy / 0777777 - if 0 <= x && x < m.dx && 0 <= y && y < m.dy { - n := uint8(min(int(m.pix[y][x])+128, 255)) - m.pix[y][x] = n - } - case 011: - m.IO = m.ctl - } -} - -func (m *SpacewarPDP1) flush() { - // Update screen image; simulate phosphor decay. - for y := 0; y < m.dy; y++ { - for x := 0; x < m.dx; x++ { - m.screen.Set(x, y, m.cmap[m.pix[y][x]]) - m.pix[y][x] >>= 1 - } - } - m.ctxt.FlushImage() -} - -func (m *SpacewarPDP1) pollInput() { - for { - select { - case ch := <-m.kc: - if 0 <= ch && ch < len(ctlBits) { - m.ctl |= ctlBits[ch] - } - if 0 <= -ch && -ch < len(ctlBits) { - m.ctl &^= ctlBits[-ch] - } - default: - return - } - } -} diff --git a/src/pkg/exp/spacewar/spacewar.html b/src/pkg/exp/spacewar/spacewar.html deleted file mode 100644 index 8f49b1c34..000000000 --- a/src/pkg/exp/spacewar/spacewar.html +++ /dev/null @@ -1,21 +0,0 @@ - -

    Spacewar

    - -
    - - -This is a Go translation of the Java emulator pdp1.java in -http://spacewar.oversigma.com/sources/sources.zip. -See pdp1.go, spacewar.go, -and -http://spacewar.oversigma.com/readme.html. -

    -The a, s, d, f keys control one of the spaceships. The k, -l, ;, ' keys control the other. The controls are spin one -way, spin the other, thrust, and fire. -
    -
    -You may need to click on the game window to -focus the keyboard on it. - -
    diff --git a/src/pkg/expvar/Makefile b/src/pkg/expvar/Makefile index 544891739..5619630d1 100644 --- a/src/pkg/expvar/Makefile +++ b/src/pkg/expvar/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=expvar GOFILES=\ diff --git a/src/pkg/expvar/expvar.go b/src/pkg/expvar/expvar.go index 4017027b7..fb20b25b2 100644 --- a/src/pkg/expvar/expvar.go +++ b/src/pkg/expvar/expvar.go @@ -6,6 +6,8 @@ // such as operation counters in servers. It exposes these variables via // HTTP at /debug/vars in JSON format. // +// Operations to set or modify these public variables are atomic. +// // In addition to adding the HTTP handler, this package registers the // following variables: // @@ -50,6 +52,12 @@ func (v *Int) Add(delta int64) { v.i += delta } +func (v *Int) Set(value int64) { + v.mu.Lock() + defer v.mu.Unlock() + v.i = value +} + // Map is a string-to-Var map variable, and satisfies the Var interface. type Map struct { m map[string]Var @@ -144,7 +152,7 @@ func (v IntFunc) String() string { return strconv.Itoa64(v()) } // The function will be called each time the Var is evaluated. type StringFunc func() string -func (f StringFunc) String() string { return f() } +func (f StringFunc) String() string { return strconv.Quote(f()) } // All published variables. @@ -153,12 +161,12 @@ var mutex sync.Mutex // Publish declares an named exported variable. This should be called from a // package's init function when it creates its Vars. If the name is already -// registered then this will log.Crash. +// registered then this will log.Panic. func Publish(name string, v Var) { mutex.Lock() defer mutex.Unlock() if _, existing := vars[name]; existing { - log.Crash("Reuse of exported var name:", name) + log.Panicln("Reuse of exported var name:", name) } vars[name] = v } @@ -210,18 +218,18 @@ func Iter() <-chan KeyValue { return c } -func expvarHandler(c *http.Conn, req *http.Request) { - c.SetHeader("content-type", "application/json; charset=utf-8") - fmt.Fprintf(c, "{\n") +func expvarHandler(w http.ResponseWriter, r *http.Request) { + w.SetHeader("content-type", "application/json; charset=utf-8") + fmt.Fprintf(w, "{\n") first := true for name, value := range vars { if !first { - fmt.Fprintf(c, ",\n") + fmt.Fprintf(w, ",\n") } first = false - fmt.Fprintf(c, "%q: %s", name, value) + fmt.Fprintf(w, "%q: %s", name, value) } - fmt.Fprintf(c, "\n}\n") + fmt.Fprintf(w, "\n}\n") } func memstats() string { diff --git a/src/pkg/expvar/expvar_test.go b/src/pkg/expvar/expvar_test.go index 98cd9c2ea..009f24d1a 100644 --- a/src/pkg/expvar/expvar_test.go +++ b/src/pkg/expvar/expvar_test.go @@ -27,6 +27,11 @@ func TestInt(t *testing.T) { if s := reqs.String(); s != "4" { t.Errorf("reqs.String() = %q, want \"4\"", s) } + + reqs.Set(-2) + if reqs.i != -2 { + t.Errorf("reqs.i = %v, want -2", reqs.i) + } } func TestString(t *testing.T) { @@ -76,13 +81,13 @@ func TestMapCounter(t *testing.T) { t.Error("red.Kind() is not a number.") } if x != 3 { - t.Error("red = %v, want 3", x) + t.Errorf("red = %v, want 3", x) } } func TestIntFunc(t *testing.T) { - x := int(4) - ix := IntFunc(func() int64 { return int64(x) }) + x := int64(4) + ix := IntFunc(func() int64 { return x }) if s := ix.String(); s != "4" { t.Errorf("ix.String() = %v, want 4", s) } @@ -92,3 +97,16 @@ func TestIntFunc(t *testing.T) { t.Errorf("ix.String() = %v, want 5", s) } } + +func TestStringFunc(t *testing.T) { + x := "hello" + sx := StringFunc(func() string { return x }) + if s, exp := sx.String(), `"hello"`; s != exp { + t.Errorf(`sx.String() = %q, want %q`, s, exp) + } + + x = "goodbye" + if s, exp := sx.String(), `"goodbye"`; s != exp { + t.Errorf(`sx.String() = %q, want %q`, s, exp) + } +} diff --git a/src/pkg/flag/Makefile b/src/pkg/flag/Makefile index 3ffbea83c..3408ca474 100644 --- a/src/pkg/flag/Makefile +++ b/src/pkg/flag/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=flag GOFILES=\ diff --git a/src/pkg/flag/export_test.go b/src/pkg/flag/export_test.go new file mode 100644 index 000000000..b5e3243b3 --- /dev/null +++ b/src/pkg/flag/export_test.go @@ -0,0 +1,32 @@ +// 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 flag + +import "os" + +// Additional routines compiled into the package only during testing. + +// ResetForTesting clears all flag state and sets the usage function as directed. +// After calling ResetForTesting, parse errors in flag handling will panic rather +// than exit the program. +func ResetForTesting(usage func()) { + flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), os.Args[1:]} + Usage = usage + panicOnError = true +} + +// ParseForTesting parses the flag state using the provided arguments. It +// should be called after 1) ResetForTesting and 2) setting up the new flags. +// The return value reports whether the parse was error-free. +func ParseForTesting(args []string) (result bool) { + defer func() { + if recover() != nil { + result = false + } + }() + os.Args = args + Parse() + return true +} diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go index 59c33403d..04fe2fa05 100644 --- a/src/pkg/flag/flag.go +++ b/src/pkg/flag/flag.go @@ -7,7 +7,7 @@ Usage: - 1) Define flags using flag.String(), Bool(), Int(), etc. Example: + Define flags using flag.String(), Bool(), Int(), etc. Example: import "flag" var ip *int = flag.Int("flagname", 1234, "help message for flagname") If you like, you can bind the flag to a variable using the Var() functions. @@ -20,17 +20,18 @@ flag.Var(&flagVal, "name", "help message for flagname") For such flags, the default value is just the initial value of the variable. - 2) After all flags are defined, call + After all flags are defined, call flag.Parse() to parse the command line into the defined flags. - 3) Flags may then be used directly. If you're using the flags themselves, + Flags may then be used directly. If you're using the flags themselves, they are all pointers; if you bind to variables, they're values. fmt.Println("ip has value ", *ip); fmt.Println("flagvar has value ", flagvar); - 4) After parsing, flag.Arg(i) is the i'th argument after the flags. - Args are indexed from 0 up to flag.NArg(). + After parsing, the arguments after the flag are available as the + slice flag.Args() or individually as flag.Arg(i). + The arguments are indexed from 0 up to flag.NArg(). Command line flag syntax: -flag @@ -48,6 +49,19 @@ Integer flags accept 1234, 0664, 0x1234 and may be negative. Boolean flags may be 1, 0, t, f, true, false, TRUE, FALSE, True, False. + + It is safe to call flag.Parse multiple times, possibly after changing + os.Args. This makes it possible to implement command lines with + subcommands that enable additional flags, as in: + + flag.Bool(...) // global options + flag.Parse() // parse leading command + subcmd := flag.Args(0) + switch subcmd { + // add per-subcommand options + } + os.Args = flag.Args() + flag.Parse() */ package flag @@ -200,9 +214,9 @@ type Flag struct { } type allFlags struct { - actual map[string]*Flag - formal map[string]*Flag - first_arg int // 0 is the program name, 1 is first arg + actual map[string]*Flag + formal map[string]*Flag + args []string // arguments after flags } var flags *allFlags @@ -275,18 +289,17 @@ func NFlag() int { return len(flags.actual) } // Arg returns the i'th command-line argument. Arg(0) is the first remaining argument // after flags have been processed. func Arg(i int) string { - i += flags.first_arg - if i < 0 || i >= len(os.Args) { + if i < 0 || i >= len(flags.args) { return "" } - return os.Args[i] + return flags.args[i] } // NArg is the number of arguments remaining after flags have been processed. -func NArg() int { return len(os.Args) - flags.first_arg } +func NArg() int { return len(flags.args) } // Args returns the non-flag command-line arguments. -func Args() []string { return os.Args[flags.first_arg:] } +func Args() []string { return flags.args } // BoolVar defines a bool flag with specified name, default value, and usage string. // The argument p points to a bool variable in which to store the value of the flag. @@ -414,23 +427,20 @@ func Var(value Value, name string, usage string) { } -func (f *allFlags) parseOne(index int) (ok bool, next int) { - s := os.Args[index] - f.first_arg = index // until proven otherwise - if len(s) == 0 { - return false, -1 +func (f *allFlags) parseOne() (ok bool) { + if len(f.args) == 0 { + return false } - if s[0] != '-' { - return false, -1 + s := f.args[0] + if len(s) == 0 || s[0] != '-' || len(s) == 1 { + return false } num_minuses := 1 - if len(s) == 1 { - return false, index - } if s[1] == '-' { num_minuses++ if len(s) == 2 { // "--" terminates the flags - return false, index + 1 + f.args = f.args[1:] + return false } } name := s[num_minuses:] @@ -440,6 +450,7 @@ func (f *allFlags) parseOne(index int) (ok bool, next int) { } // it's a flag. does it have an argument? + f.args = f.args[1:] has_value := false value := "" for i := 1; i < len(name); i++ { // equals cannot be first @@ -456,22 +467,21 @@ func (f *allFlags) parseOne(index int) (ok bool, next int) { fmt.Fprintf(os.Stderr, "flag provided but not defined: -%s\n", name) fail() } - if f, ok := flag.Value.(*boolValue); ok { // special case: doesn't need an arg + if fv, ok := flag.Value.(*boolValue); ok { // special case: doesn't need an arg if has_value { - if !f.Set(value) { - fmt.Fprintf(os.Stderr, "invalid boolean value %t for flag: -%s\n", value, name) + if !fv.Set(value) { + fmt.Fprintf(os.Stderr, "invalid boolean value %q for flag: -%s\n", value, name) fail() } } else { - f.Set("true") + fv.Set("true") } } else { // It must have a value, which might be the next argument. - if !has_value && index < len(os.Args)-1 { + if !has_value && len(f.args) > 0 { // value is the next arg has_value = true - index++ - value = os.Args[index] + value, f.args = f.args[0], f.args[1:] } if !has_value { fmt.Fprintf(os.Stderr, "flag needs an argument: -%s\n", name) @@ -479,54 +489,22 @@ func (f *allFlags) parseOne(index int) (ok bool, next int) { } ok = flag.Value.Set(value) if !ok { - fmt.Fprintf(os.Stderr, "invalid value %s for flag: -%s\n", value, name) + fmt.Fprintf(os.Stderr, "invalid value %q for flag: -%s\n", value, name) fail() } } flags.actual[name] = flag - return true, index + 1 + return true } // Parse parses the command-line flags. Must be called after all flags are defined // and before any are accessed by the program. func Parse() { - for i := 1; i < len(os.Args); { - ok, next := flags.parseOne(i) - if next > 0 { - flags.first_arg = next - i = next - } - if !ok { - break - } + flags.args = os.Args[1:] + for flags.parseOne() { } } -// ResetForTesting clears all flag state and sets the usage function as directed. -// After calling ResetForTesting, parse errors in flag handling will panic rather -// than exit the program. -// For testing only! -func ResetForTesting(usage func()) { - flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), 1} - Usage = usage - panicOnError = true -} - -// ParseForTesting parses the flag state using the provided arguments. It -// should be called after 1) ResetForTesting and 2) setting up the new flags. -// The return value reports whether the parse was error-free. -// For testing only! -func ParseForTesting(args []string) (result bool) { - defer func() { - if recover() != nil { - result = false - } - }() - os.Args = args - Parse() - return true -} - func init() { - flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), 1} + flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), os.Args[1:]} } diff --git a/src/pkg/flag/flag_test.go b/src/pkg/flag/flag_test.go index 83bf7eebf..4ebb73805 100644 --- a/src/pkg/flag/flag_test.go +++ b/src/pkg/flag/flag_test.go @@ -7,6 +7,7 @@ package flag_test import ( . "flag" "fmt" + "os" "testing" ) @@ -161,10 +162,7 @@ func (f *flagVar) String() string { } func (f *flagVar) Set(value string) bool { - n := make(flagVar, len(*f)+1) - copy(n, *f) - *f = n - (*f)[len(*f)-1] = value + *f = append(*f, value) return true } @@ -183,3 +181,21 @@ func TestUserDefined(t *testing.T) { t.Errorf("expected value %q got %q", expect, v.String()) } } + +func TestChangingArgs(t *testing.T) { + ResetForTesting(func() { t.Fatal("bad parse") }) + oldArgs := os.Args + defer func() { os.Args = oldArgs }() + os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"} + before := Bool("before", false, "") + Parse() + cmd := Arg(0) + os.Args = Args() + after := Bool("after", false, "") + Parse() + args := Args() + + if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" { + t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args) + } +} diff --git a/src/pkg/fmt/Makefile b/src/pkg/fmt/Makefile index 28ea396c7..44b48bc67 100644 --- a/src/pkg/fmt/Makefile +++ b/src/pkg/fmt/Makefile @@ -2,10 +2,11 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=fmt GOFILES=\ + doc.go\ format.go\ print.go\ scan.go\ diff --git a/src/pkg/fmt/doc.go b/src/pkg/fmt/doc.go new file mode 100644 index 000000000..f3067eac9 --- /dev/null +++ b/src/pkg/fmt/doc.go @@ -0,0 +1,163 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + Package fmt implements formatted I/O with functions analogous + to C's printf and scanf. The format 'verbs' are derived from C's but + are simpler. + + Printing: + + The verbs: + + General: + %v the value in a default format. + when printing structs, the plus flag (%+v) adds field names + %#v a Go-syntax representation of the value + %T a Go-syntax representation of the type of the value + + Boolean: + %t the word true or false + Integer: + %b base 2 + %c the character represented by the corresponding Unicode code point + %d base 10 + %o base 8 + %x base 16, with lower-case letters for a-f + %X base 16, with upper-case letters for A-F + %U unicode format: U+1234; same as "U+%x" with 4 digits default + Floating-point and complex constituents: + %e scientific notation, e.g. -1234.456e+78 + %E scientific notation, e.g. -1234.456E+78 + %f decimal point but no exponent, e.g. 123.456 + %g whichever of %e or %f produces more compact output + %G whichever of %E or %f produces more compact output + String and slice of bytes: + %s the uninterpreted bytes of the string or slice + %q a double-quoted string safely escaped with Go syntax + %x base 16 notation with two characters per byte + Pointer: + %p base 16 notation, with leading 0x + + There is no 'u' flag. Integers are printed unsigned if they have unsigned type. + Similarly, there is no need to specify the size of the operand (int8, int64). + + For numeric values, the width and precision flags control + formatting; width sets the width of the field, precision the + number of places after the decimal, if appropriate. The + format %6.2f prints 123.45. The width of a field is the number + of Unicode code points in the string. This differs from C's printf where + the field width is the number of bytes. Either or both of the + flags may be replaced with the character '*', causing their values + to be obtained from the next operand, which must be of type int. + + Other flags: + + always print a sign for numeric values + - pad with spaces on the right rather than the left (left-justify the field) + # alternate format: add leading 0 for octal (%#o), 0x for hex (%#x); + 0X for hex (%#X); suppress 0x for %p (%#p); + print a raw (backquoted) string if possible for %q (%#q) + ' ' (space) leave a space for elided sign in numbers (% d); + put spaces between bytes printing strings or slices in hex (% x, % X) + 0 pad with leading zeros rather than spaces + + For each Printf-like function, there is also a Print function + that takes no format and is equivalent to saying %v for every + operand. Another variant Println inserts blanks between + operands and appends a newline. + + Regardless of the verb, if an operand is an interface value, + the internal concrete value is used, not the interface itself. + Thus: + var i interface{} = 23 + fmt.Printf("%v\n", i) + will print 23. + + If an operand implements interface Formatter, that interface + can be used for fine control of formatting. + + If an operand implements method String() string that method + will be used to convert the object to a string, which will then + be formatted as required by the verb (if any). To avoid + recursion in cases such as + type X int + func (x X) String() string { return Sprintf("%d", x) } + cast the value before recurring: + func (x X) String() string { return Sprintf("%d", int(x)) } + + Format errors: + + If an invalid argument is given for a verb, such as providing + a string to %d, the generated string will contain a + description of the problem, as in these examples: + + Wrong type or unknown verb: %!verb(type=value) + Printf("%d", hi): %!d(string=hi) + Too many arguments: %!(EXTRA type=value) + Printf("hi", "guys"): hi%!(EXTRA string=guys) + Too few arguments: %!verb(MISSING) + Printf("hi%d"): hi %!d(MISSING) + Non-int for width or precision: %!(BADWIDTH) or %!(BADPREC) + Printf("%*s", 4.5, "hi"): %!(BADWIDTH)hi + Printf("%.*s", 4.5, "hi"): %!(BADPREC)hi + + All errors begin with the string "%!" followed sometimes + by a single character (the verb) and end with a parenthesized + description. + + Scanning: + + An analogous set of functions scans formatted text to yield + values. Scan, Scanf and Scanln read from os.Stdin; Fscan, + Fscanf and Fscanln read from a specified os.Reader; Sscan, + Sscanf and Sscanln read from an argument string. Sscanln, + Fscanln and Sscanln stop scanning at a newline and require that + the items be followed by one; Sscanf, Fscanf and Sscanf require + newlines in the input to match newlines in the format; the other + routines treat newlines as spaces. + + Scanf, Fscanf, and Sscanf parse the arguments according to a + format string, analogous to that of Printf. For example, %x + will scan an integer as a hexadecimal number, and %v will scan + the default representation format for the value. + + The formats behave analogously to those of Printf with the + following exceptions: + + %p is not implemented + %T is not implemented + %e %E %f %F %g %g are all equivalent and scan any floating point or complex value + %s and %v on strings scan a space-delimited token + + Width is interpreted in the input text (%5s means at most + five runes of input will be read to scan a string) but there + is no syntax for scanning with a precision (no %5.2f, just + %5f). + + When scanning with a format, all non-empty runs of space + characters (except newline) are equivalent to a single + space in both the format and the input. With that proviso, + text in the format string must match the input text; scanning + stops if it does not, with the return value of the function + indicating the number of arguments scanned. + + In all the scanning functions, if an operand implements method + Scan (that is, it implements the Scanner interface) that + method will be used to scan the text for that operand. Also, + if the number of arguments scanned is less than the number of + arguments provided, an error is returned. + + All arguments to be scanned must be either pointers to basic + types or implementations of the Scanner interface. + + Note: Fscan etc. can read one character (rune) past the + input they return, which means that a loop calling a scan + routine may skip some of the input. This is usually a + problem only when there is no space between input values. + However, if the reader provided to Fscan implements UnreadRune, + that method will be used to save the character and successive + calls will not lose data. To attach an UnreadRune method + to a reader without that capability, use bufio.NewReader. +*/ +package fmt diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go index 7e59d4073..0aafe6d99 100644 --- a/src/pkg/fmt/fmt_test.go +++ b/src/pkg/fmt/fmt_test.go @@ -45,11 +45,6 @@ func TestFmtInterface(t *testing.T) { } } -type fmtTest struct { - fmt string - val interface{} - out string -} const b32 uint32 = 1<<32 - 1 const b64 uint64 = 1<<64 - 1 @@ -78,268 +73,326 @@ type C struct { B } +type F int + +func (f F) Format(s State, c int) { + Fprintf(s, "<%c=F(%d)>", c, int(f)) +} + +type G int + +func (g G) GoString() string { + return Sprintf("GoString(%d)", int(g)) +} + +type S struct { + f F // a struct field that Formats + g G // a struct field that GoStrings +} + +// A type with a String method with pointer receiver for testing %p +type P int + +var pValue P + +func (p *P) String() string { + return "String(p)" +} + var b byte -var fmttests = []fmtTest{ - fmtTest{"%d", 12345, "12345"}, - fmtTest{"%v", 12345, "12345"}, - fmtTest{"%t", true, "true"}, +var fmttests = []struct { + fmt string + val interface{} + out string +}{ + {"%d", 12345, "12345"}, + {"%v", 12345, "12345"}, + {"%t", true, "true"}, // basic string - fmtTest{"%s", "abc", "abc"}, - fmtTest{"%x", "abc", "616263"}, - fmtTest{"%x", "xyz", "78797a"}, - fmtTest{"%X", "xyz", "78797A"}, - fmtTest{"%q", "abc", `"abc"`}, + {"%s", "abc", "abc"}, + {"%x", "abc", "616263"}, + {"%x", "xyz", "78797a"}, + {"%X", "xyz", "78797A"}, + {"%q", "abc", `"abc"`}, // basic bytes - fmtTest{"%s", []byte("abc"), "abc"}, - fmtTest{"%x", []byte("abc"), "616263"}, - fmtTest{"% x", []byte("abc"), "61 62 63"}, - fmtTest{"%x", []byte("xyz"), "78797a"}, - fmtTest{"%X", []byte("xyz"), "78797A"}, - fmtTest{"%q", []byte("abc"), `"abc"`}, + {"%s", []byte("abc"), "abc"}, + {"%x", []byte("abc"), "616263"}, + {"% x", []byte("abc\xff"), "61 62 63 ff"}, + {"% X", []byte("abc\xff"), "61 62 63 FF"}, + {"%x", []byte("xyz"), "78797a"}, + {"%X", []byte("xyz"), "78797A"}, + {"%q", []byte("abc"), `"abc"`}, // escaped strings - fmtTest{"%#q", `abc`, "`abc`"}, - fmtTest{"%#q", `"`, "`\"`"}, - fmtTest{"1 %#q", `\n`, "1 `\\n`"}, - fmtTest{"2 %#q", "\n", `2 "\n"`}, - fmtTest{"%q", `"`, `"\""`}, - fmtTest{"%q", "\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`}, - fmtTest{"%q", "abc\xffdef", `"abc\xffdef"`}, - fmtTest{"%q", "\u263a", `"\u263a"`}, - fmtTest{"%q", "\U0010ffff", `"\U0010ffff"`}, + {"%#q", `abc`, "`abc`"}, + {"%#q", `"`, "`\"`"}, + {"1 %#q", `\n`, "1 `\\n`"}, + {"2 %#q", "\n", `2 "\n"`}, + {"%q", `"`, `"\""`}, + {"%q", "\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`}, + {"%q", "abc\xffdef", `"abc\xffdef"`}, + {"%q", "\u263a", `"\u263a"`}, + {"%q", "\U0010ffff", `"\U0010ffff"`}, // width - fmtTest{"%5s", "abc", " abc"}, - fmtTest{"%2s", "\u263a", " \u263a"}, - fmtTest{"%-5s", "abc", "abc "}, - fmtTest{"%05s", "abc", "00abc"}, + {"%5s", "abc", " abc"}, + {"%2s", "\u263a", " \u263a"}, + {"%-5s", "abc", "abc "}, + {"%05s", "abc", "00abc"}, // integers - fmtTest{"%d", 12345, "12345"}, - fmtTest{"%d", -12345, "-12345"}, - fmtTest{"%10d", 12345, " 12345"}, - fmtTest{"%10d", -12345, " -12345"}, - fmtTest{"%+10d", 12345, " +12345"}, - fmtTest{"%010d", 12345, "0000012345"}, - fmtTest{"%010d", -12345, "-000012345"}, - fmtTest{"%-10d", 12345, "12345 "}, - fmtTest{"%010.3d", 1, " 001"}, - fmtTest{"%010.3d", -1, " -001"}, - fmtTest{"%+d", 12345, "+12345"}, - fmtTest{"%+d", -12345, "-12345"}, - fmtTest{"%+d", 0, "+0"}, - fmtTest{"% d", 0, " 0"}, - fmtTest{"% d", 12345, " 12345"}, + {"%d", 12345, "12345"}, + {"%d", -12345, "-12345"}, + {"%10d", 12345, " 12345"}, + {"%10d", -12345, " -12345"}, + {"%+10d", 12345, " +12345"}, + {"%010d", 12345, "0000012345"}, + {"%010d", -12345, "-000012345"}, + {"%-10d", 12345, "12345 "}, + {"%010.3d", 1, " 001"}, + {"%010.3d", -1, " -001"}, + {"%+d", 12345, "+12345"}, + {"%+d", -12345, "-12345"}, + {"%+d", 0, "+0"}, + {"% d", 0, " 0"}, + {"% d", 12345, " 12345"}, + + // unicode format + {"%U", 0x1, "U+0001"}, + {"%.8U", 0x2, "U+00000002"}, + {"%U", 0x1234, "U+1234"}, + {"%U", 0x12345, "U+12345"}, + {"%10.6U", 0xABC, " U+000ABC"}, + {"%-10.6U", 0xABC, "U+000ABC "}, // floats - fmtTest{"%+.3e", 0.0, "+0.000e+00"}, - fmtTest{"%+.3e", 1.0, "+1.000e+00"}, - fmtTest{"%+.3f", -1.0, "-1.000"}, - fmtTest{"% .3E", -1.0, "-1.000E+00"}, - fmtTest{"% .3e", 1.0, " 1.000e+00"}, - fmtTest{"%+.3g", 0.0, "+0"}, - fmtTest{"%+.3g", 1.0, "+1"}, - fmtTest{"%+.3g", -1.0, "-1"}, - fmtTest{"% .3g", -1.0, "-1"}, - fmtTest{"% .3g", 1.0, " 1"}, + {"%+.3e", 0.0, "+0.000e+00"}, + {"%+.3e", 1.0, "+1.000e+00"}, + {"%+.3f", -1.0, "-1.000"}, + {"% .3E", -1.0, "-1.000E+00"}, + {"% .3e", 1.0, " 1.000e+00"}, + {"%+.3g", 0.0, "+0"}, + {"%+.3g", 1.0, "+1"}, + {"%+.3g", -1.0, "-1"}, + {"% .3g", -1.0, "-1"}, + {"% .3g", 1.0, " 1"}, // complex values - fmtTest{"%+.3e", 0i, "(+0.000e+00+0.000e+00i)"}, - fmtTest{"%+.3f", 0i, "(+0.000+0.000i)"}, - fmtTest{"%+.3g", 0i, "(+0+0i)"}, - fmtTest{"%+.3e", 1 + 2i, "(+1.000e+00+2.000e+00i)"}, - fmtTest{"%+.3f", 1 + 2i, "(+1.000+2.000i)"}, - fmtTest{"%+.3g", 1 + 2i, "(+1+2i)"}, - fmtTest{"%.3e", 0i, "(0.000e+00+0.000e+00i)"}, - fmtTest{"%.3f", 0i, "(0.000+0.000i)"}, - fmtTest{"%.3g", 0i, "(0+0i)"}, - fmtTest{"%.3e", 1 + 2i, "(1.000e+00+2.000e+00i)"}, - fmtTest{"%.3f", 1 + 2i, "(1.000+2.000i)"}, - fmtTest{"%.3g", 1 + 2i, "(1+2i)"}, - fmtTest{"%.3e", -1 - 2i, "(-1.000e+00-2.000e+00i)"}, - fmtTest{"%.3f", -1 - 2i, "(-1.000-2.000i)"}, - fmtTest{"%.3g", -1 - 2i, "(-1-2i)"}, - fmtTest{"% .3E", -1 - 2i, "(-1.000E+00-2.000E+00i)"}, - fmtTest{"%+.3g", complex64(1 + 2i), "(+1+2i)"}, - fmtTest{"%+.3g", complex128(1 + 2i), "(+1+2i)"}, + {"%+.3e", 0i, "(+0.000e+00+0.000e+00i)"}, + {"%+.3f", 0i, "(+0.000+0.000i)"}, + {"%+.3g", 0i, "(+0+0i)"}, + {"%+.3e", 1 + 2i, "(+1.000e+00+2.000e+00i)"}, + {"%+.3f", 1 + 2i, "(+1.000+2.000i)"}, + {"%+.3g", 1 + 2i, "(+1+2i)"}, + {"%.3e", 0i, "(0.000e+00+0.000e+00i)"}, + {"%.3f", 0i, "(0.000+0.000i)"}, + {"%.3g", 0i, "(0+0i)"}, + {"%.3e", 1 + 2i, "(1.000e+00+2.000e+00i)"}, + {"%.3f", 1 + 2i, "(1.000+2.000i)"}, + {"%.3g", 1 + 2i, "(1+2i)"}, + {"%.3e", -1 - 2i, "(-1.000e+00-2.000e+00i)"}, + {"%.3f", -1 - 2i, "(-1.000-2.000i)"}, + {"%.3g", -1 - 2i, "(-1-2i)"}, + {"% .3E", -1 - 2i, "(-1.000E+00-2.000E+00i)"}, + {"%+.3g", complex64(1 + 2i), "(+1+2i)"}, + {"%+.3g", complex128(1 + 2i), "(+1+2i)"}, // erroneous formats - fmtTest{"", 2, "?(extra int=2)"}, - fmtTest{"%d", "hello", "%d(string=hello)"}, + {"", 2, "%!(EXTRA int=2)"}, + {"%d", "hello", "%!d(string=hello)"}, // old test/fmt_test.go - fmtTest{"%d", 1234, "1234"}, - fmtTest{"%d", -1234, "-1234"}, - fmtTest{"%d", uint(1234), "1234"}, - fmtTest{"%d", uint32(b32), "4294967295"}, - fmtTest{"%d", uint64(b64), "18446744073709551615"}, - fmtTest{"%o", 01234, "1234"}, - fmtTest{"%#o", 01234, "01234"}, - fmtTest{"%o", uint32(b32), "37777777777"}, - fmtTest{"%o", uint64(b64), "1777777777777777777777"}, - fmtTest{"%x", 0x1234abcd, "1234abcd"}, - fmtTest{"%#x", 0x1234abcd, "0x1234abcd"}, - fmtTest{"%x", b32 - 0x1234567, "fedcba98"}, - fmtTest{"%X", 0x1234abcd, "1234ABCD"}, - fmtTest{"%X", b32 - 0x1234567, "FEDCBA98"}, - fmtTest{"%#X", 0, "0X0"}, - fmtTest{"%x", b64, "ffffffffffffffff"}, - fmtTest{"%b", 7, "111"}, - fmtTest{"%b", b64, "1111111111111111111111111111111111111111111111111111111111111111"}, - fmtTest{"%b", -6, "-110"}, - fmtTest{"%e", float64(1), "1.000000e+00"}, - fmtTest{"%e", float64(1234.5678e3), "1.234568e+06"}, - fmtTest{"%e", float64(1234.5678e-8), "1.234568e-05"}, - fmtTest{"%e", float64(-7), "-7.000000e+00"}, - fmtTest{"%e", float64(-1e-9), "-1.000000e-09"}, - fmtTest{"%f", float64(1234.5678e3), "1234567.800000"}, - fmtTest{"%f", float64(1234.5678e-8), "0.000012"}, - fmtTest{"%f", float64(-7), "-7.000000"}, - fmtTest{"%f", float64(-1e-9), "-0.000000"}, - fmtTest{"%g", float64(1234.5678e3), "1.2345678e+06"}, - fmtTest{"%g", float32(1234.5678e3), "1.2345678e+06"}, - fmtTest{"%g", float64(1234.5678e-8), "1.2345678e-05"}, - fmtTest{"%g", float64(-7), "-7"}, - fmtTest{"%g", float64(-1e-9), "-1e-09"}, - fmtTest{"%g", float32(-1e-9), "-1e-09"}, - fmtTest{"%E", float64(1), "1.000000E+00"}, - fmtTest{"%E", float64(1234.5678e3), "1.234568E+06"}, - fmtTest{"%E", float64(1234.5678e-8), "1.234568E-05"}, - fmtTest{"%E", float64(-7), "-7.000000E+00"}, - fmtTest{"%E", float64(-1e-9), "-1.000000E-09"}, - fmtTest{"%G", float64(1234.5678e3), "1.2345678E+06"}, - fmtTest{"%G", float32(1234.5678e3), "1.2345678E+06"}, - fmtTest{"%G", float64(1234.5678e-8), "1.2345678E-05"}, - fmtTest{"%G", float64(-7), "-7"}, - fmtTest{"%G", float64(-1e-9), "-1E-09"}, - fmtTest{"%G", float32(-1e-9), "-1E-09"}, - fmtTest{"%c", 'x', "x"}, - fmtTest{"%c", 0xe4, "ä"}, - fmtTest{"%c", 0x672c, "本"}, - fmtTest{"%c", '日', "日"}, - fmtTest{"%20.8d", 1234, " 00001234"}, - fmtTest{"%20.8d", -1234, " -00001234"}, - fmtTest{"%20d", 1234, " 1234"}, - fmtTest{"%-20.8d", 1234, "00001234 "}, - fmtTest{"%-20.8d", -1234, "-00001234 "}, - fmtTest{"%-#20.8x", 0x1234abc, "0x01234abc "}, - fmtTest{"%-#20.8X", 0x1234abc, "0X01234ABC "}, - fmtTest{"%-#20.8o", 01234, "00001234 "}, - fmtTest{"%.20b", 7, "00000000000000000111"}, - fmtTest{"%20.5s", "qwertyuiop", " qwert"}, - fmtTest{"%.5s", "qwertyuiop", "qwert"}, - fmtTest{"%-20.5s", "qwertyuiop", "qwert "}, - fmtTest{"%20c", 'x', " x"}, - fmtTest{"%-20c", 'x', "x "}, - fmtTest{"%20.6e", 1.2345e3, " 1.234500e+03"}, - fmtTest{"%20.6e", 1.2345e-3, " 1.234500e-03"}, - fmtTest{"%20e", 1.2345e3, " 1.234500e+03"}, - fmtTest{"%20e", 1.2345e-3, " 1.234500e-03"}, - fmtTest{"%20.8e", 1.2345e3, " 1.23450000e+03"}, - fmtTest{"%20f", float64(1.23456789e3), " 1234.567890"}, - fmtTest{"%20f", float64(1.23456789e-3), " 0.001235"}, - fmtTest{"%20f", float64(12345678901.23456789), " 12345678901.234568"}, - fmtTest{"%-20f", float64(1.23456789e3), "1234.567890 "}, - fmtTest{"%20.8f", float64(1.23456789e3), " 1234.56789000"}, - fmtTest{"%20.8f", float64(1.23456789e-3), " 0.00123457"}, - fmtTest{"%g", float64(1.23456789e3), "1234.56789"}, - fmtTest{"%g", float64(1.23456789e-3), "0.00123456789"}, - fmtTest{"%g", float64(1.23456789e20), "1.23456789e+20"}, - fmtTest{"%20e", math.Inf(1), " +Inf"}, - fmtTest{"%-20f", math.Inf(-1), "-Inf "}, - fmtTest{"%20g", math.NaN(), " NaN"}, + {"%d", 1234, "1234"}, + {"%d", -1234, "-1234"}, + {"%d", uint(1234), "1234"}, + {"%d", uint32(b32), "4294967295"}, + {"%d", uint64(b64), "18446744073709551615"}, + {"%o", 01234, "1234"}, + {"%#o", 01234, "01234"}, + {"%o", uint32(b32), "37777777777"}, + {"%o", uint64(b64), "1777777777777777777777"}, + {"%x", 0x1234abcd, "1234abcd"}, + {"%#x", 0x1234abcd, "0x1234abcd"}, + {"%x", b32 - 0x1234567, "fedcba98"}, + {"%X", 0x1234abcd, "1234ABCD"}, + {"%X", b32 - 0x1234567, "FEDCBA98"}, + {"%#X", 0, "0X0"}, + {"%x", b64, "ffffffffffffffff"}, + {"%b", 7, "111"}, + {"%b", b64, "1111111111111111111111111111111111111111111111111111111111111111"}, + {"%b", -6, "-110"}, + {"%e", float64(1), "1.000000e+00"}, + {"%e", float64(1234.5678e3), "1.234568e+06"}, + {"%e", float64(1234.5678e-8), "1.234568e-05"}, + {"%e", float64(-7), "-7.000000e+00"}, + {"%e", float64(-1e-9), "-1.000000e-09"}, + {"%f", float64(1234.5678e3), "1234567.800000"}, + {"%f", float64(1234.5678e-8), "0.000012"}, + {"%f", float64(-7), "-7.000000"}, + {"%f", float64(-1e-9), "-0.000000"}, + {"%g", float64(1234.5678e3), "1.2345678e+06"}, + {"%g", float32(1234.5678e3), "1.2345678e+06"}, + {"%g", float64(1234.5678e-8), "1.2345678e-05"}, + {"%g", float64(-7), "-7"}, + {"%g", float64(-1e-9), "-1e-09"}, + {"%g", float32(-1e-9), "-1e-09"}, + {"%E", float64(1), "1.000000E+00"}, + {"%E", float64(1234.5678e3), "1.234568E+06"}, + {"%E", float64(1234.5678e-8), "1.234568E-05"}, + {"%E", float64(-7), "-7.000000E+00"}, + {"%E", float64(-1e-9), "-1.000000E-09"}, + {"%G", float64(1234.5678e3), "1.2345678E+06"}, + {"%G", float32(1234.5678e3), "1.2345678E+06"}, + {"%G", float64(1234.5678e-8), "1.2345678E-05"}, + {"%G", float64(-7), "-7"}, + {"%G", float64(-1e-9), "-1E-09"}, + {"%G", float32(-1e-9), "-1E-09"}, + {"%c", 'x', "x"}, + {"%c", 0xe4, "ä"}, + {"%c", 0x672c, "本"}, + {"%c", '日', "日"}, + {"%20.8d", 1234, " 00001234"}, + {"%20.8d", -1234, " -00001234"}, + {"%20d", 1234, " 1234"}, + {"%-20.8d", 1234, "00001234 "}, + {"%-20.8d", -1234, "-00001234 "}, + {"%-#20.8x", 0x1234abc, "0x01234abc "}, + {"%-#20.8X", 0x1234abc, "0X01234ABC "}, + {"%-#20.8o", 01234, "00001234 "}, + {"%.20b", 7, "00000000000000000111"}, + {"%20.5s", "qwertyuiop", " qwert"}, + {"%.5s", "qwertyuiop", "qwert"}, + {"%-20.5s", "qwertyuiop", "qwert "}, + {"%20c", 'x', " x"}, + {"%-20c", 'x', "x "}, + {"%20.6e", 1.2345e3, " 1.234500e+03"}, + {"%20.6e", 1.2345e-3, " 1.234500e-03"}, + {"%20e", 1.2345e3, " 1.234500e+03"}, + {"%20e", 1.2345e-3, " 1.234500e-03"}, + {"%20.8e", 1.2345e3, " 1.23450000e+03"}, + {"%20f", float64(1.23456789e3), " 1234.567890"}, + {"%20f", float64(1.23456789e-3), " 0.001235"}, + {"%20f", float64(12345678901.23456789), " 12345678901.234568"}, + {"%-20f", float64(1.23456789e3), "1234.567890 "}, + {"%20.8f", float64(1.23456789e3), " 1234.56789000"}, + {"%20.8f", float64(1.23456789e-3), " 0.00123457"}, + {"%g", float64(1.23456789e3), "1234.56789"}, + {"%g", float64(1.23456789e-3), "0.00123456789"}, + {"%g", float64(1.23456789e20), "1.23456789e+20"}, + {"%20e", math.Inf(1), " +Inf"}, + {"%-20f", math.Inf(-1), "-Inf "}, + {"%20g", math.NaN(), " NaN"}, // arrays - fmtTest{"%v", array, "[1 2 3 4 5]"}, - fmtTest{"%v", iarray, "[1 hello 2.5 ]"}, - fmtTest{"%v", &array, "&[1 2 3 4 5]"}, - fmtTest{"%v", &iarray, "&[1 hello 2.5 ]"}, + {"%v", array, "[1 2 3 4 5]"}, + {"%v", iarray, "[1 hello 2.5 ]"}, + {"%v", &array, "&[1 2 3 4 5]"}, + {"%v", &iarray, "&[1 hello 2.5 ]"}, // complexes with %v - fmtTest{"%v", 1 + 2i, "(1+2i)"}, - fmtTest{"%v", complex64(1 + 2i), "(1+2i)"}, - fmtTest{"%v", complex128(1 + 2i), "(1+2i)"}, + {"%v", 1 + 2i, "(1+2i)"}, + {"%v", complex64(1 + 2i), "(1+2i)"}, + {"%v", complex128(1 + 2i), "(1+2i)"}, // structs - fmtTest{"%v", A{1, 2, "a", []int{1, 2}}, `{1 2 a [1 2]}`}, - fmtTest{"%+v", A{1, 2, "a", []int{1, 2}}, `{i:1 j:2 s:a x:[1 2]}`}, + {"%v", A{1, 2, "a", []int{1, 2}}, `{1 2 a [1 2]}`}, + {"%+v", A{1, 2, "a", []int{1, 2}}, `{i:1 j:2 s:a x:[1 2]}`}, // +v on structs with Stringable items - fmtTest{"%+v", B{1, 2}, `{i:<1> j:2}`}, - fmtTest{"%+v", C{1, B{2, 3}}, `{i:1 B:{i:<2> j:3}}`}, + {"%+v", B{1, 2}, `{i:<1> j:2}`}, + {"%+v", C{1, B{2, 3}}, `{i:1 B:{i:<2> j:3}}`}, // q on Stringable items - fmtTest{"%s", I(23), `<23>`}, - fmtTest{"%q", I(23), `"<23>"`}, - fmtTest{"%x", I(23), `3c32333e`}, - fmtTest{"%d", I(23), `%d(string=<23>)`}, - - // %p on non-pointers - fmtTest{"%p", make(chan int), "PTR"}, - fmtTest{"%p", make(map[int]int), "PTR"}, - fmtTest{"%p", make([]int, 1), "PTR"}, + {"%s", I(23), `<23>`}, + {"%q", I(23), `"<23>"`}, + {"%x", I(23), `3c32333e`}, + {"%d", I(23), `%!d(string=<23>)`}, // go syntax - fmtTest{"%#v", A{1, 2, "a", []int{1, 2}}, `fmt_test.A{i:1, j:0x2, s:"a", x:[]int{1, 2}}`}, - fmtTest{"%#v", &b, "(*uint8)(PTR)"}, - fmtTest{"%#v", TestFmtInterface, "(func(*testing.T))(PTR)"}, - fmtTest{"%#v", make(chan int), "(chan int)(PTR)"}, - fmtTest{"%#v", uint64(1<<64 - 1), "0xffffffffffffffff"}, - fmtTest{"%#v", 1000000000, "1000000000"}, - fmtTest{"%#v", map[string]int{"a": 1, "b": 2}, `map[string] int{"a":1, "b":2}`}, - fmtTest{"%#v", map[string]B{"a": B{1, 2}, "b": B{3, 4}}, `map[string] fmt_test.B{"a":fmt_test.B{i:1, j:2}, "b":fmt_test.B{i:3, j:4}}`}, - fmtTest{"%#v", []string{"a", "b"}, `[]string{"a", "b"}`}, + {"%#v", A{1, 2, "a", []int{1, 2}}, `fmt_test.A{i:1, j:0x2, s:"a", x:[]int{1, 2}}`}, + {"%#v", &b, "(*uint8)(PTR)"}, + {"%#v", TestFmtInterface, "(func(*testing.T))(PTR)"}, + {"%#v", make(chan int), "(chan int)(PTR)"}, + {"%#v", uint64(1<<64 - 1), "0xffffffffffffffff"}, + {"%#v", 1000000000, "1000000000"}, + {"%#v", map[string]int{"a": 1, "b": 2}, `map[string] int{"a":1, "b":2}`}, + {"%#v", map[string]B{"a": {1, 2}, "b": {3, 4}}, `map[string] fmt_test.B{"a":fmt_test.B{i:1, j:2}, "b":fmt_test.B{i:3, j:4}}`}, + {"%#v", []string{"a", "b"}, `[]string{"a", "b"}`}, // slices with other formats - fmtTest{"%#x", []int{1, 2, 15}, `[0x1 0x2 0xf]`}, - fmtTest{"%x", []int{1, 2, 15}, `[1 2 f]`}, - fmtTest{"%q", []string{"a", "b"}, `["a" "b"]`}, + {"%#x", []int{1, 2, 15}, `[0x1 0x2 0xf]`}, + {"%x", []int{1, 2, 15}, `[1 2 f]`}, + {"%d", []int{1, 2, 15}, `[1 2 15]`}, + {"%d", []byte{1, 2, 15}, `[1 2 15]`}, + {"%q", []string{"a", "b"}, `["a" "b"]`}, // renamings - fmtTest{"%v", renamedBool(true), "true"}, - fmtTest{"%d", renamedBool(true), "%d(fmt_test.renamedBool=true)"}, - fmtTest{"%o", renamedInt(8), "10"}, - fmtTest{"%d", renamedInt8(-9), "-9"}, - fmtTest{"%v", renamedInt16(10), "10"}, - fmtTest{"%v", renamedInt32(-11), "-11"}, - fmtTest{"%X", renamedInt64(255), "FF"}, - fmtTest{"%v", renamedUint(13), "13"}, - fmtTest{"%o", renamedUint8(14), "16"}, - fmtTest{"%X", renamedUint16(15), "F"}, - fmtTest{"%d", renamedUint32(16), "16"}, - fmtTest{"%X", renamedUint64(17), "11"}, - fmtTest{"%o", renamedUintptr(18), "22"}, - fmtTest{"%x", renamedString("thing"), "7468696e67"}, - // TODO: It would be nice if this one worked, but it's hard. - // fmtTest{"%q", renamedBytes([]byte("hello")), `"hello"`}, - fmtTest{"%v", renamedFloat(11), "11"}, - fmtTest{"%v", renamedFloat32(22), "22"}, - fmtTest{"%v", renamedFloat64(33), "33"}, - fmtTest{"%v", renamedComplex(7 + .2i), "(7+0.2i)"}, - fmtTest{"%v", renamedComplex64(3 + 4i), "(3+4i)"}, - fmtTest{"%v", renamedComplex128(4 - 3i), "(4-3i)"}, + {"%v", renamedBool(true), "true"}, + {"%d", renamedBool(true), "%!d(fmt_test.renamedBool=true)"}, + {"%o", renamedInt(8), "10"}, + {"%d", renamedInt8(-9), "-9"}, + {"%v", renamedInt16(10), "10"}, + {"%v", renamedInt32(-11), "-11"}, + {"%X", renamedInt64(255), "FF"}, + {"%v", renamedUint(13), "13"}, + {"%o", renamedUint8(14), "16"}, + {"%X", renamedUint16(15), "F"}, + {"%d", renamedUint32(16), "16"}, + {"%X", renamedUint64(17), "11"}, + {"%o", renamedUintptr(18), "22"}, + {"%x", renamedString("thing"), "7468696e67"}, + {"%d", renamedBytes([]byte{1, 2, 15}), `[1 2 15]`}, + {"%q", renamedBytes([]byte("hello")), `"hello"`}, + {"%v", renamedFloat(11), "11"}, + {"%v", renamedFloat32(22), "22"}, + {"%v", renamedFloat64(33), "33"}, + {"%v", renamedComplex(7 + .2i), "(7+0.2i)"}, + {"%v", renamedComplex64(3 + 4i), "(3+4i)"}, + {"%v", renamedComplex128(4 - 3i), "(4-3i)"}, + + // Formatter + {"%x", F(1), ""}, + {"%x", G(2), "2"}, + {"%+v", S{F(4), G(5)}, "{f: g:5}"}, + + // GoStringer + {"%#v", G(6), "GoString(6)"}, + {"%#v", S{F(7), G(8)}, "fmt_test.S{f:, g:GoString(8)}"}, // %T - fmtTest{"%T", (4 - 3i), "complex"}, - fmtTest{"%T", renamedComplex128(4 - 3i), "fmt_test.renamedComplex128"}, - fmtTest{"%T", intVal, "int"}, - fmtTest{"%6T", &intVal, " *int"}, + {"%T", (4 - 3i), "complex"}, + {"%T", renamedComplex128(4 - 3i), "fmt_test.renamedComplex128"}, + {"%T", intVal, "int"}, + {"%6T", &intVal, " *int"}, + + // %p + {"p0=%p", new(int), "p0=PTR"}, + {"p1=%s", &pValue, "p1=String(p)"}, // String method... + {"p2=%p", &pValue, "p2=PTR"}, // ... not called with %p + + // %p on non-pointers + {"%p", make(chan int), "PTR"}, + {"%p", make(map[int]int), "PTR"}, + {"%p", make([]int, 1), "PTR"}, + {"%p", 27, "%!p(int=27)"}, // not a pointer at all // erroneous things - fmtTest{"%d", "hello", "%d(string=hello)"}, - fmtTest{"no args", "hello", "no args?(extra string=hello)"}, - fmtTest{"%s", nil, "%s()"}, - fmtTest{"%T", nil, ""}, - fmtTest{"%-1", 100, "%1(int=100)"}, + {"%s %", "hello", "hello %!(NOVERB)"}, + {"%s %.2", "hello", "hello %!(NOVERB)"}, + {"%d", "hello", "%!d(string=hello)"}, + {"no args", "hello", "no args%!(EXTRA string=hello)"}, + {"%s", nil, "%!s()"}, + {"%T", nil, ""}, + {"%-1", 100, "%!(NOVERB)%!(EXTRA int=100)"}, } func TestSprintf(t *testing.T) { for _, tt := range fmttests { s := Sprintf(tt.fmt, tt.val) - if i := strings.Index(s, "0x"); i >= 0 && strings.Index(tt.out, "PTR") >= 0 { + if i := strings.Index(s, "0x"); i >= 0 && strings.Contains(tt.out, "PTR") { j := i + 2 for ; j < len(s); j++ { c := s[j] @@ -385,6 +438,12 @@ func BenchmarkSprintfIntInt(b *testing.B) { } } +func BenchmarkSprintfPrefixedInt(b *testing.B) { + for i := 0; i < b.N; i++ { + Sprintf("This is some meaningless prefix text that needs to be scanned %d", 6) + } +} + func TestCountMallocs(t *testing.T) { mallocs := 0 - runtime.MemStats.Mallocs for i := 0; i < 100; i++ { @@ -431,24 +490,22 @@ func (*flagPrinter) Format(f State, c int) { io.WriteString(f, "["+s+"]") } -type flagTest struct { +var flagtests = []struct { in string out string -} - -var flagtests = []flagTest{ - flagTest{"%a", "[%a]"}, - flagTest{"%-a", "[%-a]"}, - flagTest{"%+a", "[%+a]"}, - flagTest{"%#a", "[%#a]"}, - flagTest{"% a", "[% a]"}, - flagTest{"%0a", "[%0a]"}, - flagTest{"%1.2a", "[%1.2a]"}, - flagTest{"%-1.2a", "[%-1.2a]"}, - flagTest{"%+1.2a", "[%+1.2a]"}, - flagTest{"%-+1.2a", "[%+-1.2a]"}, - flagTest{"%-+1.2abc", "[%+-1.2a]bc"}, - flagTest{"%-1.2abc", "[%-1.2a]bc"}, +}{ + {"%a", "[%a]"}, + {"%-a", "[%-a]"}, + {"%+a", "[%+a]"}, + {"%#a", "[%#a]"}, + {"% a", "[% a]"}, + {"%0a", "[%0a]"}, + {"%1.2a", "[%1.2a]"}, + {"%-1.2a", "[%-1.2a]"}, + {"%+1.2a", "[%+1.2a]"}, + {"%-+1.2a", "[%+-1.2a]"}, + {"%-+1.2abc", "[%+-1.2a]bc"}, + {"%-1.2abc", "[%-1.2a]bc"}, } func TestFlagParser(t *testing.T) { @@ -470,13 +527,12 @@ func TestStructPrinter(t *testing.T) { s.a = "abc" s.b = "def" s.c = 123 - type Test struct { + var tests = []struct { fmt string out string - } - var tests = []Test{ - Test{"%v", "{abc def 123}"}, - Test{"%+v", "{a:abc b:def c:123}"}, + }{ + {"%v", "{abc def 123}"}, + {"%+v", "{a:abc b:def c:123}"}, } for _, tt := range tests { out := Sprintf(tt.fmt, s) @@ -526,3 +582,73 @@ func TestEmptyMap(t *testing.T) { t.Errorf("empty map printed as %q not %q", s, emptyMapStr) } } + +// Check that Sprint (and hence Print, Fprint) puts spaces in the right places, +// that is, between arg pairs in which neither is a string. +func TestBlank(t *testing.T) { + got := Sprint("<", 1, ">:", 1, 2, 3, "!") + expect := "<1>:1 2 3!" + if got != expect { + t.Errorf("got %q expected %q", got, expect) + } +} + +// Check that Sprintln (and hence Println, Fprintln) puts spaces in the right places, +// that is, between all arg pairs. +func TestBlankln(t *testing.T) { + got := Sprintln("<", 1, ">:", 1, 2, 3, "!") + expect := "< 1 >: 1 2 3 !\n" + if got != expect { + t.Errorf("got %q expected %q", got, expect) + } +} + + +// Check Formatter with Sprint, Sprintln, Sprintf +func TestFormatterPrintln(t *testing.T) { + f := F(1) + expect := "\n" + s := Sprint(f, "\n") + if s != expect { + t.Errorf("Sprint wrong with Formatter: expected %q got %q", expect, s) + } + s = Sprintln(f) + if s != expect { + t.Errorf("Sprintln wrong with Formatter: expected %q got %q", expect, s) + } + s = Sprintf("%v\n", f) + if s != expect { + t.Errorf("Sprintf wrong with Formatter: expected %q got %q", expect, s) + } +} + +func args(a ...interface{}) []interface{} { return a } + +var startests = []struct { + fmt string + in []interface{} + out string +}{ + {"%*d", args(4, 42), " 42"}, + {"%.*d", args(4, 42), "0042"}, + {"%*.*d", args(8, 4, 42), " 0042"}, + {"%0*d", args(4, 42), "0042"}, + {"%-*d", args(4, 42), "42 "}, + + // erroneous + {"%*d", args(nil, 42), "%!(BADWIDTH)42"}, + {"%.*d", args(nil, 42), "%!(BADPREC)42"}, + {"%*d", args(5, "foo"), "%!d(string= foo)"}, + {"%*% %d", args(20, 5), "% 5"}, + {"%*", args(4), "%!(NOVERB)"}, + {"%*d", args(int32(4), 42), "%!(BADWIDTH)42"}, +} + +func TestWidthAndPrecision(t *testing.T) { + for _, tt := range startests { + s := Sprintf(tt.fmt, tt.in...) + if s != tt.out { + t.Errorf("%q: got %q expected %q", tt.fmt, s, tt.out) + } + } +} diff --git a/src/pkg/fmt/format.go b/src/pkg/fmt/format.go index 3ec1cf139..0121dda31 100644 --- a/src/pkg/fmt/format.go +++ b/src/pkg/fmt/format.go @@ -49,6 +49,7 @@ type fmt struct { plus bool sharp bool space bool + unicode bool zero bool } @@ -61,6 +62,7 @@ func (f *fmt) clearflags() { f.plus = false f.sharp = false f.space = false + f.unicode = false f.zero = false } @@ -213,6 +215,12 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) { buf[i] = '0' } } + if f.unicode { + i-- + buf[i] = '+' + i-- + buf[i] = 'U' + } if negative { i-- @@ -255,6 +263,9 @@ func (f *fmt) fmt_sx(s string) { func (f *fmt) fmt_sX(s string) { t := "" for i := 0; i < len(s); i++ { + if i > 0 && f.space { + t += " " + } v := s[i] t += string(udigits[v>>4]) t += string(udigits[v&0xF]) diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go index 20bfa9107..412260441 100644 --- a/src/pkg/fmt/print.go +++ b/src/pkg/fmt/print.go @@ -2,132 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -/* - Package fmt implements formatted I/O with functions analogous - to C's printf and scanf. The format 'verbs' are derived from C's but - are simpler. - - Printing: - - The verbs: - - General: - %v the value in a default format. - when printing structs, the plus flag (%+v) adds field names - %#v a Go-syntax representation of the value - %T a Go-syntax representation of the type of the value - - Boolean: - %t the word true or false - Integer: - %b base 2 - %c the character represented by the corresponding Unicode code point - %d base 10 - %o base 8 - %x base 16, with lower-case letters for a-f - %X base 16, with upper-case letters for A-F - Floating-point and complex constituents: - %e scientific notation, e.g. -1234.456e+78 - %E scientific notation, e.g. -1234.456E+78 - %f decimal point but no exponent, e.g. 123.456 - %g whichever of %e or %f produces more compact output - %G whichever of %E or %f produces more compact output - String and slice of bytes: - %s the uninterpreted bytes of the string or slice - %q a double-quoted string safely escaped with Go syntax - %x base 16 notation with two characters per byte - Pointer: - %p base 16 notation, with leading 0x - - There is no 'u' flag. Integers are printed unsigned if they have unsigned type. - Similarly, there is no need to specify the size of the operand (int8, int64). - - For numeric values, the width and precision flags control - formatting; width sets the width of the field, precision the - number of places after the decimal, if appropriate. The - format %6.2f prints 123.45. The width of a field is the number - of Unicode code points in the string. This differs from C's printf where - the field width is the number of bytes. - - Other flags: - + always print a sign for numeric values - - pad with spaces on the right rather than the left (left-justify the field) - # alternate format: add leading 0 for octal (%#o), 0x for hex (%#x); - suppress 0x for %p (%#p); - print a raw (backquoted) string if possible for %q (%#q) - ' ' (space) leave a space for elided sign in numbers (% d); - put spaces between bytes printing strings or slices in hex (% x) - 0 pad with leading zeros rather than spaces - - For each Printf-like function, there is also a Print function - that takes no format and is equivalent to saying %v for every - operand. Another variant Println inserts blanks between - operands and appends a newline. - - Regardless of the verb, if an operand is an interface value, - the internal concrete value is used, not the interface itself. - Thus: - var i interface{} = 23; - fmt.Printf("%v\n", i); - will print 23. - - If an operand implements interface Formatter, that interface - can be used for fine control of formatting. - - If an operand implements method String() string that method - will be used to conver the object to a string, which will then - be formatted as required by the verb (if any). To avoid - recursion in cases such as - type X int - func (x X) String() string { return Sprintf("%d", x) } - cast the value before recurring: - func (x X) String() string { return Sprintf("%d", int(x)) } - - Scanning: - - An analogous set of functions scans formatted text to yield - values. Scan, Scanf and Scanln read from os.Stdin; Fscan, - Fscanf and Fscanln read from a specified os.Reader; Sscan, - Sscanf and Sscanln read from an argument string. Sscanln, - Fscanln and Sscanln stop scanning at a newline and require that - the items be followed by one; the other routines treat newlines - as spaces. - - Scanf, Fscanf, and Sscanf parse the arguments according to a - format string, analogous to that of Printf. For example, "%x" - will scan an integer as a hexadecimal number, and %v will scan - the default representation format for the value. - - The formats behave analogously to those of Printf with the - following exceptions: - - %p is not implemented - %T is not implemented - %e %E %f %F %g %g are all equivalent and scan any floating - point or complex value - %s and %v on strings scan a space-delimited token - - Width is interpreted in the input text (%5s means at most - five runes of input will be read to scan a string) but there - is no syntax for scanning with a precision (no %5.2f, just - %5f). - - When scanning with a format, all non-empty runs of space - characters (including newline) are equivalent to a single - space in both the format and the input. With that proviso, - text in the format string must match the input text; scanning - stops if it does not, with the return value of the function - indicating the number of arguments scanned. - - In all the scanning functions, if an operand implements method - Scan (that is, it implements the Scanner interface) that - method will be used to scan the text for that operand. Also, - if the number of arguments scanned is less than the number of - arguments provided, an error is returned. - - All arguments to be scanned must be either pointers to basic - types or implementations of the Scanner interface. -*/ package fmt import ( @@ -146,10 +20,13 @@ var ( nilParenBytes = []byte("(nil)") nilBytes = []byte("nil") mapBytes = []byte("map[") - missingBytes = []byte("missing") - extraBytes = []byte("?(extra ") + missingBytes = []byte("(MISSING)") + extraBytes = []byte("%!(EXTRA ") irparenBytes = []byte("i)") bytesBytes = []byte("[]byte{") + widthBytes = []byte("%!(BADWIDTH)") + precBytes = []byte("%!(BADPREC)") + noVerbBytes = []byte("%!(NOVERB)") ) // State represents the printer state passed to custom formatters. @@ -241,12 +118,7 @@ func (p *pp) Flag(b int) bool { } func (p *pp) add(c int) { - if c < utf8.RuneSelf { - p.buf.WriteByte(byte(c)) - } else { - w := utf8.EncodeRune(c, p.runeBuf[0:]) - p.buf.Write(p.runeBuf[0:w]) - } + p.buf.WriteRune(c) } // Implement Write so we can call Fprintf on a pp (through State), for @@ -258,6 +130,7 @@ func (p *pp) Write(b []byte) (ret int, err os.Error) { // These routines end in 'f' and take a format string. // Fprintf formats according to a format specifier and writes to w. +// It returns the number of bytes written and any write error encountered. func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error os.Error) { p := newPrinter() p.doPrintf(format, a) @@ -267,8 +140,9 @@ func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error os.Erro } // Printf formats according to a format specifier and writes to standard output. +// It returns the number of bytes written and any write error encountered. func Printf(format string, a ...interface{}) (n int, errno os.Error) { - n, errno = Fprintf(os.Stdout, format, a) + n, errno = Fprintf(os.Stdout, format, a...) return n, errno } @@ -281,10 +155,17 @@ func Sprintf(format string, a ...interface{}) string { return s } +// Errorf formats according to a format specifier and returns the string +// converted to an os.ErrorString, which satisfies the os.Error interface. +func Errorf(format string, a ...interface{}) os.Error { + return os.ErrorString(Sprintf(format, a...)) +} + // These routines do not take a format string // Fprint formats using the default formats for its operands and writes to w. // Spaces are added between operands when neither is a string. +// It returns the number of bytes written and any write error encountered. func Fprint(w io.Writer, a ...interface{}) (n int, error os.Error) { p := newPrinter() p.doPrint(a, false, false) @@ -295,8 +176,9 @@ func Fprint(w io.Writer, a ...interface{}) (n int, error os.Error) { // Print formats using the default formats for its operands and writes to standard output. // Spaces are added between operands when neither is a string. +// It returns the number of bytes written and any write error encountered. func Print(a ...interface{}) (n int, errno os.Error) { - n, errno = Fprint(os.Stdout, a) + n, errno = Fprint(os.Stdout, a...) return n, errno } @@ -316,6 +198,7 @@ func Sprint(a ...interface{}) string { // Fprintln formats using the default formats for its operands and writes to w. // Spaces are always added between operands and a newline is appended. +// It returns the number of bytes written and any write error encountered. func Fprintln(w io.Writer, a ...interface{}) (n int, error os.Error) { p := newPrinter() p.doPrint(a, true, true) @@ -326,8 +209,9 @@ func Fprintln(w io.Writer, a ...interface{}) (n int, error os.Error) { // Println formats using the default formats for its operands and writes to standard output. // Spaces are always added between operands and a newline is appended. +// It returns the number of bytes written and any write error encountered. func Println(a ...interface{}) (n int, errno os.Error) { - n, errno = Fprintln(os.Stdout, a) + n, errno = Fprintln(os.Stdout, a...) return n, errno } @@ -384,6 +268,7 @@ func (p *pp) unknownType(v interface{}) { func (p *pp) badVerb(verb int, val interface{}) { p.add('%') + p.add('!') p.add(verb) p.add('(') if val == nil { @@ -411,7 +296,7 @@ func (p *pp) fmtC(c int64) { if int64(rune) != c { rune = utf8.RuneError } - w := utf8.EncodeRune(rune, p.runeBuf[0:utf8.UTFMax]) + w := utf8.EncodeRune(p.runeBuf[0:utf8.UTFMax], rune) p.fmt.pad(p.runeBuf[0:w]) } @@ -427,6 +312,8 @@ func (p *pp) fmtInt64(v int64, verb int, value interface{}) { p.fmt.integer(v, 8, signed, ldigits) case 'x': p.fmt.integer(v, 16, signed, ldigits) + case 'U': + p.fmtUnicode(v) case 'X': p.fmt.integer(v, 16, signed, udigits) default: @@ -434,7 +321,7 @@ func (p *pp) fmtInt64(v int64, verb int, value interface{}) { } } -// fmt_sharpHex64 formats a uint64 in hexadecimal and prefixes it with 0x by +// fmt0x64 formats a uint64 in hexadecimal and prefixes it with 0x by // temporarily turning on the sharp flag. func (p *pp) fmt0x64(v uint64) { sharp := p.fmt.sharp @@ -443,6 +330,23 @@ func (p *pp) fmt0x64(v uint64) { p.fmt.sharp = sharp } +// fmtUnicode formats a uint64 in U+1234 form by +// temporarily turning on the unicode flag and tweaking the precision. +func (p *pp) fmtUnicode(v int64) { + precPresent := p.fmt.precPresent + prec := p.fmt.prec + if !precPresent { + // If prec is already set, leave it alone; otherwise 4 is minimum. + p.fmt.prec = 4 + p.fmt.precPresent = true + } + p.fmt.unicode = true // turn on U+ + p.fmt.integer(int64(v), 16, unsigned, udigits) + p.fmt.unicode = false + p.fmt.prec = prec + p.fmt.precPresent = precPresent +} + func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) { switch verb { case 'b': @@ -550,7 +454,7 @@ func (p *pp) fmtString(v string, verb int, goSyntax bool, value interface{}) { } func (p *pp) fmtBytes(v []byte, verb int, goSyntax bool, depth int, value interface{}) { - if verb == 'v' { + if verb == 'v' || verb == 'd' { if goSyntax { p.buf.Write(bytesBytes) } else { @@ -588,13 +492,14 @@ func (p *pp) fmtBytes(v []byte, verb int, goSyntax bool, depth int, value interf } } -func (p *pp) fmtUintptrGetter(field interface{}, value reflect.Value, verb int, sharp bool) bool { +func (p *pp) fmtPointer(field interface{}, value reflect.Value, verb int, goSyntax bool) { v, ok := value.(uintptrGetter) - if !ok { - return false + if !ok { // reflect.PtrValue is a uintptrGetter, so failure means it's not a pointer at all. + p.badVerb(verb, field) + return } u := v.Get() - if sharp { + if goSyntax { p.add('(') p.buf.WriteString(reflect.Typeof(field).String()) p.add(')') @@ -608,7 +513,6 @@ func (p *pp) fmtUintptrGetter(field interface{}, value reflect.Value, verb int, } else { p.fmt0x64(uint64(u)) } - return true } var ( @@ -618,19 +522,49 @@ var ( uintptrBits = reflect.Typeof(uintptr(0)).Bits() ) -func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth int) (was_string bool) { - if field != nil { - switch { - default: - if stringer, ok := field.(Stringer); ok { - p.printField(stringer.String(), verb, plus, goSyntax, depth) - return false // this value is not a string - } - case goSyntax: - if stringer, ok := field.(GoStringer); ok { - p.printField(stringer.GoString(), verb, plus, goSyntax, depth) - return false // this value is not a string - } +func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth int) (wasString bool) { + if field == nil { + if verb == 'T' || verb == 'v' { + p.buf.Write(nilAngleBytes) + } else { + p.badVerb(verb, field) + } + return false + } + + // Special processing considerations. + // %T (the value's type) and %p (its address) are special; we always do them first. + switch verb { + case 'T': + p.printField(reflect.Typeof(field).String(), 's', false, false, 0) + return false + case 'p': + p.fmtPointer(field, reflect.NewValue(field), verb, goSyntax) + return false + } + // Is it a Formatter? + if formatter, ok := field.(Formatter); ok { + formatter.Format(p, verb) + return false // this value is not a string + + } + // Must not touch flags before Formatter looks at them. + if plus { + p.fmt.plus = false + } + // If we're doing Go syntax and the field knows how to supply it, take care of it now. + if goSyntax { + p.fmt.sharp = false + if stringer, ok := field.(GoStringer); ok { + // Print the result of GoString unadorned. + p.fmtString(stringer.GoString(), 's', false, field) + return false // this value is not a string + } + } else { + // Is it a Stringer? + if stringer, ok := field.(Stringer); ok { + p.printField(stringer.String(), verb, plus, false, depth) + return false // this value is not a string } } @@ -706,21 +640,8 @@ func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth return verb == 's' } - if field == nil { - if verb == 'v' { - p.buf.Write(nilAngleBytes) - } else { - p.badVerb(verb, field) - } - return false - } - - value := reflect.NewValue(field) // Need to use reflection - // Special case for reflection values that know how to print with %p. - if verb == 'p' && p.fmtUintptrGetter(field, value, verb, goSyntax) { // TODO: is this goSyntax right? - return false - } + value := reflect.NewValue(field) BigSwitch: switch f := value.(type) { @@ -806,6 +727,22 @@ BigSwitch: return p.printField(value.Interface(), verb, plus, goSyntax, depth+1) } case reflect.ArrayOrSliceValue: + // Byte slices are special. + if f.Type().(reflect.ArrayOrSliceType).Elem().Kind() == reflect.Uint8 { + // We know it's a slice of bytes, but we also know it does not have static type + // []byte, or it would have been caught above. Therefore we cannot convert + // it directly in the (slightly) obvious way: f.Interface().([]byte); it doesn't have + // that type, and we can't write an expression of the right type and do a + // conversion because we don't have a static way to write the right type. + // So we build a slice by hand. This is a rare case but it would be nice + // if reflection could help a little more. + bytes := make([]byte, f.Len()) + for i := range bytes { + bytes[i] = byte(f.Elem(i).(*reflect.UintValue).Get()) + } + p.fmtBytes(bytes, verb, goSyntax, depth, field) + return verb == 's' + } if goSyntax { p.buf.WriteString(reflect.Typeof(field).String()) p.buf.WriteByte('{') @@ -862,30 +799,40 @@ BigSwitch: } p.fmt0x64(uint64(v)) case uintptrGetter: - if p.fmtUintptrGetter(field, value, verb, goSyntax) { - break - } - p.unknownType(f) + p.fmtPointer(field, value, verb, goSyntax) default: p.unknownType(f) } return false } +// intFromArg gets the fieldnumth element of a. On return, isInt reports whether the argument has type int. +func intFromArg(a []interface{}, end, i, fieldnum int) (num int, isInt bool, newi, newfieldnum int) { + newi, newfieldnum = end, fieldnum + if i < end && fieldnum < len(a) { + num, isInt = a[fieldnum].(int) + newi, newfieldnum = i+1, fieldnum+1 + } + return +} + func (p *pp) doPrintf(format string, a []interface{}) { - end := len(format) - 1 + end := len(format) fieldnum := 0 // we process one field per non-trivial format - for i := 0; i <= end; { - c, w := utf8.DecodeRuneInString(format[i:]) - if c != '%' || i == end { - if w == 1 { - p.buf.WriteByte(byte(c)) - } else { - p.buf.WriteString(format[i : i+w]) - } - i += w - continue + for i := 0; i < end; { + lasti := i + for i < end && format[i] != '%' { + i++ } + if i > lasti { + p.buf.WriteString(format[lasti:i]) + } + if i >= end { + // done processing format string + break + } + + // Process one verb i++ // flags and widths p.fmt.clearflags() @@ -906,17 +853,35 @@ func (p *pp) doPrintf(format string, a []interface{}) { break F } } - // do we have 20 (width)? - p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end) - // do we have .20 (precision)? + // do we have width? + if i < end && format[i] == '*' { + p.fmt.wid, p.fmt.widPresent, i, fieldnum = intFromArg(a, end, i, fieldnum) + if !p.fmt.widPresent { + p.buf.Write(widthBytes) + } + } else { + p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end) + } + // do we have precision? if i < end && format[i] == '.' { - p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i+1, end) + if format[i+1] == '*' { + p.fmt.prec, p.fmt.precPresent, i, fieldnum = intFromArg(a, end, i+1, fieldnum) + if !p.fmt.precPresent { + p.buf.Write(precBytes) + } + } else { + p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i+1, end) + } } - c, w = utf8.DecodeRuneInString(format[i:]) + if i >= end { + p.buf.Write(noVerbBytes) + continue + } + c, w := utf8.DecodeRuneInString(format[i:]) i += w // percent is special - absorbs no operand if c == '%' { - p.buf.WriteByte('%') // TODO: should we bother with width & prec? + p.buf.WriteByte('%') // We ignore width and prec. continue } if fieldnum >= len(a) { // out of operands @@ -928,33 +893,8 @@ func (p *pp) doPrintf(format string, a []interface{}) { field := a[fieldnum] fieldnum++ - // %T is special; we always do it here. - if c == 'T' { - // the value's type - if field == nil { - p.buf.Write(nilAngleBytes) - break - } - p.printField(reflect.Typeof(field).String(), 's', false, false, 0) - continue - } - - // Try Formatter (except for %T). - if field != nil { - if formatter, ok := field.(Formatter); ok { - formatter.Format(p, c) - continue - } - } - goSyntax := c == 'v' && p.fmt.sharp - if goSyntax { - p.fmt.sharp = false - } plus := c == 'v' && p.fmt.plus - if plus { - p.fmt.plus = false - } p.printField(field, c, plus, goSyntax, 0) } @@ -976,18 +916,18 @@ func (p *pp) doPrintf(format string, a []interface{}) { } func (p *pp) doPrint(a []interface{}, addspace, addnewline bool) { - prev_string := false + prevString := false for fieldnum := 0; fieldnum < len(a); fieldnum++ { p.fmt.clearflags() // always add spaces if we're doing println field := a[fieldnum] if fieldnum > 0 { - _, is_string := field.(*reflect.StringValue) - if addspace || !is_string && !prev_string { + isString := field != nil && reflect.Typeof(field).Kind() == reflect.String + if addspace || !isString && !prevString { p.buf.WriteByte(' ') } } - prev_string = p.printField(field, 'v', false, false, 0) + prevString = p.printField(field, 'v', false, false, 0) } if addnewline { p.buf.WriteByte('\n') diff --git a/src/pkg/fmt/scan.go b/src/pkg/fmt/scan.go index ded1f7719..dcc42bc92 100644 --- a/src/pkg/fmt/scan.go +++ b/src/pkg/fmt/scan.go @@ -22,6 +22,14 @@ type readRuner interface { ReadRune() (rune int, size int, err os.Error) } +// unreadRuner is the interface to something that can unread runes. +// If the object provided to Scan does not satisfy this interface, +// a local buffer will be used to back up the input, but its contents +// will be lost when Scan returns. +type unreadRuner interface { + UnreadRune() os.Error +} + // ScanState represents the scanner state passed to custom scanners. // Scanners may do rune-at-a-time scanning or ask the ScanState // to discover the next space-delimited token. @@ -29,7 +37,7 @@ type ScanState interface { // GetRune reads the next rune (Unicode code point) from the input. GetRune() (rune int, err os.Error) // UngetRune causes the next call to GetRune to return the rune. - UngetRune(rune int) + UngetRune() // Width returns the value of the width option and whether it has been set. // The unit is Unicode code points. Width() (wid int, ok bool) @@ -52,20 +60,20 @@ type Scanner interface { // as space. It returns the number of items successfully scanned. // If that is less than the number of arguments, err will report why. func Scan(a ...interface{}) (n int, err os.Error) { - return Fscan(os.Stdin, a) + return Fscan(os.Stdin, a...) } // Scanln is similar to Scan, but stops scanning at a newline and // after the final item there must be a newline or EOF. func Scanln(a ...interface{}) (n int, err os.Error) { - return Fscanln(os.Stdin, a) + return Fscanln(os.Stdin, a...) } // Scanf scans text read from standard input, storing successive // space-separated values into successive arguments as determined by // the format. It returns the number of items successfully scanned. func Scanf(format string, a ...interface{}) (n int, err os.Error) { - return Fscanf(os.Stdin, format, a) + return Fscanf(os.Stdin, format, a...) } // Sscan scans the argument string, storing successive space-separated @@ -73,20 +81,20 @@ func Scanf(format string, a ...interface{}) (n int, err os.Error) { // returns the number of items successfully scanned. If that is less // than the number of arguments, err will report why. func Sscan(str string, a ...interface{}) (n int, err os.Error) { - return Fscan(strings.NewReader(str), a) + return Fscan(strings.NewReader(str), a...) } // Sscanln is similar to Sscan, but stops scanning at a newline and // after the final item there must be a newline or EOF. func Sscanln(str string, a ...interface{}) (n int, err os.Error) { - return Fscanln(strings.NewReader(str), a) + return Fscanln(strings.NewReader(str), a...) } // Sscanf scans the argument string, storing successive space-separated // values into successive arguments as determined by the format. It // returns the number of items successfully parsed. func Sscanf(str string, format string, a ...interface{}) (n int, err os.Error) { - return Fscanf(strings.NewReader(str), format, a) + return Fscanf(strings.NewReader(str), format, a...) } // Fscan scans text read from r, storing successive space-separated @@ -133,6 +141,7 @@ type ss struct { buf bytes.Buffer // token accumulator nlIsSpace bool // whether newline counts as white space peekRune int // one-rune lookahead + prevRune int // last rune returned by GetRune atEOF bool // already read EOF maxWid int // max width of field, in runes widPresent bool // width was specified @@ -142,10 +151,14 @@ type ss struct { func (s *ss) GetRune() (rune int, err os.Error) { if s.peekRune >= 0 { rune = s.peekRune + s.prevRune = rune s.peekRune = -1 return } rune, _, err = s.rr.ReadRune() + if err == nil { + s.prevRune = rune + } return } @@ -161,11 +174,14 @@ func (s *ss) getRune() (rune int) { } if s.peekRune >= 0 { rune = s.peekRune + s.prevRune = rune s.peekRune = -1 return } rune, _, err := s.rr.ReadRune() - if err != nil { + if err == nil { + s.prevRune = rune + } else if err != nil { if err == os.EOF { s.atEOF = true return EOF @@ -198,8 +214,12 @@ func (s *ss) mustGetRune() (rune int) { } -func (s *ss) UngetRune(rune int) { - s.peekRune = rune +func (s *ss) UngetRune() { + if u, ok := s.rr.(unreadRuner); ok { + u.UnreadRune() + } else { + s.peekRune = s.prevRune + } } func (s *ss) error(err os.Error) { @@ -316,14 +336,17 @@ func (s *ss) free() { _ = ssFree <- s } -// skipSpace skips spaces and maybe newlines -func (s *ss) skipSpace() { +// skipSpace skips spaces and maybe newlines. +func (s *ss) skipSpace(stopAtNewline bool) { for { rune := s.getRune() if rune == EOF { return } if rune == '\n' { + if stopAtNewline { + break + } if s.nlIsSpace { continue } @@ -331,7 +354,7 @@ func (s *ss) skipSpace() { return } if !unicode.IsSpace(rune) { - s.UngetRune(rune) + s.UngetRune() break } } @@ -341,7 +364,7 @@ func (s *ss) skipSpace() { // skips white space. For Scanln, it stops at newlines. For Scan, // newlines are treated as spaces. func (s *ss) token() string { - s.skipSpace() + s.skipSpace(false) // read until white space or newline for nrunes := 0; !s.widPresent || nrunes < s.maxWid; nrunes++ { rune := s.getRune() @@ -349,7 +372,7 @@ func (s *ss) token() string { break } if unicode.IsSpace(rune) { - s.UngetRune(rune) + s.UngetRune() break } s.buf.WriteRune(rune) @@ -365,9 +388,9 @@ func (s *ss) typeError(field interface{}, expected string) { var complexError = os.ErrorString("syntax error scanning complex number") var boolError = os.ErrorString("syntax error scanning boolean") -// accepts checks the next rune in the input. If it's a byte (sic) in the string, it puts it in the -// buffer and returns true. Otherwise it return false. -func (s *ss) accept(ok string) bool { +// consume reads the next rune in the input and reports whether it is in the ok string. +// If accept is true, it puts the character into the input token. +func (s *ss) consume(ok string, accept bool) bool { if s.wid >= s.maxWid { return false } @@ -377,17 +400,25 @@ func (s *ss) accept(ok string) bool { } for i := 0; i < len(ok); i++ { if int(ok[i]) == rune { - s.buf.WriteRune(rune) - s.wid++ + if accept { + s.buf.WriteRune(rune) + s.wid++ + } return true } } - if rune != EOF { - s.UngetRune(rune) + if rune != EOF && accept { + s.UngetRune() } return false } +// accept checks the next rune in the input. If it's a byte (sic) in the string, it puts it in the +// buffer and returns true. Otherwise it return false. +func (s *ss) accept(ok string) bool { + return s.consume(ok, true) +} + // okVerb verifies that the verb is present in the list, setting s.err appropriately if not. func (s *ss) okVerb(verb int, okVerbs, typ string) bool { for _, v := range okVerbs { @@ -437,7 +468,7 @@ const ( // getBase returns the numeric base represented by the verb and its digit string. func (s *ss) getBase(verb int) (base int, digits string) { - s.okVerb(verb, "bdoxXv", "integer") // sets s.err + s.okVerb(verb, "bdoUxXv", "integer") // sets s.err base = 10 digits = decimalDigits switch verb { @@ -447,7 +478,7 @@ func (s *ss) getBase(verb int) (base int, digits string) { case 'o': base = 8 digits = octalDigits - case 'x', 'X': + case 'x', 'X', 'U': base = 16 digits = hexadecimalDigits } @@ -482,8 +513,14 @@ func (s *ss) scanInt(verb int, bitSize int) int64 { return s.scanRune(bitSize) } base, digits := s.getBase(verb) - s.skipSpace() - s.accept(sign) // If there's a sign, it will be left in the token buffer. + s.skipSpace(false) + if verb == 'U' { + if !s.consume("U", false) || !s.consume("+", false) { + s.errorString("bad unicode format ") + } + } else { + s.accept(sign) // If there's a sign, it will be left in the token buffer. + } tok := s.scanNumber(digits) i, err := strconv.Btoi64(tok, base) if err != nil { @@ -504,7 +541,12 @@ func (s *ss) scanUint(verb int, bitSize int) uint64 { return uint64(s.scanRune(bitSize)) } base, digits := s.getBase(verb) - s.skipSpace() + s.skipSpace(false) + if verb == 'U' { + if !s.consume("U", false) || !s.consume("+", false) { + s.errorString("bad unicode format ") + } + } tok := s.scanNumber(digits) i, err := strconv.Btoui64(tok, base) if err != nil { @@ -523,8 +565,16 @@ func (s *ss) scanUint(verb int, bitSize int) uint64 { // we have at least some digits, but Atof will do that. func (s *ss) floatToken() string { s.buf.Reset() + // NaN? + if s.accept("nN") && s.accept("aA") && s.accept("nN") { + return s.buf.String() + } // leading sign? s.accept(sign) + // Inf? + if s.accept("iI") && s.accept("nN") && s.accept("fF") { + return s.buf.String() + } // digits? for s.accept(decimalDigits) { } @@ -586,7 +636,7 @@ func (s *ss) scanComplex(verb int, n int) complex128 { if !s.okVerb(verb, floatVerbs, "complex") { return 0 } - s.skipSpace() + s.skipSpace(false) sreal, simag := s.complexTokens() real := s.convertFloat(sreal, n/2) imag := s.convertFloat(simag, n/2) @@ -595,18 +645,24 @@ func (s *ss) scanComplex(verb int, n int) complex128 { // convertString returns the string represented by the next input characters. // The format of the input is determined by the verb. -func (s *ss) convertString(verb int) string { +func (s *ss) convertString(verb int) (str string) { if !s.okVerb(verb, "svqx", "string") { return "" } - s.skipSpace() + s.skipSpace(false) switch verb { case 'q': - return s.quotedString() + str = s.quotedString() case 'x': - return s.hexString() + str = s.hexString() + default: + str = s.token() // %s and %v just return the next word + } + // Empty strings other than with %q are not OK. + if len(str) == 0 && verb != 'q' && s.maxWid > 0 { + s.errorString("Scan: no data for string") } - return s.token() // %s and %v just return the next word + return } // quotedString returns the double- or back-quoted string represented by the next input characters. @@ -672,7 +728,7 @@ func (s *ss) hexByte() (b byte, ok bool) { return } if unicode.IsSpace(rune1) { - s.UngetRune(rune1) + s.UngetRune() return } rune2 := s.mustGetRune() @@ -731,7 +787,7 @@ func (s *ss) scanOne(verb int, field interface{}) { case *int32: *v = int32(s.scanInt(verb, 32)) case *int64: - *v = s.scanInt(verb, intBits) + *v = s.scanInt(verb, 64) case *uint: *v = uint(s.scanUint(verb, intBits)) case *uint8: @@ -748,17 +804,17 @@ func (s *ss) scanOne(verb int, field interface{}) { // scan in high precision and convert, in order to preserve the correct error condition. case *float: if s.okVerb(verb, floatVerbs, "float") { - s.skipSpace() + s.skipSpace(false) *v = float(s.convertFloat(s.floatToken(), int(floatBits))) } case *float32: if s.okVerb(verb, floatVerbs, "float32") { - s.skipSpace() + s.skipSpace(false) *v = float32(s.convertFloat(s.floatToken(), 32)) } case *float64: if s.okVerb(verb, floatVerbs, "float64") { - s.skipSpace() + s.skipSpace(false) *v = s.convertFloat(s.floatToken(), 64) } case *string: @@ -795,7 +851,7 @@ func (s *ss) scanOne(verb int, field interface{}) { v.Elem(i).(*reflect.UintValue).Set(uint64(str[i])) } case *reflect.FloatValue: - s.skipSpace() + s.skipSpace(false) v.Set(s.convertFloat(s.floatToken(), v.Type().Bits())) case *reflect.ComplexValue: v.Set(s.scanComplex(verb, v.Type().Bits())) @@ -878,12 +934,12 @@ func (s *ss) advance(format string) (i int) { // Space in format but not in input: error s.errorString("expected space in input to match format") } - s.skipSpace() + s.skipSpace(true) continue } inputc := s.mustGetRune() if fmtc != inputc { - s.UngetRune(inputc) + s.UngetRune() return -1 } i += w diff --git a/src/pkg/fmt/scan_test.go b/src/pkg/fmt/scan_test.go index 1e0319836..fe5ee1d61 100644 --- a/src/pkg/fmt/scan_test.go +++ b/src/pkg/fmt/scan_test.go @@ -5,10 +5,13 @@ package fmt_test import ( + "bufio" . "fmt" "io" + "math" "os" "reflect" + "regexp" "strings" "testing" "utf8" @@ -78,6 +81,12 @@ var ( renamedComplex128Val renamedComplex128 ) +type FloatTest struct { + text string + in float64 + out float64 +} + // Xs accepts any non-empty run of the verb character type Xs string @@ -100,7 +109,7 @@ func (x *Xs) Scan(state ScanState, verb int) os.Error { if err != nil { return err } - if !testing.MustCompile("^" + string(verb) + "+$").MatchString(tok) { + if !regexp.MustCompile("^" + string(verb) + "+$").MatchString(tok) { return os.ErrorString("syntax error for xs") } *x = Xs(tok) @@ -125,154 +134,163 @@ func newReader(s string) *myStringReader { var scanTests = []ScanTest{ // Numbers - ScanTest{"T\n", &boolVal, true}, // boolean test vals toggle to be sure they are written - ScanTest{"F\n", &boolVal, false}, // restored to zero value - ScanTest{"21\n", &intVal, 21}, - ScanTest{"22\n", &int8Val, int8(22)}, - ScanTest{"23\n", &int16Val, int16(23)}, - ScanTest{"24\n", &int32Val, int32(24)}, - ScanTest{"25\n", &int64Val, int64(25)}, - ScanTest{"127\n", &int8Val, int8(127)}, - ScanTest{"-21\n", &intVal, -21}, - ScanTest{"-22\n", &int8Val, int8(-22)}, - ScanTest{"-23\n", &int16Val, int16(-23)}, - ScanTest{"-24\n", &int32Val, int32(-24)}, - ScanTest{"-25\n", &int64Val, int64(-25)}, - ScanTest{"-128\n", &int8Val, int8(-128)}, - ScanTest{"+21\n", &intVal, +21}, - ScanTest{"+22\n", &int8Val, int8(+22)}, - ScanTest{"+23\n", &int16Val, int16(+23)}, - ScanTest{"+24\n", &int32Val, int32(+24)}, - ScanTest{"+25\n", &int64Val, int64(+25)}, - ScanTest{"+127\n", &int8Val, int8(+127)}, - ScanTest{"26\n", &uintVal, uint(26)}, - ScanTest{"27\n", &uint8Val, uint8(27)}, - ScanTest{"28\n", &uint16Val, uint16(28)}, - ScanTest{"29\n", &uint32Val, uint32(29)}, - ScanTest{"30\n", &uint64Val, uint64(30)}, - ScanTest{"255\n", &uint8Val, uint8(255)}, - ScanTest{"32767\n", &int16Val, int16(32767)}, - ScanTest{"2.3\n", &floatVal, 2.3}, - ScanTest{"2.3e1\n", &float32Val, float32(2.3e1)}, - ScanTest{"2.3e2\n", &float64Val, float64(2.3e2)}, - ScanTest{"2.35\n", &stringVal, "2.35"}, - ScanTest{"2345678\n", &bytesVal, []byte("2345678")}, - ScanTest{"(3.4e1-2i)\n", &complexVal, 3.4e1 - 2i}, - ScanTest{"-3.45e1-3i\n", &complex64Val, complex64(-3.45e1 - 3i)}, - ScanTest{"-.45e1-1e2i\n", &complex128Val, complex128(-.45e1 - 100i)}, - ScanTest{"hello\n", &stringVal, "hello"}, + {"T\n", &boolVal, true}, // boolean test vals toggle to be sure they are written + {"F\n", &boolVal, false}, // restored to zero value + {"21\n", &intVal, 21}, + {"22\n", &int8Val, int8(22)}, + {"23\n", &int16Val, int16(23)}, + {"24\n", &int32Val, int32(24)}, + {"25\n", &int64Val, int64(25)}, + {"127\n", &int8Val, int8(127)}, + {"-21\n", &intVal, -21}, + {"-22\n", &int8Val, int8(-22)}, + {"-23\n", &int16Val, int16(-23)}, + {"-24\n", &int32Val, int32(-24)}, + {"-25\n", &int64Val, int64(-25)}, + {"-128\n", &int8Val, int8(-128)}, + {"+21\n", &intVal, +21}, + {"+22\n", &int8Val, int8(+22)}, + {"+23\n", &int16Val, int16(+23)}, + {"+24\n", &int32Val, int32(+24)}, + {"+25\n", &int64Val, int64(+25)}, + {"+127\n", &int8Val, int8(+127)}, + {"26\n", &uintVal, uint(26)}, + {"27\n", &uint8Val, uint8(27)}, + {"28\n", &uint16Val, uint16(28)}, + {"29\n", &uint32Val, uint32(29)}, + {"30\n", &uint64Val, uint64(30)}, + {"255\n", &uint8Val, uint8(255)}, + {"32767\n", &int16Val, int16(32767)}, + {"2.3\n", &floatVal, 2.3}, + {"2.3e1\n", &float32Val, float32(2.3e1)}, + {"2.3e2\n", &float64Val, float64(2.3e2)}, + {"2.35\n", &stringVal, "2.35"}, + {"2345678\n", &bytesVal, []byte("2345678")}, + {"(3.4e1-2i)\n", &complexVal, 3.4e1 - 2i}, + {"-3.45e1-3i\n", &complex64Val, complex64(-3.45e1 - 3i)}, + {"-.45e1-1e2i\n", &complex128Val, complex128(-.45e1 - 100i)}, + {"hello\n", &stringVal, "hello"}, // Renamed types - ScanTest{"true\n", &renamedBoolVal, renamedBool(true)}, - ScanTest{"F\n", &renamedBoolVal, renamedBool(false)}, - ScanTest{"101\n", &renamedIntVal, renamedInt(101)}, - ScanTest{"102\n", &renamedIntVal, renamedInt(102)}, - ScanTest{"103\n", &renamedUintVal, renamedUint(103)}, - ScanTest{"104\n", &renamedUintVal, renamedUint(104)}, - ScanTest{"105\n", &renamedInt8Val, renamedInt8(105)}, - ScanTest{"106\n", &renamedInt16Val, renamedInt16(106)}, - ScanTest{"107\n", &renamedInt32Val, renamedInt32(107)}, - ScanTest{"108\n", &renamedInt64Val, renamedInt64(108)}, - ScanTest{"109\n", &renamedUint8Val, renamedUint8(109)}, - ScanTest{"110\n", &renamedUint16Val, renamedUint16(110)}, - ScanTest{"111\n", &renamedUint32Val, renamedUint32(111)}, - ScanTest{"112\n", &renamedUint64Val, renamedUint64(112)}, - ScanTest{"113\n", &renamedUintptrVal, renamedUintptr(113)}, - ScanTest{"114\n", &renamedStringVal, renamedString("114")}, - ScanTest{"115\n", &renamedBytesVal, renamedBytes([]byte("115"))}, + {"true\n", &renamedBoolVal, renamedBool(true)}, + {"F\n", &renamedBoolVal, renamedBool(false)}, + {"101\n", &renamedIntVal, renamedInt(101)}, + {"102\n", &renamedIntVal, renamedInt(102)}, + {"103\n", &renamedUintVal, renamedUint(103)}, + {"104\n", &renamedUintVal, renamedUint(104)}, + {"105\n", &renamedInt8Val, renamedInt8(105)}, + {"106\n", &renamedInt16Val, renamedInt16(106)}, + {"107\n", &renamedInt32Val, renamedInt32(107)}, + {"108\n", &renamedInt64Val, renamedInt64(108)}, + {"109\n", &renamedUint8Val, renamedUint8(109)}, + {"110\n", &renamedUint16Val, renamedUint16(110)}, + {"111\n", &renamedUint32Val, renamedUint32(111)}, + {"112\n", &renamedUint64Val, renamedUint64(112)}, + {"113\n", &renamedUintptrVal, renamedUintptr(113)}, + {"114\n", &renamedStringVal, renamedString("114")}, + {"115\n", &renamedBytesVal, renamedBytes([]byte("115"))}, // Custom scanner. - ScanTest{" vvv ", &xVal, Xs("vvv")}, + {" vvv ", &xVal, Xs("vvv")}, + + // Fixed bugs + {"2147483648\n", &int64Val, int64(2147483648)}, // was: integer overflow } var scanfTests = []ScanfTest{ - ScanfTest{"%v", "TRUE\n", &boolVal, true}, - ScanfTest{"%t", "false\n", &boolVal, false}, - ScanfTest{"%v", "-71\n", &intVal, -71}, - ScanfTest{"%d", "72\n", &intVal, 72}, - ScanfTest{"%c", "a\n", &intVal, 'a'}, - ScanfTest{"%c", "\u5072\n", &intVal, 0x5072}, - ScanfTest{"%c", "\u1234\n", &intVal, '\u1234'}, - ScanfTest{"%d", "73\n", &int8Val, int8(73)}, - ScanfTest{"%d", "+74\n", &int16Val, int16(74)}, - ScanfTest{"%d", "75\n", &int32Val, int32(75)}, - ScanfTest{"%d", "76\n", &int64Val, int64(76)}, - ScanfTest{"%b", "1001001\n", &intVal, 73}, - ScanfTest{"%o", "075\n", &intVal, 075}, - ScanfTest{"%x", "a75\n", &intVal, 0xa75}, - ScanfTest{"%v", "71\n", &uintVal, uint(71)}, - ScanfTest{"%d", "72\n", &uintVal, uint(72)}, - ScanfTest{"%d", "73\n", &uint8Val, uint8(73)}, - ScanfTest{"%d", "74\n", &uint16Val, uint16(74)}, - ScanfTest{"%d", "75\n", &uint32Val, uint32(75)}, - ScanfTest{"%d", "76\n", &uint64Val, uint64(76)}, - ScanfTest{"%b", "1001001\n", &uintVal, uint(73)}, - ScanfTest{"%o", "075\n", &uintVal, uint(075)}, - ScanfTest{"%x", "a75\n", &uintVal, uint(0xa75)}, - ScanfTest{"%x", "A75\n", &uintVal, uint(0xa75)}, + {"%v", "TRUE\n", &boolVal, true}, + {"%t", "false\n", &boolVal, false}, + {"%v", "-71\n", &intVal, -71}, + {"%d", "72\n", &intVal, 72}, + {"%c", "a\n", &intVal, 'a'}, + {"%c", "\u5072\n", &intVal, 0x5072}, + {"%c", "\u1234\n", &intVal, '\u1234'}, + {"%d", "73\n", &int8Val, int8(73)}, + {"%d", "+74\n", &int16Val, int16(74)}, + {"%d", "75\n", &int32Val, int32(75)}, + {"%d", "76\n", &int64Val, int64(76)}, + {"%b", "1001001\n", &intVal, 73}, + {"%o", "075\n", &intVal, 075}, + {"%x", "a75\n", &intVal, 0xa75}, + {"%v", "71\n", &uintVal, uint(71)}, + {"%d", "72\n", &uintVal, uint(72)}, + {"%d", "73\n", &uint8Val, uint8(73)}, + {"%d", "74\n", &uint16Val, uint16(74)}, + {"%d", "75\n", &uint32Val, uint32(75)}, + {"%d", "76\n", &uint64Val, uint64(76)}, + {"%b", "1001001\n", &uintVal, uint(73)}, + {"%o", "075\n", &uintVal, uint(075)}, + {"%x", "a75\n", &uintVal, uint(0xa75)}, + {"%x", "A75\n", &uintVal, uint(0xa75)}, + {"%U", "U+1234\n", &intVal, int(0x1234)}, + {"%U", "U+4567\n", &uintVal, uint(0x4567)}, // Strings - ScanfTest{"%s", "using-%s\n", &stringVal, "using-%s"}, - ScanfTest{"%x", "7573696e672d2578\n", &stringVal, "using-%x"}, - ScanfTest{"%q", `"quoted\twith\\do\u0075bl\x65s"` + "\n", &stringVal, "quoted\twith\\doubles"}, - ScanfTest{"%q", "`quoted with backs`\n", &stringVal, "quoted with backs"}, + {"%s", "using-%s\n", &stringVal, "using-%s"}, + {"%x", "7573696e672d2578\n", &stringVal, "using-%x"}, + {"%q", `"quoted\twith\\do\u0075bl\x65s"` + "\n", &stringVal, "quoted\twith\\doubles"}, + {"%q", "`quoted with backs`\n", &stringVal, "quoted with backs"}, // Byte slices - ScanfTest{"%s", "bytes-%s\n", &bytesVal, []byte("bytes-%s")}, - ScanfTest{"%x", "62797465732d2578\n", &bytesVal, []byte("bytes-%x")}, - ScanfTest{"%q", `"bytes\rwith\vdo\u0075bl\x65s"` + "\n", &bytesVal, []byte("bytes\rwith\vdoubles")}, - ScanfTest{"%q", "`bytes with backs`\n", &bytesVal, []byte("bytes with backs")}, + {"%s", "bytes-%s\n", &bytesVal, []byte("bytes-%s")}, + {"%x", "62797465732d2578\n", &bytesVal, []byte("bytes-%x")}, + {"%q", `"bytes\rwith\vdo\u0075bl\x65s"` + "\n", &bytesVal, []byte("bytes\rwith\vdoubles")}, + {"%q", "`bytes with backs`\n", &bytesVal, []byte("bytes with backs")}, // Renamed types - ScanfTest{"%v\n", "true\n", &renamedBoolVal, renamedBool(true)}, - ScanfTest{"%t\n", "F\n", &renamedBoolVal, renamedBool(false)}, - ScanfTest{"%v", "101\n", &renamedIntVal, renamedInt(101)}, - ScanfTest{"%c", "\u0101\n", &renamedIntVal, renamedInt('\u0101')}, - ScanfTest{"%o", "0146\n", &renamedIntVal, renamedInt(102)}, - ScanfTest{"%v", "103\n", &renamedUintVal, renamedUint(103)}, - ScanfTest{"%d", "104\n", &renamedUintVal, renamedUint(104)}, - ScanfTest{"%d", "105\n", &renamedInt8Val, renamedInt8(105)}, - ScanfTest{"%d", "106\n", &renamedInt16Val, renamedInt16(106)}, - ScanfTest{"%d", "107\n", &renamedInt32Val, renamedInt32(107)}, - ScanfTest{"%d", "108\n", &renamedInt64Val, renamedInt64(108)}, - ScanfTest{"%x", "6D\n", &renamedUint8Val, renamedUint8(109)}, - ScanfTest{"%o", "0156\n", &renamedUint16Val, renamedUint16(110)}, - ScanfTest{"%d", "111\n", &renamedUint32Val, renamedUint32(111)}, - ScanfTest{"%d", "112\n", &renamedUint64Val, renamedUint64(112)}, - ScanfTest{"%d", "113\n", &renamedUintptrVal, renamedUintptr(113)}, - ScanfTest{"%s", "114\n", &renamedStringVal, renamedString("114")}, - ScanfTest{"%q", "\"1155\"\n", &renamedBytesVal, renamedBytes([]byte("1155"))}, - ScanfTest{"%g", "115.1\n", &renamedFloatVal, renamedFloat(115.1)}, - ScanfTest{"%g", "116e1\n", &renamedFloat32Val, renamedFloat32(116e1)}, - ScanfTest{"%g", "-11.7e+1", &renamedFloat64Val, renamedFloat64(-11.7e+1)}, - ScanfTest{"%g", "11+5.1i\n", &renamedComplexVal, renamedComplex(11 + 5.1i)}, - ScanfTest{"%g", "11+6e1i\n", &renamedComplex64Val, renamedComplex64(11 + 6e1i)}, - ScanfTest{"%g", "-11.+7e+1i", &renamedComplex128Val, renamedComplex128(-11. + 7e+1i)}, + {"%v\n", "true\n", &renamedBoolVal, renamedBool(true)}, + {"%t\n", "F\n", &renamedBoolVal, renamedBool(false)}, + {"%v", "101\n", &renamedIntVal, renamedInt(101)}, + {"%c", "\u0101\n", &renamedIntVal, renamedInt('\u0101')}, + {"%o", "0146\n", &renamedIntVal, renamedInt(102)}, + {"%v", "103\n", &renamedUintVal, renamedUint(103)}, + {"%d", "104\n", &renamedUintVal, renamedUint(104)}, + {"%d", "105\n", &renamedInt8Val, renamedInt8(105)}, + {"%d", "106\n", &renamedInt16Val, renamedInt16(106)}, + {"%d", "107\n", &renamedInt32Val, renamedInt32(107)}, + {"%d", "108\n", &renamedInt64Val, renamedInt64(108)}, + {"%x", "6D\n", &renamedUint8Val, renamedUint8(109)}, + {"%o", "0156\n", &renamedUint16Val, renamedUint16(110)}, + {"%d", "111\n", &renamedUint32Val, renamedUint32(111)}, + {"%d", "112\n", &renamedUint64Val, renamedUint64(112)}, + {"%d", "113\n", &renamedUintptrVal, renamedUintptr(113)}, + {"%s", "114\n", &renamedStringVal, renamedString("114")}, + {"%q", "\"1155\"\n", &renamedBytesVal, renamedBytes([]byte("1155"))}, + {"%g", "115.1\n", &renamedFloatVal, renamedFloat(115.1)}, + {"%g", "116e1\n", &renamedFloat32Val, renamedFloat32(116e1)}, + {"%g", "-11.7e+1", &renamedFloat64Val, renamedFloat64(-11.7e+1)}, + {"%g", "11+5.1i\n", &renamedComplexVal, renamedComplex(11 + 5.1i)}, + {"%g", "11+6e1i\n", &renamedComplex64Val, renamedComplex64(11 + 6e1i)}, + {"%g", "-11.+7e+1i", &renamedComplex128Val, renamedComplex128(-11. + 7e+1i)}, // Interesting formats - ScanfTest{"here is\tthe value:%d", "here is the\tvalue:118\n", &intVal, 118}, - ScanfTest{"%% %%:%d", "% %:119\n", &intVal, 119}, + {"here is\tthe value:%d", "here is the\tvalue:118\n", &intVal, 118}, + {"%% %%:%d", "% %:119\n", &intVal, 119}, // Corner cases - ScanfTest{"%x", "FFFFFFFF\n", &uint32Val, uint32(0xFFFFFFFF)}, + {"%x", "FFFFFFFF\n", &uint32Val, uint32(0xFFFFFFFF)}, // Custom scanner. - ScanfTest{"%s", " sss ", &xVal, Xs("sss")}, - ScanfTest{"%2s", "sssss", &xVal, Xs("ss")}, + {"%s", " sss ", &xVal, Xs("sss")}, + {"%2s", "sssss", &xVal, Xs("ss")}, + + // Fixed bugs + {"%d\n", "27\n", &intVal, 27}, // ok + {"%d\n", "28 \n", &intVal, 28}, // was: "unexpected newline" } var overflowTests = []ScanTest{ - ScanTest{"128", &int8Val, 0}, - ScanTest{"32768", &int16Val, 0}, - ScanTest{"-129", &int8Val, 0}, - ScanTest{"-32769", &int16Val, 0}, - ScanTest{"256", &uint8Val, 0}, - ScanTest{"65536", &uint16Val, 0}, - ScanTest{"1e100", &float32Val, 0}, - ScanTest{"1e500", &float64Val, 0}, - ScanTest{"(1e100+0i)", &complexVal, 0}, - ScanTest{"(1+1e100i)", &complex64Val, 0}, - ScanTest{"(1-1e500i)", &complex128Val, 0}, + {"128", &int8Val, 0}, + {"32768", &int16Val, 0}, + {"-129", &int8Val, 0}, + {"-32769", &int16Val, 0}, + {"256", &uint8Val, 0}, + {"65536", &uint16Val, 0}, + {"1e100", &float32Val, 0}, + {"1e500", &float64Val, 0}, + {"(1e100+0i)", &complexVal, 0}, + {"(1+1e100i)", &complex64Val, 0}, + {"(1-1e500i)", &complex128Val, 0}, } var i, j, k int @@ -281,32 +299,30 @@ var s, t string var c complex var x, y Xs -func args(a ...interface{}) []interface{} { return a } - var multiTests = []ScanfMultiTest{ - ScanfMultiTest{"", "", nil, nil, ""}, - ScanfMultiTest{"%d", "23", args(&i), args(23), ""}, - ScanfMultiTest{"%2s%3s", "22333", args(&s, &t), args("22", "333"), ""}, - ScanfMultiTest{"%2d%3d", "44555", args(&i, &j), args(44, 555), ""}, - ScanfMultiTest{"%2d.%3d", "66.777", args(&i, &j), args(66, 777), ""}, - ScanfMultiTest{"%d, %d", "23, 18", args(&i, &j), args(23, 18), ""}, - ScanfMultiTest{"%3d22%3d", "33322333", args(&i, &j), args(333, 333), ""}, - ScanfMultiTest{"%6vX=%3fY", "3+2iX=2.5Y", args(&c, &f), args((3 + 2i), float(2.5)), ""}, - ScanfMultiTest{"%d%s", "123abc", args(&i, &s), args(123, "abc"), ""}, - ScanfMultiTest{"%c%c%c", "2\u50c2X", args(&i, &j, &k), args('2', '\u50c2', 'X'), ""}, + {"", "", nil, nil, ""}, + {"%d", "23", args(&i), args(23), ""}, + {"%2s%3s", "22333", args(&s, &t), args("22", "333"), ""}, + {"%2d%3d", "44555", args(&i, &j), args(44, 555), ""}, + {"%2d.%3d", "66.777", args(&i, &j), args(66, 777), ""}, + {"%d, %d", "23, 18", args(&i, &j), args(23, 18), ""}, + {"%3d22%3d", "33322333", args(&i, &j), args(333, 333), ""}, + {"%6vX=%3fY", "3+2iX=2.5Y", args(&c, &f), args((3 + 2i), float(2.5)), ""}, + {"%d%s", "123abc", args(&i, &s), args(123, "abc"), ""}, + {"%c%c%c", "2\u50c2X", args(&i, &j, &k), args('2', '\u50c2', 'X'), ""}, // Custom scanner. - ScanfMultiTest{"%2e%f", "eefffff", args(&x, &y), args(Xs("ee"), Xs("fffff")), ""}, + {"%2e%f", "eefffff", args(&x, &y), args(Xs("ee"), Xs("fffff")), ""}, // Errors - ScanfMultiTest{"%t", "23 18", args(&i), nil, "bad verb"}, - ScanfMultiTest{"%d %d %d", "23 18", args(&i, &j), args(23, 18), "too few operands"}, - ScanfMultiTest{"%d %d", "23 18 27", args(&i, &j, &k), args(23, 18), "too many operands"}, - ScanfMultiTest{"%c", "\u0100", args(&int8Val), nil, "overflow"}, - ScanfMultiTest{"X%d", "10X", args(&intVal), nil, "input does not match format"}, + {"%t", "23 18", args(&i), nil, "bad verb"}, + {"%d %d %d", "23 18", args(&i, &j), args(23, 18), "too few operands"}, + {"%d %d", "23 18 27", args(&i, &j, &k), args(23, 18), "too many operands"}, + {"%c", "\u0100", args(&int8Val), nil, "overflow"}, + {"X%d", "10X", args(&intVal), nil, "input does not match format"}, // Bad UTF-8: should see every byte. - ScanfMultiTest{"%c%c%c", "\xc2X\xc2", args(&i, &j, &k), args(utf8.RuneError, 'X', utf8.RuneError), ""}, + {"%c%c%c", "\xc2X\xc2", args(&i, &j, &k), args(utf8.RuneError, 'X', utf8.RuneError), ""}, } func testScan(name string, t *testing.T, scan func(r io.Reader, a ...interface{}) (int, os.Error)) { @@ -379,7 +395,7 @@ func TestScanf(t *testing.T) { func TestScanOverflow(t *testing.T) { // different machines and different types report errors with different strings. - re := testing.MustCompile("overflow|too large|out of range|not representable") + re := regexp.MustCompile("overflow|too large|out of range|not representable") for _, test := range overflowTests { _, err := Sscan(test.text, test.in) if err == nil { @@ -392,6 +408,57 @@ func TestScanOverflow(t *testing.T) { } } +func verifyNaN(str string, t *testing.T) { + var f float + var f32 float32 + var f64 float64 + text := str + " " + str + " " + str + n, err := Fscan(strings.NewReader(text), &f, &f32, &f64) + if err != nil { + t.Errorf("got error scanning %q: %s", text, err) + } + if n != 3 { + t.Errorf("count error scanning %q: got %d", text, n) + } + if !math.IsNaN(float64(f)) || !math.IsNaN(float64(f32)) || !math.IsNaN(f64) { + t.Errorf("didn't get NaNs scanning %q: got %g %g %g", text, f, f32, f64) + } +} + +func TestNaN(t *testing.T) { + for _, s := range []string{"nan", "NAN", "NaN"} { + verifyNaN(s, t) + } +} + +func verifyInf(str string, t *testing.T) { + var f float + var f32 float32 + var f64 float64 + text := str + " " + str + " " + str + n, err := Fscan(strings.NewReader(text), &f, &f32, &f64) + if err != nil { + t.Errorf("got error scanning %q: %s", text, err) + } + if n != 3 { + t.Errorf("count error scanning %q: got %d", text, n) + } + sign := 1 + if str[0] == '-' { + sign = -1 + } + if !math.IsInf(float64(f), sign) || !math.IsInf(float64(f32), sign) || !math.IsInf(f64, sign) { + t.Errorf("didn't get right Infs scanning %q: got %g %g %g", text, f, f32, f64) + } +} + + +func TestInf(t *testing.T) { + for _, s := range []string{"inf", "+inf", "-inf", "INF", "-INF", "+INF", "Inf", "-Inf", "+Inf"} { + verifyInf(s, t) + } +} + // TODO: there's no conversion from []T to ...T, but we can fake it. These // functions do the faking. We index the table by the length of the param list. var fscanf = []func(io.Reader, string, []interface{}) (int, os.Error){ @@ -460,6 +527,46 @@ func TestScanMultiple(t *testing.T) { if a != 123 || s != "abc" { t.Errorf("Sscan wrong values: got (%d %q) expected (123 \"abc\")", a, s) } + n, err = Sscan("asdf", &s, &a) + if n != 1 { + t.Errorf("Sscan count error: expected 1: got %d", n) + } + if err == nil { + t.Errorf("Sscan expected error; got none: %s", err) + } + if s != "asdf" { + t.Errorf("Sscan wrong values: got %q expected \"asdf\"", s) + } +} + +// Empty strings are not valid input when scanning a string. +func TestScanEmpty(t *testing.T) { + var s1, s2 string + n, err := Sscan("abc", &s1, &s2) + if n != 1 { + t.Errorf("Sscan count error: expected 1: got %d", n) + } + if err == nil { + t.Error("Sscan expected error; got none") + } + if s1 != "abc" { + t.Errorf("Sscan wrong values: got %q expected \"abc\"", s1) + } + n, err = Sscan("", &s1, &s2) + if n != 0 { + t.Errorf("Sscan count error: expected 0: got %d", n) + } + if err == nil { + t.Error("Sscan expected error; got none") + } + // Quoted empty string is OK. + n, err = Sscanf(`""`, "%q", &s1) + if n != 1 { + t.Errorf("Sscanf count error: expected 1: got %d", n) + } + if err != nil { + t.Errorf("Sscanf expected no error with quoted string; got %s", err) + } } func TestScanNotPointer(t *testing.T) { @@ -535,3 +642,24 @@ func TestEOF(t *testing.T) { t.Error("expected one EOF, got", ec.eofCount) } } + +// Verify that, at least when using bufio, successive calls to Fscan do not lose runes. +func TestUnreadRuneWithBufio(t *testing.T) { + r := bufio.NewReader(strings.NewReader("123αb")) + var i int + var a string + n, err := Fscanf(r, "%d", &i) + if n != 1 || err != nil { + t.Errorf("reading int expected one item, no errors; got %d %q", n, err) + } + if i != 123 { + t.Errorf("expected 123; got %d", i) + } + n, err = Fscanf(r, "%s", &a) + if n != 1 || err != nil { + t.Errorf("reading string expected one item, no errors; got %d %q", n, err) + } + if a != "αb" { + t.Errorf("expected αb; got %q", a) + } +} diff --git a/src/pkg/go/ast/Makefile b/src/pkg/go/ast/Makefile index 9b5c904c1..e9b885c70 100644 --- a/src/pkg/go/ast/Makefile +++ b/src/pkg/go/ast/Makefile @@ -2,12 +2,13 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=go/ast GOFILES=\ ast.go\ filter.go\ + print.go\ scope.go\ walk.go\ diff --git a/src/pkg/go/ast/ast.go b/src/pkg/go/ast/ast.go index 2fc8b215f..cf2ce36df 100644 --- a/src/pkg/go/ast/ast.go +++ b/src/pkg/go/ast/ast.go @@ -34,8 +34,8 @@ import ( // All node types implement the Node interface. type Node interface { - // Pos returns the (beginning) position of the node. - Pos() token.Position + Pos() token.Pos // position of first character belonging to the node + End() token.Pos // position of first character immediately after the node } @@ -65,19 +65,27 @@ type Decl interface { // A Comment node represents a single //-style or /*-style comment. type Comment struct { - token.Position // beginning position of the comment - Text []byte // comment text (excluding '\n' for //-style comments) + Slash token.Pos // position of "/" starting the comment + Text []byte // comment text (excluding '\n' for //-style comments) } +func (c *Comment) Pos() token.Pos { return c.Slash } +func (c *Comment) End() token.Pos { return token.Pos(int(c.Slash) + len(c.Text)) } + + // A CommentGroup represents a sequence of comments // with no other tokens and no empty lines between. // type CommentGroup struct { - List []*Comment + List []*Comment // len(List) > 0 } +func (g *CommentGroup) Pos() token.Pos { return g.List[0].Pos() } +func (g *CommentGroup) End() token.Pos { return g.List[len(g.List)-1].End() } + + // ---------------------------------------------------------------------------- // Expressions and types @@ -94,7 +102,7 @@ type Field struct { } -func (f *Field) Pos() token.Position { +func (f *Field) Pos() token.Pos { if len(f.Names) > 0 { return f.Names[0].Pos() } @@ -102,11 +110,45 @@ func (f *Field) Pos() token.Position { } +func (f *Field) End() token.Pos { + if f.Tag != nil { + return f.Tag.End() + } + return f.Type.End() +} + + // A FieldList represents a list of Fields, enclosed by parentheses or braces. type FieldList struct { - Opening token.Position // position of opening parenthesis/brace - List []*Field // field list - Closing token.Position // position of closing parenthesis/brace + Opening token.Pos // position of opening parenthesis/brace, if any + List []*Field // field list + Closing token.Pos // position of closing parenthesis/brace, if any +} + + +func (f *FieldList) Pos() token.Pos { + if f.Opening.IsValid() { + return f.Opening + } + // the list should not be empty in this case; + // be conservative and guard against bad ASTs + if len(f.List) > 0 { + return f.List[0].Pos() + } + return token.NoPos +} + + +func (f *FieldList) End() token.Pos { + if f.Closing.IsValid() { + return f.Closing + 1 + } + // the list should not be empty in this case; + // be conservative and guard against bad ASTs + if n := len(f.List); n > 0 { + return f.List[n-1].End() + } + return token.NoPos } @@ -135,28 +177,29 @@ type ( // created. // BadExpr struct { - token.Position // beginning position of bad expression + From, To token.Pos // position range of bad expression } // An Ident node represents an identifier. Ident struct { - token.Position // identifier position - Obj *Object // denoted object + NamePos token.Pos // identifier position + Name string // identifier name + Obj *Object // denoted object; or nil } // An Ellipsis node stands for the "..." type in a // parameter list or the "..." length in an array type. // Ellipsis struct { - token.Position // position of "..." - Elt Expr // ellipsis element type (parameter lists only) + Ellipsis token.Pos // position of "..." + Elt Expr // ellipsis element type (parameter lists only); or nil } // A BasicLit node represents a literal of basic type. BasicLit struct { - token.Position // literal position - Kind token.Token // token.INT, token.FLOAT, token.IMAG, token.CHAR, or token.STRING - Value []byte // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 'a', '\x7f', "foo" or `\m\n\o` + ValuePos token.Pos // literal position + Kind token.Token // token.INT, token.FLOAT, token.IMAG, token.CHAR, or token.STRING + Value []byte // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o` } // A FuncLit node represents a function literal. @@ -166,19 +209,18 @@ type ( } // A CompositeLit node represents a composite literal. - // CompositeLit struct { - Type Expr // literal type - Lbrace token.Position // position of "{" - Elts []Expr // list of composite elements - Rbrace token.Position // position of "}" + Type Expr // literal type; or nil + Lbrace token.Pos // position of "{" + Elts []Expr // list of composite elements; or nil + Rbrace token.Pos // position of "}" } // A ParenExpr node represents a parenthesized expression. ParenExpr struct { - token.Position // position of "(" - X Expr // parenthesized expression - Rparen token.Position // position of ")" + Lparen token.Pos // position of "(" + X Expr // parenthesized expression + Rparen token.Pos // position of ")" } // A SelectorExpr node represents an expression followed by a selector. @@ -189,15 +231,19 @@ type ( // An IndexExpr node represents an expression followed by an index. IndexExpr struct { - X Expr // expression - Index Expr // index expression + X Expr // expression + Lbrack token.Pos // position of "[" + Index Expr // index expression + Rbrack token.Pos // position of "]" } // An SliceExpr node represents an expression followed by slice indices. SliceExpr struct { - X Expr // expression - Index Expr // beginning of slice range - End Expr // end of slice range; or nil + X Expr // expression + Lbrack token.Pos // position of "[" + Low Expr // begin of slice range; or nil + High Expr // end of slice range; or nil + Rbrack token.Pos // position of "]" } // A TypeAssertExpr node represents an expression followed by a @@ -210,35 +256,36 @@ type ( // A CallExpr node represents an expression followed by an argument list. CallExpr struct { - Fun Expr // function expression - Lparen token.Position // position of "(" - Args []Expr // function arguments - Rparen token.Position // positions of ")" + Fun Expr // function expression + Lparen token.Pos // position of "(" + Args []Expr // function arguments; or nil + Ellipsis token.Pos // position of "...", if any + Rparen token.Pos // position of ")" } // A StarExpr node represents an expression of the form "*" Expression. // Semantically it could be a unary "*" expression, or a pointer type. + // StarExpr struct { - token.Position // position of "*" - X Expr // operand + Star token.Pos // position of "*" + X Expr // operand } // A UnaryExpr node represents a unary expression. // Unary "*" expressions are represented via StarExpr nodes. // UnaryExpr struct { - token.Position // position of Op - Op token.Token // operator - X Expr // operand + OpPos token.Pos // position of Op + Op token.Token // operator + X Expr // operand } // A BinaryExpr node represents a binary expression. - // BinaryExpr struct { - X Expr // left operand - OpPos token.Position // position of Op - Op token.Token // operator - Y Expr // right operand + X Expr // left operand + OpPos token.Pos // position of Op + Op token.Token // operator + Y Expr // right operand } // A KeyValueExpr node represents (key : value) pairs @@ -246,7 +293,7 @@ type ( // KeyValueExpr struct { Key Expr - Colon token.Position // position of ":" + Colon token.Pos // position of ":" Value Expr } ) @@ -270,66 +317,123 @@ const ( type ( // An ArrayType node represents an array or slice type. ArrayType struct { - token.Position // position of "[" - Len Expr // Ellipsis node for [...]T array types, nil for slice types - Elt Expr // element type + Lbrack token.Pos // position of "[" + Len Expr // Ellipsis node for [...]T array types, nil for slice types + Elt Expr // element type } // A StructType node represents a struct type. StructType struct { - token.Position // position of "struct" keyword - Fields *FieldList // list of field declarations - Incomplete bool // true if (source) fields are missing in the Fields list + Struct token.Pos // position of "struct" keyword + Fields *FieldList // list of field declarations + Incomplete bool // true if (source) fields are missing in the Fields list } // Pointer types are represented via StarExpr nodes. // A FuncType node represents a function type. FuncType struct { - token.Position // position of "func" keyword - Params *FieldList // (incoming) parameters - Results *FieldList // (outgoing) results + Func token.Pos // position of "func" keyword + Params *FieldList // (incoming) parameters + Results *FieldList // (outgoing) results; or nil } // An InterfaceType node represents an interface type. InterfaceType struct { - token.Position // position of "interface" keyword - Methods *FieldList // list of methods - Incomplete bool // true if (source) methods are missing in the Methods list + Interface token.Pos // position of "interface" keyword + Methods *FieldList // list of methods + Incomplete bool // true if (source) methods are missing in the Methods list } // A MapType node represents a map type. MapType struct { - token.Position // position of "map" keyword - Key Expr - Value Expr + Map token.Pos // position of "map" keyword + Key Expr + Value Expr } // A ChanType node represents a channel type. ChanType struct { - token.Position // position of "chan" keyword or "<-" (whichever comes first) - Dir ChanDir // channel direction - Value Expr // value type + Begin token.Pos // position of "chan" keyword or "<-" (whichever comes first) + Dir ChanDir // channel direction + Value Expr // value type } ) -// Pos() implementations for expression/type where the position -// corresponds to the position of a sub-node. +// Pos and End implementations for expression/type nodes. // -func (x *FuncLit) Pos() token.Position { return x.Type.Pos() } -func (x *CompositeLit) Pos() token.Position { return x.Type.Pos() } -func (x *SelectorExpr) Pos() token.Position { return x.X.Pos() } -func (x *IndexExpr) Pos() token.Position { return x.X.Pos() } -func (x *SliceExpr) Pos() token.Position { return x.X.Pos() } -func (x *TypeAssertExpr) Pos() token.Position { return x.X.Pos() } -func (x *CallExpr) Pos() token.Position { return x.Fun.Pos() } -func (x *BinaryExpr) Pos() token.Position { return x.X.Pos() } -func (x *KeyValueExpr) Pos() token.Position { return x.Key.Pos() } +func (x *BadExpr) Pos() token.Pos { return x.From } +func (x *Ident) Pos() token.Pos { return x.NamePos } +func (x *Ellipsis) Pos() token.Pos { return x.Ellipsis } +func (x *BasicLit) Pos() token.Pos { return x.ValuePos } +func (x *FuncLit) Pos() token.Pos { return x.Type.Pos() } +func (x *CompositeLit) Pos() token.Pos { + if x.Type != nil { + return x.Type.Pos() + } + return x.Lbrace +} +func (x *ParenExpr) Pos() token.Pos { return x.Lparen } +func (x *SelectorExpr) Pos() token.Pos { return x.X.Pos() } +func (x *IndexExpr) Pos() token.Pos { return x.X.Pos() } +func (x *SliceExpr) Pos() token.Pos { return x.X.Pos() } +func (x *TypeAssertExpr) Pos() token.Pos { return x.X.Pos() } +func (x *CallExpr) Pos() token.Pos { return x.Fun.Pos() } +func (x *StarExpr) Pos() token.Pos { return x.Star } +func (x *UnaryExpr) Pos() token.Pos { return x.OpPos } +func (x *BinaryExpr) Pos() token.Pos { return x.X.Pos() } +func (x *KeyValueExpr) Pos() token.Pos { return x.Key.Pos() } +func (x *ArrayType) Pos() token.Pos { return x.Lbrack } +func (x *StructType) Pos() token.Pos { return x.Struct } +func (x *FuncType) Pos() token.Pos { return x.Func } +func (x *InterfaceType) Pos() token.Pos { return x.Interface } +func (x *MapType) Pos() token.Pos { return x.Map } +func (x *ChanType) Pos() token.Pos { return x.Begin } + + +func (x *BadExpr) End() token.Pos { return x.To } +func (x *Ident) End() token.Pos { return token.Pos(int(x.NamePos) + len(x.Name)) } +func (x *Ellipsis) End() token.Pos { + if x.Elt != nil { + return x.Elt.End() + } + return x.Ellipsis + 3 // len("...") +} +func (x *BasicLit) End() token.Pos { return token.Pos(int(x.ValuePos) + len(x.Value)) } +func (x *FuncLit) End() token.Pos { return x.Body.End() } +func (x *CompositeLit) End() token.Pos { return x.Rbrace + 1 } +func (x *ParenExpr) End() token.Pos { return x.Rparen + 1 } +func (x *SelectorExpr) End() token.Pos { return x.Sel.End() } +func (x *IndexExpr) End() token.Pos { return x.Rbrack + 1 } +func (x *SliceExpr) End() token.Pos { return x.Rbrack + 1 } +func (x *TypeAssertExpr) End() token.Pos { + if x.Type != nil { + return x.Type.End() + } + return x.X.End() +} +func (x *CallExpr) End() token.Pos { return x.Rparen + 1 } +func (x *StarExpr) End() token.Pos { return x.X.End() } +func (x *UnaryExpr) End() token.Pos { return x.X.End() } +func (x *BinaryExpr) End() token.Pos { return x.Y.End() } +func (x *KeyValueExpr) End() token.Pos { return x.Value.End() } +func (x *ArrayType) End() token.Pos { return x.Elt.End() } +func (x *StructType) End() token.Pos { return x.Fields.End() } +func (x *FuncType) End() token.Pos { + if x.Results != nil { + return x.Results.End() + } + return x.Params.End() +} +func (x *InterfaceType) End() token.Pos { return x.Methods.End() } +func (x *MapType) End() token.Pos { return x.Value.End() } +func (x *ChanType) End() token.Pos { return x.Value.End() } // exprNode() ensures that only expression/type nodes can be // assigned to an ExprNode. +// func (x *BadExpr) exprNode() {} func (x *Ident) exprNode() {} func (x *Ellipsis) exprNode() {} @@ -358,17 +462,17 @@ func (x *ChanType) exprNode() {} // ---------------------------------------------------------------------------- // Convenience functions for Idents -var noPos token.Position +var noPos token.Pos -// NewIdent creates a new Ident without position and minimal object -// information. Useful for ASTs generated by code other than the Go -// parser. +// NewIdent creates a new Ident without position. +// Useful for ASTs generated by code other than the Go parser. // -func NewIdent(name string) *Ident { return &Ident{noPos, NewObj(Err, noPos, name)} } +func NewIdent(name string) *Ident { return &Ident{noPos, name, nil} } // IsExported returns whether name is an exported Go symbol // (i.e., whether it begins with an uppercase letter). +// func IsExported(name string) bool { ch, _ := utf8.DecodeRuneInString(name) return unicode.IsUpper(ch) @@ -377,16 +481,13 @@ func IsExported(name string) bool { // IsExported returns whether id is an exported Go symbol // (i.e., whether it begins with an uppercase letter). -func (id *Ident) IsExported() bool { return id.Obj.IsExported() } - - -// Name returns an identifier's name. -func (id *Ident) Name() string { return id.Obj.Name } +// +func (id *Ident) IsExported() bool { return IsExported(id.Name) } func (id *Ident) String() string { - if id != nil && id.Obj != nil { - return id.Obj.Name + if id != nil { + return id.Name } return "" } @@ -404,7 +505,7 @@ type ( // created. // BadStmt struct { - token.Position // beginning position of bad statement + From, To token.Pos // position range of bad statement } // A DeclStmt node represents a declaration in a statement list. @@ -417,12 +518,13 @@ type ( // of the immediately preceeding semicolon. // EmptyStmt struct { - token.Position // position of preceeding ";" + Semicolon token.Pos // position of preceeding ";" } // A LabeledStmt node represents a labeled statement. LabeledStmt struct { Label *Ident + Colon token.Pos // position of ":" Stmt Stmt } @@ -435,138 +537,212 @@ type ( // An IncDecStmt node represents an increment or decrement statement. IncDecStmt struct { - X Expr - Tok token.Token // INC or DEC + X Expr + TokPos token.Pos // position of Tok + Tok token.Token // INC or DEC } // An AssignStmt node represents an assignment or // a short variable declaration. + // AssignStmt struct { Lhs []Expr - TokPos token.Position // position of Tok - Tok token.Token // assignment token, DEFINE + TokPos token.Pos // position of Tok + Tok token.Token // assignment token, DEFINE Rhs []Expr } // A GoStmt node represents a go statement. GoStmt struct { - token.Position // position of "go" keyword - Call *CallExpr + Go token.Pos // position of "go" keyword + Call *CallExpr } // A DeferStmt node represents a defer statement. DeferStmt struct { - token.Position // position of "defer" keyword - Call *CallExpr + Defer token.Pos // position of "defer" keyword + Call *CallExpr } // A ReturnStmt node represents a return statement. ReturnStmt struct { - token.Position // position of "return" keyword - Results []Expr + Return token.Pos // position of "return" keyword + Results []Expr // result expressions; or nil } // A BranchStmt node represents a break, continue, goto, // or fallthrough statement. // BranchStmt struct { - token.Position // position of Tok - Tok token.Token // keyword token (BREAK, CONTINUE, GOTO, FALLTHROUGH) - Label *Ident + TokPos token.Pos // position of Tok + Tok token.Token // keyword token (BREAK, CONTINUE, GOTO, FALLTHROUGH) + Label *Ident // label name; or nil } // A BlockStmt node represents a braced statement list. BlockStmt struct { - token.Position // position of "{" - List []Stmt - Rbrace token.Position // position of "}" + Lbrace token.Pos // position of "{" + List []Stmt + Rbrace token.Pos // position of "}" } // An IfStmt node represents an if statement. IfStmt struct { - token.Position // position of "if" keyword - Init Stmt - Cond Expr - Body *BlockStmt - Else Stmt + If token.Pos // position of "if" keyword + Init Stmt // initalization statement; or nil + Cond Expr // condition; or nil + Body *BlockStmt + Else Stmt // else branch; or nil } // A CaseClause represents a case of an expression switch statement. CaseClause struct { - token.Position // position of "case" or "default" keyword - Values []Expr // nil means default case - Colon token.Position // position of ":" - Body []Stmt // statement list; or nil + Case token.Pos // position of "case" or "default" keyword + Values []Expr // nil means default case + Colon token.Pos // position of ":" + Body []Stmt // statement list; or nil } // A SwitchStmt node represents an expression switch statement. SwitchStmt struct { - token.Position // position of "switch" keyword - Init Stmt - Tag Expr - Body *BlockStmt // CaseClauses only + Switch token.Pos // position of "switch" keyword + Init Stmt // initalization statement; or nil + Tag Expr // tag expression; or nil + Body *BlockStmt // CaseClauses only } // A TypeCaseClause represents a case of a type switch statement. TypeCaseClause struct { - token.Position // position of "case" or "default" keyword - Types []Expr // nil means default case - Colon token.Position // position of ":" - Body []Stmt // statement list; or nil + Case token.Pos // position of "case" or "default" keyword + Types []Expr // nil means default case + Colon token.Pos // position of ":" + Body []Stmt // statement list; or nil } // An TypeSwitchStmt node represents a type switch statement. TypeSwitchStmt struct { - token.Position // position of "switch" keyword - Init Stmt - Assign Stmt // x := y.(type) - Body *BlockStmt // TypeCaseClauses only + Switch token.Pos // position of "switch" keyword + Init Stmt // initalization statement; or nil + Assign Stmt // x := y.(type) + Body *BlockStmt // TypeCaseClauses only } // A CommClause node represents a case of a select statement. CommClause struct { - token.Position // position of "case" or "default" keyword - Tok token.Token // ASSIGN or DEFINE (valid only if Lhs != nil) - Lhs, Rhs Expr // Rhs == nil means default case - Colon token.Position // position of ":" - Body []Stmt // statement list; or nil + Case token.Pos // position of "case" or "default" keyword + Tok token.Token // ASSIGN or DEFINE (valid only if Lhs != nil) + Lhs, Rhs Expr // Rhs == nil means default case + Colon token.Pos // position of ":" + Body []Stmt // statement list; or nil } // An SelectStmt node represents a select statement. SelectStmt struct { - token.Position // position of "select" keyword - Body *BlockStmt // CommClauses only + Select token.Pos // position of "select" keyword + Body *BlockStmt // CommClauses only } // A ForStmt represents a for statement. ForStmt struct { - token.Position // position of "for" keyword - Init Stmt - Cond Expr - Post Stmt - Body *BlockStmt + For token.Pos // position of "for" keyword + Init Stmt // initalization statement; or nil + Cond Expr // condition; or nil + Post Stmt // post iteration statement; or nil + Body *BlockStmt } // A RangeStmt represents a for statement with a range clause. RangeStmt struct { - token.Position // position of "for" keyword - Key, Value Expr // Value may be nil - TokPos token.Position // position of Tok - Tok token.Token // ASSIGN, DEFINE - X Expr // value to range over - Body *BlockStmt + For token.Pos // position of "for" keyword + Key, Value Expr // Value may be nil + TokPos token.Pos // position of Tok + Tok token.Token // ASSIGN, DEFINE + X Expr // value to range over + Body *BlockStmt } ) -// Pos() implementations for statement nodes where the position -// corresponds to the position of a sub-node. +// Pos and End implementations for statement nodes. // -func (s *DeclStmt) Pos() token.Position { return s.Decl.Pos() } -func (s *LabeledStmt) Pos() token.Position { return s.Label.Pos() } -func (s *ExprStmt) Pos() token.Position { return s.X.Pos() } -func (s *IncDecStmt) Pos() token.Position { return s.X.Pos() } -func (s *AssignStmt) Pos() token.Position { return s.Lhs[0].Pos() } +func (s *BadStmt) Pos() token.Pos { return s.From } +func (s *DeclStmt) Pos() token.Pos { return s.Decl.Pos() } +func (s *EmptyStmt) Pos() token.Pos { return s.Semicolon } +func (s *LabeledStmt) Pos() token.Pos { return s.Label.Pos() } +func (s *ExprStmt) Pos() token.Pos { return s.X.Pos() } +func (s *IncDecStmt) Pos() token.Pos { return s.X.Pos() } +func (s *AssignStmt) Pos() token.Pos { return s.Lhs[0].Pos() } +func (s *GoStmt) Pos() token.Pos { return s.Go } +func (s *DeferStmt) Pos() token.Pos { return s.Defer } +func (s *ReturnStmt) Pos() token.Pos { return s.Return } +func (s *BranchStmt) Pos() token.Pos { return s.TokPos } +func (s *BlockStmt) Pos() token.Pos { return s.Lbrace } +func (s *IfStmt) Pos() token.Pos { return s.If } +func (s *CaseClause) Pos() token.Pos { return s.Case } +func (s *SwitchStmt) Pos() token.Pos { return s.Switch } +func (s *TypeCaseClause) Pos() token.Pos { return s.Case } +func (s *TypeSwitchStmt) Pos() token.Pos { return s.Switch } +func (s *CommClause) Pos() token.Pos { return s.Case } +func (s *SelectStmt) Pos() token.Pos { return s.Select } +func (s *ForStmt) Pos() token.Pos { return s.For } +func (s *RangeStmt) Pos() token.Pos { return s.For } + + +func (s *BadStmt) End() token.Pos { return s.To } +func (s *DeclStmt) End() token.Pos { return s.Decl.End() } +func (s *EmptyStmt) End() token.Pos { + return s.Semicolon + 1 /* len(";") */ +} +func (s *LabeledStmt) End() token.Pos { return s.Stmt.End() } +func (s *ExprStmt) End() token.Pos { return s.X.End() } +func (s *IncDecStmt) End() token.Pos { + return s.TokPos + 2 /* len("++") */ +} +func (s *AssignStmt) End() token.Pos { return s.Rhs[len(s.Rhs)-1].End() } +func (s *GoStmt) End() token.Pos { return s.Call.End() } +func (s *DeferStmt) End() token.Pos { return s.Call.End() } +func (s *ReturnStmt) End() token.Pos { + if n := len(s.Results); n > 0 { + return s.Results[n-1].End() + } + return s.Return + 6 // len("return") +} +func (s *BranchStmt) End() token.Pos { + if s.Label != nil { + return s.Label.End() + } + return token.Pos(int(s.TokPos) + len(s.Tok.String())) +} +func (s *BlockStmt) End() token.Pos { return s.Rbrace + 1 } +func (s *IfStmt) End() token.Pos { + if s.Else != nil { + return s.Else.End() + } + return s.Body.End() +} +func (s *CaseClause) End() token.Pos { + if n := len(s.Body); n > 0 { + return s.Body[n-1].End() + } + return s.Colon + 1 +} +func (s *SwitchStmt) End() token.Pos { return s.Body.End() } +func (s *TypeCaseClause) End() token.Pos { + if n := len(s.Body); n > 0 { + return s.Body[n-1].End() + } + return s.Colon + 1 +} +func (s *TypeSwitchStmt) End() token.Pos { return s.Body.End() } +func (s *CommClause) End() token.Pos { + if n := len(s.Body); n > 0 { + return s.Body[n-1].End() + } + return s.Colon + 1 +} +func (s *SelectStmt) End() token.Pos { return s.Body.End() } +func (s *ForStmt) End() token.Pos { return s.Body.End() } +func (s *RangeStmt) End() token.Pos { return s.Body.End() } // stmtNode() ensures that only statement nodes can be @@ -618,9 +794,10 @@ type ( // A ValueSpec node represents a constant or variable declaration // (ConstSpec or VarSpec production). + // ValueSpec struct { Doc *CommentGroup // associated documentation; or nil - Names []*Ident // value names + Names []*Ident // value names (len(Names) > 0) Type Expr // value type; or nil Values []Expr // initial values; or nil Comment *CommentGroup // line comments; or nil @@ -630,23 +807,35 @@ type ( TypeSpec struct { Doc *CommentGroup // associated documentation; or nil Name *Ident // type name - Type Expr // *ArrayType, *StructType, *FuncType, *InterfaceType, *MapType, *ChanType or *Ident + Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes Comment *CommentGroup // line comments; or nil } ) -// Pos() implementations for spec nodes. +// Pos and End implementations for spec nodes. // -func (s *ImportSpec) Pos() token.Position { +func (s *ImportSpec) Pos() token.Pos { if s.Name != nil { return s.Name.Pos() } return s.Path.Pos() } +func (s *ValueSpec) Pos() token.Pos { return s.Names[0].Pos() } +func (s *TypeSpec) Pos() token.Pos { return s.Name.Pos() } + -func (s *ValueSpec) Pos() token.Position { return s.Names[0].Pos() } -func (s *TypeSpec) Pos() token.Position { return s.Name.Pos() } +func (s *ImportSpec) End() token.Pos { return s.Path.End() } +func (s *ValueSpec) End() token.Pos { + if n := len(s.Values); n > 0 { + return s.Values[n-1].End() + } + if s.Type != nil { + return s.Type.End() + } + return s.Names[len(s.Names)-1].End() +} +func (s *TypeSpec) End() token.Pos { return s.Type.End() } // specNode() ensures that only spec nodes can be @@ -665,7 +854,7 @@ type ( // created. // BadDecl struct { - token.Position // beginning position of bad declaration + From, To token.Pos // position range of bad declaration } // A GenDecl node (generic declaration node) represents an import, @@ -680,12 +869,12 @@ type ( // token.VAR *ValueSpec // GenDecl struct { - Doc *CommentGroup // associated documentation; or nil - token.Position // position of Tok - Tok token.Token // IMPORT, CONST, TYPE, VAR - Lparen token.Position // position of '(', if any - Specs []Spec - Rparen token.Position // position of ')', if any + Doc *CommentGroup // associated documentation; or nil + TokPos token.Pos // position of Tok + Tok token.Token // IMPORT, CONST, TYPE, VAR + Lparen token.Pos // position of '(', if any + Specs []Spec + Rparen token.Pos // position of ')', if any } // A FuncDecl node represents a function declaration. @@ -699,8 +888,26 @@ type ( ) -// The position of a FuncDecl node is the position of its function type. -func (d *FuncDecl) Pos() token.Position { return d.Type.Pos() } +// Pos and End implementations for declaration nodes. +// +func (d *BadDecl) Pos() token.Pos { return d.From } +func (d *GenDecl) Pos() token.Pos { return d.TokPos } +func (d *FuncDecl) Pos() token.Pos { return d.Type.Pos() } + + +func (d *BadDecl) End() token.Pos { return d.To } +func (d *GenDecl) End() token.Pos { + if d.Rparen.IsValid() { + return d.Rparen + 1 + } + return d.Specs[0].End() +} +func (d *FuncDecl) End() token.Pos { + if d.Body != nil { + return d.Body.End() + } + return d.Type.End() +} // declNode() ensures that only declaration nodes can be @@ -721,11 +928,20 @@ func (d *FuncDecl) declNode() {} // via Doc and Comment fields. // type File struct { - Doc *CommentGroup // associated documentation; or nil - token.Position // position of "package" keyword - Name *Ident // package name - Decls []Decl // top-level declarations - Comments []*CommentGroup // list of all comments in the source file + Doc *CommentGroup // associated documentation; or nil + Package token.Pos // position of "package" keyword + Name *Ident // package name + Decls []Decl // top-level declarations; or nil + Comments []*CommentGroup // list of all comments in the source file +} + + +func (f *File) Pos() token.Pos { return f.Package } +func (f *File) End() token.Pos { + if n := len(f.Decls); n > 0 { + return f.Decls[n-1].End() + } + return f.Name.End() } @@ -734,6 +950,10 @@ type File struct { // type Package struct { Name string // package name - Scope *Scope // package scope + Scope *Scope // package scope; or nil Files map[string]*File // Go source files by filename } + + +func (p *Package) Pos() token.Pos { return token.NoPos } +func (p *Package) End() token.Pos { return token.NoPos } diff --git a/src/pkg/go/ast/filter.go b/src/pkg/go/ast/filter.go index 009ffc21d..0c3cef4b2 100644 --- a/src/pkg/go/ast/filter.go +++ b/src/pkg/go/ast/filter.go @@ -197,7 +197,7 @@ type Filter func(string) bool func filterIdentList(list []*Ident, f Filter) []*Ident { j := 0 for _, x := range list { - if f(x.Name()) { + if f(x.Name) { list[j] = x j++ } @@ -212,7 +212,7 @@ func filterSpec(spec Spec, f Filter) bool { s.Names = filterIdentList(s.Names, f) return len(s.Names) > 0 case *TypeSpec: - return f(s.Name.Name()) + return f(s.Name.Name) } return false } @@ -236,7 +236,7 @@ func filterDecl(decl Decl, f Filter) bool { d.Specs = filterSpecList(d.Specs, f) return len(d.Specs) > 0 case *FuncDecl: - return f(d.Name.Name()) + return f(d.Name.Name) } return false } @@ -307,27 +307,6 @@ const ( var separator = &Comment{noPos, []byte("//")} -// lineAfterComment computes the position of the beginning -// of the line immediately following a comment. -func lineAfterComment(c *Comment) token.Position { - pos := c.Pos() - line := pos.Line - text := c.Text - if text[1] == '*' { - /*-style comment - determine endline */ - for _, ch := range text { - if ch == '\n' { - line++ - } - } - } - pos.Offset += len(text) + 1 // +1 for newline - pos.Line = line + 1 // line after comment - pos.Column = 1 // beginning of line - return pos -} - - // MergePackageFiles creates a file AST by merging the ASTs of the // files belonging to a package. The mode flags control merging behavior. // @@ -351,7 +330,7 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File { // a package comment; but it's better to collect extra comments // than drop them on the floor. var doc *CommentGroup - var pos token.Position + var pos token.Pos if ndocs > 0 { list := make([]*Comment, ndocs-1) // -1: no separator before first group i := 0 @@ -366,11 +345,11 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File { list[i] = c i++ } - end := lineAfterComment(f.Doc.List[len(f.Doc.List)-1]) - if end.Offset > pos.Offset { - // Keep the maximum end position as - // position for the package clause. - pos = end + if f.Package > pos { + // Keep the maximum package clause position as + // position for the package clause of the merged + // files. + pos = f.Package } } } @@ -397,7 +376,7 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File { // entities (const, type, vars) if // multiple declarations are common. if f, isFun := d.(*FuncDecl); isFun { - name := f.Name.Name() + name := f.Name.Name if j, exists := funcs[name]; exists { // function declared already if decls[j] != nil && decls[j].(*FuncDecl).Doc == nil { diff --git a/src/pkg/go/ast/print.go b/src/pkg/go/ast/print.go new file mode 100644 index 000000000..d71490d4a --- /dev/null +++ b/src/pkg/go/ast/print.go @@ -0,0 +1,217 @@ +// 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 printing suppport for ASTs. + +package ast + +import ( + "fmt" + "go/token" + "io" + "os" + "reflect" +) + + +// A FieldFilter may be provided to Fprint to control the output. +type FieldFilter func(name string, value reflect.Value) bool + + +// NotNilFilter returns true for field values that are not nil; +// it returns false otherwise. +func NotNilFilter(_ string, value reflect.Value) bool { + v, ok := value.(interface { + IsNil() bool + }) + return !ok || !v.IsNil() +} + + +// Fprint prints the (sub-)tree starting at AST node x to w. +// +// A non-nil FieldFilter f may be provided to control the output: +// struct fields for which f(fieldname, fieldvalue) is true are +// are printed; all others are filtered from the output. +// +func Fprint(w io.Writer, x interface{}, f FieldFilter) (n int, err os.Error) { + // setup printer + p := printer{ + output: w, + filter: f, + ptrmap: make(map[interface{}]int), + last: '\n', // force printing of line number on first line + } + + // install error handler + defer func() { + n = p.written + if e := recover(); e != nil { + err = e.(localError).err // re-panics if it's not a localError + } + }() + + // print x + if x == nil { + p.printf("nil\n") + return + } + p.print(reflect.NewValue(x)) + p.printf("\n") + + return +} + + +// Print prints x to standard output, skipping nil fields. +// Print(x) is the same as Fprint(os.Stdout, x, NotNilFilter). +func Print(x interface{}) (int, os.Error) { + return Fprint(os.Stdout, x, NotNilFilter) +} + + +type printer struct { + output io.Writer + filter FieldFilter + ptrmap map[interface{}]int // *reflect.PtrValue -> line number + written int // number of bytes written to output + indent int // current indentation level + last byte // the last byte processed by Write + line int // current line number +} + + +var indent = []byte(". ") + +func (p *printer) Write(data []byte) (n int, err os.Error) { + var m int + for i, b := range data { + // invariant: data[0:n] has been written + if b == '\n' { + m, err = p.output.Write(data[n : i+1]) + n += m + if err != nil { + return + } + p.line++ + } else if p.last == '\n' { + _, err = fmt.Fprintf(p.output, "%6d ", p.line) + if err != nil { + return + } + for j := p.indent; j > 0; j-- { + _, err = p.output.Write(indent) + if err != nil { + return + } + } + } + p.last = b + } + m, err = p.output.Write(data[n:]) + n += m + return +} + + +// localError wraps locally caught os.Errors so we can distinguish +// them from genuine panics which we don't want to return as errors. +type localError struct { + err os.Error +} + + +// printf is a convenience wrapper that takes care of print errors. +func (p *printer) printf(format string, args ...interface{}) { + n, err := fmt.Fprintf(p, format, args...) + p.written += n + if err != nil { + panic(localError{err}) + } +} + + +// Implementation note: Print is written for AST nodes but could be +// used to print arbitrary data structures; such a version should +// probably be in a different package. + +func (p *printer) print(x reflect.Value) { + // Note: This test is only needed because AST nodes + // embed a token.Position, and thus all of them + // understand the String() method (but it only + // applies to the Position field). + // TODO: Should reconsider this AST design decision. + if pos, ok := x.Interface().(token.Position); ok { + p.printf("%s", pos) + return + } + + if !NotNilFilter("", x) { + p.printf("nil") + return + } + + switch v := x.(type) { + case *reflect.InterfaceValue: + p.print(v.Elem()) + + case *reflect.MapValue: + p.printf("%s (len = %d) {\n", x.Type().String(), v.Len()) + p.indent++ + for _, key := range v.Keys() { + p.print(key) + p.printf(": ") + p.print(v.Elem(key)) + } + p.indent-- + p.printf("}") + + case *reflect.PtrValue: + p.printf("*") + // type-checked ASTs may contain cycles - use ptrmap + // to keep track of objects that have been printed + // already and print the respective line number instead + ptr := v.Interface() + if line, exists := p.ptrmap[ptr]; exists { + p.printf("(obj @ %d)", line) + } else { + p.ptrmap[ptr] = p.line + p.print(v.Elem()) + } + + case *reflect.SliceValue: + if s, ok := v.Interface().([]byte); ok { + p.printf("%#q", s) + return + } + p.printf("%s (len = %d) {\n", x.Type().String(), v.Len()) + p.indent++ + for i, n := 0, v.Len(); i < n; i++ { + p.printf("%d: ", i) + p.print(v.Elem(i)) + p.printf("\n") + } + p.indent-- + p.printf("}") + + case *reflect.StructValue: + p.printf("%s {\n", x.Type().String()) + p.indent++ + t := v.Type().(*reflect.StructType) + for i, n := 0, t.NumField(); i < n; i++ { + name := t.Field(i).Name + value := v.Field(i) + if p.filter == nil || p.filter(name, value) { + p.printf("%s: ", name) + p.print(value) + p.printf("\n") + } + } + p.indent-- + p.printf("}") + + default: + p.printf("%v", x.Interface()) + } +} diff --git a/src/pkg/go/ast/scope.go b/src/pkg/go/ast/scope.go index b5a38484e..956a208ae 100644 --- a/src/pkg/go/ast/scope.go +++ b/src/pkg/go/ast/scope.go @@ -2,25 +2,110 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// This file implements scopes, the objects they contain, +// and object types. + package ast -import "go/token" +// A Scope maintains the set of named language entities declared +// in the scope and a link to the immediately surrounding (outer) +// scope. +// +type Scope struct { + Outer *Scope + Objects []*Object // in declaration order + // Implementation note: In some cases (struct fields, + // function parameters) we need the source order of + // variables. Thus for now, we store scope entries + // in a linear list. If scopes become very large + // (say, for packages), we may need to change this + // to avoid slow lookups. +} + + +// NewScope creates a new scope nested in the outer scope. +func NewScope(outer *Scope) *Scope { + const n = 4 // initial scope capacity, must be > 0 + return &Scope{outer, make([]*Object, 0, n)} +} + + +// Lookup returns the object with the given name if it is +// found in scope s, otherwise it returns nil. Outer scopes +// are ignored. +// +// Lookup always returns nil if name is "_", even if the scope +// contains objects with that name. +// +func (s *Scope) Lookup(name string) *Object { + if name != "_" { + for _, obj := range s.Objects { + if obj.Name == name { + return obj + } + } + } + return nil +} + + +// Insert attempts to insert a named object into the scope s. +// If the scope does not contain an object with that name yet +// or if the object is named "_", Insert inserts the object +// and returns it. Otherwise, Insert leaves the scope unchanged +// and returns the object found in the scope instead. +// +func (s *Scope) Insert(obj *Object) *Object { + alt := s.Lookup(obj.Name) + if alt == nil { + s.append(obj) + alt = obj + } + return alt +} + + +func (s *Scope) append(obj *Object) { + s.Objects = append(s.Objects, obj) +} + +// ---------------------------------------------------------------------------- +// Objects + +// An Object describes a language entity such as a package, +// constant, type, variable, or function (incl. methods). +// +type Object struct { + Kind Kind + Name string // declared name + Type *Type + Decl interface{} // corresponding Field, XxxSpec or FuncDecl + N int // value of iota for this declaration +} + + +// NewObj creates a new object of a given kind and name. +func NewObj(kind Kind, name string) *Object { + return &Object{Kind: kind, Name: name} +} + -type ObjKind int +// Kind describes what an object represents. +type Kind int // The list of possible Object kinds. const ( - Err ObjKind = iota // object kind unknown (forward reference or error) - Pkg // package - Con // constant - Typ // type - Var // variable - Fun // function or method + Bad Kind = iota // for error handling + Pkg // package + Con // constant + Typ // type + Var // variable + Fun // function or method ) var objKindStrings = [...]string{ - Err: "", + Bad: "bad", Pkg: "package", Con: "const", Typ: "type", @@ -29,65 +114,129 @@ var objKindStrings = [...]string{ } -func (kind ObjKind) String() string { return objKindStrings[kind] } +func (kind Kind) String() string { return objKindStrings[kind] } -// An Object describes a language entity such as a package, -// constant, type, variable, or function (incl. methods). -// -type Object struct { - Kind ObjKind - Pos token.Position // declaration position - Name string // declared name +// IsExported returns whether obj is exported. +func (obj *Object) IsExported() bool { return IsExported(obj.Name) } + + +// ---------------------------------------------------------------------------- +// Types + +// A Type represents a Go type. +type Type struct { + Form Form + Obj *Object // corresponding type name, or nil + Scope *Scope // fields and methods, always present + N uint // basic type id, array length, number of function results, or channel direction + Key, Elt *Type // map key and array, pointer, slice, map or channel element + Params *Scope // function (receiver, input and result) parameters, tuple expressions (results of function calls), or nil + Expr Expr // corresponding AST expression } -func NewObj(kind ObjKind, pos token.Position, name string) *Object { - return &Object{kind, pos, name} +// NewType creates a new type of a given form. +func NewType(form Form) *Type { + return &Type{Form: form, Scope: NewScope(nil)} } -// IsExported returns whether obj is exported. -func (obj *Object) IsExported() bool { return IsExported(obj.Name) } +// Form describes the form of a type. +type Form int +// The list of possible type forms. +const ( + BadType Form = iota // for error handling + Unresolved // type not fully setup + Basic + Array + Struct + Pointer + Function + Method + Interface + Slice + Map + Channel + Tuple +) -// A Scope maintains the set of named language entities visible -// in the scope and a link to the immediately surrounding (outer) -// scope. -// -type Scope struct { - Outer *Scope - Objects map[string]*Object + +var formStrings = [...]string{ + BadType: "badType", + Unresolved: "unresolved", + Basic: "basic", + Array: "array", + Struct: "struct", + Pointer: "pointer", + Function: "function", + Method: "method", + Interface: "interface", + Slice: "slice", + Map: "map", + Channel: "channel", + Tuple: "tuple", } -// NewScope creates a new scope nested in the outer scope. -func NewScope(outer *Scope) *Scope { return &Scope{outer, make(map[string]*Object)} } - - -// Declare attempts to insert a named object into the scope s. -// If the scope does not contain an object with that name yet, -// Declare inserts the object, and returns it. Otherwise, the -// scope remains unchanged and Declare returns the object found -// in the scope instead. -func (s *Scope) Declare(obj *Object) *Object { - decl, found := s.Objects[obj.Name] - if !found { - s.Objects[obj.Name] = obj - decl = obj - } - return decl -} +func (form Form) String() string { return formStrings[form] } -// Lookup looks up an object in the current scope chain. -// The result is nil if the object is not found. -// -func (s *Scope) Lookup(name string) *Object { - for ; s != nil; s = s.Outer { - if obj, found := s.Objects[name]; found { - return obj - } - } - return nil +// The list of basic type id's. +const ( + Bool = iota + Byte + Uint + Int + Float + Complex + Uintptr + String + + Uint8 + Uint16 + Uint32 + Uint64 + + Int8 + Int16 + Int32 + Int64 + + Float32 + Float64 + + Complex64 + Complex128 + + // TODO(gri) ideal types are missing +) + + +var BasicTypes = map[uint]string{ + Bool: "bool", + Byte: "byte", + Uint: "uint", + Int: "int", + Float: "float", + Complex: "complex", + Uintptr: "uintptr", + String: "string", + + Uint8: "uint8", + Uint16: "uint16", + Uint32: "uint32", + Uint64: "uint64", + + Int8: "int8", + Int16: "int16", + Int32: "int32", + Int64: "int64", + + Float32: "float32", + Float64: "float64", + + Complex64: "complex64", + Complex128: "complex128", } diff --git a/src/pkg/go/ast/walk.go b/src/pkg/go/ast/walk.go index 6c9837a01..875a92f3f 100644 --- a/src/pkg/go/ast/walk.go +++ b/src/pkg/go/ast/walk.go @@ -10,51 +10,57 @@ import "fmt" // If the result visitor w is not nil, Walk visits each of the children // of node with the visitor w, followed by a call of w.Visit(nil). type Visitor interface { - Visit(node interface{}) (w Visitor) + Visit(node Node) (w Visitor) } -func walkIdent(v Visitor, x *Ident) { - if x != nil { +// Helper functions for common node lists. They may be empty. + +func walkIdentList(v Visitor, list []*Ident) { + for _, x := range list { Walk(v, x) } } -func walkCommentGroup(v Visitor, g *CommentGroup) { - if g != nil { - Walk(v, g) +func walkExprList(v Visitor, list []Expr) { + for _, x := range list { + Walk(v, x) } } -func walkBlockStmt(v Visitor, b *BlockStmt) { - if b != nil { - Walk(v, b) +func walkStmtList(v Visitor, list []Stmt) { + for _, x := range list { + Walk(v, x) } } -// Walk traverses an AST in depth-first order: If node != nil, it -// invokes v.Visit(node). If the visitor w returned by v.Visit(node) is -// not nil, Walk visits each of the children of node with the visitor w, -// followed by a call of w.Visit(nil). -// -// Walk may be called with any of the named ast node types. It also -// accepts arguments of type []*Field, []*Ident, []Expr, []Stmt and []Decl; -// the respective children are the slice elements. -// -func Walk(v Visitor, node interface{}) { - if node == nil { - return +func walkDeclList(v Visitor, list []Decl) { + for _, x := range list { + Walk(v, x) } +} + + +// TODO(gri): Investigate if providing a closure to Walk leads to +// simpler use (and may help eliminate Inspect in turn). + +// Walk traverses an AST in depth-first order: It starts by calling +// v.Visit(node); node must not be nil. If the visitor w returned by +// v.Visit(node) is not nil, Walk is invoked recursively with visitor +// w for each of the non-nil children of node, followed by a call of +// w.Visit(nil). +// +func Walk(v Visitor, node Node) { if v = v.Visit(node); v == nil { return } // walk children // (the order of the cases matches the order - // of the corresponding declaration in ast.go) + // of the corresponding node types in ast.go) switch n := node.(type) { // Comments and fields case *Comment: @@ -66,11 +72,17 @@ func Walk(v Visitor, node interface{}) { } case *Field: - walkCommentGroup(v, n.Doc) - Walk(v, n.Names) + if n.Doc != nil { + Walk(v, n.Doc) + } + walkIdentList(v, n.Names) Walk(v, n.Type) - Walk(v, n.Tag) - walkCommentGroup(v, n.Comment) + if n.Tag != nil { + Walk(v, n.Tag) + } + if n.Comment != nil { + Walk(v, n.Comment) + } case *FieldList: for _, f := range n.List { @@ -78,25 +90,30 @@ func Walk(v Visitor, node interface{}) { } // Expressions - case *BadExpr, *Ident, *Ellipsis, *BasicLit: + case *BadExpr, *Ident, *BasicLit: // nothing to do - case *FuncLit: - if n != nil { - Walk(v, n.Type) + case *Ellipsis: + if n.Elt != nil { + Walk(v, n.Elt) } - walkBlockStmt(v, n.Body) - case *CompositeLit: + case *FuncLit: Walk(v, n.Type) - Walk(v, n.Elts) + Walk(v, n.Body) + + case *CompositeLit: + if n.Type != nil { + Walk(v, n.Type) + } + walkExprList(v, n.Elts) case *ParenExpr: Walk(v, n.X) case *SelectorExpr: Walk(v, n.X) - walkIdent(v, n.Sel) + Walk(v, n.Sel) case *IndexExpr: Walk(v, n.X) @@ -104,16 +121,22 @@ func Walk(v Visitor, node interface{}) { case *SliceExpr: Walk(v, n.X) - Walk(v, n.Index) - Walk(v, n.End) + if n.Low != nil { + Walk(v, n.Low) + } + if n.High != nil { + Walk(v, n.High) + } case *TypeAssertExpr: Walk(v, n.X) - Walk(v, n.Type) + if n.Type != nil { + Walk(v, n.Type) + } case *CallExpr: Walk(v, n.Fun) - Walk(v, n.Args) + walkExprList(v, n.Args) case *StarExpr: Walk(v, n.X) @@ -131,7 +154,9 @@ func Walk(v Visitor, node interface{}) { // Types case *ArrayType: - Walk(v, n.Len) + if n.Len != nil { + Walk(v, n.Len) + } Walk(v, n.Elt) case *StructType: @@ -164,7 +189,7 @@ func Walk(v Visitor, node interface{}) { // nothing to do case *LabeledStmt: - walkIdent(v, n.Label) + Walk(v, n.Label) Walk(v, n.Stmt) case *ExprStmt: @@ -174,150 +199,198 @@ func Walk(v Visitor, node interface{}) { Walk(v, n.X) case *AssignStmt: - Walk(v, n.Lhs) - Walk(v, n.Rhs) + walkExprList(v, n.Lhs) + walkExprList(v, n.Rhs) case *GoStmt: - if n.Call != nil { - Walk(v, n.Call) - } + Walk(v, n.Call) case *DeferStmt: - if n.Call != nil { - Walk(v, n.Call) - } + Walk(v, n.Call) case *ReturnStmt: - Walk(v, n.Results) + walkExprList(v, n.Results) case *BranchStmt: - walkIdent(v, n.Label) + if n.Label != nil { + Walk(v, n.Label) + } case *BlockStmt: - Walk(v, n.List) + walkStmtList(v, n.List) case *IfStmt: - Walk(v, n.Init) - Walk(v, n.Cond) - walkBlockStmt(v, n.Body) - Walk(v, n.Else) + if n.Init != nil { + Walk(v, n.Init) + } + if n.Cond != nil { + Walk(v, n.Cond) + } + Walk(v, n.Body) + if n.Else != nil { + Walk(v, n.Else) + } case *CaseClause: - Walk(v, n.Values) - Walk(v, n.Body) + walkExprList(v, n.Values) + walkStmtList(v, n.Body) case *SwitchStmt: - Walk(v, n.Init) - Walk(v, n.Tag) - walkBlockStmt(v, n.Body) + if n.Init != nil { + Walk(v, n.Init) + } + if n.Tag != nil { + Walk(v, n.Tag) + } + Walk(v, n.Body) case *TypeCaseClause: - Walk(v, n.Types) - Walk(v, n.Body) + for _, x := range n.Types { + Walk(v, x) + } + walkStmtList(v, n.Body) case *TypeSwitchStmt: - Walk(v, n.Init) + if n.Init != nil { + Walk(v, n.Init) + } Walk(v, n.Assign) - walkBlockStmt(v, n.Body) + Walk(v, n.Body) case *CommClause: - Walk(v, n.Lhs) - Walk(v, n.Rhs) - Walk(v, n.Body) + if n.Lhs != nil { + Walk(v, n.Lhs) + } + if n.Rhs != nil { + Walk(v, n.Rhs) + } + walkStmtList(v, n.Body) case *SelectStmt: - walkBlockStmt(v, n.Body) + Walk(v, n.Body) case *ForStmt: - Walk(v, n.Init) - Walk(v, n.Cond) - Walk(v, n.Post) - walkBlockStmt(v, n.Body) + if n.Init != nil { + Walk(v, n.Init) + } + if n.Cond != nil { + Walk(v, n.Cond) + } + if n.Post != nil { + Walk(v, n.Post) + } + Walk(v, n.Body) case *RangeStmt: Walk(v, n.Key) - Walk(v, n.Value) + if n.Value != nil { + Walk(v, n.Value) + } Walk(v, n.X) - walkBlockStmt(v, n.Body) + Walk(v, n.Body) // Declarations case *ImportSpec: - walkCommentGroup(v, n.Doc) - walkIdent(v, n.Name) + if n.Doc != nil { + Walk(v, n.Doc) + } + if n.Name != nil { + Walk(v, n.Name) + } Walk(v, n.Path) - walkCommentGroup(v, n.Comment) + if n.Comment != nil { + Walk(v, n.Comment) + } case *ValueSpec: - walkCommentGroup(v, n.Doc) - Walk(v, n.Names) - Walk(v, n.Type) - Walk(v, n.Values) - walkCommentGroup(v, n.Comment) + if n.Doc != nil { + Walk(v, n.Doc) + } + walkIdentList(v, n.Names) + if n.Type != nil { + Walk(v, n.Type) + } + walkExprList(v, n.Values) + if n.Comment != nil { + Walk(v, n.Comment) + } case *TypeSpec: - walkCommentGroup(v, n.Doc) - walkIdent(v, n.Name) + if n.Doc != nil { + Walk(v, n.Doc) + } + Walk(v, n.Name) Walk(v, n.Type) - walkCommentGroup(v, n.Comment) + if n.Comment != nil { + Walk(v, n.Comment) + } case *BadDecl: // nothing to do case *GenDecl: - walkCommentGroup(v, n.Doc) + if n.Doc != nil { + Walk(v, n.Doc) + } for _, s := range n.Specs { Walk(v, s) } case *FuncDecl: - walkCommentGroup(v, n.Doc) + if n.Doc != nil { + Walk(v, n.Doc) + } if n.Recv != nil { Walk(v, n.Recv) } - walkIdent(v, n.Name) - if n.Type != nil { - Walk(v, n.Type) + Walk(v, n.Name) + Walk(v, n.Type) + if n.Body != nil { + Walk(v, n.Body) } - walkBlockStmt(v, n.Body) // Files and packages case *File: - walkCommentGroup(v, n.Doc) - walkIdent(v, n.Name) - Walk(v, n.Decls) + if n.Doc != nil { + Walk(v, n.Doc) + } + Walk(v, n.Name) + walkDeclList(v, n.Decls) for _, g := range n.Comments { Walk(v, g) } + // don't walk n.Comments - they have been + // visited already through the individual + // nodes case *Package: for _, f := range n.Files { Walk(v, f) } - case []*Ident: - for _, x := range n { - Walk(v, x) - } + default: + fmt.Printf("ast.Walk: unexpected node type %T", n) + panic("ast.Walk") + } - case []Expr: - for _, x := range n { - Walk(v, x) - } + v.Visit(nil) +} - case []Stmt: - for _, x := range n { - Walk(v, x) - } - case []Decl: - for _, x := range n { - Walk(v, x) - } +type inspector func(Node) bool - default: - fmt.Printf("ast.Walk: unexpected type %T", n) - panic("ast.Walk") +func (f inspector) Visit(node Node) Visitor { + if f(node) { + return f } + return nil +} - v.Visit(nil) + +// Inspect traverses an AST in depth-first order: It starts by calling +// f(node); node must not be nil. If f returns true, Inspect invokes f +// for all the non-nil children of node, recursively. +// +func Inspect(node Node, f func(Node) bool) { + Walk(inspector(f), node) } diff --git a/src/pkg/go/doc/Makefile b/src/pkg/go/doc/Makefile index 1558ac30a..a5152c793 100644 --- a/src/pkg/go/doc/Makefile +++ b/src/pkg/go/doc/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=go/doc GOFILES=\ diff --git a/src/pkg/go/doc/comment.go b/src/pkg/go/doc/comment.go index bbbc6a3c2..9ff0bd536 100644 --- a/src/pkg/go/doc/comment.go +++ b/src/pkg/go/doc/comment.go @@ -8,7 +8,6 @@ package doc import ( "go/ast" - "http" // for URLEscape "io" "regexp" "strings" @@ -63,16 +62,7 @@ func CommentText(comment *ast.CommentGroup) string { // Walk lines, stripping trailing white space and adding to list. for _, l := range cl { - l = stripTrailingWhitespace(l) - // Add to list. - n := len(lines) - if n+1 >= cap(lines) { - newlines := make([]string, n, 2*cap(lines)) - copy(newlines, lines) - lines = newlines - } - lines = lines[0 : n+1] - lines[n] = l + lines = append(lines, stripTrailingWhitespace(l)) } } @@ -88,10 +78,8 @@ func CommentText(comment *ast.CommentGroup) string { lines = lines[0:n] // Add final "" entry to get trailing newline from Join. - // The original loop always leaves room for one more. if n > 0 && lines[n-1] != "" { - lines = lines[0 : n+1] - lines[n] = "" + lines = append(lines, "") } return strings.Join(lines, "\n") @@ -195,12 +183,12 @@ var ( // into a link). Go identifiers that appear in the words map are italicized; if // the corresponding map value is not the empty string, it is considered a URL // and the word is converted into a link. If nice is set, the remaining text's -// appearance is improved where is makes sense (e.g., `` is turned into “ +// appearance is improved where it makes sense (e.g., `` is turned into “ // and '' into ”). func emphasize(w io.Writer, line []byte, words map[string]string, nice bool) { for { - m := matchRx.Execute(line) - if len(m) == 0 { + m := matchRx.FindSubmatchIndex(line) + if m == nil { break } // m >= 6 (two parenthesized sub-regexps in matchRx, 1st one is identRx) @@ -224,11 +212,10 @@ func emphasize(w io.Writer, line []byte, words map[string]string, nice bool) { italics = false // don't italicize URLs } - // write match if len(url) > 0 { w.Write(html_a) - w.Write([]byte(http.URLEscape(url))) + template.HTMLEscape(w, []byte(url)) w.Write(html_aq) } if italics { diff --git a/src/pkg/go/doc/doc.go b/src/pkg/go/doc/doc.go index b73fd285c..fb1c4e03d 100644 --- a/src/pkg/go/doc/doc.go +++ b/src/pkg/go/doc/doc.go @@ -6,7 +6,6 @@ package doc import ( - "container/vector" "go/ast" "go/token" "regexp" @@ -21,7 +20,7 @@ type typeDoc struct { // if the type declaration hasn't been seen yet, decl is nil decl *ast.GenDecl // values, factory functions, and methods associated with the type - values *vector.Vector // list of *ast.GenDecl (consts and vars) + values []*ast.GenDecl // consts and vars factories map[string]*ast.FuncDecl methods map[string]*ast.FuncDecl } @@ -37,19 +36,17 @@ type typeDoc struct { type docReader struct { doc *ast.CommentGroup // package documentation, if any pkgName string - values *vector.Vector // list of *ast.GenDecl (consts and vars) + values []*ast.GenDecl // consts and vars types map[string]*typeDoc funcs map[string]*ast.FuncDecl - bugs *vector.Vector // list of *ast.CommentGroup + bugs []*ast.CommentGroup } func (doc *docReader) init(pkgName string) { doc.pkgName = pkgName - doc.values = new(vector.Vector) doc.types = make(map[string]*typeDoc) doc.funcs = make(map[string]*ast.FuncDecl) - doc.bugs = new(vector.Vector) } @@ -69,7 +66,7 @@ func (doc *docReader) addDoc(comments *ast.CommentGroup) { n2 := len(comments.List) list := make([]*ast.Comment, n1+1+n2) // + 1 for separator line copy(list, doc.doc.List) - list[n1] = &ast.Comment{token.Position{}, []byte("//")} // separator line + list[n1] = &ast.Comment{token.NoPos, []byte("//")} // separator line copy(list[n1+1:], comments.List) doc.doc = &ast.CommentGroup{list} } @@ -77,7 +74,7 @@ func (doc *docReader) addDoc(comments *ast.CommentGroup) { func (doc *docReader) addType(decl *ast.GenDecl) { spec := decl.Specs[0].(*ast.TypeSpec) - typ := doc.lookupTypeDoc(spec.Name.Name()) + typ := doc.lookupTypeDoc(spec.Name.Name) // typ should always be != nil since declared types // are always named - be conservative and check if typ != nil { @@ -96,7 +93,7 @@ func (doc *docReader) lookupTypeDoc(name string) *typeDoc { return tdoc } // type wasn't found - add one without declaration - tdoc := &typeDoc{nil, new(vector.Vector), make(map[string]*ast.FuncDecl), make(map[string]*ast.FuncDecl)} + tdoc := &typeDoc{nil, nil, make(map[string]*ast.FuncDecl), make(map[string]*ast.FuncDecl)} doc.types[name] = tdoc return tdoc } @@ -108,7 +105,7 @@ func baseTypeName(typ ast.Expr) string { // if the type is not exported, the effect to // a client is as if there were no type name if t.IsExported() { - return string(t.Name()) + return string(t.Name) } case *ast.StarExpr: return baseTypeName(t.X) @@ -130,7 +127,7 @@ func (doc *docReader) addValue(decl *ast.GenDecl) { name := "" switch { case v.Type != nil: - // a type is present; determine it's name + // a type is present; determine its name name = baseTypeName(v.Type) case decl.Tok == token.CONST: // no type is present but we have a constant declaration; @@ -156,16 +153,16 @@ func (doc *docReader) addValue(decl *ast.GenDecl) { // determine values list const threshold = 0.75 - values := doc.values + values := &doc.values if domName != "" && domFreq >= int(float(len(decl.Specs))*threshold) { // typed entries are sufficiently frequent typ := doc.lookupTypeDoc(domName) if typ != nil { - values = typ.values // associate with that type + values = &typ.values // associate with that type } } - values.Push(decl) + *values = append(*values, decl) } @@ -173,7 +170,7 @@ func (doc *docReader) addValue(decl *ast.GenDecl) { // at least one f with associated documentation is stored in table, if there // are multiple f's with the same name. func setFunc(table map[string]*ast.FuncDecl, f *ast.FuncDecl) { - name := f.Name.Name() + name := f.Name.Name if g, exists := table[name]; exists && g.Doc != nil { // a function with the same name has already been registered; // since it has documentation, assume f is simply another @@ -188,7 +185,7 @@ func setFunc(table map[string]*ast.FuncDecl, f *ast.FuncDecl) { func (doc *docReader) addFunc(fun *ast.FuncDecl) { - name := fun.Name.Name() + name := fun.Name.Name // determine if it should be associated with a type if fun.Recv != nil { @@ -252,7 +249,6 @@ func (doc *docReader) addDecl(decl ast.Decl) { doc.addValue(d) case token.TYPE: // types are handled individually - var noPos token.Position for _, spec := range d.Specs { // make a (fake) GenDecl node for this TypeSpec // (we need to do this here - as opposed to just @@ -265,7 +261,7 @@ func (doc *docReader) addDecl(decl ast.Decl) { // makeTypeDocs below). Simpler data structures, but // would lose GenDecl documentation if the TypeSpec // has documentation as well. - doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, noPos, []ast.Spec{spec}, noPos}) + doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, token.NoPos, []ast.Spec{spec}, token.NoPos}) // A new GenDecl node is created, no need to nil out d.Doc. } } @@ -277,14 +273,9 @@ func (doc *docReader) addDecl(decl ast.Decl) { func copyCommentList(list []*ast.Comment) []*ast.Comment { - copy := make([]*ast.Comment, len(list)) - for i, c := range list { - copy[i] = c - } - return copy + return append([]*ast.Comment(nil), list...) } - var ( bug_markers = regexp.MustCompile("^/[/*][ \t]*BUG\\(.*\\):[ \t]*") // BUG(uid): bug_content = regexp.MustCompile("[^ \n\r\t]+") // at least one non-whitespace char @@ -309,13 +300,13 @@ func (doc *docReader) addFile(src *ast.File) { // collect BUG(...) comments for _, c := range src.Comments { text := c.List[0].Text - if m := bug_markers.Execute(text); len(m) > 0 { + if m := bug_markers.FindIndex(text); m != nil { // found a BUG comment; maybe empty if btxt := text[m[1]:]; bug_content.Match(btxt) { // non-empty BUG comment; collect comment without BUG prefix list := copyCommentList(c.List) list[0].Text = text[m[1]:] - doc.bugs.Push(&ast.CommentGroup{list}) + doc.bugs = append(doc.bugs, &ast.CommentGroup{list}) } } } @@ -325,7 +316,7 @@ func (doc *docReader) addFile(src *ast.File) { func NewFileDoc(file *ast.File) *PackageDoc { var r docReader - r.init(file.Name.Name()) + r.init(file.Name.Name) r.addFile(file) return r.newDoc("", nil) } @@ -370,9 +361,9 @@ func declName(d *ast.GenDecl) string { switch v := d.Specs[0].(type) { case *ast.ValueSpec: - return v.Names[0].Name() + return v.Names[0].Name case *ast.TypeSpec: - return v.Name.Name() + return v.Name.Name } return "" @@ -390,11 +381,10 @@ func (p sortValueDoc) Less(i, j int) bool { } -func makeValueDocs(v *vector.Vector, tok token.Token) []*ValueDoc { - d := make([]*ValueDoc, v.Len()) // big enough in any case +func makeValueDocs(list []*ast.GenDecl, tok token.Token) []*ValueDoc { + d := make([]*ValueDoc, len(list)) // big enough in any case n := 0 - for i := range d { - decl := v.At(i).(*ast.GenDecl) + for i, decl := range list { if decl.Tok == tok { d[n] = &ValueDoc{CommentText(decl.Doc), decl, i} n++ @@ -434,7 +424,7 @@ func makeFuncDocs(m map[string]*ast.FuncDecl) []*FuncDoc { if f.Recv != nil { doc.Recv = f.Recv.List[0].Type } - doc.Name = f.Name.Name() + doc.Name = f.Name.Name doc.Decl = f d[i] = doc i++ @@ -467,7 +457,7 @@ func (p sortTypeDoc) Less(i, j int) bool { // sort by name // pull blocks (name = "") up to top // in original order - if ni, nj := p[i].Type.Name.Name(), p[j].Type.Name.Name(); ni != nj { + if ni, nj := p[i].Type.Name.Name, p[j].Type.Name.Name; ni != nj { return ni < nj } return p[i].order < p[j].order @@ -511,7 +501,7 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc { // file containing the explicit type declaration is missing or if // an unqualified type name was used after a "." import) // 1) move values - doc.values.AppendVector(old.values) + doc.values = append(doc.values, old.values...) // 2) move factory functions for name, f := range old.factories { doc.funcs[name] = f @@ -531,10 +521,10 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc { } -func makeBugDocs(v *vector.Vector) []string { - d := make([]string, v.Len()) - for i := 0; i < v.Len(); i++ { - d[i] = CommentText(v.At(i).(*ast.CommentGroup)) +func makeBugDocs(list []*ast.CommentGroup) []string { + d := make([]string, len(list)) + for i, g := range list { + d[i] = CommentText(g) } return d } @@ -587,12 +577,12 @@ func matchDecl(d *ast.GenDecl, f Filter) bool { switch v := d.(type) { case *ast.ValueSpec: for _, name := range v.Names { - if f(name.Name()) { + if f(name.Name) { return true } } case *ast.TypeSpec: - if f(v.Name.Name()) { + if f(v.Name.Name) { return true } } diff --git a/src/pkg/go/parser/Makefile b/src/pkg/go/parser/Makefile index d9b52a7d9..d301f41eb 100644 --- a/src/pkg/go/parser/Makefile +++ b/src/pkg/go/parser/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=go/parser GOFILES=\ diff --git a/src/pkg/go/parser/interface.go b/src/pkg/go/parser/interface.go index e1ddb37c3..84d699a67 100644 --- a/src/pkg/go/parser/interface.go +++ b/src/pkg/go/parser/interface.go @@ -57,72 +57,73 @@ func (p *parser) parseEOF() os.Error { // ParseExpr parses a Go expression and returns the corresponding -// AST node. The filename, src, and scope arguments have the same interpretation +// AST node. The fset, filename, and src arguments have the same interpretation // as for ParseFile. If there is an error, the result expression // may be nil or contain a partial AST. // -func ParseExpr(filename string, src interface{}, scope *ast.Scope) (ast.Expr, os.Error) { +func ParseExpr(fset *token.FileSet, filename string, src interface{}) (ast.Expr, os.Error) { data, err := readSource(filename, src) if err != nil { return nil, err } var p parser - p.init(filename, data, scope, 0) - return p.parseExpr(), p.parseEOF() + p.init(fset, filename, data, 0) + x := p.parseExpr() + if p.tok == token.SEMICOLON { + p.next() // consume automatically inserted semicolon, if any + } + return x, p.parseEOF() } // ParseStmtList parses a list of Go statements and returns the list -// of corresponding AST nodes. The filename, src, and scope arguments have the same +// of corresponding AST nodes. The fset, filename, and src arguments have the same // interpretation as for ParseFile. If there is an error, the node // list may be nil or contain partial ASTs. // -func ParseStmtList(filename string, src interface{}, scope *ast.Scope) ([]ast.Stmt, os.Error) { +func ParseStmtList(fset *token.FileSet, filename string, src interface{}) ([]ast.Stmt, os.Error) { data, err := readSource(filename, src) if err != nil { return nil, err } var p parser - p.init(filename, data, scope, 0) + p.init(fset, filename, data, 0) return p.parseStmtList(), p.parseEOF() } // ParseDeclList parses a list of Go declarations and returns the list -// of corresponding AST nodes. The filename, src, and scope arguments have the same +// of corresponding AST nodes. The fset, filename, and src arguments have the same // interpretation as for ParseFile. If there is an error, the node // list may be nil or contain partial ASTs. // -func ParseDeclList(filename string, src interface{}, scope *ast.Scope) ([]ast.Decl, os.Error) { +func ParseDeclList(fset *token.FileSet, filename string, src interface{}) ([]ast.Decl, os.Error) { data, err := readSource(filename, src) if err != nil { return nil, err } var p parser - p.init(filename, data, scope, 0) + p.init(fset, filename, data, 0) return p.parseDeclList(), p.parseEOF() } -// ParseFile parses a Go source file and returns a File node. +// ParseFile parses the source code of a single Go source file and returns +// the corresponding ast.File node. The source code may be provided via +// the filename of the source file, or via the src parameter. // -// If src != nil, ParseFile parses the file source from src. src may -// be provided in a variety of formats. At the moment the following types -// are supported: string, []byte, and io.Reader. In this case, filename is -// only used for source position information and error messages. +// If src != nil, ParseFile parses the source from src and the filename is +// only used when recording position information. The type of the argument +// for the src parameter must be string, []byte, or io.Reader. // // If src == nil, ParseFile parses the file specified by filename. // -// If scope != nil, it is the immediately surrounding scope for the file -// (the package scope) and it is used to lookup and declare identifiers. -// When parsing multiple files belonging to a package, the same scope should -// be provided to all files. -// // The mode parameter controls the amount of source text parsed and other -// optional parser functionality. +// optional parser functionality. Position information is recorded in the +// file set fset. // // If the source couldn't be read, the returned AST is nil and the error // indicates the specific failure. If the source was read but syntax @@ -130,57 +131,57 @@ func ParseDeclList(filename string, src interface{}, scope *ast.Scope) ([]ast.De // representing the fragments of erroneous source code). Multiple errors // are returned via a scanner.ErrorList which is sorted by file position. // -func ParseFile(filename string, src interface{}, scope *ast.Scope, mode uint) (*ast.File, os.Error) { +func ParseFile(fset *token.FileSet, filename string, src interface{}, mode uint) (*ast.File, os.Error) { data, err := readSource(filename, src) if err != nil { return nil, err } var p parser - p.init(filename, data, scope, mode) + p.init(fset, filename, data, mode) return p.parseFile(), p.GetError(scanner.NoMultiples) // parseFile() reads to EOF } // ParseFiles calls ParseFile for each file in the filenames list and returns // a map of package name -> package AST with all the packages found. The mode -// bits are passed to ParseFile unchanged. +// bits are passed to ParseFile unchanged. Position information is recorded +// in the file set fset. // // Files with parse errors are ignored. In this case the map of packages may -// be incomplete (missing packages and/or incomplete packages) and the last +// be incomplete (missing packages and/or incomplete packages) and the first // error encountered is returned. // -func ParseFiles(filenames []string, scope *ast.Scope, mode uint) (map[string]*ast.Package, os.Error) { - pkgs := make(map[string]*ast.Package) - var err os.Error +func ParseFiles(fset *token.FileSet, filenames []string, mode uint) (pkgs map[string]*ast.Package, first os.Error) { + pkgs = make(map[string]*ast.Package) for _, filename := range filenames { - var src *ast.File - src, err = ParseFile(filename, nil, scope, mode) - if err == nil { - name := src.Name.Name() + if src, err := ParseFile(fset, filename, nil, mode); err == nil { + name := src.Name.Name pkg, found := pkgs[name] if !found { - pkg = &ast.Package{name, scope, make(map[string]*ast.File)} + pkg = &ast.Package{name, nil, make(map[string]*ast.File)} pkgs[name] = pkg } pkg.Files[filename] = src + } else if first == nil { + first = err } } - - return pkgs, err + return } // ParseDir calls ParseFile for the files in the directory specified by path and // returns a map of package name -> package AST with all the packages found. If // filter != nil, only the files with os.FileInfo entries passing through the filter -// are considered. The mode bits are passed to ParseFile unchanged. +// are considered. The mode bits are passed to ParseFile unchanged. Position +// information is recorded in the file set fset. // // If the directory couldn't be read, a nil map and the respective error are -// returned. If a parse error occured, a non-nil but incomplete map and the +// returned. If a parse error occurred, a non-nil but incomplete map and the // error are returned. // -func ParseDir(path string, filter func(*os.FileInfo) bool, mode uint) (map[string]*ast.Package, os.Error) { +func ParseDir(fset *token.FileSet, path string, filter func(*os.FileInfo) bool, mode uint) (map[string]*ast.Package, os.Error) { fd, err := os.Open(path, os.O_RDONLY, 0) if err != nil { return nil, err @@ -203,6 +204,5 @@ func ParseDir(path string, filter func(*os.FileInfo) bool, mode uint) (map[strin } filenames = filenames[0:n] - var scope *ast.Scope = nil // for now tracking of declarations is disabled - return ParseFiles(filenames, scope, mode) + return ParseFiles(fset, filenames, mode) } diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go index c1914005a..3b2fe4577 100644 --- a/src/pkg/go/parser/parser.go +++ b/src/pkg/go/parser/parser.go @@ -10,7 +10,6 @@ package parser import ( - "container/vector" "fmt" "go/ast" "go/scanner" @@ -36,6 +35,7 @@ const ( // The parser structure holds the parser's internal state. type parser struct { + file *token.File scanner.ErrorVector scanner scanner.Scanner @@ -45,23 +45,17 @@ type parser struct { indent uint // indentation used for tracing output // Comments - comments vector.Vector // list of *CommentGroup + comments []*ast.CommentGroup leadComment *ast.CommentGroup // the last lead comment lineComment *ast.CommentGroup // the last line comment // Next token - pos token.Position // token position - tok token.Token // one token look-ahead - lit []byte // token literal + pos token.Pos // token position + tok token.Token // one token look-ahead + lit []byte // token literal // Non-syntactic parser control exprLev int // < 0: in control clause, >= 0: in expression - - // Scopes - checkDecl bool // if set, check declarations - pkgScope *ast.Scope - fileScope *ast.Scope - funcScope *ast.Scope } @@ -75,16 +69,10 @@ func scannerMode(mode uint) uint { } -func (p *parser) init(filename string, src []byte, scope *ast.Scope, mode uint) { - p.scanner.Init(filename, src, p, scannerMode(mode)) +func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode uint) { + p.file = p.scanner.Init(fset, filename, src, p, scannerMode(mode)) p.mode = mode p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently) - if scope != nil { - p.checkDecl = true - } else { - scope = ast.NewScope(nil) // provide a dummy scope - } - p.pkgScope = scope p.next() } @@ -96,13 +84,14 @@ func (p *parser) printTrace(a ...interface{}) { const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " + ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " const n = uint(len(dots)) - fmt.Printf("%5d:%3d: ", p.pos.Line, p.pos.Column) + pos := p.file.Position(p.pos) + fmt.Printf("%5d:%3d: ", pos.Line, pos.Column) i := 2 * p.indent for ; i > n; i -= n { fmt.Print(dots) } fmt.Print(dots[0:i]) - fmt.Println(a) + fmt.Println(a...) } @@ -124,9 +113,9 @@ func un(p *parser) { func (p *parser) next0() { // Because of one-token look-ahead, print the previous token // when tracing as it provides a more readable output. The - // very first token (p.pos.Line == 0) is not initialized (it - // is token.ILLEGAL), so don't print it . - if p.trace && p.pos.Line > 0 { + // very first token (!p.pos.IsValid()) is not initialized + // (it is token.ILLEGAL), so don't print it . + if p.trace && p.pos.IsValid() { s := p.tok.String() switch { case p.tok.IsLiteral(): @@ -145,7 +134,7 @@ func (p *parser) next0() { func (p *parser) consumeComment() (comment *ast.Comment, endline int) { // /*-style comments may end on a different line than where they start. // Scan the comment for '\n' chars and adjust endline accordingly. - endline = p.pos.Line + endline = p.file.Line(p.pos) if p.lit[1] == '*' { for _, b := range p.lit { if b == '\n' { @@ -167,23 +156,17 @@ func (p *parser) consumeComment() (comment *ast.Comment, endline int) { // token terminates a comment group. // func (p *parser) consumeCommentGroup() (comments *ast.CommentGroup, endline int) { - var list vector.Vector - endline = p.pos.Line - for p.tok == token.COMMENT && endline+1 >= p.pos.Line { + var list []*ast.Comment + endline = p.file.Line(p.pos) + for p.tok == token.COMMENT && endline+1 >= p.file.Line(p.pos) { var comment *ast.Comment comment, endline = p.consumeComment() - list.Push(comment) - } - - // convert list - group := make([]*ast.Comment, len(list)) - for i, x := range list { - group[i] = x.(*ast.Comment) + list = append(list, comment) } // add comment group to the comments list - comments = &ast.CommentGroup{group} - p.comments.Push(comments) + comments = &ast.CommentGroup{list} + p.comments = append(p.comments, comments) return } @@ -207,18 +190,18 @@ func (p *parser) consumeCommentGroup() (comments *ast.CommentGroup, endline int) func (p *parser) next() { p.leadComment = nil p.lineComment = nil - line := p.pos.Line // current line + line := p.file.Line(p.pos) // current line p.next0() if p.tok == token.COMMENT { var comment *ast.CommentGroup var endline int - if p.pos.Line == line { + if p.file.Line(p.pos) == line { // The comment is on same line as previous token; it // cannot be a lead comment but may be a line comment. comment, endline = p.consumeCommentGroup() - if p.pos.Line != endline { + if p.file.Line(p.pos) != endline { // The next token is on a different line, thus // the last comment group is a line comment. p.lineComment = comment @@ -231,7 +214,7 @@ func (p *parser) next() { comment, endline = p.consumeCommentGroup() } - if endline+1 == p.pos.Line { + if endline+1 == p.file.Line(p.pos) { // The next token is following on the line immediately after the // comment group, thus the last comment group is a lead comment. p.leadComment = comment @@ -240,26 +223,35 @@ func (p *parser) next() { } -func (p *parser) errorExpected(pos token.Position, msg string) { +func (p *parser) error(pos token.Pos, msg string) { + p.Error(p.file.Position(pos), msg) +} + + +func (p *parser) errorExpected(pos token.Pos, msg string) { msg = "expected " + msg - if pos.Offset == p.pos.Offset { + 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 += " " + string(p.lit) + if p.tok == token.SEMICOLON && p.lit[0] == '\n' { + msg += ", found newline" + } else { + msg += ", found '" + p.tok.String() + "'" + if p.tok.IsLiteral() { + msg += " " + string(p.lit) + } } } - p.Error(pos, msg) + p.error(pos, msg) } -func (p *parser) expect(tok token.Token) token.Position { +func (p *parser) expect(tok token.Token) token.Pos { pos := p.pos if p.tok != tok { p.errorExpected(pos, "'"+tok.String()+"'") } - p.next() // make progress in any case + p.next() // make progress return pos } @@ -272,152 +264,51 @@ func (p *parser) expectSemi() { // ---------------------------------------------------------------------------- -// Scope support - -func (p *parser) openScope() *ast.Scope { - p.funcScope = ast.NewScope(p.funcScope) - return p.funcScope -} - - -func (p *parser) closeScope() { p.funcScope = p.funcScope.Outer } - +// Identifiers -func (p *parser) parseIdent(kind ast.ObjKind) *ast.Ident { - obj := ast.NewObj(kind, p.pos, "_") +func (p *parser) parseIdent() *ast.Ident { + pos := p.pos + name := "_" if p.tok == token.IDENT { - obj.Name = string(p.lit) + name = string(p.lit) p.next() } else { p.expect(token.IDENT) // use expect() error handling } - return &ast.Ident{obj.Pos, obj} + return &ast.Ident{pos, name, nil} } -func (p *parser) parseIdentList(kind ast.ObjKind) []*ast.Ident { +func (p *parser) parseIdentList() (list []*ast.Ident) { if p.trace { defer un(trace(p, "IdentList")) } - var list vector.Vector - list.Push(p.parseIdent(kind)) + list = append(list, p.parseIdent()) for p.tok == token.COMMA { p.next() - list.Push(p.parseIdent(kind)) + list = append(list, p.parseIdent()) } - // convert vector - idents := make([]*ast.Ident, len(list)) - for i, x := range list { - idents[i] = x.(*ast.Ident) - } - - return idents -} - - -func (p *parser) declIdent(scope *ast.Scope, id *ast.Ident) { - decl := scope.Declare(id.Obj) - if p.checkDecl && decl != id.Obj { - if decl.Kind == ast.Err { - // declared object is a forward declaration - update it - *decl = *id.Obj - id.Obj = decl - return - } - p.Error(id.Pos(), "'"+id.Name()+"' declared already at "+decl.Pos.String()) - } -} - - -func (p *parser) declIdentList(scope *ast.Scope, list []*ast.Ident) { - for _, id := range list { - p.declIdent(scope, id) - } -} - - -func (p *parser) declFieldList(scope *ast.Scope, list []*ast.Field) { - for _, f := range list { - p.declIdentList(scope, f.Names) - } -} - - -func (p *parser) findIdent() *ast.Ident { - pos := p.pos - name := "_" - var obj *ast.Object - if p.tok == token.IDENT { - name = string(p.lit) - obj = p.funcScope.Lookup(name) - p.next() - } else { - p.expect(token.IDENT) // use expect() error handling - } - if obj == nil { - // No declaration found: either we are outside any function - // (p.funcScope == nil) or the identifier is not declared - // in any function. Try the file and package scope. - obj = p.fileScope.Lookup(name) // file scope is nested in package scope - if obj == nil { - // No declaration found anywhere: track as - // unresolved identifier in the package scope. - obj = ast.NewObj(ast.Err, pos, name) - p.pkgScope.Declare(obj) - } - } - return &ast.Ident{pos, obj} -} - - -func (p *parser) findIdentInScope(scope *ast.Scope) *ast.Ident { - pos := p.pos - name := "_" - var obj *ast.Object - if p.tok == token.IDENT { - name = string(p.lit) - obj = scope.Lookup(name) - p.next() - } else { - p.expect(token.IDENT) // use expect() error handling - } - if obj == nil { - // TODO(gri) At the moment we always arrive here because - // we don't track the lookup scope (and sometimes - // we can't). Just create a useable ident for now. - obj = ast.NewObj(ast.Err, pos, name) - } - return &ast.Ident{pos, obj} + return } // ---------------------------------------------------------------------------- // Common productions -func makeExprList(list *vector.Vector) []ast.Expr { - exprs := make([]ast.Expr, len(*list)) - for i, x := range *list { - exprs[i] = x.(ast.Expr) - } - return exprs -} - - -func (p *parser) parseExprList() []ast.Expr { +func (p *parser) parseExprList() (list []ast.Expr) { if p.trace { defer un(trace(p, "ExpressionList")) } - var list vector.Vector - list.Push(p.parseExpr()) + list = append(list, p.parseExpr()) for p.tok == token.COMMA { p.next() - list.Push(p.parseExpr()) + list = append(list, p.parseExpr()) } - return makeExprList(&list) + return } @@ -432,9 +323,10 @@ func (p *parser) parseType() ast.Expr { typ := p.tryType() if typ == nil { - p.errorExpected(p.pos, "type") + pos := p.pos + p.errorExpected(pos, "type") p.next() // make progress - return &ast.BadExpr{p.pos} + return &ast.BadExpr{pos, p.pos} } return typ @@ -446,11 +338,11 @@ func (p *parser) parseQualifiedIdent() ast.Expr { defer un(trace(p, "QualifiedIdent")) } - var x ast.Expr = p.findIdent() + var x ast.Expr = p.parseIdent() if p.tok == token.PERIOD { // first identifier is a package identifier p.next() - sel := p.findIdentInScope(nil) + sel := p.parseIdent() x = &ast.SelectorExpr{x, sel} } return x @@ -486,14 +378,14 @@ func (p *parser) parseArrayType(ellipsisOk bool) ast.Expr { } -func (p *parser) makeIdentList(list *vector.Vector) []*ast.Ident { - idents := make([]*ast.Ident, len(*list)) - for i, x := range *list { +func (p *parser) makeIdentList(list []ast.Expr) []*ast.Ident { + idents := make([]*ast.Ident, len(list)) + for i, x := range list { ident, isIdent := x.(*ast.Ident) if !isIdent { pos := x.(ast.Expr).Pos() p.errorExpected(pos, "identifier") - ident = &ast.Ident{pos, ast.NewObj(ast.Err, pos, "_")} + ident = &ast.Ident{pos, "_", nil} } idents[i] = ident } @@ -508,19 +400,8 @@ func (p *parser) parseFieldDecl() *ast.Field { doc := p.leadComment - // a list of identifiers looks like a list of type names - var list vector.Vector - for { - // TODO(gri): do not allow ()'s here - list.Push(p.parseType()) - if p.tok != token.COMMA { - break - } - p.next() - } - - // if we had a list of identifiers, it must be followed by a type - typ := p.tryType() + // fields + list, typ := p.parseVarList(false) // optional tag var tag *ast.BasicLit @@ -533,15 +414,14 @@ func (p *parser) parseFieldDecl() *ast.Field { var idents []*ast.Ident if typ != nil { // IdentifierList Type - idents = p.makeIdentList(&list) + idents = p.makeIdentList(list) } else { - // Type (anonymous field) - if len(list) == 1 { - // TODO(gri): check that this looks like a type - typ = list.At(0).(ast.Expr) - } else { - p.errorExpected(p.pos, "anonymous field") - typ = &ast.BadExpr{p.pos} + // ["*"] TypeName (AnonymousField) + typ = list[0] // we always have at least one element + if n := len(list); n > 1 || !isTypeName(deref(typ)) { + pos := typ.Pos() + p.errorExpected(pos, "anonymous field") + typ = &ast.BadExpr{pos, list[n-1].End()} } } @@ -558,22 +438,16 @@ func (p *parser) parseStructType() *ast.StructType { pos := p.expect(token.STRUCT) lbrace := p.expect(token.LBRACE) - var list vector.Vector - for p.tok == token.IDENT || p.tok == token.MUL { - list.Push(p.parseFieldDecl()) + var list []*ast.Field + for p.tok == token.IDENT || p.tok == token.MUL || p.tok == token.LPAREN { + // a field declaration cannot start with a '(' but we accept + // it here for more robust parsing and better error messages + // (parseFieldDecl will check and complain if necessary) + list = append(list, p.parseFieldDecl()) } rbrace := p.expect(token.RBRACE) - // convert vector - fields := make([]*ast.Field, len(list)) - for i, x := range list { - fields[i] = x.(*ast.Field) - } - - // TODO(gri) The struct scope shouldn't get lost. - p.declFieldList(ast.NewScope(nil), fields) - - return &ast.StructType{pos, &ast.FieldList{lbrace, fields, rbrace}, false} + return &ast.StructType{pos, &ast.FieldList{lbrace, list, rbrace}, false} } @@ -589,13 +463,17 @@ func (p *parser) parsePointerType() *ast.StarExpr { } -func (p *parser) tryParameterType(ellipsisOk bool) ast.Expr { - if ellipsisOk && p.tok == token.ELLIPSIS { +func (p *parser) tryVarType(isParam bool) ast.Expr { + if isParam && p.tok == token.ELLIPSIS { pos := p.pos p.next() - typ := p.tryType() + typ := p.tryType() // don't use parseType so we can provide better error message + if typ == nil { + p.error(pos, "'...' parameter is missing type") + typ = &ast.BadExpr{pos, p.pos} + } if p.tok != token.RPAREN { - p.Error(pos, "can use '...' for last parameter only") + p.error(pos, "can use '...' with last parameter type only") } return &ast.Ellipsis{pos, typ} } @@ -603,27 +481,30 @@ func (p *parser) tryParameterType(ellipsisOk bool) ast.Expr { } -func (p *parser) parseParameterType(ellipsisOk bool) ast.Expr { - typ := p.tryParameterType(ellipsisOk) +func (p *parser) parseVarType(isParam bool) ast.Expr { + typ := p.tryVarType(isParam) if typ == nil { - p.errorExpected(p.pos, "type") + pos := p.pos + p.errorExpected(pos, "type") p.next() // make progress - typ = &ast.BadExpr{p.pos} + typ = &ast.BadExpr{pos, p.pos} } return typ } -func (p *parser) parseParameterDecl(ellipsisOk bool) (*vector.Vector, ast.Expr) { +func (p *parser) parseVarList(isParam bool) (list []ast.Expr, typ ast.Expr) { if p.trace { - defer un(trace(p, "ParameterDecl")) + defer un(trace(p, "VarList")) } // a list of identifiers looks like a list of type names - var list vector.Vector for { - // TODO(gri): do not allow ()'s here - list.Push(p.parseParameterType(ellipsisOk)) + // parseVarType accepts any type (including parenthesized ones) + // even though the syntax does not permit them here: we + // accept them all for more robust parsing and complain + // afterwards + list = append(list, p.parseVarType(isParam)) if p.tok != token.COMMA { break } @@ -631,31 +512,30 @@ func (p *parser) parseParameterDecl(ellipsisOk bool) (*vector.Vector, ast.Expr) } // if we had a list of identifiers, it must be followed by a type - typ := p.tryParameterType(ellipsisOk) + typ = p.tryVarType(isParam) - return &list, typ + return } -func (p *parser) parseParameterList(ellipsisOk bool) []*ast.Field { +func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) { if p.trace { defer un(trace(p, "ParameterList")) } - list, typ := p.parseParameterDecl(ellipsisOk) + list, typ := p.parseVarList(ellipsisOk) if typ != nil { // IdentifierList Type idents := p.makeIdentList(list) - list.Resize(0, 0) - list.Push(&ast.Field{nil, idents, typ, nil, nil}) + params = append(params, &ast.Field{nil, idents, typ, nil, nil}) if p.tok == token.COMMA { p.next() } for p.tok != token.RPAREN && p.tok != token.EOF { - idents := p.parseIdentList(ast.Var) - typ := p.parseParameterType(ellipsisOk) - list.Push(&ast.Field{nil, idents, typ, nil, nil}) + idents := p.parseIdentList() + typ := p.parseVarType(ellipsisOk) + params = append(params, &ast.Field{nil, idents, typ, nil, nil}) if p.tok != token.COMMA { break } @@ -664,23 +544,17 @@ func (p *parser) parseParameterList(ellipsisOk bool) []*ast.Field { } else { // Type { "," Type } (anonymous parameters) - // convert list of types into list of *Param - for i, x := range *list { - list.Set(i, &ast.Field{Type: x.(ast.Expr)}) + params = make([]*ast.Field, len(list)) + for i, x := range list { + params[i] = &ast.Field{Type: x} } } - // convert list - params := make([]*ast.Field, len(*list)) - for i, x := range *list { - params[i] = x.(*ast.Field) - } - - return params + return } -func (p *parser) parseParameters(scope *ast.Scope, ellipsisOk bool) *ast.FieldList { +func (p *parser) parseParameters(ellipsisOk bool) *ast.FieldList { if p.trace { defer un(trace(p, "Parameters")) } @@ -689,7 +563,6 @@ func (p *parser) parseParameters(scope *ast.Scope, ellipsisOk bool) *ast.FieldLi lparen := p.expect(token.LPAREN) if p.tok != token.RPAREN { params = p.parseParameterList(ellipsisOk) - p.declFieldList(scope, params) } rparen := p.expect(token.RPAREN) @@ -697,13 +570,13 @@ func (p *parser) parseParameters(scope *ast.Scope, ellipsisOk bool) *ast.FieldLi } -func (p *parser) parseResult(scope *ast.Scope) *ast.FieldList { +func (p *parser) parseResult() *ast.FieldList { if p.trace { defer un(trace(p, "Result")) } if p.tok == token.LPAREN { - return p.parseParameters(scope, false) + return p.parseParameters(false) } typ := p.tryType() @@ -717,28 +590,27 @@ func (p *parser) parseResult(scope *ast.Scope) *ast.FieldList { } -func (p *parser) parseSignature(scope *ast.Scope) (params, results *ast.FieldList) { +func (p *parser) parseSignature() (params, results *ast.FieldList) { if p.trace { defer un(trace(p, "Signature")) } - params = p.parseParameters(scope, true) - results = p.parseResult(scope) + params = p.parseParameters(true) + results = p.parseResult() return } -func (p *parser) parseFuncType() (*ast.Scope, *ast.FuncType) { +func (p *parser) parseFuncType() *ast.FuncType { if p.trace { defer un(trace(p, "FuncType")) } pos := p.expect(token.FUNC) - scope := ast.NewScope(p.funcScope) - params, results := p.parseSignature(scope) + params, results := p.parseSignature() - return scope, &ast.FuncType{pos, params, results} + return &ast.FuncType{pos, params, results} } @@ -754,8 +626,8 @@ func (p *parser) parseMethodSpec() *ast.Field { if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN { // method idents = []*ast.Ident{ident} - params, results := p.parseSignature(ast.NewScope(p.funcScope)) - typ = &ast.FuncType{noPos, params, results} + params, results := p.parseSignature() + typ = &ast.FuncType{token.NoPos, params, results} } else { // embedded interface typ = x @@ -773,22 +645,13 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType { pos := p.expect(token.INTERFACE) lbrace := p.expect(token.LBRACE) - var list vector.Vector + var list []*ast.Field for p.tok == token.IDENT { - list.Push(p.parseMethodSpec()) + list = append(list, p.parseMethodSpec()) } rbrace := p.expect(token.RBRACE) - // convert vector - methods := make([]*ast.Field, len(list)) - for i, x := range list { - methods[i] = x.(*ast.Field) - } - - // TODO(gri) The interface scope shouldn't get lost. - p.declFieldList(ast.NewScope(nil), methods) - - return &ast.InterfaceType{pos, &ast.FieldList{lbrace, methods, rbrace}, false} + return &ast.InterfaceType{pos, &ast.FieldList{lbrace, list, rbrace}, false} } @@ -842,8 +705,7 @@ func (p *parser) tryRawType(ellipsisOk bool) ast.Expr { case token.MUL: return p.parsePointerType() case token.FUNC: - _, typ := p.parseFuncType() - return typ + return p.parseFuncType() case token.INTERFACE: return p.parseInterfaceType() case token.MAP: @@ -869,43 +731,28 @@ func (p *parser) tryType() ast.Expr { return p.tryRawType(false) } // ---------------------------------------------------------------------------- // Blocks -func makeStmtList(list *vector.Vector) []ast.Stmt { - stats := make([]ast.Stmt, len(*list)) - for i, x := range *list { - stats[i] = x.(ast.Stmt) - } - return stats -} - - -func (p *parser) parseStmtList() []ast.Stmt { +func (p *parser) parseStmtList() (list []ast.Stmt) { if p.trace { defer un(trace(p, "StatementList")) } - var list vector.Vector for p.tok != token.CASE && p.tok != token.DEFAULT && p.tok != token.RBRACE && p.tok != token.EOF { - list.Push(p.parseStmt()) + list = append(list, p.parseStmt()) } - return makeStmtList(&list) + return } -func (p *parser) parseBody(scope *ast.Scope) *ast.BlockStmt { +func (p *parser) parseBody() *ast.BlockStmt { if p.trace { defer un(trace(p, "Body")) } - savedScope := p.funcScope - p.funcScope = scope - lbrace := p.expect(token.LBRACE) list := p.parseStmtList() rbrace := p.expect(token.RBRACE) - p.funcScope = savedScope - return &ast.BlockStmt{lbrace, list, rbrace} } @@ -915,9 +762,6 @@ func (p *parser) parseBlockStmt() *ast.BlockStmt { defer un(trace(p, "BlockStmt")) } - p.openScope() - defer p.closeScope() - lbrace := p.expect(token.LBRACE) list := p.parseStmtList() rbrace := p.expect(token.RBRACE) @@ -934,14 +778,14 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr { defer un(trace(p, "FuncTypeOrLit")) } - scope, typ := p.parseFuncType() + typ := p.parseFuncType() if p.tok != token.LBRACE { // function type only return typ } p.exprLev++ - body := p.parseBody(scope) + body := p.parseBody() p.exprLev-- return &ast.FuncLit{typ, body} @@ -958,7 +802,7 @@ func (p *parser) parseOperand() ast.Expr { switch p.tok { case token.IDENT: - return p.findIdent() + return p.parseIdent() case token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING: x := &ast.BasicLit{p.pos, p.tok, p.lit} @@ -984,9 +828,10 @@ func (p *parser) parseOperand() ast.Expr { } } - p.errorExpected(p.pos, "operand") + pos := p.pos + p.errorExpected(pos, "operand") p.next() // make progress - return &ast.BadExpr{p.pos} + return &ast.BadExpr{pos, p.pos} } @@ -998,7 +843,7 @@ func (p *parser) parseSelectorOrTypeAssertion(x ast.Expr) ast.Expr { p.expect(token.PERIOD) if p.tok == token.IDENT { // selector - sel := p.findIdentInScope(nil) + sel := p.parseIdent() return &ast.SelectorExpr{x, sel} } @@ -1022,23 +867,27 @@ func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr { defer un(trace(p, "IndexOrSlice")) } - p.expect(token.LBRACK) + lbrack := p.expect(token.LBRACK) p.exprLev++ - index := p.parseExpr() + var low, high ast.Expr + isSlice := false + if p.tok != token.COLON { + low = p.parseExpr() + } if p.tok == token.COLON { + isSlice = true p.next() - var end ast.Expr if p.tok != token.RBRACK { - end = p.parseExpr() + high = p.parseExpr() } - x = &ast.SliceExpr{x, index, end} - } else { - x = &ast.IndexExpr{x, index} } p.exprLev-- - p.expect(token.RBRACK) + rbrack := p.expect(token.RBRACK) - return x + if isSlice { + return &ast.SliceExpr{x, lbrack, low, high, rbrack} + } + return &ast.IndexExpr{x, lbrack, low, rbrack} } @@ -1049,9 +898,14 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr { lparen := p.expect(token.LPAREN) p.exprLev++ - var list vector.Vector - for p.tok != token.RPAREN && p.tok != token.EOF { - list.Push(p.parseExpr()) + var list []ast.Expr + var ellipsis token.Pos + for p.tok != token.RPAREN && p.tok != token.EOF && !ellipsis.IsValid() { + list = append(list, p.parseExpr()) + if p.tok == token.ELLIPSIS { + ellipsis = p.pos + p.next() + } if p.tok != token.COMMA { break } @@ -1060,47 +914,49 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr { p.exprLev-- rparen := p.expect(token.RPAREN) - return &ast.CallExpr{fun, lparen, makeExprList(&list), rparen} + return &ast.CallExpr{fun, lparen, list, ellipsis, rparen} } -func (p *parser) parseElement() ast.Expr { +func (p *parser) parseElement(keyOk bool) ast.Expr { if p.trace { defer un(trace(p, "Element")) } + if p.tok == token.LBRACE { + return p.parseLiteralValue(nil) + } + x := p.parseExpr() - if p.tok == token.COLON { + if keyOk && p.tok == token.COLON { colon := p.pos p.next() - x = &ast.KeyValueExpr{x, colon, p.parseExpr()} + x = &ast.KeyValueExpr{x, colon, p.parseElement(false)} } - return x } -func (p *parser) parseElementList() []ast.Expr { +func (p *parser) parseElementList() (list []ast.Expr) { if p.trace { defer un(trace(p, "ElementList")) } - var list vector.Vector for p.tok != token.RBRACE && p.tok != token.EOF { - list.Push(p.parseElement()) + list = append(list, p.parseElement(true)) if p.tok != token.COMMA { break } p.next() } - return makeExprList(&list) + return } -func (p *parser) parseCompositeLit(typ ast.Expr) ast.Expr { +func (p *parser) parseLiteralValue(typ ast.Expr) ast.Expr { if p.trace { - defer un(trace(p, "CompositeLit")) + defer un(trace(p, "LiteralValue")) } lbrace := p.expect(token.LBRACE) @@ -1115,21 +971,16 @@ func (p *parser) parseCompositeLit(typ ast.Expr) ast.Expr { } -// TODO(gri): Consider different approach to checking syntax after parsing: -// Provide a arguments (set of flags) to parsing functions -// restricting what they are supposed to accept depending -// on context. - // checkExpr checks that x is an expression (and not a type). func (p *parser) checkExpr(x ast.Expr) ast.Expr { - // TODO(gri): should provide predicate in AST nodes - switch t := x.(type) { + switch t := unparen(x).(type) { case *ast.BadExpr: case *ast.Ident: case *ast.BasicLit: case *ast.FuncLit: case *ast.CompositeLit: case *ast.ParenExpr: + panic("unreachable") case *ast.SelectorExpr: case *ast.IndexExpr: case *ast.SliceExpr: @@ -1137,7 +988,7 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr { if t.Type == nil { // the form X.(type) is only allowed in type switch expressions p.errorExpected(x.Pos(), "expression") - x = &ast.BadExpr{x.Pos()} + x = &ast.BadExpr{x.Pos(), x.End()} } case *ast.CallExpr: case *ast.StarExpr: @@ -1145,28 +996,26 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr { if t.Op == token.RANGE { // the range operator is only allowed at the top of a for statement p.errorExpected(x.Pos(), "expression") - x = &ast.BadExpr{x.Pos()} + x = &ast.BadExpr{x.Pos(), x.End()} } case *ast.BinaryExpr: default: // all other nodes are not proper expressions p.errorExpected(x.Pos(), "expression") - x = &ast.BadExpr{x.Pos()} + x = &ast.BadExpr{x.Pos(), x.End()} } return x } -// isTypeName returns true iff x is type name. +// isTypeName returns true iff x is a (qualified) TypeName. func isTypeName(x ast.Expr) bool { - // TODO(gri): should provide predicate in AST nodes switch t := x.(type) { case *ast.BadExpr: case *ast.Ident: - case *ast.ParenExpr: - return isTypeName(t.X) // TODO(gri): should (TypeName) be illegal? case *ast.SelectorExpr: - return isTypeName(t.X) + _, isIdent := t.X.(*ast.Ident) + return isIdent default: return false // all other nodes are not type names } @@ -1174,16 +1023,14 @@ func isTypeName(x ast.Expr) bool { } -// isCompositeLitType returns true iff x is a legal composite literal type. -func isCompositeLitType(x ast.Expr) bool { - // TODO(gri): should provide predicate in AST nodes +// isLiteralType returns true iff x is a legal composite literal type. +func isLiteralType(x ast.Expr) bool { switch t := x.(type) { case *ast.BadExpr: case *ast.Ident: - case *ast.ParenExpr: - return isCompositeLitType(t.X) case *ast.SelectorExpr: - return isTypeName(t.X) + _, isIdent := t.X.(*ast.Ident) + return isIdent case *ast.ArrayType: case *ast.StructType: case *ast.MapType: @@ -1194,22 +1041,41 @@ func isCompositeLitType(x ast.Expr) bool { } +// If x is of the form *T, deref returns T, otherwise it returns x. +func deref(x ast.Expr) ast.Expr { + if p, isPtr := x.(*ast.StarExpr); isPtr { + x = p.X + } + return x +} + + +// If x is of the form (T), unparen returns unparen(T), otherwise it returns x. +func unparen(x ast.Expr) ast.Expr { + if p, isParen := x.(*ast.ParenExpr); isParen { + x = unparen(p.X) + } + return x +} + + // checkExprOrType checks that x is an expression or a type // (and not a raw type such as [...]T). // func (p *parser) checkExprOrType(x ast.Expr) ast.Expr { - // TODO(gri): should provide predicate in AST nodes - switch t := x.(type) { + switch t := unparen(x).(type) { + case *ast.ParenExpr: + panic("unreachable") case *ast.UnaryExpr: if t.Op == token.RANGE { // the range operator is only allowed at the top of a for statement p.errorExpected(x.Pos(), "expression") - x = &ast.BadExpr{x.Pos()} + x = &ast.BadExpr{x.Pos(), x.End()} } case *ast.ArrayType: if len, isEllipsis := t.Len.(*ast.Ellipsis); isEllipsis { - p.Error(len.Pos(), "expected array length, found '...'") - x = &ast.BadExpr{x.Pos()} + p.error(len.Pos(), "expected array length, found '...'") + x = &ast.BadExpr{x.Pos(), x.End()} } } @@ -1234,8 +1100,8 @@ L: case token.LPAREN: x = p.parseCallOrConversion(p.checkExprOrType(x)) case token.LBRACE: - if isCompositeLitType(x) && (p.exprLev >= 0 || !isTypeName(x)) { - x = p.parseCompositeLit(x) + if isLiteralType(x) && (p.exprLev >= 0 || !isTypeName(x)) { + x = p.parseLiteralValue(x) } else { break L } @@ -1328,14 +1194,15 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt { switch p.tok { case token.COLON: // labeled statement + colon := p.pos p.next() if labelOk && len(x) == 1 { if label, isIdent := x[0].(*ast.Ident); isIdent { - return &ast.LabeledStmt{label, p.parseStmt()} + return &ast.LabeledStmt{label, colon, p.parseStmt()} } } - p.Error(x[0].Pos(), "illegal label declaration") - return &ast.BadStmt{x[0].Pos()} + p.error(x[0].Pos(), "illegal label declaration") + return &ast.BadStmt{x[0].Pos(), colon + 1} case token.DEFINE, token.ASSIGN, token.ADD_ASSIGN, @@ -1350,13 +1217,13 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt { } if len(x) > 1 { - p.Error(x[0].Pos(), "only one expression allowed") + p.error(x[0].Pos(), "only one expression allowed") // continue with first expression } if p.tok == token.INC || p.tok == token.DEC { // increment or decrement - s := &ast.IncDecStmt{x[0], p.tok} + s := &ast.IncDecStmt{x[0], p.pos, p.tok} p.next() // consume "++" or "--" return s } @@ -1385,7 +1252,7 @@ func (p *parser) parseGoStmt() ast.Stmt { call := p.parseCallExpr() p.expectSemi() if call == nil { - return &ast.BadStmt{pos} + return &ast.BadStmt{pos, pos + 2} // len("go") } return &ast.GoStmt{pos, call} @@ -1401,7 +1268,7 @@ func (p *parser) parseDeferStmt() ast.Stmt { call := p.parseCallExpr() p.expectSemi() if call == nil { - return &ast.BadStmt{pos} + return &ast.BadStmt{pos, pos + 5} // len("defer") } return &ast.DeferStmt{pos, call} @@ -1433,7 +1300,7 @@ func (p *parser) parseBranchStmt(tok token.Token) *ast.BranchStmt { s := &ast.BranchStmt{p.pos, tok, nil} p.expect(tok) if tok != token.FALLTHROUGH && p.tok == token.IDENT { - s.Label = p.findIdentInScope(nil) + s.Label = p.parseIdent() } p.expectSemi() @@ -1448,8 +1315,8 @@ func (p *parser) makeExpr(s ast.Stmt) ast.Expr { if es, isExpr := s.(*ast.ExprStmt); isExpr { return p.checkExpr(es.X) } - p.Error(s.Pos(), "expected condition, found simple statement") - return &ast.BadExpr{s.Pos()} + p.error(s.Pos(), "expected condition, found simple statement") + return &ast.BadExpr{s.Pos(), s.End()} } @@ -1489,10 +1356,6 @@ func (p *parser) parseIfStmt() *ast.IfStmt { defer un(trace(p, "IfStmt")) } - // IfStmt block - p.openScope() - defer p.closeScope() - pos := p.expect(token.IF) s1, s2, _ := p.parseControlClause(false) body := p.parseBlockStmt() @@ -1513,10 +1376,6 @@ func (p *parser) parseCaseClause() *ast.CaseClause { defer un(trace(p, "CaseClause")) } - // CaseClause block - p.openScope() - defer p.closeScope() - // SwitchCase pos := p.pos var x []ast.Expr @@ -1534,19 +1393,18 @@ func (p *parser) parseCaseClause() *ast.CaseClause { } -func (p *parser) parseTypeList() []ast.Expr { +func (p *parser) parseTypeList() (list []ast.Expr) { if p.trace { defer un(trace(p, "TypeList")) } - var list vector.Vector - list.Push(p.parseType()) + list = append(list, p.parseType()) for p.tok == token.COMMA { p.next() - list.Push(p.parseType()) + list = append(list, p.parseType()) } - return makeExprList(&list) + return } @@ -1555,10 +1413,6 @@ func (p *parser) parseTypeCaseClause() *ast.TypeCaseClause { defer un(trace(p, "TypeCaseClause")) } - // TypeCaseClause block - p.openScope() - defer p.closeScope() - // TypeSwitchCase pos := p.pos var types []ast.Expr @@ -1595,21 +1449,17 @@ func (p *parser) parseSwitchStmt() ast.Stmt { defer un(trace(p, "SwitchStmt")) } - // SwitchStmt block - p.openScope() - defer p.closeScope() - pos := p.expect(token.SWITCH) s1, s2, _ := p.parseControlClause(false) if isExprSwitch(s2) { lbrace := p.expect(token.LBRACE) - var cases vector.Vector + var list []ast.Stmt for p.tok == token.CASE || p.tok == token.DEFAULT { - cases.Push(p.parseCaseClause()) + list = append(list, p.parseCaseClause()) } rbrace := p.expect(token.RBRACE) - body := &ast.BlockStmt{lbrace, makeStmtList(&cases), rbrace} + body := &ast.BlockStmt{lbrace, list, rbrace} p.expectSemi() return &ast.SwitchStmt{pos, s1, p.makeExpr(s2), body} } @@ -1617,13 +1467,13 @@ func (p *parser) parseSwitchStmt() ast.Stmt { // type switch // TODO(gri): do all the checks! lbrace := p.expect(token.LBRACE) - var cases vector.Vector + var list []ast.Stmt for p.tok == token.CASE || p.tok == token.DEFAULT { - cases.Push(p.parseTypeCaseClause()) + list = append(list, p.parseTypeCaseClause()) } rbrace := p.expect(token.RBRACE) p.expectSemi() - body := &ast.BlockStmt{lbrace, makeStmtList(&cases), rbrace} + body := &ast.BlockStmt{lbrace, list, rbrace} return &ast.TypeSwitchStmt{pos, s1, s2, body} } @@ -1633,10 +1483,6 @@ func (p *parser) parseCommClause() *ast.CommClause { defer un(trace(p, "CommClause")) } - // CommClause block - p.openScope() - defer p.closeScope() - // CommCase pos := p.pos var tok token.Token @@ -1680,13 +1526,13 @@ func (p *parser) parseSelectStmt() *ast.SelectStmt { pos := p.expect(token.SELECT) lbrace := p.expect(token.LBRACE) - var cases vector.Vector + var list []ast.Stmt for p.tok == token.CASE || p.tok == token.DEFAULT { - cases.Push(p.parseCommClause()) + list = append(list, p.parseCommClause()) } rbrace := p.expect(token.RBRACE) p.expectSemi() - body := &ast.BlockStmt{lbrace, makeStmtList(&cases), rbrace} + body := &ast.BlockStmt{lbrace, list, rbrace} return &ast.SelectStmt{pos, body} } @@ -1697,10 +1543,6 @@ func (p *parser) parseForStmt() ast.Stmt { defer un(trace(p, "ForStmt")) } - // ForStmt block - p.openScope() - defer p.closeScope() - pos := p.expect(token.FOR) s1, s2, s3 := p.parseControlClause(true) body := p.parseBlockStmt() @@ -1710,7 +1552,7 @@ func (p *parser) parseForStmt() ast.Stmt { // possibly a for statement with a range clause; check assignment operator if as.Tok != token.ASSIGN && as.Tok != token.DEFINE { p.errorExpected(as.TokPos, "'=' or ':='") - return &ast.BadStmt{pos} + return &ast.BadStmt{pos, body.End()} } // check lhs var key, value ast.Expr @@ -1721,19 +1563,19 @@ func (p *parser) parseForStmt() ast.Stmt { key = as.Lhs[0] default: p.errorExpected(as.Lhs[0].Pos(), "1 or 2 expressions") - return &ast.BadStmt{pos} + return &ast.BadStmt{pos, body.End()} } // check rhs if len(as.Rhs) != 1 { p.errorExpected(as.Rhs[0].Pos(), "1 expressions") - return &ast.BadStmt{pos} + return &ast.BadStmt{pos, body.End()} } if rhs, isUnary := as.Rhs[0].(*ast.UnaryExpr); isUnary && rhs.Op == token.RANGE { // rhs is range expression; check lhs return &ast.RangeStmt{pos, key, value, as.TokPos, as.Tok, rhs.X, body} } else { p.errorExpected(s2.Pos(), "range clause") - return &ast.BadStmt{pos} + return &ast.BadStmt{pos, body.End()} } } else { // regular for statement @@ -1791,9 +1633,10 @@ func (p *parser) parseStmt() (s ast.Stmt) { s = &ast.EmptyStmt{p.pos} default: // no statement found - p.errorExpected(p.pos, "statement") + pos := p.pos + p.errorExpected(pos, "statement") p.next() // make progress - s = &ast.BadStmt{p.pos} + s = &ast.BadStmt{pos, p.pos} } return @@ -1813,14 +1656,10 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup) ast.Spec { var ident *ast.Ident if p.tok == token.PERIOD { - ident = &ast.Ident{p.pos, ast.NewObj(ast.Pkg, p.pos, ".")} + ident = &ast.Ident{p.pos, ".", nil} p.next() } else if p.tok == token.IDENT { - ident = p.parseIdent(ast.Pkg) - // TODO(gri) Make sure the ident is not already declared in the - // package scope. Also, cannot add the same name to - // the package scope later. - p.declIdent(p.fileScope, ident) + ident = p.parseIdent() } var path *ast.BasicLit @@ -1841,23 +1680,13 @@ func parseConstSpec(p *parser, doc *ast.CommentGroup) ast.Spec { defer un(trace(p, "ConstSpec")) } - idents := p.parseIdentList(ast.Con) - if p.funcScope == nil { - // the scope of a constant outside any function - // is the package scope - p.declIdentList(p.pkgScope, idents) - } + idents := p.parseIdentList() typ := p.tryType() var values []ast.Expr if typ != nil || p.tok == token.ASSIGN { p.expect(token.ASSIGN) values = p.parseExprList() } - if p.funcScope != nil { - // the scope of a constant inside a function - // begins after the the ConstSpec - p.declIdentList(p.funcScope, idents) - } p.expectSemi() return &ast.ValueSpec{doc, idents, typ, values, p.lineComment} @@ -1869,15 +1698,7 @@ func parseTypeSpec(p *parser, doc *ast.CommentGroup) ast.Spec { defer un(trace(p, "TypeSpec")) } - ident := p.parseIdent(ast.Typ) - // the scope of a type outside any function is - // the package scope; the scope of a type inside - // a function starts at the type identifier - scope := p.funcScope - if scope == nil { - scope = p.pkgScope - } - p.declIdent(scope, ident) + ident := p.parseIdent() typ := p.parseType() p.expectSemi() @@ -1890,23 +1711,13 @@ func parseVarSpec(p *parser, doc *ast.CommentGroup) ast.Spec { defer un(trace(p, "VarSpec")) } - idents := p.parseIdentList(ast.Var) - if p.funcScope == nil { - // the scope of a variable outside any function - // is the pkgScope - p.declIdentList(p.pkgScope, idents) - } + idents := p.parseIdentList() typ := p.tryType() var values []ast.Expr if typ == nil || p.tok == token.ASSIGN { p.expect(token.ASSIGN) values = p.parseExprList() } - if p.funcScope != nil { - // the scope of a variable inside a function - // begins after the the VarSpec - p.declIdentList(p.funcScope, idents) - } p.expectSemi() return &ast.ValueSpec{doc, idents, typ, values, p.lineComment} @@ -1915,58 +1726,51 @@ func parseVarSpec(p *parser, doc *ast.CommentGroup) ast.Spec { func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.GenDecl { if p.trace { - defer un(trace(p, keyword.String()+"Decl")) + defer un(trace(p, "GenDecl("+keyword.String()+")")) } doc := p.leadComment pos := p.expect(keyword) - var lparen, rparen token.Position - var list vector.Vector + var lparen, rparen token.Pos + var list []ast.Spec if p.tok == token.LPAREN { lparen = p.pos p.next() for p.tok != token.RPAREN && p.tok != token.EOF { - list.Push(f(p, p.leadComment)) + list = append(list, f(p, p.leadComment)) } rparen = p.expect(token.RPAREN) p.expectSemi() } else { - list.Push(f(p, nil)) + list = append(list, f(p, nil)) } - // convert vector - specs := make([]ast.Spec, len(list)) - for i, x := range list { - specs[i] = x.(ast.Spec) - } - - return &ast.GenDecl{doc, pos, keyword, lparen, specs, rparen} + return &ast.GenDecl{doc, pos, keyword, lparen, list, rparen} } -func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList { +func (p *parser) parseReceiver() *ast.FieldList { if p.trace { defer un(trace(p, "Receiver")) } pos := p.pos - par := p.parseParameters(scope, false) + par := p.parseParameters(false) // must have exactly one receiver if par.NumFields() != 1 { p.errorExpected(pos, "exactly one receiver") - par.List = []*ast.Field{&ast.Field{Type: &ast.BadExpr{noPos}}} + // TODO determine a better range for BadExpr below + par.List = []*ast.Field{&ast.Field{Type: &ast.BadExpr{pos, pos}}} + return par } + // recv type must be of the form ["*"] identifier recv := par.List[0] - - // recv type must be TypeName or *TypeName - base := recv.Type - if ptr, isPtr := base.(*ast.StarExpr); isPtr { - base = ptr.X - } - if !isTypeName(base) { - p.errorExpected(base.Pos(), "type name") + base := deref(recv.Type) + if _, isIdent := base.(*ast.Ident); !isIdent { + p.errorExpected(base.Pos(), "(unqualified) identifier") + par.List = []*ast.Field{&ast.Field{Type: &ast.BadExpr{recv.Pos(), recv.End()}}} } return par @@ -1980,20 +1784,18 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl { doc := p.leadComment pos := p.expect(token.FUNC) - scope := ast.NewScope(p.funcScope) var recv *ast.FieldList if p.tok == token.LPAREN { - recv = p.parseReceiver(scope) + recv = p.parseReceiver() } - ident := p.parseIdent(ast.Fun) - p.declIdent(p.pkgScope, ident) // there are no local function declarations - params, results := p.parseSignature(scope) + ident := p.parseIdent() + params, results := p.parseSignature() var body *ast.BlockStmt if p.tok == token.LBRACE { - body = p.parseBody(scope) + body = p.parseBody() } p.expectSemi() @@ -2023,8 +1825,8 @@ func (p *parser) parseDecl() ast.Decl { default: pos := p.pos p.errorExpected(pos, "declaration") - decl := &ast.BadDecl{pos} - p.next() // make progress in any case + p.next() // make progress + decl := &ast.BadDecl{pos, p.pos} return decl } @@ -2032,23 +1834,16 @@ func (p *parser) parseDecl() ast.Decl { } -func (p *parser) parseDeclList() []ast.Decl { +func (p *parser) parseDeclList() (list []ast.Decl) { if p.trace { defer un(trace(p, "DeclList")) } - var list vector.Vector for p.tok != token.EOF { - list.Push(p.parseDecl()) + list = append(list, p.parseDecl()) } - // convert vector - decls := make([]ast.Decl, len(list)) - for i, x := range list { - decls[i] = x.(ast.Decl) - } - - return decls + return } @@ -2063,10 +1858,9 @@ func (p *parser) parseFile() *ast.File { // package clause doc := p.leadComment pos := p.expect(token.PACKAGE) - ident := p.parseIdent(ast.Pkg) // package name is in no scope + ident := p.parseIdent() p.expectSemi() - p.fileScope = ast.NewScope(p.pkgScope) var decls []ast.Decl // Don't bother parsing the rest if we had errors already. @@ -2074,30 +1868,17 @@ func (p *parser) parseFile() *ast.File { if p.ErrorCount() == 0 && p.mode&PackageClauseOnly == 0 { // import decls - var list vector.Vector for p.tok == token.IMPORT { - list.Push(p.parseGenDecl(token.IMPORT, parseImportSpec)) + decls = append(decls, p.parseGenDecl(token.IMPORT, parseImportSpec)) } if p.mode&ImportsOnly == 0 { // rest of package body for p.tok != token.EOF { - list.Push(p.parseDecl()) + decls = append(decls, p.parseDecl()) } } - - // convert declaration list - decls = make([]ast.Decl, len(list)) - for i, x := range list { - decls[i] = x.(ast.Decl) - } - } - - // convert comments list - comments := make([]*ast.CommentGroup, len(p.comments)) - for i, x := range p.comments { - comments[i] = x.(*ast.CommentGroup) } - return &ast.File{doc, pos, ident, decls, comments} + return &ast.File{doc, pos, ident, decls, p.comments} } diff --git a/src/pkg/go/parser/parser_test.go b/src/pkg/go/parser/parser_test.go index 01327a41d..56bd80ef1 100644 --- a/src/pkg/go/parser/parser_test.go +++ b/src/pkg/go/parser/parser_test.go @@ -5,12 +5,14 @@ package parser import ( - "go/ast" + "go/token" "os" "testing" ) +var fset = token.NewFileSet() + var illegalInputs = []interface{}{ nil, 3.14, @@ -21,7 +23,7 @@ var illegalInputs = []interface{}{ func TestParseIllegalInputs(t *testing.T) { for _, src := range illegalInputs { - _, err := ParseFile("", src, nil, 0) + _, err := ParseFile(fset, "", src, 0) if err == nil { t.Errorf("ParseFile(%v) should have failed", src) } @@ -30,23 +32,26 @@ func TestParseIllegalInputs(t *testing.T) { var validPrograms = []interface{}{ + "package main\n", `package main;`, - `package main; import "fmt"; func main() { fmt.Println("Hello, World!") }` + "\n", - `package main; func main() { if f(T{}) {} }` + "\n", - `package main; func main() { _ = (<-chan int)(x) }` + "\n", - `package main; func main() { _ = (<-chan <-chan int)(x) }` + "\n", - `package main; func f(func() func() func())` + "\n", - `package main; func f(...)` + "\n", - `package main; func f(float, ...int)` + "\n", - `package main; type T []int; var a []bool; func f() { if a[T{42}[0]] {} }` + "\n", - `package main; type T []int; func g(int) bool { return true }; func f() { if g(T{42}[0]) {} }` + "\n", - `package main; type T []int; func f() { for _ = range []int{T{42}[0]} {} }` + "\n", + `package main; import "fmt"; func main() { fmt.Println("Hello, World!") };`, + `package main; func main() { if f(T{}) {} };`, + `package main; func main() { _ = (<-chan int)(x) };`, + `package main; func main() { _ = (<-chan <-chan int)(x) };`, + `package main; func f(func() func() func());`, + `package main; func f(...T);`, + `package main; func f(float, ...int);`, + `package main; func f(x int, a ...int) { f(0, a...); f(1, a...,) };`, + `package main; type T []int; var a []bool; func f() { if a[T{42}[0]] {} };`, + `package main; type T []int; func g(int) bool { return true }; func f() { if g(T{42}[0]) {} };`, + `package main; type T []int; func f() { for _ = range []int{T{42}[0]} {} };`, + `package main; var a = T{{1, 2}, {3, 4}}`, } func TestParseValidPrograms(t *testing.T) { for _, src := range validPrograms { - _, err := ParseFile("", src, ast.NewScope(nil), 0) + _, err := ParseFile(fset, "", src, 0) if err != nil { t.Errorf("ParseFile(%q): %v", src, err) } @@ -62,7 +67,7 @@ var validFiles = []string{ func TestParse3(t *testing.T) { for _, filename := range validFiles { - _, err := ParseFile(filename, nil, ast.NewScope(nil), 0) + _, err := ParseFile(fset, filename, nil, 0) if err != nil { t.Errorf("ParseFile(%s): %v", filename, err) } @@ -87,7 +92,7 @@ func dirFilter(f *os.FileInfo) bool { return nameFilter(f.Name) } func TestParse4(t *testing.T) { path := "." - pkgs, err := ParseDir(path, dirFilter, 0) + pkgs, err := ParseDir(fset, path, dirFilter, 0) if err != nil { t.Fatalf("ParseDir(%s): %v", path, err) } @@ -99,7 +104,7 @@ func TestParse4(t *testing.T) { t.Errorf(`package "parser" not found`) return } - for filename, _ := range pkg.Files { + for filename := range pkg.Files { if !nameFilter(filename) { t.Errorf("unexpected package file: %s", filename) } diff --git a/src/pkg/go/printer/Makefile b/src/pkg/go/printer/Makefile index a0fe22e42..6a71efc93 100644 --- a/src/pkg/go/printer/Makefile +++ b/src/pkg/go/printer/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=go/printer GOFILES=\ diff --git a/src/pkg/go/printer/nodes.go b/src/pkg/go/printer/nodes.go index a98af4a2a..1ee0846f6 100644 --- a/src/pkg/go/printer/nodes.go +++ b/src/pkg/go/printer/nodes.go @@ -10,7 +10,6 @@ package printer import ( "bytes" - "container/vector" "go/ast" "go/token" ) @@ -28,32 +27,30 @@ import ( // ---------------------------------------------------------------------------- // Common AST nodes. -// Print as many newlines as necessary (but at least min and and at most -// max newlines) to get to the current line. ws is printed before the first -// line break. If newSection is set, the first line break is printed as -// formfeed. Returns true if any line break was printed; returns false otherwise. +// Print as many newlines as necessary (but at least min newlines) to get to +// the current line. ws is printed before the first line break. If newSection +// is set, the first line break is printed as formfeed. Returns true if any +// line break was printed; returns false otherwise. // -// TODO(gri): Reconsider signature (provide position instead of line) +// TODO(gri): linebreak may add too many lines if the next statement at "line" +// is preceeded by comments because the computation of n assumes +// the current position before the comment and the target position +// after the comment. Thus, after interspersing such comments, the +// space taken up by them is not considered to reduce the number of +// linebreaks. At the moment there is no easy way to know about +// future (not yet interspersed) comments in this function. // -func (p *printer) linebreak(line, min, max int, ws whiteSpace, newSection bool) (printedBreak bool) { - n := line - p.pos.Line - switch { - case n < min: - n = min - case n > max: - n = max - } - +func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (printedBreak bool) { + n := p.nlines(line-p.pos.Line, min) if n > 0 { p.print(ws) if newSection { p.print(formfeed) n-- - printedBreak = true } - } - for ; n > 0; n-- { - p.print(newline) + for ; n > 0; n-- { + p.print(newline) + } printedBreak = true } return @@ -75,7 +72,7 @@ func (p *printer) setComment(g *ast.CommentGroup) { // for some reason there are pending comments; this // should never happen - handle gracefully and flush // all comments up to g, ignore anything after that - p.flush(g.List[0].Pos(), token.ILLEGAL) + p.flush(p.fset.Position(g.List[0].Pos()), token.ILLEGAL) } p.comments[0] = g p.cindex = 0 @@ -95,7 +92,7 @@ const ( // Sets multiLine to true if the identifier list spans multiple lines. -// If ident is set, a multi-line identifier list is indented after the +// If indent is set, a multi-line identifier list is indented after the // first linebreak encountered. func (p *printer) identList(list []*ast.Ident, indent bool, multiLine *bool) { // convert into an expression list so we can re-use exprList formatting @@ -107,7 +104,7 @@ func (p *printer) identList(list []*ast.Ident, indent bool, multiLine *bool) { if !indent { mode |= noIndent } - p.exprList(noPos, xlist, 1, mode, multiLine, noPos) + p.exprList(token.NoPos, xlist, 1, mode, multiLine, token.NoPos) } @@ -130,7 +127,7 @@ func (p *printer) keySize(pair *ast.KeyValueExpr) int { // TODO(gri) Consider rewriting this to be independent of []ast.Expr // so that we can use the algorithm for any kind of list // (e.g., pass list via a channel over which to range). -func (p *printer) exprList(prev token.Position, list []ast.Expr, depth int, mode exprListMode, multiLine *bool, next token.Position) { +func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, multiLine *bool, next0 token.Pos) { if len(list) == 0 { return } @@ -139,14 +136,10 @@ func (p *printer) exprList(prev token.Position, list []ast.Expr, depth int, mode p.print(blank) } - line := list[0].Pos().Line - endLine := next.Line - if endLine == 0 { - // TODO(gri): endLine may be incorrect as it is really the beginning - // of the last list entry. There may be only one, very long - // entry in which case line == endLine. - endLine = list[len(list)-1].Pos().Line - } + prev := p.fset.Position(prev0) + next := p.fset.Position(next0) + line := p.fset.Position(list[0].Pos()).Line + endLine := p.fset.Position(list[len(list)-1].End()).Line if prev.IsValid() && prev.Line == line && line == endLine { // all list entries on a single line @@ -190,7 +183,7 @@ func (p *printer) exprList(prev token.Position, list []ast.Expr, depth int, mode // lines for them. linebreakMin = 0 } - if prev.IsValid() && prev.Line < line && p.linebreak(line, linebreakMin, 2, ws, true) { + if prev.IsValid() && prev.Line < line && p.linebreak(line, linebreakMin, ws, true) { ws = ignore *multiLine = true prevBreak = 0 @@ -202,7 +195,7 @@ func (p *printer) exprList(prev token.Position, list []ast.Expr, depth int, mode // print all list elements for i, x := range list { prevLine := line - line = x.Pos().Line + line = p.fset.Position(x.Pos()).Line // determine if the next linebreak, if any, needs to use formfeed: // in general, use the entire node size to make the decision; for @@ -252,7 +245,7 @@ func (p *printer) exprList(prev token.Position, list []ast.Expr, depth int, mode // unless forceFF is set or there are multiple expressions on // the same line in which case formfeed is used // broken with a formfeed - if p.linebreak(line, linebreakMin, 2, ws, useFF || prevBreak+1 < i) { + if p.linebreak(line, linebreakMin, ws, useFF || prevBreak+1 < i) { ws = ignore *multiLine = true prevBreak = i @@ -301,15 +294,27 @@ func (p *printer) exprList(prev token.Position, list []ast.Expr, depth int, mode func (p *printer) parameters(fields *ast.FieldList, multiLine *bool) { p.print(fields.Opening, token.LPAREN) if len(fields.List) > 0 { + var prevLine, line int for i, par := range fields.List { if i > 0 { - p.print(token.COMMA, blank) + p.print(token.COMMA) + if len(par.Names) > 0 { + line = p.fset.Position(par.Names[0].Pos()).Line + } else { + line = p.fset.Position(par.Type.Pos()).Line + } + if 0 < prevLine && prevLine < line && p.linebreak(line, 0, ignore, true) { + *multiLine = true + } else { + p.print(blank) + } } if len(par.Names) > 0 { p.identList(par.Names, false, multiLine) p.print(blank) } p.expr(par.Type, multiLine) + prevLine = p.fset.Position(par.Type.Pos()).Line } } p.print(fields.Closing, token.RPAREN) @@ -337,7 +342,7 @@ func identListSize(list []*ast.Ident, maxSize int) (size int) { if i > 0 { size += 2 // ", " } - size += len(x.Name()) + size += len(x.Name) if size >= maxSize { break } @@ -366,16 +371,21 @@ func (p *printer) isOneLineFieldList(list []*ast.Field) bool { func (p *printer) setLineComment(text string) { - p.setComment(&ast.CommentGroup{[]*ast.Comment{&ast.Comment{noPos, []byte(text)}}}) + p.setComment(&ast.CommentGroup{[]*ast.Comment{&ast.Comment{token.NoPos, []byte(text)}}}) } func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprContext) { + p.nesting++ + defer func() { + p.nesting-- + }() + lbrace := fields.Opening list := fields.List rbrace := fields.Closing - if !isIncomplete && !p.commentBefore(rbrace) { + if !isIncomplete && !p.commentBefore(p.fset.Position(rbrace)) { // possibly a one-line struct/interface if len(list) == 0 { // no blank between keyword and {} in this case @@ -413,7 +423,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprC var ml bool for i, f := range list { if i > 0 { - p.linebreak(f.Pos().Line, 1, 2, ignore, ml) + p.linebreak(p.fset.Position(f.Pos()).Line, 1, ignore, ml) } ml = false extraTabs := 0 @@ -448,7 +458,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprC if len(list) > 0 { p.print(formfeed) } - p.flush(rbrace, token.RBRACE) // make sure we don't loose the last line comment + p.flush(p.fset.Position(rbrace), token.RBRACE) // make sure we don't loose the last line comment p.setLineComment("// contains unexported fields") } @@ -457,7 +467,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprC var ml bool for i, f := range list { if i > 0 { - p.linebreak(f.Pos().Line, 1, 2, ignore, ml) + p.linebreak(p.fset.Position(f.Pos()).Line, 1, ignore, ml) } ml = false p.setComment(f.Doc) @@ -475,7 +485,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprC if len(list) > 0 { p.print(formfeed) } - p.flush(rbrace, token.RBRACE) // make sure we don't loose the last line comment + p.flush(p.fset.Position(rbrace), token.RBRACE) // make sure we don't loose the last line comment p.setLineComment("// contains unexported methods") } @@ -540,7 +550,7 @@ func walkBinary(e *ast.BinaryExpr) (has5, has6 bool, maxProblem int) { case *ast.UnaryExpr: switch e.Op.String() + r.Op.String() { - case "/*": + case "/*", "&&", "&^": maxProblem = 6 case "++", "--": if maxProblem < 5 { @@ -609,11 +619,14 @@ func reduceDepth(depth int) int { // 1) If there is a binary operator with a right side unary operand // that would clash without a space, the cutoff must be (in order): // -// &^ 7 // /* 7 +// && 7 +// &^ 7 // ++ 6 // -- 6 // +// (Comparison operators always have spaces around them.) +// // 2) If there is a mix of level 6 and level 5 operators, then the cutoff // is 6 (use spaces to distinguish precedence) in Normal mode // and 5 (never use spaces) in Compact mode. @@ -643,12 +656,12 @@ func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int, multiL p.print(blank) } xline := p.pos.Line // before the operator (it may be on the next line!) - yline := x.Y.Pos().Line + yline := p.fset.Position(x.Y.Pos()).Line p.print(x.OpPos, x.Op) if xline != yline && xline > 0 && yline > 0 { // at least one line break, but respect an extra empty line // in the source - if p.linebreak(yline, 1, 2, ws, true) { + if p.linebreak(yline, 1, ws, true) { ws = ignore *multiLine = true printBlank = false // no blank after line break @@ -683,19 +696,19 @@ func splitSelector(expr ast.Expr) (body, suffix ast.Expr) { case *ast.CallExpr: body, suffix = splitSelector(x.Fun) if body != nil { - suffix = &ast.CallExpr{suffix, x.Lparen, x.Args, x.Rparen} + suffix = &ast.CallExpr{suffix, x.Lparen, x.Args, x.Ellipsis, x.Rparen} return } case *ast.IndexExpr: body, suffix = splitSelector(x.X) if body != nil { - suffix = &ast.IndexExpr{suffix, x.Index} + suffix = &ast.IndexExpr{suffix, x.Lbrack, x.Index, x.Rbrack} return } case *ast.SliceExpr: body, suffix = splitSelector(x.X) if body != nil { - suffix = &ast.SliceExpr{suffix, x.Index, x.End} + suffix = &ast.SliceExpr{suffix, x.Lbrack, x.Low, x.High, x.Rbrack} return } case *ast.TypeAssertExpr: @@ -712,23 +725,20 @@ func splitSelector(expr ast.Expr) (body, suffix ast.Expr) { // Convert an expression into an expression list split at the periods of // selector expressions. -func selectorExprList(expr ast.Expr) []ast.Expr { +func selectorExprList(expr ast.Expr) (list []ast.Expr) { // split expression - var list vector.Vector for expr != nil { var suffix ast.Expr expr, suffix = splitSelector(expr) - list.Push(suffix) + list = append(list, suffix) } - // convert expression list - result := make([]ast.Expr, len(list)) - i := len(result) - for _, x := range list { - i-- - result[i] = x.(ast.Expr) + // reverse list + for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 { + list[i], list[j] = list[j], list[i] } - return result + + return } @@ -791,16 +801,22 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi case *ast.FuncLit: p.expr(x.Type, multiLine) - p.funcBody(x.Body, distance(x.Type.Pos(), p.pos), true, multiLine) + p.funcBody(x.Body, p.distance(x.Type.Pos(), p.pos), true, multiLine) case *ast.ParenExpr: - p.print(token.LPAREN) - p.expr0(x.X, reduceDepth(depth), multiLine) // parentheses undo one level of depth - p.print(x.Rparen, token.RPAREN) + if _, hasParens := x.X.(*ast.ParenExpr); hasParens { + // don't print parentheses around an already parenthesized expression + // TODO(gri) consider making this more general and incorporate precedence levels + p.expr0(x.X, reduceDepth(depth), multiLine) // parentheses undo one level of depth + } else { + p.print(token.LPAREN) + p.expr0(x.X, reduceDepth(depth), multiLine) // parentheses undo one level of depth + p.print(x.Rparen, token.RPAREN) + } case *ast.SelectorExpr: parts := selectorExprList(expr) - p.exprList(noPos, parts, depth, periodSep, multiLine, noPos) + p.exprList(token.NoPos, parts, depth, periodSep, multiLine, token.NoPos) case *ast.TypeAssertExpr: p.expr1(x.X, token.HighestPrec, depth, 0, multiLine) @@ -815,25 +831,27 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi case *ast.IndexExpr: // TODO(gri): should treat[] like parentheses and undo one level of depth p.expr1(x.X, token.HighestPrec, 1, 0, multiLine) - p.print(token.LBRACK) + p.print(x.Lbrack, token.LBRACK) p.expr0(x.Index, depth+1, multiLine) - p.print(token.RBRACK) + p.print(x.Rbrack, token.RBRACK) case *ast.SliceExpr: // TODO(gri): should treat[] like parentheses and undo one level of depth p.expr1(x.X, token.HighestPrec, 1, 0, multiLine) - p.print(token.LBRACK) - p.expr0(x.Index, depth+1, multiLine) + p.print(x.Lbrack, token.LBRACK) + if x.Low != nil { + p.expr0(x.Low, depth+1, multiLine) + } // blanks around ":" if both sides exist and either side is a binary expression - if depth <= 1 && x.End != nil && (isBinary(x.Index) || isBinary(x.End)) { + if depth <= 1 && x.Low != nil && x.High != nil && (isBinary(x.Low) || isBinary(x.High)) { p.print(blank, token.COLON, blank) } else { p.print(token.COLON) } - if x.End != nil { - p.expr0(x.End, depth+1, multiLine) + if x.High != nil { + p.expr0(x.High, depth+1, multiLine) } - p.print(token.RBRACK) + p.print(x.Rbrack, token.RBRACK) case *ast.CallExpr: if len(x.Args) > 1 { @@ -842,10 +860,16 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi p.expr1(x.Fun, token.HighestPrec, depth, 0, multiLine) p.print(x.Lparen, token.LPAREN) p.exprList(x.Lparen, x.Args, depth, commaSep|commaTerm, multiLine, x.Rparen) + if x.Ellipsis.IsValid() { + p.print(x.Ellipsis, token.ELLIPSIS) + } p.print(x.Rparen, token.RPAREN) case *ast.CompositeLit: - p.expr1(x.Type, token.HighestPrec, depth, compositeLit, multiLine) + // composite literal elements that are composite literals themselves may have the type omitted + if x.Type != nil { + p.expr1(x.Type, token.HighestPrec, depth, compositeLit, multiLine) + } p.print(x.Lbrace, token.LBRACE) p.exprList(x.Lbrace, x.Elts, 1, commaSep|commaTerm, multiLine, x.Rbrace) p.print(x.Rbrace, token.RBRACE) @@ -917,8 +941,6 @@ func (p *printer) expr(x ast.Expr, multiLine *bool) { // ---------------------------------------------------------------------------- // Statements -const maxStmtNewlines = 2 // maximum number of newlines between statements - // Print the statement list indented, but without a newline after the last statement. // Extra line breaks between statements in the source are respected but at most one // empty line is printed between statements. @@ -931,7 +953,7 @@ func (p *printer) stmtList(list []ast.Stmt, _indent int, nextIsRBrace bool) { for i, s := range list { // _indent == 0 only for lists of switch/select case clauses; // in those cases each clause is a new section - p.linebreak(s.Pos().Line, 1, maxStmtNewlines, ignore, i == 0 || _indent == 0 || multiLine) + p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, i == 0 || _indent == 0 || multiLine) multiLine = false p.stmt(s, nextIsRBrace && i == len(list)-1, &multiLine) } @@ -945,7 +967,7 @@ func (p *printer) stmtList(list []ast.Stmt, _indent int, nextIsRBrace bool) { func (p *printer) block(s *ast.BlockStmt, indent int) { p.print(s.Pos(), token.LBRACE) p.stmtList(s.List, indent, true) - p.linebreak(s.Rbrace.Line, 1, maxStmtNewlines, ignore, true) + p.linebreak(p.fset.Position(s.Rbrace).Line, 1, ignore, true) p.print(s.Rbrace, token.RBRACE) } @@ -961,16 +983,27 @@ func isTypeName(x ast.Expr) bool { } -// TODO(gri): Decide if this should be used more broadly. The printing code -// knows when to insert parentheses for precedence reasons, but -// need to be careful to keep them around type expressions. -func stripParens(x ast.Expr, inControlClause bool) ast.Expr { - for px, hasParens := x.(*ast.ParenExpr); hasParens; px, hasParens = x.(*ast.ParenExpr) { - x = px.X - if cx, isCompositeLit := x.(*ast.CompositeLit); inControlClause && isCompositeLit && isTypeName(cx.Type) { - // composite literals inside control clauses need parens if they start with a type name; - // don't strip innermost layer - return px +func stripParens(x ast.Expr) ast.Expr { + if px, strip := x.(*ast.ParenExpr); strip { + // parentheses must not be stripped if there are any + // unparenthesized composite literals starting with + // a type name + ast.Inspect(px.X, func(node ast.Node) bool { + switch x := node.(type) { + case *ast.ParenExpr: + // parentheses protect enclosed composite literals + return false + case *ast.CompositeLit: + if isTypeName(x.Type) { + strip = false // do not strip parentheses + } + return false + } + // in all other cases, keep inspecting + return true + }) + if strip { + return stripParens(px.X) } } return x @@ -983,7 +1016,7 @@ func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, po if init == nil && post == nil { // no semicolons required if expr != nil { - p.expr(stripParens(expr, true), ignoreMultiLine) + p.expr(stripParens(expr), ignoreMultiLine) needsBlank = true } } else { @@ -994,7 +1027,7 @@ func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, po } p.print(token.SEMICOLON, blank) if expr != nil { - p.expr(stripParens(expr, true), ignoreMultiLine) + p.expr(stripParens(expr), ignoreMultiLine) needsBlank = true } if isForStmt { @@ -1032,14 +1065,14 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) { // between (see writeWhitespace) p.print(unindent) p.expr(s.Label, multiLine) - p.print(token.COLON, indent) + p.print(s.Colon, token.COLON, indent) if e, isEmpty := s.Stmt.(*ast.EmptyStmt); isEmpty { if !nextIsRBrace { p.print(newline, e.Pos(), token.SEMICOLON) break } } else { - p.print(newline) + p.linebreak(p.fset.Position(s.Stmt.Pos()).Line, 1, ignore, true) } p.stmt(s.Stmt, nextIsRBrace, multiLine) @@ -1050,7 +1083,7 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) { case *ast.IncDecStmt: const depth = 1 p.expr0(s.X, depth+1, multiLine) - p.print(s.Tok) + p.print(s.TokPos, s.Tok) case *ast.AssignStmt: var depth = 1 @@ -1059,7 +1092,7 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) { } p.exprList(s.Pos(), s.Lhs, depth, commaSep, multiLine, s.TokPos) p.print(blank, s.TokPos, s.Tok) - p.exprList(s.TokPos, s.Rhs, depth, blankStart|commaSep, multiLine, noPos) + p.exprList(s.TokPos, s.Rhs, depth, blankStart|commaSep, multiLine, token.NoPos) case *ast.GoStmt: p.print(token.GO, blank) @@ -1072,7 +1105,7 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) { case *ast.ReturnStmt: p.print(token.RETURN) if s.Results != nil { - p.exprList(s.Pos(), s.Results, 1, blankStart|commaSep, multiLine, noPos) + p.exprList(s.Pos(), s.Results, 1, blankStart|commaSep, multiLine, token.NoPos) } case *ast.BranchStmt: @@ -1175,7 +1208,7 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) { p.expr(s.Value, multiLine) } p.print(blank, s.TokPos, s.Tok, blank, token.RANGE, blank) - p.expr(stripParens(s.X, true), multiLine) + p.expr(stripParens(s.X), multiLine) p.print(blank) p.block(s.Body, 1) *multiLine = true @@ -1191,25 +1224,25 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) { // ---------------------------------------------------------------------------- // Declarations -// The parameter n is the number of specs in the group. If indent is set, +// The parameter n is the number of specs in the group. If doIndent is set, // multi-line identifier lists in the spec are indented when the first // linebreak is encountered. // Sets multiLine to true if the spec spans multiple lines. // -func (p *printer) spec(spec ast.Spec, n int, indent bool, multiLine *bool) { +func (p *printer) spec(spec ast.Spec, n int, doIndent bool, multiLine *bool) { switch s := spec.(type) { case *ast.ImportSpec: p.setComment(s.Doc) if s.Name != nil { p.expr(s.Name, multiLine) - p.print(blank) + p.print(vtab) } p.expr(s.Path, multiLine) p.setComment(s.Comment) case *ast.ValueSpec: p.setComment(s.Doc) - p.identList(s.Names, indent, multiLine) // always present + p.identList(s.Names, doIndent, multiLine) // always present if n == 1 { if s.Type != nil { p.print(blank) @@ -1217,7 +1250,7 @@ func (p *printer) spec(spec ast.Spec, n int, indent bool, multiLine *bool) { } if s.Values != nil { p.print(blank, token.ASSIGN) - p.exprList(noPos, s.Values, 1, blankStart|commaSep, multiLine, noPos) + p.exprList(token.NoPos, s.Values, 1, blankStart|commaSep, multiLine, token.NoPos) } p.setComment(s.Comment) @@ -1230,7 +1263,7 @@ func (p *printer) spec(spec ast.Spec, n int, indent bool, multiLine *bool) { } if s.Values != nil { p.print(vtab, token.ASSIGN) - p.exprList(noPos, s.Values, 1, blankStart|commaSep, multiLine, noPos) + p.exprList(token.NoPos, s.Values, 1, blankStart|commaSep, multiLine, token.NoPos) extraTabs-- } if s.Comment != nil { @@ -1271,7 +1304,7 @@ func (p *printer) genDecl(d *ast.GenDecl, multiLine *bool) { var ml bool for i, s := range d.Specs { if i > 0 { - p.linebreak(s.Pos().Line, 1, 2, ignore, ml) + p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, ml) } ml = false p.spec(s, len(d.Specs), false, &ml) @@ -1300,7 +1333,7 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) { // in RawFormat cfg := Config{Mode: RawFormat} var buf bytes.Buffer - if _, err := cfg.Fprint(&buf, n); err != nil { + if _, err := cfg.Fprint(&buf, p.fset, n); err != nil { return } if buf.Len() <= maxSize { @@ -1318,11 +1351,11 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) { func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool { pos1 := b.Pos() pos2 := b.Rbrace - if pos1.IsValid() && pos2.IsValid() && pos1.Line != pos2.Line { + if pos1.IsValid() && pos2.IsValid() && p.fset.Position(pos1).Line != p.fset.Position(pos2).Line { // opening and closing brace are on different lines - don't make it a one-liner return false } - if len(b.List) > 5 || p.commentBefore(pos2) { + if len(b.List) > 5 || p.commentBefore(p.fset.Position(pos2)) { // too many statements or there is a comment inside - don't make it a one-liner return false } @@ -1345,6 +1378,11 @@ func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool, multiLi return } + p.nesting++ + defer func() { + p.nesting-- + }() + if p.isOneLineFunc(b, headerSize) { sep := vtab if isLit { @@ -1374,7 +1412,8 @@ func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool, multiLi // distance returns the column difference between from and to if both // are on the same line; if they are on different lines (or unknown) // the result is infinity. -func distance(from, to token.Position) int { +func (p *printer) distance(from0 token.Pos, to token.Position) int { + from := p.fset.Position(from0) if from.IsValid() && to.IsValid() && from.Line == to.Line { return to.Column - from.Column } @@ -1392,7 +1431,7 @@ func (p *printer) funcDecl(d *ast.FuncDecl, multiLine *bool) { } p.expr(d.Name, multiLine) p.signature(d.Type.Params, d.Type.Results, multiLine) - p.funcBody(d.Body, distance(d.Pos(), p.pos), false, multiLine) + p.funcBody(d.Body, p.distance(d.Pos(), p.pos), false, multiLine) } @@ -1414,8 +1453,6 @@ func (p *printer) decl(decl ast.Decl, multiLine *bool) { // ---------------------------------------------------------------------------- // Files -const maxDeclNewlines = 3 // maximum number of newlines between declarations - func declToken(decl ast.Decl) (tok token.Token) { tok = token.ILLEGAL switch d := decl.(type) { @@ -1444,7 +1481,7 @@ func (p *printer) file(src *ast.File) { if prev != tok { min = 2 } - p.linebreak(d.Pos().Line, min, maxDeclNewlines, ignore, false) + p.linebreak(p.fset.Position(d.Pos()).Line, min, ignore, false) p.decl(d, ignoreMultiLine) } } diff --git a/src/pkg/go/printer/printer.go b/src/pkg/go/printer/printer.go index 53632c83d..a4ddad50e 100644 --- a/src/pkg/go/printer/printer.go +++ b/src/pkg/go/printer/printer.go @@ -18,10 +18,7 @@ import ( ) -const ( - debug = false // enable for debugging - maxNewlines = 3 // maximum vertical white space -) +const debug = false // enable for debugging type whiteSpace int @@ -41,8 +38,8 @@ var ( esc = []byte{tabwriter.Escape} htab = []byte{'\t'} htabs = []byte("\t\t\t\t\t\t\t\t") - newlines = []byte("\n\n\n\n\n\n\n\n") // more than maxNewlines - formfeeds = []byte("\f\f\f\f\f\f\f\f") // more than maxNewlines + newlines = []byte("\n\n\n\n\n\n\n\n") // more than the max determined by nlines + formfeeds = []byte("\f\f\f\f\f\f\f\f") // more than the max determined by nlines esc_quot = []byte(""") // shorter than """ esc_apos = []byte("'") // shorter than "'" @@ -65,12 +62,15 @@ type printer struct { // Configuration (does not change after initialization) output io.Writer Config + fset *token.FileSet errors chan os.Error // Current state - written int // number of bytes written - indent int // current indentation - escape bool // true if in escape sequence + nesting int // nesting level (0: top-level (package scope), >0: functions/decls.) + written int // number of bytes written + indent int // current indentation + escape bool // true if in escape sequence + lastTok token.Token // the last token printed (token.ILLEGAL if it's whitespace) // Buffered whitespace buffer []whiteSpace @@ -95,9 +95,10 @@ type printer struct { } -func (p *printer) init(output io.Writer, cfg *Config) { +func (p *printer) init(output io.Writer, cfg *Config, fset *token.FileSet) { p.output = output p.Config = *cfg + p.fset = fset p.errors = make(chan os.Error) p.buffer = make([]whiteSpace, 0, 16) // whitespace sequences are short } @@ -106,12 +107,31 @@ func (p *printer) init(output io.Writer, cfg *Config) { func (p *printer) internalError(msg ...interface{}) { if debug { fmt.Print(p.pos.String() + ": ") - fmt.Println(msg) + fmt.Println(msg...) panic("go/printer") } } +// nlines returns the adjusted number of linebreaks given the desired number +// of breaks n such that min <= result <= max where max depends on the current +// nesting level. +// +func (p *printer) nlines(n, min int) int { + if n < min { + return min + } + max := 3 // max. number of newlines at the top level (p.nesting == 0) + if p.nesting > 0 { + max = 2 // max. number of newlines everywhere else + } + if n > max { + return max + } + return n +} + + // write0 writes raw (uninterpreted) data to p.output and handles errors. // write0 does not indent after newlines, and does not HTML-escape or update p.pos. // @@ -192,6 +212,11 @@ func (p *printer) write(data []byte) { case tabwriter.Escape: p.escape = !p.escape + + // ignore escape chars introduced by printer - they are + // invisible and must not affect p.pos (was issue #1089) + p.pos.Offset-- + p.pos.Column-- } } @@ -207,9 +232,7 @@ func (p *printer) write(data []byte) { func (p *printer) writeNewlines(n int, useFF bool) { if n > 0 { - if n > maxNewlines { - n = maxNewlines - } + n = p.nlines(n, 0) if useFF { p.write(formfeeds[0:n]) } else { @@ -292,8 +315,8 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, isFirst, isKeywor } if pos.IsValid() && pos.Filename != p.last.Filename { - // comment in a different file - separate with newlines - p.writeNewlines(maxNewlines, true) + // comment in a different file - separate with newlines (writeNewlines will limit the number) + p.writeNewlines(10, true) return } @@ -380,7 +403,6 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, isFirst, isKeywor func (p *printer) writeCommentLine(comment *ast.Comment, pos token.Position, line []byte) { // line must pass through unchanged, bracket it with tabwriter.Escape - esc := []byte{tabwriter.Escape} line = bytes.Join([][]byte{esc, line, esc}, nil) // apply styler, if any @@ -576,7 +598,7 @@ func (p *printer) writeComment(comment *ast.Comment) { // shortcut common case of //-style comments if text[1] == '/' { - p.writeCommentLine(comment, comment.Pos(), text) + p.writeCommentLine(comment, p.fset.Position(comment.Pos()), text) return } @@ -588,7 +610,7 @@ func (p *printer) writeComment(comment *ast.Comment) { // write comment lines, separated by formfeed, // without a line break after the last line linebreak := formfeeds[0:1] - pos := comment.Pos() + pos := p.fset.Position(comment.Pos()) for i, line := range lines { if i > 0 { p.write(linebreak) @@ -649,14 +671,14 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (dro var last *ast.Comment for ; p.commentBefore(next); p.cindex++ { for _, c := range p.comments[p.cindex].List { - p.writeCommentPrefix(c.Pos(), next, last == nil, tok.IsKeyword()) + p.writeCommentPrefix(p.fset.Position(c.Pos()), next, last == nil, tok.IsKeyword()) p.writeComment(c) last = c } } if last != nil { - if last.Text[1] == '*' && last.Pos().Line == next.Line { + if last.Text[1] == '*' && p.fset.Position(last.Pos()).Line == next.Line { // the last comment is a /*-style comment and the next item // follows on the same line: separate with an extra blank p.write([]byte{' '}) @@ -726,6 +748,26 @@ func (p *printer) writeWhitespace(n int) { // ---------------------------------------------------------------------------- // Printing interface + +func mayCombine(prev token.Token, next byte) (b bool) { + switch prev { + case token.INT: + b = next == '.' // 1. + case token.ADD: + b = next == '+' // ++ + case token.SUB: + b = next == '-' // -- + case token.QUO: + b = next == '*' // /* + case token.LSS: + b = next == '-' || next == '<' // <- or << + case token.AND: + b = next == '&' || next == '^' // && or &^ + } + return +} + + // print prints a list of "items" (roughly corresponding to syntactic // tokens, but also including whitespace and formatting information). // It is the only print function that should be called directly from @@ -743,6 +785,7 @@ func (p *printer) print(args ...interface{}) { var data []byte var tag HTMLTag var tok token.Token + switch x := f.(type) { case whiteSpace: if x == ignore { @@ -765,7 +808,7 @@ func (p *printer) print(args ...interface{}) { if p.Styler != nil { data, tag = p.Styler.Ident(x) } else { - data = []byte(x.Name()) + data = []byte(x.Name) } tok = token.IDENT case *ast.BasicLit: @@ -779,22 +822,38 @@ func (p *printer) print(args ...interface{}) { // bytes since they do not appear in legal UTF-8 sequences) // TODO(gri): do this more efficiently. data = []byte("\xff" + string(data) + "\xff") - tok = token.INT // representing all literal tokens + tok = x.Kind case token.Token: + s := x.String() + if mayCombine(p.lastTok, s[0]) { + // the previous and the current token must be + // separated by a blank otherwise they combine + // into a different incorrect token sequence + // (except for token.INT followed by a '.' this + // should never happen because it is taken care + // of via binary expression formatting) + if len(p.buffer) != 0 { + p.internalError("whitespace buffer not empty") + } + p.buffer = p.buffer[0:1] + p.buffer[0] = ' ' + } if p.Styler != nil { data, tag = p.Styler.Token(x) } else { - data = []byte(x.String()) + data = []byte(s) } tok = x - case token.Position: + case token.Pos: if x.IsValid() { - next = x // accurate position of next item + next = p.fset.Position(x) // accurate position of next item } + tok = p.lastTok default: fmt.Fprintf(os.Stderr, "print: unsupported argument type %T\n", f) panic("go/printer type") } + p.lastTok = tok p.pos = next if data != nil { @@ -816,11 +875,11 @@ func (p *printer) print(args ...interface{}) { // before the next position in the source code. // func (p *printer) commentBefore(next token.Position) bool { - return p.cindex < len(p.comments) && p.comments[p.cindex].List[0].Pos().Offset < next.Offset + return p.cindex < len(p.comments) && p.fset.Position(p.comments[p.cindex].List[0].Pos()).Offset < next.Offset } -// Flush prints any pending comments and whitespace occuring +// Flush prints any pending comments and whitespace occurring // textually before the position of the next token tok. Flush // returns true if a pending formfeed character was dropped // from the whitespace buffer as a result of interspersing @@ -844,81 +903,85 @@ func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) { // A trimmer is an io.Writer filter for stripping tabwriter.Escape // characters, trailing blanks and tabs, and for converting formfeed // and vtab characters into newlines and htabs (in case no tabwriter -// is used). +// is used). Text bracketed by tabwriter.Escape characters is passed +// through unchanged. // type trimmer struct { output io.Writer - buf bytes.Buffer + space bytes.Buffer + state int } -// Design note: It is tempting to eliminate extra blanks occuring in +// trimmer is implemented as a state machine. +// It can be in one of the following states: +const ( + inSpace = iota + inEscape + inText +) + + +// Design note: It is tempting to eliminate extra blanks occurring in // whitespace in this function as it could simplify some // of the blanks logic in the node printing functions. // However, this would mess up any formatting done by // the tabwriter. func (p *trimmer) Write(data []byte) (n int, err os.Error) { - // m < 0: no unwritten data except for whitespace - // m >= 0: data[m:n] unwritten and no whitespace - m := 0 - if p.buf.Len() > 0 { - m = -1 - } - + m := 0 // if p.state != inSpace, data[m:n] is unwritten var b byte for n, b = range data { - switch b { - default: - // write any pending whitespace - if m < 0 { - if _, err = p.output.Write(p.buf.Bytes()); err != nil { - return - } - p.buf.Reset() - m = n - } - - case '\v': + if b == '\v' { b = '\t' // convert to htab - fallthrough - - case '\t', ' ', tabwriter.Escape: - // write any pending (non-whitespace) data - if m >= 0 { - if _, err = p.output.Write(data[m:n]); err != nil { - return - } - m = -1 + } + switch p.state { + case inSpace: + switch b { + case '\t', ' ': + p.space.WriteByte(b) // WriteByte returns no errors + case '\f', '\n': + p.space.Reset() // discard trailing space + _, err = p.output.Write(newlines[0:1]) // write newline + case tabwriter.Escape: + _, err = p.output.Write(p.space.Bytes()) + p.space.Reset() + p.state = inEscape + m = n + 1 // drop tabwriter.Escape + default: + _, err = p.output.Write(p.space.Bytes()) + p.space.Reset() + p.state = inText + m = n } - // collect whitespace but discard tabwriter.Escapes. - if b != tabwriter.Escape { - p.buf.WriteByte(b) // WriteByte returns no errors + case inEscape: + if b == tabwriter.Escape { + _, err = p.output.Write(data[m:n]) + p.state = inSpace } - - case '\f', '\n': - // discard whitespace - p.buf.Reset() - // write any pending (non-whitespace) data - if m >= 0 { - if _, err = p.output.Write(data[m:n]); err != nil { - return - } - m = -1 - } - // convert formfeed into newline - if _, err = p.output.Write(newlines[0:1]); err != nil { - return + case inText: + switch b { + case '\t', ' ': + _, err = p.output.Write(data[m:n]) + p.state = inSpace + p.space.WriteByte(b) // WriteByte returns no errors + case '\f': + data[n] = '\n' // convert to newline + case tabwriter.Escape: + _, err = p.output.Write(data[m:n]) + p.state = inEscape + m = n + 1 // drop tabwriter.Escape } } + if err != nil { + return + } } n = len(data) - // write any pending non-whitespace - if m >= 0 { - if _, err = p.output.Write(data[m:n]); err != nil { - return - } + if p.state != inSpace { + _, err = p.output.Write(data[m:n]) + p.state = inSpace } return @@ -965,10 +1028,11 @@ type Config struct { // Fprint "pretty-prints" an AST node to output and returns the number // of bytes written and an error (if any) for a given configuration cfg. +// Position information is interpreted relative to the file set fset. // The node type must be *ast.File, or assignment-compatible to ast.Expr, // ast.Decl, ast.Spec, or ast.Stmt. // -func (cfg *Config) Fprint(output io.Writer, node interface{}) (int, os.Error) { +func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) (int, os.Error) { // redirect output through a trimmer to eliminate trailing whitespace // (Input to a tabwriter must be untrimmed since trailing tabs provide // formatting information. The tabwriter could provide trimming @@ -1000,13 +1064,15 @@ func (cfg *Config) Fprint(output io.Writer, node interface{}) (int, os.Error) { // setup printer and print node var p printer - p.init(output, cfg) + p.init(output, cfg, fset) go func() { switch n := node.(type) { case ast.Expr: + p.nesting = 1 p.useNodeComments = true p.expr(n, ignoreMultiLine) case ast.Stmt: + p.nesting = 1 p.useNodeComments = true // A labeled statement will un-indent to position the // label. Set indent to 1 so we don't get indent "underflow". @@ -1015,17 +1081,20 @@ func (cfg *Config) Fprint(output io.Writer, node interface{}) (int, os.Error) { } p.stmt(n, false, ignoreMultiLine) case ast.Decl: + p.nesting = 1 p.useNodeComments = true p.decl(n, ignoreMultiLine) case ast.Spec: + p.nesting = 1 p.useNodeComments = true p.spec(n, 1, false, ignoreMultiLine) case *ast.File: + p.nesting = 0 p.comments = n.Comments p.useNodeComments = n.Comments == nil p.file(n) default: - p.errors <- os.NewError(fmt.Sprintf("printer.Fprint: unsupported node type %T", n)) + p.errors <- fmt.Errorf("printer.Fprint: unsupported node type %T", n) runtime.Goexit() } p.flush(token.Position{Offset: infinity, Line: infinity}, token.EOF) @@ -1045,7 +1114,7 @@ func (cfg *Config) Fprint(output io.Writer, node interface{}) (int, os.Error) { // Fprint "pretty-prints" an AST node to output. // It calls Config.Fprint with default settings. // -func Fprint(output io.Writer, node interface{}) os.Error { - _, err := (&Config{Tabwidth: 8}).Fprint(output, node) // don't care about number of bytes written +func Fprint(output io.Writer, fset *token.FileSet, node interface{}) os.Error { + _, err := (&Config{Tabwidth: 8}).Fprint(output, fset, node) // don't care about number of bytes written return err } diff --git a/src/pkg/go/printer/printer_test.go b/src/pkg/go/printer/printer_test.go index a5de3774a..c66471b92 100644 --- a/src/pkg/go/printer/printer_test.go +++ b/src/pkg/go/printer/printer_test.go @@ -10,6 +10,7 @@ import ( "io/ioutil" "go/ast" "go/parser" + "go/token" "path" "testing" ) @@ -24,6 +25,9 @@ const ( var update = flag.Bool("update", false, "update golden files") +var fset = token.NewFileSet() + + func lineString(text []byte, i int) string { i0 := i for i < len(text) && text[i] != '\n' { @@ -43,7 +47,7 @@ const ( func check(t *testing.T, source, golden string, mode checkMode) { // parse source - prog, err := parser.ParseFile(source, nil, nil, parser.ParseComments) + prog, err := parser.ParseFile(fset, source, nil, parser.ParseComments) if err != nil { t.Error(err) return @@ -63,7 +67,7 @@ func check(t *testing.T, source, golden string, mode checkMode) { // format source var buf bytes.Buffer - if _, err := cfg.Fprint(&buf, prog); err != nil { + if _, err := cfg.Fprint(&buf, fset, prog); err != nil { t.Error(err) } res := buf.Bytes() @@ -112,14 +116,14 @@ type entry struct { // Use gotest -update to create/update the respective golden files. var data = []entry{ - entry{"empty.input", "empty.golden", 0}, - entry{"comments.input", "comments.golden", 0}, - entry{"comments.input", "comments.x", export}, - entry{"linebreaks.input", "linebreaks.golden", 0}, - entry{"expressions.input", "expressions.golden", 0}, - entry{"expressions.input", "expressions.raw", rawFormat}, - entry{"declarations.input", "declarations.golden", 0}, - entry{"statements.input", "statements.golden", 0}, + {"empty.input", "empty.golden", 0}, + {"comments.input", "comments.golden", 0}, + {"comments.input", "comments.x", export}, + {"linebreaks.input", "linebreaks.golden", 0}, + {"expressions.input", "expressions.golden", 0}, + {"expressions.input", "expressions.raw", rawFormat}, + {"declarations.input", "declarations.golden", 0}, + {"statements.input", "statements.golden", 0}, } diff --git a/src/pkg/go/printer/testdata/comments.golden b/src/pkg/go/printer/testdata/comments.golden index 4c9f71d95..200ea332f 100644 --- a/src/pkg/go/printer/testdata/comments.golden +++ b/src/pkg/go/printer/testdata/comments.golden @@ -431,6 +431,38 @@ func _() { } +// Comments immediately adjacent to punctuation (for which the go/printer +// may obly have estimated position information) must remain after the punctuation. +func _() { + _ = T{ + 1, // comment after comma + 2, /* comment after comma */ + 3, // comment after comma + } + _ = T{ + 1, // comment after comma + 2, /* comment after comma */ + 3, // comment after comma + } + _ = T{ + /* comment before literal */ 1, + 2, /* comment before comma - ok to move after comma */ + 3, /* comment before comma - ok to move after comma */ + } + + for i = 0; // comment after semicolon + i < 9; /* comment after semicolon */ + i++ { // comment after opening curly brace + } + + // TODO(gri) the last comment in this example should be aligned */ + for i = 0; // comment after semicolon + i < 9; /* comment before semicolon - ok to move after semicolon */ + i++ /* comment before opening curly brace */ { + } +} + + // Line comments with tabs func _() { var finput *bufio.Reader // input file diff --git a/src/pkg/go/printer/testdata/comments.input b/src/pkg/go/printer/testdata/comments.input index 335e81391..4a9ea4742 100644 --- a/src/pkg/go/printer/testdata/comments.input +++ b/src/pkg/go/printer/testdata/comments.input @@ -429,6 +429,40 @@ func _() { /* closing curly brace should be on new line */ } +// Comments immediately adjacent to punctuation (for which the go/printer +// may obly have estimated position information) must remain after the punctuation. +func _() { + _ = T{ + 1, // comment after comma + 2, /* comment after comma */ + 3 , // comment after comma + } + _ = T{ + 1 ,// comment after comma + 2 ,/* comment after comma */ + 3,// comment after comma + } + _ = T{ + /* comment before literal */1, + 2/* comment before comma - ok to move after comma */, + 3 /* comment before comma - ok to move after comma */ , + } + + for + i=0;// comment after semicolon + i<9;/* comment after semicolon */ + i++{// comment after opening curly brace + } + + // TODO(gri) the last comment in this example should be aligned */ + for + i=0;// comment after semicolon + i<9/* comment before semicolon - ok to move after semicolon */; + i++ /* comment before opening curly brace */ { + } +} + + // Line comments with tabs func _() { var finput *bufio.Reader // input file diff --git a/src/pkg/go/printer/testdata/declarations.golden b/src/pkg/go/printer/testdata/declarations.golden index 67f16b805..1c091b929 100644 --- a/src/pkg/go/printer/testdata/declarations.golden +++ b/src/pkg/go/printer/testdata/declarations.golden @@ -7,10 +7,10 @@ package imports import "io" import ( - _ "io" + _ "io" ) -import _ "io" +import _ "io" import ( "io" @@ -20,40 +20,60 @@ import ( import ( "io" - aLongRename "io" + aLongRename "io" - b "io" + b "io" +) + +import ( + "unrenamed" + renamed "renameMe" + . "io" + _ "io" + "io" + . "os" ) // no newlines between consecutive single imports, but // respect extra line breaks in the source (at most one empty line) -import _ "io" -import _ "io" -import _ "io" +import _ "io" +import _ "io" +import _ "io" -import _ "os" -import _ "os" -import _ "os" +import _ "os" +import _ "os" +import _ "os" -import _ "fmt" -import _ "fmt" -import _ "fmt" +import _ "fmt" +import _ "fmt" +import _ "fmt" import "foo" // a comment import "bar" // a comment import ( - _ "foo" + _ "foo" // a comment "bar" "foo" // a comment "bar" // a comment ) +// comments + renames +import ( + "unrenamed" // a comment + renamed "renameMe" + . "io" /* a comment */ + _ "io/ioutil" // a comment + "io" // testing alignment + . "os" + // a comment +) + // a case that caused problems in the past (comment placement) import ( - . "fmt" + . "fmt" "io" "malloc" // for the malloc count test only "math" @@ -63,7 +83,7 @@ import ( // at least one empty line between declarations of different kind -import _ "io" +import _ "io" var _ int @@ -617,24 +637,79 @@ func _() { // ellipsis parameters -func _(...) func _(...int) func _(...*int) func _(...[]int) func _(...struct{}) func _(bool, ...interface{}) func _(bool, ...func()) -func _(bool, ...func(...)) +func _(bool, ...func(...int)) func _(bool, ...map[string]int) func _(bool, ...chan int) -func _(b bool, x ...) func _(b bool, x ...int) func _(b bool, x ...*int) func _(b bool, x ...[]int) func _(b bool, x ...struct{}) func _(x ...interface{}) func _(x ...func()) -func _(x ...func(...)) +func _(x ...func(...int)) func _(x ...map[string]int) func _(x ...chan int) + + +// these parameter lists must remain multi-line since they are multi-line in the source +func _(bool, +int) { +} +func _(x bool, +y int) { +} +func _(x, +y bool) { +} +func _(bool, // comment +int) { +} +func _(x bool, // comment +y int) { +} +func _(x, // comment +y bool) { +} +func _(bool, // comment +// comment +int) { +} +func _(x bool, // comment +// comment +y int) { +} +func _(x, // comment +// comment +y bool) { +} +func _(bool, +// comment +int) { +} +func _(x bool, +// comment +y int) { +} +func _(x, +// comment +y bool) { +} +func _(x, // comment +y, // comment +z bool) { +} +func _(x, // comment +y, // comment +z bool) { +} +func _(x int, // comment +y float, // comment +z bool) { +} diff --git a/src/pkg/go/printer/testdata/declarations.input b/src/pkg/go/printer/testdata/declarations.input index 095d1ddac..c826462f9 100644 --- a/src/pkg/go/printer/testdata/declarations.input +++ b/src/pkg/go/printer/testdata/declarations.input @@ -25,6 +25,15 @@ import ( b "io" ) +import ( + "unrenamed" + renamed "renameMe" + . "io" + _ "io" + "io" + . "os" +) + // no newlines between consecutive single imports, but // respect extra line breaks in the source (at most one empty line) import _ "io" @@ -51,6 +60,17 @@ import ( "bar" // a comment ) +// comments + renames +import ( + "unrenamed" // a comment + renamed "renameMe" + . "io" /* a comment */ + _ "io/ioutil" // a comment + "io" // testing alignment + . "os" + // a comment +) + // a case that caused problems in the past (comment placement) import ( . "fmt" @@ -605,24 +625,79 @@ func _() { // ellipsis parameters -func _(...) func _(...int) func _(...*int) func _(...[]int) func _(...struct{}) func _(bool, ...interface{}) func _(bool, ...func()) -func _(bool, ...func(...)) +func _(bool, ...func(...int)) func _(bool, ...map[string]int) func _(bool, ...chan int) -func _(b bool, x ...) func _(b bool, x ...int) func _(b bool, x ...*int) func _(b bool, x ...[]int) func _(b bool, x ...struct{}) func _(x ...interface{}) func _(x ...func()) -func _(x ...func(...)) +func _(x ...func(...int)) func _(x ...map[string]int) func _(x ...chan int) + + +// these parameter lists must remain multi-line since they are multi-line in the source +func _(bool, +int) { +} +func _(x bool, +y int) { +} +func _(x, +y bool) { +} +func _(bool, // comment +int) { +} +func _(x bool, // comment +y int) { +} +func _(x, // comment +y bool) { +} +func _(bool, // comment +// comment +int) { +} +func _(x bool, // comment +// comment +y int) { +} +func _(x, // comment +// comment +y bool) { +} +func _(bool, +// comment +int) { +} +func _(x bool, +// comment +y int) { +} +func _(x, +// comment +y bool) { +} +func _(x, // comment +y,// comment +z bool) { +} +func _(x, // comment + y,// comment + z bool) { +} +func _(x int, // comment + y float, // comment + z bool) { +} diff --git a/src/pkg/go/printer/testdata/expressions.golden b/src/pkg/go/printer/testdata/expressions.golden index 95e5502d3..882c7624c 100644 --- a/src/pkg/go/printer/testdata/expressions.golden +++ b/src/pkg/go/printer/testdata/expressions.golden @@ -31,6 +31,9 @@ func _() { _ = 1 + a _ = a + 1 _ = a + b + 1 + _ = s[a] + _ = s[a:] + _ = s[:b] _ = s[1:2] _ = s[a:b] _ = s[0:len(s)] @@ -56,6 +59,7 @@ func _() { _ = s[a : b-c] _ = s[0:] _ = s[a+b] + _ = s[:b-c] _ = s[a+b:] _ = a[a<= 0x80: // not ASCII - r, w = utf8.DecodeRune(S.src[S.offset:]) + r, w = utf8.DecodeRune(S.src[S.rdOffset:]) if r == utf8.RuneError && w == 1 { - S.error(S.pos, "illegal UTF-8 encoding") + S.error(S.offset, "illegal UTF-8 encoding") } } - S.offset += w + S.rdOffset += w S.ch = r } else { - S.pos.Offset = len(S.src) + S.offset = len(S.src) + if S.ch == '\n' { + S.lineOffset = S.offset + S.file.AddLine(S.offset) + } S.ch = -1 // eof } } @@ -80,24 +96,38 @@ const ( InsertSemis // automatically insert semicolons ) +// TODO(gri) Would it be better to simply provide *token.File to Init +// instead of fset, and filename, and then return the file? +// It could cause an error/panic if the provided file.Size() +// doesn't match len(src). -// Init prepares the scanner S to tokenize the text src. Calls to Scan -// will use the error handler err if they encounter a syntax error and -// err is not nil. Also, for each error encountered, the Scanner field -// ErrorCount is incremented by one. The filename parameter is used as -// filename in the token.Position returned by Scan for each token. The -// mode parameter determines how comments and illegal characters are -// handled. +// Init prepares the scanner S to tokenize the text src. It sets the +// scanner at the beginning of the source text, adds a new file with +// the given filename to the file set fset, and returns that file. +// +// Calls to Scan will use the error handler err if they encounter a +// syntax error and err is not nil. Also, for each error encountered, +// the Scanner field ErrorCount is incremented by one. The mode parameter +// determines how comments, illegal characters, and semicolons are handled. // -func (S *Scanner) Init(filename string, src []byte, err ErrorHandler, mode uint) { +func (S *Scanner) Init(fset *token.FileSet, filename string, src []byte, err ErrorHandler, mode uint) *token.File { // Explicitly initialize all fields since a scanner may be reused. + S.file = fset.AddFile(filename, fset.Base(), len(src)) + S.dir, _ = path.Split(filename) S.src = src S.err = err S.mode = mode - S.pos = token.Position{filename, 0, 1, 0} + + S.ch = ' ' S.offset = 0 + S.rdOffset = 0 + S.lineOffset = 0 + S.insertSemi = false S.ErrorCount = 0 + S.next() + + return S.file } @@ -131,111 +161,109 @@ func charString(ch int) string { } -func (S *Scanner) error(pos token.Position, msg string) { +func (S *Scanner) error(offs int, msg string) { if S.err != nil { - S.err.Error(pos, msg) + S.err.Error(S.file.Position(S.file.Pos(offs)), msg) } S.ErrorCount++ } -func (S *Scanner) expect(ch int) { - if S.ch != ch { - S.error(S.pos, "expected "+charString(ch)+", found "+charString(S.ch)) +var prefix = []byte("//line ") + +func (S *Scanner) interpretLineComment(text []byte) { + if bytes.HasPrefix(text, prefix) { + // get filename and line number, if any + if i := bytes.Index(text, []byte{':'}); i > 0 { + if line, err := strconv.Atoi(string(text[i+1:])); err == nil && line > 0 { + // valid //line filename:line comment; + filename := path.Clean(string(text[len(prefix):i])) + if filename[0] != '/' { + // make filename relative to current directory + filename = path.Join(S.dir, filename) + } + // update scanner position + S.file.AddLineInfo(S.lineOffset, filename, line-1) // -1 since comment applies to next line + } + } } - S.next() // always make progress } -var prefix = []byte("line ") - -func (S *Scanner) scanComment(pos token.Position) { - // first '/' already consumed +func (S *Scanner) scanComment() { + // initial '/' already consumed; S.ch == '/' || S.ch == '*' + offs := S.offset - 1 // position of initial '/' if S.ch == '/' { //-style comment - for S.ch >= 0 { + S.next() + for S.ch != '\n' && S.ch >= 0 { S.next() - if S.ch == '\n' { - // '\n' is not part of the comment for purposes of scanning - // (the comment ends on the same line where it started) - if pos.Column == 1 { - text := S.src[pos.Offset+2 : S.pos.Offset] - if bytes.HasPrefix(text, prefix) { - // comment starts at beginning of line with "//line "; - // get filename and line number, if any - i := bytes.Index(text, []byte{':'}) - if i >= 0 { - if line, err := strconv.Atoi(string(text[i+1:])); err == nil && line > 0 { - // valid //line filename:line comment; - // update scanner position - S.pos.Filename = string(text[len(prefix):i]) - S.pos.Line = line - 1 // -1 since the '\n' has not been consumed yet - } - } - } - } - return - } } + if offs == S.lineOffset { + // comment starts at the beginning of the current line + S.interpretLineComment(S.src[offs:S.offset]) + } + return + } - } else { - /*-style comment */ - S.expect('*') - for S.ch >= 0 { - ch := S.ch + /*-style comment */ + S.next() + for S.ch >= 0 { + ch := S.ch + S.next() + if ch == '*' && S.ch == '/' { S.next() - if ch == '*' && S.ch == '/' { - S.next() - return - } + return } } - S.error(pos, "comment not terminated") + S.error(offs, "comment not terminated") } -func (S *Scanner) findNewline(pos token.Position) bool { - // first '/' already consumed; assume S.ch == '/' || S.ch == '*' +func (S *Scanner) findLineEnd() bool { + // initial '/' already consumed + + defer func(offs int) { + // reset scanner state to where it was upon calling findLineEnd + S.ch = '/' + S.offset = offs + S.rdOffset = offs + 1 + S.next() // consume initial '/' again + }(S.offset - 1) - // read ahead until a newline or non-comment token is found - newline := false - for pos1 := pos; S.ch >= 0; { + // read ahead until a newline, EOF, or non-comment token is found + for S.ch == '/' || S.ch == '*' { if S.ch == '/' { //-style comment always contains a newline - newline = true - break + return true } - S.scanComment(pos1) - if pos1.Line < S.pos.Line { - /*-style comment contained a newline */ - newline = true - break + /*-style comment: look for newline */ + S.next() + for S.ch >= 0 { + ch := S.ch + if ch == '\n' { + return true + } + S.next() + if ch == '*' && S.ch == '/' { + S.next() + break + } } S.skipWhitespace() // S.insertSemi is set - if S.ch == '\n' { - newline = true - break + if S.ch < 0 || S.ch == '\n' { + return true } if S.ch != '/' { // non-comment token - break - } - pos1 = S.pos - S.next() - if S.ch != '/' && S.ch != '*' { - // non-comment token - break + return false } + S.next() // consume '/' } - // reset position to where it was upon calling findNewline - S.pos = pos - S.offset = pos.Offset + 1 - S.next() - - return newline + return false } @@ -250,11 +278,11 @@ func isDigit(ch int) bool { func (S *Scanner) scanIdentifier() token.Token { - pos := S.pos.Offset + offs := S.offset for isLetter(S.ch) || isDigit(S.ch) { S.next() } - return token.Lookup(S.src[pos:S.pos.Offset]) + return token.Lookup(S.src[offs:S.offset]) } @@ -278,7 +306,7 @@ func (S *Scanner) scanMantissa(base int) { } -func (S *Scanner) scanNumber(pos token.Position, seenDecimalPoint bool) token.Token { +func (S *Scanner) scanNumber(seenDecimalPoint bool) token.Token { // digitVal(S.ch) < 10 tok := token.INT @@ -290,6 +318,7 @@ func (S *Scanner) scanNumber(pos token.Position, seenDecimalPoint bool) token.To if S.ch == '0' { // int or float + offs := S.offset S.next() if S.ch == 'x' || S.ch == 'X' { // hexadecimal int @@ -309,7 +338,7 @@ func (S *Scanner) scanNumber(pos token.Position, seenDecimalPoint bool) token.To } // octal int if seenDecimalDigit { - S.error(pos, "illegal octal number") + S.error(offs, "illegal octal number") } } goto exit @@ -346,7 +375,7 @@ exit: func (S *Scanner) scanEscape(quote int) { - pos := S.pos + offs := S.offset var i, base, max uint32 switch S.ch { @@ -366,28 +395,33 @@ func (S *Scanner) scanEscape(quote int) { i, base, max = 8, 16, unicode.MaxRune default: S.next() // always make progress - S.error(pos, "unknown escape sequence") + S.error(offs, "unknown escape sequence") return } var x uint32 - for ; i > 0; i-- { + for ; i > 0 && S.ch != quote && S.ch >= 0; i-- { d := uint32(digitVal(S.ch)) - if d > base { - S.error(S.pos, "illegal character in escape sequence") - return + if d >= base { + S.error(S.offset, "illegal character in escape sequence") + break } x = x*base + d S.next() } + // in case of an error, consume remaining chars + for ; i > 0 && S.ch != quote && S.ch >= 0; i-- { + S.next() + } if x > max || 0xd800 <= x && x < 0xe000 { - S.error(pos, "escape sequence is invalid Unicode code point") + S.error(offs, "escape sequence is invalid Unicode code point") } } -func (S *Scanner) scanChar(pos token.Position) { - // '\'' already consumed +func (S *Scanner) scanChar() { + // '\'' opening already consumed + offs := S.offset - 1 n := 0 for S.ch != '\'' { @@ -395,7 +429,7 @@ func (S *Scanner) scanChar(pos token.Position) { n++ S.next() if ch == '\n' || ch < 0 { - S.error(pos, "character literal not terminated") + S.error(offs, "character literal not terminated") n = 1 break } @@ -407,19 +441,20 @@ func (S *Scanner) scanChar(pos token.Position) { S.next() if n != 1 { - S.error(pos, "illegal character literal") + S.error(offs, "illegal character literal") } } -func (S *Scanner) scanString(pos token.Position) { - // '"' already consumed +func (S *Scanner) scanString() { + // '"' opening already consumed + offs := S.offset - 1 for S.ch != '"' { ch := S.ch S.next() if ch == '\n' || ch < 0 { - S.error(pos, "string not terminated") + S.error(offs, "string not terminated") break } if ch == '\\' { @@ -431,14 +466,15 @@ func (S *Scanner) scanString(pos token.Position) { } -func (S *Scanner) scanRawString(pos token.Position) { - // '`' already consumed +func (S *Scanner) scanRawString() { + // '`' opening already consumed + offs := S.offset - 1 for S.ch != '`' { ch := S.ch S.next() if ch < 0 { - S.error(pos, "string not terminated") + S.error(offs, "string not terminated") break } } @@ -499,12 +535,17 @@ func (S *Scanner) switch4(tok0, tok1 token.Token, ch2 int, tok2, tok3 token.Toke } -var semicolon = []byte{';'} +var newline = []byte{'\n'} // Scan scans the next token and returns the token position pos, // the token tok, and the literal text lit corresponding to the // token. The source end is indicated by token.EOF. // +// If the returned token is token.SEMICOLON, the corresponding +// literal value is ";" if the semicolon was present in the source, +// and "\n" if the semicolon was inserted because of a newline or +// at EOF. +// // For more tolerant parsing, Scan will return a valid token if // possible even if a syntax error was encountered. Thus, even // if the resulting token sequence contains no illegal tokens, @@ -512,13 +553,18 @@ var semicolon = []byte{';'} // must check the scanner's ErrorCount or the number of calls // of the error handler, if there was one installed. // -func (S *Scanner) Scan() (pos token.Position, tok token.Token, lit []byte) { +// Scan adds line information to the file added to the file +// set with Init. Token positions are relative to that file +// and thus relative to the file set. +// +func (S *Scanner) Scan() (token.Pos, token.Token, []byte) { scanAgain: S.skipWhitespace() // current token start insertSemi := false - pos, tok = S.pos, token.ILLEGAL + offs := S.offset + tok := token.ILLEGAL // determine token value switch ch := S.ch; { @@ -530,36 +576,40 @@ scanAgain: } case digitVal(ch) < 10: insertSemi = true - tok = S.scanNumber(pos, false) + tok = S.scanNumber(false) default: S.next() // always make progress switch ch { case -1: + if S.insertSemi { + S.insertSemi = false // EOF consumed + return S.file.Pos(offs), token.SEMICOLON, newline + } tok = token.EOF case '\n': // we only reach here if S.insertSemi was // set in the first place and exited early // from S.skipWhitespace() S.insertSemi = false // newline consumed - return pos, token.SEMICOLON, semicolon + return S.file.Pos(offs), token.SEMICOLON, newline case '"': insertSemi = true tok = token.STRING - S.scanString(pos) + S.scanString() case '\'': insertSemi = true tok = token.CHAR - S.scanChar(pos) + S.scanChar() case '`': insertSemi = true tok = token.STRING - S.scanRawString(pos) + S.scanRawString() case ':': tok = S.switch2(token.COLON, token.DEFINE) case '.': if digitVal(S.ch) < 10 { insertSemi = true - tok = S.scanNumber(pos, true) + tok = S.scanNumber(true) } else if S.ch == '.' { S.next() if S.ch == '.' { @@ -603,15 +653,15 @@ scanAgain: case '/': if S.ch == '/' || S.ch == '*' { // comment - if S.insertSemi && S.findNewline(pos) { + if S.insertSemi && S.findLineEnd() { // reset position to the beginning of the comment - S.pos = pos - S.offset = pos.Offset + 1 S.ch = '/' + S.offset = offs + S.rdOffset = offs + 1 S.insertSemi = false // newline consumed - return pos, token.SEMICOLON, semicolon + return S.file.Pos(offs), token.SEMICOLON, newline } - S.scanComment(pos) + S.scanComment() if S.mode&ScanComments == 0 { // skip comment S.insertSemi = false // newline consumed @@ -649,7 +699,7 @@ scanAgain: tok = S.switch3(token.OR, token.OR_ASSIGN, '|', token.LOR) default: if S.mode&AllowIllegalChars == 0 { - S.error(pos, "illegal character "+charString(ch)) + S.error(offs, "illegal character "+charString(ch)) } insertSemi = S.insertSemi // preserve insertSemi info } @@ -658,21 +708,5 @@ scanAgain: if S.mode&InsertSemis != 0 { S.insertSemi = insertSemi } - return pos, tok, S.src[pos.Offset:S.pos.Offset] -} - - -// Tokenize calls a function f with the token position, token value, and token -// text for each token in the source src. The other parameters have the same -// meaning as for the Init function. Tokenize keeps scanning until f returns -// false (usually when the token value is token.EOF). The result is the number -// of errors encountered. -// -func Tokenize(filename string, src []byte, err ErrorHandler, mode uint, f func(pos token.Position, tok token.Token, lit []byte) bool) int { - var s Scanner - s.Init(filename, src, err, mode) - for f(s.Scan()) { - // action happens in f - } - return s.ErrorCount + return S.file.Pos(offs), tok, S.src[offs:S.offset] } diff --git a/src/pkg/go/scanner/scanner_test.go b/src/pkg/go/scanner/scanner_test.go index 002a81dd9..b1004f89d 100644 --- a/src/pkg/go/scanner/scanner_test.go +++ b/src/pkg/go/scanner/scanner_test.go @@ -11,6 +11,9 @@ import ( ) +var fset = token.NewFileSet() + + const /* class */ ( special = iota literal @@ -41,136 +44,136 @@ type elt struct { var tokens = [...]elt{ // Special tokens - elt{token.COMMENT, "/* a comment */", special}, - elt{token.COMMENT, "// a comment \n", special}, + {token.COMMENT, "/* a comment */", special}, + {token.COMMENT, "// a comment \n", special}, // Identifiers and basic type literals - elt{token.IDENT, "foobar", literal}, - elt{token.IDENT, "a۰۱۸", literal}, - elt{token.IDENT, "foo६४", literal}, - elt{token.IDENT, "bar9876", literal}, - elt{token.INT, "0", literal}, - elt{token.INT, "1", literal}, - elt{token.INT, "123456789012345678890", literal}, - elt{token.INT, "01234567", literal}, - elt{token.INT, "0xcafebabe", literal}, - elt{token.FLOAT, "0.", literal}, - elt{token.FLOAT, ".0", literal}, - elt{token.FLOAT, "3.14159265", literal}, - elt{token.FLOAT, "1e0", literal}, - elt{token.FLOAT, "1e+100", literal}, - elt{token.FLOAT, "1e-100", literal}, - elt{token.FLOAT, "2.71828e-1000", literal}, - elt{token.IMAG, "0i", literal}, - elt{token.IMAG, "1i", literal}, - elt{token.IMAG, "012345678901234567889i", literal}, - elt{token.IMAG, "123456789012345678890i", literal}, - elt{token.IMAG, "0.i", literal}, - elt{token.IMAG, ".0i", literal}, - elt{token.IMAG, "3.14159265i", literal}, - elt{token.IMAG, "1e0i", literal}, - elt{token.IMAG, "1e+100i", literal}, - elt{token.IMAG, "1e-100i", literal}, - elt{token.IMAG, "2.71828e-1000i", literal}, - elt{token.CHAR, "'a'", literal}, - elt{token.CHAR, "'\\000'", literal}, - elt{token.CHAR, "'\\xFF'", literal}, - elt{token.CHAR, "'\\uff16'", literal}, - elt{token.CHAR, "'\\U0000ff16'", literal}, - elt{token.STRING, "`foobar`", literal}, - elt{token.STRING, "`" + `foo + {token.IDENT, "foobar", literal}, + {token.IDENT, "a۰۱۸", literal}, + {token.IDENT, "foo६४", literal}, + {token.IDENT, "bar9876", literal}, + {token.INT, "0", literal}, + {token.INT, "1", literal}, + {token.INT, "123456789012345678890", literal}, + {token.INT, "01234567", literal}, + {token.INT, "0xcafebabe", literal}, + {token.FLOAT, "0.", literal}, + {token.FLOAT, ".0", literal}, + {token.FLOAT, "3.14159265", literal}, + {token.FLOAT, "1e0", literal}, + {token.FLOAT, "1e+100", literal}, + {token.FLOAT, "1e-100", literal}, + {token.FLOAT, "2.71828e-1000", literal}, + {token.IMAG, "0i", literal}, + {token.IMAG, "1i", literal}, + {token.IMAG, "012345678901234567889i", literal}, + {token.IMAG, "123456789012345678890i", literal}, + {token.IMAG, "0.i", literal}, + {token.IMAG, ".0i", literal}, + {token.IMAG, "3.14159265i", literal}, + {token.IMAG, "1e0i", literal}, + {token.IMAG, "1e+100i", literal}, + {token.IMAG, "1e-100i", literal}, + {token.IMAG, "2.71828e-1000i", literal}, + {token.CHAR, "'a'", literal}, + {token.CHAR, "'\\000'", literal}, + {token.CHAR, "'\\xFF'", literal}, + {token.CHAR, "'\\uff16'", literal}, + {token.CHAR, "'\\U0000ff16'", literal}, + {token.STRING, "`foobar`", literal}, + {token.STRING, "`" + `foo bar` + "`", literal, }, // Operators and delimitors - elt{token.ADD, "+", operator}, - elt{token.SUB, "-", operator}, - elt{token.MUL, "*", operator}, - elt{token.QUO, "/", operator}, - elt{token.REM, "%", operator}, - - elt{token.AND, "&", operator}, - elt{token.OR, "|", operator}, - elt{token.XOR, "^", operator}, - elt{token.SHL, "<<", operator}, - elt{token.SHR, ">>", operator}, - elt{token.AND_NOT, "&^", operator}, - - elt{token.ADD_ASSIGN, "+=", operator}, - elt{token.SUB_ASSIGN, "-=", operator}, - elt{token.MUL_ASSIGN, "*=", operator}, - elt{token.QUO_ASSIGN, "/=", operator}, - elt{token.REM_ASSIGN, "%=", operator}, - - elt{token.AND_ASSIGN, "&=", operator}, - elt{token.OR_ASSIGN, "|=", operator}, - elt{token.XOR_ASSIGN, "^=", operator}, - elt{token.SHL_ASSIGN, "<<=", operator}, - elt{token.SHR_ASSIGN, ">>=", operator}, - elt{token.AND_NOT_ASSIGN, "&^=", operator}, - - elt{token.LAND, "&&", operator}, - elt{token.LOR, "||", operator}, - elt{token.ARROW, "<-", operator}, - elt{token.INC, "++", operator}, - elt{token.DEC, "--", operator}, - - elt{token.EQL, "==", operator}, - elt{token.LSS, "<", operator}, - elt{token.GTR, ">", operator}, - elt{token.ASSIGN, "=", operator}, - elt{token.NOT, "!", operator}, - - elt{token.NEQ, "!=", operator}, - elt{token.LEQ, "<=", operator}, - elt{token.GEQ, ">=", operator}, - elt{token.DEFINE, ":=", operator}, - elt{token.ELLIPSIS, "...", operator}, - - elt{token.LPAREN, "(", operator}, - elt{token.LBRACK, "[", operator}, - elt{token.LBRACE, "{", operator}, - elt{token.COMMA, ",", operator}, - elt{token.PERIOD, ".", operator}, - - elt{token.RPAREN, ")", operator}, - elt{token.RBRACK, "]", operator}, - elt{token.RBRACE, "}", operator}, - elt{token.SEMICOLON, ";", operator}, - elt{token.COLON, ":", operator}, + {token.ADD, "+", operator}, + {token.SUB, "-", operator}, + {token.MUL, "*", operator}, + {token.QUO, "/", operator}, + {token.REM, "%", operator}, + + {token.AND, "&", operator}, + {token.OR, "|", operator}, + {token.XOR, "^", operator}, + {token.SHL, "<<", operator}, + {token.SHR, ">>", operator}, + {token.AND_NOT, "&^", operator}, + + {token.ADD_ASSIGN, "+=", operator}, + {token.SUB_ASSIGN, "-=", operator}, + {token.MUL_ASSIGN, "*=", operator}, + {token.QUO_ASSIGN, "/=", operator}, + {token.REM_ASSIGN, "%=", operator}, + + {token.AND_ASSIGN, "&=", operator}, + {token.OR_ASSIGN, "|=", operator}, + {token.XOR_ASSIGN, "^=", operator}, + {token.SHL_ASSIGN, "<<=", operator}, + {token.SHR_ASSIGN, ">>=", operator}, + {token.AND_NOT_ASSIGN, "&^=", operator}, + + {token.LAND, "&&", operator}, + {token.LOR, "||", operator}, + {token.ARROW, "<-", operator}, + {token.INC, "++", operator}, + {token.DEC, "--", operator}, + + {token.EQL, "==", operator}, + {token.LSS, "<", operator}, + {token.GTR, ">", operator}, + {token.ASSIGN, "=", operator}, + {token.NOT, "!", operator}, + + {token.NEQ, "!=", operator}, + {token.LEQ, "<=", operator}, + {token.GEQ, ">=", operator}, + {token.DEFINE, ":=", operator}, + {token.ELLIPSIS, "...", operator}, + + {token.LPAREN, "(", operator}, + {token.LBRACK, "[", operator}, + {token.LBRACE, "{", operator}, + {token.COMMA, ",", operator}, + {token.PERIOD, ".", operator}, + + {token.RPAREN, ")", operator}, + {token.RBRACK, "]", operator}, + {token.RBRACE, "}", operator}, + {token.SEMICOLON, ";", operator}, + {token.COLON, ":", operator}, // Keywords - elt{token.BREAK, "break", keyword}, - elt{token.CASE, "case", keyword}, - elt{token.CHAN, "chan", keyword}, - elt{token.CONST, "const", keyword}, - elt{token.CONTINUE, "continue", keyword}, - - elt{token.DEFAULT, "default", keyword}, - elt{token.DEFER, "defer", keyword}, - elt{token.ELSE, "else", keyword}, - elt{token.FALLTHROUGH, "fallthrough", keyword}, - elt{token.FOR, "for", keyword}, - - elt{token.FUNC, "func", keyword}, - elt{token.GO, "go", keyword}, - elt{token.GOTO, "goto", keyword}, - elt{token.IF, "if", keyword}, - elt{token.IMPORT, "import", keyword}, - - elt{token.INTERFACE, "interface", keyword}, - elt{token.MAP, "map", keyword}, - elt{token.PACKAGE, "package", keyword}, - elt{token.RANGE, "range", keyword}, - elt{token.RETURN, "return", keyword}, - - elt{token.SELECT, "select", keyword}, - elt{token.STRUCT, "struct", keyword}, - elt{token.SWITCH, "switch", keyword}, - elt{token.TYPE, "type", keyword}, - elt{token.VAR, "var", keyword}, + {token.BREAK, "break", keyword}, + {token.CASE, "case", keyword}, + {token.CHAN, "chan", keyword}, + {token.CONST, "const", keyword}, + {token.CONTINUE, "continue", keyword}, + + {token.DEFAULT, "default", keyword}, + {token.DEFER, "defer", keyword}, + {token.ELSE, "else", keyword}, + {token.FALLTHROUGH, "fallthrough", keyword}, + {token.FOR, "for", keyword}, + + {token.FUNC, "func", keyword}, + {token.GO, "go", keyword}, + {token.GOTO, "goto", keyword}, + {token.IF, "if", keyword}, + {token.IMPORT, "import", keyword}, + + {token.INTERFACE, "interface", keyword}, + {token.MAP, "map", keyword}, + {token.PACKAGE, "package", keyword}, + {token.RANGE, "range", keyword}, + {token.RETURN, "return", keyword}, + + {token.SELECT, "select", keyword}, + {token.STRUCT, "struct", keyword}, + {token.SWITCH, "switch", keyword}, + {token.TYPE, "type", keyword}, + {token.VAR, "var", keyword}, } @@ -196,18 +199,19 @@ func newlineCount(s string) int { } -func checkPos(t *testing.T, lit string, pos, expected token.Position) { +func checkPos(t *testing.T, lit string, p token.Pos, expected token.Position) { + pos := fset.Position(p) if pos.Filename != expected.Filename { - t.Errorf("bad filename for %s: got %s, expected %s", lit, pos.Filename, expected.Filename) + t.Errorf("bad filename for %q: got %s, expected %s", lit, pos.Filename, expected.Filename) } if pos.Offset != expected.Offset { - t.Errorf("bad position for %s: got %d, expected %d", lit, pos.Offset, expected.Offset) + t.Errorf("bad position for %q: got %d, expected %d", lit, pos.Offset, expected.Offset) } if pos.Line != expected.Line { - t.Errorf("bad line for %s: got %d, expected %d", lit, pos.Line, expected.Line) + t.Errorf("bad line for %q: got %d, expected %d", lit, pos.Line, expected.Line) } if pos.Column != expected.Column { - t.Errorf("bad column for %s: got %d, expected %d", lit, pos.Column, expected.Column) + t.Errorf("bad column for %q: got %d, expected %d", lit, pos.Column, expected.Column) } } @@ -219,66 +223,76 @@ func TestScan(t *testing.T) { for _, e := range tokens { src += e.lit + whitespace } - src_linecount := newlineCount(src) + src_linecount := newlineCount(src) + 1 whitespace_linecount := newlineCount(whitespace) // verify scan + var s Scanner + s.Init(fset, "", []byte(src), &testErrorHandler{t}, ScanComments) index := 0 epos := token.Position{"", 0, 1, 1} // expected position - nerrors := Tokenize("", []byte(src), &testErrorHandler{t}, ScanComments, - func(pos token.Position, tok token.Token, litb []byte) bool { - e := elt{token.EOF, "", special} - if index < len(tokens) { - e = tokens[index] - } - lit := string(litb) - if tok == token.EOF { - lit = "" - epos.Line = src_linecount - epos.Column = 1 - } - checkPos(t, lit, pos, epos) - if tok != e.tok { - t.Errorf("bad token for %q: got %s, expected %s", lit, tok.String(), e.tok.String()) - } - if e.tok.IsLiteral() && lit != e.lit { - t.Errorf("bad literal for %q: got %q, expected %q", lit, lit, e.lit) - } - if tokenclass(tok) != e.class { - t.Errorf("bad class for %q: got %d, expected %d", lit, tokenclass(tok), e.class) - } - epos.Offset += len(lit) + len(whitespace) - epos.Line += newlineCount(lit) + whitespace_linecount - if tok == token.COMMENT && litb[1] == '/' { - // correct for unaccounted '/n' in //-style comment - epos.Offset++ - epos.Line++ - } - index++ - return tok != token.EOF - }) - if nerrors != 0 { - t.Errorf("found %d errors", nerrors) + for { + pos, tok, litb := s.Scan() + e := elt{token.EOF, "", special} + if index < len(tokens) { + e = tokens[index] + } + lit := string(litb) + if tok == token.EOF { + lit = "" + epos.Line = src_linecount + epos.Column = 1 + } + checkPos(t, lit, pos, epos) + if tok != e.tok { + t.Errorf("bad token for %q: got %s, expected %s", lit, tok.String(), e.tok.String()) + } + if e.tok.IsLiteral() && lit != e.lit { + t.Errorf("bad literal for %q: got %q, expected %q", lit, lit, e.lit) + } + if tokenclass(tok) != e.class { + t.Errorf("bad class for %q: got %d, expected %d", lit, tokenclass(tok), e.class) + } + epos.Offset += len(lit) + len(whitespace) + epos.Line += newlineCount(lit) + whitespace_linecount + if tok == token.COMMENT && litb[1] == '/' { + // correct for unaccounted '/n' in //-style comment + epos.Offset++ + epos.Line++ + } + index++ + if tok == token.EOF { + break + } + } + if s.ErrorCount != 0 { + t.Errorf("found %d errors", s.ErrorCount) } } func checkSemi(t *testing.T, line string, mode uint) { var S Scanner - S.Init("TestSemis", []byte(line), nil, mode) + file := S.Init(fset, "TestSemis", []byte(line), nil, mode) pos, tok, lit := S.Scan() for tok != token.EOF { if tok == token.ILLEGAL { + // the illegal token literal indicates what + // kind of semicolon literal to expect + semiLit := "\n" + if lit[0] == '#' { + semiLit = ";" + } // next token must be a semicolon - offs := pos.Offset + 1 + semiPos := file.Position(pos) + semiPos.Offset++ + semiPos.Column++ pos, tok, lit = S.Scan() if tok == token.SEMICOLON { - if pos.Offset != offs { - t.Errorf("bad offset for %q: got %d, expected %d", line, pos.Offset, offs) - } - if string(lit) != ";" { - t.Errorf(`bad literal for %q: got %q, expected ";"`, line, lit) + if string(lit) != semiLit { + t.Errorf(`bad literal for %q: got %q, expected %q`, line, lit, semiLit) } + checkPos(t, line, pos, semiPos) } else { t.Errorf("bad token for %q: got %s, expected ;", line, tok.String()) } @@ -291,9 +305,10 @@ func checkSemi(t *testing.T, line string, mode uint) { var lines = []string{ - // the $ character indicates where a semicolon is expected + // # indicates a semicolon present in the source + // $ indicates an automatically inserted semicolon "", - "$;", + "#;", "foo$\n", "123$\n", "1.2$\n", @@ -354,7 +369,7 @@ var lines = []string{ ")$\n", "]$\n", "}$\n", - "$;\n", + "#;\n", ":\n", "break$\n", @@ -388,57 +403,66 @@ var lines = []string{ "var\n", "foo$//comment\n", + "foo$//comment", "foo$/*comment*/\n", "foo$/*\n*/", "foo$/*comment*/ \n", "foo$/*\n*/ ", + "foo $// comment\n", + "foo $// comment", "foo $/*comment*/\n", "foo $/*\n*/", - - "foo $/*comment*/\n", + "foo $/* */ /* \n */ bar$/**/\n", "foo $/*0*/ /*1*/ /*2*/\n", + "foo $/*comment*/ \n", "foo $/*0*/ /*1*/ /*2*/ \n", - "foo $/**/ /*-------------*/ /*----\n*/bar $/* \n*/baa", + "foo $/**/ /*-------------*/ /*----\n*/bar $/* \n*/baa$\n", + "foo $/* an EOF terminates a line */", + "foo $/* an EOF terminates a line */ /*", + "foo $/* an EOF terminates a line */ //", "package main$\n\nfunc main() {\n\tif {\n\t\treturn /* */ }$\n}$\n", + "package main$", } func TestSemis(t *testing.T) { for _, line := range lines { checkSemi(t, line, AllowIllegalChars|InsertSemis) - } - for _, line := range lines { checkSemi(t, line, AllowIllegalChars|InsertSemis|ScanComments) + + // if the input ended in newlines, the input must tokenize the + // same with or without those newlines + for i := len(line) - 1; i >= 0 && line[i] == '\n'; i-- { + checkSemi(t, line[0:i], AllowIllegalChars|InsertSemis) + checkSemi(t, line[0:i], AllowIllegalChars|InsertSemis|ScanComments) + } } } -type seg struct { +var segments = []struct { srcline string // a line of source text filename string // filename for current token line int // line number for current token -} - - -var segments = []seg{ +}{ // exactly one token per line since the test consumes one token per segment - seg{" line1", "TestLineComments", 1}, - seg{"\nline2", "TestLineComments", 2}, - seg{"\nline3 //line File1.go:100", "TestLineComments", 3}, // bad line comment, ignored - seg{"\nline4", "TestLineComments", 4}, - seg{"\n//line File1.go:100\n line100", "File1.go", 100}, - seg{"\n//line File2.go:200\n line200", "File2.go", 200}, - seg{"\n//line :1\n line1", "", 1}, - seg{"\n//line foo:42\n line42", "foo", 42}, - seg{"\n //line foo:42\n line44", "foo", 44}, // bad line comment, ignored - seg{"\n//line foo 42\n line46", "foo", 46}, // bad line comment, ignored - seg{"\n//line foo:42 extra text\n line48", "foo", 48}, // bad line comment, ignored - seg{"\n//line foo:42\n line42", "foo", 42}, - seg{"\n//line foo:42\n line42", "foo", 42}, - seg{"\n//line File1.go:100\n line100", "File1.go", 100}, + {" line1", "dir/TestLineComments", 1}, + {"\nline2", "dir/TestLineComments", 2}, + {"\nline3 //line File1.go:100", "dir/TestLineComments", 3}, // bad line comment, ignored + {"\nline4", "dir/TestLineComments", 4}, + {"\n//line File1.go:100\n line100", "dir/File1.go", 100}, + {"\n//line File2.go:200\n line200", "dir/File2.go", 200}, + {"\n//line :1\n line1", "dir", 1}, + {"\n//line foo:42\n line42", "dir/foo", 42}, + {"\n //line foo:42\n line44", "dir/foo", 44}, // bad line comment, ignored + {"\n//line foo 42\n line46", "dir/foo", 46}, // bad line comment, ignored + {"\n//line foo:42 extra text\n line48", "dir/foo", 48}, // bad line comment, ignored + {"\n//line /bar:42\n line42", "/bar", 42}, + {"\n//line ./foo:42\n line42", "dir/foo", 42}, + {"\n//line a/b/c/File1.go:100\n line100", "dir/a/b/c/File1.go", 100}, } @@ -452,10 +476,11 @@ func TestLineComments(t *testing.T) { // verify scan var S Scanner - S.Init("TestLineComments", []byte(src), nil, 0) + file := S.Init(fset, "dir/TestLineComments", []byte(src), nil, 0) for _, s := range segments { - pos, _, lit := S.Scan() - checkPos(t, string(lit), pos, token.Position{s.filename, pos.Offset, s.line, pos.Column}) + p, _, lit := S.Scan() + pos := file.Position(p) + checkPos(t, string(lit), p, token.Position{s.filename, pos.Offset, s.line, pos.Column}) } if S.ErrorCount != 0 { @@ -469,7 +494,11 @@ func TestInit(t *testing.T) { var s Scanner // 1st init - s.Init("", []byte("if true { }"), nil, 0) + src1 := "if true { }" + f1 := s.Init(fset, "", []byte(src1), nil, 0) + if f1.Size() != len(src1) { + t.Errorf("bad file size: got %d, expected %d", f1.Size(), len(src1)) + } s.Scan() // if s.Scan() // true _, tok, _ := s.Scan() // { @@ -478,7 +507,11 @@ func TestInit(t *testing.T) { } // 2nd init - s.Init("", []byte("go true { ]"), nil, 0) + src2 := "go true { ]" + f2 := s.Init(fset, "", []byte(src2), nil, 0) + if f2.Size() != len(src2) { + t.Errorf("bad file size: got %d, expected %d", f2.Size(), len(src2)) + } _, tok, _ = s.Scan() // go if tok != token.GO { t.Errorf("bad token: got %s, expected %s", tok.String(), token.GO) @@ -494,11 +527,11 @@ func TestIllegalChars(t *testing.T) { var s Scanner const src = "*?*$*@*" - s.Init("", []byte(src), &testErrorHandler{t}, AllowIllegalChars) + file := s.Init(fset, "", []byte(src), &testErrorHandler{t}, AllowIllegalChars) for offs, ch := range src { pos, tok, lit := s.Scan() - if pos.Offset != offs { - t.Errorf("bad position for %s: got %d, expected %d", string(lit), pos.Offset, offs) + if poffs := file.Offset(pos); poffs != offs { + t.Errorf("bad position for %s: got %d, expected %d", string(lit), poffs, offs) } if tok == token.ILLEGAL && string(lit) != string(ch) { t.Errorf("bad token: got %s, expected %s", string(lit), string(ch)) @@ -522,10 +555,13 @@ func TestStdErrorHander(t *testing.T) { "@ @ @" // original file, line 1 again v := new(ErrorVector) - nerrors := Tokenize("File1", []byte(src), v, 0, - func(pos token.Position, tok token.Token, litb []byte) bool { - return tok != token.EOF - }) + var s Scanner + s.Init(fset, "File1", []byte(src), v, 0) + for { + if _, tok, _ := s.Scan(); tok == token.EOF { + break + } + } list := v.GetErrorList(Raw) if len(list) != 9 { @@ -545,8 +581,8 @@ func TestStdErrorHander(t *testing.T) { PrintError(os.Stderr, list) } - if v.ErrorCount() != nerrors { - t.Errorf("found %d errors, expected %d", v.ErrorCount(), nerrors) + if v.ErrorCount() != s.ErrorCount { + t.Errorf("found %d errors, expected %d", v.ErrorCount(), s.ErrorCount) } } @@ -568,7 +604,7 @@ func (h *errorCollector) Error(pos token.Position, msg string) { func checkError(t *testing.T, src string, tok token.Token, pos int, err string) { var s Scanner var h errorCollector - s.Init("", []byte(src), &h, ScanComments) + s.Init(fset, "", []byte(src), &h, ScanComments) _, tok0, _ := s.Scan() _, tok1, _ := s.Scan() if tok0 != tok { @@ -593,28 +629,34 @@ func checkError(t *testing.T, src string, tok token.Token, pos int, err string) } -type srcerr struct { +var errors = []struct { src string tok token.Token pos int err string -} - -var errors = []srcerr{ - srcerr{"\"\"", token.STRING, 0, ""}, - srcerr{"\"", token.STRING, 0, "string not terminated"}, - srcerr{"/**/", token.COMMENT, 0, ""}, - srcerr{"/*", token.COMMENT, 0, "comment not terminated"}, - srcerr{"//\n", token.COMMENT, 0, ""}, - srcerr{"//", token.COMMENT, 0, "comment not terminated"}, - srcerr{"077", token.INT, 0, ""}, - srcerr{"078.", token.FLOAT, 0, ""}, - srcerr{"07801234567.", token.FLOAT, 0, ""}, - srcerr{"078e0", token.FLOAT, 0, ""}, - srcerr{"078", token.INT, 0, "illegal octal number"}, - srcerr{"07800000009", token.INT, 0, "illegal octal number"}, - srcerr{"\"abc\x00def\"", token.STRING, 4, "illegal character NUL"}, - srcerr{"\"abc\x80def\"", token.STRING, 4, "illegal UTF-8 encoding"}, +}{ + {`#`, token.ILLEGAL, 0, "illegal character '#' (U+23)"}, + {`' '`, token.CHAR, 0, ""}, + {`''`, token.CHAR, 0, "illegal character literal"}, + {`'\8'`, token.CHAR, 2, "unknown escape sequence"}, + {`'\08'`, token.CHAR, 3, "illegal character in escape sequence"}, + {`'\x0g'`, token.CHAR, 4, "illegal character in escape sequence"}, + {`'\Uffffffff'`, token.CHAR, 2, "escape sequence is invalid Unicode code point"}, + {`'`, token.CHAR, 0, "character literal not terminated"}, + {`""`, token.STRING, 0, ""}, + {`"`, token.STRING, 0, "string not terminated"}, + {"``", token.STRING, 0, ""}, + {"`", token.STRING, 0, "string not terminated"}, + {"/**/", token.COMMENT, 0, ""}, + {"/*", token.COMMENT, 0, "comment not terminated"}, + {"077", token.INT, 0, ""}, + {"078.", token.FLOAT, 0, ""}, + {"07801234567.", token.FLOAT, 0, ""}, + {"078e0", token.FLOAT, 0, ""}, + {"078", token.INT, 0, "illegal octal number"}, + {"07800000009", token.INT, 0, "illegal octal number"}, + {"\"abc\x00def\"", token.STRING, 4, "illegal character NUL"}, + {"\"abc\x80def\"", token.STRING, 4, "illegal UTF-8 encoding"}, } diff --git a/src/pkg/go/token/Makefile b/src/pkg/go/token/Makefile index 629196c5d..4a4e64dc8 100644 --- a/src/pkg/go/token/Makefile +++ b/src/pkg/go/token/Makefile @@ -2,10 +2,11 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=go/token GOFILES=\ + position.go\ token.go\ include ../../../Make.pkg diff --git a/src/pkg/go/token/position.go b/src/pkg/go/token/position.go new file mode 100644 index 000000000..0044a0ed7 --- /dev/null +++ b/src/pkg/go/token/position.go @@ -0,0 +1,409 @@ +// 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. + +// TODO(gri) consider making this a separate package outside the go directory. + +package token + +import ( + "fmt" + "sort" + "sync" +) + + +// Position describes an arbitrary source position +// including the file, line, and column location. +// A Position is valid if the line number is > 0. +// +type Position struct { + Filename string // filename, if any + Offset int // offset, starting at 0 + Line int // line number, starting at 1 + Column int // column number, starting at 1 (character count) +} + + +// IsValid returns true if the position is valid. +func (pos *Position) IsValid() bool { return pos.Line > 0 } + + +// String returns a string in one of several forms: +// +// file:line:column valid position with file name +// line:column valid position without file name +// file invalid position with file name +// - invalid position without file name +// +func (pos Position) String() string { + s := pos.Filename + if pos.IsValid() { + if s != "" { + s += ":" + } + s += fmt.Sprintf("%d:%d", pos.Line, pos.Column) + } + if s == "" { + s = "-" + } + return s +} + + +// Pos is a compact encoding of a source position within a file set. +// It can be converted into a Position for a more convenient, but much +// larger, representation. +// +// The Pos value for a given file is a number in the range [base, base+size], +// where base and size are specified when adding the file to the file set via +// AddFile. +// +// To create the Pos value for a specific source offset, first add +// the respective file to the current file set (via FileSet.AddFile) +// and then call File.Pos(offset) for that file. Given a Pos value p +// for a specific file set fset, the corresponding Position value is +// obtained by calling fset.Position(p). +// +// Pos values can be compared directly with the usual comparison operators: +// If two Pos values p and q are in the same file, comparing p and q is +// equivalent to comparing the respective source file offsets. If p and q +// are in different files, p < q is true if the file implied by p was added +// to the respective file set before the file implied by q. +// +type Pos int + + +// The zero value for Pos is NoPos; there is no file and line information +// associated with it, and NoPos().IsValid() is false. NoPos is always +// smaller than any other Pos value. The corresponding Position value +// for NoPos is the zero value for Position. +// +const NoPos Pos = 0 + + +// IsValid returns true if the position is valid. +func (p Pos) IsValid() bool { + return p != NoPos +} + + +func searchFiles(a []*File, x int) int { + return sort.Search(len(a), func(i int) bool { return a[i].base > x }) - 1 +} + + +func (s *FileSet) file(p Pos) *File { + if i := searchFiles(s.files, int(p)); i >= 0 { + f := s.files[i] + // f.base <= int(p) by definition of searchFiles + if int(p) <= f.base+f.size { + return f + } + } + return nil +} + + +// File returns the file which contains the position p. +// If no such file is found (for instance for p == NoPos), +// the result is nil. +// +func (s *FileSet) File(p Pos) (f *File) { + if p != NoPos { + s.mutex.RLock() + f = s.file(p) + s.mutex.RUnlock() + } + return +} + + +func (f *File) position(p Pos) (pos Position) { + offset := int(p) - f.base + pos.Offset = offset + pos.Filename, pos.Line, pos.Column = f.info(offset) + return +} + + +// Position converts a Pos in the fileset into a general Position. +func (s *FileSet) Position(p Pos) (pos Position) { + if p != NoPos { + // TODO(gri) consider optimizing the case where p + // is in the last file addded, or perhaps + // looked at - will eliminate one level + // of search + s.mutex.RLock() + if f := s.file(p); f != nil { + pos = f.position(p) + } + s.mutex.RUnlock() + } + return +} + + +type lineInfo struct { + offset int + filename string + line int +} + + +// AddLineInfo adds alternative file and line number information for +// a given file offset. The offset must be larger than the offset for +// the previously added alternative line info and not larger than the +// file size; otherwise the information is ignored. +// +// AddLineInfo is typically used to register alternative position +// information for //line filename:line comments in source files. +// +func (f *File) AddLineInfo(offset int, filename string, line int) { + f.set.mutex.Lock() + if i := len(f.infos); i == 0 || f.infos[i-1].offset < offset && offset <= f.size { + f.infos = append(f.infos, lineInfo{offset, filename, line}) + } + f.set.mutex.Unlock() +} + + +// A File is a handle for a file belonging to a FileSet. +// A File has a name, size, and line offset table. +// +type File struct { + set *FileSet + name string // file name as provided to AddFile + base int // Pos value range for this file is [base...base+size] + size int // file size as provided to AddFile + + // lines and infos are protected by set.mutex + lines []int + infos []lineInfo +} + + +// Name returns the file name of file f as registered with AddFile. +func (f *File) Name() string { + return f.name +} + + +// Base returns the base offset of file f as registered with AddFile. +func (f *File) Base() int { + return f.base +} + + +// Size returns the size of file f as registered with AddFile. +func (f *File) Size() int { + return f.size +} + + +// LineCount returns the number of lines in file f. +func (f *File) LineCount() int { + f.set.mutex.RLock() + n := len(f.lines) + f.set.mutex.RUnlock() + return n +} + + +// AddLine adds the line offset for a new line. +// The line offset must be larger than the offset for the previous line +// and not larger than the file size; otherwise the line offset is ignored. +// +func (f *File) AddLine(offset int) { + f.set.mutex.Lock() + if i := len(f.lines); (i == 0 || f.lines[i-1] < offset) && offset <= f.size { + f.lines = append(f.lines, offset) + } + f.set.mutex.Unlock() +} + + +// SetLines sets all line offsets for a file and returns true if successful. +// Each line offset must be larger than the offset for the previous line +// and not larger than the file size; otherwise the SetLines fails and returns +// false. +// +func (f *File) SetLines(lines []int) bool { + // verify validity of lines table + size := f.size + for i, offset := range lines { + if i > 0 && offset <= lines[i-1] || size < offset { + return false + } + } + + // set lines table + f.set.mutex.Lock() + f.lines = lines + f.set.mutex.Unlock() + return true +} + + +// Pos returns the Pos value for the given file offset; +// the offset must be <= f.Size(). +// f.Pos(f.Offset(p)) == p. +// +func (f *File) Pos(offset int) Pos { + if offset > f.size { + panic("illegal file offset") + } + return Pos(f.base + offset) +} + + +// Offset returns the offset for the given file position p; +// p must be a valid Pos value in that file. +// f.Offset(f.Pos(offset)) == offset. +// +func (f *File) Offset(p Pos) int { + if int(p) < f.base || int(p) > f.base+f.size { + panic("illegal Pos value") + } + return int(p) - f.base +} + + +// Line returns the line number for the given file position p; +// p must be a Pos value in that file or NoPos. +// +func (f *File) Line(p Pos) int { + // TODO(gri) this can be implemented much more efficiently + return f.Position(p).Line +} + + +// Position returns the Position value for the given file position p; +// p must be a Pos value in that file or NoPos. +// +func (f *File) Position(p Pos) (pos Position) { + if p != NoPos { + if int(p) < f.base || int(p) > f.base+f.size { + panic("illegal Pos value") + } + pos = f.position(p) + } + return +} + + +func searchUints(a []int, x int) int { + return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1 +} + + +func searchLineInfos(a []lineInfo, x int) int { + return sort.Search(len(a), func(i int) bool { return a[i].offset > x }) - 1 +} + + +// info returns the file name, line, and column number for a file offset. +func (f *File) info(offset int) (filename string, line, column int) { + filename = f.name + if i := searchUints(f.lines, offset); i >= 0 { + line, column = i+1, offset-f.lines[i]+1 + } + if i := searchLineInfos(f.infos, offset); i >= 0 { + alt := &f.infos[i] + filename = alt.filename + if i := searchUints(f.lines, alt.offset); i >= 0 { + line += alt.line - i - 1 + } + } + return +} + + +// A FileSet represents a set of source files. +// Methods of file sets are synchronized; multiple goroutines +// may invoke them concurrently. +// +type FileSet struct { + mutex sync.RWMutex // protects the file set + base int // base offset for the next file + files []*File // list of files in the order added to the set + index map[*File]int // file -> files index for quick lookup +} + + +// NewFileSet creates a new file set. +func NewFileSet() *FileSet { + s := new(FileSet) + s.base = 1 // 0 == NoPos + s.index = make(map[*File]int) + return s +} + + +// Base returns the minimum base offset that must be provided to +// AddFile when adding the next file. +// +func (s *FileSet) Base() int { + s.mutex.RLock() + b := s.base + s.mutex.RUnlock() + return b + +} + + +// AddFile adds a new file with a given filename, base offset, and file size +// to the file set s and returns the file. Multiple files may have the same +// name. The base offset must not be smaller than the FileSet's Base(), and +// size must not be negative. +// +// Adding the file will set the file set's Base() value to base + size + 1 +// as the minimum base value for the next file. The following relationship +// exists between a Pos value p for a given file offset offs: +// +// int(p) = base + offs +// +// with offs in the range [0, size] and thus p in the range [base, base+size]. +// For convenience, File.Pos may be used to create file-specific position +// values from a file offset. +// +func (s *FileSet) AddFile(filename string, base, size int) *File { + s.mutex.Lock() + defer s.mutex.Unlock() + if base < s.base || size < 0 { + panic("illegal base or size") + } + // base >= s.base && size >= 0 + f := &File{s, filename, base, size, []int{0}, nil} + base += size + 1 // +1 because EOF also has a position + if base < 0 { + panic("token.Pos offset overflow (> 2G of source code in file set)") + } + // add the file to the file set + s.base = base + s.index[f] = len(s.files) + s.files = append(s.files, f) + return f +} + + +// Files returns the files added to the file set. +func (s *FileSet) Files() <-chan *File { + ch := make(chan *File) + go func() { + for i := 0; ; i++ { + var f *File + s.mutex.RLock() + if i < len(s.files) { + f = s.files[i] + } + s.mutex.RUnlock() + if f == nil { + break + } + ch <- f + } + close(ch) + }() + return ch +} diff --git a/src/pkg/go/token/position_test.go b/src/pkg/go/token/position_test.go new file mode 100644 index 000000000..1cffcc3c2 --- /dev/null +++ b/src/pkg/go/token/position_test.go @@ -0,0 +1,158 @@ +// 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 token + +import ( + "fmt" + "testing" +) + + +func checkPos(t *testing.T, msg string, p, q Position) { + if p.Filename != q.Filename { + t.Errorf("%s: expected filename = %q; got %q", msg, q.Filename, p.Filename) + } + if p.Offset != q.Offset { + t.Errorf("%s: expected offset = %d; got %d", msg, q.Offset, p.Offset) + } + if p.Line != q.Line { + t.Errorf("%s: expected line = %d; got %d", msg, q.Line, p.Line) + } + if p.Column != q.Column { + t.Errorf("%s: expected column = %d; got %d", msg, q.Column, p.Column) + } +} + + +func TestNoPos(t *testing.T) { + if NoPos.IsValid() { + t.Errorf("NoPos should not be valid") + } + var fset *FileSet + checkPos(t, "nil NoPos", fset.Position(NoPos), Position{}) + fset = NewFileSet() + checkPos(t, "fset NoPos", fset.Position(NoPos), Position{}) +} + + +var tests = []struct { + filename string + size int + lines []int +}{ + {"a", 0, []int{}}, + {"b", 5, []int{0}}, + {"c", 10, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}, + {"d", 100, []int{0, 5, 10, 20, 30, 70, 71, 72, 80, 85, 90, 99}}, + {"e", 777, []int{0, 80, 100, 120, 130, 180, 267, 455, 500, 567, 620}}, +} + + +func linecol(lines []int, offs int) (int, int) { + prevLineOffs := 0 + for line, lineOffs := range lines { + if offs < lineOffs { + return line, offs - prevLineOffs + 1 + } + prevLineOffs = lineOffs + } + return len(lines), offs - prevLineOffs + 1 +} + + +func verifyPositions(t *testing.T, fset *FileSet, f *File, lines []int) { + for offs := 0; offs < f.Size(); offs++ { + p := f.Pos(offs) + offs2 := f.Offset(p) + if offs2 != offs { + t.Errorf("%s, Offset: expected offset %d; got %d", f.Name(), offs, offs2) + } + line, col := linecol(lines, offs) + msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p) + checkPos(t, msg, f.Position(f.Pos(offs)), Position{f.Name(), offs, line, col}) + checkPos(t, msg, fset.Position(p), Position{f.Name(), offs, line, col}) + } +} + + +func TestPositions(t *testing.T) { + const delta = 7 // a non-zero base offset increment + fset := NewFileSet() + for _, test := range tests { + // add file and verify name and size + f := fset.AddFile(test.filename, fset.Base()+delta, test.size) + if f.Name() != test.filename { + t.Errorf("expected filename %q; got %q", test.filename, f.Name()) + } + if f.Size() != test.size { + t.Errorf("%s: expected file size %d; got %d", f.Name(), test.size, f.Size()) + } + if fset.File(f.Pos(0)) != f { + t.Errorf("%s: f.Pos(0) was not found in f", f.Name()) + } + + // add lines individually and verify all positions + for i, offset := range test.lines { + f.AddLine(offset) + if f.LineCount() != i+1 { + t.Errorf("%s, AddLine: expected line count %d; got %d", f.Name(), i+1, f.LineCount()) + } + // adding the same offset again should be ignored + f.AddLine(offset) + if f.LineCount() != i+1 { + t.Errorf("%s, AddLine: expected unchanged line count %d; got %d", f.Name(), i+1, f.LineCount()) + } + verifyPositions(t, fset, f, test.lines[0:i+1]) + } + + // add lines at once and verify all positions + ok := f.SetLines(test.lines) + if !ok { + t.Errorf("%s: SetLines failed", f.Name()) + } + if f.LineCount() != len(test.lines) { + t.Errorf("%s, SetLines: expected line count %d; got %d", f.Name(), len(test.lines), f.LineCount()) + } + verifyPositions(t, fset, f, test.lines) + } +} + + +func TestLineInfo(t *testing.T) { + fset := NewFileSet() + f := fset.AddFile("foo", fset.Base(), 500) + lines := []int{0, 42, 77, 100, 210, 220, 277, 300, 333, 401} + // add lines individually and provide alternative line information + for _, offs := range lines { + f.AddLine(offs) + f.AddLineInfo(offs, "bar", 42) + } + // verify positions for all offsets + for offs := 0; offs <= f.Size(); offs++ { + p := f.Pos(offs) + _, col := linecol(lines, offs) + msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p) + checkPos(t, msg, f.Position(f.Pos(offs)), Position{"bar", offs, 42, col}) + checkPos(t, msg, fset.Position(p), Position{"bar", offs, 42, col}) + } +} + + +func TestFiles(t *testing.T) { + fset := NewFileSet() + for i, test := range tests { + fset.AddFile(test.filename, fset.Base(), test.size) + j := 0 + for g := range fset.Files() { + if g.Name() != tests[j].filename { + t.Errorf("expected filename = %s; got %s", tests[j].filename, g.Name()) + } + j++ + } + if j != i+1 { + t.Errorf("expected %d files; got %d", i+1, j) + } + } +} diff --git a/src/pkg/go/token/token.go b/src/pkg/go/token/token.go index 70c2501e9..1bd81c1b1 100644 --- a/src/pkg/go/token/token.go +++ b/src/pkg/go/token/token.go @@ -8,10 +8,7 @@ // package token -import ( - "fmt" - "strconv" -) +import "strconv" // Token is the set of lexical tokens of the Go programming language. @@ -321,39 +318,3 @@ func (tok Token) IsOperator() bool { return operator_beg < tok && tok < operator // returns false otherwise. // func (tok Token) IsKeyword() bool { return keyword_beg < tok && tok < keyword_end } - - -// Token source positions are represented by a Position value. -// A Position is valid if the line number is > 0. -// -type Position struct { - Filename string // filename, if any - Offset int // byte offset, starting at 0 - Line int // line number, starting at 1 - Column int // column number, starting at 1 (character count) -} - - -// Pos is an accessor method for anonymous Position fields. -// It returns its receiver. -// -func (pos *Position) Pos() Position { return *pos } - - -// IsValid returns true if the position is valid. -func (pos *Position) IsValid() bool { return pos.Line > 0 } - - -func (pos Position) String() string { - s := pos.Filename - if pos.IsValid() { - if s != "" { - s += ":" - } - s += fmt.Sprintf("%d:%d", pos.Line, pos.Column) - } - if s == "" { - s = "???" - } - return s -} diff --git a/src/pkg/go/typechecker/Makefile b/src/pkg/go/typechecker/Makefile new file mode 100644 index 000000000..62b2aa7fe --- /dev/null +++ b/src/pkg/go/typechecker/Makefile @@ -0,0 +1,13 @@ +# Copyright 2010 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../../Make.inc + +TARG=go/typechecker +GOFILES=\ + scope.go\ + typechecker.go\ + universe.go\ + +include ../../../Make.pkg diff --git a/src/pkg/go/typechecker/scope.go b/src/pkg/go/typechecker/scope.go new file mode 100644 index 000000000..114c93ea8 --- /dev/null +++ b/src/pkg/go/typechecker/scope.go @@ -0,0 +1,119 @@ +// 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 implements scope support functions. + +package typechecker + +import ( + "fmt" + "go/ast" + "go/token" +) + + +func (tc *typechecker) openScope() *ast.Scope { + tc.topScope = ast.NewScope(tc.topScope) + return tc.topScope +} + + +func (tc *typechecker) closeScope() { + tc.topScope = tc.topScope.Outer +} + + +// objPos computes the source position of the declaration of an object name. +// Only required for error reporting, so doesn't have to be fast. +func objPos(obj *ast.Object) (pos token.Pos) { + switch d := obj.Decl.(type) { + case *ast.Field: + for _, n := range d.Names { + if n.Name == obj.Name { + return n.Pos() + } + } + case *ast.ValueSpec: + for _, n := range d.Names { + if n.Name == obj.Name { + return n.Pos() + } + } + case *ast.TypeSpec: + return d.Name.Pos() + case *ast.FuncDecl: + return d.Name.Pos() + } + if debug { + fmt.Printf("decl = %T\n", obj.Decl) + } + panic("unreachable") +} + + +// declInScope declares an object of a given kind and name in scope and sets the object's Decl and N fields. +// It returns the newly allocated object. If an object with the same name already exists in scope, an error +// is reported and the object is not inserted. +// (Objects with _ name are always inserted into a scope without errors, but they cannot be found.) +func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.Kind, name *ast.Ident, decl interface{}, n int) *ast.Object { + obj := ast.NewObj(kind, name.Name) + obj.Decl = decl + obj.N = n + name.Obj = obj + if alt := scope.Insert(obj); alt != obj { + tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, objPos(alt)) + } + return obj +} + + +// decl is the same as declInScope(tc.topScope, ...) +func (tc *typechecker) decl(kind ast.Kind, name *ast.Ident, decl interface{}, n int) *ast.Object { + return tc.declInScope(tc.topScope, kind, name, decl, n) +} + + +// find returns the object with the given name if visible in the current scope hierarchy. +// If no such object is found, an error is reported and a bad object is returned instead. +func (tc *typechecker) find(name *ast.Ident) (obj *ast.Object) { + for s := tc.topScope; s != nil && obj == nil; s = s.Outer { + obj = s.Lookup(name.Name) + } + if obj == nil { + tc.Errorf(name.Pos(), "%s not declared", name.Name) + obj = ast.NewObj(ast.Bad, name.Name) + } + name.Obj = obj + return +} + + +// findField returns the object with the given name if visible in the type's scope. +// If no such object is found, an error is reported and a bad object is returned instead. +func (tc *typechecker) findField(typ *ast.Type, name *ast.Ident) (obj *ast.Object) { + // TODO(gri) This is simplistic at the moment and ignores anonymous fields. + obj = typ.Scope.Lookup(name.Name) + if obj == nil { + tc.Errorf(name.Pos(), "%s not declared", name.Name) + obj = ast.NewObj(ast.Bad, name.Name) + } + return +} + + +// printScope prints the objects in a scope. +func printScope(scope *ast.Scope) { + fmt.Printf("scope %p {", scope) + if scope != nil && len(scope.Objects) > 0 { + fmt.Println() + for _, obj := range scope.Objects { + form := "void" + if obj.Type != nil { + form = obj.Type.Form.String() + } + fmt.Printf("\t%s\t%s\n", obj.Name, form) + } + } + fmt.Printf("}\n") +} diff --git a/src/pkg/go/typechecker/testdata/test0.go b/src/pkg/go/typechecker/testdata/test0.go new file mode 100644 index 000000000..4e317f214 --- /dev/null +++ b/src/pkg/go/typechecker/testdata/test0.go @@ -0,0 +1,94 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// type declarations + +package P0 + +type ( + B bool + I int32 + A [10]P + T struct { + x, y P + } + P *T + R *R + F func(A) I + Y interface { + f(A) I + } + S []P + M map[I]F + C chan<- I +) + +type ( + a/* ERROR "illegal cycle" */ a + a/* ERROR "already declared" */ int + + b/* ERROR "illegal cycle" */ c + c d + d e + e b /* ERROR "not a type" */ + + t *t + + U V + V W + W *U + + P1 *S2 + P2 P1 + + S1 struct { + a, b, c int + u, v, a/* ERROR "already declared" */ float + } + S2/* ERROR "illegal cycle" */ struct { + x S2 + } + + L1 []L1 + L2 []int + + A1 [10]int + A2/* ERROR "illegal cycle" */ [10]A2 + A3/* ERROR "illegal cycle" */ [10]struct { + x A4 + } + A4 [10]A3 + + F1 func() + F2 func(x, y, z float) + F3 func(x, y, x /* ERROR "already declared" */ float) + F4 func() (x, y, x /* ERROR "already declared" */ float) + F5 func(x int) (x /* ERROR "already declared" */ float) + + I1 interface{} + I2 interface { + m1() + } + I3 interface { + m1() + m1 /* ERROR "already declared" */ () + } + I4 interface { + m1(x, y, x /* ERROR "already declared" */ float) + m2() (x, y, x /* ERROR "already declared" */ float) + m3(x int) (x /* ERROR "already declared" */ float) + } + I5 interface { + m1(I5) + } + + C1 chan int + C2 <-chan int + C3 chan<- C3 + + M1 map[Last]string + M2 map[string]M2 + + Last int +) diff --git a/src/pkg/go/typechecker/testdata/test1.go b/src/pkg/go/typechecker/testdata/test1.go new file mode 100644 index 000000000..b0808ee7a --- /dev/null +++ b/src/pkg/go/typechecker/testdata/test1.go @@ -0,0 +1,13 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// const and var declarations + +package P1 + +const ( + c1 /* ERROR "missing initializer" */ + c2 int = 0 + c3, c4 = 0 +) diff --git a/src/pkg/go/typechecker/testdata/test3.go b/src/pkg/go/typechecker/testdata/test3.go new file mode 100644 index 000000000..ea35808a0 --- /dev/null +++ b/src/pkg/go/typechecker/testdata/test3.go @@ -0,0 +1,38 @@ +// 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 P3 + +// function and method signatures + +func _() {} +func _() {} +func _(x, x /* ERROR "already declared" */ int) {} + +func f() {} +func f /* ERROR "already declared" */ () {} + +func (*foo /* ERROR "invalid receiver" */ ) m() {} +func (bar /* ERROR "not a type" */ ) m() {} + +func f1(x, _, _ int) (_, _ float) {} +func f2(x, y, x /* ERROR "already declared" */ int) {} +func f3(x, y int) (a, b, x /* ERROR "already declared" */ int) {} + +func (x *T) m1() {} +func (x *T) m1 /* ERROR "already declared" */ () {} +func (x T) m1 /* ERROR "already declared" */ () {} +func (T) m1 /* ERROR "already declared" */ () {} + +func (x *T) m2(u, x /* ERROR "already declared" */ int) {} +func (x *T) m3(a, b, c int) (u, x /* ERROR "already declared" */ int) {} +func (T) _(x, x /* ERROR "already declared" */ int) {} +func (T) _() (x, x /* ERROR "already declared" */ int) {} + +//func (PT) _() {} + +var bar int + +type T struct{} +type PT (T) diff --git a/src/pkg/go/typechecker/testdata/test4.go b/src/pkg/go/typechecker/testdata/test4.go new file mode 100644 index 000000000..bb9aee3ad --- /dev/null +++ b/src/pkg/go/typechecker/testdata/test4.go @@ -0,0 +1,11 @@ +// 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. + +// Constant declarations + +package P4 + +const ( + c0 /* ERROR "missing initializer" */ +) diff --git a/src/pkg/go/typechecker/typechecker.go b/src/pkg/go/typechecker/typechecker.go new file mode 100644 index 000000000..e9aefa240 --- /dev/null +++ b/src/pkg/go/typechecker/typechecker.go @@ -0,0 +1,484 @@ +// 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. + +// INCOMPLETE PACKAGE. +// This package implements typechecking of a Go AST. +// The result of the typecheck is an augmented AST +// with object and type information for each identifier. +// +package typechecker + +import ( + "fmt" + "go/ast" + "go/token" + "go/scanner" + "os" +) + + +// TODO(gri) don't report errors for objects/types that are marked as bad. + + +const debug = true // set for debugging output + + +// An importer takes an import path and returns the data describing the +// respective package's exported interface. The data format is TBD. +// +type Importer func(path string) ([]byte, os.Error) + + +// CheckPackage typechecks a package and augments the AST by setting +// *ast.Object, *ast.Type, and *ast.Scope fields accordingly. If an +// importer is provided, it is used to handle imports, otherwise they +// are ignored (likely leading to typechecking errors). +// +// If errors are reported, the AST may be incompletely augmented (fields +// may be nil) or contain incomplete object, type, or scope information. +// +func CheckPackage(fset *token.FileSet, pkg *ast.Package, importer Importer) os.Error { + var tc typechecker + tc.fset = fset + tc.importer = importer + tc.checkPackage(pkg) + return tc.GetError(scanner.Sorted) +} + + +// CheckFile typechecks a single file, but otherwise behaves like +// CheckPackage. If the complete package consists of more than just +// one file, the file may not typecheck without errors. +// +func CheckFile(fset *token.FileSet, file *ast.File, importer Importer) os.Error { + // create a single-file dummy package + pkg := &ast.Package{file.Name.Name, nil, map[string]*ast.File{fset.Position(file.Name.NamePos).Filename: file}} + return CheckPackage(fset, pkg, importer) +} + + +// ---------------------------------------------------------------------------- +// Typechecker state + +type typechecker struct { + fset *token.FileSet + scanner.ErrorVector + importer Importer + topScope *ast.Scope // current top-most scope + cyclemap map[*ast.Object]bool // for cycle detection + iota int // current value of iota +} + + +func (tc *typechecker) Errorf(pos token.Pos, format string, args ...interface{}) { + tc.Error(tc.fset.Position(pos), fmt.Sprintf(format, args...)) +} + + +func assert(pred bool) { + if !pred { + panic("internal error") + } +} + + +/* +Typechecking is done in several phases: + +phase 1: declare all global objects; also collect all function and method declarations + - all objects have kind, name, decl fields; the decl field permits + quick lookup of an object's declaration + - constant objects have an iota value + - type objects have unresolved types with empty scopes, all others have nil types + - report global double declarations + +phase 2: bind methods to their receiver base types + - received base types must be declared in the package, thus for + each method a corresponding (unresolved) type must exist + - report method double declarations and errors with base types + +phase 3: resolve all global objects + - sequentially iterate through all objects in the global scope + - resolve types for all unresolved types and assign types to + all attached methods + - assign types to all other objects, possibly by evaluating + constant and initializer expressions + - resolution may recurse; a cyclemap is used to detect cycles + - report global typing errors + +phase 4: sequentially typecheck function and method bodies + - all global objects are declared and have types and values; + all methods have types + - sequentially process statements in each body; any object + referred to must be fully defined at this point + - report local typing errors +*/ + +func (tc *typechecker) checkPackage(pkg *ast.Package) { + // setup package scope + tc.topScope = Universe + tc.openScope() + defer tc.closeScope() + + // TODO(gri) there's no file scope at the moment since we ignore imports + + // phase 1: declare all global objects; also collect all function and method declarations + var funcs []*ast.FuncDecl + for _, file := range pkg.Files { + for _, decl := range file.Decls { + tc.declGlobal(decl) + if f, isFunc := decl.(*ast.FuncDecl); isFunc { + funcs = append(funcs, f) + } + } + } + + // phase 2: bind methods to their receiver base types + for _, m := range funcs { + if m.Recv != nil { + tc.bindMethod(m) + } + } + + // phase 3: resolve all global objects + // (note that objects with _ name are also in the scope) + tc.cyclemap = make(map[*ast.Object]bool) + for _, obj := range tc.topScope.Objects { + tc.resolve(obj) + } + assert(len(tc.cyclemap) == 0) + + // 4: sequentially typecheck function and method bodies + for _, f := range funcs { + tc.checkBlock(f.Body.List, f.Name.Obj.Type) + } + + pkg.Scope = tc.topScope +} + + +func (tc *typechecker) declGlobal(global ast.Decl) { + switch d := global.(type) { + case *ast.BadDecl: + // ignore + + case *ast.GenDecl: + iota := 0 + var prev *ast.ValueSpec + for _, spec := range d.Specs { + switch s := spec.(type) { + case *ast.ImportSpec: + // TODO(gri) imports go into file scope + case *ast.ValueSpec: + switch d.Tok { + case token.CONST: + if s.Values == nil { + // create a new spec with type and values from the previous one + if prev != nil { + s = &ast.ValueSpec{s.Doc, s.Names, prev.Type, prev.Values, s.Comment} + } else { + // TODO(gri) this should probably go into the const decl code + tc.Errorf(s.Pos(), "missing initializer for const %s", s.Names[0].Name) + } + } + for _, name := range s.Names { + tc.decl(ast.Con, name, s, iota) + } + case token.VAR: + for _, name := range s.Names { + tc.decl(ast.Var, name, s, 0) + } + default: + panic("unreachable") + } + prev = s + iota++ + case *ast.TypeSpec: + obj := tc.decl(ast.Typ, s.Name, s, 0) + // give all type objects an unresolved type so + // that we can collect methods in the type scope + typ := ast.NewType(ast.Unresolved) + obj.Type = typ + typ.Obj = obj + default: + panic("unreachable") + } + } + + case *ast.FuncDecl: + if d.Recv == nil { + tc.decl(ast.Fun, d.Name, d, 0) + } + + default: + panic("unreachable") + } +} + + +// If x is of the form *T, deref returns T, otherwise it returns x. +func deref(x ast.Expr) ast.Expr { + if p, isPtr := x.(*ast.StarExpr); isPtr { + x = p.X + } + return x +} + + +func (tc *typechecker) bindMethod(method *ast.FuncDecl) { + // a method is declared in the receiver base type's scope + var scope *ast.Scope + base := deref(method.Recv.List[0].Type) + if name, isIdent := base.(*ast.Ident); isIdent { + // if base is not an *ast.Ident, we had a syntax + // error and the parser reported an error already + obj := tc.topScope.Lookup(name.Name) + if obj == nil { + tc.Errorf(name.Pos(), "invalid receiver: %s is not declared in this package", name.Name) + } else if obj.Kind != ast.Typ { + tc.Errorf(name.Pos(), "invalid receiver: %s is not a type", name.Name) + } else { + typ := obj.Type + assert(typ.Form == ast.Unresolved) + scope = typ.Scope + } + } + if scope == nil { + // no receiver type found; use a dummy scope + // (we still want to type-check the method + // body, so make sure there is a name object + // and type) + // TODO(gri) should we record the scope so + // that we don't lose the receiver for type- + // checking of the method body? + scope = ast.NewScope(nil) + } + tc.declInScope(scope, ast.Fun, method.Name, method, 0) +} + + +func (tc *typechecker) resolve(obj *ast.Object) { + // check for declaration cycles + if tc.cyclemap[obj] { + tc.Errorf(objPos(obj), "illegal cycle in declaration of %s", obj.Name) + obj.Kind = ast.Bad + return + } + tc.cyclemap[obj] = true + defer func() { + tc.cyclemap[obj] = false, false + }() + + // resolve non-type objects + typ := obj.Type + if typ == nil { + switch obj.Kind { + case ast.Bad: + // ignore + + case ast.Con: + tc.declConst(obj) + + case ast.Var: + tc.declVar(obj) + //obj.Type = tc.typeFor(nil, obj.Decl.(*ast.ValueSpec).Type, false) + + case ast.Fun: + obj.Type = ast.NewType(ast.Function) + t := obj.Decl.(*ast.FuncDecl).Type + tc.declSignature(obj.Type, nil, t.Params, t.Results) + + default: + // type objects have non-nil types when resolve is called + if debug { + fmt.Printf("kind = %s\n", obj.Kind) + } + panic("unreachable") + } + return + } + + // resolve type objects + if typ.Form == ast.Unresolved { + tc.typeFor(typ, typ.Obj.Decl.(*ast.TypeSpec).Type, false) + + // provide types for all methods + for _, obj := range typ.Scope.Objects { + if obj.Kind == ast.Fun { + assert(obj.Type == nil) + obj.Type = ast.NewType(ast.Method) + f := obj.Decl.(*ast.FuncDecl) + t := f.Type + tc.declSignature(obj.Type, f.Recv, t.Params, t.Results) + } + } + } +} + + +func (tc *typechecker) checkBlock(body []ast.Stmt, ftype *ast.Type) { + tc.openScope() + defer tc.closeScope() + + // inject function/method parameters into block scope, if any + if ftype != nil { + for _, par := range ftype.Params.Objects { + obj := tc.topScope.Insert(par) + assert(obj == par) // ftype has no double declarations + } + } + + for _, stmt := range body { + tc.checkStmt(stmt) + } +} + + +// ---------------------------------------------------------------------------- +// Types + +// unparen removes parentheses around x, if any. +func unparen(x ast.Expr) ast.Expr { + if ux, hasParens := x.(*ast.ParenExpr); hasParens { + return unparen(ux.X) + } + return x +} + + +func (tc *typechecker) declFields(scope *ast.Scope, fields *ast.FieldList, ref bool) (n uint) { + if fields != nil { + for _, f := range fields.List { + typ := tc.typeFor(nil, f.Type, ref) + for _, name := range f.Names { + fld := tc.declInScope(scope, ast.Var, name, f, 0) + fld.Type = typ + n++ + } + } + } + return n +} + + +func (tc *typechecker) declSignature(typ *ast.Type, recv, params, results *ast.FieldList) { + assert((typ.Form == ast.Method) == (recv != nil)) + typ.Params = ast.NewScope(nil) + tc.declFields(typ.Params, recv, true) + tc.declFields(typ.Params, params, true) + typ.N = tc.declFields(typ.Params, results, true) +} + + +func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Type) { + x = unparen(x) + + // type name + if t, isIdent := x.(*ast.Ident); isIdent { + obj := tc.find(t) + + if obj.Kind != ast.Typ { + tc.Errorf(t.Pos(), "%s is not a type", t.Name) + if def == nil { + typ = ast.NewType(ast.BadType) + } else { + typ = def + typ.Form = ast.BadType + } + typ.Expr = x + return + } + + if !ref { + tc.resolve(obj) // check for cycles even if type resolved + } + typ = obj.Type + + if def != nil { + // new type declaration: copy type structure + def.Form = typ.Form + def.N = typ.N + def.Key, def.Elt = typ.Key, typ.Elt + def.Params = typ.Params + def.Expr = x + typ = def + } + return + } + + // type literal + typ = def + if typ == nil { + typ = ast.NewType(ast.BadType) + } + typ.Expr = x + + switch t := x.(type) { + case *ast.SelectorExpr: + if debug { + fmt.Println("qualified identifier unimplemented") + } + typ.Form = ast.BadType + + case *ast.StarExpr: + typ.Form = ast.Pointer + typ.Elt = tc.typeFor(nil, t.X, true) + + case *ast.ArrayType: + if t.Len != nil { + typ.Form = ast.Array + // TODO(gri) compute the real length + // (this may call resolve recursively) + (*typ).N = 42 + } else { + typ.Form = ast.Slice + } + typ.Elt = tc.typeFor(nil, t.Elt, t.Len == nil) + + case *ast.StructType: + typ.Form = ast.Struct + tc.declFields(typ.Scope, t.Fields, false) + + case *ast.FuncType: + typ.Form = ast.Function + tc.declSignature(typ, nil, t.Params, t.Results) + + case *ast.InterfaceType: + typ.Form = ast.Interface + tc.declFields(typ.Scope, t.Methods, true) + + case *ast.MapType: + typ.Form = ast.Map + typ.Key = tc.typeFor(nil, t.Key, true) + typ.Elt = tc.typeFor(nil, t.Value, true) + + case *ast.ChanType: + typ.Form = ast.Channel + typ.N = uint(t.Dir) + typ.Elt = tc.typeFor(nil, t.Value, true) + + default: + if debug { + fmt.Printf("x is %T\n", x) + } + panic("unreachable") + } + + return +} + + +// ---------------------------------------------------------------------------- +// TODO(gri) implement these place holders + +func (tc *typechecker) declConst(*ast.Object) { +} + + +func (tc *typechecker) declVar(*ast.Object) { +} + + +func (tc *typechecker) checkStmt(ast.Stmt) { +} diff --git a/src/pkg/go/typechecker/typechecker_test.go b/src/pkg/go/typechecker/typechecker_test.go new file mode 100644 index 000000000..9c5b52e41 --- /dev/null +++ b/src/pkg/go/typechecker/typechecker_test.go @@ -0,0 +1,167 @@ +// 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 implements a simple typechecker test harness. Packages found +// in the testDir directory are typechecked. Error messages reported by +// the typechecker are compared against the error messages expected for +// the test files. +// +// Expected errors are indicated in the test files by putting a comment +// of the form /* ERROR "rx" */ immediately following an offending token. +// The harness will verify that an error matching the regular expression +// rx is reported at that source position. Consecutive comments may be +// used to indicate multiple errors for the same token position. +// +// For instance, the following test file indicates that a "not declared" +// error should be reported for the undeclared variable x: +// +// package P0 +// func f() { +// _ = x /* ERROR "not declared" */ + 1 +// } +// +// If the -pkg flag is set, only packages with package names matching +// the regular expression provided via the flag value are tested. + +package typechecker + +import ( + "flag" + "fmt" + "go/ast" + "go/parser" + "go/scanner" + "go/token" + "io/ioutil" + "os" + "regexp" + "sort" + "strings" + "testing" +) + + +const testDir = "./testdata" // location of test packages + +var fset = token.NewFileSet() + +var ( + pkgPat = flag.String("pkg", ".*", "regular expression to select test packages by package name") + trace = flag.Bool("trace", false, "print package names") +) + + +// ERROR comments must be of the form /* ERROR "rx" */ and rx is +// a regular expression that matches the expected error message. +var errRx = regexp.MustCompile(`^/\* *ERROR *"([^"]*)" *\*/$`) + +// expectedErrors collects the regular expressions of ERROR comments +// found in the package files of pkg and returns them in sorted order +// (by filename and position). +func expectedErrors(t *testing.T, pkg *ast.Package) (list scanner.ErrorList) { + // scan all package files + for filename := range pkg.Files { + src, err := ioutil.ReadFile(filename) + if err != nil { + t.Fatalf("expectedErrors(%s): %v", pkg.Name, err) + } + + var s scanner.Scanner + s.Init(fset, filename, src, nil, scanner.ScanComments) + var prev token.Pos // position of last non-comment token + loop: + for { + pos, tok, lit := s.Scan() + switch tok { + case token.EOF: + break loop + case token.COMMENT: + s := errRx.FindSubmatch(lit) + if len(s) == 2 { + list = append(list, &scanner.Error{fset.Position(prev), string(s[1])}) + } + default: + prev = pos + } + } + } + sort.Sort(list) // multiple files may not be sorted + return +} + + +func testFilter(f *os.FileInfo) bool { + return strings.HasSuffix(f.Name, ".go") && f.Name[0] != '.' +} + + +func checkError(t *testing.T, expected, found *scanner.Error) { + rx, err := regexp.Compile(expected.Msg) + if err != nil { + t.Errorf("%s: %v", expected.Pos, err) + return + } + + match := rx.MatchString(found.Msg) + + if expected.Pos.Offset != found.Pos.Offset { + if match { + t.Errorf("%s: expected error should have been at %s", expected.Pos, found.Pos) + } else { + t.Errorf("%s: error matching %q expected", expected.Pos, expected.Msg) + return + } + } + + if !match { + t.Errorf("%s: %q does not match %q", expected.Pos, expected.Msg, found.Msg) + } +} + + +func TestTypeCheck(t *testing.T) { + flag.Parse() + pkgRx, err := regexp.Compile(*pkgPat) + if err != nil { + t.Fatalf("illegal flag value %q: %s", *pkgPat, err) + } + + pkgs, err := parser.ParseDir(fset, testDir, testFilter, 0) + if err != nil { + scanner.PrintError(os.Stderr, err) + t.Fatalf("packages in %s contain syntax errors", testDir) + } + + for _, pkg := range pkgs { + if !pkgRx.MatchString(pkg.Name) { + continue // only test selected packages + } + + if *trace { + fmt.Println(pkg.Name) + } + + xlist := expectedErrors(t, pkg) + err := CheckPackage(fset, pkg, nil) + if err != nil { + if elist, ok := err.(scanner.ErrorList); ok { + // verify that errors match + for i := 0; i < len(xlist) && i < len(elist); i++ { + checkError(t, xlist[i], elist[i]) + } + // the correct number or errors must have been found + if len(xlist) != len(elist) { + fmt.Fprintf(os.Stderr, "%s\n", pkg.Name) + scanner.PrintError(os.Stderr, elist) + fmt.Fprintln(os.Stderr) + t.Errorf("TypeCheck(%s): %d errors expected but %d reported", pkg.Name, len(xlist), len(elist)) + } + } else { + t.Errorf("TypeCheck(%s): %v", pkg.Name, err) + } + } else if len(xlist) > 0 { + t.Errorf("TypeCheck(%s): %d errors expected but 0 reported", pkg.Name, len(xlist)) + } + } +} diff --git a/src/pkg/go/typechecker/universe.go b/src/pkg/go/typechecker/universe.go new file mode 100644 index 000000000..db950737f --- /dev/null +++ b/src/pkg/go/typechecker/universe.go @@ -0,0 +1,38 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typechecker + +import "go/ast" + +// TODO(gri) should this be in package ast? + +// The Universe scope contains all predeclared identifiers. +var Universe *ast.Scope + + +func def(obj *ast.Object) { + alt := Universe.Insert(obj) + if alt != obj { + panic("object declared twice") + } +} + + +func init() { + Universe = ast.NewScope(nil) + + // basic types + for n, name := range ast.BasicTypes { + typ := ast.NewType(ast.Basic) + typ.N = n + obj := ast.NewObj(ast.Typ, name) + obj.Type = typ + typ.Obj = obj + def(obj) + } + + // built-in functions + // TODO(gri) implement this +} diff --git a/src/pkg/gob/Makefile b/src/pkg/gob/Makefile index 1091adb01..68007c189 100644 --- a/src/pkg/gob/Makefile +++ b/src/pkg/gob/Makefile @@ -2,14 +2,16 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=gob GOFILES=\ decode.go\ decoder.go\ + doc.go\ encode.go\ encoder.go\ + error.go\ type.go\ include ../../Make.pkg diff --git a/src/pkg/gob/codec_test.go b/src/pkg/gob/codec_test.go index 2caaaa43f..d150dbe9a 100644 --- a/src/pkg/gob/codec_test.go +++ b/src/pkg/gob/codec_test.go @@ -21,50 +21,50 @@ type EncodeT struct { } var encodeT = []EncodeT{ - EncodeT{0x00, []byte{0x00}}, - EncodeT{0x0F, []byte{0x0F}}, - EncodeT{0xFF, []byte{0xFF, 0xFF}}, - EncodeT{0xFFFF, []byte{0xFE, 0xFF, 0xFF}}, - EncodeT{0xFFFFFF, []byte{0xFD, 0xFF, 0xFF, 0xFF}}, - EncodeT{0xFFFFFFFF, []byte{0xFC, 0xFF, 0xFF, 0xFF, 0xFF}}, - EncodeT{0xFFFFFFFFFF, []byte{0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, - EncodeT{0xFFFFFFFFFFFF, []byte{0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, - EncodeT{0xFFFFFFFFFFFFFF, []byte{0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, - EncodeT{0xFFFFFFFFFFFFFFFF, []byte{0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, - EncodeT{0x1111, []byte{0xFE, 0x11, 0x11}}, - EncodeT{0x1111111111111111, []byte{0xF8, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}}, - EncodeT{0x8888888888888888, []byte{0xF8, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88}}, - EncodeT{1 << 63, []byte{0xF8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {0x00, []byte{0x00}}, + {0x0F, []byte{0x0F}}, + {0xFF, []byte{0xFF, 0xFF}}, + {0xFFFF, []byte{0xFE, 0xFF, 0xFF}}, + {0xFFFFFF, []byte{0xFD, 0xFF, 0xFF, 0xFF}}, + {0xFFFFFFFF, []byte{0xFC, 0xFF, 0xFF, 0xFF, 0xFF}}, + {0xFFFFFFFFFF, []byte{0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {0xFFFFFFFFFFFF, []byte{0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {0xFFFFFFFFFFFFFF, []byte{0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {0xFFFFFFFFFFFFFFFF, []byte{0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {0x1111, []byte{0xFE, 0x11, 0x11}}, + {0x1111111111111111, []byte{0xF8, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}}, + {0x8888888888888888, []byte{0xF8, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88}}, + {1 << 63, []byte{0xF8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, +} + +// testError is meant to be used as a deferred function to turn a panic(gobError) into a +// plain test.Error call. +func testError(t *testing.T) { + if e := recover(); e != nil { + t.Error(e.(gobError).Error) // Will re-panic if not one of our errors, such as a runtime error. + } + return } // Test basic encode/decode routines for unsigned integers func TestUintCodec(t *testing.T) { + defer testError(t) b := new(bytes.Buffer) - encState := new(encoderState) - encState.b = b + encState := newEncoderState(nil, b) for _, tt := range encodeT { b.Reset() - encodeUint(encState, tt.x) - if encState.err != nil { - t.Error("encodeUint:", tt.x, encState.err) - } + encState.encodeUint(tt.x) if !bytes.Equal(tt.b, b.Bytes()) { t.Errorf("encodeUint: %#x encode: expected % x got % x", tt.x, tt.b, b.Bytes()) } } - decState := newDecodeState(b) + decState := newDecodeState(nil, &b) for u := uint64(0); ; u = (u + 1) * 7 { b.Reset() - encodeUint(encState, u) - if encState.err != nil { - t.Error("encodeUint:", u, encState.err) - } - v := decodeUint(decState) - if decState.err != nil { - t.Error("DecodeUint:", u, decState.err) - } + encState.encodeUint(u) + v := decState.decodeUint() if u != v { - t.Errorf("Encode/Decode: sent %#x received %#x\n", u, v) + t.Errorf("Encode/Decode: sent %#x received %#x", u, v) } if u&(1<<63) != 0 { break @@ -73,21 +73,15 @@ func TestUintCodec(t *testing.T) { } func verifyInt(i int64, t *testing.T) { + defer testError(t) var b = new(bytes.Buffer) - encState := new(encoderState) - encState.b = b - encodeInt(encState, i) - if encState.err != nil { - t.Error("encodeInt:", i, encState.err) - } - decState := newDecodeState(b) + encState := newEncoderState(nil, b) + encState.encodeInt(i) + decState := newDecodeState(nil, &b) decState.buf = make([]byte, 8) - j := decodeInt(decState) - if decState.err != nil { - t.Error("DecodeInt:", i, decState.err) - } + j := decState.decodeInt() if i != j { - t.Errorf("Encode/Decode: sent %#x received %#x\n", uint64(i), uint64(j)) + t.Errorf("Encode/Decode: sent %#x received %#x", uint64(i), uint64(j)) } } @@ -119,8 +113,7 @@ var bytesResult = []byte{0x07, 0x05, 'h', 'e', 'l', 'l', 'o'} func newencoderState(b *bytes.Buffer) *encoderState { b.Reset() - state := new(encoderState) - state.b = b + state := newEncoderState(nil, b) state.fieldnum = -1 return state } @@ -323,10 +316,8 @@ func TestScalarEncInstructions(t *testing.T) { } func execDec(typ string, instr *decInstr, state *decodeState, t *testing.T, p unsafe.Pointer) { - v := int(decodeUint(state)) - if state.err != nil { - t.Fatalf("decoding %s field: %v", typ, state.err) - } + defer testError(t) + v := int(state.decodeUint()) if v+state.fieldnum != 6 { t.Fatalf("decoding field number %d, got %d", 6, v+state.fieldnum) } @@ -335,7 +326,8 @@ func execDec(typ string, instr *decInstr, state *decodeState, t *testing.T, p un } func newDecodeStateFromData(data []byte) *decodeState { - state := newDecodeState(bytes.NewBuffer(data)) + b := bytes.NewBuffer(data) + state := newDecodeState(nil, &b) state.fieldnum = -1 return state } @@ -607,35 +599,35 @@ func TestScalarDecInstructions(t *testing.T) { func TestEndToEnd(t *testing.T) { type T2 struct { - t string + T string } s1 := "string1" s2 := "string2" type T1 struct { - a, b, c int - m map[string]*float - n *[3]float - strs *[2]string - int64s *[]int64 - ri complex64 - s string - y []byte - t *T2 + A, B, C int + M map[string]*float + N *[3]float + Strs *[2]string + Int64s *[]int64 + RI complex64 + S string + Y []byte + T *T2 } pi := 3.14159 e := 2.71828 t1 := &T1{ - a: 17, - b: 18, - c: -5, - m: map[string]*float{"pi": &pi, "e": &e}, - n: &[3]float{1.5, 2.5, 3.5}, - strs: &[2]string{s1, s2}, - int64s: &[]int64{77, 89, 123412342134}, - ri: 17 - 23i, - s: "Now is the time", - y: []byte("hello, sailor"), - t: &T2{"this is T2"}, + A: 17, + B: 18, + C: -5, + M: map[string]*float{"pi": &pi, "e": &e}, + N: &[3]float{1.5, 2.5, 3.5}, + Strs: &[2]string{s1, s2}, + Int64s: &[]int64{77, 89, 123412342134}, + RI: 17 - 23i, + S: "Now is the time", + Y: []byte("hello, sailor"), + T: &T2{"this is T2"}, } b := new(bytes.Buffer) err := NewEncoder(b).Encode(t1) @@ -654,13 +646,13 @@ func TestEndToEnd(t *testing.T) { func TestOverflow(t *testing.T) { type inputT struct { - maxi int64 - mini int64 - maxu uint64 - maxf float64 - minf float64 - maxc complex128 - minc complex128 + Maxi int64 + Mini int64 + Maxu uint64 + Maxf float64 + Minf float64 + Maxc complex128 + Minc complex128 } var it inputT var err os.Error @@ -671,152 +663,152 @@ func TestOverflow(t *testing.T) { // int8 b.Reset() it = inputT{ - maxi: math.MaxInt8 + 1, + Maxi: math.MaxInt8 + 1, } type outi8 struct { - maxi int8 - mini int8 + Maxi int8 + Mini int8 } var o1 outi8 enc.Encode(it) err = dec.Decode(&o1) - if err == nil || err.String() != `value for "maxi" out of range` { + if err == nil || err.String() != `value for "Maxi" out of range` { t.Error("wrong overflow error for int8:", err) } it = inputT{ - mini: math.MinInt8 - 1, + Mini: math.MinInt8 - 1, } b.Reset() enc.Encode(it) err = dec.Decode(&o1) - if err == nil || err.String() != `value for "mini" out of range` { + if err == nil || err.String() != `value for "Mini" out of range` { t.Error("wrong underflow error for int8:", err) } // int16 b.Reset() it = inputT{ - maxi: math.MaxInt16 + 1, + Maxi: math.MaxInt16 + 1, } type outi16 struct { - maxi int16 - mini int16 + Maxi int16 + Mini int16 } var o2 outi16 enc.Encode(it) err = dec.Decode(&o2) - if err == nil || err.String() != `value for "maxi" out of range` { + if err == nil || err.String() != `value for "Maxi" out of range` { t.Error("wrong overflow error for int16:", err) } it = inputT{ - mini: math.MinInt16 - 1, + Mini: math.MinInt16 - 1, } b.Reset() enc.Encode(it) err = dec.Decode(&o2) - if err == nil || err.String() != `value for "mini" out of range` { + if err == nil || err.String() != `value for "Mini" out of range` { t.Error("wrong underflow error for int16:", err) } // int32 b.Reset() it = inputT{ - maxi: math.MaxInt32 + 1, + Maxi: math.MaxInt32 + 1, } type outi32 struct { - maxi int32 - mini int32 + Maxi int32 + Mini int32 } var o3 outi32 enc.Encode(it) err = dec.Decode(&o3) - if err == nil || err.String() != `value for "maxi" out of range` { + if err == nil || err.String() != `value for "Maxi" out of range` { t.Error("wrong overflow error for int32:", err) } it = inputT{ - mini: math.MinInt32 - 1, + Mini: math.MinInt32 - 1, } b.Reset() enc.Encode(it) err = dec.Decode(&o3) - if err == nil || err.String() != `value for "mini" out of range` { + if err == nil || err.String() != `value for "Mini" out of range` { t.Error("wrong underflow error for int32:", err) } // uint8 b.Reset() it = inputT{ - maxu: math.MaxUint8 + 1, + Maxu: math.MaxUint8 + 1, } type outu8 struct { - maxu uint8 + Maxu uint8 } var o4 outu8 enc.Encode(it) err = dec.Decode(&o4) - if err == nil || err.String() != `value for "maxu" out of range` { + if err == nil || err.String() != `value for "Maxu" out of range` { t.Error("wrong overflow error for uint8:", err) } // uint16 b.Reset() it = inputT{ - maxu: math.MaxUint16 + 1, + Maxu: math.MaxUint16 + 1, } type outu16 struct { - maxu uint16 + Maxu uint16 } var o5 outu16 enc.Encode(it) err = dec.Decode(&o5) - if err == nil || err.String() != `value for "maxu" out of range` { + if err == nil || err.String() != `value for "Maxu" out of range` { t.Error("wrong overflow error for uint16:", err) } // uint32 b.Reset() it = inputT{ - maxu: math.MaxUint32 + 1, + Maxu: math.MaxUint32 + 1, } type outu32 struct { - maxu uint32 + Maxu uint32 } var o6 outu32 enc.Encode(it) err = dec.Decode(&o6) - if err == nil || err.String() != `value for "maxu" out of range` { + if err == nil || err.String() != `value for "Maxu" out of range` { t.Error("wrong overflow error for uint32:", err) } // float32 b.Reset() it = inputT{ - maxf: math.MaxFloat32 * 2, + Maxf: math.MaxFloat32 * 2, } type outf32 struct { - maxf float32 - minf float32 + Maxf float32 + Minf float32 } var o7 outf32 enc.Encode(it) err = dec.Decode(&o7) - if err == nil || err.String() != `value for "maxf" out of range` { + if err == nil || err.String() != `value for "Maxf" out of range` { t.Error("wrong overflow error for float32:", err) } // complex64 b.Reset() it = inputT{ - maxc: cmplx(math.MaxFloat32*2, math.MaxFloat32*2), + Maxc: cmplx(math.MaxFloat32*2, math.MaxFloat32*2), } type outc64 struct { - maxc complex64 - minc complex64 + Maxc complex64 + Minc complex64 } var o8 outc64 enc.Encode(it) err = dec.Decode(&o8) - if err == nil || err.String() != `value for "maxc" out of range` { + if err == nil || err.String() != `value for "Maxc" out of range` { t.Error("wrong overflow error for complex64:", err) } } @@ -824,92 +816,92 @@ func TestOverflow(t *testing.T) { func TestNesting(t *testing.T) { type RT struct { - a string - next *RT + A string + Next *RT } rt := new(RT) - rt.a = "level1" - rt.next = new(RT) - rt.next.a = "level2" + rt.A = "level1" + rt.Next = new(RT) + rt.Next.A = "level2" b := new(bytes.Buffer) NewEncoder(b).Encode(rt) var drt RT dec := NewDecoder(b) err := dec.Decode(&drt) if err != nil { - t.Errorf("decoder error:", err) + t.Fatal("decoder error:", err) } - if drt.a != rt.a { + if drt.A != rt.A { t.Errorf("nesting: encode expected %v got %v", *rt, drt) } - if drt.next == nil { + if drt.Next == nil { t.Errorf("nesting: recursion failed") } - if drt.next.a != rt.next.a { - t.Errorf("nesting: encode expected %v got %v", *rt.next, *drt.next) + if drt.Next.A != rt.Next.A { + t.Errorf("nesting: encode expected %v got %v", *rt.Next, *drt.Next) } } // These three structures have the same data with different indirections type T0 struct { - a int - b int - c int - d int + A int + B int + C int + D int } type T1 struct { - a int - b *int - c **int - d ***int + A int + B *int + C **int + D ***int } type T2 struct { - a ***int - b **int - c *int - d int + A ***int + B **int + C *int + D int } func TestAutoIndirection(t *testing.T) { // First transfer t1 into t0 var t1 T1 - t1.a = 17 - t1.b = new(int) - *t1.b = 177 - t1.c = new(*int) - *t1.c = new(int) - **t1.c = 1777 - t1.d = new(**int) - *t1.d = new(*int) - **t1.d = new(int) - ***t1.d = 17777 + t1.A = 17 + t1.B = new(int) + *t1.B = 177 + t1.C = new(*int) + *t1.C = new(int) + **t1.C = 1777 + t1.D = new(**int) + *t1.D = new(*int) + **t1.D = new(int) + ***t1.D = 17777 b := new(bytes.Buffer) enc := NewEncoder(b) enc.Encode(t1) dec := NewDecoder(b) var t0 T0 dec.Decode(&t0) - if t0.a != 17 || t0.b != 177 || t0.c != 1777 || t0.d != 17777 { + if t0.A != 17 || t0.B != 177 || t0.C != 1777 || t0.D != 17777 { t.Errorf("t1->t0: expected {17 177 1777 17777}; got %v", t0) } // Now transfer t2 into t0 var t2 T2 - t2.d = 17777 - t2.c = new(int) - *t2.c = 1777 - t2.b = new(*int) - *t2.b = new(int) - **t2.b = 177 - t2.a = new(**int) - *t2.a = new(*int) - **t2.a = new(int) - ***t2.a = 17 + t2.D = 17777 + t2.C = new(int) + *t2.C = 1777 + t2.B = new(*int) + *t2.B = new(int) + **t2.B = 177 + t2.A = new(**int) + *t2.A = new(*int) + **t2.A = new(int) + ***t2.A = 17 b.Reset() enc.Encode(t2) t0 = T0{} dec.Decode(&t0) - if t0.a != 17 || t0.b != 177 || t0.c != 1777 || t0.d != 17777 { + if t0.A != 17 || t0.B != 177 || t0.C != 1777 || t0.D != 17777 { t.Errorf("t2->t0 expected {17 177 1777 17777}; got %v", t0) } @@ -919,8 +911,8 @@ func TestAutoIndirection(t *testing.T) { enc.Encode(t0) t1 = T1{} dec.Decode(&t1) - if t1.a != 17 || *t1.b != 177 || **t1.c != 1777 || ***t1.d != 17777 { - t.Errorf("t0->t1 expected {17 177 1777 17777}; got {%d %d %d %d}", t1.a, *t1.b, **t1.c, ***t1.d) + if t1.A != 17 || *t1.B != 177 || **t1.C != 1777 || ***t1.D != 17777 { + t.Errorf("t0->t1 expected {17 177 1777 17777}; got {%d %d %d %d}", t1.A, *t1.B, **t1.C, ***t1.D) } // Now transfer t0 into t2 @@ -928,40 +920,40 @@ func TestAutoIndirection(t *testing.T) { enc.Encode(t0) t2 = T2{} dec.Decode(&t2) - if ***t2.a != 17 || **t2.b != 177 || *t2.c != 1777 || t2.d != 17777 { - t.Errorf("t0->t2 expected {17 177 1777 17777}; got {%d %d %d %d}", ***t2.a, **t2.b, *t2.c, t2.d) + if ***t2.A != 17 || **t2.B != 177 || *t2.C != 1777 || t2.D != 17777 { + t.Errorf("t0->t2 expected {17 177 1777 17777}; got {%d %d %d %d}", ***t2.A, **t2.B, *t2.C, t2.D) } // Now do t2 again but without pre-allocated pointers. b.Reset() enc.Encode(t0) - ***t2.a = 0 - **t2.b = 0 - *t2.c = 0 - t2.d = 0 + ***t2.A = 0 + **t2.B = 0 + *t2.C = 0 + t2.D = 0 dec.Decode(&t2) - if ***t2.a != 17 || **t2.b != 177 || *t2.c != 1777 || t2.d != 17777 { - t.Errorf("t0->t2 expected {17 177 1777 17777}; got {%d %d %d %d}", ***t2.a, **t2.b, *t2.c, t2.d) + if ***t2.A != 17 || **t2.B != 177 || *t2.C != 1777 || t2.D != 17777 { + t.Errorf("t0->t2 expected {17 177 1777 17777}; got {%d %d %d %d}", ***t2.A, **t2.B, *t2.C, t2.D) } } type RT0 struct { - a int - b string - c float + A int + B string + C float } type RT1 struct { - c float - b string - a int - notSet string + C float + B string + A int + NotSet string } func TestReorderedFields(t *testing.T) { var rt0 RT0 - rt0.a = 17 - rt0.b = "hello" - rt0.c = 3.14159 + rt0.A = 17 + rt0.B = "hello" + rt0.C = 3.14159 b := new(bytes.Buffer) NewEncoder(b).Encode(rt0) dec := NewDecoder(b) @@ -969,41 +961,41 @@ func TestReorderedFields(t *testing.T) { // Wire type is RT0, local type is RT1. err := dec.Decode(&rt1) if err != nil { - t.Error("decode error:", err) + t.Fatal("decode error:", err) } - if rt0.a != rt1.a || rt0.b != rt1.b || rt0.c != rt1.c { + if rt0.A != rt1.A || rt0.B != rt1.B || rt0.C != rt1.C { t.Errorf("rt1->rt0: expected %v; got %v", rt0, rt1) } } // Like an RT0 but with fields we'll ignore on the decode side. type IT0 struct { - a int64 - b string - ignore_d []int - ignore_e [3]float - ignore_f bool - ignore_g string - ignore_h []byte - ignore_i *RT1 - ignore_m map[string]int - c float + A int64 + B string + Ignore_d []int + Ignore_e [3]float + Ignore_f bool + Ignore_g string + Ignore_h []byte + Ignore_i *RT1 + Ignore_m map[string]int + C float } func TestIgnoredFields(t *testing.T) { var it0 IT0 - it0.a = 17 - it0.b = "hello" - it0.c = 3.14159 - it0.ignore_d = []int{1, 2, 3} - it0.ignore_e[0] = 1.0 - it0.ignore_e[1] = 2.0 - it0.ignore_e[2] = 3.0 - it0.ignore_f = true - it0.ignore_g = "pay no attention" - it0.ignore_h = []byte("to the curtain") - it0.ignore_i = &RT1{3.1, "hi", 7, "hello"} - it0.ignore_m = map[string]int{"one": 1, "two": 2} + it0.A = 17 + it0.B = "hello" + it0.C = 3.14159 + it0.Ignore_d = []int{1, 2, 3} + it0.Ignore_e[0] = 1.0 + it0.Ignore_e[1] = 2.0 + it0.Ignore_e[2] = 3.0 + it0.Ignore_f = true + it0.Ignore_g = "pay no attention" + it0.Ignore_h = []byte("to the curtain") + it0.Ignore_i = &RT1{3.1, "hi", 7, "hello"} + it0.Ignore_m = map[string]int{"one": 1, "two": 2} b := new(bytes.Buffer) NewEncoder(b).Encode(it0) @@ -1014,56 +1006,58 @@ func TestIgnoredFields(t *testing.T) { if err != nil { t.Error("error: ", err) } - if int(it0.a) != rt1.a || it0.b != rt1.b || it0.c != rt1.c { - t.Errorf("rt1->rt0: expected %v; got %v", it0, rt1) + if int(it0.A) != rt1.A || it0.B != rt1.B || it0.C != rt1.C { + t.Errorf("rt0->rt1: expected %v; got %v", it0, rt1) } } type Bad0 struct { - inter interface{} - c float + ch chan int + c float } +var nilEncoder *Encoder + func TestInvalidField(t *testing.T) { var bad0 Bad0 - bad0.inter = 17 + bad0.ch = make(chan int) b := new(bytes.Buffer) - err := encode(b, reflect.NewValue(&bad0)) + err := nilEncoder.encode(b, reflect.NewValue(&bad0)) if err == nil { t.Error("expected error; got none") - } else if strings.Index(err.String(), "interface") < 0 { + } else if strings.Index(err.String(), "type") < 0 { t.Error("expected type error; got", err) } } type Indirect struct { - a ***[3]int - s ***[]int - m ****map[string]int + A ***[3]int + S ***[]int + M ****map[string]int } type Direct struct { - a [3]int - s []int - m map[string]int + A [3]int + S []int + M map[string]int } func TestIndirectSliceMapArray(t *testing.T) { // Marshal indirect, unmarshal to direct. i := new(Indirect) - i.a = new(**[3]int) - *i.a = new(*[3]int) - **i.a = new([3]int) - ***i.a = [3]int{1, 2, 3} - i.s = new(**[]int) - *i.s = new(*[]int) - **i.s = new([]int) - ***i.s = []int{4, 5, 6} - i.m = new(***map[string]int) - *i.m = new(**map[string]int) - **i.m = new(*map[string]int) - ***i.m = new(map[string]int) - ****i.m = map[string]int{"one": 1, "two": 2, "three": 3} + i.A = new(**[3]int) + *i.A = new(*[3]int) + **i.A = new([3]int) + ***i.A = [3]int{1, 2, 3} + i.S = new(**[]int) + *i.S = new(*[]int) + **i.S = new([]int) + ***i.S = []int{4, 5, 6} + i.M = new(***map[string]int) + *i.M = new(**map[string]int) + **i.M = new(*map[string]int) + ***i.M = new(map[string]int) + ****i.M = map[string]int{"one": 1, "two": 2, "three": 3} b := new(bytes.Buffer) NewEncoder(b).Encode(i) dec := NewDecoder(b) @@ -1072,34 +1066,328 @@ func TestIndirectSliceMapArray(t *testing.T) { if err != nil { t.Error("error: ", err) } - if len(d.a) != 3 || d.a[0] != 1 || d.a[1] != 2 || d.a[2] != 3 { - t.Errorf("indirect to direct: d.a is %v not %v", d.a, ***i.a) + if len(d.A) != 3 || d.A[0] != 1 || d.A[1] != 2 || d.A[2] != 3 { + t.Errorf("indirect to direct: d.A is %v not %v", d.A, ***i.A) } - if len(d.s) != 3 || d.s[0] != 4 || d.s[1] != 5 || d.s[2] != 6 { - t.Errorf("indirect to direct: d.s is %v not %v", d.s, ***i.s) + if len(d.S) != 3 || d.S[0] != 4 || d.S[1] != 5 || d.S[2] != 6 { + t.Errorf("indirect to direct: d.S is %v not %v", d.S, ***i.S) } - if len(d.m) != 3 || d.m["one"] != 1 || d.m["two"] != 2 || d.m["three"] != 3 { - t.Errorf("indirect to direct: d.m is %v not %v", d.m, ***i.m) + if len(d.M) != 3 || d.M["one"] != 1 || d.M["two"] != 2 || d.M["three"] != 3 { + t.Errorf("indirect to direct: d.M is %v not %v", d.M, ***i.M) } // Marshal direct, unmarshal to indirect. - d.a = [3]int{11, 22, 33} - d.s = []int{44, 55, 66} - d.m = map[string]int{"four": 4, "five": 5, "six": 6} + d.A = [3]int{11, 22, 33} + d.S = []int{44, 55, 66} + d.M = map[string]int{"four": 4, "five": 5, "six": 6} i = new(Indirect) b.Reset() NewEncoder(b).Encode(d) dec = NewDecoder(b) err = dec.Decode(&i) if err != nil { - t.Error("error: ", err) + t.Fatal("error: ", err) + } + if len(***i.A) != 3 || (***i.A)[0] != 11 || (***i.A)[1] != 22 || (***i.A)[2] != 33 { + t.Errorf("direct to indirect: ***i.A is %v not %v", ***i.A, d.A) + } + if len(***i.S) != 3 || (***i.S)[0] != 44 || (***i.S)[1] != 55 || (***i.S)[2] != 66 { + t.Errorf("direct to indirect: ***i.S is %v not %v", ***i.S, ***i.S) + } + if len(****i.M) != 3 || (****i.M)["four"] != 4 || (****i.M)["five"] != 5 || (****i.M)["six"] != 6 { + t.Errorf("direct to indirect: ****i.M is %v not %v", ****i.M, d.M) + } +} + +// An interface with several implementations +type Squarer interface { + Square() int +} + +type Int int + +func (i Int) Square() int { + return int(i * i) +} + +type Float float + +func (f Float) Square() int { + return int(f * f) +} + +type Vector []int + +func (v Vector) Square() int { + sum := 0 + for _, x := range v { + sum += x * x + } + return sum +} + +type Point struct { + a, b int +} + +func (p Point) Square() int { + return p.a*p.a + p.b*p.b +} + +// A struct with interfaces in it. +type InterfaceItem struct { + I int + Sq1, Sq2, Sq3 Squarer + F float + Sq []Squarer +} + +// The same struct without interfaces +type NoInterfaceItem struct { + I int + F float +} + +func TestInterface(t *testing.T) { + iVal := Int(3) + fVal := Float(5) + // Sending a Vector will require that the receiver define a type in the middle of + // receiving the value for item2. + vVal := Vector{1, 2, 3} + b := new(bytes.Buffer) + item1 := &InterfaceItem{1, iVal, fVal, vVal, 11.5, []Squarer{iVal, fVal, nil, vVal}} + // Register the types. + Register(Int(0)) + Register(Float(0)) + Register(Vector{}) + err := NewEncoder(b).Encode(item1) + if err != nil { + t.Error("expected no encode error; got", err) + } + + item2 := InterfaceItem{} + err = NewDecoder(b).Decode(&item2) + if err != nil { + t.Fatal("decode:", err) + } + if item2.I != item1.I { + t.Error("normal int did not decode correctly") + } + if item2.Sq1 == nil || item2.Sq1.Square() != iVal.Square() { + t.Error("Int did not decode correctly") + } + if item2.Sq2 == nil || item2.Sq2.Square() != fVal.Square() { + t.Error("Float did not decode correctly") + } + if item2.Sq3 == nil || item2.Sq3.Square() != vVal.Square() { + t.Error("Vector did not decode correctly") + } + if item2.F != item1.F { + t.Error("normal float did not decode correctly") } - if len(***i.a) != 3 || (***i.a)[0] != 11 || (***i.a)[1] != 22 || (***i.a)[2] != 33 { - t.Errorf("direct to indirect: ***i.a is %v not %v", ***i.a, d.a) + // Now check that we received a slice of Squarers correctly, including a nil element + if len(item1.Sq) != len(item2.Sq) { + t.Fatalf("[]Squarer length wrong: got %d; expected %d", len(item2.Sq), len(item1.Sq)) } - if len(***i.s) != 3 || (***i.s)[0] != 44 || (***i.s)[1] != 55 || (***i.s)[2] != 66 { - t.Errorf("direct to indirect: ***i.s is %v not %v", ***i.s, ***i.s) + for i, v1 := range item1.Sq { + v2 := item2.Sq[i] + if v1 == nil || v2 == nil { + if v1 != nil || v2 != nil { + t.Errorf("item %d inconsistent nils", i) + } + continue + if v1.Square() != v2.Square() { + t.Errorf("item %d inconsistent values: %v %v", i, v1, v2) + } + } } - if len(****i.m) != 3 || (****i.m)["four"] != 4 || (****i.m)["five"] != 5 || (****i.m)["six"] != 6 { - t.Errorf("direct to indirect: ****i.m is %v not %v", ****i.m, d.m) + +} + +// A struct with all basic types, stored in interfaces. +type BasicInterfaceItem struct { + Int, Int8, Int16, Int32, Int64 interface{} + Uint, Uint8, Uint16, Uint32, Uint64 interface{} + Float, Float32, Float64 interface{} + Complex, Complex64, Complex128 interface{} + Bool interface{} + String interface{} + Bytes interface{} +} + +func TestInterfaceBasic(t *testing.T) { + b := new(bytes.Buffer) + item1 := &BasicInterfaceItem{ + int(1), int8(1), int16(1), int32(1), int64(1), + uint(1), uint8(1), uint16(1), uint32(1), uint64(1), + float(1), float32(1), float64(1), + complex(0i), complex64(0i), complex128(0i), + true, + "hello", + []byte("sailor"), + } + err := NewEncoder(b).Encode(item1) + if err != nil { + t.Error("expected no encode error; got", err) + } + + item2 := &BasicInterfaceItem{} + err = NewDecoder(b).Decode(&item2) + if err != nil { + t.Fatal("decode:", err) + } + if !reflect.DeepEqual(item1, item2) { + t.Errorf("encode expected %v got %v", item1, item2) + } + // Hand check a couple for correct types. + if v, ok := item2.Bool.(bool); !ok || !v { + t.Error("boolean should be true") + } + if v, ok := item2.String.(string); !ok || v != item1.String.(string) { + t.Errorf("string should be %v is %v", item1.String, v) + } +} + +type String string + +type PtrInterfaceItem struct { + Str1 interface{} // basic + Str2 interface{} // derived +} + +// We'll send pointers; should receive values. +// Also check that we can register T but send *T. +func TestInterfacePointer(t *testing.T) { + b := new(bytes.Buffer) + str1 := "howdy" + str2 := String("kiddo") + item1 := &PtrInterfaceItem{ + &str1, + &str2, + } + // Register the type. + Register(str2) + err := NewEncoder(b).Encode(item1) + if err != nil { + t.Error("expected no encode error; got", err) + } + + item2 := &PtrInterfaceItem{} + err = NewDecoder(b).Decode(&item2) + if err != nil { + t.Fatal("decode:", err) + } + // Hand test for correct types and values. + if v, ok := item2.Str1.(string); !ok || v != str1 { + t.Errorf("basic string failed: %q should be %q", v, str1) + } + if v, ok := item2.Str2.(String); !ok || v != str2 { + t.Errorf("derived type String failed: %q should be %q", v, str2) + } +} + +func TestIgnoreInterface(t *testing.T) { + iVal := Int(3) + fVal := Float(5) + // Sending a Point will require that the receiver define a type in the middle of + // receiving the value for item2. + pVal := Point{2, 3} + b := new(bytes.Buffer) + item1 := &InterfaceItem{1, iVal, fVal, pVal, 11.5, nil} + // Register the types. + Register(Int(0)) + Register(Float(0)) + Register(Point{}) + err := NewEncoder(b).Encode(item1) + if err != nil { + t.Error("expected no encode error; got", err) + } + + item2 := NoInterfaceItem{} + err = NewDecoder(b).Decode(&item2) + if err != nil { + t.Fatal("decode:", err) + } + if item2.I != item1.I { + t.Error("normal int did not decode correctly") + } + if item2.F != item2.F { + t.Error("normal float did not decode correctly") + } +} + +type U struct { + A int + B string + c float + D uint +} + +func TestUnexportedFields(t *testing.T) { + var u0 U + u0.A = 17 + u0.B = "hello" + u0.c = 3.14159 + u0.D = 23 + b := new(bytes.Buffer) + NewEncoder(b).Encode(u0) + dec := NewDecoder(b) + var u1 U + u1.c = 1234. + err := dec.Decode(&u1) + if err != nil { + t.Fatal("decode error:", err) + } + if u0.A != u0.A || u0.B != u1.B || u0.D != u1.D { + t.Errorf("u1->u0: expected %v; got %v", u0, u1) + } + if u1.c != 1234. { + t.Error("u1.c modified") + } +} + +// A type that won't be defined in the gob until we send it in an interface value. +type OnTheFly struct { + A int +} + +type DT struct { + // X OnTheFly + A int + B string + C float + I interface{} + J interface{} + I_nil interface{} + M map[string]int + T [3]int + S []string +} + +func TestDebug(t *testing.T) { + if debugFunc == nil { + return + } + Register(OnTheFly{}) + var dt DT + dt.A = 17 + dt.B = "hello" + dt.C = 3.14159 + dt.I = 271828 + dt.J = OnTheFly{3} + dt.I_nil = nil + dt.M = map[string]int{"one": 1, "two": 2} + dt.T = [3]int{11, 22, 33} + dt.S = []string{"hi", "joe"} + b := new(bytes.Buffer) + err := NewEncoder(b).Encode(dt) + if err != nil { + t.Fatal("encode:", err) + } + debugBuffer := bytes.NewBuffer(b.Bytes()) + dt2 := &DT{} + err = NewDecoder(b).Decode(&dt2) + if err != nil { + t.Error("decode:", err) } + debugFunc(debugBuffer) } diff --git a/src/pkg/gob/debug.go b/src/pkg/gob/debug.go index 02f2602ed..f3632a080 100644 --- a/src/pkg/gob/debug.go +++ b/src/pkg/gob/debug.go @@ -1,157 +1,236 @@ package gob // This file is not normally included in the gob package. Used only for debugging the package itself. +// Add debug.go to the files listed in the Makefile to add Debug to the gob package. import ( "bytes" "fmt" "io" - "log" "os" + "reflect" + "runtime" ) -// Debug prints a human-readable representation of the gob data read from r. -func Debug(r io.Reader) { NewDecoder(r).debug() } +var dump = false // If true, print the remaining bytes in the input buffer at each item. -// debug is like Decode but just prints what it finds. It should be safe even for corrupted data. -func (dec *Decoder) debug() { - dec.state.err = nil - for { - // Read a count. - var nbytes uint64 - nbytes, dec.state.err = decodeUintReader(dec.r, dec.countBuf[0:]) - if dec.state.err != nil { - break - } +// Init installs the debugging facility. If this file is not compiled in the +// package, the test in codec_test.go is a no-op. +func init() { + debugFunc = Debug +} - // Allocate the buffer. - if nbytes > uint64(len(dec.buf)) { - dec.buf = make([]byte, nbytes+1000) +// Debug prints a human-readable representation of the gob data read from r. +func Debug(r io.Reader) { + defer func() { + if e := recover(); e != nil { + if _, ok := e.(runtime.Error); ok { + panic(e) + } + fmt.Printf("error during debugging: %v\n", e) } - dec.state.b = bytes.NewBuffer(dec.buf[0:nbytes]) + }() + NewDecoder(r).debug() +} - // Read the data - _, dec.state.err = io.ReadFull(dec.r, dec.buf[0:nbytes]) - if dec.state.err != nil { - if dec.state.err == os.EOF { - dec.state.err = io.ErrUnexpectedEOF - } - break +// debugRecv is like recv but prints what it sees. +func (dec *Decoder) debugRecv() { + if dec.byteBuffer != nil && dec.byteBuffer.Len() != 0 { + fmt.Printf("error in recv: %d bytes left in input buffer\n", dec.byteBuffer.Len()) + return + } + // Read a count. + var nbytes uint64 + nbytes, dec.err = decodeUintReader(dec.r, dec.countBuf[0:]) + if dec.err != nil { + fmt.Printf("receiver error on count: %s\n", dec.err) + return + } + // Allocate the buffer. + if nbytes > uint64(len(dec.buf)) { + dec.buf = make([]byte, nbytes+1000) + } + dec.byteBuffer = bytes.NewBuffer(dec.buf[0:nbytes]) + + // Read the data + _, dec.err = io.ReadFull(dec.r, dec.buf[0:nbytes]) + if dec.err != nil { + fmt.Printf("receiver error on data: %s\n", dec.err) + if dec.err == os.EOF { + dec.err = io.ErrUnexpectedEOF } + return + } + if dump { + fmt.Printf("received %d bytes:\n\t% x\n", nbytes, dec.byteBuffer.Bytes()) + } +} + +// debug is like Decode but just prints what it finds. It should be safe even for corrupted data. +func (dec *Decoder) debug() { + // Make sure we're single-threaded through here. + dec.mutex.Lock() + defer dec.mutex.Unlock() + + dec.err = nil + dec.debugRecv() + if dec.err != nil { + return + } + dec.debugFromBuffer(0, false) +} + +// printFromBuffer prints the next value. The buffer contains data, but it may +// be a type descriptor and we may need to load more data to see the value; +// printType takes care of that. +func (dec *Decoder) debugFromBuffer(indent int, countPresent bool) { + for dec.state.b.Len() > 0 { // Receive a type id. - id := typeId(decodeInt(dec.state)) - if dec.state.err != nil { - break - } + id := typeId(dec.state.decodeInt()) // Is it a new type? if id < 0 { // 0 is the error state, handled above // If the id is negative, we have a type. - fmt.Printf("new type id %d\n", -id) - dec.printType(-id) - if dec.state.err != nil { + dec.debugRecvType(-id) + if dec.err != nil { break } continue } - fmt.Printf("type id %d\n", id) // No, it's a value. - // Make sure the type has been defined already. - _, ok := dec.wireType[id] - if !ok { - dec.state.err = errBadType + // Make sure the type has been defined already or is a builtin type (for + // top-level singleton values). + if dec.wireType[id] == nil && builtinIdToType[id] == nil { + dec.err = errBadType break } - fmt.Printf("\t%d bytes:\t% x\n", nbytes, dec.state.b.Bytes()) - dec.printData(0, id) + if countPresent { + dec.state.decodeUint() + } + dec.debugPrint(indent, id) break } - if dec.state.err != nil { - log.Stderr("debug:", dec.state.err) - } } -func (dec *Decoder) printType(id typeId) { +func (dec *Decoder) debugRecvType(id typeId) { // Have we already seen this type? That's an error if _, alreadySeen := dec.wireType[id]; alreadySeen { - dec.state.err = os.ErrorString("gob: duplicate type received") + dec.err = os.ErrorString("gob: duplicate type received") return } // Type: wire := new(wireType) - dec.state.err = dec.decode(tWireType, wire) - if dec.state.err == nil { + dec.err = dec.decode(tWireType, reflect.NewValue(wire)) + if dec.err == nil { printWireType(wire) } // Remember we've seen this type. dec.wireType[id] = wire + + // Load the next parcel. + dec.debugRecv() } func printWireType(wire *wireType) { + fmt.Printf("type definition {\n") switch { - case wire.array != nil: - printCommonType("array", &wire.array.commonType) - fmt.Printf("\tlen %d\n\telemid %d\n", wire.array.Len, wire.array.Elem) - case wire.slice != nil: - printCommonType("slice", &wire.slice.commonType) - fmt.Printf("\telemid %d\n", wire.slice.Elem) - case wire.strct != nil: - printCommonType("struct", &wire.strct.commonType) - for i, field := range wire.strct.field { - fmt.Printf("\tfield %d:\t%s\tid=%d\n", i, field.name, field.id) + case wire.ArrayT != nil: + printCommonType("array", &wire.ArrayT.CommonType) + fmt.Printf("\tlen %d\n\telemid %d\n", wire.ArrayT.Len, wire.ArrayT.Elem) + case wire.MapT != nil: + printCommonType("map", &wire.MapT.CommonType) + fmt.Printf("\tkeyid %d\n", wire.MapT.Key) + fmt.Printf("\telemid %d\n", wire.MapT.Elem) + case wire.SliceT != nil: + printCommonType("slice", &wire.SliceT.CommonType) + fmt.Printf("\telemid %d\n", wire.SliceT.Elem) + case wire.StructT != nil: + printCommonType("struct", &wire.StructT.CommonType) + for i, field := range wire.StructT.Field { + fmt.Printf("\tfield %d:\t%s\tid=%d\n", i, field.Name, field.Id) } } + fmt.Printf("}\n") } -func printCommonType(kind string, common *commonType) { - fmt.Printf("\t%s %s\n\tid: %d\n", kind, common.name, common._id) +func printCommonType(kind string, common *CommonType) { + fmt.Printf("\t%s %q\n\tid: %d\n", kind, common.Name, common.Id) } -func (dec *Decoder) printData(indent int, id typeId) { - if dec.state.err != nil { - return +func (dec *Decoder) debugPrint(indent int, id typeId) { + wire, ok := dec.wireType[id] + if ok && wire.StructT != nil { + dec.debugStruct(indent+1, id, wire) + } else { + dec.debugSingle(indent+1, id, wire) } +} + +func (dec *Decoder) debugSingle(indent int, id typeId, wire *wireType) { // is it a builtin type? _, ok := builtinIdToType[id] + if !ok && wire == nil { + errorf("type id %d not defined\n", id) + } + dec.state.decodeUint() + dec.printItem(indent, id) +} + +func (dec *Decoder) printItem(indent int, id typeId) { + if dump { + fmt.Printf("print item %d bytes: % x\n", dec.state.b.Len(), dec.state.b.Bytes()) + } + _, ok := builtinIdToType[id] if ok { dec.printBuiltin(indent, id) return } wire, ok := dec.wireType[id] if !ok { - fmt.Printf("type id %d not defined\n", id) - return + errorf("type id %d not defined\n", id) } switch { - case wire.array != nil: - dec.printArray(indent+1, wire) - case wire.slice != nil: - dec.printSlice(indent+1, wire) - case wire.strct != nil: - dec.printStruct(indent+1, wire) + case wire.ArrayT != nil: + dec.printArray(indent, wire) + case wire.MapT != nil: + dec.printMap(indent, wire) + case wire.SliceT != nil: + dec.printSlice(indent, wire) + case wire.StructT != nil: + dec.debugStruct(indent, id, wire) } } func (dec *Decoder) printArray(indent int, wire *wireType) { - elemId := wire.array.Elem - n := int(decodeUint(dec.state)) - for i := 0; i < n && dec.state.err == nil; i++ { - dec.printData(indent, elemId) + elemId := wire.ArrayT.Elem + n := int(dec.state.decodeUint()) + for i := 0; i < n && dec.err == nil; i++ { + dec.printItem(indent, elemId) } - if n != wire.array.Len { + if n != wire.ArrayT.Len { tab(indent) - fmt.Printf("(wrong length for array: %d should be %d)\n", n, wire.array.Len) + fmt.Printf("(wrong length for array: %d should be %d)\n", n, wire.ArrayT.Len) + } +} + +func (dec *Decoder) printMap(indent int, wire *wireType) { + keyId := wire.MapT.Key + elemId := wire.MapT.Elem + n := int(dec.state.decodeUint()) + for i := 0; i < n && dec.err == nil; i++ { + dec.printItem(indent, keyId) + dec.printItem(indent+1, elemId) } } func (dec *Decoder) printSlice(indent int, wire *wireType) { - elemId := wire.slice.Elem - n := int(decodeUint(dec.state)) - for i := 0; i < n && dec.state.err == nil; i++ { - dec.printData(indent, elemId) + elemId := wire.SliceT.Elem + n := int(dec.state.decodeUint()) + for i := 0; i < n && dec.err == nil; i++ { + dec.printItem(indent, elemId) } } @@ -159,58 +238,73 @@ func (dec *Decoder) printBuiltin(indent int, id typeId) { tab(indent) switch id { case tBool: - if decodeInt(dec.state) == 0 { - fmt.Printf("false") + if dec.state.decodeInt() == 0 { + fmt.Printf("false\n") } else { - fmt.Printf("true") + fmt.Printf("true\n") } case tInt: - fmt.Printf("%d", decodeInt(dec.state)) + fmt.Printf("%d\n", dec.state.decodeInt()) case tUint: - fmt.Printf("%d", decodeUint(dec.state)) + fmt.Printf("%d\n", dec.state.decodeUint()) case tFloat: - fmt.Printf("%g", floatFromBits(decodeUint(dec.state))) + fmt.Printf("%g\n", floatFromBits(dec.state.decodeUint())) case tBytes: - b := make([]byte, decodeUint(dec.state)) + b := make([]byte, dec.state.decodeUint()) dec.state.b.Read(b) - fmt.Printf("% x", b) + fmt.Printf("% x\n", b) case tString: - b := make([]byte, decodeUint(dec.state)) + b := make([]byte, dec.state.decodeUint()) + dec.state.b.Read(b) + fmt.Printf("%q\n", b) + case tInterface: + b := make([]byte, dec.state.decodeUint()) dec.state.b.Read(b) - fmt.Printf("%q", b) + if len(b) == 0 { + fmt.Printf("nil interface") + } else { + fmt.Printf("interface value; type %q\n", b) + dec.debugFromBuffer(indent, true) + } default: - fmt.Print("unknown") + fmt.Print("unknown\n") } - fmt.Print("\n") } -func (dec *Decoder) printStruct(indent int, wire *wireType) { - strct := wire.strct - state := newDecodeState(dec.state.b) +func (dec *Decoder) debugStruct(indent int, id typeId, wire *wireType) { + tab(indent) + fmt.Printf("%s struct {\n", id.name()) + strct := wire.StructT + state := newDecodeState(dec, dec.state.b) state.fieldnum = -1 - for state.err == nil { - delta := int(decodeUint(state)) + for dec.err == nil { + delta := int(state.decodeUint()) if delta < 0 { - dec.state.err = os.ErrorString("gob decode: corrupted data: negative delta") - return + errorf("gob decode: corrupted data: negative delta") } - if state.err != nil || delta == 0 { // struct terminator is zero delta fieldnum - return + if delta == 0 { // struct terminator is zero delta fieldnum + break } - fieldnum := state.fieldnum + delta - if fieldnum < 0 || fieldnum >= len(strct.field) { - dec.state.err = os.ErrorString("field number out of range") - return + fieldNum := state.fieldnum + delta + if fieldNum < 0 || fieldNum >= len(strct.Field) { + errorf("field number out of range") + break } tab(indent) - fmt.Printf("field %d:\n", fieldnum) - dec.printData(indent+1, strct.field[fieldnum].id) - state.fieldnum = fieldnum + fmt.Printf("%s(%d):\n", wire.StructT.Field[fieldNum].Name, fieldNum) + dec.printItem(indent+1, strct.Field[fieldNum].Id) + state.fieldnum = fieldNum } + tab(indent) + fmt.Printf(" } // end %s struct\n", id.name()) } func tab(indent int) { - for i := 0; i < indent; i++ { - fmt.Print("\t") + for i, w := 0, 0; i < indent; i += w { + w = 10 + if i+w > indent { + w = indent - i + } + fmt.Print("\t\t\t\t\t\t\t\t\t\t"[:w]) } } diff --git a/src/pkg/gob/decode.go b/src/pkg/gob/decode.go index a70799e9a..f88ca72da 100644 --- a/src/pkg/gob/decode.go +++ b/src/pkg/gob/decode.go @@ -13,7 +13,9 @@ import ( "math" "os" "reflect" + "unicode" "unsafe" + "utf8" ) var ( @@ -22,16 +24,20 @@ var ( errRange = os.ErrorString("gob: internal error: field numbers out of bounds") ) -// The global execution state of an instance of the decoder. +// The execution state of an instance of the decoder. A new state +// is created for nested objects. type decodeState struct { - b *bytes.Buffer - err os.Error + dec *Decoder + // The buffer is stored with an extra indirection because it may be replaced + // if we load a type during decode (when reading an interface value). + b **bytes.Buffer fieldnum int // the last field number read. buf []byte } -func newDecodeState(b *bytes.Buffer) *decodeState { +func newDecodeState(dec *Decoder, b **bytes.Buffer) *decodeState { d := new(decodeState) + d.dec = dec d.b = b d.buf = make([]byte, uint64Size) return d @@ -74,24 +80,23 @@ func decodeUintReader(r io.Reader, buf []byte) (x uint64, err os.Error) { } // decodeUint reads an encoded unsigned integer from state.r. -// Sets state.err. If state.err is already non-nil, it does nothing. // Does not check for overflow. -func decodeUint(state *decodeState) (x uint64) { - if state.err != nil { - return +func (state *decodeState) decodeUint() (x uint64) { + b, err := state.b.ReadByte() + if err != nil { + error(err) } - var b uint8 - b, state.err = state.b.ReadByte() - if b <= 0x7f { // includes state.err != nil + if b <= 0x7f { return uint64(b) } nb := -int(int8(b)) if nb > uint64Size { - state.err = errBadUint - return + error(errBadUint) + } + n, err := state.b.Read(state.buf[0:nb]) + if err != nil { + error(err) } - var n int - n, state.err = state.b.Read(state.buf[0:nb]) // Don't need to check error; it's safe to loop regardless. // Could check that the high byte is zero but it's not worth it. for i := 0; i < n; i++ { @@ -102,13 +107,9 @@ func decodeUint(state *decodeState) (x uint64) { } // decodeInt reads an encoded signed integer from state.r. -// Sets state.err. If state.err is already non-nil, it does nothing. // Does not check for overflow. -func decodeInt(state *decodeState) int64 { - x := decodeUint(state) - if state.err != nil { - return 0 - } +func (state *decodeState) decodeInt() int64 { + x := state.decodeUint() if x&1 != 0 { return ^int64(x >> 1) } @@ -146,12 +147,12 @@ func decIndirect(p unsafe.Pointer, indir int) unsafe.Pointer { } func ignoreUint(i *decInstr, state *decodeState, p unsafe.Pointer) { - decodeUint(state) + state.decodeUint() } func ignoreTwoUints(i *decInstr, state *decodeState, p unsafe.Pointer) { - decodeUint(state) - decodeUint(state) + state.decodeUint() + state.decodeUint() } func decBool(i *decInstr, state *decodeState, p unsafe.Pointer) { @@ -161,7 +162,7 @@ func decBool(i *decInstr, state *decodeState, p unsafe.Pointer) { } p = *(*unsafe.Pointer)(p) } - *(*bool)(p) = decodeInt(state) != 0 + *(*bool)(p) = state.decodeInt() != 0 } func decInt8(i *decInstr, state *decodeState, p unsafe.Pointer) { @@ -171,9 +172,9 @@ func decInt8(i *decInstr, state *decodeState, p unsafe.Pointer) { } p = *(*unsafe.Pointer)(p) } - v := decodeInt(state) + v := state.decodeInt() if v < math.MinInt8 || math.MaxInt8 < v { - state.err = i.ovfl + error(i.ovfl) } else { *(*int8)(p) = int8(v) } @@ -186,9 +187,9 @@ func decUint8(i *decInstr, state *decodeState, p unsafe.Pointer) { } p = *(*unsafe.Pointer)(p) } - v := decodeUint(state) + v := state.decodeUint() if math.MaxUint8 < v { - state.err = i.ovfl + error(i.ovfl) } else { *(*uint8)(p) = uint8(v) } @@ -201,9 +202,9 @@ func decInt16(i *decInstr, state *decodeState, p unsafe.Pointer) { } p = *(*unsafe.Pointer)(p) } - v := decodeInt(state) + v := state.decodeInt() if v < math.MinInt16 || math.MaxInt16 < v { - state.err = i.ovfl + error(i.ovfl) } else { *(*int16)(p) = int16(v) } @@ -216,9 +217,9 @@ func decUint16(i *decInstr, state *decodeState, p unsafe.Pointer) { } p = *(*unsafe.Pointer)(p) } - v := decodeUint(state) + v := state.decodeUint() if math.MaxUint16 < v { - state.err = i.ovfl + error(i.ovfl) } else { *(*uint16)(p) = uint16(v) } @@ -231,9 +232,9 @@ func decInt32(i *decInstr, state *decodeState, p unsafe.Pointer) { } p = *(*unsafe.Pointer)(p) } - v := decodeInt(state) + v := state.decodeInt() if v < math.MinInt32 || math.MaxInt32 < v { - state.err = i.ovfl + error(i.ovfl) } else { *(*int32)(p) = int32(v) } @@ -246,9 +247,9 @@ func decUint32(i *decInstr, state *decodeState, p unsafe.Pointer) { } p = *(*unsafe.Pointer)(p) } - v := decodeUint(state) + v := state.decodeUint() if math.MaxUint32 < v { - state.err = i.ovfl + error(i.ovfl) } else { *(*uint32)(p) = uint32(v) } @@ -261,7 +262,7 @@ func decInt64(i *decInstr, state *decodeState, p unsafe.Pointer) { } p = *(*unsafe.Pointer)(p) } - *(*int64)(p) = int64(decodeInt(state)) + *(*int64)(p) = int64(state.decodeInt()) } func decUint64(i *decInstr, state *decodeState, p unsafe.Pointer) { @@ -271,7 +272,7 @@ func decUint64(i *decInstr, state *decodeState, p unsafe.Pointer) { } p = *(*unsafe.Pointer)(p) } - *(*uint64)(p) = uint64(decodeUint(state)) + *(*uint64)(p) = uint64(state.decodeUint()) } // Floating-point numbers are transmitted as uint64s holding the bits @@ -290,14 +291,14 @@ func floatFromBits(u uint64) float64 { } func storeFloat32(i *decInstr, state *decodeState, p unsafe.Pointer) { - v := floatFromBits(decodeUint(state)) + v := floatFromBits(state.decodeUint()) av := v if av < 0 { av = -av } // +Inf is OK in both 32- and 64-bit floats. Underflow is always OK. if math.MaxFloat32 < av && av <= math.MaxFloat64 { - state.err = i.ovfl + error(i.ovfl) } else { *(*float32)(p) = float32(v) } @@ -320,7 +321,7 @@ func decFloat64(i *decInstr, state *decodeState, p unsafe.Pointer) { } p = *(*unsafe.Pointer)(p) } - *(*float64)(p) = floatFromBits(uint64(decodeUint(state))) + *(*float64)(p) = floatFromBits(uint64(state.decodeUint())) } // Complex numbers are just a pair of floating-point numbers, real part first. @@ -342,8 +343,8 @@ func decComplex128(i *decInstr, state *decodeState, p unsafe.Pointer) { } p = *(*unsafe.Pointer)(p) } - real := floatFromBits(uint64(decodeUint(state))) - imag := floatFromBits(uint64(decodeUint(state))) + real := floatFromBits(uint64(state.decodeUint())) + imag := floatFromBits(uint64(state.decodeUint())) *(*complex128)(p) = cmplx(real, imag) } @@ -355,7 +356,7 @@ func decUint8Array(i *decInstr, state *decodeState, p unsafe.Pointer) { } p = *(*unsafe.Pointer)(p) } - b := make([]uint8, decodeUint(state)) + b := make([]uint8, state.decodeUint()) state.b.Read(b) *(*[]uint8)(p) = b } @@ -368,13 +369,13 @@ func decString(i *decInstr, state *decodeState, p unsafe.Pointer) { } p = *(*unsafe.Pointer)(p) } - b := make([]byte, decodeUint(state)) + b := make([]byte, state.decodeUint()) state.b.Read(b) *(*string)(p) = string(b) } func ignoreUint8Array(i *decInstr, state *decodeState, p unsafe.Pointer) { - b := make([]byte, decodeUint(state)) + b := make([]byte, state.decodeUint()) state.b.Read(b) } @@ -404,15 +405,15 @@ func allocate(rtyp reflect.Type, p uintptr, indir int) uintptr { return *(*uintptr)(up) } -func decodeSingle(engine *decEngine, rtyp reflect.Type, b *bytes.Buffer, p uintptr, indir int) os.Error { +func (dec *Decoder) decodeSingle(engine *decEngine, rtyp reflect.Type, b **bytes.Buffer, p uintptr, indir int) (err os.Error) { + defer catchError(&err) p = allocate(rtyp, p, indir) - state := newDecodeState(b) + state := newDecodeState(dec, b) state.fieldnum = singletonField basep := p - delta := int(decodeUint(state)) + delta := int(state.decodeUint()) if delta != 0 { - state.err = os.ErrorString("gob decode: corrupted data: non-zero delta for singleton") - return state.err + errorf("gob decode: corrupted data: non-zero delta for singleton") } instr := &engine.instr[singletonField] ptr := unsafe.Pointer(basep) // offset will be zero @@ -420,26 +421,26 @@ func decodeSingle(engine *decEngine, rtyp reflect.Type, b *bytes.Buffer, p uintp ptr = decIndirect(ptr, instr.indir) } instr.op(instr, state, ptr) - return state.err + return nil } -func decodeStruct(engine *decEngine, rtyp *reflect.StructType, b *bytes.Buffer, p uintptr, indir int) os.Error { +func (dec *Decoder) decodeStruct(engine *decEngine, rtyp *reflect.StructType, b **bytes.Buffer, p uintptr, indir int) (err os.Error) { + defer catchError(&err) p = allocate(rtyp, p, indir) - state := newDecodeState(b) + state := newDecodeState(dec, b) state.fieldnum = -1 basep := p - for state.err == nil { - delta := int(decodeUint(state)) + for state.b.Len() > 0 { + delta := int(state.decodeUint()) if delta < 0 { - state.err = os.ErrorString("gob decode: corrupted data: negative delta") - break + errorf("gob decode: corrupted data: negative delta") } - if state.err != nil || delta == 0 { // struct terminator is zero delta fieldnum + if delta == 0 { // struct terminator is zero delta fieldnum break } fieldnum := state.fieldnum + delta if fieldnum >= len(engine.instr) { - state.err = errRange + error(errRange) break } instr := &engine.instr[fieldnum] @@ -450,36 +451,35 @@ func decodeStruct(engine *decEngine, rtyp *reflect.StructType, b *bytes.Buffer, instr.op(instr, state, p) state.fieldnum = fieldnum } - return state.err + return nil } -func ignoreStruct(engine *decEngine, b *bytes.Buffer) os.Error { - state := newDecodeState(b) +func (dec *Decoder) ignoreStruct(engine *decEngine, b **bytes.Buffer) (err os.Error) { + defer catchError(&err) + state := newDecodeState(dec, b) state.fieldnum = -1 - for state.err == nil { - delta := int(decodeUint(state)) + for state.b.Len() > 0 { + delta := int(state.decodeUint()) if delta < 0 { - state.err = os.ErrorString("gob ignore decode: corrupted data: negative delta") - break + errorf("gob ignore decode: corrupted data: negative delta") } - if state.err != nil || delta == 0 { // struct terminator is zero delta fieldnum + if delta == 0 { // struct terminator is zero delta fieldnum break } fieldnum := state.fieldnum + delta if fieldnum >= len(engine.instr) { - state.err = errRange - break + error(errRange) } instr := &engine.instr[fieldnum] instr.op(instr, state, unsafe.Pointer(nil)) state.fieldnum = fieldnum } - return state.err + return nil } -func decodeArrayHelper(state *decodeState, p uintptr, elemOp decOp, elemWid uintptr, length, elemIndir int, ovfl os.ErrorString) os.Error { +func (dec *Decoder) decodeArrayHelper(state *decodeState, p uintptr, elemOp decOp, elemWid uintptr, length, elemIndir int, ovfl os.ErrorString) { instr := &decInstr{elemOp, 0, elemIndir, 0, ovfl} - for i := 0; i < length && state.err == nil; i++ { + for i := 0; i < length; i++ { up := unsafe.Pointer(p) if elemIndir > 1 { up = decIndirect(up, elemIndir) @@ -487,17 +487,16 @@ func decodeArrayHelper(state *decodeState, p uintptr, elemOp decOp, elemWid uint elemOp(instr, state, up) p += uintptr(elemWid) } - return state.err } -func decodeArray(atyp *reflect.ArrayType, state *decodeState, p uintptr, elemOp decOp, elemWid uintptr, length, indir, elemIndir int, ovfl os.ErrorString) os.Error { +func (dec *Decoder) decodeArray(atyp *reflect.ArrayType, state *decodeState, p uintptr, elemOp decOp, elemWid uintptr, length, indir, elemIndir int, ovfl os.ErrorString) { if indir > 0 { p = allocate(atyp, p, 1) // All but the last level has been allocated by dec.Indirect } - if n := decodeUint(state); n != uint64(length) { - return os.ErrorString("gob: length mismatch in decodeArray") + if n := state.decodeUint(); n != uint64(length) { + errorf("gob: length mismatch in decodeArray") } - return decodeArrayHelper(state, p, elemOp, elemWid, length, elemIndir, ovfl) + dec.decodeArrayHelper(state, p, elemOp, elemWid, length, elemIndir, ovfl) } func decodeIntoValue(state *decodeState, op decOp, indir int, v reflect.Value, ovfl os.ErrorString) reflect.Value { @@ -510,7 +509,7 @@ func decodeIntoValue(state *decodeState, op decOp, indir int, v reflect.Value, o return v } -func decodeMap(mtyp *reflect.MapType, state *decodeState, p uintptr, keyOp, elemOp decOp, indir, keyIndir, elemIndir int, ovfl os.ErrorString) os.Error { +func (dec *Decoder) decodeMap(mtyp *reflect.MapType, state *decodeState, p uintptr, keyOp, elemOp decOp, indir, keyIndir, elemIndir int, ovfl os.ErrorString) { if indir > 0 { p = allocate(mtyp, p, 1) // All but the last level has been allocated by dec.Indirect } @@ -523,50 +522,40 @@ func decodeMap(mtyp *reflect.MapType, state *decodeState, p uintptr, keyOp, elem // that slices etc. can. We must recover a full reflection value for // the iteration. v := reflect.NewValue(unsafe.Unreflect(mtyp, unsafe.Pointer((p)))).(*reflect.MapValue) - n := int(decodeUint(state)) - for i := 0; i < n && state.err == nil; i++ { + n := int(state.decodeUint()) + for i := 0; i < n; i++ { key := decodeIntoValue(state, keyOp, keyIndir, reflect.MakeZero(mtyp.Key()), ovfl) - if state.err != nil { - break - } elem := decodeIntoValue(state, elemOp, elemIndir, reflect.MakeZero(mtyp.Elem()), ovfl) - if state.err != nil { - break - } v.SetElem(key, elem) } - return state.err } -func ignoreArrayHelper(state *decodeState, elemOp decOp, length int) os.Error { +func (dec *Decoder) ignoreArrayHelper(state *decodeState, elemOp decOp, length int) { instr := &decInstr{elemOp, 0, 0, 0, os.ErrorString("no error")} - for i := 0; i < length && state.err == nil; i++ { + for i := 0; i < length; i++ { elemOp(instr, state, nil) } - return state.err } -func ignoreArray(state *decodeState, elemOp decOp, length int) os.Error { - if n := decodeUint(state); n != uint64(length) { - return os.ErrorString("gob: length mismatch in ignoreArray") +func (dec *Decoder) ignoreArray(state *decodeState, elemOp decOp, length int) { + if n := state.decodeUint(); n != uint64(length) { + errorf("gob: length mismatch in ignoreArray") } - return ignoreArrayHelper(state, elemOp, length) + dec.ignoreArrayHelper(state, elemOp, length) } -func ignoreMap(state *decodeState, keyOp, elemOp decOp) os.Error { - n := int(decodeUint(state)) +func (dec *Decoder) ignoreMap(state *decodeState, keyOp, elemOp decOp) { + n := int(state.decodeUint()) keyInstr := &decInstr{keyOp, 0, 0, 0, os.ErrorString("no error")} elemInstr := &decInstr{elemOp, 0, 0, 0, os.ErrorString("no error")} - for i := 0; i < n && state.err == nil; i++ { + for i := 0; i < n; i++ { keyOp(keyInstr, state, nil) elemOp(elemInstr, state, nil) } - return state.err } - -func decodeSlice(atyp *reflect.SliceType, state *decodeState, p uintptr, elemOp decOp, elemWid uintptr, indir, elemIndir int, ovfl os.ErrorString) os.Error { - n := int(uintptr(decodeUint(state))) +func (dec *Decoder) decodeSlice(atyp *reflect.SliceType, state *decodeState, p uintptr, elemOp decOp, elemWid uintptr, indir, elemIndir int, ovfl os.ErrorString) { + n := int(uintptr(state.decodeUint())) if indir > 0 { up := unsafe.Pointer(p) if *(*unsafe.Pointer)(up) == nil { @@ -581,11 +570,77 @@ func decodeSlice(atyp *reflect.SliceType, state *decodeState, p uintptr, elemOp hdrp.Data = uintptr(unsafe.NewArray(atyp.Elem(), n)) hdrp.Len = n hdrp.Cap = n - return decodeArrayHelper(state, hdrp.Data, elemOp, elemWid, n, elemIndir, ovfl) + dec.decodeArrayHelper(state, hdrp.Data, elemOp, elemWid, n, elemIndir, ovfl) } -func ignoreSlice(state *decodeState, elemOp decOp) os.Error { - return ignoreArrayHelper(state, elemOp, int(decodeUint(state))) +func (dec *Decoder) ignoreSlice(state *decodeState, elemOp decOp) { + dec.ignoreArrayHelper(state, elemOp, int(state.decodeUint())) +} + +// setInterfaceValue sets an interface value to a concrete value through +// reflection. If the concrete value does not implement the interface, the +// setting will panic. This routine turns the panic into an error return. +// This dance avoids manually checking that the value satisfies the +// interface. +// TODO(rsc): avoid panic+recover after fixing issue 327. +func setInterfaceValue(ivalue *reflect.InterfaceValue, value reflect.Value) { + defer func() { + if e := recover(); e != nil { + error(e.(os.Error)) + } + }() + ivalue.Set(value) +} + +// decodeInterface receives the name of a concrete type followed by its value. +// If the name is empty, the value is nil and no value is sent. +func (dec *Decoder) decodeInterface(ityp *reflect.InterfaceType, state *decodeState, p uintptr, indir int) { + // Create an interface reflect.Value. We need one even for the nil case. + ivalue := reflect.MakeZero(ityp).(*reflect.InterfaceValue) + // Read the name of the concrete type. + b := make([]byte, state.decodeUint()) + state.b.Read(b) + name := string(b) + if name == "" { + // Copy the representation of the nil interface value to the target. + // This is horribly unsafe and special. + *(*[2]uintptr)(unsafe.Pointer(p)) = ivalue.Get() + return + } + // The concrete type must be registered. + typ, ok := nameToConcreteType[name] + if !ok { + errorf("gob: name not registered for interface: %q", name) + } + // Read the concrete value. + value := reflect.MakeZero(typ) + dec.decodeValueFromBuffer(value, false, true) + if dec.err != nil { + error(dec.err) + } + // Allocate the destination interface value. + if indir > 0 { + p = allocate(ityp, p, 1) // All but the last level has been allocated by dec.Indirect + } + // Assign the concrete value to the interface. + // Tread carefully; it might not satisfy the interface. + setInterfaceValue(ivalue, value) + // Copy the representation of the interface value to the target. + // This is horribly unsafe and special. + *(*[2]uintptr)(unsafe.Pointer(p)) = ivalue.Get() +} + +func (dec *Decoder) ignoreInterface(state *decodeState) { + // Read the name of the concrete type. + b := make([]byte, state.decodeUint()) + _, err := state.b.Read(b) + if err != nil { + error(err) + } + dec.decodeValueFromBuffer(nil, true, true) + if dec.err != nil { + error(err) + } } // Index by Go types. @@ -608,17 +663,18 @@ var decOpMap = []decOp{ // Indexed by gob types. tComplex will be added during type.init(). var decIgnoreOpMap = map[typeId]decOp{ - tBool: ignoreUint, - tInt: ignoreUint, - tUint: ignoreUint, - tFloat: ignoreUint, - tBytes: ignoreUint8Array, - tString: ignoreUint8Array, + tBool: ignoreUint, + tInt: ignoreUint, + tUint: ignoreUint, + tFloat: ignoreUint, + tBytes: ignoreUint8Array, + tString: ignoreUint8Array, + tComplex: ignoreTwoUints, } // Return the decoding op for the base type under rt and // the indirection count to reach it. -func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string) (decOp, int, os.Error) { +func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string) (decOp, int) { typ, indir := indirect(rt) var op decOp k := typ.Kind() @@ -630,32 +686,23 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string) (decOp switch t := typ.(type) { case *reflect.ArrayType: name = "element of " + name - elemId := dec.wireType[wireId].arrayT.Elem - elemOp, elemIndir, err := dec.decOpFor(elemId, t.Elem(), name) - if err != nil { - return nil, 0, err - } + elemId := dec.wireType[wireId].ArrayT.Elem + elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), name) ovfl := overflow(name) op = func(i *decInstr, state *decodeState, p unsafe.Pointer) { - state.err = decodeArray(t, state, uintptr(p), elemOp, t.Elem().Size(), t.Len(), i.indir, elemIndir, ovfl) + state.dec.decodeArray(t, state, uintptr(p), elemOp, t.Elem().Size(), t.Len(), i.indir, elemIndir, ovfl) } case *reflect.MapType: name = "element of " + name - keyId := dec.wireType[wireId].mapT.Key - elemId := dec.wireType[wireId].mapT.Elem - keyOp, keyIndir, err := dec.decOpFor(keyId, t.Key(), name) - if err != nil { - return nil, 0, err - } - elemOp, elemIndir, err := dec.decOpFor(elemId, t.Elem(), name) - if err != nil { - return nil, 0, err - } + keyId := dec.wireType[wireId].MapT.Key + elemId := dec.wireType[wireId].MapT.Elem + keyOp, keyIndir := dec.decOpFor(keyId, t.Key(), name) + elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), name) ovfl := overflow(name) op = func(i *decInstr, state *decodeState, p unsafe.Pointer) { up := unsafe.Pointer(p) - state.err = decodeMap(t, state, uintptr(up), keyOp, elemOp, i.indir, keyIndir, elemIndir, ovfl) + state.dec.decodeMap(t, state, uintptr(up), keyOp, elemOp, i.indir, keyIndir, elemIndir, ovfl) } case *reflect.SliceType: @@ -668,111 +715,105 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string) (decOp if tt, ok := builtinIdToType[wireId]; ok { elemId = tt.(*sliceType).Elem } else { - elemId = dec.wireType[wireId].sliceT.Elem - } - elemOp, elemIndir, err := dec.decOpFor(elemId, t.Elem(), name) - if err != nil { - return nil, 0, err + elemId = dec.wireType[wireId].SliceT.Elem } + elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), name) ovfl := overflow(name) op = func(i *decInstr, state *decodeState, p unsafe.Pointer) { - state.err = decodeSlice(t, state, uintptr(p), elemOp, t.Elem().Size(), i.indir, elemIndir, ovfl) + state.dec.decodeSlice(t, state, uintptr(p), elemOp, t.Elem().Size(), i.indir, elemIndir, ovfl) } case *reflect.StructType: // Generate a closure that calls out to the engine for the nested type. enginePtr, err := dec.getDecEnginePtr(wireId, typ) if err != nil { - return nil, 0, err + error(err) } op = func(i *decInstr, state *decodeState, p unsafe.Pointer) { // indirect through enginePtr to delay evaluation for recursive structs - state.err = decodeStruct(*enginePtr, t, state.b, uintptr(p), i.indir) + err = dec.decodeStruct(*enginePtr, t, state.b, uintptr(p), i.indir) + if err != nil { + error(err) + } + } + case *reflect.InterfaceType: + op = func(i *decInstr, state *decodeState, p unsafe.Pointer) { + dec.decodeInterface(t, state, uintptr(p), i.indir) } } } if op == nil { - return nil, 0, os.ErrorString("gob: decode can't handle type " + rt.String()) + errorf("gob: decode can't handle type %s", rt.String()) } - return op, indir, nil + return op, indir } // Return the decoding op for a field that has no destination. -func (dec *Decoder) decIgnoreOpFor(wireId typeId) (decOp, os.Error) { +func (dec *Decoder) decIgnoreOpFor(wireId typeId) decOp { op, ok := decIgnoreOpMap[wireId] if !ok { + if wireId == tInterface { + // Special case because it's a method: the ignored item might + // define types and we need to record their state in the decoder. + op = func(i *decInstr, state *decodeState, p unsafe.Pointer) { + dec.ignoreInterface(state) + } + return op + } // Special cases wire := dec.wireType[wireId] switch { case wire == nil: panic("internal error: can't find ignore op for type " + wireId.string()) - case wire.arrayT != nil: - elemId := wire.arrayT.Elem - elemOp, err := dec.decIgnoreOpFor(elemId) - if err != nil { - return nil, err - } + case wire.ArrayT != nil: + elemId := wire.ArrayT.Elem + elemOp := dec.decIgnoreOpFor(elemId) op = func(i *decInstr, state *decodeState, p unsafe.Pointer) { - state.err = ignoreArray(state, elemOp, wire.arrayT.Len) + state.dec.ignoreArray(state, elemOp, wire.ArrayT.Len) } - case wire.mapT != nil: - keyId := dec.wireType[wireId].mapT.Key - elemId := dec.wireType[wireId].mapT.Elem - keyOp, err := dec.decIgnoreOpFor(keyId) - if err != nil { - return nil, err - } - elemOp, err := dec.decIgnoreOpFor(elemId) - if err != nil { - return nil, err - } + case wire.MapT != nil: + keyId := dec.wireType[wireId].MapT.Key + elemId := dec.wireType[wireId].MapT.Elem + keyOp := dec.decIgnoreOpFor(keyId) + elemOp := dec.decIgnoreOpFor(elemId) op = func(i *decInstr, state *decodeState, p unsafe.Pointer) { - state.err = ignoreMap(state, keyOp, elemOp) + state.dec.ignoreMap(state, keyOp, elemOp) } - case wire.sliceT != nil: - elemId := wire.sliceT.Elem - elemOp, err := dec.decIgnoreOpFor(elemId) - if err != nil { - return nil, err - } + case wire.SliceT != nil: + elemId := wire.SliceT.Elem + elemOp := dec.decIgnoreOpFor(elemId) op = func(i *decInstr, state *decodeState, p unsafe.Pointer) { - state.err = ignoreSlice(state, elemOp) + state.dec.ignoreSlice(state, elemOp) } - case wire.structT != nil: + case wire.StructT != nil: // Generate a closure that calls out to the engine for the nested type. enginePtr, err := dec.getIgnoreEnginePtr(wireId) if err != nil { - return nil, err + error(err) } op = func(i *decInstr, state *decodeState, p unsafe.Pointer) { // indirect through enginePtr to delay evaluation for recursive structs - state.err = ignoreStruct(*enginePtr, state.b) + state.dec.ignoreStruct(*enginePtr, state.b) } } } if op == nil { - return nil, os.ErrorString("ignore can't handle type " + wireId.string()) + errorf("ignore can't handle type %s", wireId.string()) } - return op, nil + return op } // Are these two gob Types compatible? // Answers the question for basic types, arrays, and slices. // Structs are considered ok; fields will be checked later. func (dec *Decoder) compatibleType(fr reflect.Type, fw typeId) bool { - for { - if pt, ok := fr.(*reflect.PtrType); ok { - fr = pt.Elem() - continue - } - break - } + fr, _ = indirect(fr) switch t := fr.(type) { default: - // interface, map, chan, etc: cannot handle. + // map, chan, etc: cannot handle. return false case *reflect.BoolType: return fw == tBool @@ -786,20 +827,22 @@ func (dec *Decoder) compatibleType(fr reflect.Type, fw typeId) bool { return fw == tComplex case *reflect.StringType: return fw == tString + case *reflect.InterfaceType: + return fw == tInterface case *reflect.ArrayType: wire, ok := dec.wireType[fw] - if !ok || wire.arrayT == nil { + if !ok || wire.ArrayT == nil { return false } - array := wire.arrayT + array := wire.ArrayT return t.Len() == array.Len && dec.compatibleType(t.Elem(), array.Elem) case *reflect.MapType: wire, ok := dec.wireType[fw] - if !ok || wire.mapT == nil { + if !ok || wire.MapT == nil { return false } - mapType := wire.mapT - return dec.compatibleType(t.Key(), mapType.Key) && dec.compatibleType(t.Elem(), mapType.Elem) + MapType := wire.MapT + return dec.compatibleType(t.Key(), MapType.Key) && dec.compatibleType(t.Elem(), MapType.Elem) case *reflect.SliceType: // Is it an array of bytes? if t.Elem().Kind() == reflect.Uint8 { @@ -810,7 +853,7 @@ func (dec *Decoder) compatibleType(fr reflect.Type, fw typeId) bool { if tt, ok := builtinIdToType[fw]; ok { sw = tt.(*sliceType) } else { - sw = dec.wireType[fw].sliceT + sw = dec.wireType[fw].SliceT } elem, _ := indirect(t.Elem()) return sw != nil && dec.compatibleType(elem, sw.Elem) @@ -820,61 +863,74 @@ func (dec *Decoder) compatibleType(fr reflect.Type, fw typeId) bool { return true } +// typeString returns a human-readable description of the type identified by remoteId. +func (dec *Decoder) typeString(remoteId typeId) string { + if t := idToType[remoteId]; t != nil { + // globally known type. + return t.string() + } + return dec.wireType[remoteId].string() +} + + func (dec *Decoder) compileSingle(remoteId typeId, rt reflect.Type) (engine *decEngine, err os.Error) { engine = new(decEngine) engine.instr = make([]decInstr, 1) // one item name := rt.String() // best we can do if !dec.compatibleType(rt, remoteId) { - return nil, os.ErrorString("gob: wrong type received for local value " + name) - } - op, indir, err := dec.decOpFor(remoteId, rt, name) - if err != nil { - return nil, err + return nil, os.ErrorString("gob: wrong type received for local value " + name + ": " + dec.typeString(remoteId)) } + op, indir := dec.decOpFor(remoteId, rt, name) ovfl := os.ErrorString(`value for "` + name + `" out of range`) engine.instr[singletonField] = decInstr{op, singletonField, indir, 0, ovfl} engine.numInstr = 1 return } +// Is this an exported - upper case - name? +func isExported(name string) bool { + rune, _ := utf8.DecodeRuneInString(name) + return unicode.IsUpper(rune) +} + func (dec *Decoder) compileDec(remoteId typeId, rt reflect.Type) (engine *decEngine, err os.Error) { + defer catchError(&err) srt, ok := rt.(*reflect.StructType) if !ok { return dec.compileSingle(remoteId, rt) } var wireStruct *structType - // Builtin types can come from global pool; the rest must be defined by the decoder + // Builtin types can come from global pool; the rest must be defined by the decoder. + // Also we know we're decoding a struct now, so the client must have sent one. if t, ok := builtinIdToType[remoteId]; ok { - wireStruct = t.(*structType) + wireStruct, _ = t.(*structType) } else { - wireStruct = dec.wireType[remoteId].structT + wireStruct = dec.wireType[remoteId].StructT + } + if wireStruct == nil { + errorf("gob: type mismatch in decoder: want struct type %s; got non-struct", rt.String()) } engine = new(decEngine) - engine.instr = make([]decInstr, len(wireStruct.field)) + engine.instr = make([]decInstr, len(wireStruct.Field)) // Loop over the fields of the wire type. - for fieldnum := 0; fieldnum < len(wireStruct.field); fieldnum++ { - wireField := wireStruct.field[fieldnum] + for fieldnum := 0; fieldnum < len(wireStruct.Field); fieldnum++ { + wireField := wireStruct.Field[fieldnum] + if wireField.Name == "" { + errorf("gob: empty name for remote field of type %s", wireStruct.Name) + } + ovfl := overflow(wireField.Name) // Find the field of the local type with the same name. - localField, present := srt.FieldByName(wireField.name) - ovfl := overflow(wireField.name) + localField, present := srt.FieldByName(wireField.Name) // TODO(r): anonymous names - if !present { - op, err := dec.decIgnoreOpFor(wireField.id) - if err != nil { - return nil, err - } + if !present || !isExported(wireField.Name) { + op := dec.decIgnoreOpFor(wireField.Id) engine.instr[fieldnum] = decInstr{op, fieldnum, 0, 0, ovfl} continue } - if !dec.compatibleType(localField.Type, wireField.id) { - return nil, os.ErrorString("gob: wrong type (" + - localField.Type.String() + ") for received field " + - wireStruct.name + "." + wireField.name) - } - op, indir, err := dec.decOpFor(wireField.id, localField.Type, localField.Name) - if err != nil { - return nil, err + if !dec.compatibleType(localField.Type, wireField.Id) { + errorf("gob: wrong type (%s) for received field %s.%s", localField.Type, wireStruct.Name, wireField.Name) } + op, indir := dec.decOpFor(wireField.Id, localField.Type, localField.Name) engine.instr[fieldnum] = decInstr{op, fieldnum, indir, uintptr(localField.Offset), ovfl} engine.numInstr++ } @@ -899,7 +955,7 @@ func (dec *Decoder) getDecEnginePtr(remoteId typeId, rt reflect.Type) (enginePtr return } -// When ignoring data, in effect we compile it into this type +// When ignoring struct data, in effect we compile it into this type type emptyStruct struct{} var emptyStructType = reflect.Typeof(emptyStruct{}) @@ -927,13 +983,13 @@ func (dec *Decoder) decode(wireId typeId, val reflect.Value) os.Error { } engine := *enginePtr if st, ok := rt.(*reflect.StructType); ok { - if engine.numInstr == 0 && st.NumField() > 0 && len(dec.wireType[wireId].structT.field) > 0 { + if engine.numInstr == 0 && st.NumField() > 0 && len(dec.wireType[wireId].StructT.Field) > 0 { name := rt.Name() return os.ErrorString("gob: type mismatch: no fields matched compiling decoder for " + name) } - return decodeStruct(engine, st, dec.state.b, uintptr(val.Addr()), indir) + return dec.decodeStruct(engine, st, dec.state.b, uintptr(val.Addr()), indir) } - return decodeSingle(engine, rt, dec.state.b, uintptr(val.Addr()), indir) + return dec.decodeSingle(engine, rt, dec.state.b, uintptr(val.Addr()), indir) } func init() { diff --git a/src/pkg/gob/decoder.go b/src/pkg/gob/decoder.go index cf16433eb..664001a4b 100644 --- a/src/pkg/gob/decoder.go +++ b/src/pkg/gob/decoder.go @@ -24,6 +24,8 @@ type Decoder struct { countState *decodeState // reads counts from wire buf []byte countBuf [9]byte // counts may be uint64s (unlikely!), require 9 bytes + byteBuffer *bytes.Buffer + err os.Error } // NewDecoder returns a new decoder that reads from the io.Reader. @@ -31,25 +33,32 @@ func NewDecoder(r io.Reader) *Decoder { dec := new(Decoder) dec.r = r dec.wireType = make(map[typeId]*wireType) - dec.state = newDecodeState(nil) // buffer set in Decode(); rest is unimportant + dec.state = newDecodeState(dec, &dec.byteBuffer) // buffer set in Decode() dec.decoderCache = make(map[reflect.Type]map[typeId]**decEngine) dec.ignorerCache = make(map[typeId]**decEngine) return dec } +// recvType loads the definition of a type and reloads the Decoder's buffer. func (dec *Decoder) recvType(id typeId) { // Have we already seen this type? That's an error if dec.wireType[id] != nil { - dec.state.err = os.ErrorString("gob: duplicate type received") + dec.err = os.ErrorString("gob: duplicate type received") return } // Type: wire := new(wireType) - dec.state.err = dec.decode(tWireType, reflect.NewValue(wire)) + dec.err = dec.decode(tWireType, reflect.NewValue(wire)) + if dec.err != nil { + return + } // Remember we've seen this type. dec.wireType[id] = wire + + // Load the next parcel. + dec.recv() } // Decode reads the next value from the connection and stores @@ -61,69 +70,95 @@ 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.state.err = os.ErrorString("gob: attempt to decode into a non-pointer") - return dec.state.err + dec.err = os.ErrorString("gob: attempt to decode into a non-pointer") + return dec.err } return dec.DecodeValue(value) } -// DecodeValue reads the next value from the connection and stores -// it in the data represented by the reflection value. -// The value must be the correct type for the next -// data item received. -func (dec *Decoder) DecodeValue(value reflect.Value) os.Error { - // Make sure we're single-threaded through here. - dec.mutex.Lock() - defer dec.mutex.Unlock() - - dec.state.err = nil - for { - // Read a count. - var nbytes uint64 - nbytes, dec.state.err = decodeUintReader(dec.r, dec.countBuf[0:]) - if dec.state.err != nil { - break - } - // Allocate the buffer. - if nbytes > uint64(len(dec.buf)) { - dec.buf = make([]byte, nbytes+1000) - } - dec.state.b = bytes.NewBuffer(dec.buf[0:nbytes]) +// recv reads the next count-delimited item from the input. It is the converse +// of Encoder.send. +func (dec *Decoder) recv() { + // Read a count. + var nbytes uint64 + nbytes, dec.err = decodeUintReader(dec.r, dec.countBuf[0:]) + if dec.err != nil { + return + } + // Allocate the buffer. + if nbytes > uint64(len(dec.buf)) { + dec.buf = make([]byte, nbytes+1000) + } + dec.byteBuffer = bytes.NewBuffer(dec.buf[0:nbytes]) - // Read the data - _, dec.state.err = io.ReadFull(dec.r, dec.buf[0:nbytes]) - if dec.state.err != nil { - if dec.state.err == os.EOF { - dec.state.err = io.ErrUnexpectedEOF - } - break + // Read the data + _, dec.err = io.ReadFull(dec.r, dec.buf[0:nbytes]) + if dec.err != nil { + if dec.err == os.EOF { + dec.err = io.ErrUnexpectedEOF } + return + } +} +// decodeValueFromBuffer grabs the next value from the input. The Decoder's +// buffer already contains data. If the next item in the buffer is a type +// descriptor, it may be necessary to reload the buffer, but recvType does that. +func (dec *Decoder) decodeValueFromBuffer(value reflect.Value, ignoreInterfaceValue, countPresent bool) { + for dec.state.b.Len() > 0 { // Receive a type id. - id := typeId(decodeInt(dec.state)) - if dec.state.err != nil { - break - } + id := typeId(dec.state.decodeInt()) // Is it a new type? if id < 0 { // 0 is the error state, handled above // If the id is negative, we have a type. dec.recvType(-id) - if dec.state.err != nil { + if dec.err != nil { break } continue } - // No, it's a value. // Make sure the type has been defined already or is a builtin type (for // top-level singleton values). if dec.wireType[id] == nil && builtinIdToType[id] == nil { - dec.state.err = errBadType + dec.err = errBadType break } - dec.state.err = dec.decode(id, value) + // An interface value is preceded by a byte count. + if countPresent { + count := int(dec.state.decodeUint()) + if ignoreInterfaceValue { + // An interface value is preceded by a byte count. Just skip that many bytes. + dec.state.b.Next(int(count)) + break + } + // Otherwise fall through and decode it. + } + dec.err = dec.decode(id, value) break } - return dec.state.err } + +// DecodeValue reads the next value from the connection and stores +// it in the data represented by the reflection value. +// The value must be the correct type for the next +// data item received. +func (dec *Decoder) DecodeValue(value reflect.Value) os.Error { + // Make sure we're single-threaded through here. + dec.mutex.Lock() + defer dec.mutex.Unlock() + + dec.err = nil + dec.recv() + if dec.err != nil { + return dec.err + } + dec.decodeValueFromBuffer(value, false, false) + return dec.err +} + +// If debug.go is compiled into the program , debugFunc prints a human-readable +// representation of the gob data read from r by calling that file's Debug function. +// Otherwise it is nil. +var debugFunc func(io.Reader) diff --git a/src/pkg/gob/doc.go b/src/pkg/gob/doc.go new file mode 100644 index 000000000..31253f16d --- /dev/null +++ b/src/pkg/gob/doc.go @@ -0,0 +1,307 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +The gob package manages streams of gobs - binary values exchanged between an +Encoder (transmitter) and a Decoder (receiver). A typical use is transporting +arguments and results of remote procedure calls (RPCs) such as those provided by +package "rpc". + +A stream of gobs is self-describing. Each data item in the stream is preceded by +a specification of its type, expressed in terms of a small set of predefined +types. Pointers are not transmitted, but the things they point to are +transmitted; that is, the values are flattened. Recursive types work fine, but +recursive values (data with cycles) are problematic. This may change. + +To use gobs, create an Encoder and present it with a series of data items as +values or addresses that can be dereferenced to values. The Encoder makes sure +all type information is sent before it is needed. At the receive side, a +Decoder retrieves values from the encoded stream and unpacks them into local +variables. + +The source and destination values/types need not correspond exactly. For structs, +fields (identified by name) that are in the source but absent from the receiving +variable will be ignored. Fields that are in the receiving variable but missing +from the transmitted type or value will be ignored in the destination. If a field +with the same name is present in both, their types must be compatible. Both the +receiver and transmitter will do all necessary indirection and dereferencing to +convert between gobs and actual Go values. For instance, a gob type that is +schematically, + + struct { a, b int } + +can be sent from or received into any of these Go types: + + struct { a, b int } // the same + *struct { a, b int } // extra indirection of the struct + struct { *a, **b int } // extra indirection of the fields + struct { a, b int64 } // different concrete value type; see below + +It may also be received into any of these: + + struct { a, b int } // the same + struct { b, a int } // ordering doesn't matter; matching is by name + struct { a, b, c int } // extra field (c) ignored + struct { b int } // missing field (a) ignored; data will be dropped + struct { b, c int } // missing field (a) ignored; extra field (c) ignored. + +Attempting to receive into these types will draw a decode error: + + struct { a int; b uint } // change of signedness for b + struct { a int; b float } // change of type for b + struct { } // no field names in common + struct { c, d int } // no field names in common + +Integers are transmitted two ways: arbitrary precision signed integers or +arbitrary precision unsigned integers. There is no int8, int16 etc. +discrimination in the gob format; there are only signed and unsigned integers. As +described below, the transmitter sends the value in a variable-length encoding; +the receiver accepts the value and stores it in the destination variable. +Floating-point numbers are always sent using IEEE-754 64-bit precision (see +below). + +Signed integers may be received into any signed integer variable: int, int16, etc.; +unsigned integers may be received into any unsigned integer variable; and floating +point values may be received into any floating point variable. However, +the destination variable must be able to represent the value or the decode +operation will fail. + +Structs, arrays and slices are also supported. Strings and arrays of bytes are +supported with a special, efficient representation (see below). + +Functions and channels cannot be sent in a gob. Attempting +to encode a value that contains one will fail. + +The rest of this comment documents the encoding, details that are not important +for most users. Details are presented bottom-up. + +An unsigned integer is sent one of two ways. If it is less than 128, it is sent +as a byte with that value. Otherwise it is sent as a minimal-length big-endian +(high byte first) byte stream holding the value, preceded by one byte holding the +byte count, negated. Thus 0 is transmitted as (00), 7 is transmitted as (07) and +256 is transmitted as (FE 01 00). + +A boolean is encoded within an unsigned integer: 0 for false, 1 for true. + +A signed integer, i, is encoded within an unsigned integer, u. Within u, bits 1 +upward contain the value; bit 0 says whether they should be complemented upon +receipt. The encode algorithm looks like this: + + uint u; + if i < 0 { + u = (^i << 1) | 1 // complement i, bit 0 is 1 + } else { + u = (i << 1) // do not complement i, bit 0 is 0 + } + encodeUnsigned(u) + +The low bit is therefore analogous to a sign bit, but making it the complement bit +instead guarantees that the largest negative integer is not a special case. For +example, -129=^128=(^256>>1) encodes as (FE 01 01). + +Floating-point numbers are always sent as a representation of a float64 value. +That value is converted to a uint64 using math.Float64bits. The uint64 is then +byte-reversed and sent as a regular unsigned integer. The byte-reversal means the +exponent and high-precision part of the mantissa go first. Since the low bits are +often zero, this can save encoding bytes. For instance, 17.0 is encoded in only +three bytes (FE 31 40). + +Strings and slices of bytes are sent as an unsigned count followed by that many +uninterpreted bytes of the value. + +All other slices and arrays are sent as an unsigned count followed by that many +elements using the standard gob encoding for their type, recursively. + +Structs are sent as a sequence of (field number, field value) pairs. The field +value is sent using the standard gob encoding for its type, recursively. If a +field has the zero value for its type, it is omitted from the transmission. The +field number is defined by the type of the encoded struct: the first field of the +encoded type is field 0, the second is field 1, etc. When encoding a value, the +field numbers are delta encoded for efficiency and the fields are always sent in +order of increasing field number; the deltas are therefore unsigned. The +initialization for the delta encoding sets the field number to -1, so an unsigned +integer field 0 with value 7 is transmitted as unsigned delta = 1, unsigned value += 7 or (01 07). Finally, after all the fields have been sent a terminating mark +denotes the end of the struct. That mark is a delta=0 value, which has +representation (00). + +Interface types are not checked for compatibility; all interface types are +treated, for transmission, as members of a single "interface" type, analogous to +int or []byte - in effect they're all treated as interface{}. Interface values +are transmitted as a string identifying the concrete type being sent (a name +that must be pre-defined by calling Register), followed by a byte count of the +length of the following data (so the value can be skipped if it cannot be +stored), followed by the usual encoding of concrete (dynamic) value stored in +the interface value. (A nil interface value is identified by the empty string +and transmits no value.) Upon receipt, the decoder verifies that the unpacked +concrete item satisfies the interface of the receiving variable. + +The representation of types is described below. When a type is defined on a given +connection between an Encoder and Decoder, it is assigned a signed integer type +id. When Encoder.Encode(v) is called, it makes sure there is an id assigned for +the type of v and all its elements and then it sends the pair (typeid, encoded-v) +where typeid is the type id of the encoded type of v and encoded-v is the gob +encoding of the value v. + +To define a type, the encoder chooses an unused, positive type id and sends the +pair (-type id, encoded-type) where encoded-type is the gob encoding of a wireType +description, constructed from these types: + + type wireType struct { + ArrayT *ArrayType + SliceT *SliceType + StructT *StructType + MapT *MapType + } + type ArrayType struct { + CommonType + Elem typeId + Len int + } + type CommonType { + Name string // the name of the struct type + Id int // the id of the type, repeated so it's inside the type + } + type SliceType struct { + CommonType + Elem typeId + } + type StructType struct { + CommonType + Field []*fieldType // the fields of the struct. + } + type FieldType struct { + Name string // the name of the field. + Id int // the type id of the field, which must be already defined + } + type MapType struct { + CommonType + Key typeId + Elem typeId + } + +If there are nested type ids, the types for all inner type ids must be defined +before the top-level type id is used to describe an encoded-v. + +For simplicity in setup, the connection is defined to understand these types a +priori, as well as the basic gob types int, uint, etc. Their ids are: + + bool 1 + int 2 + uint 3 + float 4 + []byte 5 + string 6 + complex 7 + interface 8 + // gap for reserved ids. + WireType 16 + ArrayType 17 + CommonType 18 + SliceType 19 + StructType 20 + FieldType 21 + // 22 is slice of fieldType. + MapType 23 + +Finally, each message created by a call to Encode is preceded by an encoded +unsigned integer count of the number of bytes remaining in the message. After +the initial type name, interface values are wrapped the same way; in effect, the +interface value acts like a recursive invocation of Encode. + +In summary, a gob stream looks like + + (byteCount (-type id, encoding of a wireType)* (type id, encoding of a value))* + +where * signifies zero or more repetitions and the type id of a value must +be predefined or be defined before the value in the stream. +*/ +package gob + +/* +For implementers and the curious, here is an encoded example. Given + type Point struct {x, y int} +and the value + p := Point{22, 33} +the bytes transmitted that encode p will be: + 1f ff 81 03 01 01 05 50 6f 69 6e 74 01 ff 82 00 + 01 02 01 01 78 01 04 00 01 01 79 01 04 00 00 00 + 07 ff 82 01 2c 01 42 00 +They are determined as follows. + +Since this is the first transmission of type Point, the type descriptor +for Point itself must be sent before the value. This is the first type +we've sent on this Encoder, so it has type id 65 (0 through 64 are +reserved). + + 1f // This item (a type descriptor) is 31 bytes long. + ff 81 // The negative of the id for the type we're defining, -65. + // This is one byte (indicated by FF = -1) followed by + // ^-65<<1 | 1. The low 1 bit signals to complement the + // rest upon receipt. + + // Now we send a type descriptor, which is itself a struct (wireType). + // The type of wireType itself is known (it's built in, as is the type of + // all its components), so we just need to send a *value* of type wireType + // that represents type "Point". + // Here starts the encoding of that value. + // Set the field number implicitly to -1; this is done at the beginning + // of every struct, including nested structs. + 03 // Add 3 to field number; now 2 (wireType.structType; this is a struct). + // structType starts with an embedded commonType, which appears + // as a regular structure here too. + 01 // add 1 to field number (now 0); start of embedded commonType. + 01 // add 1 to field number (now 0, the name of the type) + 05 // string is (unsigned) 5 bytes long + 50 6f 69 6e 74 // wireType.structType.commonType.name = "Point" + 01 // add 1 to field number (now 1, the id of the type) + ff 82 // wireType.structType.commonType._id = 65 + 00 // end of embedded wiretype.structType.commonType struct + 01 // add 1 to field number (now 1, the field array in wireType.structType) + 02 // There are two fields in the type (len(structType.field)) + 01 // Start of first field structure; add 1 to get field number 0: field[0].name + 01 // 1 byte + 78 // structType.field[0].name = "x" + 01 // Add 1 to get field number 1: field[0].id + 04 // structType.field[0].typeId is 2 (signed int). + 00 // End of structType.field[0]; start structType.field[1]; set field number to -1. + 01 // Add 1 to get field number 0: field[1].name + 01 // 1 byte + 79 // structType.field[1].name = "y" + 01 // Add 1 to get field number 1: field[0].id + 04 // struct.Type.field[1].typeId is 2 (signed int). + 00 // End of structType.field[1]; end of structType.field. + 00 // end of wireType.structType structure + 00 // end of wireType structure + +Now we can send the Point value. Again the field number resets to -1: + + 07 // this value is 7 bytes long + ff 82 // the type number, 65 (1 byte (-FF) followed by 65<<1) + 01 // add one to field number, yielding field 0 + 2c // encoding of signed "22" (0x22 = 44 = 22<<1); Point.x = 22 + 01 // add one to field number, yielding field 1 + 42 // encoding of signed "33" (0x42 = 66 = 33<<1); Point.y = 33 + 00 // end of structure + +The type encoding is long and fairly intricate but we send it only once. +If p is transmitted a second time, the type is already known so the +output will be just: + + 07 ff 82 01 2c 01 42 00 + +A single non-struct value at top level is transmitted like a field with +delta tag 0. For instance, a signed integer with value 3 presented as +the argument to Encode will emit: + + 03 04 00 06 + +Which represents: + + 03 // this value is 3 bytes long + 04 // the type number, 2, represents an integer + 00 // tag delta 0 + 06 // value 3 + +*/ diff --git a/src/pkg/gob/encode.go b/src/pkg/gob/encode.go index 00548868b..3431eafa7 100644 --- a/src/pkg/gob/encode.go +++ b/src/pkg/gob/encode.go @@ -2,270 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -/* - The gob package manages streams of gobs - binary values exchanged between an - Encoder (transmitter) and a Decoder (receiver). A typical use is transporting - arguments and results of remote procedure calls (RPCs) such as those provided by - package "rpc". - - A stream of gobs is self-describing. Each data item in the stream is preceded by - a specification of its type, expressed in terms of a small set of predefined - types. Pointers are not transmitted, but the things they point to are - transmitted; that is, the values are flattened. Recursive types work fine, but - recursive values (data with cycles) are problematic. This may change. - - To use gobs, create an Encoder and present it with a series of data items as - values or addresses that can be dereferenced to values. The Encoder makes sure - all type information is sent before it is needed. At the receive side, a - Decoder retrieves values from the encoded stream and unpacks them into local - variables. - - The source and destination values/types need not correspond exactly. For structs, - fields (identified by name) that are in the source but absent from the receiving - variable will be ignored. Fields that are in the receiving variable but missing - from the transmitted type or value will be ignored in the destination. If a field - with the same name is present in both, their types must be compatible. Both the - receiver and transmitter will do all necessary indirection and dereferencing to - convert between gobs and actual Go values. For instance, a gob type that is - schematically, - - struct { a, b int } - - can be sent from or received into any of these Go types: - - struct { a, b int } // the same - *struct { a, b int } // extra indirection of the struct - struct { *a, **b int } // extra indirection of the fields - struct { a, b int64 } // different concrete value type; see below - - It may also be received into any of these: - - struct { a, b int } // the same - struct { b, a int } // ordering doesn't matter; matching is by name - struct { a, b, c int } // extra field (c) ignored - struct { b int } // missing field (a) ignored; data will be dropped - struct { b, c int } // missing field (a) ignored; extra field (c) ignored. - - Attempting to receive into these types will draw a decode error: - - struct { a int; b uint } // change of signedness for b - struct { a int; b float } // change of type for b - struct { } // no field names in common - struct { c, d int } // no field names in common - - Integers are transmitted two ways: arbitrary precision signed integers or - arbitrary precision unsigned integers. There is no int8, int16 etc. - discrimination in the gob format; there are only signed and unsigned integers. As - described below, the transmitter sends the value in a variable-length encoding; - the receiver accepts the value and stores it in the destination variable. - Floating-point numbers are always sent using IEEE-754 64-bit precision (see - below). - - Signed integers may be received into any signed integer variable: int, int16, etc.; - unsigned integers may be received into any unsigned integer variable; and floating - point values may be received into any floating point variable. However, - the destination variable must be able to represent the value or the decode - operation will fail. - - Structs, arrays and slices are also supported. Strings and arrays of bytes are - supported with a special, efficient representation (see below). - - Interfaces, functions, and channels cannot be sent in a gob. Attempting - to encode a value that contains one will fail. - - The rest of this comment documents the encoding, details that are not important - for most users. Details are presented bottom-up. - - An unsigned integer is sent one of two ways. If it is less than 128, it is sent - as a byte with that value. Otherwise it is sent as a minimal-length big-endian - (high byte first) byte stream holding the value, preceded by one byte holding the - byte count, negated. Thus 0 is transmitted as (00), 7 is transmitted as (07) and - 256 is transmitted as (FE 01 00). - - A boolean is encoded within an unsigned integer: 0 for false, 1 for true. - - A signed integer, i, is encoded within an unsigned integer, u. Within u, bits 1 - upward contain the value; bit 0 says whether they should be complemented upon - receipt. The encode algorithm looks like this: - - uint u; - if i < 0 { - u = (^i << 1) | 1 // complement i, bit 0 is 1 - } else { - u = (i << 1) // do not complement i, bit 0 is 0 - } - encodeUnsigned(u) - - The low bit is therefore analogous to a sign bit, but making it the complement bit - instead guarantees that the largest negative integer is not a special case. For - example, -129=^128=(^256>>1) encodes as (FE 01 01). - - Floating-point numbers are always sent as a representation of a float64 value. - That value is converted to a uint64 using math.Float64bits. The uint64 is then - byte-reversed and sent as a regular unsigned integer. The byte-reversal means the - exponent and high-precision part of the mantissa go first. Since the low bits are - often zero, this can save encoding bytes. For instance, 17.0 is encoded in only - three bytes (FE 31 40). - - Strings and slices of bytes are sent as an unsigned count followed by that many - uninterpreted bytes of the value. - - All other slices and arrays are sent as an unsigned count followed by that many - elements using the standard gob encoding for their type, recursively. - - Structs are sent as a sequence of (field number, field value) pairs. The field - value is sent using the standard gob encoding for its type, recursively. If a - field has the zero value for its type, it is omitted from the transmission. The - field number is defined by the type of the encoded struct: the first field of the - encoded type is field 0, the second is field 1, etc. When encoding a value, the - field numbers are delta encoded for efficiency and the fields are always sent in - order of increasing field number; the deltas are therefore unsigned. The - initialization for the delta encoding sets the field number to -1, so an unsigned - integer field 0 with value 7 is transmitted as unsigned delta = 1, unsigned value - = 7 or (01 0E). Finally, after all the fields have been sent a terminating mark - denotes the end of the struct. That mark is a delta=0 value, which has - representation (00). - - The representation of types is described below. When a type is defined on a given - connection between an Encoder and Decoder, it is assigned a signed integer type - id. When Encoder.Encode(v) is called, it makes sure there is an id assigned for - the type of v and all its elements and then it sends the pair (typeid, encoded-v) - where typeid is the type id of the encoded type of v and encoded-v is the gob - encoding of the value v. - - To define a type, the encoder chooses an unused, positive type id and sends the - pair (-type id, encoded-type) where encoded-type is the gob encoding of a wireType - description, constructed from these types: - - type wireType struct { - s structType; - } - type fieldType struct { - name string; // the name of the field. - id int; // the type id of the field, which must be already defined - } - type commonType { - name string; // the name of the struct type - id int; // the id of the type, repeated for so it's inside the type - } - type structType struct { - commonType; - field []fieldType; // the fields of the struct. - } - - If there are nested type ids, the types for all inner type ids must be defined - before the top-level type id is used to describe an encoded-v. - - For simplicity in setup, the connection is defined to understand these types a - priori, as well as the basic gob types int, uint, etc. Their ids are: - - bool 1 - int 2 - uint 3 - float 4 - []byte 5 - string 6 - wireType 7 - structType 8 - commonType 9 - fieldType 10 - - In summary, a gob stream looks like - - ((-type id, encoding of a wireType)* (type id, encoding of a value))* - - where * signifies zero or more repetitions and the type id of a value must - be predefined or be defined before the value in the stream. -*/ package gob -/* - For implementers and the curious, here is an encoded example. Given - type Point {x, y int} - and the value - p := Point{22, 33} - the bytes transmitted that encode p will be: - 1f ff 81 03 01 01 05 50 6f 69 6e 74 01 ff 82 00 01 02 01 01 78 - 01 04 00 01 01 79 01 04 00 00 00 07 ff 82 01 2c 01 42 00 07 ff - 82 01 2c 01 42 00 - They are determined as follows. - - Since this is the first transmission of type Point, the type descriptor - for Point itself must be sent before the value. This is the first type - we've sent on this Encoder, so it has type id 65 (0 through 64 are - reserved). - - 1f // This item (a type descriptor) is 31 bytes long. - ff 81 // The negative of the id for the type we're defining, -65. - // This is one byte (indicated by FF = -1) followed by - // ^-65<<1 | 1. The low 1 bit signals to complement the - // rest upon receipt. - - // Now we send a type descriptor, which is itself a struct (wireType). - // The type of wireType itself is known (it's built in, as is the type of - // all its components), so we just need to send a *value* of type wireType - // that represents type "Point". - // Here starts the encoding of that value. - // Set the field number implicitly to zero; this is done at the beginning - // of every struct, including nested structs. - 03 // Add 3 to field number; now 3 (wireType.structType; this is a struct). - // structType starts with an embedded commonType, which appears - // as a regular structure here too. - 01 // add 1 to field number (now 1); start of embedded commonType. - 01 // add one to field number (now 1, the name of the type) - 05 // string is (unsigned) 5 bytes long - 50 6f 69 6e 74 // wireType.structType.commonType.name = "Point" - 01 // add one to field number (now 2, the id of the type) - ff 82 // wireType.structType.commonType._id = 65 - 00 // end of embedded wiretype.structType.commonType struct - 01 // add one to field number (now 2, the Field array in wireType.structType) - 02 // There are two fields in the type (len(structType.field)) - 01 // Start of first field structure; add 1 to get field number 1: field[0].name - 01 // 1 byte - 78 // structType.field[0].name = "x" - 01 // Add 1 to get field number 2: field[0].id - 04 // structType.field[0].typeId is 2 (signed int). - 00 // End of structType.field[0]; start structType.field[1]; set field number to 0. - 01 // Add 1 to get field number 1: field[1].name - 01 // 1 byte - 79 // structType.field[1].name = "y" - 01 // Add 1 to get field number 2: field[0].id - 04 // struct.Type.field[1].typeId is 2 (signed int). - 00 // End of structType.field[1]; end of structType.field. - 00 // end of wireType.structType structure - 00 // end of wireType structure - - Now we can send the Point value. Again the field number resets to zero: - - 07 // this value is 7 bytes long - ff 82 // the type number, 65 (1 byte (-FF) followed by 65<<1) - 01 // add one to field number, yielding field 1 - 2c // encoding of signed "22" (0x22 = 44 = 22<<1); Point.x = 22 - 01 // add one to field number, yielding field 2 - 42 // encoding of signed "33" (0x42 = 66 = 33<<1); Point.y = 33 - 00 // end of structure - - The type encoding is long and fairly intricate but we send it only once. - If p is transmitted a second time, the type is already known so the - output will be just: - - 07 ff 82 01 2c 01 42 00 - - A single non-struct value at top level is transmitted like a field with - delta tag 0. For instance, a signed integer with value 3 presented as - the argument to Encode will emit: - - 03 04 00 06 - - Which represents: - - 03 // this value is 3 bytes long - 04 // the type number, 2, represents an integer - 00 // tag delta 0 - 06 // value 3 - -*/ - import ( "bytes" "io" @@ -282,26 +20,29 @@ const uint64Size = unsafe.Sizeof(uint64(0)) // number is initialized to -1 so 0 comes out as delta(1). A delta of // 0 terminates the structure. type encoderState struct { + enc *Encoder b *bytes.Buffer - err os.Error // error encountered during encoding. sendZero bool // encoding an array element or map key/value pair; send zero values fieldnum int // the last field number written. buf [1 + uint64Size]byte // buffer used by the encoder; here to avoid allocation. } +func newEncoderState(enc *Encoder, b *bytes.Buffer) *encoderState { + return &encoderState{enc: enc, b: b} +} + // Unsigned integers have a two-state encoding. If the number is less // than 128 (0 through 0x7F), its value is written directly. // Otherwise the value is written in big-endian byte order preceded // by the byte length, negated. -// encodeUint writes an encoded unsigned integer to state.b. Sets state.err. -// If state.err is already non-nil, it does nothing. -func encodeUint(state *encoderState, x uint64) { - if state.err != nil { - return - } +// encodeUint writes an encoded unsigned integer to state.b. +func (state *encoderState) encodeUint(x uint64) { if x <= 0x7F { - state.err = state.b.WriteByte(uint8(x)) + err := state.b.WriteByte(uint8(x)) + if err != nil { + error(err) + } return } var n, m int @@ -312,20 +53,23 @@ func encodeUint(state *encoderState, x uint64) { m-- } state.buf[m] = uint8(-(n - 1)) - n, state.err = state.b.Write(state.buf[m : uint64Size+1]) + n, err := state.b.Write(state.buf[m : uint64Size+1]) + if err != nil { + error(err) + } } // encodeInt writes an encoded signed integer to state.w. -// The low bit of the encoding says whether to bit complement the (other bits of the) uint to recover the int. -// Sets state.err. If state.err is already non-nil, it does nothing. -func encodeInt(state *encoderState, i int64) { +// The low bit of the encoding says whether to bit complement the (other bits of the) +// uint to recover the int. +func (state *encoderState) encodeInt(i int64) { var x uint64 if i < 0 { x = uint64(^i<<1) | 1 } else { x = uint64(i << 1) } - encodeUint(state, uint64(x)) + state.encodeUint(uint64(x)) } type encOp func(i *encInstr, state *encoderState, p unsafe.Pointer) @@ -342,7 +86,7 @@ type encInstr struct { // If the instruction pointer is nil, do nothing func (state *encoderState) update(instr *encInstr) { if instr != nil { - encodeUint(state, uint64(instr.field-state.fieldnum)) + state.encodeUint(uint64(instr.field - state.fieldnum)) state.fieldnum = instr.field } } @@ -368,9 +112,9 @@ func encBool(i *encInstr, state *encoderState, p unsafe.Pointer) { if b || state.sendZero { state.update(i) if b { - encodeUint(state, 1) + state.encodeUint(1) } else { - encodeUint(state, 0) + state.encodeUint(0) } } } @@ -379,7 +123,7 @@ func encInt(i *encInstr, state *encoderState, p unsafe.Pointer) { v := int64(*(*int)(p)) if v != 0 || state.sendZero { state.update(i) - encodeInt(state, v) + state.encodeInt(v) } } @@ -387,7 +131,7 @@ func encUint(i *encInstr, state *encoderState, p unsafe.Pointer) { v := uint64(*(*uint)(p)) if v != 0 || state.sendZero { state.update(i) - encodeUint(state, v) + state.encodeUint(v) } } @@ -395,7 +139,7 @@ func encInt8(i *encInstr, state *encoderState, p unsafe.Pointer) { v := int64(*(*int8)(p)) if v != 0 || state.sendZero { state.update(i) - encodeInt(state, v) + state.encodeInt(v) } } @@ -403,7 +147,7 @@ func encUint8(i *encInstr, state *encoderState, p unsafe.Pointer) { v := uint64(*(*uint8)(p)) if v != 0 || state.sendZero { state.update(i) - encodeUint(state, v) + state.encodeUint(v) } } @@ -411,7 +155,7 @@ func encInt16(i *encInstr, state *encoderState, p unsafe.Pointer) { v := int64(*(*int16)(p)) if v != 0 || state.sendZero { state.update(i) - encodeInt(state, v) + state.encodeInt(v) } } @@ -419,7 +163,7 @@ func encUint16(i *encInstr, state *encoderState, p unsafe.Pointer) { v := uint64(*(*uint16)(p)) if v != 0 || state.sendZero { state.update(i) - encodeUint(state, v) + state.encodeUint(v) } } @@ -427,7 +171,7 @@ func encInt32(i *encInstr, state *encoderState, p unsafe.Pointer) { v := int64(*(*int32)(p)) if v != 0 || state.sendZero { state.update(i) - encodeInt(state, v) + state.encodeInt(v) } } @@ -435,7 +179,7 @@ func encUint32(i *encInstr, state *encoderState, p unsafe.Pointer) { v := uint64(*(*uint32)(p)) if v != 0 || state.sendZero { state.update(i) - encodeUint(state, v) + state.encodeUint(v) } } @@ -443,7 +187,7 @@ func encInt64(i *encInstr, state *encoderState, p unsafe.Pointer) { v := *(*int64)(p) if v != 0 || state.sendZero { state.update(i) - encodeInt(state, v) + state.encodeInt(v) } } @@ -451,7 +195,7 @@ func encUint64(i *encInstr, state *encoderState, p unsafe.Pointer) { v := *(*uint64)(p) if v != 0 || state.sendZero { state.update(i) - encodeUint(state, v) + state.encodeUint(v) } } @@ -459,7 +203,7 @@ func encUintptr(i *encInstr, state *encoderState, p unsafe.Pointer) { v := uint64(*(*uintptr)(p)) if v != 0 || state.sendZero { state.update(i) - encodeUint(state, v) + state.encodeUint(v) } } @@ -484,7 +228,7 @@ func encFloat(i *encInstr, state *encoderState, p unsafe.Pointer) { if f != 0 || state.sendZero { v := floatBits(float64(f)) state.update(i) - encodeUint(state, v) + state.encodeUint(v) } } @@ -493,7 +237,7 @@ func encFloat32(i *encInstr, state *encoderState, p unsafe.Pointer) { if f != 0 || state.sendZero { v := floatBits(float64(f)) state.update(i) - encodeUint(state, v) + state.encodeUint(v) } } @@ -502,7 +246,7 @@ func encFloat64(i *encInstr, state *encoderState, p unsafe.Pointer) { if f != 0 || state.sendZero { state.update(i) v := floatBits(f) - encodeUint(state, v) + state.encodeUint(v) } } @@ -513,8 +257,8 @@ func encComplex(i *encInstr, state *encoderState, p unsafe.Pointer) { rpart := floatBits(float64(real(c))) ipart := floatBits(float64(imag(c))) state.update(i) - encodeUint(state, rpart) - encodeUint(state, ipart) + state.encodeUint(rpart) + state.encodeUint(ipart) } } @@ -524,8 +268,8 @@ func encComplex64(i *encInstr, state *encoderState, p unsafe.Pointer) { rpart := floatBits(float64(real(c))) ipart := floatBits(float64(imag(c))) state.update(i) - encodeUint(state, rpart) - encodeUint(state, ipart) + state.encodeUint(rpart) + state.encodeUint(ipart) } } @@ -535,17 +279,20 @@ func encComplex128(i *encInstr, state *encoderState, p unsafe.Pointer) { rpart := floatBits(real(c)) ipart := floatBits(imag(c)) state.update(i) - encodeUint(state, rpart) - encodeUint(state, ipart) + state.encodeUint(rpart) + state.encodeUint(ipart) } } +func encNoOp(i *encInstr, state *encoderState, p unsafe.Pointer) { +} + // Byte arrays are encoded as an unsigned count followed by the raw bytes. func encUint8Array(i *encInstr, state *encoderState, p unsafe.Pointer) { b := *(*[]byte)(p) if len(b) > 0 || state.sendZero { state.update(i) - encodeUint(state, uint64(len(b))) + state.encodeUint(uint64(len(b))) state.b.Write(b) } } @@ -555,14 +302,14 @@ func encString(i *encInstr, state *encoderState, p unsafe.Pointer) { s := *(*string)(p) if len(s) > 0 || state.sendZero { state.update(i) - encodeUint(state, uint64(len(s))) + state.encodeUint(uint64(len(s))) io.WriteString(state.b, s) } } // The end of a struct is marked by a delta field number of 0. func encStructTerminator(i *encInstr, state *encoderState, p unsafe.Pointer) { - encodeUint(state, 0) + state.encodeUint(0) } // Execution engine @@ -575,9 +322,8 @@ type encEngine struct { const singletonField = 0 -func encodeSingle(engine *encEngine, b *bytes.Buffer, basep uintptr) os.Error { - state := new(encoderState) - state.b = b +func (enc *Encoder) encodeSingle(b *bytes.Buffer, engine *encEngine, basep uintptr) { + state := newEncoderState(enc, b) state.fieldnum = singletonField // There is no surrounding struct to frame the transmission, so we must // generate data even if the item is zero. To do this, set sendZero. @@ -586,16 +332,14 @@ func encodeSingle(engine *encEngine, b *bytes.Buffer, basep uintptr) os.Error { p := unsafe.Pointer(basep) // offset will be zero if instr.indir > 0 { if p = encIndirect(p, instr.indir); p == nil { - return nil + return } } instr.op(instr, state, p) - return state.err } -func encodeStruct(engine *encEngine, b *bytes.Buffer, basep uintptr) os.Error { - state := new(encoderState) - state.b = b +func (enc *Encoder) encodeStruct(b *bytes.Buffer, engine *encEngine, basep uintptr) { + state := newEncoderState(enc, b) state.fieldnum = -1 for i := 0; i < len(engine.instr); i++ { instr := &engine.instr[i] @@ -606,33 +350,26 @@ func encodeStruct(engine *encEngine, b *bytes.Buffer, basep uintptr) os.Error { } } instr.op(instr, state, p) - if state.err != nil { - break - } } - return state.err } -func encodeArray(b *bytes.Buffer, p uintptr, op encOp, elemWid uintptr, elemIndir int, length int) os.Error { - state := new(encoderState) - state.b = b +func (enc *Encoder) encodeArray(b *bytes.Buffer, p uintptr, op encOp, elemWid uintptr, elemIndir int, length int) { + state := newEncoderState(enc, b) state.fieldnum = -1 state.sendZero = true - encodeUint(state, uint64(length)) - for i := 0; i < length && state.err == nil; i++ { + state.encodeUint(uint64(length)) + for i := 0; i < length; i++ { elemp := p up := unsafe.Pointer(elemp) if elemIndir > 0 { if up = encIndirect(up, elemIndir); up == nil { - state.err = os.ErrorString("gob: encodeArray: nil element") - break + errorf("gob: encodeArray: nil element") } elemp = uintptr(up) } op(nil, state, unsafe.Pointer(elemp)) p += uintptr(elemWid) } - return state.err } func encodeReflectValue(state *encoderState, v reflect.Value, op encOp, indir int) { @@ -640,27 +377,60 @@ func encodeReflectValue(state *encoderState, v reflect.Value, op encOp, indir in v = reflect.Indirect(v) } if v == nil { - state.err = os.ErrorString("gob: encodeReflectValue: nil element") - return + errorf("gob: encodeReflectValue: nil element") } op(nil, state, unsafe.Pointer(v.Addr())) } -func encodeMap(b *bytes.Buffer, mv *reflect.MapValue, keyOp, elemOp encOp, keyIndir, elemIndir int) os.Error { - state := new(encoderState) - state.b = b +func (enc *Encoder) encodeMap(b *bytes.Buffer, mv *reflect.MapValue, keyOp, elemOp encOp, keyIndir, elemIndir int) { + state := newEncoderState(enc, b) state.fieldnum = -1 state.sendZero = true keys := mv.Keys() - encodeUint(state, uint64(len(keys))) + state.encodeUint(uint64(len(keys))) for _, key := range keys { - if state.err != nil { - break - } encodeReflectValue(state, key, keyOp, keyIndir) encodeReflectValue(state, mv.Elem(key), elemOp, elemIndir) } - return state.err +} + +// To send an interface, we send a string identifying the concrete type, followed +// by the type identifier (which might require defining that type right now), followed +// by the concrete value. A nil value gets sent as the empty string for the name, +// followed by no value. +func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv *reflect.InterfaceValue) { + state := newEncoderState(enc, b) + state.fieldnum = -1 + state.sendZero = true + if iv.IsNil() { + state.encodeUint(0) + return + } + + typ, _ := indirect(iv.Elem().Type()) + name, ok := concreteTypeToName[typ] + if !ok { + errorf("gob: type not registered for interface: %s", typ) + } + // Send the name. + state.encodeUint(uint64(len(name))) + _, err := io.WriteString(state.b, name) + if err != nil { + error(err) + } + // Send (and maybe first define) the type id. + enc.sendTypeDescriptor(typ) + // Encode the value into a new buffer. + data := new(bytes.Buffer) + err = enc.encode(data, iv.Elem()) + if err != nil { + error(err) + } + state.encodeUint(uint64(data.Len())) + _, err = state.b.Write(data.Bytes()) + if err != nil { + error(err) + } } var encOpMap = []encOp{ @@ -687,7 +457,7 @@ var encOpMap = []encOp{ // Return the encoding op for the base type under rt and // the indirection count to reach it. -func encOpFor(rt reflect.Type) (encOp, int, os.Error) { +func (enc *Encoder) encOpFor(rt reflect.Type) (encOp, int) { typ, indir := indirect(rt) var op encOp k := typ.Kind() @@ -703,128 +473,123 @@ func encOpFor(rt reflect.Type) (encOp, int, os.Error) { break } // Slices have a header; we decode it to find the underlying array. - elemOp, indir, err := encOpFor(t.Elem()) - if err != nil { - return nil, 0, err - } + elemOp, indir := enc.encOpFor(t.Elem()) op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { slice := (*reflect.SliceHeader)(p) - if slice.Len == 0 { + if !state.sendZero && slice.Len == 0 { return } state.update(i) - state.err = encodeArray(state.b, slice.Data, elemOp, t.Elem().Size(), indir, int(slice.Len)) + state.enc.encodeArray(state.b, slice.Data, elemOp, t.Elem().Size(), indir, int(slice.Len)) } case *reflect.ArrayType: // True arrays have size in the type. - elemOp, indir, err := encOpFor(t.Elem()) - if err != nil { - return nil, 0, err - } + elemOp, indir := enc.encOpFor(t.Elem()) op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { - slice := (*reflect.SliceHeader)(p) - if slice.Len == 0 { - return - } state.update(i) - state.err = encodeArray(state.b, uintptr(p), elemOp, t.Elem().Size(), indir, t.Len()) + state.enc.encodeArray(state.b, uintptr(p), elemOp, t.Elem().Size(), indir, t.Len()) } case *reflect.MapType: - keyOp, keyIndir, err := encOpFor(t.Key()) - if err != nil { - return nil, 0, err - } - elemOp, elemIndir, err := encOpFor(t.Elem()) - if err != nil { - return nil, 0, err - } + keyOp, keyIndir := enc.encOpFor(t.Key()) + elemOp, elemIndir := enc.encOpFor(t.Elem()) op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { // Maps cannot be accessed by moving addresses around the way // that slices etc. can. We must recover a full reflection value for // the iteration. v := reflect.NewValue(unsafe.Unreflect(t, unsafe.Pointer((p)))) mv := reflect.Indirect(v).(*reflect.MapValue) - if mv.Len() == 0 { + if !state.sendZero && mv.Len() == 0 { return } state.update(i) - state.err = encodeMap(state.b, mv, keyOp, elemOp, keyIndir, elemIndir) + state.enc.encodeMap(state.b, mv, keyOp, elemOp, keyIndir, elemIndir) } case *reflect.StructType: // Generate a closure that calls out to the engine for the nested type. - _, err := getEncEngine(typ) - if err != nil { - return nil, 0, err - } + enc.getEncEngine(typ) info := mustGetTypeInfo(typ) op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { state.update(i) // indirect through info to delay evaluation for recursive structs - state.err = encodeStruct(info.encoder, state.b, uintptr(p)) + state.enc.encodeStruct(state.b, info.encoder, uintptr(p)) + } + case *reflect.InterfaceType: + op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { + // Interfaces transmit the name and contents of the concrete + // value they contain. + v := reflect.NewValue(unsafe.Unreflect(t, unsafe.Pointer((p)))) + iv := reflect.Indirect(v).(*reflect.InterfaceValue) + if !state.sendZero && (iv == nil || iv.IsNil()) { + return + } + state.update(i) + state.enc.encodeInterface(state.b, iv) } } } if op == nil { - return op, indir, os.ErrorString("gob enc: can't happen: encode type " + rt.String()) + errorf("gob enc: can't happen: encode type %s", rt.String()) } - return op, indir, nil + return op, indir } // The local Type was compiled from the actual value, so we know it's compatible. -func compileEnc(rt reflect.Type) (*encEngine, os.Error) { +func (enc *Encoder) compileEnc(rt reflect.Type) *encEngine { srt, isStruct := rt.(*reflect.StructType) engine := new(encEngine) if isStruct { engine.instr = make([]encInstr, srt.NumField()+1) // +1 for terminator for fieldnum := 0; fieldnum < srt.NumField(); fieldnum++ { f := srt.Field(fieldnum) - op, indir, err := encOpFor(f.Type) - if err != nil { - return nil, err + op, indir := enc.encOpFor(f.Type) + if !isExported(f.Name) { + op = encNoOp } engine.instr[fieldnum] = encInstr{op, fieldnum, indir, uintptr(f.Offset)} } engine.instr[srt.NumField()] = encInstr{encStructTerminator, 0, 0, 0} } else { engine.instr = make([]encInstr, 1) - op, indir, err := encOpFor(rt) - if err != nil { - return nil, err - } + op, indir := enc.encOpFor(rt) engine.instr[0] = encInstr{op, singletonField, indir, 0} // offset is zero } - return engine, nil + return engine } // typeLock must be held (or we're in initialization and guaranteed single-threaded). // The reflection type must have all its indirections processed out. -func getEncEngine(rt reflect.Type) (*encEngine, os.Error) { - info, err := getTypeInfo(rt) - if err != nil { - return nil, err +func (enc *Encoder) getEncEngine(rt reflect.Type) *encEngine { + info, err1 := getTypeInfo(rt) + if err1 != nil { + error(err1) } if info.encoder == nil { // mark this engine as underway before compiling to handle recursive types. info.encoder = new(encEngine) - info.encoder, err = compileEnc(rt) + info.encoder = enc.compileEnc(rt) } - return info.encoder, err + return info.encoder +} + +// Put this in a function so we can hold the lock only while compiling, not when encoding. +func (enc *Encoder) lockAndGetEncEngine(rt reflect.Type) *encEngine { + typeLock.Lock() + defer typeLock.Unlock() + return enc.getEncEngine(rt) } -func encode(b *bytes.Buffer, value reflect.Value) os.Error { +func (enc *Encoder) encode(b *bytes.Buffer, value reflect.Value) (err os.Error) { + defer catchError(&err) // Dereference down to the underlying object. rt, indir := indirect(value.Type()) for i := 0; i < indir; i++ { value = reflect.Indirect(value) } - typeLock.Lock() - engine, err := getEncEngine(rt) - typeLock.Unlock() - if err != nil { - return err - } + engine := enc.lockAndGetEncEngine(rt) if value.Type().Kind() == reflect.Struct { - return encodeStruct(engine, b, value.Addr()) + enc.encodeStruct(b, engine, value.Addr()) + } else { + enc.encodeSingle(b, engine, value.Addr()) } - return encodeSingle(engine, b, value.Addr()) + return nil } diff --git a/src/pkg/gob/encoder.go b/src/pkg/gob/encoder.go index aba8d0e5c..8869b2629 100644 --- a/src/pkg/gob/encoder.go +++ b/src/pkg/gob/encoder.go @@ -21,6 +21,7 @@ type Encoder struct { state *encoderState // so we can encode integers, strings directly countState *encoderState // stage for writing counts buf []byte // for collecting the output. + err os.Error } // NewEncoder returns a new encoder that will transmit on the io.Writer. @@ -28,10 +29,8 @@ func NewEncoder(w io.Writer) *Encoder { enc := new(Encoder) enc.w = w enc.sent = make(map[reflect.Type]typeId) - enc.state = new(encoderState) - enc.state.b = new(bytes.Buffer) // the rest isn't important; all we need is buffer and writer - enc.countState = new(encoderState) - enc.countState.b = new(bytes.Buffer) // the rest isn't important; all we need is buffer and writer + enc.state = newEncoderState(enc, new(bytes.Buffer)) + enc.countState = newEncoderState(enc, new(bytes.Buffer)) return enc } @@ -40,8 +39,8 @@ func (enc *Encoder) badType(rt reflect.Type) { } func (enc *Encoder) setError(err os.Error) { - if enc.state.err == nil { // remember the first. - enc.state.err = err + if enc.err == nil { // remember the first. + enc.err = err } enc.state.b.Reset() } @@ -49,7 +48,7 @@ func (enc *Encoder) setError(err os.Error) { // Send the data item preceded by a unsigned count of its length. func (enc *Encoder) send() { // Encode the length. - encodeUint(enc.countState, uint64(enc.state.b.Len())) + enc.countState.encodeUint(uint64(enc.state.b.Len())) // Build the buffer. countLen := enc.countState.b.Len() total := countLen + enc.state.b.Len() @@ -74,7 +73,7 @@ func (enc *Encoder) sendType(origt reflect.Type) (sent bool) { switch rt := rt.(type) { default: - // Basic types do not need to be described. + // Basic types and interfaces do not need to be described. return case *reflect.SliceType: // If it's []uint8, don't send; it's considered basic. @@ -92,7 +91,7 @@ func (enc *Encoder) sendType(origt reflect.Type) (sent bool) { case *reflect.StructType: // structs must be sent so we know their fields. break - case *reflect.ChanType, *reflect.FuncType, *reflect.InterfaceType: + case *reflect.ChanType, *reflect.FuncType: // Probably a bad field in a struct. enc.badType(rt) return @@ -113,11 +112,11 @@ func (enc *Encoder) sendType(origt reflect.Type) (sent bool) { } // Send the pair (-id, type) // Id: - encodeInt(enc.state, -int64(info.id)) + enc.state.encodeInt(-int64(info.id)) // Type: - encode(enc.state.b, reflect.NewValue(info.wire)) + enc.encode(enc.state.b, reflect.NewValue(info.wire)) enc.send() - if enc.state.err != nil { + if enc.err != nil { return } @@ -134,7 +133,7 @@ func (enc *Encoder) sendType(origt reflect.Type) (sent bool) { case reflect.ArrayOrSliceType: enc.sendType(st.Elem()) } - return + return true } // Encode transmits the data item represented by the empty interface value, @@ -143,30 +142,17 @@ func (enc *Encoder) Encode(e interface{}) os.Error { return enc.EncodeValue(reflect.NewValue(e)) } -// EncodeValue transmits the data item represented by the reflection value, -// guaranteeing that all necessary type information has been transmitted first. -func (enc *Encoder) EncodeValue(value reflect.Value) os.Error { - // Make sure we're single-threaded through here, so multiple - // goroutines can share an encoder. - enc.mutex.Lock() - defer enc.mutex.Unlock() - - enc.state.err = nil - rt, _ := indirect(value.Type()) - - // Sanity check only: encoder should never come in with data present. - if enc.state.b.Len() > 0 || enc.countState.b.Len() > 0 { - enc.state.err = os.ErrorString("encoder: buffer not empty") - return enc.state.err - } - +// sendTypeId makes sure the remote side knows about this type. +// It will send a descriptor if this is the first time the type has been +// sent. Regardless, it sends the id. +func (enc *Encoder) sendTypeDescriptor(rt reflect.Type) { // Make sure the type is known to the other side. // First, have we already sent this type? if _, alreadySent := enc.sent[rt]; !alreadySent { // No, so send it. sent := enc.sendType(rt) - if enc.state.err != nil { - return enc.state.err + if enc.err != nil { + return } // If the type info has still not been transmitted, it means we have // a singleton basic type (int, []byte etc.) at top level. We don't @@ -177,22 +163,45 @@ func (enc *Encoder) EncodeValue(value reflect.Value) os.Error { typeLock.Unlock() if err != nil { enc.setError(err) - return err + return } enc.sent[rt] = info.id } } // Identify the type of this top-level value. - encodeInt(enc.state, int64(enc.sent[rt])) + enc.state.encodeInt(int64(enc.sent[rt])) +} + +// EncodeValue transmits the data item represented by the reflection value, +// guaranteeing that all necessary type information has been transmitted first. +func (enc *Encoder) EncodeValue(value reflect.Value) os.Error { + // Make sure we're single-threaded through here, so multiple + // goroutines can share an encoder. + enc.mutex.Lock() + defer enc.mutex.Unlock() + + enc.err = nil + rt, _ := indirect(value.Type()) + + // Sanity check only: encoder should never come in with data present. + if enc.state.b.Len() > 0 || enc.countState.b.Len() > 0 { + enc.err = os.ErrorString("encoder: buffer not empty") + return enc.err + } + + enc.sendTypeDescriptor(rt) + if enc.err != nil { + return enc.err + } // Encode the object. - err := encode(enc.state.b, value) + err := enc.encode(enc.state.b, value) if err != nil { enc.setError(err) } else { enc.send() } - return enc.state.err + return enc.err } diff --git a/src/pkg/gob/encoder_test.go b/src/pkg/gob/encoder_test.go index b578cd0f8..db0b7db66 100644 --- a/src/pkg/gob/encoder_test.go +++ b/src/pkg/gob/encoder_test.go @@ -14,48 +14,48 @@ import ( ) type ET2 struct { - x string + X string } type ET1 struct { - a int - et2 *ET2 - next *ET1 + A int + Et2 *ET2 + Next *ET1 } // Like ET1 but with a different name for a field type ET3 struct { - a int - et2 *ET2 - differentNext *ET1 + A int + Et2 *ET2 + DifferentNext *ET1 } // Like ET1 but with a different type for a field type ET4 struct { - a int - et2 float - next int + A int + Et2 float + Next int } func TestEncoderDecoder(t *testing.T) { b := new(bytes.Buffer) enc := NewEncoder(b) et1 := new(ET1) - et1.a = 7 - et1.et2 = new(ET2) - enc.Encode(et1) - if enc.state.err != nil { - t.Error("encoder fail:", enc.state.err) + et1.A = 7 + et1.Et2 = new(ET2) + err := enc.Encode(et1) + if err != nil { + t.Error("encoder fail:", err) } dec := NewDecoder(b) newEt1 := new(ET1) - dec.Decode(newEt1) - if dec.state.err != nil { - t.Fatal("error decoding ET1:", dec.state.err) + err = dec.Decode(newEt1) + if err != nil { + t.Fatal("error decoding ET1:", err) } if !reflect.DeepEqual(et1, newEt1) { - t.Fatalf("invalid data for et1: expected %+v; got %+v\n", *et1, *newEt1) + t.Fatalf("invalid data for et1: expected %+v; got %+v", *et1, *newEt1) } if b.Len() != 0 { t.Error("not at eof;", b.Len(), "bytes left") @@ -63,25 +63,25 @@ func TestEncoderDecoder(t *testing.T) { enc.Encode(et1) newEt1 = new(ET1) - dec.Decode(newEt1) - if dec.state.err != nil { - t.Fatal("round 2: error decoding ET1:", dec.state.err) + err = dec.Decode(newEt1) + if err != nil { + t.Fatal("round 2: error decoding ET1:", err) } if !reflect.DeepEqual(et1, newEt1) { - t.Fatalf("round 2: invalid data for et1: expected %+v; got %+v\n", *et1, *newEt1) + t.Fatalf("round 2: invalid data for et1: expected %+v; got %+v", *et1, *newEt1) } if b.Len() != 0 { t.Error("round 2: not at eof;", b.Len(), "bytes left") } // Now test with a running encoder/decoder pair that we recognize a type mismatch. - enc.Encode(et1) - if enc.state.err != nil { - t.Error("round 3: encoder fail:", enc.state.err) + err = enc.Encode(et1) + if err != nil { + t.Error("round 3: encoder fail:", err) } newEt2 := new(ET2) - dec.Decode(newEt2) - if dec.state.err == nil { + err = dec.Decode(newEt2) + if err == nil { t.Fatal("round 3: expected `bad type' error decoding ET2") } } @@ -92,19 +92,19 @@ func badTypeCheck(e interface{}, shouldFail bool, msg string, t *testing.T) { b := new(bytes.Buffer) enc := NewEncoder(b) et1 := new(ET1) - et1.a = 7 - et1.et2 = new(ET2) - enc.Encode(et1) - if enc.state.err != nil { - t.Error("encoder fail:", enc.state.err) + et1.A = 7 + et1.Et2 = new(ET2) + err := enc.Encode(et1) + if err != nil { + t.Error("encoder fail:", err) } dec := NewDecoder(b) - dec.Decode(e) - if shouldFail && (dec.state.err == nil) { + err = dec.Decode(e) + if shouldFail && err == nil { t.Error("expected error for", msg) } - if !shouldFail && (dec.state.err != nil) { - t.Error("unexpected error for", msg, dec.state.err) + if !shouldFail && err != nil { + t.Error("unexpected error for", msg, err) } } @@ -118,9 +118,9 @@ func TestWrongTypeDecoder(t *testing.T) { func corruptDataCheck(s string, err os.Error, t *testing.T) { b := bytes.NewBufferString(s) dec := NewDecoder(b) - dec.Decode(new(ET2)) - if dec.state.err != err { - t.Error("expected error", err, "got", dec.state.err) + err1 := dec.Decode(new(ET2)) + if err1 != err { + t.Error("expected error", err, "got", err1) } } @@ -135,7 +135,6 @@ func TestBadData(t *testing.T) { var unsupportedValues = []interface{}{ make(chan int), func(a int) bool { return true }, - new(interface{}), } func TestUnsupported(t *testing.T) { @@ -152,14 +151,14 @@ func TestUnsupported(t *testing.T) { func encAndDec(in, out interface{}) os.Error { b := new(bytes.Buffer) enc := NewEncoder(b) - enc.Encode(in) - if enc.state.err != nil { - return enc.state.err + err := enc.Encode(in) + if err != nil { + return err } dec := NewDecoder(b) - dec.Decode(out) - if dec.state.err != nil { - return dec.state.err + err = dec.Decode(out) + if err != nil { + return err } return nil } @@ -167,7 +166,7 @@ func encAndDec(in, out interface{}) os.Error { func TestTypeToPtrType(t *testing.T) { // Encode a T, decode a *T type Type0 struct { - a int + A int } t0 := Type0{7} t0p := (*Type0)(nil) @@ -179,7 +178,7 @@ func TestTypeToPtrType(t *testing.T) { func TestPtrTypeToType(t *testing.T) { // Encode a *T, decode a T type Type1 struct { - a uint + A uint } t1p := &Type1{17} var t1 Type1 @@ -190,26 +189,26 @@ func TestPtrTypeToType(t *testing.T) { func TestTypeToPtrPtrPtrPtrType(t *testing.T) { type Type2 struct { - a ****float + A ****float } t2 := Type2{} - t2.a = new(***float) - *t2.a = new(**float) - **t2.a = new(*float) - ***t2.a = new(float) - ****t2.a = 27.4 + t2.A = new(***float) + *t2.A = new(**float) + **t2.A = new(*float) + ***t2.A = new(float) + ****t2.A = 27.4 t2pppp := new(***Type2) if err := encAndDec(t2, t2pppp); err != nil { - t.Error(err) + t.Fatal(err) } - if ****(****t2pppp).a != ****t2.a { - t.Errorf("wrong value after decode: %g not %g", ****(****t2pppp).a, ****t2.a) + if ****(****t2pppp).A != ****t2.A { + t.Errorf("wrong value after decode: %g not %g", ****(****t2pppp).A, ****t2.A) } } func TestSlice(t *testing.T) { type Type3 struct { - a []string + A []string } t3p := &Type3{[]string{"hello", "world"}} var t3 Type3 @@ -232,11 +231,11 @@ func TestValueError(t *testing.T) { func TestArray(t *testing.T) { type Type5 struct { - a [3]string - b [3]byte + A [3]string + B [3]byte } type Type6 struct { - a [2]string // can't hold t5.a + A [2]string // can't hold t5.a } t5 := Type5{[3]string{"hello", ",", "world"}, [3]byte{1, 2, 3}} var t5p Type5 @@ -252,10 +251,10 @@ func TestArray(t *testing.T) { // Regression test for bug: must send zero values inside arrays func TestDefaultsInArray(t *testing.T) { type Type7 struct { - b []bool - i []int - s []string - f []float + B []bool + I []int + S []string + F []float } t7 := Type7{ []bool{false, false, true}, @@ -274,6 +273,7 @@ var testFloat32 float32 var testString string var testSlice []string var testMap map[string]int +var testArray [7]int type SingleTest struct { in interface{} @@ -282,14 +282,16 @@ type SingleTest struct { } var singleTests = []SingleTest{ - SingleTest{17, &testInt, ""}, - SingleTest{float32(17.5), &testFloat32, ""}, - SingleTest{"bike shed", &testString, ""}, - SingleTest{[]string{"bike", "shed", "paint", "color"}, &testSlice, ""}, - SingleTest{map[string]int{"seven": 7, "twelve": 12}, &testMap, ""}, + {17, &testInt, ""}, + {float32(17.5), &testFloat32, ""}, + {"bike shed", &testString, ""}, + {[]string{"bike", "shed", "paint", "color"}, &testSlice, ""}, + {map[string]int{"seven": 7, "twelve": 12}, &testMap, ""}, + {[7]int{4, 55, 0, 0, 0, 0, 0}, &testArray, ""}, // case that once triggered a bug + {[7]int{4, 55, 1, 44, 22, 66, 1234}, &testArray, ""}, // Decode errors - SingleTest{172, &testFloat32, "wrong type"}, + {172, &testFloat32, "wrong type"}, } func TestSingletons(t *testing.T) { @@ -320,7 +322,64 @@ func TestSingletons(t *testing.T) { // Get rid of the pointer in the rhs val := reflect.NewValue(test.out).(*reflect.PtrValue).Elem().Interface() if !reflect.DeepEqual(test.in, val) { - t.Errorf("decoding int: expected %v got %v", test.in, val) + t.Errorf("decoding singleton: expected %v got %v", test.in, val) } } } + +func TestStructNonStruct(t *testing.T) { + type Struct struct { + A string + } + type NonStruct string + s := Struct{"hello"} + var sp Struct + if err := encAndDec(s, &sp); err != nil { + t.Error(err) + } + var ns NonStruct + if err := encAndDec(s, &ns); err == nil { + t.Error("should get error for struct/non-struct") + } else if strings.Index(err.String(), "type") < 0 { + t.Error("for struct/non-struct expected type error; got", err) + } + // Now try the other way + var nsp NonStruct + if err := encAndDec(ns, &nsp); err != nil { + t.Error(err) + } + if err := encAndDec(ns, &s); err == nil { + t.Error("should get error for non-struct/struct") + } else if strings.Index(err.String(), "type") < 0 { + t.Error("for non-struct/struct expected type error; got", err) + } +} + +type interfaceIndirectTestI interface { + F() bool +} + +type interfaceIndirectTestT struct{} + +func (this *interfaceIndirectTestT) F() bool { + return true +} + +// A version of a bug reported on golang-nuts. Also tests top-level +// slice of interfaces. The issue was registering *T caused T to be +// stored as the concrete type. +func TestInterfaceIndirect(t *testing.T) { + Register(&interfaceIndirectTestT{}) + b := new(bytes.Buffer) + w := []interfaceIndirectTestI{&interfaceIndirectTestT{}} + err := NewEncoder(b).Encode(w) + if err != nil { + t.Fatal("encode error:", err) + } + + var r []interfaceIndirectTestI + err = NewDecoder(b).Decode(&r) + if err != nil { + t.Fatal("decode error:", err) + } +} diff --git a/src/pkg/gob/error.go b/src/pkg/gob/error.go new file mode 100644 index 000000000..b053761fb --- /dev/null +++ b/src/pkg/gob/error.go @@ -0,0 +1,41 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gob + +import ( + "fmt" + "os" +) + +// Errors in decoding and encoding are handled using panic and recover. +// Panics caused by user error (that is, everything except run-time panics +// such as "index out of bounds" errors) do not leave the file that caused +// them, but are instead turned into plain os.Error returns. Encoding and +// decoding functions and methods that do not return an os.Error either use +// panic to report an error or are guaranteed error-free. + +// A gobError wraps an os.Error and is used to distinguish errors (panics) generated in this package. +type gobError struct { + os.Error +} + +// errorf is like error but takes Printf-style arguments to construct an os.Error. +func errorf(format string, args ...interface{}) { + error(fmt.Errorf(format, args...)) +} + +// error wraps the argument error and uses it as the argument to panic. +func error(err os.Error) { + panic(gobError{Error: err}) +} + +// catchError is meant to be used as a deferred function to turn a panic(gobError) into a +// plain os.Error. It overwrites the error return of the function that deferred its call. +func catchError(err *os.Error) { + if e := recover(); e != nil { + *err = e.(gobError).Error // Will re-panic if not one of our errors, such as a runtime error. + } + return +} diff --git a/src/pkg/gob/type.go b/src/pkg/gob/type.go index 0b01b74dc..c00af87bf 100644 --- a/src/pkg/gob/type.go +++ b/src/pkg/gob/type.go @@ -29,7 +29,7 @@ const firstUserId = 64 // lowest id number granted to user type gobType interface { id() typeId setId(id typeId) - Name() string + name() string string() string // not public; only for debugging safeString(seen map[typeId]bool) string } @@ -52,42 +52,60 @@ func (t typeId) gobType() gobType { } // string returns the string representation of the type associated with the typeId. -func (t typeId) string() string { return t.gobType().string() } +func (t typeId) string() string { + if t.gobType() == nil { + return "" + } + return t.gobType().string() +} // Name returns the name of the type associated with the typeId. -func (t typeId) Name() string { return t.gobType().Name() } +func (t typeId) name() string { + if t.gobType() == nil { + return "" + } + return t.gobType().name() +} // Common elements of all types. -type commonType struct { - name string - _id typeId +type CommonType struct { + Name string + Id typeId } -func (t *commonType) id() typeId { return t._id } +func (t *CommonType) id() typeId { return t.Id } -func (t *commonType) setId(id typeId) { t._id = id } +func (t *CommonType) setId(id typeId) { t.Id = id } -func (t *commonType) string() string { return t.name } +func (t *CommonType) string() string { return t.Name } -func (t *commonType) safeString(seen map[typeId]bool) string { - return t.name +func (t *CommonType) safeString(seen map[typeId]bool) string { + return t.Name } -func (t *commonType) Name() string { return t.name } +func (t *CommonType) name() string { return t.Name } // Create and check predefined types // The string for tBytes is "bytes" not "[]byte" to signify its specialness. var ( // Primordial types, needed during initialization. - tBool = bootstrapType("bool", false, 1) - tInt = bootstrapType("int", int(0), 2) - tUint = bootstrapType("uint", uint(0), 3) - tFloat = bootstrapType("float", float64(0), 4) - tBytes = bootstrapType("bytes", make([]byte, 0), 5) - tString = bootstrapType("string", "", 6) - // Types added to the language later, not needed during initialization. - tComplex typeId + tBool = bootstrapType("bool", false, 1) + tInt = bootstrapType("int", int(0), 2) + tUint = bootstrapType("uint", uint(0), 3) + tFloat = bootstrapType("float", float64(0), 4) + tBytes = bootstrapType("bytes", make([]byte, 0), 5) + tString = bootstrapType("string", "", 6) + tComplex = bootstrapType("complex", 0+0i, 7) + tInterface = bootstrapType("interface", interface{}(nil), 8) + // Reserve some Ids for compatible expansion + tReserved7 = bootstrapType("_reserved1", struct{ r7 int }{}, 9) + tReserved6 = bootstrapType("_reserved1", struct{ r6 int }{}, 10) + tReserved5 = bootstrapType("_reserved1", struct{ r5 int }{}, 11) + tReserved4 = bootstrapType("_reserved1", struct{ r4 int }{}, 12) + tReserved3 = bootstrapType("_reserved1", struct{ r3 int }{}, 13) + tReserved2 = bootstrapType("_reserved1", struct{ r2 int }{}, 14) + tReserved1 = bootstrapType("_reserved1", struct{ r1 int }{}, 15) ) // Predefined because it's needed by the Decoder @@ -95,15 +113,13 @@ var tWireType = mustGetTypeInfo(reflect.Typeof(wireType{})).id func init() { // Some magic numbers to make sure there are no surprises. - checkId(7, tWireType) - checkId(9, mustGetTypeInfo(reflect.Typeof(commonType{})).id) - checkId(11, mustGetTypeInfo(reflect.Typeof(structType{})).id) - checkId(12, mustGetTypeInfo(reflect.Typeof(fieldType{})).id) - - // Complex was added after gob was written, so appears after the - // fundamental types are built. - tComplex = bootstrapType("complex", 0+0i, 15) - decIgnoreOpMap[tComplex] = ignoreTwoUints + checkId(16, tWireType) + checkId(17, mustGetTypeInfo(reflect.Typeof(arrayType{})).id) + checkId(18, mustGetTypeInfo(reflect.Typeof(CommonType{})).id) + checkId(19, mustGetTypeInfo(reflect.Typeof(sliceType{})).id) + checkId(20, mustGetTypeInfo(reflect.Typeof(structType{})).id) + checkId(21, mustGetTypeInfo(reflect.Typeof(fieldType{})).id) + checkId(23, mustGetTypeInfo(reflect.Typeof(mapType{})).id) builtinIdToType = make(map[typeId]gobType) for k, v := range idToType { @@ -116,26 +132,27 @@ func init() { panic(fmt.Sprintln("nextId too large:", nextId)) } nextId = firstUserId + registerBasics() } // Array type type arrayType struct { - commonType + CommonType Elem typeId Len int } func newArrayType(name string, elem gobType, length int) *arrayType { - a := &arrayType{commonType{name: name}, elem.id(), length} + a := &arrayType{CommonType{Name: name}, elem.id(), length} setTypeId(a) return a } func (a *arrayType) safeString(seen map[typeId]bool) string { - if seen[a._id] { - return a.name + if seen[a.Id] { + return a.Name } - seen[a._id] = true + seen[a.Id] = true return fmt.Sprintf("[%d]%s", a.Len, a.Elem.gobType().safeString(seen)) } @@ -143,22 +160,22 @@ func (a *arrayType) string() string { return a.safeString(make(map[typeId]bool)) // Map type type mapType struct { - commonType + CommonType Key typeId Elem typeId } func newMapType(name string, key, elem gobType) *mapType { - m := &mapType{commonType{name: name}, key.id(), elem.id()} + m := &mapType{CommonType{Name: name}, key.id(), elem.id()} setTypeId(m) return m } func (m *mapType) safeString(seen map[typeId]bool) string { - if seen[m._id] { - return m.name + if seen[m.Id] { + return m.Name } - seen[m._id] = true + seen[m.Id] = true key := m.Key.gobType().safeString(seen) elem := m.Elem.gobType().safeString(seen) return fmt.Sprintf("map[%s]%s", key, elem) @@ -168,21 +185,21 @@ func (m *mapType) string() string { return m.safeString(make(map[typeId]bool)) } // Slice type type sliceType struct { - commonType + CommonType Elem typeId } func newSliceType(name string, elem gobType) *sliceType { - s := &sliceType{commonType{name: name}, elem.id()} + s := &sliceType{CommonType{Name: name}, elem.id()} setTypeId(s) return s } func (s *sliceType) safeString(seen map[typeId]bool) string { - if seen[s._id] { - return s.name + if seen[s.Id] { + return s.Name } - seen[s._id] = true + seen[s.Id] = true return fmt.Sprintf("[]%s", s.Elem.gobType().safeString(seen)) } @@ -190,26 +207,26 @@ func (s *sliceType) string() string { return s.safeString(make(map[typeId]bool)) // Struct type type fieldType struct { - name string - id typeId + Name string + Id typeId } type structType struct { - commonType - field []*fieldType + CommonType + Field []*fieldType } func (s *structType) safeString(seen map[typeId]bool) string { if s == nil { return "" } - if _, ok := seen[s._id]; ok { - return s.name + if _, ok := seen[s.Id]; ok { + return s.Name } - seen[s._id] = true - str := s.name + " = struct { " - for _, f := range s.field { - str += fmt.Sprintf("%s %s; ", f.name, f.id.gobType().safeString(seen)) + seen[s.Id] = true + str := s.Name + " = struct { " + for _, f := range s.Field { + str += fmt.Sprintf("%s %s; ", f.Name, f.Id.gobType().safeString(seen)) } str += "}" return str @@ -218,13 +235,13 @@ func (s *structType) safeString(seen map[typeId]bool) string { func (s *structType) string() string { return s.safeString(make(map[typeId]bool)) } func newStructType(name string) *structType { - s := &structType{commonType{name: name}, nil} + s := &structType{CommonType{Name: name}, nil} setTypeId(s) return s } // Step through the indirections on a type to discover the base type. -// Return the number of indirections. +// Return the base type and the number of indirections. func indirect(t reflect.Type) (rt reflect.Type, count int) { rt = t for { @@ -259,6 +276,9 @@ func newTypeObject(name string, rt reflect.Type) (gobType, os.Error) { case *reflect.StringType: return tString.gobType(), nil + case *reflect.InterfaceType: + return tInterface.gobType(), nil + case *reflect.ArrayType: gt, err := getType("", t.Elem()) if err != nil { @@ -300,7 +320,8 @@ func newTypeObject(name string, rt reflect.Type) (gobType, os.Error) { typ, _ := indirect(f.Type) tname := typ.Name() if tname == "" { - tname = f.Type.String() + t, _ := indirect(f.Type) + tname = t.String() } gt, err := getType(tname, f.Type) if err != nil { @@ -308,7 +329,7 @@ func newTypeObject(name string, rt reflect.Type) (gobType, os.Error) { } field[i] = &fieldType{f.Name, gt.id()} } - strType.field = field + strType.Field = field return strType, nil default: @@ -320,14 +341,7 @@ func newTypeObject(name string, rt reflect.Type) (gobType, os.Error) { // getType returns the Gob type describing the given reflect.Type. // typeLock must be held. func getType(name string, rt reflect.Type) (gobType, os.Error) { - // Flatten the data structure by collapsing out pointers - for { - pt, ok := rt.(*reflect.PtrType) - if !ok { - break - } - rt = pt.Elem() - } + rt, _ = indirect(rt) typ, present := types[rt] if present { return typ, nil @@ -341,7 +355,8 @@ func getType(name string, rt reflect.Type) (gobType, os.Error) { func checkId(want, got typeId) { if want != got { - panic("bootstrap type wrong id: " + got.Name() + " " + got.string() + " not " + want.string()) + fmt.Fprintf(os.Stderr, "checkId: %d should be %d\n", int(want), int(got)) + panic("bootstrap type wrong id: " + got.name() + " " + got.string() + " not " + want.string()) } } @@ -352,7 +367,7 @@ func bootstrapType(name string, e interface{}, expect typeId) typeId { if present { panic("bootstrap type already present: " + name + ", " + rt.String()) } - typ := &commonType{name: name} + typ := &CommonType{Name: name} types[rt] = typ setTypeId(typ) checkId(expect, nextId) @@ -371,17 +386,28 @@ func bootstrapType(name string, e interface{}, expect typeId) typeId { // To maintain binary compatibility, if you extend this type, always put // the new fields last. type wireType struct { - arrayT *arrayType - sliceT *sliceType - structT *structType - mapT *mapType + ArrayT *arrayType + SliceT *sliceType + StructT *structType + MapT *mapType } -func (w *wireType) name() string { - if w.structT != nil { - return w.structT.name +func (w *wireType) string() string { + const unknown = "unknown type" + if w == nil { + return unknown } - return "unknown" + switch { + case w.ArrayT != nil: + return w.ArrayT.Name + case w.SliceT != nil: + return w.SliceT.Name + case w.StructT != nil: + return w.StructT.Name + case w.MapT != nil: + return w.MapT.Name + } + return unknown } type typeInfo struct { @@ -410,16 +436,16 @@ func getTypeInfo(rt reflect.Type) (*typeInfo, os.Error) { t := info.id.gobType() switch typ := rt.(type) { case *reflect.ArrayType: - info.wire = &wireType{arrayT: t.(*arrayType)} + info.wire = &wireType{ArrayT: t.(*arrayType)} case *reflect.MapType: - info.wire = &wireType{mapT: t.(*mapType)} + info.wire = &wireType{MapT: t.(*mapType)} case *reflect.SliceType: // []byte == []uint8 is a special case handled separately if typ.Elem().Kind() != reflect.Uint8 { - info.wire = &wireType{sliceT: t.(*sliceType)} + info.wire = &wireType{SliceT: t.(*sliceType)} } case *reflect.StructType: - info.wire = &wireType{structT: t.(*structType)} + info.wire = &wireType{StructT: t.(*structType)} } typeInfoMap[rt] = info } @@ -434,3 +460,82 @@ func mustGetTypeInfo(rt reflect.Type) *typeInfo { } return t } + +var ( + nameToConcreteType = make(map[string]reflect.Type) + concreteTypeToName = make(map[reflect.Type]string) +) + +// RegisterName is like Register but uses the provided name rather than the +// type's default. +func RegisterName(name string, value interface{}) { + if name == "" { + // reserved for nil + panic("attempt to register empty name") + } + rt, _ := indirect(reflect.Typeof(value)) + // Check for incompatible duplicates. + if t, ok := nameToConcreteType[name]; ok && t != rt { + panic("gob: registering duplicate types for " + name) + } + if n, ok := concreteTypeToName[rt]; ok && n != name { + panic("gob: registering duplicate names for " + rt.String()) + } + // Store the name and type provided by the user.... + nameToConcreteType[name] = reflect.Typeof(value) + // but the flattened type in the type table, since that's what decode needs. + concreteTypeToName[rt] = name +} + +// Register records a type, identified by a value for that type, under its +// internal type name. That name will identify the concrete type of a value +// sent or received as an interface variable. Only types that will be +// transferred as implementations of interface values need to be registered. +// Expecting to be used only during initialization, it panics if the mapping +// between types and names is not a bijection. +func Register(value interface{}) { + // Default to printed representation for unnamed types + rt := reflect.Typeof(value) + name := rt.String() + + // But for named types (or pointers to them), qualify with import path. + // Dereference one pointer looking for a named type. + star := "" + if rt.Name() == "" { + if pt, ok := rt.(*reflect.PtrType); ok { + star = "*" + rt = pt + } + } + if rt.Name() != "" { + if rt.PkgPath() == "" { + name = star + rt.Name() + } else { + name = star + rt.PkgPath() + "." + rt.Name() + } + } + + RegisterName(name, value) +} + +func registerBasics() { + Register(int(0)) + Register(int8(0)) + Register(int16(0)) + Register(int32(0)) + Register(int64(0)) + Register(uint(0)) + Register(uint8(0)) + Register(uint16(0)) + Register(uint32(0)) + Register(uint64(0)) + Register(float(0)) + Register(float32(0)) + Register(float64(0)) + Register(complex(0i)) + Register(complex64(0i)) + Register(complex128(0i)) + Register(false) + Register("") + Register([]byte(nil)) +} diff --git a/src/pkg/gob/type_test.go b/src/pkg/gob/type_test.go index 6acfa7135..106e4f10b 100644 --- a/src/pkg/gob/type_test.go +++ b/src/pkg/gob/type_test.go @@ -15,12 +15,12 @@ type typeT struct { } var basicTypes = []typeT{ - typeT{tBool, "bool"}, - typeT{tInt, "int"}, - typeT{tUint, "uint"}, - typeT{tFloat, "float"}, - typeT{tBytes, "bytes"}, - typeT{tString, "string"}, + {tBool, "bool"}, + {tInt, "int"}, + {tUint, "uint"}, + {tFloat, "float"}, + {tBytes, "bytes"}, + {tString, "string"}, } func getTypeUnlocked(name string, rt reflect.Type) gobType { diff --git a/src/pkg/hash/Makefile b/src/pkg/hash/Makefile index aaa641f7c..56071cb33 100644 --- a/src/pkg/hash/Makefile +++ b/src/pkg/hash/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=hash GOFILES=\ diff --git a/src/pkg/hash/adler32/Makefile b/src/pkg/hash/adler32/Makefile index 6f6d66fd0..38ce537ba 100644 --- a/src/pkg/hash/adler32/Makefile +++ b/src/pkg/hash/adler32/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=hash/adler32 GOFILES=\ diff --git a/src/pkg/hash/adler32/adler32_test.go b/src/pkg/hash/adler32/adler32_test.go index cfe9c9547..ffa5569bc 100644 --- a/src/pkg/hash/adler32/adler32_test.go +++ b/src/pkg/hash/adler32/adler32_test.go @@ -15,38 +15,38 @@ type _Adler32Test struct { } var golden = []_Adler32Test{ - _Adler32Test{0x1, ""}, - _Adler32Test{0x620062, "a"}, - _Adler32Test{0x12600c4, "ab"}, - _Adler32Test{0x24d0127, "abc"}, - _Adler32Test{0x3d8018b, "abcd"}, - _Adler32Test{0x5c801f0, "abcde"}, - _Adler32Test{0x81e0256, "abcdef"}, - _Adler32Test{0xadb02bd, "abcdefg"}, - _Adler32Test{0xe000325, "abcdefgh"}, - _Adler32Test{0x118e038e, "abcdefghi"}, - _Adler32Test{0x158603f8, "abcdefghij"}, - _Adler32Test{0x3f090f02, "Discard medicine more than two years old."}, - _Adler32Test{0x46d81477, "He who has a shady past knows that nice guys finish last."}, - _Adler32Test{0x40ee0ee1, "I wouldn't marry him with a ten foot pole."}, - _Adler32Test{0x16661315, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, - _Adler32Test{0x5b2e1480, "The days of the digital watch are numbered. -Tom Stoppard"}, - _Adler32Test{0x8c3c09ea, "Nepal premier won't resign."}, - _Adler32Test{0x45ac18fd, "For every action there is an equal and opposite government program."}, - _Adler32Test{0x53c61462, "His money is twice tainted: 'taint yours and 'taint mine."}, - _Adler32Test{0x7e511e63, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, - _Adler32Test{0xe4801a6a, "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, - _Adler32Test{0x61b507df, "size: a.out: bad magic"}, - _Adler32Test{0xb8631171, "The major problem is with sendmail. -Mark Horton"}, - _Adler32Test{0x8b5e1904, "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, - _Adler32Test{0x7cc6102b, "If the enemy is within range, then so are you."}, - _Adler32Test{0x700318e7, "It's well we cannot hear the screams/That we create in others' dreams."}, - _Adler32Test{0x1e601747, "You remind me of a TV show, but that's all right: I watch it anyway."}, - _Adler32Test{0xb55b0b09, "C is as portable as Stonehedge!!"}, - _Adler32Test{0x39111dd0, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, - _Adler32Test{0x91dd304f, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, - _Adler32Test{0x2e5d1316, "How can you write a big system without C++? -Paul Glick"}, - _Adler32Test{0xd0201df6, "'Invariant assertions' is the most elegant programming technique! -Tom Szymanski"}, + {0x1, ""}, + {0x620062, "a"}, + {0x12600c4, "ab"}, + {0x24d0127, "abc"}, + {0x3d8018b, "abcd"}, + {0x5c801f0, "abcde"}, + {0x81e0256, "abcdef"}, + {0xadb02bd, "abcdefg"}, + {0xe000325, "abcdefgh"}, + {0x118e038e, "abcdefghi"}, + {0x158603f8, "abcdefghij"}, + {0x3f090f02, "Discard medicine more than two years old."}, + {0x46d81477, "He who has a shady past knows that nice guys finish last."}, + {0x40ee0ee1, "I wouldn't marry him with a ten foot pole."}, + {0x16661315, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, + {0x5b2e1480, "The days of the digital watch are numbered. -Tom Stoppard"}, + {0x8c3c09ea, "Nepal premier won't resign."}, + {0x45ac18fd, "For every action there is an equal and opposite government program."}, + {0x53c61462, "His money is twice tainted: 'taint yours and 'taint mine."}, + {0x7e511e63, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, + {0xe4801a6a, "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, + {0x61b507df, "size: a.out: bad magic"}, + {0xb8631171, "The major problem is with sendmail. -Mark Horton"}, + {0x8b5e1904, "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, + {0x7cc6102b, "If the enemy is within range, then so are you."}, + {0x700318e7, "It's well we cannot hear the screams/That we create in others' dreams."}, + {0x1e601747, "You remind me of a TV show, but that's all right: I watch it anyway."}, + {0xb55b0b09, "C is as portable as Stonehedge!!"}, + {0x39111dd0, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, + {0x91dd304f, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, + {0x2e5d1316, "How can you write a big system without C++? -Paul Glick"}, + {0xd0201df6, "'Invariant assertions' is the most elegant programming technique! -Tom Szymanski"}, } func TestGolden(t *testing.T) { diff --git a/src/pkg/hash/crc32/Makefile b/src/pkg/hash/crc32/Makefile index 071d89823..31b205185 100644 --- a/src/pkg/hash/crc32/Makefile +++ b/src/pkg/hash/crc32/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=hash/crc32 GOFILES=\ diff --git a/src/pkg/hash/crc32/crc32_test.go b/src/pkg/hash/crc32/crc32_test.go index 45ad84be7..cf5743c99 100644 --- a/src/pkg/hash/crc32/crc32_test.go +++ b/src/pkg/hash/crc32/crc32_test.go @@ -15,37 +15,37 @@ type test struct { } var golden = []test{ - test{0x0, ""}, - test{0xe8b7be43, "a"}, - test{0x9e83486d, "ab"}, - test{0x352441c2, "abc"}, - test{0xed82cd11, "abcd"}, - test{0x8587d865, "abcde"}, - test{0x4b8e39ef, "abcdef"}, - test{0x312a6aa6, "abcdefg"}, - test{0xaeef2a50, "abcdefgh"}, - test{0x8da988af, "abcdefghi"}, - test{0x3981703a, "abcdefghij"}, - test{0x6b9cdfe7, "Discard medicine more than two years old."}, - test{0xc90ef73f, "He who has a shady past knows that nice guys finish last."}, - test{0xb902341f, "I wouldn't marry him with a ten foot pole."}, - test{0x42080e8, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, - test{0x154c6d11, "The days of the digital watch are numbered. -Tom Stoppard"}, - test{0x4c418325, "Nepal premier won't resign."}, - test{0x33955150, "For every action there is an equal and opposite government program."}, - test{0x26216a4b, "His money is twice tainted: 'taint yours and 'taint mine."}, - test{0x1abbe45e, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, - test{0xc89a94f7, "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, - test{0xab3abe14, "size: a.out: bad magic"}, - test{0xbab102b6, "The major problem is with sendmail. -Mark Horton"}, - test{0x999149d7, "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, - test{0x6d52a33c, "If the enemy is within range, then so are you."}, - test{0x90631e8d, "It's well we cannot hear the screams/That we create in others' dreams."}, - test{0x78309130, "You remind me of a TV show, but that's all right: I watch it anyway."}, - test{0x7d0a377f, "C is as portable as Stonehedge!!"}, - test{0x8c79fd79, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, - test{0xa20b7167, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, - test{0x8e0bb443, "How can you write a big system without C++? -Paul Glick"}, + {0x0, ""}, + {0xe8b7be43, "a"}, + {0x9e83486d, "ab"}, + {0x352441c2, "abc"}, + {0xed82cd11, "abcd"}, + {0x8587d865, "abcde"}, + {0x4b8e39ef, "abcdef"}, + {0x312a6aa6, "abcdefg"}, + {0xaeef2a50, "abcdefgh"}, + {0x8da988af, "abcdefghi"}, + {0x3981703a, "abcdefghij"}, + {0x6b9cdfe7, "Discard medicine more than two years old."}, + {0xc90ef73f, "He who has a shady past knows that nice guys finish last."}, + {0xb902341f, "I wouldn't marry him with a ten foot pole."}, + {0x42080e8, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, + {0x154c6d11, "The days of the digital watch are numbered. -Tom Stoppard"}, + {0x4c418325, "Nepal premier won't resign."}, + {0x33955150, "For every action there is an equal and opposite government program."}, + {0x26216a4b, "His money is twice tainted: 'taint yours and 'taint mine."}, + {0x1abbe45e, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, + {0xc89a94f7, "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, + {0xab3abe14, "size: a.out: bad magic"}, + {0xbab102b6, "The major problem is with sendmail. -Mark Horton"}, + {0x999149d7, "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, + {0x6d52a33c, "If the enemy is within range, then so are you."}, + {0x90631e8d, "It's well we cannot hear the screams/That we create in others' dreams."}, + {0x78309130, "You remind me of a TV show, but that's all right: I watch it anyway."}, + {0x7d0a377f, "C is as portable as Stonehedge!!"}, + {0x8c79fd79, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, + {0xa20b7167, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, + {0x8e0bb443, "How can you write a big system without C++? -Paul Glick"}, } func TestGolden(t *testing.T) { diff --git a/src/pkg/hash/crc64/Makefile b/src/pkg/hash/crc64/Makefile index 01b755b3e..5f6c3de69 100644 --- a/src/pkg/hash/crc64/Makefile +++ b/src/pkg/hash/crc64/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=hash/crc64 GOFILES=\ diff --git a/src/pkg/hash/crc64/crc64.go b/src/pkg/hash/crc64/crc64.go index 89e431977..844386564 100644 --- a/src/pkg/hash/crc64/crc64.go +++ b/src/pkg/hash/crc64/crc64.go @@ -80,7 +80,7 @@ func (d *digest) Sum64() uint64 { return d.crc } func (d *digest) Sum() []byte { p := make([]byte, 8) s := d.Sum64() - p[0] = byte(s >> 54) + p[0] = byte(s >> 56) p[1] = byte(s >> 48) p[2] = byte(s >> 40) p[3] = byte(s >> 32) diff --git a/src/pkg/hash/crc64/crc64_test.go b/src/pkg/hash/crc64/crc64_test.go index 664e1aeb3..e932524e0 100644 --- a/src/pkg/hash/crc64/crc64_test.go +++ b/src/pkg/hash/crc64/crc64_test.go @@ -15,37 +15,37 @@ type test struct { } var golden = []test{ - test{0x0, ""}, - test{0x3420000000000000, "a"}, - test{0x36c4200000000000, "ab"}, - test{0x3776c42000000000, "abc"}, - test{0x336776c420000000, "abcd"}, - test{0x32d36776c4200000, "abcde"}, - test{0x3002d36776c42000, "abcdef"}, - test{0x31b002d36776c420, "abcdefg"}, - test{0xe21b002d36776c4, "abcdefgh"}, - test{0x8b6e21b002d36776, "abcdefghi"}, - test{0x7f5b6e21b002d367, "abcdefghij"}, - test{0x8ec0e7c835bf9cdf, "Discard medicine more than two years old."}, - test{0xc7db1759e2be5ab4, "He who has a shady past knows that nice guys finish last."}, - test{0xfbf9d9603a6fa020, "I wouldn't marry him with a ten foot pole."}, - test{0xeafc4211a6daa0ef, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, - test{0x3e05b21c7a4dc4da, "The days of the digital watch are numbered. -Tom Stoppard"}, - test{0x5255866ad6ef28a6, "Nepal premier won't resign."}, - test{0x8a79895be1e9c361, "For every action there is an equal and opposite government program."}, - test{0x8878963a649d4916, "His money is twice tainted: 'taint yours and 'taint mine."}, - test{0xa7b9d53ea87eb82f, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, - test{0xdb6805c0966a2f9c, "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, - test{0xf3553c65dacdadd2, "size: a.out: bad magic"}, - test{0x9d5e034087a676b9, "The major problem is with sendmail. -Mark Horton"}, - test{0xa6db2d7f8da96417, "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, - test{0x325e00cd2fe819f9, "If the enemy is within range, then so are you."}, - test{0x88c6600ce58ae4c6, "It's well we cannot hear the screams/That we create in others' dreams."}, - test{0x28c4a3f3b769e078, "You remind me of a TV show, but that's all right: I watch it anyway."}, - test{0xa698a34c9d9f1dca, "C is as portable as Stonehedge!!"}, - test{0xf6c1e2a8c26c5cfc, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, - test{0xd402559dfe9b70c, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, - test{0xdb6efff26aa94946, "How can you write a big system without C++? -Paul Glick"}, + {0x0, ""}, + {0x3420000000000000, "a"}, + {0x36c4200000000000, "ab"}, + {0x3776c42000000000, "abc"}, + {0x336776c420000000, "abcd"}, + {0x32d36776c4200000, "abcde"}, + {0x3002d36776c42000, "abcdef"}, + {0x31b002d36776c420, "abcdefg"}, + {0xe21b002d36776c4, "abcdefgh"}, + {0x8b6e21b002d36776, "abcdefghi"}, + {0x7f5b6e21b002d367, "abcdefghij"}, + {0x8ec0e7c835bf9cdf, "Discard medicine more than two years old."}, + {0xc7db1759e2be5ab4, "He who has a shady past knows that nice guys finish last."}, + {0xfbf9d9603a6fa020, "I wouldn't marry him with a ten foot pole."}, + {0xeafc4211a6daa0ef, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, + {0x3e05b21c7a4dc4da, "The days of the digital watch are numbered. -Tom Stoppard"}, + {0x5255866ad6ef28a6, "Nepal premier won't resign."}, + {0x8a79895be1e9c361, "For every action there is an equal and opposite government program."}, + {0x8878963a649d4916, "His money is twice tainted: 'taint yours and 'taint mine."}, + {0xa7b9d53ea87eb82f, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, + {0xdb6805c0966a2f9c, "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, + {0xf3553c65dacdadd2, "size: a.out: bad magic"}, + {0x9d5e034087a676b9, "The major problem is with sendmail. -Mark Horton"}, + {0xa6db2d7f8da96417, "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, + {0x325e00cd2fe819f9, "If the enemy is within range, then so are you."}, + {0x88c6600ce58ae4c6, "It's well we cannot hear the screams/That we create in others' dreams."}, + {0x28c4a3f3b769e078, "You remind me of a TV show, but that's all right: I watch it anyway."}, + {0xa698a34c9d9f1dca, "C is as portable as Stonehedge!!"}, + {0xf6c1e2a8c26c5cfc, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, + {0xd402559dfe9b70c, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, + {0xdb6efff26aa94946, "How can you write a big system without C++? -Paul Glick"}, } var tab = MakeTable(ISO) diff --git a/src/pkg/html/Makefile b/src/pkg/html/Makefile new file mode 100644 index 000000000..00e1c0550 --- /dev/null +++ b/src/pkg/html/Makefile @@ -0,0 +1,15 @@ +# Copyright 2010 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../Make.inc + +TARG=html +GOFILES=\ + doc.go\ + entity.go\ + escape.go\ + parse.go\ + token.go\ + +include ../../Make.pkg diff --git a/src/pkg/html/doc.go b/src/pkg/html/doc.go new file mode 100644 index 000000000..c5338d078 --- /dev/null +++ b/src/pkg/html/doc.go @@ -0,0 +1,106 @@ +// 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 html package implements an HTML5-compliant tokenizer and parser. + +Tokenization is done by creating a Tokenizer for an io.Reader r. It is the +caller's responsibility to ensure that r provides UTF-8 encoded HTML. + + z := html.NewTokenizer(r) + +Given a Tokenizer z, the HTML is tokenized by repeatedly calling z.Next(), +which parses the next token and returns its type, or an error: + + for { + tt := z.Next() + if tt == html.ErrorToken { + // ... + return ... + } + // Process the current token. + } + +There are two APIs for retrieving the current token. The high-level API is to +call Token; the low-level API is to call Text or TagName / TagAttr. Both APIs +allow optionally calling Raw after Next but before Token, Text, TagName, or +TagAttr. In EBNF notation, the valid call sequence per token is: + + Next {Raw} [ Token | Text | TagName {TagAttr} ] + +Token returns an independent data structure that completely describes a token. +Entities (such as "<") are unescaped, tag names and attribute keys are +lower-cased, and attributes are collected into a []Attribute. For example: + + for { + if z.Next() == html.ErrorToken { + // Returning os.EOF indicates success. + return z.Error() + } + emitToken(z.Token()) + } + +The low-level API performs fewer allocations and copies, but the contents of +the []byte values returned by Text, TagName and TagAttr may change on the next +call to Next. For example, to extract an HTML page's anchor text: + + depth := 0 + for { + tt := z.Next() + switch tt { + case ErrorToken: + return z.Error() + case TextToken: + if depth > 0 { + // emitBytes should copy the []byte it receives, + // if it doesn't process it immediately. + emitBytes(z.Text()) + } + case StartTagToken, EndTagToken: + tn, _ := z.TagName() + if len(tn) == 1 && tn[0] == 'a' { + if tt == StartTag { + depth++ + } else { + depth-- + } + } + } + } + +Parsing is done by calling Parse with an io.Reader, which returns the root of +the parse tree (the document element) as a *Node. It is the caller's +responsibility to ensure that the Reader provides UTF-8 encoded HTML. For +example, to process each anchor node in depth-first order: + + doc, err := html.Parse(r) + if err != nil { + // ... + } + var f func(*html.Node) + f = func(n *html.Node) { + if n.Type == html.ElementNode && n.Data == "a" { + // Do something with n... + } + for _, c := range n.Child { + f(c) + } + } + f(doc) + +The relevant specifications include: +http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html and +http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html +*/ +package html + +// The tokenization algorithm implemented by this package is not a line-by-line +// transliteration of the relatively verbose state-machine in the WHATWG +// specification. A more direct approach is used instead, where the program +// counter implies the state, such as whether it is tokenizing a tag or a text +// node. Specification compliance is verified by checking expected and actual +// outputs over a test suite rather than aiming for algorithmic fidelity. + +// TODO(nigeltao): Does a DOM API belong in this package or a separate one? +// TODO(nigeltao): How does parsing interact with a JavaScript engine? diff --git a/src/pkg/html/entity.go b/src/pkg/html/entity.go new file mode 100644 index 000000000..1530290cb --- /dev/null +++ b/src/pkg/html/entity.go @@ -0,0 +1,2250 @@ +// 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 html + +// entity is a map from HTML entity names to their values. The semicolon matters: +// http://www.whatwg.org/specs/web-apps/current-work/multipage/named-character-references.html +// lists both "amp" and "amp;" as two separate entries. +// +// Note that the HTML5 list is larger than the HTML4 list at +// http://www.w3.org/TR/html4/sgml/entities.html +var entity = map[string]int{ + "AElig;": '\U000000C6', + "AMP;": '\U00000026', + "Aacute;": '\U000000C1', + "Abreve;": '\U00000102', + "Acirc;": '\U000000C2', + "Acy;": '\U00000410', + "Afr;": '\U0001D504', + "Agrave;": '\U000000C0', + "Alpha;": '\U00000391', + "Amacr;": '\U00000100', + "And;": '\U00002A53', + "Aogon;": '\U00000104', + "Aopf;": '\U0001D538', + "ApplyFunction;": '\U00002061', + "Aring;": '\U000000C5', + "Ascr;": '\U0001D49C', + "Assign;": '\U00002254', + "Atilde;": '\U000000C3', + "Auml;": '\U000000C4', + "Backslash;": '\U00002216', + "Barv;": '\U00002AE7', + "Barwed;": '\U00002306', + "Bcy;": '\U00000411', + "Because;": '\U00002235', + "Bernoullis;": '\U0000212C', + "Beta;": '\U00000392', + "Bfr;": '\U0001D505', + "Bopf;": '\U0001D539', + "Breve;": '\U000002D8', + "Bscr;": '\U0000212C', + "Bumpeq;": '\U0000224E', + "CHcy;": '\U00000427', + "COPY;": '\U000000A9', + "Cacute;": '\U00000106', + "Cap;": '\U000022D2', + "CapitalDifferentialD;": '\U00002145', + "Cayleys;": '\U0000212D', + "Ccaron;": '\U0000010C', + "Ccedil;": '\U000000C7', + "Ccirc;": '\U00000108', + "Cconint;": '\U00002230', + "Cdot;": '\U0000010A', + "Cedilla;": '\U000000B8', + "CenterDot;": '\U000000B7', + "Cfr;": '\U0000212D', + "Chi;": '\U000003A7', + "CircleDot;": '\U00002299', + "CircleMinus;": '\U00002296', + "CirclePlus;": '\U00002295', + "CircleTimes;": '\U00002297', + "ClockwiseContourIntegral;": '\U00002232', + "CloseCurlyDoubleQuote;": '\U0000201D', + "CloseCurlyQuote;": '\U00002019', + "Colon;": '\U00002237', + "Colone;": '\U00002A74', + "Congruent;": '\U00002261', + "Conint;": '\U0000222F', + "ContourIntegral;": '\U0000222E', + "Copf;": '\U00002102', + "Coproduct;": '\U00002210', + "CounterClockwiseContourIntegral;": '\U00002233', + "Cross;": '\U00002A2F', + "Cscr;": '\U0001D49E', + "Cup;": '\U000022D3', + "CupCap;": '\U0000224D', + "DD;": '\U00002145', + "DDotrahd;": '\U00002911', + "DJcy;": '\U00000402', + "DScy;": '\U00000405', + "DZcy;": '\U0000040F', + "Dagger;": '\U00002021', + "Darr;": '\U000021A1', + "Dashv;": '\U00002AE4', + "Dcaron;": '\U0000010E', + "Dcy;": '\U00000414', + "Del;": '\U00002207', + "Delta;": '\U00000394', + "Dfr;": '\U0001D507', + "DiacriticalAcute;": '\U000000B4', + "DiacriticalDot;": '\U000002D9', + "DiacriticalDoubleAcute;": '\U000002DD', + "DiacriticalGrave;": '\U00000060', + "DiacriticalTilde;": '\U000002DC', + "Diamond;": '\U000022C4', + "DifferentialD;": '\U00002146', + "Dopf;": '\U0001D53B', + "Dot;": '\U000000A8', + "DotDot;": '\U000020DC', + "DotEqual;": '\U00002250', + "DoubleContourIntegral;": '\U0000222F', + "DoubleDot;": '\U000000A8', + "DoubleDownArrow;": '\U000021D3', + "DoubleLeftArrow;": '\U000021D0', + "DoubleLeftRightArrow;": '\U000021D4', + "DoubleLeftTee;": '\U00002AE4', + "DoubleLongLeftArrow;": '\U000027F8', + "DoubleLongLeftRightArrow;": '\U000027FA', + "DoubleLongRightArrow;": '\U000027F9', + "DoubleRightArrow;": '\U000021D2', + "DoubleRightTee;": '\U000022A8', + "DoubleUpArrow;": '\U000021D1', + "DoubleUpDownArrow;": '\U000021D5', + "DoubleVerticalBar;": '\U00002225', + "DownArrow;": '\U00002193', + "DownArrowBar;": '\U00002913', + "DownArrowUpArrow;": '\U000021F5', + "DownBreve;": '\U00000311', + "DownLeftRightVector;": '\U00002950', + "DownLeftTeeVector;": '\U0000295E', + "DownLeftVector;": '\U000021BD', + "DownLeftVectorBar;": '\U00002956', + "DownRightTeeVector;": '\U0000295F', + "DownRightVector;": '\U000021C1', + "DownRightVectorBar;": '\U00002957', + "DownTee;": '\U000022A4', + "DownTeeArrow;": '\U000021A7', + "Downarrow;": '\U000021D3', + "Dscr;": '\U0001D49F', + "Dstrok;": '\U00000110', + "ENG;": '\U0000014A', + "ETH;": '\U000000D0', + "Eacute;": '\U000000C9', + "Ecaron;": '\U0000011A', + "Ecirc;": '\U000000CA', + "Ecy;": '\U0000042D', + "Edot;": '\U00000116', + "Efr;": '\U0001D508', + "Egrave;": '\U000000C8', + "Element;": '\U00002208', + "Emacr;": '\U00000112', + "EmptySmallSquare;": '\U000025FB', + "EmptyVerySmallSquare;": '\U000025AB', + "Eogon;": '\U00000118', + "Eopf;": '\U0001D53C', + "Epsilon;": '\U00000395', + "Equal;": '\U00002A75', + "EqualTilde;": '\U00002242', + "Equilibrium;": '\U000021CC', + "Escr;": '\U00002130', + "Esim;": '\U00002A73', + "Eta;": '\U00000397', + "Euml;": '\U000000CB', + "Exists;": '\U00002203', + "ExponentialE;": '\U00002147', + "Fcy;": '\U00000424', + "Ffr;": '\U0001D509', + "FilledSmallSquare;": '\U000025FC', + "FilledVerySmallSquare;": '\U000025AA', + "Fopf;": '\U0001D53D', + "ForAll;": '\U00002200', + "Fouriertrf;": '\U00002131', + "Fscr;": '\U00002131', + "GJcy;": '\U00000403', + "GT;": '\U0000003E', + "Gamma;": '\U00000393', + "Gammad;": '\U000003DC', + "Gbreve;": '\U0000011E', + "Gcedil;": '\U00000122', + "Gcirc;": '\U0000011C', + "Gcy;": '\U00000413', + "Gdot;": '\U00000120', + "Gfr;": '\U0001D50A', + "Gg;": '\U000022D9', + "Gopf;": '\U0001D53E', + "GreaterEqual;": '\U00002265', + "GreaterEqualLess;": '\U000022DB', + "GreaterFullEqual;": '\U00002267', + "GreaterGreater;": '\U00002AA2', + "GreaterLess;": '\U00002277', + "GreaterSlantEqual;": '\U00002A7E', + "GreaterTilde;": '\U00002273', + "Gscr;": '\U0001D4A2', + "Gt;": '\U0000226B', + "HARDcy;": '\U0000042A', + "Hacek;": '\U000002C7', + "Hat;": '\U0000005E', + "Hcirc;": '\U00000124', + "Hfr;": '\U0000210C', + "HilbertSpace;": '\U0000210B', + "Hopf;": '\U0000210D', + "HorizontalLine;": '\U00002500', + "Hscr;": '\U0000210B', + "Hstrok;": '\U00000126', + "HumpDownHump;": '\U0000224E', + "HumpEqual;": '\U0000224F', + "IEcy;": '\U00000415', + "IJlig;": '\U00000132', + "IOcy;": '\U00000401', + "Iacute;": '\U000000CD', + "Icirc;": '\U000000CE', + "Icy;": '\U00000418', + "Idot;": '\U00000130', + "Ifr;": '\U00002111', + "Igrave;": '\U000000CC', + "Im;": '\U00002111', + "Imacr;": '\U0000012A', + "ImaginaryI;": '\U00002148', + "Implies;": '\U000021D2', + "Int;": '\U0000222C', + "Integral;": '\U0000222B', + "Intersection;": '\U000022C2', + "InvisibleComma;": '\U00002063', + "InvisibleTimes;": '\U00002062', + "Iogon;": '\U0000012E', + "Iopf;": '\U0001D540', + "Iota;": '\U00000399', + "Iscr;": '\U00002110', + "Itilde;": '\U00000128', + "Iukcy;": '\U00000406', + "Iuml;": '\U000000CF', + "Jcirc;": '\U00000134', + "Jcy;": '\U00000419', + "Jfr;": '\U0001D50D', + "Jopf;": '\U0001D541', + "Jscr;": '\U0001D4A5', + "Jsercy;": '\U00000408', + "Jukcy;": '\U00000404', + "KHcy;": '\U00000425', + "KJcy;": '\U0000040C', + "Kappa;": '\U0000039A', + "Kcedil;": '\U00000136', + "Kcy;": '\U0000041A', + "Kfr;": '\U0001D50E', + "Kopf;": '\U0001D542', + "Kscr;": '\U0001D4A6', + "LJcy;": '\U00000409', + "LT;": '\U0000003C', + "Lacute;": '\U00000139', + "Lambda;": '\U0000039B', + "Lang;": '\U000027EA', + "Laplacetrf;": '\U00002112', + "Larr;": '\U0000219E', + "Lcaron;": '\U0000013D', + "Lcedil;": '\U0000013B', + "Lcy;": '\U0000041B', + "LeftAngleBracket;": '\U000027E8', + "LeftArrow;": '\U00002190', + "LeftArrowBar;": '\U000021E4', + "LeftArrowRightArrow;": '\U000021C6', + "LeftCeiling;": '\U00002308', + "LeftDoubleBracket;": '\U000027E6', + "LeftDownTeeVector;": '\U00002961', + "LeftDownVector;": '\U000021C3', + "LeftDownVectorBar;": '\U00002959', + "LeftFloor;": '\U0000230A', + "LeftRightArrow;": '\U00002194', + "LeftRightVector;": '\U0000294E', + "LeftTee;": '\U000022A3', + "LeftTeeArrow;": '\U000021A4', + "LeftTeeVector;": '\U0000295A', + "LeftTriangle;": '\U000022B2', + "LeftTriangleBar;": '\U000029CF', + "LeftTriangleEqual;": '\U000022B4', + "LeftUpDownVector;": '\U00002951', + "LeftUpTeeVector;": '\U00002960', + "LeftUpVector;": '\U000021BF', + "LeftUpVectorBar;": '\U00002958', + "LeftVector;": '\U000021BC', + "LeftVectorBar;": '\U00002952', + "Leftarrow;": '\U000021D0', + "Leftrightarrow;": '\U000021D4', + "LessEqualGreater;": '\U000022DA', + "LessFullEqual;": '\U00002266', + "LessGreater;": '\U00002276', + "LessLess;": '\U00002AA1', + "LessSlantEqual;": '\U00002A7D', + "LessTilde;": '\U00002272', + "Lfr;": '\U0001D50F', + "Ll;": '\U000022D8', + "Lleftarrow;": '\U000021DA', + "Lmidot;": '\U0000013F', + "LongLeftArrow;": '\U000027F5', + "LongLeftRightArrow;": '\U000027F7', + "LongRightArrow;": '\U000027F6', + "Longleftarrow;": '\U000027F8', + "Longleftrightarrow;": '\U000027FA', + "Longrightarrow;": '\U000027F9', + "Lopf;": '\U0001D543', + "LowerLeftArrow;": '\U00002199', + "LowerRightArrow;": '\U00002198', + "Lscr;": '\U00002112', + "Lsh;": '\U000021B0', + "Lstrok;": '\U00000141', + "Lt;": '\U0000226A', + "Map;": '\U00002905', + "Mcy;": '\U0000041C', + "MediumSpace;": '\U0000205F', + "Mellintrf;": '\U00002133', + "Mfr;": '\U0001D510', + "MinusPlus;": '\U00002213', + "Mopf;": '\U0001D544', + "Mscr;": '\U00002133', + "Mu;": '\U0000039C', + "NJcy;": '\U0000040A', + "Nacute;": '\U00000143', + "Ncaron;": '\U00000147', + "Ncedil;": '\U00000145', + "Ncy;": '\U0000041D', + "NegativeMediumSpace;": '\U0000200B', + "NegativeThickSpace;": '\U0000200B', + "NegativeThinSpace;": '\U0000200B', + "NegativeVeryThinSpace;": '\U0000200B', + "NestedGreaterGreater;": '\U0000226B', + "NestedLessLess;": '\U0000226A', + "NewLine;": '\U0000000A', + "Nfr;": '\U0001D511', + "NoBreak;": '\U00002060', + "NonBreakingSpace;": '\U000000A0', + "Nopf;": '\U00002115', + "Not;": '\U00002AEC', + "NotCongruent;": '\U00002262', + "NotCupCap;": '\U0000226D', + "NotDoubleVerticalBar;": '\U00002226', + "NotElement;": '\U00002209', + "NotEqual;": '\U00002260', + "NotExists;": '\U00002204', + "NotGreater;": '\U0000226F', + "NotGreaterEqual;": '\U00002271', + "NotGreaterLess;": '\U00002279', + "NotGreaterTilde;": '\U00002275', + "NotLeftTriangle;": '\U000022EA', + "NotLeftTriangleEqual;": '\U000022EC', + "NotLess;": '\U0000226E', + "NotLessEqual;": '\U00002270', + "NotLessGreater;": '\U00002278', + "NotLessTilde;": '\U00002274', + "NotPrecedes;": '\U00002280', + "NotPrecedesSlantEqual;": '\U000022E0', + "NotReverseElement;": '\U0000220C', + "NotRightTriangle;": '\U000022EB', + "NotRightTriangleEqual;": '\U000022ED', + "NotSquareSubsetEqual;": '\U000022E2', + "NotSquareSupersetEqual;": '\U000022E3', + "NotSubsetEqual;": '\U00002288', + "NotSucceeds;": '\U00002281', + "NotSucceedsSlantEqual;": '\U000022E1', + "NotSupersetEqual;": '\U00002289', + "NotTilde;": '\U00002241', + "NotTildeEqual;": '\U00002244', + "NotTildeFullEqual;": '\U00002247', + "NotTildeTilde;": '\U00002249', + "NotVerticalBar;": '\U00002224', + "Nscr;": '\U0001D4A9', + "Ntilde;": '\U000000D1', + "Nu;": '\U0000039D', + "OElig;": '\U00000152', + "Oacute;": '\U000000D3', + "Ocirc;": '\U000000D4', + "Ocy;": '\U0000041E', + "Odblac;": '\U00000150', + "Ofr;": '\U0001D512', + "Ograve;": '\U000000D2', + "Omacr;": '\U0000014C', + "Omega;": '\U000003A9', + "Omicron;": '\U0000039F', + "Oopf;": '\U0001D546', + "OpenCurlyDoubleQuote;": '\U0000201C', + "OpenCurlyQuote;": '\U00002018', + "Or;": '\U00002A54', + "Oscr;": '\U0001D4AA', + "Oslash;": '\U000000D8', + "Otilde;": '\U000000D5', + "Otimes;": '\U00002A37', + "Ouml;": '\U000000D6', + "OverBar;": '\U0000203E', + "OverBrace;": '\U000023DE', + "OverBracket;": '\U000023B4', + "OverParenthesis;": '\U000023DC', + "PartialD;": '\U00002202', + "Pcy;": '\U0000041F', + "Pfr;": '\U0001D513', + "Phi;": '\U000003A6', + "Pi;": '\U000003A0', + "PlusMinus;": '\U000000B1', + "Poincareplane;": '\U0000210C', + "Popf;": '\U00002119', + "Pr;": '\U00002ABB', + "Precedes;": '\U0000227A', + "PrecedesEqual;": '\U00002AAF', + "PrecedesSlantEqual;": '\U0000227C', + "PrecedesTilde;": '\U0000227E', + "Prime;": '\U00002033', + "Product;": '\U0000220F', + "Proportion;": '\U00002237', + "Proportional;": '\U0000221D', + "Pscr;": '\U0001D4AB', + "Psi;": '\U000003A8', + "QUOT;": '\U00000022', + "Qfr;": '\U0001D514', + "Qopf;": '\U0000211A', + "Qscr;": '\U0001D4AC', + "RBarr;": '\U00002910', + "REG;": '\U000000AE', + "Racute;": '\U00000154', + "Rang;": '\U000027EB', + "Rarr;": '\U000021A0', + "Rarrtl;": '\U00002916', + "Rcaron;": '\U00000158', + "Rcedil;": '\U00000156', + "Rcy;": '\U00000420', + "Re;": '\U0000211C', + "ReverseElement;": '\U0000220B', + "ReverseEquilibrium;": '\U000021CB', + "ReverseUpEquilibrium;": '\U0000296F', + "Rfr;": '\U0000211C', + "Rho;": '\U000003A1', + "RightAngleBracket;": '\U000027E9', + "RightArrow;": '\U00002192', + "RightArrowBar;": '\U000021E5', + "RightArrowLeftArrow;": '\U000021C4', + "RightCeiling;": '\U00002309', + "RightDoubleBracket;": '\U000027E7', + "RightDownTeeVector;": '\U0000295D', + "RightDownVector;": '\U000021C2', + "RightDownVectorBar;": '\U00002955', + "RightFloor;": '\U0000230B', + "RightTee;": '\U000022A2', + "RightTeeArrow;": '\U000021A6', + "RightTeeVector;": '\U0000295B', + "RightTriangle;": '\U000022B3', + "RightTriangleBar;": '\U000029D0', + "RightTriangleEqual;": '\U000022B5', + "RightUpDownVector;": '\U0000294F', + "RightUpTeeVector;": '\U0000295C', + "RightUpVector;": '\U000021BE', + "RightUpVectorBar;": '\U00002954', + "RightVector;": '\U000021C0', + "RightVectorBar;": '\U00002953', + "Rightarrow;": '\U000021D2', + "Ropf;": '\U0000211D', + "RoundImplies;": '\U00002970', + "Rrightarrow;": '\U000021DB', + "Rscr;": '\U0000211B', + "Rsh;": '\U000021B1', + "RuleDelayed;": '\U000029F4', + "SHCHcy;": '\U00000429', + "SHcy;": '\U00000428', + "SOFTcy;": '\U0000042C', + "Sacute;": '\U0000015A', + "Sc;": '\U00002ABC', + "Scaron;": '\U00000160', + "Scedil;": '\U0000015E', + "Scirc;": '\U0000015C', + "Scy;": '\U00000421', + "Sfr;": '\U0001D516', + "ShortDownArrow;": '\U00002193', + "ShortLeftArrow;": '\U00002190', + "ShortRightArrow;": '\U00002192', + "ShortUpArrow;": '\U00002191', + "Sigma;": '\U000003A3', + "SmallCircle;": '\U00002218', + "Sopf;": '\U0001D54A', + "Sqrt;": '\U0000221A', + "Square;": '\U000025A1', + "SquareIntersection;": '\U00002293', + "SquareSubset;": '\U0000228F', + "SquareSubsetEqual;": '\U00002291', + "SquareSuperset;": '\U00002290', + "SquareSupersetEqual;": '\U00002292', + "SquareUnion;": '\U00002294', + "Sscr;": '\U0001D4AE', + "Star;": '\U000022C6', + "Sub;": '\U000022D0', + "Subset;": '\U000022D0', + "SubsetEqual;": '\U00002286', + "Succeeds;": '\U0000227B', + "SucceedsEqual;": '\U00002AB0', + "SucceedsSlantEqual;": '\U0000227D', + "SucceedsTilde;": '\U0000227F', + "SuchThat;": '\U0000220B', + "Sum;": '\U00002211', + "Sup;": '\U000022D1', + "Superset;": '\U00002283', + "SupersetEqual;": '\U00002287', + "Supset;": '\U000022D1', + "THORN;": '\U000000DE', + "TRADE;": '\U00002122', + "TSHcy;": '\U0000040B', + "TScy;": '\U00000426', + "Tab;": '\U00000009', + "Tau;": '\U000003A4', + "Tcaron;": '\U00000164', + "Tcedil;": '\U00000162', + "Tcy;": '\U00000422', + "Tfr;": '\U0001D517', + "Therefore;": '\U00002234', + "Theta;": '\U00000398', + "ThinSpace;": '\U00002009', + "Tilde;": '\U0000223C', + "TildeEqual;": '\U00002243', + "TildeFullEqual;": '\U00002245', + "TildeTilde;": '\U00002248', + "Topf;": '\U0001D54B', + "TripleDot;": '\U000020DB', + "Tscr;": '\U0001D4AF', + "Tstrok;": '\U00000166', + "Uacute;": '\U000000DA', + "Uarr;": '\U0000219F', + "Uarrocir;": '\U00002949', + "Ubrcy;": '\U0000040E', + "Ubreve;": '\U0000016C', + "Ucirc;": '\U000000DB', + "Ucy;": '\U00000423', + "Udblac;": '\U00000170', + "Ufr;": '\U0001D518', + "Ugrave;": '\U000000D9', + "Umacr;": '\U0000016A', + "UnderBar;": '\U0000005F', + "UnderBrace;": '\U000023DF', + "UnderBracket;": '\U000023B5', + "UnderParenthesis;": '\U000023DD', + "Union;": '\U000022C3', + "UnionPlus;": '\U0000228E', + "Uogon;": '\U00000172', + "Uopf;": '\U0001D54C', + "UpArrow;": '\U00002191', + "UpArrowBar;": '\U00002912', + "UpArrowDownArrow;": '\U000021C5', + "UpDownArrow;": '\U00002195', + "UpEquilibrium;": '\U0000296E', + "UpTee;": '\U000022A5', + "UpTeeArrow;": '\U000021A5', + "Uparrow;": '\U000021D1', + "Updownarrow;": '\U000021D5', + "UpperLeftArrow;": '\U00002196', + "UpperRightArrow;": '\U00002197', + "Upsi;": '\U000003D2', + "Upsilon;": '\U000003A5', + "Uring;": '\U0000016E', + "Uscr;": '\U0001D4B0', + "Utilde;": '\U00000168', + "Uuml;": '\U000000DC', + "VDash;": '\U000022AB', + "Vbar;": '\U00002AEB', + "Vcy;": '\U00000412', + "Vdash;": '\U000022A9', + "Vdashl;": '\U00002AE6', + "Vee;": '\U000022C1', + "Verbar;": '\U00002016', + "Vert;": '\U00002016', + "VerticalBar;": '\U00002223', + "VerticalLine;": '\U0000007C', + "VerticalSeparator;": '\U00002758', + "VerticalTilde;": '\U00002240', + "VeryThinSpace;": '\U0000200A', + "Vfr;": '\U0001D519', + "Vopf;": '\U0001D54D', + "Vscr;": '\U0001D4B1', + "Vvdash;": '\U000022AA', + "Wcirc;": '\U00000174', + "Wedge;": '\U000022C0', + "Wfr;": '\U0001D51A', + "Wopf;": '\U0001D54E', + "Wscr;": '\U0001D4B2', + "Xfr;": '\U0001D51B', + "Xi;": '\U0000039E', + "Xopf;": '\U0001D54F', + "Xscr;": '\U0001D4B3', + "YAcy;": '\U0000042F', + "YIcy;": '\U00000407', + "YUcy;": '\U0000042E', + "Yacute;": '\U000000DD', + "Ycirc;": '\U00000176', + "Ycy;": '\U0000042B', + "Yfr;": '\U0001D51C', + "Yopf;": '\U0001D550', + "Yscr;": '\U0001D4B4', + "Yuml;": '\U00000178', + "ZHcy;": '\U00000416', + "Zacute;": '\U00000179', + "Zcaron;": '\U0000017D', + "Zcy;": '\U00000417', + "Zdot;": '\U0000017B', + "ZeroWidthSpace;": '\U0000200B', + "Zeta;": '\U00000396', + "Zfr;": '\U00002128', + "Zopf;": '\U00002124', + "Zscr;": '\U0001D4B5', + "aacute;": '\U000000E1', + "abreve;": '\U00000103', + "ac;": '\U0000223E', + "acd;": '\U0000223F', + "acirc;": '\U000000E2', + "acute;": '\U000000B4', + "acy;": '\U00000430', + "aelig;": '\U000000E6', + "af;": '\U00002061', + "afr;": '\U0001D51E', + "agrave;": '\U000000E0', + "alefsym;": '\U00002135', + "aleph;": '\U00002135', + "alpha;": '\U000003B1', + "amacr;": '\U00000101', + "amalg;": '\U00002A3F', + "amp;": '\U00000026', + "and;": '\U00002227', + "andand;": '\U00002A55', + "andd;": '\U00002A5C', + "andslope;": '\U00002A58', + "andv;": '\U00002A5A', + "ang;": '\U00002220', + "ange;": '\U000029A4', + "angle;": '\U00002220', + "angmsd;": '\U00002221', + "angmsdaa;": '\U000029A8', + "angmsdab;": '\U000029A9', + "angmsdac;": '\U000029AA', + "angmsdad;": '\U000029AB', + "angmsdae;": '\U000029AC', + "angmsdaf;": '\U000029AD', + "angmsdag;": '\U000029AE', + "angmsdah;": '\U000029AF', + "angrt;": '\U0000221F', + "angrtvb;": '\U000022BE', + "angrtvbd;": '\U0000299D', + "angsph;": '\U00002222', + "angst;": '\U000000C5', + "angzarr;": '\U0000237C', + "aogon;": '\U00000105', + "aopf;": '\U0001D552', + "ap;": '\U00002248', + "apE;": '\U00002A70', + "apacir;": '\U00002A6F', + "ape;": '\U0000224A', + "apid;": '\U0000224B', + "apos;": '\U00000027', + "approx;": '\U00002248', + "approxeq;": '\U0000224A', + "aring;": '\U000000E5', + "ascr;": '\U0001D4B6', + "ast;": '\U0000002A', + "asymp;": '\U00002248', + "asympeq;": '\U0000224D', + "atilde;": '\U000000E3', + "auml;": '\U000000E4', + "awconint;": '\U00002233', + "awint;": '\U00002A11', + "bNot;": '\U00002AED', + "backcong;": '\U0000224C', + "backepsilon;": '\U000003F6', + "backprime;": '\U00002035', + "backsim;": '\U0000223D', + "backsimeq;": '\U000022CD', + "barvee;": '\U000022BD', + "barwed;": '\U00002305', + "barwedge;": '\U00002305', + "bbrk;": '\U000023B5', + "bbrktbrk;": '\U000023B6', + "bcong;": '\U0000224C', + "bcy;": '\U00000431', + "bdquo;": '\U0000201E', + "becaus;": '\U00002235', + "because;": '\U00002235', + "bemptyv;": '\U000029B0', + "bepsi;": '\U000003F6', + "bernou;": '\U0000212C', + "beta;": '\U000003B2', + "beth;": '\U00002136', + "between;": '\U0000226C', + "bfr;": '\U0001D51F', + "bigcap;": '\U000022C2', + "bigcirc;": '\U000025EF', + "bigcup;": '\U000022C3', + "bigodot;": '\U00002A00', + "bigoplus;": '\U00002A01', + "bigotimes;": '\U00002A02', + "bigsqcup;": '\U00002A06', + "bigstar;": '\U00002605', + "bigtriangledown;": '\U000025BD', + "bigtriangleup;": '\U000025B3', + "biguplus;": '\U00002A04', + "bigvee;": '\U000022C1', + "bigwedge;": '\U000022C0', + "bkarow;": '\U0000290D', + "blacklozenge;": '\U000029EB', + "blacksquare;": '\U000025AA', + "blacktriangle;": '\U000025B4', + "blacktriangledown;": '\U000025BE', + "blacktriangleleft;": '\U000025C2', + "blacktriangleright;": '\U000025B8', + "blank;": '\U00002423', + "blk12;": '\U00002592', + "blk14;": '\U00002591', + "blk34;": '\U00002593', + "block;": '\U00002588', + "bnot;": '\U00002310', + "bopf;": '\U0001D553', + "bot;": '\U000022A5', + "bottom;": '\U000022A5', + "bowtie;": '\U000022C8', + "boxDL;": '\U00002557', + "boxDR;": '\U00002554', + "boxDl;": '\U00002556', + "boxDr;": '\U00002553', + "boxH;": '\U00002550', + "boxHD;": '\U00002566', + "boxHU;": '\U00002569', + "boxHd;": '\U00002564', + "boxHu;": '\U00002567', + "boxUL;": '\U0000255D', + "boxUR;": '\U0000255A', + "boxUl;": '\U0000255C', + "boxUr;": '\U00002559', + "boxV;": '\U00002551', + "boxVH;": '\U0000256C', + "boxVL;": '\U00002563', + "boxVR;": '\U00002560', + "boxVh;": '\U0000256B', + "boxVl;": '\U00002562', + "boxVr;": '\U0000255F', + "boxbox;": '\U000029C9', + "boxdL;": '\U00002555', + "boxdR;": '\U00002552', + "boxdl;": '\U00002510', + "boxdr;": '\U0000250C', + "boxh;": '\U00002500', + "boxhD;": '\U00002565', + "boxhU;": '\U00002568', + "boxhd;": '\U0000252C', + "boxhu;": '\U00002534', + "boxminus;": '\U0000229F', + "boxplus;": '\U0000229E', + "boxtimes;": '\U000022A0', + "boxuL;": '\U0000255B', + "boxuR;": '\U00002558', + "boxul;": '\U00002518', + "boxur;": '\U00002514', + "boxv;": '\U00002502', + "boxvH;": '\U0000256A', + "boxvL;": '\U00002561', + "boxvR;": '\U0000255E', + "boxvh;": '\U0000253C', + "boxvl;": '\U00002524', + "boxvr;": '\U0000251C', + "bprime;": '\U00002035', + "breve;": '\U000002D8', + "brvbar;": '\U000000A6', + "bscr;": '\U0001D4B7', + "bsemi;": '\U0000204F', + "bsim;": '\U0000223D', + "bsime;": '\U000022CD', + "bsol;": '\U0000005C', + "bsolb;": '\U000029C5', + "bsolhsub;": '\U000027C8', + "bull;": '\U00002022', + "bullet;": '\U00002022', + "bump;": '\U0000224E', + "bumpE;": '\U00002AAE', + "bumpe;": '\U0000224F', + "bumpeq;": '\U0000224F', + "cacute;": '\U00000107', + "cap;": '\U00002229', + "capand;": '\U00002A44', + "capbrcup;": '\U00002A49', + "capcap;": '\U00002A4B', + "capcup;": '\U00002A47', + "capdot;": '\U00002A40', + "caret;": '\U00002041', + "caron;": '\U000002C7', + "ccaps;": '\U00002A4D', + "ccaron;": '\U0000010D', + "ccedil;": '\U000000E7', + "ccirc;": '\U00000109', + "ccups;": '\U00002A4C', + "ccupssm;": '\U00002A50', + "cdot;": '\U0000010B', + "cedil;": '\U000000B8', + "cemptyv;": '\U000029B2', + "cent;": '\U000000A2', + "centerdot;": '\U000000B7', + "cfr;": '\U0001D520', + "chcy;": '\U00000447', + "check;": '\U00002713', + "checkmark;": '\U00002713', + "chi;": '\U000003C7', + "cir;": '\U000025CB', + "cirE;": '\U000029C3', + "circ;": '\U000002C6', + "circeq;": '\U00002257', + "circlearrowleft;": '\U000021BA', + "circlearrowright;": '\U000021BB', + "circledR;": '\U000000AE', + "circledS;": '\U000024C8', + "circledast;": '\U0000229B', + "circledcirc;": '\U0000229A', + "circleddash;": '\U0000229D', + "cire;": '\U00002257', + "cirfnint;": '\U00002A10', + "cirmid;": '\U00002AEF', + "cirscir;": '\U000029C2', + "clubs;": '\U00002663', + "clubsuit;": '\U00002663', + "colon;": '\U0000003A', + "colone;": '\U00002254', + "coloneq;": '\U00002254', + "comma;": '\U0000002C', + "commat;": '\U00000040', + "comp;": '\U00002201', + "compfn;": '\U00002218', + "complement;": '\U00002201', + "complexes;": '\U00002102', + "cong;": '\U00002245', + "congdot;": '\U00002A6D', + "conint;": '\U0000222E', + "copf;": '\U0001D554', + "coprod;": '\U00002210', + "copy;": '\U000000A9', + "copysr;": '\U00002117', + "crarr;": '\U000021B5', + "cross;": '\U00002717', + "cscr;": '\U0001D4B8', + "csub;": '\U00002ACF', + "csube;": '\U00002AD1', + "csup;": '\U00002AD0', + "csupe;": '\U00002AD2', + "ctdot;": '\U000022EF', + "cudarrl;": '\U00002938', + "cudarrr;": '\U00002935', + "cuepr;": '\U000022DE', + "cuesc;": '\U000022DF', + "cularr;": '\U000021B6', + "cularrp;": '\U0000293D', + "cup;": '\U0000222A', + "cupbrcap;": '\U00002A48', + "cupcap;": '\U00002A46', + "cupcup;": '\U00002A4A', + "cupdot;": '\U0000228D', + "cupor;": '\U00002A45', + "curarr;": '\U000021B7', + "curarrm;": '\U0000293C', + "curlyeqprec;": '\U000022DE', + "curlyeqsucc;": '\U000022DF', + "curlyvee;": '\U000022CE', + "curlywedge;": '\U000022CF', + "curren;": '\U000000A4', + "curvearrowleft;": '\U000021B6', + "curvearrowright;": '\U000021B7', + "cuvee;": '\U000022CE', + "cuwed;": '\U000022CF', + "cwconint;": '\U00002232', + "cwint;": '\U00002231', + "cylcty;": '\U0000232D', + "dArr;": '\U000021D3', + "dHar;": '\U00002965', + "dagger;": '\U00002020', + "daleth;": '\U00002138', + "darr;": '\U00002193', + "dash;": '\U00002010', + "dashv;": '\U000022A3', + "dbkarow;": '\U0000290F', + "dblac;": '\U000002DD', + "dcaron;": '\U0000010F', + "dcy;": '\U00000434', + "dd;": '\U00002146', + "ddagger;": '\U00002021', + "ddarr;": '\U000021CA', + "ddotseq;": '\U00002A77', + "deg;": '\U000000B0', + "delta;": '\U000003B4', + "demptyv;": '\U000029B1', + "dfisht;": '\U0000297F', + "dfr;": '\U0001D521', + "dharl;": '\U000021C3', + "dharr;": '\U000021C2', + "diam;": '\U000022C4', + "diamond;": '\U000022C4', + "diamondsuit;": '\U00002666', + "diams;": '\U00002666', + "die;": '\U000000A8', + "digamma;": '\U000003DD', + "disin;": '\U000022F2', + "div;": '\U000000F7', + "divide;": '\U000000F7', + "divideontimes;": '\U000022C7', + "divonx;": '\U000022C7', + "djcy;": '\U00000452', + "dlcorn;": '\U0000231E', + "dlcrop;": '\U0000230D', + "dollar;": '\U00000024', + "dopf;": '\U0001D555', + "dot;": '\U000002D9', + "doteq;": '\U00002250', + "doteqdot;": '\U00002251', + "dotminus;": '\U00002238', + "dotplus;": '\U00002214', + "dotsquare;": '\U000022A1', + "doublebarwedge;": '\U00002306', + "downarrow;": '\U00002193', + "downdownarrows;": '\U000021CA', + "downharpoonleft;": '\U000021C3', + "downharpoonright;": '\U000021C2', + "drbkarow;": '\U00002910', + "drcorn;": '\U0000231F', + "drcrop;": '\U0000230C', + "dscr;": '\U0001D4B9', + "dscy;": '\U00000455', + "dsol;": '\U000029F6', + "dstrok;": '\U00000111', + "dtdot;": '\U000022F1', + "dtri;": '\U000025BF', + "dtrif;": '\U000025BE', + "duarr;": '\U000021F5', + "duhar;": '\U0000296F', + "dwangle;": '\U000029A6', + "dzcy;": '\U0000045F', + "dzigrarr;": '\U000027FF', + "eDDot;": '\U00002A77', + "eDot;": '\U00002251', + "eacute;": '\U000000E9', + "easter;": '\U00002A6E', + "ecaron;": '\U0000011B', + "ecir;": '\U00002256', + "ecirc;": '\U000000EA', + "ecolon;": '\U00002255', + "ecy;": '\U0000044D', + "edot;": '\U00000117', + "ee;": '\U00002147', + "efDot;": '\U00002252', + "efr;": '\U0001D522', + "eg;": '\U00002A9A', + "egrave;": '\U000000E8', + "egs;": '\U00002A96', + "egsdot;": '\U00002A98', + "el;": '\U00002A99', + "elinters;": '\U000023E7', + "ell;": '\U00002113', + "els;": '\U00002A95', + "elsdot;": '\U00002A97', + "emacr;": '\U00000113', + "empty;": '\U00002205', + "emptyset;": '\U00002205', + "emptyv;": '\U00002205', + "emsp;": '\U00002003', + "emsp13;": '\U00002004', + "emsp14;": '\U00002005', + "eng;": '\U0000014B', + "ensp;": '\U00002002', + "eogon;": '\U00000119', + "eopf;": '\U0001D556', + "epar;": '\U000022D5', + "eparsl;": '\U000029E3', + "eplus;": '\U00002A71', + "epsi;": '\U000003B5', + "epsilon;": '\U000003B5', + "epsiv;": '\U000003F5', + "eqcirc;": '\U00002256', + "eqcolon;": '\U00002255', + "eqsim;": '\U00002242', + "eqslantgtr;": '\U00002A96', + "eqslantless;": '\U00002A95', + "equals;": '\U0000003D', + "equest;": '\U0000225F', + "equiv;": '\U00002261', + "equivDD;": '\U00002A78', + "eqvparsl;": '\U000029E5', + "erDot;": '\U00002253', + "erarr;": '\U00002971', + "escr;": '\U0000212F', + "esdot;": '\U00002250', + "esim;": '\U00002242', + "eta;": '\U000003B7', + "eth;": '\U000000F0', + "euml;": '\U000000EB', + "euro;": '\U000020AC', + "excl;": '\U00000021', + "exist;": '\U00002203', + "expectation;": '\U00002130', + "exponentiale;": '\U00002147', + "fallingdotseq;": '\U00002252', + "fcy;": '\U00000444', + "female;": '\U00002640', + "ffilig;": '\U0000FB03', + "fflig;": '\U0000FB00', + "ffllig;": '\U0000FB04', + "ffr;": '\U0001D523', + "filig;": '\U0000FB01', + "flat;": '\U0000266D', + "fllig;": '\U0000FB02', + "fltns;": '\U000025B1', + "fnof;": '\U00000192', + "fopf;": '\U0001D557', + "forall;": '\U00002200', + "fork;": '\U000022D4', + "forkv;": '\U00002AD9', + "fpartint;": '\U00002A0D', + "frac12;": '\U000000BD', + "frac13;": '\U00002153', + "frac14;": '\U000000BC', + "frac15;": '\U00002155', + "frac16;": '\U00002159', + "frac18;": '\U0000215B', + "frac23;": '\U00002154', + "frac25;": '\U00002156', + "frac34;": '\U000000BE', + "frac35;": '\U00002157', + "frac38;": '\U0000215C', + "frac45;": '\U00002158', + "frac56;": '\U0000215A', + "frac58;": '\U0000215D', + "frac78;": '\U0000215E', + "frasl;": '\U00002044', + "frown;": '\U00002322', + "fscr;": '\U0001D4BB', + "gE;": '\U00002267', + "gEl;": '\U00002A8C', + "gacute;": '\U000001F5', + "gamma;": '\U000003B3', + "gammad;": '\U000003DD', + "gap;": '\U00002A86', + "gbreve;": '\U0000011F', + "gcirc;": '\U0000011D', + "gcy;": '\U00000433', + "gdot;": '\U00000121', + "ge;": '\U00002265', + "gel;": '\U000022DB', + "geq;": '\U00002265', + "geqq;": '\U00002267', + "geqslant;": '\U00002A7E', + "ges;": '\U00002A7E', + "gescc;": '\U00002AA9', + "gesdot;": '\U00002A80', + "gesdoto;": '\U00002A82', + "gesdotol;": '\U00002A84', + "gesles;": '\U00002A94', + "gfr;": '\U0001D524', + "gg;": '\U0000226B', + "ggg;": '\U000022D9', + "gimel;": '\U00002137', + "gjcy;": '\U00000453', + "gl;": '\U00002277', + "glE;": '\U00002A92', + "gla;": '\U00002AA5', + "glj;": '\U00002AA4', + "gnE;": '\U00002269', + "gnap;": '\U00002A8A', + "gnapprox;": '\U00002A8A', + "gne;": '\U00002A88', + "gneq;": '\U00002A88', + "gneqq;": '\U00002269', + "gnsim;": '\U000022E7', + "gopf;": '\U0001D558', + "grave;": '\U00000060', + "gscr;": '\U0000210A', + "gsim;": '\U00002273', + "gsime;": '\U00002A8E', + "gsiml;": '\U00002A90', + "gt;": '\U0000003E', + "gtcc;": '\U00002AA7', + "gtcir;": '\U00002A7A', + "gtdot;": '\U000022D7', + "gtlPar;": '\U00002995', + "gtquest;": '\U00002A7C', + "gtrapprox;": '\U00002A86', + "gtrarr;": '\U00002978', + "gtrdot;": '\U000022D7', + "gtreqless;": '\U000022DB', + "gtreqqless;": '\U00002A8C', + "gtrless;": '\U00002277', + "gtrsim;": '\U00002273', + "hArr;": '\U000021D4', + "hairsp;": '\U0000200A', + "half;": '\U000000BD', + "hamilt;": '\U0000210B', + "hardcy;": '\U0000044A', + "harr;": '\U00002194', + "harrcir;": '\U00002948', + "harrw;": '\U000021AD', + "hbar;": '\U0000210F', + "hcirc;": '\U00000125', + "hearts;": '\U00002665', + "heartsuit;": '\U00002665', + "hellip;": '\U00002026', + "hercon;": '\U000022B9', + "hfr;": '\U0001D525', + "hksearow;": '\U00002925', + "hkswarow;": '\U00002926', + "hoarr;": '\U000021FF', + "homtht;": '\U0000223B', + "hookleftarrow;": '\U000021A9', + "hookrightarrow;": '\U000021AA', + "hopf;": '\U0001D559', + "horbar;": '\U00002015', + "hscr;": '\U0001D4BD', + "hslash;": '\U0000210F', + "hstrok;": '\U00000127', + "hybull;": '\U00002043', + "hyphen;": '\U00002010', + "iacute;": '\U000000ED', + "ic;": '\U00002063', + "icirc;": '\U000000EE', + "icy;": '\U00000438', + "iecy;": '\U00000435', + "iexcl;": '\U000000A1', + "iff;": '\U000021D4', + "ifr;": '\U0001D526', + "igrave;": '\U000000EC', + "ii;": '\U00002148', + "iiiint;": '\U00002A0C', + "iiint;": '\U0000222D', + "iinfin;": '\U000029DC', + "iiota;": '\U00002129', + "ijlig;": '\U00000133', + "imacr;": '\U0000012B', + "image;": '\U00002111', + "imagline;": '\U00002110', + "imagpart;": '\U00002111', + "imath;": '\U00000131', + "imof;": '\U000022B7', + "imped;": '\U000001B5', + "in;": '\U00002208', + "incare;": '\U00002105', + "infin;": '\U0000221E', + "infintie;": '\U000029DD', + "inodot;": '\U00000131', + "int;": '\U0000222B', + "intcal;": '\U000022BA', + "integers;": '\U00002124', + "intercal;": '\U000022BA', + "intlarhk;": '\U00002A17', + "intprod;": '\U00002A3C', + "iocy;": '\U00000451', + "iogon;": '\U0000012F', + "iopf;": '\U0001D55A', + "iota;": '\U000003B9', + "iprod;": '\U00002A3C', + "iquest;": '\U000000BF', + "iscr;": '\U0001D4BE', + "isin;": '\U00002208', + "isinE;": '\U000022F9', + "isindot;": '\U000022F5', + "isins;": '\U000022F4', + "isinsv;": '\U000022F3', + "isinv;": '\U00002208', + "it;": '\U00002062', + "itilde;": '\U00000129', + "iukcy;": '\U00000456', + "iuml;": '\U000000EF', + "jcirc;": '\U00000135', + "jcy;": '\U00000439', + "jfr;": '\U0001D527', + "jmath;": '\U00000237', + "jopf;": '\U0001D55B', + "jscr;": '\U0001D4BF', + "jsercy;": '\U00000458', + "jukcy;": '\U00000454', + "kappa;": '\U000003BA', + "kappav;": '\U000003F0', + "kcedil;": '\U00000137', + "kcy;": '\U0000043A', + "kfr;": '\U0001D528', + "kgreen;": '\U00000138', + "khcy;": '\U00000445', + "kjcy;": '\U0000045C', + "kopf;": '\U0001D55C', + "kscr;": '\U0001D4C0', + "lAarr;": '\U000021DA', + "lArr;": '\U000021D0', + "lAtail;": '\U0000291B', + "lBarr;": '\U0000290E', + "lE;": '\U00002266', + "lEg;": '\U00002A8B', + "lHar;": '\U00002962', + "lacute;": '\U0000013A', + "laemptyv;": '\U000029B4', + "lagran;": '\U00002112', + "lambda;": '\U000003BB', + "lang;": '\U000027E8', + "langd;": '\U00002991', + "langle;": '\U000027E8', + "lap;": '\U00002A85', + "laquo;": '\U000000AB', + "larr;": '\U00002190', + "larrb;": '\U000021E4', + "larrbfs;": '\U0000291F', + "larrfs;": '\U0000291D', + "larrhk;": '\U000021A9', + "larrlp;": '\U000021AB', + "larrpl;": '\U00002939', + "larrsim;": '\U00002973', + "larrtl;": '\U000021A2', + "lat;": '\U00002AAB', + "latail;": '\U00002919', + "late;": '\U00002AAD', + "lbarr;": '\U0000290C', + "lbbrk;": '\U00002772', + "lbrace;": '\U0000007B', + "lbrack;": '\U0000005B', + "lbrke;": '\U0000298B', + "lbrksld;": '\U0000298F', + "lbrkslu;": '\U0000298D', + "lcaron;": '\U0000013E', + "lcedil;": '\U0000013C', + "lceil;": '\U00002308', + "lcub;": '\U0000007B', + "lcy;": '\U0000043B', + "ldca;": '\U00002936', + "ldquo;": '\U0000201C', + "ldquor;": '\U0000201E', + "ldrdhar;": '\U00002967', + "ldrushar;": '\U0000294B', + "ldsh;": '\U000021B2', + "le;": '\U00002264', + "leftarrow;": '\U00002190', + "leftarrowtail;": '\U000021A2', + "leftharpoondown;": '\U000021BD', + "leftharpoonup;": '\U000021BC', + "leftleftarrows;": '\U000021C7', + "leftrightarrow;": '\U00002194', + "leftrightarrows;": '\U000021C6', + "leftrightharpoons;": '\U000021CB', + "leftrightsquigarrow;": '\U000021AD', + "leftthreetimes;": '\U000022CB', + "leg;": '\U000022DA', + "leq;": '\U00002264', + "leqq;": '\U00002266', + "leqslant;": '\U00002A7D', + "les;": '\U00002A7D', + "lescc;": '\U00002AA8', + "lesdot;": '\U00002A7F', + "lesdoto;": '\U00002A81', + "lesdotor;": '\U00002A83', + "lesges;": '\U00002A93', + "lessapprox;": '\U00002A85', + "lessdot;": '\U000022D6', + "lesseqgtr;": '\U000022DA', + "lesseqqgtr;": '\U00002A8B', + "lessgtr;": '\U00002276', + "lesssim;": '\U00002272', + "lfisht;": '\U0000297C', + "lfloor;": '\U0000230A', + "lfr;": '\U0001D529', + "lg;": '\U00002276', + "lgE;": '\U00002A91', + "lhard;": '\U000021BD', + "lharu;": '\U000021BC', + "lharul;": '\U0000296A', + "lhblk;": '\U00002584', + "ljcy;": '\U00000459', + "ll;": '\U0000226A', + "llarr;": '\U000021C7', + "llcorner;": '\U0000231E', + "llhard;": '\U0000296B', + "lltri;": '\U000025FA', + "lmidot;": '\U00000140', + "lmoust;": '\U000023B0', + "lmoustache;": '\U000023B0', + "lnE;": '\U00002268', + "lnap;": '\U00002A89', + "lnapprox;": '\U00002A89', + "lne;": '\U00002A87', + "lneq;": '\U00002A87', + "lneqq;": '\U00002268', + "lnsim;": '\U000022E6', + "loang;": '\U000027EC', + "loarr;": '\U000021FD', + "lobrk;": '\U000027E6', + "longleftarrow;": '\U000027F5', + "longleftrightarrow;": '\U000027F7', + "longmapsto;": '\U000027FC', + "longrightarrow;": '\U000027F6', + "looparrowleft;": '\U000021AB', + "looparrowright;": '\U000021AC', + "lopar;": '\U00002985', + "lopf;": '\U0001D55D', + "loplus;": '\U00002A2D', + "lotimes;": '\U00002A34', + "lowast;": '\U00002217', + "lowbar;": '\U0000005F', + "loz;": '\U000025CA', + "lozenge;": '\U000025CA', + "lozf;": '\U000029EB', + "lpar;": '\U00000028', + "lparlt;": '\U00002993', + "lrarr;": '\U000021C6', + "lrcorner;": '\U0000231F', + "lrhar;": '\U000021CB', + "lrhard;": '\U0000296D', + "lrm;": '\U0000200E', + "lrtri;": '\U000022BF', + "lsaquo;": '\U00002039', + "lscr;": '\U0001D4C1', + "lsh;": '\U000021B0', + "lsim;": '\U00002272', + "lsime;": '\U00002A8D', + "lsimg;": '\U00002A8F', + "lsqb;": '\U0000005B', + "lsquo;": '\U00002018', + "lsquor;": '\U0000201A', + "lstrok;": '\U00000142', + "lt;": '\U0000003C', + "ltcc;": '\U00002AA6', + "ltcir;": '\U00002A79', + "ltdot;": '\U000022D6', + "lthree;": '\U000022CB', + "ltimes;": '\U000022C9', + "ltlarr;": '\U00002976', + "ltquest;": '\U00002A7B', + "ltrPar;": '\U00002996', + "ltri;": '\U000025C3', + "ltrie;": '\U000022B4', + "ltrif;": '\U000025C2', + "lurdshar;": '\U0000294A', + "luruhar;": '\U00002966', + "mDDot;": '\U0000223A', + "macr;": '\U000000AF', + "male;": '\U00002642', + "malt;": '\U00002720', + "maltese;": '\U00002720', + "map;": '\U000021A6', + "mapsto;": '\U000021A6', + "mapstodown;": '\U000021A7', + "mapstoleft;": '\U000021A4', + "mapstoup;": '\U000021A5', + "marker;": '\U000025AE', + "mcomma;": '\U00002A29', + "mcy;": '\U0000043C', + "mdash;": '\U00002014', + "measuredangle;": '\U00002221', + "mfr;": '\U0001D52A', + "mho;": '\U00002127', + "micro;": '\U000000B5', + "mid;": '\U00002223', + "midast;": '\U0000002A', + "midcir;": '\U00002AF0', + "middot;": '\U000000B7', + "minus;": '\U00002212', + "minusb;": '\U0000229F', + "minusd;": '\U00002238', + "minusdu;": '\U00002A2A', + "mlcp;": '\U00002ADB', + "mldr;": '\U00002026', + "mnplus;": '\U00002213', + "models;": '\U000022A7', + "mopf;": '\U0001D55E', + "mp;": '\U00002213', + "mscr;": '\U0001D4C2', + "mstpos;": '\U0000223E', + "mu;": '\U000003BC', + "multimap;": '\U000022B8', + "mumap;": '\U000022B8', + "nLeftarrow;": '\U000021CD', + "nLeftrightarrow;": '\U000021CE', + "nRightarrow;": '\U000021CF', + "nVDash;": '\U000022AF', + "nVdash;": '\U000022AE', + "nabla;": '\U00002207', + "nacute;": '\U00000144', + "nap;": '\U00002249', + "napos;": '\U00000149', + "napprox;": '\U00002249', + "natur;": '\U0000266E', + "natural;": '\U0000266E', + "naturals;": '\U00002115', + "nbsp;": '\U000000A0', + "ncap;": '\U00002A43', + "ncaron;": '\U00000148', + "ncedil;": '\U00000146', + "ncong;": '\U00002247', + "ncup;": '\U00002A42', + "ncy;": '\U0000043D', + "ndash;": '\U00002013', + "ne;": '\U00002260', + "neArr;": '\U000021D7', + "nearhk;": '\U00002924', + "nearr;": '\U00002197', + "nearrow;": '\U00002197', + "nequiv;": '\U00002262', + "nesear;": '\U00002928', + "nexist;": '\U00002204', + "nexists;": '\U00002204', + "nfr;": '\U0001D52B', + "nge;": '\U00002271', + "ngeq;": '\U00002271', + "ngsim;": '\U00002275', + "ngt;": '\U0000226F', + "ngtr;": '\U0000226F', + "nhArr;": '\U000021CE', + "nharr;": '\U000021AE', + "nhpar;": '\U00002AF2', + "ni;": '\U0000220B', + "nis;": '\U000022FC', + "nisd;": '\U000022FA', + "niv;": '\U0000220B', + "njcy;": '\U0000045A', + "nlArr;": '\U000021CD', + "nlarr;": '\U0000219A', + "nldr;": '\U00002025', + "nle;": '\U00002270', + "nleftarrow;": '\U0000219A', + "nleftrightarrow;": '\U000021AE', + "nleq;": '\U00002270', + "nless;": '\U0000226E', + "nlsim;": '\U00002274', + "nlt;": '\U0000226E', + "nltri;": '\U000022EA', + "nltrie;": '\U000022EC', + "nmid;": '\U00002224', + "nopf;": '\U0001D55F', + "not;": '\U000000AC', + "notin;": '\U00002209', + "notinva;": '\U00002209', + "notinvb;": '\U000022F7', + "notinvc;": '\U000022F6', + "notni;": '\U0000220C', + "notniva;": '\U0000220C', + "notnivb;": '\U000022FE', + "notnivc;": '\U000022FD', + "npar;": '\U00002226', + "nparallel;": '\U00002226', + "npolint;": '\U00002A14', + "npr;": '\U00002280', + "nprcue;": '\U000022E0', + "nprec;": '\U00002280', + "nrArr;": '\U000021CF', + "nrarr;": '\U0000219B', + "nrightarrow;": '\U0000219B', + "nrtri;": '\U000022EB', + "nrtrie;": '\U000022ED', + "nsc;": '\U00002281', + "nsccue;": '\U000022E1', + "nscr;": '\U0001D4C3', + "nshortmid;": '\U00002224', + "nshortparallel;": '\U00002226', + "nsim;": '\U00002241', + "nsime;": '\U00002244', + "nsimeq;": '\U00002244', + "nsmid;": '\U00002224', + "nspar;": '\U00002226', + "nsqsube;": '\U000022E2', + "nsqsupe;": '\U000022E3', + "nsub;": '\U00002284', + "nsube;": '\U00002288', + "nsubseteq;": '\U00002288', + "nsucc;": '\U00002281', + "nsup;": '\U00002285', + "nsupe;": '\U00002289', + "nsupseteq;": '\U00002289', + "ntgl;": '\U00002279', + "ntilde;": '\U000000F1', + "ntlg;": '\U00002278', + "ntriangleleft;": '\U000022EA', + "ntrianglelefteq;": '\U000022EC', + "ntriangleright;": '\U000022EB', + "ntrianglerighteq;": '\U000022ED', + "nu;": '\U000003BD', + "num;": '\U00000023', + "numero;": '\U00002116', + "numsp;": '\U00002007', + "nvDash;": '\U000022AD', + "nvHarr;": '\U00002904', + "nvdash;": '\U000022AC', + "nvinfin;": '\U000029DE', + "nvlArr;": '\U00002902', + "nvrArr;": '\U00002903', + "nwArr;": '\U000021D6', + "nwarhk;": '\U00002923', + "nwarr;": '\U00002196', + "nwarrow;": '\U00002196', + "nwnear;": '\U00002927', + "oS;": '\U000024C8', + "oacute;": '\U000000F3', + "oast;": '\U0000229B', + "ocir;": '\U0000229A', + "ocirc;": '\U000000F4', + "ocy;": '\U0000043E', + "odash;": '\U0000229D', + "odblac;": '\U00000151', + "odiv;": '\U00002A38', + "odot;": '\U00002299', + "odsold;": '\U000029BC', + "oelig;": '\U00000153', + "ofcir;": '\U000029BF', + "ofr;": '\U0001D52C', + "ogon;": '\U000002DB', + "ograve;": '\U000000F2', + "ogt;": '\U000029C1', + "ohbar;": '\U000029B5', + "ohm;": '\U000003A9', + "oint;": '\U0000222E', + "olarr;": '\U000021BA', + "olcir;": '\U000029BE', + "olcross;": '\U000029BB', + "oline;": '\U0000203E', + "olt;": '\U000029C0', + "omacr;": '\U0000014D', + "omega;": '\U000003C9', + "omicron;": '\U000003BF', + "omid;": '\U000029B6', + "ominus;": '\U00002296', + "oopf;": '\U0001D560', + "opar;": '\U000029B7', + "operp;": '\U000029B9', + "oplus;": '\U00002295', + "or;": '\U00002228', + "orarr;": '\U000021BB', + "ord;": '\U00002A5D', + "order;": '\U00002134', + "orderof;": '\U00002134', + "ordf;": '\U000000AA', + "ordm;": '\U000000BA', + "origof;": '\U000022B6', + "oror;": '\U00002A56', + "orslope;": '\U00002A57', + "orv;": '\U00002A5B', + "oscr;": '\U00002134', + "oslash;": '\U000000F8', + "osol;": '\U00002298', + "otilde;": '\U000000F5', + "otimes;": '\U00002297', + "otimesas;": '\U00002A36', + "ouml;": '\U000000F6', + "ovbar;": '\U0000233D', + "par;": '\U00002225', + "para;": '\U000000B6', + "parallel;": '\U00002225', + "parsim;": '\U00002AF3', + "parsl;": '\U00002AFD', + "part;": '\U00002202', + "pcy;": '\U0000043F', + "percnt;": '\U00000025', + "period;": '\U0000002E', + "permil;": '\U00002030', + "perp;": '\U000022A5', + "pertenk;": '\U00002031', + "pfr;": '\U0001D52D', + "phi;": '\U000003C6', + "phiv;": '\U000003D5', + "phmmat;": '\U00002133', + "phone;": '\U0000260E', + "pi;": '\U000003C0', + "pitchfork;": '\U000022D4', + "piv;": '\U000003D6', + "planck;": '\U0000210F', + "planckh;": '\U0000210E', + "plankv;": '\U0000210F', + "plus;": '\U0000002B', + "plusacir;": '\U00002A23', + "plusb;": '\U0000229E', + "pluscir;": '\U00002A22', + "plusdo;": '\U00002214', + "plusdu;": '\U00002A25', + "pluse;": '\U00002A72', + "plusmn;": '\U000000B1', + "plussim;": '\U00002A26', + "plustwo;": '\U00002A27', + "pm;": '\U000000B1', + "pointint;": '\U00002A15', + "popf;": '\U0001D561', + "pound;": '\U000000A3', + "pr;": '\U0000227A', + "prE;": '\U00002AB3', + "prap;": '\U00002AB7', + "prcue;": '\U0000227C', + "pre;": '\U00002AAF', + "prec;": '\U0000227A', + "precapprox;": '\U00002AB7', + "preccurlyeq;": '\U0000227C', + "preceq;": '\U00002AAF', + "precnapprox;": '\U00002AB9', + "precneqq;": '\U00002AB5', + "precnsim;": '\U000022E8', + "precsim;": '\U0000227E', + "prime;": '\U00002032', + "primes;": '\U00002119', + "prnE;": '\U00002AB5', + "prnap;": '\U00002AB9', + "prnsim;": '\U000022E8', + "prod;": '\U0000220F', + "profalar;": '\U0000232E', + "profline;": '\U00002312', + "profsurf;": '\U00002313', + "prop;": '\U0000221D', + "propto;": '\U0000221D', + "prsim;": '\U0000227E', + "prurel;": '\U000022B0', + "pscr;": '\U0001D4C5', + "psi;": '\U000003C8', + "puncsp;": '\U00002008', + "qfr;": '\U0001D52E', + "qint;": '\U00002A0C', + "qopf;": '\U0001D562', + "qprime;": '\U00002057', + "qscr;": '\U0001D4C6', + "quaternions;": '\U0000210D', + "quatint;": '\U00002A16', + "quest;": '\U0000003F', + "questeq;": '\U0000225F', + "quot;": '\U00000022', + "rAarr;": '\U000021DB', + "rArr;": '\U000021D2', + "rAtail;": '\U0000291C', + "rBarr;": '\U0000290F', + "rHar;": '\U00002964', + "racute;": '\U00000155', + "radic;": '\U0000221A', + "raemptyv;": '\U000029B3', + "rang;": '\U000027E9', + "rangd;": '\U00002992', + "range;": '\U000029A5', + "rangle;": '\U000027E9', + "raquo;": '\U000000BB', + "rarr;": '\U00002192', + "rarrap;": '\U00002975', + "rarrb;": '\U000021E5', + "rarrbfs;": '\U00002920', + "rarrc;": '\U00002933', + "rarrfs;": '\U0000291E', + "rarrhk;": '\U000021AA', + "rarrlp;": '\U000021AC', + "rarrpl;": '\U00002945', + "rarrsim;": '\U00002974', + "rarrtl;": '\U000021A3', + "rarrw;": '\U0000219D', + "ratail;": '\U0000291A', + "ratio;": '\U00002236', + "rationals;": '\U0000211A', + "rbarr;": '\U0000290D', + "rbbrk;": '\U00002773', + "rbrace;": '\U0000007D', + "rbrack;": '\U0000005D', + "rbrke;": '\U0000298C', + "rbrksld;": '\U0000298E', + "rbrkslu;": '\U00002990', + "rcaron;": '\U00000159', + "rcedil;": '\U00000157', + "rceil;": '\U00002309', + "rcub;": '\U0000007D', + "rcy;": '\U00000440', + "rdca;": '\U00002937', + "rdldhar;": '\U00002969', + "rdquo;": '\U0000201D', + "rdquor;": '\U0000201D', + "rdsh;": '\U000021B3', + "real;": '\U0000211C', + "realine;": '\U0000211B', + "realpart;": '\U0000211C', + "reals;": '\U0000211D', + "rect;": '\U000025AD', + "reg;": '\U000000AE', + "rfisht;": '\U0000297D', + "rfloor;": '\U0000230B', + "rfr;": '\U0001D52F', + "rhard;": '\U000021C1', + "rharu;": '\U000021C0', + "rharul;": '\U0000296C', + "rho;": '\U000003C1', + "rhov;": '\U000003F1', + "rightarrow;": '\U00002192', + "rightarrowtail;": '\U000021A3', + "rightharpoondown;": '\U000021C1', + "rightharpoonup;": '\U000021C0', + "rightleftarrows;": '\U000021C4', + "rightleftharpoons;": '\U000021CC', + "rightrightarrows;": '\U000021C9', + "rightsquigarrow;": '\U0000219D', + "rightthreetimes;": '\U000022CC', + "ring;": '\U000002DA', + "risingdotseq;": '\U00002253', + "rlarr;": '\U000021C4', + "rlhar;": '\U000021CC', + "rlm;": '\U0000200F', + "rmoust;": '\U000023B1', + "rmoustache;": '\U000023B1', + "rnmid;": '\U00002AEE', + "roang;": '\U000027ED', + "roarr;": '\U000021FE', + "robrk;": '\U000027E7', + "ropar;": '\U00002986', + "ropf;": '\U0001D563', + "roplus;": '\U00002A2E', + "rotimes;": '\U00002A35', + "rpar;": '\U00000029', + "rpargt;": '\U00002994', + "rppolint;": '\U00002A12', + "rrarr;": '\U000021C9', + "rsaquo;": '\U0000203A', + "rscr;": '\U0001D4C7', + "rsh;": '\U000021B1', + "rsqb;": '\U0000005D', + "rsquo;": '\U00002019', + "rsquor;": '\U00002019', + "rthree;": '\U000022CC', + "rtimes;": '\U000022CA', + "rtri;": '\U000025B9', + "rtrie;": '\U000022B5', + "rtrif;": '\U000025B8', + "rtriltri;": '\U000029CE', + "ruluhar;": '\U00002968', + "rx;": '\U0000211E', + "sacute;": '\U0000015B', + "sbquo;": '\U0000201A', + "sc;": '\U0000227B', + "scE;": '\U00002AB4', + "scap;": '\U00002AB8', + "scaron;": '\U00000161', + "sccue;": '\U0000227D', + "sce;": '\U00002AB0', + "scedil;": '\U0000015F', + "scirc;": '\U0000015D', + "scnE;": '\U00002AB6', + "scnap;": '\U00002ABA', + "scnsim;": '\U000022E9', + "scpolint;": '\U00002A13', + "scsim;": '\U0000227F', + "scy;": '\U00000441', + "sdot;": '\U000022C5', + "sdotb;": '\U000022A1', + "sdote;": '\U00002A66', + "seArr;": '\U000021D8', + "searhk;": '\U00002925', + "searr;": '\U00002198', + "searrow;": '\U00002198', + "sect;": '\U000000A7', + "semi;": '\U0000003B', + "seswar;": '\U00002929', + "setminus;": '\U00002216', + "setmn;": '\U00002216', + "sext;": '\U00002736', + "sfr;": '\U0001D530', + "sfrown;": '\U00002322', + "sharp;": '\U0000266F', + "shchcy;": '\U00000449', + "shcy;": '\U00000448', + "shortmid;": '\U00002223', + "shortparallel;": '\U00002225', + "shy;": '\U000000AD', + "sigma;": '\U000003C3', + "sigmaf;": '\U000003C2', + "sigmav;": '\U000003C2', + "sim;": '\U0000223C', + "simdot;": '\U00002A6A', + "sime;": '\U00002243', + "simeq;": '\U00002243', + "simg;": '\U00002A9E', + "simgE;": '\U00002AA0', + "siml;": '\U00002A9D', + "simlE;": '\U00002A9F', + "simne;": '\U00002246', + "simplus;": '\U00002A24', + "simrarr;": '\U00002972', + "slarr;": '\U00002190', + "smallsetminus;": '\U00002216', + "smashp;": '\U00002A33', + "smeparsl;": '\U000029E4', + "smid;": '\U00002223', + "smile;": '\U00002323', + "smt;": '\U00002AAA', + "smte;": '\U00002AAC', + "softcy;": '\U0000044C', + "sol;": '\U0000002F', + "solb;": '\U000029C4', + "solbar;": '\U0000233F', + "sopf;": '\U0001D564', + "spades;": '\U00002660', + "spadesuit;": '\U00002660', + "spar;": '\U00002225', + "sqcap;": '\U00002293', + "sqcup;": '\U00002294', + "sqsub;": '\U0000228F', + "sqsube;": '\U00002291', + "sqsubset;": '\U0000228F', + "sqsubseteq;": '\U00002291', + "sqsup;": '\U00002290', + "sqsupe;": '\U00002292', + "sqsupset;": '\U00002290', + "sqsupseteq;": '\U00002292', + "squ;": '\U000025A1', + "square;": '\U000025A1', + "squarf;": '\U000025AA', + "squf;": '\U000025AA', + "srarr;": '\U00002192', + "sscr;": '\U0001D4C8', + "ssetmn;": '\U00002216', + "ssmile;": '\U00002323', + "sstarf;": '\U000022C6', + "star;": '\U00002606', + "starf;": '\U00002605', + "straightepsilon;": '\U000003F5', + "straightphi;": '\U000003D5', + "strns;": '\U000000AF', + "sub;": '\U00002282', + "subE;": '\U00002AC5', + "subdot;": '\U00002ABD', + "sube;": '\U00002286', + "subedot;": '\U00002AC3', + "submult;": '\U00002AC1', + "subnE;": '\U00002ACB', + "subne;": '\U0000228A', + "subplus;": '\U00002ABF', + "subrarr;": '\U00002979', + "subset;": '\U00002282', + "subseteq;": '\U00002286', + "subseteqq;": '\U00002AC5', + "subsetneq;": '\U0000228A', + "subsetneqq;": '\U00002ACB', + "subsim;": '\U00002AC7', + "subsub;": '\U00002AD5', + "subsup;": '\U00002AD3', + "succ;": '\U0000227B', + "succapprox;": '\U00002AB8', + "succcurlyeq;": '\U0000227D', + "succeq;": '\U00002AB0', + "succnapprox;": '\U00002ABA', + "succneqq;": '\U00002AB6', + "succnsim;": '\U000022E9', + "succsim;": '\U0000227F', + "sum;": '\U00002211', + "sung;": '\U0000266A', + "sup;": '\U00002283', + "sup1;": '\U000000B9', + "sup2;": '\U000000B2', + "sup3;": '\U000000B3', + "supE;": '\U00002AC6', + "supdot;": '\U00002ABE', + "supdsub;": '\U00002AD8', + "supe;": '\U00002287', + "supedot;": '\U00002AC4', + "suphsol;": '\U000027C9', + "suphsub;": '\U00002AD7', + "suplarr;": '\U0000297B', + "supmult;": '\U00002AC2', + "supnE;": '\U00002ACC', + "supne;": '\U0000228B', + "supplus;": '\U00002AC0', + "supset;": '\U00002283', + "supseteq;": '\U00002287', + "supseteqq;": '\U00002AC6', + "supsetneq;": '\U0000228B', + "supsetneqq;": '\U00002ACC', + "supsim;": '\U00002AC8', + "supsub;": '\U00002AD4', + "supsup;": '\U00002AD6', + "swArr;": '\U000021D9', + "swarhk;": '\U00002926', + "swarr;": '\U00002199', + "swarrow;": '\U00002199', + "swnwar;": '\U0000292A', + "szlig;": '\U000000DF', + "target;": '\U00002316', + "tau;": '\U000003C4', + "tbrk;": '\U000023B4', + "tcaron;": '\U00000165', + "tcedil;": '\U00000163', + "tcy;": '\U00000442', + "tdot;": '\U000020DB', + "telrec;": '\U00002315', + "tfr;": '\U0001D531', + "there4;": '\U00002234', + "therefore;": '\U00002234', + "theta;": '\U000003B8', + "thetasym;": '\U000003D1', + "thetav;": '\U000003D1', + "thickapprox;": '\U00002248', + "thicksim;": '\U0000223C', + "thinsp;": '\U00002009', + "thkap;": '\U00002248', + "thksim;": '\U0000223C', + "thorn;": '\U000000FE', + "tilde;": '\U000002DC', + "times;": '\U000000D7', + "timesb;": '\U000022A0', + "timesbar;": '\U00002A31', + "timesd;": '\U00002A30', + "tint;": '\U0000222D', + "toea;": '\U00002928', + "top;": '\U000022A4', + "topbot;": '\U00002336', + "topcir;": '\U00002AF1', + "topf;": '\U0001D565', + "topfork;": '\U00002ADA', + "tosa;": '\U00002929', + "tprime;": '\U00002034', + "trade;": '\U00002122', + "triangle;": '\U000025B5', + "triangledown;": '\U000025BF', + "triangleleft;": '\U000025C3', + "trianglelefteq;": '\U000022B4', + "triangleq;": '\U0000225C', + "triangleright;": '\U000025B9', + "trianglerighteq;": '\U000022B5', + "tridot;": '\U000025EC', + "trie;": '\U0000225C', + "triminus;": '\U00002A3A', + "triplus;": '\U00002A39', + "trisb;": '\U000029CD', + "tritime;": '\U00002A3B', + "trpezium;": '\U000023E2', + "tscr;": '\U0001D4C9', + "tscy;": '\U00000446', + "tshcy;": '\U0000045B', + "tstrok;": '\U00000167', + "twixt;": '\U0000226C', + "twoheadleftarrow;": '\U0000219E', + "twoheadrightarrow;": '\U000021A0', + "uArr;": '\U000021D1', + "uHar;": '\U00002963', + "uacute;": '\U000000FA', + "uarr;": '\U00002191', + "ubrcy;": '\U0000045E', + "ubreve;": '\U0000016D', + "ucirc;": '\U000000FB', + "ucy;": '\U00000443', + "udarr;": '\U000021C5', + "udblac;": '\U00000171', + "udhar;": '\U0000296E', + "ufisht;": '\U0000297E', + "ufr;": '\U0001D532', + "ugrave;": '\U000000F9', + "uharl;": '\U000021BF', + "uharr;": '\U000021BE', + "uhblk;": '\U00002580', + "ulcorn;": '\U0000231C', + "ulcorner;": '\U0000231C', + "ulcrop;": '\U0000230F', + "ultri;": '\U000025F8', + "umacr;": '\U0000016B', + "uml;": '\U000000A8', + "uogon;": '\U00000173', + "uopf;": '\U0001D566', + "uparrow;": '\U00002191', + "updownarrow;": '\U00002195', + "upharpoonleft;": '\U000021BF', + "upharpoonright;": '\U000021BE', + "uplus;": '\U0000228E', + "upsi;": '\U000003C5', + "upsih;": '\U000003D2', + "upsilon;": '\U000003C5', + "upuparrows;": '\U000021C8', + "urcorn;": '\U0000231D', + "urcorner;": '\U0000231D', + "urcrop;": '\U0000230E', + "uring;": '\U0000016F', + "urtri;": '\U000025F9', + "uscr;": '\U0001D4CA', + "utdot;": '\U000022F0', + "utilde;": '\U00000169', + "utri;": '\U000025B5', + "utrif;": '\U000025B4', + "uuarr;": '\U000021C8', + "uuml;": '\U000000FC', + "uwangle;": '\U000029A7', + "vArr;": '\U000021D5', + "vBar;": '\U00002AE8', + "vBarv;": '\U00002AE9', + "vDash;": '\U000022A8', + "vangrt;": '\U0000299C', + "varepsilon;": '\U000003F5', + "varkappa;": '\U000003F0', + "varnothing;": '\U00002205', + "varphi;": '\U000003D5', + "varpi;": '\U000003D6', + "varpropto;": '\U0000221D', + "varr;": '\U00002195', + "varrho;": '\U000003F1', + "varsigma;": '\U000003C2', + "vartheta;": '\U000003D1', + "vartriangleleft;": '\U000022B2', + "vartriangleright;": '\U000022B3', + "vcy;": '\U00000432', + "vdash;": '\U000022A2', + "vee;": '\U00002228', + "veebar;": '\U000022BB', + "veeeq;": '\U0000225A', + "vellip;": '\U000022EE', + "verbar;": '\U0000007C', + "vert;": '\U0000007C', + "vfr;": '\U0001D533', + "vltri;": '\U000022B2', + "vopf;": '\U0001D567', + "vprop;": '\U0000221D', + "vrtri;": '\U000022B3', + "vscr;": '\U0001D4CB', + "vzigzag;": '\U0000299A', + "wcirc;": '\U00000175', + "wedbar;": '\U00002A5F', + "wedge;": '\U00002227', + "wedgeq;": '\U00002259', + "weierp;": '\U00002118', + "wfr;": '\U0001D534', + "wopf;": '\U0001D568', + "wp;": '\U00002118', + "wr;": '\U00002240', + "wreath;": '\U00002240', + "wscr;": '\U0001D4CC', + "xcap;": '\U000022C2', + "xcirc;": '\U000025EF', + "xcup;": '\U000022C3', + "xdtri;": '\U000025BD', + "xfr;": '\U0001D535', + "xhArr;": '\U000027FA', + "xharr;": '\U000027F7', + "xi;": '\U000003BE', + "xlArr;": '\U000027F8', + "xlarr;": '\U000027F5', + "xmap;": '\U000027FC', + "xnis;": '\U000022FB', + "xodot;": '\U00002A00', + "xopf;": '\U0001D569', + "xoplus;": '\U00002A01', + "xotime;": '\U00002A02', + "xrArr;": '\U000027F9', + "xrarr;": '\U000027F6', + "xscr;": '\U0001D4CD', + "xsqcup;": '\U00002A06', + "xuplus;": '\U00002A04', + "xutri;": '\U000025B3', + "xvee;": '\U000022C1', + "xwedge;": '\U000022C0', + "yacute;": '\U000000FD', + "yacy;": '\U0000044F', + "ycirc;": '\U00000177', + "ycy;": '\U0000044B', + "yen;": '\U000000A5', + "yfr;": '\U0001D536', + "yicy;": '\U00000457', + "yopf;": '\U0001D56A', + "yscr;": '\U0001D4CE', + "yucy;": '\U0000044E', + "yuml;": '\U000000FF', + "zacute;": '\U0000017A', + "zcaron;": '\U0000017E', + "zcy;": '\U00000437', + "zdot;": '\U0000017C', + "zeetrf;": '\U00002128', + "zeta;": '\U000003B6', + "zfr;": '\U0001D537', + "zhcy;": '\U00000436', + "zigrarr;": '\U000021DD', + "zopf;": '\U0001D56B', + "zscr;": '\U0001D4CF', + "zwj;": '\U0000200D', + "zwnj;": '\U0000200C', + "AElig": '\U000000C6', + "AMP": '\U00000026', + "Aacute": '\U000000C1', + "Acirc": '\U000000C2', + "Agrave": '\U000000C0', + "Aring": '\U000000C5', + "Atilde": '\U000000C3', + "Auml": '\U000000C4', + "COPY": '\U000000A9', + "Ccedil": '\U000000C7', + "ETH": '\U000000D0', + "Eacute": '\U000000C9', + "Ecirc": '\U000000CA', + "Egrave": '\U000000C8', + "Euml": '\U000000CB', + "GT": '\U0000003E', + "Iacute": '\U000000CD', + "Icirc": '\U000000CE', + "Igrave": '\U000000CC', + "Iuml": '\U000000CF', + "LT": '\U0000003C', + "Ntilde": '\U000000D1', + "Oacute": '\U000000D3', + "Ocirc": '\U000000D4', + "Ograve": '\U000000D2', + "Oslash": '\U000000D8', + "Otilde": '\U000000D5', + "Ouml": '\U000000D6', + "QUOT": '\U00000022', + "REG": '\U000000AE', + "THORN": '\U000000DE', + "Uacute": '\U000000DA', + "Ucirc": '\U000000DB', + "Ugrave": '\U000000D9', + "Uuml": '\U000000DC', + "Yacute": '\U000000DD', + "aacute": '\U000000E1', + "acirc": '\U000000E2', + "acute": '\U000000B4', + "aelig": '\U000000E6', + "agrave": '\U000000E0', + "amp": '\U00000026', + "aring": '\U000000E5', + "atilde": '\U000000E3', + "auml": '\U000000E4', + "brvbar": '\U000000A6', + "ccedil": '\U000000E7', + "cedil": '\U000000B8', + "cent": '\U000000A2', + "copy": '\U000000A9', + "curren": '\U000000A4', + "deg": '\U000000B0', + "divide": '\U000000F7', + "eacute": '\U000000E9', + "ecirc": '\U000000EA', + "egrave": '\U000000E8', + "eth": '\U000000F0', + "euml": '\U000000EB', + "frac12": '\U000000BD', + "frac14": '\U000000BC', + "frac34": '\U000000BE', + "gt": '\U0000003E', + "iacute": '\U000000ED', + "icirc": '\U000000EE', + "iexcl": '\U000000A1', + "igrave": '\U000000EC', + "iquest": '\U000000BF', + "iuml": '\U000000EF', + "laquo": '\U000000AB', + "lt": '\U0000003C', + "macr": '\U000000AF', + "micro": '\U000000B5', + "middot": '\U000000B7', + "nbsp": '\U000000A0', + "not": '\U000000AC', + "ntilde": '\U000000F1', + "oacute": '\U000000F3', + "ocirc": '\U000000F4', + "ograve": '\U000000F2', + "ordf": '\U000000AA', + "ordm": '\U000000BA', + "oslash": '\U000000F8', + "otilde": '\U000000F5', + "ouml": '\U000000F6', + "para": '\U000000B6', + "plusmn": '\U000000B1', + "pound": '\U000000A3', + "quot": '\U00000022', + "raquo": '\U000000BB', + "reg": '\U000000AE', + "sect": '\U000000A7', + "shy": '\U000000AD', + "sup1": '\U000000B9', + "sup2": '\U000000B2', + "sup3": '\U000000B3', + "szlig": '\U000000DF', + "thorn": '\U000000FE', + "times": '\U000000D7', + "uacute": '\U000000FA', + "ucirc": '\U000000FB', + "ugrave": '\U000000F9', + "uml": '\U000000A8', + "uuml": '\U000000FC', + "yacute": '\U000000FD', + "yen": '\U000000A5', + "yuml": '\U000000FF', +} + +// HTML entities that are two unicode codepoints. +var entity2 = map[string][2]int{ + // TODO(nigeltao): Handle replacements that are wider than their names. + // "nLt;": {'\u226A', '\u20D2'}, + // "nGt;": {'\u226B', '\u20D2'}, + "NotEqualTilde;": {'\u2242', '\u0338'}, + "NotGreaterFullEqual;": {'\u2267', '\u0338'}, + "NotGreaterGreater;": {'\u226B', '\u0338'}, + "NotGreaterSlantEqual;": {'\u2A7E', '\u0338'}, + "NotHumpDownHump;": {'\u224E', '\u0338'}, + "NotHumpEqual;": {'\u224F', '\u0338'}, + "NotLeftTriangleBar;": {'\u29CF', '\u0338'}, + "NotLessLess;": {'\u226A', '\u0338'}, + "NotLessSlantEqual;": {'\u2A7D', '\u0338'}, + "NotNestedGreaterGreater;": {'\u2AA2', '\u0338'}, + "NotNestedLessLess;": {'\u2AA1', '\u0338'}, + "NotPrecedesEqual;": {'\u2AAF', '\u0338'}, + "NotRightTriangleBar;": {'\u29D0', '\u0338'}, + "NotSquareSubset;": {'\u228F', '\u0338'}, + "NotSquareSuperset;": {'\u2290', '\u0338'}, + "NotSubset;": {'\u2282', '\u20D2'}, + "NotSucceedsEqual;": {'\u2AB0', '\u0338'}, + "NotSucceedsTilde;": {'\u227F', '\u0338'}, + "NotSuperset;": {'\u2283', '\u20D2'}, + "ThickSpace;": {'\u205F', '\u200A'}, + "acE;": {'\u223E', '\u0333'}, + "bne;": {'\u003D', '\u20E5'}, + "bnequiv;": {'\u2261', '\u20E5'}, + "caps;": {'\u2229', '\uFE00'}, + "cups;": {'\u222A', '\uFE00'}, + "fjlig;": {'\u0066', '\u006A'}, + "gesl;": {'\u22DB', '\uFE00'}, + "gvertneqq;": {'\u2269', '\uFE00'}, + "gvnE;": {'\u2269', '\uFE00'}, + "lates;": {'\u2AAD', '\uFE00'}, + "lesg;": {'\u22DA', '\uFE00'}, + "lvertneqq;": {'\u2268', '\uFE00'}, + "lvnE;": {'\u2268', '\uFE00'}, + "nGg;": {'\u22D9', '\u0338'}, + "nGtv;": {'\u226B', '\u0338'}, + "nLl;": {'\u22D8', '\u0338'}, + "nLtv;": {'\u226A', '\u0338'}, + "nang;": {'\u2220', '\u20D2'}, + "napE;": {'\u2A70', '\u0338'}, + "napid;": {'\u224B', '\u0338'}, + "nbump;": {'\u224E', '\u0338'}, + "nbumpe;": {'\u224F', '\u0338'}, + "ncongdot;": {'\u2A6D', '\u0338'}, + "nedot;": {'\u2250', '\u0338'}, + "nesim;": {'\u2242', '\u0338'}, + "ngE;": {'\u2267', '\u0338'}, + "ngeqq;": {'\u2267', '\u0338'}, + "ngeqslant;": {'\u2A7E', '\u0338'}, + "nges;": {'\u2A7E', '\u0338'}, + "nlE;": {'\u2266', '\u0338'}, + "nleqq;": {'\u2266', '\u0338'}, + "nleqslant;": {'\u2A7D', '\u0338'}, + "nles;": {'\u2A7D', '\u0338'}, + "notinE;": {'\u22F9', '\u0338'}, + "notindot;": {'\u22F5', '\u0338'}, + "nparsl;": {'\u2AFD', '\u20E5'}, + "npart;": {'\u2202', '\u0338'}, + "npre;": {'\u2AAF', '\u0338'}, + "npreceq;": {'\u2AAF', '\u0338'}, + "nrarrc;": {'\u2933', '\u0338'}, + "nrarrw;": {'\u219D', '\u0338'}, + "nsce;": {'\u2AB0', '\u0338'}, + "nsubE;": {'\u2AC5', '\u0338'}, + "nsubset;": {'\u2282', '\u20D2'}, + "nsubseteqq;": {'\u2AC5', '\u0338'}, + "nsucceq;": {'\u2AB0', '\u0338'}, + "nsupE;": {'\u2AC6', '\u0338'}, + "nsupset;": {'\u2283', '\u20D2'}, + "nsupseteqq;": {'\u2AC6', '\u0338'}, + "nvap;": {'\u224D', '\u20D2'}, + "nvge;": {'\u2265', '\u20D2'}, + "nvgt;": {'\u003E', '\u20D2'}, + "nvle;": {'\u2264', '\u20D2'}, + "nvlt;": {'\u003C', '\u20D2'}, + "nvltrie;": {'\u22B4', '\u20D2'}, + "nvrtrie;": {'\u22B5', '\u20D2'}, + "nvsim;": {'\u223C', '\u20D2'}, + "race;": {'\u223D', '\u0331'}, + "smtes;": {'\u2AAC', '\uFE00'}, + "sqcaps;": {'\u2293', '\uFE00'}, + "sqcups;": {'\u2294', '\uFE00'}, + "varsubsetneq;": {'\u228A', '\uFE00'}, + "varsubsetneqq;": {'\u2ACB', '\uFE00'}, + "varsupsetneq;": {'\u228B', '\uFE00'}, + "varsupsetneqq;": {'\u2ACC', '\uFE00'}, + "vnsub;": {'\u2282', '\u20D2'}, + "vnsup;": {'\u2283', '\u20D2'}, + "vsubnE;": {'\u2ACB', '\uFE00'}, + "vsubne;": {'\u228A', '\uFE00'}, + "vsupnE;": {'\u2ACC', '\uFE00'}, + "vsupne;": {'\u228B', '\uFE00'}, +} diff --git a/src/pkg/html/entity_test.go b/src/pkg/html/entity_test.go new file mode 100644 index 000000000..a1eb4d4f0 --- /dev/null +++ b/src/pkg/html/entity_test.go @@ -0,0 +1,26 @@ +// 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 html + +import ( + "testing" + "utf8" +) + +func TestEntityLength(t *testing.T) { + // We verify that the length of UTF-8 encoding of each value is <= 1 + len(key). + // The +1 comes from the leading "&". This property implies that the length of + // unescaped text is <= the length of escaped text. + for k, v := range entity { + if 1+len(k) < utf8.RuneLen(v) { + t.Error("escaped entity &" + k + " is shorter than its UTF-8 encoding " + string(v)) + } + } + for k, v := range entity2 { + if 1+len(k) < utf8.RuneLen(v[0])+utf8.RuneLen(v[1]) { + t.Error("escaped entity &" + k + " is shorter than its UTF-8 encoding " + string(v[0]) + string(v[1])) + } + } +} diff --git a/src/pkg/html/escape.go b/src/pkg/html/escape.go new file mode 100644 index 000000000..2799f6908 --- /dev/null +++ b/src/pkg/html/escape.go @@ -0,0 +1,224 @@ +// 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 html + +import ( + "bytes" + "strings" + "utf8" +) + +// These replacements permit compatibility with old numeric entities that +// assumed Windows-1252 encoding. +// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference +var replacementTable = [...]int{ + '\u20AC', // First entry is what 0x80 should be replaced with. + '\u0081', + '\u201A', + '\u0192', + '\u201E', + '\u2026', + '\u2020', + '\u2021', + '\u02C6', + '\u2030', + '\u0160', + '\u2039', + '\u0152', + '\u008D', + '\u017D', + '\u008F', + '\u0090', + '\u2018', + '\u2019', + '\u201C', + '\u201D', + '\u2022', + '\u2013', + '\u2014', + '\u02DC', + '\u2122', + '\u0161', + '\u203A', + '\u0153', + '\u009D', + '\u017E', + '\u0178', // Last entry is 0x9F. + // 0x00->'\uFFFD' is handled programmatically. + // 0x0D->'\u000D' is a no-op. +} + +// unescapeEntity reads an entity like "<" from b[src:] and writes the +// corresponding "<" to b[dst:], returning the incremented dst and src cursors. +// Precondition: b[src] == '&' && dst <= src. +func unescapeEntity(b []byte, dst, src int) (dst1, src1 int) { + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference + + // i starts at 1 because we already know that s[0] == '&'. + i, s := 1, b[src:] + + if len(s) <= 1 { + b[dst] = b[src] + return dst + 1, src + 1 + } + + if s[i] == '#' { + if len(s) <= 3 { // We need to have at least "&#.". + b[dst] = b[src] + return dst + 1, src + 1 + } + i++ + c := s[i] + hex := false + if c == 'x' || c == 'X' { + hex = true + i++ + } + + x := 0 + for i < len(s) { + c = s[i] + i++ + if hex { + if '0' <= c && c <= '9' { + x = 16*x + int(c) - '0' + continue + } else if 'a' <= c && c <= 'f' { + x = 16*x + int(c) - 'a' + 10 + continue + } else if 'A' <= c && c <= 'F' { + x = 16*x + int(c) - 'A' + 10 + continue + } + } else if '0' <= c && c <= '9' { + x = 10*x + int(c) - '0' + continue + } + if c != ';' { + i-- + } + break + } + + if i <= 3 { // No characters matched. + b[dst] = b[src] + return dst + 1, src + 1 + } + + if 0x80 <= x && x <= 0x9F { + // Replace characters from Windows-1252 with UTF-8 equivalents. + x = replacementTable[x-0x80] + } else if x == 0 || (0xD800 <= x && x <= 0xDFFF) || x > 0x10FFFF { + // Replace invalid characters with the replacement character. + x = '\uFFFD' + } + + return dst + utf8.EncodeRune(b[dst:], x), src + i + } + + // Consume the maximum number of characters possible, with the + // consumed characters matching one of the named references. + + // TODO(nigeltao): unescape("¬it;") should be "¬it;" + for i < len(s) { + c := s[i] + i++ + // Lower-cased characters are more common in entities, so we check for them first. + if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' { + continue + } + if c != ';' { + i-- + } + break + } + + entityName := string(s[1:i]) + if x := entity[entityName]; x != 0 { + return dst + utf8.EncodeRune(b[dst:], x), src + i + } else if x := entity2[entityName]; x[0] != 0 { // Check if it's a two-character entity. + dst1 := dst + utf8.EncodeRune(b[dst:], x[0]) + return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i + } + + dst1, src1 = dst+i, src+i + copy(b[dst:dst1], b[src:src1]) + return dst1, src1 +} + +// unescape unescapes b's entities in-place, so that "a<b" becomes "a"` + +func escape(buf *bytes.Buffer, s string) { + i := strings.IndexAny(s, escapedChars) + for i != -1 { + buf.WriteString(s[0:i]) + var esc string + switch s[i] { + case '&': + esc = "&" + case '\'': + esc = "'" + case '<': + esc = "<" + case '>': + esc = ">" + case '"': + esc = """ + default: + panic("unrecognized escape character") + } + s = s[i+1:] + buf.WriteString(esc) + i = strings.IndexAny(s, escapedChars) + } + buf.WriteString(s) +} + +// EscapeString escapes special characters like "<" to become "<". It +// escapes only five such characters: amp, apos, lt, gt and quot. +// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't +// always true. +func EscapeString(s string) string { + if strings.IndexAny(s, escapedChars) == -1 { + return s + } + buf := bytes.NewBuffer(nil) + escape(buf, s) + return buf.String() +} + +// UnescapeString unescapes entities like "<" to become "<". It unescapes a +// larger range of entities than EscapeString escapes. For example, "á" +// unescapes to "á", as does "á" and "&xE1;". +// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't +// always true. +func UnescapeString(s string) string { + for _, c := range s { + if c == '&' { + return string(unescape([]byte(s))) + } + } + return s +} diff --git a/src/pkg/html/parse.go b/src/pkg/html/parse.go new file mode 100644 index 000000000..2ef90a873 --- /dev/null +++ b/src/pkg/html/parse.go @@ -0,0 +1,666 @@ +// 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 html + +import ( + "io" + "os" +) + +// A NodeType is the type of a Node. +type NodeType int + +const ( + ErrorNode NodeType = iota + TextNode + DocumentNode + ElementNode + CommentNode +) + +// A Node consists of a NodeType and some Data (tag name for element nodes, +// content for text) and are part of a tree of Nodes. Element nodes may also +// contain a slice of Attributes. Data is unescaped, so that it looks like +// "a are re-interpreted as a two-token sequence: + //
    followed by . hasSelfClosingToken is true if we have just read + // the synthetic start tag and the next one due is the matching end tag. + hasSelfClosingToken bool + // doc is the document root element. + doc *Node + // The stack of open elements (section 10.2.3.2). + stack []*Node + // Element pointers (section 10.2.3.4). + head, form *Node + // Other parsing state flags (section 10.2.3.5). + scripting, framesetOK bool +} + +// push pushes onto the stack of open elements. +func (p *parser) push(n *Node) { + p.stack = append(p.stack, n) +} + +// top returns the top of the stack of open elements. +// This is also known as the current node. +func (p *parser) top() *Node { + if n := len(p.stack); n > 0 { + return p.stack[n-1] + } + return p.doc +} + +// pop pops the top of the stack of open elements. +// It will panic if the stack is empty. +func (p *parser) pop() *Node { + n := len(p.stack) + ret := p.stack[n-1] + p.stack = p.stack[:n-1] + return ret +} + +// stopTags for use in popUntil. These come from section 10.2.3.2. +var ( + defaultScopeStopTags = []string{"applet", "caption", "html", "table", "td", "th", "marquee", "object"} + listItemScopeStopTags = []string{"applet", "caption", "html", "table", "td", "th", "marquee", "object", "ol", "ul"} + buttonScopeStopTags = []string{"applet", "caption", "html", "table", "td", "th", "marquee", "object", "button"} + tableScopeStopTags = []string{"html", "table"} +) + +// popUntil pops the stack of open elements at the highest element whose tag +// is in matchTags, provided there is no higher element in stopTags. It returns +// whether or not there was such an element. If there was not, popUntil leaves +// the stack unchanged. +// +// For example, if the stack was: +// ["html", "body", "font", "table", "b", "i", "u"] +// then popUntil([]string{"html, "table"}, "font") would return false, but +// popUntil([]string{"html, "table"}, "i") would return true and the resultant +// stack would be: +// ["html", "body", "font", "table", "b"] +// +// If an element's tag is in both stopTags and matchTags, then the stack will +// be popped and the function returns true (provided, of course, there was no +// higher element in the stack that was also in stopTags). For example, +// popUntil([]string{"html, "table"}, "table") would return true and leave: +// ["html", "body", "font"] +func (p *parser) popUntil(stopTags []string, matchTags ...string) bool { + for i := len(p.stack) - 1; i >= 0; i-- { + tag := p.stack[i].Data + for _, t := range matchTags { + if t == tag { + p.stack = p.stack[:i] + return true + } + } + for _, t := range stopTags { + if t == tag { + return false + } + } + } + return false +} + +// addChild adds a child node n to the top element, and pushes n if it is an +// element node (text nodes are not part of the stack of open elements). +func (p *parser) addChild(n *Node) { + m := p.top() + m.Child = append(m.Child, n) + if n.Type == ElementNode { + p.push(n) + } +} + +// addText calls addChild with a text node. +func (p *parser) addText(text string) { + // TODO: merge s with previous text, if the preceding node is a text node. + // TODO: distinguish whitespace text from others. + p.addChild(&Node{ + Type: TextNode, + Data: text, + }) +} + +// addElement calls addChild with an element node. +func (p *parser) addElement(tag string, attr []Attribute) { + p.addChild(&Node{ + Type: ElementNode, + Data: tag, + Attr: attr, + }) +} + +// Section 10.2.3.3. +func (p *parser) addFormattingElement(tag string, attr []Attribute) { + p.addElement(tag, attr) + // TODO. +} + +// Section 10.2.3.3. +func (p *parser) reconstructActiveFormattingElements() { + // TODO. +} + +// read reads the next token. This is usually from the tokenizer, but it may +// be the synthesized end tag implied by a self-closing tag. +func (p *parser) read() os.Error { + if p.hasSelfClosingToken { + p.hasSelfClosingToken = false + p.tok.Type = EndTagToken + p.tok.Attr = nil + return nil + } + p.tokenizer.Next() + p.tok = p.tokenizer.Token() + switch p.tok.Type { + case ErrorToken: + return p.tokenizer.Error() + case SelfClosingTagToken: + p.hasSelfClosingToken = true + p.tok.Type = StartTagToken + } + return nil +} + +// Section 10.2.4. +func (p *parser) acknowledgeSelfClosingTag() { + p.hasSelfClosingToken = false +} + +// An insertion mode (section 10.2.3.1) is the state transition function from +// a particular state in the HTML5 parser's state machine. It updates the +// parser's fields depending on parser.token (where ErrorToken means EOF). In +// addition to returning the next insertionMode state, it also returns whether +// the token was consumed. +type insertionMode func(*parser) (insertionMode, bool) + +// useTheRulesFor runs the delegate insertionMode over p, returning the actual +// insertionMode unless the delegate caused a state transition. +// Section 10.2.3.1, "using the rules for". +func useTheRulesFor(p *parser, actual, delegate insertionMode) (insertionMode, bool) { + im, consumed := delegate(p) + if im != delegate { + return im, consumed + } + return actual, consumed +} + +// Section 10.2.5.4. +func initialIM(p *parser) (insertionMode, bool) { + // TODO: check p.tok for DOCTYPE. + return beforeHTMLIM, false +} + +// Section 10.2.5.5. +func beforeHTMLIM(p *parser) (insertionMode, bool) { + var ( + add bool + attr []Attribute + implied bool + ) + switch p.tok.Type { + case ErrorToken: + implied = true + case TextToken: + // TODO: distinguish whitespace text from others. + implied = true + case StartTagToken: + if p.tok.Data == "html" { + add = true + attr = p.tok.Attr + } else { + implied = true + } + case EndTagToken: + switch p.tok.Data { + case "head", "body", "html", "br": + implied = true + default: + // Ignore the token. + } + } + if add || implied { + p.addElement("html", attr) + } + return beforeHeadIM, !implied +} + +// Section 10.2.5.6. +func beforeHeadIM(p *parser) (insertionMode, bool) { + var ( + add bool + attr []Attribute + implied bool + ) + switch p.tok.Type { + case ErrorToken: + implied = true + case TextToken: + // TODO: distinguish whitespace text from others. + implied = true + case StartTagToken: + switch p.tok.Data { + case "head": + add = true + attr = p.tok.Attr + case "html": + return useTheRulesFor(p, beforeHeadIM, inBodyIM) + default: + implied = true + } + case EndTagToken: + switch p.tok.Data { + case "head", "body", "html", "br": + implied = true + default: + // Ignore the token. + } + } + if add || implied { + p.addElement("head", attr) + } + return inHeadIM, !implied +} + +// Section 10.2.5.7. +func inHeadIM(p *parser) (insertionMode, bool) { + var ( + pop bool + implied bool + ) + switch p.tok.Type { + case ErrorToken, TextToken: + implied = true + case StartTagToken: + switch p.tok.Data { + case "meta": + // TODO. + case "script": + // TODO. + default: + implied = true + } + case EndTagToken: + if p.tok.Data == "head" { + pop = true + } + // TODO. + } + if pop || implied { + n := p.pop() + if n.Data != "head" { + panic("html: bad parser state") + } + return afterHeadIM, !implied + } + return inHeadIM, !implied +} + +// Section 10.2.5.9. +func afterHeadIM(p *parser) (insertionMode, bool) { + var ( + add bool + attr []Attribute + framesetOK bool + implied bool + ) + switch p.tok.Type { + case ErrorToken, TextToken: + implied = true + framesetOK = true + case StartTagToken: + switch p.tok.Data { + case "html": + // TODO. + case "body": + add = true + attr = p.tok.Attr + framesetOK = false + case "frameset": + // TODO. + case "base", "basefont", "bgsound", "link", "meta", "noframes", "script", "style", "title": + // TODO. + case "head": + // TODO. + default: + implied = true + framesetOK = true + } + case EndTagToken: + // TODO. + } + if add || implied { + p.addElement("body", attr) + p.framesetOK = framesetOK + } + return inBodyIM, !implied +} + +// Section 10.2.5.10. +func inBodyIM(p *parser) (insertionMode, bool) { + var endP bool + switch p.tok.Type { + case TextToken: + p.addText(p.tok.Data) + p.framesetOK = false + case StartTagToken: + switch p.tok.Data { + case "address", "article", "aside", "blockquote", "center", "details", "dir", "div", "dl", "fieldset", "figcaption", "figure", "footer", "header", "hgroup", "menu", "nav", "ol", "p", "section", "summary", "ul": + // TODO: Do the proper "does the stack of open elements has a p element in button scope" algorithm in section 10.2.3.2. + n := p.top() + if n.Type == ElementNode && n.Data == "p" { + endP = true + } else { + p.addElement(p.tok.Data, p.tok.Attr) + } + case "h1", "h2", "h3", "h4", "h5", "h6": + // TODO: auto-insert

    if necessary. + switch n := p.top(); n.Data { + case "h1", "h2", "h3", "h4", "h5", "h6": + p.pop() + } + p.addElement(p.tok.Data, p.tok.Attr) + case "b", "big", "code", "em", "font", "i", "s", "small", "strike", "strong", "tt", "u": + p.reconstructActiveFormattingElements() + p.addFormattingElement(p.tok.Data, p.tok.Attr) + case "area", "br", "embed", "img", "input", "keygen", "wbr": + p.reconstructActiveFormattingElements() + p.addElement(p.tok.Data, p.tok.Attr) + p.pop() + p.acknowledgeSelfClosingTag() + p.framesetOK = false + case "table": + // TODO: auto-insert

    if necessary, depending on quirks mode. + p.addElement(p.tok.Data, p.tok.Attr) + p.framesetOK = false + return inTableIM, true + case "hr": + // TODO: auto-insert

    if necessary. + p.addElement(p.tok.Data, p.tok.Attr) + p.pop() + p.acknowledgeSelfClosingTag() + p.framesetOK = false + default: + // TODO. + } + case EndTagToken: + switch p.tok.Data { + case "body": + // TODO: autoclose the stack of open elements. + return afterBodyIM, true + case "a", "b", "big", "code", "em", "font", "i", "nobr", "s", "small", "strike", "strong", "tt", "u": + // TODO: implement the "adoption agency" algorithm: + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#adoptionAgency + if p.tok.Data == p.top().Data { + p.pop() + } + default: + // TODO. + } + } + if endP { + // TODO: do the proper algorithm. + n := p.pop() + if n.Type != ElementNode || n.Data != "p" { + panic("unreachable") + } + } + return inBodyIM, !endP +} + +// Section 10.2.5.12. +func inTableIM(p *parser) (insertionMode, bool) { + var ( + add bool + data string + attr []Attribute + consumed bool + ) + switch p.tok.Type { + case ErrorToken: + // Stop parsing. + return nil, true + case TextToken: + // TODO. + case StartTagToken: + switch p.tok.Data { + case "tbody", "tfoot", "thead": + add = true + data = p.tok.Data + attr = p.tok.Attr + consumed = true + case "td", "th", "tr": + add = true + data = "tbody" + default: + // TODO. + } + case EndTagToken: + switch p.tok.Data { + case "table": + if p.popUntil(tableScopeStopTags, "table") { + // TODO: "reset the insertion mode appropriately" as per 10.2.3.1. + return inBodyIM, false + } + // Ignore the token. + return inTableIM, true + case "body", "caption", "col", "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr": + // Ignore the token. + return inTableIM, true + } + } + if add { + // TODO: clear the stack back to a table context. + p.addElement(data, attr) + return inTableBodyIM, consumed + } + // TODO: return useTheRulesFor(inTableIM, inBodyIM, p) unless etc. etc. foster parenting. + return inTableIM, true +} + +// Section 10.2.5.16. +func inTableBodyIM(p *parser) (insertionMode, bool) { + var ( + add bool + data string + attr []Attribute + consumed bool + ) + switch p.tok.Type { + case ErrorToken: + // TODO. + case TextToken: + // TODO. + case StartTagToken: + switch p.tok.Data { + case "tr": + add = true + data = p.tok.Data + attr = p.tok.Attr + consumed = true + case "td", "th": + add = true + data = "tr" + consumed = false + default: + // TODO. + } + case EndTagToken: + switch p.tok.Data { + case "table": + if p.popUntil(tableScopeStopTags, "tbody", "thead", "tfoot") { + return inTableIM, false + } + // Ignore the token. + return inTableBodyIM, true + case "body", "caption", "col", "colgroup", "html", "td", "th", "tr": + // Ignore the token. + return inTableBodyIM, true + } + } + if add { + // TODO: clear the stack back to a table body context. + p.addElement(data, attr) + return inRowIM, consumed + } + return useTheRulesFor(p, inTableBodyIM, inTableIM) +} + +// Section 10.2.5.17. +func inRowIM(p *parser) (insertionMode, bool) { + switch p.tok.Type { + case ErrorToken: + // TODO. + case TextToken: + // TODO. + case StartTagToken: + switch p.tok.Data { + case "td", "th": + // TODO: clear the stack back to a table row context. + p.addElement(p.tok.Data, p.tok.Attr) + // TODO: insert a marker at the end of the list of active formatting elements. + return inCellIM, true + default: + // TODO. + } + case EndTagToken: + switch p.tok.Data { + case "tr": + // TODO. + case "table": + if p.popUntil(tableScopeStopTags, "tr") { + return inTableBodyIM, false + } + // Ignore the token. + return inRowIM, true + case "tbody", "tfoot", "thead": + // TODO. + case "body", "caption", "col", "colgroup", "html", "td", "th": + // Ignore the token. + return inRowIM, true + default: + // TODO. + } + } + return useTheRulesFor(p, inRowIM, inTableIM) +} + +// Section 10.2.5.18. +func inCellIM(p *parser) (insertionMode, bool) { + var ( + closeTheCellAndReprocess bool + ) + switch p.tok.Type { + case StartTagToken: + switch p.tok.Data { + case "caption", "col", "colgroup", "tbody", "td", "tfoot", "th", "thead", "tr": + // TODO: check for "td" or "th" in table scope. + closeTheCellAndReprocess = true + } + case EndTagToken: + switch p.tok.Data { + case "td", "th": + // TODO. + case "body", "caption", "col", "colgroup", "html": + // TODO. + case "table", "tbody", "tfoot", "thead", "tr": + // TODO: check for matching element in table scope. + closeTheCellAndReprocess = true + } + } + if closeTheCellAndReprocess { + if p.popUntil(tableScopeStopTags, "td") || p.popUntil(tableScopeStopTags, "th") { + // TODO: clear the list of active formatting elements up to the last marker. + return inRowIM, false + } + } + return useTheRulesFor(p, inCellIM, inBodyIM) +} + +// Section 10.2.5.22. +func afterBodyIM(p *parser) (insertionMode, bool) { + switch p.tok.Type { + case ErrorToken: + // TODO. + case TextToken: + // TODO. + case StartTagToken: + // TODO. + case EndTagToken: + switch p.tok.Data { + case "html": + // TODO: autoclose the stack of open elements. + return afterAfterBodyIM, true + default: + // TODO. + } + } + return afterBodyIM, true +} + +// Section 10.2.5.25. +func afterAfterBodyIM(p *parser) (insertionMode, bool) { + switch p.tok.Type { + case ErrorToken: + // Stop parsing. + return nil, true + case TextToken: + // TODO. + case StartTagToken: + if p.tok.Data == "html" { + return useTheRulesFor(p, afterAfterBodyIM, inBodyIM) + } + } + return inBodyIM, false +} + +// Parse returns the parse tree for the HTML from the given Reader. +// The input is assumed to be UTF-8 encoded. +func Parse(r io.Reader) (*Node, os.Error) { + p := &parser{ + tokenizer: NewTokenizer(r), + doc: &Node{ + Type: DocumentNode, + }, + scripting: true, + framesetOK: true, + } + // Iterate until EOF. Any other error will cause an early return. + im, consumed := initialIM, true + for { + if consumed { + if err := p.read(); err != nil { + if err == os.EOF { + break + } + return nil, err + } + } + im, consumed = im(p) + } + // Loop until the final token (the ErrorToken signifying EOF) is consumed. + for { + if im, consumed = im(p); consumed { + break + } + } + return p.doc, nil +} diff --git a/src/pkg/html/parse_test.go b/src/pkg/html/parse_test.go new file mode 100644 index 000000000..d153533b5 --- /dev/null +++ b/src/pkg/html/parse_test.go @@ -0,0 +1,158 @@ +// 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 html + +import ( + "bufio" + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "strings" + "testing" +) + +type devNull struct{} + +func (devNull) Write(p []byte) (int, os.Error) { + return len(p), nil +} + +func pipeErr(err os.Error) io.Reader { + pr, pw := io.Pipe() + pw.CloseWithError(err) + return pr +} + +func readDat(filename string, c chan io.Reader) { + f, err := os.Open("testdata/webkit/"+filename, os.O_RDONLY, 0600) + if err != nil { + c <- pipeErr(err) + return + } + defer f.Close() + + // Loop through the lines of the file. Each line beginning with "#" denotes + // a new section, which is returned as a separate io.Reader. + r := bufio.NewReader(f) + var pw *io.PipeWriter + for { + line, err := r.ReadSlice('\n') + if err != nil { + if pw != nil { + pw.CloseWithError(err) + pw = nil + } else { + c <- pipeErr(err) + } + return + } + if len(line) == 0 { + continue + } + if line[0] == '#' { + if pw != nil { + pw.Close() + } + var pr *io.PipeReader + pr, pw = io.Pipe() + c <- pr + continue + } + if line[0] != '|' { + // Strip the trailing '\n'. + line = line[:len(line)-1] + } + if pw != nil { + if _, err := pw.Write(line); err != nil { + pw.CloseWithError(err) + pw = nil + } + } + } +} + +func dumpLevel(w io.Writer, n *Node, level int) os.Error { + io.WriteString(w, "| ") + for i := 0; i < level; i++ { + io.WriteString(w, " ") + } + switch n.Type { + case ErrorNode: + return os.NewError("unexpected ErrorNode") + case DocumentNode: + return os.NewError("unexpected DocumentNode") + case ElementNode: + fmt.Fprintf(w, "<%s>", EscapeString(n.Data)) + case TextNode: + fmt.Fprintf(w, "%q", EscapeString(n.Data)) + case CommentNode: + return os.NewError("COMMENT") + default: + return os.NewError("unknown node type") + } + io.WriteString(w, "\n") + for _, c := range n.Child { + if err := dumpLevel(w, c, level+1); err != nil { + return err + } + } + return nil +} + +func dump(n *Node) (string, os.Error) { + if n == nil || len(n.Child) == 0 { + return "", nil + } + b := bytes.NewBuffer(nil) + for _, child := range n.Child { + if err := dumpLevel(b, child, 0); err != nil { + return "", err + } + } + return b.String(), nil +} + +func TestParser(t *testing.T) { + // TODO(nigeltao): Process all the .dat files, not just the first one. + filenames := []string{ + "tests1.dat", + } + for _, filename := range filenames { + rc := make(chan io.Reader) + go readDat(filename, rc) + // TODO(nigeltao): Process all test cases, not just a subset. + for i := 0; i < 22; i++ { + // Parse the #data section. + b, err := ioutil.ReadAll(<-rc) + if err != nil { + t.Fatal(err) + } + text := string(b) + doc, err := Parse(strings.NewReader(text)) + if err != nil { + t.Fatal(err) + } + actual, err := dump(doc) + if err != nil { + t.Fatal(err) + } + // Skip the #error section. + if _, err := io.Copy(devNull{}, <-rc); err != nil { + t.Fatal(err) + } + // Compare the parsed tree to the #document section. + b, err = ioutil.ReadAll(<-rc) + if err != nil { + t.Fatal(err) + } + expected := string(b) + if actual != expected { + t.Errorf("%s test #%d %q, actual vs expected:\n----\n%s----\n%s----", filename, i, text, actual, expected) + } + } + } +} diff --git a/src/pkg/html/token.go b/src/pkg/html/token.go new file mode 100644 index 000000000..d63883850 --- /dev/null +++ b/src/pkg/html/token.go @@ -0,0 +1,398 @@ +// 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 html + +import ( + "bytes" + "io" + "os" + "strconv" +) + +// A TokenType is the type of a Token. +type TokenType int + +const ( + // ErrorToken means that an error occurred during tokenization. + ErrorToken TokenType = iota + // TextToken means a text node. + TextToken + // A StartTagToken looks like . + StartTagToken + // An EndTagToken looks like . + EndTagToken + // A SelfClosingTagToken tag looks like
    . + SelfClosingTagToken +) + +// String returns a string representation of the TokenType. +func (t TokenType) String() string { + switch t { + case ErrorToken: + return "Error" + case TextToken: + return "Text" + case StartTagToken: + return "StartTag" + case EndTagToken: + return "EndTag" + case SelfClosingTagToken: + return "SelfClosingTag" + } + return "Invalid(" + strconv.Itoa(int(t)) + ")" +} + +// An Attribute is an attribute key-value pair. Key is alphabetic (and hence +// does not contain escapable characters like '&', '<' or '>'), and Val is +// unescaped (it looks like "a" + case EndTagToken: + return "" + case SelfClosingTagToken: + return "<" + t.tagString() + "/>" + } + return "Invalid(" + strconv.Itoa(int(t.Type)) + ")" +} + +// A Tokenizer returns a stream of HTML Tokens. +type Tokenizer struct { + // r is the source of the HTML text. + r io.Reader + // tt is the TokenType of the most recently read token. If tt == Error + // then err is the error associated with trying to read that token. + tt TokenType + err os.Error + // buf[p0:p1] holds the raw data of the most recent token. + // buf[p1:] is buffered input that will yield future tokens. + p0, p1 int + buf []byte +} + +// Error returns the error associated with the most recent ErrorToken token. +// This is typically os.EOF, meaning the end of tokenization. +func (z *Tokenizer) Error() os.Error { + if z.tt != ErrorToken { + return nil + } + return z.err +} + +// Raw returns the unmodified text of the current token. Calling Next, Token, +// Text, TagName or TagAttr may change the contents of the returned slice. +func (z *Tokenizer) Raw() []byte { + return z.buf[z.p0:z.p1] +} + +// readByte returns the next byte from the input stream, doing a buffered read +// from z.r into z.buf if necessary. z.buf[z.p0:z.p1] remains a contiguous byte +// slice that holds all the bytes read so far for the current token. +func (z *Tokenizer) readByte() (byte, os.Error) { + if z.p1 >= len(z.buf) { + // Our buffer is exhausted and we have to read from z.r. + // We copy z.buf[z.p0:z.p1] to the beginning of z.buf. If the length + // z.p1 - z.p0 is more than half the capacity of z.buf, then we + // allocate a new buffer before the copy. + c := cap(z.buf) + d := z.p1 - z.p0 + var buf1 []byte + if 2*d > c { + buf1 = make([]byte, d, 2*c) + } else { + buf1 = z.buf[0:d] + } + copy(buf1, z.buf[z.p0:z.p1]) + z.p0, z.p1, z.buf = 0, d, buf1[0:d] + // Now that we have copied the live bytes to the start of the buffer, + // we read from z.r into the remainder. + n, err := z.r.Read(buf1[d:cap(buf1)]) + if err != nil { + return 0, err + } + z.buf = buf1[0 : d+n] + } + x := z.buf[z.p1] + z.p1++ + return x, nil +} + +// readTo keeps reading bytes until x is found. +func (z *Tokenizer) readTo(x uint8) os.Error { + for { + c, err := z.readByte() + if err != nil { + return err + } + switch c { + case x: + return nil + case '\\': + _, err = z.readByte() + if err != nil { + return err + } + } + } + panic("unreachable") +} + +// nextTag returns the next TokenType starting from the tag open state. +func (z *Tokenizer) nextTag() (tt TokenType, err os.Error) { + c, err := z.readByte() + if err != nil { + return ErrorToken, err + } + switch { + case c == '/': + tt = EndTagToken + // Lower-cased characters are more common in tag names, so we check for them first. + case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z': + tt = StartTagToken + case c == '!': + return ErrorToken, os.NewError("html: TODO(nigeltao): implement comments") + case c == '?': + return ErrorToken, os.NewError("html: TODO(nigeltao): implement XML processing instructions") + default: + return ErrorToken, os.NewError("html: TODO(nigeltao): handle malformed tags") + } + for { + c, err := z.readByte() + if err != nil { + return TextToken, err + } + switch c { + case '"': + err = z.readTo('"') + if err != nil { + return TextToken, err + } + case '\'': + err = z.readTo('\'') + if err != nil { + return TextToken, err + } + case '>': + if z.buf[z.p1-2] == '/' && tt == StartTagToken { + return SelfClosingTagToken, nil + } + return tt, nil + } + } + panic("unreachable") +} + +// Next scans the next token and returns its type. +func (z *Tokenizer) Next() TokenType { + if z.err != nil { + z.tt = ErrorToken + return z.tt + } + z.p0 = z.p1 + c, err := z.readByte() + if err != nil { + z.tt, z.err = ErrorToken, err + return z.tt + } + if c == '<' { + z.tt, z.err = z.nextTag() + return z.tt + } + for { + c, err := z.readByte() + if err != nil { + z.tt, z.err = ErrorToken, err + if err == os.EOF { + z.tt = TextToken + } + return z.tt + } + if c == '<' { + z.p1-- + z.tt = TextToken + return z.tt + } + } + panic("unreachable") +} + +// trim returns the largest j such that z.buf[i:j] contains only white space, +// or only white space plus the final ">" or "/>" of the raw data. +func (z *Tokenizer) trim(i int) int { + k := z.p1 + for ; i < k; i++ { + switch z.buf[i] { + case ' ', '\n', '\t', '\f': + continue + case '>': + if i == k-1 { + return k + } + case '/': + if i == k-2 { + return k + } + } + return i + } + return k +} + +// lower finds the largest alphabetic [0-9A-Za-z]* word at the start of z.buf[i:] +// and returns that word lower-cased, as well as the trimmed cursor location +// after that word. +func (z *Tokenizer) lower(i int) ([]byte, int) { + i0 := i +loop: + for ; i < z.p1; i++ { + c := z.buf[i] + switch { + case '0' <= c && c <= '9': + // No-op. + case 'A' <= c && c <= 'Z': + z.buf[i] = c + 'a' - 'A' + case 'a' <= c && c <= 'z': + // No-op. + default: + break loop + } + } + return z.buf[i0:i], z.trim(i) +} + +// Text returns the raw data after unescaping. +// The contents of the returned slice may change on the next call to Next. +func (z *Tokenizer) Text() []byte { + s := unescape(z.Raw()) + z.p0 = z.p1 + return s +} + +// TagName returns the lower-cased name of a tag token (the `img` out of +// ``), and whether the tag has attributes. +// The contents of the returned slice may change on the next call to Next. +func (z *Tokenizer) TagName() (name []byte, remaining bool) { + i := z.p0 + 1 + if i >= z.p1 { + z.p0 = z.p1 + return nil, false + } + if z.buf[i] == '/' { + i++ + } + name, z.p0 = z.lower(i) + remaining = z.p0 != z.p1 + return +} + +// TagAttr returns the lower-cased key and unescaped value of the next unparsed +// attribute for the current tag token, and whether there are more attributes. +// The contents of the returned slices may change on the next call to Next. +func (z *Tokenizer) TagAttr() (key, val []byte, remaining bool) { + key, i := z.lower(z.p0) + // Get past the "=\"". + if i == z.p1 || z.buf[i] != '=' { + return + } + i = z.trim(i + 1) + if i == z.p1 || z.buf[i] != '"' { + return + } + i = z.trim(i + 1) + // Copy and unescape everything up to the closing '"'. + dst, src := i, i +loop: + for src < z.p1 { + c := z.buf[src] + switch c { + case '"': + src++ + break loop + case '&': + dst, src = unescapeEntity(z.buf, dst, src) + case '\\': + if src == z.p1 { + z.buf[dst] = '\\' + dst++ + } else { + z.buf[dst] = z.buf[src+1] + dst, src = dst+1, src+2 + } + default: + z.buf[dst] = c + dst, src = dst+1, src+1 + } + } + val, z.p0 = z.buf[i:dst], z.trim(src) + remaining = z.p0 != z.p1 + return +} + +// Token returns the next Token. The result's Data and Attr values remain valid +// after subsequent Next calls. +func (z *Tokenizer) Token() Token { + t := Token{Type: z.tt} + switch z.tt { + case TextToken: + t.Data = string(z.Text()) + case StartTagToken, EndTagToken, SelfClosingTagToken: + var attr []Attribute + name, remaining := z.TagName() + for remaining { + var key, val []byte + key, val, remaining = z.TagAttr() + attr = append(attr, Attribute{string(key), string(val)}) + } + t.Data = string(name) + t.Attr = attr + } + return t +} + +// NewTokenizer returns a new HTML Tokenizer for the given Reader. +// The input is assumed to be UTF-8 encoded. +func NewTokenizer(r io.Reader) *Tokenizer { + return &Tokenizer{ + r: r, + buf: make([]byte, 0, 4096), + } +} diff --git a/src/pkg/html/token_test.go b/src/pkg/html/token_test.go new file mode 100644 index 000000000..e07999ca5 --- /dev/null +++ b/src/pkg/html/token_test.go @@ -0,0 +1,231 @@ +// 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 html + +import ( + "bytes" + "os" + "testing" +) + +type tokenTest struct { + // A short description of the test case. + desc string + // The HTML to parse. + html string + // The string representations of the expected tokens. + tokens []string +} + +var tokenTests = []tokenTest{ + // A single text node. The tokenizer should not break text nodes on whitespace, + // nor should it normalize whitespace within a text node. + { + "text", + "foo bar", + []string{ + "foo bar", + }, + }, + // An entity. + { + "entity", + "one < two", + []string{ + "one < two", + }, + }, + // A start, self-closing and end tag. The tokenizer does not care if the start + // and end tokens don't match; that is the job of the parser. + { + "tags", + "bd", + []string{ + "", + "b", + "", + "d", + "", + }, + }, + // An attribute with a backslash. + { + "backslash", + `

    `, + []string{ + `

    `, + }, + }, + // Entities, tag name and attribute key lower-casing, and whitespace + // normalization within a tag. + { + "tricky", + "

    te<&;xt

    ", + []string{ + `

    `, + "", + "te<&;xt", + "", + "

    ", + }, + }, + // A non-existant entity. Tokenizing and converting back to a string should + // escape the "&" to become "&". + { + "noSuchEntity", + `
    <&alsoDoesntExist;&`, + []string{ + ``, + "<&alsoDoesntExist;&", + }, + }, +} + +func TestTokenizer(t *testing.T) { +loop: + for _, tt := range tokenTests { + z := NewTokenizer(bytes.NewBuffer([]byte(tt.html))) + for i, s := range tt.tokens { + if z.Next() == ErrorToken { + t.Errorf("%s token %d: want %q got error %v", tt.desc, i, s, z.Error()) + continue loop + } + actual := z.Token().String() + if s != actual { + t.Errorf("%s token %d: want %q got %q", tt.desc, i, s, actual) + continue loop + } + } + z.Next() + if z.Error() != os.EOF { + t.Errorf("%s: want EOF got %q", tt.desc, z.Token().String()) + } + } +} + +type unescapeTest struct { + // A short description of the test case. + desc string + // The HTML text. + html string + // The unescaped text. + unescaped string +} + +var unescapeTests = []unescapeTest{ + // Handle no entities. + { + "copy", + "A\ttext\nstring", + "A\ttext\nstring", + }, + // Handle simple named entities. + { + "simple", + "& > <", + "& > <", + }, + // Handle hitting the end of the string. + { + "stringEnd", + "& &", + "& &", + }, + // Handle entities with two codepoints. + { + "multiCodepoint", + "text ⋛︀ blah", + "text \u22db\ufe00 blah", + }, + // Handle decimal numeric entities. + { + "decimalEntity", + "Delta = Δ ", + "Delta = Δ ", + }, + // Handle hexadecimal numeric entities. + { + "hexadecimalEntity", + "Lambda = λ = λ ", + "Lambda = λ = λ ", + }, + // Handle numeric early termination. + { + "numericEnds", + "&# &#x €43 © = ©f = ©", + "&# &#x €43 © = ©f = ©", + }, + // Handle numeric ISO-8859-1 entity replacements. + { + "numericReplacements", + "Footnote‡", + "Footnote‡", + }, +} + +func TestUnescape(t *testing.T) { + for _, tt := range unescapeTests { + unescaped := UnescapeString(tt.html) + if unescaped != tt.unescaped { + t.Errorf("TestUnescape %s: want %q, got %q", tt.desc, tt.unescaped, unescaped) + } + } +} + +func TestUnescapeEscape(t *testing.T) { + ss := []string{ + ``, + `abc def`, + `a & b`, + `a&b`, + `a & b`, + `"`, + `"`, + `"<&>"`, + `"<&>"`, + `3&5==1 && 0<1, "0<1", a+acute=á`, + } + for _, s := range ss { + if s != UnescapeString(EscapeString(s)) { + t.Errorf("s != UnescapeString(EscapeString(s)), s=%q", s) + } + } +} + +func TestBufAPI(t *testing.T) { + s := "0123456789" + z := NewTokenizer(bytes.NewBuffer([]byte(s))) + result := bytes.NewBuffer(nil) + depth := 0 +loop: + for { + tt := z.Next() + switch tt { + case ErrorToken: + if z.Error() != os.EOF { + t.Error(z.Error()) + } + break loop + case TextToken: + if depth > 0 { + result.Write(z.Text()) + } + case StartTagToken, EndTagToken: + tn, _ := z.TagName() + if len(tn) == 1 && tn[0] == 'a' { + if tt == StartTagToken { + depth++ + } else { + depth-- + } + } + } + } + u := "14567" + v := string(result.Bytes()) + if u != v { + t.Errorf("TestBufAPI: want %q got %q", u, v) + } +} diff --git a/src/pkg/http/Makefile b/src/pkg/http/Makefile index 235ff0279..7e4f80c28 100644 --- a/src/pkg/http/Makefile +++ b/src/pkg/http/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=http GOFILES=\ diff --git a/src/pkg/http/client.go b/src/pkg/http/client.go index 54487dac2..29678ee32 100644 --- a/src/pkg/http/client.go +++ b/src/pkg/http/client.go @@ -8,11 +8,14 @@ package http import ( "bufio" + "bytes" + "crypto/tls" "encoding/base64" "fmt" "io" "net" "os" + "strconv" "strings" ) @@ -21,7 +24,7 @@ import ( func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") } // Used in Send to implement io.ReadCloser by bundling together the -// io.BufReader through which we read the response, and the underlying +// bufio.Reader through which we read the response, and the underlying // network connection. type readClose struct { io.Reader @@ -34,15 +37,15 @@ type readClose struct { // send() method is nonpublic because, when we refactor the code for persistent // connections, it may no longer make sense to have a method with this signature. func send(req *Request) (resp *Response, err os.Error) { - if req.URL.Scheme != "http" { + if req.URL.Scheme != "http" && req.URL.Scheme != "https" { return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme} } addr := req.URL.Host if !hasPort(addr) { - addr += ":http" + addr += ":" + req.URL.Scheme } - info := req.URL.Userinfo + info := req.URL.RawUserinfo if len(info) > 0 { enc := base64.URLEncoding encoded := make([]byte, enc.EncodedLen(len(info))) @@ -52,9 +55,25 @@ func send(req *Request) (resp *Response, err os.Error) { } req.Header["Authorization"] = "Basic " + string(encoded) } - conn, err := net.Dial("tcp", "", addr) - if err != nil { - return nil, err + + var conn io.ReadWriteCloser + if req.URL.Scheme == "http" { + conn, err = net.Dial("tcp", "", addr) + if err != nil { + return nil, err + } + } else { // https + conn, err = tls.Dial("tcp", "", addr, nil) + if err != nil { + return nil, err + } + h := req.URL.Host + if hasPort(h) { + h = h[0:strings.LastIndex(h, ":")] + } + if err := conn.(*tls.Conn).VerifyHostname(h); err != nil { + return nil, err + } } err = req.Write(conn) @@ -111,6 +130,7 @@ func Get(url string) (r *Response, finalURL string, err os.Error) { if req.URL, err = ParseURL(url); err != nil { break } + url = req.URL.String() if r, err = send(&req); err != nil { break } @@ -153,6 +173,41 @@ func Post(url string, bodyType string, body io.Reader) (r *Response, err os.Erro return send(&req) } +// PostForm issues a POST to the specified URL, +// with data's keys and values urlencoded as the request body. +// +// Caller should close r.Body when done reading it. +func PostForm(url string, data map[string]string) (r *Response, err os.Error) { + var req Request + req.Method = "POST" + req.ProtoMajor = 1 + req.ProtoMinor = 1 + req.Close = true + body := urlencode(data) + req.Body = nopCloser{body} + req.Header = map[string]string{ + "Content-Type": "application/x-www-form-urlencoded", + "Content-Length": strconv.Itoa(body.Len()), + } + req.ContentLength = int64(body.Len()) + + req.URL, err = ParseURL(url) + if err != nil { + return nil, err + } + + return send(&req) +} + +// TODO: remove this function when PostForm takes a multimap. +func urlencode(data map[string]string) (b *bytes.Buffer) { + m := make(map[string][]string, len(data)) + for k, v := range data { + m[k] = []string{v} + } + return bytes.NewBuffer([]byte(EncodeQuery(m))) +} + // Head issues a HEAD to the specified URL. func Head(url string) (r *Response, err os.Error) { var req Request @@ -160,6 +215,7 @@ func Head(url string) (r *Response, err os.Error) { if req.URL, err = ParseURL(url); err != nil { return } + url = req.URL.String() if r, err = send(&req); err != nil { return } diff --git a/src/pkg/http/fs.go b/src/pkg/http/fs.go index 40bb3d138..143a839a8 100644 --- a/src/pkg/http/fs.go +++ b/src/pkg/http/fs.go @@ -12,7 +12,9 @@ import ( "mime" "os" "path" + "strconv" "strings" + "time" "utf8" ) @@ -25,7 +27,7 @@ func isText(b []byte) bool { // decoding error return false } - if 0x80 <= rune && rune <= 0x9F { + if 0x7F <= rune && rune <= 0x9F { return false } if rune < ' ' { @@ -42,8 +44,8 @@ func isText(b []byte) bool { return true } -func dirList(c *Conn, f *os.File) { - fmt.Fprintf(c, "
    \n")
    +func dirList(w ResponseWriter, f *os.File) {
    +	fmt.Fprintf(w, "
    \n")
     	for {
     		dirs, err := f.Readdir(100)
     		if err != nil || len(dirs) == 0 {
    @@ -55,26 +57,25 @@ func dirList(c *Conn, f *os.File) {
     				name += "/"
     			}
     			// TODO htmlescape
    -			fmt.Fprintf(c, "%s\n", name, name)
    +			fmt.Fprintf(w, "%s\n", name, name)
     		}
     	}
    -	fmt.Fprintf(c, "
    \n") + fmt.Fprintf(w, "
    \n") } - -func serveFileInternal(c *Conn, r *Request, name string, redirect bool) { +func serveFile(w ResponseWriter, r *Request, name string, redirect bool) { const indexPage = "/index.html" // redirect .../index.html to .../ if strings.HasSuffix(r.URL.Path, indexPage) { - Redirect(c, r.URL.Path[0:len(r.URL.Path)-len(indexPage)+1], StatusMovedPermanently) + Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len(indexPage)+1], StatusMovedPermanently) return } f, err := os.Open(name, os.O_RDONLY, 0) if err != nil { // TODO expose actual error? - NotFound(c, r) + NotFound(w, r) return } defer f.Close() @@ -82,7 +83,7 @@ func serveFileInternal(c *Conn, r *Request, name string, redirect bool) { d, err1 := f.Stat() if err1 != nil { // TODO expose actual error? - NotFound(c, r) + NotFound(w, r) return } @@ -92,17 +93,23 @@ func serveFileInternal(c *Conn, r *Request, name string, redirect bool) { url := r.URL.Path if d.IsDirectory() { if url[len(url)-1] != '/' { - Redirect(c, url+"/", StatusMovedPermanently) + Redirect(w, r, url+"/", StatusMovedPermanently) return } } else { if url[len(url)-1] == '/' { - Redirect(c, url[0:len(url)-1], StatusMovedPermanently) + Redirect(w, r, url[0:len(url)-1], StatusMovedPermanently) return } } } + if t, _ := time.Parse(TimeFormat, r.Header["If-Modified-Since"]); t != nil && d.Mtime_ns/1e9 <= t.Seconds() { + w.WriteHeader(StatusNotModified) + return + } + w.SetHeader("Last-Modified", time.SecondsToUTC(d.Mtime_ns/1e9).Format(TimeFormat)) + // use contents of index.html for directory, if present if d.IsDirectory() { index := name + indexPage @@ -119,33 +126,60 @@ func serveFileInternal(c *Conn, r *Request, name string, redirect bool) { } if d.IsDirectory() { - dirList(c, f) + dirList(w, f) return } // serve file + size := d.Size + code := StatusOK + // use extension to find content type. ext := path.Ext(name) if ctype := mime.TypeByExtension(ext); ctype != "" { - c.SetHeader("Content-Type", ctype) + w.SetHeader("Content-Type", ctype) } else { // read first chunk to decide between utf-8 text and binary var buf [1024]byte - n, _ := io.ReadFull(f, buf[0:]) - b := buf[0:n] + n, _ := io.ReadFull(f, buf[:]) + b := buf[:n] if isText(b) { - c.SetHeader("Content-Type", "text-plain; charset=utf-8") + w.SetHeader("Content-Type", "text-plain; charset=utf-8") } else { - c.SetHeader("Content-Type", "application/octet-stream") // generic binary + w.SetHeader("Content-Type", "application/octet-stream") // generic binary + } + f.Seek(0, 0) // rewind to output whole file + } + + // handle Content-Range header. + // TODO(adg): handle multiple ranges + ranges, err := parseRange(r.Header["Range"], size) + if err != nil || len(ranges) > 1 { + Error(w, err.String(), StatusRequestedRangeNotSatisfiable) + return + } + if len(ranges) == 1 { + ra := ranges[0] + if _, err := f.Seek(ra.start, 0); err != nil { + Error(w, err.String(), StatusRequestedRangeNotSatisfiable) + return } - c.Write(b) + size = ra.length + code = StatusPartialContent + w.SetHeader("Content-Range", fmt.Sprintf("%d-%d/%d", ra.start, ra.start+ra.length, d.Size)) } - io.Copy(c, f) + + w.SetHeader("Accept-Ranges", "bytes") + w.SetHeader("Content-Length", strconv.Itoa64(size)) + + w.WriteHeader(code) + + io.Copyn(w, f, size) } // ServeFile replies to the request with the contents of the named file or directory. -func ServeFile(c *Conn, r *Request, name string) { - serveFileInternal(c, r, name, false) +func ServeFile(w ResponseWriter, r *Request, name string) { + serveFile(w, r, name, false) } type fileHandler struct { @@ -159,12 +193,71 @@ type fileHandler struct { // looking up the file name in the file system. func FileServer(root, prefix string) Handler { return &fileHandler{root, prefix} } -func (f *fileHandler) ServeHTTP(c *Conn, r *Request) { +func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) { path := r.URL.Path if !strings.HasPrefix(path, f.prefix) { - NotFound(c, r) + NotFound(w, r) return } path = path[len(f.prefix):] - serveFileInternal(c, r, f.root+"/"+path, true) + serveFile(w, r, f.root+"/"+path, true) +} + +// httpRange specifies the byte range to be sent to the client. +type httpRange struct { + start, length int64 +} + +// parseRange parses a Range header string as per RFC 2616. +func parseRange(s string, size int64) ([]httpRange, os.Error) { + if s == "" { + return nil, nil // header not present + } + const b = "bytes=" + if !strings.HasPrefix(s, b) { + return nil, os.NewError("invalid range") + } + var ranges []httpRange + for _, ra := range strings.Split(s[len(b):], ",", -1) { + i := strings.Index(ra, "-") + if i < 0 { + return nil, os.NewError("invalid range") + } + start, end := ra[:i], ra[i+1:] + var r httpRange + if start == "" { + // If no start is specified, end specifies the + // range start relative to the end of the file. + i, err := strconv.Atoi64(end) + if err != nil { + return nil, os.NewError("invalid range") + } + if i > size { + i = size + } + r.start = size - i + r.length = size - r.start + } else { + i, err := strconv.Atoi64(start) + if err != nil || i > size || i < 0 { + return nil, os.NewError("invalid range") + } + r.start = i + if end == "" { + // If no end is specified, range extends to end of the file. + r.length = size - r.start + } else { + i, err := strconv.Atoi64(end) + if err != nil || r.start > i { + return nil, os.NewError("invalid range") + } + if i >= size { + i = size - 1 + } + r.length = i - r.start + 1 + } + } + ranges = append(ranges, r) + } + return ranges, nil } diff --git a/src/pkg/http/fs_test.go b/src/pkg/http/fs_test.go new file mode 100644 index 000000000..0f7135692 --- /dev/null +++ b/src/pkg/http/fs_test.go @@ -0,0 +1,172 @@ +// 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 http + +import ( + "fmt" + "io/ioutil" + "net" + "os" + "sync" + "testing" +) + +var ParseRangeTests = []struct { + s string + length int64 + r []httpRange +}{ + {"", 0, nil}, + {"foo", 0, nil}, + {"bytes=", 0, nil}, + {"bytes=5-4", 10, nil}, + {"bytes=0-2,5-4", 10, nil}, + {"bytes=0-9", 10, []httpRange{{0, 10}}}, + {"bytes=0-", 10, []httpRange{{0, 10}}}, + {"bytes=5-", 10, []httpRange{{5, 5}}}, + {"bytes=0-20", 10, []httpRange{{0, 10}}}, + {"bytes=15-,0-5", 10, nil}, + {"bytes=-5", 10, []httpRange{{5, 5}}}, + {"bytes=-15", 10, []httpRange{{0, 10}}}, + {"bytes=0-499", 10000, []httpRange{{0, 500}}}, + {"bytes=500-999", 10000, []httpRange{{500, 500}}}, + {"bytes=-500", 10000, []httpRange{{9500, 500}}}, + {"bytes=9500-", 10000, []httpRange{{9500, 500}}}, + {"bytes=0-0,-1", 10000, []httpRange{{0, 1}, {9999, 1}}}, + {"bytes=500-600,601-999", 10000, []httpRange{{500, 101}, {601, 399}}}, + {"bytes=500-700,601-999", 10000, []httpRange{{500, 201}, {601, 399}}}, +} + +func TestParseRange(t *testing.T) { + for _, test := range ParseRangeTests { + r := test.r + ranges, err := parseRange(test.s, test.length) + if err != nil && r != nil { + t.Errorf("parseRange(%q) returned error %q", test.s, err) + } + if len(ranges) != len(r) { + t.Errorf("len(parseRange(%q)) = %d, want %d", test.s, len(ranges), len(r)) + continue + } + for i := range r { + if ranges[i].start != r[i].start { + t.Errorf("parseRange(%q)[%d].start = %d, want %d", test.s, i, ranges[i].start, r[i].start) + } + if ranges[i].length != r[i].length { + t.Errorf("parseRange(%q)[%d].length = %d, want %d", test.s, i, ranges[i].length, r[i].length) + } + } + } +} + +const ( + testFile = "testdata/file" + testFileLength = 11 +) + +var ( + serverOnce sync.Once + serverAddr string +) + +func startServer(t *testing.T) { + serverOnce.Do(func() { + HandleFunc("/ServeFile", func(w ResponseWriter, r *Request) { + ServeFile(w, r, "testdata/file") + }) + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal("listen:", err) + } + serverAddr = l.Addr().String() + go Serve(l, nil) + }) +} + +var ServeFileRangeTests = []struct { + start, end int + r string + code int +}{ + {0, testFileLength, "", StatusOK}, + {0, 5, "0-4", StatusPartialContent}, + {2, testFileLength, "2-", StatusPartialContent}, + {testFileLength - 5, testFileLength, "-5", StatusPartialContent}, + {3, 8, "3-7", StatusPartialContent}, + {0, 0, "20-", StatusRequestedRangeNotSatisfiable}, +} + +func TestServeFile(t *testing.T) { + startServer(t) + var err os.Error + + file, err := ioutil.ReadFile(testFile) + if err != nil { + t.Fatal("reading file:", err) + } + + // set up the Request (re-used for all tests) + var req Request + req.Header = make(map[string]string) + if req.URL, err = ParseURL("http://" + serverAddr + "/ServeFile"); err != nil { + t.Fatal("ParseURL:", err) + } + req.Method = "GET" + + // straight GET + _, body := getBody(t, req) + if !equal(body, file) { + t.Fatalf("body mismatch: got %q, want %q", body, file) + } + + // Range tests + for _, rt := range ServeFileRangeTests { + req.Header["Range"] = "bytes=" + rt.r + if rt.r == "" { + req.Header["Range"] = "" + } + r, body := getBody(t, req) + if r.StatusCode != rt.code { + t.Errorf("range=%q: StatusCode=%d, want %d", rt.r, r.StatusCode, rt.code) + } + if rt.code == StatusRequestedRangeNotSatisfiable { + continue + } + h := fmt.Sprintf("%d-%d/%d", rt.start, rt.end, testFileLength) + if rt.r == "" { + h = "" + } + if r.Header["Content-Range"] != h { + t.Errorf("header mismatch: range=%q: got %q, want %q", rt.r, r.Header["Content-Range"], h) + } + if !equal(body, file[rt.start:rt.end]) { + t.Errorf("body mismatch: range=%q: got %q, want %q", rt.r, body, file[rt.start:rt.end]) + } + } +} + +func getBody(t *testing.T, req Request) (*Response, []byte) { + r, err := send(&req) + if err != nil { + t.Fatal(req.URL.String(), "send:", err) + } + b, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Fatal("reading Body:", err) + } + return r, b +} + +func equal(a, b []byte) bool { + if len(a) != len(b) { + return false + } + for i := range a { + if a[i] != b[i] { + return false + } + } + return true +} diff --git a/src/pkg/http/lex_test.go b/src/pkg/http/lex_test.go index 043430cb7..5386f7534 100644 --- a/src/pkg/http/lex_test.go +++ b/src/pkg/http/lex_test.go @@ -15,29 +15,29 @@ type lexTest struct { } var lexTests = []lexTest{ - lexTest{ + { Raw: `"abc"def,:ghi`, Parsed: 13, Result: []string{"abcdef", "ghi"}, }, // My understanding of the RFC is that escape sequences outside of // quotes are not interpreted? - lexTest{ + { Raw: `"\t"\t"\t"`, Parsed: 10, Result: []string{"\t", "t\t"}, }, - lexTest{ + { Raw: `"\yab"\r\n`, Parsed: 10, Result: []string{"?ab", "r", "n"}, }, - lexTest{ + { Raw: "ab\f", Parsed: 3, Result: []string{"ab?"}, }, - lexTest{ + { Raw: "\"ab \" c,de f, gh, ij\n\t\r", Parsed: 23, Result: []string{"ab ", "c", "de", "f", "gh", "ij"}, diff --git a/src/pkg/http/pprof/Makefile b/src/pkg/http/pprof/Makefile index e0315112f..5858a0efa 100644 --- a/src/pkg/http/pprof/Makefile +++ b/src/pkg/http/pprof/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=http/pprof GOFILES=\ diff --git a/src/pkg/http/pprof/pprof.go b/src/pkg/http/pprof/pprof.go index 38d91afbf..f7db9aab9 100644 --- a/src/pkg/http/pprof/pprof.go +++ b/src/pkg/http/pprof/pprof.go @@ -40,28 +40,28 @@ func init() { // Cmdline responds with the running program's // command line, with arguments separated by NUL bytes. // The package initialization registers it as /debug/pprof/cmdline. -func Cmdline(c *http.Conn, r *http.Request) { - c.SetHeader("content-type", "text/plain; charset=utf-8") - fmt.Fprintf(c, strings.Join(os.Args, "\x00")) +func Cmdline(w http.ResponseWriter, r *http.Request) { + w.SetHeader("content-type", "text/plain; charset=utf-8") + fmt.Fprintf(w, strings.Join(os.Args, "\x00")) } // Heap responds with the pprof-formatted heap profile. // The package initialization registers it as /debug/pprof/heap. -func Heap(c *http.Conn, r *http.Request) { - c.SetHeader("content-type", "text/plain; charset=utf-8") - pprof.WriteHeapProfile(c) +func Heap(w http.ResponseWriter, r *http.Request) { + w.SetHeader("content-type", "text/plain; charset=utf-8") + pprof.WriteHeapProfile(w) } // Symbol looks up the program counters listed in the request, // responding with a table mapping program counters to function names. // The package initialization registers it as /debug/pprof/symbol. -func Symbol(c *http.Conn, r *http.Request) { - c.SetHeader("content-type", "text/plain; charset=utf-8") +func Symbol(w http.ResponseWriter, r *http.Request) { + w.SetHeader("content-type", "text/plain; charset=utf-8") // We don't know how many symbols we have, but we // do have symbol information. Pprof only cares whether // this number is 0 (no symbols available) or > 0. - fmt.Fprintf(c, "num_symbols: 1\n") + fmt.Fprintf(w, "num_symbols: 1\n") var b *bufio.Reader if r.Method == "POST" { @@ -71,15 +71,15 @@ func Symbol(c *http.Conn, r *http.Request) { } for { - w, err := b.ReadSlice('+') + word, err := b.ReadSlice('+') if err == nil { - w = w[0 : len(w)-1] // trim + + word = word[0 : len(word)-1] // trim + } - pc, _ := strconv.Btoui64(string(w), 0) + pc, _ := strconv.Btoui64(string(word), 0) if pc != 0 { f := runtime.FuncForPC(uintptr(pc)) if f != nil { - fmt.Fprintf(c, "%#x %s\n", pc, f.Name()) + fmt.Fprintf(w, "%#x %s\n", pc, f.Name()) } } diff --git a/src/pkg/http/readrequest_test.go b/src/pkg/http/readrequest_test.go index 7654dbfc7..067e17dda 100644 --- a/src/pkg/http/readrequest_test.go +++ b/src/pkg/http/readrequest_test.go @@ -20,7 +20,7 @@ type reqTest struct { var reqTests = []reqTest{ // Baseline test; All Request fields included for template use - reqTest{ + { "GET http://www.techcrunch.com/ HTTP/1.1\r\n" + "Host: www.techcrunch.com\r\n" + "User-Agent: Fake\r\n" + @@ -37,15 +37,15 @@ var reqTests = []reqTest{ Method: "GET", RawURL: "http://www.techcrunch.com/", URL: &URL{ - Raw: "http://www.techcrunch.com/", - Scheme: "http", - RawPath: "/", - Authority: "www.techcrunch.com", - Userinfo: "", - Host: "www.techcrunch.com", - Path: "/", - RawQuery: "", - Fragment: "", + Raw: "http://www.techcrunch.com/", + Scheme: "http", + RawPath: "/", + RawAuthority: "www.techcrunch.com", + RawUserinfo: "", + Host: "www.techcrunch.com", + Path: "/", + RawQuery: "", + Fragment: "", }, Proto: "HTTP/1.1", ProtoMajor: 1, diff --git a/src/pkg/http/request.go b/src/pkg/http/request.go index 8a72d6cfa..b88689988 100644 --- a/src/pkg/http/request.go +++ b/src/pkg/http/request.go @@ -16,6 +16,8 @@ import ( "fmt" "io" "io/ioutil" + "mime" + "mime/multipart" "os" "strconv" "strings" @@ -40,6 +42,8 @@ var ( ErrNotSupported = &ProtocolError{"feature not supported"} ErrUnexpectedTrailer = &ProtocolError{"trailer header without chunked transfer encoding"} ErrMissingContentLength = &ProtocolError{"missing ContentLength in HEAD response"} + ErrNotMultipart = &ProtocolError{"request Content-Type isn't multipart/form-data"} + ErrMissingBoundary = &ProtocolError{"no multipart boundary param Content-Type"} ) type badStringError struct { @@ -67,7 +71,7 @@ type Request struct { ProtoMajor int // 1 ProtoMinor int // 0 - // A header mapping request lines to their values. + // A header maps request lines to their values. // If the header says // // accept-encoding: gzip, deflate @@ -139,6 +143,24 @@ func (r *Request) ProtoAtLeast(major, minor int) bool { r.ProtoMajor == major && r.ProtoMinor >= minor } +// MultipartReader returns a MIME multipart reader if this is a +// multipart/form-data POST request, else returns nil and an error. +func (r *Request) MultipartReader() (multipart.Reader, os.Error) { + v, ok := r.Header["Content-Type"] + if !ok { + return nil, ErrNotMultipart + } + d, params := mime.ParseMediaType(v) + if d != "multipart/form-data" { + return nil, ErrNotMultipart + } + boundary, ok := params["boundary"] + if !ok { + return nil, ErrMissingBoundary + } + return multipart.NewReader(r.Body, boundary), nil +} + // Return value if nonempty, def otherwise. func valueOrDefault(value, def string) string { if value != "" { @@ -169,7 +191,7 @@ func (req *Request) Write(w io.Writer) os.Error { uri := req.RawURL if uri == "" { - uri = valueOrDefault(urlEscape(req.URL.Path, false), "/") + uri = valueOrDefault(urlEscape(req.URL.Path, encodePath), "/") if req.URL.RawQuery != "" { uri += "?" + req.URL.RawQuery } @@ -227,6 +249,8 @@ func readLineBytes(b *bufio.Reader) (p []byte, err os.Error) { // If the caller asked for a line, there should be a line. if err == os.EOF { err = io.ErrUnexpectedEOF + } else if err == bufio.ErrBufferFull { + err = ErrLineTooLong } return nil, err } @@ -275,7 +299,7 @@ func readKeyValue(b *bufio.Reader) (key, value string, err os.Error) { } key = string(line[0:i]) - if strings.Index(key, " ") >= 0 { + if strings.Contains(key, " ") { // Key field has space - no good. goto Malformed } @@ -360,29 +384,30 @@ func parseHTTPVersion(vers string) (int, int, bool) { return major, minor, true } -var cmap = make(map[string]string) - // CanonicalHeaderKey returns the canonical format of the // HTTP header key s. The canonicalization converts the first // letter and any letter following a hyphen to upper case; // the rest are converted to lowercase. For example, the // canonical key for "accept-encoding" is "Accept-Encoding". func CanonicalHeaderKey(s string) string { - if t, ok := cmap[s]; ok { - return t - } - // canonicalize: first letter upper case // and upper case after each dash. // (Host, User-Agent, If-Modified-Since). // HTTP headers are ASCII only, so no Unicode issues. - a := []byte(s) + var a []byte upper := true - for i, v := range a { + for i := 0; i < len(s); i++ { + v := s[i] if upper && 'a' <= v && v <= 'z' { + if a == nil { + a = []byte(s) + } a[i] = v + 'A' - 'a' } if !upper && 'A' <= v && v <= 'Z' { + if a == nil { + a = []byte(s) + } a[i] = v + 'a' - 'A' } upper = false @@ -390,9 +415,10 @@ func CanonicalHeaderKey(s string) string { upper = true } } - t := string(a) - cmap[s] = t - return t + if a != nil { + return string(a) + } + return s } type chunkedReader struct { @@ -566,9 +592,22 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { return req, nil } +// ParseQuery parses the URL-encoded query string and returns +// a map listing the values specified for each key. +// ParseQuery always returns a non-nil map containing all the +// valid query parameters found; err describes the first decoding error +// encountered, if any. func ParseQuery(query string) (m map[string][]string, err os.Error) { m = make(map[string][]string) + err = parseQuery(m, query) + return +} + +func parseQuery(m map[string][]string, query string) (err os.Error) { for _, kv := range strings.Split(query, "&", -1) { + if len(kv) == 0 { + continue + } kvPair := strings.Split(kv, "=", 2) var key, value string @@ -579,14 +618,13 @@ func ParseQuery(query string) (m map[string][]string, err os.Error) { } if e != nil { err = e + continue } - vec := vector.StringVector(m[key]) vec.Push(value) m[key] = vec } - - return + return err } // ParseForm parses the request body as a form for POST requests, or the raw query for GET requests. @@ -596,32 +634,34 @@ func (r *Request) ParseForm() (err os.Error) { return } - var query string - switch r.Method { - case "GET": - query = r.URL.RawQuery - case "POST": + r.Form = make(map[string][]string) + if r.URL != nil { + err = parseQuery(r.Form, r.URL.RawQuery) + } + if r.Method == "POST" { if r.Body == nil { - r.Form = make(map[string][]string) return os.ErrorString("missing form body") } ct := r.Header["Content-Type"] switch strings.Split(ct, ";", 2)[0] { case "text/plain", "application/x-www-form-urlencoded", "": - var b []byte - if b, err = ioutil.ReadAll(r.Body); err != nil { - r.Form = make(map[string][]string) - return err + b, e := ioutil.ReadAll(r.Body) + if e != nil { + if err == nil { + err = e + } + break + } + e = parseQuery(r.Form, string(b)) + if err == nil { + err = e } - query = string(b) // TODO(dsymonds): Handle multipart/form-data default: - r.Form = make(map[string][]string) return &badStringError{"unknown Content-Type", ct} } } - r.Form, err = ParseQuery(query) - return + return err } // FormValue returns the first value for the named component of the query. @@ -640,3 +680,14 @@ func (r *Request) expectsContinue() bool { expectation, ok := r.Header["Expect"] return ok && strings.ToLower(expectation) == "100-continue" } + +func (r *Request) wantsHttp10KeepAlive() bool { + if r.ProtoMajor != 1 || r.ProtoMinor != 0 { + return false + } + value, exists := r.Header["Connection"] + if !exists { + return false + } + return strings.Contains(strings.ToLower(value), "keep-alive") +} diff --git a/src/pkg/http/request_test.go b/src/pkg/http/request_test.go index 98d5342bb..d25e5e5e7 100644 --- a/src/pkg/http/request_test.go +++ b/src/pkg/http/request_test.go @@ -6,6 +6,9 @@ package http import ( "bytes" + "reflect" + "regexp" + "strings" "testing" ) @@ -17,15 +20,15 @@ type parseTest struct { } var parseTests = []parseTest{ - parseTest{ + { query: "a=1&b=2", out: stringMultimap{"a": []string{"1"}, "b": []string{"2"}}, }, - parseTest{ + { query: "a=1&a=2&a=banana", out: stringMultimap{"a": []string{"1", "2", "banana"}}, }, - parseTest{ + { query: "ascii=%3Ckey%3A+0x90%3E", out: stringMultimap{"ascii": []string{""}}, }, @@ -68,6 +71,22 @@ func TestQuery(t *testing.T) { } } +func TestPostQuery(t *testing.T) { + req := &Request{Method: "POST"} + req.URL, _ = ParseURL("http://www.google.com/search?q=foo&q=bar&both=x") + req.Header = map[string]string{"Content-Type": "application/x-www-form-urlencoded; boo!"} + req.Body = nopCloser{strings.NewReader("z=post&both=y")} + if q := req.FormValue("q"); q != "foo" { + t.Errorf(`req.FormValue("q") = %q, want "foo"`, q) + } + if z := req.FormValue("z"); z != "post" { + t.Errorf(`req.FormValue("z") = %q, want "post"`, z) + } + if both := req.Form["both"]; !reflect.DeepEqual(both, []string{"x", "y"}) { + t.Errorf(`req.FormValue("both") = %q, want ["x", "y"]`, both) + } +} + type stringMap map[string]string type parseContentTypeTest struct { contentType stringMap @@ -75,10 +94,10 @@ type parseContentTypeTest struct { } var parseContentTypeTests = []parseContentTypeTest{ - parseContentTypeTest{contentType: stringMap{"Content-Type": "text/plain"}}, - parseContentTypeTest{contentType: stringMap{"Content-Type": ""}}, - parseContentTypeTest{contentType: stringMap{"Content-Type": "text/plain; boundary="}}, - parseContentTypeTest{ + {contentType: stringMap{"Content-Type": "text/plain"}}, + {contentType: stringMap{"Content-Type": ""}}, + {contentType: stringMap{"Content-Type": "text/plain; boundary="}}, + { contentType: stringMap{"Content-Type": "application/unknown"}, error: true, }, @@ -101,17 +120,36 @@ func TestPostContentTypeParsing(t *testing.T) { } } +func TestMultipartReader(t *testing.T) { + req := &Request{ + Method: "POST", + Header: stringMap{"Content-Type": `multipart/form-data; boundary="foo123"`}, + Body: nopCloser{new(bytes.Buffer)}, + } + multipart, err := req.MultipartReader() + if multipart == nil { + t.Errorf("expected multipart; error: %v", err) + } + + req.Header = stringMap{"Content-Type": "text/plain"} + multipart, err = req.MultipartReader() + if multipart != nil { + t.Errorf("unexpected multipart for text/plain") + } +} + func TestRedirect(t *testing.T) { const ( - start = "http://codesearch.google.com/" - end = "http://www.google.com/codesearch" + start = "http://google.com/" + endRe = "^http://www\\.google\\.[a-z.]+/$" ) + var end = regexp.MustCompile(endRe) r, url, err := Get(start) if err != nil { t.Fatal(err) } r.Body.Close() - if r.StatusCode != 200 || url != end { - t.Fatalf("Get(%s) got status %d at %s, want 200 at %s", start, r.StatusCode, url, end) + if r.StatusCode != 200 || !end.MatchString(url) { + t.Fatalf("Get(%s) got status %d at %q, want 200 matching %q", start, r.StatusCode, url, endRe) } } diff --git a/src/pkg/http/requestwrite_test.go b/src/pkg/http/requestwrite_test.go index 469df69d7..3ceabe4ee 100644 --- a/src/pkg/http/requestwrite_test.go +++ b/src/pkg/http/requestwrite_test.go @@ -16,20 +16,20 @@ type reqWriteTest struct { var reqWriteTests = []reqWriteTest{ // HTTP/1.1 => chunked coding; no body; no trailer - reqWriteTest{ + { Request{ Method: "GET", RawURL: "http://www.techcrunch.com/", URL: &URL{ - Raw: "http://www.techcrunch.com/", - Scheme: "http", - RawPath: "http://www.techcrunch.com/", - Authority: "www.techcrunch.com", - Userinfo: "", - Host: "www.techcrunch.com", - Path: "/", - RawQuery: "", - Fragment: "", + Raw: "http://www.techcrunch.com/", + Scheme: "http", + RawPath: "http://www.techcrunch.com/", + RawAuthority: "www.techcrunch.com", + RawUserinfo: "", + Host: "www.techcrunch.com", + Path: "/", + RawQuery: "", + Fragment: "", }, Proto: "HTTP/1.1", ProtoMajor: 1, @@ -61,7 +61,7 @@ var reqWriteTests = []reqWriteTest{ "Proxy-Connection: keep-alive\r\n\r\n", }, // HTTP/1.1 => chunked coding; body; empty trailer - reqWriteTest{ + { Request{ Method: "GET", URL: &URL{ @@ -83,7 +83,7 @@ var reqWriteTests = []reqWriteTest{ "6\r\nabcdef\r\n0\r\n\r\n", }, // HTTP/1.1 POST => chunked coding; body; empty trailer - reqWriteTest{ + { Request{ Method: "POST", URL: &URL{ @@ -107,7 +107,7 @@ var reqWriteTests = []reqWriteTest{ "6\r\nabcdef\r\n0\r\n\r\n", }, // default to HTTP/1.1 - reqWriteTest{ + { Request{ Method: "GET", RawURL: "/search", diff --git a/src/pkg/http/response.go b/src/pkg/http/response.go index 6a209c9f8..a24726110 100644 --- a/src/pkg/http/response.go +++ b/src/pkg/http/response.go @@ -86,10 +86,14 @@ func ReadResponse(r *bufio.Reader, requestMethod string) (resp *Response, err os return nil, err } f := strings.Split(line, " ", 3) - if len(f) < 3 { + if len(f) < 2 { return nil, &badStringError{"malformed HTTP response", line} } - resp.Status = f[1] + " " + f[2] + reasonPhrase := "" + if len(f) > 2 { + reasonPhrase = f[2] + } + resp.Status = f[1] + " " + reasonPhrase resp.StatusCode, err = strconv.Atoi(f[1]) if err != nil { return nil, &badStringError{"malformed HTTP status code", f[1]} diff --git a/src/pkg/http/response_test.go b/src/pkg/http/response_test.go index 889b770be..89a8c3b44 100644 --- a/src/pkg/http/response_test.go +++ b/src/pkg/http/response_test.go @@ -21,7 +21,7 @@ type respTest struct { var respTests = []respTest{ // Unchunked response without Content-Length. - respTest{ + { "HTTP/1.0 200 OK\r\n" + "Connection: close\r\n" + "\r\n" + @@ -45,7 +45,7 @@ var respTests = []respTest{ }, // Unchunked response with Content-Length. - respTest{ + { "HTTP/1.0 200 OK\r\n" + "Content-Length: 10\r\n" + "Connection: close\r\n" + @@ -71,7 +71,7 @@ var respTests = []respTest{ }, // Chunked response without Content-Length. - respTest{ + { "HTTP/1.0 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + @@ -97,7 +97,7 @@ var respTests = []respTest{ }, // Chunked response with Content-Length. - respTest{ + { "HTTP/1.0 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "Content-Length: 10\r\n" + @@ -122,6 +122,44 @@ var respTests = []respTest{ "Body here\n", }, + + // Status line without a Reason-Phrase, but trailing space. + // (permitted by RFC 2616) + { + "HTTP/1.0 303 \r\n\r\n", + Response{ + Status: "303 ", + StatusCode: 303, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + RequestMethod: "GET", + Header: map[string]string{}, + Close: true, + ContentLength: -1, + }, + + "", + }, + + // Status line without a Reason-Phrase, and no trailing space. + // (not permitted by RFC 2616, but we'll accept it anyway) + { + "HTTP/1.0 303\r\n\r\n", + Response{ + Status: "303 ", + StatusCode: 303, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + RequestMethod: "GET", + Header: map[string]string{}, + Close: true, + ContentLength: -1, + }, + + "", + }, } func TestReadResponse(t *testing.T) { diff --git a/src/pkg/http/responsewrite_test.go b/src/pkg/http/responsewrite_test.go index 768064303..9f10be562 100644 --- a/src/pkg/http/responsewrite_test.go +++ b/src/pkg/http/responsewrite_test.go @@ -16,7 +16,7 @@ type respWriteTest struct { var respWriteTests = []respWriteTest{ // HTTP/1.0, identity coding; no trailer - respWriteTest{ + { Response{ StatusCode: 503, ProtoMajor: 1, @@ -31,8 +31,23 @@ var respWriteTests = []respWriteTest{ "Content-Length: 6\r\n\r\n" + "abcdef", }, + // Unchunked response without Content-Length. + { + Response{ + StatusCode: 200, + ProtoMajor: 1, + ProtoMinor: 0, + RequestMethod: "GET", + Header: map[string]string{}, + Body: nopCloser{bytes.NewBufferString("abcdef")}, + ContentLength: -1, + }, + "HTTP/1.0 200 OK\r\n" + + "\r\n" + + "abcdef", + }, // HTTP/1.1, chunked coding; empty trailer; close - respWriteTest{ + { Response{ StatusCode: 200, ProtoMajor: 1, diff --git a/src/pkg/http/serve_test.go b/src/pkg/http/serve_test.go new file mode 100644 index 000000000..43e1b93a5 --- /dev/null +++ b/src/pkg/http/serve_test.go @@ -0,0 +1,135 @@ +// 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. + +// End-to-end serving tests + +package http + +import ( + "bytes" + "os" + "net" + "testing" +) + +type dummyAddr string +type oneConnListener struct { + conn net.Conn +} + +func (l *oneConnListener) Accept() (c net.Conn, err os.Error) { + c = l.conn + if c == nil { + err = os.EOF + return + } + err = nil + l.conn = nil + return +} + +func (l *oneConnListener) Close() os.Error { + return nil +} + +func (l *oneConnListener) Addr() net.Addr { + return dummyAddr("test-address") +} + +func (a dummyAddr) Network() string { + return string(a) +} + +func (a dummyAddr) String() string { + return string(a) +} + +type testConn struct { + readBuf bytes.Buffer + writeBuf bytes.Buffer +} + +func (c *testConn) Read(b []byte) (int, os.Error) { + return c.readBuf.Read(b) +} + +func (c *testConn) Write(b []byte) (int, os.Error) { + return c.writeBuf.Write(b) +} + +func (c *testConn) Close() os.Error { + return nil +} + +func (c *testConn) LocalAddr() net.Addr { + return dummyAddr("local-addr") +} + +func (c *testConn) RemoteAddr() net.Addr { + return dummyAddr("remote-addr") +} + +func (c *testConn) SetTimeout(nsec int64) os.Error { + return nil +} + +func (c *testConn) SetReadTimeout(nsec int64) os.Error { + return nil +} + +func (c *testConn) SetWriteTimeout(nsec int64) os.Error { + return nil +} + +func TestConsumingBodyOnNextConn(t *testing.T) { + conn := new(testConn) + for i := 0; i < 2; i++ { + conn.readBuf.Write([]byte( + "POST / HTTP/1.1\r\n" + + "Host: test\r\n" + + "Content-Length: 11\r\n" + + "\r\n" + + "foo=1&bar=1")) + } + + reqNum := 0 + ch := make(chan *Request) + servech := make(chan os.Error) + listener := &oneConnListener{conn} + handler := func(res ResponseWriter, req *Request) { + reqNum++ + t.Logf("Got request #%d: %v", reqNum, req) + ch <- req + } + + go func() { + servech <- Serve(listener, HandlerFunc(handler)) + }() + + var req *Request + t.Log("Waiting for first request.") + req = <-ch + if req == nil { + t.Fatal("Got nil first request.") + } + if req.Method != "POST" { + t.Errorf("For request #1's method, got %q; expected %q", + req.Method, "POST") + } + + t.Log("Waiting for second request.") + req = <-ch + if req == nil { + t.Fatal("Got nil first request.") + } + if req.Method != "POST" { + t.Errorf("For request #2's method, got %q; expected %q", + req.Method, "POST") + } + + t.Log("Waiting for EOF.") + if serveerr := <-servech; serveerr != os.EOF { + t.Errorf("Serve returned %q; expected EOF", serveerr) + } +} diff --git a/src/pkg/http/server.go b/src/pkg/http/server.go index 81ce98229..b8783da28 100644 --- a/src/pkg/http/server.go +++ b/src/pkg/http/server.go @@ -13,6 +13,8 @@ package http import ( "bufio" + "crypto/rand" + "crypto/tls" "fmt" "io" "log" @@ -21,6 +23,7 @@ import ( "path" "strconv" "strings" + "time" ) // Errors introduced by the HTTP server. @@ -34,42 +37,93 @@ var ( // registered to serve a particular path or subtree // in the HTTP server. // -// ServeHTTP should write reply headers and data to the Conn +// ServeHTTP should write reply headers and data to the ResponseWriter // and then return. Returning signals that the request is finished // and that the HTTP server can move on to the next request on // the connection. type Handler interface { - ServeHTTP(*Conn, *Request) + ServeHTTP(ResponseWriter, *Request) } -// A Conn represents the server side of a single active HTTP connection. -type Conn struct { - RemoteAddr string // network address of remote side - Req *Request // current HTTP request +// A ResponseWriter interface is used by an HTTP handler to +// construct an HTTP response. +type ResponseWriter interface { + // RemoteAddr returns the address of the client that sent the current request + RemoteAddr() string + + // UsingTLS returns true if the client is connected using TLS + UsingTLS() bool + + // SetHeader sets a header line in the eventual response. + // For example, SetHeader("Content-Type", "text/html; charset=utf-8") + // will result in the header line + // + // Content-Type: text/html; charset=utf-8 + // + // being sent. UTF-8 encoded HTML is the default setting for + // Content-Type in this library, so users need not make that + // particular call. Calls to SetHeader after WriteHeader (or Write) + // are ignored. + SetHeader(string, string) + + // Write writes the data to the connection as part of an HTTP reply. + // If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK) + // before writing the data. + Write([]byte) (int, os.Error) + + // WriteHeader sends an HTTP response header with status code. + // If WriteHeader is not called explicitly, the first call to Write + // will trigger an implicit WriteHeader(http.StatusOK). + // Thus explicit calls to WriteHeader are mainly used to + // send error codes. + WriteHeader(int) + + // Flush sends any buffered data to the client. + Flush() + + // Hijack lets the caller take over the connection. + // After a call to Hijack(), the HTTP server library + // will not do anything else with the connection. + // It becomes the caller's responsibility to manage + // and close the connection. + Hijack() (io.ReadWriteCloser, *bufio.ReadWriter, os.Error) +} - rwc io.ReadWriteCloser // i/o connection - buf *bufio.ReadWriter // buffered rwc - handler Handler // request handler - hijacked bool // connection has been hijacked by handler +// A conn represents the server side of an HTTP connection. +type conn struct { + remoteAddr string // network address of remote side + handler Handler // request handler + rwc io.ReadWriteCloser // i/o connection + buf *bufio.ReadWriter // buffered rwc + hijacked bool // connection has been hijacked by handler + usingTLS bool // a flag indicating connection over TLS +} - // state for the current reply - closeAfterReply bool // close connection after this reply - chunking bool // using chunked transfer encoding for reply body - wroteHeader bool // reply header has been written - wroteContinue bool // 100 Continue response was written - header map[string]string // reply header parameters - written int64 // number of bytes written in body - status int // status code passed to WriteHeader +// A response represents the server side of an HTTP response. +type response struct { + conn *conn + req *Request // request for this response + chunking bool // using chunked transfer encoding for reply body + wroteHeader bool // reply header has been written + wroteContinue bool // 100 Continue response was written + header map[string]string // reply header parameters + written int64 // number of bytes written in body + status int // status code passed to WriteHeader + + // close connection after this reply. set on request and + // updated after response from handler if there's a + // "Connection: keep-alive" response header and a + // Content-Length. + closeAfterReply bool } // Create new connection from rwc. -func newConn(rwc net.Conn, handler Handler) (c *Conn, err os.Error) { - c = new(Conn) - if a := rwc.RemoteAddr(); a != nil { - c.RemoteAddr = a.String() - } +func newConn(rwc net.Conn, handler Handler) (c *conn, err os.Error) { + c = new(conn) + c.remoteAddr = rwc.RemoteAddr().String() c.handler = handler c.rwc = rwc + _, c.usingTLS = rwc.(*tls.Conn) br := bufio.NewReader(rwc) bw := bufio.NewWriter(rwc) c.buf = bufio.NewReadWriter(br, bw) @@ -79,17 +133,15 @@ func newConn(rwc net.Conn, handler Handler) (c *Conn, err os.Error) { // wrapper around io.ReaderCloser which on first read, sends an // HTTP/1.1 100 Continue header type expectContinueReader struct { - conn *Conn + resp *response readCloser io.ReadCloser } func (ecr *expectContinueReader) Read(p []byte) (n int, err os.Error) { - if !ecr.conn.wroteContinue && !ecr.conn.hijacked { - ecr.conn.wroteContinue = true - if ecr.conn.Req.ProtoAtLeast(1, 1) { - io.WriteString(ecr.conn.buf, "HTTP/1.1 100 Continue\r\n\r\n") - ecr.conn.buf.Flush() - } + if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked { + ecr.resp.wroteContinue = true + io.WriteString(ecr.resp.conn.buf, "HTTP/1.1 100 Continue\r\n\r\n") + ecr.resp.conn.buf.Flush() } return ecr.readCloser.Read(p) } @@ -98,88 +150,88 @@ func (ecr *expectContinueReader) Close() os.Error { return ecr.readCloser.Close() } +// TimeFormat is the time format to use with +// time.Parse and time.Time.Format when parsing +// or generating times in HTTP headers. +// It is like time.RFC1123 but hard codes GMT as the time zone. +const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT" + // Read next request from connection. -func (c *Conn) readRequest() (req *Request, err os.Error) { +func (c *conn) readRequest() (w *response, err os.Error) { if c.hijacked { return nil, ErrHijacked } + var req *Request if req, err = ReadRequest(c.buf.Reader); err != nil { return nil, err } - // Reset per-request connection state. - c.header = make(map[string]string) - c.wroteHeader = false - c.wroteContinue = false - c.Req = req + w = new(response) + w.conn = c + w.req = req + w.header = make(map[string]string) // Expect 100 Continue support - if req.expectsContinue() { + if req.expectsContinue() && req.ProtoAtLeast(1, 1) { // Wrap the Body reader with one that replies on the connection - req.Body = &expectContinueReader{readCloser: req.Body, conn: c} + req.Body = &expectContinueReader{readCloser: req.Body, resp: w} } // Default output is HTML encoded in UTF-8. - c.SetHeader("Content-Type", "text/html; charset=utf-8") + w.SetHeader("Content-Type", "text/html; charset=utf-8") + w.SetHeader("Date", time.UTC().Format(TimeFormat)) if req.ProtoAtLeast(1, 1) { // HTTP/1.1 or greater: use chunked transfer encoding // to avoid closing the connection at EOF. - c.chunking = true - c.SetHeader("Transfer-Encoding", "chunked") + w.chunking = true + w.SetHeader("Transfer-Encoding", "chunked") } else { // HTTP version < 1.1: cannot do chunked transfer // encoding, so signal EOF by closing connection. - // Could avoid closing the connection if there is - // a Content-Length: header in the response, - // but everyone who expects persistent connections - // does HTTP/1.1 now. - c.closeAfterReply = true - c.chunking = false + // Will be overridden if the HTTP handler ends up + // writing a Content-Length and the client requested + // "Connection: keep-alive" + w.closeAfterReply = true } - return req, nil + return w, nil } -// SetHeader sets a header line in the eventual reply. -// For example, SetHeader("Content-Type", "text/html; charset=utf-8") -// will result in the header line -// -// Content-Type: text/html; charset=utf-8 -// -// being sent. UTF-8 encoded HTML is the default setting for -// Content-Type in this library, so users need not make that -// particular call. Calls to SetHeader after WriteHeader (or Write) -// are ignored. -func (c *Conn) SetHeader(hdr, val string) { c.header[CanonicalHeaderKey(hdr)] = val } - -// WriteHeader sends an HTTP response header with status code. -// If WriteHeader is not called explicitly, the first call to Write -// will trigger an implicit WriteHeader(http.StatusOK). -// Thus explicit calls to WriteHeader are mainly used to -// send error codes. -func (c *Conn) WriteHeader(code int) { - if c.hijacked { - log.Stderr("http: Conn.WriteHeader on hijacked connection") +// UsingTLS implements the ResponseWriter.UsingTLS +func (w *response) UsingTLS() bool { + return w.conn.usingTLS +} + +// RemoteAddr implements the ResponseWriter.RemoteAddr method +func (w *response) RemoteAddr() string { return w.conn.remoteAddr } + +// SetHeader implements the ResponseWriter.SetHeader method +func (w *response) SetHeader(hdr, val string) { w.header[CanonicalHeaderKey(hdr)] = val } + +// WriteHeader implements the ResponseWriter.WriteHeader method +func (w *response) WriteHeader(code int) { + if w.conn.hijacked { + log.Print("http: response.WriteHeader on hijacked connection") return } - if c.wroteHeader { - log.Stderr("http: multiple Conn.WriteHeader calls") + if w.wroteHeader { + log.Print("http: multiple response.WriteHeader calls") return } - c.wroteHeader = true - c.status = code + w.wroteHeader = true + w.status = code if code == StatusNotModified { // Must not have body. - c.header["Content-Type"] = "", false - c.header["Transfer-Encoding"] = "", false + w.header["Content-Type"] = "", false + w.header["Transfer-Encoding"] = "", false + w.chunking = false } - c.written = 0 - if !c.Req.ProtoAtLeast(1, 0) { + if !w.req.ProtoAtLeast(1, 0) { return } proto := "HTTP/1.0" - if c.Req.ProtoAtLeast(1, 1) { + if w.req.ProtoAtLeast(1, 1) { proto = "HTTP/1.1" } codestring := strconv.Itoa(code) @@ -187,48 +239,55 @@ func (c *Conn) WriteHeader(code int) { if !ok { text = "status code " + codestring } - io.WriteString(c.buf, proto+" "+codestring+" "+text+"\r\n") - for k, v := range c.header { - io.WriteString(c.buf, k+": "+v+"\r\n") + io.WriteString(w.conn.buf, proto+" "+codestring+" "+text+"\r\n") + for k, v := range w.header { + io.WriteString(w.conn.buf, k+": "+v+"\r\n") } - io.WriteString(c.buf, "\r\n") + io.WriteString(w.conn.buf, "\r\n") } -// Write writes the data to the connection as part of an HTTP reply. -// If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK) -// before writing the data. -func (c *Conn) Write(data []byte) (n int, err os.Error) { - if c.hijacked { - log.Stderr("http: Conn.Write on hijacked connection") +// Write implements the ResponseWriter.Write method +func (w *response) Write(data []byte) (n int, err os.Error) { + if w.conn.hijacked { + log.Print("http: response.Write on hijacked connection") return 0, ErrHijacked } - if !c.wroteHeader { - c.WriteHeader(StatusOK) + if !w.wroteHeader { + if w.req.wantsHttp10KeepAlive() { + _, hasLength := w.header["Content-Length"] + if hasLength { + _, connectionHeaderSet := w.header["Connection"] + if !connectionHeaderSet { + w.header["Connection"] = "keep-alive" + } + } + } + w.WriteHeader(StatusOK) } if len(data) == 0 { return 0, nil } - if c.status == StatusNotModified { + if w.status == StatusNotModified { // Must not have body. return 0, ErrBodyNotAllowed } - c.written += int64(len(data)) // ignoring errors, for errorKludge + w.written += int64(len(data)) // ignoring errors, for errorKludge // TODO(rsc): if chunking happened after the buffering, // then there would be fewer chunk headers. // On the other hand, it would make hijacking more difficult. - if c.chunking { - fmt.Fprintf(c.buf, "%x\r\n", len(data)) // TODO(rsc): use strconv not fmt + if w.chunking { + fmt.Fprintf(w.conn.buf, "%x\r\n", len(data)) // TODO(rsc): use strconv not fmt } - n, err = c.buf.Write(data) - if err == nil && c.chunking { + n, err = w.conn.buf.Write(data) + if err == nil && w.chunking { if n != len(data) { err = io.ErrShortWrite } if err == nil { - io.WriteString(c.buf, "\r\n") + io.WriteString(w.conn.buf, "\r\n") } } @@ -242,25 +301,25 @@ func (c *Conn) Write(data []byte) (n int, err os.Error) { // long enough. The minimum lengths used in those // browsers are in the 256-512 range. // Pad to 1024 bytes. -func errorKludge(c *Conn, req *Request) { +func errorKludge(w *response) { const min = 1024 // Is this an error? - if kind := c.status / 100; kind != 4 && kind != 5 { + if kind := w.status / 100; kind != 4 && kind != 5 { return } // Did the handler supply any info? Enough? - if c.written == 0 || c.written >= min { + if w.written == 0 || w.written >= min { return } // Is it a broken browser? var msg string - switch agent := req.UserAgent; { - case strings.Index(agent, "MSIE") >= 0: + switch agent := w.req.UserAgent; { + case strings.Contains(agent, "MSIE"): msg = "Internet Explorer" - case strings.Index(agent, "Chrome/") >= 0: + case strings.Contains(agent, "Chrome/"): msg = "Chrome" default: return @@ -268,45 +327,54 @@ func errorKludge(c *Conn, req *Request) { msg += " would ignore this error page if this text weren't here.\n" // Is it text? ("Content-Type" is always in the map) - baseType := strings.Split(c.header["Content-Type"], ";", 2)[0] + baseType := strings.Split(w.header["Content-Type"], ";", 2)[0] switch baseType { case "text/html": - io.WriteString(c, "") + io.WriteString(w, " -->") case "text/plain": - io.WriteString(c, "\n") - for c.written < min { - io.WriteString(c, msg) + io.WriteString(w, "\n") + for w.written < min { + io.WriteString(w, msg) } } } -func (c *Conn) finishRequest() { - if !c.wroteHeader { - c.WriteHeader(StatusOK) +func (w *response) finishRequest() { + // If this was an HTTP/1.0 request with keep-alive and we sent a Content-Length + // back, we can make this a keep-alive response ... + if w.req.wantsHttp10KeepAlive() { + _, sentLength := w.header["Content-Length"] + if sentLength && w.header["Connection"] == "keep-alive" { + w.closeAfterReply = false + } + } + if !w.wroteHeader { + w.WriteHeader(StatusOK) } - errorKludge(c, c.Req) - if c.chunking { - io.WriteString(c.buf, "0\r\n") + errorKludge(w) + if w.chunking { + io.WriteString(w.conn.buf, "0\r\n") // trailer key/value pairs, followed by blank line - io.WriteString(c.buf, "\r\n") + io.WriteString(w.conn.buf, "\r\n") } - c.buf.Flush() + w.conn.buf.Flush() + w.req.Body.Close() } -// Flush sends any buffered data to the client. -func (c *Conn) Flush() { - if !c.wroteHeader { - c.WriteHeader(StatusOK) +// Flush implements the ResponseWriter.Flush method. +func (w *response) Flush() { + if !w.wroteHeader { + w.WriteHeader(StatusOK) } - c.buf.Flush() + w.conn.buf.Flush() } // Close the connection. -func (c *Conn) close() { +func (c *conn) close() { if c.buf != nil { c.buf.Flush() c.buf = nil @@ -318,41 +386,39 @@ func (c *Conn) close() { } // Serve a new connection. -func (c *Conn) serve() { +func (c *conn) serve() { for { - req, err := c.readRequest() + w, err := c.readRequest() if err != nil { break } - // HTTP cannot have multiple simultaneous active requests. + // HTTP cannot have multiple simultaneous active requests.[*] // Until the server replies to this request, it can't read another, // so we might as well run the handler in this goroutine. - c.handler.ServeHTTP(c, req) + // [*] Not strictly true: HTTP pipelining. We could let them all process + // in parallel even if their responses need to be serialized. + c.handler.ServeHTTP(w, w.req) if c.hijacked { return } - c.finishRequest() - if c.closeAfterReply { + w.finishRequest() + if w.closeAfterReply { break } } c.close() } -// Hijack lets the caller take over the connection. -// After a call to c.Hijack(), the HTTP server library -// will not do anything else with the connection. -// It becomes the caller's responsibility to manage -// and close the connection. -func (c *Conn) Hijack() (rwc io.ReadWriteCloser, buf *bufio.ReadWriter, err os.Error) { - if c.hijacked { +// Hijack impements the ResponseWriter.Hijack method. +func (w *response) Hijack() (rwc io.ReadWriteCloser, buf *bufio.ReadWriter, err os.Error) { + if w.conn.hijacked { return nil, nil, ErrHijacked } - c.hijacked = true - rwc = c.rwc - buf = c.buf - c.rwc = nil - c.buf = nil + w.conn.hijacked = true + rwc = w.conn.rwc + buf = w.conn.buf + w.conn.rwc = nil + w.conn.buf = nil return } @@ -360,24 +426,24 @@ func (c *Conn) Hijack() (rwc io.ReadWriteCloser, buf *bufio.ReadWriter, err os.E // ordinary functions as HTTP handlers. If f is a function // with the appropriate signature, HandlerFunc(f) is a // Handler object that calls f. -type HandlerFunc func(*Conn, *Request) +type HandlerFunc func(ResponseWriter, *Request) -// ServeHTTP calls f(c, req). -func (f HandlerFunc) ServeHTTP(c *Conn, req *Request) { - f(c, req) +// ServeHTTP calls f(w, req). +func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { + f(w, r) } // Helper handlers // Error replies to the request with the specified error message and HTTP code. -func Error(c *Conn, error string, code int) { - c.SetHeader("Content-Type", "text/plain; charset=utf-8") - c.WriteHeader(code) - fmt.Fprintln(c, error) +func Error(w ResponseWriter, error string, code int) { + w.SetHeader("Content-Type", "text/plain; charset=utf-8") + w.WriteHeader(code) + fmt.Fprintln(w, error) } // NotFound replies to the request with an HTTP 404 not found error. -func NotFound(c *Conn, req *Request) { Error(c, "404 page not found", StatusNotFound) } +func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", StatusNotFound) } // NotFoundHandler returns a simple request handler // that replies to each request with a ``404 page not found'' reply. @@ -385,59 +451,64 @@ func NotFoundHandler() Handler { return HandlerFunc(NotFound) } // Redirect replies to the request with a redirect to url, // which may be a path relative to the request path. -func Redirect(c *Conn, url string, code int) { - // RFC2616 recommends that a short note "SHOULD" be included in the - // response because older user agents may not understand 301/307. - note := "" + statusText[code] + ".\n" - if c.Req.Method == "POST" { - note = "" +func Redirect(w ResponseWriter, r *Request, url string, code int) { + if u, err := ParseURL(url); err == nil { + // If url was relative, make absolute by + // combining with request path. + // The browser would probably do this for us, + // but doing it ourselves is more reliable. + + // NOTE(rsc): RFC 2616 says that the Location + // line must be an absolute URI, like + // "http://www.google.com/redirect/", + // not a path like "/redirect/". + // Unfortunately, we don't know what to + // put in the host name section to get the + // client to connect to us again, so we can't + // know the right absolute URI to send back. + // Because of this problem, no one pays attention + // to the RFC; they all send back just a new path. + // So do we. + oldpath := r.URL.Path + if oldpath == "" { // should not happen, but avoid a crash if it does + oldpath = "/" + } + if u.Scheme == "" { + // no leading http://server + if url == "" || url[0] != '/' { + // make relative path absolute + olddir, _ := path.Split(oldpath) + url = olddir + url + } + + // clean up but preserve trailing slash + trailing := url[len(url)-1] == '/' + url = path.Clean(url) + if trailing && url[len(url)-1] != '/' { + url += "/" + } + } } - u, err := ParseURL(url) - if err != nil { - goto finish - } - - // If url was relative, make absolute by - // combining with request path. - // The browser would probably do this for us, - // but doing it ourselves is more reliable. - - // NOTE(rsc): RFC 2616 says that the Location - // line must be an absolute URI, like - // "http://www.google.com/redirect/", - // not a path like "/redirect/". - // Unfortunately, we don't know what to - // put in the host name section to get the - // client to connect to us again, so we can't - // know the right absolute URI to send back. - // Because of this problem, no one pays attention - // to the RFC; they all send back just a new path. - // So do we. - oldpath := c.Req.URL.Path - if oldpath == "" { // should not happen, but avoid a crash if it does - oldpath = "/" - } - if u.Scheme == "" { - // no leading http://server - if url == "" || url[0] != '/' { - // make relative path absolute - olddir, _ := path.Split(oldpath) - url = olddir + url - } + w.SetHeader("Location", url) + w.WriteHeader(code) - // clean up but preserve trailing slash - trailing := url[len(url)-1] == '/' - url = path.Clean(url) - if trailing && url[len(url)-1] != '/' { - url += "/" - } + // RFC2616 recommends that a short note "SHOULD" be included in the + // response because older user agents may not understand 301/307. + note := "" + statusText[code] + ".\n" + if r.Method == "POST" { + note = "" } + fmt.Fprintln(w, note) +} -finish: - c.SetHeader("Location", url) - c.WriteHeader(code) - fmt.Fprintf(c, note, url) +func htmlEscape(s string) string { + s = strings.Replace(s, "&", "&", -1) + s = strings.Replace(s, "<", "<", -1) + s = strings.Replace(s, ">", ">", -1) + s = strings.Replace(s, "\"", """, -1) + s = strings.Replace(s, "'", "'", -1) + return s } // Redirect to a fixed URL @@ -446,8 +517,8 @@ type redirectHandler struct { code int } -func (rh *redirectHandler) ServeHTTP(c *Conn, req *Request) { - Redirect(c, rh.url, rh.code) +func (rh *redirectHandler) ServeHTTP(w ResponseWriter, r *Request) { + Redirect(w, r, rh.url, rh.code) } // RedirectHandler returns a request handler that redirects @@ -523,11 +594,11 @@ func cleanPath(p string) string { // ServeHTTP dispatches the request to the handler whose // pattern most closely matches the request URL. -func (mux *ServeMux) ServeHTTP(c *Conn, req *Request) { +func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { // Clean path to canonical form and redirect. - if p := cleanPath(req.URL.Path); p != req.URL.Path { - c.SetHeader("Location", p) - c.WriteHeader(StatusMovedPermanently) + if p := cleanPath(r.URL.Path); p != r.URL.Path { + w.SetHeader("Location", p) + w.WriteHeader(StatusMovedPermanently) return } @@ -535,7 +606,7 @@ func (mux *ServeMux) ServeHTTP(c *Conn, req *Request) { var h Handler var n = 0 for k, v := range mux.m { - if !pathMatch(k, req.URL.Path) { + if !pathMatch(k, r.URL.Path) { continue } if h == nil || len(k) > n { @@ -546,7 +617,7 @@ func (mux *ServeMux) ServeHTTP(c *Conn, req *Request) { if h == nil { h = NotFoundHandler() } - h.ServeHTTP(c, req) + h.ServeHTTP(w, r) } // Handle registers the handler for the given pattern. @@ -566,7 +637,7 @@ func (mux *ServeMux) Handle(pattern string, handler Handler) { } // HandleFunc registers the handler function for the given pattern. -func (mux *ServeMux) HandleFunc(pattern string, handler func(*Conn, *Request)) { +func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { mux.Handle(pattern, HandlerFunc(handler)) } @@ -576,7 +647,7 @@ func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, h // HandleFunc registers the handler function for the given pattern // in the DefaultServeMux. -func HandleFunc(pattern string, handler func(*Conn, *Request)) { +func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) } @@ -618,8 +689,8 @@ func Serve(l net.Listener, handler Handler) os.Error { // ) // // // hello world, the web server -// func HelloServer(c *http.Conn, req *http.Request) { -// io.WriteString(c, "hello, world!\n") +// func HelloServer(w http.ResponseWriter, req *http.Request) { +// io.WriteString(w, "hello, world!\n") // } // // func main() { @@ -638,3 +709,52 @@ func ListenAndServe(addr string, handler Handler) os.Error { l.Close() return e } + +// ListenAndServeTLS acts identically to ListenAndServe, except that it +// expects HTTPS connections. Additionally, files containing a certificate and +// matching private key for the server must be provided. +// +// A trivial example server is: +// +// import ( +// "http" +// "log" +// ) +// +// func handler(w http.ResponseWriter, req *http.Request) { +// w.SetHeader("Content-Type", "text/plain") +// w.Write([]byte("This is an example server.\n")) +// } +// +// func main() { +// http.HandleFunc("/", handler) +// log.Printf("About to listen on 10443. Go to https://127.0.0.1:10443/") +// err := http.ListenAndServeTLS(":10443", "cert.pem", "key.pem", nil) +// if err != nil { +// log.Exit(err) +// } +// } +// +// One can use generate_cert.go in crypto/tls to generate cert.pem and key.pem. +func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler) os.Error { + config := &tls.Config{ + Rand: rand.Reader, + Time: time.Seconds, + NextProtos: []string{"http/1.1"}, + } + + var err os.Error + config.Certificates = make([]tls.Certificate, 1) + config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return err + } + + conn, err := net.Listen("tcp", addr) + if err != nil { + return err + } + + tlsListener := tls.NewListener(conn, config) + return Serve(tlsListener, handler) +} diff --git a/src/pkg/http/status.go b/src/pkg/http/status.go index 82a66d7ad..b6e2d65c6 100644 --- a/src/pkg/http/status.go +++ b/src/pkg/http/status.go @@ -98,3 +98,9 @@ var statusText = map[int]string{ StatusGatewayTimeout: "Gateway Timeout", StatusHTTPVersionNotSupported: "HTTP Version Not Supported", } + +// StatusText returns a text for the HTTP status code. It returns the empty +// string if the code is unknown. +func StatusText(code int) string { + return statusText[code] +} diff --git a/src/pkg/http/testdata/file b/src/pkg/http/testdata/file new file mode 100644 index 000000000..11f11f9be --- /dev/null +++ b/src/pkg/http/testdata/file @@ -0,0 +1 @@ +0123456789 diff --git a/src/pkg/http/transfer.go b/src/pkg/http/transfer.go index 5e190d74c..e62885d62 100644 --- a/src/pkg/http/transfer.go +++ b/src/pkg/http/transfer.go @@ -108,7 +108,7 @@ func (t *transferWriter) WriteHeader(w io.Writer) (err os.Error) { // writing long headers, using HTTP line splitting io.WriteString(w, "Trailer: ") needComma := false - for k, _ := range t.Trailer { + for k := range t.Trailer { k = CanonicalHeaderKey(k) switch k { case "Transfer-Encoding", "Trailer", "Content-Length": @@ -135,6 +135,8 @@ func (t *transferWriter) WriteBody(w io.Writer) (err os.Error) { if err == nil { err = cw.Close() } + } else if t.ContentLength == -1 { + _, err = io.Copy(w, t.Body) } else { _, err = io.Copy(w, io.LimitReader(t.Body, t.ContentLength)) } @@ -182,6 +184,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) { t.RequestMethod = rr.RequestMethod t.ProtoMajor = rr.ProtoMajor t.ProtoMinor = rr.ProtoMinor + t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header) case *Request: t.Header = rr.Header t.ProtoMajor = rr.ProtoMajor @@ -208,9 +211,6 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) { return err } - // Closing - t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header) - // Trailer t.Trailer, err = fixTrailer(t.Header, t.TransferEncoding) if err != nil { @@ -340,7 +340,7 @@ func fixLength(status int, requestMethod string, header map[string]string, te [] // Logic based on media type. The purpose of the following code is just // to detect whether the unsupported "multipart/byteranges" is being // used. A proper Content-Type parser is needed in the future. - if strings.Index(strings.ToLower(header["Content-Type"]), "multipart/byteranges") >= 0 { + if strings.Contains(strings.ToLower(header["Content-Type"]), "multipart/byteranges") { return -1, ErrNotSupported } @@ -350,9 +350,20 @@ func fixLength(status int, requestMethod string, header map[string]string, te [] // Determine whether to hang up after sending a request and body, or // receiving a response and body +// 'header' is the request headers func shouldClose(major, minor int, header map[string]string) bool { - if major < 1 || (major == 1 && minor < 1) { + if major < 1 { return true + } else if major == 1 && minor == 0 { + v, present := header["Connection"] + if !present { + return true + } + v = strings.ToLower(v) + if !strings.Contains(v, "keep-alive") { + return true + } + return false } else if v, present := header["Connection"]; present { // TODO: Should split on commas, toss surrounding white space, // and check each field. diff --git a/src/pkg/http/triv.go b/src/pkg/http/triv.go index 612b6161e..03cfafa7b 100644 --- a/src/pkg/http/triv.go +++ b/src/pkg/http/triv.go @@ -20,9 +20,9 @@ import ( // hello world, the web server var helloRequests = expvar.NewInt("hello-requests") -func HelloServer(c *http.Conn, req *http.Request) { +func HelloServer(w http.ResponseWriter, req *http.Request) { helloRequests.Add(1) - io.WriteString(c, "hello, world!\n") + io.WriteString(w, "hello, world!\n") } // Simple counter server. POSTing to it will set the value. @@ -34,7 +34,7 @@ type Counter struct { // it directly. func (ctr *Counter) String() string { return fmt.Sprintf("%d", ctr.n) } -func (ctr *Counter) ServeHTTP(c *http.Conn, req *http.Request) { +func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) { switch req.Method { case "GET": ctr.n++ @@ -43,53 +43,34 @@ func (ctr *Counter) ServeHTTP(c *http.Conn, req *http.Request) { io.Copy(buf, req.Body) body := buf.String() if n, err := strconv.Atoi(body); err != nil { - fmt.Fprintf(c, "bad POST: %v\nbody: [%v]\n", err, body) + fmt.Fprintf(w, "bad POST: %v\nbody: [%v]\n", err, body) } else { ctr.n = n - fmt.Fprint(c, "counter reset\n") + fmt.Fprint(w, "counter reset\n") } } - fmt.Fprintf(c, "counter = %d\n", ctr.n) -} - -// simple file server -var webroot = flag.String("root", "/home/rsc", "web root directory") -var pathVar = expvar.NewMap("file-requests") - -func FileServer(c *http.Conn, req *http.Request) { - c.SetHeader("content-type", "text/plain; charset=utf-8") - pathVar.Add(req.URL.Path, 1) - path := *webroot + req.URL.Path // TODO: insecure: use os.CleanName - f, err := os.Open(path, os.O_RDONLY, 0) - if err != nil { - c.WriteHeader(http.StatusNotFound) - fmt.Fprintf(c, "open %s: %v\n", path, err) - return - } - n, _ := io.Copy(c, f) - fmt.Fprintf(c, "[%d bytes]\n", n) - f.Close() + fmt.Fprintf(w, "counter = %d\n", ctr.n) } // simple flag server var booleanflag = flag.Bool("boolean", true, "another flag for testing") -func FlagServer(c *http.Conn, req *http.Request) { - c.SetHeader("content-type", "text/plain; charset=utf-8") - fmt.Fprint(c, "Flags:\n") +func FlagServer(w http.ResponseWriter, req *http.Request) { + w.SetHeader("content-type", "text/plain; charset=utf-8") + fmt.Fprint(w, "Flags:\n") flag.VisitAll(func(f *flag.Flag) { if f.Value.String() != f.DefValue { - fmt.Fprintf(c, "%s = %s [default = %s]\n", f.Name, f.Value.String(), f.DefValue) + fmt.Fprintf(w, "%s = %s [default = %s]\n", f.Name, f.Value.String(), f.DefValue) } else { - fmt.Fprintf(c, "%s = %s\n", f.Name, f.Value.String()) + fmt.Fprintf(w, "%s = %s\n", f.Name, f.Value.String()) } }) } // simple argument server -func ArgServer(c *http.Conn, req *http.Request) { +func ArgServer(w http.ResponseWriter, req *http.Request) { for _, s := range os.Args { - fmt.Fprint(c, s, " ") + fmt.Fprint(w, s, " ") } } @@ -106,44 +87,46 @@ func ChanCreate() Chan { return c } -func (ch Chan) ServeHTTP(c *http.Conn, req *http.Request) { - io.WriteString(c, fmt.Sprintf("channel send #%d\n", <-ch)) +func (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) { + io.WriteString(w, fmt.Sprintf("channel send #%d\n", <-ch)) } // exec a program, redirecting output -func DateServer(c *http.Conn, req *http.Request) { - c.SetHeader("content-type", "text/plain; charset=utf-8") +func DateServer(rw http.ResponseWriter, req *http.Request) { + rw.SetHeader("content-type", "text/plain; charset=utf-8") r, w, err := os.Pipe() if err != nil { - fmt.Fprintf(c, "pipe: %s\n", err) + fmt.Fprintf(rw, "pipe: %s\n", err) return } pid, err := os.ForkExec("/bin/date", []string{"date"}, os.Environ(), "", []*os.File{nil, w, w}) defer r.Close() w.Close() if err != nil { - fmt.Fprintf(c, "fork/exec: %s\n", err) + fmt.Fprintf(rw, "fork/exec: %s\n", err) return } - io.Copy(c, r) + io.Copy(rw, r) wait, err := os.Wait(pid, 0) if err != nil { - fmt.Fprintf(c, "wait: %s\n", err) + fmt.Fprintf(rw, "wait: %s\n", err) return } if !wait.Exited() || wait.ExitStatus() != 0 { - fmt.Fprintf(c, "date: %v\n", wait) + fmt.Fprintf(rw, "date: %v\n", wait) return } } -func Logger(c *http.Conn, req *http.Request) { - log.Stdout(req.URL.Raw) - c.WriteHeader(404) - c.Write([]byte("oops")) +func Logger(w http.ResponseWriter, req *http.Request) { + log.Print(req.URL.Raw) + w.WriteHeader(404) + w.Write([]byte("oops")) } +var webroot = flag.String("root", "/home/rsc", "web root directory") + func main() { flag.Parse() @@ -153,7 +136,7 @@ func main() { expvar.Publish("counter", ctr) http.Handle("/", http.HandlerFunc(Logger)) - http.Handle("/go/", http.HandlerFunc(FileServer)) + http.Handle("/go/", http.FileServer(*webroot, "/go/")) http.Handle("/flags", http.HandlerFunc(FlagServer)) http.Handle("/args", http.HandlerFunc(ArgServer)) http.Handle("/go/hello", http.HandlerFunc(HelloServer)) @@ -161,6 +144,6 @@ func main() { http.Handle("/date", http.HandlerFunc(DateServer)) err := http.ListenAndServe(":12345", nil) if err != nil { - log.Crash("ListenAndServe: ", err) + log.Panicln("ListenAndServe:", err) } } diff --git a/src/pkg/http/url.go b/src/pkg/http/url.go index 148ada4b2..f0ac4c1df 100644 --- a/src/pkg/http/url.go +++ b/src/pkg/http/url.go @@ -46,6 +46,17 @@ func unhex(c byte) byte { return 0 } +type encoding int + +const ( + encodePath encoding = 1 + iota + encodeUserPassword + encodeQueryComponent + encodeFragment + encodeOpaque +) + + type URLEscapeError string func (e URLEscapeError) String() string { @@ -54,17 +65,53 @@ func (e URLEscapeError) String() string { // Return true if the specified character should be escaped when // appearing in a URL string, according to RFC 2396. -func shouldEscape(c byte) bool { - if c <= ' ' || c >= 0x7F { - return true +// When 'all' is true the full range of reserved characters are matched. +func shouldEscape(c byte, mode encoding) bool { + // RFC 2396 §2.3 Unreserved characters (alphanum) + if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' { + return false } switch c { - case '<', '>', '#', '%', '"', // RFC 2396 delims - '{', '}', '|', '\\', '^', '[', ']', '`', // RFC2396 unwise - '?', '&', '=', '+': // RFC 2396 reserved in path - return true + case '-', '_', '.', '!', '~', '*', '\'', '(', ')': // §2.3 Unreserved characters (mark) + return false + + case '$', '&', '+', ',', '/', ':', ';', '=', '?', '@': // §2.2 Reserved characters (reserved) + // Different sections of the URL allow a few of + // the reserved characters to appear unescaped. + switch mode { + case encodePath: // §3.3 + // The RFC allows : @ & = + $ , but saves / ; for assigning + // meaning to individual path segments. This package + // only manipulates the path as a whole, so we allow those + // last two as well. Clients that need to distinguish between + // `/foo;y=z/bar` and `/foo%3by=z/bar` will have to re-decode RawPath. + // That leaves only ? to escape. + return c == '?' + + case encodeUserPassword: // §3.2.2 + // The RFC allows ; : & = + $ , in userinfo, so we must escape only @ and /. + // The parsing of userinfo treats : as special so we must escape that too. + return c == '@' || c == '/' || c == ':' + + case encodeQueryComponent: // §3.4 + // The RFC reserves (so we must escape) everything. + return true + + case encodeFragment: // §4.1 + // The RFC text is silent but the grammar allows + // everything, so escape nothing. + return false + + case encodeOpaque: // §3 opaque_part + // The RFC allows opaque_part to use all characters + // except that the leading / must be escaped. + // (We implement that case in String.) + return false + } } - return false + + // Everything else must be escaped. + return true } // CanonicalPath applies the algorithm specified in RFC 2396 to @@ -124,17 +171,19 @@ func CanonicalPath(path string) string { return string(a) } -// URLUnescape unescapes a URL-encoded string, +// URLUnescape unescapes a string in ``URL encoded'' form, // converting %AB into the byte 0xAB and '+' into ' ' (space). // It returns an error if any % is not followed // by two hexadecimal digits. -func URLUnescape(s string) (string, os.Error) { return urlUnescape(s, true) } +// Despite the name, this encoding applies only to individual +// components of the query portion of the URL. +func URLUnescape(s string) (string, os.Error) { + return urlUnescape(s, encodeQueryComponent) +} -// urlUnescape is like URLUnescape but can be told not to -// convert + into space. URLUnescape implements what is -// called "URL encoding" but that only applies to query strings. -// Elsewhere in the URL, + does not mean space. -func urlUnescape(s string, doPlus bool) (string, os.Error) { +// urlUnescape is like URLUnescape but mode specifies +// which section of the URL is being unescaped. +func urlUnescape(s string, mode encoding) (string, os.Error) { // Count %, check that they're well-formed. n := 0 hasPlus := false @@ -151,7 +200,7 @@ func urlUnescape(s string, doPlus bool) (string, os.Error) { } i += 3 case '+': - hasPlus = doPlus + hasPlus = mode == encodeQueryComponent i++ default: i++ @@ -171,7 +220,7 @@ func urlUnescape(s string, doPlus bool) (string, os.Error) { j++ i += 3 case '+': - if doPlus { + if mode == encodeQueryComponent { t[j] = ' ' } else { t[j] = '+' @@ -187,15 +236,19 @@ func urlUnescape(s string, doPlus bool) (string, os.Error) { return string(t), nil } -// URLEscape converts a string into URL-encoded form. -func URLEscape(s string) string { return urlEscape(s, true) } +// URLEscape converts a string into ``URL encoded'' form. +// Despite the name, this encoding applies only to individual +// components of the query portion of the URL. +func URLEscape(s string) string { + return urlEscape(s, encodeQueryComponent) +} -func urlEscape(s string, doPlus bool) string { +func urlEscape(s string, mode encoding) string { spaceCount, hexCount := 0, 0 for i := 0; i < len(s); i++ { c := s[i] - if shouldEscape(c) { - if c == ' ' && doPlus { + if shouldEscape(c, mode) { + if c == ' ' && mode == encodeQueryComponent { spaceCount++ } else { hexCount++ @@ -211,10 +264,10 @@ func urlEscape(s string, doPlus bool) string { j := 0 for i := 0; i < len(s); i++ { switch c := s[i]; { - case c == ' ' && doPlus: + case c == ' ' && mode == encodeQueryComponent: t[j] = '+' j++ - case shouldEscape(c): + case shouldEscape(c, mode): t[j] = '%' t[j+1] = "0123456789abcdef"[c>>4] t[j+2] = "0123456789abcdef"[c&15] @@ -227,25 +280,64 @@ func urlEscape(s string, doPlus bool) string { return string(t) } +// UnescapeUserinfo parses the RawUserinfo field of a URL +// as the form user or user:password and unescapes and returns +// the two halves. +// +// This functionality should only be used with legacy web sites. +// RFC 2396 warns that interpreting Userinfo this way +// ``is NOT RECOMMENDED, because the passing of authentication +// information in clear text (such as URI) has proven to be a +// security risk in almost every case where it has been used.'' +func UnescapeUserinfo(rawUserinfo string) (user, password string, err os.Error) { + u, p := split(rawUserinfo, ':', true) + if user, err = urlUnescape(u, encodeUserPassword); err != nil { + return "", "", err + } + if password, err = urlUnescape(p, encodeUserPassword); err != nil { + return "", "", err + } + return +} + +// EscapeUserinfo combines user and password in the form +// user:password (or just user if password is empty) and then +// escapes it for use as the URL.RawUserinfo field. +// +// This functionality should only be used with legacy web sites. +// RFC 2396 warns that interpreting Userinfo this way +// ``is NOT RECOMMENDED, because the passing of authentication +// information in clear text (such as URI) has proven to be a +// security risk in almost every case where it has been used.'' +func EscapeUserinfo(user, password string) string { + raw := urlEscape(user, encodeUserPassword) + if password != "" { + raw += ":" + urlEscape(password, encodeUserPassword) + } + return raw +} + // A URL represents a parsed URL (technically, a URI reference). // The general form represented is: // scheme://[userinfo@]host/path[?query][#fragment] -// The Raw, RawPath, and RawQuery fields are in "wire format" (special -// characters must be hex-escaped if not meant to have special meaning). +// The Raw, RawAuthority, RawPath, and RawQuery fields are in "wire format" +// (special characters must be hex-escaped if not meant to have special meaning). // All other fields are logical values; '+' or '%' represent themselves. // -// Note, the reason for using wire format for the query is that it needs -// to be split into key/value pairs before decoding. +// The various Raw values are supplied in wire format because +// clients typically have to split them into pieces before further +// decoding. type URL struct { - Raw string // the original string - Scheme string // scheme - Authority string // [userinfo@]host - Userinfo string // userinfo - Host string // host - RawPath string // /path[?query][#fragment] - Path string // /path - RawQuery string // query - Fragment string // fragment + Raw string // the original string + Scheme string // scheme + RawAuthority string // [userinfo@]host + RawUserinfo string // userinfo + Host string // host + RawPath string // /path[?query][#fragment] + Path string // /path + OpaquePath bool // path is opaque (unrooted when scheme is present) + RawQuery string // query + Fragment string // fragment } // Maybe rawurl is of the form scheme:path. @@ -301,56 +393,63 @@ func ParseURL(rawurl string) (url *URL, err os.Error) { url = new(URL) url.Raw = rawurl - // split off possible leading "http:", "mailto:", etc. + // Split off possible leading "http:", "mailto:", etc. + // Cannot contain escaped characters. var path string if url.Scheme, path, err = getscheme(rawurl); err != nil { goto Error } - // RFC 2396: a relative URI (no scheme) has a ?query, - // but absolute URIs only have query if path begins with / - var query string - if url.Scheme == "" || len(path) > 0 && path[0] == '/' { - path, query = split(path, '?', false) + if url.Scheme != "" && (len(path) == 0 || path[0] != '/') { + // RFC 2396: + // Absolute URI (has scheme) with non-rooted path + // is uninterpreted. It doesn't even have a ?query. + // This is the case that handles mailto:name@example.com. + url.RawPath = path + + if url.Path, err = urlUnescape(path, encodeOpaque); err != nil { + goto Error + } + url.OpaquePath = true + } else { + // Split off query before parsing path further. + url.RawPath = path + path, query := split(path, '?', false) if len(query) > 1 { url.RawQuery = query[1:] } - } - // Maybe path is //authority/path - if len(path) > 2 && path[0:2] == "//" { - url.Authority, path = split(path[2:], '/', false) - } - url.RawPath = path + query + // Maybe path is //authority/path + if url.Scheme != "" && len(path) > 2 && path[0:2] == "//" { + url.RawAuthority, path = split(path[2:], '/', false) + url.RawPath = url.RawPath[2+len(url.RawAuthority):] + } - // If there's no @, split's default is wrong. Check explicitly. - if strings.Index(url.Authority, "@") < 0 { - url.Host = url.Authority - } else { - url.Userinfo, url.Host = split(url.Authority, '@', true) - } + // Split authority into userinfo@host. + // If there's no @, split's default is wrong. Check explicitly. + var rawHost string + if strings.Index(url.RawAuthority, "@") < 0 { + rawHost = url.RawAuthority + } else { + url.RawUserinfo, rawHost = split(url.RawAuthority, '@', true) + } - if url.Path, err = urlUnescape(path, false); err != nil { - goto Error - } + // We leave RawAuthority only in raw form because clients + // of common protocols should be using Userinfo and Host + // instead. Clients that wish to use RawAuthority will have to + // interpret it themselves: RFC 2396 does not define the meaning. - // Remove escapes from the Authority and Userinfo fields, and verify - // that Scheme and Host contain no escapes (that would be illegal). - if url.Authority, err = urlUnescape(url.Authority, false); err != nil { - goto Error - } - if url.Userinfo, err = urlUnescape(url.Userinfo, false); err != nil { - goto Error - } - if strings.Index(url.Scheme, "%") >= 0 { - err = os.ErrorString("hexadecimal escape in scheme") - goto Error - } - if strings.Index(url.Host, "%") >= 0 { - err = os.ErrorString("hexadecimal escape in host") - goto Error - } + if strings.Contains(rawHost, "%") { + // Host cannot contain escaped characters. + err = os.ErrorString("hexadecimal escape in host") + goto Error + } + url.Host = rawHost + if url.Path, err = urlUnescape(path, encodePath); err != nil { + goto Error + } + } return url, nil Error: @@ -369,7 +468,7 @@ func ParseURLReference(rawurlref string) (url *URL, err os.Error) { url.RawPath += frag if len(frag) > 1 { frag = frag[1:] - if url.Fragment, err = urlUnescape(frag, false); err != nil { + if url.Fragment, err = urlUnescape(frag, encodeFragment); err != nil { return nil, &URLError{"parse", rawurl, err} } } @@ -379,26 +478,52 @@ func ParseURLReference(rawurlref string) (url *URL, err os.Error) { // String reassembles url into a valid URL string. // // There are redundant fields stored in the URL structure: -// the String method consults Scheme, Path, Host, Userinfo, +// the String method consults Scheme, Path, Host, RawUserinfo, // RawQuery, and Fragment, but not Raw, RawPath or Authority. func (url *URL) String() string { result := "" if url.Scheme != "" { result += url.Scheme + ":" } - if url.Host != "" || url.Userinfo != "" { + if url.Host != "" || url.RawUserinfo != "" { result += "//" - if url.Userinfo != "" { - result += urlEscape(url.Userinfo, false) + "@" + if url.RawUserinfo != "" { + // hide the password, if any + info := url.RawUserinfo + if i := strings.Index(info, ":"); i >= 0 { + info = info[0:i] + ":******" + } + result += info + "@" } result += url.Host } - result += urlEscape(url.Path, false) + if url.OpaquePath { + path := url.Path + if strings.HasPrefix(path, "/") { + result += "%2f" + path = path[1:] + } + result += urlEscape(path, encodeOpaque) + } else { + result += urlEscape(url.Path, encodePath) + } if url.RawQuery != "" { result += "?" + url.RawQuery } if url.Fragment != "" { - result += "#" + urlEscape(url.Fragment, false) + result += "#" + urlEscape(url.Fragment, encodeFragment) } return result } + +// EncodeQuery encodes the query represented as a multimap. +func EncodeQuery(m map[string][]string) string { + parts := make([]string, 0, len(m)) // will be large enough for most uses + for k, vs := range m { + prefix := URLEscape(k) + "=" + for _, v := range vs { + parts = append(parts, prefix+URLEscape(v)) + } + } + return strings.Join(parts, "&") +} diff --git a/src/pkg/http/url_test.go b/src/pkg/http/url_test.go index 3d665100a..447d5390e 100644 --- a/src/pkg/http/url_test.go +++ b/src/pkg/http/url_test.go @@ -24,125 +24,138 @@ type URLTest struct { var urltests = []URLTest{ // no path - URLTest{ + { "http://www.google.com", &URL{ - Raw: "http://www.google.com", - Scheme: "http", - Authority: "www.google.com", - Host: "www.google.com", + Raw: "http://www.google.com", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", }, "", }, // path - URLTest{ + { "http://www.google.com/", &URL{ - Raw: "http://www.google.com/", - Scheme: "http", - Authority: "www.google.com", - Host: "www.google.com", - RawPath: "/", - Path: "/", + Raw: "http://www.google.com/", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", + RawPath: "/", + Path: "/", }, "", }, // path with hex escaping - URLTest{ + { "http://www.google.com/file%20one%26two", &URL{ - Raw: "http://www.google.com/file%20one%26two", - Scheme: "http", - Authority: "www.google.com", - Host: "www.google.com", - RawPath: "/file%20one%26two", - Path: "/file one&two", + Raw: "http://www.google.com/file%20one%26two", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", + RawPath: "/file%20one%26two", + Path: "/file one&two", }, - "http://www.google.com/file%20one%26two", + "http://www.google.com/file%20one&two", }, // user - URLTest{ + { "ftp://webmaster@www.google.com/", &URL{ - Raw: "ftp://webmaster@www.google.com/", - Scheme: "ftp", - Authority: "webmaster@www.google.com", - Userinfo: "webmaster", - Host: "www.google.com", - RawPath: "/", - Path: "/", + Raw: "ftp://webmaster@www.google.com/", + Scheme: "ftp", + RawAuthority: "webmaster@www.google.com", + RawUserinfo: "webmaster", + Host: "www.google.com", + RawPath: "/", + Path: "/", }, "", }, // escape sequence in username - URLTest{ + { "ftp://john%20doe@www.google.com/", &URL{ - Raw: "ftp://john%20doe@www.google.com/", - Scheme: "ftp", - Authority: "john doe@www.google.com", - Userinfo: "john doe", - Host: "www.google.com", - RawPath: "/", - Path: "/", + Raw: "ftp://john%20doe@www.google.com/", + Scheme: "ftp", + RawAuthority: "john%20doe@www.google.com", + RawUserinfo: "john%20doe", + Host: "www.google.com", + RawPath: "/", + Path: "/", }, "ftp://john%20doe@www.google.com/", }, // query - URLTest{ + { "http://www.google.com/?q=go+language", &URL{ - Raw: "http://www.google.com/?q=go+language", - Scheme: "http", - Authority: "www.google.com", - Host: "www.google.com", - RawPath: "/?q=go+language", - Path: "/", - RawQuery: "q=go+language", + Raw: "http://www.google.com/?q=go+language", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", + RawPath: "/?q=go+language", + Path: "/", + RawQuery: "q=go+language", }, "", }, // query with hex escaping: NOT parsed - URLTest{ + { "http://www.google.com/?q=go%20language", &URL{ - Raw: "http://www.google.com/?q=go%20language", - Scheme: "http", - Authority: "www.google.com", - Host: "www.google.com", - RawPath: "/?q=go%20language", - Path: "/", - RawQuery: "q=go%20language", + Raw: "http://www.google.com/?q=go%20language", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", + RawPath: "/?q=go%20language", + Path: "/", + RawQuery: "q=go%20language", }, "", }, // %20 outside query - URLTest{ + { "http://www.google.com/a%20b?q=c+d", &URL{ - Raw: "http://www.google.com/a%20b?q=c+d", - Scheme: "http", - Authority: "www.google.com", - Host: "www.google.com", - RawPath: "/a%20b?q=c+d", - Path: "/a b", - RawQuery: "q=c+d", + Raw: "http://www.google.com/a%20b?q=c+d", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", + RawPath: "/a%20b?q=c+d", + Path: "/a b", + RawQuery: "q=c+d", }, "", }, - // path without /, so no query parsing - URLTest{ + // path without leading /, so no query parsing + { "http:www.google.com/?q=go+language", &URL{ - Raw: "http:www.google.com/?q=go+language", - Scheme: "http", - RawPath: "www.google.com/?q=go+language", - Path: "www.google.com/?q=go+language", + Raw: "http:www.google.com/?q=go+language", + Scheme: "http", + RawPath: "www.google.com/?q=go+language", + Path: "www.google.com/?q=go+language", + OpaquePath: true, + }, + "http:www.google.com/?q=go+language", + }, + // path without leading /, so no query parsing + { + "http:%2f%2fwww.google.com/?q=go+language", + &URL{ + Raw: "http:%2f%2fwww.google.com/?q=go+language", + Scheme: "http", + RawPath: "%2f%2fwww.google.com/?q=go+language", + Path: "//www.google.com/?q=go+language", + OpaquePath: true, }, - "http:www.google.com/%3fq%3dgo%2blanguage", + "http:%2f/www.google.com/?q=go+language", }, // non-authority - URLTest{ + { "mailto:/webmaster@golang.org", &URL{ Raw: "mailto:/webmaster@golang.org", @@ -153,18 +166,19 @@ var urltests = []URLTest{ "", }, // non-authority - URLTest{ + { "mailto:webmaster@golang.org", &URL{ - Raw: "mailto:webmaster@golang.org", - Scheme: "mailto", - RawPath: "webmaster@golang.org", - Path: "webmaster@golang.org", + Raw: "mailto:webmaster@golang.org", + Scheme: "mailto", + RawPath: "webmaster@golang.org", + Path: "webmaster@golang.org", + OpaquePath: true, }, "", }, // unescaped :// in query should not create a scheme - URLTest{ + { "/foo?query=http://bad", &URL{ Raw: "/foo?query=http://bad", @@ -174,59 +188,92 @@ var urltests = []URLTest{ }, "", }, + // leading // without scheme shouldn't create an authority + { + "//foo", + &URL{ + Raw: "//foo", + Scheme: "", + RawPath: "//foo", + Path: "//foo", + }, + "", + }, + { + "http://user:password@google.com", + &URL{ + Raw: "http://user:password@google.com", + Scheme: "http", + RawAuthority: "user:password@google.com", + RawUserinfo: "user:password", + Host: "google.com", + }, + "http://user:******@google.com", + }, + { + "http://user:longerpass@google.com", + &URL{ + Raw: "http://user:longerpass@google.com", + Scheme: "http", + RawAuthority: "user:longerpass@google.com", + RawUserinfo: "user:longerpass", + Host: "google.com", + }, + "http://user:******@google.com", + }, } var urlnofragtests = []URLTest{ - URLTest{ + { "http://www.google.com/?q=go+language#foo", &URL{ - Raw: "http://www.google.com/?q=go+language#foo", - Scheme: "http", - Authority: "www.google.com", - Host: "www.google.com", - RawPath: "/?q=go+language#foo", - Path: "/", - RawQuery: "q=go+language#foo", + Raw: "http://www.google.com/?q=go+language#foo", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", + RawPath: "/?q=go+language#foo", + Path: "/", + RawQuery: "q=go+language#foo", }, "", }, } var urlfragtests = []URLTest{ - URLTest{ + { "http://www.google.com/?q=go+language#foo", &URL{ - Raw: "http://www.google.com/?q=go+language#foo", - Scheme: "http", - Authority: "www.google.com", - Host: "www.google.com", - RawPath: "/?q=go+language#foo", - Path: "/", - RawQuery: "q=go+language", - Fragment: "foo", + Raw: "http://www.google.com/?q=go+language#foo", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", + RawPath: "/?q=go+language#foo", + Path: "/", + RawQuery: "q=go+language", + Fragment: "foo", }, "", }, - URLTest{ + { "http://www.google.com/?q=go+language#foo%26bar", &URL{ - Raw: "http://www.google.com/?q=go+language#foo%26bar", - Scheme: "http", - Authority: "www.google.com", - Host: "www.google.com", - RawPath: "/?q=go+language#foo%26bar", - Path: "/", - RawQuery: "q=go+language", - Fragment: "foo&bar", + Raw: "http://www.google.com/?q=go+language#foo%26bar", + Scheme: "http", + RawAuthority: "www.google.com", + Host: "www.google.com", + RawPath: "/?q=go+language#foo%26bar", + Path: "/", + RawQuery: "q=go+language", + Fragment: "foo&bar", }, - "", + "http://www.google.com/?q=go+language#foo&bar", }, } // more useful string for debugging than fmt's struct printer func ufmt(u *URL) string { return fmt.Sprintf("%q, %q, %q, %q, %q, %q, %q, %q, %q", - u.Raw, u.Scheme, u.RawPath, u.Authority, u.Userinfo, + u.Raw, u.Scheme, u.RawPath, u.RawAuthority, u.RawUserinfo, u.Host, u.Path, u.RawQuery, u.Fragment) } @@ -274,11 +321,9 @@ func DoTestString(t *testing.T, parse func(string) (*URL, os.Error), name string func TestURLString(t *testing.T) { DoTestString(t, ParseURL, "ParseURL", urltests) - DoTestString(t, ParseURL, "ParseURL", urlfragtests) DoTestString(t, ParseURL, "ParseURL", urlnofragtests) DoTestString(t, ParseURLReference, "ParseURLReference", urltests) DoTestString(t, ParseURLReference, "ParseURLReference", urlfragtests) - DoTestString(t, ParseURLReference, "ParseURLReference", urlnofragtests) } type URLEscapeTest struct { @@ -288,57 +333,57 @@ type URLEscapeTest struct { } var unescapeTests = []URLEscapeTest{ - URLEscapeTest{ + { "", "", nil, }, - URLEscapeTest{ + { "abc", "abc", nil, }, - URLEscapeTest{ + { "1%41", "1A", nil, }, - URLEscapeTest{ + { "1%41%42%43", "1ABC", nil, }, - URLEscapeTest{ + { "%4a", "J", nil, }, - URLEscapeTest{ + { "%6F", "o", nil, }, - URLEscapeTest{ + { "%", // not enough characters after % "", URLEscapeError("%"), }, - URLEscapeTest{ + { "%a", // not enough characters after % "", URLEscapeError("%a"), }, - URLEscapeTest{ + { "%1", // not enough characters after % "", URLEscapeError("%1"), }, - URLEscapeTest{ + { "123%45%6", // not enough characters after % "", URLEscapeError("%6"), }, - URLEscapeTest{ + { "%zzzzz", // invalid hex digits "", URLEscapeError("%zz"), @@ -355,27 +400,27 @@ func TestURLUnescape(t *testing.T) { } var escapeTests = []URLEscapeTest{ - URLEscapeTest{ + { "", "", nil, }, - URLEscapeTest{ + { "abc", "abc", nil, }, - URLEscapeTest{ + { "one two", "one+two", nil, }, - URLEscapeTest{ + { "10%", "10%25", nil, }, - URLEscapeTest{ + { " ?&=#+%!<>#\"{}|\\^[]`☺\t", "+%3f%26%3d%23%2b%25!%3c%3e%23%22%7b%7d%7c%5c%5e%5b%5d%60%e2%98%ba%09", nil, @@ -403,27 +448,27 @@ type CanonicalPathTest struct { } var canonicalTests = []CanonicalPathTest{ - CanonicalPathTest{"", ""}, - CanonicalPathTest{"/", "/"}, - CanonicalPathTest{".", ""}, - CanonicalPathTest{"./", ""}, - CanonicalPathTest{"/a/", "/a/"}, - CanonicalPathTest{"a/", "a/"}, - CanonicalPathTest{"a/./", "a/"}, - CanonicalPathTest{"./a", "a"}, - CanonicalPathTest{"/a/../b", "/b"}, - CanonicalPathTest{"a/../b", "b"}, - CanonicalPathTest{"a/../../b", "../b"}, - CanonicalPathTest{"a/.", "a/"}, - CanonicalPathTest{"../.././a", "../../a"}, - CanonicalPathTest{"/../.././a", "/../../a"}, - CanonicalPathTest{"a/b/g/../..", "a/"}, - CanonicalPathTest{"a/b/..", "a/"}, - CanonicalPathTest{"a/b/.", "a/b/"}, - CanonicalPathTest{"a/b/../../../..", "../.."}, - CanonicalPathTest{"a./", "a./"}, - CanonicalPathTest{"/../a/b/../../../", "/../../"}, - CanonicalPathTest{"../a/b/../../../", "../../"}, + {"", ""}, + {"/", "/"}, + {".", ""}, + {"./", ""}, + {"/a/", "/a/"}, + {"a/", "a/"}, + {"a/./", "a/"}, + {"./a", "a"}, + {"/a/../b", "/b"}, + {"a/../b", "b"}, + {"a/../../b", "../b"}, + {"a/.", "a/"}, + {"../.././a", "../../a"}, + {"/../.././a", "/../../a"}, + {"a/b/g/../..", "a/"}, + {"a/b/..", "a/"}, + {"a/b/.", "a/b/"}, + {"a/b/../../../..", "../.."}, + {"a./", "a./"}, + {"/../a/b/../../../", "/../../"}, + {"../a/b/../../../", "../../"}, } func TestCanonicalPath(t *testing.T) { @@ -434,3 +479,53 @@ func TestCanonicalPath(t *testing.T) { } } } + +type UserinfoTest struct { + User string + Password string + Raw string +} + +var userinfoTests = []UserinfoTest{ + {"user", "password", "user:password"}, + {"foo:bar", "~!@#$%^&*()_+{}|[]\\-=`:;'\"<>?,./", + "foo%3abar:~!%40%23$%25%5e&*()_+%7b%7d%7c%5b%5d%5c-=%60%3a;'%22%3c%3e?,.%2f"}, +} + +func TestEscapeUserinfo(t *testing.T) { + for _, tt := range userinfoTests { + if raw := EscapeUserinfo(tt.User, tt.Password); raw != tt.Raw { + t.Errorf("EscapeUserinfo(%q, %q) = %q, want %q", tt.User, tt.Password, raw, tt.Raw) + } + } +} + +func TestUnescapeUserinfo(t *testing.T) { + for _, tt := range userinfoTests { + if user, pass, err := UnescapeUserinfo(tt.Raw); user != tt.User || pass != tt.Password || err != nil { + t.Errorf("UnescapeUserinfo(%q) = %q, %q, %v, want %q, %q, nil", tt.Raw, user, pass, err, tt.User, tt.Password) + } + } +} + +type qMap map[string][]string + +type EncodeQueryTest struct { + m qMap + expected string + expected1 string +} + +var encodeQueryTests = []EncodeQueryTest{ + {nil, "", ""}, + {qMap{"q": {"puppies"}, "oe": {"utf8"}}, "q=puppies&oe=utf8", "oe=utf8&q=puppies"}, + {qMap{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7", "q=dogs&q=%26&q=7"}, +} + +func TestEncodeQuery(t *testing.T) { + for _, tt := range encodeQueryTests { + if q := EncodeQuery(tt.m); q != tt.expected && q != tt.expected1 { + t.Errorf(`EncodeQuery(%+v) = %q, want %q`, tt.m, q, tt.expected) + } + } +} diff --git a/src/pkg/image/Makefile b/src/pkg/image/Makefile index 9c886f9f9..739ad804b 100644 --- a/src/pkg/image/Makefile +++ b/src/pkg/image/Makefile @@ -2,11 +2,13 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=image GOFILES=\ color.go\ + format.go\ + geom.go\ image.go\ names.go\ diff --git a/src/pkg/image/color.go b/src/pkg/image/color.go index 8a865a8a0..c1345c025 100644 --- a/src/pkg/image/color.go +++ b/src/pkg/image/color.go @@ -103,6 +103,27 @@ func (c Alpha16Color) RGBA() (r, g, b, a uint32) { return a, a, a, a } +// A GrayColor represents an 8-bit grayscale color. +type GrayColor struct { + Y uint8 +} + +func (c GrayColor) RGBA() (r, g, b, a uint32) { + y := uint32(c.Y) + y |= y << 8 + return y, y, y, 0xffff +} + +// A Gray16Color represents a 16-bit grayscale color. +type Gray16Color struct { + Y uint16 +} + +func (c Gray16Color) RGBA() (r, g, b, a uint32) { + y := uint32(c.Y) + return y, y, y, 0xffff +} + // A ColorModel can convert foreign Colors, with a possible loss of precision, // to a Color from its own color model. type ColorModel interface { @@ -187,6 +208,24 @@ func toAlpha16Color(c Color) Color { return Alpha16Color{uint16(a)} } +func toGrayColor(c Color) Color { + if _, ok := c.(GrayColor); ok { + return c + } + r, g, b, _ := c.RGBA() + y := (299*r + 587*g + 114*b + 500) / 1000 + return GrayColor{uint8(y >> 8)} +} + +func toGray16Color(c Color) Color { + if _, ok := c.(Gray16Color); ok { + return c + } + r, g, b, _ := c.RGBA() + y := (299*r + 587*g + 114*b + 500) / 1000 + return Gray16Color{uint16(y)} +} + // The ColorModel associated with RGBAColor. var RGBAColorModel ColorModel = ColorModelFunc(toRGBAColor) @@ -204,3 +243,9 @@ var AlphaColorModel ColorModel = ColorModelFunc(toAlphaColor) // The ColorModel associated with Alpha16Color. var Alpha16ColorModel ColorModel = ColorModelFunc(toAlpha16Color) + +// The ColorModel associated with GrayColor. +var GrayColorModel ColorModel = ColorModelFunc(toGrayColor) + +// The ColorModel associated with Gray16Color. +var Gray16ColorModel ColorModel = ColorModelFunc(toGray16Color) diff --git a/src/pkg/image/format.go b/src/pkg/image/format.go new file mode 100644 index 000000000..1d541b094 --- /dev/null +++ b/src/pkg/image/format.go @@ -0,0 +1,86 @@ +// 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 image + +import ( + "bufio" + "io" + "os" +) + +// An UnknownFormatErr indicates that decoding encountered an unknown format. +var UnknownFormatErr = os.NewError("image: unknown format") + +// A format holds an image format's name, magic header and how to decode it. +type format struct { + name, magic string + decode func(io.Reader) (Image, os.Error) + decodeConfig func(io.Reader) (Config, os.Error) +} + +// Formats is the list of registered formats. +var formats []format + +// RegisterFormat registers an image format for use by Decode. +// Name is the name of the format, like "jpeg" or "png". +// Magic is the magic prefix that identifies the format's encoding. +// Decode is the function that decodes the encoded image. +// DecodeConfig is the function that decodes just its configuration. +func RegisterFormat(name, magic string, decode func(io.Reader) (Image, os.Error), decodeConfig func(io.Reader) (Config, os.Error)) { + formats = append(formats, format{name, magic, decode, decodeConfig}) +} + +// A reader is an io.Reader that can also peek ahead. +type reader interface { + io.Reader + Peek(int) ([]byte, os.Error) +} + +// AsReader converts an io.Reader to a reader. +func asReader(r io.Reader) reader { + if rr, ok := r.(reader); ok { + return rr + } + return bufio.NewReader(r) +} + +// sniff determines the format of r's data. +func sniff(r reader) format { + for _, f := range formats { + s, err := r.Peek(len(f.magic)) + if err == nil && string(s) == f.magic { + return f + } + } + return format{} +} + +// Decode decodes an image that has been encoded in a registered format. +// The string returned is the format name used during format registration. +// Format registration is typically done by the init method of the codec- +// specific package. +func Decode(r io.Reader) (Image, string, os.Error) { + rr := asReader(r) + f := sniff(rr) + if f.decode == nil { + return nil, "", UnknownFormatErr + } + m, err := f.decode(rr) + return m, f.name, err +} + +// DecodeConfig decodes the color model and dimensions of an image that has +// been encoded in a registered format. The string returned is the format name +// used during format registration. Format registration is typically done by +// the init method of the codec-specific package. +func DecodeConfig(r io.Reader) (Config, string, os.Error) { + rr := asReader(r) + f := sniff(rr) + if f.decodeConfig == nil { + return Config{}, "", UnknownFormatErr + } + c, err := f.decodeConfig(rr) + return c, f.name, err +} diff --git a/src/pkg/image/geom.go b/src/pkg/image/geom.go new file mode 100644 index 000000000..ccfe9cdb0 --- /dev/null +++ b/src/pkg/image/geom.go @@ -0,0 +1,223 @@ +// 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 image + +import ( + "strconv" +) + +// A Point is an X, Y coordinate pair. The axes increase right and down. +type Point struct { + X, Y int +} + +// String returns a string representation of p like "(3,4)". +func (p Point) String() string { + return "(" + strconv.Itoa(p.X) + "," + strconv.Itoa(p.Y) + ")" +} + +// Add returns the vector p+q. +func (p Point) Add(q Point) Point { + return Point{p.X + q.X, p.Y + q.Y} +} + +// Sub returns the vector p-q. +func (p Point) Sub(q Point) Point { + return Point{p.X - q.X, p.Y - q.Y} +} + +// Mul returns the vector p*k. +func (p Point) Mul(k int) Point { + return Point{p.X * k, p.Y * k} +} + +// Div returns the vector p/k. +func (p Point) Div(k int) Point { + return Point{p.X / k, p.Y / k} +} + +// Mod returns the point q in r such that p.X-q.X is a multiple of r's width +// and p.Y-q.Y is a multiple of r's height. +func (p Point) Mod(r Rectangle) Point { + w, h := r.Dx(), r.Dy() + p = p.Sub(r.Min) + p.X = p.X % w + if p.X < 0 { + p.X += w + } + p.Y = p.Y % h + if p.Y < 0 { + p.Y += h + } + return p.Add(r.Min) +} + +// Eq returns whether p and q are equal. +func (p Point) Eq(q Point) bool { + return p.X == q.X && p.Y == q.Y +} + +// ZP is the zero Point. +var ZP Point + +// Pt is shorthand for Point{X, Y}. +func Pt(X, Y int) Point { + return Point{X, Y} +} + +// A Rectangle contains the points with Min.X <= X < Max.X, Min.Y <= Y < Max.Y. +// It is well-formed if Min.X <= Max.X and likewise for Y. Points are always +// well-formed. A rectangle's methods always return well-formed outputs for +// well-formed inputs. +type Rectangle struct { + Min, Max Point +} + +// String returns a string representation of r like "(3,4)-(6,5)". +func (r Rectangle) String() string { + return r.Min.String() + "-" + r.Max.String() +} + +// Dx returns r's width. +func (r Rectangle) Dx() int { + return r.Max.X - r.Min.X +} + +// Dy returns r's height. +func (r Rectangle) Dy() int { + return r.Max.Y - r.Min.Y +} + +// Size returns r's width and height. +func (r Rectangle) Size() Point { + return Point{ + r.Max.X - r.Min.X, + r.Max.Y - r.Min.Y, + } +} + +// Add returns the rectangle r translated by p. +func (r Rectangle) Add(p Point) Rectangle { + return Rectangle{ + Point{r.Min.X + p.X, r.Min.Y + p.Y}, + Point{r.Max.X + p.X, r.Max.Y + p.Y}, + } +} + +// Add returns the rectangle r translated by -p. +func (r Rectangle) Sub(p Point) Rectangle { + return Rectangle{ + Point{r.Min.X - p.X, r.Min.Y - p.Y}, + Point{r.Max.X - p.X, r.Max.Y - p.Y}, + } +} + +// Inset returns the rectangle r inset by n, which may be negative. If either +// of r's dimensions is less than 2*n then an empty rectangle near the center +// of r will be returned. +func (r Rectangle) Inset(n int) Rectangle { + if r.Dx() < 2*n { + r.Min.X = (r.Min.X + r.Max.X) / 2 + r.Max.X = r.Min.X + } else { + r.Min.X += n + r.Max.X -= n + } + if r.Dy() < 2*n { + r.Min.Y = (r.Min.Y + r.Max.Y) / 2 + r.Max.Y = r.Min.Y + } else { + r.Min.Y += n + r.Max.Y -= n + } + return r +} + +// Intersect returns the largest rectangle contained by both r and s. If the +// two rectangles do not overlap then the zero rectangle will be returned. +func (r Rectangle) Intersect(s Rectangle) Rectangle { + if r.Min.X < s.Min.X { + r.Min.X = s.Min.X + } + if r.Min.Y < s.Min.Y { + r.Min.Y = s.Min.Y + } + if r.Max.X > s.Max.X { + r.Max.X = s.Max.X + } + if r.Max.Y > s.Max.Y { + r.Max.Y = s.Max.Y + } + if r.Min.X > r.Max.X || r.Min.Y > r.Max.Y { + return ZR + } + return r +} + +// Union returns the smallest rectangle that contains both r and s. +func (r Rectangle) Union(s Rectangle) Rectangle { + if r.Min.X > s.Min.X { + r.Min.X = s.Min.X + } + if r.Min.Y > s.Min.Y { + r.Min.Y = s.Min.Y + } + if r.Max.X < s.Max.X { + r.Max.X = s.Max.X + } + if r.Max.Y < s.Max.Y { + r.Max.Y = s.Max.Y + } + return r +} + +// Empty returns whether the rectangle contains no points. +func (r Rectangle) Empty() bool { + return r.Min.X >= r.Max.X || r.Min.Y >= r.Max.Y +} + +// Eq returns whether r and s are equal. +func (r Rectangle) Eq(s Rectangle) bool { + return r.Min.X == s.Min.X && r.Min.Y == s.Min.Y && + r.Max.X == s.Max.X && r.Max.Y == s.Max.Y +} + +// Overlaps returns whether r and s have a non-empty intersection. +func (r Rectangle) Overlaps(s Rectangle) bool { + return r.Min.X < s.Max.X && s.Min.X < r.Max.X && + r.Min.Y < s.Max.Y && s.Min.Y < r.Max.Y +} + +// Contains returns whether r contains p. +func (r Rectangle) Contains(p Point) bool { + return p.X >= r.Min.X && p.X < r.Max.X && + p.Y >= r.Min.Y && p.Y < r.Max.Y +} + +// Canon returns the canonical version of r. The returned rectangle has minimum +// and maximum coordinates swapped if necessary so that it is well-formed. +func (r Rectangle) Canon() Rectangle { + if r.Max.X < r.Min.X { + r.Min.X, r.Max.X = r.Max.X, r.Min.X + } + if r.Max.Y < r.Min.Y { + r.Min.Y, r.Max.Y = r.Max.Y, r.Min.Y + } + return r +} + +// ZR is the zero Rectangle. +var ZR Rectangle + +// Rect is shorthand for Rectangle{Pt(x0, y0), Pt(x1, y1)}. +func Rect(x0, y0, x1, y1 int) Rectangle { + if x0 > x1 { + x0, x1 = x1, x0 + } + if y0 > y1 { + y0, y1 = y1, y0 + } + return Rectangle{Point{x0, y0}, Point{x1, y1}} +} diff --git a/src/pkg/image/image.go b/src/pkg/image/image.go index decf1ce43..c0e96e1f7 100644 --- a/src/pkg/image/image.go +++ b/src/pkg/image/image.go @@ -5,50 +5,67 @@ // The image package implements a basic 2-D image library. package image -// An Image is a rectangular grid of Colors drawn from a ColorModel. +// A Config consists of an image's color model and dimensions. +type Config struct { + ColorModel ColorModel + Width, Height int +} + +// An Image is a finite rectangular grid of Colors drawn from a ColorModel. type Image interface { + // ColorModel returns the Image's ColorModel. ColorModel() ColorModel - Width() int - Height() int - // At(0, 0) returns the upper-left pixel of the grid. - // At(Width()-1, Height()-1) returns the lower-right pixel. + // Bounds returns the domain for which At can return non-zero color. + // The bounds do not necessarily contain the point (0, 0). + Bounds() Rectangle + // At returns the color of the pixel at (x, y). + // At(Bounds().Min.X, Bounds().Min.Y) returns the upper-left pixel of the grid. + // At(Bounds().Max.X-1, Bounds().Max.Y-1) returns the lower-right one. At(x, y int) Color } -// An RGBA is an in-memory image backed by a 2-D slice of RGBAColor values. +// An RGBA is an in-memory image of RGBAColor values. type RGBA struct { - // The Pixel field's indices are y first, then x, so that At(x, y) == Pixel[y][x]. - Pixel [][]RGBAColor + // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. + Pix []RGBAColor + Stride int + // Rect is the image's bounds. + Rect Rectangle } func (p *RGBA) ColorModel() ColorModel { return RGBAColorModel } -func (p *RGBA) Width() int { - if len(p.Pixel) == 0 { - return 0 +func (p *RGBA) Bounds() Rectangle { return p.Rect } + +func (p *RGBA) At(x, y int) Color { + if !p.Rect.Contains(Point{x, y}) { + return RGBAColor{} } - return len(p.Pixel[0]) + return p.Pix[y*p.Stride+x] } -func (p *RGBA) Height() int { return len(p.Pixel) } - -func (p *RGBA) At(x, y int) Color { return p.Pixel[y][x] } - -func (p *RGBA) Set(x, y int, c Color) { p.Pixel[y][x] = toRGBAColor(c).(RGBAColor) } +func (p *RGBA) Set(x, y int, c Color) { + if !p.Rect.Contains(Point{x, y}) { + return + } + p.Pix[y*p.Stride+x] = toRGBAColor(c).(RGBAColor) +} // Opaque scans the entire image and returns whether or not it is fully opaque. func (p *RGBA) Opaque() bool { - h := len(p.Pixel) - if h > 0 { - w := len(p.Pixel[0]) - for y := 0; y < h; y++ { - pix := p.Pixel[y] - for x := 0; x < w; x++ { - if pix[x].A != 0xff { - return false - } + if p.Rect.Empty() { + return true + } + base := p.Rect.Min.Y * p.Stride + i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X + for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { + for _, c := range p.Pix[i0:i1] { + if c.A != 0xff { + return false } } + i0 += p.Stride + i1 += p.Stride } return true } @@ -56,251 +73,343 @@ func (p *RGBA) Opaque() bool { // NewRGBA returns a new RGBA with the given width and height. func NewRGBA(w, h int) *RGBA { buf := make([]RGBAColor, w*h) - pix := make([][]RGBAColor, h) - for y := range pix { - pix[y] = buf[w*y : w*(y+1)] - } - return &RGBA{pix} + return &RGBA{buf, w, Rectangle{ZP, Point{w, h}}} } -// An RGBA64 is an in-memory image backed by a 2-D slice of RGBA64Color values. +// An RGBA64 is an in-memory image of RGBA64Color values. type RGBA64 struct { - // The Pixel field's indices are y first, then x, so that At(x, y) == Pixel[y][x]. - Pixel [][]RGBA64Color + // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. + Pix []RGBA64Color + Stride int + // Rect is the image's bounds. + Rect Rectangle } func (p *RGBA64) ColorModel() ColorModel { return RGBA64ColorModel } -func (p *RGBA64) Width() int { - if len(p.Pixel) == 0 { - return 0 +func (p *RGBA64) Bounds() Rectangle { return p.Rect } + +func (p *RGBA64) At(x, y int) Color { + if !p.Rect.Contains(Point{x, y}) { + return RGBA64Color{} } - return len(p.Pixel[0]) + return p.Pix[y*p.Stride+x] } -func (p *RGBA64) Height() int { return len(p.Pixel) } - -func (p *RGBA64) At(x, y int) Color { return p.Pixel[y][x] } - -func (p *RGBA64) Set(x, y int, c Color) { p.Pixel[y][x] = toRGBA64Color(c).(RGBA64Color) } +func (p *RGBA64) Set(x, y int, c Color) { + if !p.Rect.Contains(Point{x, y}) { + return + } + p.Pix[y*p.Stride+x] = toRGBA64Color(c).(RGBA64Color) +} // Opaque scans the entire image and returns whether or not it is fully opaque. func (p *RGBA64) Opaque() bool { - h := len(p.Pixel) - if h > 0 { - w := len(p.Pixel[0]) - for y := 0; y < h; y++ { - pix := p.Pixel[y] - for x := 0; x < w; x++ { - if pix[x].A != 0xffff { - return false - } + if p.Rect.Empty() { + return true + } + base := p.Rect.Min.Y * p.Stride + i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X + for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { + for _, c := range p.Pix[i0:i1] { + if c.A != 0xffff { + return false } } + i0 += p.Stride + i1 += p.Stride } return true } // NewRGBA64 returns a new RGBA64 with the given width and height. func NewRGBA64(w, h int) *RGBA64 { - buf := make([]RGBA64Color, w*h) - pix := make([][]RGBA64Color, h) - for y := range pix { - pix[y] = buf[w*y : w*(y+1)] - } - return &RGBA64{pix} + pix := make([]RGBA64Color, w*h) + return &RGBA64{pix, w, Rectangle{ZP, Point{w, h}}} } -// A NRGBA is an in-memory image backed by a 2-D slice of NRGBAColor values. +// An NRGBA is an in-memory image of NRGBAColor values. type NRGBA struct { - // The Pixel field's indices are y first, then x, so that At(x, y) == Pixel[y][x]. - Pixel [][]NRGBAColor + // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. + Pix []NRGBAColor + Stride int + // Rect is the image's bounds. + Rect Rectangle } func (p *NRGBA) ColorModel() ColorModel { return NRGBAColorModel } -func (p *NRGBA) Width() int { - if len(p.Pixel) == 0 { - return 0 +func (p *NRGBA) Bounds() Rectangle { return p.Rect } + +func (p *NRGBA) At(x, y int) Color { + if !p.Rect.Contains(Point{x, y}) { + return NRGBAColor{} } - return len(p.Pixel[0]) + return p.Pix[y*p.Stride+x] } -func (p *NRGBA) Height() int { return len(p.Pixel) } - -func (p *NRGBA) At(x, y int) Color { return p.Pixel[y][x] } - -func (p *NRGBA) Set(x, y int, c Color) { p.Pixel[y][x] = toNRGBAColor(c).(NRGBAColor) } +func (p *NRGBA) Set(x, y int, c Color) { + if !p.Rect.Contains(Point{x, y}) { + return + } + p.Pix[y*p.Stride+x] = toNRGBAColor(c).(NRGBAColor) +} // Opaque scans the entire image and returns whether or not it is fully opaque. func (p *NRGBA) Opaque() bool { - h := len(p.Pixel) - if h > 0 { - w := len(p.Pixel[0]) - for y := 0; y < h; y++ { - pix := p.Pixel[y] - for x := 0; x < w; x++ { - if pix[x].A != 0xff { - return false - } + if p.Rect.Empty() { + return true + } + base := p.Rect.Min.Y * p.Stride + i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X + for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { + for _, c := range p.Pix[i0:i1] { + if c.A != 0xff { + return false } } + i0 += p.Stride + i1 += p.Stride } return true } // NewNRGBA returns a new NRGBA with the given width and height. func NewNRGBA(w, h int) *NRGBA { - buf := make([]NRGBAColor, w*h) - pix := make([][]NRGBAColor, h) - for y := range pix { - pix[y] = buf[w*y : w*(y+1)] - } - return &NRGBA{pix} + pix := make([]NRGBAColor, w*h) + return &NRGBA{pix, w, Rectangle{ZP, Point{w, h}}} } -// A NRGBA64 is an in-memory image backed by a 2-D slice of NRGBA64Color values. +// An NRGBA64 is an in-memory image of NRGBA64Color values. type NRGBA64 struct { - // The Pixel field's indices are y first, then x, so that At(x, y) == Pixel[y][x]. - Pixel [][]NRGBA64Color + // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. + Pix []NRGBA64Color + Stride int + // Rect is the image's bounds. + Rect Rectangle } func (p *NRGBA64) ColorModel() ColorModel { return NRGBA64ColorModel } -func (p *NRGBA64) Width() int { - if len(p.Pixel) == 0 { - return 0 +func (p *NRGBA64) Bounds() Rectangle { return p.Rect } + +func (p *NRGBA64) At(x, y int) Color { + if !p.Rect.Contains(Point{x, y}) { + return NRGBA64Color{} } - return len(p.Pixel[0]) + return p.Pix[y*p.Stride+x] } -func (p *NRGBA64) Height() int { return len(p.Pixel) } - -func (p *NRGBA64) At(x, y int) Color { return p.Pixel[y][x] } - -func (p *NRGBA64) Set(x, y int, c Color) { p.Pixel[y][x] = toNRGBA64Color(c).(NRGBA64Color) } +func (p *NRGBA64) Set(x, y int, c Color) { + if !p.Rect.Contains(Point{x, y}) { + return + } + p.Pix[y*p.Stride+x] = toNRGBA64Color(c).(NRGBA64Color) +} // Opaque scans the entire image and returns whether or not it is fully opaque. func (p *NRGBA64) Opaque() bool { - h := len(p.Pixel) - if h > 0 { - w := len(p.Pixel[0]) - for y := 0; y < h; y++ { - pix := p.Pixel[y] - for x := 0; x < w; x++ { - if pix[x].A != 0xffff { - return false - } + if p.Rect.Empty() { + return true + } + base := p.Rect.Min.Y * p.Stride + i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X + for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { + for _, c := range p.Pix[i0:i1] { + if c.A != 0xffff { + return false } } + i0 += p.Stride + i1 += p.Stride } return true } // NewNRGBA64 returns a new NRGBA64 with the given width and height. func NewNRGBA64(w, h int) *NRGBA64 { - buf := make([]NRGBA64Color, w*h) - pix := make([][]NRGBA64Color, h) - for y := range pix { - pix[y] = buf[w*y : w*(y+1)] - } - return &NRGBA64{pix} + pix := make([]NRGBA64Color, w*h) + return &NRGBA64{pix, w, Rectangle{ZP, Point{w, h}}} } -// An Alpha is an in-memory image backed by a 2-D slice of AlphaColor values. +// An Alpha is an in-memory image of AlphaColor values. type Alpha struct { - // The Pixel field's indices are y first, then x, so that At(x, y) == Pixel[y][x]. - Pixel [][]AlphaColor + // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. + Pix []AlphaColor + Stride int + // Rect is the image's bounds. + Rect Rectangle } func (p *Alpha) ColorModel() ColorModel { return AlphaColorModel } -func (p *Alpha) Width() int { - if len(p.Pixel) == 0 { - return 0 +func (p *Alpha) Bounds() Rectangle { return p.Rect } + +func (p *Alpha) At(x, y int) Color { + if !p.Rect.Contains(Point{x, y}) { + return AlphaColor{} } - return len(p.Pixel[0]) + return p.Pix[y*p.Stride+x] } -func (p *Alpha) Height() int { return len(p.Pixel) } - -func (p *Alpha) At(x, y int) Color { return p.Pixel[y][x] } - -func (p *Alpha) Set(x, y int, c Color) { p.Pixel[y][x] = toAlphaColor(c).(AlphaColor) } +func (p *Alpha) Set(x, y int, c Color) { + if !p.Rect.Contains(Point{x, y}) { + return + } + p.Pix[y*p.Stride+x] = toAlphaColor(c).(AlphaColor) +} // Opaque scans the entire image and returns whether or not it is fully opaque. func (p *Alpha) Opaque() bool { - h := len(p.Pixel) - if h > 0 { - w := len(p.Pixel[0]) - for y := 0; y < h; y++ { - pix := p.Pixel[y] - for x := 0; x < w; x++ { - if pix[x].A != 0xff { - return false - } + if p.Rect.Empty() { + return true + } + base := p.Rect.Min.Y * p.Stride + i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X + for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { + for _, c := range p.Pix[i0:i1] { + if c.A != 0xff { + return false } } + i0 += p.Stride + i1 += p.Stride } return true } // NewAlpha returns a new Alpha with the given width and height. func NewAlpha(w, h int) *Alpha { - buf := make([]AlphaColor, w*h) - pix := make([][]AlphaColor, h) - for y := range pix { - pix[y] = buf[w*y : w*(y+1)] - } - return &Alpha{pix} + pix := make([]AlphaColor, w*h) + return &Alpha{pix, w, Rectangle{ZP, Point{w, h}}} } -// An Alpha16 is an in-memory image backed by a 2-D slice of Alpha16Color values. +// An Alpha16 is an in-memory image of Alpha16Color values. type Alpha16 struct { - // The Pixel field's indices are y first, then x, so that At(x, y) == Pixel[y][x]. - Pixel [][]Alpha16Color + // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. + Pix []Alpha16Color + Stride int + // Rect is the image's bounds. + Rect Rectangle } func (p *Alpha16) ColorModel() ColorModel { return Alpha16ColorModel } -func (p *Alpha16) Width() int { - if len(p.Pixel) == 0 { - return 0 +func (p *Alpha16) Bounds() Rectangle { return p.Rect } + +func (p *Alpha16) At(x, y int) Color { + if !p.Rect.Contains(Point{x, y}) { + return Alpha16Color{} } - return len(p.Pixel[0]) + return p.Pix[y*p.Stride+x] } -func (p *Alpha16) Height() int { return len(p.Pixel) } - -func (p *Alpha16) At(x, y int) Color { return p.Pixel[y][x] } - -func (p *Alpha16) Set(x, y int, c Color) { p.Pixel[y][x] = toAlpha16Color(c).(Alpha16Color) } +func (p *Alpha16) Set(x, y int, c Color) { + if !p.Rect.Contains(Point{x, y}) { + return + } + p.Pix[y*p.Stride+x] = toAlpha16Color(c).(Alpha16Color) +} // Opaque scans the entire image and returns whether or not it is fully opaque. func (p *Alpha16) Opaque() bool { - h := len(p.Pixel) - if h > 0 { - w := len(p.Pixel[0]) - for y := 0; y < h; y++ { - pix := p.Pixel[y] - for x := 0; x < w; x++ { - if pix[x].A != 0xffff { - return false - } + if p.Rect.Empty() { + return true + } + base := p.Rect.Min.Y * p.Stride + i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X + for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { + for _, c := range p.Pix[i0:i1] { + if c.A != 0xffff { + return false } } + i0 += p.Stride + i1 += p.Stride } return true } // NewAlpha16 returns a new Alpha16 with the given width and height. func NewAlpha16(w, h int) *Alpha16 { - buf := make([]Alpha16Color, w*h) - pix := make([][]Alpha16Color, h) - for y := range pix { - pix[y] = buf[w*y : w*(y+1)] + pix := make([]Alpha16Color, w*h) + return &Alpha16{pix, w, Rectangle{ZP, Point{w, h}}} +} + +// A Gray is an in-memory image of GrayColor values. +type Gray struct { + // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. + Pix []GrayColor + Stride int + // Rect is the image's bounds. + Rect Rectangle +} + +func (p *Gray) ColorModel() ColorModel { return GrayColorModel } + +func (p *Gray) Bounds() Rectangle { return p.Rect } + +func (p *Gray) At(x, y int) Color { + if !p.Rect.Contains(Point{x, y}) { + return GrayColor{} } - return &Alpha16{pix} + return p.Pix[y*p.Stride+x] +} + +func (p *Gray) Set(x, y int, c Color) { + if !p.Rect.Contains(Point{x, y}) { + return + } + p.Pix[y*p.Stride+x] = toGrayColor(c).(GrayColor) +} + +// Opaque scans the entire image and returns whether or not it is fully opaque. +func (p *Gray) Opaque() bool { + return true +} + +// NewGray returns a new Gray with the given width and height. +func NewGray(w, h int) *Gray { + pix := make([]GrayColor, w*h) + return &Gray{pix, w, Rectangle{ZP, Point{w, h}}} +} + +// A Gray16 is an in-memory image of Gray16Color values. +type Gray16 struct { + // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. + Pix []Gray16Color + Stride int + // Rect is the image's bounds. + Rect Rectangle +} + +func (p *Gray16) ColorModel() ColorModel { return Gray16ColorModel } + +func (p *Gray16) Bounds() Rectangle { return p.Rect } + +func (p *Gray16) At(x, y int) Color { + if !p.Rect.Contains(Point{x, y}) { + return Gray16Color{} + } + return p.Pix[y*p.Stride+x] +} + +func (p *Gray16) Set(x, y int, c Color) { + if !p.Rect.Contains(Point{x, y}) { + return + } + p.Pix[y*p.Stride+x] = toGray16Color(c).(Gray16Color) +} + +// Opaque scans the entire image and returns whether or not it is fully opaque. +func (p *Gray16) Opaque() bool { + return true +} + +// NewGray16 returns a new Gray16 with the given width and height. +func NewGray16(w, h int) *Gray16 { + pix := make([]Gray16Color, w*h) + return &Gray16{pix, w, Rectangle{ZP, Point{w, h}}} } // A PalettedColorModel represents a fixed palette of colors. @@ -342,30 +451,41 @@ func (p PalettedColorModel) Convert(c Color) Color { // A Paletted is an in-memory image backed by a 2-D slice of uint8 values and a PalettedColorModel. type Paletted struct { - // The Pixel field's indices are y first, then x, so that At(x, y) == Palette[Pixel[y][x]]. - Pixel [][]uint8 + // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. + Pix []uint8 + Stride int + // Rect is the image's bounds. + Rect Rectangle + // Palette is the image's palette. Palette PalettedColorModel } func (p *Paletted) ColorModel() ColorModel { return p.Palette } -func (p *Paletted) Width() int { - if len(p.Pixel) == 0 { - return 0 +func (p *Paletted) Bounds() Rectangle { return p.Rect } + +func (p *Paletted) At(x, y int) Color { + if len(p.Palette) == 0 { + return nil + } + if !p.Rect.Contains(Point{x, y}) { + return p.Palette[0] } - return len(p.Pixel[0]) + return p.Palette[p.Pix[y*p.Stride+x]] } -func (p *Paletted) Height() int { return len(p.Pixel) } - -func (p *Paletted) At(x, y int) Color { return p.Palette[p.Pixel[y][x]] } - func (p *Paletted) ColorIndexAt(x, y int) uint8 { - return p.Pixel[y][x] + if !p.Rect.Contains(Point{x, y}) { + return 0 + } + return p.Pix[y*p.Stride+x] } func (p *Paletted) SetColorIndex(x, y int, index uint8) { - p.Pixel[y][x] = index + if !p.Rect.Contains(Point{x, y}) { + return + } + p.Pix[y*p.Stride+x] = index } // Opaque scans the entire image and returns whether or not it is fully opaque. @@ -381,10 +501,6 @@ func (p *Paletted) Opaque() bool { // NewPaletted returns a new Paletted with the given width, height and palette. func NewPaletted(w, h int, m PalettedColorModel) *Paletted { - buf := make([]uint8, w*h) - pix := make([][]uint8, h) - for y := range pix { - pix[y] = buf[w*y : w*(y+1)] - } - return &Paletted{pix, m} + pix := make([]uint8, w*h) + return &Paletted{pix, w, Rectangle{ZP, Point{w, h}}, m} } diff --git a/src/pkg/image/jpeg/Makefile b/src/pkg/image/jpeg/Makefile index c84811d6a..5c5f97e71 100644 --- a/src/pkg/image/jpeg/Makefile +++ b/src/pkg/image/jpeg/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include $(GOROOT)/src/Make.$(GOARCH) +include ../../../Make.inc TARG=image/jpeg GOFILES=\ @@ -10,4 +10,4 @@ GOFILES=\ idct.go\ reader.go\ -include $(GOROOT)/src/Make.pkg +include ../../../Make.pkg diff --git a/src/pkg/image/jpeg/reader.go b/src/pkg/image/jpeg/reader.go index ec036ef4d..fb9cb11bb 100644 --- a/src/pkg/image/jpeg/reader.go +++ b/src/pkg/image/jpeg/reader.go @@ -147,7 +147,6 @@ func (d *decoder) processSOF(n int) os.Error { } } } - d.image = image.NewRGBA(d.width, d.height) return nil } @@ -206,7 +205,7 @@ func (d *decoder) calcPixel(px, py, lumaBlock, lumaIndex, chromaIndex int) { } else if b > 255 { b = 255 } - d.image.Pixel[py][px] = image.RGBAColor{uint8(r), uint8(g), uint8(b), 0xff} + d.image.Pix[py*d.image.Stride+px] = image.RGBAColor{uint8(r), uint8(g), uint8(b), 0xff} } // Convert the MCU from YCbCr to RGB. @@ -240,7 +239,7 @@ func (d *decoder) convertMCU(mx, my, h0, v0 int) { // Specified in section B.2.3. func (d *decoder) processSOS(n int) os.Error { if d.image == nil { - return FormatError("missing SOF segment") + d.image = image.NewRGBA(d.width, d.height) } if n != 4+2*nComponent { return UnsupportedError("SOS has wrong length") @@ -365,9 +364,8 @@ func (d *decoder) processDRI(n int) os.Error { return nil } -// Decode reads a JPEG formatted image from r and returns it as an image.Image. -func Decode(r io.Reader) (image.Image, os.Error) { - var d decoder +// decode reads a JPEG image from r and returns it as an image.Image. +func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, os.Error) { if rr, ok := r.(Reader); ok { d.r = rr } else { @@ -411,6 +409,9 @@ func Decode(r io.Reader) (image.Image, os.Error) { switch { case marker == sof0Marker: // Start Of Frame (Baseline). err = d.processSOF(n) + if configOnly { + return nil, err + } case marker == sof2Marker: // Start Of Frame (Progressive). err = UnsupportedError("progressive mode") case marker == dhtMarker: // Define Huffman Table. @@ -432,3 +433,23 @@ func Decode(r io.Reader) (image.Image, os.Error) { } return d.image, nil } + +// Decode reads a JPEG image from r and returns it as an image.Image. +func Decode(r io.Reader) (image.Image, os.Error) { + var d decoder + return d.decode(r, false) +} + +// DecodeConfig returns the color model and dimensions of a JPEG image without +// decoding the entire image. +func DecodeConfig(r io.Reader) (image.Config, os.Error) { + var d decoder + if _, err := d.decode(r, true); err != nil { + return image.Config{}, err + } + return image.Config{image.RGBAColorModel, d.width, d.height}, nil +} + +func init() { + image.RegisterFormat("jpeg", "\xff\xd8", Decode, DecodeConfig) +} diff --git a/src/pkg/image/names.go b/src/pkg/image/names.go index 0b621cff5..c309684ce 100644 --- a/src/pkg/image/names.go +++ b/src/pkg/image/names.go @@ -4,53 +4,64 @@ package image -// Colors from the HTML 4.01 specification: http://www.w3.org/TR/REC-html40/types.html#h-6.5 -// These names do not necessarily match those from other lists, such as the X11 color names. var ( - Aqua = ColorImage{RGBAColor{0x00, 0xff, 0xff, 0xff}} - Black = ColorImage{RGBAColor{0x00, 0x00, 0x00, 0xff}} - Blue = ColorImage{RGBAColor{0x00, 0x00, 0xff, 0xff}} - Fuchsia = ColorImage{RGBAColor{0xff, 0x00, 0xff, 0xff}} - Gray = ColorImage{RGBAColor{0x80, 0x80, 0x80, 0xff}} - Green = ColorImage{RGBAColor{0x00, 0x80, 0x00, 0xff}} - Lime = ColorImage{RGBAColor{0x00, 0xff, 0x00, 0xff}} - Maroon = ColorImage{RGBAColor{0x80, 0x00, 0x00, 0xff}} - Navy = ColorImage{RGBAColor{0x00, 0x00, 0x80, 0xff}} - Olive = ColorImage{RGBAColor{0x80, 0x80, 0x00, 0xff}} - Red = ColorImage{RGBAColor{0xff, 0x00, 0x00, 0xff}} - Purple = ColorImage{RGBAColor{0x80, 0x00, 0x80, 0xff}} - Silver = ColorImage{RGBAColor{0xc0, 0xc0, 0xc0, 0xff}} - Teal = ColorImage{RGBAColor{0x00, 0x80, 0x80, 0xff}} - White = ColorImage{RGBAColor{0xff, 0xff, 0xff, 0xff}} - Yellow = ColorImage{RGBAColor{0xff, 0xff, 0x00, 0xff}} - - // These synonyms are not in HTML 4.01. - Cyan = Aqua - Magenta = Fuchsia + // Black is an opaque black ColorImage. + Black = NewColorImage(Gray16Color{0}) + // White is an opaque white ColorImage. + White = NewColorImage(Gray16Color{0xffff}) + // Transparent is a fully transparent ColorImage. + Transparent = NewColorImage(Alpha16Color{0}) + // Opaque is a fully opaque ColorImage. + Opaque = NewColorImage(Alpha16Color{0xffff}) ) -// A ColorImage is a practically infinite-sized Image of uniform Color. +// A ColorImage is an infinite-sized Image of uniform Color. // It implements both the Color and Image interfaces. type ColorImage struct { C Color } -func (c ColorImage) RGBA() (r, g, b, a uint32) { +func (c *ColorImage) RGBA() (r, g, b, a uint32) { return c.C.RGBA() } -func (c ColorImage) ColorModel() ColorModel { +func (c *ColorImage) ColorModel() ColorModel { return ColorModelFunc(func(Color) Color { return c.C }) } -func (c ColorImage) Width() int { return 1e9 } +func (c *ColorImage) Bounds() Rectangle { return Rectangle{Point{-1e9, -1e9}, Point{1e9, 1e9}} } -func (c ColorImage) Height() int { return 1e9 } - -func (c ColorImage) At(x, y int) Color { return c.C } +func (c *ColorImage) At(x, y int) Color { return c.C } // Opaque scans the entire image and returns whether or not it is fully opaque. -func (c ColorImage) Opaque() bool { +func (c *ColorImage) Opaque() bool { _, _, _, a := c.C.RGBA() return a == 0xffff } + +func NewColorImage(c Color) *ColorImage { + return &ColorImage{c} +} + +// A Tiled is an infinite-sized Image that repeats another Image in both +// directions. Tiled{i, p}.At(x, y) will equal i.At(x+p.X, y+p.Y) for all +// points {x+p.X, y+p.Y} within i's Bounds. +type Tiled struct { + I Image + Offset Point +} + +func (t *Tiled) ColorModel() ColorModel { + return t.I.ColorModel() +} + +func (t *Tiled) Bounds() Rectangle { return Rectangle{Point{-1e9, -1e9}, Point{1e9, 1e9}} } + +func (t *Tiled) At(x, y int) Color { + p := Point{x, y}.Add(t.Offset).Mod(t.I.Bounds()) + return t.I.At(p.X, p.Y) +} + +func NewTiled(i Image, offset Point) *Tiled { + return &Tiled{i, offset} +} diff --git a/src/pkg/image/png/Makefile b/src/pkg/image/png/Makefile index 3ba0f44d3..4101f77e1 100644 --- a/src/pkg/image/png/Makefile +++ b/src/pkg/image/png/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=image/png GOFILES=\ diff --git a/src/pkg/image/png/reader.go b/src/pkg/image/png/reader.go index fddb70423..e2d679bb4 100644 --- a/src/pkg/image/png/reader.go +++ b/src/pkg/image/png/reader.go @@ -9,6 +9,7 @@ package png import ( "compress/zlib" + "fmt" "hash" "hash/crc32" "image" @@ -25,6 +26,18 @@ const ( ctTrueColorAlpha = 6 ) +// A cb is a combination of color type and bit depth. +const ( + cbInvalid = iota + cbG8 + cbTC8 + cbP8 + cbTCA8 + cbG16 + cbTC16 + cbTCA16 +) + // Filter type, as per the PNG spec. const ( ftNone = 0 @@ -50,20 +63,25 @@ const ( const pngHeader = "\x89PNG\r\n\x1a\n" +type imgOrErr struct { + img image.Image + err os.Error +} + type decoder struct { width, height int - image image.Image - colorType uint8 + palette image.PalettedColorModel + cb int stage int idatWriter io.WriteCloser - idatDone chan os.Error + idatDone chan imgOrErr tmp [3 * 256]byte } // A FormatError reports that the input is not a valid PNG. type FormatError string -func (e FormatError) String() string { return "invalid PNG format: " + string(e) } +func (e FormatError) String() string { return "png: invalid format: " + string(e) } var chunkOrderError = FormatError("chunk out of order") @@ -72,12 +90,12 @@ type IDATDecodingError struct { Err os.Error } -func (e IDATDecodingError) String() string { return "IDAT decoding error: " + e.Err.String() } +func (e IDATDecodingError) String() string { return "png: IDAT decoding error: " + e.Err.String() } // An UnsupportedError reports that the input uses a valid but unimplemented PNG feature. type UnsupportedError string -func (e UnsupportedError) String() string { return "unsupported PNG feature: " + string(e) } +func (e UnsupportedError) String() string { return "png: unsupported feature: " + string(e) } // Big-endian. func parseUint32(b []uint8) uint32 { @@ -107,9 +125,6 @@ func (d *decoder) parseIHDR(r io.Reader, crc hash.Hash32, length uint32) os.Erro return err } crc.Write(d.tmp[0:13]) - if d.tmp[8] != 8 { - return UnsupportedError("bit depth") - } if d.tmp[10] != 0 || d.tmp[11] != 0 || d.tmp[12] != 0 { return UnsupportedError("compression, filter or interlace method") } @@ -122,16 +137,31 @@ func (d *decoder) parseIHDR(r io.Reader, crc hash.Hash32, length uint32) os.Erro if nPixels != int64(int(nPixels)) { return UnsupportedError("dimension overflow") } - d.colorType = d.tmp[9] - switch d.colorType { - case ctTrueColor: - d.image = image.NewRGBA(int(w), int(h)) - case ctPaletted: - d.image = image.NewPaletted(int(w), int(h), nil) - case ctTrueColorAlpha: - d.image = image.NewNRGBA(int(w), int(h)) - default: - return UnsupportedError("color type") + d.cb = cbInvalid + switch d.tmp[8] { + case 8: + switch d.tmp[9] { + case ctGrayscale: + d.cb = cbG8 + case ctTrueColor: + d.cb = cbTC8 + case ctPaletted: + d.cb = cbP8 + case ctTrueColorAlpha: + d.cb = cbTCA8 + } + case 16: + switch d.tmp[9] { + case ctGrayscale: + d.cb = cbG16 + case ctTrueColor: + d.cb = cbTC16 + case ctTrueColorAlpha: + d.cb = cbTCA16 + } + } + if d.cb == cbInvalid { + return UnsupportedError(fmt.Sprintf("bit depth %d, color type %d", d.tmp[8], d.tmp[9])) } d.width, d.height = int(w), int(h) return nil @@ -147,17 +177,15 @@ func (d *decoder) parsePLTE(r io.Reader, crc hash.Hash32, length uint32) os.Erro return err } crc.Write(d.tmp[0:n]) - switch d.colorType { - case ctPaletted: - palette := make([]image.Color, np) + switch d.cb { + case cbP8: + d.palette = image.PalettedColorModel(make([]image.Color, np)) for i := 0; i < np; i++ { - palette[i] = image.RGBAColor{d.tmp[3*i+0], d.tmp[3*i+1], d.tmp[3*i+2], 0xff} + d.palette[i] = image.RGBAColor{d.tmp[3*i+0], d.tmp[3*i+1], d.tmp[3*i+2], 0xff} } - d.image.(*image.Paletted).Palette = image.PalettedColorModel(palette) - case ctTrueColor, ctTrueColorAlpha: + case cbTC8, cbTCA8, cbTC16, cbTCA16: // As per the PNG spec, a PLTE chunk is optional (and for practical purposes, // ignorable) for the ctTrueColor and ctTrueColorAlpha color types (section 4.1.2). - return nil default: return FormatError("PLTE, color type mismatch") } @@ -173,19 +201,20 @@ func (d *decoder) parsetRNS(r io.Reader, crc hash.Hash32, length uint32) os.Erro return err } crc.Write(d.tmp[0:n]) - switch d.colorType { - case ctTrueColor: - return UnsupportedError("TrueColor transparency") - case ctPaletted: - p := d.image.(*image.Paletted).Palette - if n > len(p) { + switch d.cb { + case cbG8, cbG16: + return UnsupportedError("grayscale transparency") + case cbTC8, cbTC16: + return UnsupportedError("truecolor transparency") + case cbP8: + if n > len(d.palette) { return FormatError("bad tRNS length") } for i := 0; i < n; i++ { - rgba := p[i].(image.RGBAColor) - p[i] = image.RGBAColor{rgba.R, rgba.G, rgba.B, d.tmp[i]} + rgba := d.palette[i].(image.RGBAColor) + d.palette[i] = image.RGBAColor{rgba.R, rgba.G, rgba.B, d.tmp[i]} } - case ctTrueColorAlpha: + case cbTCA8, cbTCA16: return FormatError("tRNS, color type mismatch") } return nil @@ -205,30 +234,54 @@ func paeth(a, b, c uint8) uint8 { return c } -func (d *decoder) idatReader(idat io.Reader) os.Error { +func (d *decoder) idatReader(idat io.Reader) (image.Image, os.Error) { r, err := zlib.NewReader(idat) if err != nil { - return err + return nil, err } defer r.Close() bpp := 0 // Bytes per pixel. maxPalette := uint8(0) var ( + gray *image.Gray rgba *image.RGBA - nrgba *image.NRGBA paletted *image.Paletted + nrgba *image.NRGBA + gray16 *image.Gray16 + rgba64 *image.RGBA64 + nrgba64 *image.NRGBA64 + img image.Image ) - switch d.colorType { - case ctTrueColor: + switch d.cb { + case cbG8: + bpp = 1 + gray = image.NewGray(d.width, d.height) + img = gray + case cbTC8: bpp = 3 - rgba = d.image.(*image.RGBA) - case ctPaletted: + rgba = image.NewRGBA(d.width, d.height) + img = rgba + case cbP8: bpp = 1 - paletted = d.image.(*image.Paletted) - maxPalette = uint8(len(paletted.Palette) - 1) - case ctTrueColorAlpha: + paletted = image.NewPaletted(d.width, d.height, d.palette) + img = paletted + maxPalette = uint8(len(d.palette) - 1) + case cbTCA8: bpp = 4 - nrgba = d.image.(*image.NRGBA) + nrgba = image.NewNRGBA(d.width, d.height) + img = nrgba + case cbG16: + bpp = 2 + gray16 = image.NewGray16(d.width, d.height) + img = gray16 + case cbTC16: + bpp = 6 + rgba64 = image.NewRGBA64(d.width, d.height) + img = rgba64 + case cbTCA16: + bpp = 8 + nrgba64 = image.NewNRGBA64(d.width, d.height) + img = nrgba64 } // cr and pr are the bytes for the current and previous row. // The +1 is for the per-row filter type, which is at cr[0]. @@ -239,7 +292,7 @@ func (d *decoder) idatReader(idat io.Reader) os.Error { // Read the decompressed bytes. _, err := io.ReadFull(r, cr) if err != nil { - return err + return nil, err } // Apply the filter. @@ -271,32 +324,56 @@ func (d *decoder) idatReader(idat io.Reader) os.Error { cdat[i] += paeth(cdat[i-bpp], pdat[i], pdat[i-bpp]) } default: - return FormatError("bad filter type") + return nil, FormatError("bad filter type") } // Convert from bytes to colors. - switch d.colorType { - case ctTrueColor: + switch d.cb { + case cbG8: + for x := 0; x < d.width; x++ { + gray.Set(x, y, image.GrayColor{cdat[x]}) + } + case cbTC8: for x := 0; x < d.width; x++ { rgba.Set(x, y, image.RGBAColor{cdat[3*x+0], cdat[3*x+1], cdat[3*x+2], 0xff}) } - case ctPaletted: + case cbP8: for x := 0; x < d.width; x++ { if cdat[x] > maxPalette { - return FormatError("palette index out of range") + return nil, FormatError("palette index out of range") } paletted.SetColorIndex(x, y, cdat[x]) } - case ctTrueColorAlpha: + case cbTCA8: for x := 0; x < d.width; x++ { nrgba.Set(x, y, image.NRGBAColor{cdat[4*x+0], cdat[4*x+1], cdat[4*x+2], cdat[4*x+3]}) } + case cbG16: + for x := 0; x < d.width; x++ { + ycol := uint16(cdat[2*x+0])<<8 | uint16(cdat[2*x+1]) + gray16.Set(x, y, image.Gray16Color{ycol}) + } + case cbTC16: + for x := 0; x < d.width; x++ { + rcol := uint16(cdat[6*x+0])<<8 | uint16(cdat[6*x+1]) + gcol := uint16(cdat[6*x+2])<<8 | uint16(cdat[6*x+3]) + bcol := uint16(cdat[6*x+4])<<8 | uint16(cdat[6*x+5]) + rgba64.Set(x, y, image.RGBA64Color{rcol, gcol, bcol, 0xffff}) + } + case cbTCA16: + for x := 0; x < d.width; x++ { + rcol := uint16(cdat[8*x+0])<<8 | uint16(cdat[8*x+1]) + gcol := uint16(cdat[8*x+2])<<8 | uint16(cdat[8*x+3]) + bcol := uint16(cdat[8*x+4])<<8 | uint16(cdat[8*x+5]) + acol := uint16(cdat[8*x+6])<<8 | uint16(cdat[8*x+7]) + nrgba64.Set(x, y, image.NRGBA64Color{rcol, gcol, bcol, acol}) + } } // The current row for y is the previous row for y+1. pr, cr = cr, pr } - return nil + return img, nil } func (d *decoder) parseIDAT(r io.Reader, crc hash.Hash32, length uint32) os.Error { @@ -308,14 +385,14 @@ func (d *decoder) parseIDAT(r io.Reader, crc hash.Hash32, length uint32) os.Erro if d.idatWriter == nil { pr, pw := io.Pipe() d.idatWriter = pw - d.idatDone = make(chan os.Error) + d.idatDone = make(chan imgOrErr) go func() { - err := d.idatReader(pr) + img, err := d.idatReader(pr) if err == os.EOF { err = FormatError("too little IDAT") } pr.CloseWithError(FormatError("too much IDAT")) - d.idatDone <- err + d.idatDone <- imgOrErr{img, err} }() } var buf [4096]byte @@ -386,7 +463,7 @@ func (d *decoder) parseChunk(r io.Reader) os.Error { } err = d.parsetRNS(r, crc, length) case "IDAT": - if d.stage < dsSeenIHDR || d.stage > dsSeenIDAT || (d.colorType == ctPaletted && d.stage == dsSeenIHDR) { + if d.stage < dsSeenIHDR || d.stage > dsSeenIDAT || (d.cb == cbP8 && d.stage == dsSeenIHDR) { return chunkOrderError } d.stage = dsSeenIDAT @@ -438,7 +515,7 @@ func (d *decoder) checkHeader(r io.Reader) os.Error { return nil } -// Decode reads a PNG formatted image from r and returns it as an image.Image. +// Decode reads a PNG image from r and returns it as an image.Image. // The type of Image returned depends on the PNG contents. func Decode(r io.Reader) (image.Image, os.Error) { var d decoder @@ -446,21 +523,66 @@ func Decode(r io.Reader) (image.Image, os.Error) { if err != nil { return nil, err } - for d.stage = dsStart; d.stage != dsSeenIEND; { + for d.stage != dsSeenIEND { err = d.parseChunk(r) if err != nil { break } } + var img image.Image if d.idatWriter != nil { d.idatWriter.Close() - err1 := <-d.idatDone + ie := <-d.idatDone if err == nil { - err = err1 + img, err = ie.img, ie.err } } if err != nil { return nil, err } - return d.image, nil + return img, nil +} + +// DecodeConfig returns the color model and dimensions of a PNG image without +// decoding the entire image. +func DecodeConfig(r io.Reader) (image.Config, os.Error) { + var d decoder + err := d.checkHeader(r) + if err != nil { + return image.Config{}, err + } + for { + err = d.parseChunk(r) + if err != nil { + return image.Config{}, err + } + if d.stage == dsSeenIHDR && d.cb != cbP8 { + break + } + if d.stage == dsSeenPLTE && d.cb == cbP8 { + break + } + } + var cm image.ColorModel + switch d.cb { + case cbG8: + cm = image.GrayColorModel + case cbTC8: + cm = image.RGBAColorModel + case cbP8: + cm = d.palette + case cbTCA8: + cm = image.NRGBAColorModel + case cbG16: + cm = image.Gray16ColorModel + case cbTC16: + cm = image.RGBA64ColorModel + case cbTCA16: + cm = image.NRGBA64ColorModel + } + return image.Config{cm, d.width, d.height}, nil +} + +func init() { + image.RegisterFormat("png", pngHeader, Decode, DecodeConfig) } diff --git a/src/pkg/image/png/reader_test.go b/src/pkg/image/png/reader_test.go index 1dc45992e..fefceee3a 100644 --- a/src/pkg/image/png/reader_test.go +++ b/src/pkg/image/png/reader_test.go @@ -14,23 +14,24 @@ import ( ) // The go PNG library currently supports only a subset of the full PNG specification. -// In particular, bit depths other than 8 are not supported, and neither are grayscale images. +// In particular, bit depths other than 8 or 16 are not supported, nor are grayscale- +// alpha images. var filenames = []string{ - //"basn0g01", // bit depth is not 8 - //"basn0g02", // bit depth is not 8 - //"basn0g04", // bit depth is not 8 - //"basn0g08", // grayscale color model - //"basn0g16", // bit depth is not 8 + //"basn0g01", // bit depth is not 8 or 16 + //"basn0g02", // bit depth is not 8 or 16 + //"basn0g04", // bit depth is not 8 or 16 + "basn0g08", + "basn0g16", "basn2c08", - //"basn2c16", // bit depth is not 8 - //"basn3p01", // bit depth is not 8 - //"basn3p02", // bit depth is not 8 - //"basn3p04", // bit depth is not 8 + "basn2c16", + //"basn3p01", // bit depth is not 8 or 16 + //"basn3p02", // bit depth is not 8 or 16 + //"basn3p04", // bit depth is not 8 or 16 "basn3p08", - //"basn4a08", // grayscale color model - //"basn4a16", // bit depth is not 8 + //"basn4a08", // grayscale-alpha color model + //"basn4a16", // grayscale-alpha color model "basn6a08", - //"basn6a16", // bit depth is not 8 + "basn6a16", } func readPng(filename string) (image.Image, os.Error) { @@ -45,23 +46,34 @@ func readPng(filename string) (image.Image, os.Error) { // An approximation of the sng command-line tool. func sng(w io.WriteCloser, filename string, png image.Image) { defer w.Close() - // For now, the go PNG parser only reads bitdepths of 8. - bitdepth := 8 + bounds := png.Bounds() + cm := png.ColorModel() + var bitdepth int + switch cm { + case image.RGBAColorModel, image.NRGBAColorModel, image.AlphaColorModel, image.GrayColorModel: + bitdepth = 8 + default: + bitdepth = 16 + } + cpm, _ := cm.(image.PalettedColorModel) + var paletted *image.Paletted + if cpm != nil { + bitdepth = 8 + paletted = png.(*image.Paletted) + } // Write the filename and IHDR. io.WriteString(w, "#SNG: from "+filename+".png\nIHDR {\n") - fmt.Fprintf(w, " width: %d; height: %d; bitdepth: %d;\n", png.Width(), png.Height(), bitdepth) - cm := png.ColorModel() - var paletted *image.Paletted - cpm, _ := cm.(image.PalettedColorModel) + fmt.Fprintf(w, " width: %d; height: %d; bitdepth: %d;\n", bounds.Dx(), bounds.Dy(), bitdepth) switch { - case cm == image.RGBAColorModel: + case cm == image.RGBAColorModel, cm == image.RGBA64ColorModel: io.WriteString(w, " using color;\n") - case cm == image.NRGBAColorModel: + case cm == image.NRGBAColorModel, cm == image.NRGBA64ColorModel: io.WriteString(w, " using color alpha;\n") + case cm == image.GrayColorModel, cm == image.Gray16ColorModel: + io.WriteString(w, " using grayscale;\n") case cpm != nil: io.WriteString(w, " using color palette;\n") - paletted = png.(*image.Paletted) default: io.WriteString(w, "unknown PNG decoder color model\n") } @@ -86,20 +98,40 @@ func sng(w io.WriteCloser, filename string, png image.Image) { // Write the IMAGE. io.WriteString(w, "IMAGE {\n pixels hex\n") - for y := 0; y < png.Height(); y++ { + for y := bounds.Min.Y; y < bounds.Max.Y; y++ { switch { + case cm == image.GrayColorModel: + for x := bounds.Min.X; x < bounds.Max.X; x++ { + gray := png.At(x, y).(image.GrayColor) + fmt.Fprintf(w, "%02x", gray.Y) + } + case cm == image.Gray16ColorModel: + for x := bounds.Min.X; x < bounds.Max.X; x++ { + gray16 := png.At(x, y).(image.Gray16Color) + fmt.Fprintf(w, "%04x ", gray16.Y) + } case cm == image.RGBAColorModel: - for x := 0; x < png.Width(); x++ { + for x := bounds.Min.X; x < bounds.Max.X; x++ { rgba := png.At(x, y).(image.RGBAColor) fmt.Fprintf(w, "%02x%02x%02x ", rgba.R, rgba.G, rgba.B) } + case cm == image.RGBA64ColorModel: + for x := bounds.Min.X; x < bounds.Max.X; x++ { + rgba64 := png.At(x, y).(image.RGBA64Color) + fmt.Fprintf(w, "%04x%04x%04x ", rgba64.R, rgba64.G, rgba64.B) + } case cm == image.NRGBAColorModel: - for x := 0; x < png.Width(); x++ { + for x := bounds.Min.X; x < bounds.Max.X; x++ { nrgba := png.At(x, y).(image.NRGBAColor) fmt.Fprintf(w, "%02x%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B, nrgba.A) } + case cm == image.NRGBA64ColorModel: + for x := bounds.Min.X; x < bounds.Max.X; x++ { + nrgba64 := png.At(x, y).(image.NRGBA64Color) + fmt.Fprintf(w, "%04x%04x%04x%04x ", nrgba64.R, nrgba64.G, nrgba64.B, nrgba64.A) + } case cpm != nil: - for x := 0; x < png.Width(); x++ { + for x := bounds.Min.X; x < bounds.Max.X; x++ { fmt.Fprintf(w, "%02x", paletted.ColorIndexAt(x, y)) } } diff --git a/src/pkg/image/png/writer.go b/src/pkg/image/png/writer.go index e186ca819..081d06bf5 100644 --- a/src/pkg/image/png/writer.go +++ b/src/pkg/image/png/writer.go @@ -15,13 +15,13 @@ import ( ) type encoder struct { - w io.Writer - m image.Image - colorType uint8 - err os.Error - header [8]byte - footer [4]byte - tmp [3 * 256]byte + w io.Writer + m image.Image + cb int + err os.Error + header [8]byte + footer [4]byte + tmp [3 * 256]byte } // Big-endian. @@ -32,10 +32,18 @@ func writeUint32(b []uint8, u uint32) { b[3] = uint8(u >> 0) } +type opaquer interface { + Opaque() bool +} + // Returns whether or not the image is fully opaque. func opaque(m image.Image) bool { - for y := 0; y < m.Height(); y++ { - for x := 0; x < m.Width(); x++ { + if o, ok := m.(opaquer); ok { + return o.Opaque() + } + b := m.Bounds() + for y := b.Min.Y; y < b.Max.Y; y++ { + for x := b.Min.X; x < b.Max.X; x++ { _, _, _, a := m.At(x, y).RGBA() if a != 0xffff { return false @@ -84,10 +92,33 @@ func (e *encoder) writeChunk(b []byte, name string) { } func (e *encoder) writeIHDR() { - writeUint32(e.tmp[0:4], uint32(e.m.Width())) - writeUint32(e.tmp[4:8], uint32(e.m.Height())) - e.tmp[8] = 8 // bit depth - e.tmp[9] = e.colorType + b := e.m.Bounds() + writeUint32(e.tmp[0:4], uint32(b.Dx())) + writeUint32(e.tmp[4:8], uint32(b.Dy())) + // Set bit depth and color type. + switch e.cb { + case cbG8: + e.tmp[8] = 8 + e.tmp[9] = ctGrayscale + case cbTC8: + e.tmp[8] = 8 + e.tmp[9] = ctTrueColor + case cbP8: + e.tmp[8] = 8 + e.tmp[9] = ctPaletted + case cbTCA8: + e.tmp[8] = 8 + e.tmp[9] = ctTrueColorAlpha + case cbG16: + e.tmp[8] = 16 + e.tmp[9] = ctGrayscale + case cbTC16: + e.tmp[8] = 16 + e.tmp[9] = ctTrueColor + case cbTCA16: + e.tmp[8] = 16 + e.tmp[9] = ctTrueColorAlpha + } e.tmp[10] = 0 // default compression method e.tmp[11] = 0 // default filter method e.tmp[12] = 0 // non-interlaced @@ -224,7 +255,7 @@ func filter(cr [][]byte, pr []byte, bpp int) int { return filter } -func writeImage(w io.Writer, m image.Image, ct uint8) os.Error { +func writeImage(w io.Writer, m image.Image, cb int) os.Error { zw, err := zlib.NewWriter(w) if err != nil { return err @@ -233,51 +264,94 @@ func writeImage(w io.Writer, m image.Image, ct uint8) os.Error { bpp := 0 // Bytes per pixel. var paletted *image.Paletted - switch ct { - case ctTrueColor: + switch cb { + case cbG8: + bpp = 1 + case cbTC8: bpp = 3 - case ctPaletted: + case cbP8: bpp = 1 paletted = m.(*image.Paletted) - case ctTrueColorAlpha: + case cbTCA8: bpp = 4 + case cbTC16: + bpp = 6 + case cbTCA16: + bpp = 8 + case cbG16: + bpp = 2 } // cr[*] and pr are the bytes for the current and previous row. // cr[0] is unfiltered (or equivalently, filtered with the ftNone filter). // cr[ft], for non-zero filter types ft, are buffers for transforming cr[0] under the // other PNG filter types. These buffers are allocated once and re-used for each row. // The +1 is for the per-row filter type, which is at cr[*][0]. + b := m.Bounds() var cr [nFilter][]uint8 for i := 0; i < len(cr); i++ { - cr[i] = make([]uint8, 1+bpp*m.Width()) + cr[i] = make([]uint8, 1+bpp*b.Dx()) cr[i][0] = uint8(i) } - pr := make([]uint8, 1+bpp*m.Width()) + pr := make([]uint8, 1+bpp*b.Dx()) - for y := 0; y < m.Height(); y++ { + for y := b.Min.Y; y < b.Max.Y; y++ { // Convert from colors to bytes. - switch ct { - case ctTrueColor: - for x := 0; x < m.Width(); x++ { + switch cb { + case cbG8: + for x := b.Min.X; x < b.Max.X; x++ { + c := image.GrayColorModel.Convert(m.At(x, y)).(image.GrayColor) + cr[0][x+1] = c.Y + } + case cbTC8: + for x := b.Min.X; x < b.Max.X; x++ { // We have previously verified that the alpha value is fully opaque. r, g, b, _ := m.At(x, y).RGBA() cr[0][3*x+1] = uint8(r >> 8) cr[0][3*x+2] = uint8(g >> 8) cr[0][3*x+3] = uint8(b >> 8) } - case ctPaletted: - for x := 0; x < m.Width(); x++ { - cr[0][x+1] = paletted.ColorIndexAt(x, y) - } - case ctTrueColorAlpha: + case cbP8: + rowOffset := y * paletted.Stride + copy(cr[0][b.Min.X+1:], paletted.Pix[rowOffset+b.Min.X:rowOffset+b.Max.X]) + case cbTCA8: // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied. - for x := 0; x < m.Width(); x++ { + for x := b.Min.X; x < b.Max.X; x++ { c := image.NRGBAColorModel.Convert(m.At(x, y)).(image.NRGBAColor) cr[0][4*x+1] = c.R cr[0][4*x+2] = c.G cr[0][4*x+3] = c.B cr[0][4*x+4] = c.A } + case cbG16: + for x := b.Min.X; x < b.Max.X; x++ { + c := image.Gray16ColorModel.Convert(m.At(x, y)).(image.Gray16Color) + cr[0][2*x+1] = uint8(c.Y >> 8) + cr[0][2*x+2] = uint8(c.Y) + } + case cbTC16: + for x := b.Min.X; x < b.Max.X; x++ { + // We have previously verified that the alpha value is fully opaque. + r, g, b, _ := m.At(x, y).RGBA() + cr[0][6*x+1] = uint8(r >> 8) + cr[0][6*x+2] = uint8(r) + cr[0][6*x+3] = uint8(g >> 8) + cr[0][6*x+4] = uint8(g) + cr[0][6*x+5] = uint8(b >> 8) + cr[0][6*x+6] = uint8(b) + } + case cbTCA16: + // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied. + for x := b.Min.X; x < b.Max.X; x++ { + c := image.NRGBA64ColorModel.Convert(m.At(x, y)).(image.NRGBA64Color) + cr[0][8*x+1] = uint8(c.R >> 8) + cr[0][8*x+2] = uint8(c.R) + cr[0][8*x+3] = uint8(c.G >> 8) + cr[0][8*x+4] = uint8(c.G) + cr[0][8*x+5] = uint8(c.B >> 8) + cr[0][8*x+6] = uint8(c.B) + cr[0][8*x+7] = uint8(c.A >> 8) + cr[0][8*x+8] = uint8(c.A) + } } // Apply the filter. @@ -305,7 +379,7 @@ func (e *encoder) writeIDATs() { if e.err != nil { return } - e.err = writeImage(bw, e.m, e.colorType) + e.err = writeImage(bw, e.m, e.cb) if e.err != nil { return } @@ -320,7 +394,7 @@ func Encode(w io.Writer, m image.Image) os.Error { // Obviously, negative widths and heights are invalid. Furthermore, the PNG // spec section 11.2.2 says that zero is invalid. Excessively large images are // also rejected. - mw, mh := int64(m.Width()), int64(m.Height()) + mw, mh := int64(m.Bounds().Dx()), int64(m.Bounds().Dy()) if mw <= 0 || mh <= 0 || mw >= 1<<32 || mh >= 1<<32 { return FormatError("invalid image size: " + strconv.Itoa64(mw) + "x" + strconv.Itoa64(mw)) } @@ -328,12 +402,28 @@ func Encode(w io.Writer, m image.Image) os.Error { var e encoder e.w = w e.m = m - e.colorType = uint8(ctTrueColorAlpha) pal, _ := m.(*image.Paletted) if pal != nil { - e.colorType = ctPaletted - } else if opaque(m) { - e.colorType = ctTrueColor + e.cb = cbP8 + } else { + switch m.ColorModel() { + case image.GrayColorModel: + e.cb = cbG8 + case image.Gray16ColorModel: + e.cb = cbG16 + case image.RGBAColorModel, image.NRGBAColorModel, image.AlphaColorModel: + if opaque(m) { + e.cb = cbTC8 + } else { + e.cb = cbTCA8 + } + default: + if opaque(m) { + e.cb = cbTC16 + } else { + e.cb = cbTCA16 + } + } } _, e.err = io.WriteString(w, pngHeader) diff --git a/src/pkg/image/png/writer_test.go b/src/pkg/image/png/writer_test.go index a61e1c95a..f218a5564 100644 --- a/src/pkg/image/png/writer_test.go +++ b/src/pkg/image/png/writer_test.go @@ -5,6 +5,7 @@ package png import ( + "bytes" "fmt" "image" "io" @@ -13,15 +14,16 @@ import ( ) func diff(m0, m1 image.Image) os.Error { - if m0.Width() != m1.Width() || m0.Height() != m1.Height() { - return os.NewError(fmt.Sprintf("dimensions differ: %dx%d vs %dx%d", m0.Width(), m0.Height(), m1.Width(), m1.Height())) + b0, b1 := m0.Bounds(), m1.Bounds() + if !b0.Eq(b1) { + return fmt.Errorf("dimensions differ: %v vs %v", b0, b1) } - for y := 0; y < m0.Height(); y++ { - for x := 0; x < m0.Width(); x++ { + for y := b0.Min.Y; y < b0.Max.Y; y++ { + for x := b0.Min.X; x < b0.Max.X; x++ { r0, g0, b0, a0 := m0.At(x, y).RGBA() r1, g1, b1, a1 := m1.At(x, y).RGBA() if r0 != r1 || g0 != g1 || b0 != b1 || a0 != a1 { - return os.NewError(fmt.Sprintf("colors differ at (%d, %d): %v vs %v", x, y, m0.At(x, y), m1.At(x, y))) + return fmt.Errorf("colors differ at (%d, %d): %v vs %v", x, y, m0.At(x, y), m1.At(x, y)) } } } @@ -67,3 +69,18 @@ func TestWriter(t *testing.T) { } } } + +func BenchmarkEncodePaletted(b *testing.B) { + b.StopTimer() + img := image.NewPaletted(640, 480, + []image.Color{ + image.RGBAColor{0, 0, 0, 255}, + image.RGBAColor{255, 255, 255, 255}, + }) + b.StartTimer() + buffer := new(bytes.Buffer) + for i := 0; i < b.N; i++ { + buffer.Reset() + Encode(buffer, img) + } +} diff --git a/src/pkg/index/suffixarray/Makefile b/src/pkg/index/suffixarray/Makefile new file mode 100644 index 000000000..297c4279f --- /dev/null +++ b/src/pkg/index/suffixarray/Makefile @@ -0,0 +1,12 @@ +# 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=index/suffixarray +GOFILES=\ + qsufsort.go\ + suffixarray.go\ + +include ../../../Make.pkg diff --git a/src/pkg/index/suffixarray/qsufsort.go b/src/pkg/index/suffixarray/qsufsort.go new file mode 100644 index 000000000..0e6894a8b --- /dev/null +++ b/src/pkg/index/suffixarray/qsufsort.go @@ -0,0 +1,164 @@ +// 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 algorithm is based on "Faster Suffix Sorting" +// by N. Jesper Larsson and Kunihiko Sadakane +// paper: http://www.larsson.dogma.net/ssrev-tr.pdf +// code: http://www.larsson.dogma.net/qsufsort.c + +// This algorithm computes the suffix array sa by computing its inverse. +// Consecutive groups of suffixes in sa are labeled as sorted groups or +// unsorted groups. For a given pass of the sorter, all suffixes are ordered +// up to their first h characters, and sa is h-ordered. Suffixes in their +// final positions and unambiguouly sorted in h-order are in a sorted group. +// Consecutive groups of suffixes with identical first h characters are an +// unsorted group. In each pass of the algorithm, unsorted groups are sorted +// according to the group number of their following suffix. + +// In the implementation, if sa[i] is negative, it indicates that i is +// the first element of a sorted group of length -sa[i], and can be skipped. +// An unsorted group sa[i:k] is given the group number of the index of its +// last element, k-1. The group numbers are stored in the inverse slice (inv), +// and when all groups are sorted, this slice is the inverse suffix array. + +package suffixarray + +import "sort" + +func qsufsort(data []byte) []int { + // initial sorting by first byte of suffix + sa := sortedByFirstByte(data) + if len(sa) < 2 { + return sa + } + // initialize the group lookup table + // this becomes the inverse of the suffix array when all groups are sorted + inv := initGroups(sa, data) + + // the index starts 1-ordered + sufSortable := &suffixSortable{sa, inv, 1} + + for sa[0] > -len(sa) { // until all suffixes are one big sorted group + // The suffixes are h-ordered, make them 2*h-ordered + pi := 0 // pi is first position of first group + sl := 0 // sl is negated length of sorted groups + for pi < len(sa) { + if s := sa[pi]; s < 0 { // if pi starts sorted group + pi -= s // skip over sorted group + sl += s // add negated length to sl + } else { // if pi starts unsorted group + if sl != 0 { + sa[pi+sl] = sl // combine sorted groups before pi + sl = 0 + } + pk := inv[s] + 1 // pk-1 is last position of unsorted group + sufSortable.sa = sa[pi:pk] + sort.Sort(sufSortable) + sufSortable.updateGroups(pi) + pi = pk // next group + } + } + if sl != 0 { // if the array ends with a sorted group + sa[pi+sl] = sl // combine sorted groups at end of sa + } + + sufSortable.h *= 2 // double sorted depth + } + + for i := range sa { // reconstruct suffix array from inverse + sa[inv[i]] = i + } + return sa +} + + +func sortedByFirstByte(data []byte) []int { + // total byte counts + var count [256]int + for _, b := range data { + count[b]++ + } + // make count[b] equal index of first occurence of b in sorted array + sum := 0 + for b := range count { + count[b], sum = sum, count[b]+sum + } + // iterate through bytes, placing index into the correct spot in sa + sa := make([]int, len(data)) + for i, b := range data { + sa[count[b]] = i + count[b]++ + } + return sa +} + + +func initGroups(sa []int, data []byte) []int { + // label contiguous same-letter groups with the same group number + inv := make([]int, len(data)) + prevGroup := len(sa) - 1 + groupByte := data[sa[prevGroup]] + for i := len(sa) - 1; i >= 0; i-- { + if b := data[sa[i]]; b < groupByte { + if prevGroup == i+1 { + sa[i+1] = -1 + } + groupByte = b + prevGroup = i + } + inv[sa[i]] = prevGroup + if prevGroup == 0 { + sa[0] = -1 + } + } + // Separate out the final suffix to the start of its group. + // This is necessary to ensure the suffix "a" is before "aba" + // when using a potentially unstable sort. + lastByte := data[len(data)-1] + s := -1 + for i := range sa { + if sa[i] >= 0 { + if data[sa[i]] == lastByte && s == -1 { + s = i + } + if sa[i] == len(sa)-1 { + sa[i], sa[s] = sa[s], sa[i] + inv[sa[s]] = s + sa[s] = -1 // mark it as an isolated sorted group + break + } + } + } + return inv +} + + +type suffixSortable struct { + sa []int + inv []int + h int +} + +func (x *suffixSortable) Len() int { return len(x.sa) } +func (x *suffixSortable) Less(i, j int) bool { return x.inv[x.sa[i]+x.h] < x.inv[x.sa[j]+x.h] } +func (x *suffixSortable) Swap(i, j int) { x.sa[i], x.sa[j] = x.sa[j], x.sa[i] } + + +func (x *suffixSortable) updateGroups(offset int) { + prev := len(x.sa) - 1 + group := x.inv[x.sa[prev]+x.h] + for i := prev; i >= 0; i-- { + if g := x.inv[x.sa[i]+x.h]; g < group { + if prev == i+1 { // previous group had size 1 and is thus sorted + x.sa[i+1] = -1 + } + group = g + prev = i + } + x.inv[x.sa[i]] = prev + offset + if prev == 0 { // first group has size 1 and is thus sorted + x.sa[0] = -1 + } + } +} diff --git a/src/pkg/index/suffixarray/suffixarray.go b/src/pkg/index/suffixarray/suffixarray.go new file mode 100644 index 000000000..628e000e1 --- /dev/null +++ b/src/pkg/index/suffixarray/suffixarray.go @@ -0,0 +1,182 @@ +// 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 suffixarray package implements substring search in logarithmic time +// using an in-memory suffix array. +// +// Example use: +// +// // create index for some data +// index := suffixarray.New(data) +// +// // lookup byte slice s +// offsets1 := index.Lookup(s, -1) // the list of all indices where s occurs in data +// offsets2 := index.Lookup(s, 3) // the list of at most 3 indices where s occurs in data +// +package suffixarray + +import ( + "bytes" + "regexp" + "sort" +) + + +// Index implements a suffix array for fast substring search. +type Index struct { + data []byte + sa []int // suffix array for data +} + + +// New creates a new Index for data. +// Index creation time is O(N*log(N)) for N = len(data). +func New(data []byte) *Index { + return &Index{data, qsufsort(data)} +} + + +// Bytes returns the data over which the index was created. +// It must not be modified. +// +func (x *Index) Bytes() []byte { + return x.data +} + + +func (x *Index) at(i int) []byte { + return x.data[x.sa[i]:] +} + + +func (x *Index) search(s []byte) int { + return sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 }) +} + + +// Lookup returns an unsorted list of at most n indices where the byte string s +// occurs in the indexed data. If n < 0, all occurrences are returned. +// The result is nil if s is empty, s is not found, or n == 0. +// Lookup time is O((log(N) + len(result))*len(s)) where N is the +// size of the indexed data. +// +func (x *Index) Lookup(s []byte, n int) (result []int) { + if len(s) > 0 && n != 0 { + // find matching suffix index i + i := x.search(s) + // x.at(i-1) < s <= x.at(i) + + // collect the following suffixes with matching prefixes + for (n < 0 || len(result) < n) && i < len(x.sa) && bytes.HasPrefix(x.at(i), s) { + result = append(result, x.sa[i]) + i++ + } + } + return +} + + +// FindAllIndex returns a sorted list of non-overlapping matches of the +// regular expression r, where a match is a pair of indices specifying +// the matched slice of x.Bytes(). If n < 0, all matches are returned +// in successive order. Otherwise, at most n matches are returned and +// they may not be successive. The result is nil if there are no matches, +// or if n == 0. +// +func (x *Index) FindAllIndex(r *regexp.Regexp, n int) (result [][]int) { + // a non-empty literal prefix is used to determine possible + // match start indices with Lookup + prefix, complete := r.LiteralPrefix() + lit := []byte(prefix) + + // worst-case scenario: no literal prefix + if prefix == "" { + return r.FindAllIndex(x.data, n) + } + + // if regexp is a literal just use Lookup and convert its + // result into match pairs + if complete { + // Lookup returns indices that may belong to overlapping matches. + // After eliminating them, we may end up with fewer than n matches. + // If we don't have enough at the end, redo the search with an + // increased value n1, but only if Lookup returned all the requested + // indices in the first place (if it returned fewer than that then + // there cannot be more). + for n1 := n; ; n1 += 2 * (n - len(result)) /* overflow ok */ { + indices := x.Lookup(lit, n1) + if len(indices) == 0 { + return + } + sort.SortInts(indices) + pairs := make([]int, 2*len(indices)) + result = make([][]int, len(indices)) + count := 0 + prev := 0 + for _, i := range indices { + if count == n { + break + } + // ignore indices leading to overlapping matches + if prev <= i { + j := 2 * count + pairs[j+0] = i + pairs[j+1] = i + len(lit) + result[count] = pairs[j : j+2] + count++ + prev = i + len(lit) + } + } + result = result[0:count] + if len(result) >= n || len(indices) != n1 { + // found all matches or there's no chance to find more + // (n and n1 can be negative) + break + } + } + if len(result) == 0 { + result = nil + } + return + } + + // regexp has a non-empty literal prefix; Lookup(lit) computes + // the indices of possible complete matches; use these as starting + // points for anchored searches + // (regexp "^" matches beginning of input, not beginning of line) + r = regexp.MustCompile("^" + r.String()) // compiles because r compiled + + // same comment about Lookup applies here as in the loop above + for n1 := n; ; n1 += 2 * (n - len(result)) /* overflow ok */ { + indices := x.Lookup(lit, n1) + if len(indices) == 0 { + return + } + sort.SortInts(indices) + result = result[0:0] + prev := 0 + for _, i := range indices { + if len(result) == n { + break + } + m := r.FindIndex(x.data[i:]) // anchored search - will not run off + // ignore indices leading to overlapping matches + if m != nil && prev <= i { + m[0] = i // correct m + m[1] += i + result = append(result, m) + prev = m[1] + } + } + if len(result) >= n || len(indices) != n1 { + // found all matches or there's no chance to find more + // (n and n1 can be negative) + break + } + } + if len(result) == 0 { + result = nil + } + return +} diff --git a/src/pkg/index/suffixarray/suffixarray_test.go b/src/pkg/index/suffixarray/suffixarray_test.go new file mode 100644 index 000000000..b3486a96d --- /dev/null +++ b/src/pkg/index/suffixarray/suffixarray_test.go @@ -0,0 +1,234 @@ +// 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 suffixarray + +import ( + "bytes" + "container/vector" + "regexp" + "sort" + "strings" + "testing" +) + + +type testCase struct { + name string // name of test case + source string // source to index + patterns []string // patterns to lookup +} + + +var testCases = []testCase{ + { + "empty string", + "", + []string{ + "", + "foo", + "(foo)", + ".*", + "a*", + }, + }, + + { + "all a's", + "aaaaaaaaaa", // 10 a's + []string{ + "", + "a", + "aa", + "aaa", + "aaaa", + "aaaaa", + "aaaaaa", + "aaaaaaa", + "aaaaaaaa", + "aaaaaaaaa", + "aaaaaaaaaa", + "aaaaaaaaaaa", // 11 a's + ".", + ".*", + "a+", + "aa+", + "aaaa[b]?", + "aaa*", + }, + }, + + { + "abc", + "abc", + []string{ + "a", + "b", + "c", + "ab", + "bc", + "abc", + "a.c", + "a(b|c)", + "abc?", + }, + }, + + { + "barbara*3", + "barbarabarbarabarbara", + []string{ + "a", + "bar", + "rab", + "arab", + "barbar", + "bara?bar", + }, + }, + + { + "typing drill", + "Now is the time for all good men to come to the aid of their country.", + []string{ + "Now", + "the time", + "to come the aid", + "is the time for all good men to come to the aid of their", + "to (come|the)?", + }, + }, +} + + +// find all occurrences of s in source; report at most n occurences +func find(src, s string, n int) []int { + var res vector.IntVector + if s != "" && n != 0 { + // find at most n occurrences of s in src + for i := -1; n < 0 || len(res) < n; { + j := strings.Index(src[i+1:], s) + if j < 0 { + break + } + i += j + 1 + res.Push(i) + } + } + return res +} + + +func testLookup(t *testing.T, tc *testCase, x *Index, s string, n int) { + res := x.Lookup([]byte(s), n) + exp := find(tc.source, s, n) + + // check that the lengths match + if len(res) != len(exp) { + t.Errorf("test %q, lookup %q (n = %d): expected %d results; got %d", tc.name, s, n, len(exp), len(res)) + } + + // if n >= 0 the number of results is limited --- unless n >= all results, + // we may obtain different positions from the Index and from find (because + // Index may not find the results in the same order as find) => in general + // we cannot simply check that the res and exp lists are equal + + // check that each result is in fact a correct match and there are no duplicates + sort.SortInts(res) + for i, r := range res { + if r < 0 || len(tc.source) <= r { + t.Errorf("test %q, lookup %q, result %d (n = %d): index %d out of range [0, %d[", tc.name, s, i, n, r, len(tc.source)) + } else if !strings.HasPrefix(tc.source[r:], s) { + t.Errorf("test %q, lookup %q, result %d (n = %d): index %d not a match", tc.name, s, i, n, r) + } + if i > 0 && res[i-1] == r { + t.Errorf("test %q, lookup %q, result %d (n = %d): found duplicate index %d", tc.name, s, i, n, r) + } + } + + if n < 0 { + // all results computed - sorted res and exp must be equal + for i, r := range res { + e := exp[i] + if r != e { + t.Errorf("test %q, lookup %q, result %d: expected index %d; got %d", tc.name, s, i, e, r) + } + } + } +} + + +func testFindAllIndex(t *testing.T, tc *testCase, x *Index, rx *regexp.Regexp, n int) { + res := x.FindAllIndex(rx, n) + exp := rx.FindAllStringIndex(tc.source, n) + + // check that the lengths match + if len(res) != len(exp) { + t.Errorf("test %q, FindAllIndex %q (n = %d): expected %d results; got %d", tc.name, rx, n, len(exp), len(res)) + } + + // if n >= 0 the number of results is limited --- unless n >= all results, + // we may obtain different positions from the Index and from regexp (because + // Index may not find the results in the same order as regexp) => in general + // we cannot simply check that the res and exp lists are equal + + // check that each result is in fact a correct match and the result is sorted + for i, r := range res { + if r[0] < 0 || r[0] > r[1] || len(tc.source) < r[1] { + t.Errorf("test %q, FindAllIndex %q, result %d (n == %d): illegal match [%d, %d]", tc.name, rx, i, n, r[0], r[1]) + } else if !rx.MatchString(tc.source[r[0]:r[1]]) { + t.Errorf("test %q, FindAllIndex %q, result %d (n = %d): [%d, %d] not a match", tc.name, rx, i, n, r[0], r[1]) + } + } + + if n < 0 { + // all results computed - sorted res and exp must be equal + for i, r := range res { + e := exp[i] + if r[0] != e[0] || r[1] != e[1] { + t.Errorf("test %q, FindAllIndex %q, result %d: expected match [%d, %d]; got [%d, %d]", + tc.name, rx, i, e[0], e[1], r[0], r[1]) + } + } + } +} + + +func testLookups(t *testing.T, tc *testCase, x *Index, n int) { + for _, pat := range tc.patterns { + testLookup(t, tc, x, pat, n) + if rx, err := regexp.Compile(pat); err == nil { + testFindAllIndex(t, tc, x, rx, n) + } + } +} + + +// index is used to hide the sort.Interface +type index Index + +func (x *index) Len() int { return len(x.sa) } +func (x *index) Less(i, j int) bool { return bytes.Compare(x.at(i), x.at(j)) < 0 } +func (x *index) Swap(i, j int) { x.sa[i], x.sa[j] = x.sa[j], x.sa[i] } +func (a *index) at(i int) []byte { return a.data[a.sa[i]:] } + + +func testConstruction(t *testing.T, tc *testCase, x *Index) { + if !sort.IsSorted((*index)(x)) { + t.Errorf("testConstruction failed %s", tc.name) + } +} + + +func TestIndex(t *testing.T) { + for _, tc := range testCases { + x := New([]byte(tc.source)) + testConstruction(t, &tc, x) + testLookups(t, &tc, x, 0) + testLookups(t, &tc, x, 1) + testLookups(t, &tc, x, 10) + testLookups(t, &tc, x, 2e9) + testLookups(t, &tc, x, -1) + } +} diff --git a/src/pkg/io/Makefile b/src/pkg/io/Makefile index 8c27ce551..9786002f0 100644 --- a/src/pkg/io/Makefile +++ b/src/pkg/io/Makefile @@ -2,11 +2,12 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=io GOFILES=\ io.go\ + multi.go\ pipe.go\ include ../../Make.pkg diff --git a/src/pkg/io/io.go b/src/pkg/io/io.go index dcdc883b1..1a6eca95a 100644 --- a/src/pkg/io/io.go +++ b/src/pkg/io/io.go @@ -19,6 +19,9 @@ type Error struct { // but failed to return an explicit error. var ErrShortWrite os.Error = &Error{"short write"} +// ErrShortBuffer means that a read required a longer buffer than was provided. +var ErrShortBuffer os.Error = &Error{"short buffer"} + // ErrUnexpectedEOF means that os.EOF was encountered in the // middle of reading a fixed-size block or data structure. var ErrUnexpectedEOF os.Error = &Error{"unexpected EOF"} @@ -165,8 +168,11 @@ func WriteString(w Writer, s string) (n int, err os.Error) { // The error is os.EOF only if no bytes were read. // If an EOF happens after reading fewer than min bytes, // ReadAtLeast returns ErrUnexpectedEOF. +// If min is greater than the length of buf, ReadAtLeast returns ErrShortBuffer. func ReadAtLeast(r Reader, buf []byte, min int) (n int, err os.Error) { - n = 0 + if len(buf) < min { + return 0, ErrShortBuffer + } for n < min { nn, e := r.Read(buf[n:]) if nn > 0 { @@ -179,7 +185,7 @@ func ReadAtLeast(r Reader, buf []byte, min int) (n int, err os.Error) { return n, e } } - return n, nil + return } // ReadFull reads exactly len(buf) bytes from r into buf. @@ -197,10 +203,15 @@ func ReadFull(r Reader, buf []byte) (n int, err os.Error) { // If dst implements the ReaderFrom interface, // the copy is implemented by calling dst.ReadFrom(src). func Copyn(dst Writer, src Reader, n int64) (written int64, err os.Error) { - // If the writer has a ReadFrom method, use it to to do the copy. + // If the writer has a ReadFrom method, use it to do the copy. // Avoids a buffer allocation and a copy. if rt, ok := dst.(ReaderFrom); ok { - return rt.ReadFrom(LimitReader(src, n)) + written, err = rt.ReadFrom(LimitReader(src, n)) + if written < n && err == nil { + // rt stopped early; must have been EOF. + err = os.EOF + } + return } buf := make([]byte, 32*1024) for written < n { @@ -240,12 +251,12 @@ func Copyn(dst Writer, src Reader, n int64) (written int64, err os.Error) { // Otherwise, if src implements the WriterTo interface, // the copy is implemented by calling src.WriteTo(dst). func Copy(dst Writer, src Reader) (written int64, err os.Error) { - // If the writer has a ReadFrom method, use it to to do the copy. + // If the writer has a ReadFrom method, use it to do the copy. // Avoids an allocation and a copy. if rt, ok := dst.(ReaderFrom); ok { return rt.ReadFrom(src) } - // Similarly, if the reader has a WriteTo method, use it to to do the copy. + // Similarly, if the reader has a WriteTo method, use it to do the copy. if wt, ok := src.(WriterTo); ok { return wt.WriteTo(dst) } @@ -336,7 +347,7 @@ func (s *SectionReader) Seek(offset int64, whence int) (ret int64, err os.Error) case 2: offset += s.limit } - if offset < s.off || offset > s.limit { + if offset < s.base || offset > s.limit { return 0, os.EINVAL } s.off = offset diff --git a/src/pkg/io/io_test.go b/src/pkg/io/io_test.go index 4ad1e5951..4fcd85e69 100644 --- a/src/pkg/io/io_test.go +++ b/src/pkg/io/io_test.go @@ -7,6 +7,8 @@ package io_test import ( "bytes" . "io" + "os" + "strings" "testing" ) @@ -78,3 +80,77 @@ func TestCopynWriteTo(t *testing.T) { t.Errorf("Copyn did not work properly") } } + +type noReadFrom struct { + w Writer +} + +func (w *noReadFrom) Write(p []byte) (n int, err os.Error) { + return w.w.Write(p) +} + +func TestCopynEOF(t *testing.T) { + // Test that EOF behavior is the same regardless of whether + // argument to Copyn has ReadFrom. + + b := new(bytes.Buffer) + + n, err := Copyn(&noReadFrom{b}, strings.NewReader("foo"), 3) + if n != 3 || err != nil { + t.Errorf("Copyn(noReadFrom, foo, 3) = %d, %v; want 3, nil", n, err) + } + + n, err = Copyn(&noReadFrom{b}, strings.NewReader("foo"), 4) + if n != 3 || err != os.EOF { + t.Errorf("Copyn(noReadFrom, foo, 4) = %d, %v; want 3, EOF", n, err) + } + + n, err = Copyn(b, strings.NewReader("foo"), 3) // b has read from + if n != 3 || err != nil { + t.Errorf("Copyn(bytes.Buffer, foo, 3) = %d, %v; want 3, nil", n, err) + } + + n, err = Copyn(b, strings.NewReader("foo"), 4) // b has read from + if n != 3 || err != os.EOF { + t.Errorf("Copyn(bytes.Buffer, foo, 4) = %d, %v; want 3, EOF", n, err) + } +} + +func TestReadAtLeast(t *testing.T) { + var rb bytes.Buffer + rb.Write([]byte("0123")) + buf := make([]byte, 2) + n, err := ReadAtLeast(&rb, buf, 2) + if err != nil { + t.Error(err) + } + n, err = ReadAtLeast(&rb, buf, 4) + if err != ErrShortBuffer { + t.Errorf("expected ErrShortBuffer got %v", err) + } + if n != 0 { + t.Errorf("expected to have read 0 bytes, got %v", n) + } + n, err = ReadAtLeast(&rb, buf, 1) + if err != nil { + t.Error(err) + } + if n != 2 { + t.Errorf("expected to have read 2 bytes, got %v", n) + } + n, err = ReadAtLeast(&rb, buf, 2) + if err != os.EOF { + t.Errorf("expected EOF, got %v", err) + } + if n != 0 { + t.Errorf("expected to have read 0 bytes, got %v", n) + } + rb.Write([]byte("4")) + n, err = ReadAtLeast(&rb, buf, 2) + if err != ErrUnexpectedEOF { + t.Errorf("expected ErrUnexpectedEOF, got %v", err) + } + if n != 1 { + t.Errorf("expected to have read 1 bytes, got %v", n) + } +} diff --git a/src/pkg/io/ioutil/Makefile b/src/pkg/io/ioutil/Makefile index 77b75bcec..d406d4b7d 100644 --- a/src/pkg/io/ioutil/Makefile +++ b/src/pkg/io/ioutil/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../../Make.$(GOARCH) +include ../../../Make.inc TARG=io/ioutil GOFILES=\ diff --git a/src/pkg/io/ioutil/ioutil.go b/src/pkg/io/ioutil/ioutil.go index d33869380..fb3fdcda1 100644 --- a/src/pkg/io/ioutil/ioutil.go +++ b/src/pkg/io/ioutil/ioutil.go @@ -49,7 +49,7 @@ func ReadFile(filename string) ([]byte, os.Error) { // WriteFile writes data to a file named by filename. // If the file does not exist, WriteFile creates it with permissions perm; // otherwise WriteFile truncates it before writing. -func WriteFile(filename string, data []byte, perm int) os.Error { +func WriteFile(filename string, data []byte, perm uint32) os.Error { f, err := os.Open(filename, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, perm) if err != nil { return err diff --git a/src/pkg/io/ioutil/ioutil_test.go b/src/pkg/io/ioutil/ioutil_test.go index ecbf41ca6..150ee6d63 100644 --- a/src/pkg/io/ioutil/ioutil_test.go +++ b/src/pkg/io/ioutil/ioutil_test.go @@ -37,7 +37,7 @@ func TestReadFile(t *testing.T) { } func TestWriteFile(t *testing.T) { - filename := "_obj/rumpelstilzchen" + filename := "_test/rumpelstilzchen" data := "Programming today is a race between software engineers striving to " + "build bigger and better idiot-proof programs, and the Universe trying " + "to produce bigger and better idiots. So far, the Universe is winning." @@ -74,19 +74,19 @@ func TestReadDir(t *testing.T) { } foundTest := false - foundObj := false + foundTestDir := false for _, dir := range list { switch { case dir.IsRegular() && dir.Name == "ioutil_test.go": foundTest = true - case dir.IsDirectory() && dir.Name == "_obj": - foundObj = true + case dir.IsDirectory() && dir.Name == "_test": + foundTestDir = true } } if !foundTest { t.Fatalf("ReadDir %s: test file not found", dirname) } - if !foundObj { - t.Fatalf("ReadDir %s: _obj directory not found", dirname) + if !foundTestDir { + t.Fatalf("ReadDir %s: _test directory not found", dirname) } } diff --git a/src/pkg/io/ioutil/tempfile_test.go b/src/pkg/io/ioutil/tempfile_test.go index fe43f9566..d949a86cf 100644 --- a/src/pkg/io/ioutil/tempfile_test.go +++ b/src/pkg/io/ioutil/tempfile_test.go @@ -23,7 +23,7 @@ func TestTempFile(t *testing.T) { t.Errorf("TempFile(dir, `ioutil_test`) = %v, %v", f, err) } if f != nil { - re := testing.MustCompile("^" + regexp.QuoteMeta(dir) + "/ioutil_test[0-9]+$") + re := regexp.MustCompile("^" + regexp.QuoteMeta(dir) + "/ioutil_test[0-9]+$") if !re.MatchString(f.Name()) { t.Errorf("TempFile(`"+dir+"`, `ioutil_test`) created bad name %s", f.Name()) } diff --git a/src/pkg/io/multi.go b/src/pkg/io/multi.go new file mode 100644 index 000000000..88e4f1b76 --- /dev/null +++ b/src/pkg/io/multi.go @@ -0,0 +1,60 @@ +// 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 io + +import "os" + +type multiReader struct { + readers []Reader +} + +func (mr *multiReader) Read(p []byte) (n int, err os.Error) { + for len(mr.readers) > 0 { + n, err = mr.readers[0].Read(p) + if n > 0 || err != os.EOF { + if err == os.EOF { + // This shouldn't happen. + // Well-behaved Readers should never + // return non-zero bytes read with an + // EOF. But if so, we clean it. + err = nil + } + return + } + mr.readers = mr.readers[1:] + } + return 0, os.EOF +} + +// MultiReader returns a Reader that's the logical concatenation of +// the provided input readers. They're read sequentially. Once all +// inputs are drained, Read will return os.EOF. +func MultiReader(readers ...Reader) Reader { + return &multiReader{readers} +} + +type multiWriter struct { + writers []Writer +} + +func (t *multiWriter) Write(p []byte) (n int, err os.Error) { + for _, w := range t.writers { + n, err = w.Write(p) + if err != nil { + return + } + if n != len(p) { + err = ErrShortWrite + return + } + } + return len(p), nil +} + +// MultiWriter creates a writer that duplicates its writes to all the +// provided writers, similar to the Unix tee(1) command. +func MultiWriter(writers ...Writer) Writer { + return &multiWriter{writers} +} diff --git a/src/pkg/io/multi_test.go b/src/pkg/io/multi_test.go new file mode 100644 index 000000000..3ecb7c75d --- /dev/null +++ b/src/pkg/io/multi_test.go @@ -0,0 +1,88 @@ +// 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 io_test + +import ( + . "io" + "bytes" + "crypto/sha1" + "fmt" + "os" + "strings" + "testing" +) + +func TestMultiReader(t *testing.T) { + var mr Reader + var buf []byte + nread := 0 + withFooBar := func(tests func()) { + r1 := strings.NewReader("foo ") + r2 := strings.NewReader("bar") + mr = MultiReader(r1, r2) + buf = make([]byte, 20) + tests() + } + expectRead := func(size int, expected string, eerr os.Error) { + nread++ + n, gerr := mr.Read(buf[0:size]) + if n != len(expected) { + t.Errorf("#%d, expected %d bytes; got %d", + nread, len(expected), n) + } + got := string(buf[0:n]) + if got != expected { + t.Errorf("#%d, expected %q; got %q", + nread, expected, got) + } + if gerr != eerr { + t.Errorf("#%d, expected error %v; got %v", + nread, eerr, gerr) + } + buf = buf[n:] + } + withFooBar(func() { + expectRead(2, "fo", nil) + expectRead(5, "o ", nil) + expectRead(5, "bar", nil) + expectRead(5, "", os.EOF) + }) + withFooBar(func() { + expectRead(4, "foo ", nil) + expectRead(1, "b", nil) + expectRead(3, "ar", nil) + expectRead(1, "", os.EOF) + }) + withFooBar(func() { + expectRead(5, "foo ", nil) + }) +} + +func TestMultiWriter(t *testing.T) { + sha1 := sha1.New() + sink := new(bytes.Buffer) + mw := MultiWriter(sha1, sink) + + sourceString := "My input text." + source := strings.NewReader(sourceString) + written, err := Copy(mw, source) + + if written != int64(len(sourceString)) { + t.Errorf("short write of %d, not %d", written, len(sourceString)) + } + + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + sha1hex := fmt.Sprintf("%x", sha1.Sum()) + if sha1hex != "01cb303fa8c30a64123067c5aa6284ba7ec2d31b" { + t.Error("incorrect sha1 value") + } + + if sink.String() != sourceString { + t.Errorf("expected %q; got %q", sourceString, sink.String()) + } +} diff --git a/src/pkg/io/pipe.go b/src/pkg/io/pipe.go index 898526921..df76418b9 100644 --- a/src/pkg/io/pipe.go +++ b/src/pkg/io/pipe.go @@ -52,6 +52,13 @@ func (p *pipe) run() { case <-p.done: if ndone++; ndone == 2 { // both reader and writer are gone + // close out any existing i/o + if r1 == nil { + p.r2 <- pipeResult{0, os.EINVAL} + } + if w1 == nil { + p.w2 <- pipeResult{0, os.EINVAL} + } return } continue @@ -89,6 +96,11 @@ func (p *pipe) run() { p.r2 <- pipeResult{0, werr} continue } + if rerr != nil { + // read end is closed + p.r2 <- pipeResult{0, os.EINVAL} + continue + } r1 = nil // disable Read until this one is done case wb = <-w1: if rerr != nil { @@ -96,6 +108,11 @@ func (p *pipe) run() { p.w2 <- pipeResult{0, rerr} continue } + if werr != nil { + // write end is closed + p.w2 <- pipeResult{0, os.EINVAL} + continue + } w1 = nil // disable Write until this one is done } @@ -275,20 +292,14 @@ func Pipe() (*PipeReader, *PipeWriter) { r.c2 = p.r2 r.cclose = p.rclose r.done = p.done - // TODO(rsc): Should be able to write - // runtime.SetFinalizer(r, (*PipeReader).finalizer) - // but 6g doesn't see the finalizer method. - runtime.SetFinalizer(&r.pipeHalf, (*pipeHalf).finalizer) + runtime.SetFinalizer(r, (*PipeReader).finalizer) w := new(PipeWriter) w.c1 = p.w1 w.c2 = p.w2 w.cclose = p.wclose w.done = p.done - // TODO(rsc): Should be able to write - // runtime.SetFinalizer(w, (*PipeWriter).finalizer) - // but 6g doesn't see the finalizer method. - runtime.SetFinalizer(&w.pipeHalf, (*pipeHalf).finalizer) + runtime.SetFinalizer(w, (*PipeWriter).finalizer) return r, w } diff --git a/src/pkg/io/pipe_test.go b/src/pkg/io/pipe_test.go index 902d7a0a3..bd4b94f0a 100644 --- a/src/pkg/io/pipe_test.go +++ b/src/pkg/io/pipe_test.go @@ -157,12 +157,12 @@ func (p pipeTest) String() string { } var pipeTests = []pipeTest{ - pipeTest{true, nil, false}, - pipeTest{true, nil, true}, - pipeTest{true, ErrShortWrite, true}, - pipeTest{false, nil, false}, - pipeTest{false, nil, true}, - pipeTest{false, ErrShortWrite, true}, + {true, nil, false}, + {true, nil, true}, + {true, ErrShortWrite, true}, + {false, nil, false}, + {false, nil, true}, + {false, ErrShortWrite, true}, } func delayClose(t *testing.T, cl closer, ch chan int, tt pipeTest) { diff --git a/src/pkg/json/Makefile b/src/pkg/json/Makefile index fa34bfd18..4e5a8a139 100644 --- a/src/pkg/json/Makefile +++ b/src/pkg/json/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=json GOFILES=\ diff --git a/src/pkg/json/decode.go b/src/pkg/json/decode.go index 3f6965009..c704cacbd 100644 --- a/src/pkg/json/decode.go +++ b/src/pkg/json/decode.go @@ -82,6 +82,18 @@ func (e *UnmarshalTypeError) String() string { return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() } +// An UnmarshalFieldError describes a JSON object key that +// led to an unexported (and therefore unwritable) struct field. +type UnmarshalFieldError struct { + Key string + Type *reflect.StructType + Field reflect.StructField +} + +func (e *UnmarshalFieldError) String() string { + return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() +} + // An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. // (The argument to Unmarshal must be a non-nil pointer.) type InvalidUnmarshalError struct { @@ -116,7 +128,9 @@ func (d *decodeState) unmarshal(v interface{}) (err os.Error) { } d.scan.reset() - d.value(pv.Elem()) + // We decode rv not pv.Elem because the Unmarshaler interface + // test must be applied at the top level of the value. + d.value(rv) return d.savedError } @@ -330,7 +344,7 @@ func (d *decodeState) array(v reflect.Value) { newcap = 4 } newv := reflect.MakeSlice(sv.Type().(*reflect.SliceType), sv.Len(), newcap) - reflect.ArrayCopy(newv, sv) + reflect.Copy(newv, sv) sv.Set(newv) } if i >= av.Len() && sv != nil { @@ -450,20 +464,32 @@ func (d *decodeState) object(v reflect.Value) { if mv != nil { subv = reflect.MakeZero(mv.Type().(*reflect.MapType).Elem()) } else { + var f reflect.StructField + var ok bool // First try for field with that tag. + st := sv.Type().(*reflect.StructType) for i := 0; i < sv.NumField(); i++ { - f := sv.Type().(*reflect.StructType).Field(i) + f = st.Field(i) if f.Tag == key { - subv = sv.Field(i) + ok = true break } } - if subv == nil { + if !ok { // Second, exact match. - subv = sv.FieldByName(key) - if subv == nil { - // Third, case-insensitive match. - subv = sv.FieldByNameFunc(func(s string) bool { return matchName(key, s) }) + f, ok = st.FieldByName(key) + } + if !ok { + // Third, case-insensitive match. + f, ok = st.FieldByNameFunc(func(s string) bool { return matchName(key, s) }) + } + + // Extract value; name must be exported. + if ok { + if f.PkgPath != "" { + d.saveError(&UnmarshalFieldError{key, st, f}) + } else { + subv = sv.FieldByIndex(f.Index) } } } @@ -805,13 +831,13 @@ func unquote(s []byte) (t string, ok bool) { if dec := utf16.DecodeRune(rune, rune1); dec != unicode.ReplacementChar { // A valid pair; consume. r += 6 - w += utf8.EncodeRune(dec, b[w:]) + w += utf8.EncodeRune(b[w:], dec) break } // Invalid surrogate; fall back to replacement rune. rune = unicode.ReplacementChar } - w += utf8.EncodeRune(rune, b[w:]) + w += utf8.EncodeRune(b[w:], rune) } // Quote, control characters are invalid. @@ -828,7 +854,7 @@ func unquote(s []byte) (t string, ok bool) { default: rune, size := utf8.DecodeRune(s[r:]) r += size - w += utf8.EncodeRune(rune, b[w:]) + w += utf8.EncodeRune(b[w:], rune) } } return string(b[0:w]), true diff --git a/src/pkg/json/decode_test.go b/src/pkg/json/decode_test.go index e10b2c56e..68cdea051 100644 --- a/src/pkg/json/decode_test.go +++ b/src/pkg/json/decode_test.go @@ -17,6 +17,30 @@ type T struct { Y int } +type tx struct { + x int +} + +var txType = reflect.Typeof((*tx)(nil)).(*reflect.PtrType).Elem().(*reflect.StructType) + +// A type that can unmarshal itself. + +type unmarshaler struct { + T bool +} + +func (u *unmarshaler) UnmarshalJSON(b []byte) os.Error { + *u = unmarshaler{true} // All we need to see that UnmarshalJson is called. + return nil +} + +var ( + um0, um1 unmarshaler // target2 of unmarshaling + ump = &um1 + umtrue = unmarshaler{true} +) + + type unmarshalTest struct { in string ptr interface{} @@ -26,26 +50,34 @@ type unmarshalTest struct { var unmarshalTests = []unmarshalTest{ // basic types - unmarshalTest{`true`, new(bool), true, nil}, - unmarshalTest{`1`, new(int), 1, nil}, - unmarshalTest{`1.2`, new(float), 1.2, nil}, - unmarshalTest{`-5`, new(int16), int16(-5), nil}, - unmarshalTest{`"a\u1234"`, new(string), "a\u1234", nil}, - unmarshalTest{`"http:\/\/"`, new(string), "http://", nil}, - unmarshalTest{`"g-clef: \uD834\uDD1E"`, new(string), "g-clef: \U0001D11E", nil}, - unmarshalTest{`"invalid: \uD834x\uDD1E"`, new(string), "invalid: \uFFFDx\uFFFD", nil}, - unmarshalTest{"null", new(interface{}), nil, nil}, - unmarshalTest{`{"X": [1,2,3], "Y": 4}`, new(T), T{Y: 4}, &UnmarshalTypeError{"array", reflect.Typeof("")}}, + {`true`, new(bool), true, nil}, + {`1`, new(int), 1, nil}, + {`1.2`, new(float), 1.2, nil}, + {`-5`, new(int16), int16(-5), nil}, + {`"a\u1234"`, new(string), "a\u1234", nil}, + {`"http:\/\/"`, new(string), "http://", nil}, + {`"g-clef: \uD834\uDD1E"`, new(string), "g-clef: \U0001D11E", nil}, + {`"invalid: \uD834x\uDD1E"`, new(string), "invalid: \uFFFDx\uFFFD", nil}, + {"null", new(interface{}), nil, nil}, + {`{"X": [1,2,3], "Y": 4}`, new(T), T{Y: 4}, &UnmarshalTypeError{"array", reflect.Typeof("")}}, + {`{"x": 1}`, new(tx), tx{}, &UnmarshalFieldError{"x", txType, txType.Field(0)}}, + + // syntax errors + {`{"X": "foo", "Y"}`, nil, nil, SyntaxError("invalid character '}' after object key")}, // composite tests - unmarshalTest{allValueIndent, new(All), allValue, nil}, - unmarshalTest{allValueCompact, new(All), allValue, nil}, - unmarshalTest{allValueIndent, new(*All), &allValue, nil}, - unmarshalTest{allValueCompact, new(*All), &allValue, nil}, - unmarshalTest{pallValueIndent, new(All), pallValue, nil}, - unmarshalTest{pallValueCompact, new(All), pallValue, nil}, - unmarshalTest{pallValueIndent, new(*All), &pallValue, nil}, - unmarshalTest{pallValueCompact, new(*All), &pallValue, nil}, + {allValueIndent, new(All), allValue, nil}, + {allValueCompact, new(All), allValue, nil}, + {allValueIndent, new(*All), &allValue, nil}, + {allValueCompact, new(*All), &allValue, nil}, + {pallValueIndent, new(All), pallValue, nil}, + {pallValueCompact, new(All), pallValue, nil}, + {pallValueIndent, new(*All), &pallValue, nil}, + {pallValueCompact, new(*All), &pallValue, nil}, + + // unmarshal interface test + {`{"T":false}`, &um0, umtrue, nil}, // use "false" so test will fail if custom unmarshaler is not called + {`{"T":false}`, &ump, &umtrue, nil}, } func TestMarshal(t *testing.T) { @@ -70,12 +102,31 @@ func TestMarshal(t *testing.T) { } } +func TestMarshalBadUTF8(t *testing.T) { + s := "hello\xffworld" + b, err := Marshal(s) + if err == nil { + t.Fatal("Marshal bad UTF8: no error") + } + if len(b) != 0 { + t.Fatal("Marshal returned data") + } + if _, ok := err.(*InvalidUTF8Error); !ok { + t.Fatalf("Marshal did not return InvalidUTF8Error: %T %v", err, err) + } +} + func TestUnmarshal(t *testing.T) { var scan scanner for i, tt := range unmarshalTests { in := []byte(tt.in) if err := checkValid(in, &scan); err != nil { - t.Errorf("#%d: checkValid: %v", i, err) + if !reflect.DeepEqual(err, tt.err) { + t.Errorf("#%d: checkValid: %v", i, err) + continue + } + } + if tt.ptr == nil { continue } // v = new(right-type) @@ -139,6 +190,16 @@ func TestUnmarshalPtrPtr(t *testing.T) { } } +func TestHTMLEscape(t *testing.T) { + b, err := MarshalForHTML("foobarbaz<>&quux") + if err != nil { + t.Fatalf("MarshalForHTML error: %v", err) + } + if !bytes.Equal(b, []byte(`"foobarbaz\u003c\u003e\u0026quux"`)) { + t.Fatalf("Unexpected encoding of \"<>&\": %s", b) + } +} + func noSpace(c int) int { if isSpace(c) { return -1 @@ -209,6 +270,8 @@ type All struct { Interface interface{} PInterface *interface{} + + unexported int } type Small struct { @@ -234,15 +297,15 @@ var allValue = All{ Foo: "foo", String: "16", Map: map[string]Small{ - "17": Small{Tag: "tag17"}, - "18": Small{Tag: "tag18"}, + "17": {Tag: "tag17"}, + "18": {Tag: "tag18"}, }, MapP: map[string]*Small{ "19": &Small{Tag: "tag19"}, "20": nil, }, EmptyMap: map[string]Small{}, - Slice: []Small{Small{Tag: "tag20"}, Small{Tag: "tag21"}}, + Slice: []Small{{Tag: "tag20"}, {Tag: "tag21"}}, SliceP: []*Small{&Small{Tag: "tag22"}, nil, &Small{Tag: "tag23"}}, EmptySlice: []Small{}, StringSlice: []string{"str24", "str25", "str26"}, diff --git a/src/pkg/json/encode.go b/src/pkg/json/encode.go index 5d7ce35cb..759b49dbe 100644 --- a/src/pkg/json/encode.go +++ b/src/pkg/json/encode.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// The json package implements encoding and decoding of JSON objects as +// defined in RFC 4627. package json import ( @@ -11,6 +13,7 @@ import ( "runtime" "sort" "strconv" + "utf8" ) // Marshal returns the JSON encoding of v. @@ -34,6 +37,7 @@ import ( // a member of the object. By default the object's key name is the // struct field name converted to lower case. If the struct field // has a tag, that tag will be used as the name instead. +// Only exported fields will be encoded. // // Map values encode as JSON objects. // The map's key type must be string; the object keys are used directly @@ -76,6 +80,43 @@ func MarshalIndent(v interface{}, prefix, indent string) ([]byte, os.Error) { return buf.Bytes(), nil } +// MarshalForHTML is like Marshal but applies HTMLEscape to the output. +func MarshalForHTML(v interface{}) ([]byte, os.Error) { + b, err := Marshal(v) + if err != nil { + return nil, err + } + var buf bytes.Buffer + HTMLEscape(&buf, b) + return buf.Bytes(), nil +} + +// HTMLEscape appends to dst the JSON-encoded src with <, >, and & +// characters inside string literals changed to \u003c, \u003e, \u0026 +// so that the JSON will be safe to embed inside HTML