// IExportShallow encodes "shallow" export data for the specified package. // // For types, we use "shallow" export data. Historically, the Go // compiler always produced a summary of the types for a given package // that included types from other packages that it indirectly // referenced: "deep" export data. This had the advantage that the // compiler (and analogous tools such as gopls) need only load one // file per direct import. However, it meant that the files tended to // get larger based on the level of the package in the import // graph. For example, higher-level packages in the kubernetes module // have over 1MB of "deep" export data, even when they have almost no // content of their own, merely because they mention a major type that // references many others. In pathological cases the export data was // 300x larger than the source for a package due to this quadratic // growth. // // "Shallow" export data means that the serialized types describe only // a single package. If those types mention types from other packages, // the type checker may need to request additional packages beyond // just the direct imports. Type information for the entire transitive // closure of imports is provided (lazily) by the DAG. // // No promises are made about the encoding other than that it can be decoded by // the same version of IIExportShallow. If you plan to save export data in the // file system, be sure to include a cryptographic digest of the executable in // the key to avoid version skew. // // If the provided reportf func is non-nil, it will be used for reporting bugs // encountered during export. // TODO(rfindley): remove reportf when we are confident enough in the new // objectpath encoding. func IExportShallow(fset *token.FileSet, pkg *types.Package, reportf ReportFunc) ([]byte, error) { … } // IImportShallow decodes "shallow" types.Package data encoded by // [IExportShallow] in the same executable. This function cannot import data // from cmd/compile or gcexportdata.Write. // // The importer calls getPackages to obtain package symbols for all // packages mentioned in the export data, including the one being // decoded. // // If the provided reportf func is non-nil, it will be used for reporting bugs // encountered during import. // TODO(rfindley): remove reportf when we are confident enough in the new // objectpath encoding. func IImportShallow(fset *token.FileSet, getPackages GetPackagesFunc, data []byte, path string, reportf ReportFunc) (*types.Package, error) { … } type ReportFunc … const bundleVersion … // IExportData writes indexed export data for pkg to out. // // If no file set is provided, position info will be missing. // The package path of the top-level package will not be recorded, // so that calls to IImportData can override with a provided package path. func IExportData(out io.Writer, fset *token.FileSet, pkg *types.Package) error { … } // IExportBundle writes an indexed export bundle for pkgs to out. func IExportBundle(out io.Writer, fset *token.FileSet, pkgs []*types.Package) error { … } func iexportCommon(out io.Writer, fset *token.FileSet, bundle, shallow bool, version int, pkgs []*types.Package) (err error) { … } // encodeFile writes to w a representation of the file sufficient to // faithfully restore position information about all needed offsets. // Mutates the needed array. func (p *iexporter) encodeFile(w *intWriter, file *token.File, needed []uint64) { … } // writeIndex writes out an object index. mainIndex indicates whether // we're writing out the main index, which is also read by // non-compiler tools and includes a complete package description // (i.e., name and height). func (w *exportWriter) writeIndex(index map[types.Object]uint64) { … } // exportName returns the 'exported' name of an object. It differs from // obj.Name() only for type parameters (see tparamExportName for details). func (p *iexporter) exportName(obj types.Object) (res string) { … } type iexporter … type filePositions … func (p *iexporter) trace(format string, args ...interface{ … } // objectpathEncoder returns the lazily allocated objectpath.Encoder to use // when encoding objects in other packages during shallow export. // // Using a shared Encoder amortizes some of cost of objectpath search. func (p *iexporter) objectpathEncoder() *objectpath.Encoder { … } // stringOff returns the offset of s within the string section. // If not already present, it's added to the end. func (p *iexporter) stringOff(s string) uint64 { … } // fileIndexAndOffset returns the index of the token.File and the byte offset of pos within it. func (p *iexporter) fileIndexAndOffset(file *token.File, pos token.Pos) (uint64, uint64) { … } // pushDecl adds n to the declaration work queue, if not already present. func (p *iexporter) pushDecl(obj types.Object) { … } type exportWriter … func (w *exportWriter) exportPath(pkg *types.Package) string { … } func (p *iexporter) doDecl(obj types.Object) { … } func (w *exportWriter) tag(tag byte) { … } func (w *exportWriter) pos(pos token.Pos) { … } // posV2 encoding (used only in shallow mode) records positions as // (file, offset), where file is the index in the token.File table // (which records the file name and newline offsets) and offset is a // byte offset. It effectively ignores //line directives. func (w *exportWriter) posV2(pos token.Pos) { … } func (w *exportWriter) posV1(pos token.Pos) { … } func (w *exportWriter) posV0(pos token.Pos) { … } func (w *exportWriter) pkg(pkg *types.Package) { … } func (w *exportWriter) qualifiedType(obj *types.TypeName) { … } // TODO(rfindley): what does 'pkg' even mean here? It would be better to pass // it in explicitly into signatures and structs that may use it for // constructing fields. func (w *exportWriter) typ(t types.Type, pkg *types.Package) { … } func (p *iexporter) newWriter() *exportWriter { … } func (w *exportWriter) flush() uint64 { … } func (p *iexporter) typOff(t types.Type, pkg *types.Package) uint64 { … } func (w *exportWriter) startType(k itag) { … } func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { … } // objectPath writes the package and objectPath to use to look up obj in a // different package, when encoding in "shallow" mode. // // When doing a shallow import, the importer creates only the local package, // and requests package symbols for dependencies from the client. // However, certain types defined in the local package may hold objects defined // (perhaps deeply) within another package. // // For example, consider the following: // // package a // func F() chan * map[string] struct { X int } // // package b // import "a" // var B = a.F() // // In this example, the type of b.B holds fields defined in package a. // In order to have the correct canonical objects for the field defined in the // type of B, they are encoded as objectPaths and later looked up in the // importer. The same problem applies to interface methods. func (w *exportWriter) objectPath(obj types.Object) { … } func (w *exportWriter) signature(sig *types.Signature) { … } func (w *exportWriter) typeList(ts *types.TypeList, pkg *types.Package) { … } func (w *exportWriter) tparamList(prefix string, list *types.TypeParamList, pkg *types.Package) { … } const blankMarker … // tparamExportName returns the 'exported' name of a type parameter, which // differs from its actual object name: it is prefixed with a qualifier, and // blank type parameter names are disambiguated by their index in the type // parameter list. func tparamExportName(prefix string, tparam *types.TypeParam) string { … } // tparamName returns the real name of a type parameter, after stripping its // qualifying prefix and reverting blank-name encoding. See tparamExportName // for details. func tparamName(exportName string) string { … } func (w *exportWriter) paramList(tup *types.Tuple) { … } func (w *exportWriter) param(obj types.Object) { … } func (w *exportWriter) value(typ types.Type, v constant.Value) { … } // constantToFloat converts a constant.Value with kind constant.Float to a // big.Float. func constantToFloat(x constant.Value) *big.Float { … } func valueToRat(x constant.Value) *big.Rat { … } // mpint exports a multi-precision integer. // // For unsigned types, small values are written out as a single // byte. Larger values are written out as a length-prefixed big-endian // byte string, where the length prefix is encoded as its complement. // For example, bytes 0, 1, and 2 directly represent the integer // values 0, 1, and 2; while bytes 255, 254, and 253 indicate a 1-, // 2-, and 3-byte big-endian string follow. // // Encoding for signed types use the same general approach as for // unsigned types, except small values use zig-zag encoding and the // bottom bit of length prefix byte for large values is reserved as a // sign bit. // // The exact boundary between small and large encodings varies // according to the maximum number of bytes needed to encode a value // of type typ. As a special case, 8-bit types are always encoded as a // single byte. // // TODO(mdempsky): Is this level of complexity really worthwhile? func (w *exportWriter) mpint(x *big.Int, typ types.Type) { … } // mpfloat exports a multi-precision floating point number. // // The number's value is decomposed into mantissa × 2**exponent, where // mantissa is an integer. The value is written out as mantissa (as a // multi-precision integer) and then the exponent, except exponent is // omitted if mantissa is zero. func (w *exportWriter) mpfloat(f *big.Float, typ types.Type) { … } func (w *exportWriter) bool(b bool) bool { … } func (w *exportWriter) int64(x int64) { … } func (w *exportWriter) uint64(x uint64) { … } func (w *exportWriter) string(s string) { … } func (w *exportWriter) localIdent(obj types.Object) { … } type intWriter … func (w *intWriter) int64(x int64) { … } func (w *intWriter) uint64(x uint64) { … } func assert(cond bool) { … } type objQueue … // empty returns true if q contains no Nodes. func (q *objQueue) empty() bool { … } // pushTail appends n to the tail of the queue. func (q *objQueue) pushTail(obj types.Object) { … } // popHead pops a node from the head of the queue. It panics if q is empty. func (q *objQueue) popHead() types.Object { … } type internalError … func (e internalError) Error() string { … } // TODO(adonovan): make this call panic, so that it's symmetric with errorf. // Otherwise it's easy to forget to do anything with the error. // // TODO(adonovan): also, consider switching the names "errorf" and // "internalErrorf" as the former is used for bugs, whose cause is // internal inconsistency, whereas the latter is used for ordinary // situations like bad input, whose cause is external. func internalErrorf(format string, args ...interface{ … }