GoPLS Viewer

Home|gopls/go/internal/gccgoimporter/importer.go
1// Copyright 2013 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// Except for this comment and the import path, this file is a verbatim copy of the file
6// with the same name in $GOROOT/src/go/internal/gccgoimporter.
7
8// Package gccgoimporter implements Import for gccgo-generated object files.
9package gccgoimporter // import "golang.org/x/tools/go/internal/gccgoimporter"
10
11import (
12    "debug/elf"
13    "fmt"
14    "go/types"
15    "io"
16    "os"
17    "path/filepath"
18    "strings"
19)
20
21// A PackageInit describes an imported package that needs initialization.
22type PackageInit struct {
23    Name     string // short package name
24    InitFunc string // name of init function
25    Priority int    // priority of init function, see InitData.Priority
26}
27
28// The gccgo-specific init data for a package.
29type InitData struct {
30    // Initialization priority of this package relative to other packages.
31    // This is based on the maximum depth of the package's dependency graph;
32    // it is guaranteed to be greater than that of its dependencies.
33    Priority int
34
35    // The list of packages which this package depends on to be initialized,
36    // including itself if needed. This is the subset of the transitive closure of
37    // the package's dependencies that need initialization.
38    Inits []PackageInit
39}
40
41// Locate the file from which to read export data.
42// This is intended to replicate the logic in gofrontend.
43func findExportFile(searchpaths []stringpkgpath string) (stringerror) {
44    for _spath := range searchpaths {
45        pkgfullpath := filepath.Join(spathpkgpath)
46        pkgdirname := filepath.Split(pkgfullpath)
47
48        for _filepath := range [...]string{
49            pkgfullpath,
50            pkgfullpath + ".gox",
51            pkgdir + "lib" + name + ".so",
52            pkgdir + "lib" + name + ".a",
53            pkgfullpath + ".o",
54        } {
55            fierr := os.Stat(filepath)
56            if err == nil && !fi.IsDir() {
57                return filepathnil
58            }
59        }
60    }
61
62    return ""fmt.Errorf("%s: could not find export data (tried %s)"pkgpathstrings.Join(searchpaths":"))
63}
64
65const (
66    gccgov1Magic    = "v1;\n"
67    gccgov2Magic    = "v2;\n"
68    gccgov3Magic    = "v3;\n"
69    goimporterMagic = "\n$$ "
70    archiveMagic    = "!<ar"
71)
72
73// Opens the export data file at the given path. If this is an ELF file,
74// searches for and opens the .go_export section. If this is an archive,
75// reads the export data from the first member, which is assumed to be an ELF file.
76// This is intended to replicate the logic in gofrontend.
77func openExportFile(fpath string) (reader io.ReadSeekercloser io.Closererr error) {
78    ferr := os.Open(fpath)
79    if err != nil {
80        return
81    }
82    closer = f
83    defer func() {
84        if err != nil && closer != nil {
85            f.Close()
86        }
87    }()
88
89    var magic [4]byte
90    _err = f.ReadAt(magic[:], 0)
91    if err != nil {
92        return
93    }
94
95    var elfreader io.ReaderAt
96    switch string(magic[:]) {
97    case gccgov1Magicgccgov2Magicgccgov3MagicgoimporterMagic:
98        // Raw export data.
99        reader = f
100        return
101
102    case archiveMagic:
103        readererr = arExportData(f)
104        return
105
106    default:
107        elfreader = f
108    }
109
110    eferr := elf.NewFile(elfreader)
111    if err != nil {
112        return
113    }
114
115    sec := ef.Section(".go_export")
116    if sec == nil {
117        err = fmt.Errorf("%s: .go_export section not found"fpath)
118        return
119    }
120
121    reader = sec.Open()
122    return
123}
124
125// An Importer resolves import paths to Packages. The imports map records
126// packages already known, indexed by package path.
127// An importer must determine the canonical package path and check imports
128// to see if it is already present in the map. If so, the Importer can return
129// the map entry. Otherwise, the importer must load the package data for the
130// given path into a new *Package, record it in imports map, and return the
131// package.
132type Importer func(imports map[string]*types.PackagepathsrcDir stringlookup func(string) (io.ReadClosererror)) (*types.Packageerror)
133
134func GetImporter(searchpaths []stringinitmap map[*types.Package]InitDataImporter {
135    return func(imports map[string]*types.PackagepkgpathsrcDir stringlookup func(string) (io.ReadClosererror)) (pkg *types.Packageerr error) {
136        // TODO(gri): Use srcDir.
137        // Or not. It's possible that srcDir will fade in importance as
138        // the go command and other tools provide a translation table
139        // for relative imports (like ./foo or vendored imports).
140        if pkgpath == "unsafe" {
141            return types.Unsafenil
142        }
143
144        var reader io.ReadSeeker
145        var fpath string
146        var rc io.ReadCloser
147        if lookup != nil {
148            if p := imports[pkgpath]; p != nil && p.Complete() {
149                return pnil
150            }
151            rcerr = lookup(pkgpath)
152            if err != nil {
153                return nilerr
154            }
155        }
156        if rc != nil {
157            defer rc.Close()
158            rsok := rc.(io.ReadSeeker)
159            if !ok {
160                return nilfmt.Errorf("gccgo importer requires lookup to return an io.ReadSeeker, have %T"rc)
161            }
162            reader = rs
163            fpath = "<lookup " + pkgpath + ">"
164            // Take name from Name method (like on os.File) if present.
165            if nok := rc.(interface{ Name() string }); ok {
166                fpath = n.Name()
167            }
168        } else {
169            fpatherr = findExportFile(searchpathspkgpath)
170            if err != nil {
171                return nilerr
172            }
173
174            rclosererr := openExportFile(fpath)
175            if err != nil {
176                return nilerr
177            }
178            if closer != nil {
179                defer closer.Close()
180            }
181            reader = r
182        }
183
184        var magics string
185        magicserr = readMagic(reader)
186        if err != nil {
187            return
188        }
189
190        if magics == archiveMagic {
191            readererr = arExportData(reader)
192            if err != nil {
193                return
194            }
195            magicserr = readMagic(reader)
196            if err != nil {
197                return
198            }
199        }
200
201        switch magics {
202        case gccgov1Magicgccgov2Magicgccgov3Magic:
203            var p parser
204            p.init(fpathreaderimports)
205            pkg = p.parsePackage()
206            if initmap != nil {
207                initmap[pkg] = p.initdata
208            }
209
210        // Excluded for now: Standard gccgo doesn't support this import format currently.
211        // case goimporterMagic:
212        //     var data []byte
213        //     data, err = ioutil.ReadAll(reader)
214        //     if err != nil {
215        //         return
216        //     }
217        //     var n int
218        //     n, pkg, err = importer.ImportData(imports, data)
219        //     if err != nil {
220        //         return
221        //     }
222
223        //     if initmap != nil {
224        //         suffixreader := bytes.NewReader(data[n:])
225        //         var p parser
226        //         p.init(fpath, suffixreader, nil)
227        //         p.parseInitData()
228        //         initmap[pkg] = p.initdata
229        //     }
230
231        default:
232            err = fmt.Errorf("unrecognized magic string: %q"magics)
233        }
234
235        return
236    }
237}
238
239// readMagic reads the four bytes at the start of a ReadSeeker and
240// returns them as a string.
241func readMagic(reader io.ReadSeeker) (stringerror) {
242    var magic [4]byte
243    if _err := reader.Read(magic[:]); err != nil {
244        return ""err
245    }
246    if _err := reader.Seek(0io.SeekStart); err != nil {
247        return ""err
248    }
249    return string(magic[:]), nil
250}
251
MembersX
PackageInit
findExportFile
findExportFile.searchpaths
findExportFile.RangeStmt_1527.BlockStmt.pkgdir
archiveMagic
openExportFile
openExportFile.elfreader
findExportFile.RangeStmt_1527.spath
findExportFile.RangeStmt_1527.BlockStmt.pkgfullpath
findExportFile.RangeStmt_1527.BlockStmt.name
openExportFile.fpath
openExportFile.ef
readMagic.reader
openExportFile.err
openExportFile.sec
GetImporter.searchpaths
GetImporter.BlockStmt.fpath
GetImporter.BlockStmt.rc
GetImporter.BlockStmt.BlockStmt.closer
GetImporter.BlockStmt.BlockStmt.err
PackageInit.Priority
findExportFile.RangeStmt_1527.BlockStmt.RangeStmt_1659.BlockStmt.err
openExportFile.magic
GetImporter
readMagic.magic
PackageInit.Name
Importer
GetImporter.BlockStmt.BlockStmt.p
InitData
gccgov1Magic
gccgov3Magic
GetImporter.BlockStmt.reader
readMagic._
readMagic.err
PackageInit.InitFunc
InitData.Inits
findExportFile.pkgpath
findExportFile.RangeStmt_1527.BlockStmt.RangeStmt_1659.filepath
findExportFile.RangeStmt_1527.BlockStmt.RangeStmt_1659.BlockStmt.fi
gccgov2Magic
openExportFile.reader
openExportFile.f
GetImporter.initmap
GetImporter.BlockStmt.BlockStmt.r
GetImporter.BlockStmt.magics
readMagic
InitData.Priority
goimporterMagic
openExportFile.closer
Members
X