// 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. // See bexport.go for the export data format and how // to make a format change. package gc import ( "bufio" "cmd/compile/internal/types" "cmd/internal/src" "encoding/binary" "fmt" "math/big" "strconv" "strings" ) // 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. type importer struct { in *bufio.Reader imp *types.Pkg // imported package buf []byte // reused for reading strings version int // export format version // object lists, in order of deserialization strList []string pathList []string pkgList []*types.Pkg typList []*types.Type funcList []*Node // nil entry means already declared trackAllTypes bool // for delayed type verification cmpList []struct{ pt, t *types.Type } // position encoding posInfoFormat bool prevFile string prevLine int posBase *src.PosBase // debugging support debugFormat bool read int // bytes read } // Import populates imp from the serialized package data read from in. func Import(imp *types.Pkg, in *bufio.Reader) { inimport = true defer func() { inimport = false }() p := importer{ in: in, imp: imp, version: -1, // unknown version strList: []string{""}, // empty string is mapped to 0 pathList: []string{""}, // empty path is mapped to 0 } // read version info var versionstr string if b := p.rawByte(); b == 'c' || b == 'd' { // Go1.7 encoding; first byte encodes low-level // encoding format (compact vs debug). // For backward-compatibility only (avoid problems with // old installed packages). Newly compiled packages use // the extensible format string. // TODO(gri) Remove this support eventually; after Go1.8. if b == 'd' { p.debugFormat = true } p.trackAllTypes = p.rawByte() == 'a' p.posInfoFormat = p.bool() versionstr = p.string() if versionstr == "v1" { p.version = 0 } } else { // Go1.8 extensible encoding // read version string and extract version number (ignore anything after the version number) versionstr = p.rawStringln(b) if s := strings.SplitN(versionstr, " ", 3); len(s) >= 2 && s[0] == "version" { if v, err := strconv.Atoi(s[1]); err == nil && v > 0 { p.version = v } } } // read version specific flags - extend as necessary switch p.version { // case 6: // ... // fallthrough case 5, 4, 3, 2, 1: p.debugFormat = p.rawStringln(p.rawByte()) == "debug" p.trackAllTypes = p.bool() p.posInfoFormat = p.bool() case 0: // Go1.7 encoding format - nothing to do here default: p.formatErrorf("unknown export format version %d (%q)", p.version, versionstr) } // --- generic export data --- // populate typList with predeclared "known" types p.typList = append(p.typList, predeclared()...) // read package data p.pkg() // defer some type-checking until all types are read in completely tcok := typecheckok typecheckok = true defercheckwidth() // read objects // phase 1 objcount := 0 for { tag := p.tagOrIndex() if tag == endTag { break } p.obj(tag) objcount++ } // self-verification if count := p.int(); count != objcount { p.formatErrorf("got %d objects; want %d", objcount, count) } // --- compiler-specific export data --- // read compiler-specific flags // phase 2 objcount = 0 for { tag := p.tagOrIndex() if tag == endTag { break } p.obj(tag) objcount++ } // self-verification if count := p.int(); count != objcount { p.formatErrorf("got %d objects; want %d", objcount, count) } // read inlineable functions bodies if dclcontext != PEXTERN { p.formatErrorf("unexpected context %d", dclcontext) } objcount = 0 for i0 := -1; ; { i := p.int() // index of function with inlineable body if i < 0 { break } // don't process the same function twice if i <= i0 { p.formatErrorf("index not increasing: %d <= %d", i, i0) } i0 = i if funcdepth != 0 { p.formatErrorf("unexpected Funcdepth %d", funcdepth) } // Note: In the original code, funchdr and funcbody are called for // all functions (that were not yet imported). Now, we are calling // them only for functions with inlineable bodies. funchdr does // parameter renaming which doesn't matter if we don't have a body. inlCost := p.int() if f := p.funcList[i]; f != nil && f.Func.Inl.Len() == 0 { // function not yet imported - read body and set it funchdr(f) body := p.stmtList() if body == nil { // Make sure empty body is not interpreted as // no inlineable body (see also parser.fnbody) // (not doing so can cause significant performance // degradation due to unnecessary calls to empty // functions). body = []*Node{nod(OEMPTY, nil, nil)} } f.Func.Inl.Set(body) f.Func.InlCost = int32(inlCost) if Debug['E'] > 0 && Debug['m'] > 2 && f.Func.Inl.Len() != 0 { if Debug['m'] > 3 { fmt.Printf("inl body for %v: %+v\n", f, f.Func.Inl) } else { fmt.Printf("inl body for %v: %v\n", f, f.Func.Inl) } } funcbody() } else { // function already imported - read body but discard declarations dclcontext = PDISCARD // throw away any declarations p.stmtList() dclcontext = PEXTERN } objcount++ } // self-verification if count := p.int(); count != objcount { p.formatErrorf("got %d functions; want %d", objcount, count) } if dclcontext != PEXTERN { p.formatErrorf("unexpected context %d", dclcontext) } p.verifyTypes() // --- end of export data --- typecheckok = tcok resumecheckwidth() if debug_dclstack != 0 { testdclstack() } } func (p *importer) formatErrorf(format string, args ...interface{}) { if debugFormat { Fatalf(format, args...) } yyerror("cannot import %q due to version skew - reinstall package (%s)", p.imp.Path, fmt.Sprintf(format, args...)) errorexit() } func (p *importer) verifyTypes() { for _, pair := range p.cmpList { pt := pair.pt t := pair.t if !eqtype(pt.Orig, t) { p.formatErrorf("inconsistent definition for type %v during import\n\t%L (in %q)\n\t%L (in %q)", pt.Sym, pt, pt.Sym.Importdef.Path, t, p.imp.Path) } } } // numImport tracks how often a package with a given name is imported. // It is used to provide a better error message (by using the package // path to disambiguate) if a package that appears multiple times with // the same name appears in an error message. var numImport = make(map[string]int) func (p *importer) pkg() *types.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 { p.formatErrorf("expected package tag, found tag = %d", i) } // read package data name := p.string() var path string if p.version >= 5 { path = p.path() } else { path = p.string() } // we should never see an empty package name if name == "" { p.formatErrorf("empty package name for path %q", path) } // we should never see a bad import path if isbadimport(path, true) { p.formatErrorf("bad package path %q for package %s", path, name) } // an empty path denotes the package we are currently importing; // it must be the first package we see if (path == "") != (len(p.pkgList) == 0) { p.formatErrorf("package path %q for pkg index %d", path, len(p.pkgList)) } // add package to pkgList pkg := p.imp if path != "" { pkg = types.NewPkg(path, "") } if pkg.Name == "" { pkg.Name = name numImport[name]++ } else if pkg.Name != name { yyerror("conflicting package names %s and %s for path %q", pkg.Name, name, path) } if myimportpath != "" && path == myimportpath { yyerror("import %q: package depends on %q (import cycle)", p.imp.Path, path) errorexit() } p.pkgList = append(p.pkgList, pkg) return pkg } func idealType(typ *types.Type) *types.Type { if typ.IsUntyped() { // canonicalize ideal types typ = types.Types[TIDEAL] } return typ } func (p *importer) obj(tag int) { switch tag { case constTag: pos := p.pos() sym := p.qualifiedName() typ := p.typ() val := p.value(typ) importconst(p.imp, sym, idealType(typ), npos(pos, nodlit(val))) case aliasTag: pos := p.pos() sym := p.qualifiedName() typ := p.typ() importalias(pos, p.imp, sym, typ) case typeTag: p.typ() case varTag: pos := p.pos() sym := p.qualifiedName() typ := p.typ() importvar(pos, p.imp, sym, typ) case funcTag: pos := p.pos() sym := p.qualifiedName() params := p.paramList() result := p.paramList() sig := functypefield(nil, params, result) importsym(p.imp, sym, ONAME) if old := asNode(sym.Def); old != nil && old.Op == ONAME { // function was imported before (via another import) if !eqtype(sig, old.Type) { p.formatErrorf("inconsistent definition for func %v during import\n\t%v\n\t%v", sym, old.Type, sig) } n := asNode(old.Type.Nname()) p.funcList = append(p.funcList, n) break } n := newfuncnamel(pos, sym) n.Type = sig // TODO(mdempsky): Stop clobbering n.Pos in declare. savedlineno := lineno lineno = pos declare(n, PFUNC) lineno = savedlineno p.funcList = append(p.funcList, n) importlist = append(importlist, n) sig.SetNname(asTypesNode(n)) if Debug['E'] > 0 { fmt.Printf("import [%q] func %v \n", p.imp.Path, n) } default: p.formatErrorf("unexpected object (tag = %d)", tag) } } func (p *importer) pos() src.XPos { if !p.posInfoFormat { return src.NoXPos } file := p.prevFile line := p.prevLine delta := p.int() line += delta if p.version >= 5 { if delta == deltaNewFile { if n := p.int(); n >= 0 { // file changed file = p.path() line = n } } } else { if delta == 0 { if n := p.int(); n >= 0 { // file changed file = p.prevFile[:n] + p.string() line = p.int() } } } if file != p.prevFile { p.prevFile = file p.posBase = src.NewFileBase(file, file) } p.prevLine = line pos := src.MakePos(p.posBase, uint(line), 0) xpos := Ctxt.PosTable.XPos(pos) return xpos } func (p *importer) path() string { // if the path was seen before, i is its index (>= 0) // (the empty string is at index 0) i := p.int() if i >= 0 { return p.pathList[i] } // otherwise, i is the negative path length (< 0) a := make([]string, -i) for n := range a { a[n] = p.string() } s := strings.Join(a, "/") p.pathList = append(p.pathList, s) return s } func (p *importer) newtyp(etype types.EType) *types.Type { t := types.New(etype) if p.trackAllTypes { p.typList = append(p.typList, t) } return t } // importtype declares that pt, an imported named type, has underlying type t. func (p *importer) importtype(pt, t *types.Type) { if pt.Etype == TFORW { copytype(asNode(pt.Nod), t) pt.Sym.Importdef = p.imp pt.Sym.Lastlineno = lineno declare(asNode(pt.Nod), PEXTERN) checkwidth(pt) } else { // pt.Orig and t must be identical. if p.trackAllTypes { // If we track all types, t may not be fully set up yet. // Collect the types and verify identity later. p.cmpList = append(p.cmpList, struct{ pt, t *types.Type }{pt, t}) } else if !eqtype(pt.Orig, t) { yyerror("inconsistent definition for type %v during import\n\t%L (in %q)\n\t%L (in %q)", pt.Sym, pt, pt.Sym.Importdef.Path, t, p.imp.Path) } } if Debug['E'] != 0 { fmt.Printf("import type %v %L\n", pt, t) } } 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) var t *types.Type switch i { case namedTag: pos := p.pos() tsym := p.qualifiedName() t = pkgtype(pos, p.imp, tsym) p.typList = append(p.typList, t) dup := !t.IsKind(types.TFORW) // type already imported // read underlying type t0 := p.typ() // TODO(mdempsky): Stop clobbering n.Pos in declare. savedlineno := lineno lineno = pos p.importtype(t, t0) lineno = savedlineno // interfaces don't have associated methods if t0.IsInterface() { break } // set correct import context (since p.typ() may be called // while importing the body of an inlined function) savedContext := dclcontext dclcontext = PEXTERN // read associated methods for i := p.int(); i > 0; i-- { mpos := p.pos() sym := p.fieldSym() // during import unexported method names should be in the type's package if !exportname(sym.Name) && sym.Pkg != tsym.Pkg { Fatalf("imported method name %+v in wrong package %s\n", sym, tsym.Pkg.Name) } recv := p.paramList() // TODO(gri) do we need a full param list for the receiver? params := p.paramList() result := p.paramList() nointerface := p.bool() mt := functypefield(recv[0], params, result) oldm := addmethod(sym, mt, false, nointerface) if dup { // An earlier import already declared this type and its methods. // Discard the duplicate method declaration. n := asNode(oldm.Type.Nname()) p.funcList = append(p.funcList, n) continue } n := newfuncnamel(mpos, methodname(sym, recv[0].Type)) n.Type = mt n.SetClass(PFUNC) checkwidth(n.Type) p.funcList = append(p.funcList, n) importlist = append(importlist, n) // (comment from parser.go) // 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. mt.SetNname(asTypesNode(n)) if Debug['E'] > 0 { fmt.Printf("import [%q] meth %v \n", p.imp.Path, n) } } dclcontext = savedContext case arrayTag: t = p.newtyp(TARRAY) bound := p.int64() elem := p.typ() t.Extra = &types.Array{Elem: elem, Bound: bound} case sliceTag: t = p.newtyp(TSLICE) elem := p.typ() t.Extra = types.Slice{Elem: elem} case dddTag: t = p.newtyp(TDDDFIELD) t.Extra = types.DDDField{T: p.typ()} case structTag: t = p.newtyp(TSTRUCT) t.SetFields(p.fieldList()) checkwidth(t) case pointerTag: t = p.newtyp(types.Tptr) t.Extra = types.Ptr{Elem: p.typ()} case signatureTag: t = p.newtyp(TFUNC) params := p.paramList() result := p.paramList() functypefield0(t, nil, params, result) case interfaceTag: if ml := p.methodList(); len(ml) == 0 { t = types.Types[TINTER] } else { t = p.newtyp(TINTER) t.SetInterface(ml) } case mapTag: t = p.newtyp(TMAP) mt := t.MapType() mt.Key = p.typ() mt.Val = p.typ() case chanTag: t = p.newtyp(TCHAN) ct := t.ChanType() ct.Dir = types.ChanDir(p.int()) ct.Elem = p.typ() default: p.formatErrorf("unexpected type (tag = %d)", i) } if t == nil { p.formatErrorf("nil type (type tag = %d)", i) } return t } func (p *importer) qualifiedName() *types.Sym { name := p.string() pkg := p.pkg() return pkg.Lookup(name) } func (p *importer) fieldList() (fields []*types.Field) { if n := p.int(); n > 0 { fields = make([]*types.Field, n) for i := range fields { fields[i] = p.field() } } return } func (p *importer) field() *types.Field { pos := p.pos() sym, alias := p.fieldName() typ := p.typ() note := p.string() f := types.NewField() if sym.Name == "" { // anonymous field: typ must be T or *T and T must be a type name s := typ.Sym if s == nil && typ.IsPtr() { s = typ.Elem().Sym // deref } sym = sym.Pkg.Lookup(s.Name) f.Embedded = 1 } else if alias { // anonymous field: we have an explicit name because it's a type alias f.Embedded = 1 } f.Sym = sym f.Nname = asTypesNode(newnamel(pos, sym)) f.Type = typ f.Note = note return f } func (p *importer) methodList() (methods []*types.Field) { for n := p.int(); n > 0; n-- { f := types.NewField() f.Nname = asTypesNode(newname(nblank.Sym)) asNode(f.Nname).Pos = p.pos() f.Type = p.typ() methods = append(methods, f) } for n := p.int(); n > 0; n-- { methods = append(methods, p.method()) } return } func (p *importer) method() *types.Field { pos := p.pos() sym := p.methodName() params := p.paramList() result := p.paramList() f := types.NewField() f.Sym = sym f.Nname = asTypesNode(newnamel(pos, sym)) f.Type = functypefield(fakeRecvField(), params, result) return f } func (p *importer) fieldName() (*types.Sym, bool) { name := p.string() if p.version == 0 && name == "_" { // version 0 didn't export a package for _ field names // but used the builtin package instead return builtinpkg.Lookup(name), false } pkg := localpkg alias := false switch name { case "": // 1) field name matches base type name and is exported: nothing to do case "?": // 2) field name matches base type name and is not exported: need package name = "" pkg = p.pkg() case "@": // 3) field name doesn't match base type name (alias name): need name and possibly package name = p.string() alias = true fallthrough default: if !exportname(name) { pkg = p.pkg() } } return pkg.Lookup(name), alias } func (p *importer) methodName() *types.Sym { name := p.string() if p.version == 0 && name == "_" { // version 0 didn't export a package for _ method names // but used the builtin package instead return builtinpkg.Lookup(name) } pkg := localpkg if !exportname(name) { pkg = p.pkg() } return pkg.Lookup(name) } func (p *importer) paramList() []*types.Field { i := p.int() if i == 0 { return nil } // negative length indicates unnamed parameters named := true if i < 0 { i = -i named = false } // i > 0 fs := make([]*types.Field, i) for i := range fs { fs[i] = p.param(named) } return fs } func (p *importer) param(named bool) *types.Field { f := types.NewField() f.Type = p.typ() if f.Type.Etype == TDDDFIELD { // TDDDFIELD indicates wrapped ... slice type f.Type = types.NewSlice(f.Type.DDDField()) f.SetIsddd(true) } if named { name := p.string() if name == "" { p.formatErrorf("expected named parameter") } // TODO(gri) Supply function/method package rather than // encoding the package for each parameter repeatedly. pkg := localpkg if name != "_" { pkg = p.pkg() } f.Sym = pkg.Lookup(name) f.Nname = asTypesNode(newname(f.Sym)) } // TODO(gri) This is compiler-specific (escape info). // Move into compiler-specific section eventually? f.Note = p.string() return f } func (p *importer) value(typ *types.Type) (x Val) { switch tag := p.tagOrIndex(); tag { case falseTag: x.U = false case trueTag: x.U = true case int64Tag: u := new(Mpint) u.SetInt64(p.int64()) u.Rune = typ == types.Idealrune x.U = u case floatTag: f := newMpflt() p.float(f) if typ == types.Idealint || typ.IsInteger() { // uncommon case: large int encoded as float u := new(Mpint) u.SetFloat(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() case unknownTag: p.formatErrorf("unknown constant (importing package with errors)") case nilTag: x.U = new(NilVal) default: p.formatErrorf("unexpected value tag %d", tag) } // verify ideal type if typ.IsUntyped() && untype(x.Ctype()) != typ { p.formatErrorf("value %v and type %v don't match", x, typ) } return } func (p *importer) float(x *Mpflt) { sign := p.int() if sign == 0 { x.SetFloat64(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 // Approach: Read nodes and use them to create/declare the same data structures // as done originally by the (hidden) parser by closely following the parser's // original code. In other words, "parsing" the import data (which happens to // be encoded in binary rather textual form) is the best way at the moment to // re-establish the syntax tree's invariants. At some future point we might be // able to avoid this round-about way and create the rewritten nodes directly, // possibly avoiding a lot of duplicate work (name resolution, type checking). // // Refined nodes (e.g., ODOTPTR as a refinement of OXDOT) are exported as their // unrefined nodes (since this is what the importer uses). The respective case // entries are unreachable in the importer. func (p *importer) stmtList() []*Node { var list []*Node for { n := p.node() if n == nil { break } // OBLOCK nodes may be created when importing ODCL nodes - unpack them if n.Op == OBLOCK { list = append(list, n.List.Slice()...) } else { list = append(list, n) } } return list } func (p *importer) exprList() []*Node { var list []*Node for { n := p.expr() if n == nil { break } list = append(list, n) } return list } func (p *importer) elemList() []*Node { c := p.int() list := make([]*Node, c) for i := range list { s := p.fieldSym() list[i] = nodSym(OSTRUCTKEY, p.expr(), s) } return list } func (p *importer) expr() *Node { n := p.node() if n != nil && n.Op == OBLOCK { Fatalf("unexpected block node: %v", n) } return n } func npos(pos src.XPos, n *Node) *Node { n.Pos = pos return n } // TODO(gri) split into expr and stmt func (p *importer) node() *Node { switch op := p.op(); op { // expressions // case OPAREN: // unreachable - unpacked by exporter // case ODDDARG: // unimplemented case OLITERAL: pos := p.pos() typ := p.typ() n := npos(pos, nodlit(p.value(typ))) if !typ.IsUntyped() { // Type-checking simplifies unsafe.Pointer(uintptr(c)) // to unsafe.Pointer(c) which then cannot type-checked // again. Re-introduce explicit uintptr(c) conversion. // (issue 16317). if typ.IsUnsafePtr() { n = nodl(pos, OCONV, n, nil) n.Type = types.Types[TUINTPTR] } n = nodl(pos, OCONV, n, nil) n.Type = typ } return n case ONAME: return npos(p.pos(), mkname(p.sym())) // case OPACK, ONONAME: // unreachable - should have been resolved by typechecking case OTYPE: return npos(p.pos(), typenod(p.typ())) // case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC: // unreachable - should have been resolved by typechecking // case OCLOSURE: // unimplemented case OPTRLIT: pos := p.pos() n := npos(pos, p.expr()) if !p.bool() /* !implicit, i.e. '&' operator */ { if n.Op == OCOMPLIT { // Special case for &T{...}: turn into (*T){...}. n.Right = nodl(pos, OIND, n.Right, nil) n.Right.SetImplicit(true) } else { n = nodl(pos, OADDR, n, nil) } } return n case OSTRUCTLIT: // TODO(mdempsky): Export position information for OSTRUCTKEY nodes. savedlineno := lineno lineno = p.pos() n := nodl(lineno, OCOMPLIT, nil, typenod(p.typ())) n.List.Set(p.elemList()) // special handling of field names lineno = savedlineno return n // case OARRAYLIT, OSLICELIT, OMAPLIT: // unreachable - mapped to case OCOMPLIT below by exporter case OCOMPLIT: n := nodl(p.pos(), OCOMPLIT, nil, typenod(p.typ())) n.List.Set(p.exprList()) return n case OKEY: pos := p.pos() left, right := p.exprsOrNil() return nodl(pos, OKEY, left, right) // case OSTRUCTKEY: // unreachable - handled in case OSTRUCTLIT by elemList // case OCALLPART: // unimplemented // case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH: // unreachable - mapped to case OXDOT below by exporter case OXDOT: // see parser.new_dotname return npos(p.pos(), nodSym(OXDOT, p.expr(), p.fieldSym())) // case ODOTTYPE, ODOTTYPE2: // unreachable - mapped to case ODOTTYPE below by exporter case ODOTTYPE: n := nodl(p.pos(), ODOTTYPE, p.expr(), nil) n.Type = p.typ() return n // case OINDEX, OINDEXMAP, OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR: // unreachable - mapped to cases below by exporter case OINDEX: return nodl(p.pos(), op, p.expr(), p.expr()) case OSLICE, OSLICE3: n := nodl(p.pos(), op, p.expr(), nil) low, high := p.exprsOrNil() var max *Node if n.Op.IsSlice3() { max = p.expr() } n.SetSliceBounds(low, high, max) return n // case OCONV, OCONVIFACE, OCONVNOP, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, ORUNESTR: // unreachable - mapped to OCONV case below by exporter case OCONV: n := nodl(p.pos(), OCONV, p.expr(), nil) n.Type = p.typ() return n case OCOPY, OCOMPLEX, OREAL, OIMAG, OAPPEND, OCAP, OCLOSE, ODELETE, OLEN, OMAKE, ONEW, OPANIC, ORECOVER, OPRINT, OPRINTN: n := npos(p.pos(), builtinCall(op)) n.List.Set(p.exprList()) if op == OAPPEND { n.SetIsddd(p.bool()) } return n // case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OGETG: // unreachable - mapped to OCALL case below by exporter case OCALL: n := nodl(p.pos(), OCALL, p.expr(), nil) n.List.Set(p.exprList()) n.SetIsddd(p.bool()) return n case OMAKEMAP, OMAKECHAN, OMAKESLICE: n := npos(p.pos(), builtinCall(OMAKE)) n.List.Append(typenod(p.typ())) n.List.Append(p.exprList()...) return n // unary expressions case OPLUS, OMINUS, OADDR, OCOM, OIND, ONOT, ORECV: return nodl(p.pos(), op, p.expr(), nil) // binary expressions case OADD, OAND, OANDAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE, OLT, OLSH, OMOD, OMUL, ONE, OOR, OOROR, ORSH, OSEND, OSUB, OXOR: return nodl(p.pos(), op, p.expr(), p.expr()) case OADDSTR: pos := p.pos() list := p.exprList() x := npos(pos, list[0]) for _, y := range list[1:] { x = nodl(pos, OADD, x, y) } return x // case OCMPSTR, OCMPIFACE: // unreachable - mapped to std comparison operators by exporter case ODCLCONST: // TODO(gri) these should not be exported in the first place return nodl(p.pos(), OEMPTY, nil, nil) // -------------------------------------------------------------------- // statements case ODCL: if p.version < 2 { // versions 0 and 1 exported a bool here but it // was always false - simply ignore in this case p.bool() } pos := p.pos() lhs := dclname(p.sym()) typ := typenod(p.typ()) return npos(pos, liststmt(variter([]*Node{lhs}, typ, nil))) // TODO(gri) avoid list creation // case ODCLFIELD: // unimplemented // case OAS, OASWB: // unreachable - mapped to OAS case below by exporter case OAS: return nodl(p.pos(), OAS, p.expr(), p.expr()) case OASOP: n := nodl(p.pos(), OASOP, nil, nil) n.Etype = types.EType(p.int()) n.Left = p.expr() if !p.bool() { n.Right = nodintconst(1) n.SetImplicit(true) } else { n.Right = p.expr() } return n // case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV: // unreachable - mapped to OAS2 case below by exporter case OAS2: n := nodl(p.pos(), OAS2, nil, nil) n.List.Set(p.exprList()) n.Rlist.Set(p.exprList()) return n case ORETURN: n := nodl(p.pos(), ORETURN, nil, nil) n.List.Set(p.exprList()) return n // case ORETJMP: // unreachable - generated by compiler for trampolin routines (not exported) case OPROC, ODEFER: return nodl(p.pos(), op, p.expr(), nil) case OIF: n := nodl(p.pos(), OIF, nil, nil) n.Ninit.Set(p.stmtList()) n.Left = p.expr() n.Nbody.Set(p.stmtList()) n.Rlist.Set(p.stmtList()) return n case OFOR: n := nodl(p.pos(), OFOR, nil, nil) n.Ninit.Set(p.stmtList()) n.Left, n.Right = p.exprsOrNil() n.Nbody.Set(p.stmtList()) return n case ORANGE: n := nodl(p.pos(), ORANGE, nil, nil) n.List.Set(p.stmtList()) n.Right = p.expr() n.Nbody.Set(p.stmtList()) return n case OSELECT, OSWITCH: n := nodl(p.pos(), op, nil, nil) n.Ninit.Set(p.stmtList()) n.Left, _ = p.exprsOrNil() n.List.Set(p.stmtList()) return n // case OCASE, OXCASE: // unreachable - mapped to OXCASE case below by exporter case OXCASE: n := nodl(p.pos(), OXCASE, nil, nil) n.List.Set(p.exprList()) // TODO(gri) eventually we must declare variables for type switch // statements (type switch statements are not yet exported) n.Nbody.Set(p.stmtList()) return n // case OFALL: // unreachable - mapped to OXFALL case below by exporter case OFALL: n := nodl(p.pos(), OFALL, nil, nil) return n case OBREAK, OCONTINUE: pos := p.pos() left, _ := p.exprsOrNil() if left != nil { left = newname(left.Sym) } return nodl(pos, op, left, nil) // case OEMPTY: // unreachable - not emitted by exporter case OGOTO, OLABEL: return nodl(p.pos(), op, newname(p.expr().Sym), nil) case OEND: return nil default: Fatalf("cannot import %v (%d) node\n"+ "==> please file an issue and assign to gri@\n", op, int(op)) panic("unreachable") // satisfy compiler } } func builtinCall(op Op) *Node { return nod(OCALL, mkname(builtinpkg.Lookup(goopnames[op])), nil) } func (p *importer) exprsOrNil() (a, b *Node) { ab := p.int() if ab&1 != 0 { a = p.expr() } if ab&2 != 0 { b = p.expr() } return } func (p *importer) fieldSym() *types.Sym { name := p.string() pkg := localpkg if !exportname(name) { pkg = p.pkg() } return pkg.Lookup(name) } func (p *importer) sym() *types.Sym { name := p.string() pkg := localpkg if name != "_" { pkg = p.pkg() } linkname := p.string() sym := pkg.Lookup(name) sym.Linkname = linkname return sym } func (p *importer) bool() bool { return p.int() != 0 } func (p *importer) op() Op { return Op(p.int()) } // ---------------------------------------------------------------------------- // 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 { p.formatErrorf("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 the string was seen before, i is its index (>= 0) // (the empty string is at index 0) i := p.rawInt64() if i >= 0 { return p.strList[i] } // otherwise, i is the negative string length (< 0) if n := int(-i); n <= cap(p.buf) { p.buf = p.buf[:n] } else { p.buf = make([]byte, n) } for i := range p.buf { p.buf[i] = p.rawByte() } s := string(p.buf) p.strList = append(p.strList, s) return s } func (p *importer) marker(want byte) { if got := p.rawByte(); got != want { p.formatErrorf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read) } pos := p.read if n := int(p.rawInt64()); n != pos { p.formatErrorf("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 { p.formatErrorf("read error: %v", err) } return i } // rawStringln should only be used to read the initial version string. func (p *importer) rawStringln(b byte) string { p.buf = p.buf[:0] for b != '\n' { p.buf = append(p.buf, b) b = p.rawByte() } return string(p.buf) } // needed for binary.ReadVarint in rawInt64 func (p *importer) ReadByte() (byte, error) { return p.rawByte(), nil } // rawByte is the bottleneck interface for reading from p.in. // It unescapes '|' 'S' to '$' and '|' '|' to '|'. // rawByte should only be used by low-level decoders. func (p *importer) rawByte() byte { c, err := p.in.ReadByte() p.read++ if err != nil { p.formatErrorf("read error: %v", err) } if c == '|' { c, err = p.in.ReadByte() p.read++ if err != nil { p.formatErrorf("read error: %v", err) } switch c { case 'S': c = '$' case '|': // nothing to do default: p.formatErrorf("unexpected escape sequence in export data") } } return c }