1// Copyright 2018 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// Indexed package import.
6// See cmd/compile/internal/gc/iexport.go for the export data format.
7
8// This file is a copy of $GOROOT/src/go/internal/gcimporter/iimport.go.
9
10package gcimporter
11
12import (
13	"bytes"
14	"encoding/binary"
15	"fmt"
16	"go/constant"
17	"go/token"
18	"go/types"
19	"io"
20	"sort"
21)
22
23type intReader struct {
24	*bytes.Reader
25	path string
26}
27
28func (r *intReader) int64() int64 {
29	i, err := binary.ReadVarint(r.Reader)
30	if err != nil {
31		errorf("import %q: read varint error: %v", r.path, err)
32	}
33	return i
34}
35
36func (r *intReader) uint64() uint64 {
37	i, err := binary.ReadUvarint(r.Reader)
38	if err != nil {
39		errorf("import %q: read varint error: %v", r.path, err)
40	}
41	return i
42}
43
44const predeclReserved = 32
45
46type itag uint64
47
48const (
49	// Types
50	definedType itag = iota
51	pointerType
52	sliceType
53	arrayType
54	chanType
55	mapType
56	signatureType
57	structType
58	interfaceType
59)
60
61// IImportData imports a package from the serialized package data
62// and returns the number of bytes consumed and a reference to the package.
63// If the export data version is not recognized or the format is otherwise
64// compromised, an error is returned.
65func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
66	const currentVersion = 0
67	version := -1
68	defer func() {
69		if e := recover(); e != nil {
70			if version > currentVersion {
71				err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
72			} else {
73				err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e)
74			}
75		}
76	}()
77
78	r := &intReader{bytes.NewReader(data), path}
79
80	version = int(r.uint64())
81	switch version {
82	case currentVersion:
83	default:
84		errorf("unknown iexport format version %d", version)
85	}
86
87	sLen := int64(r.uint64())
88	dLen := int64(r.uint64())
89
90	whence, _ := r.Seek(0, io.SeekCurrent)
91	stringData := data[whence : whence+sLen]
92	declData := data[whence+sLen : whence+sLen+dLen]
93	r.Seek(sLen+dLen, io.SeekCurrent)
94
95	p := iimporter{
96		ipath: path,
97
98		stringData:  stringData,
99		stringCache: make(map[uint64]string),
100		pkgCache:    make(map[uint64]*types.Package),
101
102		declData: declData,
103		pkgIndex: make(map[*types.Package]map[string]uint64),
104		typCache: make(map[uint64]types.Type),
105
106		fake: fakeFileSet{
107			fset:  fset,
108			files: make(map[string]*token.File),
109		},
110	}
111
112	for i, pt := range predeclared {
113		p.typCache[uint64(i)] = pt
114	}
115
116	pkgList := make([]*types.Package, r.uint64())
117	for i := range pkgList {
118		pkgPathOff := r.uint64()
119		pkgPath := p.stringAt(pkgPathOff)
120		pkgName := p.stringAt(r.uint64())
121		_ = r.uint64() // package height; unused by go/types
122
123		if pkgPath == "" {
124			pkgPath = path
125		}
126		pkg := imports[pkgPath]
127		if pkg == nil {
128			pkg = types.NewPackage(pkgPath, pkgName)
129			imports[pkgPath] = pkg
130		} else if pkg.Name() != pkgName {
131			errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path)
132		}
133
134		p.pkgCache[pkgPathOff] = pkg
135
136		nameIndex := make(map[string]uint64)
137		for nSyms := r.uint64(); nSyms > 0; nSyms-- {
138			name := p.stringAt(r.uint64())
139			nameIndex[name] = r.uint64()
140		}
141
142		p.pkgIndex[pkg] = nameIndex
143		pkgList[i] = pkg
144	}
145
146	localpkg := pkgList[0]
147
148	names := make([]string, 0, len(p.pkgIndex[localpkg]))
149	for name := range p.pkgIndex[localpkg] {
150		names = append(names, name)
151	}
152	sort.Strings(names)
153	for _, name := range names {
154		p.doDecl(localpkg, name)
155	}
156
157	for _, typ := range p.interfaceList {
158		typ.Complete()
159	}
160
161	// record all referenced packages as imports
162	list := append(([]*types.Package)(nil), pkgList[1:]...)
163	sort.Sort(byPath(list))
164	localpkg.SetImports(list)
165
166	// package was imported completely and without errors
167	localpkg.MarkComplete()
168
169	consumed, _ := r.Seek(0, io.SeekCurrent)
170	return int(consumed), localpkg, nil
171}
172
173type iimporter struct {
174	ipath string
175
176	stringData  []byte
177	stringCache map[uint64]string
178	pkgCache    map[uint64]*types.Package
179
180	declData []byte
181	pkgIndex map[*types.Package]map[string]uint64
182	typCache map[uint64]types.Type
183
184	fake          fakeFileSet
185	interfaceList []*types.Interface
186}
187
188func (p *iimporter) doDecl(pkg *types.Package, name string) {
189	// See if we've already imported this declaration.
190	if obj := pkg.Scope().Lookup(name); obj != nil {
191		return
192	}
193
194	off, ok := p.pkgIndex[pkg][name]
195	if !ok {
196		errorf("%v.%v not in index", pkg, name)
197	}
198
199	r := &importReader{p: p, currPkg: pkg}
200	r.declReader.Reset(p.declData[off:])
201
202	r.obj(name)
203}
204
205func (p *iimporter) stringAt(off uint64) string {
206	if s, ok := p.stringCache[off]; ok {
207		return s
208	}
209
210	slen, n := binary.Uvarint(p.stringData[off:])
211	if n <= 0 {
212		errorf("varint failed")
213	}
214	spos := off + uint64(n)
215	s := string(p.stringData[spos : spos+slen])
216	p.stringCache[off] = s
217	return s
218}
219
220func (p *iimporter) pkgAt(off uint64) *types.Package {
221	if pkg, ok := p.pkgCache[off]; ok {
222		return pkg
223	}
224	path := p.stringAt(off)
225	errorf("missing package %q in %q", path, p.ipath)
226	return nil
227}
228
229func (p *iimporter) typAt(off uint64, base *types.Named) types.Type {
230	if t, ok := p.typCache[off]; ok && (base == nil || !isInterface(t)) {
231		return t
232	}
233
234	if off < predeclReserved {
235		errorf("predeclared type missing from cache: %v", off)
236	}
237
238	r := &importReader{p: p}
239	r.declReader.Reset(p.declData[off-predeclReserved:])
240	t := r.doType(base)
241
242	if base == nil || !isInterface(t) {
243		p.typCache[off] = t
244	}
245	return t
246}
247
248type importReader struct {
249	p          *iimporter
250	declReader bytes.Reader
251	currPkg    *types.Package
252	prevFile   string
253	prevLine   int64
254}
255
256func (r *importReader) obj(name string) {
257	tag := r.byte()
258	pos := r.pos()
259
260	switch tag {
261	case 'A':
262		typ := r.typ()
263
264		r.declare(types.NewTypeName(pos, r.currPkg, name, typ))
265
266	case 'C':
267		typ, val := r.value()
268
269		r.declare(types.NewConst(pos, r.currPkg, name, typ, val))
270
271	case 'F':
272		sig := r.signature(nil)
273
274		r.declare(types.NewFunc(pos, r.currPkg, name, sig))
275
276	case 'T':
277		// Types can be recursive. We need to setup a stub
278		// declaration before recursing.
279		obj := types.NewTypeName(pos, r.currPkg, name, nil)
280		named := types.NewNamed(obj, nil, nil)
281		r.declare(obj)
282
283		underlying := r.p.typAt(r.uint64(), named).Underlying()
284		named.SetUnderlying(underlying)
285
286		if !isInterface(underlying) {
287			for n := r.uint64(); n > 0; n-- {
288				mpos := r.pos()
289				mname := r.ident()
290				recv := r.param()
291				msig := r.signature(recv)
292
293				named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig))
294			}
295		}
296
297	case 'V':
298		typ := r.typ()
299
300		r.declare(types.NewVar(pos, r.currPkg, name, typ))
301
302	default:
303		errorf("unexpected tag: %v", tag)
304	}
305}
306
307func (r *importReader) declare(obj types.Object) {
308	obj.Pkg().Scope().Insert(obj)
309}
310
311func (r *importReader) value() (typ types.Type, val constant.Value) {
312	typ = r.typ()
313
314	switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType {
315	case types.IsBoolean:
316		val = constant.MakeBool(r.bool())
317
318	case types.IsString:
319		val = constant.MakeString(r.string())
320
321	case types.IsInteger:
322		val = r.mpint(b)
323
324	case types.IsFloat:
325		val = r.mpfloat(b)
326
327	case types.IsComplex:
328		re := r.mpfloat(b)
329		im := r.mpfloat(b)
330		val = constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
331
332	default:
333		errorf("unexpected type %v", typ) // panics
334		panic("unreachable")
335	}
336
337	return
338}
339
340func intSize(b *types.Basic) (signed bool, maxBytes uint) {
341	if (b.Info() & types.IsUntyped) != 0 {
342		return true, 64
343	}
344
345	switch b.Kind() {
346	case types.Float32, types.Complex64:
347		return true, 3
348	case types.Float64, types.Complex128:
349		return true, 7
350	}
351
352	signed = (b.Info() & types.IsUnsigned) == 0
353	switch b.Kind() {
354	case types.Int8, types.Uint8:
355		maxBytes = 1
356	case types.Int16, types.Uint16:
357		maxBytes = 2
358	case types.Int32, types.Uint32:
359		maxBytes = 4
360	default:
361		maxBytes = 8
362	}
363
364	return
365}
366
367func (r *importReader) mpint(b *types.Basic) constant.Value {
368	signed, maxBytes := intSize(b)
369
370	maxSmall := 256 - maxBytes
371	if signed {
372		maxSmall = 256 - 2*maxBytes
373	}
374	if maxBytes == 1 {
375		maxSmall = 256
376	}
377
378	n, _ := r.declReader.ReadByte()
379	if uint(n) < maxSmall {
380		v := int64(n)
381		if signed {
382			v >>= 1
383			if n&1 != 0 {
384				v = ^v
385			}
386		}
387		return constant.MakeInt64(v)
388	}
389
390	v := -n
391	if signed {
392		v = -(n &^ 1) >> 1
393	}
394	if v < 1 || uint(v) > maxBytes {
395		errorf("weird decoding: %v, %v => %v", n, signed, v)
396	}
397
398	buf := make([]byte, v)
399	io.ReadFull(&r.declReader, buf)
400
401	// convert to little endian
402	// TODO(gri) go/constant should have a more direct conversion function
403	//           (e.g., once it supports a big.Float based implementation)
404	for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 {
405		buf[i], buf[j] = buf[j], buf[i]
406	}
407
408	x := constant.MakeFromBytes(buf)
409	if signed && n&1 != 0 {
410		x = constant.UnaryOp(token.SUB, x, 0)
411	}
412	return x
413}
414
415func (r *importReader) mpfloat(b *types.Basic) constant.Value {
416	x := r.mpint(b)
417	if constant.Sign(x) == 0 {
418		return x
419	}
420
421	exp := r.int64()
422	switch {
423	case exp > 0:
424		x = constant.Shift(x, token.SHL, uint(exp))
425	case exp < 0:
426		d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp))
427		x = constant.BinaryOp(x, token.QUO, d)
428	}
429	return x
430}
431
432func (r *importReader) ident() string {
433	return r.string()
434}
435
436func (r *importReader) qualifiedIdent() (*types.Package, string) {
437	name := r.string()
438	pkg := r.pkg()
439	return pkg, name
440}
441
442func (r *importReader) pos() token.Pos {
443	delta := r.int64()
444	if delta != deltaNewFile {
445		r.prevLine += delta
446	} else if l := r.int64(); l == -1 {
447		r.prevLine += deltaNewFile
448	} else {
449		r.prevFile = r.string()
450		r.prevLine = l
451	}
452
453	if r.prevFile == "" && r.prevLine == 0 {
454		return token.NoPos
455	}
456
457	return r.p.fake.pos(r.prevFile, int(r.prevLine))
458}
459
460func (r *importReader) typ() types.Type {
461	return r.p.typAt(r.uint64(), nil)
462}
463
464func isInterface(t types.Type) bool {
465	_, ok := t.(*types.Interface)
466	return ok
467}
468
469func (r *importReader) pkg() *types.Package { return r.p.pkgAt(r.uint64()) }
470func (r *importReader) string() string      { return r.p.stringAt(r.uint64()) }
471
472func (r *importReader) doType(base *types.Named) types.Type {
473	switch k := r.kind(); k {
474	default:
475		errorf("unexpected kind tag in %q: %v", r.p.ipath, k)
476		return nil
477
478	case definedType:
479		pkg, name := r.qualifiedIdent()
480		r.p.doDecl(pkg, name)
481		return pkg.Scope().Lookup(name).(*types.TypeName).Type()
482	case pointerType:
483		return types.NewPointer(r.typ())
484	case sliceType:
485		return types.NewSlice(r.typ())
486	case arrayType:
487		n := r.uint64()
488		return types.NewArray(r.typ(), int64(n))
489	case chanType:
490		dir := chanDir(int(r.uint64()))
491		return types.NewChan(dir, r.typ())
492	case mapType:
493		return types.NewMap(r.typ(), r.typ())
494	case signatureType:
495		r.currPkg = r.pkg()
496		return r.signature(nil)
497
498	case structType:
499		r.currPkg = r.pkg()
500
501		fields := make([]*types.Var, r.uint64())
502		tags := make([]string, len(fields))
503		for i := range fields {
504			fpos := r.pos()
505			fname := r.ident()
506			ftyp := r.typ()
507			emb := r.bool()
508			tag := r.string()
509
510			fields[i] = types.NewField(fpos, r.currPkg, fname, ftyp, emb)
511			tags[i] = tag
512		}
513		return types.NewStruct(fields, tags)
514
515	case interfaceType:
516		r.currPkg = r.pkg()
517
518		embeddeds := make([]types.Type, r.uint64())
519		for i := range embeddeds {
520			_ = r.pos()
521			embeddeds[i] = r.typ()
522		}
523
524		methods := make([]*types.Func, r.uint64())
525		for i := range methods {
526			mpos := r.pos()
527			mname := r.ident()
528
529			// TODO(mdempsky): Matches bimport.go, but I
530			// don't agree with this.
531			var recv *types.Var
532			if base != nil {
533				recv = types.NewVar(token.NoPos, r.currPkg, "", base)
534			}
535
536			msig := r.signature(recv)
537			methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig)
538		}
539
540		typ := newInterface(methods, embeddeds)
541		r.p.interfaceList = append(r.p.interfaceList, typ)
542		return typ
543	}
544}
545
546func (r *importReader) kind() itag {
547	return itag(r.uint64())
548}
549
550func (r *importReader) signature(recv *types.Var) *types.Signature {
551	params := r.paramList()
552	results := r.paramList()
553	variadic := params.Len() > 0 && r.bool()
554	return types.NewSignature(recv, params, results, variadic)
555}
556
557func (r *importReader) paramList() *types.Tuple {
558	xs := make([]*types.Var, r.uint64())
559	for i := range xs {
560		xs[i] = r.param()
561	}
562	return types.NewTuple(xs...)
563}
564
565func (r *importReader) param() *types.Var {
566	pos := r.pos()
567	name := r.ident()
568	typ := r.typ()
569	return types.NewParam(pos, r.currPkg, name, typ)
570}
571
572func (r *importReader) bool() bool {
573	return r.uint64() != 0
574}
575
576func (r *importReader) int64() int64 {
577	n, err := binary.ReadVarint(&r.declReader)
578	if err != nil {
579		errorf("readVarint: %v", err)
580	}
581	return n
582}
583
584func (r *importReader) uint64() uint64 {
585	n, err := binary.ReadUvarint(&r.declReader)
586	if err != nil {
587		errorf("readUvarint: %v", err)
588	}
589	return n
590}
591
592func (r *importReader) byte() byte {
593	x, err := r.declReader.ReadByte()
594	if err != nil {
595		errorf("declReader.ReadByte: %v", err)
596	}
597	return x
598}
599