// Copyright 2018 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.

// Indexed package import.
// See cmd/compile/internal/gc/iexport.go for the export data format.

package gcimporter

import (
	
	
	
	
	
	
	
	
)

type intReader struct {
	*bytes.Reader
	path string
}

func ( *intReader) () int64 {
	,  := binary.ReadVarint(.Reader)
	if  != nil {
		errorf("import %q: read varint error: %v", .path, )
	}
	return 
}

func ( *intReader) () uint64 {
	,  := binary.ReadUvarint(.Reader)
	if  != nil {
		errorf("import %q: read varint error: %v", .path, )
	}
	return 
}

const predeclReserved = 32

type itag uint64

const (
	// Types
	definedType itag = iota
	pointerType
	sliceType
	arrayType
	chanType
	mapType
	signatureType
	structType
	interfaceType
)

// iImportData imports a package from the serialized package data
// and returns the number of bytes consumed and a reference to the package.
// If the export data version is not recognized or the format is otherwise
// compromised, an error is returned.
func iImportData( *token.FileSet,  map[string]*types.Package,  []byte,  string) ( int,  *types.Package,  error) {
	const  = 1
	 := int64(-1)
	defer func() {
		if  := recover();  != nil {
			if  >  {
				 = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", , )
			} else {
				 = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", , )
			}
		}
	}()

	 := &intReader{bytes.NewReader(), }

	 = int64(.uint64())
	switch  {
	case , 0:
	default:
		errorf("unknown iexport format version %d", )
	}

	 := int64(.uint64())
	 := int64(.uint64())

	,  := .Seek(0, io.SeekCurrent)
	 := [ : +]
	 := [+ : ++]
	.Seek(+, io.SeekCurrent)

	 := iimporter{
		ipath:   ,
		version: int(),

		stringData:  ,
		stringCache: make(map[uint64]string),
		pkgCache:    make(map[uint64]*types.Package),

		declData: ,
		pkgIndex: make(map[*types.Package]map[string]uint64),
		typCache: make(map[uint64]types.Type),

		fake: fakeFileSet{
			fset:  ,
			files: make(map[string]*token.File),
		},
	}

	for ,  := range predeclared {
		.typCache[uint64()] = 
	}

	 := make([]*types.Package, .uint64())
	for  := range  {
		 := .uint64()
		 := .stringAt()
		 := .stringAt(.uint64())
		_ = .uint64() // package height; unused by go/types

		if  == "" {
			 = 
		}
		 := []
		if  == nil {
			 = types.NewPackage(, )
			[] = 
		} else if .Name() !=  {
			errorf("conflicting names %s and %s for package %q", .Name(), , )
		}

		.pkgCache[] = 

		 := make(map[string]uint64)
		for  := .uint64();  > 0; -- {
			 := .stringAt(.uint64())
			[] = .uint64()
		}

		.pkgIndex[] = 
		[] = 
	}

	 := [0]

	 := make([]string, 0, len(.pkgIndex[]))
	for  := range .pkgIndex[] {
		 = append(, )
	}
	sort.Strings()
	for ,  := range  {
		.doDecl(, )
	}

	for ,  := range .interfaceList {
		.Complete()
	}

	// record all referenced packages as imports
	 := append(([]*types.Package)(nil), [1:]...)
	sort.Sort(byPath())
	.SetImports()

	// package was imported completely and without errors
	.MarkComplete()

	,  := .Seek(0, io.SeekCurrent)
	return int(), , nil
}

type iimporter struct {
	ipath   string
	version int

	stringData  []byte
	stringCache map[uint64]string
	pkgCache    map[uint64]*types.Package

	declData []byte
	pkgIndex map[*types.Package]map[string]uint64
	typCache map[uint64]types.Type

	fake          fakeFileSet
	interfaceList []*types.Interface
}

func ( *iimporter) ( *types.Package,  string) {
	// See if we've already imported this declaration.
	if  := .Scope().Lookup();  != nil {
		return
	}

	,  := .pkgIndex[][]
	if ! {
		errorf("%v.%v not in index", , )
	}

	 := &importReader{p: , currPkg: }
	.declReader.Reset(.declData[:])

	.obj()
}

func ( *iimporter) ( uint64) string {
	if ,  := .stringCache[];  {
		return 
	}

	,  := binary.Uvarint(.stringData[:])
	if  <= 0 {
		errorf("varint failed")
	}
	 :=  + uint64()
	 := string(.stringData[ : +])
	.stringCache[] = 
	return 
}

func ( *iimporter) ( uint64) *types.Package {
	if ,  := .pkgCache[];  {
		return 
	}
	 := .stringAt()
	errorf("missing package %q in %q", , .ipath)
	return nil
}

func ( *iimporter) ( uint64,  *types.Named) types.Type {
	if ,  := .typCache[];  && ( == nil || !isInterface()) {
		return 
	}

	if  < predeclReserved {
		errorf("predeclared type missing from cache: %v", )
	}

	 := &importReader{p: }
	.declReader.Reset(.declData[-predeclReserved:])
	 := .doType()

	if  == nil || !isInterface() {
		.typCache[] = 
	}
	return 
}

type importReader struct {
	p          *iimporter
	declReader bytes.Reader
	currPkg    *types.Package
	prevFile   string
	prevLine   int64
	prevColumn int64
}

func ( *importReader) ( string) {
	 := .byte()
	 := .pos()

	switch  {
	case 'A':
		 := .typ()

		.declare(types.NewTypeName(, .currPkg, , ))

	case 'C':
		,  := .value()

		.declare(types.NewConst(, .currPkg, , , ))

	case 'F':
		 := .signature(nil)

		.declare(types.NewFunc(, .currPkg, , ))

	case 'T':
		// Types can be recursive. We need to setup a stub
		// declaration before recursing.
		 := types.NewTypeName(, .currPkg, , nil)
		 := types.NewNamed(, nil, nil)
		.declare()

		 := .p.typAt(.uint64(), ).Underlying()
		.SetUnderlying()

		if !isInterface() {
			for  := .uint64();  > 0; -- {
				 := .pos()
				 := .ident()
				 := .param()
				 := .signature()

				.AddMethod(types.NewFunc(, .currPkg, , ))
			}
		}

	case 'V':
		 := .typ()

		.declare(types.NewVar(, .currPkg, , ))

	default:
		errorf("unexpected tag: %v", )
	}
}

func ( *importReader) ( types.Object) {
	.Pkg().Scope().Insert()
}

func ( *importReader) () ( types.Type,  constant.Value) {
	 = .typ()

	switch  := .Underlying().(*types.Basic); .Info() & types.IsConstType {
	case types.IsBoolean:
		 = constant.MakeBool(.bool())

	case types.IsString:
		 = constant.MakeString(.string())

	case types.IsInteger:
		 = .mpint()

	case types.IsFloat:
		 = .mpfloat()

	case types.IsComplex:
		 := .mpfloat()
		 := .mpfloat()
		 = constant.BinaryOp(, token.ADD, constant.MakeImag())

	default:
		errorf("unexpected type %v", ) // panics
		panic("unreachable")
	}

	return
}

func intSize( *types.Basic) ( bool,  uint) {
	if (.Info() & types.IsUntyped) != 0 {
		return true, 64
	}

	switch .Kind() {
	case types.Float32, types.Complex64:
		return true, 3
	case types.Float64, types.Complex128:
		return true, 7
	}

	 = (.Info() & types.IsUnsigned) == 0
	switch .Kind() {
	case types.Int8, types.Uint8:
		 = 1
	case types.Int16, types.Uint16:
		 = 2
	case types.Int32, types.Uint32:
		 = 4
	default:
		 = 8
	}

	return
}

func ( *importReader) ( *types.Basic) constant.Value {
	,  := intSize()

	 := 256 - 
	if  {
		 = 256 - 2*
	}
	if  == 1 {
		 = 256
	}

	,  := .declReader.ReadByte()
	if uint() <  {
		 := int64()
		if  {
			 >>= 1
			if &1 != 0 {
				 = ^
			}
		}
		return constant.MakeInt64()
	}

	 := -
	if  {
		 = -( &^ 1) >> 1
	}
	if  < 1 || uint() >  {
		errorf("weird decoding: %v, %v => %v", , , )
	}

	 := make([]byte, )
	io.ReadFull(&.declReader, )

	// convert to little endian
	// TODO(gri) go/constant should have a more direct conversion function
	//           (e.g., once it supports a big.Float based implementation)
	for ,  := 0, len()-1;  < ; ,  = +1, -1 {
		[], [] = [], []
	}

	 := constant.MakeFromBytes()
	if  && &1 != 0 {
		 = constant.UnaryOp(token.SUB, , 0)
	}
	return 
}

func ( *importReader) ( *types.Basic) constant.Value {
	 := .mpint()
	if constant.Sign() == 0 {
		return 
	}

	 := .int64()
	switch {
	case  > 0:
		 = constant.Shift(, token.SHL, uint())
	case  < 0:
		 := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-))
		 = constant.BinaryOp(, token.QUO, )
	}
	return 
}

func ( *importReader) () string {
	return .string()
}

func ( *importReader) () (*types.Package, string) {
	 := .string()
	 := .pkg()
	return , 
}

func ( *importReader) () token.Pos {
	if .p.version >= 1 {
		.posv1()
	} else {
		.posv0()
	}

	if .prevFile == "" && .prevLine == 0 && .prevColumn == 0 {
		return token.NoPos
	}
	return .p.fake.pos(.prevFile, int(.prevLine), int(.prevColumn))
}

func ( *importReader) () {
	 := .int64()
	if  != deltaNewFile {
		.prevLine += 
	} else if  := .int64();  == -1 {
		.prevLine += deltaNewFile
	} else {
		.prevFile = .string()
		.prevLine = 
	}
}

func ( *importReader) () {
	 := .int64()
	.prevColumn +=  >> 1
	if &1 != 0 {
		 = .int64()
		.prevLine +=  >> 1
		if &1 != 0 {
			.prevFile = .string()
		}
	}
}

func ( *importReader) () types.Type {
	return .p.typAt(.uint64(), nil)
}

func isInterface( types.Type) bool {
	,  := .(*types.Interface)
	return 
}

func ( *importReader) () *types.Package { return .p.pkgAt(.uint64()) }
func ( *importReader) () string      { return .p.stringAt(.uint64()) }

func ( *importReader) ( *types.Named) types.Type {
	switch  := .kind();  {
	default:
		errorf("unexpected kind tag in %q: %v", .p.ipath, )
		return nil

	case definedType:
		,  := .qualifiedIdent()
		.p.doDecl(, )
		return .Scope().Lookup().(*types.TypeName).Type()
	case pointerType:
		return types.NewPointer(.typ())
	case sliceType:
		return types.NewSlice(.typ())
	case arrayType:
		 := .uint64()
		return types.NewArray(.typ(), int64())
	case chanType:
		 := chanDir(int(.uint64()))
		return types.NewChan(, .typ())
	case mapType:
		return types.NewMap(.typ(), .typ())
	case signatureType:
		.currPkg = .pkg()
		return .signature(nil)

	case structType:
		.currPkg = .pkg()

		 := make([]*types.Var, .uint64())
		 := make([]string, len())
		for  := range  {
			 := .pos()
			 := .ident()
			 := .typ()
			 := .bool()
			 := .string()

			[] = types.NewField(, .currPkg, , , )
			[] = 
		}
		return types.NewStruct(, )

	case interfaceType:
		.currPkg = .pkg()

		 := make([]types.Type, .uint64())
		for  := range  {
			_ = .pos()
			[] = .typ()
		}

		 := make([]*types.Func, .uint64())
		for  := range  {
			 := .pos()
			 := .ident()

			// TODO(mdempsky): Matches bimport.go, but I
			// don't agree with this.
			var  *types.Var
			if  != nil {
				 = types.NewVar(token.NoPos, .currPkg, "", )
			}

			 := .signature()
			[] = types.NewFunc(, .currPkg, , )
		}

		 := types.NewInterfaceType(, )
		.p.interfaceList = append(.p.interfaceList, )
		return 
	}
}

func ( *importReader) () itag {
	return itag(.uint64())
}

func ( *importReader) ( *types.Var) *types.Signature {
	 := .paramList()
	 := .paramList()
	 := .Len() > 0 && .bool()
	return types.NewSignature(, , , )
}

func ( *importReader) () *types.Tuple {
	 := make([]*types.Var, .uint64())
	for  := range  {
		[] = .param()
	}
	return types.NewTuple(...)
}

func ( *importReader) () *types.Var {
	 := .pos()
	 := .ident()
	 := .typ()
	return types.NewParam(, .currPkg, , )
}

func ( *importReader) () bool {
	return .uint64() != 0
}

func ( *importReader) () int64 {
	,  := binary.ReadVarint(&.declReader)
	if  != nil {
		errorf("readVarint: %v", )
	}
	return 
}

func ( *importReader) () uint64 {
	,  := binary.ReadUvarint(&.declReader)
	if  != nil {
		errorf("readUvarint: %v", )
	}
	return 
}

func ( *importReader) () byte {
	,  := .declReader.ReadByte()
	if  != nil {
		errorf("declReader.ReadByte: %v", )
	}
	return 
}