mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Previously, we used OKEY nodes to represent keyed struct literal elements. The field names were represented by an ONAME node, but this is clumsy because it's the only remaining case where ONAME was used to represent a bare identifier and not a variable. This CL introduces a new OSTRUCTKEY node op for use in struct literals. These ops instead store the field name in the node's own Sym field. This is similar in spirit to golang.org/cl/20890. Significant reduction in allocations for struct literal heavy code like package unicode: name old time/op new time/op delta Template 345ms ± 6% 341ms ± 6% ~ (p=0.141 n=29+28) Unicode 200ms ± 9% 184ms ± 7% -7.77% (p=0.000 n=29+30) GoTypes 1.04s ± 3% 1.05s ± 3% ~ (p=0.096 n=30+30) Compiler 4.47s ± 9% 4.49s ± 6% ~ (p=0.890 n=29+29) name old user-ns/op new user-ns/op delta Template 523M ±13% 516M ±17% ~ (p=0.400 n=29+30) Unicode 334M ±27% 314M ±30% ~ (p=0.093 n=30+30) GoTypes 1.53G ±10% 1.52G ±10% ~ (p=0.572 n=30+30) Compiler 6.28G ± 7% 6.34G ±11% ~ (p=0.300 n=30+30) name old alloc/op new alloc/op delta Template 44.5MB ± 0% 44.4MB ± 0% -0.35% (p=0.000 n=27+30) Unicode 39.2MB ± 0% 34.5MB ± 0% -11.79% (p=0.000 n=26+30) GoTypes 125MB ± 0% 125MB ± 0% -0.12% (p=0.000 n=29+30) Compiler 515MB ± 0% 515MB ± 0% -0.10% (p=0.000 n=29+30) name old allocs/op new allocs/op delta Template 426k ± 0% 424k ± 0% -0.39% (p=0.000 n=29+30) Unicode 374k ± 0% 323k ± 0% -13.67% (p=0.000 n=29+30) GoTypes 1.21M ± 0% 1.21M ± 0% -0.14% (p=0.000 n=29+29) Compiler 4.40M ± 0% 4.39M ± 0% -0.13% (p=0.000 n=29+30) Passes toolstash/buildall. Change-Id: Iba4ee765dd1748f67e52fcade1cd75c9f6e13fa9 Reviewed-on: https://go-review.googlesource.com/30974 Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
1286 lines
28 KiB
Go
1286 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"
|
|
"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 3:
|
|
// ...
|
|
// fallthrough
|
|
case 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 := 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
|
|
}
|
|
|
|
// 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()
|
|
|
|
n := methodname(newname(sym), recv[0].Right)
|
|
n.Type = functype(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)
|
|
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 {
|
|
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 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, 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
|
|
}
|