| #ifdef WITH_RIVE_SCRIPTING_WASM | |
| #include "rive/wasm/wamr_state_transplant.hpp" | |
| // The one TU reading wamr instance internals; runtime builds compile it | |
| // empty and use the header's inline stub. | |
| #ifdef RIVE_WASM_TIER_LADDER | |
| #include "aot_runtime.h" | |
| #include "wasm_runtime.h" | |
| #include <cstring> | |
| #include <vector> | |
| namespace rive | |
| { | |
| namespace | |
| { | |
| struct GlobalRef | |
| { | |
| uint8_t type = 0; | |
| uint32_t offset = 0; | |
| }; | |
| static size_t globalSize(uint8_t type) | |
| { | |
| switch (type) | |
| { | |
| case VALUE_TYPE_I32: | |
| case VALUE_TYPE_F32: | |
| return 4; | |
| case VALUE_TYPE_I64: | |
| case VALUE_TYPE_F64: | |
| return 8; | |
| #if WASM_ENABLE_SIMD != 0 || WASM_ENABLE_SIMDE != 0 | |
| case VALUE_TYPE_V128: | |
| return 16; | |
| #endif | |
| default: | |
| return 0; | |
| } | |
| } | |
| // The two representations agree on the wasm's global index space but not on | |
| // data offsets: the interp loader assigns natural-size offsets at load, the | |
| // aot compiler assigns its own at compile. Enumerate through each side's own | |
| // metadata. | |
| static bool collectGlobals(WASMModuleInstance* inst, | |
| std::vector<GlobalRef>& out, | |
| std::string& error) | |
| { | |
| if (inst->module_type == Wasm_Module_Bytecode) | |
| { | |
| WASMModuleInstanceExtra* extra = (WASMModuleInstanceExtra*)inst->e; | |
| for (uint32_t i = 0; i < extra->global_count; i++) | |
| { | |
| out.push_back( | |
| {extra->globals[i].type, extra->globals[i].data_offset}); | |
| } | |
| return true; | |
| } | |
| if (inst->module_type == Wasm_Module_AoT) | |
| { | |
| AOTModule* module = (AOTModule*)((AOTModuleInstance*)inst)->module; | |
| for (uint32_t i = 0; i < module->import_global_count; i++) | |
| { | |
| out.push_back({module->import_globals[i].type.val_type, | |
| module->import_globals[i].data_offset}); | |
| } | |
| for (uint32_t i = 0; i < module->global_count; i++) | |
| { | |
| out.push_back({module->globals[i].type.val_type, | |
| module->globals[i].data_offset}); | |
| } | |
| return true; | |
| } | |
| error = "unknown module representation"; | |
| return false; | |
| } | |
| } // namespace | |
| bool wamrTransplantState(wasm_module_inst_t source, | |
| wasm_module_inst_t destination, | |
| std::string& error) | |
| { | |
| WASMModuleInstance* src = (WASMModuleInstance*)source; | |
| WASMModuleInstance* dst = (WASMModuleInstance*)destination; | |
| // Memories: grow the fresh instance up to the source's live size, then | |
| // copy bytes. | |
| if (src->memory_count != dst->memory_count) | |
| { | |
| error = "memory count mismatch"; | |
| return false; | |
| } | |
| for (uint32_t i = 0; i < src->memory_count; i++) | |
| { | |
| WASMMemoryInstance* srcMem = src->memories[i]; | |
| WASMMemoryInstance* dstMem = dst->memories[i]; | |
| if (srcMem->cur_page_count > dstMem->cur_page_count) | |
| { | |
| if (!wasm_runtime_enlarge_memory(destination, | |
| srcMem->cur_page_count - | |
| dstMem->cur_page_count)) | |
| { | |
| error = "destination memory grow failed"; | |
| return false; | |
| } | |
| // Growth may reallocate the instance's memory table entry. | |
| dstMem = dst->memories[i]; | |
| } | |
| if (dstMem->memory_data_size < srcMem->memory_data_size) | |
| { | |
| error = "destination memory smaller than source after grow"; | |
| return false; | |
| } | |
| memcpy(dstMem->memory_data, | |
| srcMem->memory_data, | |
| srcMem->memory_data_size); | |
| } | |
| // Globals: per-global typed copy through each side's own offsets. | |
| std::vector<GlobalRef> srcGlobals; | |
| std::vector<GlobalRef> dstGlobals; | |
| if (!collectGlobals(src, srcGlobals, error) || | |
| !collectGlobals(dst, dstGlobals, error)) | |
| { | |
| return false; | |
| } | |
| if (srcGlobals.size() != dstGlobals.size()) | |
| { | |
| error = "global count mismatch"; | |
| return false; | |
| } | |
| for (size_t i = 0; i < srcGlobals.size(); i++) | |
| { | |
| if (srcGlobals[i].type != dstGlobals[i].type) | |
| { | |
| error = "global type mismatch"; | |
| return false; | |
| } | |
| size_t size = globalSize(srcGlobals[i].type); | |
| if (size == 0) | |
| { | |
| error = "unsupported global type"; | |
| return false; | |
| } | |
| memcpy(dst->global_data + dstGlobals[i].offset, | |
| src->global_data + srcGlobals[i].offset, | |
| size); | |
| } | |
| // Tables: entries are function indexes in both representations (non-GC | |
| // builds), so index copy is representation-neutral. | |
| if (src->table_count != dst->table_count) | |
| { | |
| error = "table count mismatch"; | |
| return false; | |
| } | |
| for (uint32_t i = 0; i < src->table_count; i++) | |
| { | |
| WASMTableInstance* srcTable = src->tables[i]; | |
| WASMTableInstance* dstTable = dst->tables[i]; | |
| if (dstTable->cur_size < srcTable->cur_size) | |
| { | |
| error = "destination table smaller than source"; | |
| return false; | |
| } | |
| memcpy(dstTable->elems, | |
| srcTable->elems, | |
| sizeof(table_elem_type_t) * srcTable->cur_size); | |
| } | |
| return true; | |
| } | |
| } // namespace rive | |
| #endif // RIVE_WASM_TIER_LADDER | |
| #endif // WITH_RIVE_SCRIPTING_WASM |