[wasm] GrowMemory should use maximum size declared in WebAssembly.Memory
BUG= Review-Url: https://codereview.chromium.org/2410763002 Cr-Commit-Position: refs/heads/master@{#40411}
This commit is contained in:
parent
d1daae6221
commit
0c6354e03b
@ -27,11 +27,20 @@ using v8::internal::wasm::ErrorThrower;
|
||||
|
||||
namespace v8 {
|
||||
|
||||
static const int kWasmMemoryBufferFieldIndex = 0;
|
||||
static const int kWasmMemoryMaximumFieldIndex = 1;
|
||||
static const int kWasmTableArrayFieldIndex = 0;
|
||||
static const int kWasmTableMaximumFieldIndex = 1;
|
||||
|
||||
enum WasmMemoryObjectData {
|
||||
kWasmMemoryBuffer,
|
||||
kWasmMemoryMaximum,
|
||||
kWasmMemoryInstanceObject
|
||||
};
|
||||
|
||||
enum WasmInternalFieldCountData {
|
||||
kWasmTableInternalFieldCount = 2,
|
||||
kWasmMemoryInternalFieldCount
|
||||
};
|
||||
|
||||
namespace {
|
||||
i::Handle<i::String> v8_str(i::Isolate* isolate, const char* str) {
|
||||
return isolate->factory()->NewStringFromAsciiChecked(str);
|
||||
@ -636,11 +645,42 @@ void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
"Receiver is not a WebAssembly.Memory")) {
|
||||
return;
|
||||
}
|
||||
if (args.Length() < 1) {
|
||||
v8::Local<v8::Value> e = v8::Exception::TypeError(
|
||||
v8_str(isolate, "Argument 0 required, must be numeric value of pages"));
|
||||
isolate->ThrowException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(rossberg): grow memory.
|
||||
v8::Local<v8::Value> e =
|
||||
v8::Exception::TypeError(v8_str(isolate, "Memory#grow unimplemented"));
|
||||
isolate->ThrowException(e);
|
||||
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::JSObject> instance(
|
||||
i::Handle<i::JSObject>::cast(instance_object));
|
||||
|
||||
// TODO(gdeepti) Implement growing memory when shared by different
|
||||
// instances.
|
||||
int32_t ret = internal::wasm::GrowInstanceMemory(i_isolate, instance, 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);
|
||||
}
|
||||
|
||||
void WebAssemblyMemoryGetBuffer(
|
||||
@ -656,8 +696,8 @@ void WebAssemblyMemoryGetBuffer(
|
||||
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(kWasmMemoryBufferFieldIndex), i_isolate);
|
||||
i::Handle<i::Object> buffer(receiver->GetInternalField(kWasmMemoryBuffer),
|
||||
i_isolate);
|
||||
DCHECK(buffer->IsJSArrayBuffer());
|
||||
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
|
||||
return_value.Set(Utils::ToLocal(buffer));
|
||||
@ -671,9 +711,9 @@ i::Handle<i::JSObject> i::WasmJs::CreateWasmMemoryObject(
|
||||
i_isolate->native_context()->wasm_memory_constructor());
|
||||
i::Handle<i::JSObject> memory_obj =
|
||||
i_isolate->factory()->NewJSObject(memory_ctor);
|
||||
memory_obj->SetInternalField(kWasmMemoryBufferFieldIndex, *buffer);
|
||||
memory_obj->SetInternalField(kWasmMemoryBuffer, *buffer);
|
||||
memory_obj->SetInternalField(
|
||||
kWasmMemoryMaximumFieldIndex,
|
||||
kWasmMemoryMaximum,
|
||||
has_maximum
|
||||
? static_cast<i::Object*>(i::Smi::FromInt(maximum))
|
||||
: static_cast<i::Object*>(i_isolate->heap()->undefined_value()));
|
||||
@ -788,7 +828,8 @@ void WasmJs::InstallWasmConstructors(Isolate* isolate,
|
||||
Handle<JSObject> table_proto =
|
||||
factory->NewJSObject(table_constructor, TENURED);
|
||||
map = isolate->factory()->NewMap(
|
||||
i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize + 2 * i::kPointerSize);
|
||||
i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize +
|
||||
kWasmTableInternalFieldCount * i::kPointerSize);
|
||||
JSFunction::SetInitialMap(table_constructor, map, table_proto);
|
||||
JSObject::AddProperty(table_proto, isolate->factory()->constructor_string(),
|
||||
table_constructor, DONT_ENUM);
|
||||
@ -804,7 +845,8 @@ void WasmJs::InstallWasmConstructors(Isolate* isolate,
|
||||
Handle<JSObject> memory_proto =
|
||||
factory->NewJSObject(memory_constructor, TENURED);
|
||||
map = isolate->factory()->NewMap(
|
||||
i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize + 2 * i::kPointerSize);
|
||||
i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize +
|
||||
kWasmMemoryInternalFieldCount * i::kPointerSize);
|
||||
JSFunction::SetInitialMap(memory_constructor, map, memory_proto);
|
||||
JSObject::AddProperty(memory_proto, isolate->factory()->constructor_string(),
|
||||
memory_constructor, DONT_ENUM);
|
||||
@ -912,9 +954,30 @@ Handle<JSArrayBuffer> WasmJs::GetWasmMemoryArrayBuffer(Isolate* isolate,
|
||||
Handle<Object> value) {
|
||||
DCHECK(IsWasmMemoryObject(isolate, value));
|
||||
Handle<Object> buf(
|
||||
JSObject::cast(*value)->GetInternalField(kWasmMemoryBufferFieldIndex),
|
||||
isolate);
|
||||
JSObject::cast(*value)->GetInternalField(kWasmMemoryBuffer), isolate);
|
||||
return Handle<JSArrayBuffer>::cast(buf);
|
||||
}
|
||||
|
||||
uint32_t WasmJs::GetWasmMemoryMaximumSize(Isolate* isolate,
|
||||
Handle<Object> value) {
|
||||
DCHECK(IsWasmMemoryObject(isolate, value));
|
||||
Object* max_mem =
|
||||
JSObject::cast(*value)->GetInternalField(kWasmMemoryMaximum);
|
||||
if (max_mem->IsUndefined(isolate)) return wasm::WasmModule::kMaxMemPages;
|
||||
uint32_t max_pages = Smi::cast(max_mem)->value();
|
||||
return max_pages;
|
||||
}
|
||||
|
||||
void WasmJs::SetWasmMemoryInstance(Isolate* isolate,
|
||||
Handle<Object> memory_object,
|
||||
Handle<JSObject> instance) {
|
||||
if (!memory_object->IsUndefined(isolate)) {
|
||||
DCHECK(IsWasmMemoryObject(isolate, memory_object));
|
||||
// TODO(gdeepti): This should be a weak list of instance objects
|
||||
// for instances that share memory.
|
||||
JSObject::cast(*memory_object)
|
||||
->SetInternalField(kWasmMemoryInstanceObject, *instance);
|
||||
}
|
||||
}
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -32,6 +32,12 @@ class WasmJs {
|
||||
|
||||
static Handle<JSArrayBuffer> GetWasmMemoryArrayBuffer(Isolate* isolate,
|
||||
Handle<Object> value);
|
||||
|
||||
static uint32_t GetWasmMemoryMaximumSize(Isolate* isolate,
|
||||
Handle<Object> value);
|
||||
|
||||
static void SetWasmMemoryInstance(Isolate* isolate, Handle<Object> value,
|
||||
Handle<JSObject> instance);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
@ -1426,6 +1426,9 @@ class WasmInstanceBuilder {
|
||||
}
|
||||
|
||||
DCHECK(wasm::IsWasmObject(*instance));
|
||||
Handle<Object> memory_object(instance->GetInternalField(kWasmMemObject),
|
||||
isolate_);
|
||||
WasmJs::SetWasmMemoryInstance(isolate_, memory_object, instance);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Run the start function if one was specified.
|
||||
@ -2205,10 +2208,20 @@ int32_t wasm::GetInstanceMemorySize(Isolate* isolate,
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GetMaxInstanceMemorySize(Isolate* isolate, Handle<JSObject> instance) {
|
||||
uint32_t max_pages = WasmModule::kMaxMemPages;
|
||||
Handle<Object> memory_object(instance->GetInternalField(kWasmMemObject),
|
||||
isolate);
|
||||
if (memory_object->IsUndefined(isolate)) return max_pages;
|
||||
return WasmJs::GetWasmMemoryMaximumSize(isolate, memory_object);
|
||||
}
|
||||
|
||||
int32_t wasm::GrowInstanceMemory(Isolate* isolate, Handle<JSObject> instance,
|
||||
uint32_t pages) {
|
||||
if (!IsWasmObject(*instance)) return false;
|
||||
if (!IsWasmObject(*instance)) return -1;
|
||||
if (pages == 0) return GetInstanceMemorySize(isolate, instance);
|
||||
uint32_t max_pages = GetMaxInstanceMemorySize(isolate, instance);
|
||||
if (WasmModule::kMaxMemPages < max_pages) return -1;
|
||||
|
||||
Address old_mem_start = nullptr;
|
||||
uint32_t old_size = 0, new_size = 0;
|
||||
@ -2216,27 +2229,24 @@ int32_t wasm::GrowInstanceMemory(Isolate* isolate, Handle<JSObject> instance,
|
||||
MaybeHandle<JSArrayBuffer> maybe_mem_buffer =
|
||||
GetInstanceMemory(isolate, instance);
|
||||
Handle<JSArrayBuffer> old_buffer;
|
||||
if (!maybe_mem_buffer.ToHandle(&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.
|
||||
// TODO(gdeepti): Fix bounds check to take into account size of memtype.
|
||||
new_size = pages * WasmModule::kPageSize;
|
||||
// The code generated in the wasm compiler guarantees this precondition.
|
||||
DCHECK(pages <= WasmModule::kMaxMemPages);
|
||||
if (max_pages < pages) return -1;
|
||||
} else {
|
||||
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_NE(0, old_size);
|
||||
DCHECK(old_size + pages * WasmModule::kPageSize <=
|
||||
std::numeric_limits<uint32_t>::max());
|
||||
new_size = old_size + pages * WasmModule::kPageSize;
|
||||
}
|
||||
|
||||
if (new_size <= old_size ||
|
||||
WasmModule::kMaxMemPages * WasmModule::kPageSize <= new_size) {
|
||||
if (new_size <= old_size || max_pages * WasmModule::kPageSize < new_size) {
|
||||
return -1;
|
||||
}
|
||||
Handle<JSArrayBuffer> buffer = NewArrayBuffer(isolate, new_size);
|
||||
|
@ -82,7 +82,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
})();
|
||||
|
||||
(function ValidateBoundsCheck() {
|
||||
print("Validate bounds check");
|
||||
print("ValidateBoundsCheck");
|
||||
let memory = new WebAssembly.Memory({initial: 1, maximum: 5});
|
||||
assertEquals(kPageSize, memory.buffer.byteLength);
|
||||
let i32 = new Int32Array(memory.buffer);
|
||||
@ -101,7 +101,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
function load() { return instance.exports.load(offset); }
|
||||
function store(value) { return instance.exports.store(offset, value); }
|
||||
|
||||
for (offset = 0; offset < kPageSize -3; offset+=4) {
|
||||
for (offset = 0; offset < kPageSize - 3; offset+=4) {
|
||||
store(offset);
|
||||
}
|
||||
for (offset = 0; offset < kPageSize - 3; offset+=4) {
|
||||
@ -111,3 +111,73 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
assertTraps(kTrapMemOutOfBounds, load);
|
||||
}
|
||||
})();
|
||||
|
||||
(function TestGrowMemoryMaxDesc() {
|
||||
print("MaximumDescriptor");
|
||||
let memory = new WebAssembly.Memory({initial: 1, maximum: 5});
|
||||
assertEquals(kPageSize, memory.buffer.byteLength);
|
||||
let i32 = new Int32Array(memory.buffer);
|
||||
let builder = new WasmModuleBuilder();
|
||||
builder.addImportedMemory("mine", "", 0, 20);
|
||||
builder.addFunction("load", kSig_i_i)
|
||||
.addBody([kExprGetLocal, 0, kExprI32LoadMem, 0, 0])
|
||||
.exportFunc();
|
||||
builder.addFunction("store", kSig_i_ii)
|
||||
.addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32StoreMem, 0, 0,
|
||||
kExprGetLocal, 1])
|
||||
.exportFunc();
|
||||
var offset;
|
||||
let instance = builder.instantiate({mine: memory});
|
||||
function load() { return instance.exports.load(offset); }
|
||||
function store(value) { return instance.exports.store(offset, value); }
|
||||
|
||||
for (var i = 1; i < 5; i++) {
|
||||
for (offset = (i - 1) * kPageSize; offset < i * kPageSize - 3; offset+=4) {
|
||||
store(offset * 2);
|
||||
}
|
||||
assertEquals(i, memory.grow(1));
|
||||
assertEquals((i + 1) * kPageSize, memory.buffer.byteLength);
|
||||
}
|
||||
for (offset = 4 * kPageSize; offset < 5 * kPageSize - 3; offset+=4) {
|
||||
store(offset * 2);
|
||||
}
|
||||
for (offset = 0; offset < 5 * kPageSize - 3; offset+=4) {
|
||||
assertEquals(offset * 2, load());
|
||||
}
|
||||
for (offset = 5 * kPageSize; offset < 5 * kPageSize + 4; offset++) {
|
||||
assertThrows(load);
|
||||
}
|
||||
assertThrows(() => memory.grow(1));
|
||||
})();
|
||||
|
||||
(function TestGrowMemoryZeroInitialMemory() {
|
||||
print("ZeroInitialMemory");
|
||||
let memory = new WebAssembly.Memory({initial: 0});
|
||||
assertEquals(0, memory.buffer.byteLength);
|
||||
let i32 = new Int32Array(memory.buffer);
|
||||
let builder = new WasmModuleBuilder();
|
||||
builder.addImportedMemory("mine");
|
||||
builder.addFunction("load", kSig_i_i)
|
||||
.addBody([kExprGetLocal, 0, kExprI32LoadMem, 0, 0])
|
||||
.exportFunc();
|
||||
builder.addFunction("store", kSig_i_ii)
|
||||
.addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32StoreMem, 0, 0,
|
||||
kExprGetLocal, 1])
|
||||
.exportFunc();
|
||||
var offset;
|
||||
let instance = builder.instantiate({mine: memory});
|
||||
function load() { return instance.exports.load(offset); }
|
||||
function store(value) { return instance.exports.store(offset, value); }
|
||||
|
||||
for (var i = 1; i < 5; i++) {
|
||||
assertEquals(i - 1, memory.grow(1));
|
||||
assertEquals(i * kPageSize, memory.buffer.byteLength);
|
||||
for (offset = (i - 1) * kPageSize; offset < i * kPageSize - 3; offset++) {
|
||||
store(offset * 2);
|
||||
}
|
||||
}
|
||||
for (offset = 5 * kPageSize; offset < 5 * kPageSize + 4; offset++) {
|
||||
assertThrows(load);
|
||||
}
|
||||
assertThrows(() => memory.grow(16381));
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user