package wazevo
import (
"encoding/binary"
"unsafe"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/experimental"
"github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi"
"github.com/tetratelabs/wazero/internal/wasm"
"github.com/tetratelabs/wazero/internal/wasmruntime"
)
type (
moduleEngine struct {
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 []byte
)
func newAlignedOpaque(size int ) moduleContextOpaque {
if size %16 != 0 {
panic ("size must be a multiple of 16" )
}
buf := make ([]byte , size +16 )
rem := uintptr (unsafe .Pointer (&buf [0 ])) % 16
buf = buf [16 -rem :]
return buf
}
func (m *moduleEngine ) setupOpaque () {
inst := m .module
offsets := &m .parent .offsets
opaque := m .opaque
binary .LittleEndian .PutUint64 (opaque [offsets .ModuleInstanceOffset :],
uint64 (uintptr (unsafe .Pointer (m .module ))),
)
if lm := offsets .LocalMemoryBegin ; lm >= 0 {
m .putLocalMemory ()
}
if globalOffset := offsets .GlobalsBegin ; globalOffset >= 0 {
for i , g := range inst .Globals {
if i < int (inst .Source .ImportGlobalCount ) {
importedME := g .Me .(*moduleEngine )
offset := importedME .parent .offsets .GlobalInstanceOffset (g .Index )
importedMEOpaque := importedME .opaque
binary .LittleEndian .PutUint64 (opaque [globalOffset :],
uint64 (uintptr (unsafe .Pointer (&importedMEOpaque [offset ]))))
} else {
binary .LittleEndian .PutUint64 (opaque [globalOffset :], g .Val )
binary .LittleEndian .PutUint64 (opaque [globalOffset +8 :], g .ValHi )
}
globalOffset += 16
}
}
if tableOffset := offsets .TablesBegin ; tableOffset >= 0 {
if len (inst .TypeIDs ) > 0 {
binary .LittleEndian .PutUint64 (opaque [offsets .TypeIDs1stElement :], uint64 (uintptr (unsafe .Pointer (&inst .TypeIDs [0 ]))))
}
for _ , table := range inst .Tables {
binary .LittleEndian .PutUint64 (opaque [tableOffset :], uint64 (uintptr (unsafe .Pointer (table ))))
tableOffset += 8
}
}
if beforeListenerOffset := offsets .BeforeListenerTrampolines1stElement ; beforeListenerOffset >= 0 {
binary .LittleEndian .PutUint64 (opaque [beforeListenerOffset :], uint64 (uintptr (unsafe .Pointer (&m .parent .listenerBeforeTrampolines [0 ]))))
}
if afterListenerOffset := offsets .AfterListenerTrampolines1stElement ; afterListenerOffset >= 0 {
binary .LittleEndian .PutUint64 (opaque [afterListenerOffset :], uint64 (uintptr (unsafe .Pointer (&m .parent .listenerAfterTrampolines [0 ]))))
}
if len (inst .DataInstances ) > 0 {
binary .LittleEndian .PutUint64 (opaque [offsets .DataInstances1stElement :], uint64 (uintptr (unsafe .Pointer (&inst .DataInstances [0 ]))))
}
if len (inst .ElementInstances ) > 0 {
binary .LittleEndian .PutUint64 (opaque [offsets .ElementInstances1stElement :], uint64 (uintptr (unsafe .Pointer (&inst .ElementInstances [0 ]))))
}
}
func (m *moduleEngine ) NewFunction (index wasm .Index ) api .Function {
if wazevoapi .PrintMachineCodeHexPerFunctionDisassemblable {
panic ("When PrintMachineCodeHexPerFunctionDisassemblable enabled, functions must not be called" )
}
localIndex := index
if importedFnCount := m .module .Source .ImportFunctionCount ; index < importedFnCount {
imported := &m .importedFunctions [index ]
return imported .me .NewFunction (imported .indexInModule )
} else {
localIndex -= importedFnCount
}
src := m .module .Source
typIndex := src .FunctionSection [localIndex ]
typ := src .TypeSection [typIndex ]
sizeOfParamResultSlice := typ .ResultNumInUint64
if ps := typ .ParamNumInUint64 ; ps > sizeOfParamResultSlice {
sizeOfParamResultSlice = ps
}
p := m .parent
offset := p .functionOffsets [localIndex ]
ce := &callEngine {
indexInModule : index ,
executable : &p .executable [offset ],
parent : m ,
preambleExecutable : &m .parent .entryPreambles [typIndex ][0 ],
sizeOfParamResultSlice : sizeOfParamResultSlice ,
requiredParams : typ .ParamNumInUint64 ,
numberOfResults : typ .ResultNumInUint64 ,
}
ce .execCtx .memoryGrowTrampolineAddress = &m .parent .sharedFunctions .memoryGrowExecutable [0 ]
ce .execCtx .stackGrowCallTrampolineAddress = &m .parent .sharedFunctions .stackGrowExecutable [0 ]
ce .execCtx .checkModuleExitCodeTrampolineAddress = &m .parent .sharedFunctions .checkModuleExitCode [0 ]
ce .execCtx .tableGrowTrampolineAddress = &m .parent .sharedFunctions .tableGrowExecutable [0 ]
ce .execCtx .refFuncTrampolineAddress = &m .parent .sharedFunctions .refFuncExecutable [0 ]
ce .execCtx .memoryWait32TrampolineAddress = &m .parent .sharedFunctions .memoryWait32Executable [0 ]
ce .execCtx .memoryWait64TrampolineAddress = &m .parent .sharedFunctions .memoryWait64Executable [0 ]
ce .execCtx .memoryNotifyTrampolineAddress = &m .parent .sharedFunctions .memoryNotifyExecutable [0 ]
ce .execCtx .memmoveAddress = memmovPtr
ce .init ()
return ce
}
func (m *moduleEngine ) GetGlobalValue (i wasm .Index ) (lo , hi uint64 ) {
offset := m .parent .offsets .GlobalInstanceOffset (i )
buf := m .opaque [offset :]
if i < m .module .Source .ImportGlobalCount {
panic ("GetGlobalValue should not be called for imported globals" )
}
return binary .LittleEndian .Uint64 (buf ), binary .LittleEndian .Uint64 (buf [8 :])
}
func (m *moduleEngine ) SetGlobalValue (i wasm .Index , lo , hi uint64 ) {
offset := m .parent .offsets .GlobalInstanceOffset (i )
buf := m .opaque [offset :]
if i < m .module .Source .ImportGlobalCount {
panic ("GetGlobalValue should not be called for imported globals" )
}
binary .LittleEndian .PutUint64 (buf , lo )
binary .LittleEndian .PutUint64 (buf [8 :], hi )
}
func (m *moduleEngine ) OwnsGlobals () bool { return true }
func (m *moduleEngine ) MemoryGrown () {
m .putLocalMemory ()
}
func (m *moduleEngine ) putLocalMemory () {
mem := m .module .MemoryInstance
offset := m .parent .offsets .LocalMemoryBegin
s := uint64 (len (mem .Buffer ))
var b uint64
if len (mem .Buffer ) > 0 {
b = uint64 (uintptr (unsafe .Pointer (&mem .Buffer [0 ])))
}
binary .LittleEndian .PutUint64 (m .opaque [offset :], b )
binary .LittleEndian .PutUint64 (m .opaque [offset +8 :], s )
}
func (m *moduleEngine ) ResolveImportedFunction (index , descFunc , indexInImportedModule wasm .Index , importedModuleEngine wasm .ModuleEngine ) {
executableOffset , moduleCtxOffset , typeIDOffset := m .parent .offsets .ImportedFunctionOffset (index )
importedME := importedModuleEngine .(*moduleEngine )
if int (indexInImportedModule ) >= len (importedME .importedFunctions ) {
indexInImportedModule -= wasm .Index (len (importedME .importedFunctions ))
} else {
imported := &importedME .importedFunctions [indexInImportedModule ]
m .ResolveImportedFunction (index , descFunc , imported .indexInModule , imported .me )
return
}
offset := importedME .parent .functionOffsets [indexInImportedModule ]
typeID := m .module .TypeIDs [descFunc ]
executable := &importedME .parent .executable [offset ]
binary .LittleEndian .PutUint64 (m .opaque [executableOffset :], uint64 (uintptr (unsafe .Pointer (executable ))))
binary .LittleEndian .PutUint64 (m .opaque [moduleCtxOffset :], uint64 (uintptr (unsafe .Pointer (importedME .opaquePtr ))))
binary .LittleEndian .PutUint64 (m .opaque [typeIDOffset :], uint64 (typeID ))
m .importedFunctions [index ] = importedFunction {me : importedME , indexInModule : indexInImportedModule }
}
func (m *moduleEngine ) ResolveImportedMemory (importedModuleEngine wasm .ModuleEngine ) {
importedME := importedModuleEngine .(*moduleEngine )
inst := importedME .module
var memInstPtr uint64
var memOwnerOpaquePtr uint64
if offs := importedME .parent .offsets ; offs .ImportedMemoryBegin >= 0 {
offset := offs .ImportedMemoryBegin
memInstPtr = binary .LittleEndian .Uint64 (importedME .opaque [offset :])
memOwnerOpaquePtr = binary .LittleEndian .Uint64 (importedME .opaque [offset +8 :])
} else {
memInstPtr = uint64 (uintptr (unsafe .Pointer (inst .MemoryInstance )))
memOwnerOpaquePtr = uint64 (uintptr (unsafe .Pointer (importedME .opaquePtr )))
}
offset := m .parent .offsets .ImportedMemoryBegin
binary .LittleEndian .PutUint64 (m .opaque [offset :], memInstPtr )
binary .LittleEndian .PutUint64 (m .opaque [offset +8 :], memOwnerOpaquePtr )
}
func (m *moduleEngine ) DoneInstantiation () {
if !m .module .Source .IsHostModule {
m .setupOpaque ()
}
}
func (m *moduleEngine ) FunctionInstanceReference (funcIndex wasm .Index ) wasm .Reference {
if funcIndex < m .module .Source .ImportFunctionCount {
begin , _ , _ := m .parent .offsets .ImportedFunctionOffset (funcIndex )
return uintptr (unsafe .Pointer (&m .opaque [begin ]))
}
localIndex := funcIndex - m .module .Source .ImportFunctionCount
p := m .parent
executable := &p .executable [p .functionOffsets [localIndex ]]
typeID := m .module .TypeIDs [m .module .Source .FunctionSection [localIndex ]]
lf := &functionInstance {
executable : executable ,
moduleContextOpaquePtr : m .opaquePtr ,
typeID : typeID ,
indexInModule : funcIndex ,
}
m .localFunctionInstances = append (m .localFunctionInstances , lf )
return uintptr (unsafe .Pointer (lf ))
}
func (m *moduleEngine ) LookupFunction (t *wasm .TableInstance , typeId wasm .FunctionTypeID , tableOffset wasm .Index ) (*wasm .ModuleInstance , wasm .Index ) {
if tableOffset >= uint32 (len (t .References )) || t .Type != wasm .RefTypeFuncref {
panic (wasmruntime .ErrRuntimeInvalidTableAccess )
}
rawPtr := t .References [tableOffset ]
if rawPtr == 0 {
panic (wasmruntime .ErrRuntimeInvalidTableAccess )
}
tf := wazevoapi .PtrFromUintptr [functionInstance ](rawPtr )
if tf .typeID != typeId {
panic (wasmruntime .ErrRuntimeIndirectCallTypeMismatch )
}
return moduleInstanceFromOpaquePtr (tf .moduleContextOpaquePtr ), tf .indexInModule
}
func moduleInstanceFromOpaquePtr(ptr *byte ) *wasm .ModuleInstance {
return *(**wasm .ModuleInstance )(unsafe .Pointer (ptr ))
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .