Last active
September 22, 2025 21:26
-
-
Save jamesu/f0f5626b3edbc34dc728dfe262484200 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (module | |
| ;; --- Import from host: print(offset: i32) -> void | |
| (import "env" "print" (func $print (param i32))) | |
| ;; --- Linear memory: fixed at 2 pages (128 KiB) --- | |
| (memory (export "memory") 2 2) | |
| ;; --- Data segment: static string --- | |
| (data (i32.const 16) "Hello World\00") | |
| ;; --- Heap layout & globals --- | |
| (global $heap_base i32 (i32.const 1024)) | |
| (global $heap_end i32 (i32.const 131072)) ;; 2 * 64 KiB | |
| (global $heap_ptr (mut i32) (i32.const 1024)) | |
| (global $free_head (mut i32) (i32.const 0)) | |
| ;; --- Helpers --- | |
| (func $align_8 (param $n i32) (result i32) | |
| local.get $n | |
| i32.const 7 | |
| i32.add | |
| i32.const -8 | |
| i32.and) | |
| (func $push_free (param $blk i32) | |
| local.get $blk | |
| global.get $free_head | |
| i32.store offset=4 | |
| local.get $blk | |
| global.set $free_head) | |
| (func $unlink (param $prev i32) (param $cur i32) | |
| (local $next i32) | |
| local.get $cur | |
| i32.load offset=4 | |
| local.set $next | |
| local.get $prev | |
| i32.eqz | |
| if | |
| local.get $next | |
| global.set $free_head | |
| else | |
| local.get $prev | |
| local.get $next | |
| i32.store offset=4 | |
| end) | |
| ;; --- Arithmetic --- | |
| (func (export "add") (param $a i32) (param $b i32) (result i32) | |
| local.get $a | |
| local.get $b | |
| i32.add) | |
| (func (export "sub") (param $a i32) (param $b i32) (result i32) | |
| local.get $a | |
| local.get $b | |
| i32.sub) | |
| ;; --- malloc --- | |
| (func (export "malloc") (param $n i32) (result i32) | |
| (local $need i32) (local $total i32) (local $prev i32) (local $cur i32) | |
| (local $sz i32) (local $hp i32) (local $newhp i32) (local $rem i32) (local $rem_blk i32) | |
| ;; handle n <= 0 | |
| local.get $n | |
| i32.const 0 | |
| i32.le_s | |
| if | |
| i32.const 0 | |
| return | |
| end | |
| ;; sizes | |
| local.get $n | |
| call $align_8 | |
| local.set $need | |
| local.get $need | |
| i32.const 8 | |
| i32.add | |
| local.set $total | |
| ;; Wrap search in an outer block so we can br to the bump path. | |
| block $bump_alloc | |
| ;; init free list scan | |
| i32.const 0 | |
| local.set $prev | |
| global.get $free_head | |
| local.set $cur | |
| loop $search | |
| ;; if cur == 0 → jump to bump_alloc | |
| local.get $cur | |
| i32.eqz | |
| if | |
| br $bump_alloc | |
| end | |
| ;; load size of current block | |
| local.get $cur | |
| i32.load | |
| local.set $sz | |
| ;; fit? | |
| local.get $sz | |
| local.get $total | |
| i32.ge_u | |
| if | |
| ;; unlink from free list | |
| local.get $prev | |
| local.get $cur | |
| call $unlink | |
| ;; compute remainder | |
| local.get $sz | |
| local.get $total | |
| i32.sub | |
| local.set $rem | |
| ;; split if remainder >= 16 (header+min payload) | |
| local.get $rem | |
| i32.const 16 | |
| i32.ge_u | |
| if | |
| local.get $cur | |
| local.get $total | |
| i32.add | |
| local.set $rem_blk | |
| local.get $rem_blk | |
| local.get $rem | |
| i32.store | |
| local.get $rem_blk | |
| call $push_free | |
| local.get $cur | |
| local.get $total | |
| i32.store | |
| else | |
| local.get $cur | |
| local.get $sz | |
| i32.store | |
| end | |
| ;; return payload | |
| local.get $cur | |
| i32.const 8 | |
| i32.add | |
| return | |
| end | |
| ;; advance | |
| local.get $cur | |
| local.set $prev | |
| local.get $cur | |
| i32.load offset=4 | |
| local.set $cur | |
| br $search | |
| end | |
| ;; --- bump allocation path (runs when we br $bump_alloc) --- | |
| global.get $heap_ptr | |
| local.set $hp | |
| local.get $hp | |
| local.get $total | |
| i32.add | |
| local.set $newhp | |
| local.get $newhp | |
| global.get $heap_end | |
| i32.gt_u | |
| if | |
| i32.const 0 | |
| return | |
| end | |
| local.get $hp | |
| local.get $total | |
| i32.store | |
| local.get $newhp | |
| global.set $heap_ptr | |
| local.get $hp | |
| i32.const 8 | |
| i32.add | |
| return | |
| end ;; end block $bump_alloc | |
| ;; We should never fall through here, but satisfy the validator: | |
| i32.const 0 | |
| return | |
| ) | |
| ;; --- free --- | |
| (func (export "free") (param $p i32) | |
| (local $blk i32) | |
| local.get $p | |
| i32.eqz | |
| if | |
| return | |
| end | |
| local.get $p | |
| i32.const 8 | |
| i32.sub | |
| local.set $blk | |
| local.get $blk | |
| call $push_free) | |
| ;; --- testPrint --- | |
| (func (export "testPrint") | |
| i32.const 16 ;; offset of "Hello World" | |
| call $print) | |
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "console/console.h" | |
| #include "console/consoleTypes.h" | |
| #include "sim/simBase.h" | |
| #include "core/fileStream.h" | |
| #include "wasm3.h" | |
| /* | |
| KorkScript WASM module binder. | |
| Module exports get exposed as namespace functions. | |
| Host exports are namespace functions which can be imported by wasm modules. | |
| Example usage: | |
| new WasmModuleObject(MyModule) | |
| { | |
| // Funcs in wasm | |
| funcName[0] = "add"; | |
| funcSig[0] = "i(ii)"; | |
| funcName[0] = "sub"; | |
| funcSig[1] = "i(ii)"; | |
| // Host funcs | |
| hostFuncName[0] = "print"; | |
| hostFuncSig[1] = "v(s)"; | |
| moduleFile = "test.wasm"; | |
| }; | |
| function MyModule::print(%this, %msg) | |
| { | |
| echo("Module Print: " @ %msg); | |
| } | |
| echo(MyModule.add(1,2)); | |
| */ | |
| class WasmModuleObject : public SimObject | |
| { | |
| typedef SimObject Parent; | |
| struct FuncInfo | |
| { | |
| StringTableEntry localName; | |
| IM3Function func; | |
| }; | |
| enum | |
| { | |
| MAX_FUNCS = 16 | |
| }; | |
| struct FuncUserInfo | |
| { | |
| WasmModuleObject* module; | |
| U32 funcIdx; | |
| }; | |
| struct ScratchAlloc | |
| { | |
| U8* ptr; | |
| U32 vmOffset; | |
| }; | |
| public: | |
| DECLARE_CONOBJECT(WasmModuleObject); | |
| IM3Runtime mRuntime; | |
| IM3Environment mEnv; | |
| IM3Module mModule; | |
| U32 mMemSize; | |
| U32 mScratchSize; | |
| StringTableEntry mModuleFile; | |
| // These get called inside WASM | |
| StringTableEntry mFuncNames[MAX_FUNCS]; | |
| StringTableEntry mFuncSignatures[MAX_FUNCS]; | |
| IM3Function mFuncs[MAX_FUNCS]; | |
| FuncUserInfo mInfos[MAX_FUNCS]; | |
| // These get called from WASM thunk | |
| StringTableEntry mHostFuncs[MAX_FUNCS]; | |
| StringTableEntry mHostFuncSignatures[MAX_FUNCS]; | |
| FuncUserInfo mHostInfos[MAX_FUNCS]; | |
| //StringTableEntry mClassName; | |
| U32 mScratchOffset; | |
| U32 mScratchAllocPtr; | |
| static void initPersistFields() | |
| { | |
| Parent::initPersistFields(); | |
| addField("memSize", TypeS32, Offset(mMemSize, WasmModuleObject)); | |
| addField("moduleFile", TypeString, Offset(mModuleFile, WasmModuleObject)); | |
| addField("funcName", TypeString, Offset(mFuncNames[2], WasmModuleObject), MAX_FUNCS-2); | |
| addField("funcSig", TypeString, Offset(mFuncSignatures[2], WasmModuleObject), MAX_FUNCS-2); | |
| addField("hostFuncName", TypeString, Offset(mHostFuncs, WasmModuleObject), MAX_FUNCS); | |
| addField("hostFuncSig", TypeString, Offset(mHostFuncSignatures, WasmModuleObject), MAX_FUNCS); | |
| // KorkScript-specific | |
| addField("className", TypeString, Offset(mClassName, WasmModuleObject)); | |
| } | |
| WasmModuleObject() | |
| { | |
| mRuntime = NULL; | |
| mEnv = NULL; | |
| mModule = NULL; | |
| mMemSize = 128 * 1024; | |
| mScratchSize = 256; | |
| mModuleFile = NULL; | |
| mClassName = NULL; | |
| memset(mFuncNames, '\0', sizeof(mFuncNames)); | |
| memset(mFuncSignatures, '\0', sizeof(mFuncSignatures)); | |
| memset(mFuncs, '\0', sizeof(mFuncs)); | |
| memset(mInfos, '\0', sizeof(mInfos)); | |
| memset(mHostFuncs, '\0', sizeof(mHostFuncs)); | |
| memset(mHostFuncSignatures, '\0', sizeof(mHostFuncSignatures)); | |
| memset(mHostInfos, '\0', sizeof(mHostInfos)); | |
| mScratchOffset = 0; | |
| mScratchAllocPtr = 0; | |
| } | |
| ~WasmModuleObject() | |
| { | |
| cleanup(); | |
| } | |
| void initScratch() | |
| { | |
| // Make sure scratch space is allocated | |
| if (mScratchOffset == 0) | |
| { | |
| if (mFuncs[0]) | |
| { | |
| // Call allocator | |
| mScratchOffset = 0; | |
| } | |
| } | |
| } | |
| ScratchAlloc allocScratch(U32 bytes) | |
| { | |
| ScratchAlloc alloc = {}; | |
| U32 memSize = 0; | |
| U8* mem = ((U8*) m3_GetMemory(mRuntime, &memSize, 0)) + mScratchAllocPtr; | |
| alloc.vmOffset = mScratchAllocPtr; | |
| mScratchAllocPtr += bytes; | |
| if (mScratchAllocPtr > mScratchSize) | |
| { | |
| alloc.vmOffset = 0; | |
| return alloc; | |
| } | |
| alloc.ptr = mem; | |
| return alloc; | |
| } | |
| void resetScratch() | |
| { | |
| mScratchAllocPtr = 0; | |
| } | |
| void cleanup() | |
| { | |
| if (!mRuntime) | |
| { | |
| return; | |
| } | |
| m3_FreeRuntime(mRuntime); | |
| m3_FreeEnvironment(mEnv); | |
| mRuntime = NULL; | |
| mEnv = NULL; | |
| } | |
| bool onAdd() | |
| { | |
| mNSLinkMask = LinkClassName; | |
| if (!Parent::onAdd()) | |
| return false; | |
| FileStream fs; | |
| // Use malloc and free for base allocators | |
| mFuncNames[0] = StringTable->insert("malloc"); | |
| mFuncNames[1] = StringTable->insert("free"); | |
| mFuncSignatures[0] = StringTable->insert("i(i)"); | |
| mFuncSignatures[1] = StringTable->insert("v(i)"); | |
| mEnv = m3_NewEnvironment(); | |
| mRuntime = m3_NewRuntime(mEnv, mMemSize, NULL); | |
| if (!mRuntime || !fs.open(mModuleFile, FileStream::Read)) | |
| { | |
| cleanup(); | |
| return false; | |
| } | |
| if (!load(fs) || !linkFuncs()) | |
| { | |
| cleanup(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool load(Stream& s) | |
| { | |
| U32 sz = s.getStreamSize(); | |
| U8* bytes = new U8[sz]; | |
| s.read(sz, bytes); | |
| M3Result res = m3_ParseModule(mEnv, &mModule, bytes, sz); | |
| if (res == NULL) | |
| { | |
| res = m3_LoadModule(mRuntime, mModule); | |
| } | |
| return res == NULL; | |
| } | |
| void convertSig(char* sig) | |
| { | |
| while (*sig != '\0') | |
| { | |
| if (*sig == 's') | |
| *sig = 'i'; | |
| sig++; | |
| } | |
| } | |
| bool isSigValid(const char* sig) | |
| { | |
| const char* startSig = sig; | |
| while (*sig != '\0' && *sig != '(') | |
| sig++; | |
| if (*sig == '\0' || (sig - startSig != 1)) | |
| return false; | |
| while (*sig != '\0' && *sig != ')') | |
| sig++; | |
| if (*sig != ')') | |
| return false; | |
| if (sig - startSig > 31) | |
| return false; | |
| return true; | |
| } | |
| U32 getSigParamCount(const char* sig) | |
| { | |
| const char* startSig = sig; | |
| while (*sig != '\0' && *sig != '(') | |
| sig++; | |
| if (*sig == '\0') | |
| return 0; | |
| startSig = sig = sig+1; | |
| while (*sig != '\0' && *sig != ')') | |
| sig++; | |
| if (*sig != ')') | |
| return 0; | |
| return (U32)(sig - startSig); | |
| } | |
| bool linkFuncs() | |
| { | |
| KorkApi::Vm* theVm = getVM(); | |
| KorkApi::NamespaceId nsId = getNamespace(); | |
| // See if we have host funcs; register these with the VM (needs to be done FIRST) | |
| for (U32 i=0; i<MAX_FUNCS; i++) | |
| { | |
| if (mHostFuncs[i] != NULL && mHostFuncs[i] != StringTable->EmptyString && | |
| mHostFuncSignatures[i] != NULL && mHostFuncSignatures[i] != StringTable->EmptyString && | |
| isSigValid(mHostFuncSignatures[i])) | |
| { | |
| char realSig[32]; | |
| FuncUserInfo* hostInfo = &mHostInfos[i]; | |
| strcpy(realSig, mHostFuncSignatures[i]); | |
| convertSig(realSig); | |
| hostInfo->module = this; | |
| hostInfo->funcIdx = i; | |
| M3Result res = m3_LinkRawFunctionEx(mModule, | |
| "env", | |
| mHostFuncs[i], | |
| realSig, | |
| thunkHostCall, | |
| hostInfo); | |
| if (res != NULL) | |
| { | |
| Con::warnf("Function %s %s not bound (%s)", mHostFuncs[i], realSig, res); | |
| } | |
| } | |
| } | |
| // Bind all funcs in nsId | |
| for (U32 i=0; i<MAX_FUNCS; i++) | |
| { | |
| if (mFuncNames[i] != NULL && mFuncNames[i] != StringTable->EmptyString && | |
| mFuncSignatures[i] != NULL && mFuncSignatures[i] != StringTable->EmptyString && | |
| isSigValid(mFuncSignatures[i])) | |
| { | |
| FuncUserInfo* info = &mInfos[i]; | |
| info->module = this; | |
| info->funcIdx = i; | |
| U32 paramCount = getSigParamCount(mFuncSignatures[i]); | |
| M3Result result = m3_FindFunction(&mFuncs[i], mRuntime, mFuncNames[i]); | |
| if (result != NULL) | |
| { | |
| Con::warnf("Can't find function %s %s (%s)", mFuncNames[i], mFuncSignatures[i], result); | |
| } | |
| else | |
| { | |
| theVm->addNamespaceFunction(nsId, mFuncNames[i], (KorkApi::StringFuncCallback)thunkCall, info, mFuncSignatures[i], paramCount+2, paramCount+2); | |
| } | |
| } | |
| } | |
| initScratch(); | |
| return true; | |
| } | |
| // TS -> WASM Thunk | |
| static const char* thunkCall(WasmModuleObject* obj, void* userPtr, int argc, char** argv) | |
| { | |
| FuncUserInfo* userInfo = (FuncUserInfo*)userPtr; | |
| WasmModuleObject* userModule = userInfo->module; | |
| KorkApi::Vm* vm = userModule->getVM(); | |
| StringTableEntry sig = userModule->mFuncSignatures[userInfo->funcIdx]; | |
| StringTableEntry fname = userModule->mFuncNames[userInfo->funcIdx]; | |
| IM3Function func = userModule->mFuncs[userInfo->funcIdx]; | |
| if (!sig || !fname || !func) | |
| return "bad_sig_or_name"; | |
| // Parse signature: <ret>(<params>) | |
| const char* s = sig; | |
| char retCh = 'v'; | |
| if (*s && *s != '(') { retCh = *s; } | |
| while (*s && *s != '(') ++s; | |
| if (*s == '(') ++s; | |
| // Prepare scratch buffer for strings | |
| userModule->resetScratch(); | |
| void* wasmArgv[16]; | |
| U64 wasmArgData[16]; | |
| U32 paramIdx = 0; | |
| argc -= 2; | |
| argv += 2; | |
| if (argc < 0 || argc != m3_GetArgCount(func) || argc > 16) | |
| return "bad_argc"; | |
| for (U32 i=0; i<argc; i++) | |
| { | |
| char t = s[i]; | |
| wasmArgv[i] = &wasmArgData[i]; | |
| void* data = (void*)&wasmArgData[i]; | |
| if (t == 's') | |
| { | |
| // copy argv[paramIdx] into module memory, get offset, pass as decimal string | |
| const char* src = argv[i] ? argv[i] : ""; | |
| U32 len = (U32)strlen(src); | |
| ScratchAlloc alloc = userModule->allocScratch(len + 1); | |
| if (!alloc.ptr) return "scratch_oom"; | |
| memcpy(alloc.ptr, src, len); | |
| alloc.ptr[len] = '\0'; | |
| ((U32*)wasmArgData)[i] = alloc.vmOffset; | |
| } | |
| else if (t == 'i') | |
| { | |
| *(S32*)(&wasmArgData[i]) = dAtoi(argv[i]); | |
| } | |
| else if (t == 'I') | |
| { | |
| *(S64*)(&wasmArgData[i]) = dAtoi(argv[i]); | |
| } | |
| else if (t == 'f') | |
| { | |
| *(F32*)(&wasmArgData[i]) = dAtof(argv[i]); | |
| } | |
| else if (t == 'F') | |
| { | |
| *(F64*)(&wasmArgData[i]) = dAtof(argv[i]); | |
| } | |
| else | |
| { | |
| ((U32*)wasmArgData)[i] = 0; | |
| } | |
| } | |
| // Call the wasm function | |
| M3Result r = m3_Call(func, argc, (const void**)wasmArgv); | |
| if (r) return r; | |
| // Prepare a buffer for returning a string to TorqueScript | |
| KorkApi::ConsoleValue outBufV = vm->getStringFuncBuffer(1024); | |
| char* outBuf = (char*)outBufV.evaluatePtr(vm->getAllocBase()); | |
| if (!outBuf) return "no_vm_buffer"; | |
| // No return? | |
| if (retCh == 'v' || m3_GetRetCount(func) == 0) | |
| { | |
| outBuf[0] = '\0'; | |
| return outBuf; | |
| } | |
| void* retPtr[1] = {}; | |
| if (retCh == 's') // string | |
| { | |
| uint32_t off = 0; | |
| retPtr[0] = &off; | |
| r = m3_GetResults(func, 1, (const void**)&retPtr); | |
| if (r) | |
| { | |
| return r; | |
| } | |
| U32 memSize = 0; | |
| U8* mem = (U8*)m3_GetMemory(userModule->mRuntime, &memSize, 0); | |
| if (!mem || off >= memSize) | |
| { | |
| outBuf[0] = '\0'; | |
| return outBuf; | |
| } | |
| const char* strInMem = (const char*)(mem + off); | |
| dStrncpy(outBuf, strInMem, 1024); | |
| return outBuf; | |
| } | |
| else if (retCh == 'i') | |
| { | |
| S32 value = 0; | |
| retPtr[0] = &value; | |
| r = m3_GetResults(func, 1, (const void**)&retPtr); | |
| if (r) | |
| { | |
| return r; | |
| } | |
| dSprintf(outBuf, 16, "%i", value); | |
| return outBuf; | |
| } | |
| else if (retCh == 'I') | |
| { | |
| S64 value = 0; | |
| retPtr[0] = &value; | |
| r = m3_GetResults(func, 1, (const void**)&retPtr); | |
| if (r) | |
| { | |
| return r; | |
| } | |
| dSprintf(outBuf, 16, "%ll", value); | |
| return outBuf; | |
| } | |
| else if (retCh == 'f') | |
| { | |
| F32 value = 0; | |
| retPtr[0] = &value; | |
| r = m3_GetResults(func, 1, (const void**)&retPtr); | |
| if (r) | |
| { | |
| return r; | |
| } | |
| dSprintf(outBuf, 32, "%.9g", value); | |
| return outBuf; | |
| } | |
| else if (retCh == 'F') | |
| { | |
| F64 value = 0; | |
| retPtr[0] = &value; | |
| r = m3_GetResults(func, 1, (const void**)&retPtr); | |
| if (r) | |
| { | |
| return r; | |
| } | |
| dSprintf(outBuf, 32, "%.17g", value); | |
| return outBuf; | |
| } | |
| else | |
| { | |
| return ""; | |
| } | |
| } | |
| // WASM -> TS Thunk | |
| // Converts input to const char* | |
| static const void* thunkHostCall(IM3Runtime rt, | |
| IM3ImportContext ctx, | |
| uint64_t* sp, // value stack (args in 64-bit slots) | |
| void* mem) | |
| { | |
| const FuncUserInfo* userInfo = (const FuncUserInfo*)ctx->userdata; | |
| WasmModuleObject* userModule = userInfo->module; | |
| StringTableEntry sig = userModule->mHostFuncSignatures[userInfo->funcIdx]; | |
| KorkApi::Vm* vm = userModule->getVM(); | |
| bool returnsString = *sig == 's'; | |
| bool returnsValue = *sig != 'v'; | |
| while (*sig != '\0' && *sig != '(') | |
| sig++; | |
| if (*sig == '(') | |
| sig++; | |
| // discover which import this is and its type | |
| uint32_t argc = m3_GetArgCount(ctx->function); | |
| const char* argv_local[16]; | |
| const char** thunk_argv = &argv_local[2]; | |
| KorkApi::ConsoleValue bufspaceV = vm->getStringFuncBuffer(1024); | |
| char* bufspace = (char*)bufspaceV.evaluatePtr(vm->getAllocBase()); | |
| size_t ofs = 0; | |
| const size_t capacity = 1024-1; | |
| argv_local[0] = NULL; | |
| argv_local[1] = NULL; | |
| for (uint32_t i = 0; i < argc; ++i) | |
| { | |
| M3ValueType t = m3_GetArgType(ctx->function, i); | |
| bool isStr = sig[i] == 's'; | |
| const char* s = ""; | |
| char* bufStart = &bufspace[ofs]; | |
| if (isStr && t == c_m3Type_i32) | |
| { | |
| // Resolve string pointer | |
| U32 memSize = 0; | |
| U8* mem = ((U8*) m3_GetMemory(userModule->mRuntime, &memSize, 0)); | |
| U32 offset = (U32)sp[i]; | |
| if (offset < memSize) | |
| { | |
| s = (const char*)mem + offset; | |
| } | |
| ofs += snprintf(bufStart, capacity-ofs, "%s", s); | |
| } | |
| else if (t == c_m3Type_i32) { int32_t v = (int32_t) sp[i]; ofs += snprintf(bufStart, sizeof(bufspace)-ofs, "%d", v); } | |
| else if (t == c_m3Type_i64) { int64_t v = (int64_t) sp[i]; ofs += snprintf(bufStart, sizeof(bufspace)-ofs, "%lld",(long long)v); } | |
| else if (t == c_m3Type_f32) { float v = *(float*) (&sp[i]); ofs += snprintf(bufStart, sizeof(bufspace)-ofs, "%.9g", v); } | |
| else if (t == c_m3Type_f64) { double v = *(double*)(&sp[i]); ofs += snprintf(bufStart, sizeof(bufspace)-ofs, "%.17g",v); } | |
| bufspace[ofs++] = '\0'; | |
| thunk_argv[i] = bufStart; | |
| } | |
| KorkApi::ConsoleValue retV; | |
| vm->callObjectFunction(userModule->getVMObject(), | |
| userModule->mHostFuncs[userInfo->funcIdx], | |
| argc+2, &argv_local[0], retV); | |
| // marshal result(s) back to wasm | |
| // If callee expects a value, set it on the stack tail according to return type. | |
| if (m3_GetRetCount(ctx->function) == 0) | |
| { | |
| return m3Err_none; | |
| } | |
| M3ValueType rt0 = m3_GetRetType(ctx->function, 0); | |
| if (rt0 != c_m3Type_i32) | |
| { | |
| returnsString = false; | |
| } | |
| if (returnsString) | |
| { | |
| // Prepare scratch buffer for strings | |
| userModule->resetScratch(); | |
| // Convert to string | |
| const char* strValue = vm->valueAsString(retV); | |
| U32 size = strlen(strValue); | |
| ScratchAlloc alloc = userModule->allocScratch(size); | |
| if (alloc.ptr) | |
| { | |
| memcpy(alloc.ptr, strValue, size); | |
| alloc.ptr[size] = '\0'; | |
| } | |
| *(uint32_t*)sp = alloc.vmOffset; | |
| } | |
| else | |
| { | |
| if (rt0 == c_m3Type_i32) { | |
| *(int32_t*)sp = vm->valueAsInt(retV); | |
| } else if (rt0 == c_m3Type_i64) { | |
| *(int64_t*)sp = vm->valueAsInt(retV); | |
| } else if (rt0 == c_m3Type_f32) { | |
| *(F32*)sp = vm->valueAsFloat(retV); | |
| } else if (rt0 == c_m3Type_f64) { | |
| *(F64*)sp = vm->valueAsFloat(retV); | |
| } else { | |
| return "m3Err_trapReturnType"; | |
| } | |
| } | |
| return m3Err_none; | |
| } | |
| }; | |
| IMPLEMENT_CONOBJECT(WasmModuleObject); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment