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>
diff --git a/go/gcimporter15/bexport.go b/go/gcimporter15/bexport.go
index 36bcf03..ece6462 100644
--- a/go/gcimporter15/bexport.go
+++ b/go/gcimporter15/bexport.go
@@ -24,12 +24,31 @@
 	"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 @@
 		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 @@
 	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 @@
 		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 @@
 	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) 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 @@
 		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 @@
 		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 @@
 	}
 	sort.Sort(methodsByName(methods))
 
+	p.int(len(methods))
+
 	if trace && methods != nil {
 		p.tracef("associated methods {>\n")
 	}
@@ -302,9 +293,15 @@
 		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) 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) 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) 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 @@
 		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 @@
 		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 @@
 	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 (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) 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 @@
 // 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))
 }
 
diff --git a/go/gcimporter15/bimport.go b/go/gcimporter15/bimport.go
index ee96a5a..b9fb761 100644
--- a/go/gcimporter15/bimport.go
+++ b/go/gcimporter15/bimport.go
@@ -504,6 +504,8 @@
 		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))
 	}