| // 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.UntypedRune | |
| 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) { | |
| 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 (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: | |
| // unreachable - mapped to case OXDOT below by exporter | |
| // 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 | |
| } |