#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _DEBUG // #define DEBUG_OUTPUT 1 #endif // #define SHOW_CALL_TIME 1 namespace xrpl { namespace { void printWasmError(std::string_view msg, wasm_trap_t* trap, beast::Journal jlog) { #ifdef DEBUG_OUTPUT auto& j = std::cerr; #else auto j = jlog.warn(); if (jlog.active(beast::Severity::Warning)) #endif { wasm_byte_vec_t errorMessage WASM_EMPTY_VEC; if (trap != nullptr) wasm_trap_message(trap, &errorMessage); if (errorMessage.size != 0u) { j << "WASMI Error: " << msg << ", " << std::string_view(errorMessage.data, errorMessage.size - 1); } else { j << "WASMI Error: " << msg; } if (errorMessage.size != 0u) wasm_byte_vec_delete(&errorMessage); } if (trap != nullptr) wasm_trap_delete(trap); #ifdef DEBUG_OUTPUT j << std::endl; #endif } // LCOV_EXCL_STOP } // namespace struct WasmiRuntimeWrapper : public WasmRuntimeWrapper { InstanceWrapper& iw; WasmiRuntimeWrapper(InstanceWrapper& iw) : iw(iw) { } Wmem getMem() override { return iw.getMem(); } std::int64_t getGas() override { return iw.getGas(); } std::int64_t setGas(std::int64_t gas) override { return iw.setGas(gas); } }; InstancePtr InstanceWrapper::init( StorePtr& s, ModulePtr& m, WasmExternVec& expt, WasmExternVec const& imports, beast::Journal j) { wasm_trap_t* trap = nullptr; InstancePtr mi = InstancePtr( wasm_instance_new(s.get(), m.get(), imports.get(), &trap), &wasm_instance_delete); if (!mi || (trap != nullptr)) { printWasmError("can't create instance", trap, j); throw std::runtime_error("can't create instance"); } wasm_instance_exports(mi.get(), expt.get()); return mi; } InstanceWrapper::InstanceWrapper() : instance(nullptr, &wasm_instance_delete) { } // LCOV_EXCL_START InstanceWrapper::InstanceWrapper(InstanceWrapper&& o) : instance(nullptr, &wasm_instance_delete) { *this = std::move(o); } // LCOV_EXCL_STOP InstanceWrapper::InstanceWrapper( StorePtr& s, ModulePtr& m, WasmExternVec const& imports, beast::Journal j) : store(s.get()), instance(init(s, m, exports, imports, j)), j(j) { } InstanceWrapper& InstanceWrapper::operator=(InstanceWrapper&& o) { if (this == &o) return *this; // LCOV_EXCL_LINE store = o.store; o.store = nullptr; exports = std::move(o.exports); memIdx = o.memIdx; o.memIdx = -1; instance = std::move(o.instance); j = o.j; return *this; } InstanceWrapper:: operator bool() const { return static_cast(instance); } FuncInfo InstanceWrapper::getFunc(std::string_view funcName, WasmExporttypeVec const& exportTypes) const { wasm_func_t const* f = nullptr; wasm_functype_t const* ft = nullptr; if (!instance) throw std::runtime_error("no instance"); // LCOV_EXCL_LINE if (exportTypes.empty()) throw std::runtime_error("no export"); // LCOV_EXCL_LINE if (exportTypes.size() != exports.size()) throw std::runtime_error("invalid export"); // LCOV_EXCL_LINE for (unsigned i = 0; i < exportTypes.size(); ++i) { auto const* expType(exportTypes[i]); wasm_name_t const* name = wasm_exporttype_name(expType); wasm_externtype_t const* exnType = wasm_exporttype_type(expType); if (wasm_externtype_kind(exnType) == WASM_EXTERN_FUNC) { if (funcName != std::string_view(name->data, name->size)) continue; auto const* exn(exports[i]); if (wasm_extern_kind(exn) != WASM_EXTERN_FUNC) throw std::runtime_error("invalid export"); // LCOV_EXCL_LINE ft = wasm_externtype_as_functype_const(exnType); f = wasm_extern_as_func_const(exn); break; } } if ((f == nullptr) || (ft == nullptr)) throw std::runtime_error("can't find function <" + std::string(funcName) + ">"); return {f, ft}; } Wmem InstanceWrapper::getMem() const { if (memIdx >= 0) { auto* e(exports[memIdx]); wasm_memory_t* mem = wasm_extern_as_memory(e); return { .p = reinterpret_cast(wasm_memory_data(mem)), .s = wasm_memory_data_size(mem)}; } wasm_memory_t* mem = nullptr; for (int i = 0; i < exports.size(); ++i) { auto* e(exports[i]); if (wasm_extern_kind(e) == WASM_EXTERN_MEMORY) { memIdx = i; mem = wasm_extern_as_memory(e); break; } } if (mem == nullptr) return {}; // LCOV_EXCL_LINE return { .p = reinterpret_cast(wasm_memory_data(mem)), .s = wasm_memory_data_size(mem)}; } std::int64_t InstanceWrapper::getGas() const { if (store == nullptr) return -1; // LCOV_EXCL_LINE std::uint64_t gas = 0; wasm_store_get_fuel(store, &gas); return static_cast(gas); } std::int64_t InstanceWrapper::setGas(std::int64_t gas) const { if (store == nullptr) return -1; // LCOV_EXCL_LINE if (gas < 0) gas = std::numeric_limits::max(); wasmi_error_t* err = wasm_store_set_fuel(store, static_cast(gas)); if (err != nullptr) { // LCOV_EXCL_START printWasmError("Can't set instance gas", nullptr, j); wasmi_error_delete(err); return -1; // LCOV_EXCL_STOP } return gas; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////// ModulePtr ModuleWrapper::init(StorePtr& s, Bytes const& wasmBin, beast::Journal j) { wasm_byte_vec_t const code{.size = wasmBin.size(), .data = (char*)(wasmBin.data())}; ModulePtr m = ModulePtr(wasm_module_new(s.get(), &code), &wasm_module_delete); if (!m) throw std::runtime_error("can't create module"); return m; } // LCOV_EXCL_START ModuleWrapper::ModuleWrapper() : module(nullptr, &wasm_module_delete) { } ModuleWrapper::ModuleWrapper(ModuleWrapper&& o) : module(nullptr, &wasm_module_delete) { *this = std::move(o); } // LCOV_EXCL_STOP ModuleWrapper::ModuleWrapper( StorePtr& s, Bytes const& wasmBin, bool instantiate, ImportVec const& imports, beast::Journal j) : module(init(s, wasmBin, j)), j(j) { wasm_module_exports(module.get(), exportTypes.get()); auto wimports = buildImports(s, imports); if (instantiate) { addInstance(s, wimports); } } // LCOV_EXCL_START ModuleWrapper& ModuleWrapper::operator=(ModuleWrapper&& o) { if (this == &o) return *this; module = std::move(o.module); instanceWrap = std::move(o.instanceWrap); exportTypes = std::move(o.exportTypes); j = o.j; return *this; } ModuleWrapper:: operator bool() const { return instanceWrap; } // LCOV_EXCL_STOP static WasmValtypeVec makeImpParams(WasmImportFunc const& imp) { auto const paramSize = imp.params.size(); if (paramSize == 0u) return {}; WasmValtypeVec v(paramSize); for (unsigned i = 0; i < paramSize; ++i) { auto const vt = imp.params[i]; switch (vt) { case WasmTypes::WtI32: v[i] = wasm_valtype_new_i32(); break; case WasmTypes::WtI64: v[i] = wasm_valtype_new_i64(); break; // LCOV_EXCL_START default: throw std::runtime_error("invalid import type"); // LCOV_EXCL_STOP } } return v; } static WasmValtypeVec makeImpReturn(WasmImportFunc const& imp) { if (!imp.result) return {}; // LCOV_EXCL_LINE WasmValtypeVec v(1); switch (*imp.result) { case WasmTypes::WtI32: v[0] = wasm_valtype_new_i32(); break; // LCOV_EXCL_START case WasmTypes::WtI64: v[0] = wasm_valtype_new_i64(); break; default: throw std::runtime_error("invalid return type"); // LCOV_EXCL_STOP } return v; } WasmExternVec ModuleWrapper::buildImports(StorePtr& s, ImportVec const& imports) const { WasmImporttypeVec importTypes; wasm_module_imports(module.get(), importTypes.get()); if (importTypes.empty()) return {}; if (imports.empty()) Throw("Empty imports"); WasmExternVec wimports(importTypes.size()); unsigned impCnt = 0; for (unsigned i = 0; i < importTypes.size(); ++i) { wasm_importtype_t const* importType = importTypes[i]; // wasm_name_t const* mn = wasm_importtype_module(importtype); // auto modName = std::string_view(mn->data, mn->num_elems); wasm_name_t const* fn = wasm_importtype_name(importType); auto fieldName = std::string_view(fn->data, fn->size); wasm_externkind_t const itype = wasm_externtype_kind(wasm_importtype_type(importType)); if ((itype) != WASM_EXTERN_FUNC) { Throw( "Invalid import type " + std::to_string(itype)); // LCOV_EXCL_LINE } // for multi-module support // if ((W_ENV != modName) && (W_HOST_LIB != modName)) // continue; auto const it = imports.find(fieldName); if (it == imports.end()) { printWasmError("Import not found: " + std::string(fieldName), nullptr, j); continue; // print all missed import } auto const& obj = it->second; auto const& imp = obj.second; WasmValtypeVec params(makeImpParams(imp)); WasmValtypeVec results(makeImpReturn(imp)); std::unique_ptr const ftype( wasm_functype_new(params.get(), results.get()), &wasm_functype_delete); params.release(); results.release(); wasm_func_t* func = wasm_func_new_with_env( s.get(), ftype.get(), reinterpret_cast(imp.wrap), (void*)&obj, nullptr); if (func == nullptr) { Throw( "can't create import function " + std::string(imp.name)); // LCOV_EXCL_LINE } wimports[i] = wasm_func_as_extern(func); ++impCnt; } if (impCnt != importTypes.size()) { printWasmError( std::string("Imports not finished: ") + std::to_string(impCnt) + "/" + std::to_string(importTypes.size()), nullptr, j); Throw("Missing imports"); } return wimports; } FuncInfo ModuleWrapper::getFunc(std::string_view funcName) const { return instanceWrap.getFunc(funcName, exportTypes); } wasm_functype_t* ModuleWrapper::getFuncType(std::string_view funcName) const { for (size_t i = 0; i < exportTypes.size(); i++) { auto const* expType(exportTypes[i]); wasm_name_t const* name = wasm_exporttype_name(expType); wasm_externtype_t const* exnType = wasm_exporttype_type(expType); if (wasm_externtype_kind(exnType) == WASM_EXTERN_FUNC && funcName == std::string_view(name->data, name->size)) { return wasm_externtype_as_functype(const_cast(exnType)); } } throw std::runtime_error("can't find function <" + std::string(funcName) + ">"); } Wmem ModuleWrapper::getMem() const { return instanceWrap.getMem(); } InstanceWrapper& ModuleWrapper::getInstance(int) { return instanceWrap; } int ModuleWrapper::addInstance(StorePtr& s, WasmExternVec const& imports) { instanceWrap = {s, module, imports, j}; return 0; } // int // my_module_t::delInstance(int i) // { // if (i >= mod_inst.size()) // return -1; // if (!mod_inst[i]) // mod_inst[i] = my_mod_inst_t(); // return i; // } std::int64_t ModuleWrapper::getGas() const { return instanceWrap ? instanceWrap.getGas() : -1; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // void // WasmiEngine::clearModules() // { // modules.clear(); // store.reset(); // to free the memory before creating new store // store = {wasm_store_new(engine.get()), &wasm_store_delete}; // } std::unique_ptr WasmiEngine::init() { wasm_config_t* config = wasm_config_new(); if (config == nullptr) { return std::unique_ptr{ nullptr, &wasm_engine_delete}; // LCOV_EXCL_LINE } wasmi_config_consume_fuel_set(config, true); wasmi_config_ignore_custom_sections_set(config, true); wasmi_config_wasm_mutable_globals_set(config, false); wasmi_config_wasm_multi_value_set(config, false); wasmi_config_wasm_sign_extension_set(config, false); wasmi_config_wasm_saturating_float_to_int_set(config, false); wasmi_config_wasm_bulk_memory_set(config, false); wasmi_config_wasm_reference_types_set(config, false); wasmi_config_wasm_tail_call_set(config, false); wasmi_config_wasm_extended_const_set(config, false); wasmi_config_floats_set(config, false); wasmi_config_wasm_multi_memory_set(config, false); wasmi_config_wasm_custom_page_sizes_set(config, false); wasmi_config_wasm_memory64_set(config, false); wasmi_config_wasm_wide_arithmetic_set(config, false); return std::unique_ptr( wasm_engine_new_with_config(config), &wasm_engine_delete); } WasmiEngine::WasmiEngine() : engine_(init()), store_(nullptr, &wasm_store_delete) { } int WasmiEngine::addModule( Bytes const& wasmCode, bool instantiate, ImportVec const& imports, int64_t gas) { moduleWrap_.reset(); store_.reset(); // to free the memory before creating new store store_ = {wasm_store_new_with_memory_max_pages(engine_.get(), maxPages), &wasm_store_delete}; if (gas < 0) gas = std::numeric_limits::max(); wasmi_error_t* err = wasm_store_set_fuel(store_.get(), static_cast(gas)); if (err != nullptr) { // LCOV_EXCL_START printWasmError("Error setting gas", nullptr, j_); wasmi_error_delete(err); throw std::runtime_error("can't set gas"); // LCOV_EXCL_STOP } moduleWrap_ = std::make_unique(store_, wasmCode, instantiate, imports, j_); if (!moduleWrap_) throw std::runtime_error("can't create module wrapper"); // LCOV_EXCL_LINE return moduleWrap_ ? 0 : -1; } // int // WasmiEngine::addInstance() // { // return module->addInstance(store.get()); // } FuncInfo WasmiEngine::getFunc(std::string_view funcName) const { return moduleWrap_->getFunc(funcName); } std::vector WasmiEngine::convertParams(std::vector const& params) { std::vector v; v.reserve(params.size()); for (auto const& p : params) { switch (p.type) { case WasmTypes::WtI32: v.push_back(WASM_I32_VAL(p.of.i32)); break; // LCOV_EXCL_START case WasmTypes::WtI64: v.push_back(WASM_I64_VAL(p.of.i64)); break; default: throw std::runtime_error( "unknown parameter type: " + std::to_string(static_cast(p.type))); break; // LCOV_EXCL_STOP } } return v; } int WasmiEngine::compareParamTypes(wasm_valtype_vec_t const* ftp, std::vector const& p) { if (ftp->size != p.size()) return std::min(ftp->size, p.size()); for (unsigned i = 0; i < ftp->size; ++i) { auto const t1 = wasm_valtype_kind(ftp->data[i]); auto const t2 = p[i].kind; if (t1 != t2) return i; } return -1; } // LCOV_EXCL_START void WasmiEngine::addParam(std::vector& in, int32_t p) { in.emplace_back(); auto& el(in.back()); memset(&el, 0, sizeof(el)); el = WASM_I32_VAL(p); // WASM_I32; } // LCOV_EXCL_STOP void WasmiEngine::addParam(std::vector& in, int64_t p) { in.emplace_back(); auto& el(in.back()); el = WASM_I64_VAL(p); } template WasmiResult WasmiEngine::call(std::string_view func, Types&&... args) { // Lookup our export function auto f = getFunc(func); return call(f, std::forward(args)...); } template WasmiResult WasmiEngine::call(FuncInfo const& f, Types&&... args) { std::vector in; return call(f, in, std::forward(args)...); } #ifdef SHOW_CALL_TIME static inline uint64_t usecs() { uint64_t x = std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch()) .count(); return x; } #endif template WasmiResult WasmiEngine::call(FuncInfo const& f, std::vector& in) { WasmiResult ret(NR); wasm_val_vec_t const inv = in.empty() ? wasm_val_vec_t WASM_EMPTY_VEC : wasm_val_vec_t{.size = in.size(), .data = in.data()}; #ifdef SHOW_CALL_TIME auto const start = usecs(); #endif wasm_trap_t* trap = wasm_func_call(f.first, &inv, ret.r.get()); #ifdef SHOW_CALL_TIME auto const finish = usecs(); auto const delta_ms = (finish - start) / 1000; std::cout << "wasm_func_call: " << delta_ms << "ms" << std::endl; #endif if (trap) { ret.f = true; printWasmError("failure to call func", trap, j_); } return ret; } template WasmiResult WasmiEngine::call(FuncInfo const& f, std::vector& in, std::int32_t p, Types&&... args) { addParam(in, p); return call(f, in, std::forward(args)...); } template WasmiResult WasmiEngine::call(FuncInfo const& f, std::vector& in, std::int64_t p, Types&&... args) { addParam(in, p); return call(f, in, std::forward(args)...); } template WasmiResult WasmiEngine::call(FuncInfo const& f, std::vector& in, Bytes const& p, Types&&... args) { return call(f, in, p.data(), p.size(), std::forward(args)...); } static inline void checkImports(ImportVec const& imports, HostFunctions* hfs) { for (auto const& obj : imports) { if (hfs != obj.second.first) Throw("Imports hf unsync"); } } Expected, TER> WasmiEngine::run( Bytes const& wasmCode, HostFunctions& hfs, int64_t gas, std::string_view funcName, std::vector const& params, ImportVec const& imports, beast::Journal j) { if (gas <= 0) return Unexpected(temBAD_AMOUNT); try { checkImports(imports, &hfs); return runHlp(wasmCode, hfs, gas, funcName, params, imports, j); } catch (std::exception const& e) { printWasmError(std::string("exception: ") + e.what(), nullptr, j); } // LCOV_EXCL_START catch (...) { printWasmError(std::string("exception: unknown"), nullptr, j); } // LCOV_EXCL_STOP return Unexpected(tecFAILED_PROCESSING); } namespace { struct CustomSection { std::string_view name; std::span payload; }; struct Version { std::string_view name; std::string_view version; Version(std::string_view theName, std::string_view theVersion) : name{theName}, version{theVersion} { } }; uint32_t readLEB128(Bytes const& wasmCode, size_t& offset) { auto result = uint32_t{}; auto shift = uint32_t{}; while (offset < wasmCode.size()) { auto byte = wasmCode[offset++]; result |= static_cast(byte & (shift < 28 ? 0x7Fu : 0x0Fu)) << shift; if ((byte & 0x80) == 0) { break; } shift += 7; if (shift >= 32) { // Drain the rest of the bytes for this leb. while (offset < wasmCode.size()) { if ((wasmCode[offset++] & 0x80) == 0) { break; } } break; } } return result; } template void filterCustomSections(Bytes const& wasmCode, Filter&& filter) { auto offset = size_t{8}; // Skip Magic number and Version if (wasmCode.size() <= offset) { // There is nothing to parse. return; } while (offset < wasmCode.size()) { auto sectionId = wasmCode[offset++]; auto sectionSize = readLEB128(wasmCode, offset); auto nextSection = offset + sectionSize; if (nextSection > wasmCode.size()) { break; } if (sectionId == 0) { // Wasm custom section marker. auto customSection = CustomSection{}; auto size = readLEB128(wasmCode, offset); if (offset + size > wasmCode.size() || offset + size > nextSection) { return; } customSection.name = std::string_view{reinterpret_cast(wasmCode.data()) + offset, size}; offset += size; if (offset >= nextSection) { offset = nextSection; continue; } size = nextSection - offset; customSection.payload = std::span{ reinterpret_cast(wasmCode.data()) + offset, size}; if (filter(customSection)) { return; } } offset = nextSection; } } std::vector extractVersionInfo(Bytes const& wasmCode) { static constexpr auto kCommonLib = "xrpl-common-stdlib-version"; static constexpr auto kEscrowLib = "xrpl-escrow-stdlib-version"; auto versions = std::vector{}; filterCustomSections(wasmCode, [&](auto const& section) { if (section.name == kCommonLib || section.name == kEscrowLib) { versions.emplace_back( section.name, std::string_view{section.payload.data(), section.payload.size()}); } // Just read until we have found all the information we are looking for. return versions.size() == 2; }); return versions; } } // namespace Expected, TER> WasmiEngine::runHlp( Bytes const& wasmCode, HostFunctions& hfs, int64_t gas, std::string_view funcName, std::vector const& params, ImportVec const& imports, beast::Journal j) { // currently only 1 module support, possible parallel UT run std::scoped_lock const lg(m_); j_ = j; if (wasmCode.empty()) throw std::runtime_error("empty module"); if (!hfs.checkSelf()) throw std::runtime_error("hfs isn't clean"); for (auto const& version : extractVersionInfo(wasmCode)) { j_.debug() << "Module version: " << version.name << " " << version.version; } // Create and instantiate the module. [[maybe_unused]] int const m = addModule(wasmCode, true, imports, gas); if (!moduleWrap_ || !moduleWrap_->instanceWrap) throw std::runtime_error("no instance"); // LCOV_EXCL_LINE auto clear = [](HostFunctions* p) { p->setRT(nullptr); }; std::unique_ptr const clearGuard(&hfs, clear); WasmiRuntimeWrapper iw(getRT()); hfs.setRT(&iw); // Call main auto const f = getFunc(!funcName.empty() ? funcName : "_start"); auto const* ftp = wasm_functype_params(f.second); // not const because passed directly to VM function (which accept non // const) auto p = convertParams(params); if (int const comp = compareParamTypes(ftp, p); comp >= 0) throw std::runtime_error("invalid parameter type #" + std::to_string(comp)); auto const res = call<1>(f, p); if (res.f) { throw std::runtime_error("<" + std::string(funcName) + "> failure"); } if (res.r.empty()) { throw std::runtime_error( "<" + std::string(funcName) + "> return nothing"); // LCOV_EXCL_LINE } if (res.r[0].kind != WASM_I32) { throw std::runtime_error( "<" + std::string(funcName) + "> return type mismatch, ret: " + std::to_string(static_cast(res.r[0].kind))); } if (gas == -1) gas = std::numeric_limits::max(); WasmResult const ret{.result = res.r[0].of.i32, .cost = gas - moduleWrap_->getGas()}; // #ifdef DEBUG_OUTPUT // auto& j = std::cerr; // #else // auto j = j_.debug(); // #endif // j << "WASMI Res: " << ret.result << " cost: " << ret.cost << std::endl; return ret; } NotTEC WasmiEngine::check( Bytes const& wasmCode, HostFunctions& hfs, std::string_view funcName, std::vector const& params, ImportVec const& imports, beast::Journal j) { try { checkImports(imports, &hfs); return checkHlp(wasmCode, hfs, funcName, params, imports, j); } catch (std::exception const& e) { printWasmError(std::string("exception: ") + e.what(), nullptr, j); } // LCOV_EXCL_START catch (...) { printWasmError(std::string("exception: unknown"), nullptr, j); } // LCOV_EXCL_STOP return temINVALID_BYTECODE; } NotTEC WasmiEngine::checkHlp( Bytes const& wasmCode, HostFunctions& hfs, std::string_view funcName, std::vector const& params, ImportVec const& imports, beast::Journal j) { // currently only 1 module support, possible parallel UT run std::scoped_lock const lg(m_); j_ = j; // Create and instantiate the module. if (wasmCode.empty()) throw std::runtime_error("empty module"); int const m = addModule(wasmCode, false, imports, -1); if ((m < 0) || !moduleWrap_) throw std::runtime_error("no module"); // LCOV_EXCL_LINE // Looking for a func and compare parameter types auto const f = moduleWrap_->getFuncType(!funcName.empty() ? funcName : "_start"); auto const* ftp = wasm_functype_params(f); auto const p = convertParams(params); if (int const comp = compareParamTypes(ftp, p); comp >= 0) throw std::runtime_error("invalid parameter type #" + std::to_string(comp)); return tesSUCCESS; } // LCOV_EXCL_START std::int64_t WasmiEngine::getGas() const { return moduleWrap_ ? moduleWrap_->getGas() : -1; } // LCOV_EXCL_STOP Wmem WasmiEngine::getMem() const { return moduleWrap_ ? moduleWrap_->getMem() : Wmem(); } InstanceWrapper& WasmiEngine::getRT(int m, int i) const { if (!moduleWrap_) throw std::runtime_error("no module"); return moduleWrap_->getInstance(i); } wasm_trap_t* WasmiEngine::newTrap(std::string const& txt) { static char empty[1] = {0}; wasm_message_t msg = {.size = 1, .data = empty}; if (!txt.empty()) wasm_name_new(&msg, txt.size() + 1, txt.c_str()); // include 0 wasm_trap_t* trap = wasm_trap_new(store_.get(), &msg); // NOLINT if (!txt.empty()) wasm_byte_vec_delete(&msg); return trap; } // LCOV_EXCL_START beast::Journal WasmiEngine::getJournal() const { return j_; } // LCOV_EXCL_STOP } // namespace xrpl