// 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 gc import ( "cmd/compile/internal/types" "cmd/internal/bio" "cmd/internal/goobj" "cmd/internal/obj" "cmd/internal/src" "encoding/binary" "fmt" "io" "math/big" "os" "strings" ) // 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 *Node) { if n.Op != ONONAME { return } r := importReaderFor(n, declImporter) if r == nil { // Can happen if user tries to reference an undeclared name. return } r.doDecl(n) } func expandInline(fn *Node) { if fn.Func.Inl.Body != nil { return } r := importReaderFor(fn, inlineImporter) if r == nil { Fatalf("missing import reader for %v", fn) } r.doInline(fn) } func importReaderFor(n *Node, importers map[*types.Sym]iimporterAndOffset) *importReader { x, ok := importers[n.Sym] if !ok { return nil } return x.p.newReader(x.off, n.Sym.Pkg) } type intReader struct { *bio.Reader pkg *types.Pkg } func (r *intReader) int64() int64 { i, err := binary.ReadVarint(r.Reader) if err != nil { yyerror("import %q: read error: %v", r.pkg.Path, err) errorexit() } return i } func (r *intReader) uint64() uint64 { i, err := binary.ReadUvarint(r.Reader) if err != nil { yyerror("import %q: read error: %v", r.pkg.Path, err) errorexit() } return i } func iimport(pkg *types.Pkg, in *bio.Reader) (fingerprint goobj.FingerprintType) { ir := &intReader{in, pkg} version := ir.uint64() if version != iexportVersion { yyerror("import %q: unknown export format version %d", pkg.Path, version) errorexit() } sLen := ir.uint64() dLen := ir.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 { yyerror("import %q: mapping input: %v", pkg.Path, err) 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 := ir.uint64(); nPkgs > 0; nPkgs-- { pkg := p.pkgAt(ir.uint64()) pkgName := p.stringAt(ir.uint64()) pkgHeight := int(ir.uint64()) if pkg.Name == "" { pkg.Name = pkgName pkg.Height = pkgHeight numImport[pkgName]++ // TODO(mdempsky): This belongs somewhere else. pkg.Lookup("_").Def = asTypesNode(nblank) } else { if pkg.Name != pkgName { Fatalf("conflicting package names %v and %v for path %q", pkg.Name, pkgName, pkg.Path) } if pkg.Height != pkgHeight { Fatalf("conflicting package heights %v and %v for path %q", pkg.Height, pkgHeight, pkg.Path) } } for nSyms := ir.uint64(); nSyms > 0; nSyms-- { s := pkg.Lookup(p.stringAt(ir.uint64())) off := ir.uint64() if _, ok := declImporter[s]; ok { continue } declImporter[s] = iimporterAndOffset{p, off} // Create stub declaration. If used, this will // be overwritten by expandDecl. if s.Def != nil { Fatalf("unexpected definition for %v: %v", s, asNode(s.Def)) } s.Def = asTypesNode(npos(src.NoXPos, dclname(s))) } } // Inline body index. for nPkgs := ir.uint64(); nPkgs > 0; nPkgs-- { pkg := p.pkgAt(ir.uint64()) for nSyms := ir.uint64(); nSyms > 0; nSyms-- { s := pkg.Lookup(p.stringAt(ir.uint64())) off := ir.uint64() if _, ok := inlineImporter[s]; ok { continue } inlineImporter[s] = iimporterAndOffset{p, off} } } // Fingerprint. _, err = io.ReadFull(in, fingerprint[:]) if err != nil { yyerror("import %s: error reading fingerprint", pkg.Path) 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 { 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(n *Node) { if n.Op != ONONAME { Fatalf("doDecl: unexpected Op for %v: %v", n.Sym, n.Op) } tag := r.byte() pos := r.pos() switch tag { case 'A': typ := r.typ() importalias(r.p.ipkg, pos, n.Sym, typ) case 'C': typ, val := r.value() importconst(r.p.ipkg, pos, n.Sym, typ, val) case 'F': typ := r.signature(nil) importfunc(r.p.ipkg, pos, n.Sym, typ) r.funcExt(n) case 'T': // Types can be recursive. We need to setup a stub // declaration before recursing. t := importtype(r.p.ipkg, pos, n.Sym) // We also need to defer width calculations until // after the underlying type has been assigned. defercheckwidth() underlying := r.typ() setUnderlying(t, underlying) resumecheckwidth() if underlying.IsInterface() { r.typeExt(t) break } ms := make([]*types.Field, r.uint64()) for i := range ms { mpos := r.pos() msym := r.ident() recv := r.param() mtyp := r.signature(recv) f := types.NewField() f.Pos = mpos f.Sym = msym f.Type = mtyp ms[i] = f m := newfuncnamel(mpos, methodSym(recv.Type, msym)) m.Type = mtyp m.SetClass(PFUNC) // methodSym already marked m.Sym as a function. // (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. mtyp.SetNname(asTypesNode(m)) } t.Methods().Set(ms) r.typeExt(t) for _, m := range ms { r.methExt(m) } case 'V': typ := r.typ() importvar(r.p.ipkg, pos, n.Sym, typ) r.varExt(n) default: Fatalf("unexpected tag: %v", tag) } } func (p *importReader) value() (typ *types.Type, v Val) { typ = p.typ() switch constTypeOf(typ) { case CTNIL: v.U = &NilVal{} case CTBOOL: v.U = p.bool() case CTSTR: v.U = p.string() case CTINT: x := new(Mpint) x.Rune = typ == types.Idealrune p.mpint(&x.Val, typ) v.U = x case CTFLT: x := newMpflt() p.float(x, typ) v.U = x case CTCPLX: x := newMpcmplx() p.float(&x.Real, typ) p.float(&x.Imag, typ) v.U = x } return } 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 { 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(x *Mpflt, typ *types.Type) { var mant big.Int p.mpint(&mant, typ) m := x.Val.SetInt(&mant) if m.Sign() == 0 { return } m.SetMantExp(m, int(p.int64())) } func (r *importReader) ident() *types.Sym { name := r.string() if name == "" { return nil } pkg := r.currPkg if types.IsExported(name) { pkg = localpkg } return pkg.Lookup(name) } func (r *importReader) qualifiedIdent() *types.Sym { name := r.string() pkg := r.pkg() return pkg.Lookup(name) } 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 { Fatalf("missing posbase") } pos := src.MakePos(r.prevBase, uint(r.prevLine), uint(r.prevColumn)) return 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 { 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: 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 := asNode(r.qualifiedIdent().PkgDef()) if n.Op == ONONAME { expandDecl(n) } if n.Op != OTYPE { 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() f.Pos = pos f.Sym = sym f.Type = typ if emb { f.Embedded = 1 } f.Note = note fs[i] = f } t := types.New(TSTRUCT) t.SetPkg(r.currPkg) t.SetFields(fs) return t case interfaceType: r.setPkg() embeddeds := make([]*types.Field, r.uint64()) for i := range embeddeds { pos := r.pos() typ := r.typ() f := types.NewField() f.Pos = pos f.Type = typ embeddeds[i] = f } methods := make([]*types.Field, r.uint64()) for i := range methods { pos := r.pos() sym := r.ident() typ := r.signature(fakeRecvField()) f := types.NewField() f.Pos = pos f.Sym = sym f.Type = typ methods[i] = f } t := types.New(TINTER) t.SetPkg(r.currPkg) t.SetInterface(append(embeddeds, methods...)) // Ensure we expand the interface in the frontend (#25055). checkwidth(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()) } t := functypefield(recv, params, results) t.SetPkg(r.currPkg) return t } 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 { f := types.NewField() f.Pos = r.pos() f.Sym = r.ident() f.Type = r.typ() return f } func (r *importReader) bool() bool { return r.uint64() != 0 } func (r *importReader) int64() int64 { n, err := binary.ReadVarint(r) if err != nil { Fatalf("readVarint: %v", err) } return n } func (r *importReader) uint64() uint64 { n, err := binary.ReadUvarint(r) if err != nil { Fatalf("readVarint: %v", err) } return n } func (r *importReader) byte() byte { x, err := r.ReadByte() if err != nil { Fatalf("declReader.ReadByte: %v", err) } return x } // Compiler-specific extensions. func (r *importReader) varExt(n *Node) { r.linkname(n.Sym) r.symIdx(n.Sym) } func (r *importReader) funcExt(n *Node) { 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 = &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(asNode(m.Type.Nname())) } 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 != "" { 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) { 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 (r *importReader) doInline(n *Node) { if len(n.Func.Inl.Body) != 0 { Fatalf("%v already has inline body", n) } funchdr(n) body := r.stmtList() funcbody() 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{} } n.Func.Inl.Body = body importlist = append(importlist, n) if Debug['E'] > 0 && Debug['m'] > 2 { if Debug['m'] > 3 { fmt.Printf("inl body for %v %#v: %+v\n", n, n.Type, asNodes(n.Func.Inl.Body)) } else { fmt.Printf("inl body for %v %#v: %v\n", n, n.Type, asNodes(n.Func.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() []*Node { var list []*Node for { n := r.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 (r *importReader) exprList() []*Node { var list []*Node for { n := r.expr() if n == nil { break } list = append(list, n) } return list } func (r *importReader) expr() *Node { n := r.node() if n != nil && n.Op == OBLOCK { Fatalf("unexpected block node: %v", n) } return n } // TODO(gri) split into expr and stmt func (r *importReader) node() *Node { switch op := r.op(); op { // expressions // case OPAREN: // unreachable - unpacked by exporter case OLITERAL: pos := r.pos() typ, val := r.value() n := npos(pos, nodlit(val)) n.Type = typ return n case ONONAME: return mkname(r.qualifiedIdent()) case ONAME: return mkname(r.ident()) // case OPACK, ONONAME: // unreachable - should have been resolved by typechecking case OTYPE: return typenod(r.typ()) // 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 OSTRUCTLIT: // TODO(mdempsky): Export position information for OSTRUCTKEY nodes. savedlineno := lineno lineno = r.pos() n := nodl(lineno, OCOMPLIT, nil, typenod(r.typ())) n.List.Set(r.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(r.pos(), OCOMPLIT, nil, typenod(r.typ())) n.List.Set(r.exprList()) return n case OKEY: pos := r.pos() left, right := r.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(r.pos(), nodSym(OXDOT, r.expr(), r.ident())) // case ODOTTYPE, ODOTTYPE2: // unreachable - mapped to case ODOTTYPE below by exporter case ODOTTYPE: n := nodl(r.pos(), ODOTTYPE, r.expr(), nil) n.Type = r.typ() return n // case OINDEX, OINDEXMAP, OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR: // unreachable - mapped to cases below by exporter case OINDEX: return nodl(r.pos(), op, r.expr(), r.expr()) case OSLICE, OSLICE3: n := nodl(r.pos(), op, r.expr(), nil) low, high := r.exprsOrNil() var max *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 OCONV: n := nodl(r.pos(), OCONV, r.expr(), nil) n.Type = r.typ() return n case OCOPY, OCOMPLEX, OREAL, OIMAG, OAPPEND, OCAP, OCLOSE, ODELETE, OLEN, OMAKE, ONEW, OPANIC, ORECOVER, OPRINT, OPRINTN: n := npos(r.pos(), builtinCall(op)) n.List.Set(r.exprList()) if op == OAPPEND { n.SetIsDDD(r.bool()) } return n // case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OGETG: // unreachable - mapped to OCALL case below by exporter case OCALL: n := nodl(r.pos(), OCALL, nil, nil) n.Ninit.Set(r.stmtList()) n.Left = r.expr() n.List.Set(r.exprList()) n.SetIsDDD(r.bool()) return n case OMAKEMAP, OMAKECHAN, OMAKESLICE: n := npos(r.pos(), builtinCall(OMAKE)) n.List.Append(typenod(r.typ())) n.List.Append(r.exprList()...) return n // unary expressions case OPLUS, ONEG, OADDR, OBITNOT, ODEREF, ONOT, ORECV: return nodl(r.pos(), op, r.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(r.pos(), op, r.expr(), r.expr()) case OADDSTR: pos := r.pos() list := r.exprList() x := npos(pos, list[0]) for _, y := range list[1:] { x = nodl(pos, OADD, x, y) } return x // -------------------------------------------------------------------- // statements case ODCL: pos := r.pos() lhs := npos(pos, dclname(r.ident())) typ := typenod(r.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(r.pos(), OAS, r.expr(), r.expr()) case OASOP: n := nodl(r.pos(), OASOP, nil, nil) n.SetSubOp(r.op()) n.Left = r.expr() if !r.bool() { n.Right = nodintconst(1) n.SetImplicit(true) } else { n.Right = r.expr() } return n // case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV: // unreachable - mapped to OAS2 case below by exporter case OAS2: n := nodl(r.pos(), OAS2, nil, nil) n.List.Set(r.exprList()) n.Rlist.Set(r.exprList()) return n case ORETURN: n := nodl(r.pos(), ORETURN, nil, nil) n.List.Set(r.exprList()) return n // case ORETJMP: // unreachable - generated by compiler for trampolin routines (not exported) case OGO, ODEFER: return nodl(r.pos(), op, r.expr(), nil) case OIF: n := nodl(r.pos(), OIF, nil, nil) n.Ninit.Set(r.stmtList()) n.Left = r.expr() n.Nbody.Set(r.stmtList()) n.Rlist.Set(r.stmtList()) return n case OFOR: n := nodl(r.pos(), OFOR, nil, nil) n.Ninit.Set(r.stmtList()) n.Left, n.Right = r.exprsOrNil() n.Nbody.Set(r.stmtList()) return n case ORANGE: n := nodl(r.pos(), ORANGE, nil, nil) n.List.Set(r.stmtList()) n.Right = r.expr() n.Nbody.Set(r.stmtList()) return n case OSELECT, OSWITCH: n := nodl(r.pos(), op, nil, nil) n.Ninit.Set(r.stmtList()) n.Left, _ = r.exprsOrNil() n.List.Set(r.stmtList()) return n case OCASE: n := nodl(r.pos(), OCASE, nil, nil) n.List.Set(r.exprList()) // TODO(gri) eventually we must declare variables for type switch // statements (type switch statements are not yet exported) n.Nbody.Set(r.stmtList()) return n case OFALL: n := nodl(r.pos(), OFALL, nil, nil) return n case OBREAK, OCONTINUE: pos := r.pos() left, _ := r.exprsOrNil() if left != nil { left = newname(left.Sym) } return nodl(pos, op, left, nil) // case OEMPTY: // unreachable - not emitted by exporter case OGOTO, OLABEL: n := nodl(r.pos(), op, nil, nil) n.Sym = lookup(r.string()) return n case OEND: return nil default: 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() Op { return Op(r.uint64()) } func (r *importReader) elemList() []*Node { c := r.uint64() list := make([]*Node, c) for i := range list { s := r.ident() list[i] = nodSym(OSTRUCTKEY, r.expr(), s) } return list } func (r *importReader) exprsOrNil() (a, b *Node) { ab := r.uint64() if ab&1 != 0 { a = r.expr() } if ab&2 != 0 { b = r.node() } return }