github.com/jhump/golang-x-tools@v0.0.0-20220218190644-4958d6d39439/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 // 21 package gcexportdata // import "github.com/jhump/golang-x-tools/go/gcexportdata" 22 23 import ( 24 "bufio" 25 "bytes" 26 "fmt" 27 "go/token" 28 "go/types" 29 "io" 30 "io/ioutil" 31 32 "github.com/jhump/golang-x-tools/go/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 workspace layout conventions of go/build. 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 func Find(importPath, srcDir string) (filename, path string) { 45 return gcimporter.FindPkg(importPath, srcDir) 46 } 47 48 // NewReader returns a reader for the export data section of an object 49 // (.o) or archive (.a) file read from r. The new reader may provide 50 // additional trailing data beyond the end of the export data. 51 func NewReader(r io.Reader) (io.Reader, error) { 52 buf := bufio.NewReader(r) 53 _, size, err := gcimporter.FindExportData(buf) 54 if err != nil { 55 return nil, err 56 } 57 58 if size >= 0 { 59 // We were given an archive and found the __.PKGDEF in it. 60 // This tells us the size of the export data, and we don't 61 // need to return the entire file. 62 return &io.LimitedReader{ 63 R: buf, 64 N: size, 65 }, nil 66 } else { 67 // We were given an object file. As such, we don't know how large 68 // the export data is and must return the entire file. 69 return buf, nil 70 } 71 } 72 73 // Read reads export data from in, decodes it, and returns type 74 // information for the package. 75 // The package name is specified by path. 76 // File position information is added to fset. 77 // 78 // Read may inspect and add to the imports map to ensure that references 79 // within the export data to other packages are consistent. The caller 80 // must ensure that imports[path] does not exist, or exists but is 81 // incomplete (see types.Package.Complete), and Read inserts the 82 // resulting package into this map entry. 83 // 84 // On return, the state of the reader is undefined. 85 func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) { 86 data, err := ioutil.ReadAll(in) 87 if err != nil { 88 return nil, fmt.Errorf("reading export data for %q: %v", path, err) 89 } 90 91 if bytes.HasPrefix(data, []byte("!<arch>")) { 92 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) 93 } 94 95 // The App Engine Go runtime v1.6 uses the old export data format. 96 // TODO(adonovan): delete once v1.7 has been around for a while. 97 if bytes.HasPrefix(data, []byte("package ")) { 98 return gcimporter.ImportData(imports, path, path, bytes.NewReader(data)) 99 } 100 101 // The indexed export format starts with an 'i'; the older 102 // binary export format starts with a 'c', 'd', or 'v' 103 // (from "version"). Select appropriate importer. 104 if len(data) > 0 && data[0] == 'i' { 105 _, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path) 106 return pkg, err 107 } 108 109 _, pkg, err := gcimporter.BImportData(fset, imports, data, path) 110 return pkg, err 111 } 112 113 // Write writes encoded type information for the specified package to out. 114 // The FileSet provides file position information for named objects. 115 func Write(out io.Writer, fset *token.FileSet, pkg *types.Package) error { 116 if _, err := io.WriteString(out, "i"); err != nil { 117 return err 118 } 119 return gcimporter.IExportData(out, fset, pkg) 120 } 121 122 // ReadBundle reads an export bundle from in, decodes it, and returns type 123 // information for the packages. 124 // File position information is added to fset. 125 // 126 // ReadBundle may inspect and add to the imports map to ensure that references 127 // within the export bundle to other packages are consistent. 128 // 129 // On return, the state of the reader is undefined. 130 // 131 // Experimental: This API is experimental and may change in the future. 132 func ReadBundle(in io.Reader, fset *token.FileSet, imports map[string]*types.Package) ([]*types.Package, error) { 133 data, err := ioutil.ReadAll(in) 134 if err != nil { 135 return nil, fmt.Errorf("reading export bundle: %v", err) 136 } 137 return gcimporter.IImportBundle(fset, imports, data) 138 } 139 140 // WriteBundle writes encoded type information for the specified packages to out. 141 // The FileSet provides file position information for named objects. 142 // 143 // Experimental: This API is experimental and may change in the future. 144 func WriteBundle(out io.Writer, fset *token.FileSet, pkgs []*types.Package) error { 145 return gcimporter.IExportBundle(out, fset, pkgs) 146 }