GoPLS Viewer

Home|gopls/internal/gcimporter/gcimporter.go
1// Copyright 2011 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// This file is a reduced copy of $GOROOT/src/go/internal/gcimporter/gcimporter.go.
6
7// Package gcimporter provides various functions for reading
8// gc-generated object files that can be used to implement the
9// Importer interface defined by the Go 1.5 standard library package.
10package gcimporter // import "golang.org/x/tools/internal/gcimporter"
11
12import (
13    "bufio"
14    "bytes"
15    "fmt"
16    "go/build"
17    "go/token"
18    "go/types"
19    "io"
20    "io/ioutil"
21    "os"
22    "os/exec"
23    "path/filepath"
24    "strings"
25    "sync"
26)
27
28const (
29    // Enable debug during development: it adds some additional checks, and
30    // prevents errors from being recovered.
31    debug = false
32
33    // If trace is set, debugging output is printed to std out.
34    trace = false
35)
36
37var exportMap sync.Map // package dir → func() (string, bool)
38
39// lookupGorootExport returns the location of the export data
40// (normally found in the build cache, but located in GOROOT/pkg
41// in prior Go releases) for the package located in pkgDir.
42//
43// (We use the package's directory instead of its import path
44// mainly to simplify handling of the packages in src/vendor
45// and cmd/vendor.)
46func lookupGorootExport(pkgDir string) (stringbool) {
47    fok := exportMap.Load(pkgDir)
48    if !ok {
49        var (
50            listOnce   sync.Once
51            exportPath string
52        )
53        f_ = exportMap.LoadOrStore(pkgDir, func() (stringbool) {
54            listOnce.Do(func() {
55                cmd := exec.Command("go""list""-export""-f""{{.Export}}"pkgDir)
56                cmd.Dir = build.Default.GOROOT
57                var output []byte
58                outputerr := cmd.Output()
59                if err != nil {
60                    return
61                }
62
63                exports := strings.Split(string(bytes.TrimSpace(output)), "\n")
64                if len(exports) != 1 {
65                    return
66                }
67
68                exportPath = exports[0]
69            })
70
71            return exportPathexportPath != ""
72        })
73    }
74
75    return f.(func() (stringbool))()
76}
77
78var pkgExts = [...]string{".a"".o"}
79
80// FindPkg returns the filename and unique package id for an import
81// path based on package information provided by build.Import (using
82// the build.Default build.Context). A relative srcDir is interpreted
83// relative to the current working directory.
84// If no file was found, an empty filename is returned.
85func FindPkg(pathsrcDir string) (filenameid string) {
86    if path == "" {
87        return
88    }
89
90    var noext string
91    switch {
92    default:
93        // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
94        // Don't require the source files to be present.
95        if abserr := filepath.Abs(srcDir); err == nil { // see issue 14282
96            srcDir = abs
97        }
98        bp_ := build.Import(pathsrcDirbuild.FindOnly|build.AllowBinary)
99        if bp.PkgObj == "" {
100            var ok bool
101            if bp.Goroot && bp.Dir != "" {
102                filenameok = lookupGorootExport(bp.Dir)
103            }
104            if !ok {
105                id = path // make sure we have an id to print in error message
106                return
107            }
108        } else {
109            noext = strings.TrimSuffix(bp.PkgObj".a")
110            id = bp.ImportPath
111        }
112
113    case build.IsLocalImport(path):
114        // "./x" -> "/this/directory/x.ext", "/this/directory/x"
115        noext = filepath.Join(srcDirpath)
116        id = noext
117
118    case filepath.IsAbs(path):
119        // for completeness only - go/build.Import
120        // does not support absolute imports
121        // "/x" -> "/x.ext", "/x"
122        noext = path
123        id = path
124    }
125
126    if false { // for debugging
127        if path != id {
128            fmt.Printf("%s -> %s\n"pathid)
129        }
130    }
131
132    if filename != "" {
133        if ferr := os.Stat(filename); err == nil && !f.IsDir() {
134            return
135        }
136    }
137
138    // try extensions
139    for _ext := range pkgExts {
140        filename = noext + ext
141        if ferr := os.Stat(filename); err == nil && !f.IsDir() {
142            return
143        }
144    }
145
146    filename = "" // not found
147    return
148}
149
150// Import imports a gc-generated package given its import path and srcDir, adds
151// the corresponding package object to the packages map, and returns the object.
152// The packages map must contain all packages already imported.
153func Import(packages map[string]*types.PackagepathsrcDir stringlookup func(path string) (io.ReadClosererror)) (pkg *types.Packageerr error) {
154    var rc io.ReadCloser
155    var filenameid string
156    if lookup != nil {
157        // With custom lookup specified, assume that caller has
158        // converted path to a canonical import path for use in the map.
159        if path == "unsafe" {
160            return types.Unsafenil
161        }
162        id = path
163
164        // No need to re-import if the package was imported completely before.
165        if pkg = packages[id]; pkg != nil && pkg.Complete() {
166            return
167        }
168        ferr := lookup(path)
169        if err != nil {
170            return nilerr
171        }
172        rc = f
173    } else {
174        filenameid = FindPkg(pathsrcDir)
175        if filename == "" {
176            if path == "unsafe" {
177                return types.Unsafenil
178            }
179            return nilfmt.Errorf("can't find import: %q"id)
180        }
181
182        // no need to re-import if the package was imported completely before
183        if pkg = packages[id]; pkg != nil && pkg.Complete() {
184            return
185        }
186
187        // open file
188        ferr := os.Open(filename)
189        if err != nil {
190            return nilerr
191        }
192        defer func() {
193            if err != nil {
194                // add file name to error
195                err = fmt.Errorf("%s: %v"filenameerr)
196            }
197        }()
198        rc = f
199    }
200    defer rc.Close()
201
202    var hdr string
203    var size int64
204    buf := bufio.NewReader(rc)
205    if hdrsizeerr = FindExportData(buf); err != nil {
206        return
207    }
208
209    switch hdr {
210    case "$$B\n":
211        var data []byte
212        dataerr = ioutil.ReadAll(buf)
213        if err != nil {
214            break
215        }
216
217        // TODO(gri): allow clients of go/importer to provide a FileSet.
218        // Or, define a new standard go/types/gcexportdata package.
219        fset := token.NewFileSet()
220
221        // The indexed export format starts with an 'i'; the older
222        // binary export format starts with a 'c', 'd', or 'v'
223        // (from "version"). Select appropriate importer.
224        if len(data) > 0 {
225            switch data[0] {
226            case 'i':
227                _pkgerr := IImportData(fsetpackagesdata[1:], id)
228                return pkgerr
229
230            case 'v''c''d':
231                _pkgerr := BImportData(fsetpackagesdataid)
232                return pkgerr
233
234            case 'u':
235                _pkgerr := UImportData(fsetpackagesdata[1:size], id)
236                return pkgerr
237
238            default:
239                l := len(data)
240                if l > 10 {
241                    l = 10
242                }
243                return nilfmt.Errorf("unexpected export data with prefix %q for path %s"string(data[:l]), id)
244            }
245        }
246
247    default:
248        err = fmt.Errorf("unknown export data header: %q"hdr)
249    }
250
251    return
252}
253
254func deref(typ types.Typetypes.Type {
255    if p_ := typ.(*types.Pointer); p != nil {
256        return p.Elem()
257    }
258    return typ
259}
260
261type byPath []*types.Package
262
263func (a byPathLen() int           { return len(a) }
264func (a byPathSwap(ij int)      { a[i], a[j] = a[j], a[i] }
265func (a byPathLess(ij intbool { return a[i].Path() < a[j].Path() }
266
MembersX
debug
byPath.Swap
byPath.Less
FindPkg.id
byPath.Len
byPath.Less.j
lookupGorootExport.BlockStmt.BlockStmt.BlockStmt.exports
Import.pkg
Import.BlockStmt.BlockStmt.BlockStmt.l
byPath.Swap.a
byPath.Swap.i
FindPkg.RangeStmt_3527.BlockStmt.err
Import.filename
Import.buf
deref.typ
Import.err
Import.BlockStmt.BlockStmt.BlockStmt._
Import.BlockStmt.BlockStmt.BlockStmt.err
FindPkg
FindPkg.srcDir
FindPkg.BlockStmt.err
ioutil
lookupGorootExport.BlockStmt.exportPath
lookupGorootExport.BlockStmt.BlockStmt.BlockStmt.cmd
exec
Import.lookup
byPath.Len.a
FindPkg.BlockStmt.BlockStmt.ok
FindPkg.RangeStmt_3527.BlockStmt.f
Import.srcDir
Import.BlockStmt.BlockStmt.BlockStmt.pkg
exportMap
lookupGorootExport
lookupGorootExport.ok
FindPkg.BlockStmt.f
FindPkg.RangeStmt_3527.ext
Import
Import.id
lookupGorootExport.BlockStmt.BlockStmt.BlockStmt.err
FindPkg.filename
FindPkg.BlockStmt.abs
Import.BlockStmt.f
Import.size
Import.BlockStmt.fset
deref
byPath.Less.i
lookupGorootExport.BlockStmt.listOnce
FindPkg.BlockStmt._
Import.packages
Import.path
Import.BlockStmt.data
byPath.Less.a
lookupGorootExport.f
lookupGorootExport.BlockStmt.BlockStmt.BlockStmt.output
FindPkg.BlockStmt.bp
byPath.Swap.j
lookupGorootExport.pkgDir
FindPkg.noext
Import.hdr
trace
FindPkg.path
Import.rc
os
Import.BlockStmt.err
byPath
Members
X