mirror of
https://github.com/golang/go.git
synced 2025-05-08 00:53:07 +00:00
This removes almost all direct access to Type’s heavily overloaded Type field. Mostly generated by eg, manually checked. Significant manual changes: * reflect.go's typPkg used Type indiscriminately. Use it only for specific etypes. * gen.go's visitComponents contained a usage of Type with structs. Using Type for structs no longer occurs, and the Fatal contained therein has not triggered, so it has been axed. * Scary code in cgen.go's cgen_slice is now explicitly scary. Passes toolstash -cmp. Change-Id: I2dbfb3c959da7ae239f964d83898c204affcabc6 Reviewed-on: https://go-review.googlesource.com/21331 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com> Reviewed-by: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
1304 lines
28 KiB
Go
1304 lines
28 KiB
Go
// Copyright 2015 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.
|
|
|
|
// Binary package export.
|
|
// Based loosely on x/tools/go/importer.
|
|
// (see fmt.go, parser.go as "documentation" for how to use/setup data structures)
|
|
//
|
|
// Use "-newexport" flag to enable.
|
|
|
|
/*
|
|
Export data encoding:
|
|
|
|
The export data is a serialized description of the graph of exported
|
|
objects: constants, types, variables, and functions. Only types can
|
|
be re-exported and so we need to know which package they are coming
|
|
from. Therefore, packages are also part of the export graph.
|
|
|
|
The roots of the graph are the list of constants, variables, functions,
|
|
and eventually types. Types are written last because most of them will
|
|
be written as part of other objects which will reduce the number of
|
|
types that need to be written separately.
|
|
|
|
The graph is serialized in in-order fashion, starting with the roots.
|
|
Each object in the graph is serialized by writing its fields sequentially.
|
|
If the field is a pointer to another object, that object is serialized,
|
|
recursively. Otherwise the field is written. Non-pointer fields are all
|
|
encoded as either an integer or string value.
|
|
|
|
Only packages and types may be referred to more than once. When getting
|
|
to a package or type that was not serialized before, an integer _index_
|
|
is assigned to it, starting at 0. In this case, the encoding starts
|
|
with an integer _tag_ < 0. The tag value indicates the kind of object
|
|
(package or type) that follows and that this is the first time that we
|
|
see this object. If the package or tag was already serialized, the encoding
|
|
starts with the respective package or type index >= 0. An importer can
|
|
trivially determine if a package or type needs to be read in for the first
|
|
time (tag < 0) and entered into the respective package or type table, or
|
|
if the package or type was seen already (index >= 0), in which case the
|
|
index is used to look up the object in a table.
|
|
|
|
Before exporting or importing, the type tables are populated with the
|
|
predeclared types (int, string, error, unsafe.Pointer, etc.). This way
|
|
they are automatically encoded with a known and fixed type index.
|
|
|
|
Encoding format:
|
|
|
|
The export data starts with a single byte indicating the encoding format
|
|
(compact, or with debugging information), followed by a version string
|
|
(so we can evolve the encoding if need be), the name of the imported
|
|
package, and a string containing platform-specific information for that
|
|
package.
|
|
|
|
After this header, the lists of objects follow. After the objects, platform-
|
|
specific data may be found which is not used strictly for type checking.
|
|
|
|
The encoding of objects is straight-forward: Constants, variables, and
|
|
functions start with their name, type, and possibly a value. Named types
|
|
record their name and package so that they can be canonicalized: If the
|
|
same type was imported before via another import, the importer must use
|
|
the previously imported type pointer so that we have exactly one version
|
|
(i.e., one pointer) for each named type (and read but discard the current
|
|
type encoding). Unnamed types simply encode their respective fields.
|
|
|
|
In the encoding, any list (of objects, struct fields, methods, parameter
|
|
names, but also the bytes of a string, etc.) starts with the list length.
|
|
This permits an importer to allocate the right amount of memory for the
|
|
list upfront, without the need to grow it later.
|
|
|
|
All integer values use variable-length encoding for compact representation.
|
|
|
|
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.
|
|
|
|
The exporter and importer are completely symmetric in implementation: For
|
|
each encoding routine there is a matching and symmetric decoding routine.
|
|
This symmetry makes it very easy to change or extend the format: If a new
|
|
field needs to be encoded, a symmetric change can be made to exporter and
|
|
importer.
|
|
*/
|
|
|
|
package gc
|
|
|
|
import (
|
|
"bytes"
|
|
"cmd/compile/internal/big"
|
|
"cmd/internal/obj"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// debugging support
|
|
const (
|
|
debugFormat = false // use debugging format for export data (emits a lot of additional data)
|
|
)
|
|
|
|
// TODO(gri) remove eventually
|
|
const forceNewExport = false // force new export format - do not submit with this flag set
|
|
|
|
const exportVersion = "v0"
|
|
|
|
// Export writes the export data for localpkg to out and returns the number of bytes written.
|
|
func Export(out *obj.Biobuf, trace bool) int {
|
|
p := exporter{
|
|
out: out,
|
|
pkgIndex: make(map[*Pkg]int),
|
|
typIndex: make(map[*Type]int),
|
|
trace: trace,
|
|
}
|
|
|
|
// write low-level encoding format
|
|
var format byte = 'c' // compact
|
|
if debugFormat {
|
|
format = 'd'
|
|
}
|
|
p.byte(format)
|
|
|
|
// --- generic export data ---
|
|
|
|
if p.trace {
|
|
p.tracef("\n--- package ---\n")
|
|
if p.indent != 0 {
|
|
Fatalf("exporter: incorrect indentation %d", p.indent)
|
|
}
|
|
}
|
|
|
|
if p.trace {
|
|
p.tracef("version = ")
|
|
}
|
|
p.string(exportVersion)
|
|
if p.trace {
|
|
p.tracef("\n")
|
|
}
|
|
|
|
// populate type map with predeclared "known" types
|
|
predecl := predeclared()
|
|
for index, typ := range predecl {
|
|
p.typIndex[typ] = index
|
|
}
|
|
if len(p.typIndex) != len(predecl) {
|
|
Fatalf("exporter: duplicate entries in type map?")
|
|
}
|
|
|
|
// write package data
|
|
if localpkg.Path != "" {
|
|
Fatalf("exporter: local package path not empty: %q", localpkg.Path)
|
|
}
|
|
p.pkg(localpkg)
|
|
|
|
// write compiler-specific flags
|
|
{
|
|
var flags string
|
|
if safemode != 0 {
|
|
flags = "safe"
|
|
}
|
|
p.string(flags)
|
|
}
|
|
if p.trace {
|
|
p.tracef("\n")
|
|
}
|
|
|
|
// collect objects to export
|
|
var consts, vars, funcs []*Sym
|
|
var types []*Type
|
|
for _, n := range exportlist {
|
|
sym := n.Sym
|
|
// TODO(gri) Closures appear marked as exported.
|
|
// Investigate and determine if we need this.
|
|
if sym.Flags&SymExported != 0 {
|
|
continue
|
|
}
|
|
sym.Flags |= SymExported
|
|
|
|
// TODO(gri) Closures have dots in their names;
|
|
// e.g., TestFloatZeroValue.func1 in math/big tests.
|
|
// We may not need this eventually. See also comment
|
|
// on sym.Flags&SymExported test above.
|
|
if strings.Contains(sym.Name, ".") {
|
|
Fatalf("exporter: unexpected symbol: %v", sym)
|
|
}
|
|
|
|
if sym.Flags&SymExport != 0 {
|
|
if sym.Def == nil {
|
|
Fatalf("exporter: unknown export symbol: %v", sym)
|
|
}
|
|
switch n := sym.Def; n.Op {
|
|
case OLITERAL:
|
|
// constant
|
|
n = typecheck(n, Erv)
|
|
if n == nil || n.Op != OLITERAL {
|
|
Fatalf("exporter: dumpexportconst: oconst nil: %v", sym)
|
|
}
|
|
consts = append(consts, sym)
|
|
|
|
case ONAME:
|
|
// variable or function
|
|
n = typecheck(n, Erv|Ecall)
|
|
if n == nil || n.Type == nil {
|
|
Fatalf("exporter: variable/function exported but not defined: %v", sym)
|
|
}
|
|
if n.Type.Etype == TFUNC && n.Class == PFUNC {
|
|
funcs = append(funcs, sym)
|
|
} else {
|
|
vars = append(vars, sym)
|
|
}
|
|
|
|
case OTYPE:
|
|
// named type
|
|
t := n.Type
|
|
if t.Etype == TFORW {
|
|
Fatalf("exporter: export of incomplete type %v", sym)
|
|
}
|
|
types = append(types, t)
|
|
|
|
default:
|
|
Fatalf("exporter: unexpected export symbol: %v %v", Oconv(n.Op, 0), sym)
|
|
}
|
|
}
|
|
}
|
|
exportlist = nil // match export.go use of exportlist
|
|
|
|
// for reproducible output
|
|
sort.Sort(symByName(consts))
|
|
sort.Sort(symByName(vars))
|
|
sort.Sort(symByName(funcs))
|
|
// sort types later when we have fewer types left
|
|
|
|
// write consts
|
|
if p.trace {
|
|
p.tracef("\n--- consts ---\n[ ")
|
|
}
|
|
p.int(len(consts))
|
|
if p.trace {
|
|
p.tracef("]\n")
|
|
}
|
|
for _, sym := range consts {
|
|
p.string(sym.Name)
|
|
n := sym.Def
|
|
p.typ(unidealType(n.Type, n.Val()))
|
|
p.value(n.Val())
|
|
if p.trace {
|
|
p.tracef("\n")
|
|
}
|
|
}
|
|
|
|
// write vars
|
|
if p.trace {
|
|
p.tracef("\n--- vars ---\n[ ")
|
|
}
|
|
p.int(len(vars))
|
|
if p.trace {
|
|
p.tracef("]\n")
|
|
}
|
|
for _, sym := range vars {
|
|
p.string(sym.Name)
|
|
p.typ(sym.Def.Type)
|
|
if p.trace {
|
|
p.tracef("\n")
|
|
}
|
|
}
|
|
|
|
// write funcs
|
|
if p.trace {
|
|
p.tracef("\n--- funcs ---\n[ ")
|
|
}
|
|
p.int(len(funcs))
|
|
if p.trace {
|
|
p.tracef("]\n")
|
|
}
|
|
for _, sym := range funcs {
|
|
p.string(sym.Name)
|
|
sig := sym.Def.Type
|
|
inlineable := p.isInlineable(sym.Def)
|
|
p.paramList(sig.Params(), inlineable)
|
|
p.paramList(sig.Results(), inlineable)
|
|
index := -1
|
|
if inlineable {
|
|
index = len(p.inlined)
|
|
p.inlined = append(p.inlined, sym.Def.Func)
|
|
}
|
|
p.int(index)
|
|
if p.trace {
|
|
p.tracef("\n")
|
|
}
|
|
}
|
|
|
|
// determine which types are still left to write and sort them
|
|
i := 0
|
|
for _, t := range types {
|
|
if _, ok := p.typIndex[t]; !ok {
|
|
types[i] = t
|
|
i++
|
|
}
|
|
}
|
|
types = types[:i]
|
|
sort.Sort(typByName(types))
|
|
|
|
// write types
|
|
if p.trace {
|
|
p.tracef("\n--- types ---\n[ ")
|
|
}
|
|
p.int(len(types))
|
|
if p.trace {
|
|
p.tracef("]\n")
|
|
}
|
|
for _, t := range types {
|
|
// 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)
|
|
if p.trace {
|
|
p.tracef("\n")
|
|
}
|
|
}
|
|
|
|
// --- compiler-specific export data ---
|
|
|
|
if p.trace {
|
|
p.tracef("\n--- inlined function bodies ---\n[ ")
|
|
if p.indent != 0 {
|
|
Fatalf("exporter: incorrect indentation")
|
|
}
|
|
}
|
|
|
|
// write inlined function bodies
|
|
p.int(len(p.inlined))
|
|
if p.trace {
|
|
p.tracef("]\n")
|
|
}
|
|
for _, f := range p.inlined {
|
|
if p.trace {
|
|
p.tracef("{ %s }\n", Hconv(f.Inl, FmtSharp))
|
|
}
|
|
p.nodeList(f.Inl)
|
|
if p.trace {
|
|
p.tracef("\n")
|
|
}
|
|
}
|
|
|
|
if p.trace {
|
|
p.tracef("\n--- end ---\n")
|
|
}
|
|
|
|
// --- end of export data ---
|
|
|
|
return p.written
|
|
}
|
|
|
|
func unidealType(typ *Type, val Val) *Type {
|
|
// Untyped (ideal) constants get their own type. This decouples
|
|
// the constant type from the encoding of the constant value.
|
|
if typ == nil || isideal(typ) {
|
|
typ = untype(val.Ctype())
|
|
}
|
|
return typ
|
|
}
|
|
|
|
type symByName []*Sym
|
|
|
|
func (a symByName) Len() int { return len(a) }
|
|
func (a symByName) Less(i, j int) bool { return a[i].Name < a[j].Name }
|
|
func (a symByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
type typByName []*Type
|
|
|
|
func (a typByName) Len() int { return len(a) }
|
|
func (a typByName) Less(i, j int) bool { return a[i].Sym.Name < a[j].Sym.Name }
|
|
func (a typByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
type exporter struct {
|
|
out *obj.Biobuf
|
|
pkgIndex map[*Pkg]int
|
|
typIndex map[*Type]int
|
|
inlined []*Func
|
|
|
|
written int // bytes written
|
|
indent int // for p.trace
|
|
trace bool
|
|
}
|
|
|
|
func (p *exporter) pkg(pkg *Pkg) {
|
|
if pkg == nil {
|
|
Fatalf("exporter: unexpected nil pkg")
|
|
}
|
|
|
|
// if we saw the package before, write its index (>= 0)
|
|
if i, ok := p.pkgIndex[pkg]; ok {
|
|
p.index('P', i)
|
|
return
|
|
}
|
|
|
|
// otherwise, remember the package, write the package tag (< 0) and package data
|
|
if p.trace {
|
|
p.tracef("P%d = { ", len(p.pkgIndex))
|
|
defer p.tracef("} ")
|
|
}
|
|
p.pkgIndex[pkg] = len(p.pkgIndex)
|
|
|
|
p.tag(packageTag)
|
|
p.string(pkg.Name)
|
|
p.string(pkg.Path)
|
|
}
|
|
|
|
func (p *exporter) typ(t *Type) {
|
|
if t == nil {
|
|
Fatalf("exporter: nil type")
|
|
}
|
|
|
|
// Possible optimization: Anonymous pointer types *T where
|
|
// T is a named type are common. We could canonicalize all
|
|
// such types *T to a single type PT = *T. This would lead
|
|
// to at most one *T entry in typIndex, and all future *T's
|
|
// would be encoded as the respective index directly. Would
|
|
// save 1 byte (pointerTag) per *T and reduce the typIndex
|
|
// size (at the cost of a canonicalization map). We can do
|
|
// this later, without encoding format change.
|
|
|
|
// if we saw the type before, write its index (>= 0)
|
|
if i, ok := p.typIndex[t]; ok {
|
|
p.index('T', i)
|
|
return
|
|
}
|
|
|
|
// otherwise, remember the type, write the type tag (< 0) and type data
|
|
if p.trace {
|
|
p.tracef("T%d = {>\n", len(p.typIndex))
|
|
defer p.tracef("<\n} ")
|
|
}
|
|
p.typIndex[t] = len(p.typIndex)
|
|
|
|
// pick off named types
|
|
if sym := t.Sym; sym != nil {
|
|
// Predeclared types should have been found in the type map.
|
|
if t.Orig == t {
|
|
Fatalf("exporter: predeclared type missing from type map?")
|
|
}
|
|
// TODO(gri) The assertion below seems incorrect (crashes during all.bash).
|
|
// Investigate.
|
|
/*
|
|
// we expect the respective definition to point to us
|
|
if sym.Def.Type != t {
|
|
Fatalf("exporter: type definition doesn't point to us?")
|
|
}
|
|
*/
|
|
|
|
p.tag(namedTag)
|
|
p.qualifiedName(sym)
|
|
|
|
// write underlying type
|
|
p.typ(t.Orig)
|
|
|
|
// interfaces don't have associated methods
|
|
if t.Orig.Etype == TINTER {
|
|
return
|
|
}
|
|
|
|
// sort methods for reproducible export format
|
|
// TODO(gri) Determine if they are already sorted
|
|
// in which case we can drop this step.
|
|
var methods []*Field
|
|
for _, m := range t.Methods().Slice() {
|
|
methods = append(methods, m)
|
|
}
|
|
sort.Sort(methodbyname(methods))
|
|
p.int(len(methods))
|
|
|
|
if p.trace && len(methods) > 0 {
|
|
p.tracef("associated methods {>")
|
|
}
|
|
|
|
for _, m := range methods {
|
|
if p.trace {
|
|
p.tracef("\n")
|
|
}
|
|
p.string(m.Sym.Name)
|
|
sig := m.Type
|
|
inlineable := p.isInlineable(sig.Nname)
|
|
p.paramList(sig.Recvs(), inlineable)
|
|
p.paramList(sig.Params(), inlineable)
|
|
p.paramList(sig.Results(), inlineable)
|
|
index := -1
|
|
if inlineable {
|
|
index = len(p.inlined)
|
|
p.inlined = append(p.inlined, sig.Nname.Func)
|
|
}
|
|
p.int(index)
|
|
}
|
|
|
|
if p.trace && len(methods) > 0 {
|
|
p.tracef("<\n} ")
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// otherwise we have a type literal
|
|
switch t.Etype {
|
|
case TARRAY:
|
|
if t.isDDDArray() {
|
|
Fatalf("array bounds should be known at export time: %v", t)
|
|
}
|
|
if t.IsArray() {
|
|
p.tag(arrayTag)
|
|
p.int64(t.Bound)
|
|
} else {
|
|
p.tag(sliceTag)
|
|
}
|
|
p.typ(t.Elem())
|
|
|
|
case TDDDFIELD:
|
|
// see p.param use of TDDDFIELD
|
|
p.tag(dddTag)
|
|
p.typ(t.Wrapped())
|
|
|
|
case TSTRUCT:
|
|
p.tag(structTag)
|
|
p.fieldList(t)
|
|
|
|
case TPTR32, TPTR64: // could use Tptr but these are constants
|
|
p.tag(pointerTag)
|
|
p.typ(t.Elem())
|
|
|
|
case TFUNC:
|
|
p.tag(signatureTag)
|
|
p.paramList(t.Params(), false)
|
|
p.paramList(t.Results(), false)
|
|
|
|
case TINTER:
|
|
p.tag(interfaceTag)
|
|
|
|
// gc doesn't separate between embedded interfaces
|
|
// and methods declared explicitly with an interface
|
|
p.int(0) // no embedded interfaces
|
|
p.methodList(t)
|
|
|
|
case TMAP:
|
|
p.tag(mapTag)
|
|
p.typ(t.Key())
|
|
p.typ(t.Val())
|
|
|
|
case TCHAN:
|
|
p.tag(chanTag)
|
|
p.int(int(t.Chan))
|
|
p.typ(t.Elem())
|
|
|
|
default:
|
|
Fatalf("exporter: unexpected type: %s (Etype = %d)", Tconv(t, 0), t.Etype)
|
|
}
|
|
}
|
|
|
|
func (p *exporter) qualifiedName(sym *Sym) {
|
|
p.string(sym.Name)
|
|
p.pkg(sym.Pkg)
|
|
}
|
|
|
|
func (p *exporter) fieldList(t *Type) {
|
|
if p.trace && t.NumFields() > 0 {
|
|
p.tracef("fields {>")
|
|
defer p.tracef("<\n} ")
|
|
}
|
|
|
|
p.int(t.NumFields())
|
|
for _, f := range t.Fields().Slice() {
|
|
if p.trace {
|
|
p.tracef("\n")
|
|
}
|
|
p.field(f)
|
|
}
|
|
}
|
|
|
|
func (p *exporter) field(f *Field) {
|
|
p.fieldName(f)
|
|
p.typ(f.Type)
|
|
p.note(f.Note)
|
|
}
|
|
|
|
func (p *exporter) note(n *string) {
|
|
var s string
|
|
if n != nil {
|
|
s = *n
|
|
}
|
|
p.string(s)
|
|
}
|
|
|
|
func (p *exporter) methodList(t *Type) {
|
|
if p.trace && t.NumFields() > 0 {
|
|
p.tracef("methods {>")
|
|
defer p.tracef("<\n} ")
|
|
}
|
|
|
|
p.int(t.NumFields())
|
|
for _, m := range t.Fields().Slice() {
|
|
if p.trace {
|
|
p.tracef("\n")
|
|
}
|
|
p.method(m)
|
|
}
|
|
}
|
|
|
|
func (p *exporter) method(m *Field) {
|
|
p.fieldName(m)
|
|
// TODO(gri) For functions signatures, we use p.typ() to export
|
|
// so we could share the same type with multiple functions. Do
|
|
// the same here, or never try to do this for functions.
|
|
p.paramList(m.Type.Params(), false)
|
|
p.paramList(m.Type.Results(), false)
|
|
}
|
|
|
|
// fieldName is like qualifiedName but it doesn't record the package
|
|
// for blank (_) or exported names.
|
|
func (p *exporter) fieldName(t *Field) {
|
|
sym := t.Sym
|
|
|
|
var name string
|
|
if t.Embedded == 0 {
|
|
name = sym.Name
|
|
} else if bname := basetypeName(t.Type); bname != "" && !exportname(bname) {
|
|
// anonymous field with unexported base type name: use "?" as field name
|
|
// (bname != "" per spec, but we are conservative in case of errors)
|
|
name = "?"
|
|
}
|
|
|
|
p.string(name)
|
|
if name == "?" || name != "_" && name != "" && !exportname(name) {
|
|
p.pkg(sym.Pkg)
|
|
}
|
|
}
|
|
|
|
func basetypeName(t *Type) string {
|
|
s := t.Sym
|
|
if s == nil && Isptr[t.Etype] {
|
|
s = t.Elem().Sym // deref
|
|
}
|
|
if s != nil {
|
|
return s.Name
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (p *exporter) paramList(params *Type, numbered bool) {
|
|
if params.Etype != TSTRUCT || !params.Funarg {
|
|
Fatalf("exporter: parameter list expected")
|
|
}
|
|
|
|
// use negative length to indicate unnamed parameters
|
|
// (look at the first parameter only since either all
|
|
// names are present or all are absent)
|
|
n := params.NumFields()
|
|
if n > 0 && parName(params.Field(0), numbered) == "" {
|
|
n = -n
|
|
}
|
|
p.int(n)
|
|
for _, q := range params.Fields().Slice() {
|
|
p.param(q, n, numbered)
|
|
}
|
|
}
|
|
|
|
func (p *exporter) param(q *Field, n int, numbered bool) {
|
|
t := q.Type
|
|
if q.Isddd {
|
|
// create a fake type to encode ... just for the p.typ call
|
|
t = typWrapper(TDDDFIELD, t.Elem())
|
|
}
|
|
p.typ(t)
|
|
if n > 0 {
|
|
p.string(parName(q, numbered))
|
|
}
|
|
// TODO(gri) This is compiler-specific (escape info).
|
|
// Move into compiler-specific section eventually?
|
|
// (Not having escape info causes tests to fail, e.g. runtime GCInfoTest)
|
|
//
|
|
// TODO(gri) The q.Note is much more verbose that necessary and
|
|
// adds significantly to export data size. FIX THIS.
|
|
p.note(q.Note)
|
|
}
|
|
|
|
func parName(q *Field, numbered bool) string {
|
|
if q.Sym == nil {
|
|
return ""
|
|
}
|
|
name := q.Sym.Name
|
|
// undo gc-internal name mangling - we just need the source name
|
|
if len(name) > 0 && name[0] == '~' {
|
|
// name is ~b%d or ~r%d
|
|
switch name[1] {
|
|
case 'b':
|
|
return "_"
|
|
case 'r':
|
|
return ""
|
|
default:
|
|
Fatalf("exporter: unexpected parameter name: %s", name)
|
|
}
|
|
}
|
|
// undo gc-internal name specialization unless required
|
|
if !numbered {
|
|
if i := strings.Index(name, "·"); i > 0 {
|
|
name = name[:i] // cut off numbering
|
|
}
|
|
}
|
|
return name
|
|
}
|
|
|
|
func (p *exporter) value(x Val) {
|
|
if p.trace {
|
|
p.tracef("= ")
|
|
}
|
|
|
|
switch x := x.U.(type) {
|
|
case bool:
|
|
tag := falseTag
|
|
if x {
|
|
tag = trueTag
|
|
}
|
|
p.tag(tag)
|
|
|
|
case *Mpint:
|
|
if Minintval[TINT64].Cmp(x) <= 0 && x.Cmp(Maxintval[TINT64]) <= 0 {
|
|
// common case: x fits into an int64 - use compact encoding
|
|
p.tag(int64Tag)
|
|
p.int64(x.Int64())
|
|
return
|
|
}
|
|
// uncommon case: large x - use float encoding
|
|
// (powers of 2 will be encoded efficiently with exponent)
|
|
f := newMpflt()
|
|
f.SetInt(x)
|
|
p.tag(floatTag)
|
|
p.float(f)
|
|
|
|
case *Mpflt:
|
|
p.tag(floatTag)
|
|
p.float(x)
|
|
|
|
case *Mpcplx:
|
|
p.tag(complexTag)
|
|
p.float(&x.Real)
|
|
p.float(&x.Imag)
|
|
|
|
case string:
|
|
p.tag(stringTag)
|
|
p.string(x)
|
|
|
|
case *NilVal:
|
|
// not a constant but used in exported function bodies
|
|
p.tag(nilTag)
|
|
|
|
default:
|
|
Fatalf("exporter: unexpected value %v (%T)", x, x)
|
|
}
|
|
}
|
|
|
|
func (p *exporter) float(x *Mpflt) {
|
|
// extract sign (there is no -0)
|
|
f := &x.Val
|
|
sign := f.Sign()
|
|
if sign == 0 {
|
|
// x == 0
|
|
p.int(0)
|
|
return
|
|
}
|
|
// x != 0
|
|
|
|
// extract exponent such that 0.5 <= m < 1.0
|
|
var m big.Float
|
|
exp := f.MantExp(&m)
|
|
|
|
// extract mantissa as *big.Int
|
|
// - set exponent large enough so mant satisfies mant.IsInt()
|
|
// - get *big.Int from mant
|
|
m.SetMantExp(&m, int(m.MinPrec()))
|
|
mant, acc := m.Int(nil)
|
|
if acc != big.Exact {
|
|
Fatalf("exporter: internal error")
|
|
}
|
|
|
|
p.int(sign)
|
|
p.int(exp)
|
|
p.string(string(mant.Bytes()))
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Inlined function bodies
|
|
|
|
func (p *exporter) isInlineable(n *Node) bool {
|
|
if n != nil && n.Func != nil && len(n.Func.Inl.Slice()) != 0 {
|
|
// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
|
|
// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
|
|
if Debug['l'] < 2 {
|
|
typecheckinl(n)
|
|
}
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (p *exporter) nodeList(list Nodes) {
|
|
if p.trace {
|
|
p.tracef("[ ")
|
|
}
|
|
p.int(list.Len())
|
|
if p.trace {
|
|
if list.Len() == 0 {
|
|
p.tracef("] {}")
|
|
} else {
|
|
p.tracef("] {>")
|
|
defer p.tracef("<\n}")
|
|
}
|
|
}
|
|
for _, n := range list.Slice() {
|
|
if p.trace {
|
|
p.tracef("\n")
|
|
}
|
|
p.node(n)
|
|
}
|
|
}
|
|
|
|
func (p *exporter) node(n *Node) {
|
|
p.op(n.Op)
|
|
|
|
switch n.Op {
|
|
// names
|
|
case ONAME, OPACK, ONONAME:
|
|
p.sym(n.Sym)
|
|
|
|
case OTYPE:
|
|
if p.bool(n.Type == nil) {
|
|
p.sym(n.Sym)
|
|
} else {
|
|
p.typ(n.Type)
|
|
}
|
|
|
|
case OLITERAL:
|
|
p.typ(unidealType(n.Type, n.Val()))
|
|
p.value(n.Val())
|
|
|
|
// expressions
|
|
case OMAKEMAP, OMAKECHAN, OMAKESLICE:
|
|
if p.bool(n.List.Len() != 0) {
|
|
p.nodeList(n.List) // TODO(gri) do we still need to export this?
|
|
}
|
|
p.nodesOrNil(n.Left, n.Right)
|
|
p.typ(n.Type)
|
|
|
|
case OPLUS, OMINUS, OADDR, OCOM, OIND, ONOT, ORECV:
|
|
p.node(n.Left)
|
|
|
|
case OADD, OAND, OANDAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE, OLT,
|
|
OLSH, OMOD, OMUL, ONE, OOR, OOROR, ORSH, OSEND,
|
|
OSUB, OXOR:
|
|
p.node(n.Left)
|
|
p.node(n.Right)
|
|
|
|
case OADDSTR:
|
|
p.nodeList(n.List)
|
|
|
|
case OPTRLIT:
|
|
p.node(n.Left)
|
|
|
|
case OSTRUCTLIT:
|
|
p.typ(n.Type)
|
|
p.nodeList(n.List)
|
|
p.bool(n.Implicit)
|
|
|
|
case OARRAYLIT, OMAPLIT:
|
|
p.typ(n.Type)
|
|
p.nodeList(n.List)
|
|
p.bool(n.Implicit)
|
|
|
|
case OKEY:
|
|
p.nodesOrNil(n.Left, n.Right)
|
|
|
|
case OCOPY, OCOMPLEX:
|
|
p.node(n.Left)
|
|
p.node(n.Right)
|
|
|
|
case OCONV, OCONVIFACE, OCONVNOP, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, ORUNESTR:
|
|
p.typ(n.Type)
|
|
if p.bool(n.Left != nil) {
|
|
p.node(n.Left)
|
|
} else {
|
|
p.nodeList(n.List)
|
|
}
|
|
|
|
case ODOT, ODOTPTR, ODOTMETH, ODOTINTER, OXDOT:
|
|
p.node(n.Left)
|
|
p.sym(n.Sym)
|
|
|
|
case ODOTTYPE, ODOTTYPE2:
|
|
p.node(n.Left)
|
|
if p.bool(n.Right != nil) {
|
|
p.node(n.Right)
|
|
} else {
|
|
p.typ(n.Type)
|
|
}
|
|
|
|
case OINDEX, OINDEXMAP, OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR:
|
|
p.node(n.Left)
|
|
p.node(n.Right)
|
|
|
|
case OREAL, OIMAG, OAPPEND, OCAP, OCLOSE, ODELETE, OLEN, OMAKE, ONEW, OPANIC,
|
|
ORECOVER, OPRINT, OPRINTN:
|
|
p.nodesOrNil(n.Left, nil)
|
|
p.nodeList(n.List)
|
|
p.bool(n.Isddd)
|
|
|
|
case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OGETG:
|
|
p.node(n.Left)
|
|
p.nodeList(n.List)
|
|
p.bool(n.Isddd)
|
|
|
|
case OCMPSTR, OCMPIFACE:
|
|
p.node(n.Left)
|
|
p.node(n.Right)
|
|
p.int(int(n.Etype))
|
|
|
|
case OPAREN:
|
|
p.node(n.Left)
|
|
|
|
// statements
|
|
case ODCL:
|
|
p.node(n.Left) // TODO(gri) compare with fmt code
|
|
p.typ(n.Left.Type)
|
|
|
|
case OAS, OASWB:
|
|
p.nodesOrNil(n.Left, n.Right) // n.Right might be nil
|
|
p.bool(n.Colas)
|
|
|
|
case OASOP:
|
|
p.node(n.Left)
|
|
// n.Implicit indicates ++ or --, n.Right is 1 in those cases
|
|
p.node(n.Right)
|
|
p.int(int(n.Etype))
|
|
|
|
case OAS2:
|
|
p.nodeList(n.List)
|
|
p.nodeList(n.Rlist)
|
|
|
|
case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
|
|
p.nodeList(n.List)
|
|
p.nodeList(n.Rlist)
|
|
|
|
case ORETURN:
|
|
p.nodeList(n.List)
|
|
|
|
case OPROC, ODEFER:
|
|
p.node(n.Left)
|
|
|
|
case OIF:
|
|
p.nodeList(n.Ninit)
|
|
p.node(n.Left)
|
|
p.nodeList(n.Nbody)
|
|
p.nodeList(n.Rlist)
|
|
|
|
case OFOR:
|
|
p.nodeList(n.Ninit)
|
|
p.nodesOrNil(n.Left, n.Right)
|
|
p.nodeList(n.Nbody)
|
|
|
|
case ORANGE:
|
|
if p.bool(n.List.Len() != 0) {
|
|
p.nodeList(n.List)
|
|
}
|
|
p.node(n.Right)
|
|
p.nodeList(n.Nbody)
|
|
|
|
case OSELECT, OSWITCH:
|
|
p.nodeList(n.Ninit)
|
|
p.nodesOrNil(n.Left, nil)
|
|
p.nodeList(n.List)
|
|
|
|
case OCASE, OXCASE:
|
|
if p.bool(n.List.Len() != 0) {
|
|
p.nodeList(n.List)
|
|
}
|
|
p.nodeList(n.Nbody)
|
|
|
|
case OBREAK, OCONTINUE, OGOTO, OFALL, OXFALL:
|
|
p.nodesOrNil(n.Left, nil)
|
|
|
|
case OEMPTY, ODCLCONST:
|
|
// nothing to do
|
|
|
|
case OLABEL:
|
|
p.node(n.Left)
|
|
|
|
default:
|
|
Fatalf("exporter: CANNOT EXPORT: %s\nPlease notify gri@\n", opnames[n.Op])
|
|
}
|
|
}
|
|
|
|
func (p *exporter) nodesOrNil(a, b *Node) {
|
|
ab := 0
|
|
if a != nil {
|
|
ab |= 1
|
|
}
|
|
if b != nil {
|
|
ab |= 2
|
|
}
|
|
p.int(ab)
|
|
if ab&1 != 0 {
|
|
p.node(a)
|
|
}
|
|
if ab&2 != 0 {
|
|
p.node(b)
|
|
}
|
|
}
|
|
|
|
func (p *exporter) sym(s *Sym) {
|
|
name := s.Name
|
|
p.string(name)
|
|
if name == "?" || name != "_" && name != "" && !exportname(name) {
|
|
p.pkg(s.Pkg)
|
|
}
|
|
}
|
|
|
|
func (p *exporter) bool(b bool) bool {
|
|
x := 0
|
|
if b {
|
|
x = 1
|
|
}
|
|
p.int(x)
|
|
return b
|
|
}
|
|
|
|
func (p *exporter) op(op Op) {
|
|
p.int(int(op))
|
|
if p.trace {
|
|
p.tracef("%s ", opnames[op])
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Low-level encoders
|
|
|
|
func (p *exporter) index(marker byte, index int) {
|
|
if index < 0 {
|
|
Fatalf("exporter: invalid index < 0")
|
|
}
|
|
if debugFormat {
|
|
p.marker('t')
|
|
}
|
|
if p.trace {
|
|
p.tracef("%c%d ", marker, index)
|
|
}
|
|
p.rawInt64(int64(index))
|
|
}
|
|
|
|
func (p *exporter) tag(tag int) {
|
|
if tag >= 0 {
|
|
Fatalf("exporter: invalid tag >= 0")
|
|
}
|
|
if debugFormat {
|
|
p.marker('t')
|
|
}
|
|
if p.trace {
|
|
p.tracef("%s ", tagString[-tag])
|
|
}
|
|
p.rawInt64(int64(tag))
|
|
}
|
|
|
|
func (p *exporter) int(x int) {
|
|
p.int64(int64(x))
|
|
}
|
|
|
|
func (p *exporter) int64(x int64) {
|
|
if debugFormat {
|
|
p.marker('i')
|
|
}
|
|
if p.trace {
|
|
p.tracef("%d ", x)
|
|
}
|
|
p.rawInt64(x)
|
|
}
|
|
|
|
func (p *exporter) string(s string) {
|
|
if debugFormat {
|
|
p.marker('s')
|
|
}
|
|
if p.trace {
|
|
p.tracef("%q ", s)
|
|
}
|
|
p.rawInt64(int64(len(s)))
|
|
for i := 0; i < len(s); i++ {
|
|
p.byte(s[i])
|
|
}
|
|
}
|
|
|
|
// 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
|
|
// debugFormat format only.
|
|
func (p *exporter) marker(m byte) {
|
|
p.byte(m)
|
|
p.rawInt64(int64(p.written))
|
|
}
|
|
|
|
// 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)
|
|
for i := 0; i < n; i++ {
|
|
p.byte(tmp[i])
|
|
}
|
|
}
|
|
|
|
// byte is the bottleneck interface to write to p.out.
|
|
// byte escapes b as follows (any encoding does that
|
|
// hides '$'):
|
|
//
|
|
// '$' => '|' 'S'
|
|
// '|' => '|' '|'
|
|
//
|
|
// Necessary so other tools can find the end of the
|
|
// export data by searching for "$$".
|
|
func (p *exporter) byte(b byte) {
|
|
switch b {
|
|
case '$':
|
|
// write '$' as '|' 'S'
|
|
b = 'S'
|
|
fallthrough
|
|
case '|':
|
|
// write '|' as '|' '|'
|
|
obj.Bputc(p.out, '|')
|
|
p.written++
|
|
}
|
|
obj.Bputc(p.out, b)
|
|
p.written++
|
|
}
|
|
|
|
// tracef is like fmt.Printf but it rewrites the format string
|
|
// to take care of indentation.
|
|
func (p *exporter) tracef(format string, args ...interface{}) {
|
|
if strings.ContainsAny(format, "<>\n") {
|
|
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++
|
|
continue
|
|
case '<':
|
|
p.indent--
|
|
continue
|
|
}
|
|
buf.WriteByte(ch)
|
|
if ch == '\n' {
|
|
for j := p.indent; j > 0; j-- {
|
|
buf.WriteString(". ")
|
|
}
|
|
}
|
|
}
|
|
format = buf.String()
|
|
}
|
|
fmt.Printf(format, args...)
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Export format
|
|
|
|
// Tags. Must be < 0.
|
|
const (
|
|
// Packages
|
|
packageTag = -(iota + 1)
|
|
|
|
// Types
|
|
namedTag
|
|
arrayTag
|
|
sliceTag
|
|
dddTag
|
|
structTag
|
|
pointerTag
|
|
signatureTag
|
|
interfaceTag
|
|
mapTag
|
|
chanTag
|
|
|
|
// Values
|
|
falseTag
|
|
trueTag
|
|
int64Tag
|
|
floatTag
|
|
fractionTag // not used by gc
|
|
complexTag
|
|
stringTag
|
|
nilTag
|
|
unknownTag // not used by gc (only appears in packages with errors)
|
|
)
|
|
|
|
// Debugging support.
|
|
// (tagString is only used when tracing is enabled)
|
|
var tagString = [...]string{
|
|
// Packages:
|
|
-packageTag: "package",
|
|
|
|
// Types:
|
|
-namedTag: "named type",
|
|
-arrayTag: "array",
|
|
-sliceTag: "slice",
|
|
-dddTag: "ddd",
|
|
-structTag: "struct",
|
|
-pointerTag: "pointer",
|
|
-signatureTag: "signature",
|
|
-interfaceTag: "interface",
|
|
-mapTag: "map",
|
|
-chanTag: "chan",
|
|
|
|
// Values:
|
|
-falseTag: "false",
|
|
-trueTag: "true",
|
|
-int64Tag: "int64",
|
|
-floatTag: "float",
|
|
-fractionTag: "fraction",
|
|
-complexTag: "complex",
|
|
-stringTag: "string",
|
|
-nilTag: "nil",
|
|
-unknownTag: "unknown",
|
|
}
|
|
|
|
// untype returns the "pseudo" untyped type for a Ctype (import/export use only).
|
|
// (we can't use an pre-initialized array because we must be sure all types are
|
|
// set up)
|
|
func untype(ctype Ctype) *Type {
|
|
switch ctype {
|
|
case CTINT:
|
|
return idealint
|
|
case CTRUNE:
|
|
return idealrune
|
|
case CTFLT:
|
|
return idealfloat
|
|
case CTCPLX:
|
|
return idealcomplex
|
|
case CTSTR:
|
|
return idealstring
|
|
case CTBOOL:
|
|
return idealbool
|
|
case CTNIL:
|
|
return Types[TNIL]
|
|
}
|
|
Fatalf("exporter: unknown Ctype")
|
|
return nil
|
|
}
|
|
|
|
var predecl []*Type // initialized lazily
|
|
|
|
func predeclared() []*Type {
|
|
if predecl == nil {
|
|
// initialize lazily to be sure that all
|
|
// elements have been initialized before
|
|
predecl = []*Type{
|
|
// basic types
|
|
Types[TBOOL],
|
|
Types[TINT],
|
|
Types[TINT8],
|
|
Types[TINT16],
|
|
Types[TINT32],
|
|
Types[TINT64],
|
|
Types[TUINT],
|
|
Types[TUINT8],
|
|
Types[TUINT16],
|
|
Types[TUINT32],
|
|
Types[TUINT64],
|
|
Types[TUINTPTR],
|
|
Types[TFLOAT32],
|
|
Types[TFLOAT64],
|
|
Types[TCOMPLEX64],
|
|
Types[TCOMPLEX128],
|
|
Types[TSTRING],
|
|
|
|
// aliases
|
|
bytetype,
|
|
runetype,
|
|
|
|
// error
|
|
errortype,
|
|
|
|
// untyped types
|
|
untype(CTBOOL),
|
|
untype(CTINT),
|
|
untype(CTRUNE),
|
|
untype(CTFLT),
|
|
untype(CTCPLX),
|
|
untype(CTSTR),
|
|
untype(CTNIL),
|
|
|
|
// package unsafe
|
|
Types[TUNSAFEPTR],
|
|
|
|
// invalid type (package contains errors)
|
|
Types[Txxx],
|
|
|
|
// any type, for builtin export data
|
|
Types[TANY],
|
|
}
|
|
}
|
|
return predecl
|
|
}
|