|  | // 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/big" | 
|  | "encoding/binary" | 
|  | "fmt" | 
|  | "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 | 
|  | buf     []byte // reused for reading strings | 
|  | version int    // export format version | 
|  |  | 
|  | // object lists, in order of deserialization | 
|  | strList       []string | 
|  | pkgList       []*Pkg | 
|  | typList       []*Type | 
|  | funcList      []*Node // nil entry means already declared | 
|  | trackAllTypes bool | 
|  |  | 
|  | // for delayed type verification | 
|  | cmpList []struct{ pt, t *Type } | 
|  |  | 
|  | // position encoding | 
|  | posInfoFormat bool | 
|  | prevFile      string | 
|  | prevLine      int | 
|  |  | 
|  | // debugging support | 
|  | debugFormat bool | 
|  | read        int // bytes read | 
|  | } | 
|  |  | 
|  | // Import populates importpkg from the serialized package data. | 
|  | func Import(in *bufio.Reader) { | 
|  | p := importer{ | 
|  | in:      in, | 
|  | version: -1,           // unknown version | 
|  | strList: []string{""}, // empty string 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 2: | 
|  | // 	... | 
|  | //	fallthrough | 
|  | case 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: | 
|  | 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 { | 
|  | 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 { | 
|  | formatErrorf("got %d objects; want %d", objcount, count) | 
|  | } | 
|  |  | 
|  | // read inlineable functions bodies | 
|  | if dclcontext != PEXTERN { | 
|  | 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 { | 
|  | formatErrorf("index not increasing: %d <= %d", i, i0) | 
|  | } | 
|  | i0 = i | 
|  |  | 
|  | if Funcdepth != 0 { | 
|  | 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. | 
|  |  | 
|  | if f := p.funcList[i]; f != nil { | 
|  | // 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) | 
|  | funcbody(f) | 
|  | } 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 { | 
|  | formatErrorf("got %d functions; want %d", objcount, count) | 
|  | } | 
|  |  | 
|  | if dclcontext != PEXTERN { | 
|  | formatErrorf("unexpected context %d", dclcontext) | 
|  | } | 
|  |  | 
|  | p.verifyTypes() | 
|  |  | 
|  | // --- end of export data --- | 
|  |  | 
|  | typecheckok = tcok | 
|  | resumecheckwidth() | 
|  |  | 
|  | testdclstack() // debugging only | 
|  | } | 
|  |  | 
|  | func formatErrorf(format string, args ...interface{}) { | 
|  | if debugFormat { | 
|  | Fatalf(format, args...) | 
|  | } | 
|  |  | 
|  | Yyerror("cannot import %q due to version skew - reinstall package (%s)", | 
|  | importpkg.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) { | 
|  | formatErrorf("inconsistent definition for type %v during import\n\t%v (in %q)\n\t%v (in %q)", pt.Sym, Tconv(pt, FmtLong), pt.Sym.Importdef.Path, Tconv(t, FmtLong), importpkg.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() *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 { | 
|  | formatErrorf("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 == "" { | 
|  | formatErrorf("empty package name for path %q", path) | 
|  | } | 
|  |  | 
|  | // we should never see a bad import path | 
|  | if isbadimport(path) { | 
|  | 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) { | 
|  | formatErrorf("package path %q for pkg index %d", path, len(p.pkgList)) | 
|  | } | 
|  |  | 
|  | // add package to pkgList | 
|  | pkg := importpkg | 
|  | if path != "" { | 
|  | pkg = mkpkg(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 incannedimport == 0 && myimportpath != "" && path == myimportpath { | 
|  | Yyerror("import %q: package depends on %q (import cycle)", importpkg.Path, path) | 
|  | errorexit() | 
|  | } | 
|  | p.pkgList = append(p.pkgList, pkg) | 
|  |  | 
|  | return pkg | 
|  | } | 
|  |  | 
|  | func idealType(typ *Type) *Type { | 
|  | if typ.IsUntyped() { | 
|  | // canonicalize ideal types | 
|  | typ = Types[TIDEAL] | 
|  | } | 
|  | return typ | 
|  | } | 
|  |  | 
|  | func (p *importer) obj(tag int) { | 
|  | switch tag { | 
|  | case constTag: | 
|  | p.pos() | 
|  | sym := p.qualifiedName() | 
|  | typ := p.typ() | 
|  | val := p.value(typ) | 
|  | importconst(sym, idealType(typ), nodlit(val)) | 
|  |  | 
|  | case typeTag: | 
|  | p.typ() | 
|  |  | 
|  | case varTag: | 
|  | p.pos() | 
|  | sym := p.qualifiedName() | 
|  | typ := p.typ() | 
|  | importvar(sym, typ) | 
|  |  | 
|  | case funcTag: | 
|  | p.pos() | 
|  | sym := p.qualifiedName() | 
|  | params := p.paramList() | 
|  | result := p.paramList() | 
|  |  | 
|  | sig := functype(nil, params, result) | 
|  | importsym(sym, ONAME) | 
|  | if sym.Def != nil && sym.Def.Op == ONAME { | 
|  | // function was imported before (via another import) | 
|  | if !Eqtype(sig, sym.Def.Type) { | 
|  | formatErrorf("inconsistent definition for func %v during import\n\t%v\n\t%v", sym, sym.Def.Type, sig) | 
|  | } | 
|  | p.funcList = append(p.funcList, nil) | 
|  | break | 
|  | } | 
|  |  | 
|  | n := newfuncname(sym) | 
|  | n.Type = sig | 
|  | declare(n, PFUNC) | 
|  | p.funcList = append(p.funcList, n) | 
|  | importlist = append(importlist, n) | 
|  |  | 
|  | if Debug['E'] > 0 { | 
|  | fmt.Printf("import [%q] func %v \n", importpkg.Path, n) | 
|  | if Debug['m'] > 2 && n.Func.Inl.Len() != 0 { | 
|  | fmt.Printf("inl body: %v\n", n.Func.Inl) | 
|  | } | 
|  | } | 
|  |  | 
|  | default: | 
|  | formatErrorf("unexpected object (tag = %d)", tag) | 
|  | } | 
|  | } | 
|  |  | 
|  | func (p *importer) pos() { | 
|  | if !p.posInfoFormat { | 
|  | return | 
|  | } | 
|  |  | 
|  | file := p.prevFile | 
|  | line := p.prevLine | 
|  | if delta := p.int(); delta != 0 { | 
|  | // line changed | 
|  | line += delta | 
|  | } else if n := p.int(); n >= 0 { | 
|  | // file changed | 
|  | file = p.prevFile[:n] + p.string() | 
|  | p.prevFile = file | 
|  | line = p.int() | 
|  | } | 
|  | p.prevLine = line | 
|  |  | 
|  | // TODO(gri) register new position | 
|  | } | 
|  |  | 
|  | func (p *importer) newtyp(etype EType) *Type { | 
|  | t := typ(etype) | 
|  | if p.trackAllTypes { | 
|  | p.typList = append(p.typList, t) | 
|  | } | 
|  | return t | 
|  | } | 
|  |  | 
|  | // This is like the function importtype but it delays the | 
|  | // type identity check for types that have been seen already. | 
|  | // importer.importtype and importtype and (export.go) need to | 
|  | // remain in sync. | 
|  | func (p *importer) importtype(pt, t *Type) { | 
|  | // override declaration in unsafe.go for Pointer. | 
|  | // there is no way in Go code to define unsafe.Pointer | 
|  | // so we have to supply it. | 
|  | if incannedimport != 0 && importpkg.Name == "unsafe" && pt.Nod.Sym.Name == "Pointer" { | 
|  | t = Types[TUNSAFEPTR] | 
|  | } | 
|  |  | 
|  | if pt.Etype == TFORW { | 
|  | n := pt.Nod | 
|  | copytype(pt.Nod, t) | 
|  | pt.Nod = n // unzero nod | 
|  | pt.Sym.Importdef = importpkg | 
|  | pt.Sym.Lastlineno = lineno | 
|  | declare(n, PEXTERN) | 
|  | checkwidth(pt) | 
|  | } else { | 
|  | // pt.Orig and t must be identical. Since t may not be | 
|  | // fully set up yet, collect the types and verify identity | 
|  | // later. | 
|  | p.cmpList = append(p.cmpList, struct{ pt, t *Type }{pt, t}) | 
|  | } | 
|  |  | 
|  | if Debug['E'] != 0 { | 
|  | fmt.Printf("import type %v %v\n", pt, Tconv(t, FmtLong)) | 
|  | } | 
|  | } | 
|  |  | 
|  | 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: | 
|  | // parser.go:hidden_importsym | 
|  | p.pos() | 
|  | tsym := p.qualifiedName() | 
|  |  | 
|  | // parser.go:hidden_pkgtype | 
|  | t = pkgtype(tsym) | 
|  | p.typList = append(p.typList, t) | 
|  |  | 
|  | // read underlying type | 
|  | // parser.go:hidden_type | 
|  | t0 := p.typ() | 
|  | if p.trackAllTypes { | 
|  | // If we track all types, we cannot check equality of previously | 
|  | // imported types until later. Use customized version of importtype. | 
|  | p.importtype(t, t0) | 
|  | } else { | 
|  | importtype(t, t0) | 
|  | } | 
|  |  | 
|  | // 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-- { | 
|  | // parser.go:hidden_fndcl | 
|  |  | 
|  | p.pos() | 
|  | sym := p.fieldSym() | 
|  |  | 
|  | recv := p.paramList() // TODO(gri) do we need a full param list for the receiver? | 
|  | params := p.paramList() | 
|  | result := p.paramList() | 
|  | nointerface := p.bool() | 
|  |  | 
|  | n := methodname(newname(sym), recv[0].Right) | 
|  | n.Type = functype(recv[0], params, result) | 
|  | checkwidth(n.Type) | 
|  | addmethod(sym, n.Type, tsym.Pkg, false, nointerface) | 
|  | 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. | 
|  | n.Type.SetNname(n) | 
|  |  | 
|  | if Debug['E'] > 0 { | 
|  | fmt.Printf("import [%q] meth %v \n", importpkg.Path, n) | 
|  | if Debug['m'] > 2 && n.Func.Inl.Len() != 0 { | 
|  | fmt.Printf("inl body: %v\n", n.Func.Inl) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | dclcontext = savedContext | 
|  |  | 
|  | case arrayTag: | 
|  | t = p.newtyp(TARRAY) | 
|  | bound := p.int64() | 
|  | elem := p.typ() | 
|  | t.Extra = &ArrayType{Elem: elem, Bound: bound} | 
|  |  | 
|  | case sliceTag: | 
|  | t = p.newtyp(TSLICE) | 
|  | elem := p.typ() | 
|  | t.Extra = SliceType{Elem: elem} | 
|  |  | 
|  | case dddTag: | 
|  | t = p.newtyp(TDDDFIELD) | 
|  | t.Extra = DDDFieldType{T: p.typ()} | 
|  |  | 
|  | case structTag: | 
|  | t = p.newtyp(TSTRUCT) | 
|  | tostruct0(t, p.fieldList()) | 
|  |  | 
|  | case pointerTag: | 
|  | t = p.newtyp(Tptr) | 
|  | t.Extra = PtrType{Elem: 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 { | 
|  | formatErrorf("unexpected embedded interface") | 
|  | } | 
|  | tointerface0(t, p.methodList()) | 
|  |  | 
|  | 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 = ChanDir(p.int()) | 
|  | ct.Elem = p.typ() | 
|  |  | 
|  | default: | 
|  | formatErrorf("unexpected type (tag = %d)", i) | 
|  | } | 
|  |  | 
|  | if t == nil { | 
|  | formatErrorf("nil type (type tag = %d)", i) | 
|  | } | 
|  |  | 
|  | return t | 
|  | } | 
|  |  | 
|  | func (p *importer) qualifiedName() *Sym { | 
|  | name := p.string() | 
|  | pkg := p.pkg() | 
|  | return pkg.Lookup(name) | 
|  | } | 
|  |  | 
|  | // parser.go:hidden_structdcl_list | 
|  | func (p *importer) fieldList() (fields []*Node) { | 
|  | if n := p.int(); n > 0 { | 
|  | fields = make([]*Node, n) | 
|  | for i := range fields { | 
|  | fields[i] = p.field() | 
|  | } | 
|  | } | 
|  | return | 
|  | } | 
|  |  | 
|  | // parser.go:hidden_structdcl | 
|  | func (p *importer) field() *Node { | 
|  | p.pos() | 
|  | sym := p.fieldName() | 
|  | typ := p.typ() | 
|  | note := p.string() | 
|  |  | 
|  | 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 && typ.IsPtr() { | 
|  | s = typ.Elem().Sym // deref | 
|  | } | 
|  | pkg := importpkg | 
|  | if sym != nil { | 
|  | pkg = sym.Pkg | 
|  | } | 
|  | n = embedded(s, pkg) | 
|  | n.Right = typenod(typ) | 
|  | } | 
|  | n.SetVal(Val{U: note}) | 
|  |  | 
|  | return n | 
|  | } | 
|  |  | 
|  | // parser.go:hidden_interfacedcl_list | 
|  | func (p *importer) methodList() (methods []*Node) { | 
|  | if n := p.int(); n > 0 { | 
|  | methods = make([]*Node, n) | 
|  | for i := range methods { | 
|  | methods[i] = p.method() | 
|  | } | 
|  | } | 
|  | return | 
|  | } | 
|  |  | 
|  | // parser.go:hidden_interfacedcl | 
|  | func (p *importer) method() *Node { | 
|  | p.pos() | 
|  | sym := p.fieldName() | 
|  | params := p.paramList() | 
|  | result := p.paramList() | 
|  | return Nod(ODCLFIELD, newname(sym), typenod(functype(fakethis(), params, result))) | 
|  | } | 
|  |  | 
|  | // parser.go:sym,hidden_importsym | 
|  | func (p *importer) fieldName() *Sym { | 
|  | name := p.string() | 
|  | if p.version == 0 && name == "_" { | 
|  | // version 0 didn't export a package for _ fields | 
|  | // but used the builtin package instead | 
|  | return builtinpkg.Lookup(name) | 
|  | } | 
|  | pkg := localpkg | 
|  | if name != "" && !exportname(name) { | 
|  | if name == "?" { | 
|  | name = "" | 
|  | } | 
|  | pkg = p.pkg() | 
|  | } | 
|  | return pkg.Lookup(name) | 
|  | } | 
|  |  | 
|  | // parser.go:ohidden_funarg_list | 
|  | func (p *importer) paramList() []*Node { | 
|  | 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 := make([]*Node, i) | 
|  | for i := range n { | 
|  | n[i] = p.param(named) | 
|  | } | 
|  | return n | 
|  | } | 
|  |  | 
|  | // parser.go:hidden_funarg | 
|  | func (p *importer) param(named bool) *Node { | 
|  | typ := p.typ() | 
|  |  | 
|  | isddd := false | 
|  | if typ.Etype == TDDDFIELD { | 
|  | // TDDDFIELD indicates wrapped ... slice type | 
|  | typ = typSlice(typ.DDDField()) | 
|  | isddd = true | 
|  | } | 
|  |  | 
|  | n := Nod(ODCLFIELD, nil, typenod(typ)) | 
|  | n.Isddd = isddd | 
|  |  | 
|  | if named { | 
|  | name := p.string() | 
|  | if name == "" { | 
|  | 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() | 
|  | } | 
|  | n.Left = newname(pkg.Lookup(name)) | 
|  | } | 
|  |  | 
|  | // TODO(gri) This is compiler-specific (escape info). | 
|  | // Move into compiler-specific section eventually? | 
|  | n.SetVal(Val{U: p.string()}) | 
|  |  | 
|  | 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) | 
|  | u.SetInt64(p.int64()) | 
|  | u.Rune = typ == idealrune | 
|  | x.U = u | 
|  |  | 
|  | case floatTag: | 
|  | f := newMpflt() | 
|  | p.float(f) | 
|  | if typ == 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: | 
|  | formatErrorf("unknown constant (importing package with errors)") | 
|  |  | 
|  | case nilTag: | 
|  | x.U = new(NilVal) | 
|  |  | 
|  | default: | 
|  | formatErrorf("unexpected value tag %d", tag) | 
|  | } | 
|  |  | 
|  | // verify ideal type | 
|  | if typ.IsUntyped() && untype(x.Ctype()) != typ { | 
|  | 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 { | 
|  | list[i] = Nod(OKEY, mkname(p.fieldSym()), p.expr()) | 
|  | } | 
|  | return list | 
|  | } | 
|  |  | 
|  | func (p *importer) expr() *Node { | 
|  | n := p.node() | 
|  | if n != nil && n.Op == OBLOCK { | 
|  | Fatalf("unexpected block node: %v", n) | 
|  | } | 
|  | 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 OREGISTER: | 
|  | //	unimplemented | 
|  |  | 
|  | case OLITERAL: | 
|  | typ := p.typ() | 
|  | n := 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() { | 
|  | conv := Nod(OCALL, typenod(Types[TUINTPTR]), nil) | 
|  | conv.List.Set1(n) | 
|  | n = conv | 
|  | } | 
|  | conv := Nod(OCALL, typenod(typ), nil) | 
|  | conv.List.Set1(n) | 
|  | n = conv | 
|  | } | 
|  | return n | 
|  |  | 
|  | case ONAME: | 
|  | return mkname(p.sym()) | 
|  |  | 
|  | // case OPACK, ONONAME: | 
|  | // 	unreachable - should have been resolved by typechecking | 
|  |  | 
|  | case OTYPE: | 
|  | if p.bool() { | 
|  | return mkname(p.sym()) | 
|  | } | 
|  | return typenod(p.typ()) | 
|  |  | 
|  | // case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC: | 
|  | //      unreachable - should have been resolved by typechecking | 
|  |  | 
|  | // case OCLOSURE: | 
|  | //	unimplemented | 
|  |  | 
|  | case OPTRLIT: | 
|  | n := p.expr() | 
|  | if !p.bool() /* !implicit, i.e. '&' operator */ { | 
|  | if n.Op == OCOMPLIT { | 
|  | // Special case for &T{...}: turn into (*T){...}. | 
|  | n.Right = Nod(OIND, n.Right, nil) | 
|  | n.Right.Implicit = true | 
|  | } else { | 
|  | n = Nod(OADDR, n, nil) | 
|  | } | 
|  | } | 
|  | return n | 
|  |  | 
|  | case OSTRUCTLIT: | 
|  | n := Nod(OCOMPLIT, nil, typenod(p.typ())) | 
|  | n.List.Set(p.elemList()) // special handling of field names | 
|  | return n | 
|  |  | 
|  | // case OARRAYLIT, OMAPLIT: | 
|  | // 	unreachable - mapped to case OCOMPLIT below by exporter | 
|  |  | 
|  | case OCOMPLIT: | 
|  | n := Nod(OCOMPLIT, nil, typenod(p.typ())) | 
|  | n.List.Set(p.exprList()) | 
|  | return n | 
|  |  | 
|  | case OKEY: | 
|  | left, right := p.exprsOrNil() | 
|  | return Nod(OKEY, left, right) | 
|  |  | 
|  | // case OCALLPART: | 
|  | //	unimplemented | 
|  |  | 
|  | // case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH: | 
|  | // 	unreachable - mapped to case OXDOT below by exporter | 
|  |  | 
|  | case OXDOT: | 
|  | // see parser.new_dotname | 
|  | return NodSym(OXDOT, p.expr(), p.fieldSym()) | 
|  |  | 
|  | // case ODOTTYPE, ODOTTYPE2: | 
|  | // 	unreachable - mapped to case ODOTTYPE below by exporter | 
|  |  | 
|  | case ODOTTYPE: | 
|  | n := Nod(ODOTTYPE, p.expr(), nil) | 
|  | if p.bool() { | 
|  | n.Right = p.expr() | 
|  | } else { | 
|  | n.Right = typenod(p.typ()) | 
|  | } | 
|  | return n | 
|  |  | 
|  | // case OINDEX, OINDEXMAP, OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR: | 
|  | // 	unreachable - mapped to cases below by exporter | 
|  |  | 
|  | case OINDEX: | 
|  | return Nod(op, p.expr(), p.expr()) | 
|  |  | 
|  | case OSLICE, OSLICE3: | 
|  | n := Nod(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 := Nod(OCALL, typenod(p.typ()), nil) | 
|  | n.List.Set(p.exprList()) | 
|  | return n | 
|  |  | 
|  | case OCOPY, OCOMPLEX, OREAL, OIMAG, OAPPEND, OCAP, OCLOSE, ODELETE, OLEN, OMAKE, ONEW, OPANIC, ORECOVER, OPRINT, OPRINTN: | 
|  | n := builtinCall(op) | 
|  | n.List.Set(p.exprList()) | 
|  | if op == OAPPEND { | 
|  | n.Isddd = p.bool() | 
|  | } | 
|  | return n | 
|  |  | 
|  | // case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OGETG: | 
|  | // 	unreachable - mapped to OCALL case below by exporter | 
|  |  | 
|  | case OCALL: | 
|  | n := Nod(OCALL, p.expr(), nil) | 
|  | n.List.Set(p.exprList()) | 
|  | n.Isddd = p.bool() | 
|  | return n | 
|  |  | 
|  | case OMAKEMAP, OMAKECHAN, OMAKESLICE: | 
|  | n := 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 Nod(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 Nod(op, p.expr(), p.expr()) | 
|  |  | 
|  | case OADDSTR: | 
|  | list := p.exprList() | 
|  | x := list[0] | 
|  | for _, y := range list[1:] { | 
|  | x = Nod(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 Nod(OEMPTY, nil, nil) | 
|  |  | 
|  | // -------------------------------------------------------------------- | 
|  | // statements | 
|  | case ODCL: | 
|  | var lhs *Node | 
|  | if p.bool() { | 
|  | lhs = p.expr() | 
|  | } else { | 
|  | lhs = dclname(p.sym()) | 
|  | } | 
|  | // TODO(gri) avoid list created here! | 
|  | return liststmt(variter([]*Node{lhs}, typenod(p.typ()), nil)) | 
|  |  | 
|  | // case ODCLFIELD: | 
|  | //	unimplemented | 
|  |  | 
|  | // case OAS, OASWB: | 
|  | // 	unreachable - mapped to OAS case below by exporter | 
|  |  | 
|  | case OAS: | 
|  | return Nod(OAS, p.expr(), p.expr()) | 
|  |  | 
|  | case OASOP: | 
|  | n := Nod(OASOP, nil, nil) | 
|  | n.Etype = EType(p.int()) | 
|  | n.Left = p.expr() | 
|  | if !p.bool() { | 
|  | n.Right = Nodintconst(1) | 
|  | n.Implicit = true | 
|  | } else { | 
|  | n.Right = p.expr() | 
|  | } | 
|  | return n | 
|  |  | 
|  | // case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV: | 
|  | // 	unreachable - mapped to OAS2 case below by exporter | 
|  |  | 
|  | case OAS2: | 
|  | n := Nod(OAS2, nil, nil) | 
|  | n.List.Set(p.exprList()) | 
|  | n.Rlist.Set(p.exprList()) | 
|  | return n | 
|  |  | 
|  | case ORETURN: | 
|  | n := Nod(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 Nod(op, p.expr(), nil) | 
|  |  | 
|  | case OIF: | 
|  | markdcl() | 
|  | n := Nod(OIF, nil, nil) | 
|  | n.Ninit.Set(p.stmtList()) | 
|  | n.Left = p.expr() | 
|  | n.Nbody.Set(p.stmtList()) | 
|  | n.Rlist.Set(p.stmtList()) | 
|  | popdcl() | 
|  | return n | 
|  |  | 
|  | case OFOR: | 
|  | markdcl() | 
|  | n := Nod(OFOR, nil, nil) | 
|  | n.Ninit.Set(p.stmtList()) | 
|  | n.Left, n.Right = p.exprsOrNil() | 
|  | n.Nbody.Set(p.stmtList()) | 
|  | popdcl() | 
|  | return n | 
|  |  | 
|  | case ORANGE: | 
|  | markdcl() | 
|  | n := Nod(ORANGE, nil, nil) | 
|  | n.List.Set(p.stmtList()) | 
|  | n.Right = p.expr() | 
|  | n.Nbody.Set(p.stmtList()) | 
|  | popdcl() | 
|  | return n | 
|  |  | 
|  | case OSELECT, OSWITCH: | 
|  | markdcl() | 
|  | n := Nod(op, nil, nil) | 
|  | n.Ninit.Set(p.stmtList()) | 
|  | n.Left, _ = p.exprsOrNil() | 
|  | n.List.Set(p.stmtList()) | 
|  | popdcl() | 
|  | return n | 
|  |  | 
|  | // case OCASE, OXCASE: | 
|  | // 	unreachable - mapped to OXCASE case below by exporter | 
|  |  | 
|  | case OXCASE: | 
|  | markdcl() | 
|  | n := Nod(OXCASE, nil, nil) | 
|  | n.Xoffset = int64(block) | 
|  | 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()) | 
|  | popdcl() | 
|  | return n | 
|  |  | 
|  | // case OFALL: | 
|  | // 	unreachable - mapped to OXFALL case below by exporter | 
|  |  | 
|  | case OXFALL: | 
|  | n := Nod(OXFALL, nil, nil) | 
|  | n.Xoffset = int64(block) | 
|  | return n | 
|  |  | 
|  | case OBREAK, OCONTINUE: | 
|  | left, _ := p.exprsOrNil() | 
|  | if left != nil { | 
|  | left = newname(left.Sym) | 
|  | } | 
|  | return Nod(op, left, nil) | 
|  |  | 
|  | // case OEMPTY: | 
|  | // 	unreachable - not emitted by exporter | 
|  |  | 
|  | case OGOTO, OLABEL: | 
|  | n := Nod(op, newname(p.expr().Sym), nil) | 
|  | n.Sym = dclstack // context, for goto restrictions | 
|  | return n | 
|  |  | 
|  | case OEND: | 
|  | return nil | 
|  |  | 
|  | default: | 
|  | Fatalf("cannot import %s (%d) node\n"+ | 
|  | "==> please file an issue and assign to gri@\n", op, 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() *Sym { | 
|  | name := p.string() | 
|  | pkg := localpkg | 
|  | if !exportname(name) { | 
|  | pkg = p.pkg() | 
|  | } | 
|  | return pkg.Lookup(name) | 
|  | } | 
|  |  | 
|  | func (p *importer) sym() *Sym { | 
|  | name := p.string() | 
|  | pkg := localpkg | 
|  | if name != "_" { | 
|  | pkg = p.pkg() | 
|  | } | 
|  | return pkg.Lookup(name) | 
|  | } | 
|  |  | 
|  | 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 { | 
|  | 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 { | 
|  | formatErrorf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read) | 
|  | } | 
|  |  | 
|  | pos := p.read | 
|  | if n := int(p.rawInt64()); n != pos { | 
|  | 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 { | 
|  | 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 { | 
|  | formatErrorf("read error: %v", err) | 
|  | } | 
|  | if c == '|' { | 
|  | c, err = p.in.ReadByte() | 
|  | p.read++ | 
|  | if err != nil { | 
|  | formatErrorf("read error: %v", err) | 
|  | } | 
|  | switch c { | 
|  | case 'S': | 
|  | c = '$' | 
|  | case '|': | 
|  | // nothing to do | 
|  | default: | 
|  | formatErrorf("unexpected escape sequence in export data") | 
|  | } | 
|  | } | 
|  | return c | 
|  | } |