package wazevo

import (
	
	

	
	
	
	
	
)

type (
	// moduleEngine implements wasm.ModuleEngine.
	moduleEngine struct {
		// opaquePtr equals &opaque[0].
		opaquePtr              *byte
		parent                 *compiledModule
		module                 *wasm.ModuleInstance
		opaque                 moduleContextOpaque
		localFunctionInstances []*functionInstance
		importedFunctions      []importedFunction
		listeners              []experimental.FunctionListener
	}

	functionInstance struct {
		executable             *byte
		moduleContextOpaquePtr *byte
		typeID                 wasm.FunctionTypeID
		indexInModule          wasm.Index
	}

	importedFunction struct {
		me            *moduleEngine
		indexInModule wasm.Index
	}

	// moduleContextOpaque is the opaque byte slice of Module instance specific contents whose size
	// is only Wasm-compile-time known, hence dynamic. Its contents are basically the pointers to the module instance,
	// specific objects as well as functions. This is sometimes called "VMContext" in other Wasm runtimes.
	//
	// Internally, the buffer is structured as follows:
	//
	// 	type moduleContextOpaque struct {
	// 	    moduleInstance                            *wasm.ModuleInstance
	// 	    localMemoryBufferPtr                      *byte                (optional)
	// 	    localMemoryLength                         uint64               (optional)
	// 	    importedMemoryInstance                    *wasm.MemoryInstance (optional)
	// 	    importedMemoryOwnerOpaqueCtx              *byte                (optional)
	// 	    importedFunctions                         [# of importedFunctions]functionInstance
	//      importedGlobals                           []ImportedGlobal       (optional)
	//      localGlobals                              []Global               (optional)
	//      typeIDsBegin                              &wasm.ModuleInstance.TypeIDs[0]  (optional)
	//      tables                                    []*wasm.TableInstance  (optional)
	// 	    beforeListenerTrampolines1stElement       **byte                 (optional)
	// 	    afterListenerTrampolines1stElement        **byte                 (optional)
	//      dataInstances1stElement                   []wasm.DataInstance    (optional)
	//      elementInstances1stElement                []wasm.ElementInstance (optional)
	// 	}
	//
	//  type ImportedGlobal struct {
	// 		*Global
	// 		_ uint64 // padding
	//  }
	//
	//  type Global struct {
	// 		Val, ValHi uint64
	//  }
	//
	// See wazevoapi.NewModuleContextOffsetData for the details of the offsets.
	//
	// Note that for host modules, the structure is entirely different. See buildHostModuleOpaque.
	moduleContextOpaque []byte
)

func newAlignedOpaque( int) moduleContextOpaque {
	// Check if the size is a multiple of 16.
	if %16 != 0 {
		panic("size must be a multiple of 16")
	}
	 := make([]byte, +16)
	// Align the buffer to 16 bytes.
	 := uintptr(unsafe.Pointer(&[0])) % 16
	 = [16-:]
	return 
}

func ( *moduleEngine) () {
	 := .module
	 := &.parent.offsets
	 := .opaque

	binary.LittleEndian.PutUint64([.ModuleInstanceOffset:],
		uint64(uintptr(unsafe.Pointer(.module))),
	)

	if  := .LocalMemoryBegin;  >= 0 {
		.putLocalMemory()
	}

	// Note: imported memory is resolved in ResolveImportedFunction.

	// Note: imported functions are resolved in ResolveImportedFunction.

	if  := .GlobalsBegin;  >= 0 {
		for ,  := range .Globals {
			if  < int(.Source.ImportGlobalCount) {
				 := .Me.(*moduleEngine)
				 := .parent.offsets.GlobalInstanceOffset(.Index)
				 := .opaque
				binary.LittleEndian.PutUint64([:],
					uint64(uintptr(unsafe.Pointer(&[]))))
			} else {
				binary.LittleEndian.PutUint64([:], .Val)
				binary.LittleEndian.PutUint64([+8:], .ValHi)
			}
			 += 16
		}
	}

	if  := .TablesBegin;  >= 0 {
		// First we write the first element's address of typeIDs.
		if len(.TypeIDs) > 0 {
			binary.LittleEndian.PutUint64([.TypeIDs1stElement:], uint64(uintptr(unsafe.Pointer(&.TypeIDs[0]))))
		}

		// Then we write the table addresses.
		for ,  := range .Tables {
			binary.LittleEndian.PutUint64([:], uint64(uintptr(unsafe.Pointer())))
			 += 8
		}
	}

	if  := .BeforeListenerTrampolines1stElement;  >= 0 {
		binary.LittleEndian.PutUint64([:], uint64(uintptr(unsafe.Pointer(&.parent.listenerBeforeTrampolines[0]))))
	}
	if  := .AfterListenerTrampolines1stElement;  >= 0 {
		binary.LittleEndian.PutUint64([:], uint64(uintptr(unsafe.Pointer(&.parent.listenerAfterTrampolines[0]))))
	}
	if len(.DataInstances) > 0 {
		binary.LittleEndian.PutUint64([.DataInstances1stElement:], uint64(uintptr(unsafe.Pointer(&.DataInstances[0]))))
	}
	if len(.ElementInstances) > 0 {
		binary.LittleEndian.PutUint64([.ElementInstances1stElement:], uint64(uintptr(unsafe.Pointer(&.ElementInstances[0]))))
	}
}

// NewFunction implements wasm.ModuleEngine.
func ( *moduleEngine) ( wasm.Index) api.Function {
	if wazevoapi.PrintMachineCodeHexPerFunctionDisassemblable {
		panic("When PrintMachineCodeHexPerFunctionDisassemblable enabled, functions must not be called")
	}

	 := 
	if  := .module.Source.ImportFunctionCount;  <  {
		 := &.importedFunctions[]
		return .me.(.indexInModule)
	} else {
		 -= 
	}

	 := .module.Source
	 := .FunctionSection[]
	 := .TypeSection[]
	 := .ResultNumInUint64
	if  := .ParamNumInUint64;  >  {
		 = 
	}
	 := .parent
	 := .functionOffsets[]

	 := &callEngine{
		indexInModule:          ,
		executable:             &.executable[],
		parent:                 ,
		preambleExecutable:     &.parent.entryPreambles[][0],
		sizeOfParamResultSlice: ,
		requiredParams:         .ParamNumInUint64,
		numberOfResults:        .ResultNumInUint64,
	}

	.execCtx.memoryGrowTrampolineAddress = &.parent.sharedFunctions.memoryGrowExecutable[0]
	.execCtx.stackGrowCallTrampolineAddress = &.parent.sharedFunctions.stackGrowExecutable[0]
	.execCtx.checkModuleExitCodeTrampolineAddress = &.parent.sharedFunctions.checkModuleExitCode[0]
	.execCtx.tableGrowTrampolineAddress = &.parent.sharedFunctions.tableGrowExecutable[0]
	.execCtx.refFuncTrampolineAddress = &.parent.sharedFunctions.refFuncExecutable[0]
	.execCtx.memoryWait32TrampolineAddress = &.parent.sharedFunctions.memoryWait32Executable[0]
	.execCtx.memoryWait64TrampolineAddress = &.parent.sharedFunctions.memoryWait64Executable[0]
	.execCtx.memoryNotifyTrampolineAddress = &.parent.sharedFunctions.memoryNotifyExecutable[0]
	.execCtx.memmoveAddress = memmovPtr
	.init()
	return 
}

// GetGlobalValue implements the same method as documented on wasm.ModuleEngine.
func ( *moduleEngine) ( wasm.Index) (,  uint64) {
	 := .parent.offsets.GlobalInstanceOffset()
	 := .opaque[:]
	if  < .module.Source.ImportGlobalCount {
		panic("GetGlobalValue should not be called for imported globals")
	}
	return binary.LittleEndian.Uint64(), binary.LittleEndian.Uint64([8:])
}

// SetGlobalValue implements the same method as documented on wasm.ModuleEngine.
func ( *moduleEngine) ( wasm.Index, ,  uint64) {
	 := .parent.offsets.GlobalInstanceOffset()
	 := .opaque[:]
	if  < .module.Source.ImportGlobalCount {
		panic("GetGlobalValue should not be called for imported globals")
	}
	binary.LittleEndian.PutUint64(, )
	binary.LittleEndian.PutUint64([8:], )
}

// OwnsGlobals implements the same method as documented on wasm.ModuleEngine.
func ( *moduleEngine) () bool { return true }

// MemoryGrown implements wasm.ModuleEngine.
func ( *moduleEngine) () {
	.putLocalMemory()
}

// putLocalMemory writes the local memory buffer pointer and length to the opaque buffer.
func ( *moduleEngine) () {
	 := .module.MemoryInstance
	 := .parent.offsets.LocalMemoryBegin

	 := uint64(len(.Buffer))
	var  uint64
	if len(.Buffer) > 0 {
		 = uint64(uintptr(unsafe.Pointer(&.Buffer[0])))
	}
	binary.LittleEndian.PutUint64(.opaque[:], )
	binary.LittleEndian.PutUint64(.opaque[+8:], )
}

// ResolveImportedFunction implements wasm.ModuleEngine.
func ( *moduleEngine) (, ,  wasm.Index,  wasm.ModuleEngine) {
	, ,  := .parent.offsets.ImportedFunctionOffset()
	 := .(*moduleEngine)

	if int() >= len(.importedFunctions) {
		 -= wasm.Index(len(.importedFunctions))
	} else {
		 := &.importedFunctions[]
		.(, , .indexInModule, .me)
		return // Recursively resolve the imported function.
	}

	 := .parent.functionOffsets[]
	 := .module.TypeIDs[]
	 := &.parent.executable[]
	// Write functionInstance.
	binary.LittleEndian.PutUint64(.opaque[:], uint64(uintptr(unsafe.Pointer())))
	binary.LittleEndian.PutUint64(.opaque[:], uint64(uintptr(unsafe.Pointer(.opaquePtr))))
	binary.LittleEndian.PutUint64(.opaque[:], uint64())

	// Write importedFunction so that it can be used by NewFunction.
	.importedFunctions[] = importedFunction{me: , indexInModule: }
}

// ResolveImportedMemory implements wasm.ModuleEngine.
func ( *moduleEngine) ( wasm.ModuleEngine) {
	 := .(*moduleEngine)
	 := .module

	var  uint64
	var  uint64
	if  := .parent.offsets; .ImportedMemoryBegin >= 0 {
		 := .ImportedMemoryBegin
		 = binary.LittleEndian.Uint64(.opaque[:])
		 = binary.LittleEndian.Uint64(.opaque[+8:])
	} else {
		 = uint64(uintptr(unsafe.Pointer(.MemoryInstance)))
		 = uint64(uintptr(unsafe.Pointer(.opaquePtr)))
	}
	 := .parent.offsets.ImportedMemoryBegin
	binary.LittleEndian.PutUint64(.opaque[:], )
	binary.LittleEndian.PutUint64(.opaque[+8:], )
}

// DoneInstantiation implements wasm.ModuleEngine.
func ( *moduleEngine) () {
	if !.module.Source.IsHostModule {
		.setupOpaque()
	}
}

// FunctionInstanceReference implements wasm.ModuleEngine.
func ( *moduleEngine) ( wasm.Index) wasm.Reference {
	if  < .module.Source.ImportFunctionCount {
		, ,  := .parent.offsets.ImportedFunctionOffset()
		return uintptr(unsafe.Pointer(&.opaque[]))
	}
	 :=  - .module.Source.ImportFunctionCount
	 := .parent
	 := &.executable[.functionOffsets[]]
	 := .module.TypeIDs[.module.Source.FunctionSection[]]

	 := &functionInstance{
		executable:             ,
		moduleContextOpaquePtr: .opaquePtr,
		typeID:                 ,
		indexInModule:          ,
	}
	.localFunctionInstances = append(.localFunctionInstances, )
	return uintptr(unsafe.Pointer())
}

// LookupFunction implements wasm.ModuleEngine.
func ( *moduleEngine) ( *wasm.TableInstance,  wasm.FunctionTypeID,  wasm.Index) (*wasm.ModuleInstance, wasm.Index) {
	if  >= uint32(len(.References)) || .Type != wasm.RefTypeFuncref {
		panic(wasmruntime.ErrRuntimeInvalidTableAccess)
	}
	 := .References[]
	if  == 0 {
		panic(wasmruntime.ErrRuntimeInvalidTableAccess)
	}

	 := wazevoapi.PtrFromUintptr[functionInstance]()
	if .typeID !=  {
		panic(wasmruntime.ErrRuntimeIndirectCallTypeMismatch)
	}
	return moduleInstanceFromOpaquePtr(.moduleContextOpaquePtr), .indexInModule
}

func moduleInstanceFromOpaquePtr( *byte) *wasm.ModuleInstance {
	return *(**wasm.ModuleInstance)(unsafe.Pointer())
}