[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:
gdeepti 2016-11-18 19:31:12 -08:00 committed by Commit bot
parent 06f8e87726
commit 30ef8e33f3
9 changed files with 534 additions and 95 deletions

View File

@ -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) {

View File

@ -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));

View File

@ -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) {

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}
})();

View File

@ -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);

View 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());
})();