go/gcimporter15: update gcexporter to match gc export format

Change-Id: Icd84cbef6463ba584a2a29f01b23c7e4542d0101
Reviewed-on: https://go-review.googlesource.com/21618
Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
Robert Griesemer 2016-04-06 14:13:27 -07:00
parent f07cde3d91
commit 83f918d66b
2 changed files with 107 additions and 120 deletions

View File

@ -24,12 +24,31 @@ import (
"strings"
)
// If debugFormat is set, each integer and string value is preceded by a marker
// and position information in the encoding. This mechanism permits an importer
// to recognize immediately when it is out of sync. The importer recognizes this
// mode automatically (i.e., it can import export data produced with debugging
// support even if debugFormat is not set at the time of import). This mode will
// lead to massively larger export data (by a factor of 2 to 3) and should only
// be enabled during development and debugging.
//
// NOTE: This flag is the first flag to enable if importing dies because of
// (suspected) format errors, and whenever a change is made to the format.
const debugFormat = false // default: false
// If trace is set, debugging output is printed to std out.
const trace = false // default: false
const exportVersion = "v0"
const (
debugFormat = false // use debugging format for export data (emits a lot of additional data)
trace = false
)
type exporter struct {
out bytes.Buffer
pkgIndex map[*types.Package]int
typIndex map[types.Type]int
written int // bytes written
indent int // for trace
}
// BExportData returns binary export data for pkg.
func BExportData(pkg *types.Package) []byte {
@ -38,7 +57,7 @@ func BExportData(pkg *types.Package) []byte {
typIndex: make(map[types.Type]int),
}
// write low-level encoding format
// first byte indicates low-level encoding format
var format byte = 'c' // compact
if debugFormat {
format = 'd'
@ -50,10 +69,13 @@ func BExportData(pkg *types.Package) []byte {
if trace {
p.tracef("\n--- generic export data ---\n")
if p.indent != 0 {
log.Fatalf("incorrect indentation %d", p.indent)
log.Fatalf("gcimporter: incorrect indentation %d", p.indent)
}
}
if trace {
p.tracef("version = ")
}
p.string(exportVersion)
if trace {
p.tracef("\n")
@ -64,97 +86,41 @@ func BExportData(pkg *types.Package) []byte {
p.typIndex[typ] = index
}
if len(p.typIndex) != len(predeclared) {
log.Fatalf("duplicate entries in type map?")
log.Fatalf("gcimporter: duplicate entries in type map?")
}
// write package data
p.pkg(pkg, true)
// write compiler-specific flags
p.string("")
p.string("") // no flags to write in our case
if trace {
p.tracef("\n")
}
// Collect objects to export, already sorted by name.
var consts []*types.Const
var vars []*types.Var
var funcs []*types.Func
var typs []*types.TypeName
// write objects
objcount := 0
scope := pkg.Scope()
for _, name := range scope.Names() {
if !ast.IsExported(name) {
continue
}
switch obj := scope.Lookup(name).(type) {
case *types.Const:
consts = append(consts, obj)
case *types.Var:
vars = append(vars, obj)
case *types.Func:
funcs = append(funcs, obj)
case *types.TypeName:
typs = append(typs, obj)
if trace {
p.tracef("\n")
}
p.obj(scope.Lookup(name))
objcount++
}
// write consts
p.int(len(consts))
for _, obj := range consts {
p.string(obj.Name())
p.typ(obj.Type())
p.value(obj.Val())
}
// write vars
p.int(len(vars))
for _, obj := range vars {
p.string(obj.Name())
p.typ(obj.Type())
}
// write funcs
p.int(len(funcs))
for _, obj := range funcs {
p.string(obj.Name())
sig := obj.Type().(*types.Signature)
p.paramList(sig.Params(), sig.Variadic())
p.paramList(sig.Results(), false)
p.int(-1) // no inlined function bodies
}
// Determine which types are still left to write.
i := 0
for _, t := range typs {
if _, ok := p.typIndex[t.Type()]; !ok {
typs[i] = t
i++
}
}
typs = typs[:i]
// Write types.
p.int(len(typs))
for _, t := range typs {
// Writing a type may further reduce the number of types
// that are left to be written, but at this point we don't
// care.
p.typ(t.Type())
}
// indicate end of list
if trace {
p.tracef("\n")
}
p.tag(endTag)
// --- compiler-specific export data ---
if trace {
p.tracef("\n--- compiler specific export data ---\n")
if p.indent != 0 {
log.Fatalf("incorrect indentation")
}
}
// for self-verification only (redundant)
p.int(objcount)
if trace {
p.tracef("\n")
@ -165,19 +131,9 @@ func BExportData(pkg *types.Package) []byte {
return p.out.Bytes()
}
type exporter struct {
out bytes.Buffer
pkgIndex map[*types.Package]int
typIndex map[types.Type]int
written int // bytes written
indent int // for trace
trace bool
}
func (p *exporter) pkg(pkg *types.Package, emptypath bool) {
if pkg == nil {
log.Fatalf("unexpected nil pkg")
log.Fatalf("gcimporter: unexpected nil pkg")
}
// if we saw the package before, write its index (>= 0)
@ -202,9 +158,44 @@ func (p *exporter) pkg(pkg *types.Package, emptypath bool) {
}
}
func (p *exporter) obj(obj types.Object) {
switch obj := obj.(type) {
case *types.Const:
p.tag(constTag)
p.qualifiedName(obj)
p.typ(obj.Type())
p.value(obj.Val())
case *types.TypeName:
p.tag(typeTag)
p.typ(obj.Type())
case *types.Var:
p.tag(varTag)
p.qualifiedName(obj)
p.typ(obj.Type())
case *types.Func:
p.tag(funcTag)
p.qualifiedName(obj)
sig := obj.Type().(*types.Signature)
p.paramList(sig.Params(), sig.Variadic())
p.paramList(sig.Results(), false)
p.int(-1) // no inlined function bodies
default:
log.Fatalf("gcimporter: unexpected object %v (%T)", obj, obj)
}
}
func (p *exporter) qualifiedName(obj types.Object) {
p.string(obj.Name())
p.pkg(obj.Pkg(), false)
}
func (p *exporter) typ(t types.Type) {
if t == nil {
log.Fatalf("nil type")
log.Fatalf("gcimporter: nil type")
}
// Possible optimization: Anonymous pointer types *T where
@ -236,7 +227,7 @@ func (p *exporter) typ(t types.Type) {
p.qualifiedName(t.Obj())
p.typ(t.Underlying())
if !types.IsInterface(t) {
p.declaredMethods(t)
p.assocMethods(t)
}
case *types.Array:
@ -280,13 +271,11 @@ func (p *exporter) typ(t types.Type) {
p.typ(t.Elem())
default:
log.Fatalf("unexpected type %T: %s", t, t)
log.Fatalf("gcimporter: unexpected type %T: %s", t, t)
}
}
func (p *exporter) declaredMethods(named *types.Named) {
p.int(named.NumMethods())
func (p *exporter) assocMethods(named *types.Named) {
// Sort methods (for determinism).
var methods []*types.Func
for i := 0; i < named.NumMethods(); i++ {
@ -294,6 +283,8 @@ func (p *exporter) declaredMethods(named *types.Named) {
}
sort.Sort(methodsByName(methods))
p.int(len(methods))
if trace && methods != nil {
p.tracef("associated methods {>\n")
}
@ -302,9 +293,15 @@ func (p *exporter) declaredMethods(named *types.Named) {
if trace && i > 0 {
p.tracef("\n")
}
p.string(m.Name())
name := m.Name()
p.string(name)
if !exported(name) {
p.pkg(m.Pkg(), false)
}
sig := m.Type().(*types.Signature)
p.recv(sig.Recv())
p.paramList(types.NewTuple(sig.Recv()), false)
p.paramList(sig.Params(), sig.Variadic())
p.paramList(sig.Results(), false)
p.int(-1) // no inlining
@ -321,24 +318,6 @@ func (x methodsByName) Len() int { return len(x) }
func (x methodsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x methodsByName) Less(i, j int) bool { return x[i].Name() < x[j].Name() }
func (p *exporter) recv(recv *types.Var) {
// Use negative length to indicate unnamed parameter.
if recv.Name() == "" {
p.int(-1)
p.typ(recv.Type())
} else {
p.int(1)
p.typ(recv.Type())
p.string(recv.Name())
}
p.string("")
}
func (p *exporter) qualifiedName(obj types.Object) {
p.string(obj.Name())
p.pkg(obj.Pkg(), false)
}
func (p *exporter) fieldList(t *types.Struct) {
if trace && t.NumFields() > 0 {
p.tracef("fields {>\n")
@ -357,7 +336,7 @@ func (p *exporter) fieldList(t *types.Struct) {
func (p *exporter) field(f *types.Var) {
if !f.IsField() {
log.Fatalf("field expected")
log.Fatalf("gcimporter: field expected")
}
p.fieldName(f)
@ -386,7 +365,7 @@ func (p *exporter) iface(t *types.Interface) {
func (p *exporter) method(m *types.Func) {
sig := m.Type().(*types.Signature)
if sig.Recv() == nil {
log.Fatalf("method expected")
log.Fatalf("gcimporter: method expected")
}
p.string(m.Name())
@ -440,8 +419,9 @@ func (p *exporter) paramList(params *types.Tuple, variadic bool) {
p.typ(t)
if n > 0 {
p.string(q.Name())
p.pkg(q.Pkg(), false)
}
p.string("")
p.string("") // no compiler-specific info
}
}
@ -484,17 +464,17 @@ func (p *exporter) value(x constant.Value) {
p.string(constant.StringVal(x))
case constant.Unknown:
// (Package contains type errors.)
// package contains type errors
p.tag(unknownTag)
default:
log.Fatalf("unexpected value %v (%T)", x, x)
log.Fatalf("gcimporter: unexpected value %v (%T)", x, x)
}
}
func (p *exporter) float(x constant.Value) {
if x.Kind() != constant.Float {
log.Fatalf("unexpected constant %v, want float", x)
log.Fatalf("gcimporter: unexpected constant %v, want float", x)
}
// extract sign (there is no -0)
sign := constant.Sign(x)
@ -529,7 +509,7 @@ func (p *exporter) float(x constant.Value) {
m.SetMantExp(&m, int(m.MinPrec()))
mant, acc := m.Int(nil)
if acc != big.Exact {
log.Fatalf("internal error")
log.Fatalf("gcimporter: internal error")
}
p.int(sign)
@ -552,7 +532,7 @@ func valueToRat(x constant.Value) *big.Rat {
func (p *exporter) index(marker byte, index int) {
if index < 0 {
log.Fatalf("invalid index < 0")
log.Fatalf("gcimporter: invalid index < 0")
}
if debugFormat {
p.marker('t')
@ -565,7 +545,7 @@ func (p *exporter) index(marker byte, index int) {
func (p *exporter) tag(tag int) {
if tag >= 0 {
log.Fatalf("invalid tag >= 0")
log.Fatalf("gcimporter: invalid tag >= 0")
}
if debugFormat {
p.marker('t')
@ -608,6 +588,11 @@ func (p *exporter) string(s string) {
// debugFormat format only.
func (p *exporter) marker(m byte) {
p.byte(m)
// Enable this for help tracking down the location
// of an incorrect marker when running in debugFormat.
if false && trace {
p.tracef("#%d ", p.written)
}
p.rawInt64(int64(p.written))
}

View File

@ -504,6 +504,8 @@ func (p *importer) value() constant.Value {
return constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
case stringTag:
return constant.MakeString(p.string())
case unknownTag:
return constant.MakeUnknown()
default:
panic(fmt.Sprintf("unexpected value tag %d", tag))
}