github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/internal/gcimporter/iexport.go (about)

     1  // Copyright 2019 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  // Indexed binary package export.
     6  // This file was derived from $GOROOT/src/cmd/compile/internal/gc/iexport.go;
     7  // see that file for specification of the format.
     8  
     9  package gcimporter
    10  
    11  import (
    12  	"bytes"
    13  	"encoding/binary"
    14  	"fmt"
    15  	"go/constant"
    16  	"go/token"
    17  	"go/types"
    18  	"io"
    19  	"math/big"
    20  	"reflect"
    21  	"sort"
    22  	"strconv"
    23  	"strings"
    24  
    25  	"golang.org/x/tools/internal/tokeninternal"
    26  	"golang.org/x/tools/internal/typeparams"
    27  )
    28  
    29  // IExportShallow encodes "shallow" export data for the specified package.
    30  //
    31  // No promises are made about the encoding other than that it can be
    32  // decoded by the same version of IIExportShallow. If you plan to save
    33  // export data in the file system, be sure to include a cryptographic
    34  // digest of the executable in the key to avoid version skew.
    35  func IExportShallow(fset *token.FileSet, pkg *types.Package) ([]byte, error) {
    36  	// In principle this operation can only fail if out.Write fails,
    37  	// but that's impossible for bytes.Buffer---and as a matter of
    38  	// fact iexportCommon doesn't even check for I/O errors.
    39  	// TODO(adonovan): handle I/O errors properly.
    40  	// TODO(adonovan): use byte slices throughout, avoiding copying.
    41  	const bundle, shallow = false, true
    42  	var out bytes.Buffer
    43  	err := iexportCommon(&out, fset, bundle, shallow, iexportVersion, []*types.Package{pkg})
    44  	return out.Bytes(), err
    45  }
    46  
    47  // IImportShallow decodes "shallow" types.Package data encoded by IExportShallow
    48  // in the same executable. This function cannot import data from
    49  // cmd/compile or gcexportdata.Write.
    50  func IImportShallow(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string, insert InsertType) (*types.Package, error) {
    51  	const bundle = false
    52  	pkgs, err := iimportCommon(fset, imports, data, bundle, path, insert)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	return pkgs[0], nil
    57  }
    58  
    59  // InsertType is the type of a function that creates a types.TypeName
    60  // object for a named type and inserts it into the scope of the
    61  // specified Package.
    62  type InsertType = func(pkg *types.Package, name string)
    63  
    64  // Current bundled export format version. Increase with each format change.
    65  // 0: initial implementation
    66  const bundleVersion = 0
    67  
    68  // IExportData writes indexed export data for pkg to out.
    69  //
    70  // If no file set is provided, position info will be missing.
    71  // The package path of the top-level package will not be recorded,
    72  // so that calls to IImportData can override with a provided package path.
    73  func IExportData(out io.Writer, fset *token.FileSet, pkg *types.Package) error {
    74  	const bundle, shallow = false, false
    75  	return iexportCommon(out, fset, bundle, shallow, iexportVersion, []*types.Package{pkg})
    76  }
    77  
    78  // IExportBundle writes an indexed export bundle for pkgs to out.
    79  func IExportBundle(out io.Writer, fset *token.FileSet, pkgs []*types.Package) error {
    80  	const bundle, shallow = true, false
    81  	return iexportCommon(out, fset, bundle, shallow, iexportVersion, pkgs)
    82  }
    83  
    84  func iexportCommon(out io.Writer, fset *token.FileSet, bundle, shallow bool, version int, pkgs []*types.Package) (err error) {
    85  	if !debug {
    86  		defer func() {
    87  			if e := recover(); e != nil {
    88  				if ierr, ok := e.(internalError); ok {
    89  					err = ierr
    90  					return
    91  				}
    92  				// Not an internal error; panic again.
    93  				panic(e)
    94  			}
    95  		}()
    96  	}
    97  
    98  	p := iexporter{
    99  		fset:        fset,
   100  		version:     version,
   101  		shallow:     shallow,
   102  		allPkgs:     map[*types.Package]bool{},
   103  		stringIndex: map[string]uint64{},
   104  		declIndex:   map[types.Object]uint64{},
   105  		tparamNames: map[types.Object]string{},
   106  		typIndex:    map[types.Type]uint64{},
   107  	}
   108  	if !bundle {
   109  		p.localpkg = pkgs[0]
   110  	}
   111  
   112  	for i, pt := range predeclared() {
   113  		p.typIndex[pt] = uint64(i)
   114  	}
   115  	if len(p.typIndex) > predeclReserved {
   116  		panic(internalErrorf("too many predeclared types: %d > %d", len(p.typIndex), predeclReserved))
   117  	}
   118  
   119  	// Initialize work queue with exported declarations.
   120  	for _, pkg := range pkgs {
   121  		scope := pkg.Scope()
   122  		for _, name := range scope.Names() {
   123  			if token.IsExported(name) {
   124  				p.pushDecl(scope.Lookup(name))
   125  			}
   126  		}
   127  
   128  		if bundle {
   129  			// Ensure pkg and its imports are included in the index.
   130  			p.allPkgs[pkg] = true
   131  			for _, imp := range pkg.Imports() {
   132  				p.allPkgs[imp] = true
   133  			}
   134  		}
   135  	}
   136  
   137  	// Loop until no more work.
   138  	for !p.declTodo.empty() {
   139  		p.doDecl(p.declTodo.popHead())
   140  	}
   141  
   142  	// Produce index of offset of each file record in files.
   143  	var files intWriter
   144  	var fileOffset []uint64 // fileOffset[i] is offset in files of file encoded as i
   145  	if p.shallow {
   146  		fileOffset = make([]uint64, len(p.fileInfos))
   147  		for i, info := range p.fileInfos {
   148  			fileOffset[i] = uint64(files.Len())
   149  			p.encodeFile(&files, info.file, info.needed)
   150  		}
   151  	}
   152  
   153  	// Append indices to data0 section.
   154  	dataLen := uint64(p.data0.Len())
   155  	w := p.newWriter()
   156  	w.writeIndex(p.declIndex)
   157  
   158  	if bundle {
   159  		w.uint64(uint64(len(pkgs)))
   160  		for _, pkg := range pkgs {
   161  			w.pkg(pkg)
   162  			imps := pkg.Imports()
   163  			w.uint64(uint64(len(imps)))
   164  			for _, imp := range imps {
   165  				w.pkg(imp)
   166  			}
   167  		}
   168  	}
   169  	w.flush()
   170  
   171  	// Assemble header.
   172  	var hdr intWriter
   173  	if bundle {
   174  		hdr.uint64(bundleVersion)
   175  	}
   176  	hdr.uint64(uint64(p.version))
   177  	hdr.uint64(uint64(p.strings.Len()))
   178  	if p.shallow {
   179  		hdr.uint64(uint64(files.Len()))
   180  		hdr.uint64(uint64(len(fileOffset)))
   181  		for _, offset := range fileOffset {
   182  			hdr.uint64(offset)
   183  		}
   184  	}
   185  	hdr.uint64(dataLen)
   186  
   187  	// Flush output.
   188  	io.Copy(out, &hdr)
   189  	io.Copy(out, &p.strings)
   190  	if p.shallow {
   191  		io.Copy(out, &files)
   192  	}
   193  	io.Copy(out, &p.data0)
   194  
   195  	return nil
   196  }
   197  
   198  // encodeFile writes to w a representation of the file sufficient to
   199  // faithfully restore position information about all needed offsets.
   200  // Mutates the needed array.
   201  func (p *iexporter) encodeFile(w *intWriter, file *token.File, needed []uint64) {
   202  	_ = needed[0] // precondition: needed is non-empty
   203  
   204  	w.uint64(p.stringOff(file.Name()))
   205  
   206  	size := uint64(file.Size())
   207  	w.uint64(size)
   208  
   209  	// Sort the set of needed offsets. Duplicates are harmless.
   210  	sort.Slice(needed, func(i, j int) bool { return needed[i] < needed[j] })
   211  
   212  	lines := tokeninternal.GetLines(file) // byte offset of each line start
   213  	w.uint64(uint64(len(lines)))
   214  
   215  	// Rather than record the entire array of line start offsets,
   216  	// we save only a sparse list of (index, offset) pairs for
   217  	// the start of each line that contains a needed position.
   218  	var sparse [][2]int // (index, offset) pairs
   219  outer:
   220  	for i, lineStart := range lines {
   221  		lineEnd := size
   222  		if i < len(lines)-1 {
   223  			lineEnd = uint64(lines[i+1])
   224  		}
   225  		// Does this line contains a needed offset?
   226  		if needed[0] < lineEnd {
   227  			sparse = append(sparse, [2]int{i, lineStart})
   228  			for needed[0] < lineEnd {
   229  				needed = needed[1:]
   230  				if len(needed) == 0 {
   231  					break outer
   232  				}
   233  			}
   234  		}
   235  	}
   236  
   237  	// Delta-encode the columns.
   238  	w.uint64(uint64(len(sparse)))
   239  	var prev [2]int
   240  	for _, pair := range sparse {
   241  		w.uint64(uint64(pair[0] - prev[0]))
   242  		w.uint64(uint64(pair[1] - prev[1]))
   243  		prev = pair
   244  	}
   245  }
   246  
   247  // writeIndex writes out an object index. mainIndex indicates whether
   248  // we're writing out the main index, which is also read by
   249  // non-compiler tools and includes a complete package description
   250  // (i.e., name and height).
   251  func (w *exportWriter) writeIndex(index map[types.Object]uint64) {
   252  	type pkgObj struct {
   253  		obj  types.Object
   254  		name string // qualified name; differs from obj.Name for type params
   255  	}
   256  	// Build a map from packages to objects from that package.
   257  	pkgObjs := map[*types.Package][]pkgObj{}
   258  
   259  	// For the main index, make sure to include every package that
   260  	// we reference, even if we're not exporting (or reexporting)
   261  	// any symbols from it.
   262  	if w.p.localpkg != nil {
   263  		pkgObjs[w.p.localpkg] = nil
   264  	}
   265  	for pkg := range w.p.allPkgs {
   266  		pkgObjs[pkg] = nil
   267  	}
   268  
   269  	for obj := range index {
   270  		name := w.p.exportName(obj)
   271  		pkgObjs[obj.Pkg()] = append(pkgObjs[obj.Pkg()], pkgObj{obj, name})
   272  	}
   273  
   274  	var pkgs []*types.Package
   275  	for pkg, objs := range pkgObjs {
   276  		pkgs = append(pkgs, pkg)
   277  
   278  		sort.Slice(objs, func(i, j int) bool {
   279  			return objs[i].name < objs[j].name
   280  		})
   281  	}
   282  
   283  	sort.Slice(pkgs, func(i, j int) bool {
   284  		return w.exportPath(pkgs[i]) < w.exportPath(pkgs[j])
   285  	})
   286  
   287  	w.uint64(uint64(len(pkgs)))
   288  	for _, pkg := range pkgs {
   289  		w.string(w.exportPath(pkg))
   290  		w.string(pkg.Name())
   291  		w.uint64(uint64(0)) // package height is not needed for go/types
   292  
   293  		objs := pkgObjs[pkg]
   294  		w.uint64(uint64(len(objs)))
   295  		for _, obj := range objs {
   296  			w.string(obj.name)
   297  			w.uint64(index[obj.obj])
   298  		}
   299  	}
   300  }
   301  
   302  // exportName returns the 'exported' name of an object. It differs from
   303  // obj.Name() only for type parameters (see tparamExportName for details).
   304  func (p *iexporter) exportName(obj types.Object) (res string) {
   305  	if name := p.tparamNames[obj]; name != "" {
   306  		return name
   307  	}
   308  	return obj.Name()
   309  }
   310  
   311  type iexporter struct {
   312  	fset    *token.FileSet
   313  	out     *bytes.Buffer
   314  	version int
   315  
   316  	shallow  bool           // don't put types from other packages in the index
   317  	localpkg *types.Package // (nil in bundle mode)
   318  
   319  	// allPkgs tracks all packages that have been referenced by
   320  	// the export data, so we can ensure to include them in the
   321  	// main index.
   322  	allPkgs map[*types.Package]bool
   323  
   324  	declTodo objQueue
   325  
   326  	strings     intWriter
   327  	stringIndex map[string]uint64
   328  
   329  	// In shallow mode, object positions are encoded as (file, offset).
   330  	// Each file is recorded as a line-number table.
   331  	// Only the lines of needed positions are saved faithfully.
   332  	fileInfo  map[*token.File]uint64 // value is index in fileInfos
   333  	fileInfos []*filePositions
   334  
   335  	data0       intWriter
   336  	declIndex   map[types.Object]uint64
   337  	tparamNames map[types.Object]string // typeparam->exported name
   338  	typIndex    map[types.Type]uint64
   339  
   340  	indent int // for tracing support
   341  }
   342  
   343  type filePositions struct {
   344  	file   *token.File
   345  	needed []uint64 // unordered list of needed file offsets
   346  }
   347  
   348  func (p *iexporter) trace(format string, args ...interface{}) {
   349  	if !trace {
   350  		// Call sites should also be guarded, but having this check here allows
   351  		// easily enabling/disabling debug trace statements.
   352  		return
   353  	}
   354  	fmt.Printf(strings.Repeat("..", p.indent)+format+"\n", args...)
   355  }
   356  
   357  // stringOff returns the offset of s within the string section.
   358  // If not already present, it's added to the end.
   359  func (p *iexporter) stringOff(s string) uint64 {
   360  	off, ok := p.stringIndex[s]
   361  	if !ok {
   362  		off = uint64(p.strings.Len())
   363  		p.stringIndex[s] = off
   364  
   365  		p.strings.uint64(uint64(len(s)))
   366  		p.strings.WriteString(s)
   367  	}
   368  	return off
   369  }
   370  
   371  // fileIndexAndOffset returns the index of the token.File and the byte offset of pos within it.
   372  func (p *iexporter) fileIndexAndOffset(file *token.File, pos token.Pos) (uint64, uint64) {
   373  	index, ok := p.fileInfo[file]
   374  	if !ok {
   375  		index = uint64(len(p.fileInfo))
   376  		p.fileInfos = append(p.fileInfos, &filePositions{file: file})
   377  		if p.fileInfo == nil {
   378  			p.fileInfo = make(map[*token.File]uint64)
   379  		}
   380  		p.fileInfo[file] = index
   381  	}
   382  	// Record each needed offset.
   383  	info := p.fileInfos[index]
   384  	offset := uint64(file.Offset(pos))
   385  	info.needed = append(info.needed, offset)
   386  
   387  	return index, offset
   388  }
   389  
   390  // pushDecl adds n to the declaration work queue, if not already present.
   391  func (p *iexporter) pushDecl(obj types.Object) {
   392  	// Package unsafe is known to the compiler and predeclared.
   393  	// Caller should not ask us to do export it.
   394  	if obj.Pkg() == types.Unsafe {
   395  		panic("cannot export package unsafe")
   396  	}
   397  
   398  	// Shallow export data: don't index decls from other packages.
   399  	if p.shallow && obj.Pkg() != p.localpkg {
   400  		return
   401  	}
   402  
   403  	if _, ok := p.declIndex[obj]; ok {
   404  		return
   405  	}
   406  
   407  	p.declIndex[obj] = ^uint64(0) // mark obj present in work queue
   408  	p.declTodo.pushTail(obj)
   409  }
   410  
   411  // exportWriter handles writing out individual data section chunks.
   412  type exportWriter struct {
   413  	p *iexporter
   414  
   415  	data       intWriter
   416  	currPkg    *types.Package
   417  	prevFile   string
   418  	prevLine   int64
   419  	prevColumn int64
   420  }
   421  
   422  func (w *exportWriter) exportPath(pkg *types.Package) string {
   423  	if pkg == w.p.localpkg {
   424  		return ""
   425  	}
   426  	return pkg.Path()
   427  }
   428  
   429  func (p *iexporter) doDecl(obj types.Object) {
   430  	if trace {
   431  		p.trace("exporting decl %v (%T)", obj, obj)
   432  		p.indent++
   433  		defer func() {
   434  			p.indent--
   435  			p.trace("=> %s", obj)
   436  		}()
   437  	}
   438  	w := p.newWriter()
   439  	w.setPkg(obj.Pkg(), false)
   440  
   441  	switch obj := obj.(type) {
   442  	case *types.Var:
   443  		w.tag('V')
   444  		w.pos(obj.Pos())
   445  		w.typ(obj.Type(), obj.Pkg())
   446  
   447  	case *types.Func:
   448  		sig, _ := obj.Type().(*types.Signature)
   449  		if sig.Recv() != nil {
   450  			// We shouldn't see methods in the package scope,
   451  			// but the type checker may repair "func () F() {}"
   452  			// to "func (Invalid) F()" and then treat it like "func F()",
   453  			// so allow that. See golang/go#57729.
   454  			if sig.Recv().Type() != types.Typ[types.Invalid] {
   455  				panic(internalErrorf("unexpected method: %v", sig))
   456  			}
   457  		}
   458  
   459  		// Function.
   460  		if typeparams.ForSignature(sig).Len() == 0 {
   461  			w.tag('F')
   462  		} else {
   463  			w.tag('G')
   464  		}
   465  		w.pos(obj.Pos())
   466  		// The tparam list of the function type is the declaration of the type
   467  		// params. So, write out the type params right now. Then those type params
   468  		// will be referenced via their type offset (via typOff) in all other
   469  		// places in the signature and function where they are used.
   470  		//
   471  		// While importing the type parameters, tparamList computes and records
   472  		// their export name, so that it can be later used when writing the index.
   473  		if tparams := typeparams.ForSignature(sig); tparams.Len() > 0 {
   474  			w.tparamList(obj.Name(), tparams, obj.Pkg())
   475  		}
   476  		w.signature(sig)
   477  
   478  	case *types.Const:
   479  		w.tag('C')
   480  		w.pos(obj.Pos())
   481  		w.value(obj.Type(), obj.Val())
   482  
   483  	case *types.TypeName:
   484  		t := obj.Type()
   485  
   486  		if tparam, ok := t.(*typeparams.TypeParam); ok {
   487  			w.tag('P')
   488  			w.pos(obj.Pos())
   489  			constraint := tparam.Constraint()
   490  			if p.version >= iexportVersionGo1_18 {
   491  				implicit := false
   492  				if iface, _ := constraint.(*types.Interface); iface != nil {
   493  					implicit = typeparams.IsImplicit(iface)
   494  				}
   495  				w.bool(implicit)
   496  			}
   497  			w.typ(constraint, obj.Pkg())
   498  			break
   499  		}
   500  
   501  		if obj.IsAlias() {
   502  			w.tag('A')
   503  			w.pos(obj.Pos())
   504  			w.typ(t, obj.Pkg())
   505  			break
   506  		}
   507  
   508  		// Defined type.
   509  		named, ok := t.(*types.Named)
   510  		if !ok {
   511  			panic(internalErrorf("%s is not a defined type", t))
   512  		}
   513  
   514  		if typeparams.ForNamed(named).Len() == 0 {
   515  			w.tag('T')
   516  		} else {
   517  			w.tag('U')
   518  		}
   519  		w.pos(obj.Pos())
   520  
   521  		if typeparams.ForNamed(named).Len() > 0 {
   522  			// While importing the type parameters, tparamList computes and records
   523  			// their export name, so that it can be later used when writing the index.
   524  			w.tparamList(obj.Name(), typeparams.ForNamed(named), obj.Pkg())
   525  		}
   526  
   527  		underlying := obj.Type().Underlying()
   528  		w.typ(underlying, obj.Pkg())
   529  
   530  		if types.IsInterface(t) {
   531  			break
   532  		}
   533  
   534  		n := named.NumMethods()
   535  		w.uint64(uint64(n))
   536  		for i := 0; i < n; i++ {
   537  			m := named.Method(i)
   538  			w.pos(m.Pos())
   539  			w.string(m.Name())
   540  			sig, _ := m.Type().(*types.Signature)
   541  
   542  			// Receiver type parameters are type arguments of the receiver type, so
   543  			// their name must be qualified before exporting recv.
   544  			if rparams := typeparams.RecvTypeParams(sig); rparams.Len() > 0 {
   545  				prefix := obj.Name() + "." + m.Name()
   546  				for i := 0; i < rparams.Len(); i++ {
   547  					rparam := rparams.At(i)
   548  					name := tparamExportName(prefix, rparam)
   549  					w.p.tparamNames[rparam.Obj()] = name
   550  				}
   551  			}
   552  			w.param(sig.Recv())
   553  			w.signature(sig)
   554  		}
   555  
   556  	default:
   557  		panic(internalErrorf("unexpected object: %v", obj))
   558  	}
   559  
   560  	p.declIndex[obj] = w.flush()
   561  }
   562  
   563  func (w *exportWriter) tag(tag byte) {
   564  	w.data.WriteByte(tag)
   565  }
   566  
   567  func (w *exportWriter) pos(pos token.Pos) {
   568  	if w.p.shallow {
   569  		w.posV2(pos)
   570  	} else if w.p.version >= iexportVersionPosCol {
   571  		w.posV1(pos)
   572  	} else {
   573  		w.posV0(pos)
   574  	}
   575  }
   576  
   577  // posV2 encoding (used only in shallow mode) records positions as
   578  // (file, offset), where file is the index in the token.File table
   579  // (which records the file name and newline offsets) and offset is a
   580  // byte offset. It effectively ignores //line directives.
   581  func (w *exportWriter) posV2(pos token.Pos) {
   582  	if pos == token.NoPos {
   583  		w.uint64(0)
   584  		return
   585  	}
   586  	file := w.p.fset.File(pos) // fset must be non-nil
   587  	index, offset := w.p.fileIndexAndOffset(file, pos)
   588  	w.uint64(1 + index)
   589  	w.uint64(offset)
   590  }
   591  
   592  func (w *exportWriter) posV1(pos token.Pos) {
   593  	if w.p.fset == nil {
   594  		w.int64(0)
   595  		return
   596  	}
   597  
   598  	p := w.p.fset.Position(pos)
   599  	file := p.Filename
   600  	line := int64(p.Line)
   601  	column := int64(p.Column)
   602  
   603  	deltaColumn := (column - w.prevColumn) << 1
   604  	deltaLine := (line - w.prevLine) << 1
   605  
   606  	if file != w.prevFile {
   607  		deltaLine |= 1
   608  	}
   609  	if deltaLine != 0 {
   610  		deltaColumn |= 1
   611  	}
   612  
   613  	w.int64(deltaColumn)
   614  	if deltaColumn&1 != 0 {
   615  		w.int64(deltaLine)
   616  		if deltaLine&1 != 0 {
   617  			w.string(file)
   618  		}
   619  	}
   620  
   621  	w.prevFile = file
   622  	w.prevLine = line
   623  	w.prevColumn = column
   624  }
   625  
   626  func (w *exportWriter) posV0(pos token.Pos) {
   627  	if w.p.fset == nil {
   628  		w.int64(0)
   629  		return
   630  	}
   631  
   632  	p := w.p.fset.Position(pos)
   633  	file := p.Filename
   634  	line := int64(p.Line)
   635  
   636  	// When file is the same as the last position (common case),
   637  	// we can save a few bytes by delta encoding just the line
   638  	// number.
   639  	//
   640  	// Note: Because data objects may be read out of order (or not
   641  	// at all), we can only apply delta encoding within a single
   642  	// object. This is handled implicitly by tracking prevFile and
   643  	// prevLine as fields of exportWriter.
   644  
   645  	if file == w.prevFile {
   646  		delta := line - w.prevLine
   647  		w.int64(delta)
   648  		if delta == deltaNewFile {
   649  			w.int64(-1)
   650  		}
   651  	} else {
   652  		w.int64(deltaNewFile)
   653  		w.int64(line) // line >= 0
   654  		w.string(file)
   655  		w.prevFile = file
   656  	}
   657  	w.prevLine = line
   658  }
   659  
   660  func (w *exportWriter) pkg(pkg *types.Package) {
   661  	// Ensure any referenced packages are declared in the main index.
   662  	w.p.allPkgs[pkg] = true
   663  
   664  	w.string(w.exportPath(pkg))
   665  }
   666  
   667  func (w *exportWriter) qualifiedType(obj *types.TypeName) {
   668  	name := w.p.exportName(obj)
   669  
   670  	// Ensure any referenced declarations are written out too.
   671  	w.p.pushDecl(obj)
   672  	w.string(name)
   673  	w.pkg(obj.Pkg())
   674  }
   675  
   676  func (w *exportWriter) typ(t types.Type, pkg *types.Package) {
   677  	w.data.uint64(w.p.typOff(t, pkg))
   678  }
   679  
   680  func (p *iexporter) newWriter() *exportWriter {
   681  	return &exportWriter{p: p}
   682  }
   683  
   684  func (w *exportWriter) flush() uint64 {
   685  	off := uint64(w.p.data0.Len())
   686  	io.Copy(&w.p.data0, &w.data)
   687  	return off
   688  }
   689  
   690  func (p *iexporter) typOff(t types.Type, pkg *types.Package) uint64 {
   691  	off, ok := p.typIndex[t]
   692  	if !ok {
   693  		w := p.newWriter()
   694  		w.doTyp(t, pkg)
   695  		off = predeclReserved + w.flush()
   696  		p.typIndex[t] = off
   697  	}
   698  	return off
   699  }
   700  
   701  func (w *exportWriter) startType(k itag) {
   702  	w.data.uint64(uint64(k))
   703  }
   704  
   705  func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
   706  	if trace {
   707  		w.p.trace("exporting type %s (%T)", t, t)
   708  		w.p.indent++
   709  		defer func() {
   710  			w.p.indent--
   711  			w.p.trace("=> %s", t)
   712  		}()
   713  	}
   714  	switch t := t.(type) {
   715  	case *types.Named:
   716  		if targs := typeparams.NamedTypeArgs(t); targs.Len() > 0 {
   717  			w.startType(instanceType)
   718  			// TODO(rfindley): investigate if this position is correct, and if it
   719  			// matters.
   720  			w.pos(t.Obj().Pos())
   721  			w.typeList(targs, pkg)
   722  			w.typ(typeparams.NamedTypeOrigin(t), pkg)
   723  			return
   724  		}
   725  		w.startType(definedType)
   726  		w.qualifiedType(t.Obj())
   727  
   728  	case *typeparams.TypeParam:
   729  		w.startType(typeParamType)
   730  		w.qualifiedType(t.Obj())
   731  
   732  	case *types.Pointer:
   733  		w.startType(pointerType)
   734  		w.typ(t.Elem(), pkg)
   735  
   736  	case *types.Slice:
   737  		w.startType(sliceType)
   738  		w.typ(t.Elem(), pkg)
   739  
   740  	case *types.Array:
   741  		w.startType(arrayType)
   742  		w.uint64(uint64(t.Len()))
   743  		w.typ(t.Elem(), pkg)
   744  
   745  	case *types.Chan:
   746  		w.startType(chanType)
   747  		// 1 RecvOnly; 2 SendOnly; 3 SendRecv
   748  		var dir uint64
   749  		switch t.Dir() {
   750  		case types.RecvOnly:
   751  			dir = 1
   752  		case types.SendOnly:
   753  			dir = 2
   754  		case types.SendRecv:
   755  			dir = 3
   756  		}
   757  		w.uint64(dir)
   758  		w.typ(t.Elem(), pkg)
   759  
   760  	case *types.Map:
   761  		w.startType(mapType)
   762  		w.typ(t.Key(), pkg)
   763  		w.typ(t.Elem(), pkg)
   764  
   765  	case *types.Signature:
   766  		w.startType(signatureType)
   767  		w.setPkg(pkg, true)
   768  		w.signature(t)
   769  
   770  	case *types.Struct:
   771  		w.startType(structType)
   772  		n := t.NumFields()
   773  		if n > 0 {
   774  			w.setPkg(t.Field(0).Pkg(), true) // qualifying package for field objects
   775  		} else {
   776  			w.setPkg(pkg, true)
   777  		}
   778  		w.uint64(uint64(n))
   779  		for i := 0; i < n; i++ {
   780  			f := t.Field(i)
   781  			w.pos(f.Pos())
   782  			w.string(f.Name()) // unexported fields implicitly qualified by prior setPkg
   783  			w.typ(f.Type(), pkg)
   784  			w.bool(f.Anonymous())
   785  			w.string(t.Tag(i)) // note (or tag)
   786  		}
   787  
   788  	case *types.Interface:
   789  		w.startType(interfaceType)
   790  		w.setPkg(pkg, true)
   791  
   792  		n := t.NumEmbeddeds()
   793  		w.uint64(uint64(n))
   794  		for i := 0; i < n; i++ {
   795  			ft := t.EmbeddedType(i)
   796  			tPkg := pkg
   797  			if named, _ := ft.(*types.Named); named != nil {
   798  				w.pos(named.Obj().Pos())
   799  			} else {
   800  				w.pos(token.NoPos)
   801  			}
   802  			w.typ(ft, tPkg)
   803  		}
   804  
   805  		n = t.NumExplicitMethods()
   806  		w.uint64(uint64(n))
   807  		for i := 0; i < n; i++ {
   808  			m := t.ExplicitMethod(i)
   809  			w.pos(m.Pos())
   810  			w.string(m.Name())
   811  			sig, _ := m.Type().(*types.Signature)
   812  			w.signature(sig)
   813  		}
   814  
   815  	case *typeparams.Union:
   816  		w.startType(unionType)
   817  		nt := t.Len()
   818  		w.uint64(uint64(nt))
   819  		for i := 0; i < nt; i++ {
   820  			term := t.Term(i)
   821  			w.bool(term.Tilde())
   822  			w.typ(term.Type(), pkg)
   823  		}
   824  
   825  	default:
   826  		panic(internalErrorf("unexpected type: %v, %v", t, reflect.TypeOf(t)))
   827  	}
   828  }
   829  
   830  func (w *exportWriter) setPkg(pkg *types.Package, write bool) {
   831  	if write {
   832  		w.pkg(pkg)
   833  	}
   834  
   835  	w.currPkg = pkg
   836  }
   837  
   838  func (w *exportWriter) signature(sig *types.Signature) {
   839  	w.paramList(sig.Params())
   840  	w.paramList(sig.Results())
   841  	if sig.Params().Len() > 0 {
   842  		w.bool(sig.Variadic())
   843  	}
   844  }
   845  
   846  func (w *exportWriter) typeList(ts *typeparams.TypeList, pkg *types.Package) {
   847  	w.uint64(uint64(ts.Len()))
   848  	for i := 0; i < ts.Len(); i++ {
   849  		w.typ(ts.At(i), pkg)
   850  	}
   851  }
   852  
   853  func (w *exportWriter) tparamList(prefix string, list *typeparams.TypeParamList, pkg *types.Package) {
   854  	ll := uint64(list.Len())
   855  	w.uint64(ll)
   856  	for i := 0; i < list.Len(); i++ {
   857  		tparam := list.At(i)
   858  		// Set the type parameter exportName before exporting its type.
   859  		exportName := tparamExportName(prefix, tparam)
   860  		w.p.tparamNames[tparam.Obj()] = exportName
   861  		w.typ(list.At(i), pkg)
   862  	}
   863  }
   864  
   865  const blankMarker = "$"
   866  
   867  // tparamExportName returns the 'exported' name of a type parameter, which
   868  // differs from its actual object name: it is prefixed with a qualifier, and
   869  // blank type parameter names are disambiguated by their index in the type
   870  // parameter list.
   871  func tparamExportName(prefix string, tparam *typeparams.TypeParam) string {
   872  	assert(prefix != "")
   873  	name := tparam.Obj().Name()
   874  	if name == "_" {
   875  		name = blankMarker + strconv.Itoa(tparam.Index())
   876  	}
   877  	return prefix + "." + name
   878  }
   879  
   880  // tparamName returns the real name of a type parameter, after stripping its
   881  // qualifying prefix and reverting blank-name encoding. See tparamExportName
   882  // for details.
   883  func tparamName(exportName string) string {
   884  	// Remove the "path" from the type param name that makes it unique.
   885  	ix := strings.LastIndex(exportName, ".")
   886  	if ix < 0 {
   887  		errorf("malformed type parameter export name %s: missing prefix", exportName)
   888  	}
   889  	name := exportName[ix+1:]
   890  	if strings.HasPrefix(name, blankMarker) {
   891  		return "_"
   892  	}
   893  	return name
   894  }
   895  
   896  func (w *exportWriter) paramList(tup *types.Tuple) {
   897  	n := tup.Len()
   898  	w.uint64(uint64(n))
   899  	for i := 0; i < n; i++ {
   900  		w.param(tup.At(i))
   901  	}
   902  }
   903  
   904  func (w *exportWriter) param(obj types.Object) {
   905  	w.pos(obj.Pos())
   906  	w.localIdent(obj)
   907  	w.typ(obj.Type(), obj.Pkg())
   908  }
   909  
   910  func (w *exportWriter) value(typ types.Type, v constant.Value) {
   911  	w.typ(typ, nil)
   912  	if w.p.version >= iexportVersionGo1_18 {
   913  		w.int64(int64(v.Kind()))
   914  	}
   915  
   916  	switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType {
   917  	case types.IsBoolean:
   918  		w.bool(constant.BoolVal(v))
   919  	case types.IsInteger:
   920  		var i big.Int
   921  		if i64, exact := constant.Int64Val(v); exact {
   922  			i.SetInt64(i64)
   923  		} else if ui64, exact := constant.Uint64Val(v); exact {
   924  			i.SetUint64(ui64)
   925  		} else {
   926  			i.SetString(v.ExactString(), 10)
   927  		}
   928  		w.mpint(&i, typ)
   929  	case types.IsFloat:
   930  		f := constantToFloat(v)
   931  		w.mpfloat(f, typ)
   932  	case types.IsComplex:
   933  		w.mpfloat(constantToFloat(constant.Real(v)), typ)
   934  		w.mpfloat(constantToFloat(constant.Imag(v)), typ)
   935  	case types.IsString:
   936  		w.string(constant.StringVal(v))
   937  	default:
   938  		if b.Kind() == types.Invalid {
   939  			// package contains type errors
   940  			break
   941  		}
   942  		panic(internalErrorf("unexpected type %v (%v)", typ, typ.Underlying()))
   943  	}
   944  }
   945  
   946  // constantToFloat converts a constant.Value with kind constant.Float to a
   947  // big.Float.
   948  func constantToFloat(x constant.Value) *big.Float {
   949  	x = constant.ToFloat(x)
   950  	// Use the same floating-point precision (512) as cmd/compile
   951  	// (see Mpprec in cmd/compile/internal/gc/mpfloat.go).
   952  	const mpprec = 512
   953  	var f big.Float
   954  	f.SetPrec(mpprec)
   955  	if v, exact := constant.Float64Val(x); exact {
   956  		// float64
   957  		f.SetFloat64(v)
   958  	} else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int {
   959  		// TODO(gri): add big.Rat accessor to constant.Value.
   960  		n := valueToRat(num)
   961  		d := valueToRat(denom)
   962  		f.SetRat(n.Quo(n, d))
   963  	} else {
   964  		// Value too large to represent as a fraction => inaccessible.
   965  		// TODO(gri): add big.Float accessor to constant.Value.
   966  		_, ok := f.SetString(x.ExactString())
   967  		assert(ok)
   968  	}
   969  	return &f
   970  }
   971  
   972  // mpint exports a multi-precision integer.
   973  //
   974  // For unsigned types, small values are written out as a single
   975  // byte. Larger values are written out as a length-prefixed big-endian
   976  // byte string, where the length prefix is encoded as its complement.
   977  // For example, bytes 0, 1, and 2 directly represent the integer
   978  // values 0, 1, and 2; while bytes 255, 254, and 253 indicate a 1-,
   979  // 2-, and 3-byte big-endian string follow.
   980  //
   981  // Encoding for signed types use the same general approach as for
   982  // unsigned types, except small values use zig-zag encoding and the
   983  // bottom bit of length prefix byte for large values is reserved as a
   984  // sign bit.
   985  //
   986  // The exact boundary between small and large encodings varies
   987  // according to the maximum number of bytes needed to encode a value
   988  // of type typ. As a special case, 8-bit types are always encoded as a
   989  // single byte.
   990  //
   991  // TODO(mdempsky): Is this level of complexity really worthwhile?
   992  func (w *exportWriter) mpint(x *big.Int, typ types.Type) {
   993  	basic, ok := typ.Underlying().(*types.Basic)
   994  	if !ok {
   995  		panic(internalErrorf("unexpected type %v (%T)", typ.Underlying(), typ.Underlying()))
   996  	}
   997  
   998  	signed, maxBytes := intSize(basic)
   999  
  1000  	negative := x.Sign() < 0
  1001  	if !signed && negative {
  1002  		panic(internalErrorf("negative unsigned integer; type %v, value %v", typ, x))
  1003  	}
  1004  
  1005  	b := x.Bytes()
  1006  	if len(b) > 0 && b[0] == 0 {
  1007  		panic(internalErrorf("leading zeros"))
  1008  	}
  1009  	if uint(len(b)) > maxBytes {
  1010  		panic(internalErrorf("bad mpint length: %d > %d (type %v, value %v)", len(b), maxBytes, typ, x))
  1011  	}
  1012  
  1013  	maxSmall := 256 - maxBytes
  1014  	if signed {
  1015  		maxSmall = 256 - 2*maxBytes
  1016  	}
  1017  	if maxBytes == 1 {
  1018  		maxSmall = 256
  1019  	}
  1020  
  1021  	// Check if x can use small value encoding.
  1022  	if len(b) <= 1 {
  1023  		var ux uint
  1024  		if len(b) == 1 {
  1025  			ux = uint(b[0])
  1026  		}
  1027  		if signed {
  1028  			ux <<= 1
  1029  			if negative {
  1030  				ux--
  1031  			}
  1032  		}
  1033  		if ux < maxSmall {
  1034  			w.data.WriteByte(byte(ux))
  1035  			return
  1036  		}
  1037  	}
  1038  
  1039  	n := 256 - uint(len(b))
  1040  	if signed {
  1041  		n = 256 - 2*uint(len(b))
  1042  		if negative {
  1043  			n |= 1
  1044  		}
  1045  	}
  1046  	if n < maxSmall || n >= 256 {
  1047  		panic(internalErrorf("encoding mistake: %d, %v, %v => %d", len(b), signed, negative, n))
  1048  	}
  1049  
  1050  	w.data.WriteByte(byte(n))
  1051  	w.data.Write(b)
  1052  }
  1053  
  1054  // mpfloat exports a multi-precision floating point number.
  1055  //
  1056  // The number's value is decomposed into mantissa × 2**exponent, where
  1057  // mantissa is an integer. The value is written out as mantissa (as a
  1058  // multi-precision integer) and then the exponent, except exponent is
  1059  // omitted if mantissa is zero.
  1060  func (w *exportWriter) mpfloat(f *big.Float, typ types.Type) {
  1061  	if f.IsInf() {
  1062  		panic("infinite constant")
  1063  	}
  1064  
  1065  	// Break into f = mant × 2**exp, with 0.5 <= mant < 1.
  1066  	var mant big.Float
  1067  	exp := int64(f.MantExp(&mant))
  1068  
  1069  	// Scale so that mant is an integer.
  1070  	prec := mant.MinPrec()
  1071  	mant.SetMantExp(&mant, int(prec))
  1072  	exp -= int64(prec)
  1073  
  1074  	manti, acc := mant.Int(nil)
  1075  	if acc != big.Exact {
  1076  		panic(internalErrorf("mantissa scaling failed for %f (%s)", f, acc))
  1077  	}
  1078  	w.mpint(manti, typ)
  1079  	if manti.Sign() != 0 {
  1080  		w.int64(exp)
  1081  	}
  1082  }
  1083  
  1084  func (w *exportWriter) bool(b bool) bool {
  1085  	var x uint64
  1086  	if b {
  1087  		x = 1
  1088  	}
  1089  	w.uint64(x)
  1090  	return b
  1091  }
  1092  
  1093  func (w *exportWriter) int64(x int64)   { w.data.int64(x) }
  1094  func (w *exportWriter) uint64(x uint64) { w.data.uint64(x) }
  1095  func (w *exportWriter) string(s string) { w.uint64(w.p.stringOff(s)) }
  1096  
  1097  func (w *exportWriter) localIdent(obj types.Object) {
  1098  	// Anonymous parameters.
  1099  	if obj == nil {
  1100  		w.string("")
  1101  		return
  1102  	}
  1103  
  1104  	name := obj.Name()
  1105  	if name == "_" {
  1106  		w.string("_")
  1107  		return
  1108  	}
  1109  
  1110  	w.string(name)
  1111  }
  1112  
  1113  type intWriter struct {
  1114  	bytes.Buffer
  1115  }
  1116  
  1117  func (w *intWriter) int64(x int64) {
  1118  	var buf [binary.MaxVarintLen64]byte
  1119  	n := binary.PutVarint(buf[:], x)
  1120  	w.Write(buf[:n])
  1121  }
  1122  
  1123  func (w *intWriter) uint64(x uint64) {
  1124  	var buf [binary.MaxVarintLen64]byte
  1125  	n := binary.PutUvarint(buf[:], x)
  1126  	w.Write(buf[:n])
  1127  }
  1128  
  1129  func assert(cond bool) {
  1130  	if !cond {
  1131  		panic("internal error: assertion failed")
  1132  	}
  1133  }
  1134  
  1135  // The below is copied from go/src/cmd/compile/internal/gc/syntax.go.
  1136  
  1137  // objQueue is a FIFO queue of types.Object. The zero value of objQueue is
  1138  // a ready-to-use empty queue.
  1139  type objQueue struct {
  1140  	ring       []types.Object
  1141  	head, tail int
  1142  }
  1143  
  1144  // empty returns true if q contains no Nodes.
  1145  func (q *objQueue) empty() bool {
  1146  	return q.head == q.tail
  1147  }
  1148  
  1149  // pushTail appends n to the tail of the queue.
  1150  func (q *objQueue) pushTail(obj types.Object) {
  1151  	if len(q.ring) == 0 {
  1152  		q.ring = make([]types.Object, 16)
  1153  	} else if q.head+len(q.ring) == q.tail {
  1154  		// Grow the ring.
  1155  		nring := make([]types.Object, len(q.ring)*2)
  1156  		// Copy the old elements.
  1157  		part := q.ring[q.head%len(q.ring):]
  1158  		if q.tail-q.head <= len(part) {
  1159  			part = part[:q.tail-q.head]
  1160  			copy(nring, part)
  1161  		} else {
  1162  			pos := copy(nring, part)
  1163  			copy(nring[pos:], q.ring[:q.tail%len(q.ring)])
  1164  		}
  1165  		q.ring, q.head, q.tail = nring, 0, q.tail-q.head
  1166  	}
  1167  
  1168  	q.ring[q.tail%len(q.ring)] = obj
  1169  	q.tail++
  1170  }
  1171  
  1172  // popHead pops a node from the head of the queue. It panics if q is empty.
  1173  func (q *objQueue) popHead() types.Object {
  1174  	if q.empty() {
  1175  		panic("dequeue empty")
  1176  	}
  1177  	obj := q.ring[q.head%len(q.ring)]
  1178  	q.head++
  1179  	return obj
  1180  }