3aa4f05d0c
Host info used to be stored on the global reference underlying a Ref; now it is stored in a JSWeakMap and hence tied to the lifetime of the actual object on V8's heap. Additionally, the internal metadata needed for C-API functions is now stored on the SharedFunctionInfo and no longer overlaps with the host info mechanism. Bonus content: Roll 6db391e: Remove a few more leftover uses of _enum types Change-Id: Ibb1fa4b0dd5157fef15c030bac705a11aa3beaea Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1768368 Reviewed-by: Andreas Haas <ahaas@chromium.org> Commit-Queue: Jakob Kummerow <jkummerow@chromium.org> Cr-Commit-Position: refs/heads/master@{#63400}
188 lines
6.7 KiB
C++
188 lines
6.7 KiB
C++
// 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
|