Commit bd03c642 authored by mtrofin's avatar mtrofin Committed by Commit bot

[wasm] Compile and Instantiation

Implemented the WebAssembly.Module and WebAssembly.Instance
in terms of the WasmModule::CompileFunctions and
WasmModule::Instantiate APIs.

Added negative tests - for invalid module object.

BUG=

Review-Url: https://codereview.chromium.org/2121593002
Cr-Commit-Position: refs/heads/master@{#37775}
parent 78bf1bff
......@@ -48,7 +48,7 @@ i::MaybeHandle<i::FixedArray> CompileModule(
} else if (result.failed()) {
thrower->Failed("", result);
} else {
compiled_module = result.val->CompileFunctions(isolate);
compiled_module = result.val->CompileFunctions(isolate, thrower);
}
if (result.val) delete result.val;
......
......@@ -195,8 +195,9 @@ i::MaybeHandle<i::JSObject> InstantiateModuleCommon(
}
i::MaybeHandle<i::FixedArray> compiled_module =
result.val->CompileFunctions(isolate);
if (!compiled_module.is_null()) {
result.val->CompileFunctions(isolate, thrower);
if (!thrower->error()) {
DCHECK(!compiled_module.is_null());
object = i::wasm::WasmModule::Instantiate(
isolate, compiled_module.ToHandleChecked(), ffi, memory);
if (!object.is_null()) {
......@@ -296,22 +297,33 @@ void InstantiateModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
InstantiateModuleCommon(args, buffer.start, buffer.end, &thrower);
}
static i::MaybeHandle<i::JSObject> CreateModuleObject(
v8::Isolate* isolate, const v8::Local<v8::Value> source,
ErrorThrower* thrower) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::MaybeHandle<i::JSObject> nothing;
RawBuffer buffer = GetRawBufferSource(source, thrower);
if (buffer.start == nullptr) return i::MaybeHandle<i::JSObject>();
// TODO(rossberg): Once we can, do compilation here.
DCHECK(source->IsArrayBuffer() || source->IsTypedArray());
i::Zone zone(i_isolate->allocator());
i::wasm::ModuleResult result = i::wasm::DecodeWasmModule(
i_isolate, &zone, buffer.start, buffer.end, false, i::wasm::kWasmOrigin);
std::unique_ptr<const i::wasm::WasmModule> decoded_module(result.val);
if (result.failed()) {
thrower->Failed("", result);
return nothing;
}
i::MaybeHandle<i::FixedArray> compiled_module =
decoded_module->CompileFunctions(i_isolate, thrower);
if (compiled_module.is_null()) return nothing;
Local<Context> context = isolate->GetCurrentContext();
i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
i::Handle<i::JSFunction> module_cons(i_context->wasm_module_constructor());
i::Handle<i::JSObject> module_obj =
i_isolate->factory()->NewJSObject(module_cons);
module_obj->SetInternalField(0, *compiled_module.ToHandleChecked());
i::Handle<i::Object> module_ref = Utils::OpenHandle(*source);
i::Handle<i::Symbol> module_sym(i_context->wasm_module_sym());
i::Object::SetProperty(module_obj, module_sym, module_ref, i::STRICT).Check();
......@@ -331,13 +343,15 @@ void WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
i::MaybeHandle<i::JSObject> module_obj =
CreateModuleObject(isolate, args[0], &thrower);
if (module_obj.is_null()) return;
Local<Context> context = isolate->GetCurrentContext();
v8::Local<v8::Promise::Resolver> resolver;
if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) return;
if (thrower.error()) {
resolver->Reject(context, Utils::ToLocal(thrower.Reify()));
} else {
resolver->Resolve(context, Utils::ToLocal(module_obj.ToHandleChecked()));
}
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(resolver->GetPromise());
}
......@@ -363,25 +377,59 @@ void WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
HandleScope scope(args.GetIsolate());
v8::Isolate* isolate = args.GetIsolate();
ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate),
"WebAssembly.Instance()");
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
ErrorThrower thrower(i_isolate, "WebAssembly.Instance()");
if (args.Length() < 1) {
thrower.Error("Argument 0 must be a WebAssembly.Module");
thrower.Error(
"Argument 0 must be provided, and must be a WebAssembly.Module object");
return;
}
Local<Context> context = isolate->GetCurrentContext();
i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
i::Handle<i::Symbol> module_sym(i_context->wasm_module_sym());
i::MaybeHandle<i::Object> source =
i::Object::GetProperty(Utils::OpenHandle(*args[0]), module_sym);
if (source.is_null()) return;
if (source.is_null() || source.ToHandleChecked()->IsUndefined(i_isolate)) {
thrower.Error("Argument 0 must be a WebAssembly.Module");
return;
}
RawBuffer buffer =
GetRawBufferSource(Utils::ToLocal(source.ToHandleChecked()), &thrower);
if (buffer.start == nullptr) return;
Local<Object> obj = Local<Object>::Cast(args[0]);
InstantiateModuleCommon(args, buffer.start, buffer.end, &thrower);
i::Handle<i::JSObject> module_obj =
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj));
if (module_obj->GetInternalFieldCount() < 1 ||
!module_obj->GetInternalField(0)->IsFixedArray()) {
thrower.Error("Argument 0 is an invalid WebAssembly.Module");
return;
}
i::Handle<i::FixedArray> compiled_code = i::Handle<i::FixedArray>(
i::FixedArray::cast(module_obj->GetInternalField(0)));
i::Handle<i::JSReceiver> ffi = i::Handle<i::JSObject>::null();
if (args.Length() > 1 && args[1]->IsObject()) {
Local<Object> obj = Local<Object>::Cast(args[1]);
ffi = i::Handle<i::JSReceiver>::cast(v8::Utils::OpenHandle(*obj));
}
i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null();
if (args.Length() > 2 && args[2]->IsArrayBuffer()) {
Local<Object> obj = Local<Object>::Cast(args[2]);
i::Handle<i::Object> mem_obj = v8::Utils::OpenHandle(*obj);
memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj));
}
i::MaybeHandle<i::JSObject> instance =
i::wasm::WasmModule::Instantiate(i_isolate, compiled_code, ffi, memory);
if (instance.is_null()) {
thrower.Error("Could not instantiate module");
return;
}
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(Utils::ToLocal(instance.ToHandleChecked()));
}
} // namespace
......@@ -478,6 +526,11 @@ void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) {
InstallFunc(isolate, wasm_object, "Module", WebAssemblyModule);
Handle<JSFunction> instance_constructor =
InstallFunc(isolate, wasm_object, "Instance", WebAssemblyInstance);
i::Handle<i::Map> map = isolate->factory()->NewMap(
i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize + i::kPointerSize);
module_constructor->set_prototype_or_initial_map(*map);
map->SetConstructor(*module_constructor);
context->set_wasm_module_constructor(*module_constructor);
context->set_wasm_instance_constructor(*instance_constructor);
}
......
......@@ -1013,9 +1013,9 @@ bool SetupExportsObject(Handle<FixedArray> compiled_module, Isolate* isolate,
} // namespace
MaybeHandle<FixedArray> WasmModule::CompileFunctions(Isolate* isolate) const {
MaybeHandle<FixedArray> WasmModule::CompileFunctions(
Isolate* isolate, ErrorThrower* thrower) const {
Factory* factory = isolate->factory();
ErrorThrower thrower(isolate, "WasmModule::CompileFunctions()");
MaybeHandle<FixedArray> nothing;
......@@ -1052,14 +1052,14 @@ MaybeHandle<FixedArray> WasmModule::CompileFunctions(Isolate* isolate) const {
static_cast<int>(functions.size()));
if (FLAG_wasm_num_compilation_tasks != 0) {
CompileInParallel(isolate, this,
temp_instance_for_compilation.function_code, &thrower,
temp_instance_for_compilation.function_code, thrower,
&module_env);
} else {
CompileSequentially(isolate, this,
temp_instance_for_compilation.function_code, &thrower,
temp_instance_for_compilation.function_code, thrower,
&module_env);
}
if (thrower.error()) return nothing;
if (thrower->error()) return nothing;
// At this point, compilation has completed. Update the code table.
for (size_t i = FLAG_skip_compiling_wasm_funcs;
......@@ -1099,7 +1099,7 @@ MaybeHandle<FixedArray> WasmModule::CompileFunctions(Isolate* isolate) const {
temp_instance_for_compilation.function_code[exp.func_index];
Handle<Code> export_code = compiler::CompileJSToWasmWrapper(
isolate, &module_env, code, exp.func_index);
if (thrower.error()) return nothing;
if (thrower->error()) return nothing;
export_metadata->set(kExportCode, *export_code);
export_metadata->set(kExportName, *name);
export_metadata->set(
......@@ -1509,7 +1509,8 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
}
if (thrower.error()) return -1;
MaybeHandle<FixedArray> compiled_module = module->CompileFunctions(isolate);
MaybeHandle<FixedArray> compiled_module =
module->CompileFunctions(isolate, &thrower);
if (compiled_module.is_null()) return -1;
Handle<JSObject> instance =
......
......@@ -233,7 +233,8 @@ struct WasmModule {
Handle<JSReceiver> ffi,
Handle<JSArrayBuffer> memory);
MaybeHandle<FixedArray> CompileFunctions(Isolate* isolate) const;
MaybeHandle<FixedArray> CompileFunctions(Isolate* isolate,
ErrorThrower* thrower) const;
uint32_t FunctionTableSize() const {
if (indirect_table_size > 0) {
......
......@@ -58,3 +58,98 @@ CheckInstance(new WebAssembly.Instance(module));
let promise = WebAssembly.compile(buffer);
promise.then(module => CheckInstance(new WebAssembly.Instance(module)));
// Negative tests.
(function InvalidModules() {
let invalid_cases = [undefined, 1, "", "a", {some:1, obj: "b"}];
let len = invalid_cases.length;
for (var i = 0; i < len; ++i) {
try {
let instance = new WebAssembly.Instance(1);
assertUnreachable("should not be able to instantiate invalid modules.");
} catch (e) {
assertContains("Argument 0", e.toString());
}
}
})();
// Compile async an invalid blob.
(function InvalidBinaryAsyncCompilation() {
let builder = new WasmModuleBuilder();
builder.addFunction("f", kSig_i_i)
.addBody([kExprCallImport, kArity0, 0]);
let promise = WebAssembly.compile(builder.toBuffer());
promise
.then(compiled =>
assertUnreachable("should not be able to compile invalid blob."))
.catch(e => assertContains("invalid signature index", e.toString()));
})();
// Multiple instances tests.
(function ManyInstances() {
let compiled_module = new WebAssembly.Module(buffer);
let instance_1 = new WebAssembly.Instance(compiled_module);
let instance_2 = new WebAssembly.Instance(compiled_module);
assertTrue(instance_1 != instance_2);
})();
(function ManyInstancesAsync() {
let promise = WebAssembly.compile(buffer);
promise.then(compiled_module => {
let instance_1 = new WebAssembly.Instance(compiled_module);
let instance_2 = new WebAssembly.Instance(compiled_module);
assertTrue(instance_1 != instance_2);
});
})();
(function InstancesAreIsolatedFromEachother() {
var builder = new WasmModuleBuilder();
builder.addMemory(1,1, true);
var kSig_v_i = makeSig([kAstI32], []);
var signature = builder.addType(kSig_v_i);
builder.addImport("some_value", kSig_i);
builder.addImport("writer", signature);
builder.addFunction("main", kSig_i_i)
.addBody([
kExprI32Const, 1,
kExprGetLocal, 0,
kExprI32LoadMem, 0, 0,
kExprCallIndirect, kArity1, signature,
kExprGetLocal,0,
kExprI32LoadMem,0, 0,
kExprCallImport, kArity0, 0,
kExprI32Add
]).exportFunc();
// writer(mem[i]);
// return mem[i] + some_value();
builder.addFunction("_wrap_writer", signature)
.addBody([
kExprGetLocal, 0,
kExprCallImport, kArity1, 1]);
builder.appendToTable([0, 1]);
var module = new WebAssembly.Module(builder.toBuffer());
var mem_1 = new ArrayBuffer(4);
var mem_2 = new ArrayBuffer(4);
var view_1 = new Int32Array(mem_1);
var view_2 = new Int32Array(mem_2);
view_1[0] = 42;
view_2[0] = 1000;
var outval_1;
var outval_2;
var i1 = new WebAssembly.Instance(module, {some_value: () => 1,
writer: (x)=>outval_1 = x }, mem_1);
var i2 = new WebAssembly.Instance(module, {some_value: () => 2,
writer: (x)=>outval_2 = x }, mem_2);
assertEquals(43, i1.exports.main(0));
assertEquals(1002, i2.exports.main(0));
assertEquals(42, outval_1);
assertEquals(1000, outval_2);
})();
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