package wasm

import (
	
	
	
	
	
	

	
	
	
	
	
	internalsys 
	
)

// nameToModuleShrinkThreshold is the size the nameToModule map can grow to
// before it starts to be monitored for shrinking.
// The capacity will never be smaller than this once the threshold is met.
const nameToModuleShrinkThreshold = 100

type (
	// Store is the runtime representation of "instantiated" Wasm module and objects.
	// Multiple modules can be instantiated within a single store, and each instance,
	// (e.g. function instance) can be referenced by other module instances in a Store via Module.ImportSection.
	//
	// Every type whose name ends with "Instance" suffix belongs to exactly one store.
	//
	// Note that store is not thread (concurrency) safe, meaning that using single Store
	// via multiple goroutines might result in race conditions. In that case, the invocation
	// and access to any methods and field of Store must be guarded by mutex.
	//
	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#store%E2%91%A0
	Store struct {
		// moduleList ensures modules are closed in reverse initialization order.
		moduleList *ModuleInstance // guarded by mux

		// nameToModule holds the instantiated Wasm modules by module name from Instantiate.
		// It ensures no race conditions instantiating two modules of the same name.
		nameToModule map[string]*ModuleInstance // guarded by mux

		// nameToModuleCap tracks the growth of the nameToModule map in order to
		// track when to shrink it.
		nameToModuleCap int // guarded by mux

		// EnabledFeatures are read-only to allow optimizations.
		EnabledFeatures api.CoreFeatures

		// Engine is a global context for a Store which is in responsible for compilation and execution of Wasm modules.
		Engine Engine

		// typeIDs maps each FunctionType.String() to a unique FunctionTypeID. This is used at runtime to
		// do type-checks on indirect function calls.
		typeIDs map[string]FunctionTypeID

		// functionMaxTypes represents the limit on the number of function types in a store.
		// Note: this is fixed to 2^27 but have this a field for testability.
		functionMaxTypes uint32

		// mux is used to guard the fields from concurrent access.
		mux sync.RWMutex
	}

	// ModuleInstance represents instantiated wasm module.
	// The difference from the spec is that in wazero, a ModuleInstance holds pointers
	// to the instances, rather than "addresses" (i.e. index to Store.Functions, Globals, etc) for convenience.
	//
	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-moduleinst
	//
	// This implements api.Module.
	ModuleInstance struct {
		internalapi.WazeroOnlyType

		ModuleName     string
		Exports        map[string]*Export
		Globals        []*GlobalInstance
		MemoryInstance *MemoryInstance
		Tables         []*TableInstance

		// Engine implements function calls for this module.
		Engine ModuleEngine

		// TypeIDs is index-correlated with types and holds typeIDs which is uniquely assigned to a type by store.
		// This is necessary to achieve fast runtime type checking for indirect function calls at runtime.
		TypeIDs []FunctionTypeID

		// DataInstances holds data segments bytes of the module.
		// This is only used by bulk memory operations.
		//
		// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/exec/runtime.html#data-instances
		DataInstances []DataInstance

		// ElementInstances holds the element instance, and each holds the references to either functions
		// or external objects (unimplemented).
		ElementInstances []ElementInstance

		// Sys is exposed for use in special imports such as WASI, assemblyscript.
		//
		// # Notes
		//
		//   - This is a part of ModuleInstance so that scope and Close is coherent.
		//   - This is not exposed outside this repository (as a host function
		//	  parameter) because we haven't thought through capabilities based
		//	  security implications.
		Sys *internalsys.Context

		// Closed is used both to guard moduleEngine.CloseWithExitCode and to store the exit code.
		//
		// The update value is closedType + exitCode << 32. This ensures an exit code of zero isn't mistaken for never closed.
		//
		// Note: Exclusively reading and updating this with atomics guarantees cross-goroutine observations.
		// See /RATIONALE.md
		Closed atomic.Uint64

		// CodeCloser is non-nil when the code should be closed after this module.
		CodeCloser api.Closer

		// s is the Store on which this module is instantiated.
		s *Store
		// prev and next hold the nodes in the linked list of ModuleInstance held by Store.
		prev, next *ModuleInstance
		// Source is a pointer to the Module from which this ModuleInstance derives.
		Source *Module

		// CloseNotifier is an experimental hook called once on close.
		CloseNotifier experimental.CloseNotifier
	}

	// DataInstance holds bytes corresponding to the data segment in a module.
	//
	// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/exec/runtime.html#data-instances
	DataInstance = []byte

	// GlobalInstance represents a global instance in a store.
	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#global-instances%E2%91%A0
	GlobalInstance struct {
		Type GlobalType
		// Val holds a 64-bit representation of the actual value.
		// If me is non-nil, the value will not be updated and the current value is stored in the module engine.
		Val uint64
		// ValHi is only used for vector type globals, and holds the higher bits of the vector.
		// If me is non-nil, the value will not be updated and the current value is stored in the module engine.
		ValHi uint64
		// Me is the module engine that owns this global instance.
		// The .Val and .ValHi fields are only valid when me is nil.
		// If me is non-nil, the value is stored in the module engine.
		Me    ModuleEngine
		Index Index
	}

	// FunctionTypeID is a uniquely assigned integer for a function type.
	// This is wazero specific runtime object and specific to a store,
	// and used at runtime to do type-checks on indirect function calls.
	FunctionTypeID uint32
)

// The wazero specific limitations described at RATIONALE.md.
const maximumFunctionTypes = 1 << 27

// GetFunctionTypeID is used by emscripten.
func ( *ModuleInstance) ( *FunctionType) FunctionTypeID {
	,  := .s.GetFunctionTypeID()
	if  != nil {
		// This is not recoverable in practice since the only error GetFunctionTypeID returns is
		// when there's too many function types in the store.
		panic()
	}
	return 
}

func ( *ModuleInstance) ( []ElementSegment) {
	.ElementInstances = make([][]Reference, len())
	for ,  := range  {
		if .Type == RefTypeFuncref && .Mode == ElementModePassive {
			// Only passive elements can be access as element instances.
			// See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/syntax/modules.html#element-segments
			 := .Init
			 := make([]Reference, len())
			.ElementInstances[] = 
			for ,  := range  {
				if ,  := unwrapElementInitGlobalReference();  {
					 := .Globals[]
					[] = Reference(.Val)
				} else {
					if  != ElementInitNullReference {
						[] = .Engine.FunctionInstanceReference()
					}
				}
			}
		}
	}
}

func ( *ModuleInstance) ( []ElementSegment) {
	for  := range  {
		 := &[]
		if !.IsActive() ||
			// Per https://github.com/WebAssembly/spec/issues/1427 init can be no-op.
			len(.Init) == 0 {
			continue
		}
		var  uint32
		if .OffsetExpr.Opcode == OpcodeGlobalGet {
			// Ignore error as it's already validated.
			, ,  := leb128.LoadUint32(.OffsetExpr.Data)
			 := .Globals[]
			 = uint32(.Val)
		} else {
			// Ignore error as it's already validated.
			, ,  := leb128.LoadInt32(.OffsetExpr.Data)
			 = uint32()
		}

		 := .Tables[.TableIndex]
		 := .References
		if int()+len(.Init) > len() {
			// ErrElementOffsetOutOfBounds is the error raised when the active element offset exceeds the table length.
			// Before CoreFeatureReferenceTypes, this was checked statically before instantiation, after the proposal,
			// this must be raised as runtime error (as in assert_trap in spectest), not even an instantiation error.
			// https://github.com/WebAssembly/spec/blob/d39195773112a22b245ffbe864bab6d1182ccb06/test/core/linking.wast#L264-L274
			//
			// In wazero, we ignore it since in any way, the instantiated module and engines are fine and can be used
			// for function invocations.
			return
		}

		if .Type == RefTypeExternref {
			for  := 0;  < len(.Init); ++ {
				[+uint32()] = Reference(0)
			}
		} else {
			for ,  := range .Init {
				if  == ElementInitNullReference {
					continue
				}

				var  Reference
				if ,  := unwrapElementInitGlobalReference();  {
					 := .Globals[]
					 = Reference(.Val)
				} else {
					 = .Engine.FunctionInstanceReference()
				}
				[+uint32()] = 
			}
		}
	}
}

// validateData ensures that data segments are valid in terms of memory boundary.
// Note: this is used only when bulk-memory/reference type feature is disabled.
func ( *ModuleInstance) ( []DataSegment) ( error) {
	for  := range  {
		 := &[]
		if !.IsPassive() {
			 := int(executeConstExpressionI32(.Globals, &.OffsetExpression))
			 :=  + len(.Init)
			if  < 0 ||  > len(.MemoryInstance.Buffer) {
				return fmt.Errorf("%s[%d]: out of bounds memory access", SectionIDName(SectionIDData), )
			}
		}
	}
	return
}

// applyData uses the given data segments and mutate the memory according to the initial contents on it
// and populate the `DataInstances`. This is called after all the validation phase passes and out of
// bounds memory access error here is not a validation error, but rather a runtime error.
func ( *ModuleInstance) ( []DataSegment) error {
	.DataInstances = make([][]byte, len())
	for  := range  {
		 := &[]
		.DataInstances[] = .Init
		if !.IsPassive() {
			 := executeConstExpressionI32(.Globals, &.OffsetExpression)
			if  < 0 || int()+len(.Init) > len(.MemoryInstance.Buffer) {
				return fmt.Errorf("%s[%d]: out of bounds memory access", SectionIDName(SectionIDData), )
			}
			copy(.MemoryInstance.Buffer[:], .Init)
		}
	}
	return nil
}

// GetExport returns an export of the given name and type or errs if not exported or the wrong type.
func ( *ModuleInstance) ( string,  ExternType) (*Export, error) {
	,  := .Exports[]
	if ! {
		return nil, fmt.Errorf("%q is not exported in module %q", , .ModuleName)
	}
	if .Type !=  {
		return nil, fmt.Errorf("export %q in module %q is a %s, not a %s", , .ModuleName, ExternTypeName(.Type), ExternTypeName())
	}
	return , nil
}

func ( api.CoreFeatures,  Engine) *Store {
	return &Store{
		nameToModule:     map[string]*ModuleInstance{},
		nameToModuleCap:  nameToModuleShrinkThreshold,
		EnabledFeatures:  ,
		Engine:           ,
		typeIDs:          map[string]FunctionTypeID{},
		functionMaxTypes: maximumFunctionTypes,
	}
}

// Instantiate uses name instead of the Module.NameSection ModuleName as it allows instantiating the same module under
// different names safely and concurrently.
//
// * ctx: the default context used for function calls.
// * name: the name of the module.
// * sys: the system context, which will be closed (SysContext.Close) on ModuleInstance.Close.
//
// Note: Module.Validate must be called prior to instantiation.
func ( *Store) (
	 context.Context,
	 *Module,
	 string,
	 *internalsys.Context,
	 []FunctionTypeID,
) (*ModuleInstance, error) {
	// Instantiate the module and add it to the store so that other modules can import it.
	,  := .instantiate(, , , , )
	if  != nil {
		return nil, 
	}

	// Now that the instantiation is complete without error, add it.
	if  = .registerModule();  != nil {
		_ = .Close()
		return nil, 
	}
	return , nil
}

func ( *Store) (
	 context.Context,
	 *Module,
	 string,
	 *internalsys.Context,
	 []FunctionTypeID,
) ( *ModuleInstance,  error) {
	 = &ModuleInstance{ModuleName: , TypeIDs: , Sys: , s: , Source: }

	.Tables = make([]*TableInstance, int(.ImportTableCount)+len(.TableSection))
	.Globals = make([]*GlobalInstance, int(.ImportGlobalCount)+len(.GlobalSection))
	.Engine,  = .Engine.NewModuleEngine(, )
	if  != nil {
		return nil, 
	}

	if  = .resolveImports(, );  != nil {
		return nil, 
	}

	 = .buildTables(,
		// As of reference-types proposal, boundary check must be done after instantiation.
		.EnabledFeatures.IsEnabled(api.CoreFeatureReferenceTypes))
	if  != nil {
		return nil, 
	}

	,  := .Value(expctxkeys.MemoryAllocatorKey{}).(experimental.MemoryAllocator)

	.buildGlobals(, .Engine.FunctionInstanceReference)
	.buildMemory(, )
	.Exports = .Exports
	for ,  := range .Exports {
		if .Type == ExternTypeTable {
			 := .Tables[.Index]
			.involvingModuleInstances = append(.involvingModuleInstances, )
		}
	}

	// As of reference types proposal, data segment validation must happen after instantiation,
	// and the side effect must persist even if there's out of bounds error after instantiation.
	// https://github.com/WebAssembly/spec/blob/d39195773112a22b245ffbe864bab6d1182ccb06/test/core/linking.wast#L395-L405
	if !.EnabledFeatures.IsEnabled(api.CoreFeatureReferenceTypes) {
		if  = .validateData(.DataSection);  != nil {
			return nil, 
		}
	}

	// After engine creation, we can create the funcref element instances and initialize funcref type globals.
	.buildElementInstances(.ElementSection)

	// Now all the validation passes, we are safe to mutate memory instances (possibly imported ones).
	if  = .applyData(.DataSection);  != nil {
		return nil, 
	}

	.applyElements(.ElementSection)

	.Engine.DoneInstantiation()

	// Execute the start function.
	if .StartSection != nil {
		 := *.StartSection
		 := .Engine.NewFunction()
		_,  = .Call()
		if ,  := .(*sys.ExitError);  { // Don't wrap an exit error!
			return nil, 
		} else if  != nil {
			return nil, fmt.Errorf("start %s failed: %w", .funcDesc(SectionIDFunction, ), )
		}
	}
	return
}

func ( *ModuleInstance) ( context.Context,  *Module) ( error) {
	// Check if ctx contains an ImportResolver.
	,  := .Value(expctxkeys.ImportResolverKey{}).(experimental.ImportResolver)

	for ,  := range .ImportPerModule {
		var  *ModuleInstance
		if  != nil {
			if  := ();  != nil {
				 = .(*ModuleInstance)
			}
		}
		if  == nil {
			,  = .s.module()
			if  != nil {
				return 
			}
		}

		for ,  := range  {
			var  *Export
			,  = .getExport(.Name, .Type)
			if  != nil {
				return
			}

			switch .Type {
			case ExternTypeFunc:
				 := &.TypeSection[.DescFunc]
				 := .Source
				 := .typeOfFunction(.Index)
				if !.EqualsSignature(.Params, .Results) {
					 = errorInvalidImport(, fmt.Errorf("signature mismatch: %s != %s", , ))
					return
				}

				.Engine.ResolveImportedFunction(.IndexPerType, .DescFunc, .Index, .Engine)
			case ExternTypeTable:
				 := .DescTable
				 := .Tables[.Index]
				if .Type != .Type {
					 = errorInvalidImport(, fmt.Errorf("table type mismatch: %s != %s",
						RefTypeName(.Type), RefTypeName(.Type)))
					return
				}

				if .Min > .Min {
					 = errorMinSizeMismatch(, .Min, .Min)
					return
				}

				if .Max != nil {
					 := *.Max
					if .Max == nil {
						 = errorNoMax(, )
						return
					} else if  < *.Max {
						 = errorMaxSizeMismatch(, , *.Max)
						return
					}
				}
				.Tables[.IndexPerType] = 
				.involvingModuleInstancesMutex.Lock()
				if len(.involvingModuleInstances) == 0 {
					panic("BUG: involvingModuleInstances must not be nil when it's imported")
				}
				.involvingModuleInstances = append(.involvingModuleInstances, )
				.involvingModuleInstancesMutex.Unlock()
			case ExternTypeMemory:
				 := .DescMem
				 := .MemoryInstance

				if .Min > memoryBytesNumToPages(uint64(len(.Buffer))) {
					 = errorMinSizeMismatch(, .Min, .Min)
					return
				}

				if .Max < .Max {
					 = errorMaxSizeMismatch(, .Max, .Max)
					return
				}
				.MemoryInstance = 
				.Engine.ResolveImportedMemory(.Engine)
			case ExternTypeGlobal:
				 := .DescGlobal
				 := .Globals[.Index]

				if .Mutable != .Type.Mutable {
					 = errorInvalidImport(, fmt.Errorf("mutability mismatch: %t != %t",
						.Mutable, .Type.Mutable))
					return
				}

				if .ValType != .Type.ValType {
					 = errorInvalidImport(, fmt.Errorf("value type mismatch: %s != %s",
						ValueTypeName(.ValType), ValueTypeName(.Type.ValType)))
					return
				}
				.Globals[.IndexPerType] = 
			}
		}
	}
	return
}

func errorMinSizeMismatch( *Import, ,  uint32) error {
	return errorInvalidImport(, fmt.Errorf("minimum size mismatch: %d > %d", , ))
}

func errorNoMax( *Import,  uint32) error {
	return errorInvalidImport(, fmt.Errorf("maximum size mismatch: %d, but actual has no max", ))
}

func errorMaxSizeMismatch( *Import, ,  uint32) error {
	return errorInvalidImport(, fmt.Errorf("maximum size mismatch: %d < %d", , ))
}

func errorInvalidImport( *Import,  error) error {
	return fmt.Errorf("import %s[%s.%s]: %w", ExternTypeName(.Type), .Module, .Name, )
}

// executeConstExpressionI32 executes the ConstantExpression which returns ValueTypeI32.
// The validity of the expression is ensured when calling this function as this is only called
// during instantiation phrase, and the validation happens in compilation (validateConstExpression).
func executeConstExpressionI32( []*GlobalInstance,  *ConstantExpression) ( int32) {
	switch .Opcode {
	case OpcodeI32Const:
		, _, _ = leb128.LoadInt32(.Data)
	case OpcodeGlobalGet:
		, ,  := leb128.LoadUint32(.Data)
		 := []
		 = int32(.Val)
	}
	return
}

// initialize initializes the value of this global instance given the const expr and imported globals.
// funcRefResolver is called to get the actual funcref (engine specific) from the OpcodeRefFunc const expr.
//
// Global initialization constant expression can only reference the imported globals.
// See the note on https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#constant-expressions%E2%91%A0
func ( *GlobalInstance) ( []*GlobalInstance,  *ConstantExpression,  func( Index) Reference) {
	switch .Opcode {
	case OpcodeI32Const:
		// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
		, ,  := leb128.LoadInt32(.Data)
		.Val = uint64(uint32())
	case OpcodeI64Const:
		// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
		, ,  := leb128.LoadInt64(.Data)
		.Val = uint64()
	case OpcodeF32Const:
		.Val = uint64(binary.LittleEndian.Uint32(.Data))
	case OpcodeF64Const:
		.Val = binary.LittleEndian.Uint64(.Data)
	case OpcodeGlobalGet:
		, ,  := leb128.LoadUint32(.Data)
		 := []
		switch .Type.ValType {
		case ValueTypeI32:
			.Val = uint64(uint32(.Val))
		case ValueTypeI64:
			.Val = .Val
		case ValueTypeF32:
			.Val = .Val
		case ValueTypeF64:
			.Val = .Val
		case ValueTypeV128:
			.Val, .ValHi = .Val, .ValHi
		case ValueTypeFuncref, ValueTypeExternref:
			.Val = .Val
		}
	case OpcodeRefNull:
		switch .Data[0] {
		case ValueTypeExternref, ValueTypeFuncref:
			.Val = 0 // Reference types are opaque 64bit pointer at runtime.
		}
	case OpcodeRefFunc:
		, ,  := leb128.LoadUint32(.Data)
		.Val = uint64(())
	case OpcodeVecV128Const:
		.Val, .ValHi = binary.LittleEndian.Uint64(.Data[0:8]), binary.LittleEndian.Uint64(.Data[8:16])
	}
}

// String implements api.Global.
func ( *GlobalInstance) () string {
	switch .Type.ValType {
	case ValueTypeI32, ValueTypeI64:
		return fmt.Sprintf("global(%d)", .Val)
	case ValueTypeF32:
		return fmt.Sprintf("global(%f)", api.DecodeF32(.Val))
	case ValueTypeF64:
		return fmt.Sprintf("global(%f)", api.DecodeF64(.Val))
	default:
		panic(fmt.Errorf("BUG: unknown value type %X", .Type.ValType))
	}
}

func ( *GlobalInstance) () (uint64, uint64) {
	if .Me != nil {
		return .Me.GetGlobalValue(.Index)
	}
	return .Val, .ValHi
}

func ( *GlobalInstance) (,  uint64) {
	if .Me != nil {
		.Me.SetGlobalValue(.Index, , )
	} else {
		.Val, .ValHi = , 
	}
}

func ( *Store) ( []FunctionType) ([]FunctionTypeID, error) {
	 := make([]FunctionTypeID, len())
	for  := range  {
		 := &[]
		,  := .GetFunctionTypeID()
		if  != nil {
			return nil, 
		}
		[] = 
	}
	return , nil
}

func ( *Store) ( *FunctionType) (FunctionTypeID, error) {
	.mux.RLock()
	 := .key()
	,  := .typeIDs[]
	.mux.RUnlock()
	if ! {
		.mux.Lock()
		defer .mux.Unlock()
		// Check again in case another goroutine has already added the type.
		if ,  = .typeIDs[];  {
			return , nil
		}
		 := len(.typeIDs)
		if uint32() >= .functionMaxTypes {
			return 0, fmt.Errorf("too many function types in a store")
		}
		 = FunctionTypeID()
		.typeIDs[] = 
	}
	return , nil
}

// CloseWithExitCode implements the same method as documented on wazero.Runtime.
func ( *Store) ( context.Context,  uint32) error {
	.mux.Lock()
	defer .mux.Unlock()
	// Close modules in reverse initialization order.
	var  []error
	for  := .moduleList;  != nil;  = .next {
		// If closing this module errs, proceed anyway to close the others.
		if  := .closeWithExitCode(, );  != nil {
			 = append(, )
		}
	}
	.moduleList = nil
	.nameToModule = nil
	.nameToModuleCap = 0
	.typeIDs = nil
	return errors.Join(...)
}