[wasm-c-api] Roll bf31edf: Fix life times of host info

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}
This commit is contained in:
Jakob Kummerow 2019-08-26 14:56:05 +02:00 committed by Commit Bot
parent 763f63fff6
commit 3aa4f05d0c
13 changed files with 305 additions and 110 deletions

View File

@ -1000,7 +1000,7 @@ extern class WasmJSFunctionData extends Struct {
extern class WasmCapiFunctionData extends Struct { extern class WasmCapiFunctionData extends Struct {
call_target: RawPtr; call_target: RawPtr;
embedder_data: RawPtr; embedder_data: Foreign; // Managed<wasm::FuncData>
wrapper_code: Code; wrapper_code: Code;
serialized_signature: ByteArray; // PodArray<wasm::ValueType> serialized_signature: ByteArray; // PodArray<wasm::ValueType>
} }

View File

@ -5952,9 +5952,9 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
Node* sfi_data = LOAD_RAW( Node* sfi_data = LOAD_RAW(
shared, SharedFunctionInfo::kFunctionDataOffset - kHeapObjectTag, shared, SharedFunctionInfo::kFunctionDataOffset - kHeapObjectTag,
MachineType::TypeCompressedTagged()); MachineType::TypeCompressedTagged());
Node* host_data = LOAD_RAW( Node* host_data_foreign = LOAD_RAW(
sfi_data, WasmCapiFunctionData::kEmbedderDataOffset - kHeapObjectTag, sfi_data, WasmCapiFunctionData::kEmbedderDataOffset - kHeapObjectTag,
MachineType::Pointer()); MachineType::TypeCompressedTagged());
BuildModifyThreadInWasmFlag(false); BuildModifyThreadInWasmFlag(false);
Node* isolate_root = BuildLoadIsolateRoot(); Node* isolate_root = BuildLoadIsolateRoot();
@ -5968,11 +5968,12 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
Node* function = Node* function =
graph()->NewNode(mcgraph()->common()->ExternalConstant(ref)); graph()->NewNode(mcgraph()->common()->ExternalConstant(ref));
// Parameters: void* data, Address arguments. // Parameters: Address host_data_foreign, Address arguments.
MachineType host_sig_types[] = { MachineType host_sig_types[] = {
MachineType::Pointer(), MachineType::Pointer(), MachineType::Pointer()}; MachineType::Pointer(), MachineType::Pointer(), MachineType::Pointer()};
MachineSignature host_sig(1, 2, host_sig_types); MachineSignature host_sig(1, 2, host_sig_types);
Node* return_value = BuildCCall(&host_sig, function, host_data, values); Node* return_value =
BuildCCall(&host_sig, function, host_data_foreign, values);
BuildModifyThreadInWasmFlag(true); BuildModifyThreadInWasmFlag(true);

View File

@ -2061,7 +2061,7 @@ void WasmCapiFunctionData::WasmCapiFunctionDataPrint(
std::ostream& os) { // NOLINT std::ostream& os) { // NOLINT
PrintHeader(os, "WasmCapiFunctionData"); PrintHeader(os, "WasmCapiFunctionData");
os << "\n - call_target: " << call_target(); os << "\n - call_target: " << call_target();
os << "\n - embedder_data: " << embedder_data(); os << "\n - embedder_data: " << Brief(embedder_data());
os << "\n - wrapper_code: " << Brief(wrapper_code()); os << "\n - wrapper_code: " << Brief(wrapper_code());
os << "\n - serialized_signature: " << Brief(serialized_signature()); os << "\n - serialized_signature: " << Brief(serialized_signature());
os << "\n"; os << "\n";

View File

@ -29,6 +29,8 @@
#include "include/libplatform/libplatform.h" #include "include/libplatform/libplatform.h"
#include "src/api/api-inl.h" #include "src/api/api-inl.h"
#include "src/compiler/wasm-compiler.h" #include "src/compiler/wasm-compiler.h"
#include "src/objects/js-collection-inl.h"
#include "src/objects/managed.h"
#include "src/objects/stack-frame-info-inl.h" #include "src/objects/stack-frame-info-inl.h"
#include "src/wasm/leb-helper.h" #include "src/wasm/leb-helper.h"
#include "src/wasm/module-instantiate.h" #include "src/wasm/module-instantiate.h"
@ -270,6 +272,38 @@ StoreImpl::~StoreImpl() {
delete create_params_.array_buffer_allocator; delete create_params_.array_buffer_allocator;
} }
struct ManagedData {
ManagedData(void* info, void (*finalizer)(void*))
: info(info), finalizer(finalizer) {}
~ManagedData() {
if (finalizer) (*finalizer)(info);
}
void* info;
void (*finalizer)(void*);
};
void StoreImpl::SetHostInfo(i::Handle<i::Object> object, void* info,
void (*finalizer)(void*)) {
i::HandleScope scope(i_isolate());
// Ideally we would specify the total size kept alive by {info} here,
// but all we get from the embedder is a {void*}, so our best estimate
// is the size of the metadata.
size_t estimated_size = sizeof(ManagedData);
i::Handle<i::Object> wrapper = i::Managed<ManagedData>::FromRawPtr(
i_isolate(), estimated_size, new ManagedData(info, finalizer));
int32_t hash = object->GetOrCreateHash(i_isolate()).value();
i::JSWeakCollection::Set(host_info_map_, object, wrapper, hash);
}
void* StoreImpl::GetHostInfo(i::Handle<i::Object> key) {
i::Object raw =
i::EphemeronHashTable::cast(host_info_map_->table()).Lookup(key);
if (raw.IsTheHole(i_isolate())) return nullptr;
return i::Managed<ManagedData>::cast(raw).raw()->info;
}
template <> template <>
struct implement<Store> { struct implement<Store> {
using type = StoreImpl; using type = StoreImpl;
@ -286,26 +320,29 @@ auto Store::make(Engine*) -> own<Store> {
// Create isolate. // Create isolate.
store->create_params_.array_buffer_allocator = store->create_params_.array_buffer_allocator =
v8::ArrayBuffer::Allocator::NewDefaultAllocator(); v8::ArrayBuffer::Allocator::NewDefaultAllocator();
auto isolate = v8::Isolate::New(store->create_params_); v8::Isolate* isolate = v8::Isolate::New(store->create_params_);
if (!isolate) return own<Store>(); if (!isolate) return own<Store>();
store->isolate_ = isolate;
isolate->SetData(0, store.get());
// We intentionally do not call isolate->Enter() here, because that would
// prevent embedders from using stores with overlapping but non-nested
// lifetimes. The consequence is that Isolate::Current() is dysfunctional
// and hence must not be called by anything reachable via this file.
{ {
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
// Create context. // Create context.
auto context = v8::Context::New(isolate); v8::Local<v8::Context> context = v8::Context::New(isolate);
if (context.IsEmpty()) return own<Store>(); if (context.IsEmpty()) return own<Store>();
v8::Context::Scope context_scope(context); context->Enter(); // The Exit() call is in ~StoreImpl.
store->isolate_ = isolate;
store->context_ = v8::Eternal<v8::Context>(isolate, context); store->context_ = v8::Eternal<v8::Context>(isolate, context);
// Create weak map for Refs with host info.
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
store->host_info_map_ = i_isolate->global_handles()->Create(
*i_isolate->factory()->NewJSWeakMap());
} }
// We intentionally do not call isolate->Enter() here, because that would
// prevent embedders from using stores with overlapping but non-nested
// lifetimes. The consequence is that Isolate::Current() is dysfunctional
// and hence must not be called by anything reachable via this file.
store->context()->Enter();
isolate->SetData(0, store.get());
// We want stack traces for traps. // We want stack traces for traps.
constexpr int kStackLimit = 10; constexpr int kStackLimit = 10;
isolate->SetCaptureStackTraceForUncaughtExceptions(true, kStackLimit, isolate->SetCaptureStackTraceForUncaughtExceptions(true, kStackLimit,
@ -712,15 +749,7 @@ class RefImpl {
return make_own(seal<Ref>(self)); return make_own(seal<Ref>(self));
} }
void Reset() { ~RefImpl() { i::GlobalHandles::Destroy(location()); }
i::GlobalHandles::Destroy(location());
if (host_data_) {
if (host_data_->finalizer) {
host_data_->finalizer(host_data_->info);
}
delete host_data_;
}
}
own<Ref> copy() const { return make(store(), v8_object()); } own<Ref> copy() const { return make(store(), v8_object()); }
@ -730,41 +759,20 @@ class RefImpl {
i::Handle<JSType> v8_object() const { return i::Handle<JSType>::cast(val_); } i::Handle<JSType> v8_object() const { return i::Handle<JSType>::cast(val_); }
void* get_host_info() const { void* get_host_info() const { return store()->GetHostInfo(v8_object()); }
if (host_data_ == nullptr) return nullptr;
return host_data_->info;
}
void set_host_info(void* info, void (*finalizer)(void*)) { void set_host_info(void* info, void (*finalizer)(void*)) {
host_data_ = new HostData(location(), info, finalizer); store()->SetHostInfo(v8_object(), info, finalizer);
i::GlobalHandles::MakeWeak(host_data_->location, host_data_, &v8_finalizer,
v8::WeakCallbackType::kParameter);
} }
private: private:
struct HostData {
HostData(i::Address* location, void* info, void (*finalizer)(void*))
: location(location), info(info), finalizer(finalizer) {}
i::Address* location;
void* info;
void (*finalizer)(void*);
};
RefImpl() {} RefImpl() {}
static void v8_finalizer(const v8::WeakCallbackInfo<void>& info) {
HostData* data = reinterpret_cast<HostData*>(info.GetParameter());
i::GlobalHandles::Destroy(data->location);
if (data->finalizer) (*data->finalizer)(data->info);
delete data;
}
i::Address* location() const { i::Address* location() const {
return reinterpret_cast<i::Address*>(val_.address()); return reinterpret_cast<i::Address*>(val_.address());
} }
i::Handle<i::JSReceiver> val_; i::Handle<i::JSReceiver> val_;
HostData* host_data_ = nullptr;
}; };
template <> template <>
@ -773,7 +781,6 @@ struct implement<Ref> {
}; };
Ref::~Ref() { Ref::~Ref() {
impl(this)->Reset();
delete impl(this); delete impl(this);
} }
@ -1197,8 +1204,7 @@ struct FuncData {
if (finalizer) (*finalizer)(env); if (finalizer) (*finalizer)(env);
} }
static i::Address v8_callback(void* data, i::Address argv); static i::Address v8_callback(i::Address host_data_foreign, i::Address argv);
static void finalize_func_data(void* data);
}; };
namespace { namespace {
@ -1270,11 +1276,12 @@ auto make_func(Store* store_abs, FuncData* data) -> own<Func> {
auto store = impl(store_abs); auto store = impl(store_abs);
i::Isolate* isolate = store->i_isolate(); i::Isolate* isolate = store->i_isolate();
i::HandleScope handle_scope(isolate); i::HandleScope handle_scope(isolate);
i::Handle<i::Managed<FuncData>> embedder_data =
i::Managed<FuncData>::FromRawPtr(isolate, sizeof(FuncData), data);
i::Handle<i::WasmCapiFunction> function = i::WasmCapiFunction::New( i::Handle<i::WasmCapiFunction> function = i::WasmCapiFunction::New(
isolate, reinterpret_cast<i::Address>(&FuncData::v8_callback), data, isolate, reinterpret_cast<i::Address>(&FuncData::v8_callback),
SignatureHelper::Serialize(isolate, data->type.get())); embedder_data, SignatureHelper::Serialize(isolate, data->type.get()));
auto func = implement<Func>::type::make(store, function); auto func = implement<Func>::type::make(store, function);
func->set_host_info(data, &FuncData::finalize_func_data);
return func; return func;
} }
@ -1435,7 +1442,7 @@ void PopArgs(i::wasm::FunctionSig* sig, Val results[],
own<Trap> CallWasmCapiFunction(i::WasmCapiFunctionData data, const Val args[], own<Trap> CallWasmCapiFunction(i::WasmCapiFunctionData data, const Val args[],
Val results[]) { Val results[]) {
FuncData* func_data = reinterpret_cast<FuncData*>(data.embedder_data()); FuncData* func_data = i::Managed<FuncData>::cast(data.embedder_data()).raw();
if (func_data->kind == FuncData::kCallback) { if (func_data->kind == FuncData::kCallback) {
return (func_data->callback)(args, results); return (func_data->callback)(args, results);
} }
@ -1530,8 +1537,10 @@ auto Func::call(const Val args[], Val results[]) const -> own<Trap> {
return nullptr; return nullptr;
} }
i::Address FuncData::v8_callback(void* data, i::Address argv) { i::Address FuncData::v8_callback(i::Address host_data_foreign,
FuncData* self = reinterpret_cast<FuncData*>(data); i::Address argv) {
FuncData* self =
i::Managed<FuncData>::cast(i::Object(host_data_foreign))->raw();
StoreImpl* store = impl(self->store); StoreImpl* store = impl(self->store);
i::Isolate* isolate = store->i_isolate(); i::Isolate* isolate = store->i_isolate();
i::HandleScope scope(isolate); i::HandleScope scope(isolate);
@ -1619,10 +1628,6 @@ i::Address FuncData::v8_callback(void* data, i::Address argv) {
return i::kNullAddress; return i::kNullAddress;
} }
void FuncData::finalize_func_data(void* data) {
delete reinterpret_cast<FuncData*>(data);
}
// Global Instances // Global Instances
template <> template <>
@ -2251,7 +2256,7 @@ extern "C++" inline auto hide_mutability(wasm::Mutability mutability)
return static_cast<wasm_mutability_t>(mutability); return static_cast<wasm_mutability_t>(mutability);
} }
extern "C++" inline auto reveal_mutability(wasm_mutability_enum mutability) extern "C++" inline auto reveal_mutability(wasm_mutability_t mutability)
-> wasm::Mutability { -> wasm::Mutability {
return static_cast<wasm::Mutability>(mutability); return static_cast<wasm::Mutability>(mutability);
} }
@ -2278,7 +2283,7 @@ extern "C++" inline auto hide_externkind(wasm::ExternKind kind)
return static_cast<wasm_externkind_t>(kind); return static_cast<wasm_externkind_t>(kind);
} }
extern "C++" inline auto reveal_externkind(wasm_externkind_enum kind) extern "C++" inline auto reveal_externkind(wasm_externkind_t kind)
-> wasm::ExternKind { -> wasm::ExternKind {
return static_cast<wasm::ExternKind>(kind); return static_cast<wasm::ExternKind>(kind);
} }
@ -2330,8 +2335,7 @@ WASM_DEFINE_TYPE(globaltype, wasm::GlobalType)
wasm_globaltype_t* wasm_globaltype_new(wasm_valtype_t* content, wasm_globaltype_t* wasm_globaltype_new(wasm_valtype_t* content,
wasm_mutability_t mutability) { wasm_mutability_t mutability) {
return release_globaltype(wasm::GlobalType::make( return release_globaltype(wasm::GlobalType::make(
adopt_valtype(content), adopt_valtype(content), reveal_mutability(mutability)));
reveal_mutability(static_cast<wasm_mutability_enum>(mutability))));
} }
const wasm_valtype_t* wasm_globaltype_content(const wasm_globaltype_t* gt) { const wasm_valtype_t* wasm_globaltype_content(const wasm_globaltype_t* gt) {

View File

@ -7,8 +7,17 @@
#include "include/v8.h" #include "include/v8.h"
#include "src/common/globals.h" #include "src/common/globals.h"
#include "src/handles/handles.h"
#include "third_party/wasm-api/wasm.hh" #include "third_party/wasm-api/wasm.hh"
namespace v8 {
namespace internal {
class JSWeakMap;
} // namespace internal
} // namespace v8
namespace wasm { namespace wasm {
class StoreImpl { class StoreImpl {
@ -27,6 +36,10 @@ class StoreImpl {
reinterpret_cast<v8::Isolate*>(isolate)->GetData(0)); reinterpret_cast<v8::Isolate*>(isolate)->GetData(0));
} }
void SetHostInfo(i::Handle<i::Object> object, void* info,
void (*finalizer)(void*));
void* GetHostInfo(i::Handle<i::Object> key);
private: private:
friend own<Store> Store::make(Engine*); friend own<Store> Store::make(Engine*);
@ -35,6 +48,7 @@ class StoreImpl {
v8::Isolate::CreateParams create_params_; v8::Isolate::CreateParams create_params_;
v8::Isolate* isolate_ = nullptr; v8::Isolate* isolate_ = nullptr;
v8::Eternal<v8::Context> context_; v8::Eternal<v8::Context> context_;
i::Handle<i::JSWeakMap> host_info_map_;
}; };
} // namespace wasm } // namespace wasm

View File

@ -357,8 +357,7 @@ OBJECT_CONSTRUCTORS_IMPL(WasmCapiFunctionData, Struct)
CAST_ACCESSOR(WasmCapiFunctionData) CAST_ACCESSOR(WasmCapiFunctionData)
PRIMITIVE_ACCESSORS(WasmCapiFunctionData, call_target, Address, PRIMITIVE_ACCESSORS(WasmCapiFunctionData, call_target, Address,
kCallTargetOffset) kCallTargetOffset)
PRIMITIVE_ACCESSORS(WasmCapiFunctionData, embedder_data, void*, ACCESSORS(WasmCapiFunctionData, embedder_data, Foreign, kEmbedderDataOffset)
kEmbedderDataOffset)
ACCESSORS(WasmCapiFunctionData, wrapper_code, Code, kWrapperCodeOffset) ACCESSORS(WasmCapiFunctionData, wrapper_code, Code, kWrapperCodeOffset)
ACCESSORS(WasmCapiFunctionData, serialized_signature, PodArray<wasm::ValueType>, ACCESSORS(WasmCapiFunctionData, serialized_signature, PodArray<wasm::ValueType>,
kSerializedSignatureOffset) kSerializedSignatureOffset)

View File

@ -2162,13 +2162,13 @@ bool WasmCapiFunction::IsWasmCapiFunction(Object object) {
} }
Handle<WasmCapiFunction> WasmCapiFunction::New( Handle<WasmCapiFunction> WasmCapiFunction::New(
Isolate* isolate, Address call_target, void* embedder_data, Isolate* isolate, Address call_target, Handle<Foreign> embedder_data,
Handle<PodArray<wasm::ValueType>> serialized_signature) { Handle<PodArray<wasm::ValueType>> serialized_signature) {
Handle<WasmCapiFunctionData> fun_data = Handle<WasmCapiFunctionData> fun_data =
Handle<WasmCapiFunctionData>::cast(isolate->factory()->NewStruct( Handle<WasmCapiFunctionData>::cast(isolate->factory()->NewStruct(
WASM_CAPI_FUNCTION_DATA_TYPE, AllocationType::kOld)); WASM_CAPI_FUNCTION_DATA_TYPE, AllocationType::kOld));
fun_data->set_call_target(call_target); fun_data->set_call_target(call_target);
fun_data->set_embedder_data(embedder_data); fun_data->set_embedder_data(*embedder_data);
fun_data->set_serialized_signature(*serialized_signature); fun_data->set_serialized_signature(*serialized_signature);
// TODO(jkummerow): Install a JavaScript wrapper. For now, calling // TODO(jkummerow): Install a JavaScript wrapper. For now, calling
// these functions directly is unsupported; they can only be called // these functions directly is unsupported; they can only be called

View File

@ -707,7 +707,7 @@ class WasmCapiFunction : public JSFunction {
static bool IsWasmCapiFunction(Object object); static bool IsWasmCapiFunction(Object object);
static Handle<WasmCapiFunction> New( static Handle<WasmCapiFunction> New(
Isolate* isolate, Address call_target, void* embedder_data, Isolate* isolate, Address call_target, Handle<Foreign> embedder_data,
Handle<PodArray<wasm::ValueType>> serialized_signature); Handle<PodArray<wasm::ValueType>> serialized_signature);
Address GetHostCallTarget() const; Address GetHostCallTarget() const;
@ -764,7 +764,7 @@ class WasmIndirectFunctionTable : public Struct {
class WasmCapiFunctionData : public Struct { class WasmCapiFunctionData : public Struct {
public: public:
DECL_PRIMITIVE_ACCESSORS(call_target, Address) DECL_PRIMITIVE_ACCESSORS(call_target, Address)
DECL_PRIMITIVE_ACCESSORS(embedder_data, void*) DECL_ACCESSORS(embedder_data, Foreign)
DECL_ACCESSORS(wrapper_code, Code) DECL_ACCESSORS(wrapper_code, Code)
DECL_ACCESSORS(serialized_signature, PodArray<wasm::ValueType>) DECL_ACCESSORS(serialized_signature, PodArray<wasm::ValueType>)
@ -776,7 +776,7 @@ class WasmCapiFunctionData : public Struct {
DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize, DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize,
TORQUE_GENERATED_WASM_CAPI_FUNCTION_DATA_FIELDS) TORQUE_GENERATED_WASM_CAPI_FUNCTION_DATA_FIELDS)
STATIC_ASSERT(kStartOfStrongFieldsOffset == kWrapperCodeOffset); STATIC_ASSERT(kStartOfStrongFieldsOffset == kEmbedderDataOffset);
using BodyDescriptor = FlexibleBodyDescriptor<kStartOfStrongFieldsOffset>; using BodyDescriptor = FlexibleBodyDescriptor<kStartOfStrongFieldsOffset>;
OBJECT_CONSTRUCTORS(WasmCapiFunctionData, Struct); OBJECT_CONSTRUCTORS(WasmCapiFunctionData, Struct);

View File

@ -15,6 +15,8 @@ int g_functions_finalized = 0;
int g_foreigns_finalized = 0; int g_foreigns_finalized = 0;
int g_modules_finalized = 0; int g_modules_finalized = 0;
const int kModuleMagic = 42;
void FinalizeInstance(void* data) { void FinalizeInstance(void* data) {
int iteration = static_cast<int>(reinterpret_cast<intptr_t>(data)); int iteration = static_cast<int>(reinterpret_cast<intptr_t>(data));
g_instances_finalized += iteration; g_instances_finalized += iteration;
@ -34,6 +36,31 @@ void FinalizeModule(void* data) {
g_modules_finalized += static_cast<int>(reinterpret_cast<intptr_t>(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 } // namespace
TEST_F(WasmCapiTest, InstanceFinalization) { TEST_F(WasmCapiTest, InstanceFinalization) {
@ -45,31 +72,114 @@ TEST_F(WasmCapiTest, InstanceFinalization) {
g_functions_finalized = 0; g_functions_finalized = 0;
g_foreigns_finalized = 0; g_foreigns_finalized = 0;
g_modules_finalized = 0; g_modules_finalized = 0;
module()->set_host_info(reinterpret_cast<void*>(42), &FinalizeModule); module()->set_host_info(reinterpret_cast<void*>(kModuleMagic),
&FinalizeModule);
static const int kIterations = 10; static const int kIterations = 10;
for (int iteration = 0; iteration < kIterations; iteration++) { RunInStore(store(), wire_bytes(), kIterations);
void* finalizer_data = reinterpret_cast<void*>(iteration); {
own<Instance> instance = Instance::make(store(), module(), nullptr); own<Store> store2 = Store::make(engine());
EXPECT_NE(nullptr, instance.get()); RunInStore(store2.get(), wire_bytes(), kIterations);
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);
own<Foreign> foreign = Foreign::make(store());
foreign->set_host_info(finalizer_data, &FinalizeForeign);
} }
RunInStore(store(), wire_bytes(), kIterations);
Shutdown(); Shutdown();
// Verify that (1) all finalizers were called, and (2) they passed the // 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 // correct host data: the loop above sets {i} as data, and the finalizer
// callbacks add them all up, so the expected value is // callbacks add them all up, so the expected value after three rounds is
// sum([0, 1, ..., kIterations - 1]), which per Gauss's formula is: // 3 * sum([0, 1, ..., kIterations - 1]), which per Gauss's formula is:
static const int kExpected = (kIterations * (kIterations - 1)) / 2; static const int kExpected = 3 * ((kIterations * (kIterations - 1)) / 2);
EXPECT_EQ(g_instances_finalized, kExpected); EXPECT_EQ(g_instances_finalized, kExpected);
// There are two functions per iteration.
EXPECT_EQ(g_functions_finalized, kExpected); EXPECT_EQ(g_functions_finalized, kExpected);
EXPECT_EQ(g_foreigns_finalized, kExpected); EXPECT_EQ(g_foreigns_finalized, kExpected);
EXPECT_EQ(g_modules_finalized, 42); 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 wasm

View File

@ -115,6 +115,7 @@ class WasmCapiTest : public ::testing::Test {
} }
void Shutdown() { void Shutdown() {
exports_.reset();
instance_.reset(); instance_.reset();
module_.reset(); module_.reset();
store_.reset(); store_.reset();

View File

@ -2,8 +2,8 @@ Name: Wasm C/C++ API
Short Name: wasm-c-api Short Name: wasm-c-api
URL: https://github.com/WebAssembly/wasm-c-api/ URL: https://github.com/WebAssembly/wasm-c-api/
Version: 0 Version: 0
Revision: 5c742b048f7766a0c00be3a7af23fb71ba816026 Revision: 6db391ee7121a0695602945d11001ea3e00b0afb
Date: 2019-03-18 Date: 2019-08-08
License: Apache 2.0 License: Apache 2.0
License File: LICENSE License File: LICENSE
Security Critical: yes Security Critical: yes

View File

@ -9,23 +9,21 @@
const int iterations = 100000; const int iterations = 100000;
int live_count = 0;
void finalize(void* data) { void finalize(void* data) {
int i = (int)data; int i = (int)data;
if (i % (iterations / 10) == 0) printf("Finalizing #%d...\n", i); if (i % (iterations / 10) == 0) printf("Finalizing #%d...\n", i);
--live_count;
} }
int main(int argc, const char* argv[]) { void run_in_store(wasm_store_t* store) {
// Initialize.
printf("Initializing...\n");
wasm_engine_t* engine = wasm_engine_new();
wasm_store_t* store = wasm_store_new(engine);
// Load binary. // Load binary.
printf("Loading binary...\n"); printf("Loading binary...\n");
FILE* file = fopen("finalize.wasm", "r"); FILE* file = fopen("finalize.wasm", "r");
if (!file) { if (!file) {
printf("> Error loading module!\n"); printf("> Error loading module!\n");
return 1; exit(1);
} }
fseek(file, 0L, SEEK_END); fseek(file, 0L, SEEK_END);
size_t file_size = ftell(file); size_t file_size = ftell(file);
@ -34,7 +32,7 @@ int main(int argc, const char* argv[]) {
wasm_byte_vec_new_uninitialized(&binary, file_size); wasm_byte_vec_new_uninitialized(&binary, file_size);
if (fread(binary.data, file_size, 1, file) != 1) { if (fread(binary.data, file_size, 1, file) != 1) {
printf("> Error loading module!\n"); printf("> Error loading module!\n");
return 1; exit(1);
} }
fclose(file); fclose(file);
@ -43,7 +41,7 @@ int main(int argc, const char* argv[]) {
own wasm_module_t* module = wasm_module_new(store, &binary); own wasm_module_t* module = wasm_module_new(store, &binary);
if (!module) { if (!module) {
printf("> Error compiling module!\n"); printf("> Error compiling module!\n");
return 1; exit(1);
} }
wasm_byte_vec_delete(&binary); wasm_byte_vec_delete(&binary);
@ -56,18 +54,53 @@ int main(int argc, const char* argv[]) {
wasm_instance_new(store, module, NULL, NULL); wasm_instance_new(store, module, NULL, NULL);
if (!instance) { if (!instance) {
printf("> Error instantiating module %d!\n", i); printf("> Error instantiating module %d!\n", i);
return 1; exit(1);
} }
void* data = (void*)(intptr_t)i; void* data = (void*)(intptr_t)i;
wasm_instance_set_host_info_with_finalizer(instance, data, &finalize); wasm_instance_set_host_info_with_finalizer(instance, data, &finalize);
wasm_instance_delete(instance); wasm_instance_delete(instance);
++live_count;
} }
wasm_module_delete(module); wasm_module_delete(module);
}
int main(int argc, const char* argv[]) {
// Initialize.
printf("Initializing...\n");
wasm_engine_t* engine = wasm_engine_new();
printf("Live count %d\n", live_count);
printf("Creating store 1...\n");
wasm_store_t* store1 = wasm_store_new(engine);
printf("Running in store 1...\n");
run_in_store(store1);
printf("Live count %d\n", live_count);
printf("Creating store 2...\n");
wasm_store_t* store2 = wasm_store_new(engine);
printf("Running in store 2...\n");
run_in_store(store2);
printf("Live count %d\n", live_count);
printf("Deleting store 2...\n");
wasm_store_delete(store2);
printf("Live count %d\n", live_count);
printf("Running in store 1...\n");
run_in_store(store1);
printf("Live count %d\n", live_count);
printf("Deleting store 1...\n");
wasm_store_delete(store1);
printf("Live count %d\n", live_count);
assert(live_count == 0);
// Shut down. // Shut down.
printf("Shutting down...\n"); printf("Shutting down...\n");
wasm_store_delete(store);
wasm_engine_delete(engine); wasm_engine_delete(engine);
// All done. // All done.

View File

@ -9,20 +9,17 @@
const int iterations = 100000; const int iterations = 100000;
int live_count = 0;
void finalize(void* data) { void finalize(void* data) {
intptr_t i = reinterpret_cast<intptr_t>(data); intptr_t i = reinterpret_cast<intptr_t>(data);
if (i % (iterations / 10) == 0) { if (i % (iterations / 10) == 0) {
std::cout << "Finalizing #" << i << "..." << std::endl; std::cout << "Finalizing #" << i << "..." << std::endl;
} }
--live_count;
} }
void run() { void run_in_store(wasm::Store* store) {
// Initialize.
std::cout << "Initializing..." << std::endl;
auto engine = wasm::Engine::make();
auto store_ = wasm::Store::make(engine.get());
auto store = store_.get();
// Load binary. // Load binary.
std::cout << "Loading binary..." << std::endl; std::cout << "Loading binary..." << std::endl;
std::ifstream file("finalize.wasm"); std::ifstream file("finalize.wasm");
@ -55,6 +52,7 @@ void run() {
exit(1); exit(1);
} }
instance->set_host_info(reinterpret_cast<void*>(i), &finalize); instance->set_host_info(reinterpret_cast<void*>(i), &finalize);
++live_count;
} }
// Shut down. // Shut down.
@ -62,8 +60,43 @@ void run() {
} }
void run() {
// Initialize.
std::cout << "Initializing..." << std::endl;
auto engine = wasm::Engine::make();
std::cout << "Live count " << live_count << std::endl;
std::cout << "Creating store 1..." << std::endl;
auto store1 = wasm::Store::make(engine.get());
std::cout << "Running in store 1..." << std::endl;
run_in_store(store1.get());
std::cout << "Live count " << live_count << std::endl;
{
std::cout << "Creating store 2..." << std::endl;
auto store2 = wasm::Store::make(engine.get());
std::cout << "Running in store 2..." << std::endl;
run_in_store(store2.get());
std::cout << "Live count " << live_count << std::endl;
std::cout << "Deleting store 2..." << std::endl;
std::cout << "Live count " << live_count << std::endl;
}
std::cout << "Running in store 1..." << std::endl;
run_in_store(store1.get());
std::cout << "Live count " << live_count << std::endl;
std::cout << "Deleting store 1..." << std::endl;
}
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
run(); run();
std::cout << "Live count " << live_count << std::endl;
assert(live_count == 0);
std::cout << "Done." << std::endl; std::cout << "Done." << std::endl;
return 0; return 0;
} }