WebAssembly: improve compilation error messages
authorjfbastien@apple.com <jfbastien@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 15 Dec 2016 23:42:19 +0000 (23:42 +0000)
committerjfbastien@apple.com <jfbastien@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 15 Dec 2016 23:42:19 +0000 (23:42 +0000)
https://bugs.webkit.org/show_bug.cgi?id=163919

Reviewed by Saam Barati.

JSTests:

Update error messages in these tests.
Use the assert.throws facility in many of them which weren't already.

* wasm/js-api/element.js:
(assert.throws.new.WebAssembly.Module.builder.WebAssembly):
(assert.throws):
* wasm/js-api/global-error.js:
(assert.throws.new.WebAssembly.Module.bin):
(assert.throws):
(new.Number):
* wasm/js-api/table.js:
(assert.throws.new.WebAssembly.Module.builder.WebAssembly):
(assert.throws):
(assertBadTableImport):
* wasm/js-api/test_Data.js:
(DataSectionWithoutMemory):
* wasm/js-api/test_Start.js:
(InvalidStartFunctionIndex):
* wasm/js-api/test_basic_api.js:
(const.c.in.constructorProperties.switch):

Source/JavaScriptCore:

The error handling messages were underwhelming because most
locations merely returned `false` on failure. This patch uses
std::expected to denote that failure isn't expected. Doing this
makes it almost impossible to mess up the code: either a function
returns a result (or a partial result for internal helpers) or an
error. We're not synchronizing the error string with the m_failed
bool anymore, and the caller will abort if they try to get a
result but the outcome was an error.

This also shortens the code significantly using macros, while also
judiciously preventing inlining of error handling code and biasing
towards success using UNLIKELY. This means that the generated code
should be more efficient (no string formatting on success, and
regalloc can avoid these unlikely paths).

The patch adds a few missing checks as well, especially related to
count limits and memory allocation failure.

As a follow-up I'd like to improve WTF::makeString further, so it
does coercions to string and understands ADL as I did in this
patch.

* wasm/WasmB3IRGenerator.cpp:
(JSC::Wasm::B3IRGenerator::fail):
(JSC::Wasm::parseAndCompile):
* wasm/WasmB3IRGenerator.h:
* wasm/WasmFormat.h:
(JSC::Wasm::isValidExternalKind):
(JSC::Wasm::makeString):
* wasm/WasmFunctionParser.h:
* wasm/WasmModuleParser.cpp:
* wasm/WasmModuleParser.h:
* wasm/WasmParser.h:
(JSC::Wasm::FailureHelper::makeString):
(JSC::Wasm::Parser::fail):
(JSC::Wasm::Parser<SuccessType>::Parser):
(JSC::Wasm::Parser<SuccessType>::consumeCharacter):
(JSC::Wasm::Parser<SuccessType>::consumeString):
(JSC::Wasm::Parser<SuccessType>::consumeUTF8String):
(JSC::Wasm::Parser<SuccessType>::parseVarUInt32):
(JSC::Wasm::Parser<SuccessType>::parseVarUInt64):
(JSC::Wasm::Parser<SuccessType>::parseVarInt32):
(JSC::Wasm::Parser<SuccessType>::parseVarInt64):
(JSC::Wasm::Parser<SuccessType>::parseUInt32):
(JSC::Wasm::Parser<SuccessType>::parseUInt64):
(JSC::Wasm::Parser<SuccessType>::parseUInt8):
(JSC::Wasm::Parser<SuccessType>::parseInt7):
(JSC::Wasm::Parser<SuccessType>::parseUInt7):
(JSC::Wasm::Parser<SuccessType>::parseVarUInt1):
(JSC::Wasm::Parser<SuccessType>::parseResultType):
(JSC::Wasm::Parser<SuccessType>::parseValueType):
(JSC::Wasm::Parser<SuccessType>::parseExternalKind):
* wasm/WasmPlan.cpp:
(JSC::Wasm::Plan::run):
* wasm/WasmSections.h:
(JSC::Wasm::isValidSection):
(JSC::Wasm::validateOrder):
(JSC::Wasm::makeString):
* wasm/WasmValidate.cpp:
(JSC::Wasm::Validate::fail):
(JSC::Wasm::Validate::addUnreachable):
(JSC::Wasm::validateFunction):
* wasm/WasmValidate.h:
* wasm/generateWasmB3IRGeneratorInlinesHeader.py:
* wasm/generateWasmOpsHeader.py:
* wasm/generateWasmValidateInlinesHeader.py:
(loadMacro):
(storeMacro):
* wasm/js/WebAssemblyInstanceConstructor.cpp:
(JSC::constructJSWebAssemblyInstance):
* wasm/js/WebAssemblyModuleRecord.cpp:
(JSC::WebAssemblyModuleRecord::link):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@209880 268f45cc-cd09-0410-ab3c-d52691b4dbfc

24 files changed:
JSTests/ChangeLog
JSTests/wasm/js-api/element.js
JSTests/wasm/js-api/global-error.js
JSTests/wasm/js-api/table.js
JSTests/wasm/js-api/test_Data.js
JSTests/wasm/js-api/test_Start.js
JSTests/wasm/js-api/test_basic_api.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
Source/JavaScriptCore/wasm/WasmB3IRGenerator.h
Source/JavaScriptCore/wasm/WasmFormat.h
Source/JavaScriptCore/wasm/WasmFunctionParser.h
Source/JavaScriptCore/wasm/WasmModuleParser.cpp
Source/JavaScriptCore/wasm/WasmModuleParser.h
Source/JavaScriptCore/wasm/WasmParser.h
Source/JavaScriptCore/wasm/WasmPlan.cpp
Source/JavaScriptCore/wasm/WasmSections.h
Source/JavaScriptCore/wasm/WasmValidate.cpp
Source/JavaScriptCore/wasm/WasmValidate.h
Source/JavaScriptCore/wasm/generateWasmB3IRGeneratorInlinesHeader.py
Source/JavaScriptCore/wasm/generateWasmOpsHeader.py
Source/JavaScriptCore/wasm/generateWasmValidateInlinesHeader.py
Source/JavaScriptCore/wasm/js/WebAssemblyInstanceConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp

index bc85a45..b738a52 100644 (file)
@@ -1,5 +1,33 @@
 2016-12-15  JF Bastien  <jfbastien@apple.com>
 
+        WebAssembly: improve compilation error messages
+        https://bugs.webkit.org/show_bug.cgi?id=163919
+
+        Reviewed by Saam Barati.
+
+        Update error messages in these tests.
+        Use the assert.throws facility in many of them which weren't already.
+
+        * wasm/js-api/element.js:
+        (assert.throws.new.WebAssembly.Module.builder.WebAssembly):
+        (assert.throws):
+        * wasm/js-api/global-error.js:
+        (assert.throws.new.WebAssembly.Module.bin):
+        (assert.throws):
+        (new.Number):
+        * wasm/js-api/table.js:
+        (assert.throws.new.WebAssembly.Module.builder.WebAssembly):
+        (assert.throws):
+        (assertBadTableImport):
+        * wasm/js-api/test_Data.js:
+        (DataSectionWithoutMemory):
+        * wasm/js-api/test_Start.js:
+        (InvalidStartFunctionIndex):
+        * wasm/js-api/test_basic_api.js:
+        (const.c.in.constructorProperties.switch):
+
+2016-12-15  JF Bastien  <jfbastien@apple.com>
+
         WebAssembly API: improve data section errors
         https://bugs.webkit.org/show_bug.cgi?id=165733
 
index 4c81c3e..160ffd0 100644 (file)
@@ -1,21 +1,6 @@
 import Builder from '../Builder.js';
 import * as assert from '../assert.js';
 
-function assertBadBinary(builder, str) {
-    const bin = builder.WebAssembly().get();
-    let threw = false;
-    try {
-        new WebAssembly.Module(bin);
-    } catch(e) {
-        threw = true;
-        assert.truthy(e.toString().indexOf(str) !== -1);
-        assert.truthy(e instanceof WebAssembly.CompileError);
-    }
-    assert.truthy(threw);
-}
-
-const badElementSectionString = "couldn't parse section Element";
-
 {
     // Bad element section b/c no Table section/import.
     const builder = new Builder()
@@ -33,7 +18,7 @@ const badElementSectionString = "couldn't parse section Element";
             .End()
         .End();
 
-    assertBadBinary(builder, badElementSectionString);
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 22 / 41: Element section expects a Table to be present (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
 }
 
 {
@@ -56,7 +41,7 @@ const badElementSectionString = "couldn't parse section Element";
             .End()
         .End();
 
-    assertBadBinary(builder, badElementSectionString);
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 30 / 47: Element section can only have one Table for now (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
 }
 
 {
@@ -79,7 +64,7 @@ const badElementSectionString = "couldn't parse section Element";
             .End()
         .End();
 
-    assertBadBinary(builder, badElementSectionString);
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 35 / 49: Element section's 0th element writes to index 20 which exceeds the maximum 20 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
 }
 
 {
@@ -102,7 +87,7 @@ const badElementSectionString = "couldn't parse section Element";
             .End()
         .End();
 
-    assertBadBinary(builder, badElementSectionString);
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 35 / 48: Element section's 0th element writes to index 20 which exceeds the maximum 20 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
 }
 
 {
@@ -125,7 +110,7 @@ const badElementSectionString = "couldn't parse section Element";
             .End()
         .End();
 
-    assertBadBinary(builder, badElementSectionString);
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 38 / 50: Element section's 0th element's 2th index is 1 which exceeds the function index space size of 1 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
 }
 
 {
index 6fa35ec..2a7d8c0 100644 (file)
@@ -23,14 +23,7 @@ import Builder from '../Builder.js';
     const bin = builder.WebAssembly();
     bin.trim();
 
-    let passed = false;
-    try {
-        const module = new WebAssembly.Module(bin.get());
-    } catch (e) {
-        if (e.message === "couldn't parse section Global: Global declarations (evaluating 'new WebAssembly.Module(bin.get())')")
-            passed = true;
-    }
-    assert.truthy(passed);
+    assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 26 / 59: get_global's index 0 exceeds the number of imports 0 (evaluating 'new WebAssembly.Module(bin.get())')");
 }
 
 
@@ -59,14 +52,7 @@ import Builder from '../Builder.js';
     const bin = builder.WebAssembly();
     bin.trim();
 
-    let passed = false;
-    try {
-        const module = new WebAssembly.Module(bin.get());
-    } catch (e) {
-        if (e.message === "couldn't parse section Import: Import declarations (evaluating 'new WebAssembly.Module(bin.get())')")
-            passed = true;
-    }
-    assert.truthy(passed);
+    assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 32 / 76: Mutable Globals aren't supported (evaluating 'new WebAssembly.Module(bin.get())')");
 }
 
 {
@@ -91,14 +77,7 @@ import Builder from '../Builder.js';
     const bin = builder.WebAssembly();
     bin.trim();
 
-    let passed = false;
-    try {
-        const module = new WebAssembly.Module(bin.get());
-    } catch (e) {
-        if (e.message === "couldn't parse section Export: Exports (evaluating 'new WebAssembly.Module(bin.get())')")
-            passed = true;
-    }
-    assert.truthy(passed);
+    assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 51 / 59: 1th Export isn't immutable, named 'global' (evaluating 'new WebAssembly.Module(bin.get())')");
 }
 
 {
@@ -124,14 +103,7 @@ import Builder from '../Builder.js';
     const bin = builder.WebAssembly();
     bin.trim();
 
-    let passed = false;
-    try {
-        const module = new WebAssembly.Module(bin.get());
-    } catch (e) {
-        if (e.message === "Attempt to store to immutable global. (evaluating 'new WebAssembly.Module(bin.get())')")
-            passed = true;
-    }
-    assert.truthy(passed);
+    assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't validate: set_global 0 is immutable (evaluating 'new WebAssembly.Module(bin.get())')");
 }
 
 
@@ -158,14 +130,7 @@ import Builder from '../Builder.js';
     const bin = builder.WebAssembly();
     bin.trim();
 
-    let passed = false;
-    try {
-        const module = new WebAssembly.Module(bin.get());
-    } catch (e) {
-        if (e.message === "Attempt to use unknown global. (evaluating 'new WebAssembly.Module(bin.get())')")
-            passed = true;
-    }
-    assert.truthy(passed);
+    assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't validate: set_global 1 of unknown global, limit is 1 (evaluating 'new WebAssembly.Module(bin.get())')");
 }
 
 
@@ -191,14 +156,7 @@ import Builder from '../Builder.js';
     const bin = builder.WebAssembly();
     bin.trim();
 
-    let passed = false;
-    try {
-        const module = new WebAssembly.Module(bin.get());
-    } catch (e) {
-        if (e.message === "Attempt to set global with type: F32 with a variable of type: I32 (evaluating 'new WebAssembly.Module(bin.get())')")
-            passed = true;
-    }
-    assert.truthy(passed);
+    assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't validate: set_global 0 with type F32 with a variable of type I32 (evaluating 'new WebAssembly.Module(bin.get())')");
 }
 
 for ( let imp of [undefined, null, {}, () => {}, "number", new Number(4)]) {
@@ -226,13 +184,6 @@ for ( let imp of [undefined, null, {}, () => {}, "number", new Number(4)]) {
     const bin = builder.WebAssembly();
     bin.trim();
 
-    let passed = false;
     const module = new WebAssembly.Module(bin.get());
-    try {
-        new WebAssembly.Instance(module, { imp: { global: imp } });
-    } catch (e) {
-        if (e.message === "imported global must be a number (evaluating 'new WebAssembly.Instance(module, { imp: { global: imp } })')")
-            passed = true;
-    }
-    assert.truthy(passed);
+    assert.throws(() => new WebAssembly.Instance(module, { imp: { global: imp } }), TypeError, "imported global must be a number (evaluating 'new WebAssembly.Instance(module, { imp: { global: imp } })')");
 }
index 37587d0..db73421 100644 (file)
@@ -1,15 +1,6 @@
 import Builder from '../Builder.js';
 import * as assert from '../assert.js';
 
-const badTableString = "couldn't parse section Table: Indirect function table and other tables (evaluating 'new WebAssembly.Module(bin)')";
-const badImportString = "couldn't parse section Import: Import declarations (evaluating 'new WebAssembly.Module(bin)')";
-const badExportString = "couldn't parse section Export: Exports (evaluating 'new WebAssembly.Module(bin)')";
-
-function assertBadBinary(builder, str) {
-    const bin = builder.WebAssembly().get();
-    assert.throws(() => new WebAssembly.Module(bin), WebAssembly.CompileError, str);
-}
-
 {
     const builder = new Builder()
         .Type().End()
@@ -22,7 +13,7 @@ function assertBadBinary(builder, str) {
         .End()
         .Code()
         .End();
-    assertBadBinary(builder, badTableString);
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 34 / 41: Table section cannot exist if an Import has a table (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
 }
 
 {
@@ -35,7 +26,7 @@ function assertBadBinary(builder, str) {
         .End()
         .Code()
         .End();
-    assertBadBinary(builder, badTableString);
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 17 / 28: Table count of 2 is invalid, only 1 is allowed for now (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
 }
 
 {
@@ -51,7 +42,7 @@ function assertBadBinary(builder, str) {
                 .CallIndirect(0, 0)
             .End()
         .End();
-    assertBadBinary(builder, "call_indirect is only valid when a table is defined or imported (evaluating 'new WebAssembly.Module(bin)')");
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 4 / 7: call_indirect is only valid when a table is defined or imported (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
 }
 
 {
@@ -70,7 +61,7 @@ function assertBadBinary(builder, str) {
                 .CallIndirect(0, 1)
             .End()
         .End();
-    assertBadBinary(builder, "call_indirect 'reserved' varuint1 must be 0x0 (evaluating 'new WebAssembly.Module(bin)')");
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 6 / 7: call_indirect's 'reserved' varuint1 must be 0x0 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
 }
 
 {
@@ -83,7 +74,7 @@ function assertBadBinary(builder, str) {
         .End()
         .Code()
         .End();
-    assertBadBinary(builder, badExportString);
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 23 / 26: can't export a non-existent Table (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
 }
 
 {
@@ -99,10 +90,10 @@ function assertBadBinary(builder, str) {
         .End()
         .Code()
         .End();
-    assertBadBinary(builder, badExportString);
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 30 / 33: can't export Table 1 only zero-index Table is currently supported (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
 }
 
-function assertBadTable(tableDescription) {
+function assertBadTable(tableDescription, message) {
     const builder = new Builder()
         .Type().End()
         .Function().End()
@@ -111,10 +102,10 @@ function assertBadTable(tableDescription) {
         .End()
         .Code()
         .End();
-    assertBadBinary(builder, badTableString);
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, message);
 }
 
-function assertBadTableImport(tableDescription) {
+function assertBadTableImport(tableDescription, message) {
     const builder = new Builder()
         .Type().End()
         .Import()
@@ -123,29 +114,53 @@ function assertBadTableImport(tableDescription) {
         .Function().End()
         .Code()
         .End();
-    assertBadBinary(builder, badImportString);
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, message);
 }
 
 {
     let badDescriptions = [
-        {initial: 10, element: "i32"},
-        {initial: 10, element: "f32"},
-        {initial: 10, element: "f64"},
-        {initial: 10, element: "i64"},
-        {initial: 10, maximum: 20, element: "i32"},
-        {initial: 10, maximum: 20, element: "f32"},
-        {initial: 10, maximum: 20, element: "f64"},
-        {initial: 10, maximum: 20, element: "i64"},
+        [{initial: 10, element: "i32"},
+         "WebAssembly.Module doesn't parse at byte 18 / 23: Table type should be anyfunc, got -1 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 26 / 34: Table type should be anyfunc, got -1 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"],
+        [{initial: 10, element: "f32"},
+         "WebAssembly.Module doesn't parse at byte 18 / 23: Table type should be anyfunc, got -3 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 26 / 34: Table type should be anyfunc, got -3 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"],
+        [{initial: 10, element: "f64"},
+         "WebAssembly.Module doesn't parse at byte 18 / 23: Table type should be anyfunc, got -4 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 26 / 34: Table type should be anyfunc, got -4 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"],
+        [{initial: 10, element: "i64"},
+         "WebAssembly.Module doesn't parse at byte 18 / 23: Table type should be anyfunc, got -2 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 26 / 34: Table type should be anyfunc, got -2 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"],
+        [{initial: 10, maximum: 20, element: "i32"},
+         "WebAssembly.Module doesn't parse at byte 18 / 24: Table type should be anyfunc, got -1 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 26 / 35: Table type should be anyfunc, got -1 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"],
+        [{initial: 10, maximum: 20, element: "f32"},
+         "WebAssembly.Module doesn't parse at byte 18 / 24: Table type should be anyfunc, got -3 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 26 / 35: Table type should be anyfunc, got -3 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"],
+        [{initial: 10, maximum: 20, element: "f64"},
+         "WebAssembly.Module doesn't parse at byte 18 / 24: Table type should be anyfunc, got -4 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 26 / 35: Table type should be anyfunc, got -4 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"],
+        [{initial: 10, maximum: 20, element: "i64"},
+         "WebAssembly.Module doesn't parse at byte 18 / 24: Table type should be anyfunc, got -2 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 26 / 35: Table type should be anyfunc, got -2 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"],
 
-        {initial: 10, maximum: 9, element: "anyfunc"},
-        {initial: 1, maximum: 0, element: "anyfunc"},
-        {initial: 2**32 - 1, maximum: 2**32 - 2, element: "anyfunc"},
-        {initial: 2**31, element: "anyfunc"},
+        [{initial: 10, maximum: 9, element: "anyfunc"},
+         "WebAssembly.Module doesn't parse at byte 21 / 24: resizable limits has a initial page count of 10 which is greater than its maximum 9 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 29 / 35: resizable limits has a initial page count of 10 which is greater than its maximum 9 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"],
+        [{initial: 1, maximum: 0, element: "anyfunc"},
+         "WebAssembly.Module doesn't parse at byte 21 / 24: resizable limits has a initial page count of 1 which is greater than its maximum 0 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 29 / 35: resizable limits has a initial page count of 1 which is greater than its maximum 0 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"],
+        [{initial: 2**32 - 1, maximum: 2**32 - 2, element: "anyfunc"},
+         "WebAssembly.Module doesn't parse at byte 29 / 32: resizable limits has a initial page count of 4294967295 which is greater than its maximum 4294967294 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 37 / 43: resizable limits has a initial page count of 4294967295 which is greater than its maximum 4294967294 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"],
+        [{initial: 2**31, element: "anyfunc"},
+         "WebAssembly.Module doesn't parse at byte 24 / 27: Table's initial page count of 2147483648 is invalid (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 32 / 38: Table's initial page count of 2147483648 is invalid (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"],
     ];
 
     for (const d of badDescriptions) {
-        assertBadTable(d);
-        assertBadTableImport(d);
+        assertBadTable(d[0], d[1]);
+        assertBadTableImport(d[0], d[2]);
     }
 }
 
@@ -159,7 +174,7 @@ function assertBadTableImport(tableDescription) {
         .Function().End()
         .Code()
         .End();
-    assertBadBinary(builder, badImportString);
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 39 / 48: Table section cannot exist if an Import has a table (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
 }
 
 
@@ -173,23 +188,15 @@ function assertBadTableImport(tableDescription) {
             .Function().End()
             .Code()
             .End();
-
-        let threw = false;
         const module = new WebAssembly.Module(builder.WebAssembly().get());
-        try {
-            new WebAssembly.Instance(module, {imp: {table}});
-        } catch (e) {
-            assert.eq(e.toString(), message);
-            threw = true;
-        }
-        assert.truthy(threw);
+        assert.throws(() => new WebAssembly.Instance(module, {imp: {table}}), TypeError, message);
     }
 
     const badTables = [
-        [{initial: 100, maximum:100, element:"anyfunc"}, new WebAssembly.Table({initial:100, element: "anyfunc"}), "TypeError: Table import does not have a 'maximum' but the module requires that it does"],
-        [{initial: 100, maximum:100, element:"anyfunc"}, new WebAssembly.Table({initial:100, maximum:101, element: "anyfunc"}), "TypeError: Imported Table's 'maximum' is larger than the module's expected 'maximum'"],
-        [{initial: 100, element:"anyfunc"}, new WebAssembly.Table({initial:10, element: "anyfunc"}), "TypeError: Table import provided an 'initial' that is too small"],
-        [{initial: 10, element:"anyfunc"}, new WebAssembly.Table({initial:9, element: "anyfunc"}), "TypeError: Table import provided an 'initial' that is too small"],
+        [{initial: 100, maximum:100, element:"anyfunc"}, new WebAssembly.Table({initial:100, element: "anyfunc"}), "Table import does not have a 'maximum' but the module requires that it does"],
+        [{initial: 100, maximum:100, element:"anyfunc"}, new WebAssembly.Table({initial:100, maximum:101, element: "anyfunc"}), "Imported Table's 'maximum' is larger than the module's expected 'maximum'"],
+        [{initial: 100, element:"anyfunc"}, new WebAssembly.Table({initial:10, element: "anyfunc"}), "Table import provided an 'initial' that is too small"],
+        [{initial: 10, element:"anyfunc"}, new WebAssembly.Table({initial:9, element: "anyfunc"}), "Table import provided an 'initial' that is too small"],
     ];
     for (const [d, t, m] of badTables) {
         assertBadTableInstance(d, t, m);
index 7ebce95..7c1fa18 100644 (file)
@@ -52,7 +52,7 @@ const assertMemoryAllZero = memory => {
           .Segment([0xff]).Offset(0).End()
         .End();
     const bin = builder.WebAssembly().get();
-    assert.throws(() => new WebAssembly.Module(bin), WebAssembly.CompileError, `couldn't parse section Data: Data segments (evaluating 'new WebAssembly.Module(bin)')`);
+    assert.throws(() => new WebAssembly.Module(bin), WebAssembly.CompileError, `WebAssembly.Module doesn't parse at byte 13 / 20: Data section cannot exist without a Memory section or Import (evaluating 'new WebAssembly.Module(bin)')`);
 })();
 
 (function EmptyDataSectionWithoutMemory() {
@@ -62,7 +62,7 @@ const assertMemoryAllZero = memory => {
           .Segment([]).Offset(0).End()
         .End();
     const bin = builder.WebAssembly().get();
-    assert.throws(() => new WebAssembly.Module(bin), WebAssembly.CompileError, `couldn't parse section Data: Data segments (evaluating 'new WebAssembly.Module(bin)')`);
+    assert.throws(() => new WebAssembly.Module(bin), WebAssembly.CompileError, `WebAssembly.Module doesn't parse at byte 13 / 19: Data section cannot exist without a Memory section or Import (evaluating 'new WebAssembly.Module(bin)')`);
 })();
 
 (function DataSectionBiggerThanMemory() {
index 05d619d..b444569 100644 (file)
@@ -31,5 +31,5 @@ import Builder from '../Builder.js';
         .Start(0).End() // Invalid index.
         .Code().End();
     const bin = b.WebAssembly().get();
-    assert.throws(() => new WebAssembly.Module(bin), Error, `couldn't parse section Start: Start function declaration (evaluating 'new WebAssembly.Module(bin)')`);
+    assert.throws(() => new WebAssembly.Module(bin), Error, `WebAssembly.Module doesn't parse at byte 17 / 20: Start index 0 exceeds function index space 0 (evaluating 'new WebAssembly.Module(bin)')`);
 })();
index 7b3d137..fa411d5 100644 (file)
@@ -55,7 +55,7 @@ for (const c in constructorProperties) {
             assert.throws(() => new WebAssembly[c](invalid), TypeError, `first argument to WebAssembly.Module must be an ArrayBufferView or an ArrayBuffer (evaluating 'new WebAssembly[c](invalid)')`);
         for (const buffer of [new ArrayBuffer(), new DataView(new ArrayBuffer()), new Int8Array(), new Uint8Array(), new Uint8ClampedArray(), new Int16Array(), new Uint16Array(), new Int32Array(), new Uint32Array(), new Float32Array(), new Float64Array()])
             // FIXME the following should be WebAssembly.CompileError. https://bugs.webkit.org/show_bug.cgi?id=163768
-            assert.throws(() => new WebAssembly[c](buffer), Error, `Module is 0 bytes, expected at least 8 bytes (evaluating 'new WebAssembly[c](buffer)')`);
+            assert.throws(() => new WebAssembly[c](buffer), Error, `WebAssembly.Module doesn't parse at byte 0 / 0: expected a module of at least 8 bytes (evaluating 'new WebAssembly[c](buffer)')`);
         assert.instanceof(new WebAssembly[c](emptyModuleArray), WebAssembly.Module);
         // FIXME test neutered TypedArray and TypedArrayView. https://bugs.webkit.org/show_bug.cgi?id=163899
         break;
index b7e3d6b..e96cbfd 100644 (file)
@@ -1,5 +1,85 @@
 2016-12-15  JF Bastien  <jfbastien@apple.com>
 
+        WebAssembly: improve compilation error messages
+        https://bugs.webkit.org/show_bug.cgi?id=163919
+
+        Reviewed by Saam Barati.
+
+        The error handling messages were underwhelming because most
+        locations merely returned `false` on failure. This patch uses
+        std::expected to denote that failure isn't expected. Doing this
+        makes it almost impossible to mess up the code: either a function
+        returns a result (or a partial result for internal helpers) or an
+        error. We're not synchronizing the error string with the m_failed
+        bool anymore, and the caller will abort if they try to get a
+        result but the outcome was an error.
+
+        This also shortens the code significantly using macros, while also
+        judiciously preventing inlining of error handling code and biasing
+        towards success using UNLIKELY. This means that the generated code
+        should be more efficient (no string formatting on success, and
+        regalloc can avoid these unlikely paths).
+
+        The patch adds a few missing checks as well, especially related to
+        count limits and memory allocation failure.
+
+        As a follow-up I'd like to improve WTF::makeString further, so it
+        does coercions to string and understands ADL as I did in this
+        patch.
+
+        * wasm/WasmB3IRGenerator.cpp:
+        (JSC::Wasm::B3IRGenerator::fail):
+        (JSC::Wasm::parseAndCompile):
+        * wasm/WasmB3IRGenerator.h:
+        * wasm/WasmFormat.h:
+        (JSC::Wasm::isValidExternalKind):
+        (JSC::Wasm::makeString):
+        * wasm/WasmFunctionParser.h:
+        * wasm/WasmModuleParser.cpp:
+        * wasm/WasmModuleParser.h:
+        * wasm/WasmParser.h:
+        (JSC::Wasm::FailureHelper::makeString):
+        (JSC::Wasm::Parser::fail):
+        (JSC::Wasm::Parser<SuccessType>::Parser):
+        (JSC::Wasm::Parser<SuccessType>::consumeCharacter):
+        (JSC::Wasm::Parser<SuccessType>::consumeString):
+        (JSC::Wasm::Parser<SuccessType>::consumeUTF8String):
+        (JSC::Wasm::Parser<SuccessType>::parseVarUInt32):
+        (JSC::Wasm::Parser<SuccessType>::parseVarUInt64):
+        (JSC::Wasm::Parser<SuccessType>::parseVarInt32):
+        (JSC::Wasm::Parser<SuccessType>::parseVarInt64):
+        (JSC::Wasm::Parser<SuccessType>::parseUInt32):
+        (JSC::Wasm::Parser<SuccessType>::parseUInt64):
+        (JSC::Wasm::Parser<SuccessType>::parseUInt8):
+        (JSC::Wasm::Parser<SuccessType>::parseInt7):
+        (JSC::Wasm::Parser<SuccessType>::parseUInt7):
+        (JSC::Wasm::Parser<SuccessType>::parseVarUInt1):
+        (JSC::Wasm::Parser<SuccessType>::parseResultType):
+        (JSC::Wasm::Parser<SuccessType>::parseValueType):
+        (JSC::Wasm::Parser<SuccessType>::parseExternalKind):
+        * wasm/WasmPlan.cpp:
+        (JSC::Wasm::Plan::run):
+        * wasm/WasmSections.h:
+        (JSC::Wasm::isValidSection):
+        (JSC::Wasm::validateOrder):
+        (JSC::Wasm::makeString):
+        * wasm/WasmValidate.cpp:
+        (JSC::Wasm::Validate::fail):
+        (JSC::Wasm::Validate::addUnreachable):
+        (JSC::Wasm::validateFunction):
+        * wasm/WasmValidate.h:
+        * wasm/generateWasmB3IRGeneratorInlinesHeader.py:
+        * wasm/generateWasmOpsHeader.py:
+        * wasm/generateWasmValidateInlinesHeader.py:
+        (loadMacro):
+        (storeMacro):
+        * wasm/js/WebAssemblyInstanceConstructor.cpp:
+        (JSC::constructJSWebAssemblyInstance):
+        * wasm/js/WebAssemblyModuleRecord.cpp:
+        (JSC::WebAssemblyModuleRecord::link):
+
+2016-12-15  JF Bastien  <jfbastien@apple.com>
+
         WebAssembly API: improve data section errors, initialize after Element
         https://bugs.webkit.org/show_bug.cgi?id=165733
 
index 6948f12..fd379f0 100644 (file)
@@ -137,53 +137,66 @@ public:
 
     static constexpr ExpressionType emptyExpression = nullptr;
 
+    typedef String ErrorType;
+    typedef UnexpectedType<ErrorType> UnexpectedResult;
+    typedef Expected<std::unique_ptr<WasmInternalFunction>, ErrorType> Result;
+    typedef Expected<void, ErrorType> PartialResult;
+    template <typename ...Args>
+    NEVER_INLINE UnexpectedResult WARN_UNUSED_RETURN fail(Args... args) const
+    {
+        using namespace FailureHelper; // See ADL comment in WasmParser.h.
+        return UnexpectedResult(makeString(ASCIILiteral("WebAssembly.Module failed compiling: "), makeString(args)...));
+    }
+#define WASM_COMPILE_FAIL_IF(condition, ...) do { \
+        if (UNLIKELY(condition))                  \
+            return fail(__VA_ARGS__);             \
+    } while (0)
+
     B3IRGenerator(VM&, const ModuleInformation&, Procedure&, WasmInternalFunction*, Vector<UnlinkedWasmToWasmCall>&, const ImmutableFunctionIndexSpace&);
 
-    bool WARN_UNUSED_RETURN addArguments(const Vector<Type>&);
-    bool WARN_UNUSED_RETURN addLocal(Type, uint32_t);
+    PartialResult WARN_UNUSED_RETURN addArguments(const Vector<Type>&);
+    PartialResult WARN_UNUSED_RETURN addLocal(Type, uint32_t);
     ExpressionType addConstant(Type, uint64_t);
 
     // Locals
-    bool WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
-    bool WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
+    PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
+    PartialResult WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
 
     // Globals
-    bool WARN_UNUSED_RETURN getGlobal(uint32_t index, ExpressionType& result);
-    bool WARN_UNUSED_RETURN setGlobal(uint32_t index, ExpressionType value);
+    PartialResult WARN_UNUSED_RETURN getGlobal(uint32_t index, ExpressionType& result);
+    PartialResult WARN_UNUSED_RETURN setGlobal(uint32_t index, ExpressionType value);
 
     // Memory
-    bool WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset);
-    bool WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset);
+    PartialResult WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset);
+    PartialResult WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset);
 
     // Basic operators
     template<OpType>
-    bool WARN_UNUSED_RETURN addOp(ExpressionType arg, ExpressionType& result);
+    PartialResult WARN_UNUSED_RETURN addOp(ExpressionType arg, ExpressionType& result);
     template<OpType>
-    bool WARN_UNUSED_RETURN addOp(ExpressionType left, ExpressionType right, ExpressionType& result);
-    bool WARN_UNUSED_RETURN addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result);
+    PartialResult WARN_UNUSED_RETURN addOp(ExpressionType left, ExpressionType right, ExpressionType& result);
+    PartialResult WARN_UNUSED_RETURN addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result);
 
     // Control flow
     ControlData WARN_UNUSED_RETURN addBlock(Type signature);
     ControlData WARN_UNUSED_RETURN addLoop(Type signature);
-    bool WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature, ControlData& result);
-    bool WARN_UNUSED_RETURN addElse(ControlData&, const ExpressionList&);
-    bool WARN_UNUSED_RETURN addElseToUnreachable(ControlData&);
+    PartialResult WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature, ControlData& result);
+    PartialResult WARN_UNUSED_RETURN addElse(ControlData&, const ExpressionList&);
+    PartialResult WARN_UNUSED_RETURN addElseToUnreachable(ControlData&);
 
-    bool WARN_UNUSED_RETURN addReturn(const ExpressionList& returnValues);
-    bool WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& returnValues);
-    bool WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTargets, const ExpressionList& expressionStack);
-    bool WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack);
-    bool WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&);
+    PartialResult WARN_UNUSED_RETURN addReturn(const ExpressionList& returnValues);
+    PartialResult WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& returnValues);
+    PartialResult WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTargets, const ExpressionList& expressionStack);
+    PartialResult WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack);
+    PartialResult WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&);
 
     // Calls
-    bool WARN_UNUSED_RETURN addCall(uint32_t calleeIndex, const Signature*, Vector<ExpressionType>& args, ExpressionType& result);
-    bool WARN_UNUSED_RETURN addCallIndirect(const Signature*, Vector<ExpressionType>& args, ExpressionType& result);
-    void addUnreachable();
+    PartialResult WARN_UNUSED_RETURN addCall(uint32_t calleeIndex, const Signature*, Vector<ExpressionType>& args, ExpressionType& result);
+    PartialResult WARN_UNUSED_RETURN addCallIndirect(const Signature*, Vector<ExpressionType>& args, ExpressionType& result);
+    PartialResult WARN_UNUSED_RETURN addUnreachable();
 
     void dump(const Vector<ControlEntry>& controlStack, const ExpressionList& expressionStack);
 
-    void setErrorMessage(String&&) { UNREACHABLE_FOR_PLATFORM(); }
-
 private:
     ExpressionType emitCheckAndPreparePointer(ExpressionType pointer, uint32_t offset, uint32_t sizeOfOp);
     ExpressionType emitLoadOp(LoadOpType, Origin, ExpressionType pointer, uint32_t offset);
@@ -291,24 +304,22 @@ Value* B3IRGenerator::zeroForType(Type type)
     return zeroValue;
 }
 
-bool B3IRGenerator::addLocal(Type type, uint32_t count)
+auto B3IRGenerator::addLocal(Type type, uint32_t count) -> PartialResult
 {
-    if (!m_locals.tryReserveCapacity(m_locals.size() + count))
-        return false;
+    WASM_COMPILE_FAIL_IF(!m_locals.tryReserveCapacity(m_locals.size() + count), "can't allocate memory for ", m_locals.size() + count, " locals");
 
     for (uint32_t i = 0; i < count; ++i) {
         Variable* local = m_proc.addVariable(toB3Type(type));
         m_locals.uncheckedAppend(local);
         m_currentBlock->appendNew<VariableValue>(m_proc, Set, Origin(), local, zeroForType(type));
     }
-    return true;
+    return { };
 }
 
-bool B3IRGenerator::addArguments(const Vector<Type>& types)
+auto B3IRGenerator::addArguments(const Vector<Type>& types) -> PartialResult
 {
     ASSERT(!m_locals.size());
-    if (!m_locals.tryReserveCapacity(types.size()))
-        return false;
+    WASM_COMPILE_FAIL_IF(!m_locals.tryReserveCapacity(types.size()), "can't allocate memory for ", types.size(), " arguments");
 
     m_locals.grow(types.size());
     wasmCallingConvention().loadArguments(types, m_proc, m_currentBlock, Origin(),
@@ -317,45 +328,46 @@ bool B3IRGenerator::addArguments(const Vector<Type>& types)
             m_locals[i] = argumentVariable;
             m_currentBlock->appendNew<VariableValue>(m_proc, Set, Origin(), argumentVariable, argument);
         });
-    return true;
+    return { };
 }
 
-bool B3IRGenerator::getLocal(uint32_t index, ExpressionType& result)
+auto B3IRGenerator::getLocal(uint32_t index, ExpressionType& result) -> PartialResult
 {
     ASSERT(m_locals[index]);
     result = m_currentBlock->appendNew<VariableValue>(m_proc, B3::Get, Origin(), m_locals[index]);
-    return true;
+    return { };
 }
 
-void B3IRGenerator::addUnreachable()
+auto B3IRGenerator::addUnreachable() -> PartialResult
 {
     B3::PatchpointValue* unreachable = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, Origin());
     unreachable->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
         jit.breakpoint();
     });
     unreachable->effects.terminal = true;
+    return { };
 }
 
-bool B3IRGenerator::setLocal(uint32_t index, ExpressionType value)
+auto B3IRGenerator::setLocal(uint32_t index, ExpressionType value) -> PartialResult
 {
     ASSERT(m_locals[index]);
     m_currentBlock->appendNew<VariableValue>(m_proc, B3::Set, Origin(), m_locals[index], value);
-    return true;
+    return { };
 }
 
-bool B3IRGenerator::getGlobal(uint32_t index, ExpressionType& result)
+auto B3IRGenerator::getGlobal(uint32_t index, ExpressionType& result) -> PartialResult
 {
     Value* globalsArray = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), m_instanceValue, JSWebAssemblyInstance::offsetOfGlobals());
     result = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, toB3Type(m_info.globals[index].type), Origin(), globalsArray, index * sizeof(Register));
-    return true;
+    return { };
 }
 
-bool B3IRGenerator::setGlobal(uint32_t index, ExpressionType value)
+auto B3IRGenerator::setGlobal(uint32_t index, ExpressionType value) -> PartialResult
 {
     ASSERT(toB3Type(m_info.globals[index].type) == value->type());
     Value* globalsArray = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), m_instanceValue, JSWebAssemblyInstance::offsetOfGlobals());
     m_currentBlock->appendNew<MemoryValue>(m_proc, Store, Origin(), value, globalsArray, index * sizeof(Register));
-    return true;
+    return { };
 }
 
 inline Value* B3IRGenerator::emitCheckAndPreparePointer(ExpressionType pointer, uint32_t offset, uint32_t sizeOfOperation)
@@ -465,12 +477,12 @@ inline Value* B3IRGenerator::emitLoadOp(LoadOpType op, Origin origin, Expression
     RELEASE_ASSERT_NOT_REACHED();
 }
 
-bool B3IRGenerator::load(LoadOpType op, ExpressionType pointer, ExpressionType& result, uint32_t offset)
+auto B3IRGenerator::load(LoadOpType op, ExpressionType pointer, ExpressionType& result, uint32_t offset) -> PartialResult
 {
     ASSERT(pointer->type() == Int32);
 
     result = emitLoadOp(op, Origin(), emitCheckAndPreparePointer(pointer, offset, sizeOfLoadOp(op)), offset);
-    return true;
+    return { };
 }
 
 inline uint32_t sizeOfStoreOp(StoreOpType op)
@@ -527,18 +539,18 @@ inline void B3IRGenerator::emitStoreOp(StoreOpType op, Origin origin, Expression
     RELEASE_ASSERT_NOT_REACHED();
 }
 
-bool B3IRGenerator::store(StoreOpType op, ExpressionType pointer, ExpressionType value, uint32_t offset)
+auto B3IRGenerator::store(StoreOpType op, ExpressionType pointer, ExpressionType value, uint32_t offset) -> PartialResult
 {
     ASSERT(pointer->type() == Int32);
 
     emitStoreOp(op, Origin(), emitCheckAndPreparePointer(pointer, offset, sizeOfStoreOp(op)), value, offset);
-    return true;
+    return { };
 }
 
-bool B3IRGenerator::addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result)
+auto B3IRGenerator::addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result) -> PartialResult
 {
     result = m_currentBlock->appendNew<Value>(m_proc, B3::Select, Origin(), condition, nonZero, zero);
-    return true;
+    return { };
 }
 
 B3IRGenerator::ExpressionType B3IRGenerator::addConstant(Type type, uint64_t value)
@@ -576,7 +588,7 @@ B3IRGenerator::ControlData B3IRGenerator::addLoop(Type signature)
     return ControlData(m_proc, signature, BlockType::Loop, continuation, body);
 }
 
-bool B3IRGenerator::addIf(ExpressionType condition, Type signature, ControlType& result)
+auto B3IRGenerator::addIf(ExpressionType condition, Type signature, ControlType& result) -> PartialResult
 {
     // FIXME: This needs to do some kind of stack passing.
 
@@ -591,35 +603,35 @@ bool B3IRGenerator::addIf(ExpressionType condition, Type signature, ControlType&
 
     m_currentBlock = taken;
     result = ControlData(m_proc, signature, BlockType::If, continuation, notTaken);
-    return true;
+    return { };
 }
 
-bool B3IRGenerator::addElse(ControlData& data, const ExpressionList& currentStack)
+auto B3IRGenerator::addElse(ControlData& data, const ExpressionList& currentStack) -> PartialResult
 {
     unifyValuesWithBlock(currentStack, data.result);
     m_currentBlock->appendNewControlValue(m_proc, Jump, Origin(), data.continuation);
     return addElseToUnreachable(data);
 }
 
-bool B3IRGenerator::addElseToUnreachable(ControlData& data)
+auto B3IRGenerator::addElseToUnreachable(ControlData& data) -> PartialResult
 {
     ASSERT(data.type() == BlockType::If);
     m_currentBlock = data.special;
     data.convertIfToBlock();
-    return true;
+    return { };
 }
 
-bool B3IRGenerator::addReturn(const ExpressionList& returnValues)
+auto B3IRGenerator::addReturn(const ExpressionList& returnValues) -> PartialResult
 {
     ASSERT(returnValues.size() <= 1);
     if (returnValues.size())
         m_currentBlock->appendNewControlValue(m_proc, B3::Return, Origin(), returnValues[0]);
     else
         m_currentBlock->appendNewControlValue(m_proc, B3::Return, Origin());
-    return true;
+    return { };
 }
 
-bool B3IRGenerator::addBranch(ControlData& data, ExpressionType condition, const ExpressionList& returnValues)
+auto B3IRGenerator::addBranch(ControlData& data, ExpressionType condition, const ExpressionList& returnValues) -> PartialResult
 {
     if (data.type() != BlockType::Loop)
         unifyValuesWithBlock(returnValues, data.result);
@@ -637,10 +649,10 @@ bool B3IRGenerator::addBranch(ControlData& data, ExpressionType condition, const
         target->addPredecessor(m_currentBlock);
     }
 
-    return true;
+    return { };
 }
 
-bool B3IRGenerator::addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack)
+auto B3IRGenerator::addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack) -> PartialResult
 {
     for (size_t i = 0; i < targets.size(); ++i)
         unifyValuesWithBlock(expressionStack, targets[i]->result);
@@ -651,10 +663,10 @@ bool B3IRGenerator::addSwitch(ExpressionType condition, const Vector<ControlData
     for (size_t i = 0; i < targets.size(); ++i)
         switchValue->appendCase(SwitchCase(i, FrequentedBlock(targets[i]->targetBlockForBranch())));
 
-    return true;
+    return { };
 }
 
-bool B3IRGenerator::endBlock(ControlEntry& entry, ExpressionList& expressionStack)
+auto B3IRGenerator::endBlock(ControlEntry& entry, ExpressionList& expressionStack) -> PartialResult
 {
     ControlData& data = entry.controlData;
 
@@ -666,7 +678,7 @@ bool B3IRGenerator::endBlock(ControlEntry& entry, ExpressionList& expressionStac
 }
 
 
-bool B3IRGenerator::addEndToUnreachable(ControlEntry& entry)
+auto B3IRGenerator::addEndToUnreachable(ControlEntry& entry) -> PartialResult
 {
     ControlData& data = entry.controlData;
     m_currentBlock = data.continuation;
@@ -679,10 +691,10 @@ bool B3IRGenerator::addEndToUnreachable(ControlEntry& entry)
     for (Variable* result : data.result)
         entry.enclosedExpressionStack.append(m_currentBlock->appendNew<VariableValue>(m_proc, B3::Get, Origin(), result));
 
-    return true;
+    return { };
 }
 
-bool B3IRGenerator::addCall(uint32_t functionIndex, const Signature* signature, Vector<ExpressionType>& args, ExpressionType& result)
+auto B3IRGenerator::addCall(uint32_t functionIndex, const Signature* signature, Vector<ExpressionType>& args, ExpressionType& result) -> PartialResult
 {
     ASSERT(signature->arguments.size() == args.size());
 
@@ -705,10 +717,10 @@ bool B3IRGenerator::addCall(uint32_t functionIndex, const Signature* signature,
                 });
             });
         });
-    return true;
+    return { };
 }
 
-bool B3IRGenerator::addCallIndirect(const Signature* signature, Vector<ExpressionType>& args, ExpressionType& result)
+auto B3IRGenerator::addCallIndirect(const Signature* signature, Vector<ExpressionType>& args, ExpressionType& result) -> PartialResult
 {
     ExpressionType calleeIndex = args.takeLast();
     ASSERT(signature->arguments.size() == args.size());
@@ -779,7 +791,7 @@ bool B3IRGenerator::addCallIndirect(const Signature* signature, Vector<Expressio
             });
         });
 
-    return true;
+    return { };
 }
 
 void B3IRGenerator::unify(Variable* variable, ExpressionType source)
@@ -907,15 +919,14 @@ static std::unique_ptr<Compilation> createJSToWasmWrapper(VM& vm, WasmInternalFu
     return jsEntrypoint;
 }
 
-std::unique_ptr<WasmInternalFunction> parseAndCompile(VM& vm, const uint8_t* functionStart, size_t functionLength, const Signature* signature, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ImmutableFunctionIndexSpace& functionIndexSpace, const ModuleInformation& info, unsigned optLevel)
+Expected<std::unique_ptr<WasmInternalFunction>, String> parseAndCompile(VM& vm, const uint8_t* functionStart, size_t functionLength, const Signature* signature, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ImmutableFunctionIndexSpace& functionIndexSpace, const ModuleInformation& info, unsigned optLevel)
 {
     auto result = std::make_unique<WasmInternalFunction>();
 
     Procedure procedure;
     B3IRGenerator context(vm, info, procedure, result.get(), unlinkedWasmToWasmCalls, functionIndexSpace);
     FunctionParser<B3IRGenerator> parser(context, functionStart, functionLength, signature, functionIndexSpace, info);
-    if (!parser.parse())
-        RELEASE_ASSERT_NOT_REACHED();
+    WASM_FAIL_IF_HELPER_FAILS(parser.parse());
 
     procedure.resetReachability();
     validate(procedure, "After parsing:\n");
@@ -929,13 +940,13 @@ std::unique_ptr<WasmInternalFunction> parseAndCompile(VM& vm, const uint8_t* fun
     result->wasmEntrypoint.compilation = std::make_unique<Compilation>(vm, procedure, optLevel);
     result->wasmEntrypoint.calleeSaveRegisters = procedure.calleeSaveRegisters();
     result->jsToWasmEntrypoint.compilation = createJSToWasmWrapper(vm, *result, signature, result->wasmEntrypoint.compilation->code(), info.memory);
-    return result;
+    return WTFMove(result);
 }
 
 // Custom wasm ops. These are the ones too messy to do in wasm.json.
 
 template<>
-bool B3IRGenerator::addOp<OpType::I32Ctz>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<OpType::I32Ctz>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int32, Origin());
     patchpoint->append(arg, ValueRep::SomeRegister);
@@ -944,11 +955,11 @@ bool B3IRGenerator::addOp<OpType::I32Ctz>(ExpressionType arg, ExpressionType& re
     });
     patchpoint->effects = Effects::none();
     result = patchpoint;
-    return true;
+    return { };
 }
 
 template<>
-bool B3IRGenerator::addOp<OpType::I64Ctz>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<OpType::I64Ctz>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int64, Origin());
     patchpoint->append(arg, ValueRep::SomeRegister);
@@ -957,33 +968,33 @@ bool B3IRGenerator::addOp<OpType::I64Ctz>(ExpressionType arg, ExpressionType& re
     });
     patchpoint->effects = Effects::none();
     result = patchpoint;
-    return true;
+    return { };
 }
 
 template<>
-bool B3IRGenerator::addOp<OpType::I32Popcnt>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<OpType::I32Popcnt>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     // FIXME: This should use the popcnt instruction if SSE4 is available but we don't have code to detect SSE4 yet.
     // see: https://bugs.webkit.org/show_bug.cgi?id=165363
     uint32_t (*popcount)(int32_t) = [] (int32_t value) -> uint32_t { return __builtin_popcount(value); };
     Value* funcAddress = m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), bitwise_cast<void*>(popcount));
     result = m_currentBlock->appendNew<CCallValue>(m_proc, Int32, Origin(), Effects::none(), funcAddress, arg);
-    return true;
+    return { };
 }
 
 template<>
-bool B3IRGenerator::addOp<OpType::I64Popcnt>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<OpType::I64Popcnt>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     // FIXME: This should use the popcnt instruction if SSE4 is available but we don't have code to detect SSE4 yet.
     // see: https://bugs.webkit.org/show_bug.cgi?id=165363
     uint64_t (*popcount)(int64_t) = [] (int64_t value) -> uint64_t { return __builtin_popcountll(value); };
     Value* funcAddress = m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), bitwise_cast<void*>(popcount));
     result = m_currentBlock->appendNew<CCallValue>(m_proc, Int64, Origin(), Effects::none(), funcAddress, arg);
-    return true;
+    return { };
 }
 
 template<>
-bool B3IRGenerator::addOp<F64ConvertUI64>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<F64ConvertUI64>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Double, Origin());
     if (isX86())
@@ -999,11 +1010,11 @@ bool B3IRGenerator::addOp<F64ConvertUI64>(ExpressionType arg, ExpressionType& re
     });
     patchpoint->effects = Effects::none();
     result = patchpoint;
-    return true;
+    return { };
 }
 
 template<>
-bool B3IRGenerator::addOp<OpType::F32ConvertUI64>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<OpType::F32ConvertUI64>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Float, Origin());
     if (isX86())
@@ -1019,11 +1030,11 @@ bool B3IRGenerator::addOp<OpType::F32ConvertUI64>(ExpressionType arg, Expression
     });
     patchpoint->effects = Effects::none();
     result = patchpoint;
-    return true;
+    return { };
 }
 
 template<>
-bool B3IRGenerator::addOp<OpType::F64Nearest>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<OpType::F64Nearest>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Double, Origin());
     patchpoint->append(arg, ValueRep::SomeRegister);
@@ -1032,11 +1043,11 @@ bool B3IRGenerator::addOp<OpType::F64Nearest>(ExpressionType arg, ExpressionType
     });
     patchpoint->effects = Effects::none();
     result = patchpoint;
-    return true;
+    return { };
 }
 
 template<>
-bool B3IRGenerator::addOp<OpType::F32Nearest>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<OpType::F32Nearest>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Float, Origin());
     patchpoint->append(arg, ValueRep::SomeRegister);
@@ -1045,11 +1056,11 @@ bool B3IRGenerator::addOp<OpType::F32Nearest>(ExpressionType arg, ExpressionType
     });
     patchpoint->effects = Effects::none();
     result = patchpoint;
-    return true;
+    return { };
 }
 
 template<>
-bool B3IRGenerator::addOp<OpType::F64Trunc>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<OpType::F64Trunc>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Double, Origin());
     patchpoint->append(arg, ValueRep::SomeRegister);
@@ -1058,11 +1069,11 @@ bool B3IRGenerator::addOp<OpType::F64Trunc>(ExpressionType arg, ExpressionType&
     });
     patchpoint->effects = Effects::none();
     result = patchpoint;
-    return true;
+    return { };
 }
 
 template<>
-bool B3IRGenerator::addOp<OpType::F32Trunc>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<OpType::F32Trunc>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Float, Origin());
     patchpoint->append(arg, ValueRep::SomeRegister);
@@ -1071,11 +1082,11 @@ bool B3IRGenerator::addOp<OpType::F32Trunc>(ExpressionType arg, ExpressionType&
     });
     patchpoint->effects = Effects::none();
     result = patchpoint;
-    return true;
+    return { };
 }
 
 template<>
-bool B3IRGenerator::addOp<OpType::I32TruncSF64>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<OpType::I32TruncSF64>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     Value* max = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), -static_cast<double>(std::numeric_limits<int32_t>::min()));
     Value* min = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), static_cast<double>(std::numeric_limits<int32_t>::min()));
@@ -1094,11 +1105,11 @@ bool B3IRGenerator::addOp<OpType::I32TruncSF64>(ExpressionType arg, ExpressionTy
     });
     patchpoint->effects = Effects::none();
     result = patchpoint;
-    return true;
+    return { };
 }
 
 template<>
-bool B3IRGenerator::addOp<OpType::I32TruncSF32>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<OpType::I32TruncSF32>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     Value* max = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), -static_cast<float>(std::numeric_limits<int32_t>::min()));
     Value* min = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), static_cast<float>(std::numeric_limits<int32_t>::min()));
@@ -1117,12 +1128,12 @@ bool B3IRGenerator::addOp<OpType::I32TruncSF32>(ExpressionType arg, ExpressionTy
     });
     patchpoint->effects = Effects::none();
     result = patchpoint;
-    return true;
+    return { };
 }
 
 
 template<>
-bool B3IRGenerator::addOp<OpType::I32TruncUF64>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<OpType::I32TruncUF64>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     Value* max = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), static_cast<double>(std::numeric_limits<int32_t>::min()) * -2.0);
     Value* min = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), -1.0);
@@ -1141,11 +1152,11 @@ bool B3IRGenerator::addOp<OpType::I32TruncUF64>(ExpressionType arg, ExpressionTy
     });
     patchpoint->effects = Effects::none();
     result = patchpoint;
-    return true;
+    return { };
 }
 
 template<>
-bool B3IRGenerator::addOp<OpType::I32TruncUF32>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<OpType::I32TruncUF32>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     Value* max = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), static_cast<float>(std::numeric_limits<int32_t>::min()) * -2.0);
     Value* min = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), -1.0);
@@ -1164,11 +1175,11 @@ bool B3IRGenerator::addOp<OpType::I32TruncUF32>(ExpressionType arg, ExpressionTy
     });
     patchpoint->effects = Effects::none();
     result = patchpoint;
-    return true;
+    return { };
 }
 
 template<>
-bool B3IRGenerator::addOp<OpType::I64TruncSF64>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<OpType::I64TruncSF64>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     Value* max = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), -static_cast<double>(std::numeric_limits<int64_t>::min()));
     Value* min = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), static_cast<double>(std::numeric_limits<int64_t>::min()));
@@ -1187,11 +1198,11 @@ bool B3IRGenerator::addOp<OpType::I64TruncSF64>(ExpressionType arg, ExpressionTy
     });
     patchpoint->effects = Effects::none();
     result = patchpoint;
-    return true;
+    return { };
 }
 
 template<>
-bool B3IRGenerator::addOp<OpType::I64TruncUF64>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<OpType::I64TruncUF64>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     Value* max = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), static_cast<double>(std::numeric_limits<int64_t>::min()) * -2.0);
     Value* min = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), -1.0);
@@ -1229,11 +1240,11 @@ bool B3IRGenerator::addOp<OpType::I64TruncUF64>(ExpressionType arg, ExpressionTy
     });
     patchpoint->effects = Effects::none();
     result = patchpoint;
-    return true;
+    return { };
 }
 
 template<>
-bool B3IRGenerator::addOp<OpType::I64TruncSF32>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<OpType::I64TruncSF32>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     Value* max = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), -static_cast<float>(std::numeric_limits<int64_t>::min()));
     Value* min = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), static_cast<float>(std::numeric_limits<int64_t>::min()));
@@ -1252,11 +1263,11 @@ bool B3IRGenerator::addOp<OpType::I64TruncSF32>(ExpressionType arg, ExpressionTy
     });
     patchpoint->effects = Effects::none();
     result = patchpoint;
-    return true;
+    return { };
 }
 
 template<>
-bool B3IRGenerator::addOp<OpType::I64TruncUF32>(ExpressionType arg, ExpressionType& result)
+auto B3IRGenerator::addOp<OpType::I64TruncUF32>(ExpressionType arg, ExpressionType& result) -> PartialResult
 {
     Value* max = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), static_cast<float>(std::numeric_limits<int64_t>::min()) * -2.0);
     Value* min = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), -1.0);
@@ -1294,7 +1305,7 @@ bool B3IRGenerator::addOp<OpType::I64TruncUF32>(ExpressionType arg, ExpressionTy
     });
     patchpoint->effects = Effects::none();
     result = patchpoint;
-    return true;
+    return { };
 }
 
 } } // namespace JSC::Wasm
index ba10aa3..dfe6a46 100644 (file)
@@ -30,6 +30,7 @@
 #include "B3Compilation.h"
 #include "VM.h"
 #include "WasmFormat.h"
+#include <wtf/Expected.h>
 
 extern "C" void dumpProcedure(void*);
 
@@ -37,7 +38,7 @@ namespace JSC { namespace Wasm {
 
 class MemoryInformation;
 
-std::unique_ptr<WasmInternalFunction> parseAndCompile(VM&, const uint8_t*, size_t, const Signature*, Vector<UnlinkedWasmToWasmCall>&, const ImmutableFunctionIndexSpace&, const ModuleInformation&, unsigned optLevel = 1);
+Expected<std::unique_ptr<WasmInternalFunction>, String> parseAndCompile(VM&, const uint8_t*, size_t, const Signature*, Vector<UnlinkedWasmToWasmCall>&, const ImmutableFunctionIndexSpace&, const ModuleInformation&, unsigned optLevel = 1);
 
 } } // namespace JSC::Wasm
 
index 4e0eafd..e49a891 100644 (file)
@@ -61,34 +61,45 @@ inline bool isValueType(Type type)
     return false;
 }
     
-struct External {
-    enum Kind : uint8_t {
-        // FIXME auto-generate this. https://bugs.webkit.org/show_bug.cgi?id=165231
-        Function = 0,
-        Table = 1,
-        Memory = 2,
-        Global = 3,
-    };
-    template<typename Int>
-    static bool isValid(Int val)
-    {
-        switch (val) {
-        case Function:
-        case Table:
-        case Memory:
-        case Global:
-            return true;
-        default:
-            return false;
-        }
-    }
-    
-    static_assert(Function == 0, "Wasm needs Function to have the value 0");
-    static_assert(Table    == 1, "Wasm needs Table to have the value 1");
-    static_assert(Memory   == 2, "Wasm needs Memory to have the value 2");
-    static_assert(Global   == 3, "Wasm needs Global to have the value 3");
+enum class ExternalKind : uint8_t {
+    // FIXME auto-generate this. https://bugs.webkit.org/show_bug.cgi?id=165231
+    Function = 0,
+    Table = 1,
+    Memory = 2,
+    Global = 3,
 };
 
+template<typename Int>
+static bool isValidExternalKind(Int val)
+{
+    switch (val) {
+    case static_cast<Int>(ExternalKind::Function):
+    case static_cast<Int>(ExternalKind::Table):
+    case static_cast<Int>(ExternalKind::Memory):
+    case static_cast<Int>(ExternalKind::Global):
+        return true;
+    default:
+        return false;
+    }
+}
+
+static_assert(static_cast<int>(ExternalKind::Function) == 0, "Wasm needs Function to have the value 0");
+static_assert(static_cast<int>(ExternalKind::Table)    == 1, "Wasm needs Table to have the value 1");
+static_assert(static_cast<int>(ExternalKind::Memory)   == 2, "Wasm needs Memory to have the value 2");
+static_assert(static_cast<int>(ExternalKind::Global)   == 3, "Wasm needs Global to have the value 3");
+
+static inline const char* makeString(ExternalKind kind)
+{
+    switch (kind) {
+    case ExternalKind::Function: return "Function";
+    case ExternalKind::Table: return "Table";
+    case ExternalKind::Memory: return "Memory";
+    case ExternalKind::Global: return "Global";
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+    return "?";
+}
+
 struct Signature {
     Type returnType;
     Vector<Type> arguments;
@@ -97,13 +108,13 @@ struct Signature {
 struct Import {
     Identifier module;
     Identifier field;
-    External::Kind kind;
+    ExternalKind kind;
     unsigned kindIndex; // Index in the vector of the corresponding kind.
 };
 
 struct Export {
     Identifier field;
-    External::Kind kind;
+    ExternalKind kind;
     unsigned kindIndex; // Index in the vector of the corresponding kind.
 };
 
index a87c35f..784a8c6 100644 (file)
@@ -39,7 +39,7 @@ enum class BlockType {
 };
 
 template<typename Context>
-class FunctionParser : public Parser {
+class FunctionParser : public Parser<void> {
 public:
     typedef typename Context::ExpressionType ExpressionType;
     typedef typename Context::ControlType ControlType;
@@ -47,7 +47,7 @@ public:
 
     FunctionParser(Context&, const uint8_t* functionStart, size_t functionLength, const Signature*, const ImmutableFunctionIndexSpace&, const ModuleInformation&);
 
-    bool WARN_UNUSED_RETURN parse();
+    Result WARN_UNUSED_RETURN parse();
 
     struct ControlEntry {
         ExpressionList enclosedExpressionStack;
@@ -57,25 +57,26 @@ public:
 private:
     static const bool verbose = false;
 
-    bool WARN_UNUSED_RETURN parseBody();
-    bool WARN_UNUSED_RETURN parseExpression(OpType);
-    bool WARN_UNUSED_RETURN parseUnreachableExpression(OpType);
-    bool WARN_UNUSED_RETURN addReturn();
-    bool WARN_UNUSED_RETURN unifyControl(Vector<ExpressionType>&, unsigned level);
+    PartialResult WARN_UNUSED_RETURN parseBody();
+    PartialResult WARN_UNUSED_RETURN parseExpression(OpType);
+    PartialResult WARN_UNUSED_RETURN parseUnreachableExpression(OpType);
+    PartialResult WARN_UNUSED_RETURN addReturn();
+    PartialResult WARN_UNUSED_RETURN unifyControl(Vector<ExpressionType>&, unsigned level);
 
-    bool WARN_UNUSED_RETURN popExpressionStack(ExpressionType& result);
+#define WASM_TRY_POP_EXPRESSION_STACK_INTO(result, what) do {                               \
+        WASM_PARSER_FAIL_IF(m_expressionStack.isEmpty(), "can't pop empty stack in " what); \
+        result = m_expressionStack.takeLast();                                              \
+    } while (0)
 
     template<OpType>
-    bool WARN_UNUSED_RETURN unaryCase();
+    PartialResult WARN_UNUSED_RETURN unaryCase();
 
     template<OpType>
-    bool WARN_UNUSED_RETURN binaryCase();
+    PartialResult WARN_UNUSED_RETURN binaryCase();
 
-    bool setErrorMessage(String&& message)
-    {
-        m_context.setErrorMessage(WTFMove(message));
-        return false;
-    }
+#define WASM_TRY_ADD_TO_CONTEXT(add_expression) WASM_FAIL_IF_HELPER_FAILS(m_context.add_expression)
+
+    // FIXME add a macro as above for WASM_TRY_APPEND_TO_CONTROL_STACK https://bugs.webkit.org/show_bug.cgi?id=165862
 
     Context& m_context;
     ExpressionList m_expressionStack;
@@ -99,118 +100,104 @@ FunctionParser<Context>::FunctionParser(Context& context, const uint8_t* functio
 }
 
 template<typename Context>
-bool FunctionParser<Context>::parse()
+auto FunctionParser<Context>::parse() -> Result
 {
-    if (!m_context.addArguments(m_signature->arguments))
-        return false;
-
     uint32_t localCount;
-    if (!parseVarUInt32(localCount))
-        return false;
+
+    WASM_PARSER_FAIL_IF(!m_context.addArguments(m_signature->arguments), "can't add ", m_signature->arguments.size(), " arguments to Function");
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(localCount), "can't get local count");
+    WASM_PARSER_FAIL_IF(localCount == std::numeric_limits<uint32_t>::max(), "Function section's local count is too big ", localCount);
 
     for (uint32_t i = 0; i < localCount; ++i) {
         uint32_t numberOfLocals;
-        if (!parseVarUInt32(numberOfLocals))
-            return false;
-
         Type typeOfLocal;
-        if (!parseValueType(typeOfLocal))
-            return false;
 
-        if (!m_context.addLocal(typeOfLocal, numberOfLocals))
-            return false;
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(numberOfLocals), "can't get Function's number of locals in group ", i);
+        WASM_PARSER_FAIL_IF(numberOfLocals == std::numeric_limits<uint32_t>::max(), "Function section's ", i, "th local group count is too big ", numberOfLocals);
+        WASM_PARSER_FAIL_IF(!parseValueType(typeOfLocal), "can't get Function local's type in group ", i);
+        WASM_PARSER_FAIL_IF(!m_context.addLocal(typeOfLocal, numberOfLocals), "can't add ", numberOfLocals, " Function locals from group ", i);
     }
 
-    return parseBody();
+    WASM_FAIL_IF_HELPER_FAILS(parseBody());
+
+    return { };
 }
 
 template<typename Context>
-bool FunctionParser<Context>::parseBody()
+auto FunctionParser<Context>::parseBody() -> PartialResult
 {
     while (true) {
         uint8_t op;
-        if (!parseUInt8(op) || !isValidOpType(op)) {
-            if (verbose)
-                WTF::dataLogLn("attempted to decode invalid op: ", RawPointer(reinterpret_cast<void*>(op)), " at offset: ", RawPointer(reinterpret_cast<void*>(m_offset)));
-            return false;
-        }
+        WASM_PARSER_FAIL_IF(!parseUInt8(op), "can't decode opcode");
+        WASM_PARSER_FAIL_IF(!isValidOpType(op), "invalid opcode ", op);
 
         if (verbose) {
             dataLogLn("processing op (", m_unreachableBlocks, "): ",  RawPointer(reinterpret_cast<void*>(op)), " at offset: ", RawPointer(reinterpret_cast<void*>(m_offset)));
             m_context.dump(m_controlStack, m_expressionStack);
         }
 
-        if (op == OpType::End && !m_controlStack.size())
-            return m_unreachableBlocks ? true : addReturn();
-
-        if (m_unreachableBlocks) {
-            if (!parseUnreachableExpression(static_cast<OpType>(op))) {
-                if (verbose)
-                    dataLogLn("failed to process unreachable op:", op);
-                return false;
-            }
-        } else if (!parseExpression(static_cast<OpType>(op))) {
-            if (verbose)
-                dataLogLn("failed to process op:", op);
-            return false;
+        if (op == OpType::End && !m_controlStack.size()) {
+            if (m_unreachableBlocks)
+                return { };
+            return addReturn();
         }
 
+        if (m_unreachableBlocks)
+            WASM_FAIL_IF_HELPER_FAILS(parseUnreachableExpression(static_cast<OpType>(op)));
+        else
+            WASM_FAIL_IF_HELPER_FAILS(parseExpression(static_cast<OpType>(op)));
     }
 
     RELEASE_ASSERT_NOT_REACHED();
 }
 
 template<typename Context>
-bool FunctionParser<Context>::addReturn()
+auto FunctionParser<Context>::addReturn() -> PartialResult
 {
     ExpressionList returnValues;
     if (m_signature->returnType != Void) {
         ExpressionType returnValue;
-        if (!popExpressionStack(returnValue))
-            return false;
+        WASM_TRY_POP_EXPRESSION_STACK_INTO(returnValue, "return");
         returnValues.append(returnValue);
     }
 
     m_unreachableBlocks = 1;
-    return m_context.addReturn(returnValues);
+    WASM_TRY_ADD_TO_CONTEXT(addReturn(returnValues));
+    return { };
 }
 
 template<typename Context>
 template<OpType op>
-bool FunctionParser<Context>::binaryCase()
+auto FunctionParser<Context>::binaryCase() -> PartialResult
 {
     ExpressionType right;
-    if (!popExpressionStack(right))
-        return false;
-
     ExpressionType left;
-    if (!popExpressionStack(left))
-        return false;
-
     ExpressionType result;
-    if (!m_context.template addOp<op>(left, right, result))
-        return false;
+
+    WASM_TRY_POP_EXPRESSION_STACK_INTO(right, "binary right");
+    WASM_TRY_POP_EXPRESSION_STACK_INTO(left, "binary left");
+    WASM_TRY_ADD_TO_CONTEXT(template addOp<op>(left, right, result));
+
     m_expressionStack.append(result);
-    return true;
+    return { };
 }
 
 template<typename Context>
 template<OpType op>
-bool FunctionParser<Context>::unaryCase()
+auto FunctionParser<Context>::unaryCase() -> PartialResult
 {
     ExpressionType value;
-    if (!popExpressionStack(value))
-        return false;
-
     ExpressionType result;
-    if (!m_context.template addOp<op>(value, result))
-        return false;
+
+    WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "unary");
+    WASM_TRY_ADD_TO_CONTEXT(template addOp<op>(value, result));
+
     m_expressionStack.append(result);
-    return true;
+    return { };
 }
 
 template<typename Context>
-bool FunctionParser<Context>::parseExpression(OpType op)
+auto FunctionParser<Context>::parseExpression(OpType op) -> PartialResult
 {
     switch (op) {
 #define CREATE_CASE(name, id, b3op, inc) case OpType::name: return binaryCase<OpType::name>();
@@ -241,327 +228,248 @@ bool FunctionParser<Context>::parseExpression(OpType op)
 
     case OpType::Select: {
         ExpressionType condition;
-        if (!popExpressionStack(condition))
-            return false;
-
         ExpressionType zero;
-        if (!popExpressionStack(zero))
-            return false;
-
         ExpressionType nonZero;
-        if (!popExpressionStack(nonZero))
-            return false;
+
+        WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "select condition");
+        WASM_TRY_POP_EXPRESSION_STACK_INTO(zero, "select zero");
+        WASM_TRY_POP_EXPRESSION_STACK_INTO(nonZero, "select non-zero");
 
         ExpressionType result;
-        if (!m_context.addSelect(condition, nonZero, zero, result))
-            return false;
+        WASM_TRY_ADD_TO_CONTEXT(addSelect(condition, nonZero, zero, result));
 
         m_expressionStack.append(result);
-        return true;
+        return { };
     }
 
 #define CREATE_CASE(name, id, b3op, inc) case OpType::name:
     FOR_EACH_WASM_MEMORY_LOAD_OP(CREATE_CASE) {
         uint32_t alignment;
-        if (!parseVarUInt32(alignment))
-            return false;
-
         uint32_t offset;
-        if (!parseVarUInt32(offset))
-            return false;
-
         ExpressionType pointer;
-        if (!popExpressionStack(pointer))
-            return false;
-
         ExpressionType result;
-        if (!m_context.load(static_cast<LoadOpType>(op), pointer, result, offset))
-            return false;
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(alignment), "can't get load alignment");
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(offset), "can't get load offset");
+        WASM_TRY_POP_EXPRESSION_STACK_INTO(pointer, "load pointer");
+        WASM_TRY_ADD_TO_CONTEXT(load(static_cast<LoadOpType>(op), pointer, result, offset));
         m_expressionStack.append(result);
-        return true;
+        return { };
     }
 
     FOR_EACH_WASM_MEMORY_STORE_OP(CREATE_CASE) {
         uint32_t alignment;
-        if (!parseVarUInt32(alignment))
-            return false;
-
         uint32_t offset;
-        if (!parseVarUInt32(offset))
-            return false;
-
         ExpressionType value;
-        if (!popExpressionStack(value))
-            return false;
-
         ExpressionType pointer;
-        if (!popExpressionStack(pointer))
-            return false;
-
-        return m_context.store(static_cast<StoreOpType>(op), pointer, value, offset);
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(alignment), "can't get store alignment");
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(offset), "can't get store offset");
+        WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "store value");
+        WASM_TRY_POP_EXPRESSION_STACK_INTO(pointer, "store pointer");
+        WASM_TRY_ADD_TO_CONTEXT(store(static_cast<StoreOpType>(op), pointer, value, offset));
+        return { };
     }
 #undef CREATE_CASE
 
     case OpType::F32Const: {
         uint32_t constant;
-        if (!parseUInt32(constant))
-            return false;
+        WASM_PARSER_FAIL_IF(!parseUInt32(constant), "can't parse 32-bit floating-point constant");
         m_expressionStack.append(m_context.addConstant(F32, constant));
-        return true;
+        return { };
     }
 
     case OpType::I32Const: {
         int32_t constant;
-        if (!parseVarInt32(constant))
-            return false;
+        WASM_PARSER_FAIL_IF(!parseVarInt32(constant), "can't parse 32-bit constant");
         m_expressionStack.append(m_context.addConstant(I32, constant));
-        return true;
+        return { };
     }
 
     case OpType::F64Const: {
         uint64_t constant;
-        if (!parseUInt64(constant))
-            return false;
+        WASM_PARSER_FAIL_IF(!parseUInt64(constant), "can't parse 64-bit floating-point constant");
         m_expressionStack.append(m_context.addConstant(F64, constant));
-        return true;
+        return { };
     }
 
     case OpType::I64Const: {
         int64_t constant;
-        if (!parseVarInt64(constant))
-            return false;
+        WASM_PARSER_FAIL_IF(!parseVarInt64(constant), "can't parse 64-bit constant");
         m_expressionStack.append(m_context.addConstant(I64, constant));
-        return true;
+        return { };
     }
 
     case OpType::GetLocal: {
         uint32_t index;
-        if (!parseVarUInt32(index))
-            return false;
         ExpressionType result;
-        if (!m_context.getLocal(index, result))
-            return false;
-
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for get_local");
+        WASM_PARSER_FAIL_IF(!m_context.getLocal(index, result), "can't get_local at index", index);
         m_expressionStack.append(result);
-        return true;
+        return { };
     }
 
     case OpType::SetLocal: {
         uint32_t index;
-        if (!parseVarUInt32(index))
-            return false;
         ExpressionType value;
-        if (!popExpressionStack(value))
-            return false;
-        return m_context.setLocal(index, value);
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for set_local");
+        WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "set_local");
+        WASM_TRY_ADD_TO_CONTEXT(setLocal(index, value));
+        return { };
     }
 
     case OpType::TeeLocal: {
         uint32_t index;
-        if (!parseVarUInt32(index))
-            return false;
-        if (!m_expressionStack.size())
-            return false;
-        return m_context.setLocal(index, m_expressionStack.last());
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for tee_local");
+        WASM_PARSER_FAIL_IF(m_expressionStack.isEmpty(), "can't tee_local on empty expression stack");
+        WASM_TRY_ADD_TO_CONTEXT(setLocal(index, m_expressionStack.last()));
+        return { };
     }
 
     case OpType::GetGlobal: {
         uint32_t index;
-        if (!parseVarUInt32(index))
-            return false;
         ExpressionType result;
-        if (!m_context.getGlobal(index, result))
-            return false;
-
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get get_global's index");
+        WASM_TRY_ADD_TO_CONTEXT(getGlobal(index, result));
         m_expressionStack.append(result);
-        return true;
+        return { };
     }
 
     case OpType::SetGlobal: {
         uint32_t index;
-        if (!parseVarUInt32(index))
-            return false;
         ExpressionType value;
-        if (!popExpressionStack(value))
-            return false;
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get set_global's index");
+        WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "set_global value");
+        WASM_TRY_ADD_TO_CONTEXT(setGlobal(index, value));
         return m_context.setGlobal(index, value);
     }
 
     case OpType::Call: {
         uint32_t functionIndex;
-        if (!parseVarUInt32(functionIndex))
-            return false;
-
-        if (functionIndex >= m_functionIndexSpace.size)
-            return false;
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(functionIndex), "can't parse call's function index");
+        WASM_PARSER_FAIL_IF(functionIndex >= m_functionIndexSpace.size, "call function index ", functionIndex, " exceeds function index space ", m_functionIndexSpace.size);
 
         const Signature* calleeSignature = m_functionIndexSpace.buffer.get()[functionIndex].signature;
-
-        if (calleeSignature->arguments.size() > m_expressionStack.size())
-            return false;
+        WASM_PARSER_FAIL_IF(calleeSignature->arguments.size() > m_expressionStack.size(), "call function index ", functionIndex, " has ", calleeSignature->arguments.size(), " arguments, but the expression stack currently holds ", m_expressionStack.size(), " values");
 
         size_t firstArgumentIndex = m_expressionStack.size() - calleeSignature->arguments.size();
         Vector<ExpressionType> args;
-        args.reserveInitialCapacity(calleeSignature->arguments.size());
-        for (unsigned i = firstArgumentIndex; i < m_expressionStack.size(); ++i)
-            args.append(m_expressionStack[i]);
+        WASM_PARSER_FAIL_IF(!args.tryReserveCapacity(calleeSignature->arguments.size()), "can't allocate enough memory for call's ", calleeSignature->arguments.size(), " arguments");
+        for (size_t i = firstArgumentIndex; i < m_expressionStack.size(); ++i)
+            args.uncheckedAppend(m_expressionStack[i]);
         m_expressionStack.shrink(firstArgumentIndex);
 
         ExpressionType result = Context::emptyExpression;
-        if (!m_context.addCall(functionIndex, calleeSignature, args, result))
-            return false;
+        WASM_TRY_ADD_TO_CONTEXT(addCall(functionIndex, calleeSignature, args, result));
 
         if (result != Context::emptyExpression)
             m_expressionStack.append(result);
 
-        return true;
+            return { };
     }
 
     case OpType::CallIndirect: {
-        if (!m_info.tableInformation)
-            return setErrorMessage("call_indirect is only valid when a table is defined or imported");
         uint32_t signatureIndex;
-        if (!parseVarUInt32(signatureIndex))
-            return false;
-
         uint8_t reserved;
-        if (!parseVarUInt1(reserved))
-            return false;
-
-        if (reserved != 0)
-            return setErrorMessage("call_indirect 'reserved' varuint1 must be 0x0");
-
-        if (m_info.signatures.size() <= signatureIndex)
-            return setErrorMessage("Tried to use a signature outside the range of valid signatures");
+        WASM_PARSER_FAIL_IF(!m_info.tableInformation, "call_indirect is only valid when a table is defined or imported");
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(signatureIndex), "can't get call_indirect's signature index");
+        WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't get call_indirect's reserved byte");
+        WASM_PARSER_FAIL_IF(reserved, "call_indirect's 'reserved' varuint1 must be 0x0");
+        WASM_PARSER_FAIL_IF(m_info.signatures.size() <= signatureIndex, "call_indirect's signature index ", signatureIndex, " exceeds known signatures ", m_info.signatures.size());
 
         const Signature* calleeSignature = &m_info.signatures[signatureIndex];
         size_t argumentCount = calleeSignature->arguments.size() + 1; // Add the callee's index.
-        if (argumentCount > m_expressionStack.size())
-            return setErrorMessage("Not enough values on the stack for call_indirect");
+        WASM_PARSER_FAIL_IF(argumentCount > m_expressionStack.size(), "call_indirect expects ", argumentCount, " arguments, but the expression stack currently holds ", m_expressionStack.size(), " values");
 
         Vector<ExpressionType> args;
-        if (!args.tryReserveCapacity(argumentCount))
-            return setErrorMessage("Out of memory");
-
+        WASM_PARSER_FAIL_IF(!args.tryReserveCapacity(argumentCount), "can't allocate enough memory for ", argumentCount, " call_indirect arguments");
         size_t firstArgumentIndex = m_expressionStack.size() - argumentCount;
-        for (unsigned i = firstArgumentIndex; i < m_expressionStack.size(); ++i)
+        for (size_t i = firstArgumentIndex; i < m_expressionStack.size(); ++i)
             args.uncheckedAppend(m_expressionStack[i]);
         m_expressionStack.shrink(firstArgumentIndex);
 
         ExpressionType result = Context::emptyExpression;
-        if (!m_context.addCallIndirect(calleeSignature, args, result))
-            return false;
+        WASM_TRY_ADD_TO_CONTEXT(addCallIndirect(calleeSignature, args, result));
 
         if (result != Context::emptyExpression)
             m_expressionStack.append(result);
 
-        return true;
+        return { };
     }
 
     case OpType::Block: {
         Type inlineSignature;
-        if (!parseResultType(inlineSignature))
-            return false;
-
+        WASM_PARSER_FAIL_IF(!parseResultType(inlineSignature), "can't get block's inline signature");
         m_controlStack.append({ WTFMove(m_expressionStack), m_context.addBlock(inlineSignature) });
         m_expressionStack = ExpressionList();
-        return true;
+        return { };
     }
 
     case OpType::Loop: {
         Type inlineSignature;
-        if (!parseResultType(inlineSignature))
-            return false;
-
+        WASM_PARSER_FAIL_IF(!parseResultType(inlineSignature), "can't get loop's inline signature");
         m_controlStack.append({ WTFMove(m_expressionStack), m_context.addLoop(inlineSignature) });
         m_expressionStack = ExpressionList();
-        return true;
+        return { };
     }
 
     case OpType::If: {
         Type inlineSignature;
-        if (!parseResultType(inlineSignature))
-            return false;
-
         ExpressionType condition;
-        if (!popExpressionStack(condition))
-            return false;
-
         ControlType control;
-        if (!m_context.addIf(condition, inlineSignature, control))
-            return false;
-
+        WASM_PARSER_FAIL_IF(!parseResultType(inlineSignature), "can't get if's inline signature");
+        WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "if condition");
+        WASM_TRY_ADD_TO_CONTEXT(addIf(condition, inlineSignature, control));
         m_controlStack.append({ WTFMove(m_expressionStack), control });
         m_expressionStack = ExpressionList();
-        return true;
+        return { };
     }
 
     case OpType::Else: {
-        if (!m_controlStack.size()) {
-            setErrorMessage("Attempted to use else block at the top-level of a function");
-            return false;
-        }
-
-        if (!m_context.addElse(m_controlStack.last().controlData, m_expressionStack))
-            return false;
+        WASM_PARSER_FAIL_IF(m_controlStack.isEmpty(), "can't use else block at the top-level of a function");
+        WASM_TRY_ADD_TO_CONTEXT(addElse(m_controlStack.last().controlData, m_expressionStack));
         m_expressionStack.shrink(0);
-        return true;
+        return { };
     }
 
     case OpType::Br:
     case OpType::BrIf: {
         uint32_t target;
-        if (!parseVarUInt32(target) || target >= m_controlStack.size())
-            return false;
-
         ExpressionType condition = Context::emptyExpression;
-        if (op == OpType::BrIf) {
-            if (!popExpressionStack(condition))
-                return false;
-        } else
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(target), "can't get br / br_if's target");
+        WASM_PARSER_FAIL_IF(target >= m_controlStack.size(), "br / br_if's target ", target, " exceeds control stack size ", m_controlStack.size());
+        if (op == OpType::BrIf)
+            WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "br / br_if condition");
+        else
             m_unreachableBlocks = 1;
 
         ControlType& data = m_controlStack[m_controlStack.size() - 1 - target].controlData;
 
-        return m_context.addBranch(data, condition, m_expressionStack);
+        WASM_TRY_ADD_TO_CONTEXT(addBranch(data, condition, m_expressionStack));
+        return { };
     }
 
     case OpType::BrTable: {
         uint32_t numberOfTargets;
-        if (!parseVarUInt32(numberOfTargets))
-            return false;
+        ExpressionType condition;
+        uint32_t defaultTarget;
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(numberOfTargets), "can't get the number of targets for br_table");
+        WASM_PARSER_FAIL_IF(numberOfTargets == std::numeric_limits<uint32_t>::max(), "br_table's number of targets is too big ", numberOfTargets);
 
         Vector<ControlType*> targets;
-        if (!targets.tryReserveCapacity(numberOfTargets))
-            return false;
-
+        WASM_PARSER_FAIL_IF(!targets.tryReserveCapacity(numberOfTargets), "can't allocate memory for ", numberOfTargets, " br_table targets");
         for (uint32_t i = 0; i < numberOfTargets; ++i) {
             uint32_t target;
-            if (!parseVarUInt32(target))
-                return false;
-
-            if (target >= m_controlStack.size())
-                return false;
-
+            WASM_PARSER_FAIL_IF(!parseVarUInt32(target), "can't get ", i, "th target for br_table");
+            WASM_PARSER_FAIL_IF(target >= m_controlStack.size(), "br_table's ", i, "th target ", target, " exceeds control stack size ", m_controlStack.size());
             targets.uncheckedAppend(&m_controlStack[m_controlStack.size() - 1 - target].controlData);
         }
 
-        uint32_t defaultTarget;
-        if (!parseVarUInt32(defaultTarget))
-            return false;
-
-        if (defaultTarget >= m_controlStack.size())
-            return false;
-
-        ExpressionType condition;
-        if (!popExpressionStack(condition))
-            return false;
-        
-        if (!m_context.addSwitch(condition, targets, m_controlStack[m_controlStack.size() - 1 - defaultTarget].controlData, m_expressionStack))
-            return false;
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(defaultTarget), "can't get default target for br_table");
+        WASM_PARSER_FAIL_IF(defaultTarget >= m_controlStack.size(), "br_table's default target ", defaultTarget, " exceeds control stack size ", m_controlStack.size());
+        WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "br_table condition");
+        WASM_TRY_ADD_TO_CONTEXT(addSwitch(condition, targets, m_controlStack[m_controlStack.size() - 1 - defaultTarget].controlData, m_expressionStack));
 
         m_unreachableBlocks = 1;
-        return true;
+        return { };
     }
 
     case OpType::Return: {
@@ -573,82 +481,78 @@ bool FunctionParser<Context>::parseExpression(OpType op)
         // FIXME: This is a little weird in that it will modify the expressionStack for the result of the block.
         // That's a little too effectful for me but I don't have a better API right now.
         // see: https://bugs.webkit.org/show_bug.cgi?id=164353
-        if (!m_context.endBlock(data, m_expressionStack))
-            return false;
+        WASM_TRY_ADD_TO_CONTEXT(endBlock(data, m_expressionStack));
         m_expressionStack.swap(data.enclosedExpressionStack);
-        return true;
+        return { };
     }
 
     case OpType::Unreachable: {
-        m_context.addUnreachable();
+        WASM_TRY_ADD_TO_CONTEXT(addUnreachable());
         m_unreachableBlocks = 1;
-        return true;
+        return { };
     }
 
     case OpType::Drop: {
-        if (!m_expressionStack.size()) {
-            setErrorMessage("Attempted to drop an expression from an empty stack.");
-            return false;
-        }
+        WASM_PARSER_FAIL_IF(!m_expressionStack.size(), "can't drop on empty stack");
         m_expressionStack.takeLast();
-        return true;
+        return { };
     }
 
     case OpType::Nop: {
-        return true;
+        return { };
     }
 
     case OpType::GrowMemory:
+        return fail("not yet implemented: grow_memory"); // FIXME: Not yet implemented.
+
     case OpType::CurrentMemory:
-        // FIXME: Not yet implemented.
-        return false;
+        return fail("not yet implemented: current_memory"); // FIXME: Not yet implemented.
+
     }
 
     ASSERT_NOT_REACHED();
 }
 
 template<typename Context>
-bool FunctionParser<Context>::parseUnreachableExpression(OpType op)
+auto FunctionParser<Context>::parseUnreachableExpression(OpType op) -> PartialResult
 {
     ASSERT(m_unreachableBlocks);
     switch (op) {
     case OpType::Else: {
         if (m_unreachableBlocks > 1)
-            return true;
+            return { };
 
         ControlEntry& data = m_controlStack.last();
         m_unreachableBlocks = 0;
-        if (!m_context.addElseToUnreachable(data.controlData))
-            return false;
+        WASM_TRY_ADD_TO_CONTEXT(addElseToUnreachable(data.controlData));
         m_expressionStack.shrink(0);
-        return true;
+        return { };
     }
 
     case OpType::End: {
         if (m_unreachableBlocks == 1) {
             ControlEntry data = m_controlStack.takeLast();
-            if (!m_context.addEndToUnreachable(data))
-                return false;
+            WASM_TRY_ADD_TO_CONTEXT(addEndToUnreachable(data));
             m_expressionStack.swap(data.enclosedExpressionStack);
         }
         m_unreachableBlocks--;
-        return true;
+        return { };
     }
 
     case OpType::Loop:
     case OpType::If:
     case OpType::Block: {
         m_unreachableBlocks++;
-        return true;
+        return { };
     }
 
     // two immediate cases
     case OpType::Br:
     case OpType::BrIf: {
         uint32_t unused;
-        if (!parseVarUInt32(unused))
-            return false;
-        return parseVarUInt32(unused);
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get br / br_if in unreachable context");
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get br / br_if in unreachable context");
+        return { };
     }
 
     // one immediate cases
@@ -659,25 +563,14 @@ bool FunctionParser<Context>::parseUnreachableExpression(OpType op)
     case OpType::SetLocal:
     case OpType::GetLocal: {
         uint32_t unused;
-        return parseVarUInt32(unused);
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get const / local in unreachable context");
+        return { };
     }
 
     default:
         break;
     }
-    return true;
-}
-
-template<typename Context>
-bool FunctionParser<Context>::popExpressionStack(ExpressionType& result)
-{
-    if (m_expressionStack.size()) {
-        result = m_expressionStack.takeLast();
-        return true;
-    }
-
-    setErrorMessage("Attempted to use a stack value when none existed");
-    return false;
+    return { };
 }
 
 } } // namespace JSC::Wasm
index 1d27dbf..f4f76b0 100644 (file)
 
 namespace JSC { namespace Wasm {
 
-static const bool verbose = false;
-
-bool ModuleParser::parse()
+auto ModuleParser::parse() -> Result
 {
-    m_module = std::make_unique<ModuleInformation>();
-
+    m_result.module = std::make_unique<ModuleInformation>();
     const size_t minSize = 8;
-    if (length() < minSize) {
-        m_errorMessage = "Module is " + String::number(length()) + " bytes, expected at least " + String::number(minSize) + " bytes";
-        return false;
-    }
-    if (!consumeCharacter(0) || !consumeString("asm")) {
-        m_errorMessage = "Modules doesn't start with '\\0asm'";
-        return false;
-    }
-
     uint32_t versionNumber;
-    if (!parseUInt32(versionNumber)) {
-        // FIXME improve error message https://bugs.webkit.org/show_bug.cgi?id=163919
-        m_errorMessage = "couldn't parse version number";
-        return false;
-    }
 
-    if (versionNumber != expectedVersionNumber) {
-        // FIXME improve error message https://bugs.webkit.org/show_bug.cgi?id=163919
-        m_errorMessage = "unexpected version number";
-        return false;
-    }
+    WASM_PARSER_FAIL_IF(length() < minSize, "expected a module of at least ", minSize, " bytes");
+    WASM_PARSER_FAIL_IF(!consumeCharacter(0) || !consumeString("asm"), "modules doesn't start with '\\0asm'");
+    WASM_PARSER_FAIL_IF(!parseUInt32(versionNumber), "can't parse version number");
+    WASM_PARSER_FAIL_IF(versionNumber != expectedVersionNumber, "unexpected version number ", versionNumber, " expected ", expectedVersionNumber);
 
-
-    if (verbose)
-        dataLogLn("Passed processing header.");
-
-    Sections::Section previousSection = Sections::Unknown;
+    Section previousSection = Section::Unknown;
     while (m_offset < length()) {
-        if (verbose)
-            dataLogLn("Starting to parse next section at offset: ", m_offset);
-
         uint8_t sectionByte;
-        if (!parseUInt7(sectionByte)) {
-            // FIXME improve error message https://bugs.webkit.org/show_bug.cgi?id=163919
-            m_errorMessage = "couldn't get section byte";
-            return false;
-        }
 
-        if (verbose)
-            dataLogLn("Section byte: ", sectionByte);
+        WASM_PARSER_FAIL_IF(!parseUInt7(sectionByte), "can't get section byte");
 
-        Sections::Section section = Sections::Unknown;
+        Section section = Section::Unknown;
         if (sectionByte) {
-            if (sectionByte < Sections::Unknown)
-                section = static_cast<Sections::Section>(sectionByte);
-        }
-
-        if (!Sections::validateOrder(previousSection, section)) {
-            // FIXME improve error message https://bugs.webkit.org/show_bug.cgi?id=163919
-            m_errorMessage = "invalid section order";
-            return false;
+            if (isValidSection(sectionByte))
+                section = static_cast<Section>(sectionByte);
         }
 
         uint32_t sectionLength;
-        if (!parseVarUInt32(sectionLength)) {
-            // FIXME improve error message https://bugs.webkit.org/show_bug.cgi?id=163919
-            m_errorMessage = "couldn't get section length";
-            return false;
-        }
-
-        if (sectionLength > length() - m_offset) {
-            // FIXME improve error message https://bugs.webkit.org/show_bug.cgi?id=163919
-            m_errorMessage = "section content would overflow Module's size";
-            return false;
-        }
+        WASM_PARSER_FAIL_IF(!validateOrder(previousSection, section), "invalid section order, ", previousSection, " followed by ", section);
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(sectionLength), "can't get ", section, " section's length");
+        WASM_PARSER_FAIL_IF(sectionLength > length() - m_offset, section, "section of size ", sectionLength, " would overflow Module's size");
 
         auto end = m_offset + sectionLength;
 
         switch (section) {
-        // FIXME improve error message in macro below https://bugs.webkit.org/show_bug.cgi?id=163919
-#define WASM_SECTION_PARSE(NAME, ID, DESCRIPTION) \
-        case Sections::NAME: { \
-            if (verbose) \
-                dataLogLn("Parsing " DESCRIPTION); \
-            if (!parse ## NAME()) { \
-                m_errorMessage = "couldn't parse section " #NAME ": " DESCRIPTION; \
-                return false; \
-            } \
-            break; \
+#define WASM_SECTION_PARSE(NAME, ID, DESCRIPTION)                   \
+        case Section::NAME: {                                       \
+            WASM_FAIL_IF_HELPER_FAILS(parse ## NAME());             \
+            break;                                                  \
         }
         FOR_EACH_WASM_SECTION(WASM_SECTION_PARSE)
 #undef WASM_SECTION_PARSE
 
-        case Sections::Unknown: {
-            if (verbose)
-                dataLogLn("Unknown section, skipping.");
+        case Section::Unknown: {
             // Ignore section's name LEB and bytes: they're already included in sectionLength.
             m_offset += sectionLength;
             break;
         }
         }
 
-        if (verbose)
-            dataLogLn("Finished parsing section.");
-
-        if (end != m_offset) {
-            // FIXME improve error message https://bugs.webkit.org/show_bug.cgi?id=163919
-            m_errorMessage = "parsing ended before the end of the section";
-            return false;
-        }
+        WASM_PARSER_FAIL_IF(end != m_offset, "parsing ended before the end of ", section, " section");
 
         previousSection = section;
     }
 
-    // TODO
-    m_failed = false;
-    return true;
+    return WTFMove(m_result);
 }
 
-bool ModuleParser::parseType()
+auto ModuleParser::parseType() -> PartialResult
 {
     uint32_t count;
-    if (!parseVarUInt32(count)
-        || count == std::numeric_limits<uint32_t>::max()
-        || !m_module->signatures.tryReserveCapacity(count))
-        return false;
-    if (verbose)
-        dataLogLn("  count: ", count);
+
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(count), "can't get Type section's count");
+    WASM_PARSER_FAIL_IF(count == std::numeric_limits<uint32_t>::max(), "Type section's count is too big ", count);
+    WASM_PARSER_FAIL_IF(!m_result.module->signatures.tryReserveCapacity(count), "can't allocate enough memory for Type section's ", count, " entries");
 
     for (uint32_t i = 0; i < count; ++i) {
         int8_t type;
-        if (!parseInt7(type))
-            return false;
-        if (type != Func)
-            return false;
-
-        if (verbose)
-            dataLogLn("Got function type.");
-
         uint32_t argumentCount;
         Vector<Type> argumentTypes;
-        if (!parseVarUInt32(argumentCount)
-            || argumentCount == std::numeric_limits<uint32_t>::max()
-            || !argumentTypes.tryReserveCapacity(argumentCount))
-            return false;
-        if (verbose)
-            dataLogLn("  argument count: ", argumentCount);
+
+        WASM_PARSER_FAIL_IF(!parseInt7(type), "can't get ", i, "th Type's type");
+        WASM_PARSER_FAIL_IF(type != Func, i, "th Type is non-Func ", type);
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(argumentCount), "can't get ", i, "th Type's argument count");
+        WASM_PARSER_FAIL_IF(argumentCount == std::numeric_limits<uint32_t>::max(), i, "th argument count is too big ", argumentCount);
+        WASM_PARSER_FAIL_IF(!argumentTypes.tryReserveCapacity(argumentCount), "can't allocate enough memory for Type section's ", i, "th ", argumentCount, " arguments");
 
         for (unsigned i = 0; i < argumentCount; ++i) {
             Type argumentType;
-            if (!parseResultType(argumentType))
-                return false;
+            WASM_PARSER_FAIL_IF(!parseResultType(argumentType), "can't get ", i, "th argument Type");
             argumentTypes.uncheckedAppend(argumentType);
         }
 
         uint8_t returnCount;
-        if (!parseVarUInt1(returnCount))
-            return false;
+        WASM_PARSER_FAIL_IF(!parseVarUInt1(returnCount), "can't get ", i, "th Type's return count");
         Type returnType;
 
-        if (verbose)
-            dataLogLn(returnCount);
-
         if (returnCount) {
             Type value;
-            if (!parseValueType(value))
-                return false;
+            WASM_PARSER_FAIL_IF(!parseValueType(value), "can't get ", i, "th Type's return value");
             returnType = static_cast<Type>(value);
         } else
             returnType = Type::Void;
 
-        m_module->signatures.uncheckedAppend({ returnType, WTFMove(argumentTypes) });
+        m_result.module->signatures.uncheckedAppend({ returnType, WTFMove(argumentTypes) });
     }
-    return true;
+    return { };
 }
 
-bool ModuleParser::parseImport()
+auto ModuleParser::parseImport() -> PartialResult
 {
     uint32_t importCount;
-    if (!parseVarUInt32(importCount)
-        || importCount == std::numeric_limits<uint32_t>::max()
-        || !m_module->globals.tryReserveCapacity(importCount) // FIXME this over-allocates when we fix the FIXMEs below.
-        || !m_module->imports.tryReserveCapacity(importCount) // FIXME this over-allocates when we fix the FIXMEs below.
-        || !m_module->importFunctions.tryReserveCapacity(importCount) // FIXME this over-allocates when we fix the FIXMEs below.
-        || !m_functionIndexSpace.tryReserveCapacity(importCount)) // FIXME this over-allocates when we fix the FIXMEs below. We'll allocate some more here when we know how many functions to expect.
-        return false;
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(importCount), "can't get Import section's count");
+    WASM_PARSER_FAIL_IF(importCount == std::numeric_limits<uint32_t>::max(), "Import section's count is too big ", importCount);
+    WASM_PARSER_FAIL_IF(!m_result.module->globals.tryReserveCapacity(importCount), "can't allocate enough memory for ", importCount, " globals"); // FIXME this over-allocates when we fix the FIXMEs below.
+    WASM_PARSER_FAIL_IF(!m_result.module->imports.tryReserveCapacity(importCount), "can't allocate enough memory for ", importCount, " imports"); // FIXME this over-allocates when we fix the FIXMEs below.
+    WASM_PARSER_FAIL_IF(!m_result.module->importFunctions.tryReserveCapacity(importCount), "can't allocate enough memory for ", importCount, " import functions"); // FIXME this over-allocates when we fix the FIXMEs below.
+    WASM_PARSER_FAIL_IF(!m_result.functionIndexSpace.tryReserveCapacity(importCount), "can't allocate enough memory for ", importCount, " functions in the index space"); // FIXME this over-allocates when we fix the FIXMEs below. We'll allocate some more here when we know how many functions to expect.
 
     for (uint32_t importNumber = 0; importNumber < importCount; ++importNumber) {
         Import imp;
@@ -229,179 +150,160 @@ bool ModuleParser::parseImport()
         uint32_t fieldLen;
         String moduleString;
         String fieldString;
-        if (!parseVarUInt32(moduleLen)
-            || !consumeUTF8String(moduleString, moduleLen))
-            return false;
+
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(moduleLen), "can't get ", importNumber, "th Import's module name length");
+        WASM_PARSER_FAIL_IF(!consumeUTF8String(moduleString, moduleLen), "can't get ", importNumber, "th Import's module name of length ", moduleLen);
         imp.module = Identifier::fromString(m_vm, moduleString);
-        if (!parseVarUInt32(fieldLen)
-            || !consumeUTF8String(fieldString, fieldLen))
-            return false;
+
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(fieldLen), "can't get ", importNumber, "th Import's field name length in module '", moduleString, "'");
+        WASM_PARSER_FAIL_IF(!consumeUTF8String(fieldString, fieldLen), "can't get ", importNumber, "th Import's field name of length ", moduleLen, " in module '", moduleString, "'");
         imp.field = Identifier::fromString(m_vm, fieldString);
-        if (!parseExternalKind(imp.kind))
-            return false;
+
+        WASM_PARSER_FAIL_IF(!parseExternalKind(imp.kind), "can't get ", importNumber, "th Import's kind in module '", moduleString, "' field '", fieldString, "'");
         switch (imp.kind) {
-        case External::Function: {
+        case ExternalKind::Function: {
             uint32_t functionSignatureIndex;
-            if (!parseVarUInt32(functionSignatureIndex)
-                || functionSignatureIndex >= m_module->signatures.size())
-                return false;
-            imp.kindIndex = m_module->importFunctions.size();
-            Signature* signature = &m_module->signatures[functionSignatureIndex];
-            m_module->importFunctions.uncheckedAppend(signature);
-            m_functionIndexSpace.uncheckedAppend(signature);
+            WASM_PARSER_FAIL_IF(!parseVarUInt32(functionSignatureIndex), "can't get ", importNumber, "th Import's function signature in module '", moduleString, "' field '", fieldString, "'");
+            WASM_PARSER_FAIL_IF(functionSignatureIndex >= m_result.module->signatures.size(), "invalid function signature for ", importNumber, "th Import, ", functionSignatureIndex, " is out of range of ", m_result.module->signatures.size(), " in module '", moduleString, "' field '", fieldString, "'");
+            imp.kindIndex = m_result.module->importFunctions.size();
+            Signature* signature = &m_result.module->signatures[functionSignatureIndex];
+            m_result.module->importFunctions.uncheckedAppend(signature);
+            m_result.functionIndexSpace.uncheckedAppend(signature);
             break;
         }
-        case External::Table: {
+        case ExternalKind::Table: {
             bool isImport = true;
-            if (!parseTableHelper(isImport))
-                return false;
+            PartialResult result = parseTableHelper(isImport);
+            if (UNLIKELY(!result))
+                return result.getUnexpected();
             break;
         }
-        case External::Memory: {
+        case ExternalKind::Memory: {
             bool isImport = true;
-            if (!parseMemoryHelper(isImport))
-                return false;
+            PartialResult result = parseMemoryHelper(isImport);
+            if (UNLIKELY(!result))
+                return result.getUnexpected();
             break;
         }
-        case External::Global: {
+        case ExternalKind::Global: {
             Global global;
-            if (!parseGlobalType(global))
-                return false;
-
-            if (global.mutability == Global::Mutable)
-                return false;
+            WASM_FAIL_IF_HELPER_FAILS(parseGlobalType(global));
+            WASM_PARSER_FAIL_IF(global.mutability == Global::Mutable, "Mutable Globals aren't supported");
 
-            imp.kindIndex = m_module->globals.size();
-            m_module->globals.uncheckedAppend(WTFMove(global));
+            imp.kindIndex = m_result.module->globals.size();
+            m_result.module->globals.uncheckedAppend(WTFMove(global));
             break;
         }
         }
 
-        m_module->imports.uncheckedAppend(imp);
+        m_result.module->imports.uncheckedAppend(imp);
     }
 
-    m_module->firstInternalGlobal = m_module->globals.size();
-    return true;
+    m_result.module->firstInternalGlobal = m_result.module->globals.size();
+    return { };
 }
 
-bool ModuleParser::parseFunction()
+auto ModuleParser::parseFunction() -> PartialResult
 {
     uint32_t count;
-    if (!parseVarUInt32(count)
-        || count == std::numeric_limits<uint32_t>::max()
-        || !m_module->internalFunctionSignatures.tryReserveCapacity(count)
-        || !m_functionLocationInBinary.tryReserveCapacity(count)
-        || !m_functionIndexSpace.tryReserveCapacity(m_functionIndexSpace.size() + count))
-        return false;
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(count), "can't get Function section's count");
+    WASM_PARSER_FAIL_IF(count == std::numeric_limits<uint32_t>::max(), "Function section's count is too big ", count);
+    WASM_PARSER_FAIL_IF(!m_result.module->internalFunctionSignatures.tryReserveCapacity(count), "can't allocate enough memory for ", count, " Function signatures");
+    WASM_PARSER_FAIL_IF(!m_result.functionLocationInBinary.tryReserveCapacity(count), "can't allocate enough memory for ", count, "Function locations");
+    WASM_PARSER_FAIL_IF(!m_result.functionIndexSpace.tryReserveCapacity(m_result.functionIndexSpace.size() + count), "can't allocate enough memory for ", count, " more functions in the function index space");
 
     for (uint32_t i = 0; i < count; ++i) {
         uint32_t typeNumber;
-        if (!parseVarUInt32(typeNumber)
-            || typeNumber >= m_module->signatures.size())
-            return false;
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(typeNumber), "can't get ", i, "th Function's type number");
+        WASM_PARSER_FAIL_IF(typeNumber >= m_result.module->signatures.size(), i, "th Function type number is invalid ", typeNumber);
 
-        Signature* signature = &m_module->signatures[typeNumber];
+        Signature* signature = &m_result.module->signatures[typeNumber];
         // The Code section fixes up start and end.
         size_t start = 0;
         size_t end = 0;
-        m_module->internalFunctionSignatures.uncheckedAppend(signature);
-        m_functionLocationInBinary.uncheckedAppend({ start, end });
-        m_functionIndexSpace.uncheckedAppend(signature);
+        m_result.module->internalFunctionSignatures.uncheckedAppend(signature);
+        m_result.functionLocationInBinary.uncheckedAppend({ start, end });
+        m_result.functionIndexSpace.uncheckedAppend(signature);
     }
 
-    return true;
+    return { };
 }
 
-bool ModuleParser::parseResizableLimits(uint32_t& initial, std::optional<uint32_t>& maximum)
+auto ModuleParser::parseResizableLimits(uint32_t& initial, std::optional<uint32_t>& maximum) -> PartialResult
 {
     ASSERT(!maximum);
 
     uint8_t flags;
-    if (!parseVarUInt1(flags))
-        return false;
-
-    if (!parseVarUInt32(initial))
-        return false;
+    WASM_PARSER_FAIL_IF(!parseVarUInt1(flags), "can't parse resizable limits flags");
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(initial), "can't parse resizable limits initial page count");
 
     if (flags) {
         uint32_t maximumInt;
-        if (!parseVarUInt32(maximumInt))
-            return false;
-
-        if (initial > maximumInt)
-            return false;
-
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(maximumInt), "can't parse resizable limits maximum page count");
+        WASM_PARSER_FAIL_IF(initial > maximumInt, "resizable limits has a initial page count of ", initial, " which is greater than its maximum ", maximumInt);
         maximum = maximumInt;
     }
 
-    return true;
+    return { };
 }
 
-bool ModuleParser::parseTableHelper(bool isImport)
+auto ModuleParser::parseTableHelper(bool isImport) -> PartialResult
 {
-    // We're only allowed a total of one Table import or definition.
-    if (m_hasTable)
-        return false;
+    WASM_PARSER_FAIL_IF(m_hasTable, "Table section cannot exist if an Import has a table");
 
     m_hasTable = true;
 
     int8_t type;
-    if (!parseInt7(type))
-        return false;
-    if (type != Wasm::Anyfunc)
-        return false;
+    WASM_PARSER_FAIL_IF(!parseInt7(type), "can't parse Table type");
+    WASM_PARSER_FAIL_IF(type != Wasm::Anyfunc, "Table type should be anyfunc, got ", type);
 
     uint32_t initial;
     std::optional<uint32_t> maximum;
-    if (!parseResizableLimits(initial, maximum))
-        return false;
-
-    if (!JSWebAssemblyTable::isValidSize(initial))
-        return false;
+    PartialResult limits = parseResizableLimits(initial, maximum);
+    if (UNLIKELY(!limits))
+        return limits.getUnexpected();
+    WASM_PARSER_FAIL_IF(!JSWebAssemblyTable::isValidSize(initial), "Table's initial page count of ", initial, " is invalid");
 
     ASSERT(!maximum || *maximum >= initial);
 
-    m_module->tableInformation = TableInformation(initial, maximum, isImport);
+    m_result.module->tableInformation = TableInformation(initial, maximum, isImport);
 
-    return true;
+    return { };
 }
 
-bool ModuleParser::parseTable()
+auto ModuleParser::parseTable() -> PartialResult
 {
     uint32_t count;
-    if (!parseVarUInt32(count))
-        return false;
-
-    // We only allow one table for now.
-    if (count != 1)
-        return false;
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(count), "can't get Table's count");
+    WASM_PARSER_FAIL_IF(count != 1, "Table count of ", count, " is invalid, only 1 is allowed for now");
 
     bool isImport = false;
-    return parseTableHelper(isImport);
+    PartialResult result = parseTableHelper(isImport);
+    if (UNLIKELY(!result))
+        return result.getUnexpected();
+
+    return { };
 }
 
-bool ModuleParser::parseMemoryHelper(bool isImport)
+auto ModuleParser::parseMemoryHelper(bool isImport) -> PartialResult
 {
-    // We don't allow redeclaring memory. Either via import or definition.
-    if (m_module->memory)
-        return false;
+    WASM_PARSER_FAIL_IF(m_result.module->memory, "Memory section cannot exist if an Import has a memory");
 
     PageCount initialPageCount;
     PageCount maximumPageCount;
     {
         uint32_t initial;
         std::optional<uint32_t> maximum;
-        if (!parseResizableLimits(initial, maximum))
-            return false;
+        PartialResult limits = parseResizableLimits(initial, maximum);
+        if (UNLIKELY(!limits))
+            return limits.getUnexpected();
         ASSERT(!maximum || *maximum >= initial);
-        if (!PageCount::isValid(initial))
-            return false;
+        WASM_PARSER_FAIL_IF(!PageCount::isValid(initial), "Memory's initial page count of ", initial, " is invalid");
 
         initialPageCount = PageCount(initial);
 
         if (maximum) {
-            if (!PageCount::isValid(*maximum))
-                return false;
+            WASM_PARSER_FAIL_IF(!PageCount::isValid(*maximum), "Memory's maximum page count of ", *maximum, " is invalid");
             maximumPageCount = PageCount(*maximum);
         }
     }
@@ -409,43 +311,36 @@ bool ModuleParser::parseMemoryHelper(bool isImport)
     ASSERT(!maximumPageCount || maximumPageCount >= initialPageCount);
 
     Vector<unsigned> pinnedSizes = { 0 };
-    m_module->memory = MemoryInformation(initialPageCount, maximumPageCount, pinnedSizes, isImport);
-    return true;
+    m_result.module->memory = MemoryInformation(initialPageCount, maximumPageCount, pinnedSizes, isImport);
+    return { };
 }
 
-bool ModuleParser::parseMemory()
+auto ModuleParser::parseMemory() -> PartialResult
 {
     uint8_t count;
-    if (!parseVarUInt1(count))
-        return false;
+    WASM_PARSER_FAIL_IF(!parseVarUInt1(count), "can't parse Memory section's count");
 
     if (!count)
-        return true;
+        return { };
 
-    // We only allow one memory for now.
-    if (count != 1)
-        return false;
+    WASM_PARSER_FAIL_IF(count != 1, "Memory section has more than one memory, WebAssembly currently only allows zero or one");
 
     bool isImport = false;
     return parseMemoryHelper(isImport);
 }
 
-bool ModuleParser::parseGlobal()
+auto ModuleParser::parseGlobal() -> PartialResult
 {
     uint32_t globalCount;
-    if (!parseVarUInt32(globalCount))
-        return false;
-    if (!m_module->globals.tryReserveCapacity(globalCount + m_module->firstInternalGlobal))
-        return false;
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(globalCount), "can't get Global section's count");
+    WASM_PARSER_FAIL_IF(!m_result.module->globals.tryReserveCapacity(globalCount + m_result.module->firstInternalGlobal), "can't allocate memory for ", globalCount + m_result.module->firstInternalGlobal, " globals");
 
     for (uint32_t globalIndex = 0; globalIndex < globalCount; ++globalIndex) {
         Global global;
-        if (!parseGlobalType(global))
-            return false;
-
         uint8_t initOpcode;
-        if (!parseInitExpr(initOpcode, global.initialBitsOrImportNumber))
-            return false;
+
+        WASM_FAIL_IF_HELPER_FAILS(parseGlobalType(global));
+        WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, global.initialBitsOrImportNumber));
 
         global.initializationType = Global::FromExpression;
         Type typeForInitOpcode;
@@ -463,130 +358,103 @@ bool ModuleParser::parseGlobal()
             typeForInitOpcode = F64;
             break;
         case GetGlobal:
-            if (global.initialBitsOrImportNumber >= m_module->firstInternalGlobal)
-                return false;
-            typeForInitOpcode = m_module->globals[global.initialBitsOrImportNumber].type;
+            WASM_PARSER_FAIL_IF(global.initialBitsOrImportNumber >= m_result.module->firstInternalGlobal, globalIndex, "th Global uses get_global ", global.initialBitsOrImportNumber, " which exceeds the first internal global ", m_result.module->firstInternalGlobal);
+            typeForInitOpcode = m_result.module->globals[global.initialBitsOrImportNumber].type;
             global.initializationType = Global::FromGlobalImport;
             break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
         }
 
-        if (typeForInitOpcode != global.type)
-            return false;
+        WASM_PARSER_FAIL_IF(typeForInitOpcode != global.type, "Global init_expr opcode of type ", typeForInitOpcode, " doesn't match global's type ", global.type);
 
-        m_module->globals.uncheckedAppend(WTFMove(global));
+        m_result.module->globals.uncheckedAppend(WTFMove(global));
     }
 
-    return true;
+    return { };
 }
 
-bool ModuleParser::parseExport()
+auto ModuleParser::parseExport() -> PartialResult
 {
     uint32_t exportCount;
-    if (!parseVarUInt32(exportCount)
-        || exportCount == std::numeric_limits<uint32_t>::max()
-        || !m_module->exports.tryReserveCapacity(exportCount))
-        return false;
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(exportCount), "can't get Export section's count");
+    WASM_PARSER_FAIL_IF(exportCount == std::numeric_limits<uint32_t>::max(), "Export section's count is too big ", exportCount);
+    WASM_PARSER_FAIL_IF(!m_result.module->exports.tryReserveCapacity(exportCount), "can't allocate enough memory for ", exportCount, " exports");
 
     for (uint32_t exportNumber = 0; exportNumber < exportCount; ++exportNumber) {
         Export exp;
         uint32_t fieldLen;
         String fieldString;
-        if (!parseVarUInt32(fieldLen)
-            || !consumeUTF8String(fieldString, fieldLen))
-            return false;
-        exp.field = Identifier::fromString(m_vm, fieldString);
-
-        if (!parseExternalKind(exp.kind))
-            return false;
 
-        if (!parseVarUInt32(exp.kindIndex))
-            return false;
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(fieldLen), "can't get ", exportNumber, "th Export's field name length");
+        WASM_PARSER_FAIL_IF(!consumeUTF8String(fieldString, fieldLen), "can't get ", exportNumber, "th Export's field name of length ", fieldLen);
+        exp.field = Identifier::fromString(m_vm, fieldString);
 
+        WASM_PARSER_FAIL_IF(!parseExternalKind(exp.kind), "can't get ", exportNumber, "th Export's kind, named '", fieldString, "'");
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(exp.kindIndex), "can't get ", exportNumber, "th Export's kind index, named '", fieldString, "'");
         switch (exp.kind) {
-        case External::Function: {
-            if (exp.kindIndex >= m_functionIndexSpace.size())
-                return false;
+        case ExternalKind::Function: {
+            WASM_PARSER_FAIL_IF(exp.kindIndex >= m_result.functionIndexSpace.size(), exportNumber, "th Export has invalid function number ", exp.kindIndex, " it exceeds the function index space ", m_result.functionIndexSpace.size(), ", named '", fieldString, "'");
             break;
         }
-        case External::Table: {
-            if (!m_hasTable)
-                return false;
-            if (exp.kindIndex != 0)
-                return false;
+        case ExternalKind::Table: {
+            WASM_PARSER_FAIL_IF(!m_hasTable, "can't export a non-existent Table");
+            WASM_PARSER_FAIL_IF(exp.kindIndex, "can't export Table ", exp.kindIndex, " only zero-index Table is currently supported");
             break;
         }
-        case External::Memory: {
-            if (!m_module->memory)
-                return false;
-            if (exp.kindIndex != 0)
-                return false;
+        case ExternalKind::Memory: {
+            WASM_PARSER_FAIL_IF(!m_result.module->memory, "can't export a non-existent Memory");
+            WASM_PARSER_FAIL_IF(exp.kindIndex, "can't export Memory ", exp.kindIndex, " only one Table is currently supported");
             break;
         }
-        case External::Global: {
-            if (exp.kindIndex >= m_module->globals.size())
-                return false;
-
-            if (m_module->globals[exp.kindIndex].mutability != Global::Immutable)
-                return false;
+        case ExternalKind::Global: {
+            WASM_PARSER_FAIL_IF(exp.kindIndex >= m_result.module->globals.size(), exportNumber, "th Export has invalid global number ", exp.kindIndex, " it exceeds the globals count ", m_result.module->globals.size(), ", named '", fieldString, "'");
+            WASM_PARSER_FAIL_IF(m_result.module->globals[exp.kindIndex].mutability != Global::Immutable, exportNumber, "th Export isn't immutable, named '", fieldString, "'");
             break;
         }
         }
 
-        m_module->exports.uncheckedAppend(exp);
+        m_result.module->exports.uncheckedAppend(exp);
     }
 
-    return true;
+    return { };
 }
 
-bool ModuleParser::parseStart()
+auto ModuleParser::parseStart() -> PartialResult
 {
     uint32_t startFunctionIndex;
-    if (!parseVarUInt32(startFunctionIndex)
-        || startFunctionIndex >= m_functionIndexSpace.size())
-        return false;
-    Signature* signature = m_functionIndexSpace[startFunctionIndex].signature;
-    if (signature->arguments.size() != 0
-        || signature->returnType != Void)
-        return false;
-    m_module->startFunctionIndexSpace = startFunctionIndex;
-    return true;
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(startFunctionIndex), "can't get Start index");
+    WASM_PARSER_FAIL_IF(startFunctionIndex >= m_result.functionIndexSpace.size(), "Start index ", startFunctionIndex, " exceeds function index space ", m_result.functionIndexSpace.size());
+    Signature* signature = m_result.functionIndexSpace[startFunctionIndex].signature;
+    WASM_PARSER_FAIL_IF(!signature->arguments.isEmpty(), "Start function can't have arguments");
+    WASM_PARSER_FAIL_IF(signature->returnType != Void, "Start function can't return a value");
+    m_result.module->startFunctionIndexSpace = startFunctionIndex;
+    return { };
 }
 
-bool ModuleParser::parseElement()
+auto ModuleParser::parseElement() -> PartialResult
 {
-    if (!m_hasTable)
-        return false;
+    WASM_PARSER_FAIL_IF(!m_hasTable, "Element section expects a Table to be present");
 
     uint32_t elementCount;
-    if (!parseVarUInt32(elementCount))
-        return false;
-    if (!m_module->elements.tryReserveCapacity(elementCount))
-        return false;
-
-    for (unsigned i = 0; i < elementCount; ++i) {
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(elementCount), "can't get Element section's count");
+    WASM_PARSER_FAIL_IF(elementCount == std::numeric_limits<uint32_t>::max(), "Element section's count is too big ", elementCount);
+    WASM_PARSER_FAIL_IF(!m_result.module->elements.tryReserveCapacity(elementCount), "can't allocate memory for ", elementCount, " Elements");
+    for (unsigned elementNum = 0; elementNum < elementCount; ++elementNum) {
         uint32_t tableIndex;
-        if (!parseVarUInt32(tableIndex))
-            return false;
-        // We only support one table for now.
-        if (tableIndex != 0)
-            return false;
-
         uint64_t offset;
         uint8_t initOpcode;
-        if (!parseInitExpr(initOpcode, offset))
-            return false;
-
-        if (initOpcode != OpType::I32Const)
-            return false;
-
         uint32_t indexCount;
-        if (!parseVarUInt32(indexCount))
-            return false;
 
-        ASSERT(!!m_module->tableInformation);
-        if (std::optional<uint32_t> maximum = m_module->tableInformation.maximum()) {
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't get ", elementNum, "th Element table index");
+        WASM_PARSER_FAIL_IF(tableIndex, "Element section can only have one Table for now");
+        WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, offset));
+        WASM_PARSER_FAIL_IF(initOpcode != OpType::I32Const, "Element section doesn't support non-i32 init_expr opcode for now, got ", initOpcode);
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(indexCount), "can't get ", elementNum, "th index count for Element section");
+        WASM_PARSER_FAIL_IF(indexCount == std::numeric_limits<uint32_t>::max(), "Element section's ", elementNum, "th index count is too big ", indexCount);
+
+        ASSERT(!!m_result.module->tableInformation);
+        if (std::optional<uint32_t> maximum = m_result.module->tableInformation.maximum()) {
             // FIXME: should indexCount being zero be a validation error?
             // https://bugs.webkit.org/show_bug.cgi?id=165826
             if (indexCount) {
@@ -594,185 +462,148 @@ bool ModuleParser::parseElement()
                 // Should they be though?
                 // https://bugs.webkit.org/show_bug.cgi?id=165827
                 uint64_t lastWrittenIndex = static_cast<uint64_t>(indexCount) + static_cast<uint64_t>(offset) - 1;
-                if (lastWrittenIndex >= static_cast<uint64_t>(*maximum))
-                    return false;
+                WASM_PARSER_FAIL_IF(lastWrittenIndex >= static_cast<uint64_t>(*maximum), "Element section's ", elementNum, "th element writes to index ", lastWrittenIndex, " which exceeds the maximum ", *maximum);
             }
         }
 
         Element element;
-        if (!element.functionIndices.tryReserveCapacity(indexCount))
-            return false;
+        WASM_PARSER_FAIL_IF(!element.functionIndices.tryReserveCapacity(indexCount), "can't allocate memory for ", indexCount, " Element indices");
 
         element.offset = offset;
 
-        for (unsigned i = 0; i < indexCount; ++i) {
+        for (unsigned index = 0; index < indexCount; ++index) {
             uint32_t functionIndex;
-            if (!parseVarUInt32(functionIndex))
-                return false;
-
-            if (functionIndex >= m_functionIndexSpace.size())
-                return false;
+            WASM_PARSER_FAIL_IF(!parseVarUInt32(functionIndex), "can't get Element section's ", elementNum, "th element's ", index, "th index");
+            WASM_PARSER_FAIL_IF(functionIndex >= m_result.functionIndexSpace.size(), "Element section's ", elementNum, "th element's ", index, "th index is ", functionIndex, " which exceeds the function index space size of ", m_result.functionIndexSpace.size());
 
             element.functionIndices.uncheckedAppend(functionIndex);
         }
 
-        m_module->elements.uncheckedAppend(WTFMove(element));
+        m_result.module->elements.uncheckedAppend(WTFMove(element));
     }
 
-    return true;
+    return { };
 }
 
-bool ModuleParser::parseCode()
+auto ModuleParser::parseCode() -> PartialResult
 {
     uint32_t count;
-    if (!parseVarUInt32(count)
-        || count == std::numeric_limits<uint32_t>::max()
-        || count != m_functionLocationInBinary.size())
-        return false;
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(count), "can't get Code section's count");
+    WASM_PARSER_FAIL_IF(count == std::numeric_limits<uint32_t>::max(), "Code section's count is too big ", count);
+    WASM_PARSER_FAIL_IF(count != m_result.functionLocationInBinary.size(), "Code section count ", count, " exceeds the declared number of functions ", m_result.functionLocationInBinary.size());
 
     for (uint32_t i = 0; i < count; ++i) {
         uint32_t functionSize;
-        if (!parseVarUInt32(functionSize)
-            || functionSize > length()
-            || functionSize > length() - m_offset)
-            return false;
-
-        m_functionLocationInBinary[i].start = m_offset;
-        m_functionLocationInBinary[i].end = m_offset + functionSize;
-        m_offset = m_functionLocationInBinary[i].end;
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(functionSize), "can't get ", i, "th Code function's size");
+        WASM_PARSER_FAIL_IF(functionSize > length(), "Code function's size ", functionSize, " exceeds the module's size ", length());
+        WASM_PARSER_FAIL_IF(functionSize > length() - m_offset, "Code function's size ", functionSize, " exceeds the module's remaining size", length() - m_offset);
+
+        m_result.functionLocationInBinary[i].start = m_offset;
+        m_result.functionLocationInBinary[i].end = m_offset + functionSize;
+        m_offset = m_result.functionLocationInBinary[i].end;
     }
 
-    return true;
+    return { };
 }
 
-bool ModuleParser::parseInitExpr(uint8_t& opcode, uint64_t& bitsOrImportNumber)
+auto ModuleParser::parseInitExpr(uint8_t& opcode, uint64_t& bitsOrImportNumber) -> PartialResult
 {
-    if (!parseUInt8(opcode))
-        return false;
+    WASM_PARSER_FAIL_IF(!parseUInt8(opcode), "can't get init_expr's opcode");
 
     switch (opcode) {
     case I32Const: {
         int32_t constant;
-        if (!parseVarInt32(constant))
-            return false;
+        WASM_PARSER_FAIL_IF(!parseVarInt32(constant), "can't get constant value for init_expr's i32.const");
         bitsOrImportNumber = static_cast<uint64_t>(constant);
         break;
     }
 
     case I64Const: {
         int64_t constant;
-        if (!parseVarInt64(constant))
-            return false;
+        WASM_PARSER_FAIL_IF(!parseVarInt64(constant), "can't get constant value for init_expr's i64.const");
         bitsOrImportNumber = constant;
         break;
     }
 
     case F32Const: {
         uint32_t constant;
-        if (!parseUInt32(constant))
-            return false;
+        WASM_PARSER_FAIL_IF(!parseUInt32(constant), "can't get constant value for init_expr's f32.const");
         bitsOrImportNumber = constant;
         break;
     }
 
     case F64Const: {
         uint64_t constant;
-        if (!parseUInt64(constant))
-            return false;
+        WASM_PARSER_FAIL_IF(!parseUInt64(constant), "can't get constant value for init_expr's f64.const");
         bitsOrImportNumber = constant;
         break;
     }
 
     case GetGlobal: {
         uint32_t index;
-        if (!parseVarUInt32(index))
-            return false;
-
-        if (index >= m_module->imports.size())
-            return false;
-        const Import& import = m_module->imports[index];
-        if (m_module->imports[index].kind != External::Global
-            || import.kindIndex >= m_module->firstInternalGlobal)
-            return false;
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get get_global's index");
+        WASM_PARSER_FAIL_IF(index >= m_result.module->imports.size(), "get_global's index ", index, " exceeds the number of imports ", m_result.module->imports.size());
+        const Import& import = m_result.module->imports[index];
+        WASM_PARSER_FAIL_IF(m_result.module->imports[index].kind != ExternalKind::Global, "get_global's import kind is ", m_result.module->imports[index].kind, " should be global");
+        WASM_PARSER_FAIL_IF(import.kindIndex >= m_result.module->firstInternalGlobal, "get_global import kind index ", import.kindIndex, " exceeds the first internal global ", m_result.module->firstInternalGlobal);
 
-        ASSERT(m_module->globals[import.kindIndex].mutability == Global::Immutable);
+        ASSERT(m_result.module->globals[import.kindIndex].mutability == Global::Immutable);
 
         bitsOrImportNumber = index;
         break;
     }
 
     default:
-        return false;
+        WASM_PARSER_FAIL_IF(false, "unknown init_expr opcode ", opcode);
     }
 
     uint8_t endOpcode;
-    if (!parseUInt8(endOpcode) || endOpcode != OpType::End)
-        return false;
+    WASM_PARSER_FAIL_IF(!parseUInt8(endOpcode), "can't get init_expr's end opcode");
+    WASM_PARSER_FAIL_IF(endOpcode != OpType::End, "init_expr should end with end, ended with ", endOpcode);
 
-    return true;
+    return { };
 }
 
-bool ModuleParser::parseGlobalType(Global& global)
+auto ModuleParser::parseGlobalType(Global& global) -> PartialResult
 {
     uint8_t mutability;
-    if (!parseValueType(global.type) || !parseVarUInt1(mutability))
-        return false;
+    WASM_PARSER_FAIL_IF(!parseValueType(global.type), "can't get Global's value type");
+    WASM_PARSER_FAIL_IF(!parseVarUInt1(mutability), "can't get Global type's mutability");
     global.mutability = static_cast<Global::Mutability>(mutability);
-    return true;
+    return { };
 }
 
-bool ModuleParser::parseData()
+auto ModuleParser::parseData() -> PartialResult
 {
     uint32_t segmentCount;
-    if (!m_module->memory)
-        return false;
-    if (!parseVarUInt32(segmentCount)
-        || segmentCount == std::numeric_limits<uint32_t>::max()
-        || !m_module->data.tryReserveCapacity(segmentCount))
-        return false;
-    if (verbose)
-        dataLogLn("  segments: ", segmentCount);
+    WASM_PARSER_FAIL_IF(!m_result.module->memory, "Data section cannot exist without a Memory section or Import");
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(segmentCount), "can't get Data section's count");
+    WASM_PARSER_FAIL_IF(segmentCount == std::numeric_limits<uint32_t>::max(), "Data section's count is too big ", segmentCount);
+    WASM_PARSER_FAIL_IF(!m_result.module->data.tryReserveCapacity(segmentCount), "can't allocate enough memory for Data section's ", segmentCount, " segments");
 
     for (uint32_t segmentNumber = 0; segmentNumber < segmentCount; ++segmentNumber) {
-        if (verbose)
-            dataLogLn("  segment #", segmentNumber);
         uint32_t index;
         uint64_t offset;
         uint8_t initOpcode;
         uint32_t dataByteLength;
-        if (!parseVarUInt32(index)
-            || index)
-            return false;
-
-        if (!parseInitExpr(initOpcode, offset))
-            return false;
-
-        if (initOpcode != OpType::I32Const)
-            return false;
-
-        if (verbose)
-            dataLogLn("    offset: ", offset);
 
-        if (!parseVarUInt32(dataByteLength)
-            || dataByteLength == std::numeric_limits<uint32_t>::max())
-            return false;
-        if (verbose)
-            dataLogLn("    data bytes: ", dataByteLength);
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get ", segmentNumber, "th Data segment's index");
+        WASM_PARSER_FAIL_IF(index, segmentNumber, "th Data segment has non-zero index ", index);
+        WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, offset));
+        WASM_PARSER_FAIL_IF(initOpcode != OpType::I32Const, segmentNumber, "th Data segment has opcode ", initOpcode, " expected i32.const");
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(dataByteLength), "can't get ", segmentNumber, "th Data segment's data byte length");
+        WASM_PARSER_FAIL_IF(dataByteLength == std::numeric_limits<uint32_t>::max(), segmentNumber, "th Data segment's data byte length is too big ", dataByteLength);
 
         Segment* segment = Segment::make(offset, dataByteLength);
-        if (!segment)
-            return false;
-        m_module->data.uncheckedAppend(Segment::makePtr(segment));
+        WASM_PARSER_FAIL_IF(!segment, "can't allocate enough memory for ", segmentNumber, "th Data segment of size ", dataByteLength);
+        m_result.module->data.uncheckedAppend(Segment::makePtr(segment));
         for (uint32_t dataByte = 0; dataByte < dataByteLength; ++dataByte) {
             uint8_t byte;
-            if (!parseUInt8(byte))
-                return false;
+            WASM_PARSER_FAIL_IF(!parseUInt8(byte), "can't get ", dataByte, "th data byte from ", segmentNumber, "th Data segment");
             segment->byte(dataByte) = byte;
-            if (verbose)
-                dataLogLn("    [", dataByte, "] = ", segment->byte(dataByte));
         }
     }
-    return true;
+    return { };
 }
 
 } } // namespace JSC::Wasm
index b718743..6a2436e 100644 (file)
 
 namespace JSC { namespace Wasm {
 
-class ModuleParser : public Parser {
+struct ModuleParserResult {
+    std::unique_ptr<ModuleInformation> module;
+    FunctionIndexSpace functionIndexSpace;
+    Vector<FunctionLocationInBinary> functionLocationInBinary;
+};
+
+class ModuleParser : public Parser<ModuleParserResult> {
 public:
 
     ModuleParser(VM* vm, const uint8_t* sourceBuffer, size_t sourceLength)
@@ -47,51 +53,23 @@ public:
     {
     }
 
-    bool WARN_UNUSED_RETURN parse();
-    bool WARN_UNUSED_RETURN failed() const { return m_failed; }
-    const String& errorMessage() const
-    {
-        RELEASE_ASSERT(failed());
-        return m_errorMessage;
-    }
-
-    std::unique_ptr<ModuleInformation>& moduleInformation()
-    {
-        RELEASE_ASSERT(!failed());
-        return m_module;
-    }
-
-    FunctionIndexSpace& functionIndexSpace()
-    {
-        RELEASE_ASSERT(!failed());
-        return m_functionIndexSpace;
-    }
-
-    Vector<FunctionLocationInBinary>& functionLocationInBinary()
-    {
-        RELEASE_ASSERT(!failed());
-        return m_functionLocationInBinary;
-    }
+    Result WARN_UNUSED_RETURN parse();
 
 private:
-    bool parseGlobalType(Global&);
 
-#define WASM_SECTION_DECLARE_PARSER(NAME, ID, DESCRIPTION) bool WARN_UNUSED_RETURN parse ## NAME();
+#define WASM_SECTION_DECLARE_PARSER(NAME, ID, DESCRIPTION) PartialResult WARN_UNUSED_RETURN parse ## NAME();
     FOR_EACH_WASM_SECTION(WASM_SECTION_DECLARE_PARSER)
 #undef WASM_SECTION_DECLARE_PARSER
 
-    bool WARN_UNUSED_RETURN parseMemoryHelper(bool isImport);
-    bool WARN_UNUSED_RETURN parseTableHelper(bool isImport);
-    bool WARN_UNUSED_RETURN parseResizableLimits(uint32_t& initial, std::optional<uint32_t>& maximum);
-    bool WARN_UNUSED_RETURN parseInitExpr(uint8_t&, uint64_t&);
+    PartialResult WARN_UNUSED_RETURN parseGlobalType(Global&);
+    PartialResult WARN_UNUSED_RETURN parseMemoryHelper(bool isImport);
+    PartialResult WARN_UNUSED_RETURN parseTableHelper(bool isImport);
+    PartialResult WARN_UNUSED_RETURN parseResizableLimits(uint32_t& initial, std::optional<uint32_t>& maximum);
+    PartialResult WARN_UNUSED_RETURN parseInitExpr(uint8_t&, uint64_t&);
 
     VM* m_vm;
-    std::unique_ptr<ModuleInformation> m_module;
-    FunctionIndexSpace m_functionIndexSpace;
-    Vector<FunctionLocationInBinary> m_functionLocationInBinary;
-    bool m_failed { true };
+    ModuleParserResult m_result;
     bool m_hasTable { false };
-    String m_errorMessage;
 };
 
 } } // namespace JSC::Wasm
index 08fabbf..d6ae8c5 100644 (file)
 #include "WasmFormat.h"
 #include "WasmOps.h"
 #include "WasmSections.h"
+#include <type_traits>
+#include <wtf/Expected.h>
 #include <wtf/LEBDecoder.h>
 #include <wtf/StdLibExtras.h>
 #include <wtf/text/WTFString.h>
 
 namespace JSC { namespace Wasm {
 
+namespace FailureHelper {
+// FIXME We should move this to makeString. It's in its own namespace to enable C++ Argument Dependent Lookup à la std::swap: user code can deblare its own "boxFailure" and the fail() helper will find it.
+static inline auto makeString(const char *failure) { return ASCIILiteral(failure); }
+template <typename Int, typename = typename std::enable_if<std::is_integral<Int>::value>::type>
+static inline auto makeString(Int failure) { return String::number(failure); }
+}
+
+template<typename SuccessType>
 class Parser {
+public:
+    typedef String ErrorType;
+    typedef UnexpectedType<ErrorType> UnexpectedResult;
+    typedef Expected<void, ErrorType> PartialResult;
+    typedef Expected<SuccessType, ErrorType> Result;
+
 protected:
     Parser(const uint8_t*, size_t);
 
@@ -60,25 +76,44 @@ protected:
 
     bool WARN_UNUSED_RETURN parseResultType(Type&);
     bool WARN_UNUSED_RETURN parseValueType(Type&);
-    bool WARN_UNUSED_RETURN parseExternalKind(External::Kind&);
+    bool WARN_UNUSED_RETURN parseExternalKind(ExternalKind&);
 
     const uint8_t* source() const { return m_source; }
     size_t length() const { return m_sourceLength; }
 
     size_t m_offset = 0;
 
+    template <typename ...Args>
+    NEVER_INLINE UnexpectedResult WARN_UNUSED_RETURN fail(Args... args) const
+    {
+        using namespace FailureHelper; // See ADL comment in namespace above.
+        return UnexpectedResult(makeString(ASCIILiteral("WebAssembly.Module doesn't parse at byte "), String::number(m_offset), ASCIILiteral(" / "), String::number(m_sourceLength), ASCIILiteral(": "), makeString(args)...));
+    }
+#define WASM_PARSER_FAIL_IF(condition, ...) do { \
+    if (UNLIKELY(condition))                     \
+        return fail(__VA_ARGS__);                \
+    } while (0)
+
+#define WASM_FAIL_IF_HELPER_FAILS(helper) do {   \
+        auto helperResult = helper;              \
+        if (UNLIKELY(!helperResult))             \
+            return helperResult.getUnexpected(); \
+    } while (0)
+
 private:
     const uint8_t* m_source;
     size_t m_sourceLength;
 };
 
-ALWAYS_INLINE Parser::Parser(const uint8_t* sourceBuffer, size_t sourceLength)
+template<typename SuccessType>
+ALWAYS_INLINE Parser<SuccessType>::Parser(const uint8_t* sourceBuffer, size_t sourceLength)
     : m_source(sourceBuffer)
     , m_sourceLength(sourceLength)
 {
 }
 
-ALWAYS_INLINE bool Parser::consumeCharacter(char c)
+template<typename SuccessType>
+ALWAYS_INLINE bool Parser<SuccessType>::consumeCharacter(char c)
 {
     if (m_offset >= length())
         return false;
@@ -89,7 +124,8 @@ ALWAYS_INLINE bool Parser::consumeCharacter(char c)
     return false;
 }
 
-ALWAYS_INLINE bool Parser::consumeString(const char* str)
+template<typename SuccessType>
+ALWAYS_INLINE bool Parser<SuccessType>::consumeString(const char* str)
 {
     unsigned start = m_offset;
     if (m_offset >= length())
@@ -103,7 +139,8 @@ ALWAYS_INLINE bool Parser::consumeString(const char* str)
     return true;
 }
 
-ALWAYS_INLINE bool Parser::consumeUTF8String(String& result, size_t stringLength)
+template<typename SuccessType>
+ALWAYS_INLINE bool Parser<SuccessType>::consumeUTF8String(String& result, size_t stringLength)
 {
     if (stringLength == 0) {
         result = emptyString();
@@ -118,27 +155,32 @@ ALWAYS_INLINE bool Parser::consumeUTF8String(String& result, size_t stringLength
     return true;
 }
 
-ALWAYS_INLINE bool Parser::parseVarUInt32(uint32_t& result)
+template<typename SuccessType>
+ALWAYS_INLINE bool Parser<SuccessType>::parseVarUInt32(uint32_t& result)
 {
     return WTF::LEBDecoder::decodeUInt32(m_source, m_sourceLength, m_offset, result);
 }
 
-ALWAYS_INLINE bool Parser::parseVarUInt64(uint64_t& result)
+template<typename SuccessType>
+ALWAYS_INLINE bool Parser<SuccessType>::parseVarUInt64(uint64_t& result)
 {
     return WTF::LEBDecoder::decodeUInt64(m_source, m_sourceLength, m_offset, result);
 }
 
-ALWAYS_INLINE bool Parser::parseVarInt32(int32_t& result)
+template<typename SuccessType>
+ALWAYS_INLINE bool Parser<SuccessType>::parseVarInt32(int32_t& result)
 {
     return WTF::LEBDecoder::decodeInt32(m_source, m_sourceLength, m_offset, result);
 }
 
-ALWAYS_INLINE bool Parser::parseVarInt64(int64_t& result)
+template<typename SuccessType>
+ALWAYS_INLINE bool Parser<SuccessType>::parseVarInt64(int64_t& result)
 {
     return WTF::LEBDecoder::decodeInt64(m_source, m_sourceLength, m_offset, result);
 }
 
-ALWAYS_INLINE bool Parser::parseUInt32(uint32_t& result)
+template<typename SuccessType>
+ALWAYS_INLINE bool Parser<SuccessType>::parseUInt32(uint32_t& result)
 {
     if (length() < 4 || m_offset > length() - 4)
         return false;
@@ -147,7 +189,8 @@ ALWAYS_INLINE bool Parser::parseUInt32(uint32_t& result)
     return true;
 }
 
-ALWAYS_INLINE bool Parser::parseUInt64(uint64_t& result)
+template<typename SuccessType>
+ALWAYS_INLINE bool Parser<SuccessType>::parseUInt64(uint64_t& result)
 {
     if (length() < 8 || m_offset > length() - 8)
         return false;
@@ -156,7 +199,8 @@ ALWAYS_INLINE bool Parser::parseUInt64(uint64_t& result)
     return true;
 }
 
-ALWAYS_INLINE bool Parser::parseUInt8(uint8_t& result)
+template<typename SuccessType>
+ALWAYS_INLINE bool Parser<SuccessType>::parseUInt8(uint8_t& result)
 {
     if (m_offset >= length())
         return false;
@@ -164,7 +208,8 @@ ALWAYS_INLINE bool Parser::parseUInt8(uint8_t& result)
     return true;
 }
 
-ALWAYS_INLINE bool Parser::parseInt7(int8_t& result)
+template<typename SuccessType>
+ALWAYS_INLINE bool Parser<SuccessType>::parseInt7(int8_t& result)
 {
     if (m_offset >= length())
         return false;
@@ -173,7 +218,8 @@ ALWAYS_INLINE bool Parser::parseInt7(int8_t& result)
     return (v & 0x80) == 0;
 }
 
-ALWAYS_INLINE bool Parser::parseUInt7(uint8_t& result)
+template<typename SuccessType>
+ALWAYS_INLINE bool Parser<SuccessType>::parseUInt7(uint8_t& result)
 {
     if (m_offset >= length())
         return false;
@@ -181,7 +227,8 @@ ALWAYS_INLINE bool Parser::parseUInt7(uint8_t& result)
     return result < 0x80;
 }
 
-ALWAYS_INLINE bool Parser::parseVarUInt1(uint8_t& result)
+template<typename SuccessType>
+ALWAYS_INLINE bool Parser<SuccessType>::parseVarUInt1(uint8_t& result)
 {
     uint32_t temp;
     if (!parseVarUInt32(temp))
@@ -190,7 +237,8 @@ ALWAYS_INLINE bool Parser::parseVarUInt1(uint8_t& result)
     return temp <= 1;
 }
 
-ALWAYS_INLINE bool Parser::parseResultType(Type& result)
+template<typename SuccessType>
+ALWAYS_INLINE bool Parser<SuccessType>::parseResultType(Type& result)
 {
     int8_t value;
     if (!parseInt7(value))
@@ -201,19 +249,21 @@ ALWAYS_INLINE bool Parser::parseResultType(Type& result)
     return true;
 }
 
-ALWAYS_INLINE bool Parser::parseValueType(Type& result)
+template<typename SuccessType>
+ALWAYS_INLINE bool Parser<SuccessType>::parseValueType(Type& result)
 {
     return parseResultType(result) && isValueType(result);
 }
-    
-ALWAYS_INLINE bool Parser::parseExternalKind(External::Kind& result)
+
+template<typename SuccessType>
+ALWAYS_INLINE bool Parser<SuccessType>::parseExternalKind(ExternalKind& result)
 {
     uint8_t value;
     if (!parseUInt7(value))
         return false;
-    if (!External::isValid(value))
+    if (!isValidExternalKind(value))
         return false;
-    result = static_cast<External::Kind>(value);
+    result = static_cast<ExternalKind>(value);
     return true;
 }
 
index 68329e5..1c0e019 100644 (file)
@@ -60,23 +60,18 @@ Plan::Plan(VM* vm, const uint8_t* source, size_t sourceLength)
 
 void Plan::run()
 {
-    if (verbose)
-        dataLogLn("Starting plan.");
     {
         ModuleParser moduleParser(m_vm, m_source, m_sourceLength);
-        if (!moduleParser.parse()) {
-            if (verbose)
-                dataLogLn("Parsing module failed: ", moduleParser.errorMessage());
-            m_errorMessage = moduleParser.errorMessage();
-            return;
+        auto parseResult = moduleParser.parse();
+        if (!parseResult) {
+            m_errorMessage = parseResult.error();
+            return; // FIXME return an Expected.
         }
-        m_moduleInformation = WTFMove(moduleParser.moduleInformation());
-        m_functionLocationInBinary = WTFMove(moduleParser.functionLocationInBinary());
-        m_functionIndexSpace.size = moduleParser.functionIndexSpace().size();
-        m_functionIndexSpace.buffer = moduleParser.functionIndexSpace().releaseBuffer();
+        m_moduleInformation = WTFMove(parseResult->module);
+        m_functionLocationInBinary = WTFMove(parseResult->functionLocationInBinary);
+        m_functionIndexSpace.size = parseResult->functionIndexSpace.size();
+        m_functionIndexSpace.buffer = parseResult->functionIndexSpace.releaseBuffer();
     }
-    if (verbose)
-        dataLogLn("Parsed module.");
 
     auto tryReserveCapacity = [this] (auto& vector, size_t size, const char* what) {
         if (UNLIKELY(!vector.tryReserveCapacity(size))) {
@@ -97,7 +92,7 @@ void Plan::run()
 
     for (unsigned importIndex = 0; importIndex < m_moduleInformation->imports.size(); ++importIndex) {
         Import* import = &m_moduleInformation->imports[importIndex];
-        if (import->kind != External::Function)
+        if (import->kind != ExternalKind::Function)
             continue;
         unsigned importFunctionIndex = m_wasmToJSStubs.size();
         if (verbose)
@@ -117,19 +112,24 @@ void Plan::run()
         unsigned functionIndexSpace = m_wasmToJSStubs.size() + functionIndex;
         ASSERT(m_functionIndexSpace.buffer.get()[functionIndexSpace].signature == signature);
 
-        String error = validateFunction(functionStart, functionLength, signature, m_functionIndexSpace, *m_moduleInformation);
-        if (!error.isNull()) {
+        auto validateResult = validateFunction(functionStart, functionLength, signature, m_functionIndexSpace, *m_moduleInformation);
+        if (!validateResult) {
             if (verbose) {
                 for (unsigned i = 0; i < functionLength; ++i)
                     dataLog(RawPointer(reinterpret_cast<void*>(functionStart[i])), ", ");
                 dataLogLn();
             }
-            m_errorMessage = error;
+            m_errorMessage = validateResult.error(); // FIXME make this an Expected.
             return;
         }
 
         unlinkedWasmToWasmCalls.uncheckedAppend(Vector<UnlinkedWasmToWasmCall>());
-        m_wasmInternalFunctions.uncheckedAppend(parseAndCompile(*m_vm, functionStart, functionLength, signature, unlinkedWasmToWasmCalls.at(functionIndex), m_functionIndexSpace, *m_moduleInformation));
+        auto parseAndCompileResult = parseAndCompile(*m_vm, functionStart, functionLength, signature, unlinkedWasmToWasmCalls.at(functionIndex), m_functionIndexSpace, *m_moduleInformation);
+        if (UNLIKELY(!parseAndCompileResult)) {
+            m_errorMessage = parseAndCompileResult.error();
+            return; // FIXME make this an Expected.
+        }
+        m_wasmInternalFunctions.uncheckedAppend(WTFMove(*parseAndCompileResult));
         m_functionIndexSpace.buffer.get()[functionIndexSpace].code = m_wasmInternalFunctions[functionIndex]->wasmEntrypoint.compilation->code().executableAddress();
     }
 
index 9960524..5ee7bcc 100644 (file)
@@ -42,21 +42,44 @@ namespace JSC { namespace Wasm {
     macro(Code,    10, "Function bodies (code)") \
     macro(Data,    11, "Data segments")
 
-struct Sections {
-    enum Section : uint8_t {
+enum class Section : uint8_t {
 #define DEFINE_WASM_SECTION_ENUM(NAME, ID, DESCRIPTION) NAME = ID,
-        FOR_EACH_WASM_SECTION(DEFINE_WASM_SECTION_ENUM)
+    FOR_EACH_WASM_SECTION(DEFINE_WASM_SECTION_ENUM)
 #undef DEFINE_WASM_SECTION_ENUM
-        Unknown
-    };
-    static bool validateOrder(Section previous, Section next)
-    {
-        if (previous == Unknown)
-            return true;
-        return previous < next;
-    }
+    Unknown
 };
 
+template<typename Int>
+static inline bool isValidSection(Int section)
+{
+    switch (section) {
+#define VALIDATE_SECTION(NAME, ID, DESCRIPTION) case static_cast<Int>(Section::NAME): return true;
+        FOR_EACH_WASM_SECTION(VALIDATE_SECTION)
+#undef VALIDATE_SECTION
+    default:
+        return false;
+    }
+}
+
+static inline bool validateOrder(Section previous, Section next)
+{
+    if (previous == Section::Unknown)
+        return true;
+    return static_cast<uint8_t>(previous) < static_cast<uint8_t>(next);
+}
+
+static inline const char* makeString(Section section)
+{
+    switch (section) {
+#define STRINGIFY_SECTION_NAME(NAME, ID, DESCRIPTION) case Section::NAME: return #NAME;
+        FOR_EACH_WASM_SECTION(STRINGIFY_SECTION_NAME)
+#undef STRINGIFY_SECTION_NAME
+    default:
+        RELEASE_ASSERT_NOT_REACHED();
+        return "?";
+    }
+}
+
 } } // namespace JSC::Wasm
 
 #endif // ENABLE(WEBASSEMBLY)
index f79bc63..d1b556e 100644 (file)
@@ -71,6 +71,9 @@ public:
         BlockType m_blockType;
         Type m_signature;
     };
+    typedef String ErrorType;
+    typedef UnexpectedType<ErrorType> UnexpectedResult;
+    typedef Expected<void, ErrorType> Result;
     typedef Type ExpressionType;
     typedef ControlData ControlType;
     typedef Vector<ExpressionType, 1> ExpressionList;
@@ -78,144 +81,129 @@ public:
 
     static const ExpressionType emptyExpression = Void;
 
-    bool WARN_UNUSED_RETURN addArguments(const Vector<Type>&);
-    bool WARN_UNUSED_RETURN addLocal(Type, uint32_t);
+    template <typename ...Args>
+    NEVER_INLINE UnexpectedResult WARN_UNUSED_RETURN fail(Args... args) const
+    {
+        using namespace FailureHelper; // See ADL comment in WasmParser.h.
+        return UnexpectedResult(makeString(ASCIILiteral("WebAssembly.Module doesn't validate: "), makeString(args)...));
+    }
+#define WASM_VALIDATOR_FAIL_IF(condition, ...) do { \
+        if (UNLIKELY(condition))                    \
+        return fail(__VA_ARGS__);                   \
+    } while (0)
+
+    Result WARN_UNUSED_RETURN addArguments(const Vector<Type>&);
+    Result WARN_UNUSED_RETURN addLocal(Type, uint32_t);
     ExpressionType addConstant(Type type, uint64_t) { return type; }
 
     // Locals
-    bool WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
-    bool WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
+    Result WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
+    Result WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
 
     // Globals
-    bool WARN_UNUSED_RETURN getGlobal(uint32_t index, ExpressionType& result);
-    bool WARN_UNUSED_RETURN setGlobal(uint32_t index, ExpressionType value);
+    Result WARN_UNUSED_RETURN getGlobal(uint32_t index, ExpressionType& result);
+    Result WARN_UNUSED_RETURN setGlobal(uint32_t index, ExpressionType value);
 
     // Memory
-    bool WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset);
-    bool WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset);
+    Result WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset);
+    Result WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset);
 
     // Basic operators
     template<OpType>
-    bool WARN_UNUSED_RETURN addOp(ExpressionType arg, ExpressionType& result);
+    Result WARN_UNUSED_RETURN addOp(ExpressionType arg, ExpressionType& result);
     template<OpType>
-    bool WARN_UNUSED_RETURN addOp(ExpressionType left, ExpressionType right, ExpressionType& result);
-    bool WARN_UNUSED_RETURN addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result);
+    Result WARN_UNUSED_RETURN addOp(ExpressionType left, ExpressionType right, ExpressionType& result);
+    Result WARN_UNUSED_RETURN addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result);
 
     // Control flow
     ControlData WARN_UNUSED_RETURN addBlock(Type signature);
     ControlData WARN_UNUSED_RETURN addLoop(Type signature);
-    bool WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature, ControlData& result);
-    bool WARN_UNUSED_RETURN addElse(ControlData&, const ExpressionList&);
-    bool WARN_UNUSED_RETURN addElseToUnreachable(ControlData&);
+    Result WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature, ControlData& result);
+    Result WARN_UNUSED_RETURN addElse(ControlData&, const ExpressionList&);
+    Result WARN_UNUSED_RETURN addElseToUnreachable(ControlData&);
 
-    bool WARN_UNUSED_RETURN addReturn(const ExpressionList& returnValues);
-    bool WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& expressionStack);
-    bool WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack);
-    bool WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack);
-    bool WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&);
+    Result WARN_UNUSED_RETURN addReturn(const ExpressionList& returnValues);
+    Result WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& expressionStack);
+    Result WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack);
+    Result WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack);
+    Result WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&);
 
-    void addUnreachable() { }
+    Result WARN_UNUSED_RETURN addUnreachable() { return { }; }
 
     // Calls
-    bool WARN_UNUSED_RETURN addCall(unsigned calleeIndex, const Signature*, const Vector<ExpressionType>& args, ExpressionType& result);
-    bool WARN_UNUSED_RETURN addCallIndirect(const Signature*, const Vector<ExpressionType>& args, ExpressionType& result);
-
-    void dump(const Vector<ControlEntry>& controlStack, const ExpressionList& expressionStack);
+    Result WARN_UNUSED_RETURN addCall(unsigned calleeIndex, const Signature*, const Vector<ExpressionType>& args, ExpressionType& result);
+    Result WARN_UNUSED_RETURN addCallIndirect(const Signature*, const Vector<ExpressionType>& args, ExpressionType& result);
 
     bool hasMemory() const { return !!m_module.memory; }
 
-    void setErrorMessage(String&& message) { ASSERT(m_errorMessage.isNull()); m_errorMessage = WTFMove(message); }
-    String errorMessage() const { return m_errorMessage; }
     Validate(ExpressionType returnType, const ModuleInformation& module)
         : m_returnType(returnType)
         , m_module(module)
     {
     }
 
+    void dump(const Vector<ControlEntry>&, const ExpressionList&);
+
 private:
-    bool unify(Type, Type);
-    bool unify(const ExpressionList&, const ControlData&);
+    Result unify(Type, Type);
+    Result unify(const ExpressionList&, const ControlData&);
 
-    bool checkBranchTarget(ControlData& target, const ExpressionList& expressionStack);
+    Result checkBranchTarget(ControlData& target, const ExpressionList& expressionStack);
 
     ExpressionType m_returnType;
     Vector<Type> m_locals;
-    String m_errorMessage;
     const ModuleInformation& m_module;
 };
 
-bool Validate::addArguments(const Vector<Type>& args)
+auto Validate::addArguments(const Vector<Type>& args) -> Result
 {
-    for (Type arg : args) {
-        if (!addLocal(arg, 1))
-            return false;
-    }
-    return true;
+    for (Type arg : args)
+        WASM_FAIL_IF_HELPER_FAILS(addLocal(arg, 1));
+    return { };
 }
 
-bool Validate::addLocal(Type type, uint32_t count)
+auto Validate::addLocal(Type type, uint32_t count) -> Result
 {
-    if (!m_locals.tryReserveCapacity(m_locals.size() + count))
-        return false;
+    size_t size = m_locals.size() + count;
+    WASM_VALIDATOR_FAIL_IF(!m_locals.tryReserveCapacity(size), "can't allocate memory for ", size, " locals");
 
     for (uint32_t i = 0; i < count; ++i)
         m_locals.uncheckedAppend(type);
-    return true;
+    return { };
 }
 
-bool Validate::getLocal(uint32_t index, ExpressionType& result)
+auto Validate::getLocal(uint32_t index, ExpressionType& result) -> Result
 {
-    if (index < m_locals.size()) {
-        result = m_locals[index];
-        return true;
-    }
-    m_errorMessage = ASCIILiteral("Attempt to use unknown local.");
-    return false;
+    WASM_VALIDATOR_FAIL_IF(index >= m_locals.size(), "attempt to use unknown local ", index, " last one is ", m_locals.size());
+    result = m_locals[index];
+    return { };
 }
 
-bool Validate::setLocal(uint32_t index, ExpressionType value)
+auto Validate::setLocal(uint32_t index, ExpressionType value) -> Result
 {
     ExpressionType localType;
-    if (!getLocal(index, localType))
-        return false;
-
-    if (localType == value)
-        return true;
-
-    m_errorMessage = makeString("Attempt to set local with type: ", toString(localType), " with a variable of type: ", toString(value));
-    return false;
+    WASM_FAIL_IF_HELPER_FAILS(getLocal(index, localType));
+    WASM_VALIDATOR_FAIL_IF(localType != value, "set_local to type ", value, " expected ", localType);
+    return { };
 }
 
-bool Validate::getGlobal(uint32_t index, ExpressionType& result)
+auto Validate::getGlobal(uint32_t index, ExpressionType& result) -> Result
 {
-    if (index < m_module.globals.size()) {
-        result = m_module.globals[index].type;
-        ASSERT(isValueType(result));
-        return true;
-    }
-    m_errorMessage = ASCIILiteral("Attempt to use unknown global.");
-    return false;
+    WASM_VALIDATOR_FAIL_IF(index >= m_module.globals.size(), "get_global ", index, " of unknown global, limit is ", m_module.globals.size());
+    result = m_module.globals[index].type;
+    ASSERT(isValueType(result));
+    return { };
 }
 
-bool Validate::setGlobal(uint32_t index, ExpressionType value)
+auto Validate::setGlobal(uint32_t index, ExpressionType value) -> Result
 {
-    if (index >= m_module.globals.size()) {
-        m_errorMessage = ASCIILiteral("Attempt to use unknown global.");
-        return false;
-    }
-
-    if (m_module.globals[index].mutability == Global::Immutable) {
-        m_errorMessage = ASCIILiteral("Attempt to store to immutable global.");
-        return false;
-    }
+    WASM_VALIDATOR_FAIL_IF(index >= m_module.globals.size(), "set_global ", index, " of unknown global, limit is ", m_module.globals.size());
+    WASM_VALIDATOR_FAIL_IF(m_module.globals[index].mutability == Global::Immutable, "set_global ", index, " is immutable");
 
     ExpressionType globalType = m_module.globals[index].type;
     ASSERT(isValueType(globalType));
-    if (globalType == value)
-        return true;
-
-    m_errorMessage = makeString("Attempt to set global with type: ", toString(globalType), " with a variable of type: ", toString(value));
-    return false;
+    WASM_VALIDATOR_FAIL_IF(globalType != value, "set_global ", index, " with type ", globalType, " with a variable of type ", value);
+    return { };
 }
 
 Validate::ControlType Validate::addBlock(Type signature)
@@ -228,209 +216,129 @@ Validate::ControlType Validate::addLoop(Type signature)
     return ControlData(BlockType::Loop, signature);
 }
 
-bool Validate::addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result)
+auto Validate::addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result) -> Result
 {
-    if (condition != I32) {
-        m_errorMessage = makeString("Attempting to use ", toString(condition), " as the condition for select");
-        return false;
-    }
-
-    if (nonZero != zero) {
-        m_errorMessage = makeString("Result types of select don't match. Got: ", toString(nonZero), " and ", toString(zero));
-        return false;
-    }
-
+    WASM_VALIDATOR_FAIL_IF(condition != I32, "select condition must be i32, got ", condition);
+    WASM_VALIDATOR_FAIL_IF(nonZero != zero, "select result types must match, got ", nonZero, " and ", zero);
     result = zero;
-    return true;
+    return { };
 }
 
-bool Validate::addIf(ExpressionType condition, Type signature, ControlType& result)
+auto Validate::addIf(ExpressionType condition, Type signature, ControlType& result) -> Result
 {
-    if (condition != I32) {
-        m_errorMessage = makeString("Attempting to use ", toString(condition), " as the condition for an if block");
-        return false;
-    }
+    WASM_VALIDATOR_FAIL_IF(condition != I32, "if condition must be i32, got ", condition);
     result = ControlData(BlockType::If, signature);
-    return true;
+    return { };
 }
 
-bool Validate::addElse(ControlType& current, const ExpressionList& values)
+auto Validate::addElse(ControlType& current, const ExpressionList& values) -> Result
 {
-    if (!unify(values, current)) {
-        ASSERT(errorMessage());
-        return false;
-    }
-
+    WASM_FAIL_IF_HELPER_FAILS(unify(values, current));
     return addElseToUnreachable(current);
 }
 
-bool Validate::addElseToUnreachable(ControlType& current)
+auto Validate::addElseToUnreachable(ControlType& current) -> Result
 {
-    if (current.type() != BlockType::If) {
-        m_errorMessage = makeString("Attempting to add else block to something other than an if");
-        return false;
-    }
-
+    WASM_VALIDATOR_FAIL_IF(current.type() != BlockType::If, "else block isn't associated to an if");
     current = ControlData(BlockType::Block, current.signature());
-    return true;
+    return { };
 }
 
-bool Validate::addReturn(const ExpressionList& returnValues)
+auto Validate::addReturn(const ExpressionList& returnValues) -> Result
 {
     if (m_returnType == Void)
-        return true;
+        return { };
     ASSERT(returnValues.size() == 1);
-
-    if (m_returnType == returnValues[0])
-        return true;
-
-    m_errorMessage = makeString("Attempting to add return with type: ", toString(returnValues[0]), " but function expects return with type: ", toString(m_returnType));
-    return false;
+    WASM_VALIDATOR_FAIL_IF(m_returnType != returnValues[0], "return type ", returnValues[0], " doesn't match function's return type ", m_returnType);
+    return { };
 }
 
-bool Validate::checkBranchTarget(ControlType& target, const ExpressionList& expressionStack)
+auto Validate::checkBranchTarget(ControlType& target, const ExpressionList& expressionStack) -> Result
     {
         if (target.type() == BlockType::Loop)
-            return true;
+            return { };
 
         if (target.signature() == Void)
-            return true;
-
-        if (!expressionStack.size()) {
-            m_errorMessage = makeString("Attempting to branch to block with expected type: ", toString(target.signature()), " but the stack was empty");
-            return false;
-        }
+            return { };
 
-        if (target.signature() == expressionStack.last())
-            return true;
+        WASM_VALIDATOR_FAIL_IF(expressionStack.isEmpty(), "branch to block on empty expression stack");
+        WASM_VALIDATOR_FAIL_IF(target.signature() != expressionStack.last(), "branch's stack type doesn't match block's type");
 
-        m_errorMessage = makeString("Attempting to branch to block with expected type: ", toString(target.signature()), " but stack has type: ", toString(target.signature()));
-        return false;
+        return { };
     }
 
-bool Validate::addBranch(ControlType& target, ExpressionType condition, const ExpressionList& stack)
+auto Validate::addBranch(ControlType& target, ExpressionType condition, const ExpressionList& stack) -> Result
 {
     // Void means this is an unconditional branch.
-    if (condition != Void && condition != I32) {
-        m_errorMessage = makeString("Attempting to add a conditional branch with condition type: ", toString(condition), " but expected i32.");
-        return false;
-    }
-
+    WASM_VALIDATOR_FAIL_IF(condition != Void && condition != I32, "conditional branch with non-i32 condition ", condition);
     return checkBranchTarget(target, stack);
 }
 
-bool Validate::addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack)
+auto Validate::addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack) -> Result
 {
-    if (condition != I32) {
-        m_errorMessage = makeString("Attempting to add a br_table with condition type: ", toString(condition), " but expected i32.");
-        return false;
-    }
+    WASM_VALIDATOR_FAIL_IF(condition != I32, "br_table with non-i32 condition ", condition);
 
-    for (auto target : targets) {
-        if (defaultTarget.signature() != target->signature()) {
-            m_errorMessage = makeString("Attempting to add a br_table with different expected types. Default target has type: ", toString(defaultTarget.signature()), " but case has type: ", toString(target->signature()));
-            return false;
-        }
-    }
+    for (auto target : targets)
+        WASM_VALIDATOR_FAIL_IF(defaultTarget.signature() != target->signature(), "br_table target type mismatch");
 
     return checkBranchTarget(defaultTarget, expressionStack);
 }
 
-bool Validate::endBlock(ControlEntry& entry, ExpressionList& stack)
+auto Validate::endBlock(ControlEntry& entry, ExpressionList& stack) -> Result
 {
     ControlData& block = entry.controlData;
     if (block.signature() == Void)
-        return true;
-
-    if (!stack.size()) {
-        m_errorMessage = makeString("Block fallthough expected type: ", toString(block.signature()), " but the stack was empty");
-        return false;
-    }
+        return { };
 
-    if (block.signature() == stack.last()) {
-        entry.enclosedExpressionStack.append(block.signature());
-        return true;
-    }
+    WASM_VALIDATOR_FAIL_IF(stack.isEmpty(), "typed block falls through on empty stack");
+    WASM_VALIDATOR_FAIL_IF(block.signature() != stack.last(), "block fallthrough doesn't match its declared type");
 
-    m_errorMessage = makeString("Block fallthrough has expected type: ", toString(block.signature()), " but produced type: ", toString(block.signature()));
-    return false;
+    entry.enclosedExpressionStack.append(block.signature());
+    return { };
 }
 
-bool Validate::addEndToUnreachable(ControlEntry& entry)
+auto Validate::addEndToUnreachable(ControlEntry& entry) -> Result
 {
     if (entry.controlData.signature() != Void)
         entry.enclosedExpressionStack.append(entry.controlData.signature());
-    return true;
+    return { };
 }
 
-bool Validate::addCall(unsigned, const Signature* signature, const Vector<ExpressionType>& args, ExpressionType& result)
+auto Validate::addCall(unsigned, const Signature* signature, const Vector<ExpressionType>& args, ExpressionType& result) -> Result
 {
-    if (signature->arguments.size() != args.size()) {
-        StringBuilder builder;
-        builder.append("Arity mismatch in call, expected: ");
-        builder.appendNumber(signature->arguments.size());
-        builder.append(" but got: ");
-        builder.appendNumber(args.size());
-        m_errorMessage = builder.toString();
-        return false;
-    }
+    WASM_VALIDATOR_FAIL_IF(signature->arguments.size() != args.size(), "arity mismatch in call, got ", args.size(), " arguments, expected ", signature->arguments.size());
 
-    for (unsigned i = 0; i < args.size(); ++i) {
-        if (args[i] != signature->arguments[i]) {
-            m_errorMessage = makeString("Expected argument type: ", toString(signature->arguments[i]), " does not match passed argument type: ", toString(args[i]));
-            return false;
-        }
-    }
+    for (unsigned i = 0; i < args.size(); ++i)
+        WASM_VALIDATOR_FAIL_IF(args[i] != signature->arguments[i], "argument type mismatch in call, got ", args[i], ", expected ", signature->arguments[i]);
 
     result = signature->returnType;
-    return true;
+    return { };
 }
 
-bool Validate::addCallIndirect(const Signature* signature, const Vector<ExpressionType>& args, ExpressionType& result)
+auto Validate::addCallIndirect(const Signature* signature, const Vector<ExpressionType>& args, ExpressionType& result) -> Result
 {
     const auto argumentCount = signature->arguments.size();
-    if (argumentCount != args.size() - 1) {
-        StringBuilder builder;
-        builder.append("Arity mismatch in call_indirect, expected: ");
-        builder.appendNumber(signature->arguments.size());
-        builder.append(" but got: ");
-        builder.appendNumber(args.size());
-        m_errorMessage = builder.toString();
-        return false;
-    }
+    WASM_VALIDATOR_FAIL_IF(argumentCount != args.size() - 1, "arity mismatch in call_indirect, got ", args.size() - 1, " arguments, expected ", argumentCount);
 
-    for (unsigned i = 0; i < argumentCount; ++i) {
-        if (args[i] != signature->arguments[i]) {
-            m_errorMessage = makeString("Expected argument type: ", toString(signature->arguments[i]), " does not match passed argument type: ", toString(args[i]));
-            return false;
-        }
-    }
+    for (unsigned i = 0; i < argumentCount; ++i)
+        WASM_VALIDATOR_FAIL_IF(args[i] != signature->arguments[i], "argument type mismatch in call_indirect, got ", args[i], ", expected ", signature->arguments[i]);
+
+    WASM_VALIDATOR_FAIL_IF(args.last() != I32, "non-i32 call_indirect index ", args.last());
 
-    if (args.last() != I32) {
-        m_errorMessage = makeString("Expected call_indirect target index to have type: i32 but got type: ", toString(args.last()));
-        return false;
-    }
-    
     result = signature->returnType;
-    return true;
+    return { };
 }
 
-bool Validate::unify(const ExpressionList& values, const ControlType& block)
+auto Validate::unify(const ExpressionList& values, const ControlType& block) -> Result
 {
     ASSERT(values.size() <= 1);
     if (block.signature() == Void)
-        return true;
-
-    if (!values.size()) {
-        m_errorMessage = makeString("Block has non-void signature but has no stack entries on exit");
-        return false;
-    }
+        return { };
 
-    if (values[0] == block.signature())
-        return true;
+    WASM_VALIDATOR_FAIL_IF(values.isEmpty(), "non-void block ends with an empty stack");
+    WASM_VALIDATOR_FAIL_IF(values[0] != block.signature(), "control flow returns with unexpected type");
 
-    m_errorMessage = makeString("Expected control flow to return value with type: ", toString(block.signature()), " but got value with type: ", toString(values[0]));
-    return false;
+    return { };
 }
 
 void Validate::dump(const Vector<ControlEntry>&, const ExpressionList&)
@@ -439,21 +347,12 @@ void Validate::dump(const Vector<ControlEntry>&, const ExpressionList&)
     // Think of this as penance for the sin of bad error messages.
 }
 
-String validateFunction(const uint8_t* source, size_t length, const Signature* signature, const ImmutableFunctionIndexSpace& functionIndexSpace, const ModuleInformation& module)
+Expected<void, String> validateFunction(const uint8_t* source, size_t length, const Signature* signature, const ImmutableFunctionIndexSpace& functionIndexSpace, const ModuleInformation& module)
 {
     Validate context(signature->returnType, module);
     FunctionParser<Validate> validator(context, source, length, signature, functionIndexSpace, module);
-
-    if (!validator.parse()) {
-        // FIXME: add better location information here. see: https://bugs.webkit.org/show_bug.cgi?id=164288
-        // FIXME: We should never not have an error message if we return false.
-        // see: https://bugs.webkit.org/show_bug.cgi?id=164354
-        if (context.errorMessage().isNull())
-            return "Unknown error";
-        return context.errorMessage();
-    }
-
-    return String();
+    WASM_FAIL_IF_HELPER_FAILS(validator.parse());
+    return { };
 }
 
 } } // namespace JSC::Wasm
index 861fd72..1f00417 100644 (file)
 #if ENABLE(WEBASSEMBLY)
 
 #include "WasmFormat.h"
+#include <wtf/Expected.h>
 
 namespace JSC { namespace Wasm {
 
-String validateFunction(const uint8_t*, size_t, const Signature*, const ImmutableFunctionIndexSpace&, const ModuleInformation&);
+Expected<void, String> validateFunction(const uint8_t*, size_t, const Signature*, const ImmutableFunctionIndexSpace&, const ModuleInformation&);
 
 } } // namespace JSC::Wasm
 
index 804cb21..596d859 100755 (executable)
@@ -189,10 +189,10 @@ def generateSimpleCode(op):
     args = ["ExpressionType arg" + str(param) for param in range(len(opcode["parameter"]))]
     args.append("ExpressionType& result")
     return """
-template<> bool B3IRGenerator::addOp<OpType::""" + wasm.toCpp(op["name"]) + ">(" + ", ".join(args) + """)
+template<> auto B3IRGenerator::addOp<OpType::""" + wasm.toCpp(op["name"]) + ">(" + ", ".join(args) + """) -> PartialResult
 {
 """ + generateB3Code(opcode, b3op) + """;
-    return true;
+    return { };
 }
 """
 
index 00d2838..15c1f3c 100755 (executable)
@@ -151,7 +151,7 @@ inline B3::Type toB3Type(Type type)
 #undef CREATE_CASE
 
 #define CREATE_CASE(name, id, b3type, inc) case name: return #name;
-inline const char* toString(Type type)
+inline const char* makeString(Type type)
 {
     switch (type) {
     FOR_EACH_WASM_TYPE(CREATE_CASE)
index 25a392e..52b66b1 100755 (executable)
@@ -60,15 +60,13 @@ def toCpp(name):
 def unaryMacro(name):
     op = opcodes[name]
     return """
-template<> bool Validate::addOp<OpType::""" + toCpp(name) + """>(ExpressionType value, ExpressionType& result)
+template<> auto Validate::addOp<OpType::""" + toCpp(name) + """>(ExpressionType value, ExpressionType& result) -> Result
 {
-    if (value != """ + cppType(op["parameter"][0]) + """) {
-        m_errorMessage = makeString(\"""" + name + """ expects the value to be of type: ", toString(""" + cppType(op["parameter"][0]) + """), " but got a value with type: ", toString(value));
-        return false;
-    }
+    if (UNLIKELY(value != """ + cppType(op["parameter"][0]) + """))
+        return UnexpectedType<Result::ErrorType>("validation failed: """ + name + """ value type mismatch");
 
     result = """ + cppType(op["return"][0]) + """;
-    return true;
+    return { };
 }
 """
 
@@ -76,20 +74,16 @@ template<> bool Validate::addOp<OpType::""" + toCpp(name) + """>(ExpressionType
 def binaryMacro(name):
     op = opcodes[name]
     return """
-template<> bool Validate::addOp<OpType::""" + toCpp(name) + """>(ExpressionType left, ExpressionType right, ExpressionType& result)
+template<> auto Validate::addOp<OpType::""" + toCpp(name) + """>(ExpressionType left, ExpressionType right, ExpressionType& result) -> Result
 {
-    if (left != """ + cppType(op["parameter"][0]) + """) {
-        m_errorMessage = makeString(\"""" + name + """ expects the left value to be of type: ", toString(""" + cppType(op["parameter"][0]) + """), " but got a value with type: ", toString(left));
-        return false;
-    }
+    if (UNLIKELY(left != """ + cppType(op["parameter"][0]) + """))
+        return UnexpectedType<Result::ErrorType>("validation failed: """ + name + """ left value type mismatch");
 
-    if (right != """ + cppType(op["parameter"][1]) + """) {
-        m_errorMessage = makeString(\"""" + name + """ expects the right value to be of type: ", toString(""" + cppType(op["parameter"][0]) + """), " but got a value with type: ", toString(right));
-        return false;
-    }
+    if (UNLIKELY(right != """ + cppType(op["parameter"][1]) + """))
+        return UnexpectedType<Result::ErrorType>("validation failed: """ + name + """ right value type mismatch");
 
     result = """ + cppType(op["return"][0]) + """;
-    return true;
+    return { };
 }
 """
 
@@ -97,13 +91,11 @@ def loadMacro(name):
     op = opcodes[name]
     return """
     case LoadOpType::""" + toCpp(name) + """: {
-        if (pointer != """ + cppType(op["parameter"][0]) + """) {
-            m_errorMessage = makeString(\"""" + name + """ expects the pointer to be of type: ", toString(""" + cppType(op["parameter"][0]) + """), " but got a value with type: ", toString(pointer));
-            return false;
-        }
+        if (UNLIKELY(pointer != """ + cppType(op["parameter"][0]) + """))
+            return UnexpectedType<Result::ErrorType>("validation failed: """ + name + """ pointer type mismatch");
 
         result = """ + cppType(op["return"][0]) + """;
-        return true;
+        return { };
     }"""
 
 
@@ -111,17 +103,13 @@ def storeMacro(name):
     op = opcodes[name]
     return """
     case StoreOpType::""" + toCpp(name) + """: {
-        if (pointer != """ + cppType(op["parameter"][0]) + """) {
-            m_errorMessage = makeString(\"""" + name + """ expects the pointer to be of type: ", toString(""" + cppType(op["parameter"][0]) + """), " but got a value with type: ", toString(pointer));
-            return false;
-        }
+        if (UNLIKELY(pointer != """ + cppType(op["parameter"][0]) + """))
+            return UnexpectedType<Result::ErrorType>("validation failed: """ + name + """ pointer type mismatch");
 
-        if (value != """ + cppType(op["parameter"][1]) + """) {
-            m_errorMessage = makeString(\"""" + name + """ expects the value to be of type: ", toString(""" + cppType(op["parameter"][0]) + """), " but got a value with type: ", toString(value));
-            return false;
-        }
+        if (UNLIKELY(value != """ + cppType(op["parameter"][1]) + """))
+            return UnexpectedType<Result::ErrorType>("validation failed: """ + name + """ value type mismatch");
 
-        return true;
+        return { };
     }"""
 
 
@@ -141,20 +129,20 @@ namespace JSC { namespace Wasm {
 
 """ + unarySpecializations + binarySpecializations + """
 
-bool Validate::load(LoadOpType op, ExpressionType pointer, ExpressionType& result, uint32_t)
+auto Validate::load(LoadOpType op, ExpressionType pointer, ExpressionType& result, uint32_t) -> Result
 {
-    if (!hasMemory())
-        return false;
+    if (UNLIKELY(!hasMemory()))
+        return UnexpectedType<Result::ErrorType>("validation failed: load instruction without memory");
 
     switch (op) {
 """ + loadCases + """
     }
 }
 
-bool Validate::store(StoreOpType op, ExpressionType pointer, ExpressionType value, uint32_t)
+auto Validate::store(StoreOpType op, ExpressionType pointer, ExpressionType value, uint32_t) -> Result
 {
-    if (!hasMemory())
-        return false;
+    if (UNLIKELY(!hasMemory()))
+        return UnexpectedType<Result::ErrorType>("validation failed: store instruction without memory");
 
     switch (op) {
 """ + storeCases + """
index f00a261..9d95926 100644 (file)
@@ -106,7 +106,7 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
         RETURN_IF_EXCEPTION(throwScope, { });
 
         switch (import.kind) {
-        case Wasm::External::Function: {
+        case Wasm::ExternalKind::Function: {
             // 4. If i is a function import:
             // i. If IsCallable(v) is false, throw a TypeError.
             if (!value.isFunction())
@@ -129,7 +129,7 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
             // v. Append closure to imports.
             break;
         }
-        case Wasm::External::Table: {
+        case Wasm::ExternalKind::Table: {
             RELEASE_ASSERT(!hasTableImport); // This should be guaranteed by a validation failure.
             // 7. Otherwise (i is a table import):
             hasTableImport = true;
@@ -160,7 +160,7 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
             instance->setTable(vm, table);
             break;
         }
-        case Wasm::External::Memory: {
+        case Wasm::ExternalKind::Memory: {
             // 6. If i is a memory import:
             RELEASE_ASSERT(!hasMemoryImport); // This should be guaranteed by a validation failure.
             RELEASE_ASSERT(moduleInformation.memory);
@@ -192,7 +192,7 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
             instance->setMemory(vm, memory);
             break;
         }
-        case Wasm::External::Global: {
+        case Wasm::ExternalKind::Global: {
             // 5. If i is a global import:
             // i. If i is not an immutable global, throw a TypeError.
             ASSERT(moduleInformation.globals[import.kindIndex].mutability == Wasm::Global::Immutable);
index b0154be..0c6d881 100644 (file)
@@ -105,7 +105,7 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
     for (const auto& exp : moduleInformation.exports) {
         JSValue exportedValue;
         switch (exp.kind) {
-        case Wasm::External::Function: {
+        case Wasm::ExternalKind::Function: {
             // 1. If e is a closure c:
             //   i. If there is an Exported Function Exotic Object func in funcs whose func.[[Closure]] equals c, then return func.
             //   ii. (Note: At most one wrapper is created for any closure, so func is unique, even if there are multiple occurrances in the list. Moreover, if the item was an import that is already an Exported Function Exotic Object, then the original function object will be found. For imports that are regular JS functions, a new wrapper will be created.)
@@ -126,7 +126,7 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
                 m_startFunction.set(vm, this, function);
             break;
         }
-        case Wasm::External::Table: {
+        case Wasm::ExternalKind::Table: {
             // This should be guaranteed by module verification.
             RELEASE_ASSERT(instance->table()); 
             ASSERT(exp.kindIndex == 0);
@@ -134,7 +134,7 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
             exportedValue = instance->table();
             break;
         }
-        case Wasm::External::Memory: {
+        case Wasm::ExternalKind::Memory: {
             // This should be guaranteed by module verification.
             RELEASE_ASSERT(instance->memory()); 
             ASSERT(exp.kindIndex == 0);
@@ -142,7 +142,7 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
             exportedValue = instance->memory();
             break;
         }
-        case Wasm::External::Global: {
+        case Wasm::ExternalKind::Global: {
             // Assert: the global is immutable by MVP validation constraint.
             const Wasm::Global& global = moduleInformation.globals[exp.kindIndex];
             ASSERT(global.mutability == Wasm::Global::Immutable);