Compare commits

..

2 Commits

Author SHA1 Message Date
Peng Wang
3a1e72f066 wip, more wat files 2025-12-05 17:55:21 -05:00
Peng Wang
52baedc571 a batch of memory, table, and trap tests 2025-12-02 17:31:31 -05:00
36 changed files with 1068 additions and 0 deletions

18
clang_format.sh Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/bash
modified=$1
dir=`pwd`
clangf=clang-format-10
clangf=clang-format
if [ "$1" = "--all" ]
then
modified=`git status|egrep "modified|new file"|egrep "(cpp|h)$" | sed -E -e 's/modified://' -e 's/new file://' -e 's/^[[:space:]]+//'`
fi
for i in $modified
do
basedir=$(dirname "$i")
file=$(basename "$i")
echo "$basedir $file"
cd $basedir
$clangf -style=file -i "$file"
cd $dir
done

View File

@@ -715,6 +715,141 @@ struct Wasm_test : public beast::unit_test::suite
}
}
void
testWasmMemory()
{
testcase("Wasm additional memory limit tests");
auto& engine = WasmEngine::instance();
{
auto const ws = boost::algorithm::unhex(memoryPointerAtLimitHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(re->result == 1);
}
}
{
auto const ws = boost::algorithm::unhex(memoryPointerOverLimitHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
BEAST_EXPECT(!re.has_value());
}
{
auto const ws = boost::algorithm::unhex(memoryOffsetOverLimitHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
BEAST_EXPECT(!re.has_value());
}
{
auto const ws =
boost::algorithm::unhex(memoryEndOfWordOverLimitHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
BEAST_EXPECT(!re.has_value());
}
{
auto const ws = boost::algorithm::unhex(memoryGrow0To1PageHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(re->result == 1);
}
}
{
auto const ws = boost::algorithm::unhex(memoryLastByteOf8MBHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(re->result == 1);
}
}
{
auto const ws = boost::algorithm::unhex(memoryGrow1MoreThan8MBHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(re->result == -1);
}
}
}
void
testWasmTable()
{
testcase("Wasm table limit tests");
auto& engine = WasmEngine::instance();
{
auto const ws = boost::algorithm::unhex(table64ElementsHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(re->result == 1);
}
}
{
auto const ws = boost::algorithm::unhex(table65ElementsHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
BEAST_EXPECT(!re.has_value());
}
{
auto const ws = boost::algorithm::unhex(table2TablesHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
BEAST_EXPECT(!re.has_value());
}
}
void
testWasmProposal()
{
testcase("Wasm disabled proposal tests");
auto& engine = WasmEngine::instance();
{
auto const ws = boost::algorithm::unhex(proposalMutableGlobalHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
BEAST_EXPECT(!re.has_value());
}
}
void
testWasmTrap()
{
testcase("Wasm trap tests");
auto& engine = WasmEngine::instance();
{
auto const ws = boost::algorithm::unhex(trapDivideBy0Hex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
BEAST_EXPECT(!re.has_value());
}
}
void
testWasmWasi()
{
testcase("Wasm Wasi tests");
auto& engine = WasmEngine::instance();
{
auto const ws = boost::algorithm::unhex(wasiGetTimeHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
BEAST_EXPECT(!re.has_value());
}
{
auto const ws = boost::algorithm::unhex(wasiPrintHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
BEAST_EXPECT(!re.has_value());
}
}
void
run() override
{
@@ -736,6 +871,11 @@ struct Wasm_test : public beast::unit_test::suite
testCodecovWasm();
testDisabledFloat();
testWasmMemory();
testWasmTable();
testWasmProposal();
testWasmTrap();
testWasmWasi();
// perfTest();
}
};

View File

@@ -1068,3 +1068,138 @@ extern std::string const disabledFloatHex =
"6503050b5f5f686561705f6261736503060a5f5f686561705f656e640307"
"0d5f5f6d656d6f72795f6261736503080c5f5f7461626c655f6261736503"
"090a150202000b100043000000c54300200045921a41010b";
extern std::string const memoryPointerAtLimitHex =
"0061736d010000000105016000017f030201000503010001070a010666696e69736800000a"
"0e010c0041ffff032d00001a41010b";
extern std::string const memoryPointerOverLimitHex =
"0061736d010000000105016000017f030201000503010001070a010666696e69736800000a"
"0e010c00418080042d00001a41010b";
extern std::string const memoryOffsetOverLimitHex =
"0061736d010000000105016000017f030201000503010001071302066d656d6f7279020006"
"66696e69736800000a0e010c00410028028080041a41010b";
extern std::string const memoryEndOfWordOverLimitHex =
"0061736d010000000105016000017f030201000503010001071302066d656d6f7279020006"
"66696e69736800000a0e010c0041feff032802001a41010b";
extern std::string const memoryGrow0To1PageHex =
"0061736d010000000105016000017f030201000503010000071302066d656d6f7279020006"
"66696e69736800000a0b010900410140001a41010b";
extern std::string const memoryGrow1To0PageHex =
"0061736d010000000105016000017f030201000503010001071302066d656d6f7279020006"
"66696e69736800000a0b010900417f40001a41010b";
extern std::string const memoryLastByteOf8MBHex =
"0061736d010000000105016000017f030201000506010180018001071302066d656d6f7279"
"02000666696e69736800000a0f010d0041ffffff032d00001a41010b";
extern std::string const memoryGrow1MoreThan8MBHex =
"0061736d010000000105016000017f03020100050401008001071302066d656d6f72790200"
"0666696e69736800000a1301110041014000417f460440417f0f0b41010b";
extern std::string const memoryGrow0MoreThan8MBHex =
"0061736d010000000105016000017f03020100050401008001071302066d656d6f72790200"
"0666696e69736800000a1301110041004000417f460440417f0f0b41010b";
extern std::string const memoryInit1MoreThan8MBHex =
"0061736d010000000105016000017f030201000506010181018101071302066d656d6f7279"
"02000666696e69736800000a0f010d0041ffffff032d00001a41010b";
extern std::string const memoryNegativeAddressHex =
"0061736d010000000105016000017f030201000506010180018001071302066d656d6f7279"
"02000666696e69736800000a0c010a00417f2d00001a41010b";
extern std::string const table64ElementsHex =
"0061736d010000000108026000006000017f0303020001040401700040070a010666696e69"
"736800010946010041000b4000000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000000000000000000000000000"
"00000a090202000b040041010b";
extern std::string const table65ElementsHex =
"0061736d010000000108026000006000017f0303020001040401700041070a010666696e69"
"736800010947010041000b4100000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000000000000000000000000000"
"0000000a090202000b040041010b";
extern std::string const table2TablesHex =
"0061736d010000000108026000006000017f03030200010409027001010170010101070a01"
"0666696e6973680001090f020041000b0100020141000b0001000a090202000b040041010"
"b";
extern std::string const tableUintMaxHex =
"0061736d010000000105016000017f030201000408017000ffffffff0f070a010666696e69"
"736800000a0601040041010b";
extern std::string const proposalMutableGlobalHex =
"0061736d010000000105016000017f030201000606017f0141000b07140207636f756e7465"
"7203000666696e69736800000a0d010b00230041016a240041010b";
extern std::string const proposalGcStructNewHex =
"0061736d01000000010b026000017f5f027f017f0103020100070a010666696e6973680000"
"0a0a010800fb01011a41010b";
extern std::string const proposalMultiValueHex =
"0061736d010000000110036000027f7f6000017f60027f7f017f0303020001070a01066669"
"6e69736800010a14020600410a41140b0b00100002026a411e460b0b";
extern std::string const proposalSignExtHex =
"0061736d010000000105016000017f03020100070a010666696e69736800000a0b01090041"
"ff01c0417f460b";
extern std::string const proposalFloatToIntHex =
"0061736d010000000105016000017f03020100070a010666696e69736800000a1201100043"
"f9021550fc0041ffffffff07460b";
extern std::string const proposalBulkMemoryHex =
"0061736d010000000105016000017f030201000503010001071302066d656d6f7279020006"
"66696e69736800000a1f011d004100412a3a000041e40041004101fc0a000041e4002d0000"
"412a460b";
extern std::string const proposalRefTypesHex =
"0061736d010000000105016000017f020f0103656e76057461626c65016f00010302010007"
"0a010666696e69736800000a0c010a004100d06f260041010b";
extern std::string const proposalTailCallHex =
"0061736d010000000105016000017f0303020000070a010666696e69736800010a0b020400"
"41010b040012000b";
extern std::string const proposalExtendedConst =
"0061736d010000000105016000017f030201000609017f00410a41206a0b070a010666696e"
"69736800000a090107002300412a460b";
extern std::string const trapDivideBy0Hex =
"0061736d010000000105016000017f03020100070a010666696e69736800000a0c010a0041"
"2a41006d1a41010b";
extern std::string const trapIntOverflow =
"0061736d010000000105016000017f03020100070a010666696e69736800000a0d010b0041"
"8080808078417f6d0b";
extern std::string const trapUnreachable =
"0061736d010000000105016000017f03020100070a010666696e69736800000a0701050000"
"41010b";
extern std::string const trapNullCall =
"0061736d010000000105016000017f03020100040401700001070a010666696e6973680000"
"0a0901070041001100000b";
extern std::string const trapFuncSigMismatch =
"0061736d010000000108026000006000017f0303020001040401700001070a010666696e69"
"736800010907010041000b01000a0d020300010b070041001101000b";
extern std::string const wasiGetTimeHex =
"0061736d01000000010c0260037f7e7f017f6000017f02290116776173695f736e61707368"
"6f745f70726576696577310e636c6f636b5f74696d655f6765740000030201010503010001"
"071302066d656d6f727902000666696e69736800010a16011400410042e807410010004504"
"7f410105417f0b0b";
extern std::string const wasiPrintHex =
"0061736d01000000010d0260047f7f7f7f017f6000017f02230116776173695f736e617073"
"686f745f70726576696577310866645f77726974650000030201010503010001071302066d"
"656d6f727902000666696e69736800010a1d011b01017f4118210041014100410120001000"
"45047f410105417f0b0b0b1e030041100b0648656c6c6f0a0041000b04100000000041040b"
"0406000000";

View File

@@ -27,3 +27,39 @@ extern std::string const floatTestsWasmHex;
extern std::string const float0Hex;
extern std::string const disabledFloatHex;
extern std::string const memoryPointerAtLimitHex;
extern std::string const memoryPointerOverLimitHex;
extern std::string const memoryOffsetOverLimitHex;
extern std::string const memoryEndOfWordOverLimitHex;
extern std::string const memoryGrow0To1PageHex;
extern std::string const memoryGrow1To0PageHex;
extern std::string const memoryLastByteOf8MBHex;
extern std::string const memoryGrow1MoreThan8MBHex;
extern std::string const memoryGrow0MoreThan8MBHex;
extern std::string const memoryInit1MoreThan8MBHex;
extern std::string const memoryNegativeAddressHex;
extern std::string const table64ElementsHex;
extern std::string const table65ElementsHex;
extern std::string const table2TablesHex;
extern std::string const tableUintMaxHex;
extern std::string const proposalMutableGlobalHex;
extern std::string const proposalGcStructNewHex;
extern std::string const proposalMultiValueHex;
extern std::string const proposalSignExtHex;
extern std::string const proposalFloatToIntHex;
extern std::string const proposalBulkMemoryHex;
extern std::string const proposalRefTypesHex;
extern std::string const proposalTailCallHex;
extern std::string const proposalExtendedConst;
extern std::string const trapDivideBy0Hex;
extern std::string const trapIntOverflow;
extern std::string const trapUnreachable;
extern std::string const trapNullCall;
extern std::string const trapFuncSigMismatch;
extern std::string const wasiGetTimeHex;
extern std::string const wasiPrintHex;

View File

@@ -0,0 +1,28 @@
(module
;; 1. Define Memory: 1 Page = 64KB = 65,536 bytes
(memory 1)
;; Export memory so the host can inspect it if needed
(export "memory" (memory 0))
(func $test_straddle (result i32)
;; Push the address onto the stack.
;; 65534 is valid, but it is only 2 bytes away from the end.
i32.const 65534
;; Attempt to load an i32 (4 bytes) from that address.
;; This requires bytes 65534, 65535, 65536, and 65537.
;; Since 65536 is the first invalid byte, this MUST trap.
i32.load
;; Clean up the stack.
;; The load pushed a value, but we don't care what it is.
drop
;; Return 1 to signal "I survived the memory access"
i32.const 1
)
;; Export the function so you can call it from your host (JS, Python, etc.)
(export "finish" (func $test_straddle))
)

View File

@@ -0,0 +1,29 @@
(module
;; Start at your limit: 128 pages (8MB)
(memory 128)
(export "memory" (memory 0))
(func $try_grow_beyond_limit (result i32)
;; Attempt to grow by 0 page
i32.const 0
memory.grow
;; memory.grow returns:
;; -1 if the growth failed (Correct behavior for your limit)
;; 128 (old size) if growth succeeded (Means limit was bypassed)
;; Check if result == -1
i32.const -1
i32.eq
if
;; Growth FAILED (Host blocked it). Return -1.
i32.const -1
return
end
;; Growth SUCCEEDED (Host allowed it). Return 1.
i32.const 1
)
(export "finish" (func $try_grow_beyond_limit))
)

View File

@@ -0,0 +1,26 @@
(module
;; 1. Define Memory: Start with 0 pages
(memory 0)
;; Export memory to host
(export "memory" (memory 0))
(func $grow_from_zero (result i32)
;; We have 0 pages. We want to add 1 page.
;; Push delta (1) onto stack.
i32.const 1
;; Grow the memory.
;; If successful: memory becomes 64KB, returns old size (0).
;; If failed: memory stays 0, returns -1.
memory.grow
;; Drop the return value of memory.grow
drop
;; Return 1 (as requested)
i32.const 1
)
(export "finish" (func $grow_from_zero))
)

View File

@@ -0,0 +1,29 @@
(module
;; Start at your limit: 128 pages (8MB)
(memory 128)
(export "memory" (memory 0))
(func $try_grow_beyond_limit (result i32)
;; Attempt to grow by 1 page
i32.const 1
memory.grow
;; memory.grow returns:
;; -1 if the growth failed (Correct behavior for your limit)
;; 128 (old size) if growth succeeded (Means limit was bypassed)
;; Check if result == -1
i32.const -1
i32.eq
if
;; Growth FAILED (Host blocked it). Return -1.
i32.const -1
return
end
;; Growth SUCCEEDED (Host allowed it). Return 1.
i32.const 1
)
(export "finish" (func $try_grow_beyond_limit))
)

View File

@@ -0,0 +1,24 @@
(module
;; 1. Define Memory: Start with 1 pages
(memory 1)
;; Export memory to host
(export "memory" (memory 0))
(func $grow_negative (result i32)
;; We have 1 page. We want to reduce 1 page.
;; Push delta (-1) onto stack.
i32.const -1
;; Grow the memory.
memory.grow
;; Drop the return value of memory.grow
drop
;; Return 1 (as requested)
i32.const 1
)
(export "finish" (func $grow_negative))
)

View File

@@ -0,0 +1,27 @@
(module
;; Define memory: 129 pages (> 8MB limit) min, 129 pages max
(memory 129 129)
;; Export memory so host can verify size
(export "memory" (memory 0))
;; access last byte of 8MB limit
(func $access_last_byte (result i32)
;; Math: 128 pages * 64,536 bytes/page = 8,388,608 bytes
;; Valid indices: 0 to 8,388,607
;; Push the address of the LAST valid byte
i32.const 8388607
;; Load byte from that address
i32.load8_u
;; Drop the value (we don't care what it is, just that we could read it)
drop
;; Return 1 to indicate success
i32.const 1
)
(export "finish" (func $access_last_byte))
)

View File

@@ -0,0 +1,26 @@
(module
;; Define memory: 128 pages (8MB) min, 128 pages max
(memory 128 128)
;; Export memory so host can verify size
(export "memory" (memory 0))
(func $access_last_byte (result i32)
;; Math: 128 pages * 64,536 bytes/page = 8,388,608 bytes
;; Valid indices: 0 to 8,388,607
;; Push the address of the LAST valid byte
i32.const 8388607
;; Load byte from that address
i32.load8_u
;; Drop the value (we don't care what it is, just that we could read it)
drop
;; Return 1 to indicate success
i32.const 1
)
(export "finish" (func $access_last_byte))
)

View File

@@ -0,0 +1,23 @@
(module
;; Define memory: 128 pages (8MB) min, 128 pages max
(memory 128 128)
;; Export memory so host can verify size
(export "memory" (memory 0))
(func $access_last_byte (result i32)
;; Push a negative address
i32.const -1
;; Load byte from that address
i32.load8_u
;; Drop the value
drop
;; Return 1 to indicate success
i32.const 1
)
(export "finish" (func $access_last_byte))
)

View File

@@ -0,0 +1,27 @@
(module
;; 1. Define Memory: 1 Page = 64KB
(memory 1)
(export "memory" (memory 0))
(func $test_offset_overflow (result i32)
;; 1. Push the base address onto the stack.
;; We use '0', which is the safest, most valid address possible.
i32.const 0
;; 2. Attempt to load using a static offset.
;; syntax: i32.load offset=N align=N
;; We set the offset to 65536 (the size of the memory).
;; The effective address becomes 0 + 65536 = 65536.
i32.load offset=65536
;; Clean up the stack.
;; The load pushed a value, but we don't care what it is.
drop
;; Return 1 to signal "I survived the memory access"
i32.const 1
)
(export "finish" (func $test_offset_overflow))
)

View File

@@ -0,0 +1,22 @@
(module
;; Define 1 page of memory (64KB = 65,536 bytes)
(memory 1)
(func $read_edge (result i32)
;; Push the index of the LAST valid byte
i32.const 65535
;; Load 1 byte (unsigned)
i32.load8_u
;; Clean up the stack.
;; The load pushed a value, but we don't care what it is.
drop
;; Return 1 to signal "I survived the memory access"
i32.const 1
)
;; Export as "finish" as requested
(export "finish" (func $read_edge))
)

View File

@@ -0,0 +1,23 @@
(module
;; Define 1 page of memory (64KB = 65,536 bytes)
(memory 1)
(func $read_overflow (result i32)
;; Push the index of the FIRST invalid byte
;; Memory is 0..65535, so 65536 is out of bounds.
i32.const 65536
;; Load 1 byte (unsigned)
i32.load8_u
;; Clean up the stack.
;; The load pushed a value, but we don't care what it is.
drop
;; Return 1 to signal "I survived the memory access"
i32.const 1
)
;; Export as "finish" as requested
(export "finish" (func $read_overflow))
)

View File

@@ -0,0 +1,25 @@
(module
;; Define 1 page of memory
(memory 1)
(export "memory" (memory 0))
(func $test_bulk_ops (result i32)
;; Setup: Write value 42 at index 0 so we have something to copy
(i32.store8 (i32.const 0) (i32.const 42))
;; Test memory.copy (Opcode 0xFC 0x0A)
;; Copy 1 byte from offset 0 to offset 100
(memory.copy
(i32.const 100) ;; Destination Offset
(i32.const 0) ;; Source Offset
(i32.const 1) ;; Size (bytes)
)
;; Verify: Read byte at offset 100. Should be 42.
(i32.load8_u (i32.const 100))
(i32.const 42)
i32.eq
)
(export "finish" (func $test_bulk_ops))
)

View File

@@ -0,0 +1,15 @@
(module
;; 1. Define a global using an EXTENDED constant expression.
;; MVP only allows (i32.const X).
;; This proposal allows (i32.add (i32.const X) (i32.const Y)).
(global $g i32 (i32.add (i32.const 10) (i32.const 32)))
(func $finish (result i32)
;; 2. verify the global equals 42
global.get $g
i32.const 42
i32.eq
)
(export "finish" (func $finish))
)

View File

@@ -0,0 +1,18 @@
(module
(func $test_saturation (result i32)
;; 1. Push a float that is too big for a 32-bit integer
;; 1e10 (10 billion) > 2.14 billion (Max i32)
f32.const 1.0e10
;; 2. Attempt saturating conversion (Opcode 0xFC 0x00)
;; If supported: Clamps to MAX_I32.
;; If disabled: Validation error (unknown instruction).
i32.trunc_sat_f32_s
;; 3. Check if result is MAX_I32 (2147483647)
i32.const 2147483647
i32.eq
)
(export "finish" (func $test_saturation))
)

View File

@@ -0,0 +1,12 @@
;; generated by wasm-tools print gc_test.wasm that has the following hex
;; 0061736d01000000010b026000017f5f027f017f0103020100070a010666696e69736800000a0a010800fb01011a41010b
(module
(type (;0;) (func (result i32)))
(type (;1;) (struct (field (mut i32)) (field (mut i32))))
(export "finish" (func 0))
(func (;0;) (type 0) (result i32)
struct.new_default 1
drop
i32.const 1
)
)

View File

@@ -0,0 +1,22 @@
(module
;; 1. Function returning TWO values (Multi-Value feature)
(func $get_numbers (result i32 i32)
i32.const 10
i32.const 20
)
(func $finish (result i32)
;; Call pushes [10, 20] onto the stack
call $get_numbers
;; 2. Block taking TWO parameters (Multi-Value feature)
;; It consumes the [10, 20] from the stack.
block (param i32 i32) (result i32)
i32.add ;; 10 + 20 = 30
i32.const 30 ;; Expected result
i32.eq ;; Compare: returns 1 if equal
end
)
(export "finish" (func $finish))
)

View File

@@ -0,0 +1,25 @@
(module
;; Define a mutable global initialized to 0
(global $counter (mut i32) (i32.const 0))
;; EXPORTING a mutable global is the key feature of this proposal.
;; In strict MVP, exported globals had to be immutable (const).
(export "counter" (global $counter))
(func $finish (result i32)
;; 1. Get current value
global.get $counter
;; 2. Add 1
i32.const 1
i32.add
;; 3. Set new value (Mutation)
global.set $counter
;; 4. Return 1 for success
i32.const 1
)
(export "finish" (func $finish))
)

View File

@@ -0,0 +1,18 @@
(module
;; Import a table from the host that holds externrefs
(import "env" "table" (table 1 externref))
(func $test_ref_types (result i32)
;; Store a null externref into the table at index 0
;; If reference_types is disabled, 'externref' and 'ref.null' will fail parsing.
(table.set
(i32.const 0) ;; Index
(ref.null extern) ;; Value (Null External Reference)
)
;; Return 1 (Success)
i32.const 1
)
(export "finish" (func $test_ref_types))
)

View File

@@ -0,0 +1,18 @@
(module
(func $test_sign_ext (result i32)
;; Push 255 (0x000000FF) onto the stack
i32.const 255
;; Sign-extend from 8-bit to 32-bit
;; If 255 is treated as an i8, it is -1.
;; Result should be -1 (0xFFFFFFFF).
;; Without this proposal, this opcode (0xC0) causes a validation error.
i32.extend8_s
;; Check if result is -1
i32.const -1
i32.eq
)
(export "finish" (func $test_sign_ext))
)

View File

@@ -0,0 +1 @@
;;hard to generate

View File

@@ -0,0 +1,15 @@
(module
;; Define a simple function we can tail-call
(func $target (result i32)
i32.const 1
)
(func $finish (result i32)
;; Try to use the 'return_call' instruction (Opcode 0x12)
;; If Tail Call proposal is disabled, this fails to Compile/Validate.
;; If enabled, it jumps to $target, which returns 1.
return_call $target
)
(export "finish" (func $finish))
)

View File

@@ -0,0 +1,24 @@
(module
;; Define a dummy function to put in the tables
(func $dummy)
;; TABLE 0: The default table (allowed in MVP)
;; Size: 1 initial, 1 max
(table $t0 1 1 funcref)
;; Initialize Table 0 at index 0
(elem (table $t0) (i32.const 0) $dummy)
;; TABLE 1: The second table (Requires Reference Types proposal)
;; If strict MVP is enforced, the parser should error here.
(table $t1 1 1 funcref)
;; Initialize Table 1 at index 0
(elem (table $t1) (i32.const 0) $dummy)
(func $finish (result i32)
;; If we successfully loaded a module with 2 tables, return 1.
i32.const 1
)
(export "finish" (func $finish))
)

View File

@@ -0,0 +1,25 @@
(module
;; Define a table with exactly 64 entries
(table 64 funcref)
;; A dummy function to reference
(func $dummy)
;; Initialize the table at offset 0 with 64 references to $dummy
(elem (i32.const 0)
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 8
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 16
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 24
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 32
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 40
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 48
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 56
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 64
)
;; Standard finish function
(func $finish (result i32)
i32.const 1
)
(export "finish" (func $finish))
)

View File

@@ -0,0 +1,25 @@
(module
;; Define a table with exactly 65 entries
(table 65 funcref)
;; A dummy function to reference
(func $dummy)
;; Initialize the table at offset 0 with 65 references to $dummy
(elem (i32.const 0)
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 8
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 16
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 24
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 32
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 40
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 48
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 56
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 64
$dummy ;; 65 (The one that breaks the camel's back)
)
(func $finish (result i32)
i32.const 1
)
(export "finish" (func $finish))
)

View File

@@ -0,0 +1,15 @@
(module
;; Definition: (table <min> <optional_max> <type>)
;; We use 0xFFFFFFFF (4,294,967,295), which is the unsigned equivalent of -1.
;; This tests if the runtime handles the maximum possible u32 value
;; without integer overflows or attempting a massive allocation.
;;
;; Note that using -1 as the table size cannot be parsed by wasm-tools or wat2wasm
(table 0xFFFFFFFF funcref)
(func $finish (result i32)
;; If the module loads despite the massive table, return 1.
i32.const 1
)
(export "finish" (func $finish))
)

View File

@@ -0,0 +1,15 @@
(module
(func $finish (export "finish") (result i32)
;; Setup for Requirement 2: Divide an i32 by 0
i32.const 42 ;; Push numerator
i32.const 0 ;; Push denominator (0)
i32.div_s ;; Perform signed division (42 / 0)
;; --- NOTE: Execution usually traps (crashes) at the line above ---
;; Logic to satisfy Requirement 1: Return i32 = 1
;; If execution continued, we would drop the division result and return 1
drop ;; Clear the stack
i32.const 1 ;; Push the return value
)
)

View File

@@ -0,0 +1,33 @@
(module
;; Define a table with 1 slot
(table 1 funcref)
;; Define Type A: Takes nothing, returns nothing
(type $type_void (func))
;; Define Type B: Takes nothing, returns i32
(type $type_i32 (func (result i32)))
;; Define a function of Type A
(func $void_func (type $type_void)
nop
)
;; Put Type A function into Table[0]
(elem (i32.const 0) $void_func)
(func $finish (result i32)
;; Attempt to call Index 0, but CLAIM we expect Type B (result i32).
;; The function at Index 0 matches Type A.
;; TRAP: "indirect call type mismatch"
;; 1. Push the table index (0) onto the stack
i32.const 0
;; 2. Call indirect using Type B signature.
;; This pops the index (0) from the stack.
call_indirect (type $type_i32)
)
(export "finish" (func $finish))
)

View File

@@ -0,0 +1,18 @@
(module
(func $test_int_overflow (result i32)
;; 1. Push INT_MIN (-2147483648)
;; In Hex: 0x80000000
i32.const -2147483648
;; 2. Push -1
i32.const -1
;; 3. Signed Division
;; This specific case is the ONLY integer arithmetic operation
;; (besides divide by zero) that traps in the spec.
;; Result would be +2147483648, which is too big for signed i32.
i32.div_s
)
(export "finish" (func $test_int_overflow))
)

View File

@@ -0,0 +1,22 @@
(module
;; Table size is 1, so Index 0 is VALID bounds.
;; However, we do NOT initialize it, so it contains 'ref.null'.
(table 1 funcref)
(type $t (func (result i32)))
(func $finish (result i32)
;; Call Index 0.
;; Bounds check passes (0 < 1).
;; Null check fails.
;; TRAP: "uninitialized element" or "undefined element"
;; 1. Push the index (0) onto the stack first
i32.const 0
;; 2. Perform the call. This pops the index.
call_indirect (type $t)
)
(export "finish" (func $finish))
)

View File

@@ -0,0 +1,12 @@
(module
(func $finish (result i32)
;; This instruction explicitly causes a trap.
;; It consumes no fuel (beyond the instruction itself) and stops execution.
unreachable
;; This code is dead and never reached
i32.const 1
)
(export "finish" (func $finish))
)

View File

@@ -0,0 +1,38 @@
(module
;; Import clock_time_get from WASI
;; Signature: (param clock_id precision return_ptr) (result errno)
(import "wasi_snapshot_preview1" "clock_time_get"
(func $clock_time_get (param i32 i64 i32) (result i32))
)
(memory 1)
(export "memory" (memory 0))
(func $finish (result i32)
;; We will store the timestamp (a 64-bit integer) at address 0.
;; No setup required in memory beforehand!
;; Call the function
(call $clock_time_get
(i32.const 0) ;; clock_id: 0 = Realtime (Wallclock)
(i64.const 1000) ;; precision: 1000ns (hint to OS)
(i32.const 0) ;; result_ptr: Write the time to address 0
)
;; The function returns an 'errno' (error code).
;; 0 = Success. Anything else = Error.
;; Check if errno (top of stack) is 0
i32.eqz
if (result i32)
;; Success! The time is now stored in heap[0..8].
;; We return 1 as requested.
i32.const 1
else
;; Failed (maybe WASI is disabled or clock is missing)
i32.const -1
end
)
(export "finish" (func $finish))
)

View File

@@ -0,0 +1,59 @@
(module
;; Import WASI fd_write
;; Signature: (fd, iovs_ptr, iovs_len, nwritten_ptr) -> errno
(import "wasi_snapshot_preview1" "fd_write"
(func $fd_write (param i32 i32 i32 i32) (result i32))
)
(memory 1)
(export "memory" (memory 0))
;; --- DATA SEGMENTS ---
;; 1. The String Data "Hello\n" placed at offset 16
;; We assume offset 0-16 is reserved for the IOVec struct
(data (i32.const 16) "Hello\n")
;; 2. The IO Vector (struct iovec) placed at offset 0
;; Structure: { buf_ptr: u32, buf_len: u32 }
;; Field 1: buf_ptr = 16 (Location of "Hello\n")
;; Encoded in little-endian: 10 00 00 00
(data (i32.const 0) "\10\00\00\00")
;; Field 2: buf_len = 6 (Length of "Hello\n")
;; Encoded in little-endian: 06 00 00 00
(data (i32.const 4) "\06\00\00\00")
(func $finish (result i32)
(local $nwritten_ptr i32)
;; We will ask WASI to write the "number of bytes written" to address 24
;; (safely after our string data)
i32.const 24
local.set $nwritten_ptr
;; Call fd_write
(call $fd_write
(i32.const 1) ;; fd: 1 = STDOUT
(i32.const 0) ;; iovs_ptr: Address 0 (where we defined the struct)
(i32.const 1) ;; iovs_len: We are passing 1 vector
(local.get $nwritten_ptr) ;; nwritten_ptr: Address 24
)
;; The function returns an 'errno' (i32).
;; 0 means Success.
;; Check if errno == 0
i32.eqz
if (result i32)
;; Success: Return 1
i32.const 1
else
;; Failure: Return -1
i32.const -1
end
)
(export "finish" (func $finish))
)