]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/go/internal/modindex/read.go
cmd/go: don't install most GOROOT .a files in pkg
[gostls13.git] / src / cmd / go / internal / modindex / read.go
1 // Copyright 2022 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 package modindex
6
7 import (
8         "bytes"
9         "encoding/binary"
10         "errors"
11         "fmt"
12         "go/build"
13         "go/build/constraint"
14         "go/token"
15         "internal/buildinternal"
16         "internal/godebug"
17         "internal/goroot"
18         "path"
19         "path/filepath"
20         "runtime"
21         "runtime/debug"
22         "sort"
23         "strings"
24         "sync"
25         "time"
26         "unsafe"
27
28         "cmd/go/internal/base"
29         "cmd/go/internal/cache"
30         "cmd/go/internal/cfg"
31         "cmd/go/internal/fsys"
32         "cmd/go/internal/imports"
33         "cmd/go/internal/par"
34         "cmd/go/internal/str"
35 )
36
37 // enabled is used to flag off the behavior of the module index on tip.
38 // It will be removed before the release.
39 // TODO(matloob): Remove enabled once we have more confidence on the
40 // module index.
41 var enabled bool = godebug.Get("goindex") != "0"
42
43 // Module represents and encoded module index file. It is used to
44 // do the equivalent of build.Import of packages in the module and answer other
45 // questions based on the index file's data.
46 type Module struct {
47         modroot string
48         d       *decoder
49         n       int // number of packages
50 }
51
52 // moduleHash returns an ActionID corresponding to the state of the module
53 // located at filesystem path modroot.
54 func moduleHash(modroot string, ismodcache bool) (cache.ActionID, error) {
55         // We expect modules stored within the module cache to be checksummed and
56         // immutable, and we expect released modules within GOROOT to change only
57         // infrequently (when the Go version changes).
58         if !ismodcache {
59                 // The contents of this module may change over time. We don't want to pay
60                 // the cost to detect changes and re-index whenever they occur, so just
61                 // don't index it at all.
62                 //
63                 // Note that this is true even for modules in GOROOT/src: non-release builds
64                 // of the Go toolchain may have arbitrary development changes on top of the
65                 // commit reported by runtime.Version, or could be completly artificial due
66                 // to lacking a `git` binary (like "devel gomote.XXXXX", as synthesized by
67                 // "gomote push" as of 2022-06-15). (Release builds shouldn't have
68                 // modifications, but we don't want to use a behavior for releases that we
69                 // haven't tested during development.)
70                 return cache.ActionID{}, ErrNotIndexed
71         }
72
73         h := cache.NewHash("moduleIndex")
74         // TODO(bcmills): Since modules in the index are checksummed, we could
75         // probably improve the cache hit rate by keying off of the module
76         // path@version (perhaps including the checksum?) instead of the module root
77         // directory.
78         fmt.Fprintf(h, "module index %s %s %v\n", runtime.Version(), indexVersion, modroot)
79         return h.Sum(), nil
80 }
81
82 const modTimeCutoff = 2 * time.Second
83
84 // dirHash returns an ActionID corresponding to the state of the package
85 // located at filesystem path pkgdir.
86 func dirHash(modroot, pkgdir string) (cache.ActionID, error) {
87         h := cache.NewHash("moduleIndex")
88         fmt.Fprintf(h, "modroot %s\n", modroot)
89         fmt.Fprintf(h, "package %s %s %v\n", runtime.Version(), indexVersion, pkgdir)
90         entries, err := fsys.ReadDir(pkgdir)
91         if err != nil {
92                 // pkgdir might not be a directory. give up on hashing.
93                 return cache.ActionID{}, ErrNotIndexed
94         }
95         cutoff := time.Now().Add(-modTimeCutoff)
96         for _, info := range entries {
97                 if info.IsDir() {
98                         continue
99                 }
100
101                 if !info.Mode().IsRegular() {
102                         return cache.ActionID{}, ErrNotIndexed
103                 }
104                 // To avoid problems for very recent files where a new
105                 // write might not change the mtime due to file system
106                 // mtime precision, reject caching if a file was read that
107                 // is less than modTimeCutoff old.
108                 //
109                 // This is the same strategy used for hashing test inputs.
110                 // See hashOpen in cmd/go/internal/test/test.go for the
111                 // corresponding code.
112                 if info.ModTime().After(cutoff) {
113                         return cache.ActionID{}, ErrNotIndexed
114                 }
115
116                 fmt.Fprintf(h, "file %v %v %v\n", info.Name(), info.ModTime(), info.Size())
117         }
118         return h.Sum(), nil
119 }
120
121 var modrootCache par.Cache
122
123 var ErrNotIndexed = errors.New("not in module index")
124
125 var (
126         errDisabled           = fmt.Errorf("%w: module indexing disabled", ErrNotIndexed)
127         errNotFromModuleCache = fmt.Errorf("%w: not from module cache", ErrNotIndexed)
128 )
129
130 // GetPackage returns the IndexPackage for the package at the given path.
131 // It will return ErrNotIndexed if the directory should be read without
132 // using the index, for instance because the index is disabled, or the packgae
133 // is not in a module.
134 func GetPackage(modroot, pkgdir string) (*IndexPackage, error) {
135         mi, err := GetModule(modroot)
136         if err == nil {
137                 return mi.Package(relPath(pkgdir, modroot)), nil
138         }
139         if !errors.Is(err, errNotFromModuleCache) {
140                 return nil, err
141         }
142         if cfg.BuildContext.Compiler == "gccgo" && str.HasPathPrefix(modroot, cfg.GOROOTsrc) {
143                 return nil, err // gccgo has no sources for GOROOT packages.
144         }
145         return openIndexPackage(modroot, pkgdir)
146 }
147
148 // GetModule returns the Module for the given modroot.
149 // It will return ErrNotIndexed if the directory should be read without
150 // using the index, for instance because the index is disabled, or the packgae
151 // is not in a module.
152 func GetModule(modroot string) (*Module, error) {
153         if !enabled || cache.DefaultDir() == "off" {
154                 return nil, errDisabled
155         }
156         if modroot == "" {
157                 panic("modindex.GetPackage called with empty modroot")
158         }
159         if cfg.BuildMod == "vendor" {
160                 // Even if the main module is in the module cache,
161                 // its vendored dependencies are not loaded from their
162                 // usual cached locations.
163                 return nil, errNotFromModuleCache
164         }
165         modroot = filepath.Clean(modroot)
166         if !str.HasFilePathPrefix(modroot, cfg.GOMODCACHE) {
167                 return nil, errNotFromModuleCache
168         }
169         return openIndexModule(modroot, true)
170 }
171
172 var mcache par.Cache
173
174 // openIndexModule returns the module index for modPath.
175 // It will return ErrNotIndexed if the module can not be read
176 // using the index because it contains symlinks.
177 func openIndexModule(modroot string, ismodcache bool) (*Module, error) {
178         type result struct {
179                 mi  *Module
180                 err error
181         }
182         r := mcache.Do(modroot, func() any {
183                 fsys.Trace("openIndexModule", modroot)
184                 id, err := moduleHash(modroot, ismodcache)
185                 if err != nil {
186                         return result{nil, err}
187                 }
188                 data, _, err := cache.Default().GetMmap(id)
189                 if err != nil {
190                         // Couldn't read from modindex. Assume we couldn't read from
191                         // the index because the module hasn't been indexed yet.
192                         data, err = indexModule(modroot)
193                         if err != nil {
194                                 return result{nil, err}
195                         }
196                         if err = cache.Default().PutBytes(id, data); err != nil {
197                                 return result{nil, err}
198                         }
199                 }
200                 mi, err := fromBytes(modroot, data)
201                 if err != nil {
202                         return result{nil, err}
203                 }
204                 return result{mi, nil}
205         }).(result)
206         return r.mi, r.err
207 }
208
209 var pcache par.Cache
210
211 func openIndexPackage(modroot, pkgdir string) (*IndexPackage, error) {
212         type result struct {
213                 pkg *IndexPackage
214                 err error
215         }
216         r := pcache.Do([2]string{modroot, pkgdir}, func() any {
217                 fsys.Trace("openIndexPackage", pkgdir)
218                 id, err := dirHash(modroot, pkgdir)
219                 if err != nil {
220                         return result{nil, err}
221                 }
222                 data, _, err := cache.Default().GetMmap(id)
223                 if err != nil {
224                         // Couldn't read from index. Assume we couldn't read from
225                         // the index because the package hasn't been indexed yet.
226                         data = indexPackage(modroot, pkgdir)
227                         if err = cache.Default().PutBytes(id, data); err != nil {
228                                 return result{nil, err}
229                         }
230                 }
231                 pkg, err := packageFromBytes(modroot, data)
232                 if err != nil {
233                         return result{nil, err}
234                 }
235                 return result{pkg, nil}
236         }).(result)
237         return r.pkg, r.err
238 }
239
240 var errCorrupt = errors.New("corrupt index")
241
242 // protect marks the start of a large section of code that accesses the index.
243 // It should be used as:
244 //
245 //      defer unprotect(protect, &err)
246 //
247 // It should not be used for trivial accesses which would be
248 // dwarfed by the overhead of the defer.
249 func protect() bool {
250         return debug.SetPanicOnFault(true)
251 }
252
253 var isTest = false
254
255 // unprotect marks the end of a large section of code that accesses the index.
256 // It should be used as:
257 //
258 //      defer unprotect(protect, &err)
259 //
260 // end looks for panics due to errCorrupt or bad mmap accesses.
261 // When it finds them, it adds explanatory text, consumes the panic, and sets *errp instead.
262 // If errp is nil, end adds the explanatory text but then calls base.Fatalf.
263 func unprotect(old bool, errp *error) {
264         // SetPanicOnFault's errors _may_ satisfy this interface. Even though it's not guaranteed
265         // that all its errors satisfy this interface, we'll only check for these errors so that
266         // we don't suppress panics that could have been produced from other sources.
267         type addrer interface {
268                 Addr() uintptr
269         }
270
271         debug.SetPanicOnFault(old)
272
273         if e := recover(); e != nil {
274                 if _, ok := e.(addrer); ok || e == errCorrupt {
275                         // This panic was almost certainly caused by SetPanicOnFault or our panic(errCorrupt).
276                         err := fmt.Errorf("error reading module index: %v", e)
277                         if errp != nil {
278                                 *errp = err
279                                 return
280                         }
281                         if isTest {
282                                 panic(err)
283                         }
284                         base.Fatalf("%v", err)
285                 }
286                 // The panic was likely not caused by SetPanicOnFault.
287                 panic(e)
288         }
289 }
290
291 // fromBytes returns a *Module given the encoded representation.
292 func fromBytes(moddir string, data []byte) (m *Module, err error) {
293         if !enabled {
294                 panic("use of index")
295         }
296
297         defer unprotect(protect(), &err)
298
299         if !bytes.HasPrefix(data, []byte(indexVersion+"\n")) {
300                 return nil, errCorrupt
301         }
302
303         const hdr = len(indexVersion + "\n")
304         d := &decoder{data: data}
305         str := d.intAt(hdr)
306         if str < hdr+8 || len(d.data) < str {
307                 return nil, errCorrupt
308         }
309         d.data, d.str = data[:str], d.data[str:]
310         // Check that string table looks valid.
311         // First string is empty string (length 0),
312         // and we leave a marker byte 0xFF at the end
313         // just to make sure that the file is not truncated.
314         if len(d.str) == 0 || d.str[0] != 0 || d.str[len(d.str)-1] != 0xFF {
315                 return nil, errCorrupt
316         }
317
318         n := d.intAt(hdr + 4)
319         if n < 0 || n > (len(d.data)-8)/8 {
320                 return nil, errCorrupt
321         }
322
323         m = &Module{
324                 moddir,
325                 d,
326                 n,
327         }
328         return m, nil
329 }
330
331 // packageFromBytes returns a *IndexPackage given the encoded representation.
332 func packageFromBytes(modroot string, data []byte) (p *IndexPackage, err error) {
333         m, err := fromBytes(modroot, data)
334         if err != nil {
335                 return nil, err
336         }
337         if m.n != 1 {
338                 return nil, fmt.Errorf("corrupt single-package index")
339         }
340         return m.pkg(0), nil
341 }
342
343 // pkgDir returns the dir string of the i'th package in the index.
344 func (m *Module) pkgDir(i int) string {
345         if i < 0 || i >= m.n {
346                 panic(errCorrupt)
347         }
348         return m.d.stringAt(12 + 8 + 8*i)
349 }
350
351 // pkgOff returns the offset of the data for the i'th package in the index.
352 func (m *Module) pkgOff(i int) int {
353         if i < 0 || i >= m.n {
354                 panic(errCorrupt)
355         }
356         return m.d.intAt(12 + 8 + 8*i + 4)
357 }
358
359 // Walk calls f for each package in the index, passing the path to that package relative to the module root.
360 func (m *Module) Walk(f func(path string)) {
361         defer unprotect(protect(), nil)
362         for i := 0; i < m.n; i++ {
363                 f(m.pkgDir(i))
364         }
365 }
366
367 // relPath returns the path relative to the module's root.
368 func relPath(path, modroot string) string {
369         return str.TrimFilePathPrefix(filepath.Clean(path), filepath.Clean(modroot))
370 }
371
372 // Import is the equivalent of build.Import given the information in Module.
373 func (rp *IndexPackage) Import(bctxt build.Context, mode build.ImportMode) (p *build.Package, err error) {
374         defer unprotect(protect(), &err)
375
376         ctxt := (*Context)(&bctxt)
377
378         p = &build.Package{}
379
380         p.ImportPath = "."
381         p.Dir = filepath.Join(rp.modroot, rp.dir)
382
383         var pkgerr error
384         switch ctxt.Compiler {
385         case "gccgo", "gc":
386         default:
387                 // Save error for end of function.
388                 pkgerr = fmt.Errorf("import %q: unknown compiler %q", p.Dir, ctxt.Compiler)
389         }
390
391         if p.Dir == "" {
392                 return p, fmt.Errorf("import %q: import of unknown directory", p.Dir)
393         }
394
395         // goroot and gopath
396         inTestdata := func(sub string) bool {
397                 return strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || str.HasPathPrefix(sub, "testdata")
398         }
399         if !inTestdata(rp.dir) {
400                 // In build.go, p.Root should only be set in the non-local-import case, or in
401                 // GOROOT or GOPATH. Since module mode only calls Import with path set to "."
402                 // and the module index doesn't apply outside modules, the GOROOT case is
403                 // the only case where p.Root needs to be set.
404                 if ctxt.GOROOT != "" && str.HasFilePathPrefix(p.Dir, cfg.GOROOTsrc) && p.Dir != cfg.GOROOTsrc {
405                         p.Root = ctxt.GOROOT
406                         p.Goroot = true
407                         modprefix := str.TrimFilePathPrefix(rp.modroot, cfg.GOROOTsrc)
408                         p.ImportPath = rp.dir
409                         if modprefix != "" {
410                                 p.ImportPath = filepath.Join(modprefix, p.ImportPath)
411                         }
412
413                         // Set GOROOT-specific fields (sometimes for modules in a GOPATH directory).
414                         // The fields set below (SrcRoot, PkgRoot, BinDir, PkgTargetRoot, and PkgObj)
415                         // are only set in build.Import if p.Root != "".
416                         var pkgtargetroot string
417                         var pkga string
418                         suffix := ""
419                         if ctxt.InstallSuffix != "" {
420                                 suffix = "_" + ctxt.InstallSuffix
421                         }
422                         switch ctxt.Compiler {
423                         case "gccgo":
424                                 pkgtargetroot = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
425                                 dir, elem := path.Split(p.ImportPath)
426                                 pkga = pkgtargetroot + "/" + dir + "lib" + elem + ".a"
427                         case "gc":
428                                 pkgtargetroot = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
429                                 pkga = pkgtargetroot + "/" + p.ImportPath + ".a"
430                         }
431                         p.SrcRoot = ctxt.joinPath(p.Root, "src")
432                         p.PkgRoot = ctxt.joinPath(p.Root, "pkg")
433                         p.BinDir = ctxt.joinPath(p.Root, "bin")
434                         if pkga != "" {
435                                 // Always set PkgTargetRoot. It might be used when building in shared
436                                 // mode.
437                                 p.PkgTargetRoot = ctxt.joinPath(p.Root, pkgtargetroot)
438
439                                 // Set the install target if applicable.
440                                 if strings.ToLower(godebug.Get("installgoroot")) == "all" ||
441                                         !p.Goroot || buildinternal.NeedsInstalledDotA(p.ImportPath) {
442                                         p.PkgObj = ctxt.joinPath(p.Root, pkga)
443                                 }
444                         }
445                 }
446         }
447
448         if rp.error != nil {
449                 if errors.Is(rp.error, errCannotFindPackage) && ctxt.Compiler == "gccgo" && p.Goroot {
450                         return p, nil
451                 }
452                 return p, rp.error
453         }
454
455         if mode&build.FindOnly != 0 {
456                 return p, pkgerr
457         }
458
459         // We need to do a second round of bad file processing.
460         var badGoError error
461         badFiles := make(map[string]bool)
462         badFile := func(name string, err error) {
463                 if badGoError == nil {
464                         badGoError = err
465                 }
466                 if !badFiles[name] {
467                         p.InvalidGoFiles = append(p.InvalidGoFiles, name)
468                         badFiles[name] = true
469                 }
470         }
471
472         var Sfiles []string // files with ".S"(capital S)/.sx(capital s equivalent for case insensitive filesystems)
473         var firstFile string
474         embedPos := make(map[string][]token.Position)
475         testEmbedPos := make(map[string][]token.Position)
476         xTestEmbedPos := make(map[string][]token.Position)
477         importPos := make(map[string][]token.Position)
478         testImportPos := make(map[string][]token.Position)
479         xTestImportPos := make(map[string][]token.Position)
480         allTags := make(map[string]bool)
481         for _, tf := range rp.sourceFiles {
482                 name := tf.name()
483                 if error := tf.error(); error != "" {
484                         badFile(name, errors.New(tf.error()))
485                         continue
486                 } else if parseError := tf.parseError(); parseError != "" {
487                         badFile(name, parseErrorFromString(tf.parseError()))
488                         // Fall through: we still want to list files with parse errors.
489                 }
490
491                 var shouldBuild = true
492                 if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles {
493                         shouldBuild = false
494                 } else if goBuildConstraint := tf.goBuildConstraint(); goBuildConstraint != "" {
495                         x, err := constraint.Parse(goBuildConstraint)
496                         if err != nil {
497                                 return p, fmt.Errorf("%s: parsing //go:build line: %v", name, err)
498                         }
499                         shouldBuild = ctxt.eval(x, allTags)
500                 } else if plusBuildConstraints := tf.plusBuildConstraints(); len(plusBuildConstraints) > 0 {
501                         for _, text := range plusBuildConstraints {
502                                 if x, err := constraint.Parse(text); err == nil {
503                                         if !ctxt.eval(x, allTags) {
504                                                 shouldBuild = false
505                                         }
506                                 }
507                         }
508                 }
509
510                 ext := nameExt(name)
511                 if !shouldBuild || tf.ignoreFile() {
512                         if ext == ".go" {
513                                 p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
514                         } else if fileListForExt((*Package)(p), ext) != nil {
515                                 p.IgnoredOtherFiles = append(p.IgnoredOtherFiles, name)
516                         }
517                         continue
518                 }
519
520                 // Going to save the file. For non-Go files, can stop here.
521                 switch ext {
522                 case ".go":
523                         // keep going
524                 case ".S", ".sx":
525                         // special case for cgo, handled at end
526                         Sfiles = append(Sfiles, name)
527                         continue
528                 default:
529                         if list := fileListForExt((*Package)(p), ext); list != nil {
530                                 *list = append(*list, name)
531                         }
532                         continue
533                 }
534
535                 pkg := tf.pkgName()
536                 if pkg == "documentation" {
537                         p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
538                         continue
539                 }
540                 isTest := strings.HasSuffix(name, "_test.go")
541                 isXTest := false
542                 if isTest && strings.HasSuffix(tf.pkgName(), "_test") && p.Name != tf.pkgName() {
543                         isXTest = true
544                         pkg = pkg[:len(pkg)-len("_test")]
545                 }
546
547                 if !isTest && tf.binaryOnly() {
548                         p.BinaryOnly = true
549                 }
550
551                 if p.Name == "" {
552                         p.Name = pkg
553                         firstFile = name
554                 } else if pkg != p.Name {
555                         // TODO(#45999): The choice of p.Name is arbitrary based on file iteration
556                         // order. Instead of resolving p.Name arbitrarily, we should clear out the
557                         // existing Name and mark the existing files as also invalid.
558                         badFile(name, &MultiplePackageError{
559                                 Dir:      p.Dir,
560                                 Packages: []string{p.Name, pkg},
561                                 Files:    []string{firstFile, name},
562                         })
563                 }
564                 // Grab the first package comment as docs, provided it is not from a test file.
565                 if p.Doc == "" && !isTest && !isXTest {
566                         if synopsis := tf.synopsis(); synopsis != "" {
567                                 p.Doc = synopsis
568                         }
569                 }
570
571                 // Record Imports and information about cgo.
572                 isCgo := false
573                 imports := tf.imports()
574                 for _, imp := range imports {
575                         if imp.path == "C" {
576                                 if isTest {
577                                         badFile(name, fmt.Errorf("use of cgo in test %s not supported", name))
578                                         continue
579                                 }
580                                 isCgo = true
581                         }
582                 }
583                 if directives := tf.cgoDirectives(); directives != "" {
584                         if err := ctxt.saveCgo(name, (*Package)(p), directives); err != nil {
585                                 badFile(name, err)
586                         }
587                 }
588
589                 var fileList *[]string
590                 var importMap, embedMap map[string][]token.Position
591                 switch {
592                 case isCgo:
593                         allTags["cgo"] = true
594                         if ctxt.CgoEnabled {
595                                 fileList = &p.CgoFiles
596                                 importMap = importPos
597                                 embedMap = embedPos
598                         } else {
599                                 // Ignore Imports and Embeds from cgo files if cgo is disabled.
600                                 fileList = &p.IgnoredGoFiles
601                         }
602                 case isXTest:
603                         fileList = &p.XTestGoFiles
604                         importMap = xTestImportPos
605                         embedMap = xTestEmbedPos
606                 case isTest:
607                         fileList = &p.TestGoFiles
608                         importMap = testImportPos
609                         embedMap = testEmbedPos
610                 default:
611                         fileList = &p.GoFiles
612                         importMap = importPos
613                         embedMap = embedPos
614                 }
615                 *fileList = append(*fileList, name)
616                 if importMap != nil {
617                         for _, imp := range imports {
618                                 importMap[imp.path] = append(importMap[imp.path], imp.position)
619                         }
620                 }
621                 if embedMap != nil {
622                         for _, e := range tf.embeds() {
623                                 embedMap[e.pattern] = append(embedMap[e.pattern], e.position)
624                         }
625                 }
626         }
627
628         p.EmbedPatterns, p.EmbedPatternPos = cleanDecls(embedPos)
629         p.TestEmbedPatterns, p.TestEmbedPatternPos = cleanDecls(testEmbedPos)
630         p.XTestEmbedPatterns, p.XTestEmbedPatternPos = cleanDecls(xTestEmbedPos)
631
632         p.Imports, p.ImportPos = cleanDecls(importPos)
633         p.TestImports, p.TestImportPos = cleanDecls(testImportPos)
634         p.XTestImports, p.XTestImportPos = cleanDecls(xTestImportPos)
635
636         for tag := range allTags {
637                 p.AllTags = append(p.AllTags, tag)
638         }
639         sort.Strings(p.AllTags)
640
641         if len(p.CgoFiles) > 0 {
642                 p.SFiles = append(p.SFiles, Sfiles...)
643                 sort.Strings(p.SFiles)
644         } else {
645                 p.IgnoredOtherFiles = append(p.IgnoredOtherFiles, Sfiles...)
646                 sort.Strings(p.IgnoredOtherFiles)
647         }
648
649         if badGoError != nil {
650                 return p, badGoError
651         }
652         if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
653                 return p, &build.NoGoError{Dir: p.Dir}
654         }
655         return p, pkgerr
656 }
657
658 // IsStandardPackage reports whether path is a standard package
659 // for the goroot and compiler using the module index if possible,
660 // and otherwise falling back to internal/goroot.IsStandardPackage
661 func IsStandardPackage(goroot_, compiler, path string) bool {
662         if !enabled || compiler != "gc" {
663                 return goroot.IsStandardPackage(goroot_, compiler, path)
664         }
665
666         reldir := filepath.FromSlash(path) // relative dir path in module index for package
667         modroot := filepath.Join(goroot_, "src")
668         if str.HasFilePathPrefix(reldir, "cmd") {
669                 reldir = str.TrimFilePathPrefix(reldir, "cmd")
670                 modroot = filepath.Join(modroot, "cmd")
671         }
672         if _, err := GetPackage(modroot, filepath.Join(modroot, reldir)); err == nil {
673                 // Note that goroot.IsStandardPackage doesn't check that the directory
674                 // actually contains any go files-- merely that it exists. GetPackage
675                 // returning a nil error is enough for us to know the directory exists.
676                 return true
677         } else if errors.Is(err, ErrNotIndexed) {
678                 // Fall back because package isn't indexable. (Probably because
679                 // a file was modified recently)
680                 return goroot.IsStandardPackage(goroot_, compiler, path)
681         }
682         return false
683 }
684
685 // IsDirWithGoFiles is the equivalent of fsys.IsDirWithGoFiles using the information in the index.
686 func (rp *IndexPackage) IsDirWithGoFiles() (_ bool, err error) {
687         defer func() {
688                 if e := recover(); e != nil {
689                         err = fmt.Errorf("error reading module index: %v", e)
690                 }
691         }()
692         for _, sf := range rp.sourceFiles {
693                 if strings.HasSuffix(sf.name(), ".go") {
694                         return true, nil
695                 }
696         }
697         return false, nil
698 }
699
700 // ScanDir implements imports.ScanDir using the information in the index.
701 func (rp *IndexPackage) ScanDir(tags map[string]bool) (sortedImports []string, sortedTestImports []string, err error) {
702         // TODO(matloob) dir should eventually be relative to indexed directory
703         // TODO(matloob): skip reading raw package and jump straight to data we need?
704
705         defer func() {
706                 if e := recover(); e != nil {
707                         err = fmt.Errorf("error reading module index: %v", e)
708                 }
709         }()
710
711         imports_ := make(map[string]bool)
712         testImports := make(map[string]bool)
713         numFiles := 0
714
715 Files:
716         for _, sf := range rp.sourceFiles {
717                 name := sf.name()
718                 if strings.HasPrefix(name, "_") || strings.HasPrefix(name, ".") || !strings.HasSuffix(name, ".go") || !imports.MatchFile(name, tags) {
719                         continue
720                 }
721
722                 // The following section exists for backwards compatibility reasons:
723                 // scanDir ignores files with import "C" when collecting the list
724                 // of imports unless the "cgo" tag is provided. The following comment
725                 // is copied from the original.
726                 //
727                 // import "C" is implicit requirement of cgo tag.
728                 // When listing files on the command line (explicitFiles=true)
729                 // we do not apply build tag filtering but we still do apply
730                 // cgo filtering, so no explicitFiles check here.
731                 // Why? Because we always have, and it's not worth breaking
732                 // that behavior now.
733                 imps := sf.imports() // TODO(matloob): directly read import paths to avoid the extra strings?
734                 for _, imp := range imps {
735                         if imp.path == "C" && !tags["cgo"] && !tags["*"] {
736                                 continue Files
737                         }
738                 }
739
740                 if !shouldBuild(sf, tags) {
741                         continue
742                 }
743                 numFiles++
744                 m := imports_
745                 if strings.HasSuffix(name, "_test.go") {
746                         m = testImports
747                 }
748                 for _, p := range imps {
749                         m[p.path] = true
750                 }
751         }
752         if numFiles == 0 {
753                 return nil, nil, imports.ErrNoGo
754         }
755         return keys(imports_), keys(testImports), nil
756 }
757
758 func keys(m map[string]bool) []string {
759         list := make([]string, 0, len(m))
760         for k := range m {
761                 list = append(list, k)
762         }
763         sort.Strings(list)
764         return list
765 }
766
767 // implements imports.ShouldBuild in terms of an index sourcefile.
768 func shouldBuild(sf *sourceFile, tags map[string]bool) bool {
769         if goBuildConstraint := sf.goBuildConstraint(); goBuildConstraint != "" {
770                 x, err := constraint.Parse(goBuildConstraint)
771                 if err != nil {
772                         return false
773                 }
774                 return imports.Eval(x, tags, true)
775         }
776
777         plusBuildConstraints := sf.plusBuildConstraints()
778         for _, text := range plusBuildConstraints {
779                 if x, err := constraint.Parse(text); err == nil {
780                         if !imports.Eval(x, tags, true) {
781                                 return false
782                         }
783                 }
784         }
785
786         return true
787 }
788
789 // IndexPackage holds the information needed to access information in the
790 // index needed to load a package in a specific directory.
791 type IndexPackage struct {
792         error error
793         dir   string // directory of the package relative to the modroot
794
795         modroot string
796
797         // Source files
798         sourceFiles []*sourceFile
799 }
800
801 var errCannotFindPackage = errors.New("cannot find package")
802
803 // Package and returns finds the package with the given path (relative to the module root).
804 // If the package does not exist, Package returns an IndexPackage that will return an
805 // appropriate error from its methods.
806 func (m *Module) Package(path string) *IndexPackage {
807         defer unprotect(protect(), nil)
808
809         i, ok := sort.Find(m.n, func(i int) int {
810                 return strings.Compare(path, m.pkgDir(i))
811         })
812         if !ok {
813                 return &IndexPackage{error: fmt.Errorf("%w %q in:\n\t%s", errCannotFindPackage, path, filepath.Join(m.modroot, path))}
814         }
815         return m.pkg(i)
816 }
817
818 // pkgAt returns the i'th IndexPackage in m.
819 func (m *Module) pkg(i int) *IndexPackage {
820         r := m.d.readAt(m.pkgOff(i))
821         p := new(IndexPackage)
822         if errstr := r.string(); errstr != "" {
823                 p.error = errors.New(errstr)
824         }
825         p.dir = r.string()
826         p.sourceFiles = make([]*sourceFile, r.int())
827         for i := range p.sourceFiles {
828                 p.sourceFiles[i] = &sourceFile{
829                         d:   m.d,
830                         pos: r.int(),
831                 }
832         }
833         p.modroot = m.modroot
834         return p
835 }
836
837 // sourceFile represents the information of a given source file in the module index.
838 type sourceFile struct {
839         d               *decoder // encoding of this source file
840         pos             int      // start of sourceFile encoding in d
841         onceReadImports sync.Once
842         savedImports    []rawImport // saved imports so that they're only read once
843 }
844
845 // Offsets for fields in the sourceFile.
846 const (
847         sourceFileError = 4 * iota
848         sourceFileParseError
849         sourceFileSynopsis
850         sourceFileName
851         sourceFilePkgName
852         sourceFileIgnoreFile
853         sourceFileBinaryOnly
854         sourceFileCgoDirectives
855         sourceFileGoBuildConstraint
856         sourceFileNumPlusBuildConstraints
857 )
858
859 func (sf *sourceFile) error() string {
860         return sf.d.stringAt(sf.pos + sourceFileError)
861 }
862 func (sf *sourceFile) parseError() string {
863         return sf.d.stringAt(sf.pos + sourceFileParseError)
864 }
865 func (sf *sourceFile) synopsis() string {
866         return sf.d.stringAt(sf.pos + sourceFileSynopsis)
867 }
868 func (sf *sourceFile) name() string {
869         return sf.d.stringAt(sf.pos + sourceFileName)
870 }
871 func (sf *sourceFile) pkgName() string {
872         return sf.d.stringAt(sf.pos + sourceFilePkgName)
873 }
874 func (sf *sourceFile) ignoreFile() bool {
875         return sf.d.boolAt(sf.pos + sourceFileIgnoreFile)
876 }
877 func (sf *sourceFile) binaryOnly() bool {
878         return sf.d.boolAt(sf.pos + sourceFileBinaryOnly)
879 }
880 func (sf *sourceFile) cgoDirectives() string {
881         return sf.d.stringAt(sf.pos + sourceFileCgoDirectives)
882 }
883 func (sf *sourceFile) goBuildConstraint() string {
884         return sf.d.stringAt(sf.pos + sourceFileGoBuildConstraint)
885 }
886
887 func (sf *sourceFile) plusBuildConstraints() []string {
888         pos := sf.pos + sourceFileNumPlusBuildConstraints
889         n := sf.d.intAt(pos)
890         pos += 4
891         ret := make([]string, n)
892         for i := 0; i < n; i++ {
893                 ret[i] = sf.d.stringAt(pos)
894                 pos += 4
895         }
896         return ret
897 }
898
899 func (sf *sourceFile) importsOffset() int {
900         pos := sf.pos + sourceFileNumPlusBuildConstraints
901         n := sf.d.intAt(pos)
902         // each build constraint is 1 uint32
903         return pos + 4 + n*4
904 }
905
906 func (sf *sourceFile) embedsOffset() int {
907         pos := sf.importsOffset()
908         n := sf.d.intAt(pos)
909         // each import is 5 uint32s (string + tokpos)
910         return pos + 4 + n*(4*5)
911 }
912
913 func (sf *sourceFile) imports() []rawImport {
914         sf.onceReadImports.Do(func() {
915                 importsOffset := sf.importsOffset()
916                 r := sf.d.readAt(importsOffset)
917                 numImports := r.int()
918                 ret := make([]rawImport, numImports)
919                 for i := 0; i < numImports; i++ {
920                         ret[i] = rawImport{r.string(), r.tokpos()}
921                 }
922                 sf.savedImports = ret
923         })
924         return sf.savedImports
925 }
926
927 func (sf *sourceFile) embeds() []embed {
928         embedsOffset := sf.embedsOffset()
929         r := sf.d.readAt(embedsOffset)
930         numEmbeds := r.int()
931         ret := make([]embed, numEmbeds)
932         for i := range ret {
933                 ret[i] = embed{r.string(), r.tokpos()}
934         }
935         return ret
936 }
937
938 func asString(b []byte) string {
939         return unsafe.String(unsafe.SliceData(b), len(b))
940 }
941
942 // A decoder helps decode the index format.
943 type decoder struct {
944         data []byte // data after header
945         str  []byte // string table
946 }
947
948 // intAt returns the int at the given offset in d.data.
949 func (d *decoder) intAt(off int) int {
950         if off < 0 || len(d.data)-off < 4 {
951                 panic(errCorrupt)
952         }
953         i := binary.LittleEndian.Uint32(d.data[off : off+4])
954         if int32(i)>>31 != 0 {
955                 panic(errCorrupt)
956         }
957         return int(i)
958 }
959
960 // boolAt returns the bool at the given offset in d.data.
961 func (d *decoder) boolAt(off int) bool {
962         return d.intAt(off) != 0
963 }
964
965 // stringTableAt returns the string pointed at by the int at the given offset in d.data.
966 func (d *decoder) stringAt(off int) string {
967         return d.stringTableAt(d.intAt(off))
968 }
969
970 // stringTableAt returns the string at the given offset in the string table d.str.
971 func (d *decoder) stringTableAt(off int) string {
972         if off < 0 || off >= len(d.str) {
973                 panic(errCorrupt)
974         }
975         s := d.str[off:]
976         v, n := binary.Uvarint(s)
977         if n <= 0 || v > uint64(len(s[n:])) {
978                 panic(errCorrupt)
979         }
980         return asString(s[n : n+int(v)])
981 }
982
983 // A reader reads sequential fields from a section of the index format.
984 type reader struct {
985         d   *decoder
986         pos int
987 }
988
989 // readAt returns a reader starting at the given position in d.
990 func (d *decoder) readAt(pos int) *reader {
991         return &reader{d, pos}
992 }
993
994 // int reads the next int.
995 func (r *reader) int() int {
996         i := r.d.intAt(r.pos)
997         r.pos += 4
998         return i
999 }
1000
1001 // string reads the next string.
1002 func (r *reader) string() string {
1003         return r.d.stringTableAt(r.int())
1004 }
1005
1006 // bool reads the next bool.
1007 func (r *reader) bool() bool {
1008         return r.int() != 0
1009 }
1010
1011 // tokpos reads the next token.Position.
1012 func (r *reader) tokpos() token.Position {
1013         return token.Position{
1014                 Filename: r.string(),
1015                 Offset:   r.int(),
1016                 Line:     r.int(),
1017                 Column:   r.int(),
1018         }
1019 }