Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion include/proxy-wasm/wasm_vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,21 @@ template <size_t N>
using WasmCallVoid = std::function<WasmCallInFuncType<N, void, ContextBase *, Word>>;
template <size_t N>
using WasmCallWord = std::function<WasmCallInFuncType<N, Word, ContextBase *, Word>>;
// Callback used to test arg passing from host to wasm.
using WasmCall_WWlfd = std::function<Word(ContextBase *, Word, uint64_t, float, double)>;
// 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<uint64_t(ContextBase *, float)>;
using WasmCall_fff = std::function<float(ContextBase *, float, float)>;
using WasmCall_dfff = std::function<double(ContextBase *, float, float, float)>;

#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) _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.
Expand Down
12 changes: 10 additions & 2 deletions include/proxy-wasm/word.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<float>(__builtin_bswap32(std::bit_cast<int32_t>(x)));
}
static inline double bswap(double x) {
return std::bit_cast<double>(__builtin_bswap64(std::bit_cast<int64_t>(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)
Expand Down
1 change: 1 addition & 0 deletions src/v8/v8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ template <> constexpr auto convertArgToValKind<uint32_t>() { return wasm::ValKin
template <> constexpr auto convertArgToValKind<int64_t>() { return wasm::ValKind::I64; };
template <> constexpr auto convertArgToValKind<uint64_t>() { return wasm::ValKind::I64; };
template <> constexpr auto convertArgToValKind<double>() { return wasm::ValKind::F64; };
template <> constexpr auto convertArgToValKind<float>() { return wasm::ValKind::F32; };

template <typename T, std::size_t... I>
constexpr auto convertArgsTupleToValTypesImpl(std::index_sequence<I...> /*comptime*/) {
Expand Down
10 changes: 9 additions & 1 deletion src/wamr/wamr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,10 @@ template <> void assignVal(uint64_t t, wasm_val_t &val) {
val.kind = WASM_I64;
val.of.i64 = static_cast<int64_t>(t);
}
template <> void assignVal(float t, wasm_val_t &val) {
val.kind = WASM_F32;
val.of.f32 = static_cast<float>(t);
}
template <> void assignVal(double t, wasm_val_t &val) {
val.kind = WASM_F64;
val.of.f64 = t;
Expand All @@ -485,17 +489,21 @@ template <> auto convertArgToValTypePtr<Word>() { return wasm_valtype_new_i32();
template <> auto convertArgToValTypePtr<uint32_t>() { return wasm_valtype_new_i32(); };
template <> auto convertArgToValTypePtr<int64_t>() { return wasm_valtype_new_i64(); };
template <> auto convertArgToValTypePtr<uint64_t>() { return wasm_valtype_new_i64(); };
template <> auto convertArgToValTypePtr<float>() { return wasm_valtype_new_f32(); };
template <> auto convertArgToValTypePtr<double>() { return wasm_valtype_new_f64(); };

template <typename T> T convertValueTypeToArg(wasm_val_t val);
template <> uint32_t convertValueTypeToArg<uint32_t>(wasm_val_t val) {
return static_cast<uint32_t>(val.of.i32);
}
template <> Word convertValueTypeToArg<Word>(wasm_val_t val) { return val.of.i32; }
template <> Word convertValueTypeToArg<Word>(wasm_val_t val) {
return std::bit_cast<uint32_t>(val.of.i32);
}
template <> int64_t convertValueTypeToArg<int64_t>(wasm_val_t val) { return val.of.i64; }
template <> uint64_t convertValueTypeToArg<uint64_t>(wasm_val_t val) {
return static_cast<uint64_t>(val.of.i64);
}
template <> float convertValueTypeToArg<float>(wasm_val_t val) { return val.of.f32; }
template <> double convertValueTypeToArg<double>(wasm_val_t val) { return val.of.f64; }

template <typename T, typename U, std::size_t... I>
Expand Down
7 changes: 6 additions & 1 deletion src/wasmedge/wasmedge.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ template <> WasmEdge_Value makeVal(uint64_t t) {
return WasmEdge_ValueGenI64(static_cast<int64_t>(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) {
Expand Down Expand Up @@ -143,20 +144,24 @@ template <> WasmEdge_ValType convArgToValType<Word>() { return WasmEdge_ValTypeG
template <> WasmEdge_ValType convArgToValType<uint32_t>() { return WasmEdge_ValTypeGenI32(); }
template <> WasmEdge_ValType convArgToValType<int64_t>() { return WasmEdge_ValTypeGenI64(); }
template <> WasmEdge_ValType convArgToValType<uint64_t>() { return WasmEdge_ValTypeGenI64(); }
template <> WasmEdge_ValType convArgToValType<float>() { return WasmEdge_ValTypeGenF32(); }
template <> WasmEdge_ValType convArgToValType<double>() { return WasmEdge_ValTypeGenF64(); }

// Helper templates to convert valtype to arg.
template <typename T> T convValTypeToArg(WasmEdge_Value val);
template <> uint32_t convValTypeToArg<uint32_t>(WasmEdge_Value val) {
return static_cast<uint32_t>(WasmEdge_ValueGetI32(val));
}
template <> Word convValTypeToArg<Word>(WasmEdge_Value val) { return WasmEdge_ValueGetI32(val); }
template <> Word convValTypeToArg<Word>(WasmEdge_Value val) {
return std::bit_cast<uint32_t>(WasmEdge_ValueGetI32(val));
}
template <> int64_t convValTypeToArg<int64_t>(WasmEdge_Value val) {
return WasmEdge_ValueGetI64(val);
}
template <> uint64_t convValTypeToArg<uint64_t>(WasmEdge_Value val) {
return static_cast<uint64_t>(WasmEdge_ValueGetI64(val));
}
template <> float convValTypeToArg<float>(WasmEdge_Value val) { return WasmEdge_ValueGetF32(val); }
template <> double convValTypeToArg<double>(WasmEdge_Value val) {
return WasmEdge_ValueGetF64(val);
}
Expand Down
10 changes: 9 additions & 1 deletion src/wasmtime/wasmtime.cc
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,10 @@ template <> void assignVal(uint64_t t, wasm_val_t &val) {
val.kind = WASM_I64;
val.of.i64 = static_cast<int64_t>(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;
Expand All @@ -458,17 +462,21 @@ template <> auto convertArgToValTypePtr<Word>() { return wasm_valtype_new_i32();
template <> auto convertArgToValTypePtr<uint32_t>() { return wasm_valtype_new_i32(); };
template <> auto convertArgToValTypePtr<int64_t>() { return wasm_valtype_new_i64(); };
template <> auto convertArgToValTypePtr<uint64_t>() { return wasm_valtype_new_i64(); };
template <> auto convertArgToValTypePtr<float>() { return wasm_valtype_new_f32(); };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: order.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

template <> auto convertArgToValTypePtr<double>() { return wasm_valtype_new_f64(); };

template <typename T> T convertValueTypeToArg(wasm_val_t val);
template <> uint32_t convertValueTypeToArg<uint32_t>(wasm_val_t val) {
return static_cast<uint32_t>(val.of.i32);
}
template <> Word convertValueTypeToArg<Word>(wasm_val_t val) { return val.of.i32; }
template <> Word convertValueTypeToArg<Word>(wasm_val_t val) {
return std::bit_cast<uint32_t>(val.of.i32);
}
template <> int64_t convertValueTypeToArg<int64_t>(wasm_val_t val) { return val.of.i64; }
template <> uint64_t convertValueTypeToArg<uint64_t>(wasm_val_t val) {
return static_cast<uint64_t>(val.of.i64);
}
template <> float convertValueTypeToArg<float>(wasm_val_t val) { return val.of.f32; }
template <> double convertValueTypeToArg<double>(wasm_val_t val) { return val.of.f64; }

template <typename T, typename U, std::size_t... I>
Expand Down
17 changes: 17 additions & 0 deletions test/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ cc_test(
timeout = "long",
srcs = ["runtime_test.cc"],
data = [
"//test/test_data:arg_passing.wasm",
"//test/test_data:callback.wasm",
"//test/test_data:clock.wasm",
"//test/test_data:resource_limits.wasm",
Expand All @@ -84,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"],
Expand Down
148 changes: 148 additions & 0 deletions test/arg_passing_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// 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 <optional>

#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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use getStatus to get uint32, string pair and test both in one go?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getStatus passes the returned pair to wasm as output parameters, whereas get_header_map_pairs uses the returned value as the hostcall return value.

// GetHeaderMapPairs passes this value as the hostcall return value as
// opposed to output parameter.
return static_cast<WasmResult>(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<ArgPassingContext *>(wasm_->vm_context());
ASSERT_NE(context_, nullptr);
}

std::optional<ArgPassingWasm> wasm_;
ArgPassingContext *context_;
};

INSTANTIATE_TEST_SUITE_P(WasmEngines, ArgPassingTest, testing::ValuesIn(getWasmEngines()),
[](const testing::TestParamInfo<std::string> &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_).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) {
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();
}

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ArgPassingTest);

} // namespace
} // namespace proxy_wasm
5 changes: 5 additions & 0 deletions test/test_data/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ wasm_rust_binary(
srcs = ["trap.rs"],
)

wasm_rust_binary(
name = "arg_passing.wasm",
srcs = ["arg_passing.rs"],
)

wasm_rust_binary(
name = "resource_limits.wasm",
srcs = ["resource_limits.rs"],
Expand Down
Loading
Loading