#include #include #include #include #include #include #include #include // IWYU pragma: keep #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _DEBUG // #define DEBUG_OUTPUT 1 #endif #include #include namespace xrpl::test { bool testGetDataIncrement(); using Add_proto = int32_t(int32_t, int32_t); static wasm_trap_t* add(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) { int32_t const val1 = params->data[0].of.i32; int32_t const val2 = params->data[1].of.i32; // printf("Host function \"Add\": %d + %d\n", Val1, Val2); results->data[0] = WASM_I32_VAL(val1 + val2); return nullptr; } std::vector hexToBytes(std::string const& hex) { auto const ws = boost::algorithm::unhex(hex); return Bytes(ws.begin(), ws.end()); } template unsigned uleb128(IT& it, T val) { unsigned count = 0; do { std::uint8_t byte = val & 0x7f; val >>= 7; if (val) byte |= 0x80; *it++ = byte; ++count; } while (val != 0); return count; } template std::pair uleb128(IT&& it) { static_assert(sizeof(*it) == 1, "invalid iterator type"); std::uint64_t val = 0; std::uint64_t byte = 0; unsigned shift = 0; unsigned count = 0; do { if (shift > (sizeof(std::uint64_t) * 8) - 7) return {0, 0}; byte = *it++; val |= (byte & 0x7F) << shift; shift += 7; ++count; } while (byte >= 0x80); return {val, count}; } static std::pair getSection(Bytes const& module, std::uint8_t n) { static std::uint8_t const kHdr[] = {0x00, 0x61, 0x73, 0x6D}; static std::uint8_t const kVer[] = {0x01, 0x00, 0x00, 0x00}; static std::uint8_t const kLastSec = 12; // sections: // 0: "Custom", 1: "Type", 2: "Import", 3: "Function", 4: "Table", 5: "Memory", 6: "Global", // 7: "Export", 8: "Start", 9: "Element", 10: "Code", 11: "Data", 12: "DataCount" if (module.size() < sizeof(kHdr) + sizeof(kVer) + 2) return {0, 0}; if (memcmp(module.data(), kHdr, sizeof(kHdr)) != 0) return {0, 0}; if (memcmp(module.data() + sizeof(kHdr), kVer, sizeof(kVer)) != 0) return {0, 0}; unsigned pos = sizeof(kHdr) + sizeof(kVer); // sections start for (; pos < module.size();) { auto const start = pos; std::uint8_t const byte = module[pos++]; if (byte > kLastSec) return {0, 0}; auto [sz, cnt] = uleb128(module.cbegin() + pos); if (cnt == 0u) return {0, 0}; if (pos + cnt + sz > module.size()) return {0, 0}; pos += cnt + sz; if (byte == n) return {start, pos}; } return {0, 0}; } static std::optional runFinishFunction(std::string const& code) { auto& engine = WasmEngine::instance(); auto const wasm = hexToBytes(code); HostFunctions hfs; auto const re = engine.run(wasm, hfs, 10'000'000, "finish"); if (re.has_value()) { return std::optional(re->result); } return std::nullopt; } static bool finishFunctionReturns(std::string const& code, int32_t expected) { auto const result = runFinishFunction(code); return result.has_value() && *result == expected; } struct Wasm_test : public beast::unit_test::Suite { void checkResult( Expected, TER> re, int32_t expectedResult, int64_t expectedCost, std::source_location const location = std::source_location::current()) { auto const lineStr = " (" + std::to_string(location.line()) + ")"; if (BEAST_EXPECTS(re.has_value(), transToken(re.error()) + lineStr)) { BEAST_EXPECTS(re->result == expectedResult, std::to_string(re->result) + lineStr); BEAST_EXPECTS(re->cost == expectedCost, std::to_string(re->cost) + lineStr); } } void testGetDataHelperFunctions() { testcase("getData helper functions"); BEAST_EXPECT(testGetDataIncrement()); } void testWasmLib() { testcase("wasm lib test"); // clang-format off /* The WASM module buffer. */ Bytes const wasm = {/* WASM header */ 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, /* Type section */ 0x01, 0x07, 0x01, /* function type {i32, i32} -> {i32} */ 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, /* Import section */ 0x02, 0x13, 0x01, /* module name: "extern" */ 0x06, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6E, /* extern name: "func-add" */ 0x08, 0x66, 0x75, 0x6E, 0x63, 0x2D, 0x61, 0x64, 0x64, /* import desc: func 0 */ 0x00, 0x00, /* Function section */ 0x03, 0x02, 0x01, 0x00, /* Export section */ 0x07, 0x0A, 0x01, /* export name: "addTwo" */ 0x06, 0x61, 0x64, 0x64, 0x54, 0x77, 0x6F, /* export desc: func 0 */ 0x00, 0x01, /* Code section */ 0x0A, 0x0A, 0x01, /* code body */ 0x08, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0x00, 0x0B}; // clang-format on auto& vm = WasmEngine::instance(); HostFunctions hfs; ImportVec imports; WasmImpFunc(imports, "func-add", reinterpret_cast(&add), &hfs); auto re = vm.run(wasm, hfs, 10'000'000, "addTwo", wasmParams(1234, 5678), imports); // if (res) printf("invokeAdd get the result: %d\n", res.value()); checkResult(re, 6'912, 59); } void testBadWasm() { testcase("bad wasm test"); using namespace test::jtx; Env const env{*this}; HostFunctions hfs(env.journal); { auto wasm = hexToBytes("00000000"); std::string const funcName("mock_escrow"); auto re = runEscrowWasm(wasm, hfs, 15, funcName, {}); BEAST_EXPECT(!re); } { auto wasm = hexToBytes("00112233445566778899AA"); std::string const funcName("mock_escrow"); auto const re = preflightEscrowWasm(wasm, hfs, funcName); BEAST_EXPECT(!isTesSuccess(re)); } { // FinishFunction wrong function name // pub fn bad() -> bool { // unsafe { host_lib::getLedgerSqn() >= 5 } // } auto const badWasm = hexToBytes( "0061736d010000000105016000017f02190108686f73745f6c69620c6765" "744c656467657253716e00000302010005030100100611027f00418080c0" "000b7f00418080c0000b072b04066d656d6f727902000362616400010a5f" "5f646174615f656e6403000b5f5f686561705f6261736503010a09010700" "100041044a0b004d0970726f64756365727302086c616e67756167650104" "52757374000c70726f6365737365642d6279010572757374631d312e3835" "2e31202834656231363132353020323032352d30332d31352900490f7461" "726765745f6665617475726573042b0f6d757461626c652d676c6f62616c" "732b087369676e2d6578742b0f7265666572656e63652d74797065732b0a" "6d756c746976616c7565"); auto const re = preflightEscrowWasm(badWasm, hfs, escrowFunctionName); BEAST_EXPECT(!isTesSuccess(re)); } } void testWasmLedgerSqn() { testcase("Wasm get ledger sequence"); auto ledgerSqnWasm = hexToBytes(kLedgerSqnWasmHex); using namespace test::jtx; Env env{*this}; TestLedgerDataProvider hfs(env); ImportVec imports; WASM_IMPORT_FUNC2(imports, getLedgerSqn, "get_ledger_sqn", &hfs, 33); auto& engine = WasmEngine::instance(); auto re = engine.run(ledgerSqnWasm, hfs, 1'000'000, escrowFunctionName, {}, imports, env.journal); checkResult(re, 0, 440); env.close(); env.close(); // empty module, throwing exception re = engine.run({}, hfs, 1'000'000, escrowFunctionName, {}, imports, env.journal); BEAST_EXPECT(!re); env.close(); } void testImpExp() { testcase("Wasm import/export functions"); auto impExpWasm = hexToBytes(kImpExpHex); using namespace test::jtx; Env env{*this}; TestLedgerDataProvider hfs(env); ImportVec imports; WASM_IMPORT_FUNC2(imports, getLedgerSqn, "get_ledger_sqn", &hfs, 33); WASM_IMPORT_FUNC2(imports, getParentLedgerHash, "get_parent_ledger_hash", &hfs, 60); auto& engine = WasmEngine::instance(); // Test exp_func1() - should return 1 auto re = engine.run(impExpWasm, hfs, 1'000'000, "exp_func1", {}, imports, env.journal); checkResult(re, 1, 30); // Test exp_func2(5) - should return 2 * 5 = 10 re = engine.run( impExpWasm, hfs, 1'000'000, "exp_func2", wasmParams(5), imports, env.journal); checkResult(re, 10, 52); // Test test_imports() - should call get_ledger_sqn and get_parent_ledger_hash re = engine.run(impExpWasm, hfs, 1'000'000, "test_imports", {}, imports, env.journal); // Should return the ledger sequence number (3 by default in test env) checkResult(re, 3, 294); // Test corrupted import/export sections - invert each byte and expect failure testcase("Wasm import/export section corruption"); { // Import section(#2): bytes [26, 79) - 53 bytes // Export section(#7): bytes [90, 141) - 51 bytes auto [importStart, importEnd] = getSection(impExpWasm, 2); auto [exportStart, exportEnd] = getSection(impExpWasm, 7); BEAST_EXPECTS(importStart == 26, std::to_string(importStart)); BEAST_EXPECTS(importEnd == 79, std::to_string(importEnd)); BEAST_EXPECTS(exportStart == 90, std::to_string(exportStart)); BEAST_EXPECTS(exportEnd == 141, std::to_string(exportEnd)); auto testInv = [&](unsigned i) { auto corruptedWasm = impExpWasm; corruptedWasm[i] = ~corruptedWasm[i]; // Invert byte // Try to run any function - should fail due to corruption auto result = engine.run( corruptedWasm, hfs, 1'000'000, "exp_func1", {}, imports, env.journal); BEAST_EXPECT(!result); }; // Test each byte in import section for (unsigned i = importStart; i < importEnd; ++i) testInv(i); // Test each byte in export section for (unsigned i = exportStart; i < exportEnd; ++i) testInv(i); } env.close(); } void testWasmFib() { testcase("Wasm fibo"); auto const fibWasm = hexToBytes(kFibWasmHex); auto& engine = WasmEngine::instance(); HostFunctions hfs; auto const re = engine.run(fibWasm, hfs, 10'000'000, "fib", wasmParams(10)); checkResult(re, 55, 1'137); } void testHFCost() { testcase("wasm test host functions cost"); using namespace test::jtx; Env env(*this); { auto const allHostFuncWasm = hexToBytes(kAllHostFunctionsWasmHex); auto& engine = WasmEngine::instance(); TestHostFunctions hfs(env, 0); auto imp = createWasmImport(hfs); for (auto& i : imp) i.second.second.gas = 0; auto re = engine.run( allHostFuncWasm, hfs, 1'000'000, escrowFunctionName, {}, imp, env.journal); checkResult(re, 1, 27'080); env.close(); } env.close(); env.close(); env.close(); env.close(); env.close(); { auto const allHostFuncWasm = hexToBytes(kAllHostFunctionsWasmHex); auto& engine = WasmEngine::instance(); TestHostFunctions hfs(env, 0); auto const imp = createWasmImport(hfs); auto re = engine.run( allHostFuncWasm, hfs, 1'000'000, escrowFunctionName, {}, imp, env.journal); checkResult(re, 1, 70'340); env.close(); } // not enough gas { auto const allHostFuncWasm = hexToBytes(kAllHostFunctionsWasmHex); auto& engine = WasmEngine::instance(); TestHostFunctions hfs(env, 0); auto const imp = createWasmImport(hfs); auto re = engine.run(allHostFuncWasm, hfs, 200, escrowFunctionName, {}, imp, env.journal); if (BEAST_EXPECT(!re)) { BEAST_EXPECTS( re.error() == tecFAILED_PROCESSING, std::to_string(TERtoInt(re.error()))); } env.close(); } } void testEscrowWasmDN() { testcase("escrow wasm devnet test"); auto const allHFWasm = hexToBytes(kAllHostFunctionsWasmHex); using namespace test::jtx; Env env{*this}; { TestHostFunctions hfs(env, 0); auto re = runEscrowWasm(allHFWasm, hfs, 100'000, escrowFunctionName, {}); checkResult(re, 1, 70'340); } { // Invalid gas limit (0) should be rejected (boundary condition) TestHostFunctions hfs(env, 0); auto re = runEscrowWasm(allHFWasm, hfs, -1, escrowFunctionName, {}); BEAST_EXPECT(!re.has_value()); BEAST_EXPECT(re.error() == temBAD_AMOUNT); } { // Invalid gas limit (-1) should be rejected TestHostFunctions hfs(env, 0); auto re = runEscrowWasm(allHFWasm, hfs, 0, escrowFunctionName, {}); BEAST_EXPECT(!re.has_value()); BEAST_EXPECT(re.error() == temBAD_AMOUNT); } { // max() gas TestHostFunctions hfs(env, 0); auto re = runEscrowWasm( allHFWasm, hfs, std::numeric_limits::max(), escrowFunctionName, {}); checkResult(re, 1, 70'340); } { // fail because trying to access nonexistent field struct FieldNotFoundHostFunctions : public TestHostFunctions { explicit FieldNotFoundHostFunctions(Env& env) : TestHostFunctions(env) { } Expected getTxField(SField const& fname) const override { return Unexpected(HostFunctionError::FieldNotFound); } }; FieldNotFoundHostFunctions hfs(env); auto re = runEscrowWasm(allHFWasm, hfs, 100'000, escrowFunctionName, {}); checkResult(re, -201, 28'965); } { // fail because trying to allocate more than MAX_PAGES memory struct OversizedFieldHostFunctions : public TestHostFunctions { explicit OversizedFieldHostFunctions(Env& env) : TestHostFunctions(env) { } Expected getTxField(SField const& fname) const override { return Bytes((128 + 1) * 64 * 1024, 1); } }; OversizedFieldHostFunctions hfs(env); auto re = runEscrowWasm(allHFWasm, hfs, 100'000, escrowFunctionName, {}); checkResult(re, -201, 28'965); } // This test use log output, so DEBUG_OUTPUT must be disabled. #ifndef DEBUG_OUTPUT { // fail because recursion too deep auto const deepWasm = hexToBytes(kDeepRecursionHex); TestHostFunctionsSink hfs(env); std::string const funcName("finish"); auto re = runEscrowWasm(deepWasm, hfs, 1'000'000'000, funcName, {}); BEAST_EXPECT(!re && re.error()); // std::cout << "bad case (deep recursion) result " << re.error() // << std::endl; auto const& sink = hfs.getSink(); auto countSubstr = [](std::string const& str, std::string const& substr) { std::size_t pos = 0; int occurrences = 0; while ((pos = str.find(substr, pos)) != std::string::npos) { occurrences++; pos += substr.length(); } return occurrences; }; auto const s = sink.messages().str(); BEAST_EXPECT(countSubstr(s, "WASMI Error: failure to call func") == 1); BEAST_EXPECT(countSubstr(s, "exception: failure") > 0); } #endif { // infinite loop auto const infiniteLoopWasm = hexToBytes(kInfiniteLoopWasmHex); std::string const funcName("loop"); TestHostFunctions hfs(env, 0); // infinite loop should be caught and fail auto const re = runEscrowWasm(infiniteLoopWasm, hfs, 1'000'000, funcName, {}); if (BEAST_EXPECT(!re.has_value())) { BEAST_EXPECT(re.error() == tecFAILED_PROCESSING); } } { // expected import not provided auto const lgrSqnWasm = hexToBytes(kLedgerSqnWasmHex); TestLedgerDataProvider hfs(env); ImportVec imports; WASM_IMPORT_FUNC2(imports, getLedgerSqn, "get_ledger_sqn2", &hfs); auto& engine = WasmEngine::instance(); auto re = engine.run( lgrSqnWasm, hfs, 1'000'000, escrowFunctionName, {}, imports, env.journal); BEAST_EXPECT(!re); } { // bad import format auto const lgrSqnWasm = hexToBytes(kLedgerSqnWasmHex); TestLedgerDataProvider hfs(env); ImportVec imports; WASM_IMPORT_FUNC2(imports, getLedgerSqn, "get_ledger_sqn", &hfs); imports["get_ledger_sqn"].first = nullptr; auto& engine = WasmEngine::instance(); auto re = engine.run( lgrSqnWasm, hfs, 1'000'000, escrowFunctionName, {}, imports, env.journal); BEAST_EXPECT(!re); } { // bad function name auto const lgrSqnWasm = hexToBytes(kLedgerSqnWasmHex); TestLedgerDataProvider hfs(env); ImportVec imports; WASM_IMPORT_FUNC2(imports, getLedgerSqn, "get_ledger_sqn", &hfs); auto& engine = WasmEngine::instance(); auto re = engine.run(lgrSqnWasm, hfs, 1'000'000, "func1", {}, imports, env.journal); BEAST_EXPECT(!re); } } void testFloat() { testcase("float point"); std::string const funcName("finish"); using namespace test::jtx; Env env(*this); { auto const floatTestWasm = hexToBytes(kFloatTestsWasmHex); TestHostFunctions hfs(env, 0); auto re = runEscrowWasm(floatTestWasm, hfs, 200'000, funcName, {}); checkResult(re, 1, 134'938); env.close(); } { auto const float0Wasm = hexToBytes(kFloat0Hex); TestHostFunctions hfs(env, 0); auto re = runEscrowWasm(float0Wasm, hfs, 100'000, funcName, {}); checkResult(re, 1, 4'309); env.close(); } } void testCodecovWasm() { testcase("Codecov wasm test"); using namespace test::jtx; Env env{*this}; auto const codecovWasm = hexToBytes(kCodecovTestsWasmHex); TestHostFunctions hfs(env, 0); auto const allowance = 220'169; auto re = runEscrowWasm(codecovWasm, hfs, allowance, escrowFunctionName, {}); checkResult(re, 1, allowance); } void testDisabledFloat() { testcase("disabled float"); using namespace test::jtx; Env env{*this}; auto disabledFloatWasm = hexToBytes(kDisabledFloatHex); std::string const funcName("finish"); TestHostFunctions hfs(env, 0); { // f32 set constant, opcode disabled exception auto const re = runEscrowWasm(disabledFloatWasm, hfs, 1'000'000, funcName, {}); if (BEAST_EXPECT(!re.has_value())) { BEAST_EXPECT(re.error() == tecFAILED_PROCESSING); } } { // f32 add, can't create module exception disabledFloatWasm[0x117] = 0x92; auto const re = runEscrowWasm(disabledFloatWasm, hfs, 1'000'000, funcName, {}); if (BEAST_EXPECT(!re.has_value())) { BEAST_EXPECT(re.error() == tecFAILED_PROCESSING); } } } void testWasmMemory() { testcase("Wasm additional memory limit tests"); BEAST_EXPECT(finishFunctionReturns(kMemoryPointerAtLimitHex, 1)); BEAST_EXPECT(!runFinishFunction(kMemoryPointerOverLimitHex).has_value()); BEAST_EXPECT(!runFinishFunction(kMemoryOffsetOverLimitHex).has_value()); BEAST_EXPECT(!runFinishFunction(kMemoryEndOfWordOverLimitHex).has_value()); BEAST_EXPECT(finishFunctionReturns(kMemoryGrow0To1PageHex, 1)); BEAST_EXPECT(finishFunctionReturns(kMemoryGrow1To0PageHex, -1)); BEAST_EXPECT(finishFunctionReturns(kMemoryLastByteOf8MbHex, 1)); BEAST_EXPECT(finishFunctionReturns(kMemoryGrow1MoreThan8MbHex, -1)); BEAST_EXPECT(finishFunctionReturns(kMemoryGrow0MoreThan8MbHex, 1)); BEAST_EXPECT(!runFinishFunction(kMemoryInit1MoreThan8MbHex).has_value()); BEAST_EXPECT(!runFinishFunction(kMemoryNegativeAddressHex).has_value()); } void testWasmTable() { testcase("Wasm table limit tests"); BEAST_EXPECT(finishFunctionReturns(kTable64ElementsHex, 1)); BEAST_EXPECT(!runFinishFunction(kTable65ElementsHex).has_value()); BEAST_EXPECT(!runFinishFunction(kTable2TablesHex).has_value()); BEAST_EXPECT(finishFunctionReturns(kTable0ElementsHex, 1)); BEAST_EXPECT(!runFinishFunction(kTableUintMaxHex).has_value()); } void testWasmProposal() { testcase("Wasm disabled proposal tests"); BEAST_EXPECT(!runFinishFunction(kProposalMutableGlobalHex).has_value()); BEAST_EXPECT(!runFinishFunction(kProposalGcStructNewHex).has_value()); BEAST_EXPECT(!runFinishFunction(kProposalMultiValueHex).has_value()); BEAST_EXPECT(!runFinishFunction(kProposalSignExtHex).has_value()); BEAST_EXPECT(!runFinishFunction(kProposalFloatToIntHex).has_value()); BEAST_EXPECT(!runFinishFunction(kProposalBulkMemoryHex).has_value()); BEAST_EXPECT(!runFinishFunction(kProposalRefTypesHex).has_value()); BEAST_EXPECT(!runFinishFunction(kProposalTailCallHex).has_value()); BEAST_EXPECT(!runFinishFunction(kProposalExtendedConstHex).has_value()); BEAST_EXPECT(!runFinishFunction(kProposalMultiMemoryHex).has_value()); BEAST_EXPECT(!runFinishFunction(kProposalCustomPageSizesHex).has_value()); BEAST_EXPECT(!runFinishFunction(kProposalMemory64Hex).has_value()); BEAST_EXPECT(!runFinishFunction(kProposalWideArithmeticHex).has_value()); } void testWasmTrap() { testcase("Wasm trap tests"); BEAST_EXPECT(!runFinishFunction(kTrapDivideBy0Hex).has_value()); BEAST_EXPECT(!runFinishFunction(kTrapIntOverflowHex).has_value()); BEAST_EXPECT(!runFinishFunction(kTrapUnreachableHex).has_value()); BEAST_EXPECT(!runFinishFunction(kTrapNullCallHex).has_value()); BEAST_EXPECT(!runFinishFunction(kTrapFuncSigMismatchHex).has_value()); } void testWasmWasi() { testcase("Wasm Wasi tests"); BEAST_EXPECT(!runFinishFunction(kWasiGetTimeHex).has_value()); BEAST_EXPECT(!runFinishFunction(kWasiPrintHex).has_value()); } void testWasmSectionCorruption() { testcase("Wasm Section Corruption tests"); BEAST_EXPECT(!runFinishFunction(kBadMagicNumberHex).has_value()); BEAST_EXPECT(!runFinishFunction(kBadVersionNumberHex).has_value()); BEAST_EXPECT(!runFinishFunction(kLyingHeaderHex).has_value()); BEAST_EXPECT(!runFinishFunction(kNeverEndingNumberHex).has_value()); BEAST_EXPECT(!runFinishFunction(kVectorLieHex).has_value()); BEAST_EXPECT(!runFinishFunction(kSectionOrderingHex).has_value()); BEAST_EXPECT(!runFinishFunction(kGhostPayloadHex).has_value()); BEAST_EXPECT(!runFinishFunction(kJunkAfterSectionHex).has_value()); BEAST_EXPECT(!runFinishFunction(kInvalidSectionIdHex).has_value()); BEAST_EXPECT(!runFinishFunction(kLocalVariableBombHex).has_value()); } void testStartFunctionLoop() { testcase("infinite loop in start function"); using namespace test::jtx; Env env(*this); auto const startLoopWasm = hexToBytes(kStartLoopHex); TestLedgerDataProvider hfs(env); ImportVec const imports; auto& engine = WasmEngine::instance(); auto checkRes = engine.check(startLoopWasm, hfs, "finish", {}, imports, env.journal); BEAST_EXPECTS(checkRes == tesSUCCESS, std::to_string(TERtoInt(checkRes))); auto re = engine.run(startLoopWasm, hfs, 1'000'000, escrowFunctionName, {}, imports, env.journal); BEAST_EXPECTS(re.error() == tecFAILED_PROCESSING, std::to_string(TERtoInt(re.error()))); } void testBadAlign() { testcase("Wasm Bad Align"); // bad_align.c auto const badAlignWasm = hexToBytes(kBadAlignWasmHex); using namespace test::jtx; Env env{*this}; TestHostFunctions hfs(env, 0); auto imports = createWasmImport(hfs); { // Calls float_from_uint with bad alignment. // Can be checked through codecov auto& engine = WasmEngine::instance(); auto re = engine.run(badAlignWasm, hfs, 1'000'000, "test", {}, imports, env.journal); if (BEAST_EXPECTS(re, transToken(re.error()))) { BEAST_EXPECTS(re->result == 0x47308594, std::to_string(re->result)); } } env.close(); } void testReturnType() { using namespace test::jtx; Env env(*this); TestHostFunctions hfs(env, 0); testcase("Wasm invalid return type"); // return int64. { // (module // (memory (export "memory") 1) // (func (export "finish") (result i64) // i64.const 0x100000000)) auto const wasmHex = "0061736d010000000105016000017e030201000503010001" "071302066d656d6f727902000666696e69736800000a0a01" "08004280808080100b"; auto const wasm = hexToBytes(wasmHex); auto const re = runEscrowWasm(wasm, hfs, 100'000, escrowFunctionName, {}); BEAST_EXPECT(!re); } // return void. wasmi return execution error { //(module // (type (;0;) (func)) // (func (;0;) (type 0) // return) // (memory (;0;) 1) // (export "memory" (memory 0)) // (export "finish" (func 0))) auto const wasmHex = "0061736d01000000010401600000030201000503010001071302066d656d6f" "727902000666696e69736800000a050103000f0b"; auto const wasm = hexToBytes(wasmHex); auto const re = runEscrowWasm(wasm, hfs, 100'000, escrowFunctionName, {}); BEAST_EXPECT(!re); } // return i32, i32. wasmi doesn't create module { //(module // (memory (export "memory") 1) // (func (export "finish") (result i32 i32) // i32.const 0x10000000 // i32.const 0x100000FF)) auto const wasmHex = "0061736d010000000106016000027f7f030201000503010001071302066d65" "6d6f727902000666696e69736800000a10010e0041808080800141ff818080" "010b"; auto const wasm = hexToBytes(wasmHex); auto const re = runEscrowWasm(wasm, hfs, 100'000, escrowFunctionName, {}); BEAST_EXPECT(!re); } } void testParameterType() { using namespace test::jtx; Env env(*this); TestHostFunctions hfs(env, 0); testcase("Wasm invalid params"); // (module // (memory (export "memory") 1) // (func $test1 (export "test1") (param i32) (result i32) // i32.const 1000) // (func $test2 (export "test2") (param i32 i32) (result i32) // i32.const 1001)) auto const wasmHex = "0061736d01000000010c0260017f017f60027f7f017f03030200010503010001071a03066d656d6f727902" "00057465737431000005746573743200010a0d02050041e8070b050041e9070b"; auto const wasm = hexToBytes(wasmHex); // good params, module is working properly { auto const re = runEscrowWasm(wasm, hfs, 100'000, "test2", wasmParams(2, 10)); BEAST_EXPECT(re && re->result == 1001 && re->cost == 37); } // no params { auto const re = runEscrowWasm(wasm, hfs, 100'000, "test1", {}); BEAST_EXPECT(!re); } // more params { auto const re = runEscrowWasm(wasm, hfs, 100'000, "test1", wasmParams(0, 1)); BEAST_EXPECT(!re); } // less params { auto const re = runEscrowWasm(wasm, hfs, 100'000, "test2", wasmParams(1)); BEAST_EXPECT(!re); } // invalid type { auto const re = runEscrowWasm(wasm, hfs, 100'000, "test1", wasmParams(std::int64_t(15))); BEAST_EXPECT(!re); } } void testSwapBytes() { testcase("Wasm swap bytes"); uint64_t const swapDataU64 = 0x123456789abcdeffull; uint64_t const reverseSwapDataU64 = 0xffdebc9a78563412ull; int64_t const swapDataI64 = 0x123456789abcdeffll; int64_t const reverseSwapDataI64 = 0xffdebc9a78563412ll; uint32_t const swapDataU32 = 0x12789aff; uint32_t const reverseSwapDataU32 = 0xff9a7812; int32_t const swapDataI32 = 0x12789aff; int32_t const reverseSwapDataI32 = 0xff9a7812; uint16_t const swapDataU16 = 0x12ff; uint16_t const reverseSwapDataU16 = 0xff12; int16_t const swapDataI16 = 0x12ff; int16_t const reverseSwapDataI16 = 0xff12; uint64_t b1 = swapDataU64; int64_t b2 = swapDataI64; b1 = adjustWasmEndianessHlp(b1); b2 = adjustWasmEndianessHlp(b2); BEAST_EXPECT(b1 == reverseSwapDataU64); BEAST_EXPECT(b2 == reverseSwapDataI64); b1 = adjustWasmEndianessHlp(b1); b2 = adjustWasmEndianessHlp(b2); BEAST_EXPECT(b1 == swapDataU64); BEAST_EXPECT(b2 == swapDataI64); uint32_t b3 = swapDataU32; int32_t b4 = swapDataI32; b3 = adjustWasmEndianessHlp(b3); b4 = adjustWasmEndianessHlp(b4); BEAST_EXPECT(b3 == reverseSwapDataU32); BEAST_EXPECT(b4 == reverseSwapDataI32); b3 = adjustWasmEndianessHlp(b3); b4 = adjustWasmEndianessHlp(b4); BEAST_EXPECT(b3 == swapDataU32); BEAST_EXPECT(b4 == swapDataI32); uint16_t b5 = swapDataU16; int16_t b6 = swapDataI16; b5 = adjustWasmEndianessHlp(b5); b6 = adjustWasmEndianessHlp(b6); BEAST_EXPECT(b5 == reverseSwapDataU16); BEAST_EXPECT(b6 == reverseSwapDataI16); b5 = adjustWasmEndianessHlp(b5); b6 = adjustWasmEndianessHlp(b6); BEAST_EXPECT(b5 == swapDataU16); BEAST_EXPECT(b6 == swapDataI16); } void testManyParams() { testcase("Wasm Many params"); auto const params1k = hexToBytes(kThousandParamsHex); auto const params1k1 = hexToBytes(kThousand1ParamsHex); using namespace test::jtx; Env env{*this}; TestHostFunctions hfs(env, 0); auto imports = createWasmImport(hfs); // add 1k parameter (max that wasmi support) std::vector params; params.reserve(1000); for (int i = 0; i < 1000; ++i) params.push_back({.type = WasmTypes::WtI32, .of = {.i32 = 2 * i}}); auto& engine = WasmEngine::instance(); { auto re = engine.run(params1k, hfs, 1'000'000, "test", params, imports, env.journal); BEAST_EXPECT(re && re->result == 999000); } // add 1 more parameter, module can't be created now params.push_back({.type = WasmTypes::WtI32, .of = {.i32 = 2 * 1000}}); { auto re = engine.run(params1k1, hfs, 1'000'000, "test", params, imports, env.journal); BEAST_EXPECT(!re); } // function that create 10k local variables auto const locals10k = hexToBytes(kLocals10kHex); { auto re = engine.run( locals10k, hfs, 1'000'000, "test", wasmParams(0, 1), imports, env.journal); BEAST_EXPECT(re && re->result == 890'489'442); } // module has 5k functions auto const functions5k = hexToBytes(kFunctions5kHex); { auto re = engine.run( functions5k, hfs, 1'000'000, "test0001", wasmParams(2, 3), imports, env.journal); BEAST_EXPECT(re && re->result == 5); } env.close(); } void testOpcodes() { using namespace test::jtx; unsigned const reserved = 64; std::uint8_t const nop = 0x01; std::array const codeMarker = { nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop}; auto const opcReserved = hexToBytes(kOpcReservedHex); Env env{*this}; auto& engine = WasmEngine::instance(); TestHostFunctions hfs(env, 0); auto imports = createWasmImport(hfs); env.close(); { auto run = [&](std::vector const& code, bool good = false, int64_t cost = -1, std::source_location const location = std::source_location::current()) { auto const lineStr = " (" + std::to_string(location.line()) + ")"; auto re = engine.run(code, hfs, 1'000'000, "all_instructions", {}, imports, env.journal); if (BEAST_EXPECTS(re.has_value() == good, transToken(re.error()) + lineStr) && good) BEAST_EXPECTS(re->cost == cost, std::to_string(re->cost) + lineStr); }; // 1 byte instruction auto test = [&](std::uint8_t start, std::uint8_t finish, bool good = false, int64_t cost = -1, std::source_location const location = std::source_location::current()) { auto const lineStr = " (" + std::to_string(location.line()) + ")"; auto code = opcReserved; auto codeRange = std::ranges::search(code, codeMarker); if (!BEAST_EXPECTS(!codeRange.empty(), lineStr)) return; auto it = codeRange.begin(); for (std::uint16_t i = start; i <= finish; ++i) { *it = i; run(code, good, cost, location); } }; // 2 bytes instruction auto test2 = [&](std::uint8_t major, std::uint16_t start, std::uint16_t finish, bool good = false, int64_t cost = -1, std::source_location const location = std::source_location::current()) { auto const lineStr = " (" + std::to_string(location.line()) + ")"; auto code = opcReserved; auto codeRange = std::ranges::search(code, codeMarker); if (!BEAST_EXPECTS(!codeRange.empty(), lineStr)) return; auto it = codeRange.begin(); *it++ = major; for (std::uint16_t i = start; i <= finish; ++i) { auto it2 = it; uleb128(it2, i); run(code, good, cost, location); } }; // multibytes instructions auto testMB = [&](std::vector const& codeSnap, bool good = false, int64_t cost = -1, std::source_location const location = std::source_location::current()) { auto const lineStr = " (" + std::to_string(location.line()) + ")"; auto code = opcReserved; auto codeRange = std::ranges::search(code, codeMarker); if (!BEAST_EXPECTS(!codeRange.empty(), lineStr)) return; if (!BEAST_EXPECTS(codeSnap.size() < reserved, lineStr)) return; auto it = codeRange.begin(); for (auto x : codeSnap) *it++ = x; run(code, good, cost, location); }; // normal run testcase("Wasm reserved opcodes main"); test(nop, nop, true, 534); // reserved main test(0x06, 0x0A); test(0x12, 0x19); test(0x25, 0x27); test(0xC0, 0xFA); test(0xFF, 0xFF); // reserved gc, string testcase("Wasm reserved opcodes gc"); test2(0xFB, 0x00, 0xBF); // not supported by compiler // reserved FC testcase("Wasm reserved opcodes FC"); test2(0xFC, 0x00, 0x07); // floats, disabled test2(0xFC, 0x12, 0x1F); // reserved SIMD testcase("Wasm reserved opcodes SIMD"); test2(0xFD, 0x9A, 0x9A); test2(0xFD, 0xA2, 0xA2); test2(0xFD, 0xA5, 0xA6); test2(0xFD, 0xAF, 0xB0); test2(0xFD, 0xB2, 0xB4); test2(0xFD, 0xB8, 0xB8); test2(0xFD, 0xC2, 0xC2); test2(0xFD, 0xC5, 0xC6); test2(0xFD, 0xCF, 0xD0); test2(0xFD, 0xD2, 0xD4); test2(0xFD, 0xE2, 0xE2); test2(0xFD, 0xEE, 0xEE); test2(0xFD, 0x115, 0x12F); testcase("Wasm opcodes THREADS"); test2(0xFE, 0x00, 0x4F); // not supported by compiler // FC mem instructions testMB({0x41, 0x00, 0x41, 0x00, 0x41, 0x04, 0xFC, 0x08, 0x00, 0x00}); // memory.init testMB({0xFC, 0x09, 0x00}); // data.drop testMB({0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0xFC, 0x0A, 0x00, 0x00}); // memory.copy testMB({0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0xFC, 0x0B, 0x00}); // memory.fill testMB({0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0xFC, 0x0C, 0x00, 0x00}); // table.init testMB({0xFC, 0x0D, 0x00}); // elem.drop testMB({0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0xFC, 0x0E, 0x00, 0x00}); // table.copy testMB({0xD2, 0x00, 0x41, 0x00, 0xFC, 0x0F, 0x00, 0x1A}); // table.grow testMB({0x1A, 0xFC, 0x10, 0x00, 0x1A}); // table.size testMB({0x41, 0x00, 0xD2, 0x00, 0x41, 0x00, 0xFC, 0x11, 0x00}); // table.fill testcase("Wasm opcodes SIMD"); // clang-format off // generated by auggie // SIMD instructions testMB({0x41, 0x00, 0xFD, 0x00, 0x04, 0x00, 0x1A}); // v128.load testMB({0x41, 0x00, 0xFD, 0x01, 0x03, 0x00, 0x1A}); // v128.load8x8_s testMB({0x41, 0x00, 0xFD, 0x02, 0x03, 0x00, 0x1A}); // v128.load8x8_u testMB({0x41, 0x00, 0xFD, 0x03, 0x03, 0x00, 0x1A}); // v128.load16x4_s testMB({0x41, 0x00, 0xFD, 0x04, 0x03, 0x00, 0x1A}); // v128.load16x4_u testMB({0x41, 0x00, 0xFD, 0x05, 0x03, 0x00, 0x1A}); // v128.load32x2_s testMB({0x41, 0x00, 0xFD, 0x06, 0x03, 0x00, 0x1A}); // v128.load32x2_u testMB({0x41, 0x00, 0xFD, 0x07, 0x00, 0x00, 0x1A}); // v128.load8_splat testMB({0x41, 0x00, 0xFD, 0x08, 0x01, 0x00, 0x1A}); // v128.load16_splat testMB({0x41, 0x00, 0xFD, 0x09, 0x02, 0x00, 0x1A}); // v128.load32_splat testMB({0x41, 0x00, 0xFD, 0x0A, 0x03, 0x00, 0x1A}); // v128.load64_splat testMB({0x41, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0B, 0x04, 0x00}); // v128.store testMB({0xFD, 0x0C, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1A}); // v128.const testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0D, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x1A}); // i8x16.shuffle testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0E, 0x1A}); // i8x16.swizzle testMB({0x41, 0x2A, 0xFD, 0x0F, 0x1A}); // i8x16.splat testMB({0x41, 0x2A, 0xFD, 0x10, 0x1A}); // i16x8.splat testMB({0x41, 0x2A, 0xFD, 0x11, 0x1A}); // i32x4.splat testMB({0x42, 0x2A, 0xFD, 0x12, 0x1A}); // i64x2.splat testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x15, 0x00, 0x1A}); // i8x16.extract_lane_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x16, 0x00, 0x1A}); // i8x16.extract_lane_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x2A, 0xFD, 0x17, 0x00, 0x1A}); // i8x16.replace_lane testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x18, 0x00, 0x1A}); // i16x8.extract_lane_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x19, 0x00, 0x1A}); // i16x8.extract_lane_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x2A, 0xFD, 0x1A, 0x00, 0x1A}); // i16x8.replace_lane testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x1B, 0x00, 0x1A}); // i32x4.extract_lane testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x2A, 0xFD, 0x1C, 0x00, 0x1A}); // i32x4.replace_lane testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x1D, 0x00, 0x1A}); // i64x2.extract_lane testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x2A, 0xFD, 0x1E, 0x00, 0x1A}); // i64x2.replace_lane testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x1F, 0x00, 0x1A}); // f32x4.extract_lane testMB( {0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x80, 0x3F, 0xFD, 0x20, 0x00, 0x1A}); // f32x4.replace_lane testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x21, 0x00, 0x1A}); // f64x2.extract_lane testMB( {0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0xFD, 0x22, 0x00, 0x1A}); // f64x2.replace_lane testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x23, 0x1A}); // i8x16.eq testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x24, 0x1A}); // i8x16.ne testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x25, 0x1A}); // i8x16.lt_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x26, 0x1A}); // i8x16.lt_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x27, 0x1A}); // i8x16.gt_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x28, 0x1A}); // i8x16.gt_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x29, 0x1A}); // i8x16.le_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x2A, 0x1A}); // i8x16.le_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x2B, 0x1A}); // i8x16.ge_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x2C, 0x1A}); // i8x16.ge_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x2D, 0x1A}); // i16x8.eq testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x2E, 0x1A}); // i16x8.ne testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x2F, 0x1A}); // i16x8.lt_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x30, 0x1A}); // i16x8.lt_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x31, 0x1A}); // i16x8.gt_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x32, 0x1A}); // i16x8.gt_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x33, 0x1A}); // i16x8.le_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x34, 0x1A}); // i16x8.le_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x35, 0x1A}); // i16x8.ge_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x36, 0x1A}); // i16x8.ge_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x37, 0x1A}); // i32x4.eq testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x38, 0x1A}); // i32x4.ne testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x39, 0x1A}); // i32x4.lt_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x3A, 0x1A}); // i32x4.lt_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x3B, 0x1A}); // i32x4.gt_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x3C, 0x1A}); // i32x4.gt_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x3D, 0x1A}); // i32x4.le_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x3E, 0x1A}); // i32x4.le_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x3F, 0x1A}); // i32x4.ge_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x40, 0x1A}); // i32x4.ge_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x4D, 0x1A}); // v128.not testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x4E, 0x1A}); // v128.and testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x4F, 0x1A}); // v128.andnot testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x50, 0x1A}); // v128.or testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x51, 0x1A}); // v128.xor testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x52, 0x1A}); // v128.bitselect testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x53, 0x1A}); // v128.any_true testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x60, 0x1A}); // i8x16.abs testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x61, 0x1A}); // i8x16.neg testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x62, 0x1A}); // i8x16.popcnt testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x63, 0x1A}); // i8x16.all_true testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x64, 0x1A}); // i8x16.bitmask testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0x6B, 0x1A}); // i8x16.shl testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0x6C, 0x1A}); // i8x16.shr_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0x6D, 0x1A}); // i8x16.shr_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x6E, 0x1A}); // i8x16.add testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x6F, 0x1A}); // i8x16.add_sat_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x70, 0x1A}); // i8x16.add_sat_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x71, 0x1A}); // i8x16.sub testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x72, 0x1A}); // i8x16.sub_sat_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x73, 0x1A}); // i8x16.sub_sat_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x76, 0x1A}); // i8x16.min_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x77, 0x1A}); // i8x16.min_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x78, 0x1A}); // i8x16.max_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x79, 0x1A}); // i8x16.max_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x7B, 0x1A}); // i8x16.avgr_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x80, 0x01, 0x1A}); // i16x8.abs testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x81, 0x01, 0x1A}); // i16x8.neg testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x83, 0x01, 0x1A}); // i16x8.all_true testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x84, 0x01, 0x1A}); // i16x8.bitmask testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0x8B, 0x01, 0x1A}); // i16x8.shl testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0x8C, 0x01, 0x1A}); // i16x8.shr_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0x8D, 0x01, 0x1A}); // i16x8.shr_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x8E, 0x01, 0x1A}); // i16x8.add testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x8F, 0x01, 0x1A}); // i16x8.add_sat_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x90, 0x01, 0x1A}); // i16x8.add_sat_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x91, 0x01, 0x1A}); // i16x8.sub testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x92, 0x01, 0x1A}); // i16x8.sub_sat_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x93, 0x01, 0x1A}); // i16x8.sub_sat_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x95, 0x01, 0x1A}); // i16x8.mul testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x96, 0x01, 0x1A}); // i16x8.min_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x97, 0x01, 0x1A}); // i16x8.min_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x98, 0x01, 0x1A}); // i16x8.max_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x99, 0x01, 0x1A}); // i16x8.max_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x9B, 0x01, 0x1A}); // i16x8.avgr_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xA0, 0x01, 0x1A}); // i32x4.abs testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xA1, 0x01, 0x1A}); // i32x4.neg testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xA3, 0x01, 0x1A}); // i32x4.all_true testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xA4, 0x01, 0x1A}); // i32x4.bitmask testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0xAB, 0x01, 0x1A}); // i32x4.shl testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0xAC, 0x01, 0x1A}); // i32x4.shr_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0xAD, 0x01, 0x1A}); // i32x4.shr_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xAE, 0x01, 0x1A}); // i32x4.add testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xB1, 0x01, 0x1A}); // i32x4.sub testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xB5, 0x01, 0x1A}); // i32x4.mul testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xB6, 0x01, 0x1A}); // i32x4.min_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xB7, 0x01, 0x1A}); // i32x4.min_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xB8, 0x01, 0x1A}); // i32x4.max_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xB9, 0x01, 0x1A}); // i32x4.max_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xBA, 0x01, 0x1A}); // i32x4.dot_i16x8_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xC0, 0x01, 0x1A}); // i64x2.abs testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xC1, 0x01, 0x1A}); // i64x2.neg testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xC3, 0x01, 0x1A}); // i64x2.all_true testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xC4, 0x01, 0x1A}); // i64x2.bitmask testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0xCB, 0x01, 0x1A}); // i64x2.shl testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0xCC, 0x01, 0x1A}); // i64x2.shr_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x01, 0xFD, 0xCD, 0x01, 0x1A}); // i64x2.shr_u testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xCE, 0x01, 0x1A}); // i64x2.add testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xD1, 0x01, 0x1A}); // i64x2.sub testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xD5, 0x01, 0x1A}); // i64x2.mul testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xD6, 0x01, 0x1A}); // i64x2.eq testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xD7, 0x01, 0x1A}); // i64x2.ne testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xD8, 0x01, 0x1A}); // i64x2.lt_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xD9, 0x01, 0x1A}); // i64x2.gt_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xDA, 0x01, 0x1A}); // i64x2.le_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xDB, 0x01, 0x1A}); // i64x2.ge_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xF8, 0x01, 0x1A}); // i32x4.trunc_sat_f32x4_s testMB({0xFD, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xF9, 0x01, 0x1A}); // i32x4.trunc_sat_f32x4_u // clang-format on } } void run() override { using namespace test::jtx; testGetDataHelperFunctions(); testWasmLib(); testBadWasm(); testWasmLedgerSqn(); testImpExp(); testWasmFib(); testHFCost(); testEscrowWasmDN(); testFloat(); testCodecovWasm(); testDisabledFloat(); testWasmMemory(); testWasmTable(); testWasmProposal(); testWasmTrap(); testWasmWasi(); testWasmSectionCorruption(); testStartFunctionLoop(); testBadAlign(); testReturnType(); testSwapBytes(); testManyParams(); testParameterType(); testOpcodes(); } }; BEAST_DEFINE_TESTSUITE(Wasm, app, xrpl); } // namespace xrpl::test