aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/typecheck/iimport.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/typecheck/iimport.go')
-rw-r--r--src/cmd/compile/internal/typecheck/iimport.go1142
1 files changed, 1142 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go
new file mode 100644
index 0000000000..ab43d4f71b
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/iimport.go
@@ -0,0 +1,1142 @@
+// Copyright 2018 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.
+
+// Indexed package import.
+// See iexport.go for the export data format.
+
+package typecheck
+
+import (
+ "encoding/binary"
+ "fmt"
+ "go/constant"
+ "io"
+ "math/big"
+ "os"
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
+ "cmd/internal/bio"
+ "cmd/internal/goobj"
+ "cmd/internal/obj"
+ "cmd/internal/src"
+)
+
+// An iimporterAndOffset identifies an importer and an offset within
+// its data section.
+type iimporterAndOffset struct {
+ p *iimporter
+ off uint64
+}
+
+var (
+ // DeclImporter maps from imported identifiers to an importer
+ // and offset where that identifier's declaration can be read.
+ DeclImporter = map[*types.Sym]iimporterAndOffset{}
+
+ // inlineImporter is like declImporter, but for inline bodies
+ // for function and method symbols.
+ inlineImporter = map[*types.Sym]iimporterAndOffset{}
+)
+
+func expandDecl(n ir.Node) ir.Node {
+ if n, ok := n.(*ir.Name); ok {
+ return n
+ }
+
+ id := n.(*ir.Ident)
+ if n := id.Sym().PkgDef(); n != nil {
+ return n.(*ir.Name)
+ }
+
+ r := importReaderFor(id.Sym(), DeclImporter)
+ if r == nil {
+ // Can happen if user tries to reference an undeclared name.
+ return n
+ }
+
+ return r.doDecl(n.Sym())
+}
+
+func ImportBody(fn *ir.Func) {
+ if fn.Inl.Body != nil {
+ return
+ }
+
+ r := importReaderFor(fn.Nname.Sym(), inlineImporter)
+ if r == nil {
+ base.Fatalf("missing import reader for %v", fn)
+ }
+
+ r.doInline(fn)
+}
+
+func importReaderFor(sym *types.Sym, importers map[*types.Sym]iimporterAndOffset) *importReader {
+ x, ok := importers[sym]
+ if !ok {
+ return nil
+ }
+
+ return x.p.newReader(x.off, sym.Pkg)
+}
+
+type intReader struct {
+ *bio.Reader
+ pkg *types.Pkg
+}
+
+func (r *intReader) int64() int64 {
+ i, err := binary.ReadVarint(r.Reader)
+ if err != nil {
+ base.Errorf("import %q: read error: %v", r.pkg.Path, err)
+ base.ErrorExit()
+ }
+ return i
+}
+
+func (r *intReader) uint64() uint64 {
+ i, err := binary.ReadUvarint(r.Reader)
+ if err != nil {
+ base.Errorf("import %q: read error: %v", r.pkg.Path, err)
+ base.ErrorExit()
+ }
+ return i
+}
+
+func ReadImports(pkg *types.Pkg, in *bio.Reader) (fingerprint goobj.FingerprintType) {
+ ird := &intReader{in, pkg}
+
+ version := ird.uint64()
+ if version != iexportVersion {
+ base.Errorf("import %q: unknown export format version %d", pkg.Path, version)
+ base.ErrorExit()
+ }
+
+ sLen := ird.uint64()
+ dLen := ird.uint64()
+
+ // Map string (and data) section into memory as a single large
+ // string. This reduces heap fragmentation and allows
+ // returning individual substrings very efficiently.
+ data, err := mapFile(in.File(), in.Offset(), int64(sLen+dLen))
+ if err != nil {
+ base.Errorf("import %q: mapping input: %v", pkg.Path, err)
+ base.ErrorExit()
+ }
+ stringData := data[:sLen]
+ declData := data[sLen:]
+
+ in.MustSeek(int64(sLen+dLen), os.SEEK_CUR)
+
+ p := &iimporter{
+ ipkg: pkg,
+
+ pkgCache: map[uint64]*types.Pkg{},
+ posBaseCache: map[uint64]*src.PosBase{},
+ typCache: map[uint64]*types.Type{},
+
+ stringData: stringData,
+ declData: declData,
+ }
+
+ for i, pt := range predeclared() {
+ p.typCache[uint64(i)] = pt
+ }
+
+ // Declaration index.
+ for nPkgs := ird.uint64(); nPkgs > 0; nPkgs-- {
+ pkg := p.pkgAt(ird.uint64())
+ pkgName := p.stringAt(ird.uint64())
+ pkgHeight := int(ird.uint64())
+ if pkg.Name == "" {
+ pkg.Name = pkgName
+ pkg.Height = pkgHeight
+ types.NumImport[pkgName]++
+
+ // TODO(mdempsky): This belongs somewhere else.
+ pkg.Lookup("_").Def = ir.BlankNode
+ } else {
+ if pkg.Name != pkgName {
+ base.Fatalf("conflicting package names %v and %v for path %q", pkg.Name, pkgName, pkg.Path)
+ }
+ if pkg.Height != pkgHeight {
+ base.Fatalf("conflicting package heights %v and %v for path %q", pkg.Height, pkgHeight, pkg.Path)
+ }
+ }
+
+ for nSyms := ird.uint64(); nSyms > 0; nSyms-- {
+ s := pkg.Lookup(p.stringAt(ird.uint64()))
+ off := ird.uint64()
+
+ if _, ok := DeclImporter[s]; !ok {
+ DeclImporter[s] = iimporterAndOffset{p, off}
+ }
+ }
+ }
+
+ // Inline body index.
+ for nPkgs := ird.uint64(); nPkgs > 0; nPkgs-- {
+ pkg := p.pkgAt(ird.uint64())
+
+ for nSyms := ird.uint64(); nSyms > 0; nSyms-- {
+ s := pkg.Lookup(p.stringAt(ird.uint64()))
+ off := ird.uint64()
+
+ if _, ok := inlineImporter[s]; !ok {
+ inlineImporter[s] = iimporterAndOffset{p, off}
+ }
+ }
+ }
+
+ // Fingerprint.
+ _, err = io.ReadFull(in, fingerprint[:])
+ if err != nil {
+ base.Errorf("import %s: error reading fingerprint", pkg.Path)
+ base.ErrorExit()
+ }
+ return fingerprint
+}
+
+type iimporter struct {
+ ipkg *types.Pkg
+
+ pkgCache map[uint64]*types.Pkg
+ posBaseCache map[uint64]*src.PosBase
+ typCache map[uint64]*types.Type
+
+ stringData string
+ declData string
+}
+
+func (p *iimporter) stringAt(off uint64) string {
+ var x [binary.MaxVarintLen64]byte
+ n := copy(x[:], p.stringData[off:])
+
+ slen, n := binary.Uvarint(x[:n])
+ if n <= 0 {
+ base.Fatalf("varint failed")
+ }
+ spos := off + uint64(n)
+ return p.stringData[spos : spos+slen]
+}
+
+func (p *iimporter) posBaseAt(off uint64) *src.PosBase {
+ if posBase, ok := p.posBaseCache[off]; ok {
+ return posBase
+ }
+
+ file := p.stringAt(off)
+ posBase := src.NewFileBase(file, file)
+ p.posBaseCache[off] = posBase
+ return posBase
+}
+
+func (p *iimporter) pkgAt(off uint64) *types.Pkg {
+ if pkg, ok := p.pkgCache[off]; ok {
+ return pkg
+ }
+
+ pkg := p.ipkg
+ if pkgPath := p.stringAt(off); pkgPath != "" {
+ pkg = types.NewPkg(pkgPath, "")
+ }
+ p.pkgCache[off] = pkg
+ return pkg
+}
+
+// An importReader keeps state for reading an individual imported
+// object (declaration or inline body).
+type importReader struct {
+ strings.Reader
+ p *iimporter
+
+ currPkg *types.Pkg
+ prevBase *src.PosBase
+ prevLine int64
+ prevColumn int64
+}
+
+func (p *iimporter) newReader(off uint64, pkg *types.Pkg) *importReader {
+ r := &importReader{
+ p: p,
+ currPkg: pkg,
+ }
+ // (*strings.Reader).Reset wasn't added until Go 1.7, and we
+ // need to build with Go 1.4.
+ r.Reader = *strings.NewReader(p.declData[off:])
+ return r
+}
+
+func (r *importReader) string() string { return r.p.stringAt(r.uint64()) }
+func (r *importReader) posBase() *src.PosBase { return r.p.posBaseAt(r.uint64()) }
+func (r *importReader) pkg() *types.Pkg { return r.p.pkgAt(r.uint64()) }
+
+func (r *importReader) setPkg() {
+ r.currPkg = r.pkg()
+}
+
+func (r *importReader) doDecl(sym *types.Sym) *ir.Name {
+ tag := r.byte()
+ pos := r.pos()
+
+ switch tag {
+ case 'A':
+ typ := r.typ()
+
+ return importalias(r.p.ipkg, pos, sym, typ)
+
+ case 'C':
+ typ := r.typ()
+ val := r.value(typ)
+
+ return importconst(r.p.ipkg, pos, sym, typ, val)
+
+ case 'F':
+ typ := r.signature(nil)
+
+ n := importfunc(r.p.ipkg, pos, sym, typ)
+ r.funcExt(n)
+ return n
+
+ case 'T':
+ // Types can be recursive. We need to setup a stub
+ // declaration before recursing.
+ n := importtype(r.p.ipkg, pos, sym)
+ t := n.Type()
+
+ // We also need to defer width calculations until
+ // after the underlying type has been assigned.
+ types.DeferCheckSize()
+ underlying := r.typ()
+ t.SetUnderlying(underlying)
+ types.ResumeCheckSize()
+
+ if underlying.IsInterface() {
+ r.typeExt(t)
+ return n
+ }
+
+ ms := make([]*types.Field, r.uint64())
+ for i := range ms {
+ mpos := r.pos()
+ msym := r.ident()
+ recv := r.param()
+ mtyp := r.signature(recv)
+
+ fn := ir.NewFunc(mpos)
+ fn.SetType(mtyp)
+ m := ir.NewFuncNameAt(mpos, ir.MethodSym(recv.Type, msym), fn)
+ m.SetType(mtyp)
+ m.Class_ = ir.PFUNC
+ // methodSym already marked m.Sym as a function.
+
+ f := types.NewField(mpos, msym, mtyp)
+ f.Nname = m
+ ms[i] = f
+ }
+ t.Methods().Set(ms)
+
+ r.typeExt(t)
+ for _, m := range ms {
+ r.methExt(m)
+ }
+ return n
+
+ case 'V':
+ typ := r.typ()
+
+ n := importvar(r.p.ipkg, pos, sym, typ)
+ r.varExt(n)
+ return n
+
+ default:
+ base.Fatalf("unexpected tag: %v", tag)
+ panic("unreachable")
+ }
+}
+
+func (p *importReader) value(typ *types.Type) constant.Value {
+ switch constTypeOf(typ) {
+ case constant.Bool:
+ return constant.MakeBool(p.bool())
+ case constant.String:
+ return constant.MakeString(p.string())
+ case constant.Int:
+ var i big.Int
+ p.mpint(&i, typ)
+ return makeInt(&i)
+ case constant.Float:
+ return p.float(typ)
+ case constant.Complex:
+ return makeComplex(p.float(typ), p.float(typ))
+ }
+
+ base.Fatalf("unexpected value type: %v", typ)
+ panic("unreachable")
+}
+
+func (p *importReader) mpint(x *big.Int, typ *types.Type) {
+ signed, maxBytes := intSize(typ)
+
+ maxSmall := 256 - maxBytes
+ if signed {
+ maxSmall = 256 - 2*maxBytes
+ }
+ if maxBytes == 1 {
+ maxSmall = 256
+ }
+
+ n, _ := p.ReadByte()
+ if uint(n) < maxSmall {
+ v := int64(n)
+ if signed {
+ v >>= 1
+ if n&1 != 0 {
+ v = ^v
+ }
+ }
+ x.SetInt64(v)
+ return
+ }
+
+ v := -n
+ if signed {
+ v = -(n &^ 1) >> 1
+ }
+ if v < 1 || uint(v) > maxBytes {
+ base.Fatalf("weird decoding: %v, %v => %v", n, signed, v)
+ }
+ b := make([]byte, v)
+ p.Read(b)
+ x.SetBytes(b)
+ if signed && n&1 != 0 {
+ x.Neg(x)
+ }
+}
+
+func (p *importReader) float(typ *types.Type) constant.Value {
+ var mant big.Int
+ p.mpint(&mant, typ)
+ var f big.Float
+ f.SetInt(&mant)
+ if f.Sign() != 0 {
+ f.SetMantExp(&f, int(p.int64()))
+ }
+ return constant.Make(&f)
+}
+
+func (r *importReader) ident() *types.Sym {
+ name := r.string()
+ if name == "" {
+ return nil
+ }
+ pkg := r.currPkg
+ if types.IsExported(name) {
+ pkg = types.LocalPkg
+ }
+ return pkg.Lookup(name)
+}
+
+func (r *importReader) qualifiedIdent() *ir.Ident {
+ name := r.string()
+ pkg := r.pkg()
+ sym := pkg.Lookup(name)
+ return ir.NewIdent(src.NoXPos, sym)
+}
+
+func (r *importReader) pos() src.XPos {
+ delta := r.int64()
+ r.prevColumn += delta >> 1
+ if delta&1 != 0 {
+ delta = r.int64()
+ r.prevLine += delta >> 1
+ if delta&1 != 0 {
+ r.prevBase = r.posBase()
+ }
+ }
+
+ if (r.prevBase == nil || r.prevBase.AbsFilename() == "") && r.prevLine == 0 && r.prevColumn == 0 {
+ // TODO(mdempsky): Remove once we reliably write
+ // position information for all nodes.
+ return src.NoXPos
+ }
+
+ if r.prevBase == nil {
+ base.Fatalf("missing posbase")
+ }
+ pos := src.MakePos(r.prevBase, uint(r.prevLine), uint(r.prevColumn))
+ return base.Ctxt.PosTable.XPos(pos)
+}
+
+func (r *importReader) typ() *types.Type {
+ return r.p.typAt(r.uint64())
+}
+
+func (p *iimporter) typAt(off uint64) *types.Type {
+ t, ok := p.typCache[off]
+ if !ok {
+ if off < predeclReserved {
+ base.Fatalf("predeclared type missing from cache: %d", off)
+ }
+ t = p.newReader(off-predeclReserved, nil).typ1()
+ p.typCache[off] = t
+ }
+ return t
+}
+
+func (r *importReader) typ1() *types.Type {
+ switch k := r.kind(); k {
+ default:
+ base.Fatalf("unexpected kind tag in %q: %v", r.p.ipkg.Path, k)
+ return nil
+
+ case definedType:
+ // We might be called from within doInline, in which
+ // case Sym.Def can point to declared parameters
+ // instead of the top-level types. Also, we don't
+ // support inlining functions with local defined
+ // types. Therefore, this must be a package-scope
+ // type.
+ n := expandDecl(r.qualifiedIdent())
+ if n.Op() != ir.OTYPE {
+ base.Fatalf("expected OTYPE, got %v: %v, %v", n.Op(), n.Sym(), n)
+ }
+ return n.Type()
+ case pointerType:
+ return types.NewPtr(r.typ())
+ case sliceType:
+ return types.NewSlice(r.typ())
+ case arrayType:
+ n := r.uint64()
+ return types.NewArray(r.typ(), int64(n))
+ case chanType:
+ dir := types.ChanDir(r.uint64())
+ return types.NewChan(r.typ(), dir)
+ case mapType:
+ return types.NewMap(r.typ(), r.typ())
+
+ case signatureType:
+ r.setPkg()
+ return r.signature(nil)
+
+ case structType:
+ r.setPkg()
+
+ fs := make([]*types.Field, r.uint64())
+ for i := range fs {
+ pos := r.pos()
+ sym := r.ident()
+ typ := r.typ()
+ emb := r.bool()
+ note := r.string()
+
+ f := types.NewField(pos, sym, typ)
+ if emb {
+ f.Embedded = 1
+ }
+ f.Note = note
+ fs[i] = f
+ }
+
+ return types.NewStruct(r.currPkg, fs)
+
+ case interfaceType:
+ r.setPkg()
+
+ embeddeds := make([]*types.Field, r.uint64())
+ for i := range embeddeds {
+ pos := r.pos()
+ typ := r.typ()
+
+ embeddeds[i] = types.NewField(pos, nil, typ)
+ }
+
+ methods := make([]*types.Field, r.uint64())
+ for i := range methods {
+ pos := r.pos()
+ sym := r.ident()
+ typ := r.signature(fakeRecvField())
+
+ methods[i] = types.NewField(pos, sym, typ)
+ }
+
+ t := types.NewInterface(r.currPkg, append(embeddeds, methods...))
+
+ // Ensure we expand the interface in the frontend (#25055).
+ types.CheckSize(t)
+ return t
+ }
+}
+
+func (r *importReader) kind() itag {
+ return itag(r.uint64())
+}
+
+func (r *importReader) signature(recv *types.Field) *types.Type {
+ params := r.paramList()
+ results := r.paramList()
+ if n := len(params); n > 0 {
+ params[n-1].SetIsDDD(r.bool())
+ }
+ return types.NewSignature(r.currPkg, recv, params, results)
+}
+
+func (r *importReader) paramList() []*types.Field {
+ fs := make([]*types.Field, r.uint64())
+ for i := range fs {
+ fs[i] = r.param()
+ }
+ return fs
+}
+
+func (r *importReader) param() *types.Field {
+ return types.NewField(r.pos(), r.ident(), r.typ())
+}
+
+func (r *importReader) bool() bool {
+ return r.uint64() != 0
+}
+
+func (r *importReader) int64() int64 {
+ n, err := binary.ReadVarint(r)
+ if err != nil {
+ base.Fatalf("readVarint: %v", err)
+ }
+ return n
+}
+
+func (r *importReader) uint64() uint64 {
+ n, err := binary.ReadUvarint(r)
+ if err != nil {
+ base.Fatalf("readVarint: %v", err)
+ }
+ return n
+}
+
+func (r *importReader) byte() byte {
+ x, err := r.ReadByte()
+ if err != nil {
+ base.Fatalf("declReader.ReadByte: %v", err)
+ }
+ return x
+}
+
+// Compiler-specific extensions.
+
+func (r *importReader) varExt(n ir.Node) {
+ r.linkname(n.Sym())
+ r.symIdx(n.Sym())
+}
+
+func (r *importReader) funcExt(n *ir.Name) {
+ r.linkname(n.Sym())
+ r.symIdx(n.Sym())
+
+ // Escape analysis.
+ for _, fs := range &types.RecvsParams {
+ for _, f := range fs(n.Type()).FieldSlice() {
+ f.Note = r.string()
+ }
+ }
+
+ // Inline body.
+ if u := r.uint64(); u > 0 {
+ n.Func.Inl = &ir.Inline{
+ Cost: int32(u - 1),
+ }
+ n.Func.Endlineno = r.pos()
+ }
+}
+
+func (r *importReader) methExt(m *types.Field) {
+ if r.bool() {
+ m.SetNointerface(true)
+ }
+ r.funcExt(m.Nname.(*ir.Name))
+}
+
+func (r *importReader) linkname(s *types.Sym) {
+ s.Linkname = r.string()
+}
+
+func (r *importReader) symIdx(s *types.Sym) {
+ lsym := s.Linksym()
+ idx := int32(r.int64())
+ if idx != -1 {
+ if s.Linkname != "" {
+ base.Fatalf("bad index for linknamed symbol: %v %d\n", lsym, idx)
+ }
+ lsym.SymIdx = idx
+ lsym.Set(obj.AttrIndexed, true)
+ }
+}
+
+func (r *importReader) typeExt(t *types.Type) {
+ t.SetNotInHeap(r.bool())
+ i, pi := r.int64(), r.int64()
+ if i != -1 && pi != -1 {
+ typeSymIdx[t] = [2]int64{i, pi}
+ }
+}
+
+// Map imported type T to the index of type descriptor symbols of T and *T,
+// so we can use index to reference the symbol.
+var typeSymIdx = make(map[*types.Type][2]int64)
+
+func BaseTypeIndex(t *types.Type) int64 {
+ tbase := t
+ if t.IsPtr() && t.Sym() == nil && t.Elem().Sym() != nil {
+ tbase = t.Elem()
+ }
+ i, ok := typeSymIdx[tbase]
+ if !ok {
+ return -1
+ }
+ if t != tbase {
+ return i[1]
+ }
+ return i[0]
+}
+
+func (r *importReader) doInline(fn *ir.Func) {
+ if len(fn.Inl.Body) != 0 {
+ base.Fatalf("%v already has inline body", fn)
+ }
+
+ StartFuncBody(fn)
+ body := r.stmtList()
+ FinishFuncBody()
+ 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 = []ir.Node{}
+ }
+ fn.Inl.Body = body
+
+ importlist = append(importlist, fn)
+
+ if base.Flag.E > 0 && base.Flag.LowerM > 2 {
+ if base.Flag.LowerM > 3 {
+ fmt.Printf("inl body for %v %v: %+v\n", fn, fn.Type(), ir.Nodes(fn.Inl.Body))
+ } else {
+ fmt.Printf("inl body for %v %v: %v\n", fn, fn.Type(), ir.Nodes(fn.Inl.Body))
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+// 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 (r *importReader) stmtList() []ir.Node {
+ var list []ir.Node
+ for {
+ n := r.node()
+ if n == nil {
+ break
+ }
+ // OBLOCK nodes are not written to the import data directly,
+ // but the handling of ODCL calls liststmt, which creates one.
+ // Inline them into the statement list.
+ if n.Op() == ir.OBLOCK {
+ n := n.(*ir.BlockStmt)
+ list = append(list, n.List...)
+ } else {
+ list = append(list, n)
+ }
+
+ }
+ return list
+}
+
+func (r *importReader) caseList(sw ir.Node) []ir.Node {
+ namedTypeSwitch := isNamedTypeSwitch(sw)
+
+ cases := make([]ir.Node, r.uint64())
+ for i := range cases {
+ cas := ir.NewCaseStmt(r.pos(), nil, nil)
+ cas.List.Set(r.stmtList())
+ if namedTypeSwitch {
+ // Note: per-case variables will have distinct, dotted
+ // names after import. That's okay: swt.go only needs
+ // Sym for diagnostics anyway.
+ caseVar := ir.NewNameAt(cas.Pos(), r.ident())
+ Declare(caseVar, DeclContext)
+ cas.Vars = []ir.Node{caseVar}
+ caseVar.Defn = sw.(*ir.SwitchStmt).Tag
+ }
+ cas.Body.Set(r.stmtList())
+ cases[i] = cas
+ }
+ return cases
+}
+
+func (r *importReader) exprList() []ir.Node {
+ var list []ir.Node
+ for {
+ n := r.expr()
+ if n == nil {
+ break
+ }
+ list = append(list, n)
+ }
+ return list
+}
+
+func (r *importReader) expr() ir.Node {
+ n := r.node()
+ if n != nil && n.Op() == ir.OBLOCK {
+ n := n.(*ir.BlockStmt)
+ base.Fatalf("unexpected block node: %v", n)
+ }
+ return n
+}
+
+// TODO(gri) split into expr and stmt
+func (r *importReader) node() ir.Node {
+ switch op := r.op(); op {
+ // expressions
+ // case OPAREN:
+ // unreachable - unpacked by exporter
+
+ case ir.ONIL:
+ pos := r.pos()
+ typ := r.typ()
+
+ n := npos(pos, NodNil())
+ n.SetType(typ)
+ return n
+
+ case ir.OLITERAL:
+ pos := r.pos()
+ typ := r.typ()
+
+ n := npos(pos, ir.NewLiteral(r.value(typ)))
+ n.SetType(typ)
+ return n
+
+ case ir.ONONAME:
+ return r.qualifiedIdent()
+
+ case ir.ONAME:
+ return r.ident().Def.(*ir.Name)
+
+ // case OPACK, ONONAME:
+ // unreachable - should have been resolved by typechecking
+
+ case ir.OTYPE:
+ return ir.TypeNode(r.typ())
+
+ case ir.OTYPESW:
+ pos := r.pos()
+ var tag *ir.Ident
+ if s := r.ident(); s != nil {
+ tag = ir.NewIdent(pos, s)
+ }
+ expr, _ := r.exprsOrNil()
+ return ir.NewTypeSwitchGuard(pos, tag, expr)
+
+ // case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
+ // unreachable - should have been resolved by typechecking
+
+ // case OCLOSURE:
+ // unimplemented
+
+ // case OPTRLIT:
+ // unreachable - mapped to case OADDR below by exporter
+
+ case ir.OSTRUCTLIT:
+ // TODO(mdempsky): Export position information for OSTRUCTKEY nodes.
+ savedlineno := base.Pos
+ base.Pos = r.pos()
+ n := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(r.typ()).(ir.Ntype), nil)
+ n.List.Set(r.elemList()) // special handling of field names
+ base.Pos = savedlineno
+ return n
+
+ // case OARRAYLIT, OSLICELIT, OMAPLIT:
+ // unreachable - mapped to case OCOMPLIT below by exporter
+
+ case ir.OCOMPLIT:
+ n := ir.NewCompLitExpr(r.pos(), ir.OCOMPLIT, ir.TypeNode(r.typ()).(ir.Ntype), nil)
+ n.List.Set(r.exprList())
+ return n
+
+ case ir.OKEY:
+ pos := r.pos()
+ left, right := r.exprsOrNil()
+ return ir.NewKeyExpr(pos, left, right)
+
+ // case OSTRUCTKEY:
+ // unreachable - handled in case OSTRUCTLIT by elemList
+
+ // case OCALLPART:
+ // unreachable - mapped to case OXDOT below by exporter
+
+ // case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH:
+ // unreachable - mapped to case OXDOT below by exporter
+
+ case ir.OXDOT:
+ // see parser.new_dotname
+ return ir.NewSelectorExpr(r.pos(), ir.OXDOT, r.expr(), r.ident())
+
+ // case ODOTTYPE, ODOTTYPE2:
+ // unreachable - mapped to case ODOTTYPE below by exporter
+
+ case ir.ODOTTYPE:
+ n := ir.NewTypeAssertExpr(r.pos(), r.expr(), nil)
+ n.SetType(r.typ())
+ return n
+
+ // case OINDEX, OINDEXMAP, OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR:
+ // unreachable - mapped to cases below by exporter
+
+ case ir.OINDEX:
+ return ir.NewIndexExpr(r.pos(), r.expr(), r.expr())
+
+ case ir.OSLICE, ir.OSLICE3:
+ n := ir.NewSliceExpr(r.pos(), op, r.expr())
+ low, high := r.exprsOrNil()
+ var max ir.Node
+ if n.Op().IsSlice3() {
+ max = r.expr()
+ }
+ n.SetSliceBounds(low, high, max)
+ return n
+
+ // case OCONV, OCONVIFACE, OCONVNOP, OBYTES2STR, ORUNES2STR, OSTR2BYTES, OSTR2RUNES, ORUNESTR:
+ // unreachable - mapped to OCONV case below by exporter
+
+ case ir.OCONV:
+ n := ir.NewConvExpr(r.pos(), ir.OCONV, nil, r.expr())
+ n.SetType(r.typ())
+ return n
+
+ case ir.OCOPY, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCAP, ir.OCLOSE, ir.ODELETE, ir.OLEN, ir.OMAKE, ir.ONEW, ir.OPANIC, ir.ORECOVER, ir.OPRINT, ir.OPRINTN:
+ n := builtinCall(r.pos(), op)
+ n.Args.Set(r.exprList())
+ if op == ir.OAPPEND {
+ n.IsDDD = r.bool()
+ }
+ return n
+
+ // case OCALLFUNC, OCALLMETH, OCALLINTER, OGETG:
+ // unreachable - mapped to OCALL case below by exporter
+
+ case ir.OCALL:
+ n := ir.NewCallExpr(r.pos(), ir.OCALL, nil, nil)
+ n.PtrInit().Set(r.stmtList())
+ n.X = r.expr()
+ n.Args.Set(r.exprList())
+ n.IsDDD = r.bool()
+ return n
+
+ case ir.OMAKEMAP, ir.OMAKECHAN, ir.OMAKESLICE:
+ n := builtinCall(r.pos(), ir.OMAKE)
+ n.Args.Append(ir.TypeNode(r.typ()))
+ n.Args.Append(r.exprList()...)
+ return n
+
+ // unary expressions
+ case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV:
+ return ir.NewUnaryExpr(r.pos(), op, r.expr())
+
+ case ir.OADDR:
+ return NodAddrAt(r.pos(), r.expr())
+
+ case ir.ODEREF:
+ return ir.NewStarExpr(r.pos(), r.expr())
+
+ // binary expressions
+ case ir.OADD, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OEQ, ir.OGE, ir.OGT, ir.OLE, ir.OLT,
+ ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR:
+ return ir.NewBinaryExpr(r.pos(), op, r.expr(), r.expr())
+
+ case ir.OANDAND, ir.OOROR:
+ return ir.NewLogicalExpr(r.pos(), op, r.expr(), r.expr())
+
+ case ir.OSEND:
+ return ir.NewSendStmt(r.pos(), r.expr(), r.expr())
+
+ case ir.OADDSTR:
+ pos := r.pos()
+ list := r.exprList()
+ x := npos(pos, list[0])
+ for _, y := range list[1:] {
+ x = ir.NewBinaryExpr(pos, ir.OADD, x, y)
+ }
+ return x
+
+ // --------------------------------------------------------------------
+ // statements
+ case ir.ODCL:
+ pos := r.pos()
+ lhs := ir.NewDeclNameAt(pos, ir.ONAME, r.ident())
+ lhs.SetType(r.typ())
+
+ Declare(lhs, ir.PAUTO)
+
+ var stmts ir.Nodes
+ stmts.Append(ir.NewDecl(base.Pos, ir.ODCL, lhs))
+ stmts.Append(ir.NewAssignStmt(base.Pos, lhs, nil))
+ return ir.NewBlockStmt(pos, stmts)
+
+ // case OAS, OASWB:
+ // unreachable - mapped to OAS case below by exporter
+
+ case ir.OAS:
+ return ir.NewAssignStmt(r.pos(), r.expr(), r.expr())
+
+ case ir.OASOP:
+ n := ir.NewAssignOpStmt(r.pos(), ir.OXXX, nil, nil)
+ n.AsOp = r.op()
+ n.X = r.expr()
+ if !r.bool() {
+ n.Y = ir.NewInt(1)
+ n.IncDec = true
+ } else {
+ n.Y = r.expr()
+ }
+ return n
+
+ // case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
+ // unreachable - mapped to OAS2 case below by exporter
+
+ case ir.OAS2:
+ n := ir.NewAssignListStmt(r.pos(), ir.OAS2, nil, nil)
+ n.Lhs.Set(r.exprList())
+ n.Rhs.Set(r.exprList())
+ return n
+
+ case ir.ORETURN:
+ n := ir.NewReturnStmt(r.pos(), nil)
+ n.Results.Set(r.exprList())
+ return n
+
+ // case ORETJMP:
+ // unreachable - generated by compiler for trampolin routines (not exported)
+
+ case ir.OGO, ir.ODEFER:
+ return ir.NewGoDeferStmt(r.pos(), op, r.expr())
+
+ case ir.OIF:
+ n := ir.NewIfStmt(r.pos(), nil, nil, nil)
+ n.PtrInit().Set(r.stmtList())
+ n.Cond = r.expr()
+ n.Body.Set(r.stmtList())
+ n.Else.Set(r.stmtList())
+ return n
+
+ case ir.OFOR:
+ n := ir.NewForStmt(r.pos(), nil, nil, nil, nil)
+ n.PtrInit().Set(r.stmtList())
+ left, right := r.exprsOrNil()
+ n.Cond = left
+ n.Post = right
+ n.Body.Set(r.stmtList())
+ return n
+
+ case ir.ORANGE:
+ n := ir.NewRangeStmt(r.pos(), nil, nil, nil)
+ n.Vars.Set(r.stmtList())
+ n.X = r.expr()
+ n.Body.Set(r.stmtList())
+ return n
+
+ case ir.OSELECT:
+ n := ir.NewSelectStmt(r.pos(), nil)
+ n.PtrInit().Set(r.stmtList())
+ r.exprsOrNil() // TODO(rsc): Delete (and fix exporter). These are always nil.
+ n.Cases.Set(r.caseList(n))
+ return n
+
+ case ir.OSWITCH:
+ n := ir.NewSwitchStmt(r.pos(), nil, nil)
+ n.PtrInit().Set(r.stmtList())
+ left, _ := r.exprsOrNil()
+ n.Tag = left
+ n.Cases.Set(r.caseList(n))
+ return n
+
+ // case OCASE:
+ // handled by caseList
+
+ case ir.OFALL:
+ n := ir.NewBranchStmt(r.pos(), ir.OFALL, nil)
+ return n
+
+ // case OEMPTY:
+ // unreachable - not emitted by exporter
+
+ case ir.OBREAK, ir.OCONTINUE, ir.OGOTO:
+ var sym *types.Sym
+ pos := r.pos()
+ if label := r.string(); label != "" {
+ sym = Lookup(label)
+ }
+ return ir.NewBranchStmt(pos, op, sym)
+
+ case ir.OLABEL:
+ return ir.NewLabelStmt(r.pos(), Lookup(r.string()))
+
+ case ir.OEND:
+ return nil
+
+ default:
+ base.Fatalf("cannot import %v (%d) node\n"+
+ "\t==> please file an issue and assign to gri@", op, int(op))
+ panic("unreachable") // satisfy compiler
+ }
+}
+
+func (r *importReader) op() ir.Op {
+ return ir.Op(r.uint64())
+}
+
+func (r *importReader) elemList() []ir.Node {
+ c := r.uint64()
+ list := make([]ir.Node, c)
+ for i := range list {
+ s := r.ident()
+ list[i] = ir.NewStructKeyExpr(base.Pos, s, r.expr())
+ }
+ return list
+}
+
+func (r *importReader) exprsOrNil() (a, b ir.Node) {
+ ab := r.uint64()
+ if ab&1 != 0 {
+ a = r.expr()
+ }
+ if ab&2 != 0 {
+ b = r.node()
+ }
+ return
+}
+
+func builtinCall(pos src.XPos, op ir.Op) *ir.CallExpr {
+ return ir.NewCallExpr(pos, ir.OCALL, ir.NewIdent(base.Pos, types.BuiltinPkg.Lookup(ir.OpNames[op])), nil)
+}
+
+func npos(pos src.XPos, n ir.Node) ir.Node {
+ n.SetPos(pos)
+ return n
+}