| // 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) |
| } |