package gcimporter
import (
)
const debugFormat = false
const exportVersion = 4
const trackAllTypes = false
type exporter struct {
fset *token.FileSet
out bytes.Buffer
strIndex map[string]int
pkgIndex map[*types.Package]int
typIndex map[types.Type]int
posInfoFormat bool
prevFile string
prevLine int
written int
indent int
}
type internalError string
func ( internalError) () string { return "gcimporter: " + string() }
func ( string, ...interface{}) error {
return internalError(fmt.Sprintf(, ...))
}
func ( *token.FileSet, *types.Package) ( []byte, error) {
if !debug {
defer func() {
if := recover(); != nil {
if , := .(internalError); {
=
return
}
panic()
}
}()
}
:= exporter{
fset: ,
strIndex: map[string]int{"": 0},
pkgIndex: make(map[*types.Package]int),
typIndex: make(map[types.Type]int),
posInfoFormat: true,
}
.rawStringln(fmt.Sprintf("version %d", exportVersion))
var string
if debugFormat {
= "debug"
}
.rawStringln()
.bool(trackAllTypes)
.bool(.posInfoFormat)
for , := range predeclared() {
.typIndex[] =
}
if len(.typIndex) != len(predeclared()) {
return nil, internalError("duplicate entries in type map?")
}
.pkg(, true)
if trace {
.tracef("\n")
}
:= 0
:= .Scope()
for , := range .Names() {
if !token.IsExported() {
continue
}
if trace {
.tracef("\n")
}
.obj(.Lookup())
++
}
if trace {
.tracef("\n")
}
.tag(endTag)
.int()
if trace {
.tracef("\n")
}
return .out.Bytes(), nil
}
func ( *exporter) ( *types.Package, bool) {
if == nil {
panic(internalError("unexpected nil pkg"))
}
if , := .pkgIndex[]; {
.index('P', )
return
}
if trace {
.tracef("P%d = { ", len(.pkgIndex))
defer .tracef("} ")
}
.pkgIndex[] = len(.pkgIndex)
.tag(packageTag)
.string(.Name())
if {
.string("")
} else {
.string(.Path())
}
}
func ( *exporter) ( types.Object) {
switch obj := .(type) {
case *types.Const:
.tag(constTag)
.pos()
.qualifiedName()
.typ(.Type())
.value(.Val())
case *types.TypeName:
if .IsAlias() {
.tag(aliasTag)
.pos()
.qualifiedName()
} else {
.tag(typeTag)
}
.typ(.Type())
case *types.Var:
.tag(varTag)
.pos()
.qualifiedName()
.typ(.Type())
case *types.Func:
.tag(funcTag)
.pos()
.qualifiedName()
:= .Type().(*types.Signature)
.paramList(.Params(), .Variadic())
.paramList(.Results(), false)
default:
panic(internalErrorf("unexpected object %v (%T)", , ))
}
}
func ( *exporter) ( types.Object) {
if !.posInfoFormat {
return
}
, := .fileLine()
if == .prevFile {
:= - .prevLine
.int()
if == 0 {
.int(-1)
}
} else {
.int(0)
:= commonPrefixLen(.prevFile, )
.int()
.string([:])
.prevFile =
.int()
}
.prevLine =
}
func ( *exporter) ( types.Object) ( string, int) {
if .fset != nil {
:= .fset.Position(.Pos())
= .Filename
= .Line
}
return
}
func (, string) int {
if len() > len() {
, = ,
}
:= 0
for < len() && [] == [] {
++
}
return
}
func ( *exporter) ( types.Object) {
.string(.Name())
.pkg(.Pkg(), false)
}
func ( *exporter) ( types.Type) {
if == nil {
panic(internalError("nil type"))
}
if , := .typIndex[]; {
.index('T', )
return
}
if trackAllTypes {
if trace {
.tracef("T%d = {>\n", len(.typIndex))
defer .tracef("<\n} ")
}
.typIndex[] = len(.typIndex)
}
switch t := .(type) {
case *types.Named:
if !trackAllTypes {
.typIndex[] = len(.typIndex)
}
.tag(namedTag)
.pos(.Obj())
.qualifiedName(.Obj())
.(.Underlying())
if !types.IsInterface() {
.assocMethods()
}
case *types.Array:
.tag(arrayTag)
.int64(.Len())
.(.Elem())
case *types.Slice:
.tag(sliceTag)
.(.Elem())
case *dddSlice:
.tag(dddTag)
.(.elem)
case *types.Struct:
.tag(structTag)
.fieldList()
case *types.Pointer:
.tag(pointerTag)
.(.Elem())
case *types.Signature:
.tag(signatureTag)
.paramList(.Params(), .Variadic())
.paramList(.Results(), false)
case *types.Interface:
.tag(interfaceTag)
.iface()
case *types.Map:
.tag(mapTag)
.(.Key())
.(.Elem())
case *types.Chan:
.tag(chanTag)
.int(int(3 - .Dir()))
.(.Elem())
default:
panic(internalErrorf("unexpected type %T: %s", , ))
}
}
func ( *exporter) ( *types.Named) {
var []*types.Func
for := 0; < .NumMethods(); ++ {
= append(, .Method())
}
sort.Sort(methodsByName())
.int(len())
if trace && != nil {
.tracef("associated methods {>\n")
}
for , := range {
if trace && > 0 {
.tracef("\n")
}
.pos()
:= .Name()
.string()
if !exported() {
.pkg(.Pkg(), false)
}
:= .Type().(*types.Signature)
.paramList(types.NewTuple(.Recv()), false)
.paramList(.Params(), .Variadic())
.paramList(.Results(), false)
.int(0)
}
if trace && != nil {
.tracef("<\n} ")
}
}
type methodsByName []*types.Func
func ( methodsByName) () int { return len() }
func ( methodsByName) (, int) { [], [] = [], [] }
func ( methodsByName) (, int) bool { return [].Name() < [].Name() }
func ( *exporter) ( *types.Struct) {
if trace && .NumFields() > 0 {
.tracef("fields {>\n")
defer .tracef("<\n} ")
}
.int(.NumFields())
for := 0; < .NumFields(); ++ {
if trace && > 0 {
.tracef("\n")
}
.field(.Field())
.string(.Tag())
}
}
func ( *exporter) ( *types.Var) {
if !.IsField() {
panic(internalError("field expected"))
}
.pos()
.fieldName()
.typ(.Type())
}
func ( *exporter) ( *types.Interface) {
.int(0)
:= .NumMethods()
if trace && > 0 {
.tracef("methods {>\n")
defer .tracef("<\n} ")
}
.int()
for := 0; < ; ++ {
if trace && > 0 {
.tracef("\n")
}
.method(.Method())
}
}
func ( *exporter) ( *types.Func) {
:= .Type().(*types.Signature)
if .Recv() == nil {
panic(internalError("method expected"))
}
.pos()
.string(.Name())
if .Name() != "_" && !token.IsExported(.Name()) {
.pkg(.Pkg(), false)
}
.paramList(.Params(), .Variadic())
.paramList(.Results(), false)
}
func ( *exporter) ( *types.Var) {
:= .Name()
if .Anonymous() {
:= basetypeName(.Type())
if == {
if token.IsExported() {
= ""
} else {
= "?"
}
} else {
.string("@")
}
}
.string()
if != "" && !token.IsExported() {
.pkg(.Pkg(), false)
}
}
func ( types.Type) string {
switch typ := deref().(type) {
case *types.Basic:
return .Name()
case *types.Named:
return .Obj().Name()
default:
return ""
}
}
func ( *exporter) ( *types.Tuple, bool) {
:= .Len()
if > 0 && .At(0).Name() == "" {
= -
}
.int()
for := 0; < .Len(); ++ {
:= .At()
:= .Type()
if && == .Len()-1 {
= &dddSlice{.(*types.Slice).Elem()}
}
.typ()
if > 0 {
:= .Name()
.string()
if != "_" {
.pkg(.Pkg(), false)
}
}
.string("")
}
}
func ( *exporter) ( constant.Value) {
if trace {
.tracef("= ")
}
switch .Kind() {
case constant.Bool:
:= falseTag
if constant.BoolVal() {
= trueTag
}
.tag()
case constant.Int:
if , := constant.Int64Val(); {
.tag(int64Tag)
.int64()
return
}
.tag(floatTag)
.float(constant.ToFloat())
case constant.Float:
.tag(floatTag)
.float()
case constant.Complex:
.tag(complexTag)
.float(constant.Real())
.float(constant.Imag())
case constant.String:
.tag(stringTag)
.string(constant.StringVal())
case constant.Unknown:
.tag(unknownTag)
default:
panic(internalErrorf("unexpected value %v (%T)", , ))
}
}
func ( *exporter) ( constant.Value) {
if .Kind() != constant.Float {
panic(internalErrorf("unexpected constant %v, want float", ))
}
:= constant.Sign()
if == 0 {
.int(0)
return
}
var big.Float
if , := constant.Float64Val(); {
.SetFloat64()
} else if , := constant.Num(), constant.Denom(); .Kind() == constant.Int {
:= valueToRat()
.SetRat(.Quo(, valueToRat()))
} else {
.SetFloat64(math.MaxFloat64)
}
var big.Float
:= .MantExp(&)
.SetMantExp(&, int(.MinPrec()))
, := .Int(nil)
if != big.Exact {
panic(internalError("internal error"))
}
.int()
.int()
.string(string(.Bytes()))
}
func ( constant.Value) *big.Rat {
:= constant.Bytes()
for := 0; < len()/2; ++ {
[], [len()-1-] = [len()-1-], []
}
return new(big.Rat).SetInt(new(big.Int).SetBytes())
}
func ( *exporter) ( bool) bool {
if trace {
.tracef("[")
defer .tracef("= %v] ", )
}
:= 0
if {
= 1
}
.int()
return
}
func ( *exporter) ( byte, int) {
if < 0 {
panic(internalError("invalid index < 0"))
}
if debugFormat {
.marker('t')
}
if trace {
.tracef("%c%d ", , )
}
.rawInt64(int64())
}
func ( *exporter) ( int) {
if >= 0 {
panic(internalError("invalid tag >= 0"))
}
if debugFormat {
.marker('t')
}
if trace {
.tracef("%s ", tagString[-])
}
.rawInt64(int64())
}
func ( *exporter) ( int) {
.int64(int64())
}
func ( *exporter) ( int64) {
if debugFormat {
.marker('i')
}
if trace {
.tracef("%d ", )
}
.rawInt64()
}
func ( *exporter) ( string) {
if debugFormat {
.marker('s')
}
if trace {
.tracef("%q ", )
}
if , := .strIndex[]; {
.rawInt64(int64())
return
}
.strIndex[] = len(.strIndex)
.rawInt64(-int64(len()))
for := 0; < len(); ++ {
.rawByte([])
}
}
func ( *exporter) ( byte) {
.rawByte()
if false && trace {
.tracef("#%d ", .written)
}
.rawInt64(int64(.written))
}
func ( *exporter) ( int64) {
var [binary.MaxVarintLen64]byte
:= binary.PutVarint([:], )
for := 0; < ; ++ {
.rawByte([])
}
}
func ( *exporter) ( string) {
for := 0; < len(); ++ {
.rawByte([])
}
.rawByte('\n')
}
func ( *exporter) ( byte) {
switch {
case '$':
= 'S'
fallthrough
case '|':
.out.WriteByte('|')
.written++
}
.out.WriteByte()
.written++
}
func ( *exporter) ( string, ...interface{}) {
if strings.ContainsAny(, "<>\n") {
var bytes.Buffer
for := 0; < len(); ++ {
:= []
switch {
case '>':
.indent++
continue
case '<':
.indent--
continue
}
.WriteByte()
if == '\n' {
for := .indent; > 0; -- {
.WriteString(". ")
}
}
}
= .String()
}
fmt.Printf(, ...)
}
var tagString = [...]string{
-packageTag: "package",
-namedTag: "named type",
-arrayTag: "array",
-sliceTag: "slice",
-dddTag: "ddd",
-structTag: "struct",
-pointerTag: "pointer",
-signatureTag: "signature",
-interfaceTag: "interface",
-mapTag: "map",
-chanTag: "chan",
-falseTag: "false",
-trueTag: "true",
-int64Tag: "int64",
-floatTag: "float",
-fractionTag: "fraction",
-complexTag: "complex",
-stringTag: "string",
-unknownTag: "unknown",
-aliasTag: "alias",
}