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 --- 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 + 1173 files changed, 83939 insertions(+), 58006 deletions(-) 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 (limited to 'src') 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