// 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
}