blob: 02f91289855fc30cff2f33d15a092e0a420bf878 [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.
package importer
import (
"bytes"
"encoding/binary"
"fmt"
"go/ast"
"strings"
"llvm.org/llgo/third_party/gotools/go/exact"
"llvm.org/llgo/third_party/gotools/go/types"
)
// debugging support
const (
debug = false // emit debugging data
trace = false // print emitted data
)
// format returns a byte indicating the low-level encoding/decoding format
// (debug vs product).
func format() byte {
if debug {
return 'd'
}
return 'p'
}
// ExportData serializes the interface (exported package objects)
// of package pkg and returns the corresponding data. The export
// format is described elsewhere (TODO).
func ExportData(pkg *types.Package) []byte {
p := exporter{
data: append([]byte(magic), format()),
pkgIndex: make(map[*types.Package]int),
typIndex: make(map[types.Type]int),
}
// populate typIndex with predeclared types
for _, t := range predeclared {
p.typIndex[t] = len(p.typIndex)
}
if trace {
p.tracef("export %s\n", pkg.Name())
defer p.tracef("\n")
}
p.string(version)
p.pkg(pkg)
// collect exported objects from package scope
var list []types.Object
scope := pkg.Scope()
for _, name := range scope.Names() {
if exported(name) {
list = append(list, scope.Lookup(name))
}
}
// write objects
p.int(len(list))
for _, obj := range list {
p.obj(obj)
}
return p.data
}
type exporter struct {
data []byte
pkgIndex map[*types.Package]int
typIndex map[types.Type]int
// tracing support
indent string
}
func (p *exporter) pkg(pkg *types.Package) {
if trace {
p.tracef("package { ")
defer p.tracef("} ")
}
if pkg == nil {
panic("unexpected nil pkg")
}
// if the package was seen before, write its index (>= 0)
if i, ok := p.pkgIndex[pkg]; ok {
p.int(i)
return
}
p.pkgIndex[pkg] = len(p.pkgIndex)
// otherwise, write the package tag (< 0) and package data
p.int(packageTag)
p.string(pkg.Name())
p.string(pkg.Path())
}
func (p *exporter) obj(obj types.Object) {
if trace {
p.tracef("object %s {\n", obj.Name())
defer p.tracef("}\n")
}
switch obj := obj.(type) {
case *types.Const:
p.int(constTag)
p.string(obj.Name())
p.typ(obj.Type())
p.value(obj.Val())
case *types.TypeName:
p.int(typeTag)
// name is written by corresponding named type
p.typ(obj.Type().(*types.Named))
case *types.Var:
p.int(varTag)
p.string(obj.Name())
p.typ(obj.Type())
case *types.Func:
p.int(funcTag)
p.string(obj.Name())
p.typ(obj.Type())
default:
panic(fmt.Sprintf("unexpected object type %T", obj))
}
}
func (p *exporter) value(x exact.Value) {
if trace {
p.tracef("value { ")
defer p.tracef("} ")
}
switch kind := x.Kind(); kind {
case exact.Bool:
tag := falseTag
if exact.BoolVal(x) {
tag = trueTag
}
p.int(tag)
case exact.Int:
if i, ok := exact.Int64Val(x); ok {
p.int(int64Tag)
p.int64(i)
return
}
p.int(floatTag)
p.float(x)
case exact.Float:
p.int(fractionTag)
p.fraction(x)
case exact.Complex:
p.int(complexTag)
p.fraction(exact.Real(x))
p.fraction(exact.Imag(x))
case exact.String:
p.int(stringTag)
p.string(exact.StringVal(x))
default:
panic(fmt.Sprintf("unexpected value kind %d", kind))
}
}
func (p *exporter) float(x exact.Value) {
sign := exact.Sign(x)
p.int(sign)
if sign == 0 {
return
}
p.ufloat(x)
}
func (p *exporter) fraction(x exact.Value) {
sign := exact.Sign(x)
p.int(sign)
if sign == 0 {
return
}
p.ufloat(exact.Num(x))
p.ufloat(exact.Denom(x))
}
// ufloat writes abs(x) in form of a binary exponent
// followed by its mantissa bytes; x must be != 0.
func (p *exporter) ufloat(x exact.Value) {
mant := exact.Bytes(x)
exp8 := -1
for i, b := range mant {
if b != 0 {
exp8 = i
break
}
}
if exp8 < 0 {
panic(fmt.Sprintf("%s has no mantissa", x))
}
p.int(exp8 * 8)
p.bytes(mant[exp8:])
}
func (p *exporter) typ(typ types.Type) {
if trace {
p.tracef("type {\n")
defer p.tracef("}\n")
}
// if the type was seen before, write its index (>= 0)
if i, ok := p.typIndex[typ]; ok {
p.int(i)
return
}
p.typIndex[typ] = len(p.typIndex)
// otherwise, write the type tag (< 0) and type data
switch t := typ.(type) {
case *types.Array:
p.int(arrayTag)
p.int64(t.Len())
p.typ(t.Elem())
case *types.Slice:
p.int(sliceTag)
p.typ(t.Elem())
case *types.Struct:
p.int(structTag)
n := t.NumFields()
p.int(n)
for i := 0; i < n; i++ {
p.field(t.Field(i))
p.string(t.Tag(i))
}
case *types.Pointer:
p.int(pointerTag)
p.typ(t.Elem())
case *types.Signature:
p.int(signatureTag)
p.signature(t)
case *types.Interface:
p.int(interfaceTag)
// write embedded interfaces
m := t.NumEmbeddeds()
p.int(m)
for i := 0; i < m; i++ {
p.typ(t.Embedded(i))
}
// write methods
n := t.NumExplicitMethods()
p.int(n)
for i := 0; i < n; i++ {
m := t.ExplicitMethod(i)
p.qualifiedName(m.Pkg(), m.Name())
p.typ(m.Type())
}
case *types.Map:
p.int(mapTag)
p.typ(t.Key())
p.typ(t.Elem())
case *types.Chan:
p.int(chanTag)
p.int(int(t.Dir()))
p.typ(t.Elem())
case *types.Named:
p.int(namedTag)
// write type object
obj := t.Obj()
p.string(obj.Name())
p.pkg(obj.Pkg())
// write underlying type
p.typ(t.Underlying())
// write associated methods
n := t.NumMethods()
p.int(n)
for i := 0; i < n; i++ {
m := t.Method(i)
p.string(m.Name())
p.typ(m.Type())
}
default:
panic("unreachable")
}
}
func (p *exporter) field(f *types.Var) {
// anonymous fields have "" name
name := ""
if !f.Anonymous() {
name = f.Name()
}
// qualifiedName will always emit the field package for
// anonymous fields because "" is not an exported name.
p.qualifiedName(f.Pkg(), name)
p.typ(f.Type())
}
func (p *exporter) qualifiedName(pkg *types.Package, name string) {
p.string(name)
// exported names don't need package
if !exported(name) {
if pkg == nil {
panic(fmt.Sprintf("nil package for unexported qualified name %s", name))
}
p.pkg(pkg)
}
}
func (p *exporter) signature(sig *types.Signature) {
// We need the receiver information (T vs *T)
// for methods associated with named types.
// We do not record interface receiver types in the
// export data because 1) the importer can derive them
// from the interface type and 2) they create cycles
// in the type graph.
if recv := sig.Recv(); recv != nil {
if _, ok := recv.Type().Underlying().(*types.Interface); !ok {
// 1-element tuple
p.int(1)
p.param(recv)
} else {
// 0-element tuple
p.int(0)
}
} else {
// 0-element tuple
p.int(0)
}
p.tuple(sig.Params())
p.tuple(sig.Results())
if sig.Variadic() {
p.int(1)
} else {
p.int(0)
}
}
func (p *exporter) param(v *types.Var) {
p.string(v.Name())
p.typ(v.Type())
}
func (p *exporter) tuple(t *types.Tuple) {
n := t.Len()
p.int(n)
for i := 0; i < n; i++ {
p.param(t.At(i))
}
}
// ----------------------------------------------------------------------------
// encoders
func (p *exporter) string(s string) {
p.bytes([]byte(s)) // (could be inlined if extra allocation matters)
}
func (p *exporter) int(x int) {
p.int64(int64(x))
}
func (p *exporter) int64(x int64) {
if debug {
p.marker('i')
}
if trace {
p.tracef("%d ", x)
}
p.rawInt64(x)
}
func (p *exporter) bytes(b []byte) {
if debug {
p.marker('b')
}
if trace {
p.tracef("%q ", b)
}
p.rawInt64(int64(len(b)))
if len(b) > 0 {
p.data = append(p.data, b...)
}
}
// marker emits a marker byte and position information which makes
// it easy for a reader to detect if it is "out of sync". Used for
// debug format only.
func (p *exporter) marker(m byte) {
if debug {
p.data = append(p.data, m)
p.rawInt64(int64(len(p.data)))
}
}
// rawInt64 should only be used by low-level encoders
func (p *exporter) rawInt64(x int64) {
var tmp [binary.MaxVarintLen64]byte
n := binary.PutVarint(tmp[:], x)
p.data = append(p.data, tmp[:n]...)
}
// utility functions
func (p *exporter) tracef(format string, args ...interface{}) {
// rewrite format string to take care of indentation
const indent = ". "
if strings.IndexAny(format, "{}\n") >= 0 {
var buf bytes.Buffer
for i := 0; i < len(format); i++ {
// no need to deal with runes
ch := format[i]
switch ch {
case '{':
p.indent += indent
case '}':
p.indent = p.indent[:len(p.indent)-len(indent)]
if i+1 < len(format) && format[i+1] == '\n' {
buf.WriteByte('\n')
buf.WriteString(p.indent)
buf.WriteString("} ")
i++
continue
}
}
buf.WriteByte(ch)
if ch == '\n' {
buf.WriteString(p.indent)
}
}
format = buf.String()
}
fmt.Printf(format, args...)
}
func exported(name string) bool {
return ast.IsExported(name)
}