iexport.go (43809B)
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 package export. 6 // 7 // The indexed export data format is an evolution of the previous 8 // binary export data format. Its chief contribution is introducing an 9 // index table, which allows efficient random access of individual 10 // declarations and inline function bodies. In turn, this allows 11 // avoiding unnecessary work for compilation units that import large 12 // packages. 13 // 14 // 15 // The top-level data format is structured as: 16 // 17 // Header struct { 18 // Tag byte // 'i' 19 // Version uvarint 20 // StringSize uvarint 21 // DataSize uvarint 22 // } 23 // 24 // Strings [StringSize]byte 25 // Data [DataSize]byte 26 // 27 // MainIndex []struct{ 28 // PkgPath stringOff 29 // PkgName stringOff 30 // PkgHeight uvarint 31 // 32 // Decls []struct{ 33 // Name stringOff 34 // Offset declOff 35 // } 36 // } 37 // 38 // Fingerprint [8]byte 39 // 40 // uvarint means a uint64 written out using uvarint encoding. 41 // 42 // []T means a uvarint followed by that many T objects. In other 43 // words: 44 // 45 // Len uvarint 46 // Elems [Len]T 47 // 48 // stringOff means a uvarint that indicates an offset within the 49 // Strings section. At that offset is another uvarint, followed by 50 // that many bytes, which form the string value. 51 // 52 // declOff means a uvarint that indicates an offset within the Data 53 // section where the associated declaration can be found. 54 // 55 // 56 // There are five kinds of declarations, distinguished by their first 57 // byte: 58 // 59 // type Var struct { 60 // Tag byte // 'V' 61 // Pos Pos 62 // Type typeOff 63 // } 64 // 65 // type Func struct { 66 // Tag byte // 'F' or 'G' 67 // Pos Pos 68 // TypeParams []typeOff // only present if Tag == 'G' 69 // Signature Signature 70 // } 71 // 72 // type Const struct { 73 // Tag byte // 'C' 74 // Pos Pos 75 // Value Value 76 // } 77 // 78 // type Type struct { 79 // Tag byte // 'T' or 'U' 80 // Pos Pos 81 // TypeParams []typeOff // only present if Tag == 'U' 82 // Underlying typeOff 83 // 84 // Methods []struct{ // omitted if Underlying is an interface type 85 // Pos Pos 86 // Name stringOff 87 // Recv Param 88 // Signature Signature 89 // } 90 // } 91 // 92 // type Alias struct { 93 // Tag byte // 'A' or 'B' 94 // Pos Pos 95 // TypeParams []typeOff // only present if Tag == 'B' 96 // Type typeOff 97 // } 98 // 99 // // "Automatic" declaration of each typeparam 100 // type TypeParam struct { 101 // Tag byte // 'P' 102 // Pos Pos 103 // Implicit bool 104 // Constraint typeOff 105 // } 106 // 107 // typeOff means a uvarint that either indicates a predeclared type, 108 // or an offset into the Data section. If the uvarint is less than 109 // predeclReserved, then it indicates the index into the predeclared 110 // types list (see predeclared in bexport.go for order). Otherwise, 111 // subtracting predeclReserved yields the offset of a type descriptor. 112 // 113 // Value means a type, kind, and type-specific value. See 114 // (*exportWriter).value for details. 115 // 116 // 117 // There are twelve kinds of type descriptors, distinguished by an itag: 118 // 119 // type DefinedType struct { 120 // Tag itag // definedType 121 // Name stringOff 122 // PkgPath stringOff 123 // } 124 // 125 // type PointerType struct { 126 // Tag itag // pointerType 127 // Elem typeOff 128 // } 129 // 130 // type SliceType struct { 131 // Tag itag // sliceType 132 // Elem typeOff 133 // } 134 // 135 // type ArrayType struct { 136 // Tag itag // arrayType 137 // Len uint64 138 // Elem typeOff 139 // } 140 // 141 // type ChanType struct { 142 // Tag itag // chanType 143 // Dir uint64 // 1 RecvOnly; 2 SendOnly; 3 SendRecv 144 // Elem typeOff 145 // } 146 // 147 // type MapType struct { 148 // Tag itag // mapType 149 // Key typeOff 150 // Elem typeOff 151 // } 152 // 153 // type FuncType struct { 154 // Tag itag // signatureType 155 // PkgPath stringOff 156 // Signature Signature 157 // } 158 // 159 // type StructType struct { 160 // Tag itag // structType 161 // PkgPath stringOff 162 // Fields []struct { 163 // Pos Pos 164 // Name stringOff 165 // Type typeOff 166 // Embedded bool 167 // Note stringOff 168 // } 169 // } 170 // 171 // type InterfaceType struct { 172 // Tag itag // interfaceType 173 // PkgPath stringOff 174 // Embeddeds []struct { 175 // Pos Pos 176 // Type typeOff 177 // } 178 // Methods []struct { 179 // Pos Pos 180 // Name stringOff 181 // Signature Signature 182 // } 183 // } 184 // 185 // // Reference to a type param declaration 186 // type TypeParamType struct { 187 // Tag itag // typeParamType 188 // Name stringOff 189 // PkgPath stringOff 190 // } 191 // 192 // // Instantiation of a generic type (like List[T2] or List[int]) 193 // type InstanceType struct { 194 // Tag itag // instanceType 195 // Pos pos 196 // TypeArgs []typeOff 197 // BaseType typeOff 198 // } 199 // 200 // type UnionType struct { 201 // Tag itag // interfaceType 202 // Terms []struct { 203 // tilde bool 204 // Type typeOff 205 // } 206 // } 207 // 208 // 209 // 210 // type Signature struct { 211 // Params []Param 212 // Results []Param 213 // Variadic bool // omitted if Results is empty 214 // } 215 // 216 // type Param struct { 217 // Pos Pos 218 // Name stringOff 219 // Type typOff 220 // } 221 // 222 // 223 // Pos encodes a file:line:column triple, incorporating a simple delta 224 // encoding scheme within a data object. See exportWriter.pos for 225 // details. 226 227 package gcimporter 228 229 import ( 230 "bytes" 231 "encoding/binary" 232 "fmt" 233 "go/constant" 234 "go/token" 235 "go/types" 236 "io" 237 "math/big" 238 "reflect" 239 "slices" 240 "sort" 241 "strconv" 242 "strings" 243 244 "golang.org/x/tools/go/types/objectpath" 245 ) 246 247 // IExportShallow encodes "shallow" export data for the specified package. 248 // 249 // For types, we use "shallow" export data. Historically, the Go 250 // compiler always produced a summary of the types for a given package 251 // that included types from other packages that it indirectly 252 // referenced: "deep" export data. This had the advantage that the 253 // compiler (and analogous tools such as gopls) need only load one 254 // file per direct import. However, it meant that the files tended to 255 // get larger based on the level of the package in the import 256 // graph. For example, higher-level packages in the kubernetes module 257 // have over 1MB of "deep" export data, even when they have almost no 258 // content of their own, merely because they mention a major type that 259 // references many others. In pathological cases the export data was 260 // 300x larger than the source for a package due to this quadratic 261 // growth. 262 // 263 // "Shallow" export data means that the serialized types describe only 264 // a single package. If those types mention types from other packages, 265 // the type checker may need to request additional packages beyond 266 // just the direct imports. Type information for the entire transitive 267 // closure of imports is provided (lazily) by the DAG. 268 // 269 // No promises are made about the encoding other than that it can be decoded by 270 // the same version of IIExportShallow. If you plan to save export data in the 271 // file system, be sure to include a cryptographic digest of the executable in 272 // the key to avoid version skew. 273 // 274 // If the provided reportf func is non-nil, it is used for reporting 275 // bugs (e.g. recovered panics) encountered during export, enabling us 276 // to obtain via telemetry the stack that would otherwise be lost by 277 // merely returning an error. 278 func IExportShallow(fset *token.FileSet, pkg *types.Package, reportf ReportFunc) ([]byte, error) { 279 // In principle this operation can only fail if out.Write fails, 280 // but that's impossible for bytes.Buffer---and as a matter of 281 // fact iexportCommon doesn't even check for I/O errors. 282 // TODO(adonovan): handle I/O errors properly. 283 // TODO(adonovan): use byte slices throughout, avoiding copying. 284 const bundle, shallow = false, true 285 var out bytes.Buffer 286 err := iexportCommon(&out, fset, bundle, shallow, iexportVersion, []*types.Package{pkg}, reportf) 287 return out.Bytes(), err 288 } 289 290 // IImportShallow decodes "shallow" types.Package data encoded by 291 // [IExportShallow] in the same executable. This function cannot import data 292 // from cmd/compile or gcexportdata.Write. 293 // 294 // The importer calls getPackages to obtain package symbols for all 295 // packages mentioned in the export data, including the one being 296 // decoded. 297 // 298 // If the provided reportf func is non-nil, it will be used for reporting bugs 299 // encountered during import. 300 // TODO(rfindley): remove reportf when we are confident enough in the new 301 // objectpath encoding. 302 func IImportShallow(fset *token.FileSet, getPackages GetPackagesFunc, data []byte, path string, reportf ReportFunc) (*types.Package, error) { 303 const bundle = false 304 const shallow = true 305 pkgs, err := iimportCommon(fset, getPackages, data, bundle, path, shallow, reportf) 306 if err != nil { 307 return nil, err 308 } 309 return pkgs[0], nil 310 } 311 312 // ReportFunc is the type of a function used to report formatted bugs. 313 type ReportFunc = func(string, ...any) 314 315 // Current bundled export format version. Increase with each format change. 316 // 0: initial implementation 317 const bundleVersion = 0 318 319 // IExportData writes indexed export data for pkg to out. 320 // 321 // If no file set is provided, position info will be missing. 322 // The package path of the top-level package will not be recorded, 323 // so that calls to IImportData can override with a provided package path. 324 func IExportData(out io.Writer, fset *token.FileSet, pkg *types.Package) error { 325 const bundle, shallow = false, false 326 return iexportCommon(out, fset, bundle, shallow, iexportVersion, []*types.Package{pkg}, nil) 327 } 328 329 // IExportBundle writes an indexed export bundle for pkgs to out. 330 func IExportBundle(out io.Writer, fset *token.FileSet, pkgs []*types.Package) error { 331 const bundle, shallow = true, false 332 return iexportCommon(out, fset, bundle, shallow, iexportVersion, pkgs, nil) 333 } 334 335 func iexportCommon(out io.Writer, fset *token.FileSet, bundle, shallow bool, version int, pkgs []*types.Package, reportf ReportFunc) (err error) { 336 if !debug { 337 defer func() { 338 if e := recover(); e != nil { 339 // Report the stack via telemetry (see #71067). 340 if reportf != nil { 341 reportf("panic in exporter") 342 } 343 if ierr, ok := e.(internalError); ok { 344 // internalError usually means we exported a 345 // bad go/types data structure: a violation 346 // of an implicit precondition of Export. 347 err = ierr 348 return 349 } 350 // Not an internal error; panic again. 351 panic(e) 352 } 353 }() 354 } 355 356 p := iexporter{ 357 fset: fset, 358 version: version, 359 shallow: shallow, 360 allPkgs: map[*types.Package]bool{}, 361 stringIndex: map[string]uint64{}, 362 declIndex: map[types.Object]uint64{}, 363 tparamNames: map[types.Object]string{}, 364 typIndex: map[types.Type]uint64{}, 365 } 366 if !bundle { 367 p.localpkg = pkgs[0] 368 } 369 370 for i, pt := range predeclared() { 371 p.typIndex[pt] = uint64(i) 372 } 373 if len(p.typIndex) > predeclReserved { 374 panic(internalErrorf("too many predeclared types: %d > %d", len(p.typIndex), predeclReserved)) 375 } 376 377 // Initialize work queue with exported declarations. 378 for _, pkg := range pkgs { 379 scope := pkg.Scope() 380 for _, name := range scope.Names() { 381 if token.IsExported(name) { 382 p.pushDecl(scope.Lookup(name)) 383 } 384 } 385 386 if bundle { 387 // Ensure pkg and its imports are included in the index. 388 p.allPkgs[pkg] = true 389 for _, imp := range pkg.Imports() { 390 p.allPkgs[imp] = true 391 } 392 } 393 } 394 395 // Loop until no more work. 396 for !p.declTodo.empty() { 397 p.doDecl(p.declTodo.popHead()) 398 } 399 400 // Produce index of offset of each file record in files. 401 var files intWriter 402 var fileOffset []uint64 // fileOffset[i] is offset in files of file encoded as i 403 if p.shallow { 404 fileOffset = make([]uint64, len(p.fileInfos)) 405 for i, info := range p.fileInfos { 406 fileOffset[i] = uint64(files.Len()) 407 p.encodeFile(&files, info.file, info.needed) 408 } 409 } 410 411 // Append indices to data0 section. 412 dataLen := uint64(p.data0.Len()) 413 w := p.newWriter() 414 w.writeIndex(p.declIndex) 415 416 if bundle { 417 w.uint64(uint64(len(pkgs))) 418 for _, pkg := range pkgs { 419 w.pkg(pkg) 420 imps := pkg.Imports() 421 w.uint64(uint64(len(imps))) 422 for _, imp := range imps { 423 w.pkg(imp) 424 } 425 } 426 } 427 w.flush() 428 429 // Assemble header. 430 var hdr intWriter 431 if bundle { 432 hdr.uint64(bundleVersion) 433 } 434 hdr.uint64(uint64(p.version)) 435 hdr.uint64(uint64(p.strings.Len())) 436 if p.shallow { 437 hdr.uint64(uint64(files.Len())) 438 hdr.uint64(uint64(len(fileOffset))) 439 for _, offset := range fileOffset { 440 hdr.uint64(offset) 441 } 442 } 443 hdr.uint64(dataLen) 444 445 // Flush output. 446 io.Copy(out, &hdr) 447 io.Copy(out, &p.strings) 448 if p.shallow { 449 io.Copy(out, &files) 450 } 451 io.Copy(out, &p.data0) 452 453 return nil 454 } 455 456 // encodeFile writes to w a representation of the file sufficient to 457 // faithfully restore position information about all needed offsets. 458 // Mutates the needed array. 459 func (p *iexporter) encodeFile(w *intWriter, file *token.File, needed []uint64) { 460 _ = needed[0] // precondition: needed is non-empty 461 462 w.uint64(p.stringOff(file.Name())) 463 464 size := uint64(file.Size()) 465 w.uint64(size) 466 467 // Sort the set of needed offsets. Duplicates are harmless. 468 slices.Sort(needed) 469 470 lines := file.Lines() // byte offset of each line start 471 w.uint64(uint64(len(lines))) 472 473 // Rather than record the entire array of line start offsets, 474 // we save only a sparse list of (index, offset) pairs for 475 // the start of each line that contains a needed position. 476 var sparse [][2]int // (index, offset) pairs 477 outer: 478 for i, lineStart := range lines { 479 lineEnd := size 480 if i < len(lines)-1 { 481 lineEnd = uint64(lines[i+1]) 482 } 483 // Does this line contains a needed offset? 484 if needed[0] < lineEnd { 485 sparse = append(sparse, [2]int{i, lineStart}) 486 for needed[0] < lineEnd { 487 needed = needed[1:] 488 if len(needed) == 0 { 489 break outer 490 } 491 } 492 } 493 } 494 495 // Delta-encode the columns. 496 w.uint64(uint64(len(sparse))) 497 var prev [2]int 498 for _, pair := range sparse { 499 w.uint64(uint64(pair[0] - prev[0])) 500 w.uint64(uint64(pair[1] - prev[1])) 501 prev = pair 502 } 503 } 504 505 // writeIndex writes out an object index. mainIndex indicates whether 506 // we're writing out the main index, which is also read by 507 // non-compiler tools and includes a complete package description 508 // (i.e., name and height). 509 func (w *exportWriter) writeIndex(index map[types.Object]uint64) { 510 type pkgObj struct { 511 obj types.Object 512 name string // qualified name; differs from obj.Name for type params 513 } 514 // Build a map from packages to objects from that package. 515 pkgObjs := map[*types.Package][]pkgObj{} 516 517 // For the main index, make sure to include every package that 518 // we reference, even if we're not exporting (or reexporting) 519 // any symbols from it. 520 if w.p.localpkg != nil { 521 pkgObjs[w.p.localpkg] = nil 522 } 523 for pkg := range w.p.allPkgs { 524 pkgObjs[pkg] = nil 525 } 526 527 for obj := range index { 528 name := w.p.exportName(obj) 529 pkgObjs[obj.Pkg()] = append(pkgObjs[obj.Pkg()], pkgObj{obj, name}) 530 } 531 532 var pkgs []*types.Package 533 for pkg, objs := range pkgObjs { 534 pkgs = append(pkgs, pkg) 535 536 sort.Slice(objs, func(i, j int) bool { 537 return objs[i].name < objs[j].name 538 }) 539 } 540 541 sort.Slice(pkgs, func(i, j int) bool { 542 return w.exportPath(pkgs[i]) < w.exportPath(pkgs[j]) 543 }) 544 545 w.uint64(uint64(len(pkgs))) 546 for _, pkg := range pkgs { 547 w.string(w.exportPath(pkg)) 548 w.string(pkg.Name()) 549 w.uint64(uint64(0)) // package height is not needed for go/types 550 551 objs := pkgObjs[pkg] 552 w.uint64(uint64(len(objs))) 553 for _, obj := range objs { 554 w.string(obj.name) 555 w.uint64(index[obj.obj]) 556 } 557 } 558 } 559 560 // exportName returns the 'exported' name of an object. It differs from 561 // obj.Name() only for type parameters (see tparamExportName for details). 562 func (p *iexporter) exportName(obj types.Object) (res string) { 563 if name := p.tparamNames[obj]; name != "" { 564 return name 565 } 566 return obj.Name() 567 } 568 569 type iexporter struct { 570 fset *token.FileSet 571 version int 572 573 shallow bool // don't put types from other packages in the index 574 objEncoder *objectpath.Encoder // encodes objects from other packages in shallow mode; lazily allocated 575 localpkg *types.Package // (nil in bundle mode) 576 577 // allPkgs tracks all packages that have been referenced by 578 // the export data, so we can ensure to include them in the 579 // main index. 580 allPkgs map[*types.Package]bool 581 582 declTodo objQueue 583 584 strings intWriter 585 stringIndex map[string]uint64 586 587 // In shallow mode, object positions are encoded as (file, offset). 588 // Each file is recorded as a line-number table. 589 // Only the lines of needed positions are saved faithfully. 590 fileInfo map[*token.File]uint64 // value is index in fileInfos 591 fileInfos []*filePositions 592 593 data0 intWriter 594 declIndex map[types.Object]uint64 595 tparamNames map[types.Object]string // typeparam->exported name 596 typIndex map[types.Type]uint64 597 598 indent int // for tracing support 599 } 600 601 type filePositions struct { 602 file *token.File 603 needed []uint64 // unordered list of needed file offsets 604 } 605 606 func (p *iexporter) trace(format string, args ...any) { 607 if !trace { 608 // Call sites should also be guarded, but having this check here allows 609 // easily enabling/disabling debug trace statements. 610 return 611 } 612 fmt.Printf(strings.Repeat("..", p.indent)+format+"\n", args...) 613 } 614 615 // objectpathEncoder returns the lazily allocated objectpath.Encoder to use 616 // when encoding objects in other packages during shallow export. 617 // 618 // Using a shared Encoder amortizes some of cost of objectpath search. 619 func (p *iexporter) objectpathEncoder() *objectpath.Encoder { 620 if p.objEncoder == nil { 621 p.objEncoder = new(objectpath.Encoder) 622 } 623 return p.objEncoder 624 } 625 626 // stringOff returns the offset of s within the string section. 627 // If not already present, it's added to the end. 628 func (p *iexporter) stringOff(s string) uint64 { 629 off, ok := p.stringIndex[s] 630 if !ok { 631 off = uint64(p.strings.Len()) 632 p.stringIndex[s] = off 633 634 p.strings.uint64(uint64(len(s))) 635 p.strings.WriteString(s) 636 } 637 return off 638 } 639 640 // fileIndexAndOffset returns the index of the token.File and the byte offset of pos within it. 641 func (p *iexporter) fileIndexAndOffset(file *token.File, pos token.Pos) (uint64, uint64) { 642 index, ok := p.fileInfo[file] 643 if !ok { 644 index = uint64(len(p.fileInfo)) 645 p.fileInfos = append(p.fileInfos, &filePositions{file: file}) 646 if p.fileInfo == nil { 647 p.fileInfo = make(map[*token.File]uint64) 648 } 649 p.fileInfo[file] = index 650 } 651 // Record each needed offset. 652 info := p.fileInfos[index] 653 offset := uint64(file.Offset(pos)) 654 info.needed = append(info.needed, offset) 655 656 return index, offset 657 } 658 659 // pushDecl adds n to the declaration work queue, if not already present. 660 func (p *iexporter) pushDecl(obj types.Object) { 661 // Package unsafe is known to the compiler and predeclared. 662 // Caller should not ask us to do export it. 663 if obj.Pkg() == types.Unsafe { 664 panic("cannot export package unsafe") 665 } 666 667 // Shallow export data: don't index decls from other packages. 668 if p.shallow && obj.Pkg() != p.localpkg { 669 return 670 } 671 672 if _, ok := p.declIndex[obj]; ok { 673 return 674 } 675 676 p.declIndex[obj] = ^uint64(0) // mark obj present in work queue 677 p.declTodo.pushTail(obj) 678 } 679 680 // exportWriter handles writing out individual data section chunks. 681 type exportWriter struct { 682 p *iexporter 683 684 data intWriter 685 prevFile string 686 prevLine int64 687 prevColumn int64 688 } 689 690 func (w *exportWriter) exportPath(pkg *types.Package) string { 691 if pkg == w.p.localpkg { 692 return "" 693 } 694 return pkg.Path() 695 } 696 697 func (p *iexporter) doDecl(obj types.Object) { 698 if trace { 699 p.trace("exporting decl %v (%T)", obj, obj) 700 p.indent++ 701 defer func() { 702 p.indent-- 703 p.trace("=> %s", obj) 704 }() 705 } 706 w := p.newWriter() 707 708 switch obj := obj.(type) { 709 case *types.Var: 710 w.tag(varTag) 711 w.pos(obj.Pos()) 712 w.typ(obj.Type(), obj.Pkg()) 713 714 case *types.Func: 715 sig, _ := obj.Type().(*types.Signature) 716 if sig.Recv() != nil { 717 // We shouldn't see methods in the package scope, 718 // but the type checker may repair "func () F() {}" 719 // to "func (Invalid) F()" and then treat it like "func F()", 720 // so allow that. See golang/go#57729. 721 if sig.Recv().Type() != types.Typ[types.Invalid] { 722 panic(internalErrorf("unexpected method: %v", sig)) 723 } 724 } 725 726 // Function. 727 if sig.TypeParams().Len() == 0 { 728 w.tag(funcTag) 729 } else { 730 w.tag(genericFuncTag) 731 } 732 w.pos(obj.Pos()) 733 // The tparam list of the function type is the declaration of the type 734 // params. So, write out the type params right now. Then those type params 735 // will be referenced via their type offset (via typOff) in all other 736 // places in the signature and function where they are used. 737 // 738 // While importing the type parameters, tparamList computes and records 739 // their export name, so that it can be later used when writing the index. 740 if tparams := sig.TypeParams(); tparams.Len() > 0 { 741 w.tparamList(obj.Name(), tparams, obj.Pkg()) 742 } 743 w.signature(sig) 744 745 case *types.Const: 746 w.tag(constTag) 747 w.pos(obj.Pos()) 748 w.value(obj.Type(), obj.Val()) 749 750 case *types.TypeName: 751 t := obj.Type() 752 753 if tparam, ok := types.Unalias(t).(*types.TypeParam); ok { 754 w.tag(typeParamTag) 755 w.pos(obj.Pos()) 756 constraint := tparam.Constraint() 757 if p.version >= iexportVersionGo1_18 { 758 implicit := false 759 if iface, _ := types.Unalias(constraint).(*types.Interface); iface != nil { 760 implicit = iface.IsImplicit() 761 } 762 w.bool(implicit) 763 } 764 w.typ(constraint, obj.Pkg()) 765 break 766 } 767 768 if obj.IsAlias() { 769 alias, materialized := t.(*types.Alias) // perhaps false for certain built-ins? 770 771 var tparams *types.TypeParamList 772 if materialized { 773 tparams = alias.TypeParams() 774 } 775 if tparams.Len() == 0 { 776 w.tag(aliasTag) 777 } else { 778 w.tag(genericAliasTag) 779 } 780 w.pos(obj.Pos()) 781 if tparams.Len() > 0 { 782 w.tparamList(obj.Name(), tparams, obj.Pkg()) 783 } 784 if materialized { 785 // Preserve materialized aliases, 786 // even of non-exported types. 787 t = alias.Rhs() 788 } 789 w.typ(t, obj.Pkg()) 790 break 791 } 792 793 // Defined type. 794 named, ok := t.(*types.Named) 795 if !ok { 796 panic(internalErrorf("%s is not a defined type", t)) 797 } 798 799 if named.TypeParams().Len() == 0 { 800 w.tag(typeTag) 801 } else { 802 w.tag(genericTypeTag) 803 } 804 w.pos(obj.Pos()) 805 806 if named.TypeParams().Len() > 0 { 807 // While importing the type parameters, tparamList computes and records 808 // their export name, so that it can be later used when writing the index. 809 w.tparamList(obj.Name(), named.TypeParams(), obj.Pkg()) 810 } 811 812 underlying := named.Underlying() 813 w.typ(underlying, obj.Pkg()) 814 815 if types.IsInterface(t) { 816 break 817 } 818 819 n := named.NumMethods() 820 w.uint64(uint64(n)) 821 for i := range n { 822 m := named.Method(i) 823 w.pos(m.Pos()) 824 w.string(m.Name()) 825 sig, _ := m.Type().(*types.Signature) 826 if w.p.version >= iexportVersionGenericMethods && w.bool(sig.TypeParams().Len() > 0) { 827 w.tparamList(obj.Name()+"."+m.Name(), sig.TypeParams(), obj.Pkg()) 828 } 829 830 // Receiver type parameters are type arguments of the receiver type, so 831 // their name must be qualified before exporting recv. 832 if rparams := sig.RecvTypeParams(); rparams.Len() > 0 { 833 prefix := obj.Name() + "." + m.Name() 834 for rparam := range rparams.TypeParams() { 835 name := tparamExportName(prefix, rparam) 836 w.p.tparamNames[rparam.Obj()] = name 837 } 838 } 839 w.param(sig.Recv()) 840 w.signature(sig) 841 } 842 843 default: 844 panic(internalErrorf("unexpected object: %v", obj)) 845 } 846 847 p.declIndex[obj] = w.flush() 848 } 849 850 func (w *exportWriter) tag(tag byte) { 851 w.data.WriteByte(tag) 852 } 853 854 func (w *exportWriter) pos(pos token.Pos) { 855 if w.p.shallow { 856 w.posV2(pos) 857 } else if w.p.version >= iexportVersionPosCol { 858 w.posV1(pos) 859 } else { 860 w.posV0(pos) 861 } 862 } 863 864 // posV2 encoding (used only in shallow mode) records positions as 865 // (file, offset), where file is the index in the token.File table 866 // (which records the file name and newline offsets) and offset is a 867 // byte offset. It effectively ignores //line directives. 868 func (w *exportWriter) posV2(pos token.Pos) { 869 if pos == token.NoPos { 870 w.uint64(0) 871 return 872 } 873 file := w.p.fset.File(pos) // fset must be non-nil 874 index, offset := w.p.fileIndexAndOffset(file, pos) 875 w.uint64(1 + index) 876 w.uint64(offset) 877 } 878 879 func (w *exportWriter) posV1(pos token.Pos) { 880 if w.p.fset == nil { 881 w.int64(0) 882 return 883 } 884 885 p := w.p.fset.Position(pos) 886 file := p.Filename 887 line := int64(p.Line) 888 column := int64(p.Column) 889 890 deltaColumn := (column - w.prevColumn) << 1 891 deltaLine := (line - w.prevLine) << 1 892 893 if file != w.prevFile { 894 deltaLine |= 1 895 } 896 if deltaLine != 0 { 897 deltaColumn |= 1 898 } 899 900 w.int64(deltaColumn) 901 if deltaColumn&1 != 0 { 902 w.int64(deltaLine) 903 if deltaLine&1 != 0 { 904 w.string(file) 905 } 906 } 907 908 w.prevFile = file 909 w.prevLine = line 910 w.prevColumn = column 911 } 912 913 func (w *exportWriter) posV0(pos token.Pos) { 914 if w.p.fset == nil { 915 w.int64(0) 916 return 917 } 918 919 p := w.p.fset.Position(pos) 920 file := p.Filename 921 line := int64(p.Line) 922 923 // When file is the same as the last position (common case), 924 // we can save a few bytes by delta encoding just the line 925 // number. 926 // 927 // Note: Because data objects may be read out of order (or not 928 // at all), we can only apply delta encoding within a single 929 // object. This is handled implicitly by tracking prevFile and 930 // prevLine as fields of exportWriter. 931 932 if file == w.prevFile { 933 delta := line - w.prevLine 934 w.int64(delta) 935 if delta == deltaNewFile { 936 w.int64(-1) 937 } 938 } else { 939 w.int64(deltaNewFile) 940 w.int64(line) // line >= 0 941 w.string(file) 942 w.prevFile = file 943 } 944 w.prevLine = line 945 } 946 947 func (w *exportWriter) pkg(pkg *types.Package) { 948 if pkg == nil { 949 // [exportWriter.typ] accepts a nil pkg only for types 950 // of constants, which cannot contain named objects 951 // such as fields or methods and thus should never 952 // reach this method (#76222). 953 panic("nil package") 954 } 955 // Ensure any referenced packages are declared in the main index. 956 w.p.allPkgs[pkg] = true 957 958 w.string(w.exportPath(pkg)) 959 } 960 961 func (w *exportWriter) qualifiedType(obj *types.TypeName) { 962 name := w.p.exportName(obj) 963 964 // Ensure any referenced declarations are written out too. 965 w.p.pushDecl(obj) 966 w.string(name) 967 w.pkg(obj.Pkg()) 968 } 969 970 // typ emits the specified type. 971 // 972 // Objects within the type (struct fields and interface methods) are 973 // qualified by pkg. It may be nil if the type cannot contain objects, 974 // such as the type of a constant. 975 func (w *exportWriter) typ(t types.Type, pkg *types.Package) { 976 w.data.uint64(w.p.typOff(t, pkg)) 977 } 978 979 func (p *iexporter) newWriter() *exportWriter { 980 return &exportWriter{p: p} 981 } 982 983 func (w *exportWriter) flush() uint64 { 984 off := uint64(w.p.data0.Len()) 985 io.Copy(&w.p.data0, &w.data) 986 return off 987 } 988 989 func (p *iexporter) typOff(t types.Type, pkg *types.Package) uint64 { 990 off, ok := p.typIndex[t] 991 if !ok { 992 w := p.newWriter() 993 w.doTyp(t, pkg) 994 off = predeclReserved + w.flush() 995 p.typIndex[t] = off 996 } 997 return off 998 } 999 1000 func (w *exportWriter) startType(k itag) { 1001 w.data.uint64(uint64(k)) 1002 } 1003 1004 // doTyp is the implementation of [exportWriter.typ]. 1005 func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { 1006 if trace { 1007 w.p.trace("exporting type %s (%T)", t, t) 1008 w.p.indent++ 1009 defer func() { 1010 w.p.indent-- 1011 w.p.trace("=> %s", t) 1012 }() 1013 } 1014 switch t := t.(type) { 1015 case *types.Alias: 1016 if targs := t.TypeArgs(); targs.Len() > 0 { 1017 w.startType(instanceType) 1018 w.pos(t.Obj().Pos()) 1019 w.typeList(targs, pkg) 1020 w.typ(t.Origin(), pkg) 1021 return 1022 } 1023 w.startType(aliasType) 1024 w.qualifiedType(t.Obj()) 1025 1026 case *types.Named: 1027 if targs := t.TypeArgs(); targs.Len() > 0 { 1028 w.startType(instanceType) 1029 // TODO(rfindley): investigate if this position is correct, and if it 1030 // matters. 1031 w.pos(t.Obj().Pos()) 1032 w.typeList(targs, pkg) 1033 w.typ(t.Origin(), pkg) 1034 return 1035 } 1036 w.startType(definedType) 1037 w.qualifiedType(t.Obj()) 1038 1039 case *types.TypeParam: 1040 w.startType(typeParamType) 1041 w.qualifiedType(t.Obj()) 1042 1043 case *types.Pointer: 1044 w.startType(pointerType) 1045 w.typ(t.Elem(), pkg) 1046 1047 case *types.Slice: 1048 w.startType(sliceType) 1049 w.typ(t.Elem(), pkg) 1050 1051 case *types.Array: 1052 w.startType(arrayType) 1053 w.uint64(uint64(t.Len())) 1054 w.typ(t.Elem(), pkg) 1055 1056 case *types.Chan: 1057 w.startType(chanType) 1058 // 1 RecvOnly; 2 SendOnly; 3 SendRecv 1059 var dir uint64 1060 switch t.Dir() { 1061 case types.RecvOnly: 1062 dir = 1 1063 case types.SendOnly: 1064 dir = 2 1065 case types.SendRecv: 1066 dir = 3 1067 } 1068 w.uint64(dir) 1069 w.typ(t.Elem(), pkg) 1070 1071 case *types.Map: 1072 w.startType(mapType) 1073 w.typ(t.Key(), pkg) 1074 w.typ(t.Elem(), pkg) 1075 1076 case *types.Signature: 1077 w.startType(signatureType) 1078 w.pkg(pkg) // qualifies param/result vars 1079 w.signature(t) 1080 1081 case *types.Struct: 1082 w.startType(structType) 1083 n := t.NumFields() 1084 // Even for struct{} we must emit some qualifying package, because that's 1085 // what the compiler does, and thus that's what the importer expects. 1086 fieldPkg := pkg 1087 if n > 0 { 1088 fieldPkg = t.Field(0).Pkg() 1089 } 1090 if fieldPkg == nil { 1091 // TODO(rfindley): improve this very hacky logic. 1092 // 1093 // The importer expects a package to be set for all struct types, even 1094 // those with no fields. A better encoding might be to set NumFields 1095 // before pkg. setPkg panics with a nil package, which may be possible 1096 // to reach with invalid packages (and perhaps valid packages, too?), so 1097 // (arbitrarily) set the localpkg if available. 1098 // 1099 // Alternatively, we may be able to simply guarantee that pkg != nil, by 1100 // reconsidering the encoding of constant values. 1101 if w.p.shallow { 1102 fieldPkg = w.p.localpkg 1103 } else { 1104 panic(internalErrorf("no package to set for empty struct")) 1105 } 1106 } 1107 w.pkg(fieldPkg) 1108 w.uint64(uint64(n)) 1109 1110 for i := range n { 1111 f := t.Field(i) 1112 if w.p.shallow { 1113 w.objectPath(f) 1114 } 1115 w.pos(f.Pos()) 1116 w.string(f.Name()) // unexported fields implicitly qualified by prior setPkg 1117 w.typ(f.Type(), fieldPkg) 1118 w.bool(f.Anonymous()) 1119 w.string(t.Tag(i)) // note (or tag) 1120 } 1121 1122 case *types.Interface: 1123 w.startType(interfaceType) 1124 w.pkg(pkg) // qualifies unexported method funcs 1125 1126 n := t.NumEmbeddeds() 1127 w.uint64(uint64(n)) 1128 for i := 0; i < n; i++ { 1129 ft := t.EmbeddedType(i) 1130 if named, _ := types.Unalias(ft).(*types.Named); named != nil { 1131 w.pos(named.Obj().Pos()) 1132 } else { 1133 // e.g. ~int 1134 w.pos(token.NoPos) 1135 } 1136 w.typ(ft, pkg) 1137 } 1138 1139 // See comment for struct fields. In shallow mode we change the encoding 1140 // for interface methods that are promoted from other packages. 1141 1142 n = t.NumExplicitMethods() 1143 w.uint64(uint64(n)) 1144 for i := 0; i < n; i++ { 1145 m := t.ExplicitMethod(i) 1146 if w.p.shallow { 1147 w.objectPath(m) 1148 } 1149 w.pos(m.Pos()) 1150 w.string(m.Name()) 1151 sig, _ := m.Type().(*types.Signature) 1152 w.signature(sig) 1153 } 1154 1155 case *types.Union: 1156 w.startType(unionType) 1157 nt := t.Len() 1158 w.uint64(uint64(nt)) 1159 for i := range nt { 1160 term := t.Term(i) 1161 w.bool(term.Tilde()) 1162 w.typ(term.Type(), pkg) 1163 } 1164 1165 default: 1166 panic(internalErrorf("unexpected type: %v, %v", t, reflect.TypeOf(t))) 1167 } 1168 } 1169 1170 // objectPath writes the package and objectPath to use to look up obj in a 1171 // different package, when encoding in "shallow" mode. 1172 // 1173 // When doing a shallow import, the importer creates only the local package, 1174 // and requests package symbols for dependencies from the client. 1175 // However, certain types defined in the local package may hold objects defined 1176 // (perhaps deeply) within another package. 1177 // 1178 // For example, consider the following: 1179 // 1180 // package a 1181 // func F() chan * map[string] struct { X int } 1182 // 1183 // package b 1184 // import "a" 1185 // var B = a.F() 1186 // 1187 // In this example, the type of b.B holds fields defined in package a. 1188 // In order to have the correct canonical objects for the field defined in the 1189 // type of B, they are encoded as objectPaths and later looked up in the 1190 // importer. The same problem applies to interface methods. 1191 func (w *exportWriter) objectPath(obj types.Object) { 1192 if obj.Pkg() == nil || obj.Pkg() == w.p.localpkg { 1193 // obj.Pkg() may be nil for the builtin error.Error. 1194 // In this case, or if obj is declared in the local package, no need to 1195 // encode. 1196 w.string("") 1197 return 1198 } 1199 objectPath, err := w.p.objectpathEncoder().For(obj) 1200 if err != nil { 1201 // Fall back to the empty string, which will cause the importer to create a 1202 // new object, which matches earlier behavior. Creating a new object is 1203 // sufficient for many purposes (such as type checking), but causes certain 1204 // references algorithms to fail (golang/go#60819). However, we didn't 1205 // notice this problem during months of gopls@v0.12.0 testing. 1206 // 1207 // TODO(golang/go#61674): this workaround is insufficient, as in the case 1208 // where the field forwarded from an instantiated type that may not appear 1209 // in the export data of the original package: 1210 // 1211 // // package a 1212 // type A[P any] struct{ F P } 1213 // 1214 // // package b 1215 // type B a.A[int] 1216 // 1217 // We need to update references algorithms not to depend on this 1218 // de-duplication, at which point we may want to simply remove the 1219 // workaround here. 1220 w.string("") 1221 return 1222 } 1223 w.string(string(objectPath)) 1224 w.pkg(obj.Pkg()) 1225 } 1226 1227 func (w *exportWriter) signature(sig *types.Signature) { 1228 w.paramList(sig.Params()) 1229 w.paramList(sig.Results()) 1230 if sig.Params().Len() > 0 { 1231 w.bool(sig.Variadic()) 1232 } 1233 } 1234 1235 func (w *exportWriter) typeList(ts *types.TypeList, pkg *types.Package) { 1236 w.uint64(uint64(ts.Len())) 1237 for t := range ts.Types() { 1238 w.typ(t, pkg) 1239 } 1240 } 1241 1242 func (w *exportWriter) tparamList(prefix string, list *types.TypeParamList, pkg *types.Package) { 1243 ll := uint64(list.Len()) 1244 w.uint64(ll) 1245 for tparam := range list.TypeParams() { 1246 // Set the type parameter exportName before exporting its type. 1247 exportName := tparamExportName(prefix, tparam) 1248 w.p.tparamNames[tparam.Obj()] = exportName 1249 w.typ(tparam, pkg) 1250 } 1251 } 1252 1253 const blankMarker = "$" 1254 1255 // tparamExportName returns the 'exported' name of a type parameter, which 1256 // differs from its actual object name: it is prefixed with a qualifier, and 1257 // blank type parameter names are disambiguated by their index in the type 1258 // parameter list. 1259 func tparamExportName(prefix string, tparam *types.TypeParam) string { 1260 assert(prefix != "") 1261 name := tparam.Obj().Name() 1262 if name == "_" { 1263 name = blankMarker + strconv.Itoa(tparam.Index()) 1264 } 1265 return prefix + "." + name 1266 } 1267 1268 // tparamName returns the real name of a type parameter, after stripping its 1269 // qualifying prefix and reverting blank-name encoding. See tparamExportName 1270 // for details. 1271 func tparamName(exportName string) string { 1272 // Remove the "path" from the type param name that makes it unique. 1273 ix := strings.LastIndex(exportName, ".") 1274 if ix < 0 { 1275 errorf("malformed type parameter export name %s: missing prefix", exportName) 1276 } 1277 name := exportName[ix+1:] 1278 if strings.HasPrefix(name, blankMarker) { 1279 return "_" 1280 } 1281 return name 1282 } 1283 1284 func (w *exportWriter) paramList(tup *types.Tuple) { 1285 n := tup.Len() 1286 w.uint64(uint64(n)) 1287 for i := range n { 1288 w.param(tup.At(i)) 1289 } 1290 } 1291 1292 func (w *exportWriter) param(obj types.Object) { 1293 w.pos(obj.Pos()) 1294 w.localIdent(obj) 1295 w.typ(obj.Type(), obj.Pkg()) 1296 } 1297 1298 func (w *exportWriter) value(typ types.Type, v constant.Value) { 1299 w.typ(typ, nil) 1300 if w.p.version >= iexportVersionGo1_18 { 1301 w.int64(int64(v.Kind())) 1302 } 1303 1304 if v.Kind() == constant.Unknown { 1305 // golang/go#60605: treat unknown constant values as if they have invalid type 1306 // 1307 // This loses some fidelity over the package type-checked from source, but that 1308 // is acceptable. 1309 // 1310 // TODO(rfindley): we should switch on the recorded constant kind rather 1311 // than the constant type 1312 return 1313 } 1314 1315 switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType { 1316 case types.IsBoolean: 1317 w.bool(constant.BoolVal(v)) 1318 case types.IsInteger: 1319 var i big.Int 1320 if i64, exact := constant.Int64Val(v); exact { 1321 i.SetInt64(i64) 1322 } else if ui64, exact := constant.Uint64Val(v); exact { 1323 i.SetUint64(ui64) 1324 } else { 1325 i.SetString(v.ExactString(), 10) 1326 } 1327 w.mpint(&i, typ) 1328 case types.IsFloat: 1329 f := constantToFloat(v) 1330 w.mpfloat(f, typ) 1331 case types.IsComplex: 1332 w.mpfloat(constantToFloat(constant.Real(v)), typ) 1333 w.mpfloat(constantToFloat(constant.Imag(v)), typ) 1334 case types.IsString: 1335 w.string(constant.StringVal(v)) 1336 default: 1337 if b.Kind() == types.Invalid { 1338 // package contains type errors 1339 break 1340 } 1341 panic(internalErrorf("unexpected type %v (%v)", typ, typ.Underlying())) 1342 } 1343 } 1344 1345 // constantToFloat converts a constant.Value with kind constant.Float to a 1346 // big.Float. 1347 func constantToFloat(x constant.Value) *big.Float { 1348 x = constant.ToFloat(x) 1349 // Use the same floating-point precision (512) as cmd/compile 1350 // (see Mpprec in cmd/compile/internal/gc/mpfloat.go). 1351 const mpprec = 512 1352 var f big.Float 1353 f.SetPrec(mpprec) 1354 if v, exact := constant.Float64Val(x); exact { 1355 // float64 1356 f.SetFloat64(v) 1357 } else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int { 1358 // TODO(gri): add big.Rat accessor to constant.Value. 1359 n := valueToRat(num) 1360 d := valueToRat(denom) 1361 f.SetRat(n.Quo(n, d)) 1362 } else { 1363 // Value too large to represent as a fraction => inaccessible. 1364 // TODO(gri): add big.Float accessor to constant.Value. 1365 _, ok := f.SetString(x.ExactString()) 1366 assert(ok) 1367 } 1368 return &f 1369 } 1370 1371 func valueToRat(x constant.Value) *big.Rat { 1372 // Convert little-endian to big-endian. 1373 // I can't believe this is necessary. 1374 bytes := constant.Bytes(x) 1375 for i := 0; i < len(bytes)/2; i++ { 1376 bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i] 1377 } 1378 return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes)) 1379 } 1380 1381 // mpint exports a multi-precision integer. 1382 // 1383 // For unsigned types, small values are written out as a single 1384 // byte. Larger values are written out as a length-prefixed big-endian 1385 // byte string, where the length prefix is encoded as its complement. 1386 // For example, bytes 0, 1, and 2 directly represent the integer 1387 // values 0, 1, and 2; while bytes 255, 254, and 253 indicate a 1-, 1388 // 2-, and 3-byte big-endian string follow. 1389 // 1390 // Encoding for signed types use the same general approach as for 1391 // unsigned types, except small values use zig-zag encoding and the 1392 // bottom bit of length prefix byte for large values is reserved as a 1393 // sign bit. 1394 // 1395 // The exact boundary between small and large encodings varies 1396 // according to the maximum number of bytes needed to encode a value 1397 // of type typ. As a special case, 8-bit types are always encoded as a 1398 // single byte. 1399 // 1400 // TODO(mdempsky): Is this level of complexity really worthwhile? 1401 func (w *exportWriter) mpint(x *big.Int, typ types.Type) { 1402 basic, ok := typ.Underlying().(*types.Basic) 1403 if !ok { 1404 panic(internalErrorf("unexpected type %v (%T)", typ.Underlying(), typ.Underlying())) 1405 } 1406 1407 signed, maxBytes := intSize(basic) 1408 1409 negative := x.Sign() < 0 1410 if !signed && negative { 1411 panic(internalErrorf("negative unsigned integer; type %v, value %v", typ, x)) 1412 } 1413 1414 b := x.Bytes() 1415 if len(b) > 0 && b[0] == 0 { 1416 panic(internalErrorf("leading zeros")) 1417 } 1418 if uint(len(b)) > maxBytes { 1419 panic(internalErrorf("bad mpint length: %d > %d (type %v, value %v)", len(b), maxBytes, typ, x)) 1420 } 1421 1422 maxSmall := 256 - maxBytes 1423 if signed { 1424 maxSmall = 256 - 2*maxBytes 1425 } 1426 if maxBytes == 1 { 1427 maxSmall = 256 1428 } 1429 1430 // Check if x can use small value encoding. 1431 if len(b) <= 1 { 1432 var ux uint 1433 if len(b) == 1 { 1434 ux = uint(b[0]) 1435 } 1436 if signed { 1437 ux <<= 1 1438 if negative { 1439 ux-- 1440 } 1441 } 1442 if ux < maxSmall { 1443 w.data.WriteByte(byte(ux)) 1444 return 1445 } 1446 } 1447 1448 n := 256 - uint(len(b)) 1449 if signed { 1450 n = 256 - 2*uint(len(b)) 1451 if negative { 1452 n |= 1 1453 } 1454 } 1455 if n < maxSmall || n >= 256 { 1456 panic(internalErrorf("encoding mistake: %d, %v, %v => %d", len(b), signed, negative, n)) 1457 } 1458 1459 w.data.WriteByte(byte(n)) 1460 w.data.Write(b) 1461 } 1462 1463 // mpfloat exports a multi-precision floating point number. 1464 // 1465 // The number's value is decomposed into mantissa × 2**exponent, where 1466 // mantissa is an integer. The value is written out as mantissa (as a 1467 // multi-precision integer) and then the exponent, except exponent is 1468 // omitted if mantissa is zero. 1469 func (w *exportWriter) mpfloat(f *big.Float, typ types.Type) { 1470 if f.IsInf() { 1471 panic("infinite constant") 1472 } 1473 1474 // Break into f = mant × 2**exp, with 0.5 <= mant < 1. 1475 var mant big.Float 1476 exp := int64(f.MantExp(&mant)) 1477 1478 // Scale so that mant is an integer. 1479 prec := mant.MinPrec() 1480 mant.SetMantExp(&mant, int(prec)) 1481 exp -= int64(prec) 1482 1483 manti, acc := mant.Int(nil) 1484 if acc != big.Exact { 1485 panic(internalErrorf("mantissa scaling failed for %f (%s)", f, acc)) 1486 } 1487 w.mpint(manti, typ) 1488 if manti.Sign() != 0 { 1489 w.int64(exp) 1490 } 1491 } 1492 1493 func (w *exportWriter) bool(b bool) bool { 1494 var x uint64 1495 if b { 1496 x = 1 1497 } 1498 w.uint64(x) 1499 return b 1500 } 1501 1502 func (w *exportWriter) int64(x int64) { w.data.int64(x) } 1503 func (w *exportWriter) uint64(x uint64) { w.data.uint64(x) } 1504 func (w *exportWriter) string(s string) { w.uint64(w.p.stringOff(s)) } 1505 1506 func (w *exportWriter) localIdent(obj types.Object) { 1507 // Anonymous parameters. 1508 if obj == nil { 1509 w.string("") 1510 return 1511 } 1512 1513 name := obj.Name() 1514 if name == "_" { 1515 w.string("_") 1516 return 1517 } 1518 1519 w.string(name) 1520 } 1521 1522 type intWriter struct { 1523 bytes.Buffer 1524 } 1525 1526 func (w *intWriter) int64(x int64) { 1527 var buf [binary.MaxVarintLen64]byte 1528 n := binary.PutVarint(buf[:], x) 1529 w.Write(buf[:n]) 1530 } 1531 1532 func (w *intWriter) uint64(x uint64) { 1533 var buf [binary.MaxVarintLen64]byte 1534 n := binary.PutUvarint(buf[:], x) 1535 w.Write(buf[:n]) 1536 } 1537 1538 func assert(cond bool) { 1539 if !cond { 1540 panic("internal error: assertion failed") 1541 } 1542 } 1543 1544 // The below is copied from go/src/cmd/compile/internal/gc/syntax.go. 1545 1546 // objQueue is a FIFO queue of types.Object. The zero value of objQueue is 1547 // a ready-to-use empty queue. 1548 type objQueue struct { 1549 ring []types.Object 1550 head, tail int 1551 } 1552 1553 // empty returns true if q contains no Nodes. 1554 func (q *objQueue) empty() bool { 1555 return q.head == q.tail 1556 } 1557 1558 // pushTail appends n to the tail of the queue. 1559 func (q *objQueue) pushTail(obj types.Object) { 1560 if len(q.ring) == 0 { 1561 q.ring = make([]types.Object, 16) 1562 } else if q.head+len(q.ring) == q.tail { 1563 // Grow the ring. 1564 nring := make([]types.Object, len(q.ring)*2) 1565 // Copy the old elements. 1566 part := q.ring[q.head%len(q.ring):] 1567 if q.tail-q.head <= len(part) { 1568 part = part[:q.tail-q.head] 1569 copy(nring, part) 1570 } else { 1571 pos := copy(nring, part) 1572 copy(nring[pos:], q.ring[:q.tail%len(q.ring)]) 1573 } 1574 q.ring, q.head, q.tail = nring, 0, q.tail-q.head 1575 } 1576 1577 q.ring[q.tail%len(q.ring)] = obj 1578 q.tail++ 1579 } 1580 1581 // popHead pops a node from the head of the queue. It panics if q is empty. 1582 func (q *objQueue) popHead() types.Object { 1583 if q.empty() { 1584 panic("dequeue empty") 1585 } 1586 obj := q.ring[q.head%len(q.ring)] 1587 q.head++ 1588 return obj 1589 } 1590 1591 // internalError represents an error generated inside this package. 1592 type internalError string 1593 1594 func (e internalError) Error() string { return "gcimporter: " + string(e) } 1595 1596 // TODO(adonovan): make this call panic, so that it's symmetric with errorf. 1597 // Otherwise it's easy to forget to do anything with the error. 1598 // 1599 // TODO(adonovan): also, consider switching the names "errorf" and 1600 // "internalErrorf" as the former is used for bugs, whose cause is 1601 // internal inconsistency, whereas the latter is used for ordinary 1602 // situations like bad input, whose cause is external. 1603 func internalErrorf(format string, args ...any) error { 1604 return internalError(fmt.Sprintf(format, args...)) 1605 }