Commit 89056f51 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[wasm-c-api] Add finalization test

and make Foreign::make() functional.

Change-Id: Idca3affee5ee89f1774641c5b6475445aef25756
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1685792
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62506}
parent f6924e3d
......@@ -1259,11 +1259,12 @@ Foreign::~Foreign() {}
auto Foreign::copy() const -> own<Foreign*> { return impl(this)->copy(); }
auto Foreign::make(Store* store_abs) -> own<Foreign*> {
auto store = impl(store_abs);
auto isolate = store->i_isolate();
StoreImpl* store = impl(store_abs);
i::Isolate* isolate = store->i_isolate();
i::HandleScope handle_scope(isolate);
auto obj = i::Handle<i::JSReceiver>();
i::Handle<i::JSObject> obj =
isolate->factory()->NewJSObject(isolate->object_function());
return implement<Foreign>::type::make(store, obj);
}
......
......@@ -30,6 +30,8 @@ v8_executable("wasm_api_tests") {
"../../testing/gmock-support.h",
"../../testing/gtest-support.h",
"callbacks.cc",
"finalize.cc",
"run-all-wasm-api-tests.cc",
"wasm-api-test.h",
]
}
......@@ -2,29 +2,11 @@
// 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"
#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"
#include "third_party/wasm-api/wasm.hh"
namespace wasm {
// TODO(jkummerow): Drop these from the API.
#ifdef DEBUG
template <class T>
void vec<T>::make_data() {}
template <class T>
void vec<T>::free_data() {}
#endif
} // namespace wasm
namespace v8 {
namespace internal {
......@@ -32,118 +14,17 @@ namespace wasm {
namespace {
using ::wasm::Engine;
using ::wasm::Extern;
using ::wasm::Func;
using ::wasm::FuncType;
using ::wasm::Instance;
using ::wasm::Module;
using ::wasm::own;
using ::wasm::Ref;
using ::wasm::Store;
using ::wasm::Trap;
using ::wasm::Val;
using ::wasm::ValType;
using ::wasm::vec;
own<Trap*> Stage2(void* env, const Val args[], Val results[]);
class WasmCapiTest : public ::testing::Test {
public:
WasmCapiTest()
: Test(),
zone_(&allocator_, ZONE_NAME),
builder_(&zone_),
exports_(vec<Extern*>::make()),
wasm_i_i_sig_(1, 1, wasm_i_i_sig_types_) {
engine_ = Engine::make();
store_ = Store::make(engine_.get());
cpp_i_i_sig_ =
FuncType::make(vec<ValType*>::make(ValType::make(::wasm::I32)),
vec<ValType*>::make(ValType::make(::wasm::I32)));
}
void Instantiate(Extern* imports[]) {
ZoneBuffer buffer(&zone_);
builder_.WriteTo(buffer);
size_t size = buffer.end() - buffer.begin();
vec<byte_t> binary = vec<byte_t>::make(
size, reinterpret_cast<byte_t*>(const_cast<byte*>(buffer.begin())));
module_ = Module::make(store_.get(), binary);
DCHECK_NE(module_.get(), nullptr);
instance_ = Instance::make(store_.get(), module_.get(), imports);
DCHECK_NE(instance_.get(), nullptr);
exports_ = instance_->exports();
}
void AddExportedFunction(Vector<const char> name, byte code[],
size_t code_size) {
WasmFunctionBuilder* fun = builder()->AddFunction(wasm_i_i_sig());
fun->EmitCode(code, static_cast<uint32_t>(code_size));
fun->Emit(kExprEnd);
builder()->AddExport(name, fun);
}
Func* GetExportedFunction(size_t index) {
DCHECK_GT(exports_.size(), index);
Extern* exported = exports_[index];
DCHECK_EQ(exported->kind(), ::wasm::EXTERN_FUNC);
Func* func = exported->func();
DCHECK_NE(func, nullptr);
return func;
}
WasmModuleBuilder* builder() { return &builder_; }
Store* store() { return store_.get(); }
FunctionSig* wasm_i_i_sig() { return &wasm_i_i_sig_; }
FuncType* cpp_i_i_sig() { return cpp_i_i_sig_.get(); }
private:
AccountingAllocator allocator_;
Zone zone_;
WasmModuleBuilder builder_;
own<Engine*> engine_;
own<Store*> store_;
own<Module*> module_;
own<Instance*> instance_;
vec<Extern*> exports_;
own<FuncType*> cpp_i_i_sig_;
ValueType wasm_i_i_sig_types_[2] = {kWasmI32, kWasmI32};
FunctionSig wasm_i_i_sig_;
};
class WasmCapiCallbacksTest : public WasmCapiTest {
public:
WasmCapiCallbacksTest() : WasmCapiTest() {
// Build the following function:
// int32 stage1(int32 arg0) { return stage2(arg0); }
uint32_t stage2_index =
builder()->AddImport(ArrayVector("stage2"), wasm_i_i_sig());
byte code[] = {WASM_CALL_FUNCTION(stage2_index, WASM_GET_LOCAL(0))};
AddExportedFunction(CStrVector("stage1"), code, sizeof(code));
stage2_ = Func::make(store(), cpp_i_i_sig(), Stage2, this);
}
Func* stage2() { return stage2_.get(); }
private:
own<Func*> stage2_;
};
own<Trap*> Stage2(void* env, const Val args[], Val results[]) {
printf("Stage2...\n");
WasmCapiTest* self = reinterpret_cast<WasmCapiTest*>(env);
Func* stage3 = self->GetExportedFunction(1);
own<Trap*> result = stage3->call(args, results);
if (result) {
printf("Stage2: got exception: %s\n", result->message().get());
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 result;
return trap;
}
own<Trap*> Stage4_GC(void* env, const Val args[], Val results[]) {
......@@ -156,41 +37,24 @@ own<Trap*> Stage4_GC(void* env, const Val args[], Val results[]) {
return nullptr;
}
own<Trap*> FibonacciC(void* env, const Val args[], Val results[]) {
int32_t x = args[0].i32();
if (x == 0 || x == 1) {
results[0] = Val::i32(x);
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(ArrayVector("stage2"), wasm_i_i_sig());
byte code[] = {WASM_CALL_FUNCTION(stage2_index, WASM_GET_LOCAL(0))};
AddExportedFunction(CStrVector("stage1"), code, sizeof(code));
stage2_ = Func::make(store(), cpp_i_i_sig(), Stage2, this);
}
WasmCapiTest* self = reinterpret_cast<WasmCapiTest*>(env);
Func* fibo_wasm = self->GetExportedFunction(0);
// Aggressively re-use existing arrays. That's maybe not great coding
// style, but this test intentionally ensures that it works if someone
// insists on doing it.
Val recursive_args[] = {Val::i32(x - 1)};
own<Trap*> result = fibo_wasm->call(recursive_args, results);
DCHECK_NULL(result);
int32_t x1 = results[0].i32();
recursive_args[0] = Val::i32(x - 2);
result = fibo_wasm->call(recursive_args, results);
DCHECK_NULL(result);
int32_t x2 = results[0].i32();
results[0] = Val::i32(x1 + x2);
return nullptr;
}
own<Trap*> PlusOne(const Val args[], 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;
}
Func* stage2() { return stage2_.get(); }
private:
own<Func*> stage2_;
};
} // namespace
......@@ -204,9 +68,9 @@ TEST_F(WasmCapiCallbacksTest, Trap) {
Instantiate(imports);
Val args[] = {Val::i32(42)};
Val results[1];
own<Trap*> result = GetExportedFunction(0)->call(args, results);
EXPECT_NE(result, nullptr);
printf("Stage0: Got trap as expected: %s\n", result->message().get());
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) {
......@@ -226,11 +90,38 @@ TEST_F(WasmCapiCallbacksTest, GC) {
Instantiate(imports);
Val args[] = {Val::i32(42)};
Val results[1];
own<Trap*> result = GetExportedFunction(0)->call(args, results);
EXPECT_EQ(result, nullptr);
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 Val args[], 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 re-use existing arrays. That's maybe not great coding
// style, but this test intentionally ensures that it works if someone
// insists on doing it.
Val recursive_args[] = {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) {
......@@ -264,6 +155,23 @@ TEST_F(WasmCapiTest, Recursion) {
EXPECT_EQ(610, results[0].i32());
}
namespace {
own<Trap*> PlusOne(const Val args[], 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;
}
} // namespace
TEST_F(WasmCapiTest, DirectCallCapiFunction) {
own<FuncType*> cpp_sig =
FuncType::make(vec<ValType*>::make(
......
// 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;
void FinalizeInstance(void* data) {
g_instances_finalized += static_cast<int>(reinterpret_cast<intptr_t>(data));
}
void FinalizeFunction(void* data) {
g_functions_finalized += static_cast<int>(reinterpret_cast<intptr_t>(data));
}
void FinalizeForeign(void* data) {
g_foreigns_finalized += static_cast<int>(reinterpret_cast<intptr_t>(data));
}
void FinalizeModule(void* data) {
g_modules_finalized += static_cast<int>(reinterpret_cast<intptr_t>(data));
}
} // 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));
Compile();
g_instances_finalized = 0;
g_functions_finalized = 0;
g_foreigns_finalized = 0;
g_modules_finalized = 0;
module()->set_host_info(reinterpret_cast<void*>(42), &FinalizeModule);
static const int kIterations = 10;
for (int i = 0; i < kIterations; i++) {
own<Instance*> instance = Instance::make(store(), module(), nullptr);
EXPECT_NE(nullptr, instance.get());
instance->set_host_info(reinterpret_cast<void*>(i), &FinalizeInstance);
own<Func*> func = instance->exports()[0]->func()->copy();
ASSERT_NE(func, nullptr);
func->set_host_info(reinterpret_cast<void*>(i), &FinalizeFunction);
own<Foreign*> foreign = Foreign::make(store());
foreign->set_host_info(reinterpret_cast<void*>(i), &FinalizeForeign);
}
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 is
// sum([0, 1, ..., kIterations - 1]), which per Gauss's formula is:
static const int kExpected = (kIterations * (kIterations - 1)) / 2;
EXPECT_EQ(g_instances_finalized, kExpected);
EXPECT_EQ(g_functions_finalized, kExpected);
EXPECT_EQ(g_foreigns_finalized, kExpected);
EXPECT_EQ(g_modules_finalized, 42);
}
} // namespace wasm
} // namespace internal
} // namespace v8
// 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"
#include "third_party/wasm-api/wasm.hh"
namespace wasm {
// TODO(jkummerow): Drop these from the API.
#ifdef DEBUG
template <class T>
void vec<T>::make_data() {}
template <class T>
void vec<T>::free_data() {}
#endif
} // namespace wasm
namespace v8 {
namespace internal {
namespace wasm {
using ::wasm::Engine;
using ::wasm::Extern;
using ::wasm::Foreign;
using ::wasm::Func;
using ::wasm::FuncType;
using ::wasm::Instance;
using ::wasm::Module;
using ::wasm::own;
using ::wasm::Ref;
using ::wasm::Store;
using ::wasm::Trap;
using ::wasm::Val;
using ::wasm::ValType;
using ::wasm::vec;
class WasmCapiTest : public ::testing::Test {
public:
WasmCapiTest()
: Test(),
zone_(&allocator_, ZONE_NAME),
builder_(&zone_),
exports_(vec<Extern*>::make()),
wasm_i_i_sig_(1, 1, wasm_i_i_sig_types_) {
engine_ = Engine::make();
store_ = Store::make(engine_.get());
cpp_i_i_sig_ =
FuncType::make(vec<ValType*>::make(ValType::make(::wasm::I32)),
vec<ValType*>::make(ValType::make(::wasm::I32)));
}
void Compile() {
ZoneBuffer buffer(&zone_);
builder_.WriteTo(buffer);
size_t size = buffer.end() - buffer.begin();
vec<byte_t> binary = vec<byte_t>::make(
size, reinterpret_cast<byte_t*>(const_cast<byte*>(buffer.begin())));
module_ = Module::make(store_.get(), binary);
DCHECK_NE(module_.get(), nullptr);
}
void Instantiate(Extern* imports[]) {
Compile();
instance_ = Instance::make(store_.get(), module_.get(), imports);
DCHECK_NE(instance_.get(), nullptr);
exports_ = instance_->exports();
}
void AddExportedFunction(Vector<const char> name, byte code[],
size_t code_size) {
WasmFunctionBuilder* fun = builder()->AddFunction(wasm_i_i_sig());
fun->EmitCode(code, static_cast<uint32_t>(code_size));
fun->Emit(kExprEnd);
builder()->AddExport(name, fun);
}
Func* GetExportedFunction(size_t index) {
DCHECK_GT(exports_.size(), index);
Extern* exported = exports_[index];
DCHECK_EQ(exported->kind(), ::wasm::EXTERN_FUNC);
Func* func = exported->func();
DCHECK_NE(func, nullptr);
return func;
}
void Shutdown() {
instance_.reset();
module_.reset();
store_.reset();
engine_.reset();
}
WasmModuleBuilder* builder() { return &builder_; }
Store* store() { return store_.get(); }
Module* module() { return module_.get(); }
FunctionSig* wasm_i_i_sig() { return &wasm_i_i_sig_; }
FuncType* cpp_i_i_sig() { return cpp_i_i_sig_.get(); }
private:
AccountingAllocator allocator_;
Zone zone_;
WasmModuleBuilder builder_;
own<Engine*> engine_;
own<Store*> store_;
own<Module*> module_;
own<Instance*> instance_;
vec<Extern*> exports_;
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_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment