aboutsummaryrefslogtreecommitdiff
path: root/go/gcexportdata/importer.go
blob: efe221e7e1423e370d968eb512be02bb7ea6601e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gcexportdata

import (
	"fmt"
	"go/token"
	"go/types"
	"os"
)

// NewImporter returns a new instance of the types.Importer interface
// that reads type information from export data files written by gc.
// The Importer also satisfies types.ImporterFrom.
//
// Export data files are located using "go build" workspace conventions
// and the build.Default context.
//
// Use this importer instead of go/importer.For("gc", ...) to avoid the
// version-skew problems described in the documentation of this package,
// or to control the FileSet or access the imports map populated during
// package loading.
//
func NewImporter(fset *token.FileSet, imports map[string]*types.Package) types.ImporterFrom {
	return importer{fset, imports}
}

type importer struct {
	fset    *token.FileSet
	imports map[string]*types.Package
}

func (imp importer) Import(importPath string) (*types.Package, error) {
	return imp.ImportFrom(importPath, "", 0)
}

func (imp importer) ImportFrom(importPath, srcDir string, mode types.ImportMode) (_ *types.Package, err error) {
	filename, path := Find(importPath, srcDir)
	if filename == "" {
		if importPath == "unsafe" {
			// Even for unsafe, call Find first in case
			// the package was vendored.
			return types.Unsafe, nil
		}
		return nil, fmt.Errorf("can't find import: %s", importPath)
	}

	if pkg, ok := imp.imports[path]; ok && pkg.Complete() {
		return pkg, nil // cache hit
	}

	// open file
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer func() {
		f.Close()
		if err != nil {
			// add file name to error
			err = fmt.Errorf("reading export data: %s: %v", filename, err)
		}
	}()

	r, err := NewReader(f)
	if err != nil {
		return nil, err
	}

	return Read(r, imp.fset, imp.imports, path)
}