[wasm] WebAssembly.Memory object can be referenced by multiple Instance objects.
Add support for WebAssembly.Memory objects to be simultaneously referenced by multiple Instance objects. GrowingMemory should maintain a consistent view of memory across instances. - Store a link to instances that share WebAssembly.Memory in the WasmMemoryObject, updated on instantiate. - Implement WasmInstanceWrapper as a wrapper around the instance object to keep track of previous/next instances, instance object is stored as a WeakCell that can be garbage collected. - MemoryInstanceFinalizer maintains a valid list of instances when an instance is garbage collected. - Refactor GrowInstanceMemory to GrowMemoryBuffer that allocates a new buffer, and UncheckedUpdateInstanceMemory that updates memory references for an instance. R=titzer@chromium.org, mtrofin@chromium.org, bradnelson@chromium.org Review-Url: https://codereview.chromium.org/2471883003 Cr-Commit-Position: refs/heads/master@{#41121}
This commit is contained in:
parent
06f8e87726
commit
30ef8e33f3
@ -58,7 +58,7 @@ RUNTIME_FUNCTION(Runtime_WasmGrowMemory) {
|
||||
instance = handle(owning_instance, isolate);
|
||||
}
|
||||
return *isolate->factory()->NewNumberFromInt(
|
||||
wasm::GrowInstanceMemory(isolate, instance, delta_pages));
|
||||
wasm::GrowMemory(isolate, instance, delta_pages));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) {
|
||||
|
@ -28,12 +28,6 @@ using v8::internal::wasm::ErrorThrower;
|
||||
|
||||
namespace v8 {
|
||||
|
||||
enum WasmMemoryObjectData {
|
||||
kWasmMemoryBuffer,
|
||||
kWasmMemoryMaximum,
|
||||
kWasmMemoryInstanceObject
|
||||
};
|
||||
|
||||
namespace {
|
||||
i::Handle<i::String> v8_str(i::Isolate* isolate, const char* str) {
|
||||
return isolate->factory()->NewStringFromAsciiChecked(str);
|
||||
@ -230,6 +224,7 @@ void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
i_isolate);
|
||||
} else {
|
||||
thrower.TypeError("Argument 2 must be a WebAssembly.Memory");
|
||||
return;
|
||||
}
|
||||
}
|
||||
i::MaybeHandle<i::JSObject> instance =
|
||||
@ -530,31 +525,15 @@ void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
|
||||
uint32_t delta = args[0]->Uint32Value(context).FromJust();
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
i::Handle<i::JSObject> receiver =
|
||||
i::Handle<i::JSObject>::cast(Utils::OpenHandle(*args.This()));
|
||||
i::Handle<i::Object> instance_object(
|
||||
receiver->GetInternalField(kWasmMemoryInstanceObject), i_isolate);
|
||||
i::Handle<i::WasmInstanceObject> instance(
|
||||
i::Handle<i::WasmInstanceObject>::cast(instance_object));
|
||||
|
||||
// TODO(gdeepti) Implement growing memory when shared by different
|
||||
// instances.
|
||||
int32_t ret = internal::wasm::GrowInstanceMemory(i_isolate, instance, delta);
|
||||
i::Handle<i::Object> receiver =
|
||||
i::Handle<i::Object>::cast(Utils::OpenHandle(*args.This()));
|
||||
int32_t ret = i::wasm::GrowWebAssemblyMemory(i_isolate, receiver, delta);
|
||||
if (ret == -1) {
|
||||
v8::Local<v8::Value> e = v8::Exception::Error(
|
||||
v8_str(isolate, "Unable to grow instance memory."));
|
||||
isolate->ThrowException(e);
|
||||
return;
|
||||
}
|
||||
i::MaybeHandle<i::JSArrayBuffer> buffer =
|
||||
internal::wasm::GetInstanceMemory(i_isolate, instance);
|
||||
if (buffer.is_null()) {
|
||||
v8::Local<v8::Value> e = v8::Exception::Error(
|
||||
v8_str(isolate, "WebAssembly.Memory buffer object not set."));
|
||||
isolate->ThrowException(e);
|
||||
return;
|
||||
}
|
||||
receiver->SetInternalField(kWasmMemoryBuffer, *buffer.ToHandleChecked());
|
||||
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
|
||||
return_value.Set(ret);
|
||||
}
|
||||
@ -570,10 +549,9 @@ void WebAssemblyMemoryGetBuffer(
|
||||
return;
|
||||
}
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
i::Handle<i::JSObject> receiver =
|
||||
i::Handle<i::JSObject>::cast(Utils::OpenHandle(*args.This()));
|
||||
i::Handle<i::Object> buffer(receiver->GetInternalField(kWasmMemoryBuffer),
|
||||
i_isolate);
|
||||
i::Handle<i::WasmMemoryObject> receiver =
|
||||
i::Handle<i::WasmMemoryObject>::cast(Utils::OpenHandle(*args.This()));
|
||||
i::Handle<i::Object> buffer(receiver->get_buffer(), i_isolate);
|
||||
DCHECK(buffer->IsJSArrayBuffer());
|
||||
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
|
||||
return_value.Set(Utils::ToLocal(buffer));
|
||||
|
@ -555,12 +555,56 @@ static void ResetCompiledModule(Isolate* isolate, WasmInstanceObject* owner,
|
||||
compiled_module->reset_memory();
|
||||
}
|
||||
|
||||
static void MemoryInstanceFinalizer(Isolate* isolate,
|
||||
WasmInstanceObject* instance) {
|
||||
// If the memory object is destroyed, nothing needs to be done here.
|
||||
if (!instance->has_memory_object()) return;
|
||||
Handle<WasmInstanceWrapper> instance_wrapper =
|
||||
handle(instance->get_instance_wrapper());
|
||||
DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper));
|
||||
DCHECK(instance_wrapper->has_instance());
|
||||
bool has_prev = instance_wrapper->has_previous();
|
||||
bool has_next = instance_wrapper->has_next();
|
||||
Handle<WasmMemoryObject> memory_object(instance->get_memory_object());
|
||||
|
||||
if (!has_prev && !has_next) {
|
||||
memory_object->ResetInstancesLink(isolate);
|
||||
return;
|
||||
} else {
|
||||
Handle<WasmInstanceWrapper> next_wrapper, prev_wrapper;
|
||||
if (!has_prev) {
|
||||
Handle<WasmInstanceWrapper> next_wrapper =
|
||||
instance_wrapper->next_wrapper();
|
||||
next_wrapper->reset_previous_wrapper();
|
||||
// As this is the first link in the memory object, destroying
|
||||
// without updating memory object would corrupt the instance chain in
|
||||
// the memory object.
|
||||
memory_object->set_instances_link(*next_wrapper);
|
||||
} else if (!has_next) {
|
||||
instance_wrapper->previous_wrapper()->reset_next_wrapper();
|
||||
} else {
|
||||
DCHECK(has_next && has_prev);
|
||||
Handle<WasmInstanceWrapper> prev_wrapper =
|
||||
instance_wrapper->previous_wrapper();
|
||||
Handle<WasmInstanceWrapper> next_wrapper =
|
||||
instance_wrapper->next_wrapper();
|
||||
prev_wrapper->set_next_wrapper(*next_wrapper);
|
||||
next_wrapper->set_previous_wrapper(*prev_wrapper);
|
||||
}
|
||||
// Reset to avoid dangling pointers
|
||||
instance_wrapper->reset();
|
||||
}
|
||||
}
|
||||
|
||||
static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
|
||||
JSObject** p = reinterpret_cast<JSObject**>(data.GetParameter());
|
||||
WasmInstanceObject* owner = reinterpret_cast<WasmInstanceObject*>(*p);
|
||||
Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate());
|
||||
// Is a link to shared memory instances exists, update the list of memory
|
||||
// instances before the instance is destroyed.
|
||||
if (owner->has_instance_wrapper()) MemoryInstanceFinalizer(isolate, owner);
|
||||
WasmCompiledModule* compiled_module = owner->get_compiled_module();
|
||||
TRACE("Finalizing %d {\n", compiled_module->instance_id());
|
||||
Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate());
|
||||
DCHECK(compiled_module->has_weak_wasm_module());
|
||||
WeakCell* weak_wasm_module = compiled_module->ptr_to_weak_wasm_module();
|
||||
|
||||
@ -1220,6 +1264,10 @@ class WasmInstanceBuilder {
|
||||
LoadDataSegments(nullptr, 0);
|
||||
}
|
||||
|
||||
if (instance->has_memory_object()) {
|
||||
instance->get_memory_object()->AddInstance(isolate_, instance);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Set up the runtime support for the new instance.
|
||||
//--------------------------------------------------------------------------
|
||||
@ -1290,12 +1338,6 @@ class WasmInstanceBuilder {
|
||||
v8::WeakCallbackType::kFinalizer);
|
||||
}
|
||||
}
|
||||
|
||||
DCHECK(wasm::IsWasmInstance(*instance));
|
||||
if (instance->has_memory_object()) {
|
||||
instance->get_memory_object()->AddInstance(*instance);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Run the start function if one was specified.
|
||||
//--------------------------------------------------------------------------
|
||||
@ -1590,6 +1632,7 @@ class WasmInstanceBuilder {
|
||||
return -1;
|
||||
}
|
||||
auto memory = Handle<WasmMemoryObject>::cast(object);
|
||||
DCHECK(WasmJs::IsWasmMemoryObject(isolate_, memory));
|
||||
instance->set_memory_object(*memory);
|
||||
memory_ = Handle<JSArrayBuffer>(memory->get_buffer(), isolate_);
|
||||
break;
|
||||
@ -1771,6 +1814,8 @@ class WasmInstanceBuilder {
|
||||
} else {
|
||||
memory_object = Handle<WasmMemoryObject>(
|
||||
instance->get_memory_object(), isolate_);
|
||||
DCHECK(WasmJs::IsWasmMemoryObject(isolate_, memory_object));
|
||||
memory_object->ResetInstancesLink(isolate_);
|
||||
}
|
||||
|
||||
desc.set_value(memory_object);
|
||||
@ -2107,8 +2152,9 @@ bool wasm::ValidateModuleBytes(Isolate* isolate, const byte* start,
|
||||
return result.ok();
|
||||
}
|
||||
|
||||
MaybeHandle<JSArrayBuffer> wasm::GetInstanceMemory(
|
||||
Isolate* isolate, Handle<WasmInstanceObject> instance) {
|
||||
MaybeHandle<JSArrayBuffer> wasm::GetInstanceMemory(Isolate* isolate,
|
||||
Handle<JSObject> object) {
|
||||
auto instance = Handle<WasmInstanceObject>::cast(object);
|
||||
if (instance->has_memory_buffer()) {
|
||||
return Handle<JSArrayBuffer>(instance->get_memory_buffer(), isolate);
|
||||
}
|
||||
@ -2124,6 +2170,7 @@ void SetInstanceMemory(Handle<WasmInstanceObject> instance,
|
||||
|
||||
int32_t wasm::GetInstanceMemorySize(Isolate* isolate,
|
||||
Handle<WasmInstanceObject> instance) {
|
||||
DCHECK(IsWasmInstance(*instance));
|
||||
MaybeHandle<JSArrayBuffer> maybe_mem_buffer =
|
||||
GetInstanceMemory(isolate, instance);
|
||||
Handle<JSArrayBuffer> buffer;
|
||||
@ -2151,42 +2198,26 @@ uint32_t GetMaxInstanceMemorySize(Isolate* isolate,
|
||||
return WasmModule::kV8MaxPages;
|
||||
}
|
||||
|
||||
int32_t wasm::GrowInstanceMemory(Isolate* isolate,
|
||||
Handle<WasmInstanceObject> instance,
|
||||
uint32_t pages) {
|
||||
if (pages == 0) return GetInstanceMemorySize(isolate, instance);
|
||||
uint32_t max_pages = GetMaxInstanceMemorySize(isolate, instance);
|
||||
|
||||
Address old_mem_start = nullptr;
|
||||
uint32_t old_size = 0, new_size = 0;
|
||||
|
||||
MaybeHandle<JSArrayBuffer> maybe_mem_buffer =
|
||||
GetInstanceMemory(isolate, instance);
|
||||
Handle<JSArrayBuffer> GrowMemoryBuffer(Isolate* isolate,
|
||||
MaybeHandle<JSArrayBuffer> buffer,
|
||||
uint32_t pages, uint32_t max_pages) {
|
||||
Handle<JSArrayBuffer> old_buffer;
|
||||
if (!maybe_mem_buffer.ToHandle(&old_buffer) ||
|
||||
old_buffer->backing_store() == nullptr) {
|
||||
// If module object does not have linear memory associated with it,
|
||||
// Allocate new array buffer of given size.
|
||||
new_size = pages * WasmModule::kPageSize;
|
||||
if (max_pages < pages) return -1;
|
||||
} else {
|
||||
Address old_mem_start = nullptr;
|
||||
uint32_t old_size = 0;
|
||||
if (buffer.ToHandle(&old_buffer) && old_buffer->backing_store() != nullptr) {
|
||||
old_mem_start = static_cast<Address>(old_buffer->backing_store());
|
||||
old_size = old_buffer->byte_length()->Number();
|
||||
// If the old memory was zero-sized, we should have been in the
|
||||
// "undefined" case above.
|
||||
DCHECK_NOT_NULL(old_mem_start);
|
||||
DCHECK(old_size + pages * WasmModule::kPageSize <=
|
||||
std::numeric_limits<uint32_t>::max());
|
||||
new_size = old_size + pages * WasmModule::kPageSize;
|
||||
old_size = old_buffer->byte_length()->Number();
|
||||
}
|
||||
|
||||
DCHECK(old_size + pages * WasmModule::kPageSize <=
|
||||
std::numeric_limits<uint32_t>::max());
|
||||
uint32_t new_size = old_size + pages * WasmModule::kPageSize;
|
||||
if (new_size <= old_size || max_pages * WasmModule::kPageSize < new_size ||
|
||||
WasmModule::kV8MaxPages * WasmModule::kPageSize < new_size) {
|
||||
return -1;
|
||||
return Handle<JSArrayBuffer>::null();
|
||||
}
|
||||
|
||||
Handle<JSArrayBuffer> buffer;
|
||||
|
||||
Handle<JSArrayBuffer> new_buffer;
|
||||
if (!old_buffer.is_null() && old_buffer->has_guard_region()) {
|
||||
// We don't move the backing store, we simply change the protection to make
|
||||
// more of it accessible.
|
||||
@ -2196,37 +2227,107 @@ int32_t wasm::GrowInstanceMemory(Isolate* isolate,
|
||||
Handle<Object> new_size_object =
|
||||
isolate->factory()->NewNumberFromSize(new_size);
|
||||
old_buffer->set_byte_length(*new_size_object);
|
||||
|
||||
SetInstanceMemory(instance, *old_buffer);
|
||||
Handle<FixedArray> code_table =
|
||||
instance->get_compiled_module()->code_table();
|
||||
RelocateMemoryReferencesInCode(code_table, old_mem_start, old_mem_start,
|
||||
old_size, new_size);
|
||||
buffer = old_buffer;
|
||||
new_buffer = old_buffer;
|
||||
} else {
|
||||
const bool enable_guard_regions = false;
|
||||
buffer = NewArrayBuffer(isolate, new_size, enable_guard_regions);
|
||||
if (buffer.is_null()) return -1;
|
||||
Address new_mem_start = static_cast<Address>(buffer->backing_store());
|
||||
new_buffer = NewArrayBuffer(isolate, new_size, enable_guard_regions);
|
||||
if (new_buffer.is_null()) return new_buffer;
|
||||
Address new_mem_start = static_cast<Address>(new_buffer->backing_store());
|
||||
if (old_size != 0) {
|
||||
memcpy(new_mem_start, old_mem_start, old_size);
|
||||
}
|
||||
SetInstanceMemory(instance, *buffer);
|
||||
Handle<FixedArray> code_table =
|
||||
instance->get_compiled_module()->code_table();
|
||||
RelocateMemoryReferencesInCode(code_table, old_mem_start, new_mem_start,
|
||||
old_size, new_size);
|
||||
}
|
||||
return new_buffer;
|
||||
}
|
||||
|
||||
SetInstanceMemory(instance, *buffer);
|
||||
if (instance->has_memory_object()) {
|
||||
instance->get_memory_object()->set_buffer(*buffer);
|
||||
void UncheckedUpdateInstanceMemory(Isolate* isolate,
|
||||
Handle<WasmInstanceObject> instance,
|
||||
Address old_mem_start, uint32_t old_size) {
|
||||
DCHECK(instance->has_memory_buffer());
|
||||
Handle<JSArrayBuffer> new_buffer(instance->get_memory_buffer());
|
||||
uint32_t new_size = new_buffer->byte_length()->Number();
|
||||
DCHECK(new_size <= std::numeric_limits<uint32_t>::max());
|
||||
Address new_mem_start = static_cast<Address>(new_buffer->backing_store());
|
||||
DCHECK_NOT_NULL(new_mem_start);
|
||||
Handle<FixedArray> code_table = instance->get_compiled_module()->code_table();
|
||||
RelocateMemoryReferencesInCode(code_table, old_mem_start, new_mem_start,
|
||||
old_size, new_size);
|
||||
}
|
||||
|
||||
int32_t wasm::GrowWebAssemblyMemory(Isolate* isolate, Handle<Object> receiver,
|
||||
uint32_t pages) {
|
||||
DCHECK(WasmJs::IsWasmMemoryObject(isolate, receiver));
|
||||
Handle<WasmMemoryObject> memory_object(WasmMemoryObject::cast(*receiver));
|
||||
Handle<WasmInstanceWrapper> instance_wrapper(
|
||||
memory_object->get_instances_link());
|
||||
DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper));
|
||||
DCHECK(instance_wrapper->has_instance());
|
||||
Handle<WasmInstanceObject> instance = instance_wrapper->instance_object();
|
||||
DCHECK(IsWasmInstance(*instance));
|
||||
if (pages == 0) return GetInstanceMemorySize(isolate, instance);
|
||||
uint32_t max_pages = GetMaxInstanceMemorySize(isolate, instance);
|
||||
|
||||
// Grow memory object buffer and update instances associated with it.
|
||||
MaybeHandle<JSArrayBuffer> memory_buffer =
|
||||
handle(memory_object->get_buffer());
|
||||
Handle<JSArrayBuffer> old_buffer;
|
||||
uint32_t old_size = 0;
|
||||
Address old_mem_start = nullptr;
|
||||
if (memory_buffer.ToHandle(&old_buffer) &&
|
||||
old_buffer->backing_store() != nullptr) {
|
||||
old_size = old_buffer->byte_length()->Number();
|
||||
old_mem_start = static_cast<Address>(old_buffer->backing_store());
|
||||
}
|
||||
|
||||
Handle<JSArrayBuffer> new_buffer =
|
||||
GrowMemoryBuffer(isolate, memory_buffer, pages, max_pages);
|
||||
if (new_buffer.is_null()) return -1;
|
||||
DCHECK(!instance_wrapper->has_previous());
|
||||
SetInstanceMemory(instance, *new_buffer);
|
||||
UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size);
|
||||
while (instance_wrapper->has_next()) {
|
||||
instance_wrapper = instance_wrapper->next_wrapper();
|
||||
DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper));
|
||||
Handle<WasmInstanceObject> instance = instance_wrapper->instance_object();
|
||||
DCHECK(IsWasmInstance(*instance));
|
||||
SetInstanceMemory(instance, *new_buffer);
|
||||
UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size);
|
||||
}
|
||||
memory_object->set_buffer(*new_buffer);
|
||||
DCHECK(old_size % WasmModule::kPageSize == 0);
|
||||
return (old_size / WasmModule::kPageSize);
|
||||
}
|
||||
|
||||
int32_t wasm::GrowMemory(Isolate* isolate, Handle<WasmInstanceObject> instance,
|
||||
uint32_t pages) {
|
||||
if (!IsWasmInstance(*instance)) return -1;
|
||||
if (pages == 0) return GetInstanceMemorySize(isolate, instance);
|
||||
Handle<WasmInstanceObject> instance_obj(WasmInstanceObject::cast(*instance));
|
||||
if (!instance_obj->has_memory_object()) {
|
||||
// No other instances to grow, grow just the one.
|
||||
MaybeHandle<JSArrayBuffer> instance_buffer =
|
||||
GetInstanceMemory(isolate, instance);
|
||||
Handle<JSArrayBuffer> old_buffer;
|
||||
uint32_t old_size = 0;
|
||||
Address old_mem_start = nullptr;
|
||||
if (instance_buffer.ToHandle(&old_buffer) &&
|
||||
old_buffer->backing_store() != nullptr) {
|
||||
old_size = old_buffer->byte_length()->Number();
|
||||
old_mem_start = static_cast<Address>(old_buffer->backing_store());
|
||||
}
|
||||
uint32_t max_pages = GetMaxInstanceMemorySize(isolate, instance_obj);
|
||||
Handle<JSArrayBuffer> buffer =
|
||||
GrowMemoryBuffer(isolate, instance_buffer, pages, max_pages);
|
||||
if (buffer.is_null()) return -1;
|
||||
SetInstanceMemory(instance, *buffer);
|
||||
UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size);
|
||||
DCHECK(old_size % WasmModule::kPageSize == 0);
|
||||
return (old_size / WasmModule::kPageSize);
|
||||
} else {
|
||||
return GrowWebAssemblyMemory(
|
||||
isolate, handle(instance_obj->get_memory_object()), pages);
|
||||
}
|
||||
}
|
||||
|
||||
void testing::ValidateInstancesChain(Isolate* isolate,
|
||||
Handle<WasmModuleObject> module_obj,
|
||||
int instance_count) {
|
||||
|
@ -422,8 +422,8 @@ bool GetPositionInfo(Handle<WasmCompiledModule> compiled_module,
|
||||
// Returns nullptr on failing to get owning instance.
|
||||
WasmInstanceObject* GetOwningWasmInstance(Code* code);
|
||||
|
||||
MaybeHandle<JSArrayBuffer> GetInstanceMemory(
|
||||
Isolate* isolate, Handle<WasmInstanceObject> instance);
|
||||
MaybeHandle<JSArrayBuffer> GetInstanceMemory(Isolate* isolate,
|
||||
Handle<JSObject> instance);
|
||||
|
||||
int32_t GetInstanceMemorySize(Isolate* isolate,
|
||||
Handle<WasmInstanceObject> instance);
|
||||
@ -434,6 +434,14 @@ int32_t GrowInstanceMemory(Isolate* isolate,
|
||||
Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size,
|
||||
bool enable_guard_regions);
|
||||
|
||||
int32_t GetInstanceMemorySize(Isolate* isolate, Handle<JSObject> instance);
|
||||
|
||||
int32_t GrowWebAssemblyMemory(Isolate* isolate, Handle<Object> receiver,
|
||||
uint32_t pages);
|
||||
|
||||
int32_t GrowMemory(Isolate* isolate, Handle<WasmInstanceObject> instance,
|
||||
uint32_t pages);
|
||||
|
||||
void UpdateDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
|
||||
int index, Handle<JSFunction> js_function);
|
||||
|
||||
|
@ -184,6 +184,8 @@ Handle<WasmMemoryObject> WasmMemoryObject::New(Isolate* isolate,
|
||||
}
|
||||
|
||||
DEFINE_ACCESSORS(WasmMemoryObject, buffer, kArrayBuffer, JSArrayBuffer)
|
||||
DEFINE_OPTIONAL_ACCESSORS(WasmMemoryObject, instances_link, kInstancesLink,
|
||||
WasmInstanceWrapper)
|
||||
|
||||
uint32_t WasmMemoryObject::current_pages() {
|
||||
return SafeUint32(get_buffer()->byte_length()) / wasm::WasmModule::kPageSize;
|
||||
@ -199,10 +201,26 @@ WasmMemoryObject* WasmMemoryObject::cast(Object* object) {
|
||||
return reinterpret_cast<WasmMemoryObject*>(object);
|
||||
}
|
||||
|
||||
void WasmMemoryObject::AddInstance(WasmInstanceObject* instance) {
|
||||
// TODO(gdeepti): This should be a weak list of instance objects
|
||||
// for instances that share memory.
|
||||
SetInternalField(kInstance, instance);
|
||||
void WasmMemoryObject::AddInstance(Isolate* isolate,
|
||||
Handle<WasmInstanceObject> instance) {
|
||||
Handle<WasmInstanceWrapper> instance_wrapper;
|
||||
if (has_instances_link()) {
|
||||
Handle<WasmInstanceWrapper> current_wrapper(get_instances_link());
|
||||
DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*current_wrapper));
|
||||
DCHECK(!current_wrapper->has_previous());
|
||||
instance_wrapper = WasmInstanceWrapper::New(isolate, instance);
|
||||
instance_wrapper->set_next_wrapper(*current_wrapper);
|
||||
current_wrapper->set_previous_wrapper(*instance_wrapper);
|
||||
} else {
|
||||
instance_wrapper = WasmInstanceWrapper::New(isolate, instance);
|
||||
}
|
||||
set_instances_link(*instance_wrapper);
|
||||
instance->set_instance_wrapper(*instance_wrapper);
|
||||
}
|
||||
|
||||
void WasmMemoryObject::ResetInstancesLink(Isolate* isolate) {
|
||||
Handle<Object> undefined = isolate->factory()->undefined_value();
|
||||
SetInternalField(kInstancesLink, *undefined);
|
||||
}
|
||||
|
||||
DEFINE_ACCESSORS(WasmInstanceObject, compiled_module, kCompiledModule,
|
||||
@ -215,6 +233,8 @@ DEFINE_OPTIONAL_ACCESSORS(WasmInstanceObject, memory_object, kMemoryObject,
|
||||
WasmMemoryObject)
|
||||
DEFINE_OPTIONAL_ACCESSORS(WasmInstanceObject, debug_info, kDebugInfo,
|
||||
WasmDebugInfo)
|
||||
DEFINE_OPTIONAL_ACCESSORS(WasmInstanceObject, instance_wrapper,
|
||||
kWasmMemInstanceWrapper, WasmInstanceWrapper)
|
||||
|
||||
WasmModuleObject* WasmInstanceObject::module_object() {
|
||||
return WasmModuleObject::cast(*get_compiled_module()->wasm_module());
|
||||
@ -385,3 +405,34 @@ int WasmCompiledModule::GetFunctionOffset(uint32_t func_index) const {
|
||||
functions[func_index].code_start_offset);
|
||||
return static_cast<int>(functions[func_index].code_start_offset);
|
||||
}
|
||||
|
||||
Handle<WasmInstanceWrapper> WasmInstanceWrapper::New(
|
||||
Isolate* isolate, Handle<WasmInstanceObject> instance) {
|
||||
Handle<FixedArray> array =
|
||||
isolate->factory()->NewFixedArray(kWrapperPropertyCount, TENURED);
|
||||
Handle<WasmInstanceWrapper> instance_wrapper(
|
||||
reinterpret_cast<WasmInstanceWrapper*>(*array), isolate);
|
||||
instance_wrapper->set_instance_object(instance, isolate);
|
||||
return instance_wrapper;
|
||||
}
|
||||
|
||||
bool WasmInstanceWrapper::IsWasmInstanceWrapper(Object* obj) {
|
||||
if (!obj->IsFixedArray()) return false;
|
||||
FixedArray* array = FixedArray::cast(obj);
|
||||
if (array->length() != kWrapperPropertyCount) return false;
|
||||
if (!array->get(kWrapperInstanceObject)->IsWeakCell()) return false;
|
||||
Isolate* isolate = array->GetIsolate();
|
||||
if (!array->get(kNextInstanceWrapper)->IsUndefined(isolate) &&
|
||||
!array->get(kNextInstanceWrapper)->IsFixedArray())
|
||||
return false;
|
||||
if (!array->get(kPreviousInstanceWrapper)->IsUndefined(isolate) &&
|
||||
!array->get(kPreviousInstanceWrapper)->IsFixedArray())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void WasmInstanceWrapper::set_instance_object(Handle<JSObject> instance,
|
||||
Isolate* isolate) {
|
||||
Handle<WeakCell> cell = isolate->factory()->NewWeakCell(instance);
|
||||
set(kWrapperInstanceObject, *cell);
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ struct WasmModule;
|
||||
class WasmCompiledModule;
|
||||
class WasmDebugInfo;
|
||||
class WasmInstanceObject;
|
||||
class WasmInstanceWrapper;
|
||||
|
||||
#define DECLARE_CASTS(name) \
|
||||
static bool Is##name(Object* object); \
|
||||
@ -79,12 +80,14 @@ class WasmTableObject : public JSObject {
|
||||
class WasmMemoryObject : public JSObject {
|
||||
public:
|
||||
// TODO(titzer): add the brand as an internal field instead of a property.
|
||||
enum Fields : uint8_t { kArrayBuffer, kMaximum, kInstance, kFieldCount };
|
||||
enum Fields : uint8_t { kArrayBuffer, kMaximum, kInstancesLink, kFieldCount };
|
||||
|
||||
DECLARE_CASTS(WasmMemoryObject);
|
||||
DECLARE_ACCESSORS(buffer, JSArrayBuffer);
|
||||
DECLARE_OPTIONAL_ACCESSORS(instances_link, WasmInstanceWrapper);
|
||||
|
||||
void AddInstance(WasmInstanceObject* object);
|
||||
void AddInstance(Isolate* isolate, Handle<WasmInstanceObject> object);
|
||||
void ResetInstancesLink(Isolate* isolate);
|
||||
uint32_t current_pages();
|
||||
int32_t maximum_pages(); // returns < 0 if there is no maximum
|
||||
|
||||
@ -105,6 +108,7 @@ class WasmInstanceObject : public JSObject {
|
||||
kMemoryArrayBuffer,
|
||||
kGlobalsArrayBuffer,
|
||||
kDebugInfo,
|
||||
kWasmMemInstanceWrapper,
|
||||
kFieldCount
|
||||
};
|
||||
|
||||
@ -115,6 +119,7 @@ class WasmInstanceObject : public JSObject {
|
||||
DECLARE_OPTIONAL_ACCESSORS(memory_buffer, JSArrayBuffer);
|
||||
DECLARE_OPTIONAL_ACCESSORS(memory_object, WasmMemoryObject);
|
||||
DECLARE_OPTIONAL_ACCESSORS(debug_info, WasmDebugInfo);
|
||||
DECLARE_OPTIONAL_ACCESSORS(instance_wrapper, WasmInstanceWrapper);
|
||||
|
||||
WasmModuleObject* module_object();
|
||||
wasm::WasmModule* module();
|
||||
@ -316,6 +321,61 @@ class WasmDebugInfo : public FixedArray {
|
||||
int func_index, int byte_offset);
|
||||
};
|
||||
|
||||
class WasmInstanceWrapper : public FixedArray {
|
||||
public:
|
||||
static Handle<WasmInstanceWrapper> New(Isolate* isolate,
|
||||
Handle<WasmInstanceObject> instance);
|
||||
static WasmInstanceWrapper* cast(Object* fixed_array) {
|
||||
SLOW_DCHECK(IsWasmInstanceWrapper(fixed_array));
|
||||
return reinterpret_cast<WasmInstanceWrapper*>(fixed_array);
|
||||
}
|
||||
static bool IsWasmInstanceWrapper(Object* obj);
|
||||
bool has_instance() { return get(kWrapperInstanceObject)->IsWeakCell(); }
|
||||
Handle<WasmInstanceObject> instance_object() {
|
||||
Object* obj = get(kWrapperInstanceObject);
|
||||
DCHECK(obj->IsWeakCell());
|
||||
WeakCell* cell = WeakCell::cast(obj);
|
||||
DCHECK(cell->value()->IsJSObject());
|
||||
return handle(WasmInstanceObject::cast(cell->value()));
|
||||
}
|
||||
bool has_next() { return IsWasmInstanceWrapper(get(kNextInstanceWrapper)); }
|
||||
bool has_previous() {
|
||||
return IsWasmInstanceWrapper(get(kPreviousInstanceWrapper));
|
||||
}
|
||||
void set_instance_object(Handle<JSObject> instance, Isolate* isolate);
|
||||
void set_next_wrapper(Object* obj) {
|
||||
DCHECK(IsWasmInstanceWrapper(obj));
|
||||
set(kNextInstanceWrapper, obj);
|
||||
}
|
||||
void set_previous_wrapper(Object* obj) {
|
||||
DCHECK(IsWasmInstanceWrapper(obj));
|
||||
set(kPreviousInstanceWrapper, obj);
|
||||
}
|
||||
Handle<WasmInstanceWrapper> next_wrapper() {
|
||||
Object* obj = get(kNextInstanceWrapper);
|
||||
DCHECK(IsWasmInstanceWrapper(obj));
|
||||
return handle(WasmInstanceWrapper::cast(obj));
|
||||
}
|
||||
Handle<WasmInstanceWrapper> previous_wrapper() {
|
||||
Object* obj = get(kPreviousInstanceWrapper);
|
||||
DCHECK(IsWasmInstanceWrapper(obj));
|
||||
return handle(WasmInstanceWrapper::cast(obj));
|
||||
}
|
||||
void reset_next_wrapper() { set_undefined(kNextInstanceWrapper); }
|
||||
void reset_previous_wrapper() { set_undefined(kPreviousInstanceWrapper); }
|
||||
void reset() {
|
||||
for (int kID = 0; kID < kWrapperPropertyCount; kID++) set_undefined(kID);
|
||||
}
|
||||
|
||||
private:
|
||||
enum {
|
||||
kWrapperInstanceObject,
|
||||
kNextInstanceWrapper,
|
||||
kPreviousInstanceWrapper,
|
||||
kWrapperPropertyCount
|
||||
};
|
||||
};
|
||||
|
||||
#undef DECLARE_ACCESSORS
|
||||
#undef DECLARE_OPTIONAL_ACCESSORS
|
||||
|
||||
|
@ -40,7 +40,6 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
assertSame(memory, instance.exports.daggle);
|
||||
})();
|
||||
|
||||
|
||||
(function TestImportExport() {
|
||||
print("TestImportExport");
|
||||
var i1;
|
||||
@ -229,3 +228,141 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
}
|
||||
assertEquals(-1, instance.exports.grow(1));
|
||||
})();
|
||||
|
||||
(function TestMemoryGrowWebAssemblyInstances() {
|
||||
print("TestMemoryGrowWebAssemblyInstances");
|
||||
let memory = new WebAssembly.Memory({initial: 1, maximum: 15});
|
||||
var builder = new WasmModuleBuilder();
|
||||
builder.addImportedMemory("imported_mem");
|
||||
builder.addFunction("mem_size", kSig_i_v)
|
||||
.addBody([kExprMemorySize, kMemoryZero])
|
||||
.exportAs("mem_size");
|
||||
builder.addFunction("grow", kSig_i_i)
|
||||
.addBody([kExprGetLocal, 0, kExprGrowMemory, kMemoryZero])
|
||||
.exportFunc();
|
||||
var module = new WebAssembly.Module(builder.toBuffer());
|
||||
var instances = [];
|
||||
for (var i = 0; i < 6; i++) {
|
||||
instances.push(new WebAssembly.Instance(module, {imported_mem: memory}));
|
||||
}
|
||||
function verify_mem_size(expected_pages) {
|
||||
assertEquals(expected_pages*kPageSize,
|
||||
memory.buffer.byteLength);
|
||||
for (var i = 0; i < 6; i++) {
|
||||
assertEquals(expected_pages, instances[i].exports.mem_size());
|
||||
}
|
||||
}
|
||||
|
||||
// Verify initial memory size
|
||||
verify_mem_size(1);
|
||||
|
||||
// Verify memory size with interleaving calls to Memory.grow,
|
||||
// GrowMemory opcode.
|
||||
var current_mem_size = 1;
|
||||
for (var i = 0; i < 5; i++) {
|
||||
function grow(pages) { return instances[i].exports.grow(pages); }
|
||||
assertEquals(current_mem_size, memory.grow(1));
|
||||
verify_mem_size(++current_mem_size);
|
||||
assertEquals(current_mem_size, instances[i].exports.grow(1));
|
||||
verify_mem_size(++current_mem_size);
|
||||
}
|
||||
|
||||
assertThrows(() => memory.grow(5));
|
||||
})();
|
||||
|
||||
(function TestImportedMemoryGrowMultipleInstances() {
|
||||
print("TestImportMemoryMultipleInstances");
|
||||
let memory = new WebAssembly.Memory({initial: 5, maximum: 100});
|
||||
var builder = new WasmModuleBuilder();
|
||||
builder.addImportedMemory("imported_mem");
|
||||
builder.addFunction("mem_size", kSig_i_v)
|
||||
.addBody([kExprMemorySize, kMemoryZero])
|
||||
.exportFunc();
|
||||
builder.addFunction("grow", kSig_i_i)
|
||||
.addBody([kExprGetLocal, 0, kExprGrowMemory, kMemoryZero])
|
||||
.exportFunc();
|
||||
var instances = [];
|
||||
for (var i = 0; i < 5; i++) {
|
||||
instances.push(builder.instantiate({imported_mem: memory}));
|
||||
}
|
||||
function grow_instance_0(pages) { return instances[0].exports.grow(pages); }
|
||||
function grow_instance_1(pages) { return instances[1].exports.grow(pages); }
|
||||
function grow_instance_2(pages) { return instances[2].exports.grow(pages); }
|
||||
function grow_instance_3(pages) { return instances[3].exports.grow(pages); }
|
||||
function grow_instance_4(pages) { return instances[4].exports.grow(pages); }
|
||||
|
||||
function verify_mem_size(expected_pages) {
|
||||
assertEquals(expected_pages*kPageSize, memory.buffer.byteLength);
|
||||
for (var i = 0; i < 5; i++) {
|
||||
assertEquals(expected_pages, instances[i].exports.mem_size());
|
||||
}
|
||||
}
|
||||
|
||||
// Verify initial memory size
|
||||
verify_mem_size(5);
|
||||
|
||||
// Grow instance memory and buffer memory out of order and verify memory is
|
||||
// updated correctly.
|
||||
assertEquals(5, grow_instance_0(7));
|
||||
verify_mem_size(12);
|
||||
|
||||
assertEquals(12, memory.grow(4));
|
||||
verify_mem_size(16);
|
||||
|
||||
assertEquals(16, grow_instance_4(1));
|
||||
verify_mem_size(17);
|
||||
|
||||
assertEquals(17, grow_instance_1(6));
|
||||
verify_mem_size(23);
|
||||
|
||||
assertEquals(23, grow_instance_3(2));
|
||||
verify_mem_size(25);
|
||||
|
||||
assertEquals(25, memory.grow(10));
|
||||
verify_mem_size(35);
|
||||
|
||||
assertEquals(35, grow_instance_2(15));
|
||||
verify_mem_size(50);
|
||||
assertThrows(() => memory.grow(51));
|
||||
})();
|
||||
|
||||
(function TestExportImportedMemoryGrowMultipleInstances() {
|
||||
// TODO(gdeepti):Exported memory objects currently do not take max_size
|
||||
// into account so this can grow past the maximum specified in the exported
|
||||
// memory object. Assert that growing past maximum for exported objects fails.
|
||||
print("TestExportImportedMemoryGrowMultipleInstances");
|
||||
var instance;
|
||||
{
|
||||
let builder = new WasmModuleBuilder();
|
||||
builder.addMemory(1, 11, true);
|
||||
builder.exportMemoryAs("exported_mem");
|
||||
builder.addFunction("mem_size", kSig_i_v)
|
||||
.addBody([kExprMemorySize, kMemoryZero])
|
||||
.exportFunc();
|
||||
instance = builder.instantiate();
|
||||
}
|
||||
var builder = new WasmModuleBuilder();
|
||||
builder.addImportedMemory("imported_mem");
|
||||
builder.addFunction("mem_size", kSig_i_v)
|
||||
.addBody([kExprMemorySize, kMemoryZero])
|
||||
.exportFunc();
|
||||
builder.addFunction("grow", kSig_i_i)
|
||||
.addBody([kExprGetLocal, 0, kExprGrowMemory, kMemoryZero])
|
||||
.exportFunc();
|
||||
var instances = [];
|
||||
for (var i = 0; i < 10; i++) {
|
||||
instances.push(builder.instantiate({
|
||||
imported_mem: instance.exports.exported_mem}));
|
||||
}
|
||||
function verify_mem_size(expected_pages) {
|
||||
for (var i = 0; i < 10; i++) {
|
||||
assertEquals(expected_pages, instances[i].exports.mem_size());
|
||||
}
|
||||
}
|
||||
var current_mem_size = 1;
|
||||
for (var i = 0; i < 10; i++) {
|
||||
function grow(pages) { return instances[i].exports.grow(pages); }
|
||||
assertEquals(current_mem_size, instances[i].exports.grow(1));
|
||||
verify_mem_size(++current_mem_size);
|
||||
}
|
||||
})();
|
||||
|
@ -215,6 +215,7 @@ assertFalse(WebAssembly.validate(bytes(88, 88, 88, 88, 88, 88, 88, 88)));
|
||||
})();
|
||||
|
||||
(function MustBeMemory() {
|
||||
print("MustBeMemory...");
|
||||
var memory = new ArrayBuffer(65536);
|
||||
var module = new WebAssembly.Module(buffer);
|
||||
assertThrows(() => new WebAssembly.Instance(module, null, memory), TypeError);
|
||||
|
103
test/mjsunit/wasm/memory-instance-validation.js
Normal file
103
test/mjsunit/wasm/memory-instance-validation.js
Normal file
@ -0,0 +1,103 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// Flags: --expose-wasm --expose-gc
|
||||
|
||||
load("test/mjsunit/wasm/wasm-constants.js");
|
||||
load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
|
||||
// This test verifies that when instances are exported, Gc'ed, the other
|
||||
// instances in the chain still maintain a consistent view of the memory.
|
||||
(function ValidateSharedInstanceMemory() {
|
||||
print("ValidateSharedInstanceMemory");
|
||||
let memory = new WebAssembly.Memory({initial: 5, maximum: 100});
|
||||
var builder = new WasmModuleBuilder();
|
||||
builder.addImportedMemory("imported_mem");
|
||||
builder.addFunction("mem_size", kSig_i_v)
|
||||
.addBody([kExprMemorySize, kMemoryZero])
|
||||
.exportFunc();
|
||||
builder.addFunction("grow", kSig_i_i)
|
||||
.addBody([kExprGetLocal, 0, kExprGrowMemory, kMemoryZero])
|
||||
.exportFunc();
|
||||
var instances = [];
|
||||
for (var i = 0; i < 5; i++) {
|
||||
instances.push(builder.instantiate({imported_mem: memory}));
|
||||
}
|
||||
function grow_instance_0(pages) { return instances[0].exports.grow(pages); }
|
||||
function grow_instance_1(pages) { return instances[1].exports.grow(pages); }
|
||||
function grow_instance_2(pages) { return instances[2].exports.grow(pages); }
|
||||
function grow_instance_3(pages) { return instances[3].exports.grow(pages); }
|
||||
function grow_instance_4(pages) { return instances[4].exports.grow(pages); }
|
||||
|
||||
var start_index = 0;
|
||||
var end_index = 5;
|
||||
function verify_mem_size(expected_pages) {
|
||||
assertEquals(expected_pages*kPageSize, memory.buffer.byteLength);
|
||||
for (var i = start_index; i < end_index; i++) {
|
||||
assertEquals(expected_pages, instances[i].exports.mem_size());
|
||||
}
|
||||
}
|
||||
|
||||
// Verify initial memory size of all instances, grow and verify that all
|
||||
// instances are updated correctly.
|
||||
verify_mem_size(5);
|
||||
assertEquals(5, memory.grow(6));
|
||||
verify_mem_size(11);
|
||||
|
||||
instances[1] = null;
|
||||
gc();
|
||||
|
||||
// i[0] - i[2] - i[3] - i[4]
|
||||
start_index = 2;
|
||||
verify_mem_size(11);
|
||||
assertEquals(11, instances[0].exports.mem_size());
|
||||
assertEquals(11, grow_instance_2(10));
|
||||
assertEquals(21*kPageSize, memory.buffer.byteLength);
|
||||
verify_mem_size(21);
|
||||
assertEquals(21, instances[0].exports.mem_size());
|
||||
|
||||
instances[4] = null;
|
||||
gc();
|
||||
|
||||
// i[0] - i[2] - i[3]
|
||||
assertEquals(21, instances[0].exports.mem_size());
|
||||
assertEquals(21, instances[2].exports.mem_size());
|
||||
assertEquals(21, instances[3].exports.mem_size());
|
||||
assertEquals(21, memory.grow(2));
|
||||
assertEquals(23*kPageSize, memory.buffer.byteLength);
|
||||
assertEquals(23, instances[0].exports.mem_size());
|
||||
assertEquals(23, instances[2].exports.mem_size());
|
||||
assertEquals(23, instances[3].exports.mem_size());
|
||||
|
||||
instances[0] = null;
|
||||
gc();
|
||||
|
||||
// i[2] - i[3]
|
||||
assertEquals(23, instances[2].exports.mem_size());
|
||||
assertEquals(23, instances[3].exports.mem_size());
|
||||
assertEquals(23, grow_instance_3(5));
|
||||
assertEquals(28*kPageSize, memory.buffer.byteLength);
|
||||
assertEquals(28, instances[2].exports.mem_size());
|
||||
assertEquals(28, instances[3].exports.mem_size());
|
||||
|
||||
// Instantiate a new instance and verify that it can be grown correctly.
|
||||
instances.push(builder.instantiate({imported_mem: memory}));
|
||||
function grow_instance_5(pages) { return instances[5].exports.grow(pages); }
|
||||
|
||||
// i[2] - i[3] - i[5]
|
||||
assertEquals(28, instances[2].exports.mem_size());
|
||||
assertEquals(28, instances[3].exports.mem_size());
|
||||
assertEquals(28, instances[5].exports.mem_size());
|
||||
assertEquals(28, grow_instance_5(2));
|
||||
assertEquals(30*kPageSize, memory.buffer.byteLength);
|
||||
assertEquals(30, instances[2].exports.mem_size());
|
||||
assertEquals(30, instances[3].exports.mem_size());
|
||||
assertEquals(30, instances[5].exports.mem_size());
|
||||
assertEquals(30, memory.grow(5));
|
||||
assertEquals(35*kPageSize, memory.buffer.byteLength);
|
||||
assertEquals(35, instances[2].exports.mem_size());
|
||||
assertEquals(35, instances[3].exports.mem_size());
|
||||
assertEquals(35, instances[5].exports.mem_size());
|
||||
|
||||
})();
|
Loading…
Reference in New Issue
Block a user