blob: 6164597239b18fc7a3cf323f74f68450218ee35b [file] [log] [blame]
// Copyright 2013 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 implementation is loosely based on the algorithm described
// in: "On the linearization of graphs and writing symbol files",
// by R. Griesemer, Technical Report 156, ETH Zürich, 1991.
// package importer implements an exporter and importer for Go export data.
package importer // import "llvm.org/llgo/third_party/gotools/go/importer"
import (
"encoding/binary"
"fmt"
"go/token"
"llvm.org/llgo/third_party/gotools/go/exact"
"llvm.org/llgo/third_party/gotools/go/types"
)
// ImportData imports a package from the serialized package data
// and returns the number of bytes consumed and a reference to the package.
// If data is obviously malformed, an error is returned but in
// general it is not recommended to call ImportData on untrusted
// data.
func ImportData(imports map[string]*types.Package, data []byte) (int, *types.Package, error) {
datalen := len(data)
// check magic string
var s string
if len(data) >= len(magic) {
s = string(data[:len(magic)])
data = data[len(magic):]
}
if s != magic {
return 0, nil, fmt.Errorf("incorrect magic string: got %q; want %q", s, magic)
}
// check low-level encoding format
var m byte = 'm' // missing format
if len(data) > 0 {
m = data[0]
data = data[1:]
}
if m != format() {
return 0, nil, fmt.Errorf("incorrect low-level encoding format: got %c; want %c", m, format())
}
p := importer{
data: data,
datalen: datalen,
imports: imports,
}
// populate typList with predeclared types
for _, t := range predeclared {
p.typList = append(p.typList, t)
}
if v := p.string(); v != version {
return 0, nil, fmt.Errorf("unknown version: got %s; want %s", v, version)
}
pkg := p.pkg()
if debug && p.pkgList[0] != pkg {
panic("imported packaged not found in pkgList[0]")
}
// read objects
n := p.int()
for i := 0; i < n; i++ {
p.obj(pkg)
}
// complete interfaces
for _, typ := range p.typList {
if it, ok := typ.(*types.Interface); ok {
it.Complete()
}
}
// package was imported completely and without errors
pkg.MarkComplete()
return p.consumed(), pkg, nil
}
type importer struct {
data []byte
datalen int
imports map[string]*types.Package
pkgList []*types.Package
typList []types.Type
}
func (p *importer) pkg() *types.Package {
// if the package was seen before, i is its index (>= 0)
i := p.int()
if i >= 0 {
return p.pkgList[i]
}
// otherwise, i is the package tag (< 0)
if i != packageTag {
panic(fmt.Sprintf("unexpected package tag %d", i))
}
// read package data
name := p.string()
path := p.string()
// if the package was imported before, use that one; otherwise create a new one
pkg := p.imports[path]
if pkg == nil {
pkg = types.NewPackage(path, name)
p.imports[path] = pkg
}
p.pkgList = append(p.pkgList, pkg)
return pkg
}
func (p *importer) obj(pkg *types.Package) {
var obj types.Object
switch tag := p.int(); tag {
case constTag:
obj = types.NewConst(token.NoPos, pkg, p.string(), p.typ(), p.value())
case typeTag:
// type object is added to scope via respective named type
_ = p.typ().(*types.Named)
return
case varTag:
obj = types.NewVar(token.NoPos, pkg, p.string(), p.typ())
case funcTag:
obj = types.NewFunc(token.NoPos, pkg, p.string(), p.typ().(*types.Signature))
default:
panic(fmt.Sprintf("unexpected object tag %d", tag))
}
if alt := pkg.Scope().Insert(obj); alt != nil {
panic(fmt.Sprintf("%s already declared", alt.Name()))
}
}
func (p *importer) value() exact.Value {
switch kind := exact.Kind(p.int()); kind {
case falseTag:
return exact.MakeBool(false)
case trueTag:
return exact.MakeBool(true)
case int64Tag:
return exact.MakeInt64(p.int64())
case floatTag:
return p.float()
case fractionTag:
return p.fraction()
case complexTag:
re := p.fraction()
im := p.fraction()
return exact.BinaryOp(re, token.ADD, exact.MakeImag(im))
case stringTag:
return exact.MakeString(p.string())
default:
panic(fmt.Sprintf("unexpected value kind %d", kind))
}
}
func (p *importer) float() exact.Value {
sign := p.int()
if sign == 0 {
return exact.MakeInt64(0)
}
x := p.ufloat()
if sign < 0 {
x = exact.UnaryOp(token.SUB, x, 0)
}
return x
}
func (p *importer) fraction() exact.Value {
sign := p.int()
if sign == 0 {
return exact.MakeInt64(0)
}
x := exact.BinaryOp(p.ufloat(), token.QUO, p.ufloat())
if sign < 0 {
x = exact.UnaryOp(token.SUB, x, 0)
}
return x
}
func (p *importer) ufloat() exact.Value {
exp := p.int()
x := exact.MakeFromBytes(p.bytes())
switch {
case exp < 0:
d := exact.Shift(exact.MakeInt64(1), token.SHL, uint(-exp))
x = exact.BinaryOp(x, token.QUO, d)
case exp > 0:
x = exact.Shift(x, token.SHL, uint(exp))
}
return x
}
func (p *importer) record(t types.Type) {
p.typList = append(p.typList, t)
}
func (p *importer) typ() types.Type {
// if the type was seen before, i is its index (>= 0)
i := p.int()
if i >= 0 {
return p.typList[i]
}
// otherwise, i is the type tag (< 0)
switch i {
case arrayTag:
t := new(types.Array)
p.record(t)
n := p.int64()
*t = *types.NewArray(p.typ(), n)
return t
case sliceTag:
t := new(types.Slice)
p.record(t)
*t = *types.NewSlice(p.typ())
return t
case structTag:
t := new(types.Struct)
p.record(t)
n := p.int()
fields := make([]*types.Var, n)
tags := make([]string, n)
for i := range fields {
fields[i] = p.field()
tags[i] = p.string()
}
*t = *types.NewStruct(fields, tags)
return t
case pointerTag:
t := new(types.Pointer)
p.record(t)
*t = *types.NewPointer(p.typ())
return t
case signatureTag:
t := new(types.Signature)
p.record(t)
*t = *p.signature()
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.
n := len(p.typList)
p.record(nil)
// read embedded interfaces
embeddeds := make([]*types.Named, p.int())
for i := range embeddeds {
embeddeds[i] = p.typ().(*types.Named)
}
// read methods
methods := make([]*types.Func, p.int())
for i := range methods {
pkg, name := p.qualifiedName()
methods[i] = types.NewFunc(token.NoPos, pkg, name, p.typ().(*types.Signature))
}
t := types.NewInterface(methods, embeddeds)
p.typList[n] = t
return t
case mapTag:
t := new(types.Map)
p.record(t)
*t = *types.NewMap(p.typ(), p.typ())
return t
case chanTag:
t := new(types.Chan)
p.record(t)
*t = *types.NewChan(types.ChanDir(p.int()), p.typ())
return t
case namedTag:
// read type object
name := p.string()
pkg := p.pkg()
scope := pkg.Scope()
obj := scope.Lookup(name)
// if the object doesn't exist yet, create and insert it
if obj == nil {
obj = types.NewTypeName(token.NoPos, pkg, name, nil)
scope.Insert(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
t := obj.Type().(*types.Named)
p.record(t)
// read underlying type
t0.SetUnderlying(p.typ())
// read associated methods
for i, n := 0, p.int(); i < n; i++ {
t0.AddMethod(types.NewFunc(token.NoPos, pkg, p.string(), p.typ().(*types.Signature)))
}
return t
default:
panic(fmt.Sprintf("unexpected type tag %d", i))
}
}
func deref(typ types.Type) types.Type {
if p, _ := typ.(*types.Pointer); p != nil {
return p.Elem()
}
return typ
}
func (p *importer) field() *types.Var {
pkg, name := p.qualifiedName()
typ := p.typ()
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
name = typ.Name()
case *types.Named:
obj := typ.Obj()
name = obj.Name()
// correct the field package for anonymous fields
if exported(name) {
pkg = p.pkgList[0]
}
default:
panic("anonymous field expected")
}
anonymous = true
}
return types.NewField(token.NoPos, pkg, name, typ, anonymous)
}
func (p *importer) qualifiedName() (*types.Package, string) {
name := p.string()
pkg := p.pkgList[0] // exported names assume current package
if !exported(name) {
pkg = p.pkg()
}
return pkg, name
}
func (p *importer) signature() *types.Signature {
var recv *types.Var
if p.int() != 0 {
recv = p.param()
}
return types.NewSignature(nil, recv, p.tuple(), p.tuple(), p.int() != 0)
}
func (p *importer) param() *types.Var {
return types.NewVar(token.NoPos, nil, p.string(), p.typ())
}
func (p *importer) tuple() *types.Tuple {
vars := make([]*types.Var, p.int())
for i := range vars {
vars[i] = p.param()
}
return types.NewTuple(vars...)
}
// ----------------------------------------------------------------------------
// decoders
func (p *importer) string() string {
return string(p.bytes())
}
func (p *importer) int() int {
return int(p.int64())
}
func (p *importer) int64() int64 {
if debug {
p.marker('i')
}
return p.rawInt64()
}
// Note: bytes() returns the respective byte slice w/o copy.
func (p *importer) bytes() []byte {
if debug {
p.marker('b')
}
var b []byte
if n := int(p.rawInt64()); n > 0 {
b = p.data[:n]
p.data = p.data[n:]
}
return b
}
func (p *importer) marker(want byte) {
if debug {
if got := p.data[0]; got != want {
panic(fmt.Sprintf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.consumed()))
}
p.data = p.data[1:]
pos := p.consumed()
if n := int(p.rawInt64()); n != pos {
panic(fmt.Sprintf("incorrect position: got %d; want %d", n, pos))
}
}
}
// rawInt64 should only be used by low-level decoders
func (p *importer) rawInt64() int64 {
i, n := binary.Varint(p.data)
p.data = p.data[n:]
return i
}
func (p *importer) consumed() int {
return p.datalen - len(p.data)
}