github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/go/gcexportdata/gcexportdata.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package gcexportdata provides functions for locating, reading, and 6 // writing export data files containing type information produced by the 7 // gc compiler. This package supports go1.7 export data format and all 8 // later versions. 9 // 10 // Although it might seem convenient for this package to live alongside 11 // go/types in the standard library, this would cause version skew 12 // problems for developer tools that use it, since they must be able to 13 // consume the outputs of the gc compiler both before and after a Go 14 // update such as from Go 1.7 to Go 1.8. Because this package lives in 15 // golang.org/x/tools, sites can update their version of this repo some 16 // time before the Go 1.8 release and rebuild and redeploy their 17 // developer tools, which will then be able to consume both Go 1.7 and 18 // Go 1.8 export data files, so they will work before and after the 19 // Go update. (See discussion at https://golang.org/issue/15651.) 20 package gcexportdata // import "golang.org/x/tools/go/gcexportdata" 21 22 import ( 23 "bufio" 24 "bytes" 25 "encoding/json" 26 "fmt" 27 "go/token" 28 "go/types" 29 "io" 30 "os/exec" 31 32 "golang.org/x/tools/internal/gcimporter" 33 ) 34 35 // Find returns the name of an object (.o) or archive (.a) file 36 // containing type information for the specified import path, 37 // using the go command. 38 // If no file was found, an empty filename is returned. 39 // 40 // A relative srcDir is interpreted relative to the current working directory. 41 // 42 // Find also returns the package's resolved (canonical) import path, 43 // reflecting the effects of srcDir and vendoring on importPath. 44 // 45 // Deprecated: Use the higher-level API in golang.org/x/tools/go/packages, 46 // which is more efficient. 47 func Find(importPath, srcDir string) (filename, path string) { 48 cmd := exec.Command("go", "list", "-json", "-export", "--", importPath) 49 cmd.Dir = srcDir 50 out, err := cmd.CombinedOutput() 51 if err != nil { 52 return "", "" 53 } 54 var data struct { 55 ImportPath string 56 Export string 57 } 58 json.Unmarshal(out, &data) 59 return data.Export, data.ImportPath 60 } 61 62 // NewReader returns a reader for the export data section of an object 63 // (.o) or archive (.a) file read from r. The new reader may provide 64 // additional trailing data beyond the end of the export data. 65 func NewReader(r io.Reader) (io.Reader, error) { 66 buf := bufio.NewReader(r) 67 _, size, err := gcimporter.FindExportData(buf) 68 if err != nil { 69 return nil, err 70 } 71 72 if size >= 0 { 73 // We were given an archive and found the __.PKGDEF in it. 74 // This tells us the size of the export data, and we don't 75 // need to return the entire file. 76 return &io.LimitedReader{ 77 R: buf, 78 N: size, 79 }, nil 80 } else { 81 // We were given an object file. As such, we don't know how large 82 // the export data is and must return the entire file. 83 return buf, nil 84 } 85 } 86 87 // readAll works the same way as io.ReadAll, but avoids allocations and copies 88 // by preallocating a byte slice of the necessary size if the size is known up 89 // front. This is always possible when the input is an archive. In that case, 90 // NewReader will return the known size using an io.LimitedReader. 91 func readAll(r io.Reader) ([]byte, error) { 92 if lr, ok := r.(*io.LimitedReader); ok { 93 data := make([]byte, lr.N) 94 _, err := io.ReadFull(lr, data) 95 return data, err 96 } 97 return io.ReadAll(r) 98 } 99 100 // Read reads export data from in, decodes it, and returns type 101 // information for the package. 102 // 103 // The package path (effectively its linker symbol prefix) is 104 // specified by path, since unlike the package name, this information 105 // may not be recorded in the export data. 106 // 107 // File position information is added to fset. 108 // 109 // Read may inspect and add to the imports map to ensure that references 110 // within the export data to other packages are consistent. The caller 111 // must ensure that imports[path] does not exist, or exists but is 112 // incomplete (see types.Package.Complete), and Read inserts the 113 // resulting package into this map entry. 114 // 115 // On return, the state of the reader is undefined. 116 func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) { 117 data, err := readAll(in) 118 if err != nil { 119 return nil, fmt.Errorf("reading export data for %q: %v", path, err) 120 } 121 122 if bytes.HasPrefix(data, []byte("!<arch>")) { 123 return nil, fmt.Errorf("can't read export data for %q directly from an archive file (call gcexportdata.NewReader first to extract export data)", path) 124 } 125 126 // The indexed export format starts with an 'i'; the older 127 // binary export format starts with a 'c', 'd', or 'v' 128 // (from "version"). Select appropriate importer. 129 if len(data) > 0 { 130 switch data[0] { 131 case 'i': 132 _, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path) 133 return pkg, err 134 135 case 'v', 'c', 'd': 136 _, pkg, err := gcimporter.BImportData(fset, imports, data, path) 137 return pkg, err 138 139 case 'u': 140 _, pkg, err := gcimporter.UImportData(fset, imports, data[1:], path) 141 return pkg, err 142 143 default: 144 l := len(data) 145 if l > 10 { 146 l = 10 147 } 148 return nil, fmt.Errorf("unexpected export data with prefix %q for path %s", string(data[:l]), path) 149 } 150 } 151 return nil, fmt.Errorf("empty export data for %s", path) 152 } 153 154 // Write writes encoded type information for the specified package to out. 155 // The FileSet provides file position information for named objects. 156 func Write(out io.Writer, fset *token.FileSet, pkg *types.Package) error { 157 if _, err := io.WriteString(out, "i"); err != nil { 158 return err 159 } 160 return gcimporter.IExportData(out, fset, pkg) 161 } 162 163 // ReadBundle reads an export bundle from in, decodes it, and returns type 164 // information for the packages. 165 // File position information is added to fset. 166 // 167 // ReadBundle may inspect and add to the imports map to ensure that references 168 // within the export bundle to other packages are consistent. 169 // 170 // On return, the state of the reader is undefined. 171 // 172 // Experimental: This API is experimental and may change in the future. 173 func ReadBundle(in io.Reader, fset *token.FileSet, imports map[string]*types.Package) ([]*types.Package, error) { 174 data, err := readAll(in) 175 if err != nil { 176 return nil, fmt.Errorf("reading export bundle: %v", err) 177 } 178 return gcimporter.IImportBundle(fset, imports, data) 179 } 180 181 // WriteBundle writes encoded type information for the specified packages to out. 182 // The FileSet provides file position information for named objects. 183 // 184 // Experimental: This API is experimental and may change in the future. 185 func WriteBundle(out io.Writer, fset *token.FileSet, pkgs []*types.Package) error { 186 return gcimporter.IExportBundle(out, fset, pkgs) 187 }