From 39adde408a2080ddbc0a6540b5b69cc6bd27175d Mon Sep 17 00:00:00 2001 From: nerodesu017 Date: Mon, 18 Nov 2024 11:43:24 +0200 Subject: [PATCH 01/12] chore: format Signed-off-by: nerodesu017 --- tests/add_one.rs | 14 +- tests/arithmetic/bitwise.rs | 431 ++++++++++++++++---------------- tests/arithmetic/division.rs | 24 +- tests/arithmetic/multiply.rs | 14 +- tests/arithmetic/remainder.rs | 82 +++--- tests/arithmetic/subtraction.rs | 18 +- tests/basic_memory.rs | 2 +- tests/conversions.rs | 4 +- tests/f32.rs | 8 +- tests/function_recursion.rs | 2 +- tests/i32.rs | 6 +- tests/i64.rs | 2 +- tests/specification/run.rs | 2 +- 13 files changed, 300 insertions(+), 309 deletions(-) diff --git a/tests/add_one.rs b/tests/add_one.rs index 26286119..4c9df8f5 100644 --- a/tests/add_one.rs +++ b/tests/add_one.rs @@ -1,6 +1,6 @@ use wasm::{validate, RuntimeInstance, DEFAULT_MODULE}; -const MULTIPLY_WAT_TEMPLATE: &'static str = r#" +const MULTIPLY_WAT_TEMPLATE: &str = r#" (module (func (export "add_one") (param $x {{TYPE}}) (result {{TYPE}}) local.get $x @@ -63,21 +63,21 @@ fn i64_add_one() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - 12 as i64, + 12_i64, instance - .invoke(&instance.get_function_by_index(0, 0).unwrap(), 11 as i64) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 11_i64) .unwrap() ); assert_eq!( - 1 as i64, + 1_i64, instance - .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0 as i64) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0_i64) .unwrap() ); assert_eq!( - -5 as i64, + -5_i64, instance - .invoke(&instance.get_function_by_index(0, 0).unwrap(), -6 as i64) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -6_i64) .unwrap() ); } diff --git a/tests/arithmetic/bitwise.rs b/tests/arithmetic/bitwise.rs index 0e19ead7..d913b65b 100644 --- a/tests/arithmetic/bitwise.rs +++ b/tests/arithmetic/bitwise.rs @@ -1,6 +1,6 @@ use wasm::{validate, RuntimeInstance}; -const BASE_WAT: &'static str = r#" +const BASE_WAT: &str = r#" (module (func (export "template") (param $x i32) (param $y i32) (result i32) local.get $x @@ -9,7 +9,7 @@ const BASE_WAT: &'static str = r#" ) "#; -const BASE_COUNT_WAT: &'static str = r#" +const BASE_COUNT_WAT: &str = r#" (module (func (export "template") (param $x i32) (result i32) local.get $x @@ -509,7 +509,7 @@ pub fn i32_bitwise_shr_u() { // Minimum and maximum 32-bit integers assert_eq!( - (i32::MIN / 2) * (-1), + -(i32::MIN / 2), instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), @@ -1055,7 +1055,7 @@ pub fn i32_bitwise_popcnt() { ); } -const I64_BASE_WAT: &'static str = r#" +const I64_BASE_WAT: &str = r#" (module (func (export "template") (param $x i64) (param $y i64) (result i64) local.get $x @@ -1064,7 +1064,7 @@ const I64_BASE_WAT: &'static str = r#" ) "#; -const I64_BASE_COUNT_WAT: &'static str = r#" +const I64_BASE_COUNT_WAT: &str = r#" (module (func (export "template") (param $x i64) (result i64) local.get $x @@ -1084,34 +1084,34 @@ pub fn i64_bitwise_and() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - 1 as i64, + 1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (33 as i64, 11 as i64) + (33_i64, 11_i64) ) .unwrap() ); assert_eq!( - 5 as i64, + 5_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (77 as i64, 23 as i64) + (77_i64, 23_i64) ) .unwrap() ); assert_eq!( - 180244 as i64, + 180244_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (192534 as i64, 1231412 as i64) + (192534_i64, 1231412_i64) ) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), @@ -1133,34 +1133,34 @@ pub fn i64_bitwise_or() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - 43 as i64, + 43_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (33 as i64, 11 as i64) + (33_i64, 11_i64) ) .unwrap() ); assert_eq!( - 95 as i64, + 95_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (77 as i64, 23 as i64) + (77_i64, 23_i64) ) .unwrap() ); assert_eq!( - 1243702 as i64, + 1243702_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (192534 as i64, 1231412 as i64) + (192534_i64, 1231412_i64) ) .unwrap() ); assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), @@ -1182,34 +1182,34 @@ pub fn i64_bitwise_xor() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - 42 as i64, + 42_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (33 as i64, 11 as i64) + (33_i64, 11_i64) ) .unwrap() ); assert_eq!( - 90 as i64, + 90_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (77 as i64, 23 as i64) + (77_i64, 23_i64) ) .unwrap() ); assert_eq!( - 1063458 as i64, + 1063458_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (192534 as i64, 1231412 as i64) + (192534_i64, 1231412_i64) ) .unwrap() ); assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), @@ -1231,34 +1231,34 @@ pub fn i64_bitwise_shl() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - 67584 as i64, + 67584_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (33 as i64, 11 as i64) + (33_i64, 11_i64) ) .unwrap() ); assert_eq!( - 645922816 as i64, + 645922816_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (77 as i64, 23 as i64) + (77_i64, 23_i64) ) .unwrap() ); assert_eq!( - 99079191802150912 as i64, + 99079191802150912_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (192534 as i64, 1231412 as i64) + (192534_i64, 1231412_i64) ) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), @@ -1280,43 +1280,43 @@ pub fn i64_bitwise_shr_s() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - 8881445 as i64, + 8881445_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (142_103_123 as i64, 4 as i64) + (142_103_123_i64, 4_i64) ) .unwrap() ); assert_eq!( - 23879 as i64, + 23879_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (391_248_921 as i64, 14 as i64) + (391_248_921_i64, 14_i64) ) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (1_203_910_012 as i64, 33 as i64) + (1_203_910_012_i64, 33_i64) ) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (2_113_189_231 as i64, 33 as i64) + (2_113_189_231_i64, 33_i64) ) .unwrap() ); assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), @@ -1327,109 +1327,109 @@ pub fn i64_bitwise_shr_s() { // Basic positive number assert_eq!( - 4 as i64, + 4_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (8 as i64, 1 as i64) + (8_i64, 1_i64) ) .unwrap() ); // Shifting by 0 (no shift) assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 0 as i64) + (-1_i64, 0_i64) ) .unwrap() ); assert_eq!( - 1 as i64, + 1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (1 as i64, 0 as i64) + (1_i64, 0_i64) ) .unwrap() ); // Shifting negative numbers assert_eq!( - -4 as i64, + -4_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-8 as i64, 1 as i64) + (-8_i64, 1_i64) ) .unwrap() ); assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 1 as i64) + (-1_i64, 1_i64) ) .unwrap() ); // Shifting by 31 (maximum shift for 32-bit int) assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 31 as i64) + (-1_i64, 31_i64) ) .unwrap() ); assert_eq!( - -4294967296 as i64, + -4294967296_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN, 31 as i64) + (i64::MIN, 31_i64) ) .unwrap() ); assert_eq!( - 4294967295 as i64, + 4294967295_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MAX, 31 as i64) + (i64::MAX, 31_i64) ) .unwrap() ); // Shifting by more than 31 assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 32 as i64) + (-1_i64, 32_i64) ) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (1 as i64, 32 as i64) + (1_i64, 32_i64) ) .unwrap() ); assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 100 as i64) + (-1_i64, 100_i64) ) .unwrap() ); @@ -1440,7 +1440,7 @@ pub fn i64_bitwise_shr_s() { instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN, 1 as i64) + (i64::MIN, 1_i64) ) .unwrap() ); @@ -1449,27 +1449,27 @@ pub fn i64_bitwise_shr_s() { instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MAX, 1 as i64) + (i64::MAX, 1_i64) ) .unwrap() ); // Shifting out all bits except sign assert_eq!( - -8589934592 as i64, + -8589934592_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN, 30 as i64) + (i64::MIN, 30_i64) ) .unwrap() ); assert_eq!( - 8589934591 as i64, + 8589934591_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MAX, 30 as i64) + (i64::MAX, 30_i64) ) .unwrap() ); @@ -1487,43 +1487,43 @@ pub fn i64_bitwise_shr_u() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - 8881445 as i64, + 8881445_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (142_103_123 as i64, 4 as i64) + (142_103_123_i64, 4_i64) ) .unwrap() ); assert_eq!( - 23879 as i64, + 23879_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (391_248_921 as i64, 14 as i64) + (391_248_921_i64, 14_i64) ) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (1_203_910_012 as i64, 33 as i64) + (1_203_910_012_i64, 33_i64) ) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (2_113_189_231 as i64, 33 as i64) + (2_113_189_231_i64, 33_i64) ) .unwrap() ); assert_eq!( - 1 as i64, + 1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), @@ -1534,31 +1534,31 @@ pub fn i64_bitwise_shr_u() { // Basic positive number assert_eq!( - 4 as i64, + 4_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (8 as i64, 1 as i64) + (8_i64, 1_i64) ) .unwrap() ); // Shifting by 0 (no shift) assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 0 as i64) + (-1_i64, 0_i64) ) .unwrap() ); assert_eq!( - 1 as i64, + 1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (1 as i64, 0 as i64) + (1_i64, 0_i64) ) .unwrap() ); @@ -1569,7 +1569,7 @@ pub fn i64_bitwise_shr_u() { instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-8 as i64, 1 as i64) + (-8_i64, 1_i64) ) .unwrap() ); @@ -1578,76 +1578,76 @@ pub fn i64_bitwise_shr_u() { instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 1 as i64) + (-1_i64, 1_i64) ) .unwrap() ); // Shifting by 31 (maximum shift for 32-bit int) assert_eq!( - 8589934591 as i64, + 8589934591_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 31 as i64) + (-1_i64, 31_i64) ) .unwrap() ); assert_eq!( - 4294967296 as i64, + 4294967296_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN, 31 as i64) + (i64::MIN, 31_i64) ) .unwrap() ); assert_eq!( - 4294967295 as i64, + 4294967295_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MAX, 31 as i64) + (i64::MAX, 31_i64) ) .unwrap() ); // Shifting by more than 31 assert_eq!( - 4294967295 as i64, + 4294967295_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 32 as i64) + (-1_i64, 32_i64) ) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (1 as i64, 32 as i64) + (1_i64, 32_i64) ) .unwrap() ); assert_eq!( - 268435455 as i64, + 268435455_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 100 as i64) + (-1_i64, 100_i64) ) .unwrap() ); // Minimum and maximum 32-bit integers assert_eq!( - (i64::MIN / 2) * (-1), + -(i64::MIN / 2), instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN, 1 as i64) + (i64::MIN, 1_i64) ) .unwrap() ); @@ -1656,27 +1656,27 @@ pub fn i64_bitwise_shr_u() { instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MAX, 1 as i64) + (i64::MAX, 1_i64) ) .unwrap() ); // Shifting out all bits except sign assert_eq!( - 8589934592 as i64, + 8589934592_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN, 30 as i64) + (i64::MIN, 30_i64) ) .unwrap() ); assert_eq!( - 8589934591 as i64, + 8589934591_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MAX, 30 as i64) + (i64::MAX, 30_i64) ) .unwrap() ); @@ -1694,43 +1694,43 @@ pub fn i64_bitwise_rotl() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - 2273649968 as i64, + 2273649968_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (142_103_123 as i64, 4 as i64) + (142_103_123_i64, 4_i64) ) .unwrap() ); assert_eq!( - 6410222321664 as i64, + 6410222321664_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (391_248_921 as i64, 14 as i64) + (391_248_921_i64, 14_i64) ) .unwrap() ); assert_eq!( - -8105235815975616512 as i64, + -8105235815975616512_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (1_203_910_012 as i64, 33 as i64) + (1_203_910_012_i64, 33_i64) ) .unwrap() ); assert_eq!( - -294586798900772864 as i64, + -294586798900772864_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (2_113_189_231 as i64, 33 as i64) + (2_113_189_231_i64, 33_i64) ) .unwrap() ); assert_eq!( - 4611686018427387904 as i64, + 4611686018427387904_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), @@ -1741,149 +1741,149 @@ pub fn i64_bitwise_rotl() { // Basic positive number assert_eq!( - 16 as i64, + 16_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (8 as i64, 1 as i64) + (8_i64, 1_i64) ) .unwrap() ); // Rotating by 0 (no shift) assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 0 as i64) + (-1_i64, 0_i64) ) .unwrap() ); assert_eq!( - 1 as i64, + 1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (1 as i64, 0 as i64) + (1_i64, 0_i64) ) .unwrap() ); // Shifting negative numbers assert_eq!( - -15 as i64, + -15_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-8 as i64, 1 as i64) + (-8_i64, 1_i64) ) .unwrap() ); assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 1 as i64) + (-1_i64, 1_i64) ) .unwrap() ); // Rotating by 31 assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 31 as i64) + (-1_i64, 31_i64) ) .unwrap() ); assert_eq!( - 1073741824 as i64, + 1073741824_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN, 31 as i64) + (i64::MIN, 31_i64) ) .unwrap() ); assert_eq!( - -1073741825 as i64, + -1073741825_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MAX, 31 as i64) + (i64::MAX, 31_i64) ) .unwrap() ); // Rotating by more than 31 assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 32 as i64) + (-1_i64, 32_i64) ) .unwrap() ); assert_eq!( - 4294967296 as i64, + 4294967296_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (1 as i64, 32 as i64) + (1_i64, 32_i64) ) .unwrap() ); assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 100 as i64) + (-1_i64, 100_i64) ) .unwrap() ); // Minimum and maximum 32-bit integers assert_eq!( - 1 as i64, + 1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN, 1 as i64) + (i64::MIN, 1_i64) ) .unwrap() ); assert_eq!( - -2 as i64, + -2_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MAX, 1 as i64) + (i64::MAX, 1_i64) ) .unwrap() ); // Shifting out all bits except sign assert_eq!( - 536870912 as i64, + 536870912_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN, 30 as i64) + (i64::MIN, 30_i64) ) .unwrap() ); assert_eq!( - -536870913 as i64, + -536870913_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MAX, 30 as i64) + (i64::MAX, 30_i64) ) .unwrap() ); @@ -1901,43 +1901,43 @@ pub fn i64_bitwise_rotr() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - 3458764513829422373 as i64, + 3458764513829422373_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (142_103_123 as i64, 4 as i64) + (142_103_123_i64, 4_i64) ) .unwrap() ); assert_eq!( - -1124774006935757497 as i64, + -1124774006935757497_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (391_248_921 as i64, 14 as i64) + (391_248_921_i64, 14_i64) ) .unwrap() ); assert_eq!( - 2585377064433483776 as i64, + 2585377064433483776_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (1_203_910_012 as i64, 33 as i64) + (1_203_910_012_i64, 33_i64) ) .unwrap() ); assert_eq!( - 4538039318702194688 as i64, + 4538039318702194688_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (2_113_189_231 as i64, 33 as i64) + (2_113_189_231_i64, 33_i64) ) .unwrap() ); assert_eq!( - 1 as i64, + 1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), @@ -1948,31 +1948,31 @@ pub fn i64_bitwise_rotr() { // Basic positive number assert_eq!( - 4 as i64, + 4_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (8 as i64, 1 as i64) + (8_i64, 1_i64) ) .unwrap() ); // Rotating by 0 (no shift) assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 0 as i64) + (-1_i64, 0_i64) ) .unwrap() ); assert_eq!( - 1 as i64, + 1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (1 as i64, 0 as i64) + (1_i64, 0_i64) ) .unwrap() ); @@ -1983,74 +1983,74 @@ pub fn i64_bitwise_rotr() { instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-8 as i64, 1 as i64) + (-8_i64, 1_i64) ) .unwrap() ); assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 1 as i64) + (-1_i64, 1_i64) ) .unwrap() ); // Rotating by 31 assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 31 as i64) + (-1_i64, 31_i64) ) .unwrap() ); assert_eq!( - 4294967296 as i64, + 4294967296_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN, 31 as i64) + (i64::MIN, 31_i64) ) .unwrap() ); assert_eq!( - -4294967297 as i64, + -4294967297_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MAX, 31 as i64) + (i64::MAX, 31_i64) ) .unwrap() ); // Rotating by more than 31 assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 32 as i64) + (-1_i64, 32_i64) ) .unwrap() ); assert_eq!( - 4294967296 as i64, + 4294967296_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (1 as i64, 32 as i64) + (1_i64, 32_i64) ) .unwrap() ); assert_eq!( - -1 as i64, + -1_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-1 as i64, 100 as i64) + (-1_i64, 100_i64) ) .unwrap() ); @@ -2061,7 +2061,7 @@ pub fn i64_bitwise_rotr() { instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN, 1 as i64) + (i64::MIN, 1_i64) ) .unwrap() ); @@ -2070,27 +2070,27 @@ pub fn i64_bitwise_rotr() { instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MAX, 1 as i64) + (i64::MAX, 1_i64) ) .unwrap() ); // Shifting out all bits except sign assert_eq!( - 8589934592 as i64, + 8589934592_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN, 30 as i64) + (i64::MIN, 30_i64) ) .unwrap() ); assert_eq!( - -8589934593 as i64, + -8589934593_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MAX, 30 as i64) + (i64::MAX, 30_i64) ) .unwrap() ); @@ -2108,42 +2108,39 @@ pub fn i64_bitwise_clz() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - 58 as i64, + 58_i64, instance - .invoke(&instance.get_function_by_index(0, 0).unwrap(), 33 as i64) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 33_i64) .unwrap() ); assert_eq!( - 57 as i64, + 57_i64, instance - .invoke(&instance.get_function_by_index(0, 0).unwrap(), 77 as i64) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 77_i64) .unwrap() ); assert_eq!( - 46 as i64, + 46_i64, instance - .invoke( - &instance.get_function_by_index(0, 0).unwrap(), - 192534 as i64 - ) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 192534_i64) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance .invoke(&instance.get_function_by_index(0, 0).unwrap(), i64::MIN) .unwrap() ); assert_eq!( - 1 as i64, + 1_i64, instance .invoke(&instance.get_function_by_index(0, 0).unwrap(), i64::MAX) .unwrap() ); assert_eq!( - 64 as i64, + 64_i64, instance - .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0 as i64) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0_i64) .unwrap() ); } @@ -2160,42 +2157,39 @@ pub fn i64_bitwise_ctz() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - 0 as i64, + 0_i64, instance - .invoke(&instance.get_function_by_index(0, 0).unwrap(), 33 as i64) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 33_i64) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance - .invoke(&instance.get_function_by_index(0, 0).unwrap(), 77 as i64) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 77_i64) .unwrap() ); assert_eq!( - 1 as i64, + 1_i64, instance - .invoke( - &instance.get_function_by_index(0, 0).unwrap(), - 192534 as i64 - ) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 192534_i64) .unwrap() ); assert_eq!( - 63 as i64, + 63_i64, instance .invoke(&instance.get_function_by_index(0, 0).unwrap(), i64::MIN) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance .invoke(&instance.get_function_by_index(0, 0).unwrap(), i64::MAX) .unwrap() ); assert_eq!( - 64 as i64, + 64_i64, instance - .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0 as i64) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0_i64) .unwrap() ); } @@ -2212,42 +2206,39 @@ pub fn i64_bitwise_popcnt() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - 2 as i64, + 2_i64, instance - .invoke(&instance.get_function_by_index(0, 0).unwrap(), 33 as i64) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 33_i64) .unwrap() ); assert_eq!( - 4 as i64, + 4_i64, instance - .invoke(&instance.get_function_by_index(0, 0).unwrap(), 77 as i64) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 77_i64) .unwrap() ); assert_eq!( - 8 as i64, + 8_i64, instance - .invoke( - &instance.get_function_by_index(0, 0).unwrap(), - 192534 as i64 - ) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 192534_i64) .unwrap() ); assert_eq!( - 1 as i64, + 1_i64, instance .invoke(&instance.get_function_by_index(0, 0).unwrap(), i64::MIN) .unwrap() ); assert_eq!( - 63 as i64, + 63_i64, instance .invoke(&instance.get_function_by_index(0, 0).unwrap(), i64::MAX) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance - .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0 as i64) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0_i64) .unwrap() ); } diff --git a/tests/arithmetic/division.rs b/tests/arithmetic/division.rs index 23eb291e..eab52137 100644 --- a/tests/arithmetic/division.rs +++ b/tests/arithmetic/division.rs @@ -1,7 +1,7 @@ use wasm::{validate, RuntimeInstance}; use wasm::{RuntimeError, DEFAULT_MODULE}; -const WAT_SIGNED_DIVISION_TEMPLATE: &'static str = r#" +const WAT_SIGNED_DIVISION_TEMPLATE: &str = r#" (module (func (export "signed_division") (param $divisor {{TYPE}}) (param $dividend {{TYPE}}) (result {{TYPE}}) local.get $divisor @@ -10,7 +10,7 @@ const WAT_SIGNED_DIVISION_TEMPLATE: &'static str = r#" ) "#; -const WAT_UNSIGNED_DIVISION_TEMPLATE: &'static str = r#" +const WAT_UNSIGNED_DIVISION_TEMPLATE: &str = r#" (module (func (export "unsigned_division") (param $divisor {{TYPE}}) (param $dividend {{TYPE}}) (result {{TYPE}}) local.get $divisor @@ -319,47 +319,47 @@ pub fn i64_division_signed_simple() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - 10 as i64, + 10_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (20 as i64, 2 as i64) + (20_i64, 2_i64) ) .unwrap() ); assert_eq!( - 9_001 as i64, + 9_001_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (81_018_001 as i64, 9_001 as i64) + (81_018_001_i64, 9_001_i64) ) .unwrap() ); assert_eq!( - -10 as i64, + -10_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (20 as i64, -2 as i64) + (20_i64, -2_i64) ) .unwrap() ); assert_eq!( - 10 as i64, + 10_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-20 as i64, -2 as i64) + (-20_i64, -2_i64) ) .unwrap() ); assert_eq!( - -10 as i64, + -10_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-20 as i64, 2 as i64) + (-20_i64, 2_i64) ) .unwrap() ); diff --git a/tests/arithmetic/multiply.rs b/tests/arithmetic/multiply.rs index 76d2e408..424572c5 100644 --- a/tests/arithmetic/multiply.rs +++ b/tests/arithmetic/multiply.rs @@ -1,6 +1,6 @@ use wasm::{validate, RuntimeInstance, DEFAULT_MODULE}; -const MULTIPLY_WAT_TEMPLATE: &'static str = r#" +const MULTIPLY_WAT_TEMPLATE: &str = r#" (module (func (export "multiply") (param $x {{TYPE}}) (result {{TYPE}}) local.get $x @@ -90,21 +90,21 @@ pub fn i64_multiply() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - 33 as i64, + 33_i64, instance - .invoke(&instance.get_function_by_index(0, 0).unwrap(), 11 as i64) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 11_i64) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance - .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0 as i64) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0_i64) .unwrap() ); assert_eq!( - -30 as i64, + -30_i64, instance - .invoke(&instance.get_function_by_index(0, 0).unwrap(), -10 as i64) + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -10_i64) .unwrap() ); diff --git a/tests/arithmetic/remainder.rs b/tests/arithmetic/remainder.rs index b0dccac4..d7bbdaa3 100644 --- a/tests/arithmetic/remainder.rs +++ b/tests/arithmetic/remainder.rs @@ -1,6 +1,6 @@ use wasm::{validate, RuntimeInstance}; use wasm::{RuntimeError, DEFAULT_MODULE}; -const REM_S_WAT: &'static str = r#" +const REM_S_WAT: &str = r#" (module (func (export "rem_s") (param $divisor {{TYPE}}) (param $dividend {{TYPE}}) (result {{TYPE}}) local.get $divisor @@ -9,7 +9,7 @@ const REM_S_WAT: &'static str = r#" ) "#; -const REM_U_WAT: &'static str = r#" +const REM_U_WAT: &str = r#" (module (func (export "rem_u") (param $divisor {{TYPE}}) (param $dividend {{TYPE}}) (result {{TYPE}}) local.get $divisor @@ -27,74 +27,74 @@ pub fn i64_remainder_signed_simple() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - 0 as i64, + 0_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (20 as i64, 2 as i64) + (20_i64, 2_i64) ) .unwrap() ); assert_eq!( - 999 as i64, + 999_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (10_000 as i64, 9_001 as i64) + (10_000_i64, 9_001_i64) ) .unwrap() ); assert_eq!( - -2 as i64, + -2_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-20 as i64, 3 as i64) + (-20_i64, 3_i64) ) .unwrap() ); assert_eq!( - -2 as i64, + -2_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-20 as i64, -3 as i64) + (-20_i64, -3_i64) ) .unwrap() ); assert_eq!( - 2 as i64, + 2_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (20 as i64, -3 as i64) + (20_i64, -3_i64) ) .unwrap() ); assert_eq!( - 2 as i64, + 2_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (20 as i64, 3 as i64) + (20_i64, 3_i64) ) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN as i64, -1 as i64) + (i64::MIN, -1_i64) ) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN as i64, 2 as i64) + (i64::MIN, 2_i64) ) .unwrap() ); @@ -110,7 +110,7 @@ pub fn i64_remainder_signed_panic_dividend_0() { let result = instance.invoke::<(i64, i64), i64>( &instance.get_function_by_index(0, 0).unwrap(), - (222 as i64, 0 as i64), + (222_i64, 0_i64), ); assert_eq!(result.unwrap_err(), RuntimeError::DivideBy0); @@ -125,11 +125,11 @@ pub fn i64_remainder_unsigned_simple() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - 0 as i64, + 0_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN, 2 as i64) + (i64::MIN, 2_i64) ) .unwrap() ); @@ -138,7 +138,7 @@ pub fn i64_remainder_unsigned_simple() { instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN, -2 as i64) + (i64::MIN, -2_i64) ) .unwrap() ); @@ -147,71 +147,71 @@ pub fn i64_remainder_unsigned_simple() { instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-2 as i64, i64::MIN) + (-2_i64, i64::MIN) ) .unwrap() ); assert_eq!( - 2 as i64, + 2_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (2 as i64, i64::MIN) + (2_i64, i64::MIN) ) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (20 as i64, 2 as i64) + (20_i64, 2_i64) ) .unwrap() ); assert_eq!( - 999 as i64, + 999_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (10_000 as i64, 9_001 as i64) + (10_000_i64, 9_001_i64) ) .unwrap() ); assert_eq!( - 2 as i64, + 2_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-20 as i64, 3 as i64) + (-20_i64, 3_i64) ) .unwrap() ); assert_eq!( - -20 as i64, + -20_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-20 as i64, -3 as i64) + (-20_i64, -3_i64) ) .unwrap() ); assert_eq!( - 20 as i64, + 20_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (20 as i64, -3 as i64) + (20_i64, -3_i64) ) .unwrap() ); assert_eq!( - 2 as i64, + 2_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (20 as i64, 3 as i64) + (20_i64, 3_i64) ) .unwrap() ); @@ -220,16 +220,16 @@ pub fn i64_remainder_unsigned_simple() { instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN, -1 as i64) + (i64::MIN, -1_i64) ) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN, 2 as i64) + (i64::MIN, 2_i64) ) .unwrap() ); @@ -397,7 +397,7 @@ pub fn i32_remainder_unsigned_simple() { .unwrap() ); assert_eq!( - (i32::MIN + 2) * (-1), + -(i32::MIN + 2), instance .invoke( &instance diff --git a/tests/arithmetic/subtraction.rs b/tests/arithmetic/subtraction.rs index 61ba11c6..1860085f 100644 --- a/tests/arithmetic/subtraction.rs +++ b/tests/arithmetic/subtraction.rs @@ -1,6 +1,6 @@ use wasm::{validate, RuntimeInstance}; -const WAT_SUBTRACT_TEMPLATE: &'static str = r#" +const WAT_SUBTRACT_TEMPLATE: &str = r#" (module (func (export "subtract") (param $x {{TYPE}}) (param $y {{TYPE}}) (result {{TYPE}}) local.get $x @@ -22,29 +22,29 @@ pub fn i64_subtract() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - -10 as i64, + -10_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (1 as i64, 11 as i64) + (1_i64, 11_i64) ) .unwrap() ); assert_eq!( - 0 as i64, + 0_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (0 as i64, 0 as i64) + (0_i64, 0_i64) ) .unwrap() ); assert_eq!( - 10 as i64, + 10_i64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (-10 as i64, -20 as i64) + (-10_i64, -20_i64) ) .unwrap() ); @@ -54,7 +54,7 @@ pub fn i64_subtract() { instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MAX - 1, 0 as i64) + (i64::MAX - 1, 0_i64) ) .unwrap() ); @@ -63,7 +63,7 @@ pub fn i64_subtract() { instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), - (i64::MIN + 3, 0 as i64) + (i64::MIN + 3, 0_i64) ) .unwrap() ); diff --git a/tests/basic_memory.rs b/tests/basic_memory.rs index 056878d0..e9351e2e 100644 --- a/tests/basic_memory.rs +++ b/tests/basic_memory.rs @@ -1,5 +1,5 @@ use wasm::{validate, RuntimeInstance, DEFAULT_MODULE}; -const BASE_WAT: &'static str = r#" +const BASE_WAT: &str = r#" (module (memory 1) (func (export "store_num") (param $x {{TYPE}}) diff --git a/tests/conversions.rs b/tests/conversions.rs index 07b31ed1..f12de46b 100644 --- a/tests/conversions.rs +++ b/tests/conversions.rs @@ -19,7 +19,7 @@ use core::{f32, f64}; use hexf::{hexf32, hexf64}; use wasm::{validate, RuntimeError, RuntimeInstance}; -const WAT: &'static str = r#" +const WAT: &str = r#" (module (func (export "{{0}}") (param $x {{1}}) (result {{2}}) local.get $x @@ -3125,7 +3125,7 @@ pub fn f64_promote_f32() { .unwrap() ); assert_eq!( - 6.6382536710104395e+37_f64, + 6.638_253_671_010_439_5e37_f64, instance .invoke( &instance.get_function_by_index(0, 0).unwrap(), diff --git a/tests/f32.rs b/tests/f32.rs index 52a9e886..09b5f8f3 100644 --- a/tests/f32.rs +++ b/tests/f32.rs @@ -20,14 +20,14 @@ pub fn f32_const() { let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); assert_eq!( - 3.14159274_f32, + 3.141_592_7_f32, instance .invoke(&instance.get_function_by_index(0, 0).unwrap(), ()) .unwrap() ); } -const WAT_2_ARGS_RETURN_I32: &'static str = r#" +const WAT_2_ARGS_RETURN_I32: &str = r#" (module (func (export "f32_{{0}}") (param $x f32) (param $y f32) (result i32) local.get $x @@ -258,7 +258,7 @@ pub fn f32_ge() { ); } -const WAT_1_ARG_RETURN_F32: &'static str = r#" +const WAT_1_ARG_RETURN_F32: &str = r#" (module (func (export "f32_{{0}}") (param $x f32) (result f32) local.get $x @@ -553,7 +553,7 @@ pub fn f32_sqrt() { .is_nan()); } -const WAT_2_ARGS_RETURN_F32: &'static str = r#" +const WAT_2_ARGS_RETURN_F32: &str = r#" (module (func (export "f32_{{0}}") (param $x f32) (param $y f32) (result f32) local.get $x diff --git a/tests/function_recursion.rs b/tests/function_recursion.rs index f81d85ad..860d7024 100644 --- a/tests/function_recursion.rs +++ b/tests/function_recursion.rs @@ -1,6 +1,6 @@ use wasm::{validate, RuntimeInstance, DEFAULT_MODULE}; -const FUNCTION_CALL: &'static str = r#" +const FUNCTION_CALL: &str = r#" (module (func (export "simple_caller") (param $x i32) (param $y i32) (result i32) (call $callee (i32.mul (local.get $x) (local.get $y))) diff --git a/tests/i32.rs b/tests/i32.rs index 2a1a98e8..ed2b416c 100644 --- a/tests/i32.rs +++ b/tests/i32.rs @@ -16,7 +16,7 @@ */ use wasm::{validate, RuntimeInstance}; -const WAT: &'static str = r#" +const WAT: &str = r#" (module (func (export "i32_{{0}}") (param $x i32) (param $y i32) (result i32) local.get $x @@ -59,7 +59,7 @@ pub fn i32_add() { ); // Chaned the following value from the spec: // - 0x80000000 to -2147483648 = (0x80000000 as u32) as i32 - let i32_min = (0x80000000 as u32) as i32; + let i32_min = 0x80000000_u32 as i32; assert_eq!( i32_min, @@ -127,7 +127,7 @@ pub fn i32_sub() { ); // Chaned the following value from the spec: // - 0x80000000 to -2147483648 = (0x80000000 as u32) as i32 - let i32_min = (0x80000000 as u32) as i32; + let i32_min = 0x80000000_u32 as i32; assert_eq!( i32_min, diff --git a/tests/i64.rs b/tests/i64.rs index 1d00e72b..679c9580 100644 --- a/tests/i64.rs +++ b/tests/i64.rs @@ -16,7 +16,7 @@ */ use wasm::{validate, RuntimeInstance}; -const WAT: &'static str = r#" +const WAT: &str = r#" (module (func (export "i64_{{0}}") (param $x i64) (param $y i64) (result i32) local.get $x diff --git a/tests/specification/run.rs b/tests/specification/run.rs index 57dde52a..92dd6221 100644 --- a/tests/specification/run.rs +++ b/tests/specification/run.rs @@ -341,7 +341,7 @@ fn result_to_value(result: wast::WastRet) -> Value { } pub fn get_command(contents: &str, span: wast::token::Span) -> &str { - contents[span.offset() as usize..] + contents[span.offset()..] .lines() .next() .unwrap_or("") From 4b563d2df9681feb5eb3943051df76df8ac84523 Mon Sep 17 00:00:00 2001 From: nerodesu017 Date: Mon, 18 Nov 2024 11:44:43 +0200 Subject: [PATCH 02/12] fix: MemArg argument order Signed-off-by: nerodesu017 --- src/core/reader/types/memarg.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/reader/types/memarg.rs b/src/core/reader/types/memarg.rs index b7bb8685..80657937 100644 --- a/src/core/reader/types/memarg.rs +++ b/src/core/reader/types/memarg.rs @@ -6,21 +6,19 @@ use crate::execution::assert_validated::UnwrapValidatedExt; #[derive(Debug)] pub struct MemArg { pub offset: u32, - #[allow(dead_code)] pub align: u32, } impl WasmReadable for MemArg { fn read(wasm: &mut WasmReader) -> crate::Result { - let offset = wasm.read_var_u32()?; - #[allow(dead_code)] let align = wasm.read_var_u32()?; + let offset = wasm.read_var_u32()?; Ok(Self { offset, align }) } fn read_unvalidated(wasm: &mut WasmReader) -> Self { - let offset = wasm.read_var_u32().unwrap_validated(); let align = wasm.read_var_u32().unwrap_validated(); + let offset = wasm.read_var_u32().unwrap_validated(); Self { offset, align } } } From 5874215323e6a2d75abe6b19d94d2a0bf1f2ec74 Mon Sep 17 00:00:00 2001 From: nerodesu017 Date: Mon, 18 Nov 2024 11:48:24 +0200 Subject: [PATCH 03/12] feat: NOP opcode Signed-off-by: nerodesu017 --- src/execution/interpreter_loop.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/execution/interpreter_loop.rs b/src/execution/interpreter_loop.rs index 6892b52e..278b6a85 100644 --- a/src/execution/interpreter_loop.rs +++ b/src/execution/interpreter_loop.rs @@ -59,6 +59,9 @@ pub(super) fn run( let first_instr_byte = wasm.read_u8().unwrap_validated(); match first_instr_byte { + NOP => { + trace!("Instruction: NOP"); + } END => { let maybe_return_address = stack.pop_stackframe(); From c3266a9607b1f63f9b686ac2f7ba3cbabbd76aba Mon Sep 17 00:00:00 2001 From: nerodesu017 Date: Mon, 18 Nov 2024 11:53:30 +0200 Subject: [PATCH 04/12] wip: memory limits Signed-off-by: nerodesu017 --- src/core/error.rs | 4 ++++ src/core/reader/types/mod.rs | 33 ++++++++++++++++++++++++++++++--- src/execution/store.rs | 11 ++++------- src/lib.rs | 2 +- 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/core/error.rs b/src/core/error.rs index 42ba0915..f6fcf1c0 100644 --- a/src/core/error.rs +++ b/src/core/error.rs @@ -41,6 +41,8 @@ pub enum Error { InvalidLimitsType(u8), InvalidMutType(u8), MoreThanOneMemory, + InvalidLimit, + MemSizeTooBig, InvalidGlobalIdx(GlobalIdx), GlobalIsConst, RuntimeError(RuntimeError), @@ -110,6 +112,8 @@ impl Display for Error { Error::MoreThanOneMemory => { f.write_str("As of not only one memory is allowed per module.") } + Error::InvalidLimit => f.write_str("Size minimum must not be greater than maximum"), + Error::MemSizeTooBig => f.write_str("Memory size must be at most 65536 pages (4GiB)"), Error::InvalidGlobalIdx(idx) => f.write_fmt(format_args!( "An invalid global index `{idx}` was specified" )), diff --git a/src/core/reader/types/mod.rs b/src/core/reader/types/mod.rs index 7a94699d..09103742 100644 --- a/src/core/reader/types/mod.rs +++ b/src/core/reader/types/mod.rs @@ -206,6 +206,18 @@ pub struct Limits { pub max: Option, } +impl Limits { + // since the maximum amount of bytes is u32::MAX, the page size is 1 << 16 + // the max no. of pages = max bytes / page size = u32::MAX / (1 << 16) = 1 << 16 + pub const MAX_MEM_PAGES: u32 = 1 << 16; + // https://webassembly.github.io/reference-types/core/syntax/types.html#limits + // memtype is defined in terms of limits, which go from 0 to u32::MAX + pub const MAX_MEM_BYTES: u32 = u32::MAX; + // https://webassembly.github.io/reference-types/core/exec/runtime.html#memory-instances + // memory size is 65536 (1 << 16) + pub const MEM_PAGE_SIZE: u32 = 1 << 16; +} + impl Debug for Limits { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { match self.max { @@ -233,6 +245,10 @@ impl WasmReadable for Limits { other => return Err(Error::InvalidLimitsType(other)), }; + if limits.max.is_some() && limits.min > limits.max.unwrap() { + return Err(Error::InvalidLimit); + } + Ok(limits) } @@ -283,9 +299,20 @@ pub struct MemType { impl WasmReadable for MemType { fn read(wasm: &mut WasmReader) -> Result { - Ok(Self { - limits: Limits::read(wasm)?, - }) + let mut limit = Limits::read(wasm)?; + // Memory can only grow to 65536 pages of 64kb size (4GiB) + if limit.min > (1 << 16) { + return Err(Error::MemSizeTooBig); + } + if limit.max.is_none() { + limit.max = Some(1 << 16); + } else if limit.max.is_some() { + let max_limit = limit.max.unwrap(); + if max_limit > (1 << 16) { + return Err(Error::MemSizeTooBig); + } + } + Ok(Self { limits: limit }) } fn read_unvalidated(wasm: &mut WasmReader) -> Self { diff --git a/src/execution/store.rs b/src/execution/store.rs index 6d897b8f..afc2f590 100644 --- a/src/execution/store.rs +++ b/src/execution/store.rs @@ -15,7 +15,6 @@ use crate::execution::value::{Ref, Value}; /// pub struct Store { pub funcs: Vec, - // tables: Vec, pub mems: Vec, pub globals: Vec, } @@ -39,9 +38,8 @@ pub struct MemInst { } impl MemInst { - const PAGE_SIZE: usize = 1 << 16; pub fn new(ty: MemType) -> Self { - let initial_size = Self::PAGE_SIZE * ty.limits.min as usize; + let initial_size = (crate::Limits::MEM_PAGE_SIZE as usize) * ty.limits.min as usize; Self { ty, @@ -49,15 +47,14 @@ impl MemInst { } } - #[allow(dead_code)] pub fn grow(&mut self, delta_pages: usize) { self.data - .extend(iter::repeat(0).take(delta_pages * Self::PAGE_SIZE)) + .extend(iter::repeat(0).take(delta_pages * (crate::Limits::MEM_PAGE_SIZE as usize))) } - #[allow(dead_code)] + /// Can never be bigger than 65,356 pages pub fn size(&self) -> usize { - self.data.len() / Self::PAGE_SIZE + self.data.len() / (crate::Limits::MEM_PAGE_SIZE as usize) } } diff --git a/src/lib.rs b/src/lib.rs index 698e1667..a5a13a83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,7 @@ extern crate alloc; extern crate log; pub use core::error::{Error, Result, RuntimeError}; -pub use core::reader::types::{NumType, RefType, ValType}; +pub use core::reader::types::{Limits, NumType, RefType, ValType}; pub use execution::value::Value; pub use execution::*; pub use validation::*; From af58244a22cf8b701da49f845161936111c25002 Mon Sep 17 00:00:00 2001 From: nerodesu017 Date: Mon, 18 Nov 2024 12:13:39 +0200 Subject: [PATCH 05/12] feat: basic memory opcodes and tests Signed-off-by: nerodesu017 --- src/core/error.rs | 17 + src/core/reader/types/opcode.rs | 19 ++ src/execution/interpreter_loop.rs | 503 +++++++++++++++++++++++++++++- src/validation/code.rs | 288 ++++++++++++++--- tests/memory.rs | 299 ++++++++++++++++++ tests/memory_grow.rs | 125 ++++++++ tests/memory_redundancy.rs | 105 +++++++ tests/memory_size.rs | 189 +++++++++++ tests/memory_trap.rs | 83 +++++ 9 files changed, 1583 insertions(+), 45 deletions(-) create mode 100644 tests/memory.rs create mode 100644 tests/memory_grow.rs create mode 100644 tests/memory_redundancy.rs create mode 100644 tests/memory_size.rs create mode 100644 tests/memory_trap.rs diff --git a/src/core/error.rs b/src/core/error.rs index f6fcf1c0..1ca1649c 100644 --- a/src/core/error.rs +++ b/src/core/error.rs @@ -6,6 +6,8 @@ use core::str::Utf8Error; use crate::core::reader::section_header::SectionTy; use crate::core::reader::types::ValType; +use super::indices::MemIdx; + #[derive(Debug, PartialEq, Eq, Clone)] pub enum RuntimeError { DivideBy0, @@ -14,6 +16,7 @@ pub enum RuntimeError { StackSmash, // https://github.com/wasmi-labs/wasmi/blob/37d1449524a322817c55026eb21eb97dd693b9ce/crates/core/src/trap.rs#L265C5-L265C27 BadConversionToInteger, + MemoryAccessOutOfBounds, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -47,6 +50,9 @@ pub enum Error { GlobalIsConst, RuntimeError(RuntimeError), FoundLabel(LabelKind), + MemoryIsNotDefined(MemIdx), + // mem.align, wanted alignment + ErroneousAlignment(u32, u32), } impl Display for Error { @@ -122,6 +128,16 @@ impl Display for Error { Error::FoundLabel(lk) => f.write_fmt(format_args!( "Expecting a ValType, a Label was found: {lk:?}" )), + Error::MemoryIsNotDefined(memidx) => f.write_fmt(format_args!( + "C.mems[{}] is NOT defined when it should be", + memidx + )), + Error::ErroneousAlignment(mem_align, minimum_wanted_alignment) => { + f.write_fmt(format_args!( + "Alignment ({}) is not less or equal to {}", + mem_align, minimum_wanted_alignment + )) + } } } } @@ -134,6 +150,7 @@ impl Display for RuntimeError { RuntimeError::FunctionNotFound => f.write_str("Function not found"), RuntimeError::StackSmash => f.write_str("Stack smashed"), RuntimeError::BadConversionToInteger => f.write_str("Bad conversion to integer"), + RuntimeError::MemoryAccessOutOfBounds => f.write_str("Memory access out of bounds"), } } } diff --git a/src/core/reader/types/opcode.rs b/src/core/reader/types/opcode.rs index ab9dbbf0..d40c94e9 100644 --- a/src/core/reader/types/opcode.rs +++ b/src/core/reader/types/opcode.rs @@ -9,11 +9,30 @@ pub const LOCAL_TEE: u8 = 0x22; pub const GLOBAL_GET: u8 = 0x23; pub const GLOBAL_SET: u8 = 0x24; pub const I32_LOAD: u8 = 0x28; +pub const I64_LOAD: u8 = 0x29; pub const F32_LOAD: u8 = 0x2A; pub const F64_LOAD: u8 = 0x2B; +pub const I32_LOAD8_S: u8 = 0x2C; +pub const I32_LOAD8_U: u8 = 0x2D; +pub const I32_LOAD16_S: u8 = 0x2E; +pub const I32_LOAD16_U: u8 = 0x2F; +pub const I64_LOAD8_S: u8 = 0x30; +pub const I64_LOAD8_U: u8 = 0x31; +pub const I64_LOAD16_S: u8 = 0x32; +pub const I64_LOAD16_U: u8 = 0x33; +pub const I64_LOAD32_S: u8 = 0x34; +pub const I64_LOAD32_U: u8 = 0x35; pub const I32_STORE: u8 = 0x36; +pub const I64_STORE: u8 = 0x37; pub const F32_STORE: u8 = 0x38; pub const F64_STORE: u8 = 0x39; +pub const I32_STORE8: u8 = 0x3A; +pub const I32_STORE16: u8 = 0x3B; +pub const I64_STORE8: u8 = 0x3C; +pub const I64_STORE16: u8 = 0x3D; +pub const I64_STORE32: u8 = 0x3E; +pub const MEMORY_SIZE: u8 = 0x3F; +pub const MEMORY_GROW: u8 = 0x40; pub const I32_CONST: u8 = 0x41; pub const I64_CONST: u8 = 0x42; pub const F32_CONST: u8 = 0x43; diff --git a/src/execution/interpreter_loop.rs b/src/execution/interpreter_loop.rs index 278b6a85..63be8703 100644 --- a/src/execution/interpreter_loop.rs +++ b/src/execution/interpreter_loop.rs @@ -10,6 +10,7 @@ //! [`Error::RuntimeError`](crate::Error::RuntimeError) variant, which as per 2., we don not //! want +use alloc::vec; use alloc::vec::Vec; use crate::{ @@ -25,7 +26,7 @@ use crate::{ store::Store, value, value_stack::Stack, - NumType, RuntimeError, ValType, Value, + Limits, NumType, RuntimeError, ValType, Value, }; #[cfg(feature = "hooks")] @@ -116,7 +117,9 @@ pub(super) fn run( .unwrap_validated(); } LOCAL_GET => { - stack.get_local(wasm.read_var_u32().unwrap_validated() as LocalIdx); + let local_idx = wasm.read_var_u32().unwrap_validated() as LocalIdx; + stack.get_local(local_idx); + trace!("Instruction: local.get {} [] -> [t]", local_idx); } LOCAL_SET => stack.set_local(wasm.read_var_u32().unwrap_validated() as LocalIdx), LOCAL_TEE => stack.tee_local(wasm.read_var_u32().unwrap_validated() as LocalIdx), @@ -149,7 +152,7 @@ pub(super) fn run( let address = address as usize; mem.data.get(address..(address + 4)) }) - .expect("TODO trap here"); + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; let data: [u8; 4] = data.try_into().expect("this to be exactly 4 bytes"); u32::from_le_bytes(data) @@ -158,6 +161,33 @@ pub(super) fn run( stack.push_value(Value::I32(data)); trace!("Instruction: i32.load [{relative_address}] -> [{data}]"); } + I64_LOAD => { + let memarg = MemArg::read_unvalidated(&mut wasm); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.first().unwrap_validated(); + + let data: u64 = { + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + let _address = memarg.offset.checked_add(relative_address); + let data = memarg + .offset + .checked_add(relative_address) + .and_then(|address| { + let address = address as usize; + mem.data + .get(address..(address + 8)) + .map(|slice| slice.try_into().expect("this to be exactly 8 bytes")) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + u64::from_le_bytes(data) + }; + + stack.push_value(Value::I64(data)); + trace!("Instruction: i64.load [{relative_address}] -> [{data}]"); + } F32_LOAD => { let memarg = MemArg::read_unvalidated(&mut wasm); let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); @@ -177,13 +207,313 @@ pub(super) fn run( .get(address..(address + 4)) .map(|slice| slice.try_into().expect("this to be exactly 4 bytes")) }) - .expect("TODO trap here"); + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; f32::from_le_bytes(data) }; stack.push_value(Value::F32(value::F32(data))); trace!("Instruction: f32.load [{relative_address}] -> [{data}]"); } + F64_LOAD => { + let memarg = MemArg::read_unvalidated(&mut wasm); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.first().unwrap_validated(); + + let data: f64 = { + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + let _address = memarg.offset.checked_add(relative_address); + let data = memarg + .offset + .checked_add(relative_address) + .and_then(|address| { + let address = address as usize; + mem.data + .get(address..(address + 8)) + .map(|slice| slice.try_into().expect("this to be exactly 8 bytes")) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + f64::from_le_bytes(data) + }; + + stack.push_value(Value::F64(value::F64(data))); + trace!("Instruction: f64.load [{relative_address}] -> [{data}]"); + } + I32_LOAD8_S => { + let memarg = MemArg::read_unvalidated(&mut wasm); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.first().unwrap_validated(); // there is only one memory allowed as of now + + let data: i8 = { + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + let _address = memarg.offset.checked_add(relative_address); + let data = memarg + .offset + .checked_add(relative_address) + .and_then(|address| { + let address = address as usize; + mem.data + .get(address..(address + 1)) + .map(|slice| slice.try_into().expect("this to be exactly 1 byte")) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + // let data: [u8; 1] = data.try_into().expect("this to be exactly 1 byte"); + u8::from_le_bytes(data) as i8 + }; + + stack.push_value(Value::I32(data as u32)); + trace!("Instruction: i32.load8_s [{relative_address}] -> [{data}]"); + } + I32_LOAD8_U => { + let memarg = MemArg::read_unvalidated(&mut wasm); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.first().unwrap_validated(); // there is only one memory allowed as of now + + let data: u8 = { + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + let _address = memarg.offset.checked_add(relative_address); + let data = memarg + .offset + .checked_add(relative_address) + .and_then(|address| { + let address = address as usize; + mem.data + .get(address..(address + 1)) + .map(|slice| slice.try_into().expect("this to be exactly 1 byte")) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + u8::from_le_bytes(data) + }; + + stack.push_value(Value::I32(data as u32)); + trace!("Instruction: i32.load8_u [{relative_address}] -> [{data}]"); + } + I32_LOAD16_S => { + let memarg = MemArg::read_unvalidated(&mut wasm); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.first().unwrap_validated(); // there is only one memory allowed as of now + + let data: i16 = { + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + let _address = memarg.offset.checked_add(relative_address); + let data = memarg + .offset + .checked_add(relative_address) + .and_then(|address| { + let address = address as usize; + mem.data + .get(address..(address + 2)) + .map(|slice| slice.try_into().expect("this to be exactly 2 bytes")) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + u16::from_le_bytes(data) as i16 + }; + + stack.push_value(Value::I32(data as u32)); + trace!("Instruction: i32.load16_s [{relative_address}] -> [{data}]"); + } + I32_LOAD16_U => { + let memarg = MemArg::read_unvalidated(&mut wasm); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.first().unwrap_validated(); // there is only one memory allowed as of now + + let data: u16 = { + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + let _address = memarg.offset.checked_add(relative_address); + let data = memarg + .offset + .checked_add(relative_address) + .and_then(|address| { + let address = address as usize; + mem.data + .get(address..(address + 2)) + .map(|slice| slice.try_into().expect("this to be exactly 2 bytes")) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + u16::from_le_bytes(data) + }; + + stack.push_value(Value::I32(data as u32)); + trace!("Instruction: i32.load16_u [{relative_address}] -> [{data}]"); + } + I64_LOAD8_S => { + let memarg = MemArg::read_unvalidated(&mut wasm); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.first().unwrap_validated(); // there is only one memory allowed as of now + + let data: i8 = { + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + let _address = memarg.offset.checked_add(relative_address); + let data = memarg + .offset + .checked_add(relative_address) + .and_then(|address| { + let address = address as usize; + mem.data + .get(address..(address + 1)) + .map(|slice| slice.try_into().expect("this to be exactly 1 byte")) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + // let data: [u8; 1] = data.try_into().expect("this to be exactly 1 byte"); + u8::from_le_bytes(data) as i8 + }; + + stack.push_value(Value::I64(data as u64)); + trace!("Instruction: i64.load8_s [{relative_address}] -> [{data}]"); + } + I64_LOAD8_U => { + let memarg = MemArg::read_unvalidated(&mut wasm); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.first().unwrap_validated(); // there is only one memory allowed as of now + + let data: u8 = { + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + let _address = memarg.offset.checked_add(relative_address); + let data = memarg + .offset + .checked_add(relative_address) + .and_then(|address| { + let address = address as usize; + mem.data + .get(address..(address + 1)) + .map(|slice| slice.try_into().expect("this to be exactly 1 byte")) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + // let data: [u8; 1] = data.try_into().expect("this to be exactly 1 byte"); + u8::from_le_bytes(data) + }; + + stack.push_value(Value::I64(data as u64)); + trace!("Instruction: i64.load8_u [{relative_address}] -> [{data}]"); + } + I64_LOAD16_S => { + let memarg = MemArg::read_unvalidated(&mut wasm); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.first().unwrap_validated(); // there is only one memory allowed as of now + + let data: i16 = { + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + let _address = memarg.offset.checked_add(relative_address); + let data = memarg + .offset + .checked_add(relative_address) + .and_then(|address| { + let address = address as usize; + mem.data + .get(address..(address + 2)) + .map(|slice| slice.try_into().expect("this to be exactly 2 bytes")) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + u16::from_le_bytes(data) as i16 + }; + + stack.push_value(Value::I64(data as u64)); + trace!("Instruction: i64.load16_s [{relative_address}] -> [{data}]"); + } + I64_LOAD16_U => { + let memarg = MemArg::read_unvalidated(&mut wasm); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.first().unwrap_validated(); // there is only one memory allowed as of now + + let data: u16 = { + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + let _address = memarg.offset.checked_add(relative_address); + let data = memarg + .offset + .checked_add(relative_address) + .and_then(|address| { + let address = address as usize; + mem.data + .get(address..(address + 2)) + .map(|slice| slice.try_into().expect("this to be exactly 2 bytes")) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + u16::from_le_bytes(data) + }; + + stack.push_value(Value::I64(data as u64)); + trace!("Instruction: i64.load16_u [{relative_address}] -> [{data}]"); + } + I64_LOAD32_S => { + let memarg = MemArg::read_unvalidated(&mut wasm); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.first().unwrap_validated(); // there is only one memory allowed as of now + + let data: i32 = { + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + let _address = memarg.offset.checked_add(relative_address); + let data = memarg + .offset + .checked_add(relative_address) + .and_then(|address| { + let address = address as usize; + mem.data + .get(address..(address + 4)) + .map(|slice| slice.try_into().expect("this to be exactly 4 bytes")) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + u32::from_le_bytes(data) as i32 + }; + + stack.push_value(Value::I64(data as u64)); + trace!("Instruction: i64.load32_s [{relative_address}] -> [{data}]"); + } + I64_LOAD32_U => { + let memarg = MemArg::read_unvalidated(&mut wasm); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.first().unwrap_validated(); // there is only one memory allowed as of now + + let data: u32 = { + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + let _address = memarg.offset.checked_add(relative_address); + let data = memarg + .offset + .checked_add(relative_address) + .and_then(|address| { + let address = address as usize; + mem.data + .get(address..(address + 4)) + .map(|slice| slice.try_into().expect("this to be exactly 4 bytes")) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + u32::from_le_bytes(data) + }; + + stack.push_value(Value::I64(data as u64)); + trace!("Instruction: i64.load32_u [{relative_address}] -> [{data}]"); + } I32_STORE => { let memarg = MemArg::read_unvalidated(&mut wasm); @@ -200,11 +530,32 @@ pub(super) fn run( let address = address as usize; mem.data.get_mut(address..(address + 4)) }) - .expect("TODO trap here"); + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; memory_location.copy_from_slice(&data_to_store.to_le_bytes()); trace!("Instruction: i32.store [{relative_address} {data_to_store}] -> []"); } + I64_STORE => { + let memarg = MemArg::read_unvalidated(&mut wasm); + + let data_to_store: u32 = stack.pop_value(ValType::NumType(NumType::I64)).into(); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.get_mut(0).unwrap_validated(); // there is only one memory allowed as of now + + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + let address = memarg.offset.checked_add(relative_address); + let memory_location = address + .and_then(|address| { + let address = address as usize; + mem.data.get_mut(address..(address + 8)) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + memory_location.copy_from_slice(&data_to_store.to_le_bytes()); + trace!("Instruction: i64.store [{relative_address} {data_to_store}] -> []"); + } F32_STORE => { let memarg = MemArg::read_unvalidated(&mut wasm); @@ -221,7 +572,7 @@ pub(super) fn run( let address = address as usize; mem.data.get_mut(address..(address + 4)) }) - .expect("TODO trap here"); + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; memory_location.copy_from_slice(&data_to_store.to_le_bytes()); trace!("Instruction: f32.store [{relative_address} {data_to_store}] -> []"); @@ -242,11 +593,149 @@ pub(super) fn run( let address = address as usize; mem.data.get_mut(address..(address + 4)) }) - .expect("TODO trap here"); + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; memory_location.copy_from_slice(&data_to_store.to_le_bytes()); trace!("Instruction: f64.store [{relative_address} {data_to_store}] -> []"); } + I32_STORE8 => { + let memarg = MemArg::read_unvalidated(&mut wasm); + + let data_to_store: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.get_mut(0).unwrap_validated(); + + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + // ea => effective address + let ea = memarg.offset.checked_add(relative_address); + let memory_location = ea + .and_then(|address| { + let address = address as usize; + mem.data.get_mut(address..(address + 1)) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + memory_location.copy_from_slice(&data_to_store.to_le_bytes()[0..1]); + trace!("Instruction: i32.store8 [{relative_address} {data_to_store}] -> []"); + } + I32_STORE16 => { + let memarg = MemArg::read_unvalidated(&mut wasm); + + let data_to_store: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.get_mut(0).unwrap_validated(); + + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + // ea => effective address + let ea = memarg.offset.checked_add(relative_address); + let memory_location = ea + .and_then(|address| { + let address = address as usize; + mem.data.get_mut(address..(address + 2)) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + memory_location.copy_from_slice(&data_to_store.to_le_bytes()[0..2]); + trace!("Instruction: i32.store16 [{relative_address} {data_to_store}] -> []"); + } + I64_STORE8 => { + let memarg = MemArg::read_unvalidated(&mut wasm); + + let data_to_store: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into(); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.get_mut(0).unwrap_validated(); + + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + // ea => effective address + let ea = memarg.offset.checked_add(relative_address); + let memory_location = ea + .and_then(|address| { + let address = address as usize; + mem.data.get_mut(address..(address + 1)) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + memory_location.copy_from_slice(&data_to_store.to_le_bytes()[0..1]); + trace!("Instruction: i64.store8 [{relative_address} {data_to_store}] -> []"); + } + I64_STORE16 => { + let memarg = MemArg::read_unvalidated(&mut wasm); + + let data_to_store: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into(); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.get_mut(0).unwrap_validated(); + + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + // ea => effective address + let ea = memarg.offset.checked_add(relative_address); + let memory_location = ea + .and_then(|address| { + let address = address as usize; + mem.data.get_mut(address..(address + 2)) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + memory_location.copy_from_slice(&data_to_store.to_le_bytes()[0..2]); + trace!("Instruction: i64.store16 [{relative_address} {data_to_store}] -> []"); + } + I64_STORE32 => { + let memarg = MemArg::read_unvalidated(&mut wasm); + + let data_to_store: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into(); + let relative_address: u32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let mem = store.mems.get_mut(0).unwrap_validated(); + + // The spec states that this should be a 33 bit integer + // See: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions + // ea => effective address + let ea = memarg.offset.checked_add(relative_address); + let memory_location = ea + .and_then(|address| { + let address = address as usize; + mem.data.get_mut(address..(address + 4)) + }) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + memory_location.copy_from_slice(&data_to_store.to_le_bytes()[0..4]); + trace!("Instruction: i64.store32 [{relative_address} {data_to_store}] -> []"); + } + MEMORY_SIZE => { + let mem_idx = wasm.read_u8().unwrap_validated() as usize; + let mem = store.mems.get(mem_idx).unwrap_validated(); + let size = mem.size() as u32; + stack.push_value(Value::I32(size)); + trace!("Instruction: memory.size [] -> [{}]", size); + } + MEMORY_GROW => { + let mem_idx = wasm.read_u8().unwrap_validated() as usize; + let mem = store.mems.get_mut(mem_idx).unwrap_validated(); + let delta: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let upper_limit = if mem.ty.limits.max.is_some() { + mem.ty.limits.max.unwrap() + } else { + Limits::MAX_MEM_PAGES + }; + let pushed_value = if delta < 0 || delta as u32 + mem.size() as u32 > upper_limit { + stack.push_value((-1).into()); + -1 + } else { + let previous_size: i32 = mem.size() as i32; + mem.grow(delta as usize); + stack.push_value(previous_size.into()); + previous_size + }; + trace!("Instruction: memory.grow [{}] -> [{}]", delta, pushed_value); + } I32_CONST => { let constant = wasm.read_var_i32().unwrap_validated(); trace!("Instruction: i32.const [] -> [{constant}]"); diff --git a/src/validation/code.rs b/src/validation/code.rs index 52400d73..ecbba3e2 100644 --- a/src/validation/code.rs +++ b/src/validation/code.rs @@ -1,12 +1,12 @@ use alloc::vec::Vec; use core::iter; -use crate::core::indices::{FuncIdx, GlobalIdx, LocalIdx}; +use crate::core::indices::{FuncIdx, GlobalIdx, LocalIdx, MemIdx}; use crate::core::reader::section_header::{SectionHeader, SectionTy}; use crate::core::reader::span::Span; use crate::core::reader::types::global::Global; use crate::core::reader::types::memarg::MemArg; -use crate::core::reader::types::{FuncType, NumType, ValType}; +use crate::core::reader::types::{FuncType, MemType, NumType, ValType}; use crate::core::reader::{WasmReadable, WasmReader}; use crate::validation_stack::ValidationStack; use crate::{Error, Result}; @@ -17,6 +17,7 @@ pub fn validate_code_section( fn_types: &[FuncType], type_idx_of_fn: &[usize], globals: &[Global], + memories: &[MemType], ) -> Result> { assert_eq!(section_header.ty, SectionTy::Code); @@ -24,8 +25,6 @@ pub fn validate_code_section( let ty_idx = type_idx_of_fn[idx]; let func_ty = fn_types[ty_idx].clone(); - debug!("{:x?}", wasm.full_wasm_binary); - let func_size = wasm.read_var_u32()?; let func_block = wasm.make_span(func_size as usize)?; let previous_pc = wasm.pc; @@ -46,6 +45,7 @@ pub fn validate_code_section( globals, fn_types, type_idx_of_fn, + memories, )?; // Check if there were unread trailing instructions after the last END @@ -83,6 +83,7 @@ pub fn read_declared_locals(wasm: &mut WasmReader) -> Result> { Ok(locals) } +#[allow(clippy::too_many_arguments)] fn read_instructions( this_function_idx: usize, wasm: &mut WasmReader, @@ -91,6 +92,7 @@ fn read_instructions( globals: &[Global], fn_types: &[FuncType], type_idx_of_fn: &[usize], + memories: &[MemType], ) -> Result<()> { // TODO we must terminate only if both we saw the final `end` and when we consumed all of the code span loop { @@ -220,64 +222,274 @@ fn read_instructions( stack.assert_pop_val_type(global.ty.ty)?; } - // i32.load [i32] -> [i32] I32_LOAD => { - let _memarg = MemArg::read_unvalidated(wasm); - - // TODO check correct `memarg.align` - // TODO check if memory[0] exists - + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 4 { + return Err(Error::ErroneousAlignment(memarg.align, 4)); + } stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; - stack.push_valtype(ValType::NumType(NumType::I32)); } - // f32.load [f32] -> [f32] + I64_LOAD => { + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 8 { + return Err(Error::ErroneousAlignment(memarg.align, 8)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.push_valtype(ValType::NumType(NumType::I64)); + } F32_LOAD => { - let _memarg = MemArg::read_unvalidated(wasm); - - // Check for I32 because that's the address where we find our value + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 4 { + return Err(Error::ErroneousAlignment(memarg.align, 4)); + } stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; - stack.push_valtype(ValType::NumType(NumType::F32)); } - // f32.load [f32] -> [f32] F64_LOAD => { - let _memarg = MemArg::read_unvalidated(wasm); - - // Check for I32 because that's the address where we find our value + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 8 { + return Err(Error::ErroneousAlignment(memarg.align, 8)); + } stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; - stack.push_valtype(ValType::NumType(NumType::F64)); } - // i32.store [i32] -> [i32] + I32_LOAD8_S => { + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 1 { + return Err(Error::ErroneousAlignment(memarg.align, 1)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.push_valtype(ValType::NumType(NumType::I32)); + } + I32_LOAD8_U => { + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 1 { + return Err(Error::ErroneousAlignment(memarg.align, 1)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.push_valtype(ValType::NumType(NumType::I32)); + } + I32_LOAD16_S => { + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 2 { + return Err(Error::ErroneousAlignment(memarg.align, 2)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.push_valtype(ValType::NumType(NumType::I32)); + } + I32_LOAD16_U => { + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 2 { + return Err(Error::ErroneousAlignment(memarg.align, 2)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.push_valtype(ValType::NumType(NumType::I32)); + } + I64_LOAD8_S => { + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 1 { + return Err(Error::ErroneousAlignment(memarg.align, 1)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.push_valtype(ValType::NumType(NumType::I64)); + } + I64_LOAD8_U => { + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 1 { + return Err(Error::ErroneousAlignment(memarg.align, 1)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.push_valtype(ValType::NumType(NumType::I64)); + } + I64_LOAD16_S => { + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 2 { + return Err(Error::ErroneousAlignment(memarg.align, 2)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.push_valtype(ValType::NumType(NumType::I64)); + } + I64_LOAD16_U => { + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 2 { + return Err(Error::ErroneousAlignment(memarg.align, 2)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.push_valtype(ValType::NumType(NumType::I64)); + } + I64_LOAD32_S => { + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 4 { + return Err(Error::ErroneousAlignment(memarg.align, 4)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.push_valtype(ValType::NumType(NumType::I64)); + } + I64_LOAD32_U => { + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 4 { + return Err(Error::ErroneousAlignment(memarg.align, 4)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.push_valtype(ValType::NumType(NumType::I64)); + } I32_STORE => { - let _memarg = MemArg::read_unvalidated(wasm); - - // TODO check correct `memarg.align` - // TODO check if memory[0] exists - - // Value to store + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align >= 4 { + return Err(Error::ErroneousAlignment(memarg.align, 4)); + } stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; - // Address stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; } - // f32.store [f32] -> [f32] + I64_STORE => { + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 8 { + return Err(Error::ErroneousAlignment(memarg.align, 8)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I64))?; + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + } F32_STORE => { - let _memarg = MemArg::read_unvalidated(wasm); - - // Value to store + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align >= 4 { + return Err(Error::ErroneousAlignment(memarg.align, 4)); + } stack.assert_pop_val_type(ValType::NumType(NumType::F32))?; - // Address stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; } F64_STORE => { - let _memarg = MemArg::read_unvalidated(wasm); - - // Value to store + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 8 { + return Err(Error::ErroneousAlignment(memarg.align, 8)); + } stack.assert_pop_val_type(ValType::NumType(NumType::F64))?; - // Address stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; } + I32_STORE8 => { + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 1 { + return Err(Error::ErroneousAlignment(memarg.align, 1)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + } + I32_STORE16 => { + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 2 { + return Err(Error::ErroneousAlignment(memarg.align, 2)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + } + I64_STORE8 => { + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 1 { + return Err(Error::ErroneousAlignment(memarg.align, 1)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I64))?; + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + } + I64_STORE16 => { + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 2 { + return Err(Error::ErroneousAlignment(memarg.align, 2)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I64))?; + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + } + I64_STORE32 => { + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + let memarg = MemArg::read(wasm)?; + if memarg.align > 4 { + return Err(Error::ErroneousAlignment(memarg.align, 4)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I64))?; + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + } + MEMORY_SIZE => { + let mem_idx = wasm.read_u8()? as MemIdx; + if memories.len() <= mem_idx { + return Err(Error::MemoryIsNotDefined(mem_idx)); + } + stack.push_valtype(ValType::NumType(NumType::I32)); + } + MEMORY_GROW => { + let mem_idx = wasm.read_u8()? as MemIdx; + if memories.len() <= mem_idx { + return Err(Error::MemoryIsNotDefined(mem_idx)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.push_valtype(ValType::NumType(NumType::I32)); + } // i32.const: [] -> [i32] I32_CONST => { let _num = wasm.read_var_i32()?; diff --git a/tests/memory.rs b/tests/memory.rs new file mode 100644 index 00000000..d88bcb16 --- /dev/null +++ b/tests/memory.rs @@ -0,0 +1,299 @@ +/* +# This file incorporates code from the WebAssembly testsuite, originally +# available at https://github.com/WebAssembly/testsuite. +# +# The original code is licensed under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ +use wasm::{validate, Error, RuntimeInstance}; + +#[test_log::test] +fn memory_basic() { + let w = r#" +(module (memory 0)) +(module (memory 1)) +(module (memory 0 0)) +(module (memory 0 1)) +(module (memory 1 256)) +(module (memory 0 65536)) +"# + .split("\n") + .map(|el| el.trim()) + .filter(|el| !el.is_empty()) + .collect::>(); + + w.iter().for_each(|wat| { + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + RuntimeInstance::new(&validation_info).expect("instantiation failed"); + }); +} + +#[test_log::test] +fn memory_min_greater_than_max() { + let w = r#" + (module (memory 1 0)) + "# + .split("\n") + .map(|el| el.trim()) + .filter(|el| !el.is_empty()) + .collect::>(); + + w.iter().for_each(|wat| { + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes); + assert_eq!(validation_info.err().unwrap(), Error::InvalidLimit); + }); +} + +#[test_log::test] +fn memory_size_must_be_at_most_4gib() { + let w = r#" + (module (memory 65537)) + (module (memory 2147483648)) + (module (memory 4294967295)) + (module (memory 0 65537)) + (module (memory 0 2147483648)) + (module (memory 0 4294967295)) + "# + .split("\n") + .map(|el| el.trim()) + .filter(|el| !el.is_empty()) + .collect::>(); + + w.iter().for_each(|wat| { + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes); + assert_eq!(validation_info.err().unwrap(), Error::MemSizeTooBig); + }); +} + +macro_rules! get_func { + ($instance:ident, $func_name:expr) => { + &$instance.get_function_by_name("", $func_name).unwrap() + }; +} + +macro_rules! assert_result { + ($instance:expr, $func_name:expr, $arg:expr, $result:expr) => { + assert_eq!($result, $instance.invoke($func_name, $arg).unwrap()); + }; +} + +#[test_log::test] +fn i32_and_i64_loads() { + // #region Wat + let w = r#" + (module + (memory 1) + (data (i32.const 0) "ABC\a7D") (data (i32.const 20) "WASM") + + ;; Data section + (func (export "data") (result i32) + (i32.and + (i32.and + (i32.and + (i32.eq (i32.load8_u (i32.const 0)) (i32.const 65)) + (i32.eq (i32.load8_u (i32.const 3)) (i32.const 167)) + ) + (i32.and + (i32.eq (i32.load8_u (i32.const 6)) (i32.const 0)) + (i32.eq (i32.load8_u (i32.const 19)) (i32.const 0)) + ) + ) + (i32.and + (i32.and + (i32.eq (i32.load8_u (i32.const 20)) (i32.const 87)) + (i32.eq (i32.load8_u (i32.const 23)) (i32.const 77)) + ) + (i32.and + (i32.eq (i32.load8_u (i32.const 24)) (i32.const 0)) + (i32.eq (i32.load8_u (i32.const 1023)) (i32.const 0)) + ) + ) + ) + ) + + ;; Memory cast +;; (func (export "cast") (result f64) +;; (i64.store (i32.const 8) (i64.const -12345)) +;; (if +;; (f64.eq +;; (f64.load (i32.const 8)) +;; (f64.reinterpret_i64 (i64.const -12345)) +;; ) +;; (then (return (f64.const 0))) +;; ) +;; (i64.store align=1 (i32.const 9) (i64.const 0)) +;; (i32.store16 align=1 (i32.const 15) (i32.const 16453)) +;; (f64.load align=1 (i32.const 9)) +;; ) + + (func (export "i32_load8_s") (param $i i32) (result i32) + (i32.store8 (i32.const 8) (local.get $i)) + (i32.load8_s (i32.const 8)) + ) + (func (export "i32_load8_u") (param $i i32) (result i32) + (i32.store8 (i32.const 8) (local.get $i)) + (i32.load8_u (i32.const 8)) + ) + (func (export "i32_load16_s") (param $i i32) (result i32) + (i32.store16 (i32.const 8) (local.get $i)) + (i32.load16_s (i32.const 8)) + ) + (func (export "i32_load16_u") (param $i i32) (result i32) + (i32.store16 (i32.const 8) (local.get $i)) + (i32.load16_u (i32.const 8)) + ) + (func (export "i64_load8_s") (param $i i64) (result i64) + (i64.store8 (i32.const 8) (local.get $i)) + (i64.load8_s (i32.const 8)) + ) + (func (export "i64_load8_u") (param $i i64) (result i64) + (i64.store8 (i32.const 8) (local.get $i)) + (i64.load8_u (i32.const 8)) + ) + (func (export "i64_load16_s") (param $i i64) (result i64) + (i64.store16 (i32.const 8) (local.get $i)) + (i64.load16_s (i32.const 8)) + ) + (func (export "i64_load16_u") (param $i i64) (result i64) + (i64.store16 (i32.const 8) (local.get $i)) + (i64.load16_u (i32.const 8)) + ) + (func (export "i64_load32_s") (param $i i64) (result i64) + (i64.store32 (i32.const 8) (local.get $i)) + (i64.load32_s (i32.const 8)) + ) + (func (export "i64_load32_u") (param $i i64) (result i64) + (i64.store32 (i32.const 8) (local.get $i)) + (i64.load32_u (i32.const 8)) + ) + ) + "#; + // #endregion + + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let i32_load8_s = get_func!(i, "i32_load8_s"); + let i32_load8_u = get_func!(i, "i32_load8_u"); + let i32_load16_s = get_func!(i, "i32_load16_s"); + let i32_load16_u = get_func!(i, "i32_load16_u"); + let i64_load8_s = get_func!(i, "i64_load8_s"); + let i64_load8_u = get_func!(i, "i64_load8_u"); + let i64_load16_s = get_func!(i, "i64_load16_s"); + let i64_load16_u = get_func!(i, "i64_load16_u"); + let i64_load32_s = get_func!(i, "i64_load32_s"); + let i64_load32_u = get_func!(i, "i64_load32_u"); + let data = get_func!(i, "data"); + // let cast = get_func!(i, "cast"); + + // assert_result!(i, data, (), 1); + assert_eq!(1, i.invoke(data, ()).unwrap()); + // (assert_return (invoke "cast") (f64.const 42.0)) + + assert_result!(i, i32_load8_s, -1, -1); + assert_result!(i, i32_load8_s, -1, -1); + assert_result!(i, i32_load8_u, -1, 255); + assert_result!(i, i32_load16_s, -1, -1); + assert_result!(i, i32_load16_u, -1, 65535); + + assert_result!(i, i32_load8_s, 100, 100); + assert_result!(i, i32_load8_u, 200, 200); + assert_result!(i, i32_load16_s, 20000, 20000); + assert_result!(i, i32_load16_u, 40000, 40000); + + assert_result!(i, i32_load8_s, 0xfedc6543_u32, 0x43); + assert_result!(i, i32_load8_s, 0x3456cdef, 0xffffffef_u32); + assert_result!(i, i32_load8_u, 0xfedc6543_u32, 0x43); + assert_result!(i, i32_load8_u, 0x3456cdef, 0xef); + assert_result!(i, i32_load16_s, 0xfedc6543_u32, 0x6543); + assert_result!(i, i32_load16_s, 0x3456cdef, 0xffffcdef_u32); + assert_result!(i, i32_load16_u, 0xfedc6543_u32, 0x6543); + assert_result!(i, i32_load16_u, 0x3456cdef, 0xcdef); + + assert_result!(i, i64_load8_s, -1_i64, -1_i64); + assert_result!(i, i64_load8_u, -1_i64, 255_i64); + assert_result!(i, i64_load16_s, -1_i64, -1_i64); + assert_result!(i, i64_load16_u, -1_i64, 65535_i64); + assert_result!(i, i64_load32_s, -1_i64, -1_i64); + assert_result!(i, i64_load32_u, -1_i64, 4294967295_i64); + + assert_result!(i, i64_load8_s, 100_i64, 100_i64); + assert_result!(i, i64_load8_u, 200_i64, 200_i64); + assert_result!(i, i64_load16_s, 20000_i64, 20000_i64); + assert_result!(i, i64_load16_u, 40000_i64, 40000_i64); + assert_result!(i, i64_load32_s, 20000_i64, 20000_i64); + assert_result!(i, i64_load32_u, 40000_i64, 40000_i64); + + assert_result!(i, i64_load8_s, 0xfedcba9856346543_u64, 0x43_i64); + assert_result!( + i, + i64_load8_s, + 0x3456436598bacdef_u64, + 0xffffffffffffffef_u64 + ); + assert_result!(i, i64_load8_u, 0xfedcba9856346543_u64, 0x43_i64); + assert_result!(i, i64_load8_u, 0x3456436598bacdef_u64, 0xef_i64); + assert_result!(i, i64_load16_s, 0xfedcba9856346543_u64, 0x6543_i64); + assert_result!( + i, + i64_load16_s, + 0x3456436598bacdef_u64, + 0xffffffffffffcdef_u64 + ); + assert_result!(i, i64_load16_u, 0xfedcba9856346543_u64, 0x6543_i64); + assert_result!(i, i64_load16_u, 0x3456436598bacdef_u64, 0xcdef_i64); + assert_result!(i, i64_load32_s, 0xfedcba9856346543_u64, 0x56346543_i64); + assert_result!( + i, + i64_load32_s, + 0x3456436598bacdef_u64, + 0xffffffff98bacdef_u64 + ); + assert_result!(i, i64_load32_u, 0xfedcba9856346543_u64, 0x56346543_i64); + assert_result!(i, i64_load32_u, 0x3456436598bacdef_u64, 0x98bacdef_i64); +} + +#[test_log::test] +fn memory_test_exporting_rand_globals_doesnt_change_a_memory_s_semantics() { + let w = r#" + (module + (memory (export "memory") 1 1) + + ;; These should not change the behavior of memory accesses. + (global (export "__data_end") i32 (i32.const 10000)) + (global (export "__stack_top") i32 (i32.const 10000)) + (global (export "__heap_base") i32 (i32.const 10000)) + + (func (export "load") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let load = get_func!(i, "load"); + + assert_result!(i, load, 0, 0); + assert_result!(i, load, 10000, 0); + assert_result!(i, load, 20000, 0); + assert_result!(i, load, 30000, 0); + assert_result!(i, load, 40000, 0); + assert_result!(i, load, 50000, 0); + assert_result!(i, load, 60000, 0); + assert_result!(i, load, 65535, 0); +} diff --git a/tests/memory_grow.rs b/tests/memory_grow.rs new file mode 100644 index 00000000..195ef129 --- /dev/null +++ b/tests/memory_grow.rs @@ -0,0 +1,125 @@ +/* +# This file incorporates code from the WebAssembly testsuite, originally +# available at https://github.com/WebAssembly/testsuite. +# +# The original code is licensed under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ +use wasm::{validate, RuntimeError, RuntimeInstance}; + +macro_rules! get_func { + ($instance:ident, $func_name:expr) => { + &$instance.get_function_by_name("", $func_name).unwrap() + }; +} + +macro_rules! assert_result { + ($instance:expr, $func_name:expr, $arg:expr, $result:expr) => { + assert_eq!($result, $instance.invoke($func_name, $arg).unwrap()); + }; +} + +macro_rules! assert_error { + ($instance:expr, $func_name:expr, $arg:expr, $ret_type:ty, $invoke_param_type:ty, $invoke_return_type:ty, $err_type:expr) => { + let val: $ret_type = + $instance.invoke::<$invoke_param_type, $invoke_return_type>($func_name, $arg); + assert!(val.is_err()); + assert!(val.unwrap_err() == $err_type); + }; +} + +#[test_log::test] +fn memory_grow_test_1() { + let w = r#" + (module + (memory 0) + + (func (export "load_at_zero") (result i32) (i32.load (i32.const 0))) + (func (export "store_at_zero") (i32.store (i32.const 0) (i32.const 2))) + + (func (export "load_at_page_size") (result i32) (i32.load (i32.const 0x10000))) + (func (export "store_at_page_size") (i32.store (i32.const 0x10000) (i32.const 3))) + + (func (export "grow") (param $sz i32) (result i32) (memory.grow (local.get $sz))) + (func (export "size") (result i32) (memory.size)) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + // let x = i.invoke(function_ref, params) + assert_result!(i, get_func!(i, "size"), (), 0); + assert_error!(i, get_func!(i, "store_at_zero"), (), Result<(), RuntimeError>, (), (), RuntimeError::MemoryAccessOutOfBounds); + assert_error!(i, get_func!(i, "load_at_zero"), (), Result, (), i32, RuntimeError::MemoryAccessOutOfBounds); + assert_error!(i, get_func!(i, "store_at_page_size"), (), Result<(), RuntimeError>, (), (), RuntimeError::MemoryAccessOutOfBounds); + assert_error!(i, get_func!(i, "load_at_page_size"), (), Result, (), i32, RuntimeError::MemoryAccessOutOfBounds); + assert_result!(i, get_func!(i, "grow"), 1, 0); + assert_result!(i, get_func!(i, "size"), (), 1); + assert_result!(i, get_func!(i, "load_at_zero"), (), 0); + assert_result!(i, get_func!(i, "store_at_zero"), (), ()); + assert_result!(i, get_func!(i, "load_at_zero"), (), 2); + assert_error!(i, get_func!(i, "store_at_page_size"), (), Result<(), RuntimeError>, (), (), RuntimeError::MemoryAccessOutOfBounds); + assert_error!(i, get_func!(i, "load_at_page_size"), (), Result, (), i32, RuntimeError::MemoryAccessOutOfBounds); + assert_result!(i, get_func!(i, "grow"), 4, 1); + assert_result!(i, get_func!(i, "size"), (), 5); + assert_result!(i, get_func!(i, "load_at_zero"), (), 2); + assert_result!(i, get_func!(i, "store_at_zero"), (), ()); + assert_result!(i, get_func!(i, "load_at_zero"), (), 2); + assert_result!(i, get_func!(i, "load_at_page_size"), (), 0); + assert_result!(i, get_func!(i, "store_at_page_size"), (), ()); + assert_result!(i, get_func!(i, "load_at_page_size"), (), 3); +} + +#[test_log::test] +fn memory_grow_test_2() { + let w = r#" + (module + (memory 0) + (func (export "grow") (param i32) (result i32) (memory.grow (local.get 0))) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_result!(i, get_func!(i, "grow"), 0, 0); + assert_result!(i, get_func!(i, "grow"), 1, 0); + assert_result!(i, get_func!(i, "grow"), 0, 1); + assert_result!(i, get_func!(i, "grow"), 2, 1); + assert_result!(i, get_func!(i, "grow"), 800, 3); + assert_result!(i, get_func!(i, "grow"), 0x10000, -1); + assert_result!(i, get_func!(i, "grow"), 64736, -1); + assert_result!(i, get_func!(i, "grow"), 1, 803); +} + +#[test_log::test] +fn memory_grow_test_3() { + let w = r#" + (module + (memory 0 10) + (func (export "grow") (param i32) (result i32) (memory.grow (local.get 0))) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_result!(i, get_func!(i, "grow"), 0, 0); + assert_result!(i, get_func!(i, "grow"), 1, 0); + assert_result!(i, get_func!(i, "grow"), 1, 1); + assert_result!(i, get_func!(i, "grow"), 2, 2); + assert_result!(i, get_func!(i, "grow"), 6, 4); + assert_result!(i, get_func!(i, "grow"), 0, 10); + assert_result!(i, get_func!(i, "grow"), 1, -1); + assert_result!(i, get_func!(i, "grow"), 0x10000, -1); +} diff --git a/tests/memory_redundancy.rs b/tests/memory_redundancy.rs new file mode 100644 index 00000000..74d93dcb --- /dev/null +++ b/tests/memory_redundancy.rs @@ -0,0 +1,105 @@ +/* +# This file incorporates code from the WebAssembly testsuite, originally +# available at https://github.com/WebAssembly/testsuite. +# +# The original code is licensed under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ +use hexf::hexf32; +use wasm::{validate, RuntimeInstance}; + +macro_rules! get_func { + ($instance:ident, $func_name:expr) => { + &$instance.get_function_by_name("", $func_name).unwrap() + }; +} + +macro_rules! assert_result { + ($instance:expr, $func:expr, $arg:expr, $result:expr) => { + assert_eq!($result, $instance.invoke($func, $arg).unwrap()); + }; +} + +#[test_log::test] +fn memory_redundancy() { + let w = r#" +(module + (memory 1 1) + + (func (export "zero_everything") + (i32.store (i32.const 0) (i32.const 0)) + (i32.store (i32.const 4) (i32.const 0)) + (i32.store (i32.const 8) (i32.const 0)) + (i32.store (i32.const 12) (i32.const 0)) + ) + + (func (export "test_store_to_load") (result i32) + (i32.store (i32.const 8) (i32.const 0)) + (f32.store (i32.const 5) (f32.const -0.0)) + (i32.load (i32.const 8)) + ) + + (func (export "test_redundant_load") (result i32) + (local $t i32) + (local $s i32) + (local.set $t (i32.load (i32.const 8))) + (i32.store (i32.const 5) (i32.const 0x80000000)) + (local.set $s (i32.load (i32.const 8))) + (i32.add (local.get $t) (local.get $s)) + ) + + (func (export "test_dead_store") (result f32) + (local $t f32) + (i32.store (i32.const 8) (i32.const 0x23232323)) + (local.set $t (f32.load (i32.const 11))) + (i32.store (i32.const 8) (i32.const 0)) + (local.get $t) + ) + + ;; A function named "malloc" which implementations nonetheless shouldn't + ;; assume behaves like C malloc. + (func $malloc (export "malloc") + (param $size i32) + (result i32) + (i32.const 16) + ) + + ;; Call malloc twice, but unlike C malloc, we don't get non-aliasing pointers. + (func (export "malloc_aliasing") + (result i32) + (local $x i32) + (local $y i32) + (local.set $x (call $malloc (i32.const 4))) + (local.set $y (call $malloc (i32.const 4))) + (i32.store (local.get $x) (i32.const 42)) + (i32.store (local.get $y) (i32.const 43)) + (i32.load (local.get $x)) + ) +) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + let zero_everything = get_func!(i, "zero_everything"); + assert_result!(i, get_func!(i, "test_store_to_load"), (), 0x00000080); + i.invoke::<(), ()>(zero_everything, ()).unwrap(); + assert_result!(i, get_func!(i, "test_redundant_load"), (), 0x00000080); + i.invoke::<(), ()>(zero_everything, ()).unwrap(); + assert_result!( + i, + get_func!(i, "test_dead_store"), + (), + hexf32!("0x1.18p-144") + ); + i.invoke::<(), ()>(zero_everything, ()).unwrap(); + assert_result!(i, get_func!(i, "malloc_aliasing"), (), 43); +} diff --git a/tests/memory_size.rs b/tests/memory_size.rs new file mode 100644 index 00000000..0c1a3a5f --- /dev/null +++ b/tests/memory_size.rs @@ -0,0 +1,189 @@ +/* +# This file incorporates code from the WebAssembly testsuite, originally +# available at https://github.com/WebAssembly/testsuite. +# +# The original code is licensed under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ +use wasm::{validate, RuntimeInstance}; + +macro_rules! get_func { + ($instance:ident, $func_name:expr) => { + &$instance.get_function_by_name("", $func_name).unwrap() + }; +} + +macro_rules! assert_result { + ($instance:expr, $func:expr, $arg:expr, $result:expr) => { + assert_eq!($result, $instance.invoke($func, $arg).unwrap()); + }; +} + +#[test_log::test] +fn memory_size_1() { + let w = r#" +(module + (memory 0) + (func (export "size") (result i32) (memory.size)) + (func (export "grow") (param $sz i32) (drop (memory.grow (local.get $sz)))) +) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let size = get_func!(i, "size"); + let grow = get_func!(i, "grow"); + + assert_result!(i, size, (), 0); + assert_result!(i, grow, 1, ()); + assert_result!(i, size, (), 1); + assert_result!(i, grow, 4, ()); + assert_result!(i, size, (), 5); + assert_result!(i, grow, 0, ()); + assert_result!(i, size, (), 5); +} + +#[test_log::test] +fn memory_size_2() { + let w = r#" +(module + (memory 1) + (func (export "size") (result i32) (memory.size)) + (func (export "grow") (param $sz i32) (drop (memory.grow (local.get $sz)))) +) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let size = get_func!(i, "size"); + let grow = get_func!(i, "grow"); + + assert_result!(i, size, (), 1); + assert_result!(i, grow, 1, ()); + assert_result!(i, size, (), 2); + assert_result!(i, grow, 4, ()); + assert_result!(i, size, (), 6); + assert_result!(i, grow, 0, ()); + assert_result!(i, size, (), 6); +} + +#[test_log::test] +fn memory_size_3() { + let w = r#" +(module + (memory 0 2) + (func (export "size") (result i32) (memory.size)) + (func (export "grow") (param $sz i32) (drop (memory.grow (local.get $sz)))) +) +"#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let size = get_func!(i, "size"); + let grow = get_func!(i, "grow"); + + assert_result!(i, size, (), 0); + assert_result!(i, grow, 3, ()); + assert_result!(i, size, (), 0); + assert_result!(i, grow, 1, ()); + assert_result!(i, size, (), 1); + assert_result!(i, grow, 0, ()); + assert_result!(i, size, (), 1); + assert_result!(i, grow, 4, ()); + assert_result!(i, size, (), 1); + assert_result!(i, grow, 1, ()); + assert_result!(i, size, (), 2); +} + +#[test_log::test] +fn memory_size_4() { + let w = r#" +(module + (memory 3 8) + (func (export "size") (result i32) (memory.size)) + (func (export "grow") (param $sz i32) (drop (memory.grow (local.get $sz)))) +) +"#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let size = get_func!(i, "size"); + let grow = get_func!(i, "grow"); + + assert_result!(i, size, (), 3); + assert_result!(i, grow, 1, ()); + assert_result!(i, size, (), 4); + assert_result!(i, grow, 3, ()); + assert_result!(i, size, (), 7); + assert_result!(i, grow, 0, ()); + assert_result!(i, size, (), 7); + assert_result!(i, grow, 2, ()); + assert_result!(i, size, (), 7); + assert_result!(i, grow, 1, ()); + assert_result!(i, size, (), 8); +} + +#[test_log::test] +fn memory_size_5() { + let w = r#" + (module + (memory 1) + (func $type-result-i32-vs-empty + (memory.size) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let mut i = 0; + for byte in wasm_bytes.iter() { + print!("{:#04X} ", byte); + i += 1; + if i % 8 == 0 { + i = 0; + println!(""); + } + } + let validation_info = validate(&wasm_bytes); + assert!(validation_info.is_err()); + let validation_info_err = validation_info.err().unwrap(); + assert!(validation_info_err == wasm::Error::EndInvalidValueStack); +} + +#[test_log::test] +fn memory_size_6() { + let w = r#" + (module + (memory 1) + (func $type-result-i32-vs-f32 (result f32) + (memory.size) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let mut i = 0; + for byte in wasm_bytes.iter() { + print!("{:#04X} ", byte); + i += 1; + if i % 8 == 0 { + i = 0; + println!(""); + } + } + let validation_info = validate(&wasm_bytes); + assert!(validation_info.is_err()); + let validation_info_err = validation_info.err().unwrap(); + assert!(validation_info_err == wasm::Error::EndInvalidValueStack); +} diff --git a/tests/memory_trap.rs b/tests/memory_trap.rs new file mode 100644 index 00000000..54fd5622 --- /dev/null +++ b/tests/memory_trap.rs @@ -0,0 +1,83 @@ +/* +# This file incorporates code from the WebAssembly testsuite, originally +# available at https://github.com/WebAssembly/testsuite. +# +# The original code is licensed under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ +use wasm::{validate, RuntimeError, RuntimeInstance}; + +macro_rules! get_func { + ($instance:ident, $func_name:expr) => { + &$instance.get_function_by_name("", $func_name).unwrap() + }; +} + +macro_rules! assert_result { + ($instance:expr, $func:expr, $arg:expr, $result:expr) => { + assert_eq!($result, $instance.invoke($func, $arg).unwrap()); + }; +} + +macro_rules! assert_error { + ($instance:expr, $func:expr, $arg:expr, $ret_type:ty, $invoke_param_type:ty, $invoke_return_type:ty, $err_type:expr) => { + let val: $ret_type = + $instance.invoke::<$invoke_param_type, $invoke_return_type>($func, $arg); + assert!(val.is_err()); + assert!(val.unwrap_err() == $err_type); + }; +} + +#[test_log::test] +fn memory_trap_1() { + let w = r#" +(module + (memory 1) + + (func $addr_limit (result i32) + (i32.mul (memory.size) (i32.const 0x10000)) + ) + + (func (export "store") (param $i i32) (param $v i32) + (i32.store (i32.add (call $addr_limit) (local.get $i)) (local.get $v)) + ) + + (func (export "load") (param $i i32) (result i32) + (i32.load (i32.add (call $addr_limit) (local.get $i))) + ) + + (func (export "memory.grow") (param i32) (result i32) + (memory.grow (local.get 0)) + ) +) +"#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let store = get_func!(i, "store"); + let load = get_func!(i, "load"); + + assert_result!(i, store, (-4, 42), ()); + assert_result!(i, load, -4, 42); + assert_error!(i, store, (-3, 0x12345678), Result<(), RuntimeError>, (i32, i32), (), RuntimeError::MemoryAccessOutOfBounds); + assert_error!(i, load, -3, Result, i32, i32, RuntimeError::MemoryAccessOutOfBounds); + assert_error!(i, store, (-2, 13), Result<(), RuntimeError>, (i32, i32), (), RuntimeError::MemoryAccessOutOfBounds); + assert_error!(i, load, -2, Result, i32, i32, RuntimeError::MemoryAccessOutOfBounds); + assert_error!(i, store, (-1, 13), Result<(), RuntimeError>, (i32, i32), (), RuntimeError::MemoryAccessOutOfBounds); + assert_error!(i, load, -1, Result, i32, i32, RuntimeError::MemoryAccessOutOfBounds); + assert_error!(i, store, (0, 13), Result<(), RuntimeError>, (i32, i32), (), RuntimeError::MemoryAccessOutOfBounds); + assert_error!(i, load, 0, Result, i32, i32, RuntimeError::MemoryAccessOutOfBounds); + assert_error!(i, store, (0x80000000_u32 as i32, 13), Result<(), RuntimeError>, (i32, i32), (), RuntimeError::MemoryAccessOutOfBounds); + assert_error!(i, load, 0x80000000_u32 as i32, Result, i32, i32, RuntimeError::MemoryAccessOutOfBounds); + assert_result!(i, get_func!(i, "memory.grow"), 0x10001, -1); +} From 384464b15f43ebec483f231abd314c43beb0d600 Mon Sep 17 00:00:00 2001 From: nerodesu017 Date: Mon, 18 Nov 2024 12:19:18 +0200 Subject: [PATCH 06/12] refactor: generalize const_expr reader Signed-off-by: nerodesu017 --- src/core/reader/types/opcode.rs | 2 + src/validation/globals.rs | 186 ++++----------------- src/validation/mod.rs | 1 + src/validation/read_constant_expression.rs | 139 +++++++++++++++ 4 files changed, 173 insertions(+), 155 deletions(-) create mode 100644 src/validation/read_constant_expression.rs diff --git a/src/core/reader/types/opcode.rs b/src/core/reader/types/opcode.rs index d40c94e9..097380b0 100644 --- a/src/core/reader/types/opcode.rs +++ b/src/core/reader/types/opcode.rs @@ -160,3 +160,5 @@ pub const I32_REINTERPRET_F32: u8 = 0xBC; pub const I64_REINTERPRET_F64: u8 = 0xBD; pub const F32_REINTERPRET_I32: u8 = 0xBE; pub const F64_REINTERPRET_I64: u8 = 0xBF; +pub const REF_NULL: u8 = 0xD0; +pub const REF_FUNC: u8 = 0xD2; diff --git a/src/validation/globals.rs b/src/validation/globals.rs index d5500193..2667809f 100644 --- a/src/validation/globals.rs +++ b/src/validation/globals.rs @@ -1,155 +1,31 @@ -use alloc::vec::Vec; - -use crate::core::reader::section_header::{SectionHeader, SectionTy}; -use crate::core::reader::span::Span; -use crate::core::reader::types::global::{Global, GlobalType}; -use crate::core::reader::{WasmReadable, WasmReader}; -use crate::{Error, NumType, Result, ValType}; - -use super::validation_stack::ValidationStack; - -/// Validate the global section. -/// -/// The global section is a vector of global variables. Each [Global] variable is composed of a [GlobalType] and an -/// initialization expression represented by a constant expression. -/// -/// See [read_constant_instructions] for more information. -pub(super) fn validate_global_section( - wasm: &mut WasmReader, - section_header: SectionHeader, -) -> Result> { - assert_eq!(section_header.ty, SectionTy::Global); - - wasm.read_vec(|wasm| { - let ty = GlobalType::read(wasm)?; - let init_expr = - read_constant_instructions(wasm, ty.ty, &[/* todo!(imported globals tpyes) */])?; - - Ok(Global { ty, init_expr }) - }) -} - -/// Read and validate constant expressions. -/// -/// This function is used to validate that a constant expression produces the expected result. The main use case for -/// this is to validate that an initialization expression for a global returns the correct value. -/// -/// Note: to be valid, constant expressions may not leave garbage data on the stack. It may leave only what is expected -/// and nothing more. -/// -/// Valid constant instructions are: -/// - Core: -/// - Extended Proposal: -/// -/// # The Wonders of `global.get` -/// The `global.get` instruction is quite picky by nature. To make a long story short, there are two rules to follow to -/// be able to use this expression. -/// -/// ## 1. The referenced global must be imported -/// Take the example code: -/// ```wat -/// (module -/// (global (export "g") (mut i32) ( -/// i32.add (i32.const 1) (i32.const 2) -/// )) -/// -/// (global (export "h1") i32 ( -/// i32.const 1 -/// )) -/// -/// (global (export "h2") i32 ( -/// global.get 1 -/// )) -/// -/// (func (export "f") -/// i32.const 100 -/// global.set 0)) -/// ``` -/// -/// When compiling with wat2wasm, the following error is thrown: -/// ```wat -/// Error: validate failed: -/// test.wast:11:24: error: initializer expression can only reference an imported global -/// global.get 1 -/// ^ -/// ``` -/// -/// When compiling the code with the latest dev build of wasmtime, the following error is thrown: -/// ```wat -/// failed to parse WebAssembly module -/// -/// Caused by: -/// constant expression required: global.get of locally defined global (at offset 0x24) -/// ``` -/// -/// ## 2. The referenced global must be immutable -/// -///```wat -/// (module -/// (import "env" "g" (global (mut i32))) -/// (global (export "h") (mut i32) ( -/// i32.add (i32.const 1) (global.get 0) -/// )) -/// ) -/// ``` -/// -/// When compiling with wat2wasm, the following error is thrown: -/// ```wat -/// Error: validate failed: -/// test.wast:4:27: error: initializer expression cannot reference a mutable global -/// i32.add (i32.const 1) (global.get 0) -/// ``` -/// -/// # Note -/// The following instructions are not yet supported: -/// - `ref.null` -/// - `ref.func` -/// - `global.get` -pub(super) fn read_constant_instructions( - wasm: &mut WasmReader, - this_global_valtype: ValType, - _globals_ty: &[GlobalType], -) -> Result { - let start_pc = wasm.pc; - - // Compared to the code validation, we create the validation stack here as opposed to taking it as an argument. - let mut stack = ValidationStack::new(); - - loop { - let Ok(first_instr_byte) = wasm.read_u8() else { - return Err(Error::ExprMissingEnd); - }; - trace!("Read cosntant instruction byte {first_instr_byte:#X?} ({first_instr_byte})"); - - use crate::core::reader::types::opcode::*; - match first_instr_byte { - // Missing: ref.null, ref.func, global.get - END => { - // The stack must only contain the global's valtype - stack.assert_val_types(&[this_global_valtype])?; - return Ok(Span::new(start_pc, wasm.pc - start_pc + 1)); - } - I32_CONST => { - let _num = wasm.read_var_i32()?; - stack.push_valtype(ValType::NumType(NumType::I32)); - } - I64_CONST => { - let _num = wasm.read_var_i64()?; - stack.push_valtype(ValType::NumType(NumType::I64)); - } - I32_ADD | I32_SUB | I32_MUL => { - stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; - stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; - - stack.push_valtype(ValType::NumType(NumType::I32)); - } - I64_ADD | I64_SUB | I64_MUL => { - stack.assert_pop_val_type(ValType::NumType(NumType::I64))?; - stack.assert_pop_val_type(ValType::NumType(NumType::I64))?; - - stack.push_valtype(ValType::NumType(NumType::I64)); - } - _ => return Err(Error::InvalidInstr(first_instr_byte)), - } - } -} +use alloc::vec::Vec; + +use crate::core::reader::section_header::{SectionHeader, SectionTy}; +use crate::core::reader::types::global::{Global, GlobalType}; +use crate::core::reader::{WasmReadable, WasmReader}; +use crate::read_constant_expression::read_constant_instructions; +use crate::Result; + +/// Validate the global section. +/// +/// The global section is a vector of global variables. Each [Global] variable is composed of a [GlobalType] and an +/// initialization expression represented by a constant expression. +/// +/// See [read_constant_instructions] for more information. +pub(super) fn validate_global_section( + wasm: &mut WasmReader, + section_header: SectionHeader, +) -> Result> { + assert_eq!(section_header.ty, SectionTy::Global); + + wasm.read_vec(|wasm| { + let ty = GlobalType::read(wasm)?; + let init_expr = read_constant_instructions( + wasm, + Some(ty.ty), + Some(&[/* todo!(imported globals tpyes) */]), + )?; + + Ok(Global { ty, init_expr }) + }) +} diff --git a/src/validation/mod.rs b/src/validation/mod.rs index d6a52fa9..f544b780 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -12,6 +12,7 @@ use crate::{Error, Result}; pub(crate) mod code; pub(crate) mod globals; +pub(crate) mod read_constant_expression; pub(crate) mod validation_stack; /// Information collected from validating a module. diff --git a/src/validation/read_constant_expression.rs b/src/validation/read_constant_expression.rs new file mode 100644 index 00000000..93f59654 --- /dev/null +++ b/src/validation/read_constant_expression.rs @@ -0,0 +1,139 @@ +use crate::core::reader::span::Span; +use crate::core::reader::types::global::GlobalType; +use crate::core::reader::WasmReader; +use crate::{Error, NumType, Result, ValType}; + +use super::validation_stack::ValidationStack; + +/// Read and validate constant expressions. +/// +/// This function is used to validate that a constant expression produces the expected result. The main use case for +/// this is to validate that an initialization expression for a global returns the correct value. +/// +/// Note: to be valid, constant expressions may not leave garbage data on the stack. It may leave only what is expected +/// and nothing more. +/// +/// Valid constant instructions are: +/// - Core: +/// - Extended Proposal: +/// +/// # The Wonders of `global.get` +/// The `global.get` instruction is quite picky by nature. To make a long story short, there are two rules to follow to +/// be able to use this expression. +/// +/// ## 1. The referenced global must be imported +/// Take the example code: +/// ```wat +/// (module +/// (global (export "g") (mut i32) ( +/// i32.add (i32.const 1) (i32.const 2) +/// )) +/// +/// (global (export "h1") i32 ( +/// i32.const 1 +/// )) +/// +/// (global (export "h2") i32 ( +/// global.get 1 +/// )) +/// +/// (func (export "f") +/// i32.const 100 +/// global.set 0)) +/// ``` +/// +/// When compiling with wat2wasm, the following error is thrown: +/// ```wat +/// Error: validate failed: +/// test.wast:11:24: error: initializer expression can only reference an imported global +/// global.get 1 +/// ^ +/// ``` +/// +/// When compiling the code with the latest dev build of wasmtime, the following error is thrown: +/// ```wat +/// failed to parse WebAssembly module +/// +/// Caused by: +/// constant expression required: global.get of locally defined global (at offset 0x24) +/// ``` +/// +/// ## 2. The referenced global must be immutable +/// +///```wat +/// (module +/// (import "env" "g" (global (mut i32))) +/// (global (export "h") (mut i32) ( +/// i32.add (i32.const 1) (global.get 0) +/// )) +/// ) +/// ``` +/// +/// When compiling with wat2wasm, the following error is thrown: +/// ```wat +/// Error: validate failed: +/// test.wast:4:27: error: initializer expression cannot reference a mutable global +/// i32.add (i32.const 1) (global.get 0) +/// ``` +/// +/// # Note +/// The following instructions are not yet supported: +/// - `ref.null` +/// - `ref.func` +/// - `global.get` +pub fn read_constant_instructions( + wasm: &mut WasmReader, + this_global_valtype: Option, + _globals_ty: Option<&[GlobalType]>, +) -> Result { + let start_pc = wasm.pc; + + // Compared to the code validation, we create the validation stack here as opposed to taking it as an argument. + let mut stack = ValidationStack::new(); + + loop { + let Ok(first_instr_byte) = wasm.read_u8() else { + return Err(Error::ExprMissingEnd); + }; + trace!("Read constant instruction byte {first_instr_byte:#X?} ({first_instr_byte})"); + + use crate::core::reader::types::opcode::*; + match first_instr_byte { + // Missing: ref.null, ref.func, global.get + END => { + // The stack must only contain the global's valtype + if this_global_valtype.is_some() { + stack.assert_val_types(&[this_global_valtype.unwrap()])?; + } + return Ok(Span::new(start_pc, wasm.pc - start_pc + 1)); + } + I32_CONST => { + let _num = wasm.read_var_i32()?; + stack.push_valtype(ValType::NumType(NumType::I32)); + } + I64_CONST => { + let _num = wasm.read_var_i64()?; + stack.push_valtype(ValType::NumType(NumType::I64)); + } + I32_ADD | I32_SUB | I32_MUL => { + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + + stack.push_valtype(ValType::NumType(NumType::I32)); + } + I64_ADD | I64_SUB | I64_MUL => { + stack.assert_pop_val_type(ValType::NumType(NumType::I64))?; + stack.assert_pop_val_type(ValType::NumType(NumType::I64))?; + + stack.push_valtype(ValType::NumType(NumType::I64)); + } + REF_NULL => { + todo!(); + } + REF_FUNC => { + wasm.read_var_u32().unwrap(); + } + _ => return Err(Error::InvalidInstr(first_instr_byte)), + } + } +} From f083d8abcb40557dd5b79aebe2a1ecc8324f3806 Mon Sep 17 00:00:00 2001 From: nerodesu017 Date: Mon, 18 Nov 2024 12:40:54 +0200 Subject: [PATCH 07/12] feat: DROP opcode Signed-off-by: nerodesu017 --- src/core/error.rs | 2 ++ src/core/reader/types/opcode.rs | 1 + src/execution/interpreter_loop.rs | 3 +++ src/execution/value_stack.rs | 17 +++++++++++++++++ src/validation/code.rs | 3 +++ 5 files changed, 26 insertions(+) diff --git a/src/core/error.rs b/src/core/error.rs index 1ca1649c..37a0ec9f 100644 --- a/src/core/error.rs +++ b/src/core/error.rs @@ -41,6 +41,7 @@ pub enum Error { EndInvalidValueStack, InvalidLocalIdx, InvalidValidationStackValType(Option), + ExpectedAnOperand, InvalidLimitsType(u8), InvalidMutType(u8), MoreThanOneMemory, @@ -128,6 +129,7 @@ impl Display for Error { Error::FoundLabel(lk) => f.write_fmt(format_args!( "Expecting a ValType, a Label was found: {lk:?}" )), + Error::ExpectedAnOperand => f.write_str("Expected a ValType"), // Error => f.write_str("Expected an operand (ValType) on the stack") Error::MemoryIsNotDefined(memidx) => f.write_fmt(format_args!( "C.mems[{}] is NOT defined when it should be", memidx diff --git a/src/core/reader/types/opcode.rs b/src/core/reader/types/opcode.rs index 097380b0..9dabe0d9 100644 --- a/src/core/reader/types/opcode.rs +++ b/src/core/reader/types/opcode.rs @@ -3,6 +3,7 @@ pub const NOP: u8 = 0x01; pub const END: u8 = 0x0B; pub const RETURN: u8 = 0x0F; pub const CALL: u8 = 0x10; +pub const DROP: u8 = 0x1A; pub const LOCAL_GET: u8 = 0x20; pub const LOCAL_SET: u8 = 0x21; pub const LOCAL_TEE: u8 = 0x22; diff --git a/src/execution/interpreter_loop.rs b/src/execution/interpreter_loop.rs index 63be8703..9a67b700 100644 --- a/src/execution/interpreter_loop.rs +++ b/src/execution/interpreter_loop.rs @@ -116,6 +116,9 @@ pub(super) fn run( wasm.move_start_to(func_to_call_inst.code_expr) .unwrap_validated(); } + DROP => { + stack.drop_value(); + } LOCAL_GET => { let local_idx = wasm.read_var_u32().unwrap_validated() as LocalIdx; stack.get_local(local_idx); diff --git a/src/execution/value_stack.rs b/src/execution/value_stack.rs index 27cf4ddf..9c837633 100644 --- a/src/execution/value_stack.rs +++ b/src/execution/value_stack.rs @@ -29,6 +29,23 @@ impl Stack { Self::default() } + pub fn drop_value(&mut self) { + // If there is at least one stack frame, we shall not pop values past the current + // stackframe. However, there is one legitimate reason to pop when there is **no** current + // stackframe: after the outermost function returns, to extract the final return values of + // this interpreter invocation. + debug_assert!( + if !self.frames.is_empty() { + self.values.len() > self.current_stackframe().value_stack_base_idx + } else { + true + }, + "can not pop values past the current stackframe" + ); + + self.values.pop().unwrap_validated(); + } + /// Pop a value of the given [ValType] from the value stack pub fn pop_value(&mut self, ty: ValType) -> Value { // If there is at least one stack frame, we shall not pop values past the current diff --git a/src/validation/code.rs b/src/validation/code.rs index ecbba3e2..791a47a1 100644 --- a/src/validation/code.rs +++ b/src/validation/code.rs @@ -182,6 +182,9 @@ fn read_instructions( UNREACHABLE => { stack.make_unspecified(); } + DROP => { + stack.drop_val()?; + } // local.get: [] -> [t] LOCAL_GET => { let local_idx = wasm.read_var_u32()? as LocalIdx; From 7f64d8cc14c62db2cb72008962973450046b3e05 Mon Sep 17 00:00:00 2001 From: nerodesu017 Date: Mon, 18 Nov 2024 12:42:23 +0200 Subject: [PATCH 08/12] fix: local.set Signed-off-by: nerodesu017 --- src/execution/value_stack.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/execution/value_stack.rs b/src/execution/value_stack.rs index 9c837633..6cb72fc7 100644 --- a/src/execution/value_stack.rs +++ b/src/execution/value_stack.rs @@ -92,14 +92,14 @@ impl Stack { /// Pop value from the top of the value stack, writing it to the given local pub fn set_local(&mut self, idx: LocalIdx) { - let local_ty = self.current_stackframe().locals.get_ty(idx); - - let stack_value = self.pop_value(local_ty); debug_assert!( self.values.len() > self.current_stackframe().value_stack_base_idx, "can not pop values past the current stackframe" ); + let local_ty = self.current_stackframe().locals.get_ty(idx); + let stack_value = self.pop_value(local_ty); + trace!("Instruction: local.set [{stack_value:?}] -> []"); *self.current_stackframe_mut().locals.get_mut(idx) = stack_value; } From f57c09b6b4ca5bc555008e03557e88a5b1691a76 Mon Sep 17 00:00:00 2001 From: nerodesu017 Date: Mon, 18 Nov 2024 12:47:17 +0200 Subject: [PATCH 09/12] feat: fc extensions numeric opcodes + tests Signed-off-by: nerodesu017 --- src/core/reader/types/opcode.rs | 12 + src/execution/interpreter_loop.rs | 137 +++ src/execution/value.rs | 6 + src/validation/code.rs | 51 + tests/fc_extensions.rs | 1531 +++++++++++++++++++++++++++++ 5 files changed, 1737 insertions(+) create mode 100644 tests/fc_extensions.rs diff --git a/src/core/reader/types/opcode.rs b/src/core/reader/types/opcode.rs index 9dabe0d9..44ac32a3 100644 --- a/src/core/reader/types/opcode.rs +++ b/src/core/reader/types/opcode.rs @@ -163,3 +163,15 @@ pub const F32_REINTERPRET_I32: u8 = 0xBE; pub const F64_REINTERPRET_I64: u8 = 0xBF; pub const REF_NULL: u8 = 0xD0; pub const REF_FUNC: u8 = 0xD2; +pub const FC_EXTENSIONS: u8 = 0xFC; + +pub mod fc_extensions { + pub const I32_TRUNC_SAT_F32_S: u8 = 0x00; + pub const I32_TRUNC_SAT_F32_U: u8 = 0x01; + pub const I32_TRUNC_SAT_F64_S: u8 = 0x02; + pub const I32_TRUNC_SAT_F64_U: u8 = 0x03; + pub const I64_TRUNC_SAT_F32_S: u8 = 0x04; + pub const I64_TRUNC_SAT_F32_U: u8 = 0x05; + pub const I64_TRUNC_SAT_F64_S: u8 = 0x06; + pub const I64_TRUNC_SAT_F64_U: u8 = 0x07; +} diff --git a/src/execution/interpreter_loop.rs b/src/execution/interpreter_loop.rs index 9a67b700..b7d846f8 100644 --- a/src/execution/interpreter_loop.rs +++ b/src/execution/interpreter_loop.rs @@ -1898,6 +1898,143 @@ pub(super) fn run( trace!("Instruction: f64.reinterpret_i64 [{v1}] -> [{res:.17}]"); stack.push_value(res.into()); } + FC_EXTENSIONS => { + // Should we call instruction hook here as well? Multibyte instruction + let second_instr_byte = wasm.read_u8().unwrap_validated(); + + use crate::core::reader::types::opcode::fc_extensions::*; + match second_instr_byte { + I32_TRUNC_SAT_F32_S => { + let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into(); + let res = { + if v1.is_nan() { + 0 + } else if v1.is_negative_infinity() { + i32::MIN + } else if v1.is_infinity() { + i32::MAX + } else { + v1.as_i32() + } + }; + + trace!("Instruction: i32.trunc_sat_f32_s [{v1}] -> [{res}]"); + stack.push_value(res.into()); + } + I32_TRUNC_SAT_F32_U => { + let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into(); + let res = { + if v1.is_nan() || v1.is_negative_infinity() { + 0 + } else if v1.is_infinity() { + u32::MAX as i32 + } else { + v1.as_u32() as i32 + } + }; + + trace!("Instruction: i32.trunc_sat_f32_u [{v1}] -> [{res}]"); + stack.push_value(res.into()); + } + I32_TRUNC_SAT_F64_S => { + let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into(); + let res = { + if v1.is_nan() { + 0 + } else if v1.is_negative_infinity() { + i32::MIN + } else if v1.is_infinity() { + i32::MAX + } else { + v1.as_i32() + } + }; + + trace!("Instruction: i32.trunc_sat_f64_s [{v1}] -> [{res}]"); + stack.push_value(res.into()); + } + I32_TRUNC_SAT_F64_U => { + let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into(); + let res = { + if v1.is_nan() || v1.is_negative_infinity() { + 0 + } else if v1.is_infinity() { + u32::MAX as i32 + } else { + v1.as_u32() as i32 + } + }; + + trace!("Instruction: i32.trunc_sat_f64_u [{v1}] -> [{res}]"); + stack.push_value(res.into()); + } + I64_TRUNC_SAT_F32_S => { + let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into(); + let res = { + if v1.is_nan() { + 0 + } else if v1.is_negative_infinity() { + i64::MIN + } else if v1.is_infinity() { + i64::MAX + } else { + v1.as_i64() + } + }; + + trace!("Instruction: i64.trunc_sat_f32_s [{v1}] -> [{res}]"); + stack.push_value(res.into()); + } + I64_TRUNC_SAT_F32_U => { + let v1: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into(); + let res = { + if v1.is_nan() || v1.is_negative_infinity() { + 0 + } else if v1.is_infinity() { + u64::MAX as i64 + } else { + v1.as_u64() as i64 + } + }; + + trace!("Instruction: i64.trunc_sat_f32_u [{v1}] -> [{res}]"); + stack.push_value(res.into()); + } + I64_TRUNC_SAT_F64_S => { + let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into(); + let res = { + if v1.is_nan() { + 0 + } else if v1.is_negative_infinity() { + i64::MIN + } else if v1.is_infinity() { + i64::MAX + } else { + v1.as_i64() + } + }; + + trace!("Instruction: i64.trunc_sat_f64_s [{v1}] -> [{res}]"); + stack.push_value(res.into()); + } + I64_TRUNC_SAT_F64_U => { + let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into(); + let res = { + if v1.is_nan() || v1.is_negative_infinity() { + 0 + } else if v1.is_infinity() { + u64::MAX as i64 + } else { + v1.as_u64() as i64 + } + }; + + trace!("Instruction: i64.trunc_sat_f64_u [{v1}] -> [{res}]"); + stack.push_value(res.into()); + } + _ => unreachable!(), + } + } other => { trace!("Unknown instruction {other:#x}, skipping.."); } diff --git a/src/execution/value.rs b/src/execution/value.rs index 1ef8e9df..fadecdbd 100644 --- a/src/execution/value.rs +++ b/src/execution/value.rs @@ -104,6 +104,9 @@ impl F32 { pub fn is_infinity(&self) -> bool { self.0.is_infinite() } + pub fn is_negative_infinity(&self) -> bool { + self.0.is_infinite() && self.0 < 0.0 + } pub fn as_i32(&self) -> i32 { self.0 as i32 @@ -222,6 +225,9 @@ impl F64 { pub fn is_infinity(&self) -> bool { self.0.is_infinite() } + pub fn is_negative_infinity(&self) -> bool { + self.0.is_infinite() && self.0 < 0.0 + } pub fn as_i32(&self) -> i32 { self.0 as i32 diff --git a/src/validation/code.rs b/src/validation/code.rs index 791a47a1..2beca25a 100644 --- a/src/validation/code.rs +++ b/src/validation/code.rs @@ -673,6 +673,57 @@ fn read_instructions( stack.push_valtype(ValType::NumType(NumType::F64)); } + + + FC_EXTENSIONS => { + let Ok(second_instr_byte) = wasm.read_u8() else { + // TODO only do this if EOF + return Err(Error::ExprMissingEnd); + }; + trace!("Read instruction byte {second_instr_byte:#04X?} ({second_instr_byte}) at wasm_binary[{}]", wasm.pc); + + use crate::core::reader::types::opcode::fc_extensions::*; + match second_instr_byte { + I32_TRUNC_SAT_F32_S => { + stack.assert_pop_val_type(ValType::NumType(NumType::F32))?; + stack.push_valtype(ValType::NumType(NumType::I32)); + } + I32_TRUNC_SAT_F32_U => { + stack.assert_pop_val_type(ValType::NumType(NumType::F32))?; + stack.push_valtype(ValType::NumType(NumType::I32)); + } + I32_TRUNC_SAT_F64_S => { + stack.assert_pop_val_type(ValType::NumType(NumType::F64))?; + stack.push_valtype(ValType::NumType(NumType::I32)); + } + I32_TRUNC_SAT_F64_U => { + stack.assert_pop_val_type(ValType::NumType(NumType::F64))?; + stack.push_valtype(ValType::NumType(NumType::I32)); + } + I64_TRUNC_SAT_F32_S => { + stack.assert_pop_val_type(ValType::NumType(NumType::F32))?; + stack.push_valtype(ValType::NumType(NumType::I64)); + } + I64_TRUNC_SAT_F32_U => { + stack.assert_pop_val_type(ValType::NumType(NumType::F32))?; + stack.push_valtype(ValType::NumType(NumType::I64)); + } + I64_TRUNC_SAT_F64_S => { + stack.assert_pop_val_type(ValType::NumType(NumType::F64))?; + stack.push_valtype(ValType::NumType(NumType::I64)); + } + I64_TRUNC_SAT_F64_U => { + stack.assert_pop_val_type(ValType::NumType(NumType::F64))?; + stack.push_valtype(ValType::NumType(NumType::I64)); + } + _ => { + return Err(Error::InvalidMultiByteInstr( + first_instr_byte, + second_instr_byte, + )) + } + } + } _ => return Err(Error::InvalidInstr(first_instr_byte)), } } diff --git a/tests/fc_extensions.rs b/tests/fc_extensions.rs new file mode 100644 index 00000000..02ad328e --- /dev/null +++ b/tests/fc_extensions.rs @@ -0,0 +1,1531 @@ +/* +# This file incorporates code from the WebAssembly testsuite, originally +# available at https://github.com/WebAssembly/testsuite. +# +# The original code is licensed under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ +use core::{f32, f64}; + +use hexf::{hexf32, hexf64}; +use wasm::{validate, RuntimeInstance}; + +const WAT: &str = r#" + (module + (func (export "{{0}}") (param $x {{1}}) (result {{2}}) + local.get $x + {{0}}) + ) +"#; + +#[should_panic(expected = "validation failed: InvalidValidationStackValType(Some(NumType(F64)))")] +#[test_log::test] +pub fn i32_trunc_sat_f32_s_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.trunc_sat_f32_s") + .replace("{{1}}", "f64") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!( + -1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1) + .unwrap() + ); +} + +#[test_log::test] +pub fn i32_trunc_sat_f32_s() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.trunc_sat_f32_s") + .replace("{{1}}", "f32") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!( + 0, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0.0_f32) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -0.0_f32) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("0x1p-149") + ) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("-0x1p-149") + ) + .unwrap() + ); + assert_eq!( + 1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.0_f32) + .unwrap() + ); + assert_eq!( + 1, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("0x1.19999ap+0") + ) + .unwrap() + ); + assert_eq!( + 1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.5_f32) + .unwrap() + ); + assert_eq!( + -1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1.0_f32) + .unwrap() + ); + assert_eq!( + -1, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("-0x1.19999ap+0") + ) + .unwrap() + ); + assert_eq!( + -1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1.5_f32) + .unwrap() + ); + assert_eq!( + -1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1.9_f32) + .unwrap() + ); + assert_eq!( + -2, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -2.0_f32) + .unwrap() + ); + assert_eq!( + 2147483520, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 2147483520.0_f32 + ) + .unwrap() + ); + assert_eq!( + -2147483648, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + -2147483648.0_f32 + ) + .unwrap() + ); + assert_eq!( + 0x7fffffff, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 2147483648.0_f32 + ) + .unwrap() + ); + assert_eq!( + 0x80000000_u32 as i32, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + -2147483904.0_f32 + ) + .unwrap() + ); + assert_eq!( + 0x7fffffff, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + f32::INFINITY + ) + .unwrap() + ); + assert_eq!( + 0x80000000_u32 as i32, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + -f32::INFINITY + ) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), f32::NAN) + .unwrap() + ); + // (assert_return (invoke "i32.trunc_sat_f32_s" (f32.const nan:0x200000)) (i32.const 0)) + assert_eq!( + 0, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -f32::NAN) + .unwrap() + ); + // (assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -nan:0x200000)) (i32.const 0)) +} + +#[should_panic(expected = "validation failed: InvalidValidationStackValType(Some(NumType(F64)))")] +#[test_log::test] +pub fn i32_trunc_sat_f32_u_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.trunc_sat_f32_u") + .replace("{{1}}", "f64") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!( + -1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1) + .unwrap() + ); +} + +#[test_log::test] +pub fn i32_trunc_sat_f32_u() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.trunc_sat_f32_u") + .replace("{{1}}", "f32") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!( + 0, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0.0_f32) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -0.0_f32) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("0x1p-149") + ) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("-0x1p-149") + ) + .unwrap() + ); + assert_eq!( + 1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.0_f32) + .unwrap() + ); + assert_eq!( + 1, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("0x1.19999ap+0") + ) + .unwrap() + ); + assert_eq!( + 1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.5_f32) + .unwrap() + ); + assert_eq!( + 1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.9_f32) + .unwrap() + ); + assert_eq!( + 2, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 2.0_f32) + .unwrap() + ); + assert_eq!( + -2147483648, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 2147483648_f32 + ) + .unwrap() + ); + assert_eq!( + -256, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 4294967040.0_f32 + ) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("-0x1.ccccccp-1") + ) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("-0x1.fffffep-1") + ) + .unwrap() + ); + assert_eq!( + 0xffffffff_u32 as i32, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 4294967296.0_f32 + ) + .unwrap() + ); + assert_eq!( + 0x00000000, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1_f32) + .unwrap() + ); + assert_eq!( + 0xffffffff_u32 as i32, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + f32::INFINITY + ) + .unwrap() + ); + assert_eq!( + 0x00000000, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + f32::NEG_INFINITY + ) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), f32::NAN) + .unwrap() + ); + // (assert_return (invoke "i32.trunc_sat_f32_u" (f32.const nan:0x200000)) (i32.const 0)) + assert_eq!( + 0, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -f32::NAN) + .unwrap() + ); + // (assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -nan:0x200000)) (i32.const 0)) +} + +#[should_panic(expected = "validation failed: InvalidValidationStackValType(Some(NumType(F32)))")] +#[test_log::test] +pub fn i32_trunc_sat_f64_s_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.trunc_sat_f64_s") + .replace("{{1}}", "f32") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!( + -1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1) + .unwrap() + ); +} + +#[test_log::test] +pub fn i32_trunc_sat_f64_s() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.trunc_sat_f64_s") + .replace("{{1}}", "f64") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!( + 0, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0.0) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -0.0) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("0x0.0000000000001p-1022") + ) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("-0x0.0000000000001p-1022") + ) + .unwrap() + ); + assert_eq!( + 1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.0) + .unwrap() + ); + assert_eq!( + 1, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("0x1.199999999999ap+0") + ) + .unwrap() + ); + assert_eq!( + 1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.5) + .unwrap() + ); + assert_eq!( + -1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1.0) + .unwrap() + ); + assert_eq!( + -1, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("-0x1.199999999999ap+0") + ) + .unwrap() + ); + assert_eq!( + -1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1.5) + .unwrap() + ); + assert_eq!( + -1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1.9) + .unwrap() + ); + assert_eq!( + -2, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -2.0) + .unwrap() + ); + assert_eq!( + 2147483647, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 2147483647.0) + .unwrap() + ); + assert_eq!( + -2147483648, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + -2147483648.0 + ) + .unwrap() + ); + assert_eq!( + 0x7fffffff, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 2147483648.0) + .unwrap() + ); + assert_eq!( + 0x80000000_u32 as i32, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + -2147483649.0 + ) + .unwrap() + ); + assert_eq!( + 0x7fffffff, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + f64::INFINITY + ) + .unwrap() + ); + assert_eq!( + 0x80000000_u32 as i32, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + -f64::INFINITY + ) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), f64::NAN) + .unwrap() + ); + // (assert_return (invoke "i32.trunc_sat_s" (f32.const nan:0x200000)) (i32.const 0)) + assert_eq!( + 0, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -f64::NAN) + .unwrap() + ); + // (assert_return (invoke "i32.trunc_sat_s" (f32.const -nan:0x200000)) (i32.const 0)) +} + +#[should_panic(expected = "validation failed: InvalidValidationStackValType(Some(NumType(F32)))")] +#[test_log::test] +pub fn i32_trunc_sat_f64_u_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.trunc_sat_f64_u") + .replace("{{1}}", "f32") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!( + -1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1) + .unwrap() + ); +} + +#[test_log::test] +pub fn i32_trunc_sat_f64_u() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.trunc_sat_f64_u") + .replace("{{1}}", "f64") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!( + 0, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0.0) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -0.0) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("0x0.0000000000001p-1022") + ) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("-0x0.0000000000001p-1022") + ) + .unwrap() + ); + assert_eq!( + 1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.0) + .unwrap() + ); + assert_eq!( + 1, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("0x1.199999999999ap+0") + ) + .unwrap() + ); + assert_eq!( + 1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.5) + .unwrap() + ); + assert_eq!( + 1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.9) + .unwrap() + ); + assert_eq!( + 2, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 2.0) + .unwrap() + ); + assert_eq!( + -2147483648, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 2147483648_f64 + ) + .unwrap() + ); + assert_eq!( + -1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 4294967295.0) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("-0x1.ccccccccccccdp-1") + ) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("-0x1.fffffffffffffp-1") + ) + .unwrap() + ); + assert_eq!( + 100000000, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1e8_f64) + .unwrap() + ); + assert_eq!( + 0xffffffff_u32 as i32, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 4294967296.0) + .unwrap() + ); + assert_eq!( + 0x00000000, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1.0) + .unwrap() + ); + assert_eq!( + 0xffffffff_u32 as i32, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1e16_f64) + .unwrap() + ); + assert_eq!( + 0xffffffff_u32 as i32, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1e30_f64) + .unwrap() + ); + assert_eq!( + 0xffffffff_u32 as i32, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 9223372036854775808_f64 + ) + .unwrap() + ); + assert_eq!( + 0xffffffff_u32 as i32, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + f64::INFINITY + ) + .unwrap() + ); + assert_eq!( + 0x00000000, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + f64::NEG_INFINITY + ) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), f64::NAN) + .unwrap() + ); + // (assert_return (invoke "i32.trunc_sat_f64_u" (f64.const nan:0x4000000000000)) (i32.const 0)) + assert_eq!( + 0, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -f64::NAN) + .unwrap() + ); + // (assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -nan:0x4000000000000)) (i32.const 0)) +} + +#[should_panic(expected = "validation failed: InvalidValidationStackValType(Some(NumType(F64)))")] +#[test_log::test] +pub fn i64_trunc_sat_f32_s_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.trunc_sat_f32_s") + .replace("{{1}}", "f64") + .replace("{{2}}", "i64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!( + -1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1) + .unwrap() + ); +} + +#[test_log::test] +pub fn i64_trunc_sat_f32_s() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.trunc_sat_f32_s") + .replace("{{1}}", "f32") + .replace("{{2}}", "i64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!( + 0_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0.0_f32) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -0.0_f32) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("0x1p-149") + ) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("-0x1p-149") + ) + .unwrap() + ); + assert_eq!( + 1_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.0_f32) + .unwrap() + ); + assert_eq!( + 1_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("0x1.19999ap+0") + ) + .unwrap() + ); + assert_eq!( + 1_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.5_f32) + .unwrap() + ); + assert_eq!( + -1_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1.0_f32) + .unwrap() + ); + assert_eq!( + -1_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("-0x1.19999ap+0") + ) + .unwrap() + ); + assert_eq!( + -1_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1.5_f32) + .unwrap() + ); + assert_eq!( + -1_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1.9_f32) + .unwrap() + ); + assert_eq!( + -2_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -2.0_f32) + .unwrap() + ); + assert_eq!( + 4294967296_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 4294967296_f32 + ) + .unwrap() + ); + assert_eq!( + -4294967296_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + -4294967296_f32 + ) + .unwrap() + ); + assert_eq!( + 9223371487098961920_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 9223371487098961920.0_f32 + ) + .unwrap() + ); + assert_eq!( + -9223372036854775808_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + -9223372036854775808.0_f32 + ) + .unwrap() + ); + assert_eq!( + 0x7fffffffffffffff_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 9223372036854775808.0_f32 + ) + .unwrap() + ); + assert_eq!( + 0x8000000000000000_u64 as i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + -9223373136366403584.0_f32 + ) + .unwrap() + ); + assert_eq!( + 0x7fffffffffffffff_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + f32::INFINITY + ) + .unwrap() + ); + assert_eq!( + 0x8000000000000000_u64 as i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + -f32::INFINITY + ) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), f32::NAN) + .unwrap() + ); + // (assert_return (invoke "i64.trunc_sat_f32_s" (f32.const nan:0x200000)) (i64.const 0)) + assert_eq!( + 0_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -f32::NAN) + .unwrap() + ); + // (assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -nan:0x200000)) (i64.const 0)) +} + +#[should_panic(expected = "validation failed: InvalidValidationStackValType(Some(NumType(F64)))")] +#[test_log::test] +pub fn i64_trunc_sat_f32_u_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.trunc_sat_f32_u") + .replace("{{1}}", "f64") + .replace("{{2}}", "i64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!( + -1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1) + .unwrap() + ); +} + +#[test_log::test] +pub fn i64_trunc_sat_f32_u() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.trunc_sat_f32_u") + .replace("{{1}}", "f32") + .replace("{{2}}", "i64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!( + 0_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0.0_f32) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -0.0_f32) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("0x1p-149") + ) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("-0x1p-149") + ) + .unwrap() + ); + assert_eq!( + 1_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.0_f32) + .unwrap() + ); + assert_eq!( + 1_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("0x1.19999ap+0") + ) + .unwrap() + ); + assert_eq!( + 1_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.5_f32) + .unwrap() + ); + assert_eq!( + 4294967296_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 4294967296_f32 + ) + .unwrap() + ); + assert_eq!( + -1099511627776_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 18446742974197923840.0_f32 + ) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("-0x1.ccccccp-1") + ) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf32!("-0x1.fffffep-1") + ) + .unwrap() + ); + assert_eq!( + 0xffffffffffffffff_u64 as i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 18446744073709551616.0_f32 + ) + .unwrap() + ); + assert_eq!( + 0x0000000000000000_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1.0_f32) + .unwrap() + ); + assert_eq!( + 0xffffffffffffffff_u64 as i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + f32::INFINITY + ) + .unwrap() + ); + assert_eq!( + 0x0000000000000000_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + f32::NEG_INFINITY + ) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), f32::NAN) + .unwrap() + ); + // (assert_return (invoke "i64.trunc_sat_f32_u" (f32.const nan:0x200000)) (i64.const 0)) + assert_eq!( + 0_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -f32::NAN) + .unwrap() + ); + // (assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -nan:0x200000)) (i64.const 0)) +} + +#[should_panic(expected = "validation failed: InvalidValidationStackValType(Some(NumType(F32)))")] +#[test_log::test] +pub fn i64_trunc_sat_f64_s_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.trunc_sat_f64_s") + .replace("{{1}}", "f32") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!( + -1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1) + .unwrap() + ); +} + +#[test_log::test] +pub fn i64_trunc_sat_f64_s() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.trunc_sat_f64_s") + .replace("{{1}}", "f64") + .replace("{{2}}", "i64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!( + 0_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0.0) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -0.0) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("0x0.0000000000001p-1022") + ) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("-0x0.0000000000001p-1022") + ) + .unwrap() + ); + assert_eq!( + 1_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.0) + .unwrap() + ); + assert_eq!( + 1_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("0x1.199999999999ap+0") + ) + .unwrap() + ); + assert_eq!( + 1_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.5) + .unwrap() + ); + assert_eq!( + -1_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1.0) + .unwrap() + ); + assert_eq!( + -1_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("-0x1.199999999999ap+0") + ) + .unwrap() + ); + assert_eq!( + -1_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1.5) + .unwrap() + ); + assert_eq!( + -1_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1.9) + .unwrap() + ); + assert_eq!( + -2_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -2.0) + .unwrap() + ); + assert_eq!( + 4294967296_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 4294967296_f64 + ) + .unwrap() + ); + assert_eq!( + -4294967296_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + -4294967296_f64 + ) + .unwrap() + ); + assert_eq!( + 9223372036854774784_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 9223372036854774784.0 + ) + .unwrap() + ); + assert_eq!( + -9223372036854775808_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + -9223372036854775808.0 + ) + .unwrap() + ); + assert_eq!( + 0x7fffffffffffffff_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 9223372036854775808.0 + ) + .unwrap() + ); + assert_eq!( + 0x8000000000000000_u64 as i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + -9223372036854777856.0 + ) + .unwrap() + ); + assert_eq!( + 0x7fffffffffffffff_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + f64::INFINITY + ) + .unwrap() + ); + assert_eq!( + 0x8000000000000000_u64 as i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + -f64::INFINITY + ) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), f64::NAN) + .unwrap() + ); + // (assert_return (invoke "i64.trunc_sat_f64_s" (f64.const nan:0x4000000000000)) (i64.const 0)) + assert_eq!( + 0_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -f64::NAN) + .unwrap() + ); + // (assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -nan:0x4000000000000)) (i64.const 0)) +} + +#[should_panic(expected = "validation failed: InvalidValidationStackValType(Some(NumType(F32)))")] +#[test_log::test] +pub fn i64_trunc_sat_f64_u_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.trunc_sat_f64_u") + .replace("{{1}}", "f32") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!( + -1, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1) + .unwrap() + ); +} + +#[test_log::test] +pub fn i64_trunc_sat_f64_u() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.trunc_sat_f64_u") + .replace("{{1}}", "f64") + .replace("{{2}}", "i64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!( + 0_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 0.0) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -0.0) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("0x0.0000000000001p-1022") + ) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("-0x0.0000000000001p-1022") + ) + .unwrap() + ); + assert_eq!( + 1_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.0) + .unwrap() + ); + assert_eq!( + 1_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("0x1.199999999999ap+0") + ) + .unwrap() + ); + assert_eq!( + 1_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.5) + .unwrap() + ); + assert_eq!( + 1_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1.9) + .unwrap() + ); + assert_eq!( + 0xffffffff_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 4294967295_f64 + ) + .unwrap() + ); + assert_eq!( + 0x100000000_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 4294967296_f64 + ) + .unwrap() + ); + assert_eq!( + -2048_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 18446744073709549568.0 + ) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("-0x1.ccccccccccccdp-1") + ) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + hexf64!("-0x1.fffffffffffffp-1") + ) + .unwrap() + ); + assert_eq!( + 100000000_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1e8_f64) + .unwrap() + ); + assert_eq!( + 10000000000000000_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), 1e16_f64) + .unwrap() + ); + assert_eq!( + -9223372036854775808_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 9223372036854775808_f64 + ) + .unwrap() + ); + assert_eq!( + 0xffffffffffffffff_u64 as i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + 18446744073709551616.0_f64 + ) + .unwrap() + ); + assert_eq!( + 0x0000000000000000_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -1.0) + .unwrap() + ); + assert_eq!( + 0xffffffffffffffff_u64 as i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + f64::INFINITY + ) + .unwrap() + ); + assert_eq!( + 0x0000000000000000_i64, + instance + .invoke( + &instance.get_function_by_index(0, 0).unwrap(), + f64::NEG_INFINITY + ) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), f64::NAN) + .unwrap() + ); + // (assert_return (invoke "i64.trunc_sat_f64_u" (f64.const nan:0x4000000000000)) (i64.const 0)) + assert_eq!( + 0_i64, + instance + .invoke(&instance.get_function_by_index(0, 0).unwrap(), -f64::NAN) + .unwrap() + ); + // (assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -nan:0x4000000000000)) (i64.const 0)) +} From e660a0454048cace885ebb478a25a4ba5d50ebd2 Mon Sep 17 00:00:00 2001 From: nerodesu017 Date: Mon, 18 Nov 2024 12:49:05 +0200 Subject: [PATCH 10/12] fix: DROP opcode Signed-off-by: nerodesu017 --- src/validation/validation_stack.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/validation/validation_stack.rs b/src/validation/validation_stack.rs index a692ae40..7a327ddd 100644 --- a/src/validation/validation_stack.rs +++ b/src/validation/validation_stack.rs @@ -32,6 +32,15 @@ impl ValidationStack { self.stack.push(ValidationStackEntry::Label(label_info)); } + /// Similar to [`ValidationStack::pop`], because it pops a value from the stack, + /// but more public and doesn't actually return the popped value. + pub(super) fn drop_val(&mut self) -> Result<()> { + match self.stack.pop().ok_or(Error::EndInvalidValueStack)? { + ValidationStackEntry::Val(_) => Ok(()), + _ => Err(Error::ExpectedAnOperand), + } + } + /// This puts an unspecified element on top of the stack. /// While the top of the stack is unspecified, arbitrary value types can be popped. /// To undo this, a new label has to be pushed or an existing one has to be popped. From 51018e6c14aae2e976c9c4d6e5a674ac6795731c Mon Sep 17 00:00:00 2001 From: nerodesu017 Date: Mon, 18 Nov 2024 12:50:05 +0200 Subject: [PATCH 11/12] feat: data + data related opcodes + tests Signed-off-by: nerodesu017 --- src/core/error.rs | 8 +- src/core/indices.rs | 1 - src/core/reader/types/data.rs | 199 ++++++ src/core/reader/types/mod.rs | 1 + src/core/reader/types/opcode.rs | 4 + src/execution/interpreter_loop.rs | 161 ++++- src/execution/mod.rs | 64 +- src/execution/store.rs | 5 + src/execution/value_stack.rs | 5 + src/validation/code.rs | 49 +- src/validation/mod.rs | 39 +- tests/memory_copy.rs | 1068 +++++++++++++++++++++++++++++ tests/memory_fill.rs | 54 ++ tests/memory_init.rs | 318 +++++++++ 14 files changed, 1961 insertions(+), 15 deletions(-) create mode 100644 src/core/reader/types/data.rs create mode 100644 tests/memory_copy.rs create mode 100644 tests/memory_fill.rs create mode 100644 tests/memory_init.rs diff --git a/src/core/error.rs b/src/core/error.rs index 37a0ec9f..be6685f8 100644 --- a/src/core/error.rs +++ b/src/core/error.rs @@ -6,7 +6,7 @@ use core::str::Utf8Error; use crate::core::reader::section_header::SectionTy; use crate::core::reader::types::ValType; -use super::indices::MemIdx; +use super::indices::{DataIdx, MemIdx}; #[derive(Debug, PartialEq, Eq, Clone)] pub enum RuntimeError { @@ -54,6 +54,8 @@ pub enum Error { MemoryIsNotDefined(MemIdx), // mem.align, wanted alignment ErroneousAlignment(u32, u32), + NoDataSegments, + DataSegmentNotFound(DataIdx), } impl Display for Error { @@ -140,6 +142,10 @@ impl Display for Error { mem_align, minimum_wanted_alignment )) } + Error::NoDataSegments => f.write_str("Data Count is None"), + Error::DataSegmentNotFound(data_idx) => { + f.write_fmt(format_args!("Data Segment {} not found", data_idx)) + } } } } diff --git a/src/core/indices.rs b/src/core/indices.rs index 6829bc41..13e8c4c9 100644 --- a/src/core/indices.rs +++ b/src/core/indices.rs @@ -22,7 +22,6 @@ pub type MemIdx = usize; pub type GlobalIdx = usize; #[allow(dead_code)] pub type ElemIdx = usize; -#[allow(dead_code)] pub type DataIdx = usize; pub type LocalIdx = usize; #[allow(dead_code)] diff --git a/src/core/reader/types/data.rs b/src/core/reader/types/data.rs new file mode 100644 index 00000000..f32eb08d --- /dev/null +++ b/src/core/reader/types/data.rs @@ -0,0 +1,199 @@ +use core::fmt::{Debug, Formatter}; + +use alloc::{format, vec::Vec}; + +use crate::{ + core::{indices::MemIdx, reader::{span::Span, WasmReadable}}, + read_constant_expression::read_constant_instructions, +}; + +use super::UnwrapValidatedExt; + +pub struct DataSegment { + pub init: Vec, + pub mode: DataMode, +} + +#[derive(Clone)] +pub enum DataMode { + Passive, + Active(DataModeActive), +} + +#[derive(Clone)] +pub struct DataModeActive { + pub memory_idx: MemIdx, + pub offset: Span, +} + +impl WasmReadable for DataSegment { + fn read(wasm: &mut crate::core::reader::WasmReader) -> crate::Result { + let mode = wasm.read_var_u32()?; + let data_sec: DataSegment = match mode { + 0 => { + // active { memory 0, offset e } + trace!("Data section: active"); + let offset = { read_constant_instructions(wasm, None, None)? }; + + let byte_vec = wasm.read_vec(|el| el.read_u8())?; + + // WARN: we currently don't take into consideration how we act when we are dealing with globals here + DataSegment { + mode: DataMode::Active(DataModeActive { + memory_idx: 0, + offset, + }), + init: byte_vec, + } + } + 1 => { + // passive + // A passive data segment's contents can be copied into a memory using the `memory.init` instruction + trace!("Data section: passive"); + DataSegment { + mode: DataMode::Passive, + init: wasm.read_vec(|el| el.read_u8())?, + } + } + 2 => { + // mode active { memory x, offset e } + // this hasn't been yet implemented in wasm + // as per docs: + + // https://webassembly.github.io/spec/core/binary/modules.html#data-section + // The initial integer can be interpreted as a bitfield. Bit 0 indicates a passive segment, bit 1 indicates the presence of an explicit memory index for an active segment. + // In the current version of WebAssembly, at most one memory may be defined or imported in a single module, so all valid active data segments have a memory value of 0 + todo!("Data section: active - with multiple memories - NOT YET IMPLEMENTED!"); + } + _ => unreachable!(), + }; + + trace!("{:?}", data_sec.init); + Ok(data_sec) + } + + fn read_unvalidated(wasm: &mut crate::core::reader::WasmReader) -> Self { + let mode = wasm.read_var_u32().unwrap_validated(); + let data_sec: DataSegment = match mode { + 0 => { + // active { memory 0, offset e } + trace!("Data section: active"); + let offset = { read_constant_instructions(wasm, None, None).unwrap_validated() }; + + let byte_vec = wasm + .read_vec(|el| Ok(el.read_u8().unwrap_validated())) + .unwrap_validated(); + + // WARN: we currently don't take into consideration how we act when we are dealing with globals here + DataSegment { + mode: DataMode::Active(DataModeActive { + memory_idx: 0, + offset, + }), + init: byte_vec, + } + } + 1 => { + // passive + // A passive data segment's contents can be copied into a memory using the `memory.init` instruction + trace!("Data section: passive"); + DataSegment { + mode: DataMode::Passive, + init: wasm + .read_vec(|el| Ok(el.read_u8().unwrap_validated())) + .unwrap_validated(), + } + } + 2 => { + // mode active { memory x, offset e } + // this hasn't been yet implemented in wasm + // as per docs: + + // https://webassembly.github.io/spec/core/binary/modules.html#data-section + // The initial integer can be interpreted as a bitfield. Bit 0 indicates a passive segment, bit 1 indicates the presence of an explicit memory index for an active segment. + // In the current version of WebAssembly, at most one memory may be defined or imported in a single module, so all valid active data segments have a memory value of 0 + todo!("Data section: active - with multiple memories - NOT YET IMPLEMENTED!"); + } + _ => unreachable!(), + }; + + data_sec + } +} + +impl Debug for DataSegment { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + let mut init_str = alloc::string::String::new(); + + let iter = self.init.iter().peekable(); + // only if it's valid do we print is as a normal utf-8 char, otherwise, hex + for &byte in iter { + if let Ok(valid_char) = alloc::string::String::from_utf8(Vec::from(&[byte])) { + init_str.push_str(valid_char.as_str()); + } else { + init_str.push_str(&format!("\\x{:02x}", byte)); + } + } + + f.debug_struct("DataSegment") + .field("init", &init_str) + .field("mode", &self.mode) + .finish() + } +} + +/// +/// Usually, we'd have something like this: +/// ```wasm +/// (module +/// (memory 1) ;; memory starting with 1 page +/// (data (i32.const 0) "abc") ;; writing the array of byte "abc" in the first memory (0) at offset 0 +/// ;; for hardcoded offsets, we'll usually use i32.const because of wasm being x86 arch +/// ) +/// ``` + +/// +/// Since the span has only the start and length and acts a reference, we print the start and end (both inclusive, notice the '..=') +/// We print it in both decimal and hexadecimal so it's easy to trace in something like +impl Debug for DataMode { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + DataMode::Passive => f.debug_struct("Passive").finish(), + DataMode::Active(active_data_mode) => { + let from = active_data_mode.offset.from; + let to = active_data_mode.offset.from + active_data_mode.offset.len() - 1; + f.debug_struct("Active") + // .field("offset", format_args!("[{}..={}]", from, to)) + .field( + "offset", + &format_args!("[{}..={}] (hex = [{:X}..={:X}])", from, to, from, to), + ) + .finish() + // f. + } + } + } +} + +pub struct PassiveData { + pub init: Vec, +} + +impl Debug for PassiveData { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + let mut init_str = alloc::string::String::new(); + + let iter = self.init.iter().peekable(); + for &byte in iter { + if let Ok(valid_char) = alloc::string::String::from_utf8(Vec::from(&[byte])) { + init_str.push_str(valid_char.as_str()); + } else { + // If it's not valid UTF-8, print it as hex + init_str.push_str(&format!("\\x{:02x}", byte)); + } + } + f.debug_struct("PassiveData") + .field("init", &init_str) + .finish() + } +} diff --git a/src/core/reader/types/mod.rs b/src/core/reader/types/mod.rs index 09103742..6a3a5303 100644 --- a/src/core/reader/types/mod.rs +++ b/src/core/reader/types/mod.rs @@ -10,6 +10,7 @@ use crate::execution::assert_validated::UnwrapValidatedExt; use crate::Result; use crate::{unreachable_validated, Error}; +pub mod data; pub mod export; pub mod function_code_header; pub mod global; diff --git a/src/core/reader/types/opcode.rs b/src/core/reader/types/opcode.rs index 44ac32a3..7f0f5aac 100644 --- a/src/core/reader/types/opcode.rs +++ b/src/core/reader/types/opcode.rs @@ -174,4 +174,8 @@ pub mod fc_extensions { pub const I64_TRUNC_SAT_F32_U: u8 = 0x05; pub const I64_TRUNC_SAT_F64_S: u8 = 0x06; pub const I64_TRUNC_SAT_F64_U: u8 = 0x07; + pub const MEMORY_INIT: u8 = 0x08; + pub const DATA_DROP: u8 = 0x09; + pub const MEMORY_COPY: u8 = 0x0A; + pub const MEMORY_FILL: u8 = 0x0B; } diff --git a/src/execution/interpreter_loop.rs b/src/execution/interpreter_loop.rs index b7d846f8..72e159c6 100644 --- a/src/execution/interpreter_loop.rs +++ b/src/execution/interpreter_loop.rs @@ -16,14 +16,14 @@ use alloc::vec::Vec; use crate::{ assert_validated::UnwrapValidatedExt, core::{ - indices::{FuncIdx, GlobalIdx, LocalIdx}, + indices::{DataIdx, FuncIdx, GlobalIdx, LocalIdx}, reader::{ types::{memarg::MemArg, FuncType}, WasmReadable, WasmReader, }, }, locals::Locals, - store::Store, + store::{DataInst, Store}, value, value_stack::Stack, Limits, NumType, RuntimeError, ValType, Value, @@ -2032,6 +2032,163 @@ pub(super) fn run( trace!("Instruction: i64.trunc_sat_f64_u [{v1}] -> [{res}]"); stack.push_value(res.into()); } + // See https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#xref-syntax-instructions-syntax-instr-memory-mathsf-memory-init-x + // Copy a region from a data segment into memory + MEMORY_INIT => { + // mappings: + // n => number of bytes to copy + // s => starting pointer in the data segment + // d => destination address to copy to + let data_idx = wasm.read_var_u32().unwrap_validated() as DataIdx; + let data_init_len = store.data.get(data_idx).unwrap().data.len(); + let mem_idx = wasm.read_u8().unwrap_validated() as usize; + let mem = store.mems.get(mem_idx).unwrap_validated(); + let n: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + let s: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + let d: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + if n < 0 || s < 0 || d < 0 { + return Err(RuntimeError::MemoryAccessOutOfBounds); + } + + if s as usize + n as usize > data_init_len + || d as usize + n as usize > mem.data.len() + { + return Err(RuntimeError::MemoryAccessOutOfBounds); + } + + let data = + &store.data.get(data_idx).unwrap().data[(s as usize)..(s + n) as usize]; + store + .mems + .get_mut(mem_idx) + .unwrap_validated() + .data + .get_mut(d as usize..(d + n) as usize) + .unwrap_validated() + .copy_from_slice(data); + + trace!("Instruction: memory.init"); + } + DATA_DROP => { + // Here is debatable + // If we were to be on par with the spec we'd have to use a DataInst struct + // But since memory.init is specifically made for Passive data segments + // I thought that using DataMode would be better because we can see if the + // data segment is passive or active + + // Also, we should set data to null here (empty), which we do using an empty init vec + let data_idx = wasm.read_var_u32().unwrap_validated() as DataIdx; + store.data[data_idx] = DataInst { data: Vec::new() }; + } + // See https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#xref-syntax-instructions-syntax-instr-memory-mathsf-memory-copy + MEMORY_COPY => { + // mappings: + // n => number of bytes to copy + // s => source address to copy from + // d => destination address to copy to + let (dst, src) = ( + wasm.read_u8().unwrap_validated() as usize, + wasm.read_u8().unwrap_validated() as usize, + ); + let mem = unsafe { store.mems.get_unchecked_mut(0) }; + let n: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + let s: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + let d: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + if n < 0 || s < 0 || d < 0 { + return Err(RuntimeError::MemoryAccessOutOfBounds); + } + + if s as usize + n as usize > mem.data.len() + || d as usize + n as usize > mem.data.len() + { + return Err(RuntimeError::MemoryAccessOutOfBounds); + } + + if dst == src { + // we copy from memory X to memory X + let mem = store.mems.get_mut(src).unwrap_validated(); + mem.data + .copy_within(s as usize..(s + n) as usize, d as usize); + } else { + // we copy from one memory to another + use core::cmp::Ordering::*; + let (src_mem, dst_mem) = match dst.cmp(&src) { + Greater => { + let (left, right) = store.mems.split_at_mut(dst); + (&left[src], &mut right[0]) + } + Less => { + let (left, right) = store.mems.split_at_mut(src); + (&right[0], &mut left[dst]) + } + Equal => unreachable!(), + }; + dst_mem.data[d as usize..(d + n) as usize] + .copy_from_slice(&src_mem.data[s as usize..(s + n) as usize]); + } + + trace!("Instruction: memory.copy"); + } + // See https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#xref-syntax-instructions-syntax-instr-memory-mathsf-memory-fill + MEMORY_FILL => { + // mappings: + // n => number of bytes to update + // val => the value to set each byte to (must be < 256) + // d => the pointer to the region to update + let mem_idx = wasm.read_u8().unwrap_validated() as usize; + let mem = store.mems.get(mem_idx).unwrap_validated(); + let n: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + let val: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + // This works just fine in brave, no need to return an error, just cast to u8 (we lose the first 24 bits) + /* + ;; https://webassembly.github.io/wabt/demo/wat2wasm/ + (module + (import "js" "mem" (memory 1)) + (func (export "fill") + (memory.fill (i32.const 0) (i32.const 2777) (i32.const 100)) + ) + ) + + ;; JS + + const memory = new WebAssembly.Memory({ + initial: 1, + maximum: 1, + }); + const wasmInstance = + new WebAssembly.Instance(wasmModule, {js: {mem: memory}}); + const { fill } = wasmInstance.exports; + fill(); + console.log(new Uint8Array(memory.buffer)); + */ + + // if !(0..=255).contains(&val) { + // return Err(RuntimeError::MemoryAccessOutOfBounds); + // } + + let d: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + if n < 0 || d < 0 { + return Err(RuntimeError::MemoryAccessOutOfBounds); + } + + if n as usize + d as usize > mem.data.len() { + return Err(RuntimeError::MemoryAccessOutOfBounds); + } + + let data: Vec = vec![val as u8; (n - d) as usize]; + store + .mems + .get_mut(mem_idx) + .unwrap_validated() + .data + .get_mut(d as usize..(d + n) as usize) + .unwrap_validated() + .copy_from_slice(&data); + + trace!("Instruction: memory.fill"); + } _ => unreachable!(), } } diff --git a/src/execution/mod.rs b/src/execution/mod.rs index 3480690f..bd4414f1 100644 --- a/src/execution/mod.rs +++ b/src/execution/mod.rs @@ -5,6 +5,7 @@ use const_interpreter_loop::run_const; use function_ref::FunctionRef; use interpreter_loop::run; use locals::Locals; +use store::DataInst; use value_stack::Stack; use crate::core::reader::types::export::{Export, ExportDesc}; @@ -20,7 +21,7 @@ use crate::{RuntimeError, ValType, ValidationInfo}; // TODO pub(crate) mod assert_validated; -mod const_interpreter_loop; +pub mod const_interpreter_loop; pub mod function_ref; pub mod hooks; mod interpreter_loop; @@ -39,7 +40,7 @@ where pub wasm_bytecode: &'b [u8], types: Vec, exports: Vec, - store: Store, + pub store: Store, pub hook_set: H, } @@ -341,12 +342,68 @@ where .collect() }; - let memory_instances: Vec = validation_info + let mut memory_instances: Vec = validation_info .memories .iter() .map(|ty| MemInst::new(*ty)) .collect(); + let data_sections: Vec = validation_info + .data + .iter() + .map(|d| { + use crate::core::reader::types::data::DataMode; + if let DataMode::Active(active_data) = d.mode.clone() { + let mem_idx = active_data.memory_idx as usize; + if mem_idx != 0 { + todo!("Active data has memory_idx different than 0"); + } + assert!(memory_instances.len() > mem_idx); + + let value = { + let mut wasm = WasmReader::new(validation_info.wasm); + wasm.move_start_to(active_data.offset).unwrap_validated(); + let mut stack = Stack::new(); + run_const(wasm, &mut stack, ()); + let value = stack.peek_unknown_value(); + if value.is_none() { + panic!("No value on the stack for data segment offset"); + } + value.unwrap() + }; + + // TODO: this shouldn't be a simple value, should it? I mean it can't be, but it can also be any type of ValType + // TODO: also, do we need to forcefully make it i32? + let offset: u32 = match value { + Value::I32(val) => val, + Value::I64(val) => { + if val > u32::MAX as u64 { + panic!("i64 value for data segment offset is out of reach") + } + val as u32 + } + // TODO: implement all value types + _ => unimplemented!(), + }; + + let mem_inst = memory_instances.get_mut(mem_idx).unwrap(); + + let len = mem_inst.data.len(); + if offset as usize + d.init.len() > len { + panic!("Active data writing in memory, out of bounds"); + } + let data = mem_inst + .data + .get_mut(offset as usize..offset as usize + d.init.len()) + .unwrap(); + data.copy_from_slice(&d.init); + } + DataInst { + data: d.init.clone(), + } + }) + .collect(); + let global_instances: Vec = validation_info .globals .iter() @@ -373,6 +430,7 @@ where funcs: function_instances, mems: memory_instances, globals: global_instances, + data: data_sections, } } } diff --git a/src/execution/store.rs b/src/execution/store.rs index afc2f590..a43025c2 100644 --- a/src/execution/store.rs +++ b/src/execution/store.rs @@ -17,6 +17,7 @@ pub struct Store { pub funcs: Vec, pub mems: Vec, pub globals: Vec, + pub data: Vec, } pub struct FuncInst { @@ -63,3 +64,7 @@ pub struct GlobalInst { /// Must be of the same type as specified in `ty` pub value: Value, } + +pub struct DataInst { + pub data: Vec, +} diff --git a/src/execution/value_stack.rs b/src/execution/value_stack.rs index 6cb72fc7..31030863 100644 --- a/src/execution/value_stack.rs +++ b/src/execution/value_stack.rs @@ -79,6 +79,11 @@ impl Stack { } } + /// Returns a cloned copy of the top value on the stack, or `None` if the stack is empty + pub fn peek_unknown_value(&self) -> Option { + self.values.last().copied() + } + /// Push a value to the value stack pub fn push_value(&mut self, value: Value) { self.values.push(value); diff --git a/src/validation/code.rs b/src/validation/code.rs index 2beca25a..7105c57d 100644 --- a/src/validation/code.rs +++ b/src/validation/code.rs @@ -1,7 +1,7 @@ use alloc::vec::Vec; use core::iter; -use crate::core::indices::{FuncIdx, GlobalIdx, LocalIdx, MemIdx}; +use crate::core::indices::{DataIdx, FuncIdx, GlobalIdx, LocalIdx, MemIdx}; use crate::core::reader::section_header::{SectionHeader, SectionTy}; use crate::core::reader::span::Span; use crate::core::reader::types::global::Global; @@ -18,6 +18,7 @@ pub fn validate_code_section( type_idx_of_fn: &[usize], globals: &[Global], memories: &[MemType], + data_count: &Option, ) -> Result> { assert_eq!(section_header.ty, SectionTy::Code); @@ -46,6 +47,7 @@ pub fn validate_code_section( fn_types, type_idx_of_fn, memories, + data_count, )?; // Check if there were unread trailing instructions after the last END @@ -93,6 +95,7 @@ fn read_instructions( fn_types: &[FuncType], type_idx_of_fn: &[usize], memories: &[MemType], + data_count: &Option, ) -> Result<()> { // TODO we must terminate only if both we saw the final `end` and when we consumed all of the code span loop { @@ -716,6 +719,50 @@ fn read_instructions( stack.assert_pop_val_type(ValType::NumType(NumType::F64))?; stack.push_valtype(ValType::NumType(NumType::I64)); } + MEMORY_INIT => { + let data_idx = wasm.read_var_u32()? as DataIdx; + let mem_idx = wasm.read_u8()? as MemIdx; + if memories.len() <= mem_idx { + return Err(Error::MemoryIsNotDefined(mem_idx)); + } + if data_count.is_none() { + return Err(Error::NoDataSegments); + } + if data_count.unwrap() as usize <= data_idx { + return Err(Error::DataSegmentNotFound(data_idx)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + } + DATA_DROP => { + if data_count.is_none() { + return Err(Error::NoDataSegments); + } + let data_idx = wasm.read_var_u32()? as DataIdx; + if data_count.unwrap() as usize <= data_idx { + return Err(Error::DataSegmentNotFound(data_idx)); + } + } + MEMORY_COPY => { + let (dst, src) = (wasm.read_u8()? as usize, wasm.read_u8()? as usize); + assert!(dst == 0 && src == 0); + if memories.is_empty() { + return Err(Error::MemoryIsNotDefined(0)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + } + MEMORY_FILL => { + let mem_idx = wasm.read_u8()? as MemIdx; + if memories.len() <= mem_idx { + return Err(Error::MemoryIsNotDefined(mem_idx)); + } + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + stack.assert_pop_val_type(ValType::NumType(NumType::I32))?; + } _ => { return Err(Error::InvalidMultiByteInstr( first_instr_byte, diff --git a/src/validation/mod.rs b/src/validation/mod.rs index f544b780..96c925b2 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -3,6 +3,7 @@ use alloc::vec::Vec; use crate::core::indices::{FuncIdx, TypeIdx}; use crate::core::reader::section_header::{SectionHeader, SectionTy}; use crate::core::reader::span::Span; +use crate::core::reader::types::data::DataSegment; use crate::core::reader::types::export::Export; use crate::core::reader::types::global::Global; use crate::core::reader::types::import::Import; @@ -30,6 +31,7 @@ pub struct ValidationInfo<'bytecode> { #[allow(dead_code)] pub(crate) exports: Vec, pub(crate) func_blocks: Vec, + pub(crate) data: Vec, /// The start function which is automatically executed during instantiation pub(crate) start: Option, } @@ -124,14 +126,30 @@ pub fn validate(wasm: &[u8]) -> Result { while (skip_section(&mut wasm, &mut header)?).is_some() {} - let _: Option<()> = handle_section(&mut wasm, &mut header, SectionTy::DataCount, |_, _| { - todo!("data count section not yet supported") - })?; + // https://webassembly.github.io/spec/core/binary/modules.html#data-count-section + // As per the official documentation: + // + // The data count section is used to simplify single-pass validation. Since the data section occurs after the code section, the `memory.init` and `data.drop` and instructions would not be able to check whether the data segment index is valid until the data section is read. The data count section occurs before the code section, so a single-pass validator can use this count instead of deferring validation. + let data_count: Option = + handle_section(&mut wasm, &mut header, SectionTy::DataCount, |wasm, _| { + wasm.read_var_u32() + })?; + if data_count.is_some() { + trace!("data count: {}", data_count.unwrap()); + } while (skip_section(&mut wasm, &mut header)?).is_some() {} let func_blocks = handle_section(&mut wasm, &mut header, SectionTy::Code, |wasm, h| { - code::validate_code_section(wasm, h, &types, &functions, &globals) + code::validate_code_section( + wasm, + h, + &types, + &functions, + &globals, + &memories, + &data_count, + ) })? .unwrap_or_default(); @@ -139,9 +157,15 @@ pub fn validate(wasm: &[u8]) -> Result { while (skip_section(&mut wasm, &mut header)?).is_some() {} - let _: Option<()> = handle_section(&mut wasm, &mut header, SectionTy::Data, |_, _| { - todo!("data section not yet supported") - })?; + let data_section = handle_section(&mut wasm, &mut header, SectionTy::Data, |wasm, _| { + wasm.read_vec(DataSegment::read) + })? + .unwrap_or_default(); + + // https://webassembly.github.io/spec/core/binary/modules.html#data-count-section + if data_count.is_some() { + assert_eq!(data_count.unwrap() as usize, data_section.len()); + } while (skip_section(&mut wasm, &mut header)?).is_some() {} @@ -161,6 +185,7 @@ pub fn validate(wasm: &[u8]) -> Result { globals, exports, func_blocks, + data: data_section, start, }) } diff --git a/tests/memory_copy.rs b/tests/memory_copy.rs new file mode 100644 index 00000000..39a3bc4d --- /dev/null +++ b/tests/memory_copy.rs @@ -0,0 +1,1068 @@ +/* +# This file incorporates code from the WebAssembly testsuite, originally +# available at https://github.com/WebAssembly/testsuite. +# +# The original code is licensed under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ +use wasm::{validate, RuntimeError, RuntimeInstance}; + +macro_rules! get_func { + ($instance:ident, $func_name:expr) => { + &$instance.get_function_by_name("", $func_name).unwrap() + }; +} + +macro_rules! assert_result { + ($instance:expr, $func_name:expr, $arg:expr, $result:expr) => { + assert_eq!($result, $instance.invoke($func_name, $arg).unwrap()); + }; +} + +#[test_log::test] +fn memory_copy_test_1() { + let w = r#" + (module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (nop)) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let test = get_func!(i, "test"); + i.invoke::<(), ()>(test, ()).unwrap(); + + let load8_u = get_func!(i, "load8_u"); + let results = Vec::from([ + 0, 0, 3, 1, 4, 1, 0, 0, 0, 0, 0, 0, 7, 5, 2, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]); + for j in 0..30 { + assert_result!(i, load8_u, j as i32, results[j]); + } +} + +#[test_log::test] +fn memory_copy_test_2() { + let w = r#" + (module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 13) (i32.const 2) (i32.const 3)) + ) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let test = get_func!(i, "test"); + i.invoke::<(), ()>(test, ()).unwrap(); + + let load8_u = get_func!(i, "load8_u"); + let results = Vec::from([ + 0, 0, 3, 1, 4, 1, 0, 0, 0, 0, 0, 0, 7, 3, 1, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]); + for j in 0..30 { + assert_result!(i, load8_u, j as i32, results[j]); + } +} + +#[test_log::test] +fn memory_copy_test_3() { + let w = r#" + (module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 25) (i32.const 15) (i32.const 2)) + ) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let test = get_func!(i, "test"); + i.invoke::<(), ()>(test, ()).unwrap(); + + let load8_u = get_func!(i, "load8_u"); + let offsets = Vec::from([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, + ]); + let results = Vec::from([ + 0, 0, 3, 1, 4, 1, 0, 0, 0, 0, 0, 0, 7, 5, 2, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 3, 6, 0, 0, 0, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_copy_test_4() { + let w = r#" + (module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 13) (i32.const 25) (i32.const 3)) + ) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let test = get_func!(i, "test"); + i.invoke::<(), ()>(test, ()).unwrap(); + + let load8_u = get_func!(i, "load8_u"); + let offsets = Vec::from([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, + ]); + let results = Vec::from([ + 0, 0, 3, 1, 4, 1, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_copy_test_5() { + let w = r#" + (module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 10) (i32.const 12) (i32.const 7)) + ) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let test = get_func!(i, "test"); + i.invoke::<(), ()>(test, ()).unwrap(); + + let load8_u = get_func!(i, "load8_u"); + let offsets = Vec::from([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, + ]); + let results = Vec::from([ + 0, 0, 3, 1, 4, 1, 0, 0, 0, 0, 7, 5, 2, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_copy_test_6() { + let w = r#" + (module + (memory (export "mem") 1 1 ) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)) + ) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let run = get_func!(i, "run"); + let err = i.invoke::<(i32, i32, i32), ()>(run, (65516, 0, 40)); + if err.is_err() { + assert!(err.unwrap_err() == RuntimeError::MemoryAccessOutOfBounds); + } + + let load8_u = get_func!(i, "load8_u"); + let offsets = Vec::from([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 218, 417, 616, 815, + 1014, 1213, 1412, 1611, 1810, 2009, 2208, 2407, 2606, 2805, 3004, 3203, 3402, 3601, 3800, + 3999, 4198, 4397, 4596, 4795, 4994, 5193, 5392, 5591, 5790, 5989, 6188, 6387, 6586, 6785, + 6984, 7183, 7382, 7581, 7780, 7979, 8178, 8377, 8576, 8775, 8974, 9173, 9372, 9571, 9770, + 9969, 10168, 10367, 10566, 10765, 10964, 11163, 11362, 11561, 11760, 11959, 12158, 12357, + 12556, 12755, 12954, 13153, 13352, 13551, 13750, 13949, 14148, 14347, 14546, 14745, 14944, + 15143, 15342, 15541, 15740, 15939, 16138, 16337, 16536, 16735, 16934, 17133, 17332, 17531, + 17730, 17929, 18128, 18327, 18526, 18725, 18924, 19123, 19322, 19521, 19720, 19919, 20118, + 20317, 20516, 20715, 20914, 21113, 21312, 21511, 21710, 21909, 22108, 22307, 22506, 22705, + 22904, 23103, 23302, 23501, 23700, 23899, 24098, 24297, 24496, 24695, 24894, 25093, 25292, + 25491, 25690, 25889, 26088, 26287, 26486, 26685, 26884, 27083, 27282, 27481, 27680, 27879, + 28078, 28277, 28476, 28675, 28874, 29073, 29272, 29471, 29670, 29869, 30068, 30267, 30466, + 30665, 30864, 31063, 31262, 31461, 31660, 31859, 32058, 32257, 32456, 32655, 32854, 33053, + 33252, 33451, 33650, 33849, 34048, 34247, 34446, 34645, 34844, 35043, 35242, 35441, 35640, + 35839, 36038, 36237, 36436, 36635, 36834, 37033, 37232, 37431, 37630, 37829, 38028, 38227, + 38426, 38625, 38824, 39023, 39222, 39421, 39620, 39819, 40018, 40217, 40416, 40615, 40814, + 41013, 41212, 41411, 41610, 41809, 42008, 42207, 42406, 42605, 42804, 43003, 43202, 43401, + 43600, 43799, 43998, 44197, 44396, 44595, 44794, 44993, 45192, 45391, 45590, 45789, 45988, + 46187, 46386, 46585, 46784, 46983, 47182, 47381, 47580, 47779, 47978, 48177, 48376, 48575, + 48774, 48973, 49172, 49371, 49570, 49769, 49968, 50167, 50366, 50565, 50764, 50963, 51162, + 51361, 51560, 51759, 51958, 52157, 52356, 52555, 52754, 52953, 53152, 53351, 53550, 53749, + 53948, 54147, 54346, 54545, 54744, 54943, 55142, 55341, 55540, 55739, 55938, 56137, 56336, + 56535, 56734, 56933, 57132, 57331, 57530, 57729, 57928, 58127, 58326, 58525, 58724, 58923, + 59122, 59321, 59520, 59719, 59918, 60117, 60316, 60515, 60714, 60913, 61112, 61311, 61510, + 61709, 61908, 62107, 62306, 62505, 62704, 62903, 63102, 63301, 63500, 63699, 63898, 64097, + 64296, 64495, 64694, 64893, 65092, 65291, 65490, + ]); + let results = Vec::from([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_copy_test_7() { + let w = r#" + (module + (memory (export "mem") 1 1 ) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)) + ) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let run = get_func!(i, "run"); + let err = i.invoke::<(i32, i32, i32), ()>(run, (65515, 0, 39)); + if err.is_err() { + assert!(err.unwrap_err() == RuntimeError::MemoryAccessOutOfBounds); + } + + let load8_u = get_func!(i, "load8_u"); + let offsets = Vec::from([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 219, 418, 617, + 816, 1015, 1214, 1413, 1612, 1811, 2010, 2209, 2408, 2607, 2806, 3005, 3204, 3403, 3602, + 3801, 4000, 4199, 4398, 4597, 4796, 4995, 5194, 5393, 5592, 5791, 5990, 6189, 6388, 6587, + 6786, 6985, 7184, 7383, 7582, 7781, 7980, 8179, 8378, 8577, 8776, 8975, 9174, 9373, 9572, + 9771, 9970, 10169, 10368, 10567, 10766, 10965, 11164, 11363, 11562, 11761, 11960, 12159, + 12358, 12557, 12756, 12955, 13154, 13353, 13552, 13751, 13950, 14149, 14348, 14547, 14746, + 14945, 15144, 15343, 15542, 15741, 15940, 16139, 16338, 16537, 16736, 16935, 17134, 17333, + 17532, 17731, 17930, 18129, 18328, 18527, 18726, 18925, 19124, 19323, 19522, 19721, 19920, + 20119, 20318, 20517, 20716, 20915, 21114, 21313, 21512, 21711, 21910, 22109, 22308, 22507, + 22706, 22905, 23104, 23303, 23502, 23701, 23900, 24099, 24298, 24497, 24696, 24895, 25094, + 25293, 25492, 25691, 25890, 26089, 26288, 26487, 26686, 26885, 27084, 27283, 27482, 27681, + 27880, 28079, 28278, 28477, 28676, 28875, 29074, 29273, 29472, 29671, 29870, 30069, 30268, + 30467, 30666, 30865, 31064, 31263, 31462, 31661, 31860, 32059, 32258, 32457, 32656, 32855, + 33054, 33253, 33452, 33651, 33850, 34049, 34248, 34447, 34646, 34845, 35044, 35243, 35442, + 35641, 35840, 36039, 36238, 36437, 36636, 36835, 37034, 37233, 37432, 37631, 37830, 38029, + 38228, 38427, 38626, 38825, 39024, 39223, 39422, 39621, 39820, 40019, 40218, 40417, 40616, + 40815, 41014, 41213, 41412, 41611, 41810, 42009, 42208, 42407, 42606, 42805, 43004, 43203, + 43402, 43601, 43800, 43999, 44198, 44397, 44596, 44795, 44994, 45193, 45392, 45591, 45790, + 45989, 46188, 46387, 46586, 46785, 46984, 47183, 47382, 47581, 47780, 47979, 48178, 48377, + 48576, 48775, 48974, 49173, 49372, 49571, 49770, 49969, 50168, 50367, 50566, 50765, 50964, + 51163, 51362, 51561, 51760, 51959, 52158, 52357, 52556, 52755, 52954, 53153, 53352, 53551, + 53750, 53949, 54148, 54347, 54546, 54745, 54944, 55143, 55342, 55541, 55740, 55939, 56138, + 56337, 56536, 56735, 56934, 57133, 57332, 57531, 57730, 57929, 58128, 58327, 58526, 58725, + 58924, 59123, 59322, 59521, 59720, 59919, 60118, 60317, 60516, 60715, 60914, 61113, 61312, + 61511, 61710, 61909, 62108, 62307, 62506, 62705, 62904, 63103, 63302, 63501, 63700, 63899, + 64098, 64297, 64496, 64695, 64894, 65093, 65292, 65491, + ]); + let results = Vec::from([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_copy_test_8() { + let w = r#" + (module + (memory (export "mem") 1 1 ) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)) + ) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let run = get_func!(i, "run"); + let err = i.invoke::<(i32, i32, i32), ()>(run, (65515, 0, 39)); + if err.is_err() { + assert!(err.unwrap_err() == RuntimeError::MemoryAccessOutOfBounds); + } + + let load8_u = get_func!(i, "load8_u"); + let offsets = Vec::from([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 219, 418, 617, + 816, 1015, 1214, 1413, 1612, 1811, 2010, 2209, 2408, 2607, 2806, 3005, 3204, 3403, 3602, + 3801, 4000, 4199, 4398, 4597, 4796, 4995, 5194, 5393, 5592, 5791, 5990, 6189, 6388, 6587, + 6786, 6985, 7184, 7383, 7582, 7781, 7980, 8179, 8378, 8577, 8776, 8975, 9174, 9373, 9572, + 9771, 9970, 10169, 10368, 10567, 10766, 10965, 11164, 11363, 11562, 11761, 11960, 12159, + 12358, 12557, 12756, 12955, 13154, 13353, 13552, 13751, 13950, 14149, 14348, 14547, 14746, + 14945, 15144, 15343, 15542, 15741, 15940, 16139, 16338, 16537, 16736, 16935, 17134, 17333, + 17532, 17731, 17930, 18129, 18328, 18527, 18726, 18925, 19124, 19323, 19522, 19721, 19920, + 20119, 20318, 20517, 20716, 20915, 21114, 21313, 21512, 21711, 21910, 22109, 22308, 22507, + 22706, 22905, 23104, 23303, 23502, 23701, 23900, 24099, 24298, 24497, 24696, 24895, 25094, + 25293, 25492, 25691, 25890, 26089, 26288, 26487, 26686, 26885, 27084, 27283, 27482, 27681, + 27880, 28079, 28278, 28477, 28676, 28875, 29074, 29273, 29472, 29671, 29870, 30069, 30268, + 30467, 30666, 30865, 31064, 31263, 31462, 31661, 31860, 32059, 32258, 32457, 32656, 32855, + 33054, 33253, 33452, 33651, 33850, 34049, 34248, 34447, 34646, 34845, 35044, 35243, 35442, + 35641, 35840, 36039, 36238, 36437, 36636, 36835, 37034, 37233, 37432, 37631, 37830, 38029, + 38228, 38427, 38626, 38825, 39024, 39223, 39422, 39621, 39820, 40019, 40218, 40417, 40616, + 40815, 41014, 41213, 41412, 41611, 41810, 42009, 42208, 42407, 42606, 42805, 43004, 43203, + 43402, 43601, 43800, 43999, 44198, 44397, 44596, 44795, 44994, 45193, 45392, 45591, 45790, + 45989, 46188, 46387, 46586, 46785, 46984, 47183, 47382, 47581, 47780, 47979, 48178, 48377, + 48576, 48775, 48974, 49173, 49372, 49571, 49770, 49969, 50168, 50367, 50566, 50765, 50964, + 51163, 51362, 51561, 51760, 51959, 52158, 52357, 52556, 52755, 52954, 53153, 53352, 53551, + 53750, 53949, 54148, 54347, 54546, 54745, 54944, 55143, 55342, 55541, 55740, 55939, 56138, + 56337, 56536, 56735, 56934, 57133, 57332, 57531, 57730, 57929, 58128, 58327, 58526, 58725, + 58924, 59123, 59322, 59521, 59720, 59919, 60118, 60317, 60516, 60715, 60914, 61113, 61312, + 61511, 61710, 61909, 62108, 62307, 62506, 62705, 62904, 63103, 63302, 63501, 63700, 63899, + 64098, 64297, 64496, 64695, 64894, 65093, 65292, 65491, + ]); + let results = Vec::from([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_copy_test_9() { + let w = r#" + (module + (memory (export "mem") 1 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)) + ) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let run = get_func!(i, "run"); + let err = i.invoke::<(i32, i32, i32), ()>(run, (0, 65516, 40)); + if err.is_err() { + assert!(err.unwrap_err() == RuntimeError::MemoryAccessOutOfBounds); + } + + let load8_u = get_func!(i, "load8_u"); + let offsets = Vec::from([ + 198, 397, 596, 795, 994, 1193, 1392, 1591, 1790, 1989, 2188, 2387, 2586, 2785, 2984, 3183, + 3382, 3581, 3780, 3979, 4178, 4377, 4576, 4775, 4974, 5173, 5372, 5571, 5770, 5969, 6168, + 6367, 6566, 6765, 6964, 7163, 7362, 7561, 7760, 7959, 8158, 8357, 8556, 8755, 8954, 9153, + 9352, 9551, 9750, 9949, 10148, 10347, 10546, 10745, 10944, 11143, 11342, 11541, 11740, + 11939, 12138, 12337, 12536, 12735, 12934, 13133, 13332, 13531, 13730, 13929, 14128, 14327, + 14526, 14725, 14924, 15123, 15322, 15521, 15720, 15919, 16118, 16317, 16516, 16715, 16914, + 17113, 17312, 17511, 17710, 17909, 18108, 18307, 18506, 18705, 18904, 19103, 19302, 19501, + 19700, 19899, 20098, 20297, 20496, 20695, 20894, 21093, 21292, 21491, 21690, 21889, 22088, + 22287, 22486, 22685, 22884, 23083, 23282, 23481, 23680, 23879, 24078, 24277, 24476, 24675, + 24874, 25073, 25272, 25471, 25670, 25869, 26068, 26267, 26466, 26665, 26864, 27063, 27262, + 27461, 27660, 27859, 28058, 28257, 28456, 28655, 28854, 29053, 29252, 29451, 29650, 29849, + 30048, 30247, 30446, 30645, 30844, 31043, 31242, 31441, 31640, 31839, 32038, 32237, 32436, + 32635, 32834, 33033, 33232, 33431, 33630, 33829, 34028, 34227, 34426, 34625, 34824, 35023, + 35222, 35421, 35620, 35819, 36018, 36217, 36416, 36615, 36814, 37013, 37212, 37411, 37610, + 37809, 38008, 38207, 38406, 38605, 38804, 39003, 39202, 39401, 39600, 39799, 39998, 40197, + 40396, 40595, 40794, 40993, 41192, 41391, 41590, 41789, 41988, 42187, 42386, 42585, 42784, + 42983, 43182, 43381, 43580, 43779, 43978, 44177, 44376, 44575, 44774, 44973, 45172, 45371, + 45570, 45769, 45968, 46167, 46366, 46565, 46764, 46963, 47162, 47361, 47560, 47759, 47958, + 48157, 48356, 48555, 48754, 48953, 49152, 49351, 49550, 49749, 49948, 50147, 50346, 50545, + 50744, 50943, 51142, 51341, 51540, 51739, 51938, 52137, 52336, 52535, 52734, 52933, 53132, + 53331, 53530, 53729, 53928, 54127, 54326, 54525, 54724, 54923, 55122, 55321, 55520, 55719, + 55918, 56117, 56316, 56515, 56714, 56913, 57112, 57311, 57510, 57709, 57908, 58107, 58306, + 58505, 58704, 58903, 59102, 59301, 59500, 59699, 59898, 60097, 60296, 60495, 60694, 60893, + 61092, 61291, 61490, 61689, 61888, 62087, 62286, 62485, 62684, 62883, 63082, 63281, 63480, + 63679, 63878, 64077, 64276, 64475, 64674, 64873, 65072, 65271, 65470, 65516, 65517, 65518, + 65519, 65520, 65521, 65522, 65523, 65524, 65525, 65526, 65527, 65528, 65529, 65530, 65531, + 65532, 65533, 65534, 65535, + ]); + let results = Vec::from([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_copy_test_10() { + let w = r#" + (module + (memory (export "mem") 1 1 ) + (data (i32.const 65515) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)) + ) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let run = get_func!(i, "run"); + let err = i.invoke::<(i32, i32, i32), ()>(run, (0, 65515, 39)); + if err.is_err() { + assert!(err.unwrap_err() == RuntimeError::MemoryAccessOutOfBounds); + } + + let load8_u = get_func!(i, "load8_u"); + let offsets = Vec::from([ + 198, 397, 596, 795, 994, 1193, 1392, 1591, 1790, 1989, 2188, 2387, 2586, 2785, 2984, 3183, + 3382, 3581, 3780, 3979, 4178, 4377, 4576, 4775, 4974, 5173, 5372, 5571, 5770, 5969, 6168, + 6367, 6566, 6765, 6964, 7163, 7362, 7561, 7760, 7959, 8158, 8357, 8556, 8755, 8954, 9153, + 9352, 9551, 9750, 9949, 10148, 10347, 10546, 10745, 10944, 11143, 11342, 11541, 11740, + 11939, 12138, 12337, 12536, 12735, 12934, 13133, 13332, 13531, 13730, 13929, 14128, 14327, + 14526, 14725, 14924, 15123, 15322, 15521, 15720, 15919, 16118, 16317, 16516, 16715, 16914, + 17113, 17312, 17511, 17710, 17909, 18108, 18307, 18506, 18705, 18904, 19103, 19302, 19501, + 19700, 19899, 20098, 20297, 20496, 20695, 20894, 21093, 21292, 21491, 21690, 21889, 22088, + 22287, 22486, 22685, 22884, 23083, 23282, 23481, 23680, 23879, 24078, 24277, 24476, 24675, + 24874, 25073, 25272, 25471, 25670, 25869, 26068, 26267, 26466, 26665, 26864, 27063, 27262, + 27461, 27660, 27859, 28058, 28257, 28456, 28655, 28854, 29053, 29252, 29451, 29650, 29849, + 30048, 30247, 30446, 30645, 30844, 31043, 31242, 31441, 31640, 31839, 32038, 32237, 32436, + 32635, 32834, 33033, 33232, 33431, 33630, 33829, 34028, 34227, 34426, 34625, 34824, 35023, + 35222, 35421, 35620, 35819, 36018, 36217, 36416, 36615, 36814, 37013, 37212, 37411, 37610, + 37809, 38008, 38207, 38406, 38605, 38804, 39003, 39202, 39401, 39600, 39799, 39998, 40197, + 40396, 40595, 40794, 40993, 41192, 41391, 41590, 41789, 41988, 42187, 42386, 42585, 42784, + 42983, 43182, 43381, 43580, 43779, 43978, 44177, 44376, 44575, 44774, 44973, 45172, 45371, + 45570, 45769, 45968, 46167, 46366, 46565, 46764, 46963, 47162, 47361, 47560, 47759, 47958, + 48157, 48356, 48555, 48754, 48953, 49152, 49351, 49550, 49749, 49948, 50147, 50346, 50545, + 50744, 50943, 51142, 51341, 51540, 51739, 51938, 52137, 52336, 52535, 52734, 52933, 53132, + 53331, 53530, 53729, 53928, 54127, 54326, 54525, 54724, 54923, 55122, 55321, 55520, 55719, + 55918, 56117, 56316, 56515, 56714, 56913, 57112, 57311, 57510, 57709, 57908, 58107, 58306, + 58505, 58704, 58903, 59102, 59301, 59500, 59699, 59898, 60097, 60296, 60495, 60694, 60893, + 61092, 61291, 61490, 61689, 61888, 62087, 62286, 62485, 62684, 62883, 63082, 63281, 63480, + 63679, 63878, 64077, 64276, 64475, 64674, 64873, 65072, 65271, 65470, 65515, 65516, 65517, + 65518, 65519, 65520, 65521, 65522, 65523, 65524, 65525, 65526, 65527, 65528, 65529, 65530, + 65531, 65532, 65533, 65534, 65535, + ]); + let results = Vec::from([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_copy_test_11() { + let w = r#" + (module + (memory (export "mem") 1 1 ) + (data (i32.const 65486) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)) + ) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let run = get_func!(i, "run"); + let err = i.invoke::<(i32, i32, i32), ()>(run, (65516, 65486, 40)); + if err.is_err() { + assert!(err.unwrap_err() == RuntimeError::MemoryAccessOutOfBounds); + } + + let load8_u = get_func!(i, "load8_u"); + let offsets = Vec::from([ + 198, 397, 596, 795, 994, 1193, 1392, 1591, 1790, 1989, 2188, 2387, 2586, 2785, 2984, 3183, + 3382, 3581, 3780, 3979, 4178, 4377, 4576, 4775, 4974, 5173, 5372, 5571, 5770, 5969, 6168, + 6367, 6566, 6765, 6964, 7163, 7362, 7561, 7760, 7959, 8158, 8357, 8556, 8755, 8954, 9153, + 9352, 9551, 9750, 9949, 10148, 10347, 10546, 10745, 10944, 11143, 11342, 11541, 11740, + 11939, 12138, 12337, 12536, 12735, 12934, 13133, 13332, 13531, 13730, 13929, 14128, 14327, + 14526, 14725, 14924, 15123, 15322, 15521, 15720, 15919, 16118, 16317, 16516, 16715, 16914, + 17113, 17312, 17511, 17710, 17909, 18108, 18307, 18506, 18705, 18904, 19103, 19302, 19501, + 19700, 19899, 20098, 20297, 20496, 20695, 20894, 21093, 21292, 21491, 21690, 21889, 22088, + 22287, 22486, 22685, 22884, 23083, 23282, 23481, 23680, 23879, 24078, 24277, 24476, 24675, + 24874, 25073, 25272, 25471, 25670, 25869, 26068, 26267, 26466, 26665, 26864, 27063, 27262, + 27461, 27660, 27859, 28058, 28257, 28456, 28655, 28854, 29053, 29252, 29451, 29650, 29849, + 30048, 30247, 30446, 30645, 30844, 31043, 31242, 31441, 31640, 31839, 32038, 32237, 32436, + 32635, 32834, 33033, 33232, 33431, 33630, 33829, 34028, 34227, 34426, 34625, 34824, 35023, + 35222, 35421, 35620, 35819, 36018, 36217, 36416, 36615, 36814, 37013, 37212, 37411, 37610, + 37809, 38008, 38207, 38406, 38605, 38804, 39003, 39202, 39401, 39600, 39799, 39998, 40197, + 40396, 40595, 40794, 40993, 41192, 41391, 41590, 41789, 41988, 42187, 42386, 42585, 42784, + 42983, 43182, 43381, 43580, 43779, 43978, 44177, 44376, 44575, 44774, 44973, 45172, 45371, + 45570, 45769, 45968, 46167, 46366, 46565, 46764, 46963, 47162, 47361, 47560, 47759, 47958, + 48157, 48356, 48555, 48754, 48953, 49152, 49351, 49550, 49749, 49948, 50147, 50346, 50545, + 50744, 50943, 51142, 51341, 51540, 51739, 51938, 52137, 52336, 52535, 52734, 52933, 53132, + 53331, 53530, 53729, 53928, 54127, 54326, 54525, 54724, 54923, 55122, 55321, 55520, 55719, + 55918, 56117, 56316, 56515, 56714, 56913, 57112, 57311, 57510, 57709, 57908, 58107, 58306, + 58505, 58704, 58903, 59102, 59301, 59500, 59699, 59898, 60097, 60296, 60495, 60694, 60893, + 61092, 61291, 61490, 61689, 61888, 62087, 62286, 62485, 62684, 62883, 63082, 63281, 63480, + 63679, 63878, 64077, 64276, 64475, 64674, 64873, 65072, 65271, 65470, 65486, 65487, 65488, + 65489, 65490, 65491, 65492, 65493, 65494, 65495, 65496, 65497, 65498, 65499, 65500, 65501, + 65502, 65503, 65504, 65505, + ]); + let results = Vec::from([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_copy_test_12() { + let w = r#" + (module + (memory (export "mem") 1 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)) + ) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let run = get_func!(i, "run"); + let err = i.invoke::<(i32, i32, i32), ()>(run, (65486, 65516, 40)); + if err.is_err() { + assert!(err.unwrap_err() == RuntimeError::MemoryAccessOutOfBounds); + } + + let load8_u = get_func!(i, "load8_u"); + let offsets = Vec::from([ + 198, 397, 596, 795, 994, 1193, 1392, 1591, 1790, 1989, 2188, 2387, 2586, 2785, 2984, 3183, + 3382, 3581, 3780, 3979, 4178, 4377, 4576, 4775, 4974, 5173, 5372, 5571, 5770, 5969, 6168, + 6367, 6566, 6765, 6964, 7163, 7362, 7561, 7760, 7959, 8158, 8357, 8556, 8755, 8954, 9153, + 9352, 9551, 9750, 9949, 10148, 10347, 10546, 10745, 10944, 11143, 11342, 11541, 11740, + 11939, 12138, 12337, 12536, 12735, 12934, 13133, 13332, 13531, 13730, 13929, 14128, 14327, + 14526, 14725, 14924, 15123, 15322, 15521, 15720, 15919, 16118, 16317, 16516, 16715, 16914, + 17113, 17312, 17511, 17710, 17909, 18108, 18307, 18506, 18705, 18904, 19103, 19302, 19501, + 19700, 19899, 20098, 20297, 20496, 20695, 20894, 21093, 21292, 21491, 21690, 21889, 22088, + 22287, 22486, 22685, 22884, 23083, 23282, 23481, 23680, 23879, 24078, 24277, 24476, 24675, + 24874, 25073, 25272, 25471, 25670, 25869, 26068, 26267, 26466, 26665, 26864, 27063, 27262, + 27461, 27660, 27859, 28058, 28257, 28456, 28655, 28854, 29053, 29252, 29451, 29650, 29849, + 30048, 30247, 30446, 30645, 30844, 31043, 31242, 31441, 31640, 31839, 32038, 32237, 32436, + 32635, 32834, 33033, 33232, 33431, 33630, 33829, 34028, 34227, 34426, 34625, 34824, 35023, + 35222, 35421, 35620, 35819, 36018, 36217, 36416, 36615, 36814, 37013, 37212, 37411, 37610, + 37809, 38008, 38207, 38406, 38605, 38804, 39003, 39202, 39401, 39600, 39799, 39998, 40197, + 40396, 40595, 40794, 40993, 41192, 41391, 41590, 41789, 41988, 42187, 42386, 42585, 42784, + 42983, 43182, 43381, 43580, 43779, 43978, 44177, 44376, 44575, 44774, 44973, 45172, 45371, + 45570, 45769, 45968, 46167, 46366, 46565, 46764, 46963, 47162, 47361, 47560, 47759, 47958, + 48157, 48356, 48555, 48754, 48953, 49152, 49351, 49550, 49749, 49948, 50147, 50346, 50545, + 50744, 50943, 51142, 51341, 51540, 51739, 51938, 52137, 52336, 52535, 52734, 52933, 53132, + 53331, 53530, 53729, 53928, 54127, 54326, 54525, 54724, 54923, 55122, 55321, 55520, 55719, + 55918, 56117, 56316, 56515, 56714, 56913, 57112, 57311, 57510, 57709, 57908, 58107, 58306, + 58505, 58704, 58903, 59102, 59301, 59500, 59699, 59898, 60097, 60296, 60495, 60694, 60893, + 61092, 61291, 61490, 61689, 61888, 62087, 62286, 62485, 62684, 62883, 63082, 63281, 63480, + 63679, 63878, 64077, 64276, 64475, 64674, 64873, 65072, 65271, 65470, 65516, 65517, 65518, + 65519, 65520, 65521, 65522, 65523, 65524, 65525, 65526, 65527, 65528, 65529, 65530, 65531, + 65532, 65533, 65534, 65535, + ]); + let results = Vec::from([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_copy_test_13() { + let w = r#" + (module + (memory (export "mem") 1 1 ) + (data (i32.const 65506) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)) + ) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let run = get_func!(i, "run"); + let err = i.invoke::<(i32, i32, i32), ()>(run, (65516, 65506, 40)); + if err.is_err() { + assert!(err.unwrap_err() == RuntimeError::MemoryAccessOutOfBounds); + } + + let load8_u = get_func!(i, "load8_u"); + let offsets = Vec::from([ + 198, 397, 596, 795, 994, 1193, 1392, 1591, 1790, 1989, 2188, 2387, 2586, 2785, 2984, 3183, + 3382, 3581, 3780, 3979, 4178, 4377, 4576, 4775, 4974, 5173, 5372, 5571, 5770, 5969, 6168, + 6367, 6566, 6765, 6964, 7163, 7362, 7561, 7760, 7959, 8158, 8357, 8556, 8755, 8954, 9153, + 9352, 9551, 9750, 9949, 10148, 10347, 10546, 10745, 10944, 11143, 11342, 11541, 11740, + 11939, 12138, 12337, 12536, 12735, 12934, 13133, 13332, 13531, 13730, 13929, 14128, 14327, + 14526, 14725, 14924, 15123, 15322, 15521, 15720, 15919, 16118, 16317, 16516, 16715, 16914, + 17113, 17312, 17511, 17710, 17909, 18108, 18307, 18506, 18705, 18904, 19103, 19302, 19501, + 19700, 19899, 20098, 20297, 20496, 20695, 20894, 21093, 21292, 21491, 21690, 21889, 22088, + 22287, 22486, 22685, 22884, 23083, 23282, 23481, 23680, 23879, 24078, 24277, 24476, 24675, + 24874, 25073, 25272, 25471, 25670, 25869, 26068, 26267, 26466, 26665, 26864, 27063, 27262, + 27461, 27660, 27859, 28058, 28257, 28456, 28655, 28854, 29053, 29252, 29451, 29650, 29849, + 30048, 30247, 30446, 30645, 30844, 31043, 31242, 31441, 31640, 31839, 32038, 32237, 32436, + 32635, 32834, 33033, 33232, 33431, 33630, 33829, 34028, 34227, 34426, 34625, 34824, 35023, + 35222, 35421, 35620, 35819, 36018, 36217, 36416, 36615, 36814, 37013, 37212, 37411, 37610, + 37809, 38008, 38207, 38406, 38605, 38804, 39003, 39202, 39401, 39600, 39799, 39998, 40197, + 40396, 40595, 40794, 40993, 41192, 41391, 41590, 41789, 41988, 42187, 42386, 42585, 42784, + 42983, 43182, 43381, 43580, 43779, 43978, 44177, 44376, 44575, 44774, 44973, 45172, 45371, + 45570, 45769, 45968, 46167, 46366, 46565, 46764, 46963, 47162, 47361, 47560, 47759, 47958, + 48157, 48356, 48555, 48754, 48953, 49152, 49351, 49550, 49749, 49948, 50147, 50346, 50545, + 50744, 50943, 51142, 51341, 51540, 51739, 51938, 52137, 52336, 52535, 52734, 52933, 53132, + 53331, 53530, 53729, 53928, 54127, 54326, 54525, 54724, 54923, 55122, 55321, 55520, 55719, + 55918, 56117, 56316, 56515, 56714, 56913, 57112, 57311, 57510, 57709, 57908, 58107, 58306, + 58505, 58704, 58903, 59102, 59301, 59500, 59699, 59898, 60097, 60296, 60495, 60694, 60893, + 61092, 61291, 61490, 61689, 61888, 62087, 62286, 62485, 62684, 62883, 63082, 63281, 63480, + 63679, 63878, 64077, 64276, 64475, 64674, 64873, 65072, 65271, 65470, 65506, 65507, 65508, + 65509, 65510, 65511, 65512, 65513, 65514, 65515, 65516, 65517, 65518, 65519, 65520, 65521, + 65522, 65523, 65524, 65525, + ]); + let results = Vec::from([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_copy_test_14() { + let w = r#" + (module + (memory (export "mem") 1 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)) + ) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let run = get_func!(i, "run"); + let err = i.invoke::<(i32, i32, i32), ()>(run, (65506, 65516, 40)); + if err.is_err() { + assert!(err.unwrap_err() == RuntimeError::MemoryAccessOutOfBounds); + } + + let load8_u = get_func!(i, "load8_u"); + let offsets = Vec::from([ + 198, 397, 596, 795, 994, 1193, 1392, 1591, 1790, 1989, 2188, 2387, 2586, 2785, 2984, 3183, + 3382, 3581, 3780, 3979, 4178, 4377, 4576, 4775, 4974, 5173, 5372, 5571, 5770, 5969, 6168, + 6367, 6566, 6765, 6964, 7163, 7362, 7561, 7760, 7959, 8158, 8357, 8556, 8755, 8954, 9153, + 9352, 9551, 9750, 9949, 10148, 10347, 10546, 10745, 10944, 11143, 11342, 11541, 11740, + 11939, 12138, 12337, 12536, 12735, 12934, 13133, 13332, 13531, 13730, 13929, 14128, 14327, + 14526, 14725, 14924, 15123, 15322, 15521, 15720, 15919, 16118, 16317, 16516, 16715, 16914, + 17113, 17312, 17511, 17710, 17909, 18108, 18307, 18506, 18705, 18904, 19103, 19302, 19501, + 19700, 19899, 20098, 20297, 20496, 20695, 20894, 21093, 21292, 21491, 21690, 21889, 22088, + 22287, 22486, 22685, 22884, 23083, 23282, 23481, 23680, 23879, 24078, 24277, 24476, 24675, + 24874, 25073, 25272, 25471, 25670, 25869, 26068, 26267, 26466, 26665, 26864, 27063, 27262, + 27461, 27660, 27859, 28058, 28257, 28456, 28655, 28854, 29053, 29252, 29451, 29650, 29849, + 30048, 30247, 30446, 30645, 30844, 31043, 31242, 31441, 31640, 31839, 32038, 32237, 32436, + 32635, 32834, 33033, 33232, 33431, 33630, 33829, 34028, 34227, 34426, 34625, 34824, 35023, + 35222, 35421, 35620, 35819, 36018, 36217, 36416, 36615, 36814, 37013, 37212, 37411, 37610, + 37809, 38008, 38207, 38406, 38605, 38804, 39003, 39202, 39401, 39600, 39799, 39998, 40197, + 40396, 40595, 40794, 40993, 41192, 41391, 41590, 41789, 41988, 42187, 42386, 42585, 42784, + 42983, 43182, 43381, 43580, 43779, 43978, 44177, 44376, 44575, 44774, 44973, 45172, 45371, + 45570, 45769, 45968, 46167, 46366, 46565, 46764, 46963, 47162, 47361, 47560, 47759, 47958, + 48157, 48356, 48555, 48754, 48953, 49152, 49351, 49550, 49749, 49948, 50147, 50346, 50545, + 50744, 50943, 51142, 51341, 51540, 51739, 51938, 52137, 52336, 52535, 52734, 52933, 53132, + 53331, 53530, 53729, 53928, 54127, 54326, 54525, 54724, 54923, 55122, 55321, 55520, 55719, + 55918, 56117, 56316, 56515, 56714, 56913, 57112, 57311, 57510, 57709, 57908, 58107, 58306, + 58505, 58704, 58903, 59102, 59301, 59500, 59699, 59898, 60097, 60296, 60495, 60694, 60893, + 61092, 61291, 61490, 61689, 61888, 62087, 62286, 62485, 62684, 62883, 63082, 63281, 63480, + 63679, 63878, 64077, 64276, 64475, 64674, 64873, 65072, 65271, 65470, 65516, 65517, 65518, + 65519, 65520, 65521, 65522, 65523, 65524, 65525, 65526, 65527, 65528, 65529, 65530, 65531, + 65532, 65533, 65534, 65535, + ]); + let results = Vec::from([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_copy_test_15() { + let w = r#" + (module + (memory (export "mem") 1 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)) + ) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let run = get_func!(i, "run"); + let err = i.invoke::<(i32, i32, i32), ()>(run, (65516, 65516, 40)); + if err.is_err() { + assert!(err.unwrap_err() == RuntimeError::MemoryAccessOutOfBounds); + } + + let load8_u = get_func!(i, "load8_u"); + let offsets = Vec::from([ + 198, 397, 596, 795, 994, 1193, 1392, 1591, 1790, 1989, 2188, 2387, 2586, 2785, 2984, 3183, + 3382, 3581, 3780, 3979, 4178, 4377, 4576, 4775, 4974, 5173, 5372, 5571, 5770, 5969, 6168, + 6367, 6566, 6765, 6964, 7163, 7362, 7561, 7760, 7959, 8158, 8357, 8556, 8755, 8954, 9153, + 9352, 9551, 9750, 9949, 10148, 10347, 10546, 10745, 10944, 11143, 11342, 11541, 11740, + 11939, 12138, 12337, 12536, 12735, 12934, 13133, 13332, 13531, 13730, 13929, 14128, 14327, + 14526, 14725, 14924, 15123, 15322, 15521, 15720, 15919, 16118, 16317, 16516, 16715, 16914, + 17113, 17312, 17511, 17710, 17909, 18108, 18307, 18506, 18705, 18904, 19103, 19302, 19501, + 19700, 19899, 20098, 20297, 20496, 20695, 20894, 21093, 21292, 21491, 21690, 21889, 22088, + 22287, 22486, 22685, 22884, 23083, 23282, 23481, 23680, 23879, 24078, 24277, 24476, 24675, + 24874, 25073, 25272, 25471, 25670, 25869, 26068, 26267, 26466, 26665, 26864, 27063, 27262, + 27461, 27660, 27859, 28058, 28257, 28456, 28655, 28854, 29053, 29252, 29451, 29650, 29849, + 30048, 30247, 30446, 30645, 30844, 31043, 31242, 31441, 31640, 31839, 32038, 32237, 32436, + 32635, 32834, 33033, 33232, 33431, 33630, 33829, 34028, 34227, 34426, 34625, 34824, 35023, + 35222, 35421, 35620, 35819, 36018, 36217, 36416, 36615, 36814, 37013, 37212, 37411, 37610, + 37809, 38008, 38207, 38406, 38605, 38804, 39003, 39202, 39401, 39600, 39799, 39998, 40197, + 40396, 40595, 40794, 40993, 41192, 41391, 41590, 41789, 41988, 42187, 42386, 42585, 42784, + 42983, 43182, 43381, 43580, 43779, 43978, 44177, 44376, 44575, 44774, 44973, 45172, 45371, + 45570, 45769, 45968, 46167, 46366, 46565, 46764, 46963, 47162, 47361, 47560, 47759, 47958, + 48157, 48356, 48555, 48754, 48953, 49152, 49351, 49550, 49749, 49948, 50147, 50346, 50545, + 50744, 50943, 51142, 51341, 51540, 51739, 51938, 52137, 52336, 52535, 52734, 52933, 53132, + 53331, 53530, 53729, 53928, 54127, 54326, 54525, 54724, 54923, 55122, 55321, 55520, 55719, + 55918, 56117, 56316, 56515, 56714, 56913, 57112, 57311, 57510, 57709, 57908, 58107, 58306, + 58505, 58704, 58903, 59102, 59301, 59500, 59699, 59898, 60097, 60296, 60495, 60694, 60893, + 61092, 61291, 61490, 61689, 61888, 62087, 62286, 62485, 62684, 62883, 63082, 63281, 63480, + 63679, 63878, 64077, 64276, 64475, 64674, 64873, 65072, 65271, 65470, 65516, 65517, 65518, + 65519, 65520, 65521, 65522, 65523, 65524, 65525, 65526, 65527, 65528, 65529, 65530, 65531, + 65532, 65533, 65534, 65535, + ]); + let results = Vec::from([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_copy_test_16() { + let w = r#" + (module + (memory (export "mem") 1 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)) + ) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let run = get_func!(i, "run"); + let err = i.invoke::<(i32, i32, i32), ()>(run, (0, 65516, 4294963200_u32 as i32)); + if err.is_err() { + assert!(err.unwrap_err() == RuntimeError::MemoryAccessOutOfBounds); + } + + let load8_u = get_func!(i, "load8_u"); + let offsets = Vec::from([ + 198, 397, 596, 795, 994, 1193, 1392, 1591, 1790, 1989, 2188, 2387, 2586, 2785, 2984, 3183, + 3382, 3581, 3780, 3979, 4178, 4377, 4576, 4775, 4974, 5173, 5372, 5571, 5770, 5969, 6168, + 6367, 6566, 6765, 6964, 7163, 7362, 7561, 7760, 7959, 8158, 8357, 8556, 8755, 8954, 9153, + 9352, 9551, 9750, 9949, 10148, 10347, 10546, 10745, 10944, 11143, 11342, 11541, 11740, + 11939, 12138, 12337, 12536, 12735, 12934, 13133, 13332, 13531, 13730, 13929, 14128, 14327, + 14526, 14725, 14924, 15123, 15322, 15521, 15720, 15919, 16118, 16317, 16516, 16715, 16914, + 17113, 17312, 17511, 17710, 17909, 18108, 18307, 18506, 18705, 18904, 19103, 19302, 19501, + 19700, 19899, 20098, 20297, 20496, 20695, 20894, 21093, 21292, 21491, 21690, 21889, 22088, + 22287, 22486, 22685, 22884, 23083, 23282, 23481, 23680, 23879, 24078, 24277, 24476, 24675, + 24874, 25073, 25272, 25471, 25670, 25869, 26068, 26267, 26466, 26665, 26864, 27063, 27262, + 27461, 27660, 27859, 28058, 28257, 28456, 28655, 28854, 29053, 29252, 29451, 29650, 29849, + 30048, 30247, 30446, 30645, 30844, 31043, 31242, 31441, 31640, 31839, 32038, 32237, 32436, + 32635, 32834, 33033, 33232, 33431, 33630, 33829, 34028, 34227, 34426, 34625, 34824, 35023, + 35222, 35421, 35620, 35819, 36018, 36217, 36416, 36615, 36814, 37013, 37212, 37411, 37610, + 37809, 38008, 38207, 38406, 38605, 38804, 39003, 39202, 39401, 39600, 39799, 39998, 40197, + 40396, 40595, 40794, 40993, 41192, 41391, 41590, 41789, 41988, 42187, 42386, 42585, 42784, + 42983, 43182, 43381, 43580, 43779, 43978, 44177, 44376, 44575, 44774, 44973, 45172, 45371, + 45570, 45769, 45968, 46167, 46366, 46565, 46764, 46963, 47162, 47361, 47560, 47759, 47958, + 48157, 48356, 48555, 48754, 48953, 49152, 49351, 49550, 49749, 49948, 50147, 50346, 50545, + 50744, 50943, 51142, 51341, 51540, 51739, 51938, 52137, 52336, 52535, 52734, 52933, 53132, + 53331, 53530, 53729, 53928, 54127, 54326, 54525, 54724, 54923, 55122, 55321, 55520, 55719, + 55918, 56117, 56316, 56515, 56714, 56913, 57112, 57311, 57510, 57709, 57908, 58107, 58306, + 58505, 58704, 58903, 59102, 59301, 59500, 59699, 59898, 60097, 60296, 60495, 60694, 60893, + 61092, 61291, 61490, 61689, 61888, 62087, 62286, 62485, 62684, 62883, 63082, 63281, 63480, + 63679, 63878, 64077, 64276, 64475, 64674, 64873, 65072, 65271, 65470, 65516, 65517, 65518, + 65519, 65520, 65521, 65522, 65523, 65524, 65525, 65526, 65527, 65528, 65529, 65530, 65531, + 65532, 65533, 65534, 65535, + ]); + let results = Vec::from([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_copy_test_17() { + let w = r#" + (module + (memory (export "mem") 1 1 ) + (data (i32.const 61440) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)) + ) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let run = get_func!(i, "run"); + let err = i.invoke::<(i32, i32, i32), ()>(run, (65516, 61440, 4294967040_u32 as i32)); + if err.is_err() { + assert!(err.unwrap_err() == RuntimeError::MemoryAccessOutOfBounds); + } + + let load8_u = get_func!(i, "load8_u"); + let offsets = Vec::from([ + 198, 397, 596, 795, 994, 1193, 1392, 1591, 1790, 1989, 2188, 2387, 2586, 2785, 2984, 3183, + 3382, 3581, 3780, 3979, 4178, 4377, 4576, 4775, 4974, 5173, 5372, 5571, 5770, 5969, 6168, + 6367, 6566, 6765, 6964, 7163, 7362, 7561, 7760, 7959, 8158, 8357, 8556, 8755, 8954, 9153, + 9352, 9551, 9750, 9949, 10148, 10347, 10546, 10745, 10944, 11143, 11342, 11541, 11740, + 11939, 12138, 12337, 12536, 12735, 12934, 13133, 13332, 13531, 13730, 13929, 14128, 14327, + 14526, 14725, 14924, 15123, 15322, 15521, 15720, 15919, 16118, 16317, 16516, 16715, 16914, + 17113, 17312, 17511, 17710, 17909, 18108, 18307, 18506, 18705, 18904, 19103, 19302, 19501, + 19700, 19899, 20098, 20297, 20496, 20695, 20894, 21093, 21292, 21491, 21690, 21889, 22088, + 22287, 22486, 22685, 22884, 23083, 23282, 23481, 23680, 23879, 24078, 24277, 24476, 24675, + 24874, 25073, 25272, 25471, 25670, 25869, 26068, 26267, 26466, 26665, 26864, 27063, 27262, + 27461, 27660, 27859, 28058, 28257, 28456, 28655, 28854, 29053, 29252, 29451, 29650, 29849, + 30048, 30247, 30446, 30645, 30844, 31043, 31242, 31441, 31640, 31839, 32038, 32237, 32436, + 32635, 32834, 33033, 33232, 33431, 33630, 33829, 34028, 34227, 34426, 34625, 34824, 35023, + 35222, 35421, 35620, 35819, 36018, 36217, 36416, 36615, 36814, 37013, 37212, 37411, 37610, + 37809, 38008, 38207, 38406, 38605, 38804, 39003, 39202, 39401, 39600, 39799, 39998, 40197, + 40396, 40595, 40794, 40993, 41192, 41391, 41590, 41789, 41988, 42187, 42386, 42585, 42784, + 42983, 43182, 43381, 43580, 43779, 43978, 44177, 44376, 44575, 44774, 44973, 45172, 45371, + 45570, 45769, 45968, 46167, 46366, 46565, 46764, 46963, 47162, 47361, 47560, 47759, 47958, + 48157, 48356, 48555, 48754, 48953, 49152, 49351, 49550, 49749, 49948, 50147, 50346, 50545, + 50744, 50943, 51142, 51341, 51540, 51739, 51938, 52137, 52336, 52535, 52734, 52933, 53132, + 53331, 53530, 53729, 53928, 54127, 54326, 54525, 54724, 54923, 55122, 55321, 55520, 55719, + 55918, 56117, 56316, 56515, 56714, 56913, 57112, 57311, 57510, 57709, 57908, 58107, 58306, + 58505, 58704, 58903, 59102, 59301, 59500, 59699, 59898, 60097, 60296, 60495, 60694, 60893, + 61092, 61291, 61440, 61441, 61442, 61443, 61444, 61445, 61446, 61447, 61448, 61449, 61450, + 61451, 61452, 61453, 61454, 61455, 61456, 61457, 61458, 61459, 61510, 61709, 61908, 62107, + 62306, 62505, 62704, 62903, 63102, 63301, 63500, 63699, 63898, 64097, 64296, 64495, 64694, + 64893, 65092, 65291, 65490, + ]); + let results = Vec::from([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} diff --git a/tests/memory_fill.rs b/tests/memory_fill.rs new file mode 100644 index 00000000..5980a61c --- /dev/null +++ b/tests/memory_fill.rs @@ -0,0 +1,54 @@ +/* +# This file incorporates code from the WebAssembly testsuite, originally +# available at https://github.com/WebAssembly/testsuite. +# +# The original code is licensed under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +// use core::slice::SlicePattern; + +use wasm::{validate, RuntimeInstance}; + +macro_rules! get_func { + ($instance:ident, $func_name:expr) => { + &$instance.get_function_by_name("", $func_name).unwrap() + }; +} + +#[test_log::test] +fn memory_fill() { + let w = r#" + (module + (memory 1) + (func (export "fill") + (memory.fill (i32.const 0) (i32.const 2777) (i32.const 100)) + ) + ) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + let fill = get_func!(i, "fill"); + i.invoke::<(), ()>(fill, ()).unwrap(); + let mem = &i.store.mems[0]; + assert!(mem.data.as_slice()[0..105] + .eq_ignore_ascii_case(&vec![vec![217u8; 100], vec![0u8; 5]].concat())) +} + +// we need control flow implemented for any of these tests +#[ignore = "not yet implemented"] +#[test_log::test] +fn memory_fill_with_control_flow() { + assert!(1 == 2); +} diff --git a/tests/memory_init.rs b/tests/memory_init.rs new file mode 100644 index 00000000..eccd641c --- /dev/null +++ b/tests/memory_init.rs @@ -0,0 +1,318 @@ +/* +# This file incorporates code from the WebAssembly testsuite, originally +# available at https://github.com/WebAssembly/testsuite. +# +# The original code is licensed under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ +use wasm::Error as GeneralError; +use wasm::{validate, RuntimeError, RuntimeInstance}; + +macro_rules! get_func { + ($instance:ident, $func_name:expr) => { + &$instance.get_function_by_name("", $func_name).unwrap() + }; +} + +macro_rules! assert_result { + ($instance:expr, $func:expr, $arg:expr, $result:expr) => { + assert_eq!($result, $instance.invoke($func, $arg).unwrap()); + }; +} + +macro_rules! assert_error { + ($instance:expr, $func:expr, $arg:expr, $ret_type:ty, $invoke_param_type:ty, $invoke_return_type:ty, $err_type:expr) => { + let val: $ret_type = + $instance.invoke::<$invoke_param_type, $invoke_return_type>($func, $arg); + assert!(val.is_err()); + assert!(val.unwrap_err() == $err_type); + }; +} + +#[test_log::test] +fn memory_init_test_1() { + let w = r#" +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data "\02\07\01\08") + (data (i32.const 12) "\07\05\02\03\06") + (data "\05\09\02\07\06") + (func (export "test") + (nop)) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + let test = get_func!(i, "test"); + i.invoke::<(), ()>(test, ()).unwrap(); + + let load8_u = get_func!(i, "load8_u"); + + let offsets = Vec::from([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, + ]); + let results = Vec::from([ + 0, 0, 3, 1, 4, 1, 0, 0, 0, 0, 0, 0, 7, 5, 2, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_init_test_2() { + let w = r#" +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data "\02\07\01\08") + (data (i32.const 12) "\07\05\02\03\06") + (data "\05\09\02\07\06") + (func (export "test") + (memory.init 1 (i32.const 7) (i32.const 0) (i32.const 4))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + let test = get_func!(i, "test"); + i.invoke::<(), ()>(test, ()).unwrap(); + + let load8_u = get_func!(i, "load8_u"); + + let offsets = Vec::from([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, + ]); + let results = Vec::from([ + 0, 0, 3, 1, 4, 1, 0, 2, 7, 1, 8, 0, 7, 5, 2, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_init_test_3() { + let w = r#" +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data "\02\07\01\08") + (data (i32.const 12) "\07\05\02\03\06") + (data "\05\09\02\07\06") + (func (export "test") + (memory.init 3 (i32.const 15) (i32.const 1) (i32.const 3))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + let test = get_func!(i, "test"); + i.invoke::<(), ()>(test, ()).unwrap(); + + let load8_u = get_func!(i, "load8_u"); + + let offsets = Vec::from([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, + ]); + let results = Vec::from([ + 0, 0, 3, 1, 4, 1, 0, 0, 0, 0, 0, 0, 7, 5, 2, 9, 2, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_init_test_4() { + let w = r#" +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data "\02\07\01\08") + (data (i32.const 12) "\07\05\02\03\06") + (data "\05\09\02\07\06") + (func (export "test") + (memory.init 1 (i32.const 7) (i32.const 0) (i32.const 4)) + (data.drop 1) + (memory.init 3 (i32.const 15) (i32.const 1) (i32.const 3)) + (data.drop 3) + (memory.copy (i32.const 20) (i32.const 15) (i32.const 5)) + (memory.copy (i32.const 21) (i32.const 29) (i32.const 1)) + (memory.copy (i32.const 24) (i32.const 10) (i32.const 1)) + (memory.copy (i32.const 13) (i32.const 11) (i32.const 4)) + (memory.copy (i32.const 19) (i32.const 20) (i32.const 5))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + let test = get_func!(i, "test"); + i.invoke::<(), ()>(test, ()).unwrap(); + + let load8_u = get_func!(i, "load8_u"); + + let offsets = Vec::from([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, + ]); + let results = Vec::from([ + 0, 0, 3, 1, 4, 1, 0, 2, 7, 1, 8, 0, 7, 0, 7, 5, 2, 7, 0, 9, 0, 7, 0, 8, 8, 0, 0, 0, 0, 0, + ]); + for j in 0..offsets.len() { + assert_result!(i, load8_u, offsets[j], results[j]); + } +} + +#[test_log::test] +fn memory_init_test_5() { + let w = r#" + (module + (func (export "test") + (data.drop 0))) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let res = validate(&wasm_bytes); + assert!(res.err().unwrap() == GeneralError::DataSegmentNotFound(0)); +} + +#[test_log::test] +fn memory_init_test_6() { + let w = r#" + (module + (memory 1) + (data "\37") + (func (export "test") + (data.drop 4))) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + + let res = validate(&wasm_bytes); + assert!(res.err().unwrap() == GeneralError::DataSegmentNotFound(4)); +} + +#[test_log::test] +fn memory_init_test_7() { + let w = r#" +(module + (memory 1) + (data "\37") + (func (export "test") + (data.drop 0) + (data.drop 0))) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + let test = get_func!(i, "test"); + i.invoke::<(), ()>(test, ()).unwrap(); +} + +#[test_log::test] +fn memory_init_test_8() { + let w = r#" +(module + (memory 1) + (data "\37") + (func (export "test") + (data.drop 0) + (memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 1)))) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + assert_error!(i, get_func!(i, "test"), (), Result<(), RuntimeError>, (), (), RuntimeError::MemoryAccessOutOfBounds); +} + +#[test_log::test] +fn memory_init_test_9() { + let w = r#" +(module + (memory 1) + (data (i32.const 0) "\37") + (func (export "test") + (memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 1)))) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + assert_error!(i, get_func!(i, "test"), (), Result<(), RuntimeError>, (), (), RuntimeError::MemoryAccessOutOfBounds); +} + +#[test_log::test] +fn memory_init_test_10() { + let w = r#" + (module + (func (export "test") + (memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1)))) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + + let res = validate(&wasm_bytes); + assert!(res.err().unwrap() == GeneralError::MemoryIsNotDefined(0)); +} + +#[test_log::test] +fn memory_init_test_11() { + let w = r#" + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1)))) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + + let res = validate(&wasm_bytes); + assert!(res.err().unwrap() == GeneralError::DataSegmentNotFound(1)); +} + +#[test_log::test] +fn memory_init_test_12() { + let w = r#" +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i32.const 0) (i32.const 1)) + (memory.init 0 (i32.const 1) (i32.const 0) (i32.const 1)))) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + let test = get_func!(i, "test"); + i.invoke::<(), ()>(test, ()).unwrap(); +} + +#[test_log::test] +fn memory_init_test_13() { + let w = r#" +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1234) (i32.const 0) (i32.const 5)))) + "#; + let wasm_bytes = wat::parse_str(w).unwrap(); + let validation_info = validate(&wasm_bytes).unwrap(); + let mut i = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + assert_error!(i, get_func!(i, "test"), (), Result<(), RuntimeError>, (), (), RuntimeError::MemoryAccessOutOfBounds); +} From 84711938ea8a67333e4c4aa29d484e82e6d1610f Mon Sep 17 00:00:00 2001 From: nerodesu017 Date: Mon, 18 Nov 2024 16:08:28 +0200 Subject: [PATCH 12/12] refactor: polished memory opcodes Signed-off-by: nerodesu017 --- src/core/reader/types/data.rs | 5 +- src/execution/interpreter_loop.rs | 90 +++++++++++-------------------- src/execution/mod.rs | 2 +- src/validation/code.rs | 7 ++- 4 files changed, 41 insertions(+), 63 deletions(-) diff --git a/src/core/reader/types/data.rs b/src/core/reader/types/data.rs index f32eb08d..8451f717 100644 --- a/src/core/reader/types/data.rs +++ b/src/core/reader/types/data.rs @@ -3,7 +3,10 @@ use core::fmt::{Debug, Formatter}; use alloc::{format, vec::Vec}; use crate::{ - core::{indices::MemIdx, reader::{span::Span, WasmReadable}}, + core::{ + indices::MemIdx, + reader::{span::Span, WasmReadable}, + }, read_constant_expression::read_constant_instructions, }; diff --git a/src/execution/interpreter_loop.rs b/src/execution/interpreter_loop.rs index 72e159c6..2b85886f 100644 --- a/src/execution/interpreter_loop.rs +++ b/src/execution/interpreter_loop.rs @@ -723,11 +723,7 @@ pub(super) fn run( let mem = store.mems.get_mut(mem_idx).unwrap_validated(); let delta: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); - let upper_limit = if mem.ty.limits.max.is_some() { - mem.ty.limits.max.unwrap() - } else { - Limits::MAX_MEM_PAGES - }; + let upper_limit = mem.ty.limits.max.unwrap_or(Limits::MAX_MEM_BYTES); let pushed_value = if delta < 0 || delta as u32 + mem.size() as u32 > upper_limit { stack.push_value((-1).into()); -1 @@ -2046,24 +2042,25 @@ pub(super) fn run( let n: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); let s: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); let d: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); - if n < 0 || s < 0 || d < 0 { - return Err(RuntimeError::MemoryAccessOutOfBounds); - } - if s as usize + n as usize > data_init_len - || d as usize + n as usize > mem.data.len() - { - return Err(RuntimeError::MemoryAccessOutOfBounds); - } + let final_src_offset = (n as usize) + .checked_add(s as usize) + .filter(|&res| res <= data_init_len) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + let final_dst_offset = (n as usize) + .checked_add(d as usize) + .filter(|&res| res <= mem.data.len()) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; let data = - &store.data.get(data_idx).unwrap().data[(s as usize)..(s + n) as usize]; + &store.data.get(data_idx).unwrap().data[(s as usize)..final_src_offset]; store .mems .get_mut(mem_idx) .unwrap_validated() .data - .get_mut(d as usize..(d + n) as usize) + .get_mut(d as usize..final_dst_offset) .unwrap_validated() .copy_from_slice(data); @@ -2090,25 +2087,26 @@ pub(super) fn run( wasm.read_u8().unwrap_validated() as usize, wasm.read_u8().unwrap_validated() as usize, ); - let mem = unsafe { store.mems.get_unchecked_mut(0) }; let n: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); let s: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); let d: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); - if n < 0 || s < 0 || d < 0 { - return Err(RuntimeError::MemoryAccessOutOfBounds); - } - if s as usize + n as usize > mem.data.len() - || d as usize + n as usize > mem.data.len() - { - return Err(RuntimeError::MemoryAccessOutOfBounds); - } + let final_src_offset = (n as usize) + .checked_add(s as usize) + .filter(|&res| res <= store.mems.get(src).unwrap_validated().data.len()) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; + + // let final_dst_offset = + (n as usize) + .checked_add(d as usize) + .filter(|&res| res <= store.mems.get(dst).unwrap_validated().data.len()) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; if dst == src { // we copy from memory X to memory X let mem = store.mems.get_mut(src).unwrap_validated(); mem.data - .copy_within(s as usize..(s + n) as usize, d as usize); + .copy_within(s as usize..final_src_offset, d as usize); } else { // we copy from one memory to another use core::cmp::Ordering::*; @@ -2140,42 +2138,16 @@ pub(super) fn run( let n: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); let val: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); - // This works just fine in brave, no need to return an error, just cast to u8 (we lose the first 24 bits) - /* - ;; https://webassembly.github.io/wabt/demo/wat2wasm/ - (module - (import "js" "mem" (memory 1)) - (func (export "fill") - (memory.fill (i32.const 0) (i32.const 2777) (i32.const 100)) - ) - ) - - ;; JS - - const memory = new WebAssembly.Memory({ - initial: 1, - maximum: 1, - }); - const wasmInstance = - new WebAssembly.Instance(wasmModule, {js: {mem: memory}}); - const { fill } = wasmInstance.exports; - fill(); - console.log(new Uint8Array(memory.buffer)); - */ - - // if !(0..=255).contains(&val) { - // return Err(RuntimeError::MemoryAccessOutOfBounds); - // } + if !(0..=255).contains(&val) { + warn!("Value for memory.fill does not fit in a byte ({val})"); + } let d: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); - if n < 0 || d < 0 { - return Err(RuntimeError::MemoryAccessOutOfBounds); - } - - if n as usize + d as usize > mem.data.len() { - return Err(RuntimeError::MemoryAccessOutOfBounds); - } + let final_dst_offset = (n as usize) + .checked_add(d as usize) + .filter(|&res| res <= mem.data.len()) + .ok_or(RuntimeError::MemoryAccessOutOfBounds)?; let data: Vec = vec![val as u8; (n - d) as usize]; store @@ -2183,7 +2155,7 @@ pub(super) fn run( .get_mut(mem_idx) .unwrap_validated() .data - .get_mut(d as usize..(d + n) as usize) + .get_mut(d as usize..final_dst_offset) .unwrap_validated() .copy_from_slice(&data); diff --git a/src/execution/mod.rs b/src/execution/mod.rs index bd4414f1..b4a79fdc 100644 --- a/src/execution/mod.rs +++ b/src/execution/mod.rs @@ -354,7 +354,7 @@ where .map(|d| { use crate::core::reader::types::data::DataMode; if let DataMode::Active(active_data) = d.mode.clone() { - let mem_idx = active_data.memory_idx as usize; + let mem_idx = active_data.memory_idx; if mem_idx != 0 { todo!("Active data has memory_idx different than 0"); } diff --git a/src/validation/code.rs b/src/validation/code.rs index 7105c57d..078e8057 100644 --- a/src/validation/code.rs +++ b/src/validation/code.rs @@ -483,6 +483,7 @@ fn read_instructions( } MEMORY_SIZE => { let mem_idx = wasm.read_u8()? as MemIdx; + assert!(mem_idx == 0, "Multiple memories are not supported"); if memories.len() <= mem_idx { return Err(Error::MemoryIsNotDefined(mem_idx)); } @@ -490,6 +491,7 @@ fn read_instructions( } MEMORY_GROW => { let mem_idx = wasm.read_u8()? as MemIdx; + assert!(mem_idx == 0, "Multiple memories are not supported"); if memories.len() <= mem_idx { return Err(Error::MemoryIsNotDefined(mem_idx)); } @@ -676,7 +678,6 @@ fn read_instructions( stack.push_valtype(ValType::NumType(NumType::F64)); } - FC_EXTENSIONS => { let Ok(second_instr_byte) = wasm.read_u8() else { @@ -722,6 +723,7 @@ fn read_instructions( MEMORY_INIT => { let data_idx = wasm.read_var_u32()? as DataIdx; let mem_idx = wasm.read_u8()? as MemIdx; + assert!(mem_idx == 0, "Multiple memories are not supported"); if memories.len() <= mem_idx { return Err(Error::MemoryIsNotDefined(mem_idx)); } @@ -746,7 +748,7 @@ fn read_instructions( } MEMORY_COPY => { let (dst, src) = (wasm.read_u8()? as usize, wasm.read_u8()? as usize); - assert!(dst == 0 && src == 0); + assert!(dst == 0 && src == 0, "Multiple memories are not supported"); if memories.is_empty() { return Err(Error::MemoryIsNotDefined(0)); } @@ -756,6 +758,7 @@ fn read_instructions( } MEMORY_FILL => { let mem_idx = wasm.read_u8()? as MemIdx; + assert!(mem_idx == 0, "Multiple memories are not supported"); if memories.len() <= mem_idx { return Err(Error::MemoryIsNotDefined(mem_idx)); }