Source file src/pkg/go/internal/srcimporter/srcimporter.go
1
2
3
4
5
6
7 package srcimporter
8
9 import (
10 "fmt"
11 "go/ast"
12 "go/build"
13 "go/parser"
14 "go/token"
15 "go/types"
16 "io"
17 "os"
18 "path/filepath"
19 "sync"
20 )
21
22
23 type Importer struct {
24 ctxt *build.Context
25 fset *token.FileSet
26 sizes types.Sizes
27 packages map[string]*types.Package
28 }
29
30
31
32
33
34
35
36 func New(ctxt *build.Context, fset *token.FileSet, packages map[string]*types.Package) *Importer {
37 return &Importer{
38 ctxt: ctxt,
39 fset: fset,
40 sizes: types.SizesFor(ctxt.Compiler, ctxt.GOARCH),
41 packages: packages,
42 }
43 }
44
45
46
47 var importing types.Package
48
49
50 func (p *Importer) Import(path string) (*types.Package, error) {
51 return p.ImportFrom(path, ".", 0)
52 }
53
54
55
56
57
58
59
60 func (p *Importer) ImportFrom(path, srcDir string, mode types.ImportMode) (*types.Package, error) {
61 if mode != 0 {
62 panic("non-zero import mode")
63 }
64
65 if abs, err := p.absPath(srcDir); err == nil {
66 srcDir = abs
67 }
68 bp, err := p.ctxt.Import(path, srcDir, 0)
69 if err != nil {
70 return nil, err
71 }
72
73
74 if bp.ImportPath == "unsafe" {
75 return types.Unsafe, nil
76 }
77
78
79 pkg := p.packages[bp.ImportPath]
80 if pkg != nil {
81 if pkg == &importing {
82 return nil, fmt.Errorf("import cycle through package %q", bp.ImportPath)
83 }
84 if !pkg.Complete() {
85
86
87
88
89 return pkg, fmt.Errorf("reimported partially imported package %q", bp.ImportPath)
90 }
91 return pkg, nil
92 }
93
94 p.packages[bp.ImportPath] = &importing
95 defer func() {
96
97
98
99
100 if p.packages[bp.ImportPath] == &importing {
101 p.packages[bp.ImportPath] = nil
102 }
103 }()
104
105 var filenames []string
106 filenames = append(filenames, bp.GoFiles...)
107 filenames = append(filenames, bp.CgoFiles...)
108
109 files, err := p.parseFiles(bp.Dir, filenames)
110 if err != nil {
111 return nil, err
112 }
113
114
115 var firstHardErr error
116 conf := types.Config{
117 IgnoreFuncBodies: true,
118 FakeImportC: true,
119
120 Error: func(err error) {
121 if firstHardErr == nil && !err.(types.Error).Soft {
122 firstHardErr = err
123 }
124 },
125 Importer: p,
126 Sizes: p.sizes,
127 }
128 pkg, err = conf.Check(bp.ImportPath, p.fset, files, nil)
129 if err != nil {
130
131
132
133 if firstHardErr != nil {
134 pkg = nil
135 err = firstHardErr
136 }
137 return pkg, fmt.Errorf("type-checking package %q failed (%v)", bp.ImportPath, err)
138 }
139 if firstHardErr != nil {
140
141 panic("package is not safe yet no error was returned")
142 }
143
144 p.packages[bp.ImportPath] = pkg
145 return pkg, nil
146 }
147
148 func (p *Importer) parseFiles(dir string, filenames []string) ([]*ast.File, error) {
149
150 open := p.ctxt.OpenFile
151 if open == nil {
152 open = func(name string) (io.ReadCloser, error) { return os.Open(name) }
153 }
154
155 files := make([]*ast.File, len(filenames))
156 errors := make([]error, len(filenames))
157
158 var wg sync.WaitGroup
159 wg.Add(len(filenames))
160 for i, filename := range filenames {
161 go func(i int, filepath string) {
162 defer wg.Done()
163 src, err := open(filepath)
164 if err != nil {
165 errors[i] = err
166 return
167 }
168 files[i], errors[i] = parser.ParseFile(p.fset, filepath, src, 0)
169 src.Close()
170 }(i, p.joinPath(dir, filename))
171 }
172 wg.Wait()
173
174
175 for _, err := range errors {
176 if err != nil {
177 return nil, err
178 }
179 }
180
181 return files, nil
182 }
183
184
185
186 func (p *Importer) absPath(path string) (string, error) {
187
188
189 return filepath.Abs(path)
190 }
191
192 func (p *Importer) isAbsPath(path string) bool {
193 if f := p.ctxt.IsAbsPath; f != nil {
194 return f(path)
195 }
196 return filepath.IsAbs(path)
197 }
198
199 func (p *Importer) joinPath(elem ...string) string {
200 if f := p.ctxt.JoinPath; f != nil {
201 return f(elem...)
202 }
203 return filepath.Join(elem...)
204 }
205
View as plain text