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

// Binary package export.
// This file was derived from $GOROOT/src/cmd/compile/internal/gc/bexport.go;
// see that file for specification of the format.

package gcimporter

import (
	
	
	
	
	
	
	
	
	
	
)

// If debugFormat is set, each integer and string value is preceded by a marker
// and position information in the encoding. This mechanism permits an importer
// to recognize immediately when it is out of sync. The importer recognizes this
// mode automatically (i.e., it can import export data produced with debugging
// support even if debugFormat is not set at the time of import). This mode will
// lead to massively larger export data (by a factor of 2 to 3) and should only
// be enabled during development and debugging.
//
// NOTE: This flag is the first flag to enable if importing dies because of
// (suspected) format errors, and whenever a change is made to the format.
const debugFormat = false // default: false

// Current export format version. Increase with each format change.
//
// Note: The latest binary (non-indexed) export format is at version 6.
// This exporter is still at level 4, but it doesn't matter since
// the binary importer can handle older versions just fine.
//
//	6: package height (CL 105038) -- NOT IMPLEMENTED HERE
//	5: improved position encoding efficiency (issue 20080, CL 41619) -- NOT IMPLEMENTED HERE
//	4: type name objects support type aliases, uses aliasTag
//	3: Go1.8 encoding (same as version 2, aliasTag defined but never used)
//	2: removed unused bool in ODCL export (compiler only)
//	1: header format change (more regular), export package for _ struct fields
//	0: Go1.7 encoding
const exportVersion = 4

// trackAllTypes enables cycle tracking for all types, not just named
// types. The existing compiler invariants assume that unnamed types
// that are not completely set up are not used, or else there are spurious
// errors.
// If disabled, only named types are tracked, possibly leading to slightly
// less efficient encoding in rare cases. It also prevents the export of
// some corner-case type declarations (but those are not handled correctly
// with with the textual export format either).
// TODO(gri) enable and remove once issues caused by it are fixed
const trackAllTypes = false

type exporter struct {
	fset *token.FileSet
	out  bytes.Buffer

	// object -> index maps, indexed in order of serialization
	strIndex map[string]int
	pkgIndex map[*types.Package]int
	typIndex map[types.Type]int

	// position encoding
	posInfoFormat bool
	prevFile      string
	prevLine      int

	// debugging support
	written int // bytes written
	indent  int // for trace
}

// internalError represents an error generated inside this package.
type internalError string

func ( internalError) () string { return "gcimporter: " + string() }

func ( string,  ...interface{}) error {
	return internalError(fmt.Sprintf(, ...))
}

// BExportData returns binary export data for pkg.
// If no file set is provided, position info will be missing.
func ( *token.FileSet,  *types.Package) ( []byte,  error) {
	if !debug {
		defer func() {
			if  := recover();  != nil {
				if ,  := .(internalError);  {
					 = 
					return
				}
				// Not an internal error; panic again.
				panic()
			}
		}()
	}

	 := exporter{
		fset:          ,
		strIndex:      map[string]int{"": 0}, // empty string is mapped to 0
		pkgIndex:      make(map[*types.Package]int),
		typIndex:      make(map[types.Type]int),
		posInfoFormat: true, // TODO(gri) might become a flag, eventually
	}

	// write version info
	// The version string must start with "version %d" where %d is the version
	// number. Additional debugging information may follow after a blank; that
	// text is ignored by the importer.
	.rawStringln(fmt.Sprintf("version %d", exportVersion))
	var  string
	if debugFormat {
		 = "debug"
	}
	.rawStringln() // cannot use p.bool since it's affected by debugFormat; also want to see this clearly
	.bool(trackAllTypes)
	.bool(.posInfoFormat)

	// --- generic export data ---

	// populate type map with predeclared "known" types
	for ,  := range predeclared() {
		.typIndex[] = 
	}
	if len(.typIndex) != len(predeclared()) {
		return nil, internalError("duplicate entries in type map?")
	}

	// write package data
	.pkg(, true)
	if trace {
		.tracef("\n")
	}

	// write objects
	 := 0
	 := .Scope()
	for ,  := range .Names() {
		if !token.IsExported() {
			continue
		}
		if trace {
			.tracef("\n")
		}
		.obj(.Lookup())
		++
	}

	// indicate end of list
	if trace {
		.tracef("\n")
	}
	.tag(endTag)

	// for self-verification only (redundant)
	.int()

	if trace {
		.tracef("\n")
	}

	// --- end of export data ---

	return .out.Bytes(), nil
}

func ( *exporter) ( *types.Package,  bool) {
	if  == nil {
		panic(internalError("unexpected nil pkg"))
	}

	// if we saw the package before, write its index (>= 0)
	if ,  := .pkgIndex[];  {
		.index('P', )
		return
	}

	// otherwise, remember the package, write the package tag (< 0) and package data
	if trace {
		.tracef("P%d = { ", len(.pkgIndex))
		defer .tracef("} ")
	}
	.pkgIndex[] = len(.pkgIndex)

	.tag(packageTag)
	.string(.Name())
	if  {
		.string("")
	} else {
		.string(.Path())
	}
}

func ( *exporter) ( types.Object) {
	switch obj := .(type) {
	case *types.Const:
		.tag(constTag)
		.pos()
		.qualifiedName()
		.typ(.Type())
		.value(.Val())

	case *types.TypeName:
		if .IsAlias() {
			.tag(aliasTag)
			.pos()
			.qualifiedName()
		} else {
			.tag(typeTag)
		}
		.typ(.Type())

	case *types.Var:
		.tag(varTag)
		.pos()
		.qualifiedName()
		.typ(.Type())

	case *types.Func:
		.tag(funcTag)
		.pos()
		.qualifiedName()
		 := .Type().(*types.Signature)
		.paramList(.Params(), .Variadic())
		.paramList(.Results(), false)

	default:
		panic(internalErrorf("unexpected object %v (%T)", , ))
	}
}

func ( *exporter) ( types.Object) {
	if !.posInfoFormat {
		return
	}

	,  := .fileLine()
	if  == .prevFile {
		// common case: write line delta
		// delta == 0 means different file or no line change
		 :=  - .prevLine
		.int()
		if  == 0 {
			.int(-1) // -1 means no file change
		}
	} else {
		// different file
		.int(0)
		// Encode filename as length of common prefix with previous
		// filename, followed by (possibly empty) suffix. Filenames
		// frequently share path prefixes, so this can save a lot
		// of space and make export data size less dependent on file
		// path length. The suffix is unlikely to be empty because
		// file names tend to end in ".go".
		 := commonPrefixLen(.prevFile, )
		.int()           // n >= 0
		.string([:]) // write suffix only
		.prevFile = 
		.int()
	}
	.prevLine = 
}

func ( *exporter) ( types.Object) ( string,  int) {
	if .fset != nil {
		 := .fset.Position(.Pos())
		 = .Filename
		 = .Line
	}
	return
}

func (,  string) int {
	if len() > len() {
		,  = , 
	}
	// len(a) <= len(b)
	 := 0
	for  < len() && [] == [] {
		++
	}
	return 
}

func ( *exporter) ( types.Object) {
	.string(.Name())
	.pkg(.Pkg(), false)
}

func ( *exporter) ( types.Type) {
	if  == nil {
		panic(internalError("nil type"))
	}

	// Possible optimization: Anonymous pointer types *T where
	// T is a named type are common. We could canonicalize all
	// such types *T to a single type PT = *T. This would lead
	// to at most one *T entry in typIndex, and all future *T's
	// would be encoded as the respective index directly. Would
	// save 1 byte (pointerTag) per *T and reduce the typIndex
	// size (at the cost of a canonicalization map). We can do
	// this later, without encoding format change.

	// if we saw the type before, write its index (>= 0)
	if ,  := .typIndex[];  {
		.index('T', )
		return
	}

	// otherwise, remember the type, write the type tag (< 0) and type data
	if trackAllTypes {
		if trace {
			.tracef("T%d = {>\n", len(.typIndex))
			defer .tracef("<\n} ")
		}
		.typIndex[] = len(.typIndex)
	}

	switch t := .(type) {
	case *types.Named:
		if !trackAllTypes {
			// if we don't track all types, track named types now
			.typIndex[] = len(.typIndex)
		}

		.tag(namedTag)
		.pos(.Obj())
		.qualifiedName(.Obj())
		.(.Underlying())
		if !types.IsInterface() {
			.assocMethods()
		}

	case *types.Array:
		.tag(arrayTag)
		.int64(.Len())
		.(.Elem())

	case *types.Slice:
		.tag(sliceTag)
		.(.Elem())

	case *dddSlice:
		.tag(dddTag)
		.(.elem)

	case *types.Struct:
		.tag(structTag)
		.fieldList()

	case *types.Pointer:
		.tag(pointerTag)
		.(.Elem())

	case *types.Signature:
		.tag(signatureTag)
		.paramList(.Params(), .Variadic())
		.paramList(.Results(), false)

	case *types.Interface:
		.tag(interfaceTag)
		.iface()

	case *types.Map:
		.tag(mapTag)
		.(.Key())
		.(.Elem())

	case *types.Chan:
		.tag(chanTag)
		.int(int(3 - .Dir())) // hack
		.(.Elem())

	default:
		panic(internalErrorf("unexpected type %T: %s", , ))
	}
}

func ( *exporter) ( *types.Named) {
	// Sort methods (for determinism).
	var  []*types.Func
	for  := 0;  < .NumMethods(); ++ {
		 = append(, .Method())
	}
	sort.Sort(methodsByName())

	.int(len())

	if trace &&  != nil {
		.tracef("associated methods {>\n")
	}

	for ,  := range  {
		if trace &&  > 0 {
			.tracef("\n")
		}

		.pos()
		 := .Name()
		.string()
		if !exported() {
			.pkg(.Pkg(), false)
		}

		 := .Type().(*types.Signature)
		.paramList(types.NewTuple(.Recv()), false)
		.paramList(.Params(), .Variadic())
		.paramList(.Results(), false)
		.int(0) // dummy value for go:nointerface pragma - ignored by importer
	}

	if trace &&  != nil {
		.tracef("<\n} ")
	}
}

type methodsByName []*types.Func

func ( methodsByName) () int           { return len() }
func ( methodsByName) (,  int)      { [], [] = [], [] }
func ( methodsByName) (,  int) bool { return [].Name() < [].Name() }

func ( *exporter) ( *types.Struct) {
	if trace && .NumFields() > 0 {
		.tracef("fields {>\n")
		defer .tracef("<\n} ")
	}

	.int(.NumFields())
	for  := 0;  < .NumFields(); ++ {
		if trace &&  > 0 {
			.tracef("\n")
		}
		.field(.Field())
		.string(.Tag())
	}
}

func ( *exporter) ( *types.Var) {
	if !.IsField() {
		panic(internalError("field expected"))
	}

	.pos()
	.fieldName()
	.typ(.Type())
}

func ( *exporter) ( *types.Interface) {
	// TODO(gri): enable importer to load embedded interfaces,
	// then emit Embeddeds and ExplicitMethods separately here.
	.int(0)

	 := .NumMethods()
	if trace &&  > 0 {
		.tracef("methods {>\n")
		defer .tracef("<\n} ")
	}
	.int()
	for  := 0;  < ; ++ {
		if trace &&  > 0 {
			.tracef("\n")
		}
		.method(.Method())
	}
}

func ( *exporter) ( *types.Func) {
	 := .Type().(*types.Signature)
	if .Recv() == nil {
		panic(internalError("method expected"))
	}

	.pos()
	.string(.Name())
	if .Name() != "_" && !token.IsExported(.Name()) {
		.pkg(.Pkg(), false)
	}

	// interface method; no need to encode receiver.
	.paramList(.Params(), .Variadic())
	.paramList(.Results(), false)
}

func ( *exporter) ( *types.Var) {
	 := .Name()

	if .Anonymous() {
		// anonymous field - we distinguish between 3 cases:
		// 1) field name matches base type name and is exported
		// 2) field name matches base type name and is not exported
		// 3) field name doesn't match base type name (alias name)
		 := basetypeName(.Type())
		if  ==  {
			if token.IsExported() {
				 = "" // 1) we don't need to know the field name or package
			} else {
				 = "?" // 2) use unexported name "?" to force package export
			}
		} else {
			// 3) indicate alias and export name as is
			// (this requires an extra "@" but this is a rare case)
			.string("@")
		}
	}

	.string()
	if  != "" && !token.IsExported() {
		.pkg(.Pkg(), false)
	}
}

func ( types.Type) string {
	switch typ := deref().(type) {
	case *types.Basic:
		return .Name()
	case *types.Named:
		return .Obj().Name()
	default:
		return "" // unnamed type
	}
}

func ( *exporter) ( *types.Tuple,  bool) {
	// use negative length to indicate unnamed parameters
	// (look at the first parameter only since either all
	// names are present or all are absent)
	 := .Len()
	if  > 0 && .At(0).Name() == "" {
		 = -
	}
	.int()
	for  := 0;  < .Len(); ++ {
		 := .At()
		 := .Type()
		if  &&  == .Len()-1 {
			 = &dddSlice{.(*types.Slice).Elem()}
		}
		.typ()
		if  > 0 {
			 := .Name()
			.string()
			if  != "_" {
				.pkg(.Pkg(), false)
			}
		}
		.string("") // no compiler-specific info
	}
}

func ( *exporter) ( constant.Value) {
	if trace {
		.tracef("= ")
	}

	switch .Kind() {
	case constant.Bool:
		 := falseTag
		if constant.BoolVal() {
			 = trueTag
		}
		.tag()

	case constant.Int:
		if ,  := constant.Int64Val();  {
			// common case: x fits into an int64 - use compact encoding
			.tag(int64Tag)
			.int64()
			return
		}
		// uncommon case: large x - use float encoding
		// (powers of 2 will be encoded efficiently with exponent)
		.tag(floatTag)
		.float(constant.ToFloat())

	case constant.Float:
		.tag(floatTag)
		.float()

	case constant.Complex:
		.tag(complexTag)
		.float(constant.Real())
		.float(constant.Imag())

	case constant.String:
		.tag(stringTag)
		.string(constant.StringVal())

	case constant.Unknown:
		// package contains type errors
		.tag(unknownTag)

	default:
		panic(internalErrorf("unexpected value %v (%T)", , ))
	}
}

func ( *exporter) ( constant.Value) {
	if .Kind() != constant.Float {
		panic(internalErrorf("unexpected constant %v, want float", ))
	}
	// extract sign (there is no -0)
	 := constant.Sign()
	if  == 0 {
		// x == 0
		.int(0)
		return
	}
	// x != 0

	var  big.Float
	if ,  := constant.Float64Val();  {
		// float64
		.SetFloat64()
	} else if ,  := constant.Num(), constant.Denom(); .Kind() == constant.Int {
		// TODO(gri): add big.Rat accessor to constant.Value.
		 := valueToRat()
		.SetRat(.Quo(, valueToRat()))
	} else {
		// Value too large to represent as a fraction => inaccessible.
		// TODO(gri): add big.Float accessor to constant.Value.
		.SetFloat64(math.MaxFloat64) // FIXME
	}

	// extract exponent such that 0.5 <= m < 1.0
	var  big.Float
	 := .MantExp(&)

	// extract mantissa as *big.Int
	// - set exponent large enough so mant satisfies mant.IsInt()
	// - get *big.Int from mant
	.SetMantExp(&, int(.MinPrec()))
	,  := .Int(nil)
	if  != big.Exact {
		panic(internalError("internal error"))
	}

	.int()
	.int()
	.string(string(.Bytes()))
}

func ( constant.Value) *big.Rat {
	// Convert little-endian to big-endian.
	// I can't believe this is necessary.
	 := constant.Bytes()
	for  := 0;  < len()/2; ++ {
		[], [len()-1-] = [len()-1-], []
	}
	return new(big.Rat).SetInt(new(big.Int).SetBytes())
}

func ( *exporter) ( bool) bool {
	if trace {
		.tracef("[")
		defer .tracef("= %v] ", )
	}

	 := 0
	if  {
		 = 1
	}
	.int()
	return 
}

// ----------------------------------------------------------------------------
// Low-level encoders

func ( *exporter) ( byte,  int) {
	if  < 0 {
		panic(internalError("invalid index < 0"))
	}
	if debugFormat {
		.marker('t')
	}
	if trace {
		.tracef("%c%d ", , )
	}
	.rawInt64(int64())
}

func ( *exporter) ( int) {
	if  >= 0 {
		panic(internalError("invalid tag >= 0"))
	}
	if debugFormat {
		.marker('t')
	}
	if trace {
		.tracef("%s ", tagString[-])
	}
	.rawInt64(int64())
}

func ( *exporter) ( int) {
	.int64(int64())
}

func ( *exporter) ( int64) {
	if debugFormat {
		.marker('i')
	}
	if trace {
		.tracef("%d ", )
	}
	.rawInt64()
}

func ( *exporter) ( string) {
	if debugFormat {
		.marker('s')
	}
	if trace {
		.tracef("%q ", )
	}
	// if we saw the string before, write its index (>= 0)
	// (the empty string is mapped to 0)
	if ,  := .strIndex[];  {
		.rawInt64(int64())
		return
	}
	// otherwise, remember string and write its negative length and bytes
	.strIndex[] = len(.strIndex)
	.rawInt64(-int64(len()))
	for  := 0;  < len(); ++ {
		.rawByte([])
	}
}

// marker emits a marker byte and position information which makes
// it easy for a reader to detect if it is "out of sync". Used for
// debugFormat format only.
func ( *exporter) ( byte) {
	.rawByte()
	// Enable this for help tracking down the location
	// of an incorrect marker when running in debugFormat.
	if false && trace {
		.tracef("#%d ", .written)
	}
	.rawInt64(int64(.written))
}

// rawInt64 should only be used by low-level encoders.
func ( *exporter) ( int64) {
	var  [binary.MaxVarintLen64]byte
	 := binary.PutVarint([:], )
	for  := 0;  < ; ++ {
		.rawByte([])
	}
}

// rawStringln should only be used to emit the initial version string.
func ( *exporter) ( string) {
	for  := 0;  < len(); ++ {
		.rawByte([])
	}
	.rawByte('\n')
}

// rawByte is the bottleneck interface to write to p.out.
// rawByte escapes b as follows (any encoding does that
// hides '$'):
//
//	'$'  => '|' 'S'
//	'|'  => '|' '|'
//
// Necessary so other tools can find the end of the
// export data by searching for "$$".
// rawByte should only be used by low-level encoders.
func ( *exporter) ( byte) {
	switch  {
	case '$':
		// write '$' as '|' 'S'
		 = 'S'
		fallthrough
	case '|':
		// write '|' as '|' '|'
		.out.WriteByte('|')
		.written++
	}
	.out.WriteByte()
	.written++
}

// tracef is like fmt.Printf but it rewrites the format string
// to take care of indentation.
func ( *exporter) ( string,  ...interface{}) {
	if strings.ContainsAny(, "<>\n") {
		var  bytes.Buffer
		for  := 0;  < len(); ++ {
			// no need to deal with runes
			 := []
			switch  {
			case '>':
				.indent++
				continue
			case '<':
				.indent--
				continue
			}
			.WriteByte()
			if  == '\n' {
				for  := .indent;  > 0; -- {
					.WriteString(".  ")
				}
			}
		}
		 = .String()
	}
	fmt.Printf(, ...)
}

// Debugging support.
// (tagString is only used when tracing is enabled)
var tagString = [...]string{
	// Packages
	-packageTag: "package",

	// Types
	-namedTag:     "named type",
	-arrayTag:     "array",
	-sliceTag:     "slice",
	-dddTag:       "ddd",
	-structTag:    "struct",
	-pointerTag:   "pointer",
	-signatureTag: "signature",
	-interfaceTag: "interface",
	-mapTag:       "map",
	-chanTag:      "chan",

	// Values
	-falseTag:    "false",
	-trueTag:     "true",
	-int64Tag:    "int64",
	-floatTag:    "float",
	-fractionTag: "fraction",
	-complexTag:  "complex",
	-stringTag:   "string",
	-unknownTag:  "unknown",

	// Type aliases
	-aliasTag: "alias",
}