blob: 0a51ab037fe6ab22bdcf80b551fd765a378c5871 [file] [log] [blame]
// 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.
// Based loosely on x/tools/go/importer.
package gc
import (
"bufio"
"cmd/compile/internal/big"
"encoding/binary"
"fmt"
)
// 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.
// Import populates importpkg from the serialized package data.
func Import(in *bufio.Reader) {
p := importer{in: in}
p.buf = p.bufarray[:]
// read low-level encoding format
switch format := p.byte(); format {
case 'c':
// compact format - nothing to do
case 'd':
p.debugFormat = true
default:
Fatalf("importer: invalid encoding format in export data: got %q; want 'c' or 'd'", format)
}
// --- generic export data ---
if v := p.string(); v != exportVersion {
Fatalf("importer: unknown export data version: %s", v)
}
// populate typList with predeclared "known" types
p.typList = append(p.typList, predeclared()...)
// read package data
p.pkg()
if p.pkgList[0] != importpkg {
Fatalf("importer: imported package not found in pkgList[0]")
}
// read compiler-specific flags
importpkg.Safe = p.string() == "safe"
// defer some type-checking until all types are read in completely
// (parser.go:import_package)
tcok := typecheckok
typecheckok = true
defercheckwidth()
// read consts
for i := p.int(); i > 0; i-- {
sym := p.localname()
typ := p.typ()
val := p.value(typ)
importconst(sym, idealType(typ), nodlit(val))
}
// read vars
for i := p.int(); i > 0; i-- {
sym := p.localname()
typ := p.typ()
importvar(sym, typ)
}
// read funcs
for i := p.int(); i > 0; i-- {
// parser.go:hidden_fndcl
sym := p.localname()
params := p.paramList()
result := p.paramList()
inl := p.int()
sig := functype(nil, params, result)
importsym(sym, ONAME)
if sym.Def != nil && sym.Def.Op == ONAME && !Eqtype(sig, sym.Def.Type) {
Fatalf("importer: inconsistent definition for func %v during import\n\t%v\n\t%v", sym, sym.Def.Type, sig)
}
n := newfuncname(sym)
n.Type = sig
declare(n, PFUNC)
funchdr(n)
// parser.go:hidden_import
n.Func.Inl.Set(nil)
if inl >= 0 {
if inl != len(p.inlined) {
panic(fmt.Sprintf("inlined body list inconsistent: %d != %d", inl, len(p.inlined)))
}
p.inlined = append(p.inlined, n.Func)
}
funcbody(n)
importlist = append(importlist, n) // TODO(gri) do this only if body is inlineable?
}
// read types
for i := p.int(); i > 0; i-- {
// name is parsed as part of named type
p.typ()
}
// --- compiler-specific export data ---
// read inlined functions bodies
n := p.int()
for i := 0; i < n; i++ {
body := p.block()
const hookup = false // TODO(gri) enable and remove this condition
if hookup {
p.inlined[i].Inl.Set(body)
}
}
// --- end of export data ---
typecheckok = tcok
resumecheckwidth()
testdclstack() // debugging only
}
func idealType(typ *Type) *Type {
if typ.IsUntyped() {
// canonicalize ideal types
typ = Types[TIDEAL]
}
return typ
}
type importer struct {
in *bufio.Reader
buf []byte // for reading strings
bufarray [64]byte // initial underlying array for buf, large enough to avoid allocation when compiling std lib
pkgList []*Pkg
typList []*Type
inlined []*Func
debugFormat bool
read int // bytes read
}
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 {
Fatalf("importer: 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 == "" {
Fatalf("importer: empty package name in import")
}
// we should never see a bad import path
if isbadimport(path) {
Fatalf("importer: bad path in import: %q", path)
}
// an empty path denotes the package we are currently importing
pkg := importpkg
if path != "" {
pkg = mkpkg(path)
}
if pkg.Name == "" {
pkg.Name = name
} else if pkg.Name != name {
Fatalf("importer: inconsistent package names: got %s; want %s (path = %s)", pkg.Name, name, path)
}
p.pkgList = append(p.pkgList, pkg)
return pkg
}
func (p *importer) localname() *Sym {
// parser.go:hidden_importsym
name := p.string()
if name == "" {
Fatalf("importer: unexpected anonymous name")
}
return importpkg.Lookup(name)
}
func (p *importer) newtyp(etype EType) *Type {
t := typ(etype)
p.typList = append(p.typList, t)
return 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
tsym := p.qualifiedName()
// parser.go:hidden_pkgtype
t = pkgtype(tsym)
importsym(tsym, OTYPE)
p.typList = append(p.typList, t)
// read underlying type
// parser.go:hidden_type
t0 := p.typ()
importtype(t, t0) // parser.go:hidden_import
// interfaces don't have associated methods
if t0.IsInterface() {
break
}
// read associated methods
for i := p.int(); i > 0; i-- {
// parser.go:hidden_fndcl
name := p.string()
recv := p.paramList() // TODO(gri) do we need a full param list for the receiver?
params := p.paramList()
result := p.paramList()
inl := p.int()
pkg := localpkg
if !exportname(name) {
pkg = tsym.Pkg
}
sym := pkg.Lookup(name)
n := methodname1(newname(sym), recv[0].Right)
n.Type = functype(recv[0], params, result)
checkwidth(n.Type)
addmethod(sym, n.Type, tsym.Pkg, false, false)
funchdr(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)
// parser.go:hidden_import
n.Func.Inl.Set(nil)
if inl >= 0 {
if inl != len(p.inlined) {
panic(fmt.Sprintf("inlined body list inconsistent: %d != %d", inl, len(p.inlined)))
}
p.inlined = append(p.inlined, n.Func)
}
funcbody(n)
importlist = append(importlist, n) // TODO(gri) do this only if body is inlineable?
}
case arrayTag, sliceTag:
t = p.newtyp(TARRAY)
if i == arrayTag {
t.SetNumElem(p.int64())
} else {
t.SetNumElem(sliceBound)
}
t.Type = p.typ()
case dddTag:
t = p.newtyp(TDDDFIELD)
t.Type = p.typ()
case structTag:
t = p.newtyp(TSTRUCT)
tostruct0(t, p.fieldList())
case pointerTag:
t = p.newtyp(Tptr)
t.Type = 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 {
Fatalf("importer: unexpected embedded interface")
}
tointerface0(t, p.methodList())
case mapTag:
t = p.newtyp(TMAP)
t.Down = p.typ() // key
t.Type = p.typ() // val
case chanTag:
t = p.newtyp(TCHAN)
t.Chan = uint8(p.int())
t.Type = p.typ()
default:
Fatalf("importer: unexpected type (tag = %d)", i)
}
if t == nil {
Fatalf("importer: 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() []*Node {
i := p.int()
if i == 0 {
return nil
}
n := make([]*Node, i)
for i := range n {
n[i] = p.field()
}
return n
}
// parser.go:hidden_structdcl
func (p *importer) field() *Node {
sym := p.fieldName()
typ := p.typ()
note := p.note()
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.Type.Sym // deref
}
pkg := importpkg
if sym != nil {
pkg = sym.Pkg
}
n = embedded(s, pkg)
n.Right = typenod(typ)
}
n.SetVal(note)
return n
}
func (p *importer) note() (v Val) {
if s := p.string(); s != "" {
v.U = s
}
return
}
// parser.go:hidden_interfacedcl_list
func (p *importer) methodList() []*Node {
i := p.int()
if i == 0 {
return nil
}
n := make([]*Node, i)
for i := range n {
n[i] = p.method()
}
return n
}
// parser.go:hidden_interfacedcl
func (p *importer) method() *Node {
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()
pkg := localpkg
if name == "_" {
// During imports, unqualified non-exported identifiers are from builtinpkg
// (see parser.go:sym). The binary exporter only exports blank as a non-exported
// identifier without qualification.
pkg = builtinpkg
} else if name == "?" || 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.Wrapped())
isddd = true
}
n := Nod(ODCLFIELD, nil, typenod(typ))
n.Isddd = isddd
if named {
name := p.string()
if name == "" {
Fatalf("importer: expected named parameter")
}
// The parameter package doesn't matter; it's never consulted.
// We use the builtinpkg per parser.go:sym (line 1181).
n.Left = newname(builtinpkg.Lookup(name))
}
// TODO(gri) This is compiler-specific (escape info).
// Move into compiler-specific section eventually?
n.SetVal(p.note())
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:
Fatalf("importer: unknown constant (importing package with errors)")
case nilTag:
x.U = new(NilVal)
default:
Fatalf("importer: unexpected value tag %d", tag)
}
// verify ideal type
if typ.IsUntyped() && untype(x.Ctype()) != typ {
Fatalf("importer: 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
func (p *importer) block() []*Node {
markdcl()
// TODO(gri) populate "scope" with function parameters so they can be found
// inside the function body
list := p.nodeList()
popdcl()
return list
}
// parser.go:stmt_list
func (p *importer) nodeList() []*Node {
c := p.int()
s := make([]*Node, c)
for i := range s {
s[i] = p.node()
}
return s
}
func (p *importer) node() *Node {
// TODO(gri) eventually we may need to allocate in each branch
n := Nod(p.op(), nil, nil)
switch n.Op {
// names
case ONAME, OPACK, ONONAME:
name := mkname(p.sym())
// TODO(gri) decide what to do here (this code throws away n)
/*
if name.Op != n.Op {
Fatalf("importer: got node op = %s; want %s", opnames[name.Op], opnames[n.Op])
}
*/
n = name
case OTYPE:
if p.bool() {
n.Sym = p.sym()
} else {
n.Type = p.typ()
}
case OLITERAL:
typ := p.typ()
n.Type = idealType(typ)
n.SetVal(p.value(typ))
// expressions
case OMAKEMAP, OMAKECHAN, OMAKESLICE:
if p.bool() {
n.List.Set(p.nodeList())
}
n.Left, n.Right = p.nodesOrNil()
n.Type = p.typ()
case OPLUS, OMINUS, OADDR, OCOM, OIND, ONOT, ORECV:
n.Left = p.node()
case OADD, OAND, OANDAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE, OLT,
OLSH, OMOD, OMUL, ONE, OOR, OOROR, ORSH, OSEND,
OSUB, OXOR:
n.Left = p.node()
n.Right = p.node()
case OADDSTR:
n.List.Set(p.nodeList())
case OPTRLIT:
n.Left = p.node()
case OSTRUCTLIT:
n.Type = p.typ()
n.List.Set(p.nodeList())
n.Implicit = p.bool()
case OARRAYLIT, OMAPLIT:
n.Type = p.typ()
n.List.Set(p.nodeList())
n.Implicit = p.bool()
case OKEY:
n.Left, n.Right = p.nodesOrNil()
case OCOPY, OCOMPLEX:
n.Left = p.node()
n.Right = p.node()
case OCONV, OCONVIFACE, OCONVNOP, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, ORUNESTR:
// n.Type = p.typ()
// if p.bool() {
// n.Left = p.node()
// } else {
// n.List.Set(p.nodeList())
// }
x := Nod(OCALL, p.typ().Nod, nil)
if p.bool() {
x.List.Set1(p.node())
} else {
x.List.Set(p.nodeList())
}
return x
case ODOT, ODOTPTR, ODOTMETH, ODOTINTER, OXDOT:
// see parser.new_dotname
obj := p.node()
sel := p.sym()
if obj.Op == OPACK {
s := restrictlookup(sel.Name, obj.Name.Pkg)
obj.Used = true
return oldname(s)
}
return NodSym(OXDOT, obj, sel)
case ODOTTYPE, ODOTTYPE2:
n.Left = p.node()
if p.bool() {
n.Right = p.node()
} else {
n.Type = p.typ()
}
case OINDEX, OINDEXMAP, OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR:
n.Left = p.node()
n.Right = p.node()
case OREAL, OIMAG, OAPPEND, OCAP, OCLOSE, ODELETE, OLEN, OMAKE, ONEW, OPANIC,
ORECOVER, OPRINT, OPRINTN:
n.Left, _ = p.nodesOrNil()
n.List.Set(p.nodeList())
n.Isddd = p.bool()
case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OGETG:
n.Left = p.node()
n.List.Set(p.nodeList())
n.Isddd = p.bool()
case OCMPSTR, OCMPIFACE:
n.Left = p.node()
n.Right = p.node()
n.Etype = EType(p.int())
case OPAREN:
n.Left = p.node()
// statements
case ODCL:
n.Left = p.node() // TODO(gri) compare with fmt code
n.Left.Type = p.typ()
case OAS:
n.Left, n.Right = p.nodesOrNil()
n.Colas = p.bool() // TODO(gri) what about complexinit?
case OASOP:
n.Left = p.node()
n.Right = p.node()
n.Etype = EType(p.int())
case OAS2, OASWB:
n.List.Set(p.nodeList())
n.Rlist.Set(p.nodeList())
case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
n.List.Set(p.nodeList())
n.Rlist.Set(p.nodeList())
case ORETURN:
n.List.Set(p.nodeList())
case OPROC, ODEFER:
n.Left = p.node()
case OIF:
n.Ninit.Set(p.nodeList())
n.Left = p.node()
n.Nbody.Set(p.nodeList())
n.Rlist.Set(p.nodeList())
case OFOR:
n.Ninit.Set(p.nodeList())
n.Left, n.Right = p.nodesOrNil()
n.Nbody.Set(p.nodeList())
case ORANGE:
if p.bool() {
n.List.Set(p.nodeList())
}
n.Right = p.node()
n.Nbody.Set(p.nodeList())
case OSELECT, OSWITCH:
n.Ninit.Set(p.nodeList())
n.Left, _ = p.nodesOrNil()
n.List.Set(p.nodeList())
case OCASE, OXCASE:
if p.bool() {
n.List.Set(p.nodeList())
}
n.Nbody.Set(p.nodeList())
case OBREAK, OCONTINUE, OGOTO, OFALL, OXFALL:
n.Left, _ = p.nodesOrNil()
case OEMPTY, ODCLCONST:
// nothing to do
case OLABEL:
n.Left = p.node()
default:
panic(fmt.Sprintf("importer: %s (%d) node not yet supported", opnames[n.Op], n.Op))
}
return n
}
func (p *importer) nodesOrNil() (a, b *Node) {
ab := p.int()
if ab&1 != 0 {
a = p.node()
}
if ab&2 != 0 {
b = p.node()
}
return
}
func (p *importer) sym() *Sym {
return p.fieldName()
}
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 {
Fatalf("importer: 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 n := int(p.rawInt64()); n > 0 {
if cap(p.buf) < n {
p.buf = make([]byte, n)
} else {
p.buf = p.buf[:n]
}
for i := range p.buf {
p.buf[i] = p.byte()
}
return string(p.buf)
}
return ""
}
func (p *importer) marker(want byte) {
if got := p.byte(); got != want {
Fatalf("importer: incorrect marker: got %c; want %c (pos = %d)", got, want, p.read)
}
pos := p.read
if n := int(p.rawInt64()); n != pos {
Fatalf("importer: 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 {
Fatalf("importer: read error: %v", err)
}
return i
}
// needed for binary.ReadVarint in rawInt64
func (p *importer) ReadByte() (byte, error) {
return p.byte(), nil
}
// byte is the bottleneck interface for reading from p.in.
// It unescapes '|' 'S' to '$' and '|' '|' to '|'.
func (p *importer) byte() byte {
c, err := p.in.ReadByte()
p.read++
if err != nil {
Fatalf("importer: read error: %v", err)
}
if c == '|' {
c, err = p.in.ReadByte()
p.read++
if err != nil {
Fatalf("importer: read error: %v", err)
}
switch c {
case 'S':
c = '$'
case '|':
// nothing to do
default:
Fatalf("importer: unexpected escape sequence in export data")
}
}
return c
}