1
0
mirror of https://github.com/golang/go synced 2024-11-23 20:30:04 -07:00

cmd/compile/internal/gc: compact binary export format

The binary import/export format is significantly more
compact than the existing textual format. It should
also be faster to read and write (to be measured).

Use -newexport to enable, for instance:
export GO_GCFLAGS=-newexport; make.bash

The compiler can import packages using both the old
and the new format ("mixed mode").

Missing: export info for inlined functions bodies
(performance issue, does not affect correctness).

Disabled by default until we have inlined function
bodies and confirmation of no regression and equality
of binaries.

For #6110.
For #1909.

This change depends on:

   https://go-review.googlesource.com/16220
   https://go-review.googlesource.com/16222

(already submitted) for all.bash to work.

Some initial export data sizes for std lib packages. This data
is without exported functions with inlineable function bodies.

Package                                       old      new    new/old

archive/tar.................................13875.....3883    28%
archive/zip.................................19464.....5046    26%
bufio....................................... 7733.....2222    29%
bytes.......................................10342.....3347    32%
cmd/addr2line.................................242.......26    11%
cmd/api.....................................39305....10368    26%
cmd/asm/internal/arch.......................27732.....7939    29%
cmd/asm/internal/asm........................35264....10295    29%
cmd/asm/internal/flags........................629......178    28%
cmd/asm/internal/lex........................39248....11128    28%
cmd/asm.......................................306.......26     8%
cmd/cgo.....................................40197....10570    26%
cmd/compile/internal/amd64...................1106......214    19%
cmd/compile/internal/arm....................27891.....7710    28%
cmd/compile/internal/arm64....................891......153    17%
cmd/compile/internal/big....................21637.....8336    39%
cmd/compile/internal/gc....................109845....29727    27%
cmd/compile/internal/mips64...................972......168    17%
cmd/compile/internal/ppc64....................972......168    17%
cmd/compile/internal/x86.....................1104......195    18%
cmd/compile...................................329.......26     8%
cmd/cover...................................12986.....3749    29%
cmd/dist......................................477.......67    14%
cmd/doc.....................................23043.....6793    29%
cmd/expdump...................................167.......26    16%
cmd/fix......................................1190......208    17%
cmd/go......................................26399.....5629    21%
cmd/gofmt.....................................499.......26     5%
cmd/internal/gcprog..........................1342......490    37%
cmd/internal/goobj...........................2690......980    36%
cmd/internal/obj/arm........................32740....10057    31%
cmd/internal/obj/arm64......................46542....15364    33%
cmd/internal/obj/mips.......................42140....13731    33%
cmd/internal/obj/ppc64......................42140....13731    33%
cmd/internal/obj/x86........................52732....19015    36%
cmd/internal/obj............................36729....11690    32%
cmd/internal/objfile........................36365....10287    28%
cmd/link/internal/amd64.....................45893....12220    27%
cmd/link/internal/arm.........................307.......96    31%
cmd/link/internal/arm64.......................345.......98    28%
cmd/link/internal/ld.......................109300....46326    42%
cmd/link/internal/ppc64.......................344.......99    29%
cmd/link/internal/x86.........................334......107    32%
cmd/link......................................314.......26     8%
cmd/newlink..................................8110.....2544    31%
cmd/nm........................................210.......26    12%
cmd/objdump...................................244.......26    11%
cmd/pack....................................14248.....4066    29%
cmd/pprof/internal/commands..................5239.....1285    25%
cmd/pprof/internal/driver...................37967.....8860    23%
cmd/pprof/internal/fetch....................30962.....7337    24%
cmd/pprof/internal/plugin...................47734.....7719    16%
cmd/pprof/internal/profile..................22286.....6922    31%
cmd/pprof/internal/report...................31187.....7838    25%
cmd/pprof/internal/svg.......................4315......965    22%
cmd/pprof/internal/symbolizer...............30051.....7397    25%
cmd/pprof/internal/symbolz..................28545.....6949    24%
cmd/pprof/internal/tempfile.................12550.....3356    27%
cmd/pprof.....................................563.......26     5%
cmd/trace....................................1455......636    44%
cmd/vendor/golang.org/x/arch/arm/armasm....168035....64737    39%
cmd/vendor/golang.org/x/arch/x86/x86asm.....26871.....8578    32%
cmd/vet.....................................38980.....9913    25%
cmd/vet/whitelist.............................102.......49    48%
cmd/yacc.....................................2518......926    37%
compress/bzip2...............................6326......129     2%
compress/flate...............................7069.....2541    36%
compress/gzip...............................20143.....5069    25%
compress/lzw..................................828......295    36%
compress/zlib...............................10676.....2692    25%
container/heap................................523......181    35%
container/list...............................3517......740    21%
container/ring................................881......229    26%
crypto/aes....................................550......187    34%
crypto/cipher................................1966......825    42%
crypto.......................................1836......646    35%
crypto/des....................................632......235    37%
crypto/dsa..................................18718.....5035    27%
crypto/ecdsa................................23131.....6097    26%
crypto/elliptic.............................20790.....5740    28%
crypto/hmac...................................455......186    41%
crypto/md5...................................1375......171    12%
crypto/rand.................................18132.....4748    26%
crypto/rc4....................................561......240    43%
crypto/rsa..................................22094.....6380    29%
crypto/sha1..................................1416......172    12%
crypto/sha256.................................551......238    43%
crypto/sha512.................................839......378    45%
crypto/subtle................................1153......250    22%
crypto/tls..................................58203....17984    31%
crypto/x509/pkix............................29447.....8161    28%
database/sql/driver..........................3318.....1096    33%
database/sql................................11258.....3942    35%
debug/dwarf.................................18416.....7006    38%
debug/elf...................................57530....21014    37%
debug/gosym..................................4992.....2058    41%
debug/macho.................................23037.....6538    28%
debug/pe....................................21063.....6619    31%
debug/plan9obj...............................2467......802    33%
encoding/ascii85.............................1523......360    24%
encoding/asn1................................1718......527    31%
encoding/base32..............................2642......686    26%
encoding/base64..............................3077......800    26%
encoding/binary..............................4727.....1040    22%
encoding/csv................................12223.....2850    23%
encoding......................................383......217    57%
encoding/gob................................37563....10113    27%
encoding/hex.................................1327......390    29%
encoding/json...............................30897.....7804    25%
encoding/pem..................................595......200    34%
encoding/xml................................37798.....9336    25%
errors........................................274.......36    13%
expvar.......................................3155.....1021    32%
flag........................................19860.....2849    14%
fmt..........................................3137.....1263    40%
go/ast......................................44729....13422    30%
go/build....................................16336.....4657    29%
go/constant..................................3703......846    23%
go/doc.......................................9877.....2807    28%
go/format....................................5472.....1575    29%
go/importer..................................4980.....1301    26%
go/internal/gccgoimporter....................5587.....1525    27%
go/internal/gcimporter.......................8979.....2186    24%
go/parser...................................20692.....5304    26%
go/printer...................................7015.....2029    29%
go/scanner...................................9719.....2824    29%
go/token.....................................7933.....2465    31%
go/types....................................64569....19978    31%
hash/adler32.................................1176......176    15%
hash/crc32...................................1663......360    22%
hash/crc64...................................1587......306    19%
hash/fnv.....................................3964......260     7%
hash..........................................591......278    47%
html..........................................217.......74    34%
html/template...............................69623....12588    18%
image/color/palette...........................315.......98    31%
image/color..................................5565.....1036    19%
image/draw...................................6917.....1028    15%
image/gif....................................8894.....1654    19%
image/internal/imageutil.....................9112.....1476    16%
image/jpeg...................................6647.....1026    15%
image/png....................................6906.....1069    15%
image.......................................28992.....6139    21%
index/suffixarray...........................17106.....4773    28%
internal/singleflight........................1614......506    31%
internal/testenv............................12212.....3152    26%
internal/trace...............................2762.....1323    48%
io/ioutil...................................13502.....3682    27%
io...........................................6765.....2482    37%
log.........................................11620.....3317    29%
log/syslog..................................13516.....3821    28%
math/big....................................21819.....8320    38%
math/cmplx...................................2816......438    16%
math/rand....................................2317......929    40%
math.........................................7511.....2444    33%
mime/multipart..............................12679.....3360    27%
mime/quotedprintable.........................5458.....1235    23%
mime.........................................6076.....1628    27%
net/http/cgi................................59796....17173    29%
net/http/cookiejar..........................14781.....3739    25%
net/http/fcgi...............................57861....16426    28%
net/http/httptest...........................84100....24365    29%
net/http/httputil...........................67763....18869    28%
net/http/internal............................6907......637     9%
net/http/pprof..............................57945....16316    28%
net/http....................................95391....30210    32%
net/internal/socktest........................4555.....1453    32%
net/mail....................................14481.....3608    25%
net/rpc/jsonrpc.............................33335......988     3%
net/rpc.....................................79950....23106    29%
net/smtp....................................57790....16468    28%
net/textproto...............................11356.....3248    29%
net/url......................................3123.....1009    32%
os/exec.....................................20738.....5769    28%
os/signal.....................................437......167    38%
os..........................................24875.....6668    27%
path/filepath...............................11340.....2826    25%
path..........................................778......285    37%
reflect.....................................15469.....5198    34%
regexp......................................13627.....4661    34%
regexp/syntax................................5539.....2249    41%
runtime/debug................................9275.....2322    25%
runtime/pprof................................1355......477    35%
runtime/race...................................39.......17    44%
runtime/trace.................................228.......92    40%
runtime.....................................13498.....1821    13%
sort.........................................2848......842    30%
strconv......................................2947.....1252    42%
strings......................................7983.....2456    31%
sync/atomic..................................2666.....1149    43%
sync.........................................2568......845    33%
syscall.....................................81252....38398    47%
testing/iotest...............................2444......302    12%
testing/quick...............................18890.....5076    27%
testing.....................................16502.....4800    29%
text/scanner.................................6849.....2052    30%
text/tabwriter...............................6607.....1863    28%
text/template/parse.........................22978.....6183    27%
text/template...............................64153....11518    18%
time........................................12103.....3546    29%
unicode......................................9706.....3320    34%
unicode/utf16................................1055......148    14%
unicode/utf8.................................1118......513    46%
vendor/golang.org/x/net/http2/hpack..........8905.....2636    30%

All packages                              3518505  1017774    29%

Change-Id: Id657334f276383ff1e6fa91472d3d1db5a03349c
Reviewed-on: https://go-review.googlesource.com/13937
Run-TryBot: Robert Griesemer <gri@golang.org>
Reviewed-by: Chris Manghane <cmang@golang.org>
This commit is contained in:
Robert Griesemer 2015-08-13 19:05:37 -07:00
parent 28ef4c38c8
commit ae2f54a771
14 changed files with 2822 additions and 358 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,619 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Binary package import.
// Based loosely on x/tools/go/importer.
package gc
import (
"cmd/compile/internal/big"
"cmd/internal/obj"
"encoding/binary"
)
// The overall structure of Import is symmetric to Export: For each
// export method in bexport.go there is a matching and symmetric method
// in bimport.go. Changing the export format requires making symmetric
// changes to bimport.go and bexport.go.
// Import populates importpkg from the serialized package data.
func Import(in *obj.Biobuf) {
p := importer{in: in}
p.buf = p.bufarray[:]
// read low-level encoding format
switch format := p.byte(); format {
case 'c':
// compact format - nothing to do
case 'd':
p.debugFormat = true
default:
Fatalf("invalid encoding format in export data: got %q; want 'c' or 'd'", format)
}
// --- generic export data ---
if v := p.string(); v != exportVersion {
Fatalf("unknown export data version: %s", v)
}
// populate typList with predeclared "known" types
p.typList = append(p.typList, predeclared()...)
// read package data
p.pkg()
if p.pkgList[0] != importpkg {
Fatalf("imported package not found in pkgList[0]")
}
// read compiler-specific flags
importpkg.Safe = p.string() == "safe"
// defer some type-checking until all types are read in completely
// (go.y:import_there)
tcok := typecheckok
typecheckok = true
defercheckwidth()
// read consts
for i := p.int(); i > 0; i-- {
sym := p.localname()
typ := p.typ()
val := p.value(typ)
if isideal(typ) {
// canonicalize ideal types
typ = Types[TIDEAL]
}
importconst(sym, typ, nodlit(val))
}
// read vars
for i := p.int(); i > 0; i-- {
sym := p.localname()
typ := p.typ()
importvar(sym, typ)
}
// read funcs
for i := p.int(); i > 0; i-- {
// go.y:hidden_fndcl
sym := p.localname()
typ := p.typ()
// TODO(gri) fix this
p.int() // read and discard index of inlined function body for now
importsym(sym, ONAME)
if sym.Def != nil && sym.Def.Op == ONAME && !Eqtype(typ, sym.Def.Type) {
Fatalf("inconsistent definition for func %v during import\n\t%v\n\t%v", sym, sym.Def.Type, typ)
}
n := newfuncname(sym)
n.Type = typ
declare(n, PFUNC)
funchdr(n)
// go.y:hidden_import
n.Func.Inl = nil
funcbody(n)
importlist = append(importlist, n) // TODO(gri) do this only if body is inlineable?
}
// read types
for i := p.int(); i > 0; i-- {
// name is parsed as part of named type
p.typ()
}
// --- compiler-specific export data ---
for i := p.int(); i > 0; i-- {
p.body()
}
// --- end of export data ---
typecheckok = tcok
resumecheckwidth()
testdclstack() // debugging only
}
type importer struct {
in *obj.Biobuf
buf []byte // for reading strings
bufarray [64]byte // initial underlying array for buf, large enough to avoid allocation when compiling std lib
pkgList []*Pkg
typList []*Type
debugFormat bool
read int // bytes read
}
func (p *importer) pkg() *Pkg {
// if the package was seen before, i is its index (>= 0)
i := p.tagOrIndex()
if i >= 0 {
return p.pkgList[i]
}
// otherwise, i is the package tag (< 0)
if i != packageTag {
Fatalf("expected package tag, found tag = %d", i)
}
// read package data
name := p.string()
path := p.string()
// we should never see an empty package name
if name == "" {
Fatalf("empty package name in import")
}
// we should never see a bad import path
if isbadimport(path) {
Fatalf("bad path in import: %q", path)
}
// an empty path denotes the package we are currently importing
pkg := importpkg
if path != "" {
pkg = mkpkg(path)
}
if pkg.Name == "" {
pkg.Name = name
} else if pkg.Name != name {
Fatalf("inconsistent package names: got %s; want %s (path = %s)", pkg.Name, name, path)
}
p.pkgList = append(p.pkgList, pkg)
return pkg
}
func (p *importer) localname() *Sym {
// go.y:hidden_importsym
name := p.string()
if name == "" {
Fatalf("unexpected anonymous name")
}
structpkg = importpkg // go.y:hidden_pkg_importsym
return importpkg.Lookup(name)
}
func (p *importer) newtyp(etype int) *Type {
t := typ(etype)
p.typList = append(p.typList, t)
return t
}
func (p *importer) typ() *Type {
// if the type was seen before, i is its index (>= 0)
i := p.tagOrIndex()
if i >= 0 {
return p.typList[i]
}
// otherwise, i is the type tag (< 0)
var t *Type
switch i {
case namedTag:
// go.y:hidden_importsym
tsym := p.qualifiedName()
// go.y:hidden_pkgtype
t = pkgtype(tsym)
importsym(tsym, OTYPE)
p.typList = append(p.typList, t)
// read underlying type
// go.y:hidden_type
t0 := p.typ()
importtype(t, t0) // go.y:hidden_import
// interfaces don't have associated methods
if t0.Etype == TINTER {
break
}
// read associated methods
for i := p.int(); i > 0; i-- {
// go.y:hidden_fndcl
name := p.string()
recv := p.paramList() // TODO(gri) do we need a full param list for the receiver?
params := p.paramList()
result := p.paramList()
// TODO(gri) fix this
p.int() // read and discard index of inlined function body for now
pkg := localpkg
if !exportname(name) {
pkg = tsym.Pkg
}
sym := pkg.Lookup(name)
n := methodname1(newname(sym), recv.N.Right)
n.Type = functype(recv.N, params, result)
checkwidth(n.Type)
// addmethod uses the global variable structpkg to verify consistency
{
saved := structpkg
structpkg = tsym.Pkg
addmethod(sym, n.Type, false, nointerface)
structpkg = saved
}
nointerface = false
funchdr(n)
// (comment from go.y)
// inl.C's inlnode in on a dotmeth node expects to find the inlineable body as
// (dotmeth's type).Nname.Inl, and dotmeth's type has been pulled
// out by typecheck's lookdot as this $$.ttype. So by providing
// this back link here we avoid special casing there.
n.Type.Nname = n
// go.y:hidden_import
n.Func.Inl = nil
funcbody(n)
importlist = append(importlist, n) // TODO(gri) do this only if body is inlineable?
}
case arrayTag, sliceTag:
t = p.newtyp(TARRAY)
t.Bound = -1
if i == arrayTag {
t.Bound = p.int64()
}
t.Type = p.typ()
case dddTag:
t = p.newtyp(T_old_DARRAY)
t.Bound = -1
t.Type = p.typ()
case structTag:
t = p.newtyp(TSTRUCT)
tostruct0(t, p.fieldList())
case pointerTag:
t = p.newtyp(Tptr)
t.Type = p.typ()
case signatureTag:
t = p.newtyp(TFUNC)
params := p.paramList()
result := p.paramList()
functype0(t, nil, params, result)
case interfaceTag:
t = p.newtyp(TINTER)
if p.int() != 0 {
Fatalf("unexpected embedded interface")
}
tointerface0(t, p.methodList())
case mapTag:
t = p.newtyp(TMAP)
t.Down = p.typ() // key
t.Type = p.typ() // val
case chanTag:
t = p.newtyp(TCHAN)
t.Chan = uint8(p.int())
t.Type = p.typ()
default:
Fatalf("unexpected type (tag = %d)", i)
}
if t == nil {
Fatalf("nil type (type tag = %d)", i)
}
return t
}
func (p *importer) qualifiedName() *Sym {
name := p.string()
pkg := p.pkg()
return pkg.Lookup(name)
}
// go.y:hidden_structdcl_list
func (p *importer) fieldList() *NodeList {
i := p.int()
if i == 0 {
return nil
}
n := list1(p.field())
for i--; i > 0; i-- {
n = list(n, p.field())
}
return n
}
// go.y:hidden_structdcl
func (p *importer) field() *Node {
sym := p.fieldName()
typ := p.typ()
note := p.note()
var n *Node
if sym.Name != "" {
n = Nod(ODCLFIELD, newname(sym), typenod(typ))
} else {
// anonymous field - typ must be T or *T and T must be a type name
s := typ.Sym
if s == nil && Isptr[typ.Etype] {
s = typ.Type.Sym // deref
}
pkg := importpkg
if sym != nil {
pkg = sym.Pkg
}
n = embedded(s, pkg)
n.Right = typenod(typ)
}
n.SetVal(note)
return n
}
func (p *importer) note() (v Val) {
if s := p.string(); s != "" {
v.U = s
}
return
}
// go.y:hidden_interfacedcl_list
func (p *importer) methodList() *NodeList {
i := p.int()
if i == 0 {
return nil
}
n := list1(p.method())
for i--; i > 0; i-- {
n = list(n, p.method())
}
return n
}
// go.y:hidden_interfacedcl
func (p *importer) method() *Node {
sym := p.fieldName()
params := p.paramList()
result := p.paramList()
return Nod(ODCLFIELD, newname(sym), typenod(functype(fakethis(), params, result)))
}
// go.y:sym,hidden_importsym
func (p *importer) fieldName() *Sym {
name := p.string()
pkg := localpkg
if name == "_" {
// During imports, unqualified non-exported identifiers are from builtinpkg
// (see go.y:sym). The binary exporter only exports blank as a non-exported
// identifier without qualification.
pkg = builtinpkg
} else if name == "?" || name != "" && !exportname(name) {
if name == "?" {
name = ""
}
pkg = p.pkg()
}
return pkg.Lookup(name)
}
// go.y:ohidden_funarg_list
func (p *importer) paramList() *NodeList {
i := p.int()
if i == 0 {
return nil
}
// negative length indicates unnamed parameters
named := true
if i < 0 {
i = -i
named = false
}
// i > 0
n := list1(p.param(named))
i--
for ; i > 0; i-- {
n = list(n, p.param(named))
}
return n
}
// go.y:hidden_funarg
func (p *importer) param(named bool) *Node {
typ := p.typ()
isddd := false
if typ.Etype == T_old_DARRAY {
// T_old_DARRAY indicates ... type
typ.Etype = TARRAY
isddd = true
}
n := Nod(ODCLFIELD, nil, typenod(typ))
n.Isddd = isddd
if named {
name := p.string()
if name == "" {
Fatalf("expected named parameter")
}
// The parameter package doesn't matter; it's never consulted.
// We use the builtinpkg per go.y:sym (line 1181).
n.Left = newname(builtinpkg.Lookup(name))
}
// TODO(gri) This is compiler-specific (escape info).
// Move into compiler-specific section eventually?
n.SetVal(p.note())
return n
}
func (p *importer) value(typ *Type) (x Val) {
switch tag := p.tagOrIndex(); tag {
case falseTag:
x.U = false
case trueTag:
x.U = true
case int64Tag:
u := new(Mpint)
Mpmovecfix(u, p.int64())
u.Rune = typ == idealrune
x.U = u
case floatTag:
f := newMpflt()
p.float(f)
if typ == idealint || Isint[typ.Etype] {
// uncommon case: large int encoded as float
u := new(Mpint)
mpmovefltfix(u, f)
x.U = u
break
}
x.U = f
case complexTag:
u := new(Mpcplx)
p.float(&u.Real)
p.float(&u.Imag)
x.U = u
case stringTag:
x.U = p.string()
default:
Fatalf("unexpected value tag %d", tag)
}
// verify ideal type
if isideal(typ) && untype(x.Ctype()) != typ {
Fatalf("value %v and type %v don't match", x, typ)
}
return
}
func (p *importer) float(x *Mpflt) {
sign := p.int()
if sign == 0 {
Mpmovecflt(x, 0)
return
}
exp := p.int()
mant := new(big.Int).SetBytes([]byte(p.string()))
m := x.Val.SetInt(mant)
m.SetMantExp(m, exp-mant.BitLen())
if sign < 0 {
m.Neg(m)
}
}
// ----------------------------------------------------------------------------
// Inlined function bodies
func (p *importer) body() {
p.int()
p.block()
}
func (p *importer) block() {
for i := p.int(); i > 0; i-- {
p.stmt()
}
}
func (p *importer) stmt() {
// TODO(gri) do something sensible here
p.string()
}
// ----------------------------------------------------------------------------
// Low-level decoders
func (p *importer) tagOrIndex() int {
if p.debugFormat {
p.marker('t')
}
return int(p.rawInt64())
}
func (p *importer) int() int {
x := p.int64()
if int64(int(x)) != x {
Fatalf("exported integer too large")
}
return int(x)
}
func (p *importer) int64() int64 {
if p.debugFormat {
p.marker('i')
}
return p.rawInt64()
}
func (p *importer) string() string {
if p.debugFormat {
p.marker('s')
}
if n := int(p.rawInt64()); n > 0 {
if cap(p.buf) < n {
p.buf = make([]byte, n)
} else {
p.buf = p.buf[:n]
}
r := obj.Bread(p.in, p.buf)
p.read += r
if r != n {
Fatalf("read error: read %d bytes of %d", r, n)
}
return string(p.buf)
}
return ""
}
func (p *importer) marker(want byte) {
if got := p.byte(); got != want {
Fatalf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read)
}
pos := p.read
if n := int(p.rawInt64()); n != pos {
Fatalf("incorrect position: got %d; want %d", n, pos)
}
}
func (p *importer) byte() byte {
if c := obj.Bgetc(p.in); c >= 0 {
p.read++
return byte(c)
}
Fatalf("read error")
return 0
}
// rawInt64 should only be used by low-level decoders
func (p *importer) rawInt64() int64 {
i, err := binary.ReadVarint(p)
if err != nil {
Fatalf("read error: %v", err)
}
return i
}
// needed for binary.ReadVarint in rawInt64
func (p *importer) ReadByte() (byte, error) {
return p.byte(), nil
}

View File

@ -874,11 +874,18 @@ func checkdupfields(t *Type, what string) {
* a type for struct/interface/arglist
*/
func tostruct(l *NodeList) *Type {
var f *Type
t := typ(TSTRUCT)
tostruct0(t, l)
return t
}
func tostruct0(t *Type, l *NodeList) {
if t == nil || t.Etype != TSTRUCT {
Fatalf("struct expected")
}
for tp := &t.Type; l != nil; l = l.Next {
f = structfield(l.N)
f := structfield(l.N)
*tp = f
tp = &f.Down
@ -896,8 +903,6 @@ func tostruct(l *NodeList) *Type {
if !t.Broke {
checkwidth(t)
}
return t
}
func tofunargs(l *NodeList) *Type {
@ -996,18 +1001,23 @@ func interfacefield(n *Node) *Type {
}
func tointerface(l *NodeList) *Type {
var f *Type
var t1 *Type
t := typ(TINTER)
tointerface0(t, l)
return t
}
func tointerface0(t *Type, l *NodeList) *Type {
if t == nil || t.Etype != TINTER {
Fatalf("interface expected")
}
tp := &t.Type
for ; l != nil; l = l.Next {
f = interfacefield(l.N)
f := interfacefield(l.N)
if l.N.Left == nil && f.Type.Etype == TINTER {
// embedded interface, inline methods
for t1 = f.Type.Type; t1 != nil; t1 = t1.Down {
for t1 := f.Type.Type; t1 != nil; t1 = t1.Down {
f = typ(TFIELD)
f.Type = t1.Type
f.Broke = t1.Broke
@ -1200,6 +1210,14 @@ func isifacemethod(f *Type) bool {
*/
func functype(this *Node, in *NodeList, out *NodeList) *Type {
t := typ(TFUNC)
functype0(t, this, in, out)
return t
}
func functype0(t *Type, this *Node, in *NodeList, out *NodeList) {
if t == nil || t.Etype != TFUNC {
Fatalf("function type expected")
}
var rcvr *NodeList
if this != nil {
@ -1230,8 +1248,6 @@ func functype(this *Node, in *NodeList, out *NodeList) *Type {
t.Outnamed = true
}
}
return t
}
var methodsym_toppkg *Pkg

View File

@ -5,6 +5,7 @@
package gc
import (
"bytes"
"cmd/internal/obj"
"fmt"
"sort"
@ -12,6 +13,20 @@ import (
"unicode/utf8"
)
var (
newexport int // if set, use new export format
Debug_export int // if set, print debugging information about export data
exportsize int
)
func exportf(format string, args ...interface{}) {
n, _ := fmt.Fprintf(bout, format, args...)
exportsize += n
if Debug_export != 0 {
fmt.Printf(format, args...)
}
}
var asmlist *NodeList
// Mark n's symbol as exported
@ -35,8 +50,8 @@ func exportsym(n *Node) {
}
func exportname(s string) bool {
if s[0] < utf8.RuneSelf {
return 'A' <= s[0] && s[0] <= 'Z'
if r := s[0]; r < utf8.RuneSelf {
return 'A' <= r && r <= 'Z'
}
r, _ := utf8.DecodeRuneInString(s)
return unicode.IsUpper(r)
@ -87,7 +102,7 @@ func dumppkg(p *Pkg) {
if !p.Direct {
suffix = " // indirect"
}
fmt.Fprintf(bout, "\timport %s %q%s\n", p.Name, p.Path, suffix)
exportf("\timport %s %q%s\n", p.Name, p.Path, suffix)
}
// Look for anything we need for the inline body
@ -128,7 +143,7 @@ func reexportdep(n *Node) {
}
}
// Local variables in the bodies need their type.
// Local variables in the bodies need their type.
case ODCL:
t := n.Left.Type
@ -167,7 +182,7 @@ func reexportdep(n *Node) {
exportlist = append(exportlist, n)
}
// for operations that need a type when rendered, put the type on the export list.
// for operations that need a type when rendered, put the type on the export list.
case OCONV,
OCONVIFACE,
OCONVNOP,
@ -216,9 +231,9 @@ func dumpexportconst(s *Sym) {
dumpexporttype(t)
if t != nil && !isideal(t) {
fmt.Fprintf(bout, "\tconst %v %v = %v\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtSharp), Vconv(n.Val(), obj.FmtSharp))
exportf("\tconst %v %v = %v\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtSharp), Vconv(n.Val(), obj.FmtSharp))
} else {
fmt.Fprintf(bout, "\tconst %v = %v\n", Sconv(s, obj.FmtSharp), Vconv(n.Val(), obj.FmtSharp))
exportf("\tconst %v = %v\n", Sconv(s, obj.FmtSharp), Vconv(n.Val(), obj.FmtSharp))
}
}
@ -242,14 +257,14 @@ func dumpexportvar(s *Sym) {
}
// NOTE: The space after %#S here is necessary for ld's export data parser.
fmt.Fprintf(bout, "\tfunc %v %v { %v }\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtShort|obj.FmtSharp), Hconv(n.Func.Inl, obj.FmtSharp))
exportf("\tfunc %v %v { %v }\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtShort|obj.FmtSharp), Hconv(n.Func.Inl, obj.FmtSharp))
reexportdeplist(n.Func.Inl)
} else {
fmt.Fprintf(bout, "\tfunc %v %v\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtShort|obj.FmtSharp))
exportf("\tfunc %v %v\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtShort|obj.FmtSharp))
}
} else {
fmt.Fprintf(bout, "\tvar %v %v\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtSharp))
exportf("\tvar %v %v\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtSharp))
}
}
@ -287,10 +302,10 @@ func dumpexporttype(t *Type) {
}
sort.Sort(methodbyname(m))
fmt.Fprintf(bout, "\ttype %v %v\n", Sconv(t.Sym, obj.FmtSharp), Tconv(t, obj.FmtSharp|obj.FmtLong))
exportf("\ttype %v %v\n", Sconv(t.Sym, obj.FmtSharp), Tconv(t, obj.FmtSharp|obj.FmtLong))
for _, f := range m {
if f.Nointerface {
fmt.Fprintf(bout, "\t//go:nointerface\n")
exportf("\t//go:nointerface\n")
}
if f.Type.Nname != nil && f.Type.Nname.Func.Inl != nil { // nname was set by caninl
@ -299,10 +314,10 @@ func dumpexporttype(t *Type) {
if Debug['l'] < 2 {
typecheckinl(f.Type.Nname)
}
fmt.Fprintf(bout, "\tfunc (%v) %v %v { %v }\n", Tconv(getthisx(f.Type).Type, obj.FmtSharp), Sconv(f.Sym, obj.FmtShort|obj.FmtByte|obj.FmtSharp), Tconv(f.Type, obj.FmtShort|obj.FmtSharp), Hconv(f.Type.Nname.Func.Inl, obj.FmtSharp))
exportf("\tfunc (%v) %v %v { %v }\n", Tconv(getthisx(f.Type).Type, obj.FmtSharp), Sconv(f.Sym, obj.FmtShort|obj.FmtByte|obj.FmtSharp), Tconv(f.Type, obj.FmtShort|obj.FmtSharp), Hconv(f.Type.Nname.Func.Inl, obj.FmtSharp))
reexportdeplist(f.Type.Nname.Func.Inl)
} else {
fmt.Fprintf(bout, "\tfunc (%v) %v %v\n", Tconv(getthisx(f.Type).Type, obj.FmtSharp), Sconv(f.Sym, obj.FmtShort|obj.FmtByte|obj.FmtSharp), Tconv(f.Type, obj.FmtShort|obj.FmtSharp))
exportf("\tfunc (%v) %v %v\n", Tconv(getthisx(f.Type).Type, obj.FmtSharp), Sconv(f.Sym, obj.FmtShort|obj.FmtByte|obj.FmtSharp), Tconv(f.Type, obj.FmtShort|obj.FmtSharp))
}
}
}
@ -341,33 +356,83 @@ func dumpsym(s *Sym) {
}
func dumpexport() {
lno := lineno
if buildid != "" {
fmt.Fprintf(bout, "build id %q\n", buildid)
exportf("build id %q\n", buildid)
}
fmt.Fprintf(bout, "\n$$\npackage %s", localpkg.Name)
if safemode != 0 {
fmt.Fprintf(bout, " safe")
}
fmt.Fprintf(bout, "\n")
for _, p := range pkgs {
if p.Direct {
dumppkg(p)
size := 0 // size of export section without enclosing markers
if forceNewExport || newexport != 0 {
// binary export
// The linker also looks for the $$ marker - use char after $$ to distinguish format.
exportf("\n$$B\n") // indicate binary format
const verifyExport = true // enable to check format changes
if verifyExport {
// save a copy of the export data
var copy bytes.Buffer
bcopy := obj.Binitw(&copy)
size = Export(bcopy, Debug_export != 0)
bcopy.Flush() // flushing to bytes.Buffer cannot fail
if n, err := bout.Write(copy.Bytes()); n != size || err != nil {
Fatalf("error writing export data: got %d bytes, want %d bytes, err = %v", n, size, err)
}
// verify there's no "\n$$\n" inside the export data
// TODO(gri) fragile - the end marker needs to be fixed
// TODO(gri) investigate if exporting a string containing "\n$$\n"
// causes problems (old and new format)
if bytes.Index(copy.Bytes(), []byte("\n$$\n")) >= 0 {
Fatalf("export data contains end marker in its midst")
}
// verify that we can read the copied export data back in
// (use empty package map to avoid collisions)
savedPkgMap := pkgMap
savedPkgs := pkgs
pkgMap = make(map[string]*Pkg)
pkgs = nil
importpkg = mkpkg("")
Import(obj.Binitr(&copy)) // must not die
importpkg = nil
pkgs = savedPkgs
pkgMap = savedPkgMap
} else {
size = Export(bout, Debug_export != 0)
}
exportf("\n$$\n")
} else {
// textual export
lno := lineno
exportf("\n$$\n") // indicate textual format
exportsize = 0
exportf("package %s", localpkg.Name)
if safemode != 0 {
exportf(" safe")
}
exportf("\n")
for _, p := range pkgs {
if p.Direct {
dumppkg(p)
}
}
// exportlist grows during iteration - cannot use range
for len(exportlist) > 0 {
n := exportlist[0]
exportlist = exportlist[1:]
lineno = n.Lineno
dumpsym(n.Sym)
}
size = exportsize
exportf("\n$$\n")
lineno = lno
}
// exportlist grows during iteration - cannot use range
for len(exportlist) > 0 {
n := exportlist[0]
exportlist = exportlist[1:]
lineno = n.Lineno
dumpsym(n.Sym)
if Debug_export != 0 {
fmt.Printf("export data size = %d bytes\n", size)
}
fmt.Fprintf(bout, "\n$$\n")
lineno = lno
}
/*

View File

@ -288,7 +288,7 @@ const (
TFUNC
TARRAY
T_old_DARRAY
T_old_DARRAY // Doesn't seem to be used in existing code. Used now for Isddd export (see bexport.go). TODO(gri) rename.
TSTRUCT
TCHAN
TMAP

View File

@ -254,6 +254,7 @@ import_stmt:
break;
}
if my.Name == "init" {
lineno = int32($1)
Yyerror("cannot import package as init - init must be a func");
break;
}

View File

@ -58,6 +58,7 @@ var debugtab = []struct {
{"slice", &Debug_slice}, // print information about slice compilation
{"typeassert", &Debug_typeassert}, // print information about type assertion inlining
{"wb", &Debug_wb}, // print information about write barriers
{"export", &Debug_export}, // print export data
}
const (
@ -201,6 +202,7 @@ func Main() {
obj.Flagcount("live", "debug liveness analysis", &debuglive)
obj.Flagcount("m", "print optimization decisions", &Debug['m'])
obj.Flagcount("msan", "build code compatible with C/C++ memory sanitizer", &flag_msan)
obj.Flagcount("newexport", "use new export format", &newexport) // TODO remove eventually
obj.Flagcount("nolocalimports", "reject local (relative) imports", &nolocalimports)
obj.Flagstr("o", "write output to `file`", &outfile)
obj.Flagstr("p", "set expected package import `path`", &myimportpath)
@ -655,6 +657,7 @@ func fakeimport() {
cannedimports("fake.o", "$$\n")
}
// TODO(gri) line argument doesn't appear to be used
func importfile(f *Val, line int) {
if _, ok := f.U.(string); !ok {
Yyerror("import statement not a string")
@ -786,39 +789,58 @@ func importfile(f *Val, line int) {
// so don't record the full path.
linehistpragma(file[len(file)-len(path_)-2:]) // acts as #pragma lib
/*
* position the input right
* after $$ and return
*/
pushedio = curio
curio.bin = imp
curio.peekc = 0
curio.peekc1 = 0
curio.infile = file
curio.nlsemi = false
typecheckok = true
// In the importfile, if we find:
// $$\n (old format): position the input right after $$\n and return
// $$B\n (new format): import directly, then feed the lexer a dummy statement
// look for $$
var c int
for {
c := getc()
if c == EOF {
c = obj.Bgetc(imp)
if c < 0 {
break
}
if c != '$' {
continue
if c == '$' {
c = obj.Bgetc(imp)
if c == '$' || c < 0 {
break
}
}
c = getc()
if c == EOF {
break
}
if c != '$' {
continue
}
return
}
Yyerror("no import in %q", f.U.(string))
unimportfile()
// get character after $$
if c >= 0 {
c = obj.Bgetc(imp)
}
switch c {
case '\n':
// old export format
pushedio = curio
curio.bin = imp
curio.peekc = 0
curio.peekc1 = 0
curio.infile = file
curio.nlsemi = false
typecheckok = true
case 'B':
// new export format
obj.Bgetc(imp) // skip \n after $$B
Import(imp)
// continue as if the package was imported before (see above)
tag := ""
if importpkg.Safe {
tag = "safe"
}
p := fmt.Sprintf("package %s %s\n$$\n", importpkg.Name, tag)
cannedimports(file, p)
default:
Yyerror("no import in %q", f.U.(string))
}
}
func unimportfile() {

View File

@ -2605,6 +2605,8 @@ func hasddd(t *Type) bool {
return false
}
// downcount is the same as countfield
// TODO decide if we want both (for semantic reasons)
func downcount(t *Type) int {
n := 0
for tl := t.Type; tl != nil; tl = tl.Down {

File diff suppressed because it is too large Load Diff

View File

@ -53,6 +53,10 @@ func Binitw(w io.Writer) *Biobuf {
return &Biobuf{w: bufio.NewWriter(w)}
}
func Binitr(r io.Reader) *Biobuf {
return &Biobuf{r: bufio.NewReader(r)}
}
func (b *Biobuf) Write(p []byte) (int, error) {
return b.w.Write(p)
}

View File

@ -0,0 +1,634 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gcimporter
import (
"encoding/binary"
"fmt"
"go/constant"
"go/token"
"go/types"
"sort"
"unicode"
"unicode/utf8"
)
// BImportData imports a package from the serialized package data
// and returns the number of bytes consumed and a reference to the package.
// If data is obviously malformed, an error is returned but in
// general it is not recommended to call BImportData on untrusted data.
func BImportData(imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) {
// determine low-level encoding format
read := 0
var format byte = 'm' // missing format
if len(data) > 0 {
format = data[0]
data = data[1:]
read++
}
if format != 'c' && format != 'd' {
return read, nil, fmt.Errorf("invalid encoding format in export data: got %q; want 'c' or 'd'", format)
}
// --- generic export data ---
p := importer{
imports: imports,
data: data,
debugFormat: format == 'd',
read: read,
}
if v := p.string(); v != "v0" {
return p.read, nil, fmt.Errorf("unknown version: %s", v)
}
// populate typList with predeclared "known" types
p.typList = append(p.typList, predeclared...)
// read package data
// TODO(gri) clean this up
i := p.tagOrIndex()
if i != packageTag {
panic(fmt.Sprintf("package tag expected, got %d", i))
}
name := p.string()
if s := p.string(); s != "" {
panic(fmt.Sprintf("empty path expected, got %s", s))
}
pkg := p.imports[path]
if pkg == nil {
pkg = types.NewPackage(path, name)
p.imports[path] = pkg
}
p.pkgList = append(p.pkgList, pkg)
if debug && p.pkgList[0] != pkg {
panic("imported packaged not found in pkgList[0]")
}
// read compiler-specific flags
p.string() // discard
// read consts
for i := p.int(); i > 0; i-- {
name := p.string()
typ := p.typ()
val := p.value()
p.declare(types.NewConst(token.NoPos, pkg, name, typ, val))
}
// read vars
for i := p.int(); i > 0; i-- {
name := p.string()
typ := p.typ()
p.declare(types.NewVar(token.NoPos, pkg, name, typ))
}
// read funcs
for i := p.int(); i > 0; i-- {
name := p.string()
sig := p.typ().(*types.Signature)
p.int() // read and discard index of inlined function body
p.declare(types.NewFunc(token.NoPos, pkg, name, sig))
}
// read types
for i := p.int(); i > 0; i-- {
// name is parsed as part of named type and the
// type object is added to scope via respective
// named type
_ = p.typ().(*types.Named)
}
// complete interfaces
for _, typ := range p.typList {
if it, ok := typ.(*types.Interface); ok {
it.Complete()
}
}
// record all referenced packages as imports
list := append(([]*types.Package)(nil), p.pkgList[1:]...)
sort.Sort(byPath(list))
pkg.SetImports(list)
// package was imported completely and without errors
pkg.MarkComplete()
return p.read, pkg, nil
}
type importer struct {
imports map[string]*types.Package
data []byte
pkgList []*types.Package
typList []types.Type
debugFormat bool
read int // bytes read
}
func (p *importer) declare(obj types.Object) {
if alt := p.pkgList[0].Scope().Insert(obj); alt != nil {
// This can only happen if we import a package a second time.
panic(fmt.Sprintf("%s already declared", alt.Name()))
}
}
func (p *importer) pkg() *types.Package {
// if the package was seen before, i is its index (>= 0)
i := p.tagOrIndex()
if i >= 0 {
return p.pkgList[i]
}
// otherwise, i is the package tag (< 0)
if i != packageTag {
panic(fmt.Sprintf("unexpected package tag %d", i))
}
// read package data
name := p.string()
path := p.string()
// we should never see an empty package name
if name == "" {
panic("empty package name in import")
}
// we should never see an empty import path
if path == "" {
panic("empty import path")
}
// if the package was imported before, use that one; otherwise create a new one
pkg := p.imports[path]
if pkg == nil {
pkg = types.NewPackage(path, name)
p.imports[path] = pkg
}
p.pkgList = append(p.pkgList, pkg)
return pkg
}
func (p *importer) record(t types.Type) {
p.typList = append(p.typList, t)
}
// A dddSlice is a types.Type representing ...T parameters.
// It only appears for parameter types and does not escape
// the importer.
type dddSlice struct {
elem types.Type
}
func (t *dddSlice) Underlying() types.Type { return t }
func (t *dddSlice) String() string { return "..." + t.elem.String() }
func (p *importer) typ() types.Type {
// if the type was seen before, i is its index (>= 0)
i := p.tagOrIndex()
if i >= 0 {
return p.typList[i]
}
// otherwise, i is the type tag (< 0)
switch i {
case namedTag:
// read type object
name := p.string()
tpkg := p.pkg()
scope := tpkg.Scope()
obj := scope.Lookup(name)
// if the object doesn't exist yet, create and insert it
if obj == nil {
obj = types.NewTypeName(token.NoPos, tpkg, name, nil)
scope.Insert(obj)
}
if _, ok := obj.(*types.TypeName); !ok {
panic(fmt.Sprintf("pkg = %s, name = %s => %s", tpkg, name, obj))
}
// associate new named type with obj if it doesn't exist yet
t0 := types.NewNamed(obj.(*types.TypeName), nil, nil)
// but record the existing type, if any
t := obj.Type().(*types.Named)
p.record(t)
// read underlying type
t0.SetUnderlying(p.typ())
// interfaces don't have associated methods
if _, ok := t0.Underlying().(*types.Interface); ok {
return t
}
// read associated methods
for i := p.int(); i > 0; i-- {
name := p.string()
recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver?
params, isddd := p.paramList()
result, _ := p.paramList()
p.int() // read and discard index of inlined function body
sig := types.NewSignature(recv.At(0), params, result, isddd)
t0.AddMethod(types.NewFunc(token.NoPos, tpkg, name, sig))
}
return t
case arrayTag:
t := new(types.Array)
p.record(t)
n := p.int64()
*t = *types.NewArray(p.typ(), n)
return t
case sliceTag:
t := new(types.Slice)
p.record(t)
*t = *types.NewSlice(p.typ())
return t
case dddTag:
t := new(dddSlice)
p.record(t)
t.elem = p.typ()
return t
case structTag:
t := new(types.Struct)
p.record(t)
n := p.int()
fields := make([]*types.Var, n)
tags := make([]string, n)
for i := range fields {
fields[i] = p.field()
tags[i] = p.string()
}
*t = *types.NewStruct(fields, tags)
return t
case pointerTag:
t := new(types.Pointer)
p.record(t)
*t = *types.NewPointer(p.typ())
return t
case signatureTag:
t := new(types.Signature)
p.record(t)
params, isddd := p.paramList()
result, _ := p.paramList()
*t = *types.NewSignature(nil, params, result, isddd)
return t
case interfaceTag:
// Create a dummy entry in the type list. This is safe because we
// cannot expect the interface type to appear in a cycle, as any
// such cycle must contain a named type which would have been
// first defined earlier.
n := len(p.typList)
p.record(nil)
// no embedded interfaces with gc compiler
if p.int() != 0 {
panic("unexpected embedded interface")
}
// read methods
methods := make([]*types.Func, p.int())
for i := range methods {
pkg, name := p.fieldName()
params, isddd := p.paramList()
result, _ := p.paramList()
sig := types.NewSignature(nil, params, result, isddd)
methods[i] = types.NewFunc(token.NoPos, pkg, name, sig)
}
t := types.NewInterface(methods, nil)
p.typList[n] = t
return t
case mapTag:
t := new(types.Map)
p.record(t)
key := p.typ()
val := p.typ()
*t = *types.NewMap(key, val)
return t
case chanTag:
t := new(types.Chan)
p.record(t)
var dir types.ChanDir
// tag values must match the constants in cmd/compile/internal/gc/go.go
switch d := p.int(); d {
case 1 /* Crecv */ :
dir = types.RecvOnly
case 2 /* Csend */ :
dir = types.SendOnly
case 3 /* Cboth */ :
dir = types.SendRecv
default:
panic(fmt.Sprintf("unexpected channel dir %d", d))
}
val := p.typ()
*t = *types.NewChan(dir, val)
return t
default:
panic(fmt.Sprintf("unexpected type tag %d", i))
}
}
func (p *importer) field() *types.Var {
pkg, name := p.fieldName()
typ := p.typ()
anonymous := false
if name == "" {
// anonymous field - typ must be T or *T and T must be a type name
switch typ := deref(typ).(type) {
case *types.Basic: // basic types are named types
name = typ.Name()
case *types.Named:
pkg = p.pkgList[0]
name = typ.Obj().Name()
default:
panic("anonymous field expected")
}
anonymous = true
}
return types.NewField(token.NoPos, pkg, name, typ, anonymous)
}
func (p *importer) fieldName() (*types.Package, string) {
name := p.string()
if name == "" {
return nil, "" // anonymous field
}
pkg := p.pkgList[0]
if name == "?" || name != "_" && !exported(name) {
if name == "?" {
name = ""
}
pkg = p.pkg()
}
return pkg, name
}
func (p *importer) paramList() (*types.Tuple, bool) {
n := p.int()
if n == 0 {
return nil, false
}
// negative length indicates unnamed parameters
named := true
if n < 0 {
n = -n
named = false
}
// n > 0
params := make([]*types.Var, n)
isddd := false
for i := range params {
params[i], isddd = p.param(named)
}
return types.NewTuple(params...), isddd
}
func (p *importer) param(named bool) (*types.Var, bool) {
t := p.typ()
td, isddd := t.(*dddSlice)
if isddd {
t = types.NewSlice(td.elem)
}
var name string
if named {
name = p.string()
if name == "" {
panic("expected named parameter")
}
}
// read and discard compiler-specific info
p.string()
return types.NewVar(token.NoPos, nil, name, t), isddd
}
func exported(name string) bool {
ch, _ := utf8.DecodeRuneInString(name)
return unicode.IsUpper(ch)
}
func (p *importer) value() constant.Value {
switch kind := constant.Kind(p.int()); kind {
case falseTag:
return constant.MakeBool(false)
case trueTag:
return constant.MakeBool(true)
case int64Tag:
return constant.MakeInt64(p.int64())
case floatTag:
return p.float()
case complexTag:
re := p.float()
im := p.float()
return constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
case stringTag:
return constant.MakeString(p.string())
default:
panic(fmt.Sprintf("unexpected value kind %d", kind))
}
}
func (p *importer) float() constant.Value {
sign := p.int()
if sign == 0 {
return constant.MakeInt64(0)
}
exp := p.int()
mant := []byte(p.string()) // big endian
// remove leading 0's if any
for len(mant) > 0 && mant[0] == 0 {
mant = mant[1:]
}
// convert to little endian
// TODO(gri) go/constant should have a more direct conversion function
// (e.g., once it supports a big.Float based implementation)
for i, j := 0, len(mant)-1; i < j; i, j = i+1, j-1 {
mant[i], mant[j] = mant[j], mant[i]
}
// adjust exponent (constant.MakeFromBytes creates an integer value,
// but mant represents the mantissa bits such that 0.5 <= mant < 1.0)
exp -= len(mant) << 3
if len(mant) > 0 {
for msd := mant[len(mant)-1]; msd&0x80 == 0; msd <<= 1 {
exp++
}
}
x := constant.MakeFromBytes(mant)
switch {
case exp < 0:
d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp))
x = constant.BinaryOp(x, token.QUO, d)
case exp > 0:
x = constant.Shift(x, token.SHL, uint(exp))
}
if sign < 0 {
x = constant.UnaryOp(token.SUB, x, 0)
}
return x
}
// ----------------------------------------------------------------------------
// Low-level decoders
func (p *importer) tagOrIndex() int {
if p.debugFormat {
p.marker('t')
}
return int(p.rawInt64())
}
func (p *importer) int() int {
return int(p.int64())
}
func (p *importer) int64() int64 {
if p.debugFormat {
p.marker('i')
}
return p.rawInt64()
}
func (p *importer) string() string {
if p.debugFormat {
p.marker('s')
}
var b []byte
if n := int(p.rawInt64()); n > 0 {
b = p.data[:n]
p.data = p.data[n:]
p.read += n
}
return string(b)
}
func (p *importer) marker(want byte) {
if got := p.data[0]; got != want {
panic(fmt.Sprintf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read))
}
p.data = p.data[1:]
p.read++
pos := p.read
if n := int(p.rawInt64()); n != pos {
panic(fmt.Sprintf("incorrect position: got %d; want %d", n, pos))
}
}
// rawInt64 should only be used by low-level decoders
func (p *importer) rawInt64() int64 {
i, n := binary.Varint(p.data)
p.data = p.data[n:]
p.read += n
return i
}
// ----------------------------------------------------------------------------
// Export format
// Tags. Must be < 0.
const (
// Packages
packageTag = -(iota + 1)
// Types
namedTag
arrayTag
sliceTag
dddTag
structTag
pointerTag
signatureTag
interfaceTag
mapTag
chanTag
// Values
falseTag
trueTag
int64Tag
floatTag
fractionTag // not used by gc
complexTag
stringTag
)
var predeclared = []types.Type{
// basic types
types.Typ[types.Bool],
types.Typ[types.Int],
types.Typ[types.Int8],
types.Typ[types.Int16],
types.Typ[types.Int32],
types.Typ[types.Int64],
types.Typ[types.Uint],
types.Typ[types.Uint8],
types.Typ[types.Uint16],
types.Typ[types.Uint32],
types.Typ[types.Uint64],
types.Typ[types.Uintptr],
types.Typ[types.Float32],
types.Typ[types.Float64],
types.Typ[types.Complex64],
types.Typ[types.Complex128],
types.Typ[types.String],
// aliases
types.Universe.Lookup("byte").Type(),
types.Universe.Lookup("rune").Type(),
// error
types.Universe.Lookup("error").Type(),
// untyped types
types.Typ[types.UntypedBool],
types.Typ[types.UntypedInt],
types.Typ[types.UntypedRune],
types.Typ[types.UntypedFloat],
types.Typ[types.UntypedComplex],
types.Typ[types.UntypedString],
types.Typ[types.UntypedNil],
// package unsafe
types.Typ[types.UnsafePointer],
}

View File

@ -39,14 +39,16 @@ func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
// FindExportData positions the reader r at the beginning of the
// export data section of an underlying GC-created object/archive
// file by reading from it. The reader must be positioned at the
// start of the file before calling this function.
// start of the file before calling this function. The hdr result
// is the string before the export data, either "$$" or "$$B".
//
func FindExportData(r *bufio.Reader) (err error) {
func FindExportData(r *bufio.Reader) (hdr string, err error) {
// Read first line to make sure this is an object file.
line, err := r.ReadSlice('\n')
if err != nil {
return
}
if string(line) == "!<arch>\n" {
// Archive file. Scan to __.PKGDEF.
var name string
@ -97,12 +99,13 @@ func FindExportData(r *bufio.Reader) (err error) {
}
// Skip over object header to export data.
// Begins after first line with $$.
// Begins after first line starting with $$.
for line[0] != '$' {
if line, err = r.ReadSlice('\n'); err != nil {
return
}
}
hdr = string(line)
return
}

View File

@ -12,6 +12,7 @@ import (
"go/build"
"go/token"
"io"
"io/ioutil"
"os"
"path/filepath"
"sort"
@ -149,12 +150,25 @@ func Import(packages map[string]*types.Package, path string) (pkg *types.Package
}
}()
var hdr string
buf := bufio.NewReader(f)
if err = FindExportData(buf); err != nil {
if hdr, err = FindExportData(buf); err != nil {
return
}
pkg, err = ImportData(packages, filename, id, buf)
switch hdr {
case "$$\n":
return ImportData(packages, filename, id, buf)
case "$$B\n":
var data []byte
data, err = ioutil.ReadAll(buf)
if err == nil {
_, pkg, err = BImportData(packages, data, path)
return
}
default:
err = fmt.Errorf("unknown export data header: %q", hdr)
}
return
}

View File

@ -46,13 +46,23 @@ func compile(t *testing.T, dirname, filename string) string {
return filepath.Join(dirname, filename[:len(filename)-2]+"o")
}
// Use the same global imports map for all tests. The effect is
// as if all tested packages were imported into a single package.
var imports = make(map[string]*types.Package)
// TODO(gri) Remove this function once we switched to new export format by default.
func compileNewExport(t *testing.T, dirname, filename string) string {
testenv.MustHaveGoBuild(t)
cmd := exec.Command("go", "tool", "compile", "-newexport", filename)
cmd.Dir = dirname
out, err := cmd.CombinedOutput()
if err != nil {
t.Logf("%s", out)
t.Fatalf("go tool compile %s failed: %s", filename, err)
}
// filename should end with ".go"
return filepath.Join(dirname, filename[:len(filename)-2]+"o")
}
func testPath(t *testing.T, path string) *types.Package {
t0 := time.Now()
pkg, err := Import(imports, path)
pkg, err := Import(make(map[string]*types.Package), path)
if err != nil {
t.Errorf("testPath(%s): %s", path, err)
return nil
@ -92,7 +102,7 @@ func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
return
}
func TestImport(t *testing.T) {
func TestImportTestdata(t *testing.T) {
// This package only handles gc export data.
if runtime.Compiler != "gc" {
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
@ -103,20 +113,58 @@ func TestImport(t *testing.T) {
defer os.Remove(outFn)
}
nimports := 0
if pkg := testPath(t, "./testdata/exports"); pkg != nil {
nimports++
// The package's Imports should include all the types
// referenced by the exportdata, which may be more than
// the import statements in the package's source, but
// fewer than the transitive closure of dependencies.
want := `[package ast ("go/ast") package token ("go/token") package runtime ("runtime")]`
// The package's Imports list must include all packages
// explicitly imported by exports.go, plus all packages
// referenced indirectly via exported objects in exports.go.
// With the textual export format, the list may also include
// additional packages that are not strictly required for
// import processing alone (they are exported to err "on
// the safe side").
got := fmt.Sprint(pkg.Imports())
for _, want := range []string{"go/ast", "go/token"} {
if !strings.Contains(got, want) {
t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
}
}
}
}
// TODO(gri) Remove this function once we switched to new export format by default
// (and update the comment and want list in TestImportTestdata).
func TestImportTestdataNewExport(t *testing.T) {
// This package only handles gc export data.
if runtime.Compiler != "gc" {
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
return
}
if outFn := compileNewExport(t, "testdata", "exports.go"); outFn != "" {
defer os.Remove(outFn)
}
if pkg := testPath(t, "./testdata/exports"); pkg != nil {
// The package's Imports list must include all packages
// explicitly imported by exports.go, plus all packages
// referenced indirectly via exported objects in exports.go.
want := `[package ast ("go/ast") package token ("go/token")]`
got := fmt.Sprint(pkg.Imports())
if got != want {
t.Errorf(`Package("exports").Imports() = %s, want %s`, got, want)
}
}
nimports += testDir(t, "", time.Now().Add(maxTime)) // installed packages
}
func TestImportStdLib(t *testing.T) {
skipSpecialPlatforms(t)
// This package only handles gc export data.
if runtime.Compiler != "gc" {
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
return
}
nimports := testDir(t, "", time.Now().Add(maxTime)) // installed packages
t.Logf("tested %d imports", nimports)
}
@ -148,7 +196,7 @@ func TestImportedTypes(t *testing.T) {
importPath := s[0]
objName := s[1]
pkg, err := Import(imports, importPath)
pkg, err := Import(make(map[string]*types.Package), importPath)
if err != nil {
t.Error(err)
continue