Commit e3ff4cf8 authored by titzer's avatar titzer Committed by Commit bot

[wasm] Implement importing of WebAssembly.Memory.

R=mtrofin@chromium.org,gdeepti@chromium.org
BUG=chromium:575167

Review-Url: https://codereview.chromium.org/2392943006
Cr-Commit-Position: refs/heads/master@{#40076}
parent e5b07adf
......@@ -310,9 +310,9 @@ class ModuleDecoder : public Decoder {
}
case kExternalMemory: {
// ===== Imported memory =========================================
// import->index =
// static_cast<uint32_t>(module->memories.size());
// TODO(titzer): imported memories
consume_resizable_limits(
"memory", "pages", WasmModule::kMaxLegalPages,
&module->min_mem_pages, &module->max_mem_pages);
break;
}
case kExternalGlobal: {
......@@ -774,7 +774,7 @@ class ModuleDecoder : public Decoder {
uint32_t offset = pc_offset();
const byte* string_start = pc_;
// Consume bytes before validation to guarantee that the string is not oob.
consume_bytes(*length, "string");
if (*length > 0) consume_bytes(*length, "string");
if (ok() && validate_utf8 &&
!unibrow::Utf8::Validate(string_start, *length)) {
error(string_start, "no valid UTF-8 string");
......
......@@ -27,6 +27,8 @@ using v8::internal::wasm::ErrorThrower;
namespace v8 {
static const int kWasmMemoryBufferFieldIndex = 0;
namespace {
i::Handle<i::String> v8_str(i::Isolate* isolate, const char* str) {
return isolate->factory()->NewStringFromAsciiChecked(str);
......@@ -509,7 +511,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(0), i_isolate);
i::Handle<i::Object> buffer(
receiver->GetInternalField(kWasmMemoryBufferFieldIndex), i_isolate);
DCHECK(buffer->IsJSArrayBuffer());
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(Utils::ToLocal(buffer));
......@@ -523,7 +526,7 @@ 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(0, *buffer);
memory_obj->SetInternalField(kWasmMemoryBufferFieldIndex, *buffer);
memory_obj->SetInternalField(
1, has_maximum
? static_cast<i::Object*>(i::Smi::FromInt(maximum))
......@@ -737,5 +740,24 @@ void WasmJs::InstallWasmMapsIfNeeded(Isolate* isolate,
}
}
bool WasmJs::IsWasmMemoryObject(Isolate* isolate, Handle<Object> value) {
if (value->IsJSObject()) {
i::Handle<i::JSObject> object = i::Handle<i::JSObject>::cast(value);
i::Handle<i::Symbol> sym(isolate->context()->wasm_memory_sym(), isolate);
Maybe<bool> has_brand = i::JSObject::HasOwnProperty(object, sym);
if (has_brand.IsNothing()) return false;
if (has_brand.ToChecked()) return true;
}
return false;
}
Handle<JSArrayBuffer> WasmJs::GetWasmMemoryArrayBuffer(Isolate* isolate,
Handle<Object> value) {
DCHECK(IsWasmMemoryObject(isolate, value));
Handle<Object> buf(
JSObject::cast(*value)->GetInternalField(kWasmMemoryBufferFieldIndex),
isolate);
return Handle<JSArrayBuffer>::cast(buf);
}
} // namespace internal
} // namespace v8
......@@ -27,6 +27,11 @@ class WasmJs {
static Handle<JSObject> CreateWasmMemoryObject(Isolate* isolate,
Handle<JSArrayBuffer> buffer,
bool has_maximum, int maximum);
static bool IsWasmMemoryObject(Isolate* isolate, Handle<Object> value);
static Handle<JSArrayBuffer> GetWasmMemoryArrayBuffer(Isolate* isolate,
Handle<Object> value);
};
} // namespace internal
......
......@@ -53,6 +53,7 @@ enum WasmInstanceObjectFields {
kWasmCompiledModule = 0,
kWasmModuleFunctionTable,
kWasmModuleCodeTable,
kWasmMemObject,
kWasmMemArrayBuffer,
kWasmGlobalsArrayBuffer,
kWasmDebugInfo,
......@@ -1243,6 +1244,36 @@ class WasmInstanceBuilder {
JSObject::kHeaderSize + kWasmModuleInternalFieldCount * kPointerSize);
Handle<JSObject> instance = factory->NewJSObjectFromMap(map, TENURED);
instance->SetInternalField(kWasmModuleCodeTable, *code_table);
instance->SetInternalField(kWasmMemObject, *factory->undefined_value());
//--------------------------------------------------------------------------
// Set up the globals for the new instance.
//--------------------------------------------------------------------------
MaybeHandle<JSArrayBuffer> old_globals;
MaybeHandle<JSArrayBuffer> globals;
uint32_t globals_size = compiled_module_->globals_size();
if (globals_size > 0) {
Handle<JSArrayBuffer> global_buffer =
NewArrayBuffer(isolate_, globals_size);
globals = global_buffer;
if (globals.is_null()) {
thrower_->Error("Out of memory: wasm globals");
return nothing;
}
Address old_address =
owner.is_null() ? nullptr : GetGlobalStartAddressFromCodeTemplate(
*factory->undefined_value(),
JSObject::cast(*owner));
RelocateGlobals(instance, old_address,
static_cast<Address>(global_buffer->backing_store()));
instance->SetInternalField(kWasmGlobalsArrayBuffer, *global_buffer);
}
//--------------------------------------------------------------------------
// Process the imports for the module.
//--------------------------------------------------------------------------
int num_imported_functions = ProcessImports(globals, code_table, instance);
if (num_imported_functions < 0) return nothing;
//--------------------------------------------------------------------------
// Set up the memory for the new instance.
......@@ -1278,35 +1309,6 @@ class WasmInstanceBuilder {
compiled_module_->set_heap(memory_);
}
//--------------------------------------------------------------------------
// Set up the globals for the new instance.
//--------------------------------------------------------------------------
MaybeHandle<JSArrayBuffer> old_globals;
MaybeHandle<JSArrayBuffer> globals;
uint32_t globals_size = compiled_module_->globals_size();
if (globals_size > 0) {
Handle<JSArrayBuffer> global_buffer =
NewArrayBuffer(isolate_, globals_size);
globals = global_buffer;
if (globals.is_null()) {
thrower_->Error("Out of memory: wasm globals");
return nothing;
}
Address old_address =
owner.is_null() ? nullptr : GetGlobalStartAddressFromCodeTemplate(
*factory->undefined_value(),
JSObject::cast(*owner));
RelocateGlobals(instance, old_address,
static_cast<Address>(global_buffer->backing_store()));
instance->SetInternalField(kWasmGlobalsArrayBuffer, *global_buffer);
}
//--------------------------------------------------------------------------
// Process the imports for the module.
//--------------------------------------------------------------------------
int num_imported_functions = ProcessImports(globals, code_table);
if (num_imported_functions < 0) return nothing;
//--------------------------------------------------------------------------
// Process the initialization for the module's globals.
//--------------------------------------------------------------------------
......@@ -1626,7 +1628,7 @@ class WasmInstanceBuilder {
// order, loading them from the {ffi_} object. Returns the number of imported
// functions.
int ProcessImports(MaybeHandle<JSArrayBuffer> globals,
Handle<FixedArray> code_table) {
Handle<FixedArray> code_table, Handle<JSObject> instance) {
int num_imported_functions = 0;
if (!compiled_module_->has_imports()) return num_imported_functions;
......@@ -1668,9 +1670,17 @@ class WasmInstanceBuilder {
case kExternalTable:
// TODO(titzer): Table imports must be a WebAssembly.Table.
break;
case kExternalMemory:
// TODO(titzer): Memory imports must be a WebAssembly.Memory.
case kExternalMemory: {
Handle<Object> object = result.ToHandleChecked();
if (!WasmJs::IsWasmMemoryObject(isolate_, object)) {
ReportFFIError("memory import must be a WebAssembly.Memory object",
index, module_name, function_name);
return -1;
}
instance->SetInternalField(kWasmMemObject, *object);
memory_ = WasmJs::GetWasmMemoryArrayBuffer(isolate_, object);
break;
}
case kExternalGlobal: {
// Global imports are converted to numbers and written into the
// {globals} array buffer.
......@@ -1787,13 +1797,21 @@ class WasmInstanceBuilder {
// TODO(titzer): should it have the same identity as an import?
break;
case kExternalMemory: {
// TODO(titzer): should memory have the same identity as an
// import?
Handle<JSArrayBuffer> buffer =
Handle<JSArrayBuffer>(JSArrayBuffer::cast(
instance->GetInternalField(kWasmMemArrayBuffer)));
desc.set_value(
WasmJs::CreateWasmMemoryObject(isolate_, buffer, false, 0));
// Export the memory as a WebAssembly.Memory object.
Handle<Object> memory_object(
instance->GetInternalField(kWasmMemObject), isolate_);
if (memory_object->IsUndefined(isolate_)) {
// If there was no imported WebAssembly.Memory object, create one.
Handle<JSArrayBuffer> buffer(
JSArrayBuffer::cast(
instance->GetInternalField(kWasmMemArrayBuffer)),
isolate_);
memory_object =
WasmJs::CreateWasmMemoryObject(isolate_, buffer, false, 0);
instance->SetInternalField(kWasmMemObject, *memory_object);
}
desc.set_value(memory_object);
break;
}
case kExternalGlobal: {
......
// 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
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
(function TestOne() {
print("TestOne");
let memory = new WebAssembly.Memory({initial: 1});
assertEquals(kPageSize, memory.buffer.byteLength);
let i32 = new Int32Array(memory.buffer);
let builder = new WasmModuleBuilder();
builder.addImportedMemory("mine");
builder.addFunction("main", kSig_i_v)
.addBody([
kExprI32Const, 0,
kExprI32LoadMem, 0, 0])
.exportAs("main");
let main = builder.instantiate({mine: memory}).exports.main;
assertEquals(0, main());
i32[0] = 993377;
assertEquals(993377, main());
})();
(function TestIdentity() {
print("TestIdentity");
let memory = new WebAssembly.Memory({initial: 1});
let i32 = new Int32Array(memory.buffer);
let builder = new WasmModuleBuilder();
builder.addImportedMemory("garg");
builder.exportMemoryAs("daggle");
let instance = builder.instantiate({garg: memory});
assertSame(memory, instance.exports.daggle);
})();
(function TestImportExport() {
print("TestImportExport");
var i1;
{
let builder = new WasmModuleBuilder();
builder.addMemory(1, 1, false);
builder.exportMemoryAs("exported_mem");
builder.addFunction("foo", kSig_i_i)
.addBody([
kExprGetLocal, 0,
kExprI32LoadMem, 0, 0])
.exportAs("foo");
i1 = builder.instantiate();
}
var i2;
{
let builder = new WasmModuleBuilder();
builder.addMemory(1, 1, false);
builder.addImportedMemory("imported_mem");
builder.addFunction("bar", kSig_i_i)
.addBody([
kExprGetLocal, 0,
kExprI32LoadMem, 0, 0])
.exportAs("bar");
i2 = builder.instantiate({imported_mem: i1.exports.exported_mem});
}
let i32 = new Int32Array(i1.exports.exported_mem.buffer);
for (var i = 0; i < 1e11; i = i * 3 + 5) {
for (var j = 0; j < 10; j++) {
var val = i + 99077 + j;
i32[j] = val;
assertEquals(val | 0, i1.exports.foo(j * 4));
assertEquals(val | 0, i2.exports.bar(j * 4));
}
}
})();
......@@ -194,11 +194,20 @@ class WasmModuleBuilder {
return this.num_imported_globals++;
}
addImportedMemory(module, name, initial = 0, maximum) {
let o = {module: module, name: name, kind: kExternalMemory, initial: initial, maximum: maximum};
this.imports.push(o);
}
addDataSegment(addr, data, init) {
this.segments.push({addr: addr, data: data, init: init});
return this.segments.length - 1;
}
exportMemoryAs(name) {
this.exports.push({name: name, kind: kExternalMemory, index: 0});
}
appendToTable(array) {
this.table.push(...array);
return this;
......@@ -244,6 +253,11 @@ class WasmModuleBuilder {
} else if (imp.kind == kExternalGlobal) {
section.emit_u32v(imp.type);
section.emit_u8(imp.mutable);
} else if (imp.kind == kExternalMemory) {
var has_max = (typeof imp.maximum) != "undefined";
section.emit_u8(has_max ? 1 : 0); // flags
section.emit_u32v(imp.initial); // initial
if (has_max) section.emit_u32v(imp.maximum); // maximum
} else {
throw new Error("unknown/unsupported import kind " + imp.kind);
}
......
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