1
2
3
4
5 package noder
6
7 import (
8 "errors"
9 "fmt"
10 "internal/buildcfg"
11 "internal/pkgbits"
12 "os"
13 pathpkg "path"
14 "runtime"
15 "strings"
16 "unicode"
17 "unicode/utf8"
18
19 "cmd/compile/internal/base"
20 "cmd/compile/internal/importer"
21 "cmd/compile/internal/ir"
22 "cmd/compile/internal/typecheck"
23 "cmd/compile/internal/types"
24 "cmd/compile/internal/types2"
25 "cmd/internal/archive"
26 "cmd/internal/bio"
27 "cmd/internal/goobj"
28 "cmd/internal/objabi"
29 )
30
31 type gcimports struct {
32 ctxt *types2.Context
33 packages map[string]*types2.Package
34 }
35
36 func (m *gcimports) Import(path string) (*types2.Package, error) {
37 return m.ImportFrom(path, "" , 0)
38 }
39
40 func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*types2.Package, error) {
41 if mode != 0 {
42 panic("mode must be 0")
43 }
44
45 _, pkg, err := readImportFile(path, typecheck.Target, m.ctxt, m.packages)
46 return pkg, err
47 }
48
49 func isDriveLetter(b byte) bool {
50 return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z'
51 }
52
53
54 func islocalname(name string) bool {
55 return strings.HasPrefix(name, "/") ||
56 runtime.GOOS == "windows" && len(name) >= 3 && isDriveLetter(name[0]) && name[1] == ':' && name[2] == '/' ||
57 strings.HasPrefix(name, "./") || name == "." ||
58 strings.HasPrefix(name, "../") || name == ".."
59 }
60
61 func openPackage(path string) (*os.File, error) {
62 if islocalname(path) {
63 if base.Flag.NoLocalImports {
64 return nil, errors.New("local imports disallowed")
65 }
66
67 if base.Flag.Cfg.PackageFile != nil {
68 return os.Open(base.Flag.Cfg.PackageFile[path])
69 }
70
71
72
73
74 if file, err := os.Open(fmt.Sprintf("%s.a", path)); err == nil {
75 return file, nil
76 }
77 if file, err := os.Open(fmt.Sprintf("%s.o", path)); err == nil {
78 return file, nil
79 }
80 return nil, errors.New("file not found")
81 }
82
83
84
85
86 if q := pathpkg.Clean(path); q != path {
87 return nil, fmt.Errorf("non-canonical import path %q (should be %q)", path, q)
88 }
89
90 if base.Flag.Cfg.PackageFile != nil {
91 return os.Open(base.Flag.Cfg.PackageFile[path])
92 }
93
94 for _, dir := range base.Flag.Cfg.ImportDirs {
95 if file, err := os.Open(fmt.Sprintf("%s/%s.a", dir, path)); err == nil {
96 return file, nil
97 }
98 if file, err := os.Open(fmt.Sprintf("%s/%s.o", dir, path)); err == nil {
99 return file, nil
100 }
101 }
102
103 if buildcfg.GOROOT != "" {
104 suffix := ""
105 if base.Flag.InstallSuffix != "" {
106 suffix = "_" + base.Flag.InstallSuffix
107 } else if base.Flag.Race {
108 suffix = "_race"
109 } else if base.Flag.MSan {
110 suffix = "_msan"
111 } else if base.Flag.ASan {
112 suffix = "_asan"
113 }
114
115 if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.a", buildcfg.GOROOT, buildcfg.GOOS, buildcfg.GOARCH, suffix, path)); err == nil {
116 return file, nil
117 }
118 if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.o", buildcfg.GOROOT, buildcfg.GOOS, buildcfg.GOARCH, suffix, path)); err == nil {
119 return file, nil
120 }
121 }
122 return nil, errors.New("file not found")
123 }
124
125
126
127 func resolveImportPath(path string) (string, error) {
128
129
130
131
132 if path == "main" {
133 return "", errors.New("cannot import \"main\"")
134 }
135
136 if base.Ctxt.Pkgpath == "" {
137 panic("missing pkgpath")
138 }
139 if path == base.Ctxt.Pkgpath {
140 return "", fmt.Errorf("import %q while compiling that package (import cycle)", path)
141 }
142
143 if mapped, ok := base.Flag.Cfg.ImportMap[path]; ok {
144 path = mapped
145 }
146
147 if islocalname(path) {
148 if path[0] == '/' {
149 return "", errors.New("import path cannot be absolute path")
150 }
151
152 prefix := base.Flag.D
153 if prefix == "" {
154
155
156
157
158 prefix = base.Ctxt.Pathname
159 }
160 path = pathpkg.Join(prefix, path)
161
162 if err := checkImportPath(path, true); err != nil {
163 return "", err
164 }
165 }
166
167 return path, nil
168 }
169
170
171
172
173 func readImportFile(path string, target *ir.Package, env *types2.Context, packages map[string]*types2.Package) (pkg1 *types.Pkg, pkg2 *types2.Package, err error) {
174 path, err = resolveImportPath(path)
175 if err != nil {
176 return
177 }
178
179 if path == "unsafe" {
180 pkg1, pkg2 = types.UnsafePkg, types2.Unsafe
181
182
183
184 if !pkg1.Direct {
185 pkg1.Direct = true
186 target.Imports = append(target.Imports, pkg1)
187 }
188
189 return
190 }
191
192 pkg1 = types.NewPkg(path, "")
193 if packages != nil {
194 pkg2 = packages[path]
195 assert(pkg1.Direct == (pkg2 != nil && pkg2.Complete()))
196 }
197
198 if pkg1.Direct {
199 return
200 }
201 pkg1.Direct = true
202 target.Imports = append(target.Imports, pkg1)
203
204 f, err := openPackage(path)
205 if err != nil {
206 return
207 }
208 defer f.Close()
209
210 r, end, err := findExportData(f)
211 if err != nil {
212 return
213 }
214
215 if base.Debug.Export != 0 {
216 fmt.Printf("importing %s (%s)\n", path, f.Name())
217 }
218
219 c, err := r.ReadByte()
220 if err != nil {
221 return
222 }
223
224 pos := r.Offset()
225
226
227
228
229 var data string
230 data, err = base.MapFile(r.File(), pos, end-pos)
231 if err != nil {
232 return
233 }
234
235 switch c {
236 case 'u':
237
238 data = strings.TrimSuffix(data, "\n$$\n")
239
240 pr := pkgbits.NewPkgDecoder(pkg1.Path, data)
241
242
243 readPackage(newPkgReader(pr), pkg1, false)
244 pkg2 = importer.ReadPackage(env, packages, pr)
245
246 default:
247
248
249 err = fmt.Errorf("unexpected package format byte: %v", c)
250 return
251 }
252
253 err = addFingerprint(path, f, end)
254 return
255 }
256
257
258
259
260 func findExportData(f *os.File) (r *bio.Reader, end int64, err error) {
261 r = bio.NewReader(f)
262
263
264 line, err := r.ReadString('\n')
265 if err != nil {
266 return
267 }
268
269 if line == "!<arch>\n" {
270
271 sz := int64(archive.ReadHeader(r.Reader, "__.PKGDEF"))
272 if sz <= 0 {
273 err = errors.New("not a package file")
274 return
275 }
276 end = r.Offset() + sz
277 line, err = r.ReadString('\n')
278 if err != nil {
279 return
280 }
281 } else {
282
283
284 var fi os.FileInfo
285 fi, err = f.Stat()
286 if err != nil {
287 return
288 }
289 end = fi.Size()
290 }
291
292 if !strings.HasPrefix(line, "go object ") {
293 err = fmt.Errorf("not a go object file: %s", line)
294 return
295 }
296 if expect := objabi.HeaderString(); line != expect {
297 err = fmt.Errorf("object is [%s] expected [%s]", line, expect)
298 return
299 }
300
301
302 for !strings.HasPrefix(line, "$$") {
303 line, err = r.ReadString('\n')
304 if err != nil {
305 return
306 }
307 }
308
309
310 if line != "$$B\n" {
311 err = errors.New("old export format no longer supported (recompile library)")
312 return
313 }
314
315 return
316 }
317
318
319
320 func addFingerprint(path string, f *os.File, end int64) error {
321 const eom = "\n$$\n"
322 var fingerprint goobj.FingerprintType
323
324 var buf [len(fingerprint) + len(eom)]byte
325 if _, err := f.ReadAt(buf[:], end-int64(len(buf))); err != nil {
326 return err
327 }
328
329
330
331
332
333 if s := string(buf[len(fingerprint):]); s != eom {
334 return fmt.Errorf("expected $$ marker, but found %q", s)
335 }
336
337 copy(fingerprint[:], buf[:])
338 base.Ctxt.AddImport(path, fingerprint)
339
340 return nil
341 }
342
343 func checkImportPath(path string, allowSpace bool) error {
344 if path == "" {
345 return errors.New("import path is empty")
346 }
347
348 if strings.Contains(path, "\x00") {
349 return errors.New("import path contains NUL")
350 }
351
352 for ri := range base.ReservedImports {
353 if path == ri {
354 return fmt.Errorf("import path %q is reserved and cannot be used", path)
355 }
356 }
357
358 for _, r := range path {
359 switch {
360 case r == utf8.RuneError:
361 return fmt.Errorf("import path contains invalid UTF-8 sequence: %q", path)
362 case r < 0x20 || r == 0x7f:
363 return fmt.Errorf("import path contains control character: %q", path)
364 case r == '\\':
365 return fmt.Errorf("import path contains backslash; use slash: %q", path)
366 case !allowSpace && unicode.IsSpace(r):
367 return fmt.Errorf("import path contains space character: %q", path)
368 case strings.ContainsRune("!\"#$%&'()*,:;<=>?[]^`{|}", r):
369 return fmt.Errorf("import path contains invalid character '%c': %q", r, path)
370 }
371 }
372
373 return nil
374 }
375
View as plain text