blob: 93da85460e19603e4b6d08d67e818cee1e0351cf [file] [log] [blame]
// 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, ZoneBuffer* wire_bytes, int iterations) {
size_t size = wire_bytes->end() - wire_bytes->begin();
vec<byte_t> binary = vec<byte_t>::make(
size, reinterpret_cast<byte_t*>(const_cast<byte*>(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);
own<Instance> instance = Instance::make(store, module.get(), 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);
Val args[] = {Val::i32(iteration)};
Val results[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; }
byte code[] = {WASM_RETURN1(WASM_GET_LOCAL(0))};
AddExportedFunction(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 Val args[], 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(CStrVector("f"), wasm_i_i_sig());
builder()->ExportImportedFunction(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::I32)),
ownvec<ValType>::make(ValType::make(::wasm::I32)));
own<Func> capi_func =
Func::make(store(), capi_func_type.get(), &CapiFunction,
reinterpret_cast<void*>(base_summand));
Extern* imports[] = {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;
Val args[] = {Val::i32(kArg)};
Val results[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::I32)),
ownvec<ValType>::make(ValType::make(::wasm::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;
Val args[] = {Val::i32(kArg)};
Val results[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