// 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(reinterpret_cast(data)); g_instances_finalized += iteration; } void FinalizeFunction(void* data) { int iteration = static_cast(reinterpret_cast(data)); g_functions_finalized += iteration; } void FinalizeForeign(void* data) { int iteration = static_cast(reinterpret_cast(data)); g_foreigns_finalized += iteration; } void FinalizeModule(void* data) { g_modules_finalized += static_cast(reinterpret_cast(data)); } void RunInStore(Store* store, ZoneBuffer* wire_bytes, int iterations) { size_t size = wire_bytes->end() - wire_bytes->begin(); vec binary = vec::make( size, reinterpret_cast(const_cast(wire_bytes->begin()))); own module = Module::make(store, binary); module->set_host_info(reinterpret_cast(kModuleMagic), &FinalizeModule); for (int iteration = 0; iteration < iterations; iteration++) { void* finalizer_data = reinterpret_cast(iteration); own instance = Instance::make(store, module.get(), nullptr); EXPECT_NE(nullptr, instance.get()); instance->set_host_info(finalizer_data, &FinalizeInstance); own 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::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_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(kModuleMagic), &FinalizeModule); static const int kIterations = 10; RunInStore(store(), wire_bytes(), kIterations); { own 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 CapiFunction(void* env, const Val args[], Val results[]) { int offset = static_cast(reinterpret_cast(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(reinterpret_cast(data)); g_capi_function_finalized += value; } void FinalizeHostData(void* data) { g_host_data_finalized += static_cast(reinterpret_cast(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; void* kHostData = reinterpret_cast(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 capi_func_type = FuncType::make(ownvec::make(ValType::make(::wasm::I32)), ownvec::make(ValType::make(::wasm::I32))); own capi_func = Func::make(store(), capi_func_type.get(), &CapiFunction, reinterpret_cast(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 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 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 capi_func_type = FuncType::make(ownvec::make(ValType::make(::wasm::I32)), ownvec::make(ValType::make(::wasm::I32))); own capi_func = Func::make( store(), capi_func_type.get(), &CapiFunction, reinterpret_cast(base_summand), &FinalizeCapiFunction); capi_func->set_host_info(reinterpret_cast(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