| // 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 EType) *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] |
| } |
| for i := 0; i < n; i++ { |
| p.buf[i] = p.byte() |
| } |
| 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) |
| } |
| } |
| |
| // 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 |
| } |
| |
| // byte is the bottleneck interface for reading from p.in. |
| // It unescapes '|' 'S' to '$' and '|' '|' to '|'. |
| func (p *importer) byte() byte { |
| c := obj.Bgetc(p.in) |
| p.read++ |
| if c < 0 { |
| Fatalf("read error") |
| } |
| if c == '|' { |
| c = obj.Bgetc(p.in) |
| p.read++ |
| if c < 0 { |
| Fatalf("read error") |
| } |
| switch c { |
| case 'S': |
| c = '$' |
| case '|': |
| // nothing to do |
| default: |
| Fatalf("unexpected escape sequence in export data") |
| } |
| } |
| return byte(c) |
| } |