From c1fa38129b58f796135cf742cc2038f7b379ff05 Mon Sep 17 00:00:00 2001 From: Matt Leon Date: Tue, 17 Mar 2026 13:05:40 -0400 Subject: [PATCH 01/14] Add test for native to wasm value conversions Signed-off-by: Matt Leon --- include/proxy-wasm/wasm_vm.h | 4 +- src/wamr/wamr.cc | 4 ++ src/wasmtime/wasmtime.cc | 5 ++ test/BUILD | 1 + test/runtime_test.cc | 28 +++++++++ test/test_data/BUILD | 5 ++ test/test_data/bigendian.rs | 116 +++++++++++++++++++++++++++++++++++ test/utility.h | 16 +++++ 8 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 test/test_data/bigendian.rs diff --git a/include/proxy-wasm/wasm_vm.h b/include/proxy-wasm/wasm_vm.h index 9fc435d35..b3f485d2b 100644 --- a/include/proxy-wasm/wasm_vm.h +++ b/include/proxy-wasm/wasm_vm.h @@ -67,12 +67,14 @@ template using WasmCallVoid = std::function>; template using WasmCallWord = std::function>; +using WasmCall_WWlfd = std::function; #define FOR_ALL_WASM_VM_EXPORTS(_f) \ _f(proxy_wasm::WasmCallVoid<0>) _f(proxy_wasm::WasmCallVoid<1>) _f(proxy_wasm::WasmCallVoid<2>) \ _f(proxy_wasm::WasmCallVoid<3>) _f(proxy_wasm::WasmCallVoid<5>) \ _f(proxy_wasm::WasmCallWord<0>) _f(proxy_wasm::WasmCallWord<1>) \ - _f(proxy_wasm::WasmCallWord<2>) _f(proxy_wasm::WasmCallWord<3>) + _f(proxy_wasm::WasmCallWord<2>) _f(proxy_wasm::WasmCallWord<3>) \ + _f(proxy_wasm::WasmCall_WWlfd) // These are templates and its helper for constructing signatures of functions callbacks from Wasm // VMs. diff --git a/src/wamr/wamr.cc b/src/wamr/wamr.cc index 8eef73590..274074aa9 100644 --- a/src/wamr/wamr.cc +++ b/src/wamr/wamr.cc @@ -462,6 +462,10 @@ template <> void assignVal(uint64_t t, wasm_val_t &val) { val.kind = WASM_I64; val.of.i64 = static_cast(t); } +template <> void assignVal(float t, wasm_val_t &val) { + val.kind = WASM_F32; + val.of.f32 = static_cast(t); +} template <> void assignVal(double t, wasm_val_t &val) { val.kind = WASM_F64; val.of.f64 = t; diff --git a/src/wasmtime/wasmtime.cc b/src/wasmtime/wasmtime.cc index a72a0361d..674bb2ff8 100644 --- a/src/wasmtime/wasmtime.cc +++ b/src/wasmtime/wasmtime.cc @@ -435,6 +435,10 @@ template <> void assignVal(uint64_t t, wasm_val_t &val) { val.kind = WASM_I64; val.of.i64 = static_cast(t); } +template <> void assignVal(float t, wasm_val_t &val) { + val.kind = WASM_F32; + val.of.f32 = t; +} template <> void assignVal(double t, wasm_val_t &val) { val.kind = WASM_F64; val.of.f64 = t; @@ -459,6 +463,7 @@ template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i3 template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i64(); }; template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i64(); }; template <> auto convertArgToValTypePtr() { return wasm_valtype_new_f64(); }; +template <> auto convertArgToValTypePtr() { return wasm_valtype_new_f32(); }; template T convertValueTypeToArg(wasm_val_t val); template <> uint32_t convertValueTypeToArg(wasm_val_t val) { diff --git a/test/BUILD b/test/BUILD index 97e70558a..2cc607d29 100644 --- a/test/BUILD +++ b/test/BUILD @@ -71,6 +71,7 @@ cc_test( srcs = ["runtime_test.cc"], data = [ "//test/test_data:callback.wasm", + "//test/test_data:bigendian.wasm", "//test/test_data:clock.wasm", "//test/test_data:resource_limits.wasm", "//test/test_data:trap.wasm", diff --git a/test/runtime_test.cc b/test/runtime_test.cc index b9304aaaf..b1efdc50e 100644 --- a/test/runtime_test.cc +++ b/test/runtime_test.cc @@ -190,6 +190,34 @@ TEST_P(TestVm, Trap2) { } } +TEST_P(TestVm, BigendianValuesTranslatedProperly) { + auto source = readTestWasmFile("bigendian.wasm"); + ASSERT_FALSE(source.empty()); + auto wasm = TestWasm(std::move(vm_)); + ASSERT_TRUE(wasm.load(source, false)); + ASSERT_TRUE(wasm.initialize()); + auto *context = dynamic_cast(wasm.vm_context()); + ASSERT_NE(context, nullptr); + WasmCall_WWlfd test_bigendian; + wasm.wasm_vm()->getFunction("test_bigendian", &test_bigendian); + WasmCall_WWlfd test_bigendian_negatives; + wasm.wasm_vm()->getFunction("test_bigendian_negatives", &test_bigendian_negatives); + WasmCallWord<0> test_buffer_from_wasm; + wasm.wasm_vm()->getFunction("test_bigendian_buffer_from_wasm", &test_buffer_from_wasm); + WasmCallWord<0> test_buffer_from_host; + wasm.wasm_vm()->getFunction("test_bigendian_buffer_from_host", &test_buffer_from_host); + + ASSERT_FALSE(test_bigendian(context, 3333333333U, 11111111111111111111UL, 1111, 1111111111)); + ASSERT_FALSE( + test_bigendian_negatives(context, -1111111111, -1111111111111111111, -1111, -1111111111)); + + ASSERT_FALSE(test_buffer_from_wasm(context)); + context->isLogged("hello from little-endian wasm land!"); + + context->setBuffer(0, "hello from host land endianness!"); + ASSERT_FALSE(test_buffer_from_host(context)); +} + class TestCounterContext : public TestContext { public: TestCounterContext(WasmBase *wasm) : TestContext(wasm) {} diff --git a/test/test_data/BUILD b/test/test_data/BUILD index e5ecd439e..3db5b4592 100644 --- a/test/test_data/BUILD +++ b/test/test_data/BUILD @@ -57,6 +57,11 @@ wasm_rust_binary( srcs = ["trap.rs"], ) +wasm_rust_binary( + name = "bigendian.wasm", + srcs = ["bigendian.rs"], +) + wasm_rust_binary( name = "resource_limits.wasm", srcs = ["resource_limits.rs"], diff --git a/test/test_data/bigendian.rs b/test/test_data/bigendian.rs new file mode 100644 index 000000000..034cacbda --- /dev/null +++ b/test/test_data/bigendian.rs @@ -0,0 +1,116 @@ +// Copyright 2026 Google LLC +// +// 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. + +#[no_mangle] +pub extern "C" fn proxy_abi_version_0_2_0() {} + +#[no_mangle] +pub extern "C" fn proxy_on_memory_allocate(size: usize) -> *mut u8 { + let mut vec: Vec = Vec::with_capacity(size); + unsafe { + vec.set_len(size); + } + let slice = vec.into_boxed_slice(); + Box::into_raw(slice) as *mut u8 +} + +#[no_mangle] +pub extern "C" fn test_bigendian(uint32: u32, uint64: u64, float32: f32, float64: f64) -> i32 { + if uint32 != 3333333333 { + println!("uint32 was not little-endian: {}", uint32); + return 1; + } + if uint64 != 11111111111111111111 { + println!("uint64 was not little-endian: {}", uint32); + return 2; + } + if float32 < 1110.0 || float32 > 1112.0 { + println!("float32 was not little-endian: {}", float32); + return 3; + } + if float64 < 1111111110.0 || float64 > 1111111112.0 { + println!("float64 was not little-endian: {}", float64); + return 4; + } + return 0; +} + +#[no_mangle] +pub extern "C" fn test_bigendian_negatives( + int32: i32, + int64: i64, + float32: f32, + float64: f64, +) -> i32 { + if int32 != -1111111111 { + return 1; + } + if int64 != -1111111111111111111 { + return 2; + } + if float32 > -1110.0 || float32 < -1112.0 { + return 3; + } + if float64 > -1111111110.0 || float64 < -1111111112.0 { + return 4; + } + return 0; +} + +extern "C" { + fn proxy_log(level: u32, message_data: *const u8, message_size: usize) -> bool; +} + +#[no_mangle] +pub extern "C" fn test_bigendian_buffer_from_wasm() -> bool { + let message = "hello from little-endian wasm land!"; + unsafe { + match proxy_log(/*info*/ 2, message.as_ptr(), message.len()) { + false => false, + status => panic!("unexpected status: {}", status as u32), + } + } +} + +extern "C" { + fn proxy_get_buffer_bytes( + buffer_type: u32, + start: usize, + max_size: usize, + return_buffer_data: *mut *mut u8, + return_buffer_size: *mut usize, + ) -> bool; +} + +#[no_mangle] +pub extern "C" fn test_bigendian_buffer_from_host() -> bool { + let mut return_data: *mut u8 = std::ptr::null_mut(); + let mut return_size: usize = 0; + unsafe { + match proxy_get_buffer_bytes(0, 0, 10, &mut return_data, &mut return_size) { + false => { + if return_data.is_null() { + panic!("return_data was null"); + } + let result = + String::from_utf8(Vec::from_raw_parts(return_data, return_size, return_size)); + if result.unwrap() != "hello from host land endianness" { + panic!("message did not match expectation"); + } + false + } + status => panic!("unexpected status: {}", status as u32), + } + } +} diff --git a/test/utility.h b/test/utility.h index 0eb743037..4a70d1f71 100644 --- a/test/utility.h +++ b/test/utility.h @@ -13,6 +13,7 @@ // limitations under the License. #include "gtest/gtest.h" +#include #include #include #include @@ -109,6 +110,20 @@ class TestContext : public ContextBase { return unimplemented(); } + void setBuffer(int32_t buffer_type, std::string buffer) { + auto [it, inserted] = buffers_.emplace(buffer_type, std::make_unique()); + std::unique_ptr arr = std::make_unique(buffer.size() + 1); + std::strcpy(arr.get(), buffer.c_str()); + it->second->set(std::move(arr), buffer.size() + 1); + } + + BufferInterface *getBuffer(WasmBufferType type) override { + if (auto it = buffers_.find(static_cast(type)); it != buffers_.end()) { + return it->second.get(); + } + return nullptr; + } + bool isLogEmpty() { return log_.empty(); } bool isLogged(std::string_view message) { return log_.find(message) != std::string::npos; } @@ -133,6 +148,7 @@ class TestContext : public ContextBase { void set_allow_on_headers_stop_iteration(bool allow) { allow_on_headers_stop_iteration_ = allow; } private: + std::unordered_map> buffers_; std::string log_; static std::string global_log_; }; From 66059ad8f028ba3f9ba4b7ba8185e80191db8b73 Mon Sep 17 00:00:00 2001 From: Matt Leon Date: Tue, 17 Mar 2026 14:40:51 -0400 Subject: [PATCH 02/14] Add remaining runtime support for float type Signed-off-by: Matt Leon --- src/v8/v8.cc | 1 + src/wamr/wamr.cc | 1 + src/wasmedge/wasmedge.cc | 2 ++ 3 files changed, 4 insertions(+) diff --git a/src/v8/v8.cc b/src/v8/v8.cc index 25b6623ca..4642de7a6 100644 --- a/src/v8/v8.cc +++ b/src/v8/v8.cc @@ -245,6 +245,7 @@ template <> constexpr auto convertArgToValKind() { return wasm::ValKin template <> constexpr auto convertArgToValKind() { return wasm::ValKind::I64; }; template <> constexpr auto convertArgToValKind() { return wasm::ValKind::I64; }; template <> constexpr auto convertArgToValKind() { return wasm::ValKind::F64; }; +template <> constexpr auto convertArgToValKind() { return wasm::ValKind::F32; }; template constexpr auto convertArgsTupleToValTypesImpl(std::index_sequence /*comptime*/) { diff --git a/src/wamr/wamr.cc b/src/wamr/wamr.cc index 274074aa9..60a8a89bb 100644 --- a/src/wamr/wamr.cc +++ b/src/wamr/wamr.cc @@ -490,6 +490,7 @@ template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i3 template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i64(); }; template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i64(); }; template <> auto convertArgToValTypePtr() { return wasm_valtype_new_f64(); }; +template <> auto convertArgToValTypePtr() { return wasm_valtype_new_f32(); }; template T convertValueTypeToArg(wasm_val_t val); template <> uint32_t convertValueTypeToArg(wasm_val_t val) { diff --git a/src/wasmedge/wasmedge.cc b/src/wasmedge/wasmedge.cc index acfe15b3c..7ef31c7c6 100644 --- a/src/wasmedge/wasmedge.cc +++ b/src/wasmedge/wasmedge.cc @@ -49,6 +49,7 @@ template <> WasmEdge_Value makeVal(uint64_t t) { return WasmEdge_ValueGenI64(static_cast(t)); } template <> WasmEdge_Value makeVal(double t) { return WasmEdge_ValueGenF64(t); } +template <> WasmEdge_Value makeVal(float t) { return WasmEdge_ValueGenF32(t); } // Helper function to print values. std::string printValue(const WasmEdge_Value &value) { @@ -144,6 +145,7 @@ template <> WasmEdge_ValType convArgToValType() { return WasmEdge_ValT template <> WasmEdge_ValType convArgToValType() { return WasmEdge_ValTypeGenI64(); } template <> WasmEdge_ValType convArgToValType() { return WasmEdge_ValTypeGenI64(); } template <> WasmEdge_ValType convArgToValType() { return WasmEdge_ValTypeGenF64(); } +template <> WasmEdge_ValType convArgToValType() { return WasmEdge_ValTypeGenF32(); } // Helper templates to convert valtype to arg. template T convValTypeToArg(WasmEdge_Value val); From 6d85d345da27de463f285e14088a5ade7b53527d Mon Sep 17 00:00:00 2001 From: Matt Leon Date: Tue, 17 Mar 2026 15:09:08 -0400 Subject: [PATCH 03/14] Rename bigendian test to more generic "arg passing" Signed-off-by: Matt Leon --- test/BUILD | 2 +- test/runtime_test.cc | 24 ++++++------ test/test_data/BUILD | 4 +- .../{bigendian.rs => arg_passing.rs} | 38 ++++++++++++------- 4 files changed, 39 insertions(+), 29 deletions(-) rename test/test_data/{bigendian.rs => arg_passing.rs} (74%) diff --git a/test/BUILD b/test/BUILD index 2cc607d29..5ac871522 100644 --- a/test/BUILD +++ b/test/BUILD @@ -71,7 +71,7 @@ cc_test( srcs = ["runtime_test.cc"], data = [ "//test/test_data:callback.wasm", - "//test/test_data:bigendian.wasm", + "//test/test_data:arg_passing.wasm", "//test/test_data:clock.wasm", "//test/test_data:resource_limits.wasm", "//test/test_data:trap.wasm", diff --git a/test/runtime_test.cc b/test/runtime_test.cc index b1efdc50e..2bcfd4383 100644 --- a/test/runtime_test.cc +++ b/test/runtime_test.cc @@ -190,31 +190,31 @@ TEST_P(TestVm, Trap2) { } } -TEST_P(TestVm, BigendianValuesTranslatedProperly) { - auto source = readTestWasmFile("bigendian.wasm"); +TEST_P(TestVm, PassingValuesAcrossWasmBoundary) { + auto source = readTestWasmFile("arg_passing.wasm"); ASSERT_FALSE(source.empty()); auto wasm = TestWasm(std::move(vm_)); ASSERT_TRUE(wasm.load(source, false)); ASSERT_TRUE(wasm.initialize()); auto *context = dynamic_cast(wasm.vm_context()); ASSERT_NE(context, nullptr); - WasmCall_WWlfd test_bigendian; - wasm.wasm_vm()->getFunction("test_bigendian", &test_bigendian); - WasmCall_WWlfd test_bigendian_negatives; - wasm.wasm_vm()->getFunction("test_bigendian_negatives", &test_bigendian_negatives); + WasmCall_WWlfd test_primitives; + wasm.wasm_vm()->getFunction("test_primitives", &test_primitives); + WasmCall_WWlfd test_negative_primitives; + wasm.wasm_vm()->getFunction("test_negative_primitives", &test_negative_primitives); WasmCallWord<0> test_buffer_from_wasm; - wasm.wasm_vm()->getFunction("test_bigendian_buffer_from_wasm", &test_buffer_from_wasm); + wasm.wasm_vm()->getFunction("test_buffer_from_wasm", &test_buffer_from_wasm); WasmCallWord<0> test_buffer_from_host; - wasm.wasm_vm()->getFunction("test_bigendian_buffer_from_host", &test_buffer_from_host); + wasm.wasm_vm()->getFunction("test_buffer_from_host", &test_buffer_from_host); - ASSERT_FALSE(test_bigendian(context, 3333333333U, 11111111111111111111UL, 1111, 1111111111)); + ASSERT_FALSE(test_primitives(context, 3333333333U, 11111111111111111111UL, 1111, 1111111111)); ASSERT_FALSE( - test_bigendian_negatives(context, -1111111111, -1111111111111111111, -1111, -1111111111)); + test_negative_primitives(context, -1111111111, -1111111111111111111, -1111, -1111111111)); ASSERT_FALSE(test_buffer_from_wasm(context)); - context->isLogged("hello from little-endian wasm land!"); + context->isLogged("hello from wasm land!"); - context->setBuffer(0, "hello from host land endianness!"); + context->setBuffer(0, "hello from host land!"); ASSERT_FALSE(test_buffer_from_host(context)); } diff --git a/test/test_data/BUILD b/test/test_data/BUILD index 3db5b4592..4e27933af 100644 --- a/test/test_data/BUILD +++ b/test/test_data/BUILD @@ -58,8 +58,8 @@ wasm_rust_binary( ) wasm_rust_binary( - name = "bigendian.wasm", - srcs = ["bigendian.rs"], + name = "arg_passing.wasm", + srcs = ["arg_passing.rs"], ) wasm_rust_binary( diff --git a/test/test_data/bigendian.rs b/test/test_data/arg_passing.rs similarity index 74% rename from test/test_data/bigendian.rs rename to test/test_data/arg_passing.rs index 034cacbda..3cc4ad064 100644 --- a/test/test_data/bigendian.rs +++ b/test/test_data/arg_passing.rs @@ -25,56 +25,66 @@ pub extern "C" fn proxy_on_memory_allocate(size: usize) -> *mut u8 { Box::into_raw(slice) as *mut u8 } +extern "C" { + fn proxy_log(level: u32, message_data: *const u8, message_size: usize) -> bool; +} + +fn log(message: &str) { + unsafe { + proxy_log(/*error*/ 4, message.as_bytes().as_ptr(), message.len()); + } +} + #[no_mangle] -pub extern "C" fn test_bigendian(uint32: u32, uint64: u64, float32: f32, float64: f64) -> i32 { +pub extern "C" fn test_primitives(uint32: u32, uint64: u64, float32: f32, float64: f64) -> i32 { if uint32 != 3333333333 { - println!("uint32 was not little-endian: {}", uint32); + log(&format!("unexpected uint32 value: {}", uint32)); return 1; } if uint64 != 11111111111111111111 { - println!("uint64 was not little-endian: {}", uint32); + log(&format!("unexpected uint64 value: {}", uint32)); return 2; } if float32 < 1110.0 || float32 > 1112.0 { - println!("float32 was not little-endian: {}", float32); + log(&format!("unexpected float32 value: {}", float32)); return 3; } if float64 < 1111111110.0 || float64 > 1111111112.0 { - println!("float64 was not little-endian: {}", float64); + log(&format!("unexpected float64 value: {}", float64)); return 4; } return 0; } #[no_mangle] -pub extern "C" fn test_bigendian_negatives( +pub extern "C" fn test_negative_primitives( int32: i32, int64: i64, float32: f32, float64: f64, ) -> i32 { if int32 != -1111111111 { + log(&format!("unexpected int32 value: {}", int32)); return 1; } if int64 != -1111111111111111111 { + log(&format!("unexpected int64 value: {}", int32)); return 2; } if float32 > -1110.0 || float32 < -1112.0 { + log(&format!("unexpected float32 value: {}", float32)); return 3; } if float64 > -1111111110.0 || float64 < -1111111112.0 { + log(&format!("unexpected float64 value: {}", float64)); return 4; } return 0; } -extern "C" { - fn proxy_log(level: u32, message_data: *const u8, message_size: usize) -> bool; -} - #[no_mangle] -pub extern "C" fn test_bigendian_buffer_from_wasm() -> bool { - let message = "hello from little-endian wasm land!"; +pub extern "C" fn test_buffer_from_wasm() -> bool { + let message = "hello from wasm land!"; unsafe { match proxy_log(/*info*/ 2, message.as_ptr(), message.len()) { false => false, @@ -94,7 +104,7 @@ extern "C" { } #[no_mangle] -pub extern "C" fn test_bigendian_buffer_from_host() -> bool { +pub extern "C" fn test_buffer_from_host() -> bool { let mut return_data: *mut u8 = std::ptr::null_mut(); let mut return_size: usize = 0; unsafe { @@ -105,7 +115,7 @@ pub extern "C" fn test_bigendian_buffer_from_host() -> bool { } let result = String::from_utf8(Vec::from_raw_parts(return_data, return_size, return_size)); - if result.unwrap() != "hello from host land endianness" { + if result.unwrap() != "hello from host land" { panic!("message did not match expectation"); } false From 8d53459dc1312f399adca8272c90081c20db859f Mon Sep 17 00:00:00 2001 From: Matt Leon Date: Tue, 17 Mar 2026 15:12:16 -0400 Subject: [PATCH 04/14] Run buildifier Signed-off-by: Matt Leon --- test/BUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/BUILD b/test/BUILD index 5ac871522..be52da3f1 100644 --- a/test/BUILD +++ b/test/BUILD @@ -70,8 +70,8 @@ cc_test( timeout = "long", srcs = ["runtime_test.cc"], data = [ - "//test/test_data:callback.wasm", "//test/test_data:arg_passing.wasm", + "//test/test_data:callback.wasm", "//test/test_data:clock.wasm", "//test/test_data:resource_limits.wasm", "//test/test_data:trap.wasm", From e703840fa9290ed9648cd7c24ddb15b57f88a306 Mon Sep 17 00:00:00 2001 From: Matt Leon Date: Tue, 17 Mar 2026 17:15:58 -0400 Subject: [PATCH 05/14] std::strcpy -> strcpy Signed-off-by: Matt Leon --- test/utility.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utility.h b/test/utility.h index 4a70d1f71..357aabc2a 100644 --- a/test/utility.h +++ b/test/utility.h @@ -113,7 +113,7 @@ class TestContext : public ContextBase { void setBuffer(int32_t buffer_type, std::string buffer) { auto [it, inserted] = buffers_.emplace(buffer_type, std::make_unique()); std::unique_ptr arr = std::make_unique(buffer.size() + 1); - std::strcpy(arr.get(), buffer.c_str()); + strcpy(arr.get(), buffer.c_str()); it->second->set(std::move(arr), buffer.size() + 1); } From 66ea3303cb0bc4a07a2e431366717d317c04d7e3 Mon Sep 17 00:00:00 2001 From: Matt Leon Date: Wed, 18 Mar 2026 16:40:45 -0400 Subject: [PATCH 06/14] Expand arg passing test with host and wasm return values There's enough test cases that this warrants being run in parallel via its own test target. Signed-off-by: Matt Leon --- include/proxy-wasm/wasm_vm.h | 9 ++- test/BUILD | 16 ++++ test/arg_passing_test.cc | 137 ++++++++++++++++++++++++++++++++++ test/runtime_test.cc | 28 ------- test/test_data/arg_passing.rs | 99 +++++++++++++++++------- test/utility.h | 2 + 6 files changed, 235 insertions(+), 56 deletions(-) create mode 100644 test/arg_passing_test.cc diff --git a/include/proxy-wasm/wasm_vm.h b/include/proxy-wasm/wasm_vm.h index b3f485d2b..ef341cb45 100644 --- a/include/proxy-wasm/wasm_vm.h +++ b/include/proxy-wasm/wasm_vm.h @@ -67,14 +67,21 @@ template using WasmCallVoid = std::function>; template using WasmCallWord = std::function>; +// Callback used to test arg passing from host to wasm. using WasmCall_WWlfd = std::function; +// Types used to test return values. Floats are passed as parameters as these +// do not conflict with ProxyWasm ABI signatures. +using WasmCall_lf = std::function; +using WasmCall_fff = std::function; +using WasmCall_dfff = std::function; #define FOR_ALL_WASM_VM_EXPORTS(_f) \ _f(proxy_wasm::WasmCallVoid<0>) _f(proxy_wasm::WasmCallVoid<1>) _f(proxy_wasm::WasmCallVoid<2>) \ _f(proxy_wasm::WasmCallVoid<3>) _f(proxy_wasm::WasmCallVoid<5>) \ _f(proxy_wasm::WasmCallWord<0>) _f(proxy_wasm::WasmCallWord<1>) \ _f(proxy_wasm::WasmCallWord<2>) _f(proxy_wasm::WasmCallWord<3>) \ - _f(proxy_wasm::WasmCall_WWlfd) + _f(proxy_wasm::WasmCall_WWlfd) _f(proxy_wasm::WasmCall_lf) \ + _f(proxy_wasm::WasmCall_fff) _f(proxy_wasm::WasmCall_dfff) // These are templates and its helper for constructing signatures of functions callbacks from Wasm // VMs. diff --git a/test/BUILD b/test/BUILD index be52da3f1..38d7810d3 100644 --- a/test/BUILD +++ b/test/BUILD @@ -85,6 +85,22 @@ cc_test( ], ) +cc_test( + name = "arg_passing_test", + timeout = "long", + srcs = ["arg_passing_test.cc"], + data = [ + "//test/test_data:arg_passing.wasm", + ], + linkstatic = 1, + deps = [ + ":utility_lib", + "//:lib", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "exports_test", srcs = ["exports_test.cc"], diff --git a/test/arg_passing_test.cc b/test/arg_passing_test.cc new file mode 100644 index 000000000..d9d1172d0 --- /dev/null +++ b/test/arg_passing_test.cc @@ -0,0 +1,137 @@ +// Copyright 2026 Google LLC +// +// 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. + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include + +#include "include/proxy-wasm/context.h" +#include "include/proxy-wasm/wasm.h" + +#include "test/utility.h" + +namespace proxy_wasm { +namespace { + +class ArgPassingContext : public TestContext { +public: + using TestContext::TestContext; + WasmResult getHeaderMapPairs(WasmHeaderMapType /* type */, Pairs * /* result */) override { + return static_cast(3333333333U); + } +}; + +class ArgPassingWasm : public TestWasm { +public: + using TestWasm::TestWasm; + ContextBase *createVmContext() override { return new ArgPassingContext(this); }; +}; + +class ArgPassingTest : public TestVm { +public: + void SetUp() { + auto source = readTestWasmFile("arg_passing.wasm"); + ASSERT_FALSE(source.empty()); + wasm_.emplace(std::move(vm_)); + ASSERT_TRUE(wasm_->load(source, false)); + ASSERT_TRUE(wasm_->initialize()); + context_ = dynamic_cast(wasm_->vm_context()); + ASSERT_NE(context_, nullptr); + } + + std::optional wasm_; + ArgPassingContext *context_; +}; + +INSTANTIATE_TEST_SUITE_P(WasmEngines, ArgPassingTest, testing::ValuesIn(getWasmEngines()), + [](const testing::TestParamInfo &info) { + return info.param; + }); + +TEST_P(ArgPassingTest, WasmCallReturnsWordValue) { + WasmCallWord<0> test_return_u32; + wasm_->wasm_vm()->getFunction("test_return_u32", &test_return_u32); + + EXPECT_EQ(test_return_u32(context_), 3333333333U) << context_->getLog(); +} + +TEST_P(ArgPassingTest, WasmCallReturnsLongValue) { + WasmCall_lf test_return_u64; + wasm_->wasm_vm()->getFunction("test_return_u64", &test_return_u64); + + EXPECT_EQ(test_return_u64(context_, 1.0), 11111111111111111111UL) << context_->getLog(); +} + +TEST_P(ArgPassingTest, WasmCallReturnsFloatValue) { + WasmCall_fff test_return_f32; + wasm_->wasm_vm()->getFunction("test_return_f32", &test_return_f32); + + EXPECT_THAT(test_return_f32(context_, 1.0, 1.0), + testing::AllOf(testing::Lt(1112.0), testing::Gt(1110.0))) + << context_->getLog(); +} + +TEST_P(ArgPassingTest, WasmCallReturnsDoubleValue) { + WasmCall_dfff test_return_f64; + wasm_->wasm_vm()->getFunction("test_return_f64", &test_return_f64); + + EXPECT_THAT(test_return_f64(context_, 1.0, 1.0, 1.0), + testing::AllOf(testing::Lt(1111111112.0), testing::Gt(1111111110.0))) + << context_->getLog(); +} + +TEST_P(ArgPassingTest, HostCallReturnsWordValue) { + WasmCallWord<0> test_host_return; + wasm_->wasm_vm()->getFunction("test_host_return", &test_host_return); + + EXPECT_TRUE(test_host_return(context_)) << context_->getLog(); +} + +TEST_P(ArgPassingTest, HostPassesPrimitiveValues) { + WasmCall_WWlfd test_primitives; + wasm_->wasm_vm()->getFunction("test_primitives", &test_primitives); + + ASSERT_TRUE(test_primitives(context_, 3333333333U, 11111111111111111111UL, 1111, 1111111111)) + << context_->getLog(); +} + +TEST_P(ArgPassingTest, HostPassesNegativePrimitiveValues) { + WasmCall_WWlfd test_negative_primitives; + wasm_->wasm_vm()->getFunction("test_negative_primitives", &test_negative_primitives); + + ASSERT_TRUE( + test_negative_primitives(context_, -1111111111, -1111111111111111111, -1111, -1111111111)) + << context_->getLog(); +} + +TEST_P(ArgPassingTest, HostReadsPointersToWasmMemory) { + WasmCallWord<0> test_buffer_from_wasm; + wasm_->wasm_vm()->getFunction("test_buffer_from_wasm", &test_buffer_from_wasm); + + ASSERT_TRUE(test_buffer_from_wasm(context_)) << context_->getLog(); + + context_->isLogged("hello from wasm land!"); +} + +TEST_P(ArgPassingTest, WasmCallReadsBufferPassedByHost) { + context_->setBuffer(0, "hello from host land!"); + WasmCallWord<0> test_buffer_from_host; + wasm_->wasm_vm()->getFunction("test_buffer_from_host", &test_buffer_from_host); + + ASSERT_TRUE(test_buffer_from_host(context_)) << context_->getLog(); +} + +} // namespace +} // namespace proxy_wasm diff --git a/test/runtime_test.cc b/test/runtime_test.cc index 2bcfd4383..b9304aaaf 100644 --- a/test/runtime_test.cc +++ b/test/runtime_test.cc @@ -190,34 +190,6 @@ TEST_P(TestVm, Trap2) { } } -TEST_P(TestVm, PassingValuesAcrossWasmBoundary) { - auto source = readTestWasmFile("arg_passing.wasm"); - ASSERT_FALSE(source.empty()); - auto wasm = TestWasm(std::move(vm_)); - ASSERT_TRUE(wasm.load(source, false)); - ASSERT_TRUE(wasm.initialize()); - auto *context = dynamic_cast(wasm.vm_context()); - ASSERT_NE(context, nullptr); - WasmCall_WWlfd test_primitives; - wasm.wasm_vm()->getFunction("test_primitives", &test_primitives); - WasmCall_WWlfd test_negative_primitives; - wasm.wasm_vm()->getFunction("test_negative_primitives", &test_negative_primitives); - WasmCallWord<0> test_buffer_from_wasm; - wasm.wasm_vm()->getFunction("test_buffer_from_wasm", &test_buffer_from_wasm); - WasmCallWord<0> test_buffer_from_host; - wasm.wasm_vm()->getFunction("test_buffer_from_host", &test_buffer_from_host); - - ASSERT_FALSE(test_primitives(context, 3333333333U, 11111111111111111111UL, 1111, 1111111111)); - ASSERT_FALSE( - test_negative_primitives(context, -1111111111, -1111111111111111111, -1111, -1111111111)); - - ASSERT_FALSE(test_buffer_from_wasm(context)); - context->isLogged("hello from wasm land!"); - - context->setBuffer(0, "hello from host land!"); - ASSERT_FALSE(test_buffer_from_host(context)); -} - class TestCounterContext : public TestContext { public: TestCounterContext(WasmBase *wasm) : TestContext(wasm) {} diff --git a/test/test_data/arg_passing.rs b/test/test_data/arg_passing.rs index 3cc4ad064..cc3b1ea76 100644 --- a/test/test_data/arg_passing.rs +++ b/test/test_data/arg_passing.rs @@ -12,6 +12,28 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::time::{SystemTime, UNIX_EPOCH}; + +extern "C" { + fn proxy_log(level: u32, message_data: *const u8, message_size: usize) -> bool; +} + +fn log(message: &str) { + unsafe { + proxy_log(/*error*/ 4, message.as_bytes().as_ptr(), message.len()); + } +} + +#[no_mangle] +pub extern "C" fn _initialize() { + std::panic::set_hook(Box::new(|panic_info| { + log(&format!( + "panic message: {}", + panic_info.payload_as_str().unwrap_or("") + )); + })); +} + #[no_mangle] pub extern "C" fn proxy_abi_version_0_2_0() {} @@ -26,34 +48,60 @@ pub extern "C" fn proxy_on_memory_allocate(size: usize) -> *mut u8 { } extern "C" { - fn proxy_log(level: u32, message_data: *const u8, message_size: usize) -> bool; + // Used by test_host_return to assert on values returned from imports from the host. + fn proxy_get_header_map_pairs( + map_type: u32, + return_map_data: *mut *mut u8, + return_map_size: *mut usize, + ) -> u32; } -fn log(message: &str) { +#[no_mangle] +pub extern "C" fn test_return_u32() -> u32 { + return 3333333333; +} + +#[no_mangle] +pub extern "C" fn test_return_u64(_: f32) -> u64 { + return 11111111111111111111; +} + +#[no_mangle] +pub extern "C" fn test_return_f32(_: f32, _: f32) -> f32 { + return 1111.0f32; +} + +#[no_mangle] +pub extern "C" fn test_return_f64(_: f32, _: f32, _: f32) -> f64 { + return 1111111111.0f64; +} + +#[no_mangle] +pub extern "C" fn test_host_return() -> u32 { unsafe { - proxy_log(/*error*/ 4, message.as_bytes().as_ptr(), message.len()); + let ret = proxy_get_header_map_pairs(0, std::ptr::null_mut(), std::ptr::null_mut()); + if ret != 3333333333u32 { + panic!("unexpected get_header_map_pairs return value: {}", ret); + } } + return 1; } #[no_mangle] pub extern "C" fn test_primitives(uint32: u32, uint64: u64, float32: f32, float64: f64) -> i32 { if uint32 != 3333333333 { - log(&format!("unexpected uint32 value: {}", uint32)); - return 1; + panic!("unexpected uint32 value: {}", uint32); } if uint64 != 11111111111111111111 { - log(&format!("unexpected uint64 value: {}", uint32)); - return 2; + panic!("unexpected uint64 value: {}", uint64); } if float32 < 1110.0 || float32 > 1112.0 { - log(&format!("unexpected float32 value: {}", float32)); - return 3; + panic!("unexpected float32 value: {}", float32); } if float64 < 1111111110.0 || float64 > 1111111112.0 { - log(&format!("unexpected float64 value: {}", float64)); - return 4; + panic!("unexpected float64 value: {}", float64); } - return 0; + return 1; } #[no_mangle] @@ -64,22 +112,18 @@ pub extern "C" fn test_negative_primitives( float64: f64, ) -> i32 { if int32 != -1111111111 { - log(&format!("unexpected int32 value: {}", int32)); - return 1; + panic!("unexpected int32 value: {}", int32); } if int64 != -1111111111111111111 { - log(&format!("unexpected int64 value: {}", int32)); - return 2; + panic!("unexpected int64 value: {}", int64); } if float32 > -1110.0 || float32 < -1112.0 { - log(&format!("unexpected float32 value: {}", float32)); - return 3; + panic!("unexpected float32 value: {}", float32); } if float64 > -1111111110.0 || float64 < -1111111112.0 { - log(&format!("unexpected float64 value: {}", float64)); - return 4; + panic!("unexpected float64 value: {}", float64); } - return 0; + return 1; } #[no_mangle] @@ -87,7 +131,7 @@ pub extern "C" fn test_buffer_from_wasm() -> bool { let message = "hello from wasm land!"; unsafe { match proxy_log(/*info*/ 2, message.as_ptr(), message.len()) { - false => false, + false => true, status => panic!("unexpected status: {}", status as u32), } } @@ -108,17 +152,18 @@ pub extern "C" fn test_buffer_from_host() -> bool { let mut return_data: *mut u8 = std::ptr::null_mut(); let mut return_size: usize = 0; unsafe { - match proxy_get_buffer_bytes(0, 0, 10, &mut return_data, &mut return_size) { + match proxy_get_buffer_bytes(0, 0, 30, &mut return_data, &mut return_size) { false => { if return_data.is_null() { panic!("return_data was null"); } let result = - String::from_utf8(Vec::from_raw_parts(return_data, return_size, return_size)); - if result.unwrap() != "hello from host land" { - panic!("message did not match expectation"); + String::from_utf8(Vec::from_raw_parts(return_data, return_size, return_size)) + .unwrap(); + if result != "hello from host land!\0" { + panic!("message {} did not match expectation", result); } - false + true } status => panic!("unexpected status: {}", status as u32), } diff --git a/test/utility.h b/test/utility.h index 357aabc2a..a53ed2e4b 100644 --- a/test/utility.h +++ b/test/utility.h @@ -102,6 +102,8 @@ class TestContext : public ContextBase { return WasmResult::Ok; } + std::string_view getLog() const { return log_; } + WasmResult getProperty(std::string_view path, std::string *result) override { if (path == "plugin_root_id") { *result = root_id_; From 23f7c36304e55c1353c153fd855eace647ef3324 Mon Sep 17 00:00:00 2001 From: Matt Leon Date: Wed, 18 Mar 2026 16:42:31 -0400 Subject: [PATCH 07/14] Fix endianness conversions for u32, f32 and f64 types Uses bswap64 by default to handle int64 and uint64 values. For float and doubles, uses appropriate-size bswap operators by first bit-casting floats and doubles to their same-size int types. Otherwise, they will be coerced to an int type before conversion and be returned as int values. Signed-off-by: Matt Leon --- include/proxy-wasm/word.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/include/proxy-wasm/word.h b/include/proxy-wasm/word.h index bc0d23a8c..a51b981b3 100644 --- a/include/proxy-wasm/word.h +++ b/include/proxy-wasm/word.h @@ -24,8 +24,16 @@ namespace proxy_wasm { // Use byteswap functions only when compiling for big-endian platforms. #if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \ __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -#define htowasm(x, vm_uses_wasm_byte_order) ((vm_uses_wasm_byte_order) ? __builtin_bswap32(x) : (x)) -#define wasmtoh(x, vm_uses_wasm_byte_order) ((vm_uses_wasm_byte_order) ? __builtin_bswap32(x) : (x)) +static inline float bswap(float x) { + return std::bit_cast(__builtin_bswap32(std::bit_cast(x))); +} +static inline double bswap(double x) { + return std::bit_cast(__builtin_bswap64(std::bit_cast(x))); +} +static inline uint32_t bswap(uint32_t x) { return __builtin_bswap32(x); } +static inline auto bswap(auto x) { return __builtin_bswap64(x); } +#define htowasm(x, vm_uses_wasm_byte_order) ((vm_uses_wasm_byte_order) ? bswap(x) : (x)) +#define wasmtoh(x, vm_uses_wasm_byte_order) ((vm_uses_wasm_byte_order) ? bswap(x) : (x)) #else #define htowasm(x, vm_uses_wasm_byte_order) (x) #define wasmtoh(x, vm_uses_wasm_byte_order) (x) From bf28c18f794f5dac992ea44884415ef133ab797c Mon Sep 17 00:00:00 2001 From: Matt Leon Date: Wed, 18 Mar 2026 16:46:38 -0400 Subject: [PATCH 08/14] Fix high bits stored in uint64 representation of Word Without first converting to uint32_t, the getters for wasm_val_t for wamr, wasmtime, and wasmedge returned a signed integer type. For uint32 values high enough to be in the negative range, sign extension would be applied when the value was coerced into proxy_wasm::Word. This resulted in Word values that did not match the comment on Word ``` // Represents a Wasm-native word-sized datum. On 32-bit VMs, the high bits are always zero. // The Wasm/VM API treats all bits as significant. ``` nor the exact value returned from the wasm plugin. Signed-off-by: Matt Leon --- src/wamr/wamr.cc | 7 +++++-- src/wasmedge/wasmedge.cc | 5 ++++- src/wasmtime/wasmtime.cc | 4 +++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/wamr/wamr.cc b/src/wamr/wamr.cc index 60a8a89bb..f445186b7 100644 --- a/src/wamr/wamr.cc +++ b/src/wamr/wamr.cc @@ -489,18 +489,21 @@ template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i32(); template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i32(); }; template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i64(); }; template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i64(); }; -template <> auto convertArgToValTypePtr() { return wasm_valtype_new_f64(); }; template <> auto convertArgToValTypePtr() { return wasm_valtype_new_f32(); }; +template <> auto convertArgToValTypePtr() { return wasm_valtype_new_f64(); }; template T convertValueTypeToArg(wasm_val_t val); template <> uint32_t convertValueTypeToArg(wasm_val_t val) { return static_cast(val.of.i32); } -template <> Word convertValueTypeToArg(wasm_val_t val) { return val.of.i32; } +template <> Word convertValueTypeToArg(wasm_val_t val) { + return std::bit_cast(val.of.i32); +} template <> int64_t convertValueTypeToArg(wasm_val_t val) { return val.of.i64; } template <> uint64_t convertValueTypeToArg(wasm_val_t val) { return static_cast(val.of.i64); } +template <> float convertValueTypeToArg(wasm_val_t val) { return val.of.f32; } template <> double convertValueTypeToArg(wasm_val_t val) { return val.of.f64; } template diff --git a/src/wasmedge/wasmedge.cc b/src/wasmedge/wasmedge.cc index 7ef31c7c6..cc93dce69 100644 --- a/src/wasmedge/wasmedge.cc +++ b/src/wasmedge/wasmedge.cc @@ -152,13 +152,16 @@ template T convValTypeToArg(WasmEdge_Value val); template <> uint32_t convValTypeToArg(WasmEdge_Value val) { return static_cast(WasmEdge_ValueGetI32(val)); } -template <> Word convValTypeToArg(WasmEdge_Value val) { return WasmEdge_ValueGetI32(val); } +template <> Word convValTypeToArg(WasmEdge_Value val) { + return std::bit_cast(WasmEdge_ValueGetI32(val)); +} template <> int64_t convValTypeToArg(WasmEdge_Value val) { return WasmEdge_ValueGetI64(val); } template <> uint64_t convValTypeToArg(WasmEdge_Value val) { return static_cast(WasmEdge_ValueGetI64(val)); } +template <> float convValTypeToArg(WasmEdge_Value val) { return WasmEdge_ValueGetF32(val); } template <> double convValTypeToArg(WasmEdge_Value val) { return WasmEdge_ValueGetF64(val); } diff --git a/src/wasmtime/wasmtime.cc b/src/wasmtime/wasmtime.cc index 674bb2ff8..5321e47df 100644 --- a/src/wasmtime/wasmtime.cc +++ b/src/wasmtime/wasmtime.cc @@ -469,7 +469,9 @@ template T convertValueTypeToArg(wasm_val_t val); template <> uint32_t convertValueTypeToArg(wasm_val_t val) { return static_cast(val.of.i32); } -template <> Word convertValueTypeToArg(wasm_val_t val) { return val.of.i32; } +template <> Word convertValueTypeToArg(wasm_val_t val) { + return std::bit_cast(val.of.i32); +} template <> int64_t convertValueTypeToArg(wasm_val_t val) { return val.of.i64; } template <> uint64_t convertValueTypeToArg(wasm_val_t val) { return static_cast(val.of.i64); From 87e4f1492679a83368af5ff209dfd91efdb53c90 Mon Sep 17 00:00:00 2001 From: Matt Leon Date: Wed, 18 Mar 2026 16:53:55 -0400 Subject: [PATCH 09/14] Add float converter for wasmtime with c-api Signed-off-by: Matt Leon --- src/wasmtime/wasmtime.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wasmtime/wasmtime.cc b/src/wasmtime/wasmtime.cc index 5321e47df..f436bc172 100644 --- a/src/wasmtime/wasmtime.cc +++ b/src/wasmtime/wasmtime.cc @@ -476,6 +476,7 @@ template <> int64_t convertValueTypeToArg(wasm_val_t val) { return val. template <> uint64_t convertValueTypeToArg(wasm_val_t val) { return static_cast(val.of.i64); } +template <> float convertValueTypeToArg(wasm_val_t val) { return val.of.f32; } template <> double convertValueTypeToArg(wasm_val_t val) { return val.of.f64; } template From 1ef54ccc5d693b13784e27eb41de5aa229c5b3b6 Mon Sep 17 00:00:00 2001 From: Matt Leon Date: Wed, 18 Mar 2026 17:07:09 -0400 Subject: [PATCH 10/14] Add test for returning negative ints from wasm Signed-off-by: Matt Leon --- test/arg_passing_test.cc | 9 ++++++++- test/test_data/arg_passing.rs | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/test/arg_passing_test.cc b/test/arg_passing_test.cc index d9d1172d0..051e061c8 100644 --- a/test/arg_passing_test.cc +++ b/test/arg_passing_test.cc @@ -64,7 +64,14 @@ TEST_P(ArgPassingTest, WasmCallReturnsWordValue) { WasmCallWord<0> test_return_u32; wasm_->wasm_vm()->getFunction("test_return_u32", &test_return_u32); - EXPECT_EQ(test_return_u32(context_), 3333333333U) << context_->getLog(); + EXPECT_EQ(test_return_u32(context_).u32(), 3333333333U) << context_->getLog(); +} + +TEST_P(ArgPassingTest, WasmCallReturnsNegativeWordValue) { + WasmCallWord<0> test_return_i32; + wasm_->wasm_vm()->getFunction("test_return_i32", &test_return_i32); + + EXPECT_EQ(test_return_i32(context_).u32(), -1111111111) << context_->getLog(); } TEST_P(ArgPassingTest, WasmCallReturnsLongValue) { diff --git a/test/test_data/arg_passing.rs b/test/test_data/arg_passing.rs index cc3b1ea76..1188b4076 100644 --- a/test/test_data/arg_passing.rs +++ b/test/test_data/arg_passing.rs @@ -61,6 +61,11 @@ pub extern "C" fn test_return_u32() -> u32 { return 3333333333; } +#[no_mangle] +pub extern "C" fn test_return_i32() -> i32 { + return -1111111111; +} + #[no_mangle] pub extern "C" fn test_return_u64(_: f32) -> u64 { return 11111111111111111111; From 8dc77e7e72a2117168d4ad7271ed84694f8e7731 Mon Sep 17 00:00:00 2001 From: Matt Leon Date: Wed, 18 Mar 2026 17:26:10 -0400 Subject: [PATCH 11/14] Suppress gtest warning for arg_passing_test in nullvm Signed-off-by: Matt Leon --- test/arg_passing_test.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/arg_passing_test.cc b/test/arg_passing_test.cc index 051e061c8..6f4458ec5 100644 --- a/test/arg_passing_test.cc +++ b/test/arg_passing_test.cc @@ -140,5 +140,7 @@ TEST_P(ArgPassingTest, WasmCallReadsBufferPassedByHost) { ASSERT_TRUE(test_buffer_from_host(context_)) << context_->getLog(); } +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ArgPassingTest); + } // namespace } // namespace proxy_wasm From 0cdd8b3f1c856a8d80278e723c25583be415e758 Mon Sep 17 00:00:00 2001 From: Matt Leon Date: Wed, 18 Mar 2026 17:28:30 -0400 Subject: [PATCH 12/14] Remove unused import from arg_passing.rs Signed-off-by: Matt Leon --- test/test_data/arg_passing.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/test_data/arg_passing.rs b/test/test_data/arg_passing.rs index 1188b4076..8b8cda5f9 100644 --- a/test/test_data/arg_passing.rs +++ b/test/test_data/arg_passing.rs @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::time::{SystemTime, UNIX_EPOCH}; - extern "C" { fn proxy_log(level: u32, message_data: *const u8, message_size: usize) -> bool; } From 37ec622c146fbab97ffbfe4fae2ef1765e5402c0 Mon Sep 17 00:00:00 2001 From: Matt Leon Date: Thu, 19 Mar 2026 09:38:29 -0400 Subject: [PATCH 13/14] Fix type narrowing compile error in wasm_vm_test Signed-off-by: Matt Leon --- test/wasm_vm_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/wasm_vm_test.cc b/test/wasm_vm_test.cc index 346fe2a07..d792c63df 100644 --- a/test/wasm_vm_test.cc +++ b/test/wasm_vm_test.cc @@ -85,7 +85,7 @@ TEST_P(TestVm, Memory) { ASSERT_EQ(100, word.u64_); uint32_t data[2] = {htowasm(static_cast(-1), vm_->usesWasmByteOrder()), - htowasm(200, vm_->usesWasmByteOrder())}; + htowasm(200U, vm_->usesWasmByteOrder())}; ASSERT_TRUE(vm_->setMemory(0x200, sizeof(int32_t) * 2, static_cast(data))); ASSERT_TRUE(vm_->getWord(0x200, &word)); ASSERT_EQ(-1, static_cast(word.u64_)); From ef77f96a55b5f81154cc42a96174462f7d09083c Mon Sep 17 00:00:00 2001 From: Matt Leon Date: Thu, 19 Mar 2026 14:12:03 -0400 Subject: [PATCH 14/14] Address PR comments Signed-off-by: Matt Leon --- src/wasmedge/wasmedge.cc | 2 +- src/wasmtime/wasmtime.cc | 2 +- test/arg_passing_test.cc | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/wasmedge/wasmedge.cc b/src/wasmedge/wasmedge.cc index cc93dce69..b3873c549 100644 --- a/src/wasmedge/wasmedge.cc +++ b/src/wasmedge/wasmedge.cc @@ -144,8 +144,8 @@ template <> WasmEdge_ValType convArgToValType() { return WasmEdge_ValTypeG template <> WasmEdge_ValType convArgToValType() { return WasmEdge_ValTypeGenI32(); } template <> WasmEdge_ValType convArgToValType() { return WasmEdge_ValTypeGenI64(); } template <> WasmEdge_ValType convArgToValType() { return WasmEdge_ValTypeGenI64(); } -template <> WasmEdge_ValType convArgToValType() { return WasmEdge_ValTypeGenF64(); } template <> WasmEdge_ValType convArgToValType() { return WasmEdge_ValTypeGenF32(); } +template <> WasmEdge_ValType convArgToValType() { return WasmEdge_ValTypeGenF64(); } // Helper templates to convert valtype to arg. template T convValTypeToArg(WasmEdge_Value val); diff --git a/src/wasmtime/wasmtime.cc b/src/wasmtime/wasmtime.cc index f436bc172..7fd990262 100644 --- a/src/wasmtime/wasmtime.cc +++ b/src/wasmtime/wasmtime.cc @@ -462,8 +462,8 @@ template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i32(); template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i32(); }; template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i64(); }; template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i64(); }; -template <> auto convertArgToValTypePtr() { return wasm_valtype_new_f64(); }; template <> auto convertArgToValTypePtr() { return wasm_valtype_new_f32(); }; +template <> auto convertArgToValTypePtr() { return wasm_valtype_new_f64(); }; template T convertValueTypeToArg(wasm_val_t val); template <> uint32_t convertValueTypeToArg(wasm_val_t val) { diff --git a/test/arg_passing_test.cc b/test/arg_passing_test.cc index 6f4458ec5..eb6474a8b 100644 --- a/test/arg_passing_test.cc +++ b/test/arg_passing_test.cc @@ -29,6 +29,8 @@ class ArgPassingContext : public TestContext { public: using TestContext::TestContext; WasmResult getHeaderMapPairs(WasmHeaderMapType /* type */, Pairs * /* result */) override { + // GetHeaderMapPairs passes this value as the hostcall return value as + // opposed to output parameter. return static_cast(3333333333U); } };