go/src/cmd/compile/internal/gc/bimport.go
Matthew Dempsky bcc0247331 cmd/compile: avoid double export of aliased objects
Instead of writing out the original object for each alias, ensure we
export the original object before any aliases. This allows the aliases
to simply refer back to the original object by qualified name.

Fixes #17636.

Change-Id: If80fa8c66b8fee8344a00b55d25a8aef22abd859
Reviewed-on: https://go-review.googlesource.com/32575
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
2016-11-02 19:19:36 +00:00

1303 lines
28 KiB
Go

// 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"
"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
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 4:
// ...
// fallthrough
case 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:
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%L (in %q)\n\t%L (in %q)", pt.Sym, pt, pt.Sym.Importdef.Path, t, 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 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 := functypefield(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)
}
}
case aliasTag:
p.pos()
alias := importpkg.Lookup(p.string())
orig := p.qualifiedName()
// Although the protocol allows the alias to precede the original,
// this never happens in files produced by gc.
alias.Flags |= SymAlias
alias.Def = orig.Def
importsym(alias, orig.Def.Op)
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
}
// importtype declares that pt, an imported named type, has underlying type t.
func (p *importer) importtype(pt, t *Type) {
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.
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 *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, importpkg.Path)
}
}
if Debug['E'] != 0 {
fmt.Printf("import type %v %L\n", pt, 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:
// 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()
p.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()
// 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()
base := recv[0].Type
star := false
if base.IsPtr() {
base = base.Elem()
star = true
}
n := methodname0(sym, star, base.Sym)
n.Type = functypefield(recv[0], params, result)
checkwidth(n.Type)
addmethod(sym, n.Type, 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)
t.SetFields(p.fieldList())
checkwidth(t)
case pointerTag:
t = p.newtyp(Tptr)
t.Extra = PtrType{Elem: p.typ()}
case signatureTag:
t = p.newtyp(TFUNC)
params := p.paramList()
result := p.paramList()
functypefield0(t, nil, params, result)
case interfaceTag:
t = p.newtyp(TINTER)
if p.int() != 0 {
formatErrorf("unexpected embedded interface")
}
t.SetFields(p.methodList())
checkwidth(t)
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 []*Field) {
if n := p.int(); n > 0 {
fields = make([]*Field, n)
for i := range fields {
fields[i] = p.field()
}
}
return
}
// parser.go:hidden_structdcl
func (p *importer) field() *Field {
p.pos()
sym := p.fieldName()
typ := p.typ()
note := p.string()
f := 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
}
f.Sym = sym
f.Nname = newname(sym)
f.Type = typ
f.Note = note
return f
}
// parser.go:hidden_interfacedcl_list
func (p *importer) methodList() (methods []*Field) {
if n := p.int(); n > 0 {
methods = make([]*Field, n)
for i := range methods {
methods[i] = p.method()
}
}
return
}
// parser.go:hidden_interfacedcl
func (p *importer) method() *Field {
p.pos()
sym := p.fieldName()
params := p.paramList()
result := p.paramList()
f := newField()
f.Sym = sym
f.Nname = newname(sym)
f.Type = functypefield(fakethisfield(), params, result)
return f
}
// 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() []*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([]*Field, i)
for i := range fs {
fs[i] = p.param(named)
}
return fs
}
// parser.go:hidden_funarg
func (p *importer) param(named bool) *Field {
f := newField()
f.Type = p.typ()
if f.Type.Etype == TDDDFIELD {
// TDDDFIELD indicates wrapped ... slice type
f.Type = typSlice(f.Type.DDDField())
f.Isddd = true
}
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()
}
f.Sym = pkg.Lookup(name)
f.Nname = 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 *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 {
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
}
// 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:
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, OSLICELIT, 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 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 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:
if p.version < 2 {
// versions 0 and 1 exported a bool here but it
// was always false - simply ignore in this case
p.bool()
}
lhs := dclname(p.sym())
typ := typenod(p.typ())
return 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 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 %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() *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
}