Commit 6934db7c authored by gdeepti's avatar gdeepti Committed by Commit bot

[wasm] WebAssembly.Memory.grow() should handle the no instance case

 - Currently WebAssembly.Memory.grow() assumes that it always has an instance associated with it,
 fix to grow and reflect new size when no instance is associated with memory object.
 - Correctness fixes for the js api, throw range errors instead of generic errors

BUG=chromium:680938

R=bradnelson@chromium.org, titzer@chromium.org

Review-Url: https://codereview.chromium.org/2638243002
Cr-Commit-Position: refs/heads/master@{#42432}
parent d6a103d2
...@@ -635,20 +635,36 @@ void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -635,20 +635,36 @@ void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
"Receiver is not a WebAssembly.Memory")) { "Receiver is not a WebAssembly.Memory")) {
return; return;
} }
if (args.Length() < 1) { int64_t delta_size = 0;
if (args.Length() < 1 || !args[0]->IntegerValue(context).To(&delta_size)) {
v8::Local<v8::Value> e = v8::Exception::TypeError( v8::Local<v8::Value> e = v8::Exception::TypeError(
v8_str(isolate, "Argument 0 required, must be numeric value of pages")); v8_str(isolate, "Argument 0 required, must be numeric value of pages"));
isolate->ThrowException(e); isolate->ThrowException(e);
return; return;
} }
i::Handle<i::WasmMemoryObject> receiver =
uint32_t delta = args[0]->Uint32Value(context).FromJust(); i::Handle<i::WasmMemoryObject>::cast(Utils::OpenHandle(*args.This()));
int64_t max_size64 = receiver->maximum_pages();
if (max_size64 < 0 ||
max_size64 > static_cast<int64_t>(i::wasm::kV8MaxWasmTableSize)) {
max_size64 = i::wasm::kV8MaxWasmMemoryPages;
}
i::Handle<i::JSArrayBuffer> old_buffer(receiver->buffer());
uint32_t old_size =
old_buffer->byte_length()->Number() / i::wasm::kSpecMaxWasmMemoryPages;
int64_t new_size64 = old_size + delta_size;
if (delta_size < 0 || max_size64 < new_size64 || new_size64 < old_size) {
v8::Local<v8::Value> e = v8::Exception::RangeError(v8_str(
isolate, new_size64 < old_size ? "trying to shrink memory"
: "maximum memory size exceeded"));
isolate->ThrowException(e);
return;
}
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::Handle<i::Object> receiver = int32_t ret = i::wasm::GrowWebAssemblyMemory(
i::Handle<i::Object>::cast(Utils::OpenHandle(*args.This())); i_isolate, receiver, static_cast<uint32_t>(delta_size));
int32_t ret = i::wasm::GrowWebAssemblyMemory(i_isolate, receiver, delta);
if (ret == -1) { if (ret == -1) {
v8::Local<v8::Value> e = v8::Exception::Error( v8::Local<v8::Value> e = v8::Exception::RangeError(
v8_str(isolate, "Unable to grow instance memory.")); v8_str(isolate, "Unable to grow instance memory."));
isolate->ThrowException(e); isolate->ThrowException(e);
return; return;
......
...@@ -2325,20 +2325,12 @@ void UncheckedUpdateInstanceMemory(Isolate* isolate, ...@@ -2325,20 +2325,12 @@ void UncheckedUpdateInstanceMemory(Isolate* isolate,
old_size, new_size); old_size, new_size);
} }
int32_t wasm::GrowWebAssemblyMemory(Isolate* isolate, Handle<Object> receiver, int32_t wasm::GrowWebAssemblyMemory(Isolate* isolate,
Handle<WasmMemoryObject> receiver,
uint32_t pages) { uint32_t pages) {
DCHECK(WasmJs::IsWasmMemoryObject(isolate, receiver)); DCHECK(WasmJs::IsWasmMemoryObject(isolate, receiver));
Handle<WasmMemoryObject> memory_object = Handle<WasmMemoryObject> memory_object =
handle(WasmMemoryObject::cast(*receiver)); handle(WasmMemoryObject::cast(*receiver));
Handle<WasmInstanceWrapper> instance_wrapper(memory_object->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 = GetMaxInstanceMemoryPages(isolate, instance);
// Grow memory object buffer and update instances associated with it.
MaybeHandle<JSArrayBuffer> memory_buffer = handle(memory_object->buffer()); MaybeHandle<JSArrayBuffer> memory_buffer = handle(memory_object->buffer());
Handle<JSArrayBuffer> old_buffer; Handle<JSArrayBuffer> old_buffer;
uint32_t old_size = 0; uint32_t old_size = 0;
...@@ -2348,19 +2340,46 @@ int32_t wasm::GrowWebAssemblyMemory(Isolate* isolate, Handle<Object> receiver, ...@@ -2348,19 +2340,46 @@ int32_t wasm::GrowWebAssemblyMemory(Isolate* isolate, Handle<Object> receiver,
old_size = old_buffer->byte_length()->Number(); old_size = old_buffer->byte_length()->Number();
old_mem_start = static_cast<Address>(old_buffer->backing_store()); old_mem_start = static_cast<Address>(old_buffer->backing_store());
} }
Handle<JSArrayBuffer> new_buffer = // Return current size if grow by 0
GrowMemoryBuffer(isolate, memory_buffer, pages, max_pages); if (pages == 0) {
if (new_buffer.is_null()) return -1; DCHECK(old_size % WasmModule::kPageSize == 0);
DCHECK(!instance_wrapper->has_previous()); return (old_size / WasmModule::kPageSize);
SetInstanceMemory(instance, *new_buffer); }
UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size); Handle<JSArrayBuffer> new_buffer;
while (instance_wrapper->has_next()) { if (!memory_object->has_instances_link()) {
instance_wrapper = instance_wrapper->next_wrapper(); // Memory object does not have an instance associated with it, just grow
uint32_t max_pages;
if (memory_object->has_maximum_pages()) {
max_pages = static_cast<uint32_t>(memory_object->maximum_pages());
if (kV8MaxWasmMemoryPages < max_pages) return -1;
} else {
max_pages = kV8MaxWasmMemoryPages;
}
new_buffer = GrowMemoryBuffer(isolate, memory_buffer, pages, max_pages);
if (new_buffer.is_null()) return -1;
} else {
Handle<WasmInstanceWrapper> instance_wrapper(
memory_object->instances_link());
DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper)); DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper));
DCHECK(instance_wrapper->has_instance());
Handle<WasmInstanceObject> instance = instance_wrapper->instance_object(); Handle<WasmInstanceObject> instance = instance_wrapper->instance_object();
DCHECK(IsWasmInstance(*instance)); DCHECK(IsWasmInstance(*instance));
uint32_t max_pages = GetMaxInstanceMemoryPages(isolate, instance);
// Grow memory object buffer and update instances associated with it.
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); SetInstanceMemory(instance, *new_buffer);
UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size); 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); memory_object->set_buffer(*new_buffer);
DCHECK(old_size % WasmModule::kPageSize == 0); DCHECK(old_size % WasmModule::kPageSize == 0);
......
...@@ -24,6 +24,7 @@ class WasmCompiledModule; ...@@ -24,6 +24,7 @@ class WasmCompiledModule;
class WasmDebugInfo; class WasmDebugInfo;
class WasmModuleObject; class WasmModuleObject;
class WasmInstanceObject; class WasmInstanceObject;
class WasmMemoryObject;
namespace compiler { namespace compiler {
class CallDescriptor; class CallDescriptor;
...@@ -432,7 +433,8 @@ int32_t GrowInstanceMemory(Isolate* isolate, ...@@ -432,7 +433,8 @@ int32_t GrowInstanceMemory(Isolate* isolate,
Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size, Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size,
bool enable_guard_regions); bool enable_guard_regions);
int32_t GrowWebAssemblyMemory(Isolate* isolate, Handle<Object> receiver, int32_t GrowWebAssemblyMemory(Isolate* isolate,
Handle<WasmMemoryObject> receiver,
uint32_t pages); uint32_t pages);
int32_t GrowMemory(Isolate* isolate, Handle<WasmInstanceObject> instance, int32_t GrowMemory(Isolate* isolate, Handle<WasmInstanceObject> instance,
......
// Copyright 2017 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.
var v17 = {};
var v32 = {};
v39 = new WebAssembly.Memory(v32);
v49 = v39.grow(v17);
...@@ -373,29 +373,32 @@ assertEq(memGrowDesc.configurable, true); ...@@ -373,29 +373,32 @@ assertEq(memGrowDesc.configurable, true);
// 'WebAssembly.Memory.prototype.grow' method // 'WebAssembly.Memory.prototype.grow' method
if (false) { // TODO: bugs with Memory.grow
let memGrow = memGrowDesc.value; let memGrow = memGrowDesc.value;
assertEq(memGrow.length, 1); assertEq(memGrow.length, 1);
assertErrorMessage(() => memGrow.call(), TypeError, /called on incompatible undefined/); assertErrorMessage(() => memGrow.call(), TypeError,
assertErrorMessage(() => memGrow.call({}), TypeError, /called on incompatible Object/); /called on incompatible undefined/);
assertErrorMessage(() => memGrow.call(mem1, -1), RangeError, /bad Memory grow delta/); assertErrorMessage(() => memGrow.call({}), TypeError,
assertErrorMessage(() => memGrow.call(mem1, Math.pow(2,32)), RangeError, /bad Memory grow delta/); /called on incompatible Object/);
assertErrorMessage(() => memGrow.call(mem1, -1), RangeError,
/bad Memory grow delta/);
assertErrorMessage(() => memGrow.call(mem1, Math.pow(2,32)), RangeError,
/bad Memory grow delta/);
var mem = new Memory({initial:1, maximum:2}); var mem = new Memory({initial:1, maximum:2});
var buf = mem.buffer; var buf = mem.buffer;
assertEq(buf.byteLength, kPageSize); assertEq(buf.byteLength, kPageSize);
assertEq(mem.grow(0), 1); assertEq(mem.grow(0), 1);
assertEq(buf !== mem.buffer, true); // TODO(gdeepti): Pending spec clarification
assertEq(buf.byteLength, 0); // assertEq(buf !== mem.buffer, true);
// assertEq(buf.byteLength, 0);
buf = mem.buffer; buf = mem.buffer;
assertEq(buf.byteLength, kPageSize); assertEq(buf.byteLength, kPageSize);
assertEq(mem.grow(1), 1); assertEq(mem.grow(1), 1);
assertEq(buf !== mem.buffer, true); // TODO(gdeepti): assertEq(buf !== mem.buffer, true);
assertEq(buf.byteLength, 0); // TODO(gdeepti): assertEq(buf.byteLength, 0);
buf = mem.buffer; buf = mem.buffer;
assertEq(buf.byteLength, 2 * kPageSize); assertEq(buf.byteLength, 2 * kPageSize);
assertErrorMessage(() => mem.grow(1), Error, /failed to grow memory/); assertErrorMessage(() => mem.grow(1), Error, /failed to grow memory/);
assertEq(buf, mem.buffer); assertEq(buf, mem.buffer);
}
// 'WebAssembly.Table' data property // 'WebAssembly.Table' data property
let tableDesc = Object.getOwnPropertyDescriptor(WebAssembly, 'Table'); let tableDesc = Object.getOwnPropertyDescriptor(WebAssembly, 'Table');
......
...@@ -91,3 +91,13 @@ function assertMemoryIsValid(memory) { ...@@ -91,3 +91,13 @@ function assertMemoryIsValid(memory) {
assertThrows(() => {'use strict'; memory.buffer = memory.buffer}, TypeError) assertThrows(() => {'use strict'; memory.buffer = memory.buffer}, TypeError)
assertThrows(() => ({__proto__: memory}).buffer, TypeError) assertThrows(() => ({__proto__: memory}).buffer, TypeError)
})(); })();
(function TestMemoryGrow() {
var kPageSize = 65536;
let memory = new WebAssembly.Memory({initial: 1, maximum:30});
assertEquals(1, memory.grow(9));
assertTrue(memory.buffer instanceof ArrayBuffer);
assertTrue(10*kPageSize == memory.buffer.byteLength);
assertMemoryIsValid(memory);
assertThrows(() => memory.grow(21));
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment