Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

48
deps/v8/test/wasm-api-tests/BUILD.gn vendored Normal file
View File

@ -0,0 +1,48 @@
# Copyright 2019 The V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("../../gni/v8.gni")
v8_executable("wasm_api_tests") {
testonly = true
deps = [
"../:common_test_headers",
"../..:v8_internal_headers",
"../..:v8_maybe_icu",
"../..:wee8",
"//build/win:default_exe_manifest",
"//testing/gmock",
"//testing/gtest",
]
data_deps = [ "../../tools:v8_testrunner" ]
data = [
"testcfg.py",
"wasm-api-tests.status",
]
configs = [ "../..:internal_config_base" ]
sources = [
"../../testing/gmock-support.h",
"../../testing/gtest-support.h",
"callbacks.cc",
"finalize.cc",
"globals.cc",
"hostref.cc",
"memory.cc",
"multi-return.cc",
"reflect.cc",
"regressions.cc",
"run-all-wasm-api-tests.cc",
"serialize.cc",
"startup-errors.cc",
"table.cc",
"threads.cc",
"traps.cc",
"wasm-api-test.h",
]
}

5
deps/v8/test/wasm-api-tests/DEPS vendored Normal file
View File

@ -0,0 +1,5 @@
include_rules = [
"+src",
"+testing",
"+third_party/wasm-api"
]

346
deps/v8/test/wasm-api-tests/callbacks.cc vendored Normal file
View File

@ -0,0 +1,346 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/execution/isolate.h"
#include "src/heap/heap.h"
#include "src/wasm/c-api.h"
#include "test/wasm-api-tests/wasm-api-test.h"
namespace v8 {
namespace internal {
namespace wasm {
namespace {
own<Trap> Stage2(void* env, const vec<Val>& args, vec<Val>& results) {
printf("Stage2...\n");
WasmCapiTest* self = reinterpret_cast<WasmCapiTest*>(env);
Func* stage3 = self->GetExportedFunction(1);
own<Trap> trap = stage3->call(args, results);
if (trap) {
printf("Stage2: got exception: %s\n", trap->message().get());
} else {
printf("Stage2: call successful\n");
}
return trap;
}
own<Trap> Stage4_GC(void* env, const vec<Val>& args, vec<Val>& results) {
printf("Stage4...\n");
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env);
isolate->heap()->PreciseCollectAllGarbage(GCFlag::kForced,
GarbageCollectionReason::kTesting);
results[0] = Val::i32(args[0].i32() + 1);
return nullptr;
}
class WasmCapiCallbacksTest : public WasmCapiTest {
public:
WasmCapiCallbacksTest() : WasmCapiTest() {
// Build the following function:
// int32 stage1(int32 arg0) { return stage2(arg0); }
uint32_t stage2_index =
builder()->AddImport(base::CStrVector("stage2"), wasm_i_i_sig());
uint8_t code[] = {WASM_CALL_FUNCTION(stage2_index, WASM_LOCAL_GET(0))};
AddExportedFunction(base::CStrVector("stage1"), code, sizeof(code));
stage2_ = Func::make(store(), cpp_i_i_sig(), Stage2, this);
}
Func* stage2() { return stage2_.get(); }
void AddExportedFunction(base::Vector<const char> name, uint8_t code[],
size_t code_size) {
WasmCapiTest::AddExportedFunction(name, code, code_size, wasm_i_i_sig());
}
private:
own<Func> stage2_;
};
} // namespace
TEST_F(WasmCapiCallbacksTest, Trap) {
// Build the following function:
// int32 stage3_trap(int32 arg0) { unreachable(); }
uint8_t code[] = {WASM_UNREACHABLE};
AddExportedFunction(base::CStrVector("stage3_trap"), code, sizeof(code));
vec<Extern*> imports = vec<Extern*>::make(stage2());
Instantiate(imports);
vec<Val> args = vec<Val>::make(Val::i32(42));
vec<Val> results = vec<Val>::make_uninitialized(1);
own<Trap> trap = GetExportedFunction(0)->call(args, results);
EXPECT_NE(trap, nullptr);
printf("Stage0: Got trap as expected: %s\n", trap->message().get());
}
TEST_F(WasmCapiCallbacksTest, GC) {
// Build the following function:
// int32 stage3_to4(int32 arg0) { return stage4(arg0); }
uint32_t stage4_index =
builder()->AddImport(base::CStrVector("stage4"), wasm_i_i_sig());
uint8_t code[] = {WASM_CALL_FUNCTION(stage4_index, WASM_LOCAL_GET(0))};
AddExportedFunction(base::CStrVector("stage3_to4"), code, sizeof(code));
i::Isolate* isolate =
reinterpret_cast<::wasm::StoreImpl*>(store())->i_isolate();
own<Func> stage4 = Func::make(store(), cpp_i_i_sig(), Stage4_GC, isolate);
EXPECT_EQ(cpp_i_i_sig()->params().size(), stage4->type()->params().size());
EXPECT_EQ(cpp_i_i_sig()->results().size(), stage4->type()->results().size());
vec<Extern*> imports = vec<Extern*>::make(stage2(), stage4.get());
Instantiate(imports);
vec<Val> args = vec<Val>::make(Val::i32(42));
vec<Val> results = vec<Val>::make_uninitialized(1);
own<Trap> trap = GetExportedFunction(0)->call(args, results);
EXPECT_EQ(trap, nullptr);
EXPECT_EQ(43, results[0].i32());
}
namespace {
own<Trap> FibonacciC(void* env, const vec<Val>& args, vec<Val>& results) {
int32_t x = args[0].i32();
if (x == 0 || x == 1) {
results[0] = Val::i32(x);
return nullptr;
}
WasmCapiTest* self = reinterpret_cast<WasmCapiTest*>(env);
Func* fibo_wasm = self->GetExportedFunction(0);
// Aggressively reuse existing arrays. That's maybe not great coding
// style, but this test intentionally ensures that it works if someone
// insists on doing it.
vec<Val> recursive_args = vec<Val>::make(Val::i32(x - 1));
own<Trap> trap = fibo_wasm->call(recursive_args, results);
DCHECK_NULL(trap);
int32_t x1 = results[0].i32();
recursive_args[0] = Val::i32(x - 2);
trap = fibo_wasm->call(recursive_args, results);
DCHECK_NULL(trap);
int32_t x2 = results[0].i32();
results[0] = Val::i32(x1 + x2);
return nullptr;
}
} // namespace
TEST_F(WasmCapiTest, Recursion) {
// Build the following function:
// int32 fibonacci_wasm(int32 arg0) {
// if (arg0 == 0) return 0;
// if (arg0 == 1) return 1;
// return fibonacci_c(arg0 - 1) + fibonacci_c(arg0 - 2);
// }
uint32_t fibo_c_index =
builder()->AddImport(base::CStrVector("fibonacci_c"), wasm_i_i_sig());
uint8_t code_fibo[] = {
WASM_IF(WASM_I32_EQ(WASM_LOCAL_GET(0), WASM_ZERO),
WASM_RETURN(WASM_ZERO)),
WASM_IF(WASM_I32_EQ(WASM_LOCAL_GET(0), WASM_ONE), WASM_RETURN(WASM_ONE)),
// Muck with the parameter to ensure callers don't depend on its value.
WASM_LOCAL_SET(0, WASM_I32_SUB(WASM_LOCAL_GET(0), WASM_ONE)),
WASM_RETURN(WASM_I32_ADD(
WASM_CALL_FUNCTION(fibo_c_index, WASM_LOCAL_GET(0)),
WASM_CALL_FUNCTION(fibo_c_index,
WASM_I32_SUB(WASM_LOCAL_GET(0), WASM_ONE))))};
AddExportedFunction(base::CStrVector("fibonacci_wasm"), code_fibo,
sizeof(code_fibo), wasm_i_i_sig());
own<Func> fibonacci = Func::make(store(), cpp_i_i_sig(), FibonacciC, this);
vec<Extern*> imports = vec<Extern*>::make(fibonacci.get());
Instantiate(imports);
// Enough iterations to make it interesting, few enough to keep it fast.
vec<Val> args = vec<Val>::make(Val::i32(15));
vec<Val> results = vec<Val>::make_uninitialized(1);
own<Trap> result = GetExportedFunction(0)->call(args, results);
EXPECT_EQ(result, nullptr);
EXPECT_EQ(610, results[0].i32());
}
namespace {
own<Trap> PlusOne(const vec<Val>& args, vec<Val>& results) {
int32_t a0 = args[0].i32();
results[0] = Val::i32(a0 + 1);
int64_t a1 = args[1].i64();
results[1] = Val::i64(a1 + 1);
float a2 = args[2].f32();
results[2] = Val::f32(a2 + 1);
double a3 = args[3].f64();
results[3] = Val::f64(a3 + 1);
results[4] = Val::ref(args[4].ref()->copy()); // No +1 for Refs.
return nullptr;
}
own<Trap> PlusOneWithManyArgs(const vec<Val>& args, vec<Val>& results) {
int32_t a0 = args[0].i32();
results[0] = Val::i32(a0 + 1);
int64_t a1 = args[1].i64();
results[1] = Val::i64(a1 + 1);
float a2 = args[2].f32();
results[2] = Val::f32(a2 + 1);
double a3 = args[3].f64();
results[3] = Val::f64(a3 + 1);
results[4] = Val::ref(args[4].ref()->copy()); // No +1 for Refs.
int32_t a5 = args[5].i32();
results[5] = Val::i32(a5 + 1);
int64_t a6 = args[6].i64();
results[6] = Val::i64(a6 + 1);
float a7 = args[7].f32();
results[7] = Val::f32(a7 + 1);
double a8 = args[8].f64();
results[8] = Val::f64(a8 + 1);
int32_t a9 = args[9].i32();
results[9] = Val::i32(a9 + 1);
int64_t a10 = args[10].i64();
results[10] = Val::i64(a10 + 1);
float a11 = args[11].f32();
results[11] = Val::f32(a11 + 1);
double a12 = args[12].f64();
results[12] = Val::f64(a12 + 1);
int32_t a13 = args[13].i32();
results[13] = Val::i32(a13 + 1);
return nullptr;
}
} // namespace
TEST_F(WasmCapiTest, DirectCallCapiFunction) {
own<FuncType> cpp_sig = FuncType::make(
ownvec<ValType>::make(ValType::make(::wasm::ValKind::I32),
ValType::make(::wasm::ValKind::I64),
ValType::make(::wasm::ValKind::F32),
ValType::make(::wasm::ValKind::F64),
ValType::make(::wasm::ValKind::EXTERNREF)),
ownvec<ValType>::make(ValType::make(::wasm::ValKind::I32),
ValType::make(::wasm::ValKind::I64),
ValType::make(::wasm::ValKind::F32),
ValType::make(::wasm::ValKind::F64),
ValType::make(::wasm::ValKind::EXTERNREF)));
own<Func> func = Func::make(store(), cpp_sig.get(), PlusOne);
vec<Extern*> imports = vec<Extern*>::make(func.get());
ValueType wasm_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64,
kWasmExternRef, kWasmI32, kWasmI64, kWasmF32,
kWasmF64, kWasmExternRef};
FunctionSig wasm_sig(5, 5, wasm_types);
int func_index = builder()->AddImport(base::CStrVector("func"), &wasm_sig);
builder()->ExportImportedFunction(base::CStrVector("func"), func_index);
Instantiate(imports);
int32_t a0 = 42;
int64_t a1 = 0x1234c0ffee;
float a2 = 1234.5;
double a3 = 123.45;
vec<Val> args = vec<Val>::make(Val::i32(a0), Val::i64(a1), Val::f32(a2),
Val::f64(a3), Val::ref(func->copy()));
vec<Val> results = vec<Val>::make_uninitialized(5);
// Test that {func} can be called directly.
own<Trap> trap = func->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(a0 + 1, results[0].i32());
EXPECT_EQ(a1 + 1, results[1].i64());
EXPECT_EQ(a2 + 1, results[2].f32());
EXPECT_EQ(a3 + 1, results[3].f64());
EXPECT_TRUE(func->same(results[4].ref()));
// Test that {func} can be called after import/export round-tripping.
trap = GetExportedFunction(0)->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(a0 + 1, results[0].i32());
EXPECT_EQ(a1 + 1, results[1].i64());
EXPECT_EQ(a2 + 1, results[2].f32());
EXPECT_EQ(a3 + 1, results[3].f64());
EXPECT_TRUE(func->same(results[4].ref()));
}
TEST_F(WasmCapiTest, DirectCallCapiFunctionWithManyArgs) {
// Test with many arguments to make sure that CWasmArgumentsPacker won't use
// its buffer-on-stack optimization.
own<FuncType> cpp_sig = FuncType::make(
ownvec<ValType>::make(ValType::make(::wasm::ValKind::I32),
ValType::make(::wasm::ValKind::I64),
ValType::make(::wasm::ValKind::F32),
ValType::make(::wasm::ValKind::F64),
ValType::make(::wasm::ValKind::EXTERNREF),
ValType::make(::wasm::ValKind::I32),
ValType::make(::wasm::ValKind::I64),
ValType::make(::wasm::ValKind::F32),
ValType::make(::wasm::ValKind::F64),
ValType::make(::wasm::ValKind::I32),
ValType::make(::wasm::ValKind::I64),
ValType::make(::wasm::ValKind::F32),
ValType::make(::wasm::ValKind::F64),
ValType::make(::wasm::ValKind::I32)),
ownvec<ValType>::make(ValType::make(::wasm::ValKind::I32),
ValType::make(::wasm::ValKind::I64),
ValType::make(::wasm::ValKind::F32),
ValType::make(::wasm::ValKind::F64),
ValType::make(::wasm::ValKind::EXTERNREF),
ValType::make(::wasm::ValKind::I32),
ValType::make(::wasm::ValKind::I64),
ValType::make(::wasm::ValKind::F32),
ValType::make(::wasm::ValKind::F64),
ValType::make(::wasm::ValKind::I32),
ValType::make(::wasm::ValKind::I64),
ValType::make(::wasm::ValKind::F32),
ValType::make(::wasm::ValKind::F64),
ValType::make(::wasm::ValKind::I32)));
own<Func> func = Func::make(store(), cpp_sig.get(), PlusOneWithManyArgs);
vec<Extern*> imports = vec<Extern*>::make(func.get());
ValueType wasm_types[] = {
kWasmI32, kWasmI64, kWasmF32, kWasmF64, kWasmExternRef, kWasmI32,
kWasmI64, kWasmF32, kWasmF64, kWasmI32, kWasmI64, kWasmF32,
kWasmF64, kWasmI32, kWasmI32, kWasmI64, kWasmF32, kWasmF64,
kWasmExternRef, kWasmI32, kWasmI64, kWasmF32, kWasmF64, kWasmI32,
kWasmI64, kWasmF32, kWasmF64, kWasmI32};
FunctionSig wasm_sig(14, 14, wasm_types);
int func_index = builder()->AddImport(base::CStrVector("func"), &wasm_sig);
builder()->ExportImportedFunction(base::CStrVector("func"), func_index);
Instantiate(imports);
int32_t a0 = 42;
int64_t a1 = 0x1234c0ffee;
float a2 = 1234.5;
double a3 = 123.45;
vec<Val> args =
vec<Val>::make(Val::i32(a0), Val::i64(a1), Val::f32(a2), Val::f64(a3),
Val::ref(func->copy()), Val::i32(a0), Val::i64(a1),
Val::f32(a2), Val::f64(a3), Val::i32(a0), Val::i64(a1),
Val::f32(a2), Val::f64(a3), Val::i32(a0));
vec<Val> results = vec<Val>::make_uninitialized(14);
// Test that {func} can be called directly.
own<Trap> trap = func->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(a0 + 1, results[0].i32());
EXPECT_EQ(a1 + 1, results[1].i64());
EXPECT_EQ(a2 + 1, results[2].f32());
EXPECT_EQ(a3 + 1, results[3].f64());
EXPECT_TRUE(func->same(results[4].ref()));
EXPECT_EQ(a0 + 1, results[5].i32());
EXPECT_EQ(a1 + 1, results[6].i64());
EXPECT_EQ(a2 + 1, results[7].f32());
EXPECT_EQ(a3 + 1, results[8].f64());
EXPECT_EQ(a0 + 1, results[9].i32());
EXPECT_EQ(a1 + 1, results[10].i64());
EXPECT_EQ(a2 + 1, results[11].f32());
EXPECT_EQ(a3 + 1, results[12].f64());
EXPECT_EQ(a0 + 1, results[13].i32());
// Test that {func} can be called after import/export round-tripping.
trap = GetExportedFunction(0)->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(a0 + 1, results[0].i32());
EXPECT_EQ(a1 + 1, results[1].i64());
EXPECT_EQ(a2 + 1, results[2].f32());
EXPECT_EQ(a3 + 1, results[3].f64());
EXPECT_TRUE(func->same(results[4].ref()));
EXPECT_EQ(a0 + 1, results[5].i32());
EXPECT_EQ(a1 + 1, results[6].i64());
EXPECT_EQ(a2 + 1, results[7].f32());
EXPECT_EQ(a3 + 1, results[8].f64());
EXPECT_EQ(a0 + 1, results[9].i32());
EXPECT_EQ(a1 + 1, results[10].i64());
EXPECT_EQ(a2 + 1, results[11].f32());
EXPECT_EQ(a3 + 1, results[12].f64());
EXPECT_EQ(a0 + 1, results[13].i32());
}
} // namespace wasm
} // namespace internal
} // namespace v8

193
deps/v8/test/wasm-api-tests/finalize.cc vendored Normal file
View File

@ -0,0 +1,193 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "test/wasm-api-tests/wasm-api-test.h"
namespace v8 {
namespace internal {
namespace wasm {
namespace {
int g_instances_finalized = 0;
int g_functions_finalized = 0;
int g_foreigns_finalized = 0;
int g_modules_finalized = 0;
const int kModuleMagic = 42;
void FinalizeInstance(void* data) {
int iteration = static_cast<int>(reinterpret_cast<intptr_t>(data));
g_instances_finalized += iteration;
}
void FinalizeFunction(void* data) {
int iteration = static_cast<int>(reinterpret_cast<intptr_t>(data));
g_functions_finalized += iteration;
}
void FinalizeForeign(void* data) {
int iteration = static_cast<int>(reinterpret_cast<intptr_t>(data));
g_foreigns_finalized += iteration;
}
void FinalizeModule(void* data) {
g_modules_finalized += static_cast<int>(reinterpret_cast<intptr_t>(data));
}
void RunInStore(Store* store, base::Vector<const uint8_t> wire_bytes,
int iterations) {
vec<byte_t> binary = vec<byte_t>::make(
wire_bytes.size(),
reinterpret_cast<byte_t*>(const_cast<uint8_t*>(wire_bytes.begin())));
own<Module> module = Module::make(store, binary);
module->set_host_info(reinterpret_cast<void*>(kModuleMagic), &FinalizeModule);
for (int iteration = 0; iteration < iterations; iteration++) {
void* finalizer_data = reinterpret_cast<void*>(iteration);
vec<Extern*> imports = vec<Extern*>::make_uninitialized();
own<Instance> instance =
Instance::make(store, module.get(), imports, nullptr);
EXPECT_NE(nullptr, instance.get());
instance->set_host_info(finalizer_data, &FinalizeInstance);
own<Func> func = instance->exports()[0]->func()->copy();
ASSERT_NE(func, nullptr);
func->set_host_info(finalizer_data, &FinalizeFunction);
vec<Val> args = vec<Val>::make(Val::i32(iteration));
vec<Val> results = vec<Val>::make_uninitialized(1);
func->call(args, results);
EXPECT_EQ(iteration, results[0].i32());
own<Foreign> foreign = Foreign::make(store);
foreign->set_host_info(finalizer_data, &FinalizeForeign);
}
}
} // namespace
TEST_F(WasmCapiTest, InstanceFinalization) {
// Add a dummy function: f(x) { return x; }
uint8_t code[] = {WASM_RETURN(WASM_LOCAL_GET(0))};
AddExportedFunction(base::CStrVector("f"), code, sizeof(code),
wasm_i_i_sig());
Compile();
g_instances_finalized = 0;
g_functions_finalized = 0;
g_foreigns_finalized = 0;
g_modules_finalized = 0;
module()->set_host_info(reinterpret_cast<void*>(kModuleMagic),
&FinalizeModule);
static const int kIterations = 10;
RunInStore(store(), wire_bytes(), kIterations);
{
own<Store> store2 = Store::make(engine());
RunInStore(store2.get(), wire_bytes(), kIterations);
}
RunInStore(store(), wire_bytes(), kIterations);
Shutdown();
// Verify that (1) all finalizers were called, and (2) they passed the
// correct host data: the loop above sets {i} as data, and the finalizer
// callbacks add them all up, so the expected value after three rounds is
// 3 * sum([0, 1, ..., kIterations - 1]), which per Gauss's formula is:
static const int kExpected = 3 * ((kIterations * (kIterations - 1)) / 2);
EXPECT_EQ(g_instances_finalized, kExpected);
// There are two functions per iteration.
EXPECT_EQ(g_functions_finalized, kExpected);
EXPECT_EQ(g_foreigns_finalized, kExpected);
EXPECT_EQ(g_modules_finalized, 4 * kModuleMagic);
}
namespace {
own<Trap> CapiFunction(void* env, const vec<Val>& args, vec<Val>& results) {
int offset = static_cast<int>(reinterpret_cast<intptr_t>(env));
results[0] = Val::i32(offset + args[0].i32());
return nullptr;
}
int g_host_data_finalized = 0;
int g_capi_function_finalized = 0;
void FinalizeCapiFunction(void* data) {
int value = static_cast<int>(reinterpret_cast<intptr_t>(data));
g_capi_function_finalized += value;
}
void FinalizeHostData(void* data) {
g_host_data_finalized += static_cast<int>(reinterpret_cast<intptr_t>(data));
}
} // namespace
TEST_F(WasmCapiTest, CapiFunctionLifetimes) {
uint32_t func_index =
builder()->AddImport(base::CStrVector("f"), wasm_i_i_sig());
builder()->ExportImportedFunction(base::CStrVector("f"), func_index);
Compile();
own<Instance> instance;
void* kHostData = reinterpret_cast<void*>(1234);
int base_summand = 1000;
{
// Test that the own<> pointers for Func and FuncType can go out of scope
// without affecting the ability of the Func to be called later.
own<FuncType> capi_func_type = FuncType::make(
ownvec<ValType>::make(ValType::make(::wasm::ValKind::I32)),
ownvec<ValType>::make(ValType::make(::wasm::ValKind::I32)));
own<Func> capi_func =
Func::make(store(), capi_func_type.get(), &CapiFunction,
reinterpret_cast<void*>(base_summand));
vec<Extern*> imports = vec<Extern*>::make(capi_func.get());
instance = Instance::make(store(), module(), imports);
// TODO(jkummerow): It may or may not be desirable to be able to set
// host data even here and have it survive the import/export dance.
// We are awaiting resolution of the discussion at:
// https://github.com/WebAssembly/wasm-c-api/issues/100
}
{
ownvec<Extern> exports = instance->exports();
Func* exported_func = exports[0]->func();
constexpr int kArg = 123;
vec<Val> args = vec<Val>::make(Val::i32(kArg));
vec<Val> results = vec<Val>::make_uninitialized(1);
exported_func->call(args, results);
EXPECT_EQ(base_summand + kArg, results[0].i32());
// Host data should survive destruction of the own<> pointer.
exported_func->set_host_info(kHostData);
}
{
ownvec<Extern> exports = instance->exports();
Func* exported_func = exports[0]->func();
EXPECT_EQ(kHostData, exported_func->get_host_info());
}
// Test that a Func can have its own internal metadata, an {env}, and
// separate {host info}, without any of that interfering with each other.
g_host_data_finalized = 0;
g_capi_function_finalized = 0;
base_summand = 23;
constexpr int kFinalizerData = 345;
{
own<FuncType> capi_func_type = FuncType::make(
ownvec<ValType>::make(ValType::make(::wasm::ValKind::I32)),
ownvec<ValType>::make(ValType::make(::wasm::ValKind::I32)));
own<Func> capi_func = Func::make(
store(), capi_func_type.get(), &CapiFunction,
reinterpret_cast<void*>(base_summand), &FinalizeCapiFunction);
capi_func->set_host_info(reinterpret_cast<void*>(kFinalizerData),
&FinalizeHostData);
constexpr int kArg = 19;
vec<Val> args = vec<Val>::make(Val::i32(kArg));
vec<Val> results = vec<Val>::make_uninitialized(1);
capi_func->call(args, results);
EXPECT_EQ(base_summand + kArg, results[0].i32());
}
instance.reset();
Shutdown();
EXPECT_EQ(base_summand, g_capi_function_finalized);
EXPECT_EQ(kFinalizerData, g_host_data_finalized);
}
} // namespace wasm
} // namespace internal
} // namespace v8

215
deps/v8/test/wasm-api-tests/globals.cc vendored Normal file
View File

@ -0,0 +1,215 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "test/wasm-api-tests/wasm-api-test.h"
namespace v8 {
namespace internal {
namespace wasm {
using ::wasm::GlobalType;
TEST_F(WasmCapiTest, Globals) {
const bool kMutable = true;
const bool kImmutable = false;
// Define imported and exported globals in the module.
const uint32_t cfi_index = builder()->AddGlobalImport(
base::CStrVector("const f32"), kWasmF32, kImmutable);
const uint32_t cii_index = builder()->AddGlobalImport(
base::CStrVector("const i64"), kWasmI64, kImmutable);
const uint32_t vfi_index = builder()->AddGlobalImport(
base::CStrVector("var f32"), kWasmF32, kMutable);
const uint32_t vii_index = builder()->AddGlobalImport(
base::CStrVector("var i64"), kWasmI64, kMutable);
const int kNumImported = 4;
const uint32_t cfe_index =
kNumImported +
builder()->AddExportedGlobal(kWasmF32, kImmutable, WasmInitExpr(5.f),
base::CStrVector("const f32"));
const uint32_t cie_index =
kNumImported + builder()->AddExportedGlobal(
kWasmI64, kImmutable, WasmInitExpr(int64_t{6}),
base::CStrVector("const i64"));
const uint32_t vfe_index =
kNumImported + builder()->AddExportedGlobal(kWasmF32, kMutable,
WasmInitExpr(7.f),
base::CStrVector("var f32"));
const uint32_t vie_index =
kNumImported + builder()->AddExportedGlobal(kWasmI64, kMutable,
WasmInitExpr(int64_t{8}),
base::CStrVector("var i64"));
// Define functions for inspecting globals.
ValueType f32_type[] = {kWasmF32};
ValueType i64_type[] = {kWasmI64};
FunctionSig return_f32(1, 0, f32_type);
FunctionSig return_i64(1, 0, i64_type);
uint8_t gcfi[] = {WASM_GLOBAL_GET(cfi_index)};
AddExportedFunction(base::CStrVector("get const f32 import"), gcfi,
sizeof(gcfi), &return_f32);
uint8_t gcii[] = {WASM_GLOBAL_GET(cii_index)};
AddExportedFunction(base::CStrVector("get const i64 import"), gcii,
sizeof(gcii), &return_i64);
uint8_t gvfi[] = {WASM_GLOBAL_GET(vfi_index)};
AddExportedFunction(base::CStrVector("get var f32 import"), gvfi,
sizeof(gvfi), &return_f32);
uint8_t gvii[] = {WASM_GLOBAL_GET(vii_index)};
AddExportedFunction(base::CStrVector("get var i64 import"), gvii,
sizeof(gvii), &return_i64);
uint8_t gcfe[] = {WASM_GLOBAL_GET(cfe_index)};
AddExportedFunction(base::CStrVector("get const f32 export"), gcfe,
sizeof(gcfe), &return_f32);
uint8_t gcie[] = {WASM_GLOBAL_GET(cie_index)};
AddExportedFunction(base::CStrVector("get const i64 export"), gcie,
sizeof(gcie), &return_i64);
uint8_t gvfe[] = {WASM_GLOBAL_GET(vfe_index)};
AddExportedFunction(base::CStrVector("get var f32 export"), gvfe,
sizeof(gvfe), &return_f32);
uint8_t gvie[] = {WASM_GLOBAL_GET(vie_index)};
AddExportedFunction(base::CStrVector("get var i64 export"), gvie,
sizeof(gvie), &return_i64);
// Define functions for manipulating globals.
FunctionSig param_f32(0, 1, f32_type);
FunctionSig param_i64(0, 1, i64_type);
uint8_t svfi[] = {WASM_GLOBAL_SET(vfi_index, WASM_LOCAL_GET(0))};
AddExportedFunction(base::CStrVector("set var f32 import"), svfi,
sizeof(svfi), &param_f32);
uint8_t svii[] = {WASM_GLOBAL_SET(vii_index, WASM_LOCAL_GET(0))};
AddExportedFunction(base::CStrVector("set var i64 import"), svii,
sizeof(svii), &param_i64);
uint8_t svfe[] = {WASM_GLOBAL_SET(vfe_index, WASM_LOCAL_GET(0))};
AddExportedFunction(base::CStrVector("set var f32 export"), svfe,
sizeof(svfe), &param_f32);
uint8_t svie[] = {WASM_GLOBAL_SET(vie_index, WASM_LOCAL_GET(0))};
AddExportedFunction(base::CStrVector("set var i64 export"), svie,
sizeof(svie), &param_i64);
// Create imported globals.
own<GlobalType> const_f32_type = GlobalType::make(
ValType::make(::wasm::ValKind::F32), ::wasm::Mutability::CONST);
own<GlobalType> const_i64_type = GlobalType::make(
ValType::make(::wasm::ValKind::I64), ::wasm::Mutability::CONST);
own<GlobalType> var_f32_type = GlobalType::make(
ValType::make(::wasm::ValKind::F32), ::wasm::Mutability::VAR);
own<GlobalType> var_i64_type = GlobalType::make(
ValType::make(::wasm::ValKind::I64), ::wasm::Mutability::VAR);
own<Global> const_f32_import =
Global::make(store(), const_f32_type.get(), Val::f32(1));
own<Global> const_i64_import =
Global::make(store(), const_i64_type.get(), Val::i64(2));
own<Global> var_f32_import =
Global::make(store(), var_f32_type.get(), Val::f32(3));
own<Global> var_i64_import =
Global::make(store(), var_i64_type.get(), Val::i64(4));
vec<Extern*> imports =
vec<Extern*>::make(const_f32_import.get(), const_i64_import.get(),
var_f32_import.get(), var_i64_import.get());
Instantiate(imports);
// Extract exports.
size_t i = 0;
Global* const_f32_export = GetExportedGlobal(i++);
Global* const_i64_export = GetExportedGlobal(i++);
Global* var_f32_export = GetExportedGlobal(i++);
Global* var_i64_export = GetExportedGlobal(i++);
Func* get_const_f32_import = GetExportedFunction(i++);
Func* get_const_i64_import = GetExportedFunction(i++);
Func* get_var_f32_import = GetExportedFunction(i++);
Func* get_var_i64_import = GetExportedFunction(i++);
Func* get_const_f32_export = GetExportedFunction(i++);
Func* get_const_i64_export = GetExportedFunction(i++);
Func* get_var_f32_export = GetExportedFunction(i++);
Func* get_var_i64_export = GetExportedFunction(i++);
Func* set_var_f32_import = GetExportedFunction(i++);
Func* set_var_i64_import = GetExportedFunction(i++);
Func* set_var_f32_export = GetExportedFunction(i++);
Func* set_var_i64_export = GetExportedFunction(i++);
// Try cloning.
EXPECT_TRUE(var_f32_import->copy()->same(var_f32_import.get()));
// Check initial values.
EXPECT_EQ(1.f, const_f32_import->get().f32());
EXPECT_EQ(2, const_i64_import->get().i64());
EXPECT_EQ(3.f, var_f32_import->get().f32());
EXPECT_EQ(4, var_i64_import->get().i64());
EXPECT_EQ(5.f, const_f32_export->get().f32());
EXPECT_EQ(6, const_i64_export->get().i64());
EXPECT_EQ(7.f, var_f32_export->get().f32());
EXPECT_EQ(8, var_i64_export->get().i64());
vec<Val> result = vec<Val>::make_uninitialized(1);
vec<Val> empty_args = vec<Val>::make_uninitialized();
get_const_f32_import->call(empty_args, result);
EXPECT_EQ(1.f, result[0].f32());
get_const_i64_import->call(empty_args, result);
EXPECT_EQ(2, result[0].i64());
get_var_f32_import->call(empty_args, result);
EXPECT_EQ(3.f, result[0].f32());
get_var_i64_import->call(empty_args, result);
EXPECT_EQ(4, result[0].i64());
get_const_f32_export->call(empty_args, result);
EXPECT_EQ(5.f, result[0].f32());
get_const_i64_export->call(empty_args, result);
EXPECT_EQ(6, result[0].i64());
get_var_f32_export->call(empty_args, result);
EXPECT_EQ(7.f, result[0].f32());
get_var_i64_export->call(empty_args, result);
EXPECT_EQ(8, result[0].i64());
// Modify variables through the API and check again.
var_f32_import->set(Val::f32(33));
var_i64_import->set(Val::i64(34));
var_f32_export->set(Val::f32(35));
var_i64_export->set(Val::i64(36));
EXPECT_EQ(33.f, var_f32_import->get().f32());
EXPECT_EQ(34, var_i64_import->get().i64());
EXPECT_EQ(35.f, var_f32_export->get().f32());
EXPECT_EQ(36, var_i64_export->get().i64());
get_var_f32_import->call(empty_args, result);
EXPECT_EQ(33.f, result[0].f32());
get_var_i64_import->call(empty_args, result);
EXPECT_EQ(34, result[0].i64());
get_var_f32_export->call(empty_args, result);
EXPECT_EQ(35.f, result[0].f32());
get_var_i64_export->call(empty_args, result);
EXPECT_EQ(36, result[0].i64());
// Modify variables through calls and check again.
vec<Val> empty_rets = vec<Val>::make_uninitialized();
vec<Val> args = vec<Val>::make_uninitialized(1);
args[0] = Val::f32(73);
set_var_f32_import->call(args, empty_rets);
args[0] = Val::i64(74);
set_var_i64_import->call(args, empty_rets);
args[0] = Val::f32(75);
set_var_f32_export->call(args, empty_rets);
args[0] = Val::i64(76);
set_var_i64_export->call(args, empty_rets);
EXPECT_EQ(73.f, var_f32_import->get().f32());
EXPECT_EQ(74, var_i64_import->get().i64());
EXPECT_EQ(75.f, var_f32_export->get().f32());
EXPECT_EQ(76, var_i64_export->get().i64());
get_var_f32_import->call(empty_args, result);
EXPECT_EQ(73.f, result[0].f32());
get_var_i64_import->call(empty_args, result);
EXPECT_EQ(74, result[0].i64());
get_var_f32_export->call(empty_args, result);
EXPECT_EQ(75.f, result[0].f32());
get_var_i64_export->call(empty_args, result);
EXPECT_EQ(76, result[0].i64());
}
} // namespace wasm
} // namespace internal
} // namespace v8

184
deps/v8/test/wasm-api-tests/hostref.cc vendored Normal file
View File

@ -0,0 +1,184 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <iostream>
#include "test/wasm-api-tests/wasm-api-test.h"
namespace v8 {
namespace internal {
namespace wasm {
using ::wasm::Frame;
using ::wasm::Message;
namespace {
own<Trap> IdentityCallback(const vec<Val>& args, vec<Val>& results) {
results[0] = args[0].copy();
return nullptr;
}
} // namespace
TEST_F(WasmCapiTest, HostRef) {
ValueType rr_reps[] = {kWasmExternRef, kWasmExternRef};
ValueType ri_reps[] = {kWasmExternRef, kWasmI32};
ValueType ir_reps[] = {kWasmI32, kWasmExternRef};
// Naming convention: result_params_sig.
FunctionSig r_r_sig(1, 1, rr_reps);
FunctionSig v_r_sig(0, 1, rr_reps);
FunctionSig r_v_sig(1, 0, rr_reps);
FunctionSig v_ir_sig(0, 2, ir_reps);
FunctionSig r_i_sig(1, 1, ri_reps);
uint32_t func_index = builder()->AddImport(base::CStrVector("f"), &r_r_sig);
const bool kMutable = true;
uint32_t global_index = builder()->AddExportedGlobal(
kWasmExternRef, kMutable, WasmInitExpr::RefNullConst(kWasmExternRef),
base::CStrVector("global"));
uint32_t table_index = builder()->AddTable(kWasmExternRef, 10);
builder()->AddExport(base::CStrVector("table"), kExternalTable, table_index);
uint8_t global_set_code[] = {
WASM_GLOBAL_SET(global_index, WASM_LOCAL_GET(0))};
AddExportedFunction(base::CStrVector("global.set"), global_set_code,
sizeof(global_set_code), &v_r_sig);
uint8_t global_get_code[] = {WASM_GLOBAL_GET(global_index)};
AddExportedFunction(base::CStrVector("global.get"), global_get_code,
sizeof(global_get_code), &r_v_sig);
uint8_t table_set_code[] = {
WASM_TABLE_SET(table_index, WASM_LOCAL_GET(0), WASM_LOCAL_GET(1))};
AddExportedFunction(base::CStrVector("table.set"), table_set_code,
sizeof(table_set_code), &v_ir_sig);
uint8_t table_get_code[] = {WASM_TABLE_GET(table_index, WASM_LOCAL_GET(0))};
AddExportedFunction(base::CStrVector("table.get"), table_get_code,
sizeof(table_get_code), &r_i_sig);
uint8_t func_call_code[] = {
WASM_CALL_FUNCTION(func_index, WASM_LOCAL_GET(0))};
AddExportedFunction(base::CStrVector("func.call"), func_call_code,
sizeof(func_call_code), &r_r_sig);
own<FuncType> func_type = FuncType::make(
ownvec<ValType>::make(ValType::make(::wasm::ValKind::EXTERNREF)),
ownvec<ValType>::make(ValType::make(::wasm::ValKind::EXTERNREF)));
own<Func> callback = Func::make(store(), func_type.get(), IdentityCallback);
vec<Extern*> imports = vec<Extern*>::make(callback.get());
Instantiate(imports);
Global* global = GetExportedGlobal(0);
Table* table = GetExportedTable(1);
const Func* global_set = GetExportedFunction(2);
const Func* global_get = GetExportedFunction(3);
const Func* table_set = GetExportedFunction(4);
const Func* table_get = GetExportedFunction(5);
const Func* func_call = GetExportedFunction(6);
own<Foreign> host1 = Foreign::make(store());
own<Foreign> host2 = Foreign::make(store());
host1->set_host_info(reinterpret_cast<void*>(1));
host2->set_host_info(reinterpret_cast<void*>(2));
// Basic checks.
EXPECT_TRUE(host1->copy()->same(host1.get()));
EXPECT_TRUE(host2->copy()->same(host2.get()));
Val val = Val::ref(host1->copy());
EXPECT_TRUE(val.ref()->copy()->same(host1.get()));
own<Ref> ref = val.release_ref();
EXPECT_EQ(nullptr, val.ref());
EXPECT_TRUE(ref->copy()->same(host1.get()));
// Interact with the Global.
vec<Val> args = vec<Val>::make_uninitialized(2);
vec<Val> results = vec<Val>::make_uninitialized(1);
vec<Val> empty_args = vec<Val>::make_uninitialized();
vec<Val> empty_rets = vec<Val>::make_uninitialized();
own<Trap> trap = global_get->call(empty_args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(nullptr, results[0].release_ref());
args[0] = Val::ref(host1.get()->copy());
trap = global_set->call(args, empty_rets);
EXPECT_EQ(nullptr, trap);
trap = global_get->call(empty_args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_TRUE(results[0].release_ref()->same(host1.get()));
args[0] = Val::ref(host2.get()->copy());
trap = global_set->call(args, empty_rets);
EXPECT_EQ(nullptr, trap);
trap = global_get->call(empty_args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_TRUE(results[0].release_ref()->same(host2.get()));
args[0] = Val::ref(own<Ref>());
trap = global_set->call(args, empty_rets);
EXPECT_EQ(nullptr, trap);
trap = global_get->call(empty_args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(nullptr, results[0].release_ref());
EXPECT_EQ(nullptr, global->get().release_ref());
global->set(Val(host2->copy()));
trap = global_get->call(empty_args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_TRUE(results[0].release_ref()->same(host2.get()));
EXPECT_TRUE(global->get().release_ref()->same(host2.get()));
// Interact with the Table.
args[0] = Val::i32(0);
trap = table_get->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(nullptr, results[0].release_ref());
args[0] = Val::i32(1);
trap = table_get->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(nullptr, results[0].release_ref());
args[0] = Val::i32(0);
args[1] = Val::ref(host1.get()->copy());
trap = table_set->call(args, empty_rets);
EXPECT_EQ(nullptr, trap);
args[0] = Val::i32(1);
args[1] = Val::ref(host2.get()->copy());
trap = table_set->call(args, empty_rets);
EXPECT_EQ(nullptr, trap);
args[0] = Val::i32(0);
trap = table_get->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_TRUE(results[0].release_ref()->same(host1.get()));
args[0] = Val::i32(1);
trap = table_get->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_TRUE(results[0].release_ref()->same(host2.get()));
args[0] = Val::i32(0);
args[1] = Val::ref(own<Ref>());
trap = table_set->call(args, empty_rets);
EXPECT_EQ(nullptr, trap);
trap = table_get->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(nullptr, results[0].release_ref());
EXPECT_EQ(nullptr, table->get(2));
table->set(2, host1.get());
args[0] = Val::i32(2);
trap = table_get->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_TRUE(results[0].release_ref()->same(host1.get()));
EXPECT_TRUE(table->get(2)->same(host1.get()));
// Interact with the Function.
args[0] = Val::ref(own<Ref>());
trap = func_call->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(nullptr, results[0].release_ref());
args[0] = Val::ref(host1.get()->copy());
trap = func_call->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_TRUE(results[0].release_ref()->same(host1.get()));
args[0] = Val::ref(host2.get()->copy());
trap = func_call->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_TRUE(results[0].release_ref()->same(host2.get()));
}
} // namespace wasm
} // namespace internal
} // namespace v8

129
deps/v8/test/wasm-api-tests/memory.cc vendored Normal file
View File

@ -0,0 +1,129 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "test/wasm-api-tests/wasm-api-test.h"
namespace v8 {
namespace internal {
namespace wasm {
using ::wasm::Limits;
using ::wasm::MemoryType;
TEST_F(WasmCapiTest, Memory) {
builder()->AddMemory(2, 3);
builder()->AddExport(base::CStrVector("memory"), kExternalMemory, 0);
ValueType i32_type[] = {kWasmI32, kWasmI32};
FunctionSig return_i32(1, 0, i32_type);
FunctionSig param_i32_return_i32(1, 1, i32_type);
FunctionSig param_i32_i32(0, 2, i32_type);
uint8_t size_code[] = {WASM_MEMORY_SIZE};
AddExportedFunction(base::CStrVector("size"), size_code, sizeof(size_code),
&return_i32);
uint8_t load_code[] = {WASM_LOAD_MEM(MachineType::Int8(), WASM_LOCAL_GET(0))};
AddExportedFunction(base::CStrVector("load"), load_code, sizeof(load_code),
&param_i32_return_i32);
uint8_t store_code[] = {WASM_STORE_MEM(MachineType::Int8(), WASM_LOCAL_GET(0),
WASM_LOCAL_GET(1))};
AddExportedFunction(base::CStrVector("store"), store_code, sizeof(store_code),
&param_i32_i32);
uint8_t data[] = {0x1, 0x2, 0x3, 0x4};
builder()->AddDataSegment(data, sizeof(data), 0x1000);
vec<Extern*> imports = vec<Extern*>::make_uninitialized();
Instantiate(imports);
Memory* memory = GetExportedMemory(0);
Func* size_func = GetExportedFunction(1);
Func* load_func = GetExportedFunction(2);
Func* store_func = GetExportedFunction(3);
// Try cloning.
EXPECT_TRUE(memory->copy()->same(memory));
// Check initial state.
EXPECT_EQ(2u, memory->size());
EXPECT_EQ(0x20000u, memory->data_size());
EXPECT_EQ(0, memory->data()[0]);
EXPECT_EQ(1, memory->data()[0x1000]);
EXPECT_EQ(4, memory->data()[0x1003]);
vec<Val> args = vec<Val>::make_uninitialized(2);
vec<Val> result = vec<Val>::make_uninitialized(1);
// size == 2
vec<Val> empty_args = vec<Val>::make_uninitialized();
vec<Val> empty_rets = vec<Val>::make_uninitialized();
size_func->call(empty_args, result);
EXPECT_EQ(2, result[0].i32());
// load(0) == 0
args[0] = Val::i32(0x0);
load_func->call(args, result);
EXPECT_EQ(0, result[0].i32());
// load(0x1000) == 1
args[0] = Val::i32(0x1000);
load_func->call(args, result);
EXPECT_EQ(1, result[0].i32());
// load(0x1003) == 4
args[0] = Val::i32(0x1003);
load_func->call(args, result);
EXPECT_EQ(4, result[0].i32());
// load(0x1FFFF) == 0
args[0] = Val::i32(0x1FFFF);
load_func->call(args, result);
EXPECT_EQ(0, result[0].i32());
// load(0x20000) -> trap
args[0] = Val::i32(0x20000);
own<Trap> trap = load_func->call(args, result);
EXPECT_NE(nullptr, trap.get());
// Mutate memory.
memory->data()[0x1003] = 5;
args[0] = Val::i32(0x1002);
args[1] = Val::i32(6);
trap = store_func->call(args, empty_rets);
EXPECT_EQ(nullptr, trap.get());
args[0] = Val::i32(0x20000);
trap = store_func->call(args, empty_rets);
EXPECT_NE(nullptr, trap.get());
EXPECT_EQ(6, memory->data()[0x1002]);
EXPECT_EQ(5, memory->data()[0x1003]);
args[0] = Val::i32(0x1002);
load_func->call(args, result);
EXPECT_EQ(6, result[0].i32());
args[0] = Val::i32(0x1003);
load_func->call(args, result);
EXPECT_EQ(5, result[0].i32());
// Grow memory.
EXPECT_EQ(true, memory->grow(1));
EXPECT_EQ(3u, memory->size());
EXPECT_EQ(0x30000u, memory->data_size());
args[0] = Val::i32(0x20000);
trap = load_func->call(args, result);
EXPECT_EQ(nullptr, trap.get());
EXPECT_EQ(0, result[0].i32());
trap = store_func->call(args, empty_rets);
EXPECT_EQ(nullptr, trap.get());
args[0] = Val::i32(0x30000);
trap = load_func->call(args, result);
EXPECT_NE(nullptr, trap.get());
trap = store_func->call(args, empty_rets);
EXPECT_NE(nullptr, trap.get());
EXPECT_EQ(false, memory->grow(1));
EXPECT_EQ(true, memory->grow(0));
// Create standalone memory.
// TODO(wasm): Once Wasm allows multiple memories, turn this into an import.
own<MemoryType> mem_type = MemoryType::make(Limits(5, 5));
own<Memory> memory2 = Memory::make(store(), mem_type.get());
EXPECT_EQ(5u, memory2->size());
EXPECT_EQ(false, memory2->grow(1));
EXPECT_EQ(true, memory2->grow(0));
}
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,59 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "test/wasm-api-tests/wasm-api-test.h"
namespace v8 {
namespace internal {
namespace wasm {
using ::wasm::ValKind::I32;
using ::wasm::ValKind::I64;
namespace {
own<Trap> Callback(const vec<Val>& args, vec<Val>& results) {
results[0] = args[3].copy();
results[1] = args[1].copy();
results[2] = args[2].copy();
results[3] = args[0].copy();
return nullptr;
}
} // namespace
TEST_F(WasmCapiTest, MultiReturn) {
ValueType reps[] = {kWasmI32, kWasmI64, kWasmI64, kWasmI32,
kWasmI32, kWasmI64, kWasmI64, kWasmI32};
FunctionSig sig(4, 4, reps);
uint32_t func_index = builder()->AddImport(base::CStrVector("f"), &sig);
uint8_t code[] = {WASM_CALL_FUNCTION(func_index, WASM_LOCAL_GET(0),
WASM_LOCAL_GET(2), WASM_LOCAL_GET(1),
WASM_LOCAL_GET(3))};
AddExportedFunction(base::CStrVector("g"), code, sizeof(code), &sig);
ownvec<ValType> types =
ownvec<ValType>::make(ValType::make(I32), ValType::make(I64),
ValType::make(I64), ValType::make(I32));
own<FuncType> func_type =
FuncType::make(types.deep_copy(), types.deep_copy());
own<Func> callback = Func::make(store(), func_type.get(), Callback);
vec<Extern*> imports = vec<Extern*>::make(callback.get());
Instantiate(imports);
Func* run_func = GetExportedFunction(0);
vec<Val> args =
vec<Val>::make(Val::i32(1), Val::i64(2), Val::i64(3), Val::i32(4));
vec<Val> results = vec<Val>::make_uninitialized(4);
own<Trap> trap = run_func->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(4, results[0].i32());
EXPECT_EQ(3, results[1].i64());
EXPECT_EQ(2, results[2].i64());
EXPECT_EQ(1, results[3].i32());
}
} // namespace wasm
} // namespace internal
} // namespace v8

111
deps/v8/test/wasm-api-tests/reflect.cc vendored Normal file
View File

@ -0,0 +1,111 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "test/wasm-api-tests/wasm-api-test.h"
#include "src/execution/isolate.h"
#include "src/heap/heap.h"
#include "src/wasm/c-api.h"
namespace v8 {
namespace internal {
namespace wasm {
using ::wasm::ExportType;
using ::wasm::Extern;
using ::wasm::GlobalType;
using ::wasm::MemoryType;
using ::wasm::TableType;
namespace {
const char* kFuncName = "func1";
const char* kGlobalName = "global2";
const char* kTableName = "table3";
const char* kMemoryName = "memory4";
void ExpectName(const char* expected, const ::wasm::Name& name) {
size_t len = strlen(expected);
EXPECT_EQ(len, name.size());
EXPECT_EQ(0, strncmp(expected, name.get(), len));
}
} // namespace
TEST_F(WasmCapiTest, Reflect) {
// Create a module exporting a function, a global, a table, and a memory.
uint8_t code[] = {WASM_UNREACHABLE};
ValueType types[] = {kWasmI32, kWasmExternRef, kWasmI32,
kWasmI64, kWasmF32, kWasmF64};
FunctionSig sig(2, 4, types);
AddExportedFunction(base::CStrVector(kFuncName), code, sizeof(code), &sig);
builder()->AddExportedGlobal(kWasmF64, false, WasmInitExpr(0.0),
base::CStrVector(kGlobalName));
builder()->AddTable(kWasmFuncRef, 12, 12);
builder()->AddExport(base::CStrVector(kTableName), kExternalTable, 0);
builder()->AddMemory(1);
builder()->AddExport(base::CStrVector(kMemoryName), kExternalMemory, 0);
vec<Extern*> imports = vec<Extern*>::make_uninitialized();
Instantiate(imports);
ownvec<ExportType> export_types = module()->exports();
const ownvec<Extern>& exports = this->exports();
EXPECT_EQ(exports.size(), export_types.size());
EXPECT_EQ(4u, exports.size());
for (size_t i = 0; i < exports.size(); i++) {
::wasm::ExternKind kind = exports[i]->kind();
const ::wasm::ExternType* extern_type = export_types[i]->type();
EXPECT_EQ(kind, extern_type->kind());
if (kind == ::wasm::ExternKind::FUNC) {
ExpectName(kFuncName, export_types[i]->name());
const FuncType* type = extern_type->func();
const ownvec<ValType>& params = type->params();
EXPECT_EQ(4u, params.size());
EXPECT_EQ(::wasm::ValKind::I32, params[0]->kind());
EXPECT_EQ(::wasm::ValKind::I64, params[1]->kind());
EXPECT_EQ(::wasm::ValKind::F32, params[2]->kind());
EXPECT_EQ(::wasm::ValKind::F64, params[3]->kind());
const ownvec<ValType>& results = type->results();
EXPECT_EQ(2u, results.size());
EXPECT_EQ(::wasm::ValKind::I32, results[0]->kind());
EXPECT_EQ(::wasm::ValKind::EXTERNREF, results[1]->kind());
const Func* func = exports[i]->func();
EXPECT_EQ(4u, func->param_arity());
EXPECT_EQ(2u, func->result_arity());
} else if (kind == ::wasm::ExternKind::GLOBAL) {
ExpectName(kGlobalName, export_types[i]->name());
const GlobalType* type = extern_type->global();
EXPECT_EQ(::wasm::ValKind::F64, type->content()->kind());
EXPECT_EQ(::wasm::Mutability::CONST, type->mutability());
} else if (kind == ::wasm::ExternKind::TABLE) {
ExpectName(kTableName, export_types[i]->name());
const TableType* type = extern_type->table();
EXPECT_EQ(::wasm::ValKind::FUNCREF, type->element()->kind());
::wasm::Limits limits = type->limits();
EXPECT_EQ(12u, limits.min);
EXPECT_EQ(12u, limits.max);
} else if (kind == ::wasm::ExternKind::MEMORY) {
ExpectName(kMemoryName, export_types[i]->name());
const MemoryType* type = extern_type->memory();
::wasm::Limits limits = type->limits();
EXPECT_EQ(1u, limits.min);
EXPECT_EQ(std::numeric_limits<uint32_t>::max(), limits.max);
} else {
UNREACHABLE();
}
}
}
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,28 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "test/wasm-api-tests/wasm-api-test.h"
namespace v8 {
namespace internal {
namespace wasm {
TEST_F(WasmCapiTest, Regressions) {
FunctionSig sig(0, 0, nullptr);
WasmFunctionBuilder* start_func = builder()->AddFunction(&sig);
start_func->EmitCode({WASM_UNREACHABLE, WASM_END});
builder()->MarkStartFunction(start_func);
builder()->AddImport(base::CStrVector("dummy"), &sig);
// Ensure we can validate.
bool valid = Validate();
EXPECT_EQ(valid, true);
// Ensure we can compile after validating.
Compile();
}
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,26 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "include/v8-initialization.h"
#include "src/flags/flags.h"
#include "src/trap-handler/trap-handler.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
int main(int argc, char** argv) {
// Don't catch SEH exceptions and continue as the following tests might hang
// in an broken environment on windows.
GTEST_FLAG_SET(catch_exceptions, false);
testing::InitGoogleMock(&argc, argv);
v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
v8::V8::InitializeExternalStartupData(argv[0]);
if (V8_TRAP_HANDLER_SUPPORTED) {
constexpr bool kUseDefaultTrapHandler = true;
if (!v8::V8::EnableWebAssemblyTrapHandler(kUseDefaultTrapHandler)) {
FATAL("Could not register trap handler");
}
}
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,75 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/common/ptr-compr-inl.h"
#include "src/execution/isolate.h"
#include "src/wasm/c-api.h"
#include "test/wasm-api-tests/wasm-api-test.h"
namespace v8 {
namespace internal {
namespace wasm {
namespace {
bool g_callback_called;
own<Trap> Callback(const vec<Val>& args, vec<Val>& results) {
g_callback_called = true;
return nullptr;
}
} // namespace
TEST_F(WasmCapiTest, Serialize) {
FunctionSig sig(0, 0, nullptr);
uint32_t callback_index =
builder()->AddImport(base::CStrVector("callback"), &sig);
uint8_t code[] = {WASM_CALL_FUNCTION0(callback_index)};
AddExportedFunction(base::CStrVector("run"), code, sizeof(code), &sig);
Compile();
vec<byte_t> serialized = module()->serialize();
EXPECT_TRUE(serialized); // Serialization succeeded.
// We reset the module and collect it to make sure the NativeModuleCache does
// not contain it anymore. Otherwise deserialization will not happen.
ResetModule();
{
Isolate* isolate =
reinterpret_cast<::wasm::StoreImpl*>(store())->i_isolate();
v8::Isolate::Scope isolate_scope(reinterpret_cast<v8::Isolate*>(isolate));
// This method might be called on a thread that's not bound to any Isolate
// and thus pointer compression schemes might have cage base value unset.
// Ensure cage bases are initialized so that the V8 heap can be accessed.
i::PtrComprCageAccessScope ptr_compr_cage_access_scope(isolate);
Heap* heap = isolate->heap();
heap->PreciseCollectAllGarbage(GCFlag::kForced,
GarbageCollectionReason::kTesting);
heap->PreciseCollectAllGarbage(GCFlag::kForced,
GarbageCollectionReason::kTesting);
}
own<Module> deserialized = Module::deserialize(store(), serialized);
// Try to serialize the module again. This can fail if deserialization does
// not set up a clean state.
deserialized->serialize();
own<FuncType> callback_type =
FuncType::make(ownvec<ValType>::make(), ownvec<ValType>::make());
own<Func> callback = Func::make(store(), callback_type.get(), Callback);
vec<Extern*> imports = vec<Extern*>::make(callback.get());
own<Instance> instance = Instance::make(store(), deserialized.get(), imports);
ownvec<Extern> exports = instance->exports();
Func* run = exports[0]->func();
g_callback_called = false;
auto rets = vec<Val>::make_uninitialized();
run->call(vec<Val>::make_uninitialized(), rets);
EXPECT_TRUE(g_callback_called);
}
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,61 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "test/wasm-api-tests/wasm-api-test.h"
namespace v8 {
namespace internal {
namespace wasm {
namespace {
own<Trap> DummyCallback(const vec<Val>& args, vec<Val>& results) {
return nullptr;
}
} // namespace
TEST_F(WasmCapiTest, StartupErrors) {
FunctionSig sig(0, 0, nullptr);
WasmFunctionBuilder* start_func = builder()->AddFunction(&sig);
start_func->EmitCode({WASM_UNREACHABLE, WASM_END});
builder()->MarkStartFunction(start_func);
builder()->AddImport(base::CStrVector("dummy"), &sig);
Compile();
own<Trap> trap;
// Try to make an Instance with non-matching imports.
own<Func> bad_func = Func::make(store(), cpp_i_i_sig(), DummyCallback);
vec<Extern*> bad_imports = vec<Extern*>::make(bad_func.get());
own<Instance> instance =
Instance::make(store(), module(), bad_imports, &trap);
EXPECT_EQ(nullptr, instance);
EXPECT_NE(nullptr, trap);
EXPECT_STREQ(
"Uncaught LinkError: instantiation: Import #0 \"\" \"dummy\": "
"imported function does not match the expected type",
trap->message().get());
EXPECT_EQ(nullptr, trap->origin());
// Don't crash if there is no {trap}.
instance = Instance::make(store(), module(), bad_imports, nullptr);
EXPECT_EQ(nullptr, instance);
// Try to make an instance with a {start} function that traps.
own<FuncType> good_sig =
FuncType::make(ownvec<ValType>::make(), ownvec<ValType>::make());
own<Func> good_func = Func::make(store(), good_sig.get(), DummyCallback);
vec<Extern*> good_imports = vec<Extern*>::make(good_func.get());
instance = Instance::make(store(), module(), good_imports, &trap);
EXPECT_EQ(nullptr, instance);
EXPECT_NE(nullptr, trap);
EXPECT_STREQ("Uncaught RuntimeError: unreachable", trap->message().get());
EXPECT_NE(nullptr, trap->origin());
// Don't crash if there is no {trap}.
instance = Instance::make(store(), module(), good_imports, nullptr);
EXPECT_EQ(nullptr, instance);
}
} // namespace wasm
} // namespace internal
} // namespace v8

125
deps/v8/test/wasm-api-tests/table.cc vendored Normal file
View File

@ -0,0 +1,125 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "test/wasm-api-tests/wasm-api-test.h"
namespace v8 {
namespace internal {
namespace wasm {
using ::wasm::Extern;
using ::wasm::Limits;
using ::wasm::TableType;
using ::wasm::ValKind::FUNCREF;
namespace {
own<Trap> Negate(const vec<Val>& args, vec<Val>& results) {
results[0] = Val(-args[0].i32());
return nullptr;
}
void ExpectTrap(const Func* func, int arg1, int arg2) {
vec<Val> args = vec<Val>::make(Val::i32(arg1), Val::i32(arg2));
vec<Val> results = vec<Val>::make_uninitialized(1);
own<Trap> trap = func->call(args, results);
EXPECT_NE(nullptr, trap);
}
void ExpectResult(int expected, const Func* func, int arg1, int arg2) {
vec<Val> args = vec<Val>::make(Val::i32(arg1), Val::i32(arg2));
vec<Val> results = vec<Val>::make_uninitialized(1);
own<Trap> trap = func->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(expected, results[0].i32());
}
} // namespace
TEST_F(WasmCapiTest, Table) {
const uint32_t table_index = builder()->AddTable(kWasmFuncRef, 2, 10);
builder()->AddExport(base::CStrVector("table"), kExternalTable, table_index);
const ModuleTypeIndex sig_i_i_index =
builder()->AddSignature(wasm_i_i_sig(), true);
ValueType reps[] = {kWasmI32, kWasmI32, kWasmI32};
FunctionSig call_sig(1, 2, reps);
uint8_t call_code[] = {
WASM_CALL_INDIRECT(sig_i_i_index, WASM_LOCAL_GET(0), WASM_LOCAL_GET(1))};
AddExportedFunction(base::CStrVector("call_indirect"), call_code,
sizeof(call_code), &call_sig);
uint8_t f_code[] = {WASM_LOCAL_GET(0)};
AddExportedFunction(base::CStrVector("f"), f_code, sizeof(f_code),
wasm_i_i_sig());
uint8_t g_code[] = {WASM_I32V_1(42)};
AddExportedFunction(base::CStrVector("g"), g_code, sizeof(g_code),
wasm_i_i_sig());
// Set table[1] to {f}, which has function index 1.
builder()->SetIndirectFunction(
table_index, 1, 1,
WasmModuleBuilder::WasmElemSegment::kRelativeToImports);
vec<Extern*> imports = vec<Extern*>::make_uninitialized();
Instantiate(imports);
Table* table = GetExportedTable(0);
Func* call_indirect = GetExportedFunction(1);
Func* f = GetExportedFunction(2);
Func* g = GetExportedFunction(3);
own<Func> h = Func::make(store(), cpp_i_i_sig(), Negate);
// Try cloning.
EXPECT_TRUE(table->copy()->same(table));
// Check initial table state.
EXPECT_EQ(2u, table->size());
EXPECT_EQ(nullptr, table->get(0));
EXPECT_NE(nullptr, table->get(1));
ExpectTrap(call_indirect, 0, 0);
ExpectResult(7, call_indirect, 7, 1);
ExpectTrap(call_indirect, 0, 2);
// Mutate table.
EXPECT_TRUE(table->set(0, g));
EXPECT_TRUE(table->set(1, nullptr));
EXPECT_FALSE(table->set(2, f));
EXPECT_NE(nullptr, table->get(0));
EXPECT_EQ(nullptr, table->get(1));
ExpectResult(42, call_indirect, 7, 0);
ExpectTrap(call_indirect, 0, 1);
ExpectTrap(call_indirect, 0, 2);
// Grow table.
EXPECT_TRUE(table->grow(3));
EXPECT_EQ(5u, table->size());
EXPECT_TRUE(table->set(2, f));
EXPECT_TRUE(table->set(3, h.get()));
EXPECT_FALSE(table->set(5, nullptr));
EXPECT_NE(nullptr, table->get(2));
EXPECT_NE(nullptr, table->get(3));
EXPECT_EQ(nullptr, table->get(4));
ExpectResult(5, call_indirect, 5, 2);
ExpectResult(-6, call_indirect, 6, 3);
ExpectTrap(call_indirect, 0, 4);
ExpectTrap(call_indirect, 0, 5);
EXPECT_TRUE(table->grow(2, f));
EXPECT_EQ(7u, table->size());
EXPECT_NE(nullptr, table->get(5));
EXPECT_NE(nullptr, table->get(6));
EXPECT_FALSE(table->grow(5));
EXPECT_TRUE(table->grow(3));
EXPECT_TRUE(table->grow(0));
// Create standalone table.
// TODO(wasm+): Once Wasm allows multiple tables, turn this into import.
own<TableType> tabletype =
TableType::make(ValType::make(FUNCREF), Limits(5, 5));
own<Table> table2 = Table::make(store(), tabletype.get());
EXPECT_EQ(5u, table2->size());
EXPECT_FALSE(table2->grow(1));
EXPECT_TRUE(table2->grow(0));
}
} // namespace wasm
} // namespace internal
} // namespace v8

78
deps/v8/test/wasm-api-tests/testcfg.py vendored Normal file
View File

@ -0,0 +1,78 @@
# Copyright 2019 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
from testrunner.local import command
from testrunner.local import utils
from testrunner.local import testsuite
from testrunner.objects import testcase
SHELL = "wasm_api_tests"
class VariantsGenerator(testsuite.VariantsGenerator):
def _get_variants(self, test):
return self._standard_variant
class TestLoader(testsuite.TestLoader):
def _list_test_filenames(self):
output = None
for i in range(3): # Try 3 times in case of errors.
args = ['--gtest_list_tests'] + self.test_config.extra_flags
cmd = self.ctx.command(
cmd_prefix=self.test_config.command_prefix,
shell=self.ctx.platform_shell(SHELL, args,
self.test_config.shell_dir),
args=args)
output = cmd.execute()
if output.exit_code == 0:
break
print("Test executable failed to list the tests (try %d).\n\nCmd:" % i)
print(cmd)
print("\nStdout:")
print(output.stdout)
print("\nStderr:")
print(output.stderr)
print("\nExit code: %d" % output.exit_code)
else:
raise Exception("Test executable failed to list the tests.")
# TODO create an ExecutableTestLoader for refactoring this similar to
# JSTestLoader.
test_names = []
test_case = ''
for line in output.stdout.splitlines():
test_desc = line.strip().split()[0]
if test_desc.endswith('.'):
test_case = test_desc
elif test_case and test_desc:
test_names.append(test_case + test_desc)
return sorted(test_names)
class TestSuite(testsuite.TestSuite):
def _test_loader_class(self):
return TestLoader
def _test_class(self):
return TestCase
def _variants_gen_class(self):
return VariantsGenerator
class TestCase(testcase.TestCase):
def _get_suite_flags(self):
return (
[f"--gtest_filter={self.name}"] +
[f"--gtest_random_seed={self.random_seed}"] +
["--gtest_print_time=0"]
)
def get_shell(self):
return SHELL

107
deps/v8/test/wasm-api-tests/threads.cc vendored Normal file
View File

@ -0,0 +1,107 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "test/wasm-api-tests/wasm-api-test.h"
#include <mutex>
#include <thread>
namespace v8 {
namespace internal {
namespace wasm {
using ::wasm::Shared;
namespace {
const int kNumThreads = 10;
const int kIterationsPerThread = 3;
int g_traces;
own<Trap> Callback(void* env, const vec<Val>& args, vec<Val>& results) {
std::lock_guard<std::mutex> lock(*reinterpret_cast<std::mutex*>(env));
g_traces += args[0].i32();
return nullptr;
}
void Main(Engine* engine, Shared<Module>* shared, std::mutex* mutex, int id) {
own<Store> store = Store::make(engine);
own<Module> module = Module::obtain(store.get(), shared);
EXPECT_NE(nullptr, module.get());
for (int i = 0; i < kIterationsPerThread; i++) {
std::this_thread::sleep_for(std::chrono::microseconds(100));
// Create imports.
own<FuncType> func_type = FuncType::make(
ownvec<ValType>::make(ValType::make(::wasm::ValKind::I32)),
ownvec<ValType>::make());
own<Func> func = Func::make(store.get(), func_type.get(), Callback, mutex);
own<::wasm::GlobalType> global_type = ::wasm::GlobalType::make(
ValType::make(::wasm::ValKind::I32), ::wasm::Mutability::CONST);
own<Global> global =
Global::make(store.get(), global_type.get(), Val::i32(id));
// Instantiate and run.
// With the current implementation of the WasmModuleBuilder, global
// imports always come before function imports, regardless of the
// order of builder()->Add*Import() calls below.
vec<Extern*> imports = vec<Extern*>::make(global.get(), func.get());
own<Instance> instance = Instance::make(store.get(), module.get(), imports);
ownvec<Extern> exports = instance->exports();
Func* run_func = exports[0]->func();
vec<Val> rets = vec<Val>::make_uninitialized();
vec<Val> args = vec<Val>::make_uninitialized();
run_func->call(args, rets);
}
}
} // namespace
TEST_F(WasmCapiTest, Threads) {
// Create module.
ValueType i32_type[] = {kWasmI32};
FunctionSig param_i32(0, 1, i32_type);
uint32_t callback_index =
builder()->AddImport(base::CStrVector("callback"), &param_i32);
uint32_t global_index =
builder()->AddGlobalImport(base::CStrVector("id"), kWasmI32, false);
uint8_t code[] = {
WASM_CALL_FUNCTION(callback_index, WASM_GLOBAL_GET(global_index))};
FunctionSig empty_sig(0, 0, nullptr);
AddExportedFunction(base::CStrVector("run"), code, sizeof(code), &empty_sig);
Compile();
own<Shared<Module>> shared = module()->share();
// Spawn threads.
g_traces = 0;
std::mutex mutex;
std::thread threads[kNumThreads];
for (int i = 0; i < kNumThreads; i++) {
threads[i] = std::thread(Main, engine(), shared.get(), &mutex, i);
}
for (int i = 0; i < kNumThreads; i++) {
threads[i].join();
}
// Each thread in each iteration adds its ID to {traces}, so in the end
// we expect kIterationsPerThread * sum([0, ..., kNumThreads-1]).
// Per Gauss:
const int kExpected =
kIterationsPerThread * (kNumThreads - 1) * kNumThreads / 2;
EXPECT_EQ(kExpected, g_traces);
}
TEST_F(WasmCapiTest, MultiStoresOneThread) {
// These Stores intentionally have overlapping, but non-nested lifetimes.
own<Store> store1 = Store::make(engine());
own<Store> store2 = Store::make(engine());
own<Store> store3 = Store::make(engine());
store1.reset();
store2.reset();
store3.reset();
}
} // namespace wasm
} // namespace internal
} // namespace v8

135
deps/v8/test/wasm-api-tests/traps.cc vendored Normal file
View File

@ -0,0 +1,135 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <iostream>
#include "src/execution/isolate.h"
#include "src/wasm/c-api.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-engine.h"
#include "test/wasm-api-tests/wasm-api-test.h"
namespace v8 {
namespace internal {
namespace wasm {
using ::wasm::Frame;
using ::wasm::Message;
namespace {
own<Trap> FailCallback(void* env, const vec<Val>& arg, vec<Val>& results) {
Store* store = reinterpret_cast<Store*>(env);
Message message = Message::make(std::string("callback abort"));
return Trap::make(store, message);
}
void ExpectMessage(const char* expected, const Message& message) {
size_t len = strlen(expected);
EXPECT_EQ(len, message.size());
EXPECT_EQ(0, strncmp(expected, message.get(), len));
}
} // namespace
TEST_F(WasmCapiTest, Traps) {
ValueType i32_type[] = {kWasmI32};
FunctionSig sig(1, 0, i32_type);
uint32_t callback_index =
builder()->AddImport(base::CStrVector("callback"), &sig);
uint8_t code[] = {WASM_CALL_FUNCTION0(callback_index)};
AddExportedFunction(base::CStrVector("callback"), code, sizeof(code), &sig);
uint8_t code2[] = {WASM_CALL_FUNCTION0(3)};
AddExportedFunction(base::CStrVector("unreachable"), code2, sizeof(code2),
&sig);
// The first constant is a 4-byte dummy so that the {unreachable} trap
// has a more interesting offset. This is called by code2.
uint8_t code3[] = {WASM_I32V_3(0), WASM_UNREACHABLE, WASM_I32V_1(1)};
AddFunction(code3, sizeof(code3), &sig);
// Check that traps returned from a C callback are uncatchable in Wasm.
uint8_t code4[] = {WASM_TRY_CATCH_ALL_T(
kWasmI32, WASM_CALL_FUNCTION0(callback_index), WASM_I32V(42))};
AddExportedFunction(base::CStrVector("uncatchable"), code4, sizeof(code4),
&sig);
own<FuncType> func_type = FuncType::make(
ownvec<ValType>::make(),
ownvec<ValType>::make(ValType::make(::wasm::ValKind::I32)));
own<Func> cpp_callback =
Func::make(store(), func_type.get(), FailCallback,
reinterpret_cast<void*>(store()), nullptr);
vec<Extern*> imports = vec<Extern*>::make(cpp_callback.get());
Instantiate(imports);
// Use internal machinery to parse the module to find the function offsets.
// This makes the test more robust than hardcoding them.
WasmDetectedFeatures unused_detected_features;
ModuleResult result =
DecodeWasmModule(WasmEnabledFeatures::All(), wire_bytes(), false,
ModuleOrigin::kWasmOrigin, &unused_detected_features);
ASSERT_TRUE(result.ok());
const WasmFunction* func1 = &result.value()->functions[1];
const WasmFunction* func2 = &result.value()->functions[2];
const WasmFunction* func3 = &result.value()->functions[3];
const uint32_t func1_offset = func1->code.offset();
const uint32_t func2_offset = func2->code.offset();
const uint32_t func3_offset = func3->code.offset();
Func* cpp_trapping_func = GetExportedFunction(0);
auto empty_rets = vec<Val>::make_uninitialized();
own<Trap> cpp_trap =
cpp_trapping_func->call(vec<Val>::make_uninitialized(), empty_rets);
EXPECT_NE(nullptr, cpp_trap.get());
ExpectMessage("Uncaught Error: callback abort", cpp_trap->message());
own<Frame> frame = cpp_trap->origin();
EXPECT_TRUE(frame->instance()->same(instance()));
EXPECT_EQ(1u, frame->func_index());
EXPECT_EQ(1u, frame->func_offset());
EXPECT_EQ(func1_offset + frame->func_offset(), frame->module_offset());
ownvec<Frame> trace = cpp_trap->trace();
EXPECT_EQ(1u, trace.size());
frame.reset(trace[0].release());
EXPECT_TRUE(frame->instance()->same(instance()));
EXPECT_EQ(1u, frame->func_index());
EXPECT_EQ(1u, frame->func_offset());
EXPECT_EQ(func1_offset + frame->func_offset(), frame->module_offset());
Func* wasm_trapping_func = GetExportedFunction(1);
own<Trap> wasm_trap =
wasm_trapping_func->call(vec<Val>::make_uninitialized(), empty_rets);
EXPECT_NE(nullptr, wasm_trap.get());
ExpectMessage("Uncaught RuntimeError: unreachable", wasm_trap->message());
frame = wasm_trap->origin();
EXPECT_TRUE(frame->instance()->same(instance()));
EXPECT_EQ(3u, frame->func_index());
EXPECT_EQ(5u, frame->func_offset());
EXPECT_EQ(func3_offset + frame->func_offset(), frame->module_offset());
trace = wasm_trap->trace();
EXPECT_EQ(2u, trace.size());
frame.reset(trace[0].release());
EXPECT_TRUE(frame->instance()->same(instance()));
EXPECT_EQ(3u, frame->func_index());
EXPECT_EQ(5u, frame->func_offset());
EXPECT_EQ(func3_offset + frame->func_offset(), frame->module_offset());
frame.reset(trace[1].release());
EXPECT_TRUE(frame->instance()->same(instance()));
EXPECT_EQ(2u, frame->func_index());
EXPECT_EQ(1u, frame->func_offset());
EXPECT_EQ(func2_offset + frame->func_offset(), frame->module_offset());
Func* wasm_uncatchable_func = GetExportedFunction(2);
vec<Val> args = vec<Val>::make_uninitialized();
vec<Val> results = vec<Val>::make(Val(3.14)); // Sentinel value.
own<Trap> uncatchable_trap = wasm_uncatchable_func->call(args, results);
EXPECT_NE(nullptr, uncatchable_trap.get());
EXPECT_EQ(::wasm::ValKind::F64, results[0].kind());
}
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,187 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TEST_WASM_API_TESTS_WASM_API_TEST_H_
#define TEST_WASM_API_TESTS_WASM_API_TEST_H_
#include "src/wasm/wasm-module-builder.h"
#include "src/wasm/wasm-opcodes.h"
#include "src/zone/accounting-allocator.h"
#include "src/zone/zone.h"
#include "test/common/wasm/wasm-macro-gen.h"
#include "testing/gtest/include/gtest/gtest.h"
#define LIBWASM_STATIC 1
#include "third_party/wasm-api/wasm.hh"
namespace v8 {
namespace internal {
namespace wasm {
using ::wasm::Engine;
using ::wasm::Extern;
using ::wasm::Foreign;
using ::wasm::Func;
using ::wasm::FuncType;
using ::wasm::Global;
using ::wasm::Instance;
using ::wasm::Memory;
using ::wasm::Module;
using ::wasm::own;
using ::wasm::ownvec;
using ::wasm::Ref;
using ::wasm::Store;
using ::wasm::Table;
using ::wasm::Trap;
using ::wasm::Val;
using ::wasm::ValType;
using ::wasm::vec;
class WasmCapiTest : public ::testing::Test {
public:
WasmCapiTest()
: Test(),
engine_(Engine::make()),
allocator_(std::make_unique<AccountingAllocator>()),
zone_(std::make_unique<Zone>(allocator_.get(), ZONE_NAME)),
wire_bytes_(zone_.get()),
builder_(zone_->New<WasmModuleBuilder>(zone_.get())),
exports_(ownvec<Extern>::make()),
binary_(vec<byte_t>::make()),
wasm_i_i_sig_(1, 1, wasm_i_i_sig_types_) {
store_ = Store::make(engine_.get());
cpp_i_i_sig_ = FuncType::make(
ownvec<ValType>::make(ValType::make(::wasm::ValKind::I32)),
ownvec<ValType>::make(ValType::make(::wasm::ValKind::I32)));
}
bool Validate() {
if (binary_.size() == 0) {
builder_->WriteTo(&wire_bytes_);
size_t size = wire_bytes_.end() - wire_bytes_.begin();
binary_ = vec<byte_t>::make(
size,
reinterpret_cast<byte_t*>(const_cast<uint8_t*>(wire_bytes_.begin())));
}
return Module::validate(store_.get(), binary_);
}
void Compile() {
if (binary_.size() == 0) {
builder_->WriteTo(&wire_bytes_);
size_t size = wire_bytes_.end() - wire_bytes_.begin();
binary_ = vec<byte_t>::make(
size,
reinterpret_cast<byte_t*>(const_cast<uint8_t*>(wire_bytes_.begin())));
}
module_ = Module::make(store_.get(), binary_);
DCHECK_NE(module_.get(), nullptr);
}
void Instantiate(vec<Extern*>& imports) {
Compile();
instance_ = Instance::make(store_.get(), module_.get(), imports);
DCHECK_NE(instance_.get(), nullptr);
exports_ = instance_->exports();
}
void AddExportedFunction(base::Vector<const char> name, uint8_t code[],
size_t code_size, FunctionSig* sig) {
WasmFunctionBuilder* fun = builder()->AddFunction(sig);
fun->EmitCode(code, static_cast<uint32_t>(code_size));
fun->Emit(kExprEnd);
builder()->AddExport(name, fun);
}
void AddFunction(uint8_t code[], size_t code_size, FunctionSig* sig) {
WasmFunctionBuilder* fun = builder()->AddFunction(sig);
fun->EmitCode(code, static_cast<uint32_t>(code_size));
fun->Emit(kExprEnd);
}
Func* GetExportedFunction(size_t index) {
DCHECK_GT(exports_.size(), index);
Extern* exported = exports_[index].get();
DCHECK_EQ(exported->kind(), ::wasm::ExternKind::FUNC);
Func* func = exported->func();
DCHECK_NE(func, nullptr);
return func;
}
Global* GetExportedGlobal(size_t index) {
DCHECK_GT(exports_.size(), index);
Extern* exported = exports_[index].get();
DCHECK_EQ(exported->kind(), ::wasm::ExternKind::GLOBAL);
Global* global = exported->global();
DCHECK_NE(global, nullptr);
return global;
}
Memory* GetExportedMemory(size_t index) {
DCHECK_GT(exports_.size(), index);
Extern* exported = exports_[index].get();
DCHECK_EQ(exported->kind(), ::wasm::ExternKind::MEMORY);
Memory* memory = exported->memory();
DCHECK_NE(memory, nullptr);
return memory;
}
Table* GetExportedTable(size_t index) {
DCHECK_GT(exports_.size(), index);
Extern* exported = exports_[index].get();
DCHECK_EQ(exported->kind(), ::wasm::ExternKind::TABLE);
Table* table = exported->table();
DCHECK_NE(table, nullptr);
return table;
}
void ResetModule() { module_.reset(); }
void Shutdown() {
exports_.reset();
instance_.reset();
module_.reset();
store_.reset();
builder_ = nullptr;
zone_.reset();
allocator_.reset();
engine_.reset();
}
WasmModuleBuilder* builder() { return builder_; }
Engine* engine() { return engine_.get(); }
Store* store() { return store_.get(); }
Module* module() { return module_.get(); }
Instance* instance() { return instance_.get(); }
const ownvec<Extern>& exports() { return exports_; }
base::Vector<const uint8_t> wire_bytes() {
return base::VectorOf(wire_bytes_);
}
FunctionSig* wasm_i_i_sig() { return &wasm_i_i_sig_; }
FuncType* cpp_i_i_sig() { return cpp_i_i_sig_.get(); }
private:
own<Engine> engine_;
std::unique_ptr<AccountingAllocator> allocator_;
std::unique_ptr<Zone> zone_;
ZoneBuffer wire_bytes_;
WasmModuleBuilder* builder_;
own<Store> store_;
own<Module> module_;
own<Instance> instance_;
ownvec<Extern> exports_;
vec<byte_t> binary_;
own<FuncType> cpp_i_i_sig_;
ValueType wasm_i_i_sig_types_[2] = {kWasmI32, kWasmI32};
FunctionSig wasm_i_i_sig_;
};
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // TEST_WASM_API_TESTS_WASM_API_TEST_H_

View File

@ -0,0 +1,17 @@
# Copyright 2019 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
[
# TODO(v8:7777): Change this once wasm is supported in jitless mode.
['not has_webassembly or variant == jitless', {
'*': [SKIP],
}], # not has_webassembly or variant == jitless
################################################################################
['variant == stress_snapshot', {
'*': [SKIP], # only relevant for mjsunit tests.
}],
]