Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(464)

Unified Diff: src/wasm/wasm-module.cc

Issue 2471883003: [wasm] WebAssembly.Memory object can be referenced by multiple Instance objects. (Closed)
Patch Set: Rebase, add Dcheck Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/wasm/wasm-module.cc
diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc
index aa385f824e6917b38815aadf8d047c4fa5964ea4..4f6dbac5d2855832052ec923119592565d8541bd 100644
--- a/src/wasm/wasm-module.cc
+++ b/src/wasm/wasm-module.cc
@@ -81,6 +81,7 @@ enum WasmInstanceObjectFields {
kWasmMemArrayBuffer,
kWasmGlobalsArrayBuffer,
kWasmDebugInfo,
+ kWasmMemInstanceWrapper,
kWasmInstanceInternalFieldCount
};
@@ -1223,11 +1224,6 @@ class WasmInstanceBuilder {
}
}
- DCHECK(wasm::IsWasmInstance(*instance));
- Handle<Object> memory_object(instance->GetInternalField(kWasmMemObject),
- isolate_);
- WasmJs::SetWasmMemoryInstance(isolate_, memory_object, instance);
-
//--------------------------------------------------------------------------
// Run the start function if one was specified.
//--------------------------------------------------------------------------
@@ -1504,6 +1500,7 @@ class WasmInstanceBuilder {
}
instance->SetInternalField(kWasmMemObject, *object);
memory_ = WasmJs::GetWasmMemoryArrayBuffer(isolate_, object);
+ WasmJs::SetWasmMemoryInstance(isolate_, object, instance);
break;
}
case kExternalGlobal: {
@@ -1679,6 +1676,9 @@ class WasmInstanceBuilder {
memory_object =
WasmJs::CreateWasmMemoryObject(isolate_, buffer, false, 0);
instance->SetInternalField(kWasmMemObject, *memory_object);
+ } else {
+ DCHECK(WasmJs::IsWasmMemoryObject(isolate_, memory_object));
+ WasmJs::ResetWasmMemoryInstance(isolate_, memory_object);
}
desc.set_value(memory_object);
@@ -2072,6 +2072,12 @@ bool wasm::ValidateModuleBytes(Isolate* isolate, const byte* start,
return false;
}
+void wasm::SetInstanceMemoryObject(Handle<JSObject> instance,
+ Handle<Object> mem_object) {
+ DCHECK(IsWasmInstance(*instance));
+ instance->SetInternalField(kWasmMemObject, *mem_object);
+}
+
MaybeHandle<JSArrayBuffer> wasm::GetInstanceMemory(Isolate* isolate,
Handle<JSObject> instance) {
Object* mem = instance->GetInternalField(kWasmMemArrayBuffer);
@@ -2090,6 +2096,7 @@ void SetInstanceMemory(Handle<JSObject> instance, JSArrayBuffer* buffer) {
int32_t wasm::GetInstanceMemorySize(Isolate* isolate,
Handle<JSObject> instance) {
+ DCHECK(IsWasmInstance(*instance));
MaybeHandle<JSArrayBuffer> maybe_mem_buffer =
GetInstanceMemory(isolate, instance);
Handle<JSArrayBuffer> buffer;
@@ -2108,59 +2115,110 @@ uint32_t GetMaxInstanceMemorySize(Isolate* isolate, Handle<JSObject> instance) {
return WasmJs::GetWasmMemoryMaximumSize(isolate, memory_object);
}
-int32_t wasm::GrowInstanceMemory(Isolate* isolate, Handle<JSObject> instance,
- uint32_t pages) {
- if (!IsWasmInstance(*instance)) return -1;
- if (pages == 0) return GetInstanceMemorySize(isolate, instance);
- uint32_t max_pages = GetMaxInstanceMemorySize(isolate, instance);
- if (WasmModule::kV8MaxPages < max_pages) return -1;
-
+Handle<JSArrayBuffer> GrowMemoryBuffer(Isolate* isolate,
+ MaybeHandle<JSArrayBuffer> buffer,
+ uint32_t pages, uint32_t max_pages) {
+ Handle<JSArrayBuffer> old_buffer;
Address old_mem_start = nullptr;
- uint32_t old_size = 0, new_size = 0;
+ 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());
+ DCHECK_NOT_NULL(old_mem_start);
+ 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) {
+ return Handle<JSArrayBuffer>::null();
+ }
+ Handle<JSArrayBuffer> new_buffer = NewArrayBuffer(isolate, new_size);
+ Address new_mem_start = static_cast<Address>(new_buffer->backing_store());
+ if (new_buffer.is_null() || new_mem_start == nullptr) {
+ return Handle<JSArrayBuffer>::null();
+ }
+ if (old_size != 0) memcpy(new_mem_start, old_mem_start, old_size);
+ return new_buffer;
+}
+int32_t UncheckedUpdateInstanceMemory(Isolate* isolate,
+ Handle<JSObject> instance,
+ Handle<JSArrayBuffer> buffer) {
MaybeHandle<JSArrayBuffer> maybe_mem_buffer =
GetInstanceMemory(isolate, instance);
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 (maybe_mem_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;
- }
-
- if (new_size <= old_size || max_pages * WasmModule::kPageSize < new_size) {
- return -1;
+ old_size = old_buffer->byte_length()->Number();
}
- Handle<JSArrayBuffer> buffer = NewArrayBuffer(isolate, new_size);
- if (buffer.is_null()) return -1;
+ uint32_t new_size = buffer->byte_length()->Number();
+ DCHECK(new_size <= std::numeric_limits<uint32_t>::max());
Address new_mem_start = static_cast<Address>(buffer->backing_store());
- if (old_size != 0) {
- memcpy(new_mem_start, old_mem_start, old_size);
- }
+ DCHECK_NOT_NULL(new_mem_start);
SetInstanceMemory(instance, *buffer);
Handle<FixedArray> code_table = GetCompiledModule(*instance)->code_table();
RelocateMemoryReferencesInCode(code_table, old_mem_start, new_mem_start,
old_size, new_size);
- Handle<Object> memory_object(instance->GetInternalField(kWasmMemObject),
- isolate);
- if (!memory_object->IsUndefined(isolate)) {
- WasmJs::SetWasmMemoryArrayBuffer(isolate, memory_object, buffer);
- }
-
DCHECK(old_size % WasmModule::kPageSize == 0);
return (old_size / WasmModule::kPageSize);
}
+int32_t wasm::GrowWebAssemblyMemory(Isolate* isolate,
Mircea Trofin 2016/11/09 18:57:10 please add comment explaining what the return is.
gdeepti 2016/11/16 05:34:10 This is as per Spec for GrowMemory - Return the pr
+ Handle<Object> memory_object,
+ uint32_t pages) {
+ DCHECK(WasmJs::IsWasmMemoryObject(isolate, memory_object));
+ uint32_t max_pages = WasmJs::GetWasmMemoryMaximumSize(isolate, memory_object);
+ if (WasmModule::kV8MaxPages < max_pages) return -1;
Mircea Trofin 2016/11/09 18:57:10 Should this be a DCHECK rather, and have the calle
gdeepti 2016/11/16 05:34:10 As per the spec, grow_memory should return -1 for
+ Handle<WasmInstanceWrapper> instance_wrapper =
+ Handle<WasmInstanceWrapper>::cast(
+ WasmJs::GetWasmMemoryInstanceWrapper(isolate, memory_object));
+ DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper));
+ DCHECK(instance_wrapper->has_instance());
+ Handle<JSObject> instance = instance_wrapper->instance_object();
+ DCHECK(IsWasmInstance(*instance));
+ if (pages == 0) return GetInstanceMemorySize(isolate, instance);
Mircea Trofin 2016/11/09 18:57:09 why support pages == 0? Should this be an early DC
gdeepti 2016/11/16 05:34:10 The Spec says - "Return the previous memory size i
+
+ // Grow memory object buffer and update instances associated with it.
+ MaybeHandle<JSArrayBuffer> old_buffer =
+ WasmJs::GetWasmMemoryArrayBuffer(isolate, memory_object);
+ Handle<JSArrayBuffer> new_buffer =
+ GrowMemoryBuffer(isolate, old_buffer, pages, max_pages);
+ if (new_buffer.is_null()) return -1;
+ DCHECK(!instance_wrapper->has_previous());
+ int32_t ret = UncheckedUpdateInstanceMemory(isolate, instance, new_buffer);
+ while (instance_wrapper->has_next()) {
Mircea Trofin 2016/11/09 18:57:09 nit: perhaps paranoia, but you could do this in a
gdeepti 2016/11/16 05:34:10 I would prefer to leave this as is, as this seems
+ instance_wrapper = instance_wrapper->next_wrapper();
+ DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper));
+ Handle<JSObject> instance = instance_wrapper->instance_object();
+ DCHECK(IsWasmInstance(*instance));
+ CHECK_EQ(ret, UncheckedUpdateInstanceMemory(isolate, instance, new_buffer));
+ }
+ WasmJs::SetWasmMemoryArrayBuffer(isolate, memory_object, new_buffer);
+ return ret;
+}
+
+int32_t wasm::GrowMemory(Isolate* isolate, Handle<JSObject> instance,
+ uint32_t pages) {
+ if (!IsWasmInstance(*instance)) return -1;
+ if (pages == 0) return GetInstanceMemorySize(isolate, instance);
+ Handle<Object> obj(instance->GetInternalField(kWasmMemObject), isolate);
+ if (obj->IsUndefined(isolate)) {
+ // No other instances to grow, grow just the one.
+ MaybeHandle<JSArrayBuffer> old_buffer =
+ GetInstanceMemory(isolate, instance);
+ Handle<JSArrayBuffer> buffer =
+ GrowMemoryBuffer(isolate, old_buffer, pages, WasmModule::kV8MaxPages);
+ if (buffer.is_null()) return -1;
+ return UncheckedUpdateInstanceMemory(isolate, instance, buffer);
+ } else {
+ return GrowWebAssemblyMemory(isolate, obj, pages);
+ }
+}
+
void testing::ValidateInstancesChain(Isolate* isolate,
Handle<JSObject> wasm_module,
int instance_count) {
@@ -2239,3 +2297,88 @@ void WasmCompiledModule::RecreateModuleWrapper(Isolate* isolate,
compiled_module->set_module_wrapper(module_wrapper);
DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module));
}
+
+static void MemoryInstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
Mircea Trofin 2016/11/09 18:57:10 I'd piggy-back on the existing finalizer registrat
gdeepti 2016/11/16 05:34:10 The MemoryInstanceFinalizer is optional, i.e, only
+ JSObject** p = reinterpret_cast<JSObject**>(data.GetParameter());
+ JSObject* instance = *p;
+ Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate());
+ Handle<WasmInstanceWrapper> instance_wrapper =
+ handle(WasmInstanceWrapper::cast(
+ instance->GetInternalField(kWasmMemInstanceWrapper)));
+
+ 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<Object> memory_object(instance->GetInternalField(kWasmMemObject),
+ isolate);
+
+ if (!has_prev && !has_next) {
+ if (!memory_object->IsUndefined(isolate)) {
+ WasmJs::ResetWasmMemoryInstance(isolate, memory_object);
+ }
+ GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
+ 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.
Mircea Trofin 2016/11/09 18:57:10 This comment makes me believe even moreso that we
gdeepti 2016/11/16 05:34:10 Agreed, Done.
+ const int kWasmMemObjectInstancesLink = 2;
Mircea Trofin 2016/11/09 18:57:10 please see my earlier comment about fragility. Ple
gdeepti 2016/11/16 05:34:10 Done.
+ JSObject::cast(*memory_object)
+ ->SetInternalField(kWasmMemObjectInstancesLink, *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();
+ GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
+ }
+}
+
+Handle<WasmInstanceWrapper> WasmInstanceWrapper::New(
+ Isolate* isolate, Handle<JSObject> 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<Object> global_handle = isolate->global_handles()->Create(*instance);
+ Handle<WeakCell> cell = isolate->factory()->NewWeakCell(instance);
+ set(kWrapperInstanceObject, *cell);
+ GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(),
+ &MemoryInstanceFinalizer,
bradnelson 2016/11/09 01:40:11 You should be able to piggy-back this on the exist
gdeepti 2016/11/16 05:34:10 Done.
+ v8::WeakCallbackType::kFinalizer);
+}

Powered by Google App Engine
This is Rietveld 408576698