| // 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. |
| |
| // This file is a copy of $GOROOT/src/go/internal/gcimporter/bimport.go. |
| |
| package gcimporter |
| |
| import ( |
| "encoding/binary" |
| "fmt" |
| "go/constant" |
| "go/token" |
| "go/types" |
| "sort" |
| "strconv" |
| "strings" |
| "sync" |
| "unicode" |
| "unicode/utf8" |
| ) |
| |
| type importer struct { |
| imports map[string]*types.Package |
| data []byte |
| importpath string |
| buf []byte // for reading strings |
| version int // export format version |
| |
| // object lists |
| strList []string // in order of appearance |
| pathList []string // in order of appearance |
| pkgList []*types.Package // in order of appearance |
| typList []types.Type // in order of appearance |
| interfaceList []*types.Interface // for delayed completion only |
| trackAllTypes bool |
| |
| // position encoding |
| posInfoFormat bool |
| prevFile string |
| prevLine int |
| fake fakeFileSet |
| |
| // debugging support |
| debugFormat bool |
| read int // bytes read |
| } |
| |
| // BImportData imports a package from the serialized package data |
| // and returns the number of bytes consumed and a reference to the package. |
| // If the export data version is not recognized or the format is otherwise |
| // compromised, an error is returned. |
| func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { |
| // catch panics and return them as errors |
| const currentVersion = 6 |
| version := -1 // unknown version |
| defer func() { |
| if e := recover(); e != nil { |
| // Return a (possibly nil or incomplete) package unchanged (see #16088). |
| if version > currentVersion { |
| err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e) |
| } else { |
| err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e) |
| } |
| } |
| }() |
| |
| p := importer{ |
| imports: imports, |
| data: data, |
| importpath: path, |
| version: version, |
| strList: []string{""}, // empty string is mapped to 0 |
| pathList: []string{""}, // empty string is mapped to 0 |
| fake: fakeFileSet{ |
| fset: fset, |
| files: make(map[string]*fileInfo), |
| }, |
| } |
| defer p.fake.setLines() // set lines for files in fset |
| |
| // read version info |
| var versionstr string |
| if b := p.rawByte(); b == 'c' || b == 'd' { |
| // Go1.7 encoding; first byte encodes low-level |
| // encoding format (compact vs debug). |
| // For backward-compatibility only (avoid problems with |
| // old installed packages). Newly compiled packages use |
| // the extensible format string. |
| // TODO(gri) Remove this support eventually; after Go1.8. |
| if b == 'd' { |
| p.debugFormat = true |
| } |
| p.trackAllTypes = p.rawByte() == 'a' |
| p.posInfoFormat = p.int() != 0 |
| versionstr = p.string() |
| if versionstr == "v1" { |
| version = 0 |
| } |
| } else { |
| // Go1.8 extensible encoding |
| // read version string and extract version number (ignore anything after the version number) |
| versionstr = p.rawStringln(b) |
| if s := strings.SplitN(versionstr, " ", 3); len(s) >= 2 && s[0] == "version" { |
| if v, err := strconv.Atoi(s[1]); err == nil && v > 0 { |
| version = v |
| } |
| } |
| } |
| p.version = version |
| |
| // read version specific flags - extend as necessary |
| switch p.version { |
| // case currentVersion: |
| // ... |
| // fallthrough |
| case currentVersion, 5, 4, 3, 2, 1: |
| p.debugFormat = p.rawStringln(p.rawByte()) == "debug" |
| p.trackAllTypes = p.int() != 0 |
| p.posInfoFormat = p.int() != 0 |
| case 0: |
| // Go1.7 encoding format - nothing to do here |
| default: |
| errorf("unknown bexport format version %d (%q)", p.version, versionstr) |
| } |
| |
| // --- generic export data --- |
| |
| // populate typList with predeclared "known" types |
| p.typList = append(p.typList, predeclared()...) |
| |
| // read package data |
| pkg = p.pkg() |
| |
| // read objects of phase 1 only (see cmd/compile/internal/gc/bexport.go) |
| objcount := 0 |
| for { |
| tag := p.tagOrIndex() |
| if tag == endTag { |
| break |
| } |
| p.obj(tag) |
| objcount++ |
| } |
| |
| // self-verification |
| if count := p.int(); count != objcount { |
| errorf("got %d objects; want %d", objcount, count) |
| } |
| |
| // ignore compiler-specific import data |
| |
| // complete interfaces |
| // TODO(gri) re-investigate if we still need to do this in a delayed fashion |
| for _, typ := range p.interfaceList { |
| typ.Complete() |
| } |
| |
| // record all referenced packages as imports |
| list := append(([]*types.Package)(nil), p.pkgList[1:]...) |
| sort.Sort(byPath(list)) |
| pkg.SetImports(list) |
| |
| // package was imported completely and without errors |
| pkg.MarkComplete() |
| |
| return p.read, pkg, nil |
| } |
| |
| func errorf(format string, args ...interface{}) { |
| panic(fmt.Sprintf(format, args...)) |
| } |
| |
| func (p *importer) pkg() *types.Package { |
| // 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 { |
| errorf("unexpected package tag %d version %d", i, p.version) |
| } |
| |
| // read package data |
| name := p.string() |
| var path string |
| if p.version >= 5 { |
| path = p.path() |
| } else { |
| path = p.string() |
| } |
| if p.version >= 6 { |
| p.int() // package height; unused by go/types |
| } |
| |
| // we should never see an empty package name |
| if name == "" { |
| errorf("empty package name in import") |
| } |
| |
| // an empty path denotes the package we are currently importing; |
| // it must be the first package we see |
| if (path == "") != (len(p.pkgList) == 0) { |
| errorf("package path %q for pkg index %d", path, len(p.pkgList)) |
| } |
| |
| // if the package was imported before, use that one; otherwise create a new one |
| if path == "" { |
| path = p.importpath |
| } |
| pkg := p.imports[path] |
| if pkg == nil { |
| pkg = types.NewPackage(path, name) |
| p.imports[path] = pkg |
| } else if pkg.Name() != name { |
| errorf("conflicting names %s and %s for package %q", pkg.Name(), name, path) |
| } |
| p.pkgList = append(p.pkgList, pkg) |
| |
| return pkg |
| } |
| |
| // objTag returns the tag value for each object kind. |
| func objTag(obj types.Object) int { |
| switch obj.(type) { |
| case *types.Const: |
| return constTag |
| case *types.TypeName: |
| return typeTag |
| case *types.Var: |
| return varTag |
| case *types.Func: |
| return funcTag |
| default: |
| errorf("unexpected object: %v (%T)", obj, obj) // panics |
| panic("unreachable") |
| } |
| } |
| |
| func sameObj(a, b types.Object) bool { |
| // Because unnamed types are not canonicalized, we cannot simply compare types for |
| // (pointer) identity. |
| // Ideally we'd check equality of constant values as well, but this is good enough. |
| return objTag(a) == objTag(b) && types.Identical(a.Type(), b.Type()) |
| } |
| |
| func (p *importer) declare(obj types.Object) { |
| pkg := obj.Pkg() |
| if alt := pkg.Scope().Insert(obj); alt != nil { |
| // This can only trigger if we import a (non-type) object a second time. |
| // Excluding type aliases, this cannot happen because 1) we only import a package |
| // once; and b) we ignore compiler-specific export data which may contain |
| // functions whose inlined function bodies refer to other functions that |
| // were already imported. |
| // However, type aliases require reexporting the original type, so we need |
| // to allow it (see also the comment in cmd/compile/internal/gc/bimport.go, |
| // method importer.obj, switch case importing functions). |
| // TODO(gri) review/update this comment once the gc compiler handles type aliases. |
| if !sameObj(obj, alt) { |
| errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt) |
| } |
| } |
| } |
| |
| func (p *importer) obj(tag int) { |
| switch tag { |
| case constTag: |
| pos := p.pos() |
| pkg, name := p.qualifiedName() |
| typ := p.typ(nil, nil) |
| val := p.value() |
| p.declare(types.NewConst(pos, pkg, name, typ, val)) |
| |
| case aliasTag: |
| // TODO(gri) verify type alias hookup is correct |
| pos := p.pos() |
| pkg, name := p.qualifiedName() |
| typ := p.typ(nil, nil) |
| p.declare(types.NewTypeName(pos, pkg, name, typ)) |
| |
| case typeTag: |
| p.typ(nil, nil) |
| |
| case varTag: |
| pos := p.pos() |
| pkg, name := p.qualifiedName() |
| typ := p.typ(nil, nil) |
| p.declare(types.NewVar(pos, pkg, name, typ)) |
| |
| case funcTag: |
| pos := p.pos() |
| pkg, name := p.qualifiedName() |
| params, isddd := p.paramList() |
| result, _ := p.paramList() |
| sig := types.NewSignature(nil, params, result, isddd) |
| p.declare(types.NewFunc(pos, pkg, name, sig)) |
| |
| default: |
| errorf("unexpected object tag %d", tag) |
| } |
| } |
| |
| const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go |
| |
| func (p *importer) pos() token.Pos { |
| if !p.posInfoFormat { |
| return token.NoPos |
| } |
| |
| file := p.prevFile |
| line := p.prevLine |
| delta := p.int() |
| line += delta |
| if p.version >= 5 { |
| if delta == deltaNewFile { |
| if n := p.int(); n >= 0 { |
| // file changed |
| file = p.path() |
| line = n |
| } |
| } |
| } else { |
| if delta == 0 { |
| if n := p.int(); n >= 0 { |
| // file changed |
| file = p.prevFile[:n] + p.string() |
| line = p.int() |
| } |
| } |
| } |
| p.prevFile = file |
| p.prevLine = line |
| |
| return p.fake.pos(file, line, 0) |
| } |
| |
| // Synthesize a token.Pos |
| type fakeFileSet struct { |
| fset *token.FileSet |
| files map[string]*fileInfo |
| } |
| |
| type fileInfo struct { |
| file *token.File |
| lastline int |
| } |
| |
| const maxlines = 64 * 1024 |
| |
| func (s *fakeFileSet) pos(file string, line, column int) token.Pos { |
| // TODO(mdempsky): Make use of column. |
| |
| // Since we don't know the set of needed file positions, we reserve maxlines |
| // positions per file. We delay calling token.File.SetLines until all |
| // positions have been calculated (by way of fakeFileSet.setLines), so that |
| // we can avoid setting unnecessary lines. See also golang/go#46586. |
| f := s.files[file] |
| if f == nil { |
| f = &fileInfo{file: s.fset.AddFile(file, -1, maxlines)} |
| s.files[file] = f |
| } |
| if line > maxlines { |
| line = 1 |
| } |
| if line > f.lastline { |
| f.lastline = line |
| } |
| |
| // Return a fake position assuming that f.file consists only of newlines. |
| return token.Pos(f.file.Base() + line - 1) |
| } |
| |
| func (s *fakeFileSet) setLines() { |
| fakeLinesOnce.Do(func() { |
| fakeLines = make([]int, maxlines) |
| for i := range fakeLines { |
| fakeLines[i] = i |
| } |
| }) |
| for _, f := range s.files { |
| f.file.SetLines(fakeLines[:f.lastline]) |
| } |
| } |
| |
| var ( |
| fakeLines []int |
| fakeLinesOnce sync.Once |
| ) |
| |
| func (p *importer) qualifiedName() (pkg *types.Package, name string) { |
| name = p.string() |
| pkg = p.pkg() |
| return |
| } |
| |
| func (p *importer) record(t types.Type) { |
| p.typList = append(p.typList, t) |
| } |
| |
| // A dddSlice is a types.Type representing ...T parameters. |
| // It only appears for parameter types and does not escape |
| // the importer. |
| type dddSlice struct { |
| elem types.Type |
| } |
| |
| func (t *dddSlice) Underlying() types.Type { return t } |
| func (t *dddSlice) String() string { return "..." + t.elem.String() } |
| |
| // parent is the package which declared the type; parent == nil means |
| // the package currently imported. The parent package is needed for |
| // exported struct fields and interface methods which don't contain |
| // explicit package information in the export data. |
| // |
| // A non-nil tname is used as the "owner" of the result type; i.e., |
| // the result type is the underlying type of tname. tname is used |
| // to give interface methods a named receiver type where possible. |
| func (p *importer) typ(parent *types.Package, tname *types.Named) types.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) |
| switch i { |
| case namedTag: |
| // read type object |
| pos := p.pos() |
| parent, name := p.qualifiedName() |
| scope := parent.Scope() |
| obj := scope.Lookup(name) |
| |
| // if the object doesn't exist yet, create and insert it |
| if obj == nil { |
| obj = types.NewTypeName(pos, parent, name, nil) |
| scope.Insert(obj) |
| } |
| |
| if _, ok := obj.(*types.TypeName); !ok { |
| errorf("pkg = %s, name = %s => %s", parent, name, obj) |
| } |
| |
| // associate new named type with obj if it doesn't exist yet |
| t0 := types.NewNamed(obj.(*types.TypeName), nil, nil) |
| |
| // but record the existing type, if any |
| tname := obj.Type().(*types.Named) // tname is either t0 or the existing type |
| p.record(tname) |
| |
| // read underlying type |
| t0.SetUnderlying(p.typ(parent, t0)) |
| |
| // interfaces don't have associated methods |
| if types.IsInterface(t0) { |
| return tname |
| } |
| |
| // read associated methods |
| for i := p.int(); i > 0; i-- { |
| // TODO(gri) replace this with something closer to fieldName |
| pos := p.pos() |
| name := p.string() |
| if !exported(name) { |
| p.pkg() |
| } |
| |
| recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver? |
| params, isddd := p.paramList() |
| result, _ := p.paramList() |
| p.int() // go:nointerface pragma - discarded |
| |
| sig := types.NewSignature(recv.At(0), params, result, isddd) |
| t0.AddMethod(types.NewFunc(pos, parent, name, sig)) |
| } |
| |
| return tname |
| |
| case arrayTag: |
| t := new(types.Array) |
| if p.trackAllTypes { |
| p.record(t) |
| } |
| |
| n := p.int64() |
| *t = *types.NewArray(p.typ(parent, nil), n) |
| return t |
| |
| case sliceTag: |
| t := new(types.Slice) |
| if p.trackAllTypes { |
| p.record(t) |
| } |
| |
| *t = *types.NewSlice(p.typ(parent, nil)) |
| return t |
| |
| case dddTag: |
| t := new(dddSlice) |
| if p.trackAllTypes { |
| p.record(t) |
| } |
| |
| t.elem = p.typ(parent, nil) |
| return t |
| |
| case structTag: |
| t := new(types.Struct) |
| if p.trackAllTypes { |
| p.record(t) |
| } |
| |
| *t = *types.NewStruct(p.fieldList(parent)) |
| return t |
| |
| case pointerTag: |
| t := new(types.Pointer) |
| if p.trackAllTypes { |
| p.record(t) |
| } |
| |
| *t = *types.NewPointer(p.typ(parent, nil)) |
| return t |
| |
| case signatureTag: |
| t := new(types.Signature) |
| if p.trackAllTypes { |
| p.record(t) |
| } |
| |
| params, isddd := p.paramList() |
| result, _ := p.paramList() |
| *t = *types.NewSignature(nil, params, result, isddd) |
| return t |
| |
| case interfaceTag: |
| // Create a dummy entry in the type list. This is safe because we |
| // cannot expect the interface type to appear in a cycle, as any |
| // such cycle must contain a named type which would have been |
| // first defined earlier. |
| // TODO(gri) Is this still true now that we have type aliases? |
| // See issue #23225. |
| n := len(p.typList) |
| if p.trackAllTypes { |
| p.record(nil) |
| } |
| |
| var embeddeds []types.Type |
| for n := p.int(); n > 0; n-- { |
| p.pos() |
| embeddeds = append(embeddeds, p.typ(parent, nil)) |
| } |
| |
| t := newInterface(p.methodList(parent, tname), embeddeds) |
| p.interfaceList = append(p.interfaceList, t) |
| if p.trackAllTypes { |
| p.typList[n] = t |
| } |
| return t |
| |
| case mapTag: |
| t := new(types.Map) |
| if p.trackAllTypes { |
| p.record(t) |
| } |
| |
| key := p.typ(parent, nil) |
| val := p.typ(parent, nil) |
| *t = *types.NewMap(key, val) |
| return t |
| |
| case chanTag: |
| t := new(types.Chan) |
| if p.trackAllTypes { |
| p.record(t) |
| } |
| |
| dir := chanDir(p.int()) |
| val := p.typ(parent, nil) |
| *t = *types.NewChan(dir, val) |
| return t |
| |
| default: |
| errorf("unexpected type tag %d", i) // panics |
| panic("unreachable") |
| } |
| } |
| |
| func chanDir(d int) types.ChanDir { |
| // tag values must match the constants in cmd/compile/internal/gc/go.go |
| switch d { |
| case 1 /* Crecv */ : |
| return types.RecvOnly |
| case 2 /* Csend */ : |
| return types.SendOnly |
| case 3 /* Cboth */ : |
| return types.SendRecv |
| default: |
| errorf("unexpected channel dir %d", d) |
| return 0 |
| } |
| } |
| |
| func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags []string) { |
| if n := p.int(); n > 0 { |
| fields = make([]*types.Var, n) |
| tags = make([]string, n) |
| for i := range fields { |
| fields[i], tags[i] = p.field(parent) |
| } |
| } |
| return |
| } |
| |
| func (p *importer) field(parent *types.Package) (*types.Var, string) { |
| pos := p.pos() |
| pkg, name, alias := p.fieldName(parent) |
| typ := p.typ(parent, nil) |
| tag := p.string() |
| |
| anonymous := false |
| if name == "" { |
| // anonymous field - typ must be T or *T and T must be a type name |
| switch typ := deref(typ).(type) { |
| case *types.Basic: // basic types are named types |
| pkg = nil // // objects defined in Universe scope have no package |
| name = typ.Name() |
| case *types.Named: |
| name = typ.Obj().Name() |
| default: |
| errorf("named base type expected") |
| } |
| anonymous = true |
| } else if alias { |
| // anonymous field: we have an explicit name because it's an alias |
| anonymous = true |
| } |
| |
| return types.NewField(pos, pkg, name, typ, anonymous), tag |
| } |
| |
| func (p *importer) methodList(parent *types.Package, baseType *types.Named) (methods []*types.Func) { |
| if n := p.int(); n > 0 { |
| methods = make([]*types.Func, n) |
| for i := range methods { |
| methods[i] = p.method(parent, baseType) |
| } |
| } |
| return |
| } |
| |
| func (p *importer) method(parent *types.Package, baseType *types.Named) *types.Func { |
| pos := p.pos() |
| pkg, name, _ := p.fieldName(parent) |
| // If we don't have a baseType, use a nil receiver. |
| // A receiver using the actual interface type (which |
| // we don't know yet) will be filled in when we call |
| // types.Interface.Complete. |
| var recv *types.Var |
| if baseType != nil { |
| recv = types.NewVar(token.NoPos, parent, "", baseType) |
| } |
| params, isddd := p.paramList() |
| result, _ := p.paramList() |
| sig := types.NewSignature(recv, params, result, isddd) |
| return types.NewFunc(pos, pkg, name, sig) |
| } |
| |
| func (p *importer) fieldName(parent *types.Package) (pkg *types.Package, name string, alias bool) { |
| name = p.string() |
| pkg = parent |
| if pkg == nil { |
| // use the imported package instead |
| pkg = p.pkgList[0] |
| } |
| if p.version == 0 && name == "_" { |
| // version 0 didn't export a package for _ fields |
| return |
| } |
| switch name { |
| case "": |
| // 1) field name matches base type name and is exported: nothing to do |
| case "?": |
| // 2) field name matches base type name and is not exported: need package |
| name = "" |
| pkg = p.pkg() |
| case "@": |
| // 3) field name doesn't match type name (alias) |
| name = p.string() |
| alias = true |
| fallthrough |
| default: |
| if !exported(name) { |
| pkg = p.pkg() |
| } |
| } |
| return |
| } |
| |
| func (p *importer) paramList() (*types.Tuple, bool) { |
| n := p.int() |
| if n == 0 { |
| return nil, false |
| } |
| // negative length indicates unnamed parameters |
| named := true |
| if n < 0 { |
| n = -n |
| named = false |
| } |
| // n > 0 |
| params := make([]*types.Var, n) |
| isddd := false |
| for i := range params { |
| params[i], isddd = p.param(named) |
| } |
| return types.NewTuple(params...), isddd |
| } |
| |
| func (p *importer) param(named bool) (*types.Var, bool) { |
| t := p.typ(nil, nil) |
| td, isddd := t.(*dddSlice) |
| if isddd { |
| t = types.NewSlice(td.elem) |
| } |
| |
| var pkg *types.Package |
| var name string |
| if named { |
| name = p.string() |
| if name == "" { |
| errorf("expected named parameter") |
| } |
| if name != "_" { |
| pkg = p.pkg() |
| } |
| if i := strings.Index(name, "·"); i > 0 { |
| name = name[:i] // cut off gc-specific parameter numbering |
| } |
| } |
| |
| // read and discard compiler-specific info |
| p.string() |
| |
| return types.NewVar(token.NoPos, pkg, name, t), isddd |
| } |
| |
| func exported(name string) bool { |
| ch, _ := utf8.DecodeRuneInString(name) |
| return unicode.IsUpper(ch) |
| } |
| |
| func (p *importer) value() constant.Value { |
| switch tag := p.tagOrIndex(); tag { |
| case falseTag: |
| return constant.MakeBool(false) |
| case trueTag: |
| return constant.MakeBool(true) |
| case int64Tag: |
| return constant.MakeInt64(p.int64()) |
| case floatTag: |
| return p.float() |
| case complexTag: |
| re := p.float() |
| im := p.float() |
| return constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) |
| case stringTag: |
| return constant.MakeString(p.string()) |
| case unknownTag: |
| return constant.MakeUnknown() |
| default: |
| errorf("unexpected value tag %d", tag) // panics |
| panic("unreachable") |
| } |
| } |
| |
| func (p *importer) float() constant.Value { |
| sign := p.int() |
| if sign == 0 { |
| return constant.MakeInt64(0) |
| } |
| |
| exp := p.int() |
| mant := []byte(p.string()) // big endian |
| |
| // remove leading 0's if any |
| for len(mant) > 0 && mant[0] == 0 { |
| mant = mant[1:] |
| } |
| |
| // convert to little endian |
| // TODO(gri) go/constant should have a more direct conversion function |
| // (e.g., once it supports a big.Float based implementation) |
| for i, j := 0, len(mant)-1; i < j; i, j = i+1, j-1 { |
| mant[i], mant[j] = mant[j], mant[i] |
| } |
| |
| // adjust exponent (constant.MakeFromBytes creates an integer value, |
| // but mant represents the mantissa bits such that 0.5 <= mant < 1.0) |
| exp -= len(mant) << 3 |
| if len(mant) > 0 { |
| for msd := mant[len(mant)-1]; msd&0x80 == 0; msd <<= 1 { |
| exp++ |
| } |
| } |
| |
| x := constant.MakeFromBytes(mant) |
| switch { |
| case exp < 0: |
| d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp)) |
| x = constant.BinaryOp(x, token.QUO, d) |
| case exp > 0: |
| x = constant.Shift(x, token.SHL, uint(exp)) |
| } |
| |
| if sign < 0 { |
| x = constant.UnaryOp(token.SUB, x, 0) |
| } |
| return x |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // 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 { |
| errorf("exported integer too large") |
| } |
| return int(x) |
| } |
| |
| func (p *importer) int64() int64 { |
| if p.debugFormat { |
| p.marker('i') |
| } |
| |
| return p.rawInt64() |
| } |
| |
| func (p *importer) path() string { |
| if p.debugFormat { |
| p.marker('p') |
| } |
| // if the path was seen before, i is its index (>= 0) |
| // (the empty string is at index 0) |
| i := p.rawInt64() |
| if i >= 0 { |
| return p.pathList[i] |
| } |
| // otherwise, i is the negative path length (< 0) |
| a := make([]string, -i) |
| for n := range a { |
| a[n] = p.string() |
| } |
| s := strings.Join(a, "/") |
| p.pathList = append(p.pathList, s) |
| return s |
| } |
| |
| func (p *importer) string() string { |
| if p.debugFormat { |
| p.marker('s') |
| } |
| // if the string was seen before, i is its index (>= 0) |
| // (the empty string is at index 0) |
| i := p.rawInt64() |
| if i >= 0 { |
| return p.strList[i] |
| } |
| // otherwise, i is the negative string length (< 0) |
| if n := int(-i); n <= cap(p.buf) { |
| p.buf = p.buf[:n] |
| } else { |
| p.buf = make([]byte, n) |
| } |
| for i := range p.buf { |
| p.buf[i] = p.rawByte() |
| } |
| s := string(p.buf) |
| p.strList = append(p.strList, s) |
| return s |
| } |
| |
| func (p *importer) marker(want byte) { |
| if got := p.rawByte(); got != want { |
| errorf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read) |
| } |
| |
| pos := p.read |
| if n := int(p.rawInt64()); n != pos { |
| errorf("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 { |
| errorf("read error: %v", err) |
| } |
| return i |
| } |
| |
| // rawStringln should only be used to read the initial version string. |
| func (p *importer) rawStringln(b byte) string { |
| p.buf = p.buf[:0] |
| for b != '\n' { |
| p.buf = append(p.buf, b) |
| b = p.rawByte() |
| } |
| return string(p.buf) |
| } |
| |
| // needed for binary.ReadVarint in rawInt64 |
| func (p *importer) ReadByte() (byte, error) { |
| return p.rawByte(), nil |
| } |
| |
| // byte is the bottleneck interface for reading p.data. |
| // It unescapes '|' 'S' to '$' and '|' '|' to '|'. |
| // rawByte should only be used by low-level decoders. |
| func (p *importer) rawByte() byte { |
| b := p.data[0] |
| r := 1 |
| if b == '|' { |
| b = p.data[1] |
| r = 2 |
| switch b { |
| case 'S': |
| b = '$' |
| case '|': |
| // nothing to do |
| default: |
| errorf("unexpected escape sequence in export data") |
| } |
| } |
| p.data = p.data[r:] |
| p.read += r |
| return b |
| |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // Export format |
| |
| // Tags. Must be < 0. |
| const ( |
| // Objects |
| packageTag = -(iota + 1) |
| constTag |
| typeTag |
| varTag |
| funcTag |
| endTag |
| |
| // Types |
| namedTag |
| arrayTag |
| sliceTag |
| dddTag |
| structTag |
| pointerTag |
| signatureTag |
| interfaceTag |
| mapTag |
| chanTag |
| |
| // Values |
| falseTag |
| trueTag |
| int64Tag |
| floatTag |
| fractionTag // not used by gc |
| complexTag |
| stringTag |
| nilTag // only used by gc (appears in exported inlined function bodies) |
| unknownTag // not used by gc (only appears in packages with errors) |
| |
| // Type aliases |
| aliasTag |
| ) |
| |
| var predeclOnce sync.Once |
| var predecl []types.Type // initialized lazily |
| |
| func predeclared() []types.Type { |
| predeclOnce.Do(func() { |
| // initialize lazily to be sure that all |
| // elements have been initialized before |
| predecl = []types.Type{ // basic types |
| types.Typ[types.Bool], |
| types.Typ[types.Int], |
| types.Typ[types.Int8], |
| types.Typ[types.Int16], |
| types.Typ[types.Int32], |
| types.Typ[types.Int64], |
| types.Typ[types.Uint], |
| types.Typ[types.Uint8], |
| types.Typ[types.Uint16], |
| types.Typ[types.Uint32], |
| types.Typ[types.Uint64], |
| types.Typ[types.Uintptr], |
| types.Typ[types.Float32], |
| types.Typ[types.Float64], |
| types.Typ[types.Complex64], |
| types.Typ[types.Complex128], |
| types.Typ[types.String], |
| |
| // basic type aliases |
| types.Universe.Lookup("byte").Type(), |
| types.Universe.Lookup("rune").Type(), |
| |
| // error |
| types.Universe.Lookup("error").Type(), |
| |
| // untyped types |
| types.Typ[types.UntypedBool], |
| types.Typ[types.UntypedInt], |
| types.Typ[types.UntypedRune], |
| types.Typ[types.UntypedFloat], |
| types.Typ[types.UntypedComplex], |
| types.Typ[types.UntypedString], |
| types.Typ[types.UntypedNil], |
| |
| // package unsafe |
| types.Typ[types.UnsafePointer], |
| |
| // invalid type |
| types.Typ[types.Invalid], // only appears in packages with errors |
| |
| // used internally by gc; never used by this package or in .a files |
| anyType{}, |
| } |
| predecl = append(predecl, additionalPredeclared()...) |
| }) |
| return predecl |
| } |
| |
| type anyType struct{} |
| |
| func (t anyType) Underlying() types.Type { return t } |
| func (t anyType) String() string { return "any" } |