github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/x/tools/go/gcimporter15/bexport.go (about)

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build go1.6
     6  
     7  // Binary package export.
     8  // This file was derived from $GOROOT/src/cmd/compile/internal/gc/bexport.go;
     9  // see that file for specification of the format.
    10  
    11  package gcimporter
    12  
    13  import (
    14  	"bytes"
    15  	"encoding/binary"
    16  	"fmt"
    17  	"go/ast"
    18  	"go/constant"
    19  	"go/token"
    20  	"go/types"
    21  	"log"
    22  	"math"
    23  	"math/big"
    24  	"sort"
    25  	"strings"
    26  )
    27  
    28  // If debugFormat is set, each integer and string value is preceded by a marker
    29  // and position information in the encoding. This mechanism permits an importer
    30  // to recognize immediately when it is out of sync. The importer recognizes this
    31  // mode automatically (i.e., it can import export data produced with debugging
    32  // support even if debugFormat is not set at the time of import). This mode will
    33  // lead to massively larger export data (by a factor of 2 to 3) and should only
    34  // be enabled during development and debugging.
    35  //
    36  // NOTE: This flag is the first flag to enable if importing dies because of
    37  // (suspected) format errors, and whenever a change is made to the format.
    38  const debugFormat = false // default: false
    39  
    40  // If trace is set, debugging output is printed to std out.
    41  const trace = false // default: false
    42  
    43  const exportVersion = "v0"
    44  
    45  // trackAllTypes enables cycle tracking for all types, not just named
    46  // types. The existing compiler invariants assume that unnamed types
    47  // that are not completely set up are not used, or else there are spurious
    48  // errors.
    49  // If disabled, only named types are tracked, possibly leading to slightly
    50  // less efficient encoding in rare cases. It also prevents the export of
    51  // some corner-case type declarations (but those are not handled correctly
    52  // with with the textual export format either).
    53  // TODO(gri) enable and remove once issues caused by it are fixed
    54  const trackAllTypes = false
    55  
    56  type exporter struct {
    57  	fset *token.FileSet
    58  	out  bytes.Buffer
    59  
    60  	// object -> index maps, indexed in order of serialization
    61  	strIndex map[string]int
    62  	pkgIndex map[*types.Package]int
    63  	typIndex map[types.Type]int
    64  
    65  	// position encoding
    66  	posInfoFormat bool
    67  	prevFile      string
    68  	prevLine      int
    69  
    70  	// debugging support
    71  	written int // bytes written
    72  	indent  int // for trace
    73  }
    74  
    75  // BExportData returns binary export data for pkg.
    76  // If no file set is provided, position info will be missing.
    77  func BExportData(fset *token.FileSet, pkg *types.Package) []byte {
    78  	p := exporter{
    79  		fset:          fset,
    80  		strIndex:      map[string]int{"": 0}, // empty string is mapped to 0
    81  		pkgIndex:      make(map[*types.Package]int),
    82  		typIndex:      make(map[types.Type]int),
    83  		posInfoFormat: true, // TODO(gri) might become a flag, eventually
    84  	}
    85  
    86  	// first byte indicates low-level encoding format
    87  	var format byte = 'c' // compact
    88  	if debugFormat {
    89  		format = 'd'
    90  	}
    91  	p.rawByte(format)
    92  
    93  	format = 'n' // track named types only
    94  	if trackAllTypes {
    95  		format = 'a'
    96  	}
    97  	p.rawByte(format)
    98  
    99  	// posInfo exported or not?
   100  	p.bool(p.posInfoFormat)
   101  
   102  	// --- generic export data ---
   103  
   104  	if trace {
   105  		p.tracef("\n--- generic export data ---\n")
   106  		if p.indent != 0 {
   107  			log.Fatalf("gcimporter: incorrect indentation %d", p.indent)
   108  		}
   109  	}
   110  
   111  	if trace {
   112  		p.tracef("version = ")
   113  	}
   114  	p.string(exportVersion)
   115  	if trace {
   116  		p.tracef("\n")
   117  	}
   118  
   119  	// populate type map with predeclared "known" types
   120  	for index, typ := range predeclared {
   121  		p.typIndex[typ] = index
   122  	}
   123  	if len(p.typIndex) != len(predeclared) {
   124  		log.Fatalf("gcimporter: duplicate entries in type map?")
   125  	}
   126  
   127  	// write package data
   128  	p.pkg(pkg, true)
   129  	if trace {
   130  		p.tracef("\n")
   131  	}
   132  
   133  	// write objects
   134  	objcount := 0
   135  	scope := pkg.Scope()
   136  	for _, name := range scope.Names() {
   137  		if !ast.IsExported(name) {
   138  			continue
   139  		}
   140  		if trace {
   141  			p.tracef("\n")
   142  		}
   143  		p.obj(scope.Lookup(name))
   144  		objcount++
   145  	}
   146  
   147  	// indicate end of list
   148  	if trace {
   149  		p.tracef("\n")
   150  	}
   151  	p.tag(endTag)
   152  
   153  	// for self-verification only (redundant)
   154  	p.int(objcount)
   155  
   156  	if trace {
   157  		p.tracef("\n")
   158  	}
   159  
   160  	// --- end of export data ---
   161  
   162  	return p.out.Bytes()
   163  }
   164  
   165  func (p *exporter) pkg(pkg *types.Package, emptypath bool) {
   166  	if pkg == nil {
   167  		log.Fatalf("gcimporter: unexpected nil pkg")
   168  	}
   169  
   170  	// if we saw the package before, write its index (>= 0)
   171  	if i, ok := p.pkgIndex[pkg]; ok {
   172  		p.index('P', i)
   173  		return
   174  	}
   175  
   176  	// otherwise, remember the package, write the package tag (< 0) and package data
   177  	if trace {
   178  		p.tracef("P%d = { ", len(p.pkgIndex))
   179  		defer p.tracef("} ")
   180  	}
   181  	p.pkgIndex[pkg] = len(p.pkgIndex)
   182  
   183  	p.tag(packageTag)
   184  	p.string(pkg.Name())
   185  	if emptypath {
   186  		p.string("")
   187  	} else {
   188  		p.string(pkg.Path())
   189  	}
   190  }
   191  
   192  func (p *exporter) obj(obj types.Object) {
   193  	switch obj := obj.(type) {
   194  	case *types.Const:
   195  		p.tag(constTag)
   196  		p.pos(obj)
   197  		p.qualifiedName(obj)
   198  		p.typ(obj.Type())
   199  		p.value(obj.Val())
   200  
   201  	case *types.TypeName:
   202  		p.tag(typeTag)
   203  		p.typ(obj.Type())
   204  
   205  	case *types.Var:
   206  		p.tag(varTag)
   207  		p.pos(obj)
   208  		p.qualifiedName(obj)
   209  		p.typ(obj.Type())
   210  
   211  	case *types.Func:
   212  		p.tag(funcTag)
   213  		p.pos(obj)
   214  		p.qualifiedName(obj)
   215  		sig := obj.Type().(*types.Signature)
   216  		p.paramList(sig.Params(), sig.Variadic())
   217  		p.paramList(sig.Results(), false)
   218  
   219  	default:
   220  		log.Fatalf("gcimporter: unexpected object %v (%T)", obj, obj)
   221  	}
   222  }
   223  
   224  func (p *exporter) pos(obj types.Object) {
   225  	if !p.posInfoFormat {
   226  		return
   227  	}
   228  
   229  	file, line := p.fileLine(obj)
   230  	if file == p.prevFile {
   231  		// common case: write line delta
   232  		// delta == 0 means different file or no line change
   233  		delta := line - p.prevLine
   234  		p.int(delta)
   235  		if delta == 0 {
   236  			p.int(-1) // -1 means no file change
   237  		}
   238  	} else {
   239  		// different file
   240  		p.int(0)
   241  		// Encode filename as length of common prefix with previous
   242  		// filename, followed by (possibly empty) suffix. Filenames
   243  		// frequently share path prefixes, so this can save a lot
   244  		// of space and make export data size less dependent on file
   245  		// path length. The suffix is unlikely to be empty because
   246  		// file names tend to end in ".go".
   247  		n := commonPrefixLen(p.prevFile, file)
   248  		p.int(n)           // n >= 0
   249  		p.string(file[n:]) // write suffix only
   250  		p.prevFile = file
   251  		p.int(line)
   252  	}
   253  	p.prevLine = line
   254  }
   255  
   256  func (p *exporter) fileLine(obj types.Object) (file string, line int) {
   257  	if p.fset != nil {
   258  		pos := p.fset.Position(obj.Pos())
   259  		file = pos.Filename
   260  		line = pos.Line
   261  	}
   262  	return
   263  }
   264  
   265  func commonPrefixLen(a, b string) int {
   266  	if len(a) > len(b) {
   267  		a, b = b, a
   268  	}
   269  	// len(a) <= len(b)
   270  	i := 0
   271  	for i < len(a) && a[i] == b[i] {
   272  		i++
   273  	}
   274  	return i
   275  }
   276  
   277  func (p *exporter) qualifiedName(obj types.Object) {
   278  	p.string(obj.Name())
   279  	p.pkg(obj.Pkg(), false)
   280  }
   281  
   282  func (p *exporter) typ(t types.Type) {
   283  	if t == nil {
   284  		log.Fatalf("gcimporter: nil type")
   285  	}
   286  
   287  	// Possible optimization: Anonymous pointer types *T where
   288  	// T is a named type are common. We could canonicalize all
   289  	// such types *T to a single type PT = *T. This would lead
   290  	// to at most one *T entry in typIndex, and all future *T's
   291  	// would be encoded as the respective index directly. Would
   292  	// save 1 byte (pointerTag) per *T and reduce the typIndex
   293  	// size (at the cost of a canonicalization map). We can do
   294  	// this later, without encoding format change.
   295  
   296  	// if we saw the type before, write its index (>= 0)
   297  	if i, ok := p.typIndex[t]; ok {
   298  		p.index('T', i)
   299  		return
   300  	}
   301  
   302  	// otherwise, remember the type, write the type tag (< 0) and type data
   303  	if trackAllTypes {
   304  		if trace {
   305  			p.tracef("T%d = {>\n", len(p.typIndex))
   306  			defer p.tracef("<\n} ")
   307  		}
   308  		p.typIndex[t] = len(p.typIndex)
   309  	}
   310  
   311  	switch t := t.(type) {
   312  	case *types.Named:
   313  		if !trackAllTypes {
   314  			// if we don't track all types, track named types now
   315  			p.typIndex[t] = len(p.typIndex)
   316  		}
   317  
   318  		p.tag(namedTag)
   319  		p.pos(t.Obj())
   320  		p.qualifiedName(t.Obj())
   321  		p.typ(t.Underlying())
   322  		if !types.IsInterface(t) {
   323  			p.assocMethods(t)
   324  		}
   325  
   326  	case *types.Array:
   327  		p.tag(arrayTag)
   328  		p.int64(t.Len())
   329  		p.typ(t.Elem())
   330  
   331  	case *types.Slice:
   332  		p.tag(sliceTag)
   333  		p.typ(t.Elem())
   334  
   335  	case *dddSlice:
   336  		p.tag(dddTag)
   337  		p.typ(t.elem)
   338  
   339  	case *types.Struct:
   340  		p.tag(structTag)
   341  		p.fieldList(t)
   342  
   343  	case *types.Pointer:
   344  		p.tag(pointerTag)
   345  		p.typ(t.Elem())
   346  
   347  	case *types.Signature:
   348  		p.tag(signatureTag)
   349  		p.paramList(t.Params(), t.Variadic())
   350  		p.paramList(t.Results(), false)
   351  
   352  	case *types.Interface:
   353  		p.tag(interfaceTag)
   354  		p.iface(t)
   355  
   356  	case *types.Map:
   357  		p.tag(mapTag)
   358  		p.typ(t.Key())
   359  		p.typ(t.Elem())
   360  
   361  	case *types.Chan:
   362  		p.tag(chanTag)
   363  		p.int(int(3 - t.Dir())) // hack
   364  		p.typ(t.Elem())
   365  
   366  	default:
   367  		log.Fatalf("gcimporter: unexpected type %T: %s", t, t)
   368  	}
   369  }
   370  
   371  func (p *exporter) assocMethods(named *types.Named) {
   372  	// Sort methods (for determinism).
   373  	var methods []*types.Func
   374  	for i := 0; i < named.NumMethods(); i++ {
   375  		methods = append(methods, named.Method(i))
   376  	}
   377  	sort.Sort(methodsByName(methods))
   378  
   379  	p.int(len(methods))
   380  
   381  	if trace && methods != nil {
   382  		p.tracef("associated methods {>\n")
   383  	}
   384  
   385  	for i, m := range methods {
   386  		if trace && i > 0 {
   387  			p.tracef("\n")
   388  		}
   389  
   390  		p.pos(m)
   391  		name := m.Name()
   392  		p.string(name)
   393  		if !exported(name) {
   394  			p.pkg(m.Pkg(), false)
   395  		}
   396  
   397  		sig := m.Type().(*types.Signature)
   398  		p.paramList(types.NewTuple(sig.Recv()), false)
   399  		p.paramList(sig.Params(), sig.Variadic())
   400  		p.paramList(sig.Results(), false)
   401  	}
   402  
   403  	if trace && methods != nil {
   404  		p.tracef("<\n} ")
   405  	}
   406  }
   407  
   408  type methodsByName []*types.Func
   409  
   410  func (x methodsByName) Len() int           { return len(x) }
   411  func (x methodsByName) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
   412  func (x methodsByName) Less(i, j int) bool { return x[i].Name() < x[j].Name() }
   413  
   414  func (p *exporter) fieldList(t *types.Struct) {
   415  	if trace && t.NumFields() > 0 {
   416  		p.tracef("fields {>\n")
   417  		defer p.tracef("<\n} ")
   418  	}
   419  
   420  	p.int(t.NumFields())
   421  	for i := 0; i < t.NumFields(); i++ {
   422  		if trace && i > 0 {
   423  			p.tracef("\n")
   424  		}
   425  		p.field(t.Field(i))
   426  		p.string(t.Tag(i))
   427  	}
   428  }
   429  
   430  func (p *exporter) field(f *types.Var) {
   431  	if !f.IsField() {
   432  		log.Fatalf("gcimporter: field expected")
   433  	}
   434  
   435  	p.pos(f)
   436  	p.fieldName(f)
   437  	p.typ(f.Type())
   438  }
   439  
   440  func (p *exporter) iface(t *types.Interface) {
   441  	// TODO(gri): enable importer to load embedded interfaces,
   442  	// then emit Embeddeds and ExplicitMethods separately here.
   443  	p.int(0)
   444  
   445  	n := t.NumMethods()
   446  	if trace && n > 0 {
   447  		p.tracef("methods {>\n")
   448  		defer p.tracef("<\n} ")
   449  	}
   450  	p.int(n)
   451  	for i := 0; i < n; i++ {
   452  		if trace && i > 0 {
   453  			p.tracef("\n")
   454  		}
   455  		p.method(t.Method(i))
   456  	}
   457  }
   458  
   459  func (p *exporter) method(m *types.Func) {
   460  	sig := m.Type().(*types.Signature)
   461  	if sig.Recv() == nil {
   462  		log.Fatalf("gcimporter: method expected")
   463  	}
   464  
   465  	p.pos(m)
   466  	p.string(m.Name())
   467  	if m.Name() != "_" && !ast.IsExported(m.Name()) {
   468  		p.pkg(m.Pkg(), false)
   469  	}
   470  
   471  	// interface method; no need to encode receiver.
   472  	p.paramList(sig.Params(), sig.Variadic())
   473  	p.paramList(sig.Results(), false)
   474  }
   475  
   476  // fieldName is like qualifiedName but it doesn't record the package
   477  // for blank (_) or exported names.
   478  func (p *exporter) fieldName(f *types.Var) {
   479  	name := f.Name()
   480  
   481  	// anonymous field with unexported base type name: use "?" as field name
   482  	// (bname != "" per spec, but we are conservative in case of errors)
   483  	if f.Anonymous() {
   484  		base := f.Type()
   485  		if ptr, ok := base.(*types.Pointer); ok {
   486  			base = ptr.Elem()
   487  		}
   488  		if named, ok := base.(*types.Named); ok && !named.Obj().Exported() {
   489  			name = "?"
   490  		}
   491  	}
   492  
   493  	p.string(name)
   494  	if name == "?" || name != "_" && !f.Exported() {
   495  		p.pkg(f.Pkg(), false)
   496  	}
   497  }
   498  
   499  func (p *exporter) paramList(params *types.Tuple, variadic bool) {
   500  	// use negative length to indicate unnamed parameters
   501  	// (look at the first parameter only since either all
   502  	// names are present or all are absent)
   503  	n := params.Len()
   504  	if n > 0 && params.At(0).Name() == "" {
   505  		n = -n
   506  	}
   507  	p.int(n)
   508  	for i := 0; i < params.Len(); i++ {
   509  		q := params.At(i)
   510  		t := q.Type()
   511  		if variadic && i == params.Len()-1 {
   512  			t = &dddSlice{t.(*types.Slice).Elem()}
   513  		}
   514  		p.typ(t)
   515  		if n > 0 {
   516  			name := q.Name()
   517  			p.string(name)
   518  			if name != "_" {
   519  				p.pkg(q.Pkg(), false)
   520  			}
   521  		}
   522  		p.string("") // no compiler-specific info
   523  	}
   524  }
   525  
   526  func (p *exporter) value(x constant.Value) {
   527  	if trace {
   528  		p.tracef("= ")
   529  	}
   530  
   531  	switch x.Kind() {
   532  	case constant.Bool:
   533  		tag := falseTag
   534  		if constant.BoolVal(x) {
   535  			tag = trueTag
   536  		}
   537  		p.tag(tag)
   538  
   539  	case constant.Int:
   540  		if v, exact := constant.Int64Val(x); exact {
   541  			// common case: x fits into an int64 - use compact encoding
   542  			p.tag(int64Tag)
   543  			p.int64(v)
   544  			return
   545  		}
   546  		// uncommon case: large x - use float encoding
   547  		// (powers of 2 will be encoded efficiently with exponent)
   548  		p.tag(floatTag)
   549  		p.float(constant.ToFloat(x))
   550  
   551  	case constant.Float:
   552  		p.tag(floatTag)
   553  		p.float(x)
   554  
   555  	case constant.Complex:
   556  		p.tag(complexTag)
   557  		p.float(constant.Real(x))
   558  		p.float(constant.Imag(x))
   559  
   560  	case constant.String:
   561  		p.tag(stringTag)
   562  		p.string(constant.StringVal(x))
   563  
   564  	case constant.Unknown:
   565  		// package contains type errors
   566  		p.tag(unknownTag)
   567  
   568  	default:
   569  		log.Fatalf("gcimporter: unexpected value %v (%T)", x, x)
   570  	}
   571  }
   572  
   573  func (p *exporter) float(x constant.Value) {
   574  	if x.Kind() != constant.Float {
   575  		log.Fatalf("gcimporter: unexpected constant %v, want float", x)
   576  	}
   577  	// extract sign (there is no -0)
   578  	sign := constant.Sign(x)
   579  	if sign == 0 {
   580  		// x == 0
   581  		p.int(0)
   582  		return
   583  	}
   584  	// x != 0
   585  
   586  	var f big.Float
   587  	if v, exact := constant.Float64Val(x); exact {
   588  		// float64
   589  		f.SetFloat64(v)
   590  	} else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int {
   591  		// TODO(gri): add big.Rat accessor to constant.Value.
   592  		r := valueToRat(num)
   593  		f.SetRat(r.Quo(r, valueToRat(denom)))
   594  	} else {
   595  		// Value too large to represent as a fraction => inaccessible.
   596  		// TODO(gri): add big.Float accessor to constant.Value.
   597  		f.SetFloat64(math.MaxFloat64) // FIXME
   598  	}
   599  
   600  	// extract exponent such that 0.5 <= m < 1.0
   601  	var m big.Float
   602  	exp := f.MantExp(&m)
   603  
   604  	// extract mantissa as *big.Int
   605  	// - set exponent large enough so mant satisfies mant.IsInt()
   606  	// - get *big.Int from mant
   607  	m.SetMantExp(&m, int(m.MinPrec()))
   608  	mant, acc := m.Int(nil)
   609  	if acc != big.Exact {
   610  		log.Fatalf("gcimporter: internal error")
   611  	}
   612  
   613  	p.int(sign)
   614  	p.int(exp)
   615  	p.string(string(mant.Bytes()))
   616  }
   617  
   618  func valueToRat(x constant.Value) *big.Rat {
   619  	// Convert little-endian to big-endian.
   620  	// I can't believe this is necessary.
   621  	bytes := constant.Bytes(x)
   622  	for i := 0; i < len(bytes)/2; i++ {
   623  		bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i]
   624  	}
   625  	return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes))
   626  }
   627  
   628  func (p *exporter) bool(b bool) bool {
   629  	if trace {
   630  		p.tracef("[")
   631  		defer p.tracef("= %v] ", b)
   632  	}
   633  
   634  	x := 0
   635  	if b {
   636  		x = 1
   637  	}
   638  	p.int(x)
   639  	return b
   640  }
   641  
   642  // ----------------------------------------------------------------------------
   643  // Low-level encoders
   644  
   645  func (p *exporter) index(marker byte, index int) {
   646  	if index < 0 {
   647  		log.Fatalf("gcimporter: invalid index < 0")
   648  	}
   649  	if debugFormat {
   650  		p.marker('t')
   651  	}
   652  	if trace {
   653  		p.tracef("%c%d ", marker, index)
   654  	}
   655  	p.rawInt64(int64(index))
   656  }
   657  
   658  func (p *exporter) tag(tag int) {
   659  	if tag >= 0 {
   660  		log.Fatalf("gcimporter: invalid tag >= 0")
   661  	}
   662  	if debugFormat {
   663  		p.marker('t')
   664  	}
   665  	if trace {
   666  		p.tracef("%s ", tagString[-tag])
   667  	}
   668  	p.rawInt64(int64(tag))
   669  }
   670  
   671  func (p *exporter) int(x int) {
   672  	p.int64(int64(x))
   673  }
   674  
   675  func (p *exporter) int64(x int64) {
   676  	if debugFormat {
   677  		p.marker('i')
   678  	}
   679  	if trace {
   680  		p.tracef("%d ", x)
   681  	}
   682  	p.rawInt64(x)
   683  }
   684  
   685  func (p *exporter) string(s string) {
   686  	if debugFormat {
   687  		p.marker('s')
   688  	}
   689  	if trace {
   690  		p.tracef("%q ", s)
   691  	}
   692  	// if we saw the string before, write its index (>= 0)
   693  	// (the empty string is mapped to 0)
   694  	if i, ok := p.strIndex[s]; ok {
   695  		p.rawInt64(int64(i))
   696  		return
   697  	}
   698  	// otherwise, remember string and write its negative length and bytes
   699  	p.strIndex[s] = len(p.strIndex)
   700  	p.rawInt64(-int64(len(s)))
   701  	for i := 0; i < len(s); i++ {
   702  		p.rawByte(s[i])
   703  	}
   704  }
   705  
   706  // marker emits a marker byte and position information which makes
   707  // it easy for a reader to detect if it is "out of sync". Used for
   708  // debugFormat format only.
   709  func (p *exporter) marker(m byte) {
   710  	p.rawByte(m)
   711  	// Enable this for help tracking down the location
   712  	// of an incorrect marker when running in debugFormat.
   713  	if false && trace {
   714  		p.tracef("#%d ", p.written)
   715  	}
   716  	p.rawInt64(int64(p.written))
   717  }
   718  
   719  // rawInt64 should only be used by low-level encoders
   720  func (p *exporter) rawInt64(x int64) {
   721  	var tmp [binary.MaxVarintLen64]byte
   722  	n := binary.PutVarint(tmp[:], x)
   723  	for i := 0; i < n; i++ {
   724  		p.rawByte(tmp[i])
   725  	}
   726  }
   727  
   728  // rawByte is the bottleneck interface to write to p.out.
   729  // rawByte escapes b as follows (any encoding does that
   730  // hides '$'):
   731  //
   732  //	'$'  => '|' 'S'
   733  //	'|'  => '|' '|'
   734  //
   735  // Necessary so other tools can find the end of the
   736  // export data by searching for "$$".
   737  // rawByte should only be used by low-level encoders.
   738  func (p *exporter) rawByte(b byte) {
   739  	switch b {
   740  	case '$':
   741  		// write '$' as '|' 'S'
   742  		b = 'S'
   743  		fallthrough
   744  	case '|':
   745  		// write '|' as '|' '|'
   746  		p.out.WriteByte('|')
   747  		p.written++
   748  	}
   749  	p.out.WriteByte(b)
   750  	p.written++
   751  }
   752  
   753  // tracef is like fmt.Printf but it rewrites the format string
   754  // to take care of indentation.
   755  func (p *exporter) tracef(format string, args ...interface{}) {
   756  	if strings.IndexAny(format, "<>\n") >= 0 {
   757  		var buf bytes.Buffer
   758  		for i := 0; i < len(format); i++ {
   759  			// no need to deal with runes
   760  			ch := format[i]
   761  			switch ch {
   762  			case '>':
   763  				p.indent++
   764  				continue
   765  			case '<':
   766  				p.indent--
   767  				continue
   768  			}
   769  			buf.WriteByte(ch)
   770  			if ch == '\n' {
   771  				for j := p.indent; j > 0; j-- {
   772  					buf.WriteString(".  ")
   773  				}
   774  			}
   775  		}
   776  		format = buf.String()
   777  	}
   778  	fmt.Printf(format, args...)
   779  }
   780  
   781  // Debugging support.
   782  // (tagString is only used when tracing is enabled)
   783  var tagString = [...]string{
   784  	// Packages:
   785  	-packageTag: "package",
   786  
   787  	// Types:
   788  	-namedTag:     "named type",
   789  	-arrayTag:     "array",
   790  	-sliceTag:     "slice",
   791  	-dddTag:       "ddd",
   792  	-structTag:    "struct",
   793  	-pointerTag:   "pointer",
   794  	-signatureTag: "signature",
   795  	-interfaceTag: "interface",
   796  	-mapTag:       "map",
   797  	-chanTag:      "chan",
   798  
   799  	// Values:
   800  	-falseTag:    "false",
   801  	-trueTag:     "true",
   802  	-int64Tag:    "int64",
   803  	-floatTag:    "float",
   804  	-fractionTag: "fraction",
   805  	-complexTag:  "complex",
   806  	-stringTag:   "string",
   807  	-unknownTag:  "unknown",
   808  }