// 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 {
	*bufio.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 
}

// Keep this in sync with constants in iexport.go.
const (
	iexportVersionGo1_11   = 0
	iexportVersionPosCol   = 1
	iexportVersionGenerics = 2
	iexportVersionGo1_18   = 2

	iexportVersionCurrent = 2
)

type ident struct {
	pkg  *types.Package
	name string
}

const predeclReserved = 32

type itag uint64

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

// 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,  *bufio.Reader,  string) ( *types.Package,  error) {
	const  = iexportVersionCurrent
	 := 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{, }

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

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

	if  > math.MaxUint64- {
		errorf("lengths out of range (%d, %d)", , )
	}

	,  := saferio.ReadData(, +)
	if  != nil {
		errorf("cannot read %d bytes of stringData and declData: %s", +, )
	}
	 := [:]
	 := [:]

	 := iimporter{
		exportVersion: ,
		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),
		// Separate map for typeparams, keyed by their package and unique
		// name (name with subscript).
		tparamIndex: make(map[ident]*types.TypeParam),

		fake: fakeFileSet{
			fset:  ,
			files: make(map[string]*fileInfo),
		},
	}
	defer .fake.setLines() // set lines for files in fset

	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(, )
	}

	// SetConstraint can't be called if the constraint type is not yet complete.
	// When type params are created in the 'P' case of (*importReader).obj(),
	// the associated constraint type may not be complete due to recursion.
	// Therefore, we defer calling SetConstraint there, and call it here instead
	// after all types are complete.
	for ,  := range .later {
		.t.SetConstraint(.constraint)
	}

	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()
	return , nil
}

type setConstraintArgs struct {
	t          *types.TypeParam
	constraint types.Type
}

type iimporter struct {
	exportVersion int64
	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
	tparamIndex map[ident]*types.TypeParam

	fake          fakeFileSet
	interfaceList []*types.Interface

	// Arguments for calls to SetConstraint that are deferred due to recursive types
	later []setConstraintArgs
}

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[];  && canReuse(, ) {
		return 
	}

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

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

	if canReuse(, ) {
		.typCache[] = 
	}
	return 
}

// canReuse reports whether the type rhs on the RHS of the declaration for def
// may be re-used.
//
// Specifically, if def is non-nil and rhs is an interface type with methods, it
// may not be re-used because we have a convention of setting the receiver type
// for interface methods to def.
func canReuse( *types.Named,  types.Type) bool {
	if  == nil {
		return true
	}
	,  := .(*types.Interface)
	if  == nil {
		return true
	}
	// Don't use iface.Empty() here as iface may not be complete.
	return .NumEmbeddeds() == 0 && .NumExplicitMethods() == 0
}

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', 'G':
		var  []*types.TypeParam
		if  == 'G' {
			 = .tparamList()
		}
		 := .signature(nil, nil, )
		.declare(types.NewFunc(, .currPkg, , ))

	case 'T', 'U':
		// Types can be recursive. We need to setup a stub
		// declaration before recurring.
		 := types.NewTypeName(, .currPkg, , nil)
		 := types.NewNamed(, nil, nil)
		// Declare obj before calling r.tparamList, so the new type name is recognized
		// if used in the constraint of one of its own typeparams (see #48280).
		.declare()
		if  == 'U' {
			 := .tparamList()
			.SetTypeParams()
		}

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

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

				// If the receiver has any targs, set those as the
				// rparams of the method (since those are the
				// typeparams being used in the method sig/body).
				 := baseType(.Type()).TypeArgs()
				var  []*types.TypeParam
				if .Len() > 0 {
					 = make([]*types.TypeParam, .Len())
					for  := range  {
						[], _ = .At().(*types.TypeParam)
					}
				}
				 := .signature(, , nil)

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

	case 'P':
		// We need to "declare" a typeparam in order to have a name that
		// can be referenced recursively (if needed) in the type param's
		// bound.
		if .p.exportVersion < iexportVersionGenerics {
			errorf("unexpected type param type")
		}
		// Remove the "path" from the type param name that makes it unique,
		// and revert any unique name used for blank typeparams.
		 := tparamName()
		 := types.NewTypeName(, .currPkg, , nil)
		 := types.NewTypeParam(, nil)
		// To handle recursive references to the typeparam within its
		// bound, save the partial type in tparamIndex before reading the bounds.
		 := ident{.currPkg, }
		.p.tparamIndex[] = 

		var  bool
		if .p.exportVersion >= iexportVersionGo1_18 {
			 = .bool()
		}
		 := .typ()
		if  {
			,  := .(*types.Interface)
			if  == nil {
				errorf("non-interface constraint marked implicit")
			}
			.MarkImplicit()
		}
		// The constraint type may not be complete, if we
		// are in the middle of a type recursion involving type
		// constraints. So, we defer SetConstraint until we have
		// completely set up all types in ImportData.
		.p.later = append(.p.later, setConstraintArgs{t: , constraint: })

	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()
	if .p.exportVersion >= iexportVersionGo1_18 {
		// TODO: add support for using the kind
		_ = constant.Kind(.int64())
	}

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

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

	case types.IsInteger:
		var  big.Int
		.mpint(&, )
		 = constant.Make(&)

	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) ( *big.Int,  *types.Basic) {
	,  := intSize()

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

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

	 := -
	if  {
		 = -( &^ 1) >> 1
	}
	if  < 1 || uint() >  {
		errorf("weird decoding: %v, %v => %v", , , )
	}
	 := make([]byte, )
	io.ReadFull(&.declReader, )
	.SetBytes()
	if  && &1 != 0 {
		.Neg()
	}
}

func ( *importReader) ( *types.Basic) constant.Value {
	var  big.Int
	.mpint(&, )
	var  big.Float
	.SetInt(&)
	if .Sign() != 0 {
		.SetMantExp(&, int(.int64()))
	}
	return constant.Make(&)
}

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, nil, 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(, nil, nil)
			[] = types.NewFunc(, .currPkg, , )
		}

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

	case typeParamType:
		if .p.exportVersion < iexportVersionGenerics {
			errorf("unexpected type param type")
		}
		,  := .qualifiedIdent()
		 := ident{, }
		if ,  := .p.tparamIndex[];  {
			// We're already in the process of importing this typeparam.
			return 
		}
		// Otherwise, import the definition of the typeparam now.
		.p.doDecl(, )
		return .p.tparamIndex[]

	case instanceType:
		if .p.exportVersion < iexportVersionGenerics {
			errorf("unexpected instantiation type")
		}
		// pos does not matter for instances: they are positioned on the original
		// type.
		_ = .pos()
		 := .uint64()
		 := make([]types.Type, )
		for  := range  {
			[] = .typ()
		}
		 := .typ()
		// The imported instantiated type doesn't include any methods, so
		// we must always use the methods of the base (orig) type.
		// TODO provide a non-nil *Context
		,  := types.Instantiate(nil, , , false)
		return 

	case unionType:
		if .p.exportVersion < iexportVersionGenerics {
			errorf("unexpected instantiation type")
		}
		 := make([]*types.Term, .uint64())
		for  := range  {
			[] = types.NewTerm(.bool(), .typ())
		}
		return types.NewUnion()
	}
}

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

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

func ( *importReader) () []*types.TypeParam {
	 := .uint64()
	if  == 0 {
		return nil
	}
	 := make([]*types.TypeParam, )
	for  := range  {
		[], _ = .typ().(*types.TypeParam)
	}
	return 
}

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 
}

func baseType( types.Type) *types.Named {
	// pointer receivers are never types.Named types
	if ,  := .(*types.Pointer);  != nil {
		 = .Elem()
	}
	// receiver base types are always (possibly generic) types.Named types
	,  := .(*types.Named)
	return 
}

const blankMarker = "$"

// tparamName returns the real name of a type parameter, after stripping its
// qualifying prefix and reverting blank-name encoding. See tparamExportName
// for details.
func tparamName( string) string {
	// Remove the "path" from the type param name that makes it unique.
	 := strings.LastIndex(, ".")
	if  < 0 {
		errorf("malformed type parameter export name %s: missing prefix", )
	}
	 := [+1:]
	if strings.HasPrefix(, blankMarker) {
		return "_"
	}
	return 
}