Commit 7a8cd551 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[wasm] Check that sync and async errors match

This makes the existing error message tests also test the error
produced by asynchronous compilation and instantiation.
It also slightly tweaks the error message to contain the name of the
API function invoked instead of "WebAssembly Instantiation".

R=titzer@chromium.org

Cq-Include-Trybots: luci.chromium.try:linux-blink-rel
Bug: chromium:926311
Change-Id: If4ab963cee8267d43b289169d21b31637c471d6d
Reviewed-on: https://chromium-review.googlesource.com/c/1456085
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59442}
parent 155ccadd
...@@ -228,7 +228,7 @@ MaybeHandle<WasmInstanceObject> WasmEngine::SyncInstantiate( ...@@ -228,7 +228,7 @@ MaybeHandle<WasmInstanceObject> WasmEngine::SyncInstantiate(
void WasmEngine::AsyncInstantiate( void WasmEngine::AsyncInstantiate(
Isolate* isolate, std::unique_ptr<InstantiationResultResolver> resolver, Isolate* isolate, std::unique_ptr<InstantiationResultResolver> resolver,
Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports) { Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports) {
ErrorThrower thrower(isolate, "WebAssembly Instantiation"); ErrorThrower thrower(isolate, "WebAssembly.instantiate()");
// Instantiate a TryCatch so that caught exceptions won't progagate out. // Instantiate a TryCatch so that caught exceptions won't progagate out.
// They will still be set as pending exceptions on the isolate. // They will still be set as pending exceptions on the isolate.
// TODO(clemensh): Avoid TryCatch, use Execution::TryCall internally to invoke // TODO(clemensh): Avoid TryCatch, use Execution::TryCall internally to invoke
......
...@@ -723,7 +723,7 @@ MaybeLocal<Value> WebAssemblyInstantiateImpl(Isolate* isolate, ...@@ -723,7 +723,7 @@ MaybeLocal<Value> WebAssemblyInstantiateImpl(Isolate* isolate,
i::MaybeHandle<i::Object> instance_object; i::MaybeHandle<i::Object> instance_object;
{ {
ScheduledErrorThrower thrower(i_isolate, "WebAssembly Instantiation"); ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Instance()");
// TODO(ahaas): These checks on the module should not be necessary here They // TODO(ahaas): These checks on the module should not be necessary here They
// are just a workaround for https://crbug.com/837417. // are just a workaround for https://crbug.com/837417.
...@@ -862,7 +862,7 @@ void WebAssemblyInstantiate(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -862,7 +862,7 @@ void WebAssemblyInstantiate(const v8::FunctionCallbackInfo<v8::Value>& args) {
i_isolate->CountUsage( i_isolate->CountUsage(
v8::Isolate::UseCounterFeature::kWebAssemblyInstantiation); v8::Isolate::UseCounterFeature::kWebAssemblyInstantiation);
ScheduledErrorThrower thrower(i_isolate, "WebAssembly Instantiation"); ScheduledErrorThrower thrower(i_isolate, "WebAssembly.instantiate()");
HandleScope scope(isolate); HandleScope scope(isolate);
......
...@@ -6,128 +6,130 @@ ...@@ -6,128 +6,130 @@
load("test/mjsunit/wasm/wasm-module-builder.js"); load("test/mjsunit/wasm/wasm-module-builder.js");
function module(bytes) {
let buffer = bytes;
if (typeof buffer === 'string') {
buffer = new ArrayBuffer(bytes.length);
let view = new Uint8Array(buffer);
for (let i = 0; i < bytes.length; ++i) {
view[i] = bytes.charCodeAt(i);
}
}
return new WebAssembly.Module(buffer);
}
function instance(bytes, imports = {}) {
return new WebAssembly.Instance(module(bytes), imports);
}
// instantiate should succeed but run should fail.
function instantiateAndFailAtRuntime(bytes, imports = {}) {
var instance = new WebAssembly.Instance(module(bytes), imports);
instance.exports.run();
}
function builder() { function builder() {
return new WasmModuleBuilder; return new WasmModuleBuilder;
} }
function assertCompileError(bytes, msg) { function assertCompileError(bytes, msg) {
if (typeof msg === 'string') msg = 'WebAssembly.Module(): ' + msg; assertThrows(
assertThrows(() => module(bytes), WebAssembly.CompileError, msg); () => new WebAssembly.Module(bytes), WebAssembly.CompileError,
'WebAssembly.Module(): ' + msg);
assertThrowsAsync(
WebAssembly.compile(bytes), WebAssembly.CompileError,
'WebAssembly.compile(): ' + msg);
}
function assertInstantiateError(error, bytes, imports = {}, msg) {
assertThrows(
() => new WebAssembly.Instance(new WebAssembly.Module(bytes), imports),
error, 'WebAssembly.Instance(): ' + msg);
assertThrowsAsync(
WebAssembly.instantiate(bytes, imports), error,
'WebAssembly.instantiate(): ' + msg);
} }
// default imports to {} so we get LinkError by default, thus allowing us to // default imports to {} so we get LinkError by default, thus allowing us to
// distinguish the TypeError we want to catch // distinguish the TypeError we want to catch
function assertTypeError(bytes, imports = {}, msg) { function assertTypeError(bytes, imports = {}, msg) {
assertThrows(() => instance(bytes, imports), TypeError, msg); assertInstantiateError(TypeError, bytes, imports, msg);
} }
function assertLinkError(bytes, imports, msg) { function assertLinkError(bytes, imports, msg) {
assertThrows(() => instance(bytes, imports), WebAssembly.LinkError, msg); assertInstantiateError(WebAssembly.LinkError, bytes, imports, msg);
} }
function assertConversionError(bytes, imports, msg) { function assertConversionError(bytes, imports, msg) {
assertThrows( let instance =
() => instantiateAndFailAtRuntime(bytes, imports), TypeError, msg); new WebAssembly.Instance(new WebAssembly.Module(bytes), imports);
assertThrows(() => instance.exports.run(), TypeError, msg);
} }
(function TestDecodingError() { (function TestDecodingError() {
print(arguments.callee.name); print(arguments.callee.name);
assertCompileError("", /is empty/); assertCompileError(bytes(), 'BufferSource argument is empty');
assertCompileError("X", /expected 4 bytes, fell off end @\+0/); assertCompileError(bytes('X'), 'expected 4 bytes, fell off end @+0');
assertCompileError( assertCompileError(
"\0x00asm", /expected magic word 00 61 73 6d, found 00 78 30 30 @\+0/); bytes('\0x00asm'),
'expected magic word 00 61 73 6d, found 00 78 30 30 @+0');
})(); })();
(function TestValidationError() { (function TestValidationError() {
print(arguments.callee.name); print(arguments.callee.name);
let f_error = msg => 'Compiling wasm function "f" failed: ' + msg;
assertCompileError( assertCompileError(
builder().addFunction('f', kSig_i_v).end().toBuffer(), builder().addFunction('f', kSig_i_v).end().toBuffer(),
'Compiling wasm function "f" failed: ' + f_error('function body must end with "end" opcode @+24'));
'function body must end with "end" opcode @+24');
assertCompileError( assertCompileError(
builder().addFunction('f', kSig_i_v).addBody([kExprReturn]) builder().addFunction('f', kSig_i_v).addBody([kExprReturn])
.end().toBuffer(), .end().toBuffer(),
/expected 1 elements on the stack for return, found 0 @/); f_error('expected 1 elements on the stack for return, found 0 @+24'));
assertCompileError(builder().addFunction('f', kSig_v_v).addBody([ assertCompileError(builder().addFunction('f', kSig_v_v).addBody([
kExprGetLocal, 0 kExprGetLocal, 0
]).end().toBuffer(), /invalid local index: 0 @/); ]).end().toBuffer(), f_error('invalid local index: 0 @+24'));
assertCompileError( assertCompileError(
builder().addStart(0).toBuffer(), /function index 0 out of bounds/); builder().addStart(0).toBuffer(),
'start function index 0 out of bounds (0 entries) @+10');
})(); })();
function import_error(index, module, func, msg) {
let full_msg = 'Import #' + index + ' module=\"' + module + '\"';
if (func !== undefined) full_msg += ' function=\"' + func + '\"';
return full_msg + ' error: ' + msg;
}
(function TestTypeError() { (function TestTypeError() {
print(arguments.callee.name); print(arguments.callee.name);
let b; let b = builder();
b = builder(); b.addImport('foo', 'bar', kSig_v_v);
b.addImport("foo", "bar", kSig_v_v); let msg =
assertTypeError(b.toBuffer(), {}, /module is not an object or function/); import_error(0, 'foo', undefined, 'module is not an object or function');
assertTypeError(b.toBuffer(), {}, msg);
b = builder(); b = builder();
b.addImportedGlobal("foo", "bar", kWasmI32); b.addImportedGlobal('foo', 'bar', kWasmI32);
assertTypeError(b.toBuffer(), {}, /module is not an object or function/); assertTypeError(b.toBuffer(), {}, msg);
b = builder(); b = builder();
b.addImportedMemory("foo", "bar"); b.addImportedMemory('foo', 'bar');
assertTypeError(b.toBuffer(), {}, /module is not an object or function/); assertTypeError(b.toBuffer(), {}, msg);
})(); })();
(function TestLinkingError() { (function TestLinkingError() {
print(arguments.callee.name); print(arguments.callee.name);
let b; let b;
let msg;
b = builder(); b = builder();
b.addImport("foo", "bar", kSig_v_v); msg = import_error(0, 'foo', 'bar', 'function import requires a callable');
assertLinkError( b.addImport('foo', 'bar', kSig_v_v);
b.toBuffer(), {foo: {}}, /function import requires a callable/); assertLinkError(b.toBuffer(), {foo: {}}, msg);
b = builder(); b = builder();
b.addImport("foo", "bar", kSig_v_v); b.addImport('foo', 'bar', kSig_v_v);
assertLinkError( assertLinkError(b.toBuffer(), {foo: {bar: 9}}, msg);
b.toBuffer(), {foo: {bar: 9}}, /function import requires a callable/);
b = builder(); b = builder();
b.addImportedGlobal("foo", "bar", kWasmI32); msg = import_error(
assertLinkError(b.toBuffer(), {foo: {}}, /global import must be a number/); 0, 'foo', 'bar',
'global import must be a number or WebAssembly.Global object');
b.addImportedGlobal('foo', 'bar', kWasmI32);
assertLinkError(b.toBuffer(), {foo: {}}, msg);
b = builder(); b = builder();
b.addImportedGlobal("foo", "bar", kWasmI32); b.addImportedGlobal('foo', 'bar', kWasmI32);
assertLinkError( assertLinkError(b.toBuffer(), {foo: {bar: ''}}, msg);
b.toBuffer(), {foo: {bar: ""}}, /global import must be a number/);
b = builder(); b = builder();
b.addImportedGlobal("foo", "bar", kWasmI32); b.addImportedGlobal('foo', 'bar', kWasmI32);
assertLinkError( assertLinkError(b.toBuffer(), {foo: {bar: () => 9}}, msg);
b.toBuffer(), {foo: {bar: () => 9}}, /global import must be a number/);
b = builder(); b = builder();
b.addImportedMemory("foo", "bar"); msg = import_error(
assertLinkError( 0, 'foo', 'bar', 'memory import must be a WebAssembly.Memory object');
b.toBuffer(), {foo: {}}, b.addImportedMemory('foo', 'bar');
/memory import must be a WebAssembly\.Memory object/); assertLinkError(b.toBuffer(), {foo: {}}, msg);
b = builder(); b = builder();
b.addImportedMemory("foo", "bar", 1); b.addImportedMemory('foo', 'bar', 1);
assertLinkError( assertLinkError(
b.toBuffer(), {foo: {bar: () => new WebAssembly.Memory({initial: 0})}}, b.toBuffer(), {foo: {bar: () => new WebAssembly.Memory({initial: 0})}},
/memory import must be a WebAssembly\.Memory object/); msg);
})(); })();
(function TestTrapUnreachable() { (function TestTrapUnreachable() {
......
...@@ -30,12 +30,17 @@ function checkSuccessfulInstantiation(builder, ffi, handler) { ...@@ -30,12 +30,17 @@ function checkSuccessfulInstantiation(builder, ffi, handler) {
assertPromiseResult(builder.asyncInstantiate(ffi), handler); assertPromiseResult(builder.asyncInstantiate(ffi), handler);
} }
function checkFailingInstantiation(builder, ffi, error, message) { function checkFailingInstantiation(
builder, ffi, error, message, prepend_context = true) {
// Test synchronous instantiation. // Test synchronous instantiation.
assertThrows(_ => builder.instantiate(ffi), error, message); assertThrows(
_ => builder.instantiate(ffi), error,
(prepend_context ? 'WebAssembly.Instance(): ' : '') + message);
// Test asynchronous instantiation. // Test asynchronous instantiation.
assertThrowsAsync(builder.asyncInstantiate(ffi), error, message); assertThrowsAsync(
builder.asyncInstantiate(ffi), error,
(prepend_context ? 'WebAssembly.instantiate(): ' : '') + message);
} }
(function testValidFFI() { (function testValidFFI() {
...@@ -48,19 +53,19 @@ function checkFailingInstantiation(builder, ffi, error, message) { ...@@ -48,19 +53,19 @@ function checkFailingInstantiation(builder, ffi, error, message) {
print(arguments.callee.name); print(arguments.callee.name);
checkFailingInstantiation( checkFailingInstantiation(
CreateDefaultBuilder(), 17, TypeError, CreateDefaultBuilder(), 17, TypeError,
'WebAssembly Instantiation: Argument 1 must be an object'); 'Argument 1 must be an object');
checkFailingInstantiation( checkFailingInstantiation(
CreateDefaultBuilder(), {}, TypeError, CreateDefaultBuilder(), {}, TypeError,
'WebAssembly Instantiation: Import #0 module="mod" error: module is not an object or function'); 'Import #0 module="mod" error: module is not an object or function');
checkFailingInstantiation( checkFailingInstantiation(
CreateDefaultBuilder(), {mod: {}}, WebAssembly.LinkError, CreateDefaultBuilder(), {mod: {}}, WebAssembly.LinkError,
'WebAssembly Instantiation: Import #0 module="mod" function="fun" error: function import requires a callable'); 'Import #0 module="mod" function="fun" error: function import requires a callable');
checkFailingInstantiation( checkFailingInstantiation(
CreateDefaultBuilder(), {mod: {fun: {}}}, WebAssembly.LinkError, CreateDefaultBuilder(), {mod: {fun: {}}}, WebAssembly.LinkError,
'WebAssembly Instantiation: Import #0 module="mod" function="fun" error: function import requires a callable'); 'Import #0 module="mod" function="fun" error: function import requires a callable');
checkFailingInstantiation( checkFailingInstantiation(
CreateDefaultBuilder(), {mod: {fun: 0}}, WebAssembly.LinkError, CreateDefaultBuilder(), {mod: {fun: 0}}, WebAssembly.LinkError,
'WebAssembly Instantiation: Import #0 module="mod" function="fun" error: function import requires a callable'); 'Import #0 module="mod" function="fun" error: function import requires a callable');
})(); })();
(function testImportWithInvalidSignature() { (function testImportWithInvalidSignature() {
...@@ -79,7 +84,7 @@ function checkFailingInstantiation(builder, ffi, error, message) { ...@@ -79,7 +84,7 @@ function checkFailingInstantiation(builder, ffi, error, message) {
let exported = builder.instantiate().exports.exp; let exported = builder.instantiate().exports.exp;
checkFailingInstantiation( checkFailingInstantiation(
CreateDefaultBuilder(), {mod: {fun: exported}}, WebAssembly.LinkError, CreateDefaultBuilder(), {mod: {fun: exported}}, WebAssembly.LinkError,
'WebAssembly Instantiation: Import #0 module="mod" function="fun" error: imported function does not match the expected type'); 'Import #0 module="mod" function="fun" error: imported function does not match the expected type');
})(); })();
(function regression870646() { (function regression870646() {
...@@ -91,7 +96,8 @@ function checkFailingInstantiation(builder, ffi, error, message) { ...@@ -91,7 +96,8 @@ function checkFailingInstantiation(builder, ffi, error, message) {
} }
}); });
checkFailingInstantiation(CreateDefaultBuilder(), ffi, Error, 'my_exception'); checkFailingInstantiation(
CreateDefaultBuilder(), ffi, Error, 'my_exception', false);
})(); })();
// "fun" matches signature "i_dd" // "fun" matches signature "i_dd"
......
...@@ -59,7 +59,7 @@ checkExports('☺☺mul☺☺', '☺☺mul☺☺', '☺☺add☺☺', '☺☺add ...@@ -59,7 +59,7 @@ checkExports('☺☺mul☺☺', '☺☺mul☺☺', '☺☺add☺☺', '☺☺add
builder.addImport('three snowmen: ☃☃☃', 'foo', kSig_i_v); builder.addImport('three snowmen: ☃☃☃', 'foo', kSig_i_v);
assertThrows( assertThrows(
() => builder.instantiate({}), TypeError, () => builder.instantiate({}), TypeError,
/WebAssembly Instantiation: Import #0 module="three snowmen: ☃☃☃" error: /); /WebAssembly.Instance\(\): Import #0 module="three snowmen: ☃☃☃" error: /);
})(); })();
(function errorMessageUnicodeInImportElemName() { (function errorMessageUnicodeInImportElemName() {
...@@ -67,7 +67,7 @@ checkExports('☺☺mul☺☺', '☺☺mul☺☺', '☺☺add☺☺', '☺☺add ...@@ -67,7 +67,7 @@ checkExports('☺☺mul☺☺', '☺☺mul☺☺', '☺☺add☺☺', '☺☺add
builder.addImport('mod', 'three snowmen: ☃☃☃', kSig_i_v); builder.addImport('mod', 'three snowmen: ☃☃☃', kSig_i_v);
assertThrows( assertThrows(
() => builder.instantiate({mod: {}}), WebAssembly.LinkError, () => builder.instantiate({mod: {}}), WebAssembly.LinkError,
'WebAssembly Instantiation: Import #0 module="mod" function="three ' + 'WebAssembly.Instance\(\): Import #0 module="mod" function="three ' +
'snowmen: ☃☃☃" error: function import requires a callable'); 'snowmen: ☃☃☃" error: function import requires a callable');
})(); })();
...@@ -78,7 +78,7 @@ checkExports('☺☺mul☺☺', '☺☺mul☺☺', '☺☺add☺☺', '☺☺add ...@@ -78,7 +78,7 @@ checkExports('☺☺mul☺☺', '☺☺mul☺☺', '☺☺add☺☺', '☺☺add
builder.addImport(mod_name, func_name, kSig_i_v); builder.addImport(mod_name, func_name, kSig_i_v);
assertThrows( assertThrows(
() => builder.instantiate({[mod_name]: {}}), WebAssembly.LinkError, () => builder.instantiate({[mod_name]: {}}), WebAssembly.LinkError,
'WebAssembly Instantiation: Import #0 module="' + mod_name + 'WebAssembly.Instance(): Import #0 module="' + mod_name +
'" function="' + func_name + '" function="' + func_name +
'" error: function import requires a callable'); '" error: function import requires a callable');
})(); })();
...@@ -8,15 +8,30 @@ let byte_view = new Uint8Array(__buffer); ...@@ -8,15 +8,30 @@ let byte_view = new Uint8Array(__buffer);
let f32_view = new Float32Array(__buffer); let f32_view = new Float32Array(__buffer);
let f64_view = new Float64Array(__buffer); let f64_view = new Float64Array(__buffer);
function bytes() { // The bytes function receives one of
var buffer = new ArrayBuffer(arguments.length); // - several arguments, each of which is either a number or a string of length
var view = new Uint8Array(buffer); // 1; if it's a string, the charcode of the contained character is used.
for (var i = 0; i < arguments.length; i++) { // - a single array argument containing the actual arguments
var val = arguments[i]; // - a single string; the returned buffer will contain the char codes of all
if ((typeof val) == "string") val = val.charCodeAt(0); // contained characters.
function bytes(...input) {
if (input.length == 1 && typeof input[0] == 'array') input = input[0];
if (input.length == 1 && typeof input[0] == 'string') {
let len = input[0].length;
let view = new Uint8Array(len);
for (let i = 0; i < len; i++) view[i] = input[0].charCodeAt(i);
return view.buffer;
}
let view = new Uint8Array(input.length);
for (let i = 0; i < input.length; i++) {
let val = input[i];
if (typeof val == 'string') {
assertEquals(1, val.length, 'string inputs must have length 1');
val = val.charCodeAt(0);
}
view[i] = val | 0; view[i] = val | 0;
} }
return buffer; return view.buffer;
} }
// Header declaration constants // Header declaration constants
...@@ -34,23 +49,10 @@ var kHeaderSize = 8; ...@@ -34,23 +49,10 @@ var kHeaderSize = 8;
var kPageSize = 65536; var kPageSize = 65536;
var kSpecMaxPages = 65535; var kSpecMaxPages = 65535;
function bytesWithHeader() { function bytesWithHeader(...input) {
var buffer = new ArrayBuffer(kHeaderSize + arguments.length); const header =
var view = new Uint8Array(buffer); [kWasmH0, kWasmH1, kWasmH2, kWasmH3, kWasmV0, kWasmV1, kWasmV2, kWasmV3];
view[0] = kWasmH0; return bytes(header + input);
view[1] = kWasmH1;
view[2] = kWasmH2;
view[3] = kWasmH3;
view[4] = kWasmV0;
view[5] = kWasmV1;
view[6] = kWasmV2;
view[7] = kWasmV3;
for (var i = 0; i < arguments.length; i++) {
var val = arguments[i];
if ((typeof val) == "string") val = val.charCodeAt(0);
view[kHeaderSize + i] = val | 0;
}
return buffer;
} }
let kDeclNoLocals = 0; let kDeclNoLocals = 0;
......
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