Diff
Modified: trunk/JSTests/ChangeLog (219968 => 219969)
--- trunk/JSTests/ChangeLog 2017-07-26 22:57:06 UTC (rev 219968)
+++ trunk/JSTests/ChangeLog 2017-07-26 23:29:25 UTC (rev 219969)
@@ -1,3 +1,17 @@
+2017-07-26 JF Bastien <[email protected]>
+
+ WebAssembly: test throwing out of the start function
+ https://bugs.webkit.org/show_bug.cgi?id=165714
+ <rdar://problem/29760251>
+
+ Reviewed by Keith Miller.
+
+ * wasm/assert.js:
+ * wasm/function-tests/trap-from-start.js: Added.
+ (StartTraps):
+ * wasm/function-tests/trap-from-start-async.js: Added.
+ (async.StartTrapsAsync):
+
2017-07-21 Yusuke Suzuki <[email protected]>
[FTL] Arguments elimination is suppressed by unreachable blocks
Modified: trunk/JSTests/wasm/assert.js (219968 => 219969)
--- trunk/JSTests/wasm/assert.js 2017-07-26 22:57:06 UTC (rev 219968)
+++ trunk/JSTests/wasm/assert.js 2017-07-26 23:29:25 UTC (rev 219969)
@@ -135,6 +135,28 @@
_fail(`Expected to throw a ${type.name} with message "${message}"`);
};
+export async function throwsAsync(promise, type, message) {
+ try {
+ await promise;
+ } catch (e) {
+ if (e instanceof type) {
+ if (e.message === message)
+ return e;
+ // Ignore source information at the end of the error message if the
+ // expected message didn't specify that information. Sometimes it
+ // changes, or it's tricky to get just right.
+ const evaluatingIndex = e.message.indexOf(" (evaluating '");
+ if (evaluatingIndex !== -1) {
+ const cleanMessage = e.message.substring(0, evaluatingIndex);
+ if (cleanMessage === message)
+ return e;
+ }
+ }
+ _fail(`Expected to throw a ${type.name} with message "${message}", got ${e.name} with message "${e.message}"`);
+ }
+ _fail(`Expected to throw a ${type.name} with message "${message}"`);
+}
+
const _instanceof = (obj, type, msg) => {
if (!(obj instanceof type))
_fail(`Expected a ${typeof(type)}, got ${typeof obj}`);
Added: trunk/JSTests/wasm/function-tests/trap-from-start-async.js (0 => 219969)
--- trunk/JSTests/wasm/function-tests/trap-from-start-async.js (rev 0)
+++ trunk/JSTests/wasm/function-tests/trap-from-start-async.js 2017-07-26 23:29:25 UTC (rev 219969)
@@ -0,0 +1,83 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+const memoryInfo = { initial: 2 };
+const tableInfo = { element: "anyfunc", initial: 8 };
+
+async function StartTrapsAsync() {
+ const builder = (new Builder())
+ .Type().End()
+ .Import()
+ .Memory("imp", "memory", memoryInfo)
+ .Table("imp", "table", tableInfo)
+ .Function("imp", "func", { params: ["i32"] })
+ .End()
+ .Function().End()
+ .Start("startMeUp").End()
+ .Element()
+ .Element({ offset: 4, functionIndices: [0, 1] })
+ .End()
+ .Code()
+ .Function("startMeUp", { params: [] })
+ .I32Const(0).I32Load(2, 0).I32Const(0xfeedface).I32Store(2, 0)
+ .I32Const(4).I32Load(2, 0).I32Const(0xc0fec0fe).I32Store(2, 0) // This will trap.
+ // This is unreachable:
+ .I32Const(42).Call(0) // Calls func(42).
+ .End()
+ .End()
+ .Data()
+ .Segment([0xef, 0xbe, 0xad, 0xde]).Offset(1024).End()
+ .End();
+
+ const memory = new WebAssembly.Memory(memoryInfo);
+ const buffer = new Uint32Array(memory.buffer);
+
+ const table = new WebAssembly.Table(tableInfo);
+
+ // The instance will use these as addresses for stores.
+ buffer[0] = 128;
+ buffer[1] = 0xc0defefe; // This is out of bounds.
+
+ // This function shouldn't get called because the trap occurs before the call.
+ let value = 0;
+ const func = v => value = v;
+
+ const module = new WebAssembly.Module(builder.WebAssembly().get());
+ const imp = { imp: { memory, table, func } };
+
+ const promise = WebAssembly.instantiate(module, imp);
+ await assert.throwsAsync(promise, WebAssembly.RuntimeError, `Out of bounds memory access`);
+
+ assert.eq(value, 0);
+
+ for (let i = 0; i < buffer.length; ++i) {
+ switch (i) {
+ case 0: assert.eq(buffer[i], 128); break; // Initial ArrayBuffer store.
+ case 1: assert.eq(buffer[i], 0xc0defefe); break; // Initial ArrayBuffer store.
+ case 32: assert.eq(buffer[i], 0xfeedface); break; // First store from start function.
+ case 256: assert.eq(buffer[i], 0xdeadbeef); break; // Data segment.
+ default: assert.eq(buffer[i], 0); break; // The rest.
+ }
+ }
+
+ for (let i = 0; i < table.length; ++i) {
+ switch (i) {
+ case 4: assert.isObject(table.get(i)); break;
+ case 5: assert.isObject(table.get(i)); break;
+ default: assert.eq(table.get(i), null); break;
+ }
+ }
+
+ // Call the imported `func`.
+ table.get(4)(0xf00f);
+ assert.eq(value, 0xf00f);
+ value = 0;
+
+ // Call the start function again on the instance. The instance is otherwise inaccessible!
+ buffer[32] = 0; // Reset the location which will be set by the first store.
+ assert.throws(() => table.get(5)(), WebAssembly.RuntimeError, `Out of bounds memory access`);
+ assert.eq(buffer[32], 0xfeedface); // The first store should still succeed.
+ assert.eq(value, 0);
+}
+
+assert.asyncTest(StartTrapsAsync());
Added: trunk/JSTests/wasm/function-tests/trap-from-start.js (0 => 219969)
--- trunk/JSTests/wasm/function-tests/trap-from-start.js (rev 0)
+++ trunk/JSTests/wasm/function-tests/trap-from-start.js 2017-07-26 23:29:25 UTC (rev 219969)
@@ -0,0 +1,80 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+const memoryInfo = { initial: 2 };
+const tableInfo = { element: "anyfunc", initial: 8 };
+
+(function StartTraps() {
+ const builder = (new Builder())
+ .Type().End()
+ .Import()
+ .Memory("imp", "memory", memoryInfo)
+ .Table("imp", "table", tableInfo)
+ .Function("imp", "func", { params: ["i32"] })
+ .End()
+ .Function().End()
+ .Start("startMeUp").End()
+ .Element()
+ .Element({ offset: 4, functionIndices: [0, 1] })
+ .End()
+ .Code()
+ .Function("startMeUp", { params: [] })
+ .I32Const(0).I32Load(2, 0).I32Const(0xfeedface).I32Store(2, 0)
+ .I32Const(4).I32Load(2, 0).I32Const(0xc0fec0fe).I32Store(2, 0) // This will trap.
+ // This is unreachable:
+ .I32Const(42).Call(0) // Calls func(42).
+ .End()
+ .End()
+ .Data()
+ .Segment([0xef, 0xbe, 0xad, 0xde]).Offset(1024).End()
+ .End();
+
+ const memory = new WebAssembly.Memory(memoryInfo);
+ const buffer = new Uint32Array(memory.buffer);
+
+ const table = new WebAssembly.Table(tableInfo);
+
+ // The instance will use these as addresses for stores.
+ buffer[0] = 128;
+ buffer[1] = 0xc0defefe; // This is out of bounds.
+
+ // This function shouldn't get called because the trap occurs before the call.
+ let value = 0;
+ const func = v => value = v;
+
+ const module = new WebAssembly.Module(builder.WebAssembly().get());
+ const imp = { imp: { memory, table, func } };
+
+ assert.throws(() => new WebAssembly.Instance(module, imp), WebAssembly.RuntimeError, `Out of bounds memory access`);
+
+ assert.eq(value, 0);
+
+ for (let i = 0; i < buffer.length; ++i) {
+ switch (i) {
+ case 0: assert.eq(buffer[i], 128); break; // Initial ArrayBuffer store.
+ case 1: assert.eq(buffer[i], 0xc0defefe); break; // Initial ArrayBuffer store.
+ case 32: assert.eq(buffer[i], 0xfeedface); break; // First store from start function.
+ case 256: assert.eq(buffer[i], 0xdeadbeef); break; // Data segment.
+ default: assert.eq(buffer[i], 0); break; // The rest.
+ }
+ }
+
+ for (let i = 0; i < table.length; ++i) {
+ switch (i) {
+ case 4: assert.isObject(table.get(i)); break;
+ case 5: assert.isObject(table.get(i)); break;
+ default: assert.eq(table.get(i), null); break;
+ }
+ }
+
+ // Call the imported `func`.
+ table.get(4)(0xf00f);
+ assert.eq(value, 0xf00f);
+ value = 0;
+
+ // Call the start function again on the instance. The instance is otherwise inaccessible!
+ buffer[32] = 0; // Reset the location which will be set by the first store.
+ assert.throws(() => table.get(5)(), WebAssembly.RuntimeError, `Out of bounds memory access`);
+ assert.eq(buffer[32], 0xfeedface); // The first store should still succeed.
+ assert.eq(value, 0);
+})();