package wasm

import (
	
	
	

	
	
)

// Table describes the limits of elements and its type in a table.
type Table struct {
	Min  uint32
	Max  *uint32
	Type RefType
}

// RefType is either RefTypeFuncref or RefTypeExternref as of WebAssembly core 2.0.
type RefType = byte

const (
	// RefTypeFuncref represents a reference to a function.
	RefTypeFuncref = ValueTypeFuncref
	// RefTypeExternref represents a reference to a host object, which is not currently supported in wazero.
	RefTypeExternref = ValueTypeExternref
)

func ( RefType) ( string) {
	switch  {
	case RefTypeFuncref:
		 = "funcref"
	case RefTypeExternref:
		 = "externref"
	default:
		 = fmt.Sprintf("unknown(0x%x)", )
	}
	return
}

// ElementMode represents a mode of element segment which is either active, passive or declarative.
//
// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/syntax/modules.html#element-segments
type ElementMode = byte

const (
	// ElementModeActive is the mode which requires the runtime to initialize table with the contents in .Init field combined with OffsetExpr.
	ElementModeActive ElementMode = iota
	// ElementModePassive is the mode which doesn't require the runtime to initialize table, and only used with OpcodeTableInitName.
	ElementModePassive
	// ElementModeDeclarative is introduced in reference-types proposal which can be used to declare function indexes used by OpcodeRefFunc.
	ElementModeDeclarative
)

// ElementSegment are initialization instructions for a TableInstance
//
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-elem
type ElementSegment struct {
	// OffsetExpr returns the table element offset to apply to Init indices.
	// Note: This can be validated prior to instantiation unless it includes OpcodeGlobalGet (an imported global).
	OffsetExpr ConstantExpression

	// TableIndex is the table's index to which this element segment is applied.
	// Note: This is used if and only if the Mode is active.
	TableIndex Index

	// Followings are set/used regardless of the Mode.

	// Init indices are (nullable) table elements where each index is the function index by which the module initialize the table.
	Init []Index

	// Type holds the type of this element segment, which is the RefType in WebAssembly 2.0.
	Type RefType

	// Mode is the mode of this element segment.
	Mode ElementMode
}

const (
	// ElementInitNullReference represents the null reference in ElementSegment's Init.
	// In Wasm spec, an init item represents either Function's Index or null reference,
	// and in wazero, we limit the maximum number of functions available in a module to
	// MaximumFunctionIndex. Therefore, it is safe to use 1 << 31 to represent the null
	// reference in Element segments.
	ElementInitNullReference Index = 1 << 31
	// elementInitImportedGlobalReferenceType represents an init item which is resolved via an imported global constexpr.
	// The actual function reference stored at Global is only known at instantiation-time, so we set this flag
	// to items of ElementSegment.Init at binary decoding, and unwrap this flag at instantiation to resolve the value.
	//
	// This might collide the init element resolved via ref.func instruction which is resolved with the func index at decoding,
	// but in practice, that is not allowed in wazero thanks to our limit MaximumFunctionIndex. Thus, it is safe to set this flag
	// in init element to indicate as such.
	elementInitImportedGlobalReferenceType Index = 1 << 30
)

// unwrapElementInitGlobalReference takes an item of the init vector of an ElementSegment,
// and returns the Global index if it is supposed to get generated from a global.
// ok is true if the given init item is as such.
func unwrapElementInitGlobalReference( Index) ( Index,  bool) {
	if &elementInitImportedGlobalReferenceType == elementInitImportedGlobalReferenceType {
		return  &^ elementInitImportedGlobalReferenceType, true
	}
	return , false
}

// WrapGlobalIndexAsElementInit wraps the given index as an init item which is resolved via an imported global value.
// See the comments on elementInitImportedGlobalReferenceType for more details.
func ( Index) Index {
	return  | elementInitImportedGlobalReferenceType
}

// IsActive returns true if the element segment is "active" mode which requires the runtime to initialize table
// with the contents in .Init field.
func ( *ElementSegment) () bool {
	return .Mode == ElementModeActive
}

// TableInstance represents a table of (RefTypeFuncref) elements in a module.
//
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#table-instances%E2%91%A0
type TableInstance struct {
	// References holds references whose type is either RefTypeFuncref or RefTypeExternref (unsupported).
	//
	// Currently, only function references are supported.
	References []Reference

	// Min is the minimum (function) elements in this table and cannot grow to accommodate ElementSegment.
	Min uint32

	// Max if present is the maximum (function) elements in this table, or nil if unbounded.
	Max *uint32

	// Type is either RefTypeFuncref or RefTypeExternRef.
	Type RefType

	// The following is only used when the table is exported.

	// involvingModuleInstances is a set of module instances which are involved in the table instance.
	// This is critical for safety purpose because once a table is imported, it can hold any reference to
	// any function in the owner and importing module instances. Therefore, these module instance,
	// transitively the compiled modules, must be alive as long as the table instance is alive.
	involvingModuleInstances []*ModuleInstance
	// involvingModuleInstancesMutex is a mutex to protect involvingModuleInstances.
	involvingModuleInstancesMutex sync.RWMutex
}

// ElementInstance represents an element instance in a module.
//
// See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/exec/runtime.html#element-instances
type ElementInstance = []Reference

// Reference is the runtime representation of RefType which is either RefTypeFuncref or RefTypeExternref.
type Reference = uintptr

// validateTable ensures any ElementSegment is valid. This caches results via Module.validatedActiveElementSegments.
// Note: limitsType are validated by decoders, so not re-validated here.
func ( *Module) ( api.CoreFeatures,  []Table,  uint32) error {
	if len() > int() {
		return fmt.Errorf("too many tables in a module: %d given with limit %d", len(), )
	}

	 := .ImportTableCount

	// Create bounds checks as these can err prior to instantiation
	 := .ImportFunctionCount + .SectionElementCount(SectionIDFunction)
	 := .ImportGlobalCount + .SectionElementCount(SectionIDGlobal)

	// Now, we have to figure out which table elements can be resolved before instantiation and also fail early if there
	// are any imported globals that are known to be invalid by their declarations.
	for  := range .ElementSection {
		 := &.ElementSection[]
		 := Index()
		 := uint32(len(.Init))

		// Any offset applied is to the element, not the function index: validate here if the funcidx is sound.
		for ,  := range .Init {
			if  == ElementInitNullReference {
				continue
			}
			,  := unwrapElementInitGlobalReference()
			if  {
				if  >=  {
					return fmt.Errorf("%s[%d].init[%d] global index %d out of range", SectionIDName(SectionIDElement), , , )
				}
			} else {
				if .Type == RefTypeExternref {
					return fmt.Errorf("%s[%d].init[%d] must be ref.null but was %d", SectionIDName(SectionIDElement), , , )
				}
				if  >=  {
					return fmt.Errorf("%s[%d].init[%d] func index %d out of range", SectionIDName(SectionIDElement), , , )
				}
			}
		}

		if .IsActive() {
			if len() <= int(.TableIndex) {
				return fmt.Errorf("unknown table %d as active element target", .TableIndex)
			}

			 := [.TableIndex]
			if .Type != .Type {
				return fmt.Errorf("element type mismatch: table has %s but element has %s",
					RefTypeName(.Type), RefTypeName(.Type),
				)
			}

			// global.get needs to be discovered during initialization
			 := .OffsetExpr.Opcode
			if  == OpcodeGlobalGet {
				, ,  := leb128.LoadUint32(.OffsetExpr.Data)
				if  != nil {
					return fmt.Errorf("%s[%d] couldn't read global.get parameter: %w", SectionIDName(SectionIDElement), , )
				} else if  = .verifyImportGlobalI32(SectionIDElement, , );  != nil {
					return 
				}
			} else if  == OpcodeI32Const {
				// Per https://github.com/WebAssembly/spec/blob/wg-1.0/test/core/elem.wast#L117 we must pass if imported
				// table has set its min=0. Per https://github.com/WebAssembly/spec/blob/wg-1.0/test/core/elem.wast#L142, we
				// have to do fail if module-defined min=0.
				if !.IsEnabled(api.CoreFeatureReferenceTypes) && .TableIndex >=  {
					// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
					, ,  := leb128.LoadInt32(.OffsetExpr.Data)
					if  != nil {
						return fmt.Errorf("%s[%d] couldn't read i32.const parameter: %w", SectionIDName(SectionIDElement), , )
					}
					 := Index()
					if  = checkSegmentBounds(.Min, uint64()+uint64(), );  != nil {
						return 
					}
				}
			} else {
				return fmt.Errorf("%s[%d] has an invalid const expression: %s", SectionIDName(SectionIDElement), , InstructionName())
			}
		}
	}
	return nil
}

// buildTable returns TableInstances if the module defines or imports a table.
//   - importedTables: returned as `tables` unmodified.
//   - importedGlobals: include all instantiated, imported globals.
//
// If the result `init` is non-nil, it is the `tableInit` parameter of Engine.NewModuleEngine.
//
// Note: An error is only possible when an ElementSegment.OffsetExpr is out of range of the TableInstance.Min.
func ( *ModuleInstance) ( *Module,  bool) ( error) {
	 := .ImportTableCount
	for  := range .TableSection {
		 := &.TableSection[]
		// The module defining the table is the one that sets its Min/Max etc.
		.Tables[] = &TableInstance{
			References: make([]Reference, .Min), Min: .Min, Max: .Max,
			Type: .Type,
		}
		++
	}

	if ! {
		for  := range .ElementSection { // Do not loop over the value since elementSegments is a slice of value.
			 := &.ElementSection[]
			 := .Tables[.TableIndex]
			var  uint32
			if .OffsetExpr.Opcode == OpcodeGlobalGet {
				// Ignore error as it's already validated.
				, ,  := leb128.LoadUint32(.OffsetExpr.Data)
				 := .Globals[]
				 = uint32(.Val)
			} else { // i32.const
				// Ignore error as it's already validated.
				, ,  := leb128.LoadInt32(.OffsetExpr.Data)
				 = uint32()
			}

			// Check to see if we are out-of-bounds
			 := uint64(len(.Init))
			if  = checkSegmentBounds(.Min, uint64()+, Index());  != nil {
				return
			}
		}
	}
	return
}

// checkSegmentBounds fails if the capacity needed for an ElementSegment.Init is larger than limitsType.Min
//
// WebAssembly 1.0 (20191205) doesn't forbid growing to accommodate element segments, and spectests are inconsistent.
// For example, the spectests enforce elements within Table limitsType.Min, but ignore Import.DescTable min. What this
// means is we have to delay offset checks on imported tables until we link to them.
// e.g. https://github.com/WebAssembly/spec/blob/wg-1.0/test/core/elem.wast#L117 wants pass on min=0 for import
// e.g. https://github.com/WebAssembly/spec/blob/wg-1.0/test/core/elem.wast#L142 wants fail on min=0 module-defined
func checkSegmentBounds( uint32,  uint64,  Index) error { // uint64 in case offset was set to -1
	if  > uint64() {
		return fmt.Errorf("%s[%d].init exceeds min table size", SectionIDName(SectionIDElement), )
	}
	return nil
}

func ( *Module) ( SectionID,  Index,  uint32) error {
	 := uint32(math.MaxUint32) // +1 == 0
	for  := range .ImportSection {
		 := &.ImportSection[]
		if .Type == ExternTypeGlobal {
			++
			if  ==  {
				if .DescGlobal.ValType != ValueTypeI32 {
					return fmt.Errorf("%s[%d] (global.get %d): import[%d].global.ValType != i32", SectionIDName(), , , )
				}
				return nil
			}
		}
	}
	return fmt.Errorf("%s[%d] (global.get %d): out of range of imported globals", SectionIDName(), , )
}

// Grow appends the `initialRef` by `delta` times into the References slice.
// Returns -1 if the operation is not valid, otherwise the old length of the table.
//
// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/exec/instructions.html#xref-syntax-instructions-syntax-instr-table-mathsf-table-grow-x
func ( *TableInstance) ( uint32,  Reference) ( uint32) {
	 = uint32(len(.References))
	if  == 0 {
		return
	}

	if  := int64() + int64(); // adding as 64bit ints to avoid overflow.
	 >= math.MaxUint32 || (.Max != nil &&  > int64(*.Max)) {
		return 0xffffffff // = -1 in signed 32-bit integer.
	}

	.References = append(.References, make([]uintptr, )...)
	if  == 0 {
		return
	}

	// Uses the copy trick for faster filling the new region with the initial value.
	// https://github.com/golang/go/blob/go1.24.0/src/slices/slices.go#L514-L517
	 := .References[:]
	[0] = 
	for  := 1;  < len();  *= 2 {
		copy([:], [:])
	}
	return
}