[WebAssembly] Parse wasm modules in a streaming fashion
authoryusukesuzuki@slowstart.org <yusukesuzuki@slowstart.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 28 Aug 2018 06:38:29 +0000 (06:38 +0000)
committeryusukesuzuki@slowstart.org <yusukesuzuki@slowstart.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 28 Aug 2018 06:38:29 +0000 (06:38 +0000)
https://bugs.webkit.org/show_bug.cgi?id=188943

Reviewed by Mark Lam.

JSTests:

Wasm parsing error should not report the total byte size since streaming parsing does not
want to load all the bytes.
Add a simple test wasm/stress/streaming-basic.js for initial streaming parsing implementation.

* wasm/function-tests/invalid-duplicate-export.js:
* wasm/function-tests/memory-alignment.js:
(const.op.of.WASM.opcodes):
* wasm/function-tests/memory-section-and-import.js:
* wasm/function-tests/void-argument-type-should-be-a-validation-error.js:
* wasm/js-api/Module-compile.js:
(async.testPromiseAPI):
* 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):
* wasm/js-api/table.js:
(new.WebAssembly.Module):
(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):
* wasm/js-api/version.js:
* wasm/stress/nameSection.wasm: Added.
* wasm/stress/streaming-basic.js: Added.
(check):

Source/JavaScriptCore:

This patch adds Wasm::StreamingParser, which parses wasm binary in a streaming fashion.
Currently, this StreamingParser is not enabled and integrated. In subsequent patches,
we start integrating it into BBQPlan and dropping the old ModuleParser.

* JavaScriptCore.xcodeproj/project.pbxproj:
* Sources.txt:
* tools/JSDollarVM.cpp:
(WTF::WasmStreamingParser::WasmStreamingParser):
(WTF::WasmStreamingParser::create):
(WTF::WasmStreamingParser::createStructure):
(WTF::WasmStreamingParser::streamingParser):
(WTF::WasmStreamingParser::finishCreation):
(WTF::functionWasmStreamingParserAddBytes):
(WTF::functionWasmStreamingParserFinalize):
(JSC::functionCreateWasmStreamingParser):
(JSC::JSDollarVM::finishCreation):
The $vm Wasm::StreamingParser object is introduced for testing purpose. Added new stress test uses
this interface to test streaming parser in the JSC shell.

* wasm/WasmBBQPlan.cpp:
(JSC::Wasm::BBQPlan::BBQPlan):
(JSC::Wasm::BBQPlan::parseAndValidateModule):
(JSC::Wasm::BBQPlan::prepare):
(JSC::Wasm::BBQPlan::compileFunctions):
(JSC::Wasm::BBQPlan::complete):
(JSC::Wasm::BBQPlan::work):
* wasm/WasmBBQPlan.h:
BBQPlan has m_source, but once ModuleInformation is parsed, it is no longer necessary.
In subsequent patches, we will remove this, and stream the data into the BBQPlan.

* wasm/WasmFormat.h:
* wasm/WasmModuleInformation.cpp:
(JSC::Wasm::ModuleInformation::ModuleInformation):
* wasm/WasmModuleInformation.h:
One of the largest change in this patch is that ModuleInformation no longer holds source bytes,
since source bytes can be added in a streaming fashion. Instead of holding all the source bytes
in ModuleInformation, each function (ModuleInformation::functions, FunctionData) should have
Vector<uint8_t> for its data. This data is eventually filled by StreamingParser, and compiling
a function with this data can be done concurrently with StreamingParser.

(JSC::Wasm::ModuleInformation::create):
(JSC::Wasm::ModuleInformation::memoryCount const):
(JSC::Wasm::ModuleInformation::tableCount const):
memoryCount and tableCount should be recorded in ModuleInformation.

* wasm/WasmModuleParser.cpp:
(JSC::Wasm::ModuleParser::parse):
(JSC::Wasm::makeI32InitExpr): Deleted.
(JSC::Wasm::ModuleParser::parseType): Deleted.
(JSC::Wasm::ModuleParser::parseImport): Deleted.
(JSC::Wasm::ModuleParser::parseFunction): Deleted.
(JSC::Wasm::ModuleParser::parseResizableLimits): Deleted.
(JSC::Wasm::ModuleParser::parseTableHelper): Deleted.
(JSC::Wasm::ModuleParser::parseTable): Deleted.
(JSC::Wasm::ModuleParser::parseMemoryHelper): Deleted.
(JSC::Wasm::ModuleParser::parseMemory): Deleted.
(JSC::Wasm::ModuleParser::parseGlobal): Deleted.
(JSC::Wasm::ModuleParser::parseExport): Deleted.
(JSC::Wasm::ModuleParser::parseStart): Deleted.
(JSC::Wasm::ModuleParser::parseElement): Deleted.
(JSC::Wasm::ModuleParser::parseCode): Deleted.
(JSC::Wasm::ModuleParser::parseInitExpr): Deleted.
(JSC::Wasm::ModuleParser::parseGlobalType): Deleted.
(JSC::Wasm::ModuleParser::parseData): Deleted.
(JSC::Wasm::ModuleParser::parseCustom): Deleted.
Extract section parsing code out from ModuleParser. We create SectionParser and ModuleParser uses it.
SectionParser is also used by StreamingParser.

* wasm/WasmModuleParser.h:
(): Deleted.
* wasm/WasmNameSection.h:
(JSC::Wasm::NameSection::NameSection):
(JSC::Wasm::NameSection::create):
(JSC::Wasm::NameSection::setHash):
Hash calculation is deferred since all the source is not available in streaming parsing.

* wasm/WasmNameSectionParser.cpp:
(JSC::Wasm::NameSectionParser::parse):
* wasm/WasmNameSectionParser.h:
Use Ref<NameSection>.

* wasm/WasmOMGPlan.cpp:
(JSC::Wasm::OMGPlan::work):
Wasm::Plan no longer have m_source since data will be eventually filled in a streaming fashion.
OMGPlan can get data of the function by using ModuleInformation::functions.

* wasm/WasmParser.h:
(JSC::Wasm::Parser::source const):
(JSC::Wasm::Parser::length const):
(JSC::Wasm::Parser::offset const):
(JSC::Wasm::Parser::fail const):
(JSC::Wasm::makeI32InitExpr):
* wasm/WasmPlan.cpp:
(JSC::Wasm::Plan::Plan):
Wasm::Plan should not have all the source apriori. Streamed data will be pumped from the provider.

* wasm/WasmPlan.h:
* wasm/WasmSectionParser.cpp: Copied from Source/JavaScriptCore/wasm/WasmModuleParser.cpp.
SectionParser is extracted from ModuleParser. And it is used by both the old (currently working)
ModuleParser and the new StreamingParser.

(JSC::Wasm::SectionParser::parseType):
(JSC::Wasm::SectionParser::parseImport):
(JSC::Wasm::SectionParser::parseFunction):
(JSC::Wasm::SectionParser::parseResizableLimits):
(JSC::Wasm::SectionParser::parseTableHelper):
(JSC::Wasm::SectionParser::parseTable):
(JSC::Wasm::SectionParser::parseMemoryHelper):
(JSC::Wasm::SectionParser::parseMemory):
(JSC::Wasm::SectionParser::parseGlobal):
(JSC::Wasm::SectionParser::parseExport):
(JSC::Wasm::SectionParser::parseStart):
(JSC::Wasm::SectionParser::parseElement):
(JSC::Wasm::SectionParser::parseCode):
(JSC::Wasm::SectionParser::parseInitExpr):
(JSC::Wasm::SectionParser::parseGlobalType):
(JSC::Wasm::SectionParser::parseData):
(JSC::Wasm::SectionParser::parseCustom):
* wasm/WasmSectionParser.h: Copied from Source/JavaScriptCore/wasm/WasmModuleParser.h.
* wasm/WasmStreamingParser.cpp: Added.
(JSC::Wasm::parseUInt7):
(JSC::Wasm::StreamingParser::fail):
(JSC::Wasm::StreamingParser::StreamingParser):
(JSC::Wasm::StreamingParser::parseModuleHeader):
(JSC::Wasm::StreamingParser::parseSectionID):
(JSC::Wasm::StreamingParser::parseSectionSize):
(JSC::Wasm::StreamingParser::parseCodeSectionSize):
Code section in Wasm binary is specially handled compared with the other sections since it includes
a bunch of functions. StreamingParser extracts each function in a streaming fashion and enable
streaming validation / compilation of Wasm functions.

(JSC::Wasm::StreamingParser::parseFunctionSize):
(JSC::Wasm::StreamingParser::parseFunctionPayload):
(JSC::Wasm::StreamingParser::parseSectionPayload):
(JSC::Wasm::StreamingParser::consume):
(JSC::Wasm::StreamingParser::consumeVarUInt32):
(JSC::Wasm::StreamingParser::addBytes):
(JSC::Wasm::StreamingParser::failOnState):
(JSC::Wasm::StreamingParser::finalize):
* wasm/WasmStreamingParser.h: Added.
(JSC::Wasm::StreamingParser::addBytes):
(JSC::Wasm::StreamingParser::errorMessage const):
This is our new StreamingParser implementation. StreamingParser::consumeXXX functions get data, and
StreamingParser::parseXXX functions parse consumed data. The user of StreamingParser calls
StreamingParser::addBytes() to pump the bytes stream into the parser. And once all the data is pumped,
the user calls StreamingParser::finalize. StreamingParser is a state machine which feeds on the
incoming byte stream.

* wasm/js/JSWebAssemblyModule.cpp:
(JSC::JSWebAssemblyModule::source const): Deleted.
All the source should not be held.

* wasm/js/JSWebAssemblyModule.h:
* wasm/js/WebAssemblyPrototype.cpp:
(JSC::webAssemblyValidateFunc):

Source/WTF:

Add maxByteLength function to get the maximum size for T.

* wtf/LEBDecoder.h:
(WTF::LEBDecoder::maxByteLength):
(WTF::LEBDecoder::decodeUInt):
(WTF::LEBDecoder::decodeInt):

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

42 files changed:
JSTests/ChangeLog
JSTests/wasm/function-tests/invalid-duplicate-export.js
JSTests/wasm/function-tests/memory-alignment.js
JSTests/wasm/function-tests/memory-section-and-import.js
JSTests/wasm/function-tests/void-argument-type-should-be-a-validation-error.js
JSTests/wasm/js-api/Module-compile.js
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
JSTests/wasm/js-api/version.js
JSTests/wasm/stress/nameSection.wasm [new file with mode: 0644]
JSTests/wasm/stress/streaming-basic.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/Sources.txt
Source/JavaScriptCore/tools/JSDollarVM.cpp
Source/JavaScriptCore/wasm/WasmBBQPlan.cpp
Source/JavaScriptCore/wasm/WasmBBQPlan.h
Source/JavaScriptCore/wasm/WasmFormat.h
Source/JavaScriptCore/wasm/WasmModuleInformation.cpp
Source/JavaScriptCore/wasm/WasmModuleInformation.h
Source/JavaScriptCore/wasm/WasmModuleParser.cpp
Source/JavaScriptCore/wasm/WasmModuleParser.h
Source/JavaScriptCore/wasm/WasmNameSection.h
Source/JavaScriptCore/wasm/WasmNameSectionParser.cpp
Source/JavaScriptCore/wasm/WasmNameSectionParser.h
Source/JavaScriptCore/wasm/WasmOMGPlan.cpp
Source/JavaScriptCore/wasm/WasmParser.h
Source/JavaScriptCore/wasm/WasmPlan.cpp
Source/JavaScriptCore/wasm/WasmPlan.h
Source/JavaScriptCore/wasm/WasmSectionParser.cpp [new file with mode: 0644]
Source/JavaScriptCore/wasm/WasmSectionParser.h [new file with mode: 0644]
Source/JavaScriptCore/wasm/WasmStreamingParser.cpp [new file with mode: 0644]
Source/JavaScriptCore/wasm/WasmStreamingParser.h [new file with mode: 0644]
Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h
Source/JavaScriptCore/wasm/js/WebAssemblyPrototype.cpp
Source/WTF/ChangeLog
Source/WTF/wtf/LEBDecoder.h

index d3dd930..698a160 100644 (file)
@@ -1,3 +1,42 @@
+2018-08-27  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
+
+        [WebAssembly] Parse wasm modules in a streaming fashion
+        https://bugs.webkit.org/show_bug.cgi?id=188943
+
+        Reviewed by Mark Lam.
+
+        Wasm parsing error should not report the total byte size since streaming parsing does not
+        want to load all the bytes.
+        Add a simple test wasm/stress/streaming-basic.js for initial streaming parsing implementation.
+
+        * wasm/function-tests/invalid-duplicate-export.js:
+        * wasm/function-tests/memory-alignment.js:
+        (const.op.of.WASM.opcodes):
+        * wasm/function-tests/memory-section-and-import.js:
+        * wasm/function-tests/void-argument-type-should-be-a-validation-error.js:
+        * wasm/js-api/Module-compile.js:
+        (async.testPromiseAPI):
+        * 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):
+        * wasm/js-api/table.js:
+        (new.WebAssembly.Module):
+        (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):
+        * wasm/js-api/version.js:
+        * wasm/stress/nameSection.wasm: Added.
+        * wasm/stress/streaming-basic.js: Added.
+        (check):
+
 2018-08-27  Mark Lam  <mark.lam@apple.com>
 
         Fix exception throwing code so that topCallFrame and topEntryFrame stay true to their names.
index c47b1e1..418d51a 100644 (file)
@@ -15,5 +15,5 @@ import * as assert from '../assert.js';
         .End();
 
     const bin = builder.WebAssembly().get();
-    assert.throws(() => new WebAssembly.Module(bin), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 31 / 39: duplicate export: 'foo'");
+    assert.throws(() => new WebAssembly.Module(bin), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 31: duplicate export: 'foo'");
 }
index abc5741..e605898 100644 (file)
@@ -42,7 +42,7 @@ for (const op of WASM.opcodes("memory")) {
         if (alignLog2 <= maxAlignLog2)
             instance();
         else
-            assert.throws(instance, WebAssembly.CompileError, `WebAssembly.Module doesn't parse at byte ${start} / ${end}: byte alignment ${1 << alignLog2} exceeds ${info.type}'s natural alignment ${1 << maxAlignLog2}, in function at index 0`);
+            assert.throws(instance, WebAssembly.CompileError, `WebAssembly.Module doesn't parse at byte ${start}: byte alignment ${1 << alignLog2} exceeds ${info.type}'s natural alignment ${1 << maxAlignLog2}, in function at index 0`);
 
     }
 }
index a1db9ce..15207ef 100644 (file)
@@ -28,4 +28,4 @@ const builder1 = (new Builder())
       .Code().End();
 
 const i0 = instantiate(builder0);
-assert.throws(() => instantiate(builder1, { imp: { memory: i0.exports.memory } }), WebAssembly.CompileError, `WebAssembly.Module doesn't parse at byte 35 / 41: there can at most be one Memory section for now`);
+assert.throws(() => instantiate(builder1, { imp: { memory: i0.exports.memory } }), WebAssembly.CompileError, `WebAssembly.Module doesn't parse at byte 35: there can at most be one Memory section for now`);
index 47149b1..2178810 100644 (file)
@@ -17,6 +17,6 @@ function getBinary(params) {
     return builder.WebAssembly().get();
 }
 
-assert.throws(() => new WebAssembly.Module(getBinary(["i32", "void"])), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 15 / 47: can't get 1th argument Type");
-assert.throws(() => new WebAssembly.Module(getBinary(["void"])), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 14 / 46: can't get 0th argument Type");
-assert.throws(() => new WebAssembly.Module(getBinary(["i32", "void", "i32"])), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 15 / 48: can't get 1th argument Type");
+assert.throws(() => new WebAssembly.Module(getBinary(["i32", "void"])), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 15: can't get 1th argument Type");
+assert.throws(() => new WebAssembly.Module(getBinary(["void"])), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 14: can't get 0th argument Type");
+assert.throws(() => new WebAssembly.Module(getBinary(["i32", "void", "i32"])), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 15: can't get 1th argument Type");
index d3beb33..d723bf4 100644 (file)
@@ -21,7 +21,7 @@ async function testPromiseAPI() {
             await WebAssembly.compile(builder.WebAssembly().get());
         } catch(e) {
             assert.truthy(e instanceof WebAssembly.CompileError);
-            assert.truthy(e.message === "WebAssembly.Module doesn't parse at byte 34 / 43: there can at most be one Memory section for now");
+            assert.truthy(e.message === "WebAssembly.Module doesn't parse at byte 34: there can at most be one Memory section for now");
         }
     }
 
index 188ac74..fcbe282 100644 (file)
@@ -18,7 +18,7 @@ import * as assert from '../assert.js';
             .End()
         .End();
 
-    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 24 / 41: Element section for Table 0 exceeds available Table 0");
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 24: Element section for Table 0 exceeds available Table 0");
 }
 
 {
@@ -41,7 +41,7 @@ import * as assert from '../assert.js';
             .End()
         .End();
 
-    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 30 / 47: Element section for Table 1 exceeds available Table 1");
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 30: Element section for Table 1 exceeds available Table 1");
 }
 
 {
@@ -112,7 +112,7 @@ import * as assert from '../assert.js';
             .End()
         .End();
 
-    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())')");
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 38: 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())')");
 }
 
 {
@@ -215,5 +215,5 @@ import * as assert from '../assert.js';
         return new WebAssembly.Module(bin);
     }
 
-    assert.throws(() => badModule(), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 58 / 72: Element init_expr must produce an i32");
+    assert.throws(() => badModule(), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 58: Element init_expr must produce an i32");
 }
index 09f8d79..d3edf89 100644 (file)
@@ -23,7 +23,7 @@ import Builder from '../Builder.js';
     const bin = builder.WebAssembly();
     bin.trim();
 
-    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 globals 0 (evaluating 'new WebAssembly.Module(bin.get())')");
+    assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 26: get_global's index 0 exceeds the number of globals 0 (evaluating 'new WebAssembly.Module(bin.get())')");
 }
 
 
@@ -52,7 +52,7 @@ import Builder from '../Builder.js';
     const bin = builder.WebAssembly();
     bin.trim();
 
-    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())')");
+    assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 32: Mutable Globals aren't supported (evaluating 'new WebAssembly.Module(bin.get())')");
 }
 
 {
@@ -77,7 +77,7 @@ import Builder from '../Builder.js';
     const bin = builder.WebAssembly();
     bin.trim();
 
-    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())')");
+    assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 51: 1th Export isn't immutable, named 'global' (evaluating 'new WebAssembly.Module(bin.get())')");
 }
 
 {
index b832e11..cf68b44 100644 (file)
@@ -13,7 +13,7 @@ import * as assert from '../assert.js';
         .End()
         .Code()
         .End();
-    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 34 / 41: Cannot have more than one Table for now");
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 34: Cannot have more than one Table for now");
 }
 
 {
@@ -38,7 +38,7 @@ import * as assert from '../assert.js';
         .End()
         .Code()
         .End();
-    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, at most 1 is allowed for now (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 17: Table count of 2 is invalid, at most 1 is allowed for now (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
 }
 
 {
@@ -54,7 +54,7 @@ import * as assert from '../assert.js';
                 .CallIndirect(0, 0)
             .End()
         .End();
-    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, in function at index 0 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 4: call_indirect is only valid when a table is defined or imported, in function at index 0 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
 }
 
 {
@@ -73,7 +73,7 @@ import * as assert from '../assert.js';
                 .CallIndirect(0, 1)
             .End()
         .End();
-    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, in function at index 0 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 6: call_indirect's 'reserved' varuint1 must be 0x0, in function at index 0 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')");
 }
 
 {
@@ -86,7 +86,7 @@ import * as assert from '../assert.js';
         .End()
         .Code()
         .End();
-    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 23 / 26: can't export Table 0 there are 0 Tables");
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 23: can't export Table 0 there are 0 Tables");
 }
 
 {
@@ -102,7 +102,7 @@ import * as assert from '../assert.js';
         .End()
         .Code()
         .End();
-    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 30 / 33: can't export Table 1 there are 1 Tables");
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 30: can't export Table 1 there are 1 Tables");
 }
 
 function assertBadTable(tableDescription, message) {
@@ -132,42 +132,42 @@ function assertBadTableImport(tableDescription, message) {
 {
     let badDescriptions = [
         [{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())')"],
+         "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc, got -1 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 26: 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())')"],
+         "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc, got -3 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 26: 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())')"],
+         "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc, got -4 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 26: 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())')"],
+         "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc, got -2 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 26: 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())')"],
+         "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc, got -1 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 26: 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())')"],
+         "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc, got -3 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 26: 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())')"],
+         "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc, got -4 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 26: 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())')"],
+         "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc, got -2 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 26: Table type should be anyfunc, got -2 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"],
 
         [{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())')"],
+         "WebAssembly.Module doesn't parse at byte 21: 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: 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())')"],
+         "WebAssembly.Module doesn't parse at byte 21: 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: 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())')"],
+         "WebAssembly.Module doesn't parse at byte 29: 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: 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 too big, maximum 10000000 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
-         "WebAssembly.Module doesn't parse at byte 32 / 38: Table's initial page count of 2147483648 is too big, maximum 10000000 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"],
+         "WebAssembly.Module doesn't parse at byte 24: Table's initial page count of 2147483648 is too big, maximum 10000000 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')",
+         "WebAssembly.Module doesn't parse at byte 32: Table's initial page count of 2147483648 is too big, maximum 10000000 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"],
     ];
 
     for (const d of badDescriptions) {
@@ -186,7 +186,7 @@ function assertBadTableImport(tableDescription, message) {
         .Function().End()
         .Code()
         .End();
-    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 39 / 48: Cannot have more than one Table for now");
+    assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 39: Cannot have more than one Table for now");
 }
 
 
index cca0cf3..4171435 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, `WebAssembly.Module doesn't parse at byte 15 / 20: 0th Data segment has index 0 which exceeds the number of Memories 0`);
+    assert.throws(() => new WebAssembly.Module(bin), WebAssembly.CompileError, `WebAssembly.Module doesn't parse at byte 15: 0th Data segment has index 0 which exceeds the number of Memories 0`);
 })();
 
 (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, `WebAssembly.Module doesn't parse at byte 15 / 19: 0th Data segment has index 0 which exceeds the number of Memories 0`);
+    assert.throws(() => new WebAssembly.Module(bin), WebAssembly.CompileError, `WebAssembly.Module doesn't parse at byte 15: 0th Data segment has index 0 which exceeds the number of Memories 0`);
 })();
 
 (function DataSectionBiggerThanMemory() {
index b444569..5c3150e 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, `WebAssembly.Module doesn't parse at byte 17 / 20: Start index 0 exceeds function index space 0 (evaluating 'new WebAssembly.Module(bin)')`);
+    assert.throws(() => new WebAssembly.Module(bin), Error, `WebAssembly.Module doesn't parse at byte 17: Start index 0 exceeds function index space 0 (evaluating 'new WebAssembly.Module(bin)')`);
 })();
index 08e74aa..1a2d222 100644 (file)
@@ -67,7 +67,7 @@ for (const c in constructorProperties) {
             assert.throws(() => new WebAssembly[c](invalid), TypeError, `first argument 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, `WebAssembly.Module doesn't parse at byte 0 / 0: expected a module of at least 8 bytes (evaluating 'new WebAssembly[c](buffer)')`);
+            assert.throws(() => new WebAssembly[c](buffer), Error, `WebAssembly.Module doesn't parse at byte 0: expected a module of at least 8 bytes (evaluating 'new WebAssembly[c](buffer)')`);
         assert.instanceof(new WebAssembly[c](emptyModuleArray), WebAssembly.Module);
         break;
     case "Instance":
index 81cd7ff..0486fcf 100644 (file)
@@ -5,5 +5,5 @@ for (let version = 0; version < 256; ++version) {
     if (version === 1)
         continue;
     const emptyModuleArray = Uint8Array.of(0x0, 0x61, 0x73, 0x6d, version, 0x00, 0x00, 0x00);
-    assert.throws(() => new WebAssembly.Module(emptyModuleArray), WebAssembly.CompileError, `WebAssembly.Module doesn't parse at byte 8 / 8: unexpected version number ${version} expected 1`);
+    assert.throws(() => new WebAssembly.Module(emptyModuleArray), WebAssembly.CompileError, `WebAssembly.Module doesn't parse at byte 8: unexpected version number ${version} expected 1`);
 }
diff --git a/JSTests/wasm/stress/nameSection.wasm b/JSTests/wasm/stress/nameSection.wasm
new file mode 100644 (file)
index 0000000..24b22b1
Binary files /dev/null and b/JSTests/wasm/stress/nameSection.wasm differ
diff --git a/JSTests/wasm/stress/streaming-basic.js b/JSTests/wasm/stress/streaming-basic.js
new file mode 100644 (file)
index 0000000..b3fbc24
--- /dev/null
@@ -0,0 +1,18 @@
+import * as assert from '../assert.js';
+
+var data = read("./nameSection.wasm", "binary");
+
+function check(step)
+{
+    var parser = $vm.createWasmStreamingParser();
+    for (var i = 0; i < data.byteLength; i += step) {
+        parser.addBytes(data.subarray(i, i + Math.min(step, data.byteLength - i)));
+    }
+    assert.eq(parser.finalize(), 7);
+}
+
+for (var i = 0; i < 10; ++i)
+    check(i + 1);
+check(100);
+check(1000);
+check(2000);
index f4798da..59898cd 100644 (file)
@@ -1,3 +1,166 @@
+2018-08-27  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
+
+        [WebAssembly] Parse wasm modules in a streaming fashion
+        https://bugs.webkit.org/show_bug.cgi?id=188943
+
+        Reviewed by Mark Lam.
+
+        This patch adds Wasm::StreamingParser, which parses wasm binary in a streaming fashion.
+        Currently, this StreamingParser is not enabled and integrated. In subsequent patches,
+        we start integrating it into BBQPlan and dropping the old ModuleParser.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * Sources.txt:
+        * tools/JSDollarVM.cpp:
+        (WTF::WasmStreamingParser::WasmStreamingParser):
+        (WTF::WasmStreamingParser::create):
+        (WTF::WasmStreamingParser::createStructure):
+        (WTF::WasmStreamingParser::streamingParser):
+        (WTF::WasmStreamingParser::finishCreation):
+        (WTF::functionWasmStreamingParserAddBytes):
+        (WTF::functionWasmStreamingParserFinalize):
+        (JSC::functionCreateWasmStreamingParser):
+        (JSC::JSDollarVM::finishCreation):
+        The $vm Wasm::StreamingParser object is introduced for testing purpose. Added new stress test uses
+        this interface to test streaming parser in the JSC shell.
+
+        * wasm/WasmBBQPlan.cpp:
+        (JSC::Wasm::BBQPlan::BBQPlan):
+        (JSC::Wasm::BBQPlan::parseAndValidateModule):
+        (JSC::Wasm::BBQPlan::prepare):
+        (JSC::Wasm::BBQPlan::compileFunctions):
+        (JSC::Wasm::BBQPlan::complete):
+        (JSC::Wasm::BBQPlan::work):
+        * wasm/WasmBBQPlan.h:
+        BBQPlan has m_source, but once ModuleInformation is parsed, it is no longer necessary.
+        In subsequent patches, we will remove this, and stream the data into the BBQPlan.
+
+        * wasm/WasmFormat.h:
+        * wasm/WasmModuleInformation.cpp:
+        (JSC::Wasm::ModuleInformation::ModuleInformation):
+        * wasm/WasmModuleInformation.h:
+        One of the largest change in this patch is that ModuleInformation no longer holds source bytes,
+        since source bytes can be added in a streaming fashion. Instead of holding all the source bytes
+        in ModuleInformation, each function (ModuleInformation::functions, FunctionData) should have
+        Vector<uint8_t> for its data. This data is eventually filled by StreamingParser, and compiling
+        a function with this data can be done concurrently with StreamingParser.
+
+        (JSC::Wasm::ModuleInformation::create):
+        (JSC::Wasm::ModuleInformation::memoryCount const):
+        (JSC::Wasm::ModuleInformation::tableCount const):
+        memoryCount and tableCount should be recorded in ModuleInformation.
+
+        * wasm/WasmModuleParser.cpp:
+        (JSC::Wasm::ModuleParser::parse):
+        (JSC::Wasm::makeI32InitExpr): Deleted.
+        (JSC::Wasm::ModuleParser::parseType): Deleted.
+        (JSC::Wasm::ModuleParser::parseImport): Deleted.
+        (JSC::Wasm::ModuleParser::parseFunction): Deleted.
+        (JSC::Wasm::ModuleParser::parseResizableLimits): Deleted.
+        (JSC::Wasm::ModuleParser::parseTableHelper): Deleted.
+        (JSC::Wasm::ModuleParser::parseTable): Deleted.
+        (JSC::Wasm::ModuleParser::parseMemoryHelper): Deleted.
+        (JSC::Wasm::ModuleParser::parseMemory): Deleted.
+        (JSC::Wasm::ModuleParser::parseGlobal): Deleted.
+        (JSC::Wasm::ModuleParser::parseExport): Deleted.
+        (JSC::Wasm::ModuleParser::parseStart): Deleted.
+        (JSC::Wasm::ModuleParser::parseElement): Deleted.
+        (JSC::Wasm::ModuleParser::parseCode): Deleted.
+        (JSC::Wasm::ModuleParser::parseInitExpr): Deleted.
+        (JSC::Wasm::ModuleParser::parseGlobalType): Deleted.
+        (JSC::Wasm::ModuleParser::parseData): Deleted.
+        (JSC::Wasm::ModuleParser::parseCustom): Deleted.
+        Extract section parsing code out from ModuleParser. We create SectionParser and ModuleParser uses it.
+        SectionParser is also used by StreamingParser.
+
+        * wasm/WasmModuleParser.h:
+        (): Deleted.
+        * wasm/WasmNameSection.h:
+        (JSC::Wasm::NameSection::NameSection):
+        (JSC::Wasm::NameSection::create):
+        (JSC::Wasm::NameSection::setHash):
+        Hash calculation is deferred since all the source is not available in streaming parsing.
+
+        * wasm/WasmNameSectionParser.cpp:
+        (JSC::Wasm::NameSectionParser::parse):
+        * wasm/WasmNameSectionParser.h:
+        Use Ref<NameSection>.
+
+        * wasm/WasmOMGPlan.cpp:
+        (JSC::Wasm::OMGPlan::work):
+        Wasm::Plan no longer have m_source since data will be eventually filled in a streaming fashion.
+        OMGPlan can get data of the function by using ModuleInformation::functions.
+
+        * wasm/WasmParser.h:
+        (JSC::Wasm::Parser::source const):
+        (JSC::Wasm::Parser::length const):
+        (JSC::Wasm::Parser::offset const):
+        (JSC::Wasm::Parser::fail const):
+        (JSC::Wasm::makeI32InitExpr):
+        * wasm/WasmPlan.cpp:
+        (JSC::Wasm::Plan::Plan):
+        Wasm::Plan should not have all the source apriori. Streamed data will be pumped from the provider.
+
+        * wasm/WasmPlan.h:
+        * wasm/WasmSectionParser.cpp: Copied from Source/JavaScriptCore/wasm/WasmModuleParser.cpp.
+        SectionParser is extracted from ModuleParser. And it is used by both the old (currently working)
+        ModuleParser and the new StreamingParser.
+
+        (JSC::Wasm::SectionParser::parseType):
+        (JSC::Wasm::SectionParser::parseImport):
+        (JSC::Wasm::SectionParser::parseFunction):
+        (JSC::Wasm::SectionParser::parseResizableLimits):
+        (JSC::Wasm::SectionParser::parseTableHelper):
+        (JSC::Wasm::SectionParser::parseTable):
+        (JSC::Wasm::SectionParser::parseMemoryHelper):
+        (JSC::Wasm::SectionParser::parseMemory):
+        (JSC::Wasm::SectionParser::parseGlobal):
+        (JSC::Wasm::SectionParser::parseExport):
+        (JSC::Wasm::SectionParser::parseStart):
+        (JSC::Wasm::SectionParser::parseElement):
+        (JSC::Wasm::SectionParser::parseCode):
+        (JSC::Wasm::SectionParser::parseInitExpr):
+        (JSC::Wasm::SectionParser::parseGlobalType):
+        (JSC::Wasm::SectionParser::parseData):
+        (JSC::Wasm::SectionParser::parseCustom):
+        * wasm/WasmSectionParser.h: Copied from Source/JavaScriptCore/wasm/WasmModuleParser.h.
+        * wasm/WasmStreamingParser.cpp: Added.
+        (JSC::Wasm::parseUInt7):
+        (JSC::Wasm::StreamingParser::fail):
+        (JSC::Wasm::StreamingParser::StreamingParser):
+        (JSC::Wasm::StreamingParser::parseModuleHeader):
+        (JSC::Wasm::StreamingParser::parseSectionID):
+        (JSC::Wasm::StreamingParser::parseSectionSize):
+        (JSC::Wasm::StreamingParser::parseCodeSectionSize):
+        Code section in Wasm binary is specially handled compared with the other sections since it includes
+        a bunch of functions. StreamingParser extracts each function in a streaming fashion and enable
+        streaming validation / compilation of Wasm functions.
+
+        (JSC::Wasm::StreamingParser::parseFunctionSize):
+        (JSC::Wasm::StreamingParser::parseFunctionPayload):
+        (JSC::Wasm::StreamingParser::parseSectionPayload):
+        (JSC::Wasm::StreamingParser::consume):
+        (JSC::Wasm::StreamingParser::consumeVarUInt32):
+        (JSC::Wasm::StreamingParser::addBytes):
+        (JSC::Wasm::StreamingParser::failOnState):
+        (JSC::Wasm::StreamingParser::finalize):
+        * wasm/WasmStreamingParser.h: Added.
+        (JSC::Wasm::StreamingParser::addBytes):
+        (JSC::Wasm::StreamingParser::errorMessage const):
+        This is our new StreamingParser implementation. StreamingParser::consumeXXX functions get data, and
+        StreamingParser::parseXXX functions parse consumed data. The user of StreamingParser calls
+        StreamingParser::addBytes() to pump the bytes stream into the parser. And once all the data is pumped,
+        the user calls StreamingParser::finalize. StreamingParser is a state machine which feeds on the
+        incoming byte stream.
+
+        * wasm/js/JSWebAssemblyModule.cpp:
+        (JSC::JSWebAssemblyModule::source const): Deleted.
+        All the source should not be held.
+
+        * wasm/js/JSWebAssemblyModule.h:
+        * wasm/js/WebAssemblyPrototype.cpp:
+        (JSC::webAssemblyValidateFunc):
+
 2018-08-27  Mark Lam  <mark.lam@apple.com>
 
         Fix exception throwing code so that topCallFrame and topEntryFrame stay true to their names.
index a87e273..e88a74a 100644 (file)
                E393ADD81FE702D00022D681 /* WeakMapImplInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = E393ADD71FE702CC0022D681 /* WeakMapImplInlines.h */; };
                E39D45F51D39005600B3B377 /* InterpreterInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = E39D9D841D39000600667282 /* InterpreterInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E39DA4A71B7E8B7C0084F33A /* JSModuleRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = E39DA4A51B7E8B7C0084F33A /* JSModuleRecord.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               E3A0531A21342B680022EC14 /* WasmStreamingParser.h in Headers */ = {isa = PBXBuildFile; fileRef = E3A0531621342B660022EC14 /* WasmStreamingParser.h */; };
+               E3A0531C21342B680022EC14 /* WasmSectionParser.h in Headers */ = {isa = PBXBuildFile; fileRef = E3A0531821342B670022EC14 /* WasmSectionParser.h */; };
                E3A32BC71FC83147007D7E76 /* WeakMapImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = E3A32BC61FC8312E007D7E76 /* WeakMapImpl.h */; };
                E3A421431D6F58930007C617 /* PreciseJumpTargetsInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = E3A421421D6F588F0007C617 /* PreciseJumpTargetsInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E3BFD0BC1DAF808E0065DEA2 /* AccessCaseSnippetParams.h in Headers */ = {isa = PBXBuildFile; fileRef = E3BFD0BA1DAF807C0065DEA2 /* AccessCaseSnippetParams.h */; };
                E39D9D841D39000600667282 /* InterpreterInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InterpreterInlines.h; sourceTree = "<group>"; };
                E39DA4A41B7E8B7C0084F33A /* JSModuleRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSModuleRecord.cpp; sourceTree = "<group>"; };
                E39DA4A51B7E8B7C0084F33A /* JSModuleRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSModuleRecord.h; sourceTree = "<group>"; };
+               E3A0531621342B660022EC14 /* WasmStreamingParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmStreamingParser.h; sourceTree = "<group>"; };
+               E3A0531721342B660022EC14 /* WasmSectionParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmSectionParser.cpp; sourceTree = "<group>"; };
+               E3A0531821342B670022EC14 /* WasmSectionParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmSectionParser.h; sourceTree = "<group>"; };
+               E3A0531921342B670022EC14 /* WasmStreamingParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmStreamingParser.cpp; sourceTree = "<group>"; };
                E3A32BC51FC8312D007D7E76 /* WeakMapImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WeakMapImpl.cpp; sourceTree = "<group>"; };
                E3A32BC61FC8312E007D7E76 /* WeakMapImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WeakMapImpl.h; sourceTree = "<group>"; };
                E3A421421D6F588F0007C617 /* PreciseJumpTargetsInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreciseJumpTargetsInlines.h; sourceTree = "<group>"; };
                                53F40E8C1D5901F20099A1B6 /* WasmParser.h */,
                                531374BE1D5CE95000AF7A0B /* WasmPlan.cpp */,
                                531374BC1D5CE67600AF7A0B /* WasmPlan.h */,
+                               E3A0531721342B660022EC14 /* WasmSectionParser.cpp */,
+                               E3A0531821342B670022EC14 /* WasmSectionParser.h */,
                                53F40E841D58F9770099A1B6 /* WasmSections.h */,
                                AD7438BE1E04579200FD0C2A /* WasmSignature.cpp */,
                                AD7438BF1E04579200FD0C2A /* WasmSignature.h */,
+                               E3A0531921342B670022EC14 /* WasmStreamingParser.cpp */,
+                               E3A0531621342B660022EC14 /* WasmStreamingParser.h */,
                                AD5C36E31F69EC8B000BCAAF /* WasmTable.cpp */,
                                AD5C36E41F69EC8B000BCAAF /* WasmTable.h */,
                                5250D2CF1E8DA05A0029A932 /* WasmThunks.cpp */,
                                C2FCAE1317A9C24E0034C735 /* BytecodeLivenessAnalysis.h in Headers */,
                                0F666EC0183566F900D017F1 /* BytecodeLivenessAnalysisInlines.h in Headers */,
                                E328DAEB1D38D005001A2529 /* BytecodeRewriter.h in Headers */,
-                               FEA3BBAC212C97CB00E93AD1 /* DFGCFG.h in Headers */,
                                6514F21918B3E1670098FF8B /* Bytecodes.h in Headers */,
                                0F885E111849A3BE00F1E3FA /* BytecodeUseDef.h in Headers */,
                                0F8023EA1613832B00A0BA45 /* ByValInfo.h in Headers */,
                                0FBDB9AD1AB0FBC6000B57E5 /* DFGCallCreateDirectArgumentsSlowPathGenerator.h in Headers */,
                                0F7B294B14C3CD2F007C3DB1 /* DFGCapabilities.h in Headers */,
                                0FFFC95814EF90A200C72532 /* DFGCFAPhase.h in Headers */,
+                               FEA3BBAC212C97CB00E93AD1 /* DFGCFG.h in Headers */,
                                0F3B3A281544C997003ED0FF /* DFGCFGSimplificationPhase.h in Headers */,
                                0F9D36951AE9CC33000D4DFB /* DFGCleanUpPhase.h in Headers */,
                                A77A424017A0BBFD00A8DB81 /* DFGClobberize.h in Headers */,
                                79B759761DFA4C600052174C /* WasmPageCount.h in Headers */,
                                53F40E8D1D5901F20099A1B6 /* WasmParser.h in Headers */,
                                531374BD1D5CE67600AF7A0B /* WasmPlan.h in Headers */,
+                               E3A0531C21342B680022EC14 /* WasmSectionParser.h in Headers */,
                                53F40E851D58F9770099A1B6 /* WasmSections.h in Headers */,
                                AD7438C01E0457A400FD0C2A /* WasmSignature.h in Headers */,
+                               E3A0531A21342B680022EC14 /* WasmStreamingParser.h in Headers */,
                                AD5C36E61F69EC91000BCAAF /* WasmTable.h in Headers */,
                                5250D2D21E8DA05A0029A932 /* WasmThunks.h in Headers */,
                                53E9E0AF1EAEC45700FEE251 /* WasmTierUpCount.h in Headers */,
index f587854..e2baf1a 100644 (file)
@@ -991,7 +991,9 @@ wasm/WasmOMGPlan.cpp
 wasm/WasmOpcodeOrigin.cpp
 wasm/WasmPageCount.cpp
 wasm/WasmPlan.cpp
+wasm/WasmSectionParser.cpp
 wasm/WasmSignature.cpp
+wasm/WasmStreamingParser.cpp
 wasm/WasmTable.cpp
 wasm/WasmTable.h
 wasm/WasmThunks.cpp
index eb57c78..ef25572 100644 (file)
 #include <wtf/ProcessID.h>
 #include <wtf/StringPrintStream.h>
 
+#if ENABLE(WEBASSEMBLY)
+#include "JSWebAssemblyHelpers.h"
+#include "WasmStreamingParser.h"
+#endif
+
 using namespace JSC;
 using namespace WTF;
 
@@ -1093,6 +1098,80 @@ void Element::finishCreation(VM& vm, Root* root)
     m_root->setElement(this);
 }
 
+#if ENABLE(WEBASSEMBLY)
+
+static EncodedJSValue JSC_HOST_CALL functionWasmStreamingParserAddBytes(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionWasmStreamingParserFinalize(ExecState*);
+
+class WasmStreamingParser : public JSDestructibleObject {
+public:
+    WasmStreamingParser(VM& vm, Structure* structure)
+        : Base(vm, structure)
+        , m_info(Wasm::ModuleInformation::create())
+        , m_streamingParser(m_info.get())
+    {
+    }
+
+    using Base = JSDestructibleObject;
+
+    static WasmStreamingParser* create(VM& vm, JSGlobalObject* globalObject)
+    {
+        Structure* structure = createStructure(vm, globalObject, jsNull());
+        WasmStreamingParser* result = new (NotNull, allocateCell<WasmStreamingParser>(vm.heap, sizeof(WasmStreamingParser))) WasmStreamingParser(vm, structure);
+        result->finishCreation(vm);
+        return result;
+    }
+
+    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+    {
+        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
+    }
+
+    Wasm::StreamingParser& streamingParser() { return m_streamingParser; }
+
+    void finishCreation(VM& vm)
+    {
+        Base::finishCreation(vm);
+
+        JSGlobalObject* globalObject = this->globalObject(vm);
+        putDirectNativeFunction(vm, globalObject, Identifier::fromString(&vm, "addBytes"), 0, functionWasmStreamingParserAddBytes, NoIntrinsic, static_cast<unsigned>(PropertyAttribute::DontEnum));
+        putDirectNativeFunction(vm, globalObject, Identifier::fromString(&vm, "finalize"), 0, functionWasmStreamingParserFinalize, NoIntrinsic, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    }
+
+    DECLARE_INFO;
+
+    Ref<Wasm::ModuleInformation> m_info;
+    Wasm::StreamingParser m_streamingParser;
+};
+
+const ClassInfo WasmStreamingParser::s_info = { "WasmStreamingParser", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(WasmStreamingParser) };
+
+EncodedJSValue JSC_HOST_CALL functionWasmStreamingParserAddBytes(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(exec->vm());
+    auto* thisObject = jsDynamicCast<WasmStreamingParser*>(vm, exec->thisValue());
+    if (!thisObject) {
+        scope.release();
+        return JSValue::encode(jsBoolean(false));
+    }
+    auto data = getWasmBufferFromValue(exec, exec->argument(0));
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    scope.release();
+    return JSValue::encode(jsNumber(static_cast<int32_t>(thisObject->streamingParser().addBytes(bitwise_cast<const uint8_t*>(data.first), data.second))));
+}
+
+EncodedJSValue JSC_HOST_CALL functionWasmStreamingParserFinalize(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto* thisObject = jsDynamicCast<WasmStreamingParser*>(vm, exec->thisValue());
+    if (!thisObject)
+        return JSValue::encode(jsBoolean(false));
+    return JSValue::encode(jsNumber(static_cast<int32_t>(thisObject->streamingParser().finalize())));
+}
+
+#endif
+
 } // namespace
 
 namespace JSC {
@@ -1640,6 +1719,15 @@ static EncodedJSValue JSC_HOST_CALL functionCreateDOMJITGetterBaseJSObject(ExecS
     return JSValue::encode(result);
 }
 
+#if ENABLE(WEBASSEMBLY)
+static EncodedJSValue JSC_HOST_CALL functionCreateWasmStreamingParser(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    JSLockHolder lock(vm);
+    return JSValue::encode(WasmStreamingParser::create(vm, exec->lexicalGlobalObject()));
+}
+#endif
+
 static EncodedJSValue JSC_HOST_CALL functionSetImpureGetterDelegate(ExecState* exec)
 {
     VM& vm = exec->vm();
@@ -2040,6 +2128,9 @@ void JSDollarVM::finishCreation(VM& vm)
     addFunction(vm, "createDOMJITCheckSubClassObject", functionCreateDOMJITCheckSubClassObject, 0);
     addFunction(vm, "createDOMJITGetterBaseJSObject", functionCreateDOMJITGetterBaseJSObject, 0);
     addFunction(vm, "createBuiltin", functionCreateBuiltin, 2);
+#if ENABLE(WEBASSEMBLY)
+    addFunction(vm, "createWasmStreamingParser", functionCreateWasmStreamingParser, 0);
+#endif
     addFunction(vm, "getPrivateProperty", functionGetPrivateProperty, 2);
     addFunction(vm, "setImpureGetterDelegate", functionSetImpureGetterDelegate, 2);
 
index f672686..43c8f56 100644 (file)
@@ -59,13 +59,15 @@ BBQPlan::BBQPlan(Context* context, Ref<ModuleInformation> info, AsyncWork work,
 }
 
 BBQPlan::BBQPlan(Context* context, Vector<uint8_t>&& source, AsyncWork work, CompletionTask&& task, CreateEmbedderWrapper&& createEmbedderWrapper, ThrowWasmException throwWasmException)
-    : BBQPlan(context, adoptRef(*new ModuleInformation(WTFMove(source))), work, WTFMove(task), WTFMove(createEmbedderWrapper), throwWasmException)
+    : Base(context, ModuleInformation::create(), WTFMove(task), WTFMove(createEmbedderWrapper), throwWasmException)
+    , m_source(WTFMove(source))
+    , m_state(State::Initial)
+    , m_asyncWork(work)
 {
-    m_state = State::Initial;
 }
 
-BBQPlan::BBQPlan(Context* context, const uint8_t* source, size_t sourceLength, AsyncWork work, CompletionTask&& task)
-    : Base(context, source, sourceLength, WTFMove(task))
+BBQPlan::BBQPlan(Context* context, AsyncWork work, CompletionTask&& task)
+    : Base(context, WTFMove(task))
     , m_state(State::Initial)
     , m_asyncWork(work)
 {
@@ -90,7 +92,7 @@ void BBQPlan::moveToState(State state)
     m_state = state;
 }
 
-bool BBQPlan::parseAndValidateModule()
+bool BBQPlan::parseAndValidateModule(const uint8_t* source, size_t sourceLength)
 {
     if (m_state != State::Initial)
         return true;
@@ -101,7 +103,7 @@ bool BBQPlan::parseAndValidateModule()
         startTime = MonotonicTime::now();
 
     {
-        ModuleParser moduleParser(m_source, m_sourceLength, m_moduleInformation);
+        ModuleParser moduleParser(source, sourceLength, m_moduleInformation);
         auto parseResult = moduleParser.parse();
         if (!parseResult) {
             Base::fail(holdLock(m_lock), WTFMove(parseResult.error()));
@@ -109,20 +111,21 @@ bool BBQPlan::parseAndValidateModule()
         }
     }
 
-    const auto& functionLocations = m_moduleInformation->functionLocationInBinary;
-    for (unsigned functionIndex = 0; functionIndex < functionLocations.size(); ++functionIndex) {
-        dataLogLnIf(WasmBBQPlanInternal::verbose, "Processing function starting at: ", functionLocations[functionIndex].start, " and ending at: ", functionLocations[functionIndex].end);
-        const uint8_t* functionStart = m_source + functionLocations[functionIndex].start;
-        size_t functionLength = functionLocations[functionIndex].end - functionLocations[functionIndex].start;
-        ASSERT(functionLength <= m_sourceLength);
+    const auto& functions = m_moduleInformation->functions;
+    for (unsigned functionIndex = 0; functionIndex < functions.size(); ++functionIndex) {
+        const auto& function = functions[functionIndex];
+        dataLogLnIf(WasmBBQPlanInternal::verbose, "Processing function starting at: ", function.start, " and ending at: ", function.end);
+        size_t functionLength = function.end - function.start;
+        ASSERT(functionLength <= sourceLength);
+        ASSERT(functionLength == function.data.size());
         SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
         const Signature& signature = SignatureInformation::get(signatureIndex);
 
-        auto validationResult = validateFunction(functionStart, functionLength, signature, m_moduleInformation.get());
+        auto validationResult = validateFunction(function.data.data(), function.data.size(), signature, m_moduleInformation.get());
         if (!validationResult) {
             if (WasmBBQPlanInternal::verbose) {
                 for (unsigned i = 0; i < functionLength; ++i)
-                    dataLog(RawPointer(reinterpret_cast<void*>(functionStart[i])), ", ");
+                    dataLog(RawPointer(reinterpret_cast<void*>(function.data[i])), ", ");
                 dataLogLn();
             }
             Base::fail(holdLock(m_lock), makeString(validationResult.error(), ", in function at index ", String::number(functionIndex))); // FIXME make this an Expected.
@@ -156,18 +159,18 @@ void BBQPlan::prepare()
         return true;
     };
 
-    const auto& functionLocations = m_moduleInformation->functionLocationInBinary;
+    const auto& functions = m_moduleInformation->functions;
     if (!tryReserveCapacity(m_wasmToWasmExitStubs, m_moduleInformation->importFunctionSignatureIndices.size(), " WebAssembly to JavaScript stubs")
-        || !tryReserveCapacity(m_unlinkedWasmToWasmCalls, functionLocations.size(), " unlinked WebAssembly to WebAssembly calls")
-        || !tryReserveCapacity(m_wasmInternalFunctions, functionLocations.size(), " WebAssembly functions")
-        || !tryReserveCapacity(m_compilationContexts, functionLocations.size(), " compilation contexts")
-        || !tryReserveCapacity(m_tierUpCounts, functionLocations.size(), " tier-up counts"))
+        || !tryReserveCapacity(m_unlinkedWasmToWasmCalls, functions.size(), " unlinked WebAssembly to WebAssembly calls")
+        || !tryReserveCapacity(m_wasmInternalFunctions, functions.size(), " WebAssembly functions")
+        || !tryReserveCapacity(m_compilationContexts, functions.size(), " compilation contexts")
+        || !tryReserveCapacity(m_tierUpCounts, functions.size(), " tier-up counts"))
         return;
 
-    m_unlinkedWasmToWasmCalls.resize(functionLocations.size());
-    m_wasmInternalFunctions.resize(functionLocations.size());
-    m_compilationContexts.resize(functionLocations.size());
-    m_tierUpCounts.resize(functionLocations.size());
+    m_unlinkedWasmToWasmCalls.resize(functions.size());
+    m_wasmInternalFunctions.resize(functions.size());
+    m_compilationContexts.resize(functions.size());
+    m_tierUpCounts.resize(functions.size());
 
     for (unsigned importIndex = 0; importIndex < m_moduleInformation->imports.size(); ++importIndex) {
         Import* import = &m_moduleInformation->imports[importIndex];
@@ -241,7 +244,7 @@ void BBQPlan::compileFunctions(CompilationEffort effort)
     ThreadCountHolder holder(*this);
 
     size_t bytesCompiled = 0;
-    const auto& functionLocations = m_moduleInformation->functionLocationInBinary;
+    const auto& functions = m_moduleInformation->functions;
     while (true) {
         if (effort == Partial && bytesCompiled >= Options::webAssemblyPartialCompileLimit())
             return;
@@ -249,7 +252,7 @@ void BBQPlan::compileFunctions(CompilationEffort effort)
         uint32_t functionIndex;
         {
             auto locker = holdLock(m_lock);
-            if (m_currentIndex >= functionLocations.size()) {
+            if (m_currentIndex >= functions.size()) {
                 if (hasWork())
                     moveToState(State::Compiled);
                 return;
@@ -258,18 +261,16 @@ void BBQPlan::compileFunctions(CompilationEffort effort)
             ++m_currentIndex;
         }
 
-        const uint8_t* functionStart = m_source + functionLocations[functionIndex].start;
-        size_t functionLength = functionLocations[functionIndex].end - functionLocations[functionIndex].start;
-        ASSERT(functionLength <= m_sourceLength);
+        const auto& function = functions[functionIndex];
         SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
         const Signature& signature = SignatureInformation::get(signatureIndex);
         unsigned functionIndexSpace = m_wasmToWasmExitStubs.size() + functionIndex;
         ASSERT_UNUSED(functionIndexSpace, m_moduleInformation->signatureIndexFromFunctionIndexSpace(functionIndexSpace) == signatureIndex);
-        ASSERT(validateFunction(functionStart, functionLength, signature, m_moduleInformation.get()));
+        ASSERT(validateFunction(function.data.data(), function.data.size(), signature, m_moduleInformation.get()));
 
         m_unlinkedWasmToWasmCalls[functionIndex] = Vector<UnlinkedWasmToWasmCall>();
         TierUpCount* tierUp = Options::useBBQTierUpChecks() ? &m_tierUpCounts[functionIndex] : nullptr;
-        auto parseAndCompileResult = parseAndCompile(m_compilationContexts[functionIndex], functionStart, functionLength, signature, m_unlinkedWasmToWasmCalls[functionIndex], m_moduleInformation.get(), m_mode, CompilationMode::BBQMode, functionIndex, tierUp, m_throwWasmException);
+        auto parseAndCompileResult = parseAndCompile(m_compilationContexts[functionIndex], function.data.data(), function.data.size(), signature, m_unlinkedWasmToWasmCalls[functionIndex], m_moduleInformation.get(), m_mode, CompilationMode::BBQMode, functionIndex, tierUp, m_throwWasmException);
 
         if (UNLIKELY(!parseAndCompileResult)) {
             auto locker = holdLock(m_lock);
@@ -277,7 +278,7 @@ void BBQPlan::compileFunctions(CompilationEffort effort)
                 // Multiple compiles could fail simultaneously. We arbitrarily choose the first.
                 fail(locker, makeString(parseAndCompileResult.error(), ", in function at index ", String::number(functionIndex))); // FIXME make this an Expected.
             }
-            m_currentIndex = functionLocations.size();
+            m_currentIndex = functions.size();
             return;
         }
 
@@ -289,17 +290,17 @@ void BBQPlan::compileFunctions(CompilationEffort effort)
             ASSERT_UNUSED(result, result.isNewEntry);
         }
 
-        bytesCompiled += functionLength;
+        bytesCompiled += function.data.size();
     }
 }
 
 void BBQPlan::complete(const AbstractLocker& locker)
 {
-    ASSERT(m_state != State::Compiled || m_currentIndex >= m_moduleInformation->functionLocationInBinary.size());
+    ASSERT(m_state != State::Compiled || m_currentIndex >= m_moduleInformation->functions.size());
     dataLogLnIf(WasmBBQPlanInternal::verbose, "Starting Completion");
 
     if (!failed() && m_state == State::Compiled) {
-        for (uint32_t functionIndex = 0; functionIndex < m_moduleInformation->functionLocationInBinary.size(); functionIndex++) {
+        for (uint32_t functionIndex = 0; functionIndex < m_moduleInformation->functions.size(); functionIndex++) {
             CompilationContext& context = m_compilationContexts[functionIndex];
             SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
             const Signature& signature = SignatureInformation::get(signatureIndex);
@@ -351,7 +352,7 @@ void BBQPlan::work(CompilationEffort effort)
 {
     switch (m_state) {
     case State::Initial:
-        parseAndValidateModule();
+        parseAndValidateModule(m_source.data(), m_source.size());
         if (!hasWork()) {
             ASSERT(isComplete());
             return;
index d88145d..941ac75 100644 (file)
@@ -52,11 +52,14 @@ public:
     // Note: CompletionTask should not hold a reference to the Plan otherwise there will be a reference cycle.
     BBQPlan(Context*, Ref<ModuleInformation>, AsyncWork, CompletionTask&&, CreateEmbedderWrapper&&, ThrowWasmException);
     JS_EXPORT_PRIVATE BBQPlan(Context*, Vector<uint8_t>&&, AsyncWork, CompletionTask&&, CreateEmbedderWrapper&&, ThrowWasmException);
-    // Note: This constructor should only be used if you are not actually building a module e.g. validation/function tests
-    // FIXME: When we get rid of function tests we should remove AsyncWork from this constructor.
-    JS_EXPORT_PRIVATE BBQPlan(Context*, const uint8_t*, size_t, AsyncWork, CompletionTask&&);
+    BBQPlan(Context*, AsyncWork, CompletionTask&&);
 
-    bool parseAndValidateModule();
+
+    bool parseAndValidateModule()
+    {
+        return parseAndValidateModule(m_source.data(), m_source.size());
+    }
+    bool parseAndValidateModule(const uint8_t*, size_t);
 
     JS_EXPORT_PRIVATE void prepare();
     void compileFunctions(CompilationEffort);
@@ -136,6 +139,7 @@ private:
 
     const char* stateString(State);
     
+    Vector<uint8_t> m_source;
     Bag<CallLinkInfo> m_callLinkInfos;
     Vector<MacroAssemblerCodeRef<WasmEntryPtrTag>> m_wasmToWasmExitStubs;
     Vector<std::unique_ptr<InternalFunction>> m_wasmInternalFunctions;
index 2784e90..e43f4ed 100644 (file)
@@ -140,9 +140,10 @@ struct Global {
     uint64_t initialBitsOrImportNumber { 0 };
 };
 
-struct FunctionLocationInBinary {
+struct FunctionData {
     size_t start;
     size_t end;
+    Vector<uint8_t> data;
 };
 
 class I32InitExpr {
index cb8db0e..8a254c0 100644 (file)
 #include "WasmNameSection.h"
 #include <wtf/SHA1.h>
 
-namespace {
-CString sha1(const Vector<uint8_t>& input)
-{
-    SHA1 hash;
-    hash.addBytes(input);
-    return hash.computeHexDigest();
-}
-}
-
 namespace JSC { namespace Wasm {
 
-ModuleInformation::ModuleInformation(Vector<uint8_t>&& sourceBytes)
-    : source(WTFMove(sourceBytes))
-    , hash(Options::useEagerWebAssemblyModuleHashing() ? std::make_optional(sha1(source)) : std::nullopt)
-    , nameSection(new NameSection(hash))
+ModuleInformation::ModuleInformation()
+    : nameSection(NameSection::create())
 {
 }
 ModuleInformation::~ModuleInformation() { }
index 0bab57e..aa7bfc0 100644 (file)
 namespace JSC { namespace Wasm {
 
 struct ModuleInformation : public ThreadSafeRefCounted<ModuleInformation> {
-    ModuleInformation() = delete;
+    ModuleInformation();
     ModuleInformation(const ModuleInformation&) = delete;
     ModuleInformation(ModuleInformation&&) = delete;
 
-    ModuleInformation(Vector<uint8_t>&& sourceBytes);
+    static Ref<ModuleInformation> create()
+    {
+        return adoptRef(*new ModuleInformation);
+    }
 
     JS_EXPORT_PRIVATE ~ModuleInformation();
     
@@ -58,8 +61,10 @@ struct ModuleInformation : public ThreadSafeRefCounted<ModuleInformation> {
     uint32_t importFunctionCount() const { return importFunctionSignatureIndices.size(); }
     uint32_t internalFunctionCount() const { return internalFunctionSignatureIndices.size(); }
 
-    const Vector<uint8_t> source;
-    const std::optional<CString> hash;
+    // Currently, our wasm implementation allows only one memory and table.
+    // If we need to remove this limitation, we would have MemoryInformation and TableInformation in the Vectors.
+    uint32_t memoryCount() const { return memory ? 1 : 0; }
+    uint32_t tableCount() const { return tableInformation ? 1 : 0; }
 
     Vector<Import> imports;
     Vector<SignatureIndex> importFunctionSignatureIndices;
@@ -68,7 +73,7 @@ struct ModuleInformation : public ThreadSafeRefCounted<ModuleInformation> {
 
     MemoryInformation memory;
 
-    Vector<FunctionLocationInBinary> functionLocationInBinary;
+    Vector<FunctionData> functions;
 
     Vector<Export> exports;
     std::optional<uint32_t> startFunctionIndexSpace;
@@ -78,7 +83,7 @@ struct ModuleInformation : public ThreadSafeRefCounted<ModuleInformation> {
     Vector<Global> globals;
     unsigned firstInternalGlobal { 0 };
     Vector<CustomSection> customSections;
-    RefPtr<NameSection> nameSection;
+    Ref<NameSection> nameSection;
 };
 
     
index f03e698..225dba6 100644 (file)
 #include "WasmMemoryInformation.h"
 #include "WasmNameSectionParser.h"
 #include "WasmOps.h"
+#include "WasmSectionParser.h"
 #include "WasmSections.h"
 
 namespace JSC { namespace Wasm {
 
-ALWAYS_INLINE I32InitExpr makeI32InitExpr(uint8_t opcode, uint32_t bits)
-{
-    RELEASE_ASSERT(opcode == I32Const || opcode == GetGlobal);
-    if (opcode == I32Const)
-        return I32InitExpr::constValue(bits);
-    return I32InitExpr::globalImport(bits);
-}
-
 auto ModuleParser::parse() -> Result
 {
     const size_t minSize = 8;
@@ -71,19 +64,19 @@ auto ModuleParser::parse() -> Result
         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;
+        SectionParser parser(source() + m_offset, sectionLength, m_offset, m_info.get());
 
         switch (section) {
 #define WASM_SECTION_PARSE(NAME, ID, DESCRIPTION)                   \
         case Section::NAME: {                                       \
-            WASM_FAIL_IF_HELPER_FAILS(parse ## NAME());             \
+            WASM_FAIL_IF_HELPER_FAILS(parser.parse ## NAME());             \
             break;                                                  \
         }
         FOR_EACH_KNOWN_WASM_SECTION(WASM_SECTION_PARSE)
 #undef WASM_SECTION_PARSE
 
         case Section::Custom: {
-            WASM_FAIL_IF_HELPER_FAILS(parseCustom(sectionLength));
+            WASM_FAIL_IF_HELPER_FAILS(parser.parseCustom());
             break;
         }
 
@@ -93,538 +86,24 @@ auto ModuleParser::parse() -> Result
         }
         }
 
-        WASM_PARSER_FAIL_IF(end != m_offset, "parsing ended before the end of ", section, " section");
+        WASM_PARSER_FAIL_IF(parser.length() != parser.offset(), "parsing ended before the end of ", section, " section");
+
+        m_offset += sectionLength;
 
 
         if (isKnownSection(section))
             previousKnownSection = section;
     }
 
-    return { };
-}
-
-auto ModuleParser::parseType() -> PartialResult
-{
-    uint32_t count;
-
-    WASM_PARSER_FAIL_IF(!parseVarUInt32(count), "can't get Type section's count");
-    WASM_PARSER_FAIL_IF(count > maxTypes, "Type section's count is too big ", count, " maximum ", maxTypes);
-    WASM_PARSER_FAIL_IF(!m_info->usedSignatures.tryReserveCapacity(count), "can't allocate enough memory for Type section's ", count, " entries");
-
-    for (uint32_t i = 0; i < count; ++i) {
-        int8_t type;
-        uint32_t argumentCount;
-        Vector<Type> argumentTypes;
-
-        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 > maxFunctionParams, i, "th argument count is too big ", argumentCount, " maximum ", maxFunctionParams);
-        RefPtr<Signature> maybeSignature = Signature::tryCreate(argumentCount);
-        WASM_PARSER_FAIL_IF(!maybeSignature, "can't allocate enough memory for Type section's ", i, "th signature");
-        Ref<Signature> signature = maybeSignature.releaseNonNull();
-
-        for (unsigned i = 0; i < argumentCount; ++i) {
-            Type argumentType;
-            WASM_PARSER_FAIL_IF(!parseValueType(argumentType), "can't get ", i, "th argument Type");
-            signature->argument(i) = argumentType;
-        }
-
-        uint8_t returnCount;
-        WASM_PARSER_FAIL_IF(!parseVarUInt1(returnCount), "can't get ", i, "th Type's return count");
-        Type returnType;
-        if (returnCount) {
-            Type value;
-            WASM_PARSER_FAIL_IF(!parseValueType(value), "can't get ", i, "th Type's return value");
-            returnType = static_cast<Type>(value);
-        } else
-            returnType = Type::Void;
-        signature->returnType() = returnType;
-
-        std::pair<SignatureIndex, Ref<Signature>> result = SignatureInformation::adopt(WTFMove(signature));
-        m_info->usedSignatures.uncheckedAppend(WTFMove(result.second));
-    }
-    return { };
-}
-
-auto ModuleParser::parseImport() -> PartialResult
-{
-    uint32_t importCount;
-    WASM_PARSER_FAIL_IF(!parseVarUInt32(importCount), "can't get Import section's count");
-    WASM_PARSER_FAIL_IF(importCount > maxImports, "Import section's count is too big ", importCount, " maximum ", maxImports);
-    WASM_PARSER_FAIL_IF(!m_info->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_info->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_info->importFunctionSignatureIndices.tryReserveCapacity(importCount), "can't allocate enough memory for ", importCount, " import function signatures"); // FIXME this over-allocates when we fix the FIXMEs below.
-
-    for (uint32_t importNumber = 0; importNumber < importCount; ++importNumber) {
-        uint32_t moduleLen;
-        uint32_t fieldLen;
-        Name moduleString;
-        Name fieldString;
-        ExternalKind kind;
-        unsigned kindIndex { 0 };
-
-        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);
-
-        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, "'");
-
-        WASM_PARSER_FAIL_IF(!parseExternalKind(kind), "can't get ", importNumber, "th Import's kind in module '", moduleString, "' field '", fieldString, "'");
-        switch (kind) {
-        case ExternalKind::Function: {
-            uint32_t functionSignatureIndex;
-            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_info->usedSignatures.size(), "invalid function signature for ", importNumber, "th Import, ", functionSignatureIndex, " is out of range of ", m_info->usedSignatures.size(), " in module '", moduleString, "' field '", fieldString, "'");
-            kindIndex = m_info->importFunctionSignatureIndices.size();
-            SignatureIndex signatureIndex = SignatureInformation::get(m_info->usedSignatures[functionSignatureIndex]);
-            m_info->importFunctionSignatureIndices.uncheckedAppend(signatureIndex);
-            break;
-        }
-        case ExternalKind::Table: {
-            bool isImport = true;
-            PartialResult result = parseTableHelper(isImport);
-            if (UNLIKELY(!result))
-                return makeUnexpected(WTFMove(result.error()));
-            break;
-        }
-        case ExternalKind::Memory: {
-            bool isImport = true;
-            PartialResult result = parseMemoryHelper(isImport);
-            if (UNLIKELY(!result))
-                return makeUnexpected(WTFMove(result.error()));
-            break;
-        }
-        case ExternalKind::Global: {
-            Global global;
-            WASM_FAIL_IF_HELPER_FAILS(parseGlobalType(global));
-            WASM_PARSER_FAIL_IF(global.mutability == Global::Mutable, "Mutable Globals aren't supported");
-
-            kindIndex = m_info->globals.size();
-            m_info->globals.uncheckedAppend(WTFMove(global));
-            break;
-        }
-        }
-
-        m_info->imports.uncheckedAppend({ WTFMove(moduleString), WTFMove(fieldString), kind, kindIndex });
-    }
-
-    m_info->firstInternalGlobal = m_info->globals.size();
-    return { };
-}
-
-auto ModuleParser::parseFunction() -> PartialResult
-{
-    uint32_t count;
-    WASM_PARSER_FAIL_IF(!parseVarUInt32(count), "can't get Function section's count");
-    WASM_PARSER_FAIL_IF(count > maxFunctions, "Function section's count is too big ", count, " maximum ", maxFunctions);
-    WASM_PARSER_FAIL_IF(!m_info->internalFunctionSignatureIndices.tryReserveCapacity(count), "can't allocate enough memory for ", count, " Function signatures");
-    WASM_PARSER_FAIL_IF(!m_info->functionLocationInBinary.tryReserveCapacity(count), "can't allocate enough memory for ", count, "Function locations");
-
-    for (uint32_t i = 0; i < count; ++i) {
-        uint32_t typeNumber;
-        WASM_PARSER_FAIL_IF(!parseVarUInt32(typeNumber), "can't get ", i, "th Function's type number");
-        WASM_PARSER_FAIL_IF(typeNumber >= m_info->usedSignatures.size(), i, "th Function type number is invalid ", typeNumber);
-
-        SignatureIndex signatureIndex = SignatureInformation::get(m_info->usedSignatures[typeNumber]);
-        // The Code section fixes up start and end.
-        size_t start = 0;
-        size_t end = 0;
-        m_info->internalFunctionSignatureIndices.uncheckedAppend(signatureIndex);
-        m_info->functionLocationInBinary.uncheckedAppend({ start, end });
+    if (UNLIKELY(Options::useEagerWebAssemblyModuleHashing())) {
+        SHA1 hasher;
+        hasher.addBytes(source(), length());
+        m_info->nameSection->setHash(hasher.computeHexDigest());
     }
 
     return { };
 }
 
-auto ModuleParser::parseResizableLimits(uint32_t& initial, std::optional<uint32_t>& maximum) -> PartialResult
-{
-    ASSERT(!maximum);
-
-    uint8_t flags;
-    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;
-        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 { };
-}
-
-auto ModuleParser::parseTableHelper(bool isImport) -> PartialResult
-{
-    WASM_PARSER_FAIL_IF(m_tableCount > 0, "Cannot have more than one Table for now");
-
-    ++m_tableCount;
-
-    int8_t type;
-    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;
-    PartialResult limits = parseResizableLimits(initial, maximum);
-    if (UNLIKELY(!limits))
-        return makeUnexpected(WTFMove(limits.error()));
-    WASM_PARSER_FAIL_IF(initial > maxTableEntries, "Table's initial page count of ", initial, " is too big, maximum ", maxTableEntries);
-
-    ASSERT(!maximum || *maximum >= initial);
-
-    m_info->tableInformation = TableInformation(initial, maximum, isImport);
-
-    return { };
-}
-
-auto ModuleParser::parseTable() -> PartialResult
-{
-    uint32_t count;
-    WASM_PARSER_FAIL_IF(!parseVarUInt32(count), "can't get Table's count");
-    WASM_PARSER_FAIL_IF(count > 1, "Table count of ", count, " is invalid, at most 1 is allowed for now");
-
-    if (!count)
-        return { };
-
-    bool isImport = false;
-    PartialResult result = parseTableHelper(isImport);
-    if (UNLIKELY(!result))
-        return makeUnexpected(WTFMove(result.error()));
-
-    return { };
-}
-
-auto ModuleParser::parseMemoryHelper(bool isImport) -> PartialResult
-{
-    WASM_PARSER_FAIL_IF(m_memoryCount, "there can at most be one Memory section for now");
-
-    ++m_memoryCount;
-
-    PageCount initialPageCount;
-    PageCount maximumPageCount;
-    {
-        uint32_t initial;
-        std::optional<uint32_t> maximum;
-        PartialResult limits = parseResizableLimits(initial, maximum);
-        if (UNLIKELY(!limits))
-            return makeUnexpected(WTFMove(limits.error()));
-        ASSERT(!maximum || *maximum >= initial);
-        WASM_PARSER_FAIL_IF(!PageCount::isValid(initial), "Memory's initial page count of ", initial, " is invalid");
-
-        initialPageCount = PageCount(initial);
-
-        if (maximum) {
-            WASM_PARSER_FAIL_IF(!PageCount::isValid(*maximum), "Memory's maximum page count of ", *maximum, " is invalid");
-            maximumPageCount = PageCount(*maximum);
-        }
-    }
-    ASSERT(initialPageCount);
-    ASSERT(!maximumPageCount || maximumPageCount >= initialPageCount);
-
-    m_info->memory = MemoryInformation(initialPageCount, maximumPageCount, isImport);
-    return { };
-}
-
-auto ModuleParser::parseMemory() -> PartialResult
-{
-    uint8_t count;
-    WASM_PARSER_FAIL_IF(!parseVarUInt1(count), "can't parse Memory section's count");
-
-    if (!count)
-        return { };
-
-    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);
-}
-
-auto ModuleParser::parseGlobal() -> PartialResult
-{
-    uint32_t globalCount;
-    WASM_PARSER_FAIL_IF(!parseVarUInt32(globalCount), "can't get Global section's count");
-    WASM_PARSER_FAIL_IF(globalCount > maxGlobals, "Global section's count is too big ", globalCount, " maximum ", maxGlobals);
-    size_t totalBytes = globalCount + m_info->firstInternalGlobal;
-    WASM_PARSER_FAIL_IF((static_cast<uint32_t>(totalBytes) < globalCount) || !m_info->globals.tryReserveCapacity(totalBytes), "can't allocate memory for ", totalBytes, " globals");
-
-    for (uint32_t globalIndex = 0; globalIndex < globalCount; ++globalIndex) {
-        Global global;
-        uint8_t initOpcode;
-
-        WASM_FAIL_IF_HELPER_FAILS(parseGlobalType(global));
-        Type typeForInitOpcode;
-        WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, global.initialBitsOrImportNumber, typeForInitOpcode));
-        if (initOpcode == GetGlobal)
-            global.initializationType = Global::FromGlobalImport;
-        else
-            global.initializationType = Global::FromExpression;
-        WASM_PARSER_FAIL_IF(typeForInitOpcode != global.type, "Global init_expr opcode of type ", typeForInitOpcode, " doesn't match global's type ", global.type);
-
-        m_info->globals.uncheckedAppend(WTFMove(global));
-    }
-
-    return { };
-}
-
-auto ModuleParser::parseExport() -> PartialResult
-{
-    uint32_t exportCount;
-    WASM_PARSER_FAIL_IF(!parseVarUInt32(exportCount), "can't get Export section's count");
-    WASM_PARSER_FAIL_IF(exportCount > maxExports, "Export section's count is too big ", exportCount, " maximum ", maxExports);
-    WASM_PARSER_FAIL_IF(!m_info->exports.tryReserveCapacity(exportCount), "can't allocate enough memory for ", exportCount, " exports");
-
-    HashSet<String> exportNames;
-    for (uint32_t exportNumber = 0; exportNumber < exportCount; ++exportNumber) {
-        uint32_t fieldLen;
-        Name fieldString;
-        ExternalKind kind;
-        unsigned kindIndex;
-
-        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);
-        String fieldName = String::fromUTF8(fieldString);
-        WASM_PARSER_FAIL_IF(exportNames.contains(fieldName), "duplicate export: '", fieldString, "'");
-        exportNames.add(fieldName);
-
-        WASM_PARSER_FAIL_IF(!parseExternalKind(kind), "can't get ", exportNumber, "th Export's kind, named '", fieldString, "'");
-        WASM_PARSER_FAIL_IF(!parseVarUInt32(kindIndex), "can't get ", exportNumber, "th Export's kind index, named '", fieldString, "'");
-        switch (kind) {
-        case ExternalKind::Function: {
-            WASM_PARSER_FAIL_IF(kindIndex >= m_info->functionIndexSpaceSize(), exportNumber, "th Export has invalid function number ", kindIndex, " it exceeds the function index space ", m_info->functionIndexSpaceSize(), ", named '", fieldString, "'");
-            break;
-        }
-        case ExternalKind::Table: {
-            WASM_PARSER_FAIL_IF(kindIndex >= m_tableCount, "can't export Table ", kindIndex, " there are ", m_tableCount, " Tables");
-            break;
-        }
-        case ExternalKind::Memory: {
-            WASM_PARSER_FAIL_IF(!m_info->memory, "can't export a non-existent Memory");
-            WASM_PARSER_FAIL_IF(kindIndex, "can't export Memory ", kindIndex, " only one Table is currently supported");
-            break;
-        }
-        case ExternalKind::Global: {
-            WASM_PARSER_FAIL_IF(kindIndex >= m_info->globals.size(), exportNumber, "th Export has invalid global number ", kindIndex, " it exceeds the globals count ", m_info->globals.size(), ", named '", fieldString, "'");
-            WASM_PARSER_FAIL_IF(m_info->globals[kindIndex].mutability != Global::Immutable, exportNumber, "th Export isn't immutable, named '", fieldString, "'");
-            break;
-        }
-        }
-
-        m_info->exports.uncheckedAppend({ WTFMove(fieldString), kind, kindIndex });
-    }
-
-    return { };
-}
-
-auto ModuleParser::parseStart() -> PartialResult
-{
-    uint32_t startFunctionIndex;
-    WASM_PARSER_FAIL_IF(!parseVarUInt32(startFunctionIndex), "can't get Start index");
-    WASM_PARSER_FAIL_IF(startFunctionIndex >= m_info->functionIndexSpaceSize(), "Start index ", startFunctionIndex, " exceeds function index space ", m_info->functionIndexSpaceSize());
-    SignatureIndex signatureIndex = m_info->signatureIndexFromFunctionIndexSpace(startFunctionIndex);
-    const Signature& signature = SignatureInformation::get(signatureIndex);
-    WASM_PARSER_FAIL_IF(signature.argumentCount(), "Start function can't have arguments");
-    WASM_PARSER_FAIL_IF(signature.returnType() != Void, "Start function can't return a value");
-    m_info->startFunctionIndexSpace = startFunctionIndex;
-    return { };
-}
-
-auto ModuleParser::parseElement() -> PartialResult
-{
-    uint32_t elementCount;
-    WASM_PARSER_FAIL_IF(!parseVarUInt32(elementCount), "can't get Element section's count");
-    WASM_PARSER_FAIL_IF(elementCount > maxTableEntries, "Element section's count is too big ", elementCount, " maximum ", maxTableEntries);
-    WASM_PARSER_FAIL_IF(!m_info->elements.tryReserveCapacity(elementCount), "can't allocate memory for ", elementCount, " Elements");
-    for (unsigned elementNum = 0; elementNum < elementCount; ++elementNum) {
-        uint32_t tableIndex;
-        uint64_t initExprBits;
-        uint8_t initOpcode;
-        uint32_t indexCount;
-
-        WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't get ", elementNum, "th Element table index");
-        WASM_PARSER_FAIL_IF(tableIndex >= m_tableCount, "Element section for Table ", tableIndex, " exceeds available Table ", m_tableCount);
-        Type initExprType;
-        WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, initExprBits, initExprType));
-        WASM_PARSER_FAIL_IF(initExprType != I32, "Element init_expr must produce an i32");
-        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_info->tableInformation);
-
-        Element element(makeI32InitExpr(initOpcode, initExprBits));
-        WASM_PARSER_FAIL_IF(!element.functionIndices.tryReserveCapacity(indexCount), "can't allocate memory for ", indexCount, " Element indices");
-
-        for (unsigned index = 0; index < indexCount; ++index) {
-            uint32_t functionIndex;
-            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_info->functionIndexSpaceSize(), "Element section's ", elementNum, "th element's ", index, "th index is ", functionIndex, " which exceeds the function index space size of ", m_info->functionIndexSpaceSize());
-
-            element.functionIndices.uncheckedAppend(functionIndex);
-        }
-
-        m_info->elements.uncheckedAppend(WTFMove(element));
-    }
-
-    return { };
-}
-
-auto ModuleParser::parseCode() -> PartialResult
-{
-    uint32_t count;
-    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_info->functionLocationInBinary.size(), "Code section count ", count, " exceeds the declared number of functions ", m_info->functionLocationInBinary.size());
-
-    for (uint32_t i = 0; i < count; ++i) {
-        uint32_t functionSize;
-        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);
-        WASM_PARSER_FAIL_IF(functionSize > maxFunctionSize, "Code function's size ", functionSize, " is too big");
-
-        m_info->functionLocationInBinary[i].start = m_offset;
-        m_info->functionLocationInBinary[i].end = m_offset + functionSize;
-        m_offset = m_info->functionLocationInBinary[i].end;
-    }
-
-    return { };
-}
-
-auto ModuleParser::parseInitExpr(uint8_t& opcode, uint64_t& bitsOrImportNumber, Type& resultType) -> PartialResult
-{
-    WASM_PARSER_FAIL_IF(!parseUInt8(opcode), "can't get init_expr's opcode");
-
-    switch (opcode) {
-    case I32Const: {
-        int32_t constant;
-        WASM_PARSER_FAIL_IF(!parseVarInt32(constant), "can't get constant value for init_expr's i32.const");
-        bitsOrImportNumber = static_cast<uint64_t>(constant);
-        resultType = I32;
-        break;
-    }
-
-    case I64Const: {
-        int64_t constant;
-        WASM_PARSER_FAIL_IF(!parseVarInt64(constant), "can't get constant value for init_expr's i64.const");
-        bitsOrImportNumber = constant;
-        resultType = I64;
-        break;
-    }
-
-    case F32Const: {
-        uint32_t constant;
-        WASM_PARSER_FAIL_IF(!parseUInt32(constant), "can't get constant value for init_expr's f32.const");
-        bitsOrImportNumber = constant;
-        resultType = F32;
-        break;
-    }
-
-    case F64Const: {
-        uint64_t constant;
-        WASM_PARSER_FAIL_IF(!parseUInt64(constant), "can't get constant value for init_expr's f64.const");
-        bitsOrImportNumber = constant;
-        resultType = F64;
-        break;
-    }
-
-    case GetGlobal: {
-        uint32_t index;
-        WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get get_global's index");
-
-        WASM_PARSER_FAIL_IF(index >= m_info->globals.size(), "get_global's index ", index, " exceeds the number of globals ", m_info->globals.size());
-        WASM_PARSER_FAIL_IF(index >= m_info->firstInternalGlobal, "get_global import kind index ", index, " exceeds the first internal global ", m_info->firstInternalGlobal);
-
-        ASSERT(m_info->globals[index].mutability == Global::Immutable);
-        resultType = m_info->globals[index].type;
-        bitsOrImportNumber = index;
-        break;
-    }
-
-    default:
-        WASM_PARSER_FAIL_IF(true, "unknown init_expr opcode ", opcode);
-    }
-
-    uint8_t endOpcode;
-    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 { };
-}
-
-auto ModuleParser::parseGlobalType(Global& global) -> PartialResult
-{
-    uint8_t mutability;
-    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 { };
-}
-
-auto ModuleParser::parseData() -> PartialResult
-{
-    uint32_t segmentCount;
-    WASM_PARSER_FAIL_IF(!parseVarUInt32(segmentCount), "can't get Data section's count");
-    WASM_PARSER_FAIL_IF(segmentCount > maxDataSegments, "Data section's count is too big ", segmentCount, " maximum ", maxDataSegments);
-    WASM_PARSER_FAIL_IF(!m_info->data.tryReserveCapacity(segmentCount), "can't allocate enough memory for Data section's ", segmentCount, " segments");
-
-    for (uint32_t segmentNumber = 0; segmentNumber < segmentCount; ++segmentNumber) {
-        uint32_t memoryIndex;
-        uint64_t initExprBits;
-        uint8_t initOpcode;
-        uint32_t dataByteLength;
-
-        WASM_PARSER_FAIL_IF(!parseVarUInt32(memoryIndex), "can't get ", segmentNumber, "th Data segment's index");
-        WASM_PARSER_FAIL_IF(memoryIndex >= m_memoryCount, segmentNumber, "th Data segment has index ", memoryIndex, " which exceeds the number of Memories ", m_memoryCount);
-        Type initExprType;
-        WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, initExprBits, initExprType));
-        WASM_PARSER_FAIL_IF(initExprType != I32, segmentNumber, "th Data segment's init_expr must produce an i32");
-        WASM_PARSER_FAIL_IF(!parseVarUInt32(dataByteLength), "can't get ", segmentNumber, "th Data segment's data byte length");
-        WASM_PARSER_FAIL_IF(dataByteLength > maxModuleSize, segmentNumber, "th Data segment's data byte length is too big ", dataByteLength, " maximum ", maxModuleSize);
-
-        Segment* segment = Segment::create(makeI32InitExpr(initOpcode, initExprBits), dataByteLength);
-        WASM_PARSER_FAIL_IF(!segment, "can't allocate enough memory for ", segmentNumber, "th Data segment of size ", dataByteLength);
-        m_info->data.uncheckedAppend(Segment::adoptPtr(segment));
-        for (uint32_t dataByte = 0; dataByte < dataByteLength; ++dataByte) {
-            uint8_t byte;
-            WASM_PARSER_FAIL_IF(!parseUInt8(byte), "can't get ", dataByte, "th data byte from ", segmentNumber, "th Data segment");
-            segment->byte(dataByte) = byte;
-        }
-    }
-    return { };
-}
-    
-auto ModuleParser::parseCustom(uint32_t sectionLength) -> PartialResult
-{
-    const uint32_t customSectionStartOffset = m_offset;
-
-    CustomSection section;
-    uint32_t customSectionNumber = m_info->customSections.size() + 1;
-    uint32_t nameLen;
-    WASM_PARSER_FAIL_IF(!m_info->customSections.tryReserveCapacity(customSectionNumber), "can't allocate enough memory for ", customSectionNumber, "th custom section");
-    WASM_PARSER_FAIL_IF(!parseVarUInt32(nameLen), "can't get ", customSectionNumber, "th custom section's name length");
-    WASM_PARSER_FAIL_IF(!consumeUTF8String(section.name, nameLen), "nameLen get ", customSectionNumber, "th custom section's name of length ", nameLen);
-
-    uint32_t payloadBytes = sectionLength - (m_offset - customSectionStartOffset);
-    WASM_PARSER_FAIL_IF(!section.payload.tryReserveCapacity(payloadBytes), "can't allocate enough memory for ", customSectionNumber, "th custom section's ", payloadBytes, " bytes");
-    for (uint32_t byteNumber = 0; byteNumber < payloadBytes; ++byteNumber) {
-        uint8_t byte;
-        WASM_PARSER_FAIL_IF(!parseUInt8(byte), "can't get ", byteNumber, "th data byte from ", customSectionNumber, "th custom section");
-        section.payload.uncheckedAppend(byte);
-    }
-
-    Name nameName = { 'n', 'a', 'm', 'e' };
-    if (section.name == nameName) {
-        NameSectionParser nameSectionParser(section.payload.begin(), section.payload.size(), m_info);
-        if (auto nameSection = nameSectionParser.parse())
-            m_info->nameSection = WTFMove(*nameSection);
-    }
-
-    m_info->customSections.uncheckedAppend(WTFMove(section));
-
-    return { };
-}
-
 } } // namespace JSC::Wasm
 
 #endif // ENABLE(WEBASSEMBLY)
index a651be1..d33b779 100644 (file)
@@ -46,21 +46,7 @@ public:
     Result WARN_UNUSED_RETURN parse();
 
 private:
-
-#define WASM_SECTION_DECLARE_PARSER(NAME, ID, DESCRIPTION) PartialResult WARN_UNUSED_RETURN parse ## NAME();
-    FOR_EACH_KNOWN_WASM_SECTION(WASM_SECTION_DECLARE_PARSER)
-#undef WASM_SECTION_DECLARE_PARSER
-
-    PartialResult WARN_UNUSED_RETURN parseCustom(uint32_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&, Type& initExprType);
-
     Ref<ModuleInformation> m_info;
-    uint32_t m_memoryCount { 0 };
-    uint32_t m_tableCount { 0 };
 };
 
 } } // namespace JSC::Wasm
index 7c02f2f..fe63cf5 100644 (file)
@@ -36,11 +36,20 @@ namespace JSC { namespace Wasm {
 
 struct NameSection : public ThreadSafeRefCounted<NameSection> {
     WTF_MAKE_NONCOPYABLE(NameSection);
-
 public:
-    NameSection(const std::optional<CString> &hash)
-        : moduleHash(hash ? hash->length() : 3)
+    NameSection()
+    {
+        setHash(std::nullopt);
+    }
+
+    static Ref<NameSection> create()
+    {
+        return adoptRef(*new NameSection);
+    }
+
+    void setHash(const std::optional<CString> &hash)
     {
+        moduleHash = Name(hash ? hash->length() : 3);
         if (hash) {
             for (size_t i = 0; i < hash->length(); ++i)
                 moduleHash[i] = static_cast<uint8_t>(*(hash->data() + i));
index 7339621..5ebdc0c 100644 (file)
@@ -35,7 +35,7 @@ namespace JSC { namespace Wasm {
 
 auto NameSectionParser::parse() -> Result
 {
-    RefPtr<NameSection> nameSection(adoptRef(*new NameSection(m_info.hash)));
+    Ref<NameSection> nameSection = NameSection::create();
     WASM_PARSER_FAIL_IF(!nameSection->functionNames.tryReserveCapacity(m_info.functionIndexSpaceSize()), "can't allocate enough memory for function names");
     nameSection->functionNames.resize(m_info.functionIndexSpaceSize());
 
@@ -96,7 +96,7 @@ auto NameSectionParser::parse() -> Result
         }
         WASM_PARSER_FAIL_IF(payloadStart + payloadLength != m_offset);
     }
-    return nameSection;
+    return WTFMove(nameSection);
 }
 
 } } // namespace JSC::Wasm
index b7af0d9..d8e6ae7 100644 (file)
@@ -33,7 +33,7 @@
 
 namespace JSC { namespace Wasm {
 
-class NameSectionParser : public Parser<RefPtr<NameSection>> {
+class NameSectionParser : public Parser<Ref<NameSection>> {
 public:
     NameSectionParser(const uint8_t* sourceBuffer, size_t sourceLength, const ModuleInformation& info)
         : Parser(sourceBuffer, sourceLength)
index 47dead6..9e0c4a5 100644 (file)
@@ -69,21 +69,18 @@ void OMGPlan::work(CompilationEffort)
 {
     ASSERT(m_codeBlock->runnable());
     ASSERT(m_codeBlock.ptr() == m_module->codeBlockFor(mode()));
-    const FunctionLocationInBinary& location = m_moduleInformation->functionLocationInBinary[m_functionIndex];
-    const uint8_t* functionStart = m_moduleInformation->source.data() + location.start;
-    const size_t functionLength = location.end - location.start;
-    ASSERT(functionStart + functionLength <= m_moduleInformation->source.end());
+    const FunctionData& function = m_moduleInformation->functions[m_functionIndex];
 
     const uint32_t functionIndexSpace = m_functionIndex + m_module->moduleInformation().importFunctionCount();
     ASSERT(functionIndexSpace < m_module->moduleInformation().functionIndexSpaceSize());
 
     SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[m_functionIndex];
     const Signature& signature = SignatureInformation::get(signatureIndex);
-    ASSERT(validateFunction(functionStart, functionLength, signature, m_moduleInformation.get()));
+    ASSERT(validateFunction(function.data.data(), function.data.size(), signature, m_moduleInformation.get()));
 
     Vector<UnlinkedWasmToWasmCall> unlinkedCalls;
     CompilationContext context;
-    auto parseAndCompileResult = parseAndCompile(context, functionStart, functionLength, signature, unlinkedCalls, m_moduleInformation.get(), m_mode, CompilationMode::OMGMode, m_functionIndex);
+    auto parseAndCompileResult = parseAndCompile(context, function.data.data(), function.data.size(), signature, unlinkedCalls, m_moduleInformation.get(), m_mode, CompilationMode::OMGMode, m_functionIndex);
 
     if (UNLIKELY(!parseAndCompileResult)) {
         fail(holdLock(m_lock), makeString(parseAndCompileResult.error(), "when trying to tier up ", String::number(m_functionIndex)));
index 6510705..9fc4a45 100644 (file)
@@ -58,6 +58,10 @@ public:
     typedef Expected<void, ErrorType> PartialResult;
     typedef Expected<SuccessType, ErrorType> Result;
 
+    const uint8_t* source() const { return m_source; }
+    size_t length() const { return m_sourceLength; }
+    size_t offset() const { return m_offset; }
+
 protected:
     Parser(const uint8_t*, size_t);
 
@@ -81,16 +85,13 @@ protected:
     bool WARN_UNUSED_RETURN parseValueType(Type&);
     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("WebAssembly.Module doesn't parse at byte "_s, String::number(m_offset), " / "_s, String::number(m_sourceLength), ": "_s, makeString(args)...));
+        return UnexpectedResult(makeString("WebAssembly.Module doesn't parse at byte "_s, String::number(m_offset), ": "_s, makeString(args)...));
     }
 #define WASM_PARSER_FAIL_IF(condition, ...) do { \
     if (UNLIKELY(condition))                     \
@@ -285,6 +286,14 @@ ALWAYS_INLINE bool Parser<SuccessType>::parseExternalKind(ExternalKind& result)
     return true;
 }
 
+ALWAYS_INLINE I32InitExpr makeI32InitExpr(uint8_t opcode, uint32_t bits)
+{
+    RELEASE_ASSERT(opcode == I32Const || opcode == GetGlobal);
+    if (opcode == I32Const)
+        return I32InitExpr::constValue(bits);
+    return I32InitExpr::globalImport(bits);
+}
+
 } } // namespace JSC::Wasm
 
 #endif // ENABLE(WEBASSEMBLY)
index a34deec..9974ecf 100644 (file)
@@ -53,8 +53,6 @@ Plan::Plan(Context* context, Ref<ModuleInformation> info, CompletionTask&& task,
     : m_moduleInformation(WTFMove(info))
     , m_createEmbedderWrapper(WTFMove(createEmbedderWrapper))
     , m_throwWasmException(throwWasmException)
-    , m_source(m_moduleInformation->source.data())
-    , m_sourceLength(m_moduleInformation->source.size())
 {
     m_completionTasks.append(std::make_pair(context, WTFMove(task)));
 }
@@ -64,10 +62,8 @@ Plan::Plan(Context* context, Ref<ModuleInformation> info, CompletionTask&& task)
 {
 }
 
-Plan::Plan(Context* context, const uint8_t* source, size_t sourceLength, CompletionTask&& task)
-    : m_moduleInformation(adoptRef(*new ModuleInformation(Vector<uint8_t>())))
-    , m_source(source)
-    , m_sourceLength(sourceLength)
+Plan::Plan(Context* context, CompletionTask&& task)
+    : m_moduleInformation(ModuleInformation::create())
 {
     m_completionTasks.append(std::make_pair(context, WTFMove(task)));
 }
index 7a07bfd..9ec6886 100644 (file)
@@ -54,7 +54,7 @@ public:
     Plan(Context*, Ref<ModuleInformation>, CompletionTask&&);
 
     // Note: This constructor should only be used if you are not actually building a module e.g. validation/function tests
-    JS_EXPORT_PRIVATE Plan(Context*, const uint8_t*, size_t, CompletionTask&&);
+    JS_EXPORT_PRIVATE Plan(Context*, CompletionTask&&);
     virtual JS_EXPORT_PRIVATE ~Plan();
 
     // If you guarantee the ordering here, you can rely on FIFO of the
@@ -90,8 +90,6 @@ protected:
     CreateEmbedderWrapper m_createEmbedderWrapper;
     ThrowWasmException m_throwWasmException { nullptr };
 
-    const uint8_t* m_source;
-    const size_t m_sourceLength;
     String m_errorMessage;
     MemoryMode m_mode { MemoryMode::BoundsChecking };
     Lock m_lock;
diff --git a/Source/JavaScriptCore/wasm/WasmSectionParser.cpp b/Source/JavaScriptCore/wasm/WasmSectionParser.cpp
new file mode 100644 (file)
index 0000000..9a9bacb
--- /dev/null
@@ -0,0 +1,562 @@
+/*
+ * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2018 Yusuke Suzuki <yusukesuzuki@slowstart.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WasmSectionParser.h"
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "IdentifierInlines.h"
+#include "WasmMemoryInformation.h"
+#include "WasmNameSectionParser.h"
+#include "WasmOps.h"
+#include "WasmSections.h"
+
+namespace JSC { namespace Wasm {
+
+auto SectionParser::parseType() -> PartialResult
+{
+    uint32_t count;
+
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(count), "can't get Type section's count");
+    WASM_PARSER_FAIL_IF(count > maxTypes, "Type section's count is too big ", count, " maximum ", maxTypes);
+    WASM_PARSER_FAIL_IF(!m_info->usedSignatures.tryReserveCapacity(count), "can't allocate enough memory for Type section's ", count, " entries");
+
+    for (uint32_t i = 0; i < count; ++i) {
+        int8_t type;
+        uint32_t argumentCount;
+        Vector<Type> argumentTypes;
+
+        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 > maxFunctionParams, i, "th argument count is too big ", argumentCount, " maximum ", maxFunctionParams);
+        RefPtr<Signature> maybeSignature = Signature::tryCreate(argumentCount);
+        WASM_PARSER_FAIL_IF(!maybeSignature, "can't allocate enough memory for Type section's ", i, "th signature");
+        Ref<Signature> signature = maybeSignature.releaseNonNull();
+
+        for (unsigned i = 0; i < argumentCount; ++i) {
+            Type argumentType;
+            WASM_PARSER_FAIL_IF(!parseValueType(argumentType), "can't get ", i, "th argument Type");
+            signature->argument(i) = argumentType;
+        }
+
+        uint8_t returnCount;
+        WASM_PARSER_FAIL_IF(!parseVarUInt1(returnCount), "can't get ", i, "th Type's return count");
+        Type returnType;
+        if (returnCount) {
+            Type value;
+            WASM_PARSER_FAIL_IF(!parseValueType(value), "can't get ", i, "th Type's return value");
+            returnType = static_cast<Type>(value);
+        } else
+            returnType = Type::Void;
+        signature->returnType() = returnType;
+
+        std::pair<SignatureIndex, Ref<Signature>> result = SignatureInformation::adopt(WTFMove(signature));
+        m_info->usedSignatures.uncheckedAppend(WTFMove(result.second));
+    }
+    return { };
+}
+
+auto SectionParser::parseImport() -> PartialResult
+{
+    uint32_t importCount;
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(importCount), "can't get Import section's count");
+    WASM_PARSER_FAIL_IF(importCount > maxImports, "Import section's count is too big ", importCount, " maximum ", maxImports);
+    WASM_PARSER_FAIL_IF(!m_info->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_info->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_info->importFunctionSignatureIndices.tryReserveCapacity(importCount), "can't allocate enough memory for ", importCount, " import function signatures"); // FIXME this over-allocates when we fix the FIXMEs below.
+
+    for (uint32_t importNumber = 0; importNumber < importCount; ++importNumber) {
+        uint32_t moduleLen;
+        uint32_t fieldLen;
+        Name moduleString;
+        Name fieldString;
+        ExternalKind kind;
+        unsigned kindIndex { 0 };
+
+        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);
+
+        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, "'");
+
+        WASM_PARSER_FAIL_IF(!parseExternalKind(kind), "can't get ", importNumber, "th Import's kind in module '", moduleString, "' field '", fieldString, "'");
+        switch (kind) {
+        case ExternalKind::Function: {
+            uint32_t functionSignatureIndex;
+            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_info->usedSignatures.size(), "invalid function signature for ", importNumber, "th Import, ", functionSignatureIndex, " is out of range of ", m_info->usedSignatures.size(), " in module '", moduleString, "' field '", fieldString, "'");
+            kindIndex = m_info->importFunctionSignatureIndices.size();
+            SignatureIndex signatureIndex = SignatureInformation::get(m_info->usedSignatures[functionSignatureIndex]);
+            m_info->importFunctionSignatureIndices.uncheckedAppend(signatureIndex);
+            break;
+        }
+        case ExternalKind::Table: {
+            bool isImport = true;
+            PartialResult result = parseTableHelper(isImport);
+            if (UNLIKELY(!result))
+                return makeUnexpected(WTFMove(result.error()));
+            break;
+        }
+        case ExternalKind::Memory: {
+            bool isImport = true;
+            PartialResult result = parseMemoryHelper(isImport);
+            if (UNLIKELY(!result))
+                return makeUnexpected(WTFMove(result.error()));
+            break;
+        }
+        case ExternalKind::Global: {
+            Global global;
+            WASM_FAIL_IF_HELPER_FAILS(parseGlobalType(global));
+            WASM_PARSER_FAIL_IF(global.mutability == Global::Mutable, "Mutable Globals aren't supported");
+
+            kindIndex = m_info->globals.size();
+            m_info->globals.uncheckedAppend(WTFMove(global));
+            break;
+        }
+        }
+
+        m_info->imports.uncheckedAppend({ WTFMove(moduleString), WTFMove(fieldString), kind, kindIndex });
+    }
+
+    m_info->firstInternalGlobal = m_info->globals.size();
+    return { };
+}
+
+auto SectionParser::parseFunction() -> PartialResult
+{
+    uint32_t count;
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(count), "can't get Function section's count");
+    WASM_PARSER_FAIL_IF(count > maxFunctions, "Function section's count is too big ", count, " maximum ", maxFunctions);
+    WASM_PARSER_FAIL_IF(!m_info->internalFunctionSignatureIndices.tryReserveCapacity(count), "can't allocate enough memory for ", count, " Function signatures");
+    WASM_PARSER_FAIL_IF(!m_info->functions.tryReserveCapacity(count), "can't allocate enough memory for ", count, "Function locations");
+
+    for (uint32_t i = 0; i < count; ++i) {
+        uint32_t typeNumber;
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(typeNumber), "can't get ", i, "th Function's type number");
+        WASM_PARSER_FAIL_IF(typeNumber >= m_info->usedSignatures.size(), i, "th Function type number is invalid ", typeNumber);
+
+        SignatureIndex signatureIndex = SignatureInformation::get(m_info->usedSignatures[typeNumber]);
+        // The Code section fixes up start and end.
+        size_t start = 0;
+        size_t end = 0;
+        m_info->internalFunctionSignatureIndices.uncheckedAppend(signatureIndex);
+        m_info->functions.uncheckedAppend({ start, end, Vector<uint8_t>() });
+    }
+
+    return { };
+}
+
+auto SectionParser::parseResizableLimits(uint32_t& initial, std::optional<uint32_t>& maximum) -> PartialResult
+{
+    ASSERT(!maximum);
+
+    uint8_t flags;
+    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;
+        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 { };
+}
+
+auto SectionParser::parseTableHelper(bool isImport) -> PartialResult
+{
+    WASM_PARSER_FAIL_IF(m_info->tableCount() > 0, "Cannot have more than one Table for now");
+
+    int8_t type;
+    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;
+    PartialResult limits = parseResizableLimits(initial, maximum);
+    if (UNLIKELY(!limits))
+        return makeUnexpected(WTFMove(limits.error()));
+    WASM_PARSER_FAIL_IF(initial > maxTableEntries, "Table's initial page count of ", initial, " is too big, maximum ", maxTableEntries);
+
+    ASSERT(!maximum || *maximum >= initial);
+
+    m_info->tableInformation = TableInformation(initial, maximum, isImport);
+
+    return { };
+}
+
+auto SectionParser::parseTable() -> PartialResult
+{
+    uint32_t count;
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(count), "can't get Table's count");
+    WASM_PARSER_FAIL_IF(count > 1, "Table count of ", count, " is invalid, at most 1 is allowed for now");
+
+    if (!count)
+        return { };
+
+    bool isImport = false;
+    PartialResult result = parseTableHelper(isImport);
+    if (UNLIKELY(!result))
+        return makeUnexpected(WTFMove(result.error()));
+
+    return { };
+}
+
+auto SectionParser::parseMemoryHelper(bool isImport) -> PartialResult
+{
+    WASM_PARSER_FAIL_IF(m_info->memoryCount(), "there can at most be one Memory section for now");
+
+    PageCount initialPageCount;
+    PageCount maximumPageCount;
+    {
+        uint32_t initial;
+        std::optional<uint32_t> maximum;
+        PartialResult limits = parseResizableLimits(initial, maximum);
+        if (UNLIKELY(!limits))
+            return makeUnexpected(WTFMove(limits.error()));
+        ASSERT(!maximum || *maximum >= initial);
+        WASM_PARSER_FAIL_IF(!PageCount::isValid(initial), "Memory's initial page count of ", initial, " is invalid");
+
+        initialPageCount = PageCount(initial);
+
+        if (maximum) {
+            WASM_PARSER_FAIL_IF(!PageCount::isValid(*maximum), "Memory's maximum page count of ", *maximum, " is invalid");
+            maximumPageCount = PageCount(*maximum);
+        }
+    }
+    ASSERT(initialPageCount);
+    ASSERT(!maximumPageCount || maximumPageCount >= initialPageCount);
+
+    m_info->memory = MemoryInformation(initialPageCount, maximumPageCount, isImport);
+    return { };
+}
+
+auto SectionParser::parseMemory() -> PartialResult
+{
+    uint8_t count;
+    WASM_PARSER_FAIL_IF(!parseVarUInt1(count), "can't parse Memory section's count");
+
+    if (!count)
+        return { };
+
+    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);
+}
+
+auto SectionParser::parseGlobal() -> PartialResult
+{
+    uint32_t globalCount;
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(globalCount), "can't get Global section's count");
+    WASM_PARSER_FAIL_IF(globalCount > maxGlobals, "Global section's count is too big ", globalCount, " maximum ", maxGlobals);
+    size_t totalBytes = globalCount + m_info->firstInternalGlobal;
+    WASM_PARSER_FAIL_IF((static_cast<uint32_t>(totalBytes) < globalCount) || !m_info->globals.tryReserveCapacity(totalBytes), "can't allocate memory for ", totalBytes, " globals");
+
+    for (uint32_t globalIndex = 0; globalIndex < globalCount; ++globalIndex) {
+        Global global;
+        uint8_t initOpcode;
+
+        WASM_FAIL_IF_HELPER_FAILS(parseGlobalType(global));
+        Type typeForInitOpcode;
+        WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, global.initialBitsOrImportNumber, typeForInitOpcode));
+        if (initOpcode == GetGlobal)
+            global.initializationType = Global::FromGlobalImport;
+        else
+            global.initializationType = Global::FromExpression;
+        WASM_PARSER_FAIL_IF(typeForInitOpcode != global.type, "Global init_expr opcode of type ", typeForInitOpcode, " doesn't match global's type ", global.type);
+
+        m_info->globals.uncheckedAppend(WTFMove(global));
+    }
+
+    return { };
+}
+
+auto SectionParser::parseExport() -> PartialResult
+{
+    uint32_t exportCount;
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(exportCount), "can't get Export section's count");
+    WASM_PARSER_FAIL_IF(exportCount > maxExports, "Export section's count is too big ", exportCount, " maximum ", maxExports);
+    WASM_PARSER_FAIL_IF(!m_info->exports.tryReserveCapacity(exportCount), "can't allocate enough memory for ", exportCount, " exports");
+
+    HashSet<String> exportNames;
+    for (uint32_t exportNumber = 0; exportNumber < exportCount; ++exportNumber) {
+        uint32_t fieldLen;
+        Name fieldString;
+        ExternalKind kind;
+        unsigned kindIndex;
+
+        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);
+        String fieldName = String::fromUTF8(fieldString);
+        WASM_PARSER_FAIL_IF(exportNames.contains(fieldName), "duplicate export: '", fieldString, "'");
+        exportNames.add(fieldName);
+
+        WASM_PARSER_FAIL_IF(!parseExternalKind(kind), "can't get ", exportNumber, "th Export's kind, named '", fieldString, "'");
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(kindIndex), "can't get ", exportNumber, "th Export's kind index, named '", fieldString, "'");
+        switch (kind) {
+        case ExternalKind::Function: {
+            WASM_PARSER_FAIL_IF(kindIndex >= m_info->functionIndexSpaceSize(), exportNumber, "th Export has invalid function number ", kindIndex, " it exceeds the function index space ", m_info->functionIndexSpaceSize(), ", named '", fieldString, "'");
+            break;
+        }
+        case ExternalKind::Table: {
+            WASM_PARSER_FAIL_IF(kindIndex >= m_info->tableCount(), "can't export Table ", kindIndex, " there are ", m_info->tableCount(), " Tables");
+            break;
+        }
+        case ExternalKind::Memory: {
+            WASM_PARSER_FAIL_IF(!m_info->memory, "can't export a non-existent Memory");
+            WASM_PARSER_FAIL_IF(kindIndex, "can't export Memory ", kindIndex, " only one Table is currently supported");
+            break;
+        }
+        case ExternalKind::Global: {
+            WASM_PARSER_FAIL_IF(kindIndex >= m_info->globals.size(), exportNumber, "th Export has invalid global number ", kindIndex, " it exceeds the globals count ", m_info->globals.size(), ", named '", fieldString, "'");
+            WASM_PARSER_FAIL_IF(m_info->globals[kindIndex].mutability != Global::Immutable, exportNumber, "th Export isn't immutable, named '", fieldString, "'");
+            break;
+        }
+        }
+
+        m_info->exports.uncheckedAppend({ WTFMove(fieldString), kind, kindIndex });
+    }
+
+    return { };
+}
+
+auto SectionParser::parseStart() -> PartialResult
+{
+    uint32_t startFunctionIndex;
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(startFunctionIndex), "can't get Start index");
+    WASM_PARSER_FAIL_IF(startFunctionIndex >= m_info->functionIndexSpaceSize(), "Start index ", startFunctionIndex, " exceeds function index space ", m_info->functionIndexSpaceSize());
+    SignatureIndex signatureIndex = m_info->signatureIndexFromFunctionIndexSpace(startFunctionIndex);
+    const Signature& signature = SignatureInformation::get(signatureIndex);
+    WASM_PARSER_FAIL_IF(signature.argumentCount(), "Start function can't have arguments");
+    WASM_PARSER_FAIL_IF(signature.returnType() != Void, "Start function can't return a value");
+    m_info->startFunctionIndexSpace = startFunctionIndex;
+    return { };
+}
+
+auto SectionParser::parseElement() -> PartialResult
+{
+    uint32_t elementCount;
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(elementCount), "can't get Element section's count");
+    WASM_PARSER_FAIL_IF(elementCount > maxTableEntries, "Element section's count is too big ", elementCount, " maximum ", maxTableEntries);
+    WASM_PARSER_FAIL_IF(!m_info->elements.tryReserveCapacity(elementCount), "can't allocate memory for ", elementCount, " Elements");
+    for (unsigned elementNum = 0; elementNum < elementCount; ++elementNum) {
+        uint32_t tableIndex;
+        uint64_t initExprBits;
+        uint8_t initOpcode;
+        uint32_t indexCount;
+
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't get ", elementNum, "th Element table index");
+        WASM_PARSER_FAIL_IF(tableIndex >= m_info->tableCount(), "Element section for Table ", tableIndex, " exceeds available Table ", m_info->tableCount());
+        Type initExprType;
+        WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, initExprBits, initExprType));
+        WASM_PARSER_FAIL_IF(initExprType != I32, "Element init_expr must produce an i32");
+        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_info->tableInformation);
+
+        Element element(makeI32InitExpr(initOpcode, initExprBits));
+        WASM_PARSER_FAIL_IF(!element.functionIndices.tryReserveCapacity(indexCount), "can't allocate memory for ", indexCount, " Element indices");
+
+        for (unsigned index = 0; index < indexCount; ++index) {
+            uint32_t functionIndex;
+            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_info->functionIndexSpaceSize(), "Element section's ", elementNum, "th element's ", index, "th index is ", functionIndex, " which exceeds the function index space size of ", m_info->functionIndexSpaceSize());
+
+            element.functionIndices.uncheckedAppend(functionIndex);
+        }
+
+        m_info->elements.uncheckedAppend(WTFMove(element));
+    }
+
+    return { };
+}
+
+// This function will be changed to be RELEASE_ASSERT_NOT_REACHED once we switch our parsing infrastructure to the streaming parser.
+auto SectionParser::parseCode() -> PartialResult
+{
+    uint32_t count;
+    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_info->functions.size(), "Code section count ", count, " exceeds the declared number of functions ", m_info->functions.size());
+
+    for (uint32_t i = 0; i < count; ++i) {
+        uint32_t functionSize;
+        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);
+        WASM_PARSER_FAIL_IF(functionSize > maxFunctionSize, "Code function's size ", functionSize, " is too big");
+
+        Vector<uint8_t> data(functionSize);
+        std::memcpy(data.data(), source() + m_offset, functionSize);
+        m_info->functions[i].start = m_offsetInSource + m_offset;
+        m_info->functions[i].end = m_offsetInSource + m_offset + functionSize;
+        m_info->functions[i].data = WTFMove(data);
+        m_offset += functionSize;
+    }
+
+    return { };
+}
+
+auto SectionParser::parseInitExpr(uint8_t& opcode, uint64_t& bitsOrImportNumber, Type& resultType) -> PartialResult
+{
+    WASM_PARSER_FAIL_IF(!parseUInt8(opcode), "can't get init_expr's opcode");
+
+    switch (opcode) {
+    case I32Const: {
+        int32_t constant;
+        WASM_PARSER_FAIL_IF(!parseVarInt32(constant), "can't get constant value for init_expr's i32.const");
+        bitsOrImportNumber = static_cast<uint64_t>(constant);
+        resultType = I32;
+        break;
+    }
+
+    case I64Const: {
+        int64_t constant;
+        WASM_PARSER_FAIL_IF(!parseVarInt64(constant), "can't get constant value for init_expr's i64.const");
+        bitsOrImportNumber = constant;
+        resultType = I64;
+        break;
+    }
+
+    case F32Const: {
+        uint32_t constant;
+        WASM_PARSER_FAIL_IF(!parseUInt32(constant), "can't get constant value for init_expr's f32.const");
+        bitsOrImportNumber = constant;
+        resultType = F32;
+        break;
+    }
+
+    case F64Const: {
+        uint64_t constant;
+        WASM_PARSER_FAIL_IF(!parseUInt64(constant), "can't get constant value for init_expr's f64.const");
+        bitsOrImportNumber = constant;
+        resultType = F64;
+        break;
+    }
+
+    case GetGlobal: {
+        uint32_t index;
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get get_global's index");
+
+        WASM_PARSER_FAIL_IF(index >= m_info->globals.size(), "get_global's index ", index, " exceeds the number of globals ", m_info->globals.size());
+        WASM_PARSER_FAIL_IF(index >= m_info->firstInternalGlobal, "get_global import kind index ", index, " exceeds the first internal global ", m_info->firstInternalGlobal);
+
+        ASSERT(m_info->globals[index].mutability == Global::Immutable);
+        resultType = m_info->globals[index].type;
+        bitsOrImportNumber = index;
+        break;
+    }
+
+    default:
+        WASM_PARSER_FAIL_IF(true, "unknown init_expr opcode ", opcode);
+    }
+
+    uint8_t endOpcode;
+    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 { };
+}
+
+auto SectionParser::parseGlobalType(Global& global) -> PartialResult
+{
+    uint8_t mutability;
+    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 { };
+}
+
+auto SectionParser::parseData() -> PartialResult
+{
+    uint32_t segmentCount;
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(segmentCount), "can't get Data section's count");
+    WASM_PARSER_FAIL_IF(segmentCount > maxDataSegments, "Data section's count is too big ", segmentCount, " maximum ", maxDataSegments);
+    WASM_PARSER_FAIL_IF(!m_info->data.tryReserveCapacity(segmentCount), "can't allocate enough memory for Data section's ", segmentCount, " segments");
+
+    for (uint32_t segmentNumber = 0; segmentNumber < segmentCount; ++segmentNumber) {
+        uint32_t memoryIndex;
+        uint64_t initExprBits;
+        uint8_t initOpcode;
+        uint32_t dataByteLength;
+
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(memoryIndex), "can't get ", segmentNumber, "th Data segment's index");
+        WASM_PARSER_FAIL_IF(memoryIndex >= m_info->memoryCount(), segmentNumber, "th Data segment has index ", memoryIndex, " which exceeds the number of Memories ", m_info->memoryCount());
+        Type initExprType;
+        WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, initExprBits, initExprType));
+        WASM_PARSER_FAIL_IF(initExprType != I32, segmentNumber, "th Data segment's init_expr must produce an i32");
+        WASM_PARSER_FAIL_IF(!parseVarUInt32(dataByteLength), "can't get ", segmentNumber, "th Data segment's data byte length");
+        WASM_PARSER_FAIL_IF(dataByteLength > maxModuleSize, segmentNumber, "th Data segment's data byte length is too big ", dataByteLength, " maximum ", maxModuleSize);
+
+        Segment* segment = Segment::create(makeI32InitExpr(initOpcode, initExprBits), dataByteLength);
+        WASM_PARSER_FAIL_IF(!segment, "can't allocate enough memory for ", segmentNumber, "th Data segment of size ", dataByteLength);
+        m_info->data.uncheckedAppend(Segment::adoptPtr(segment));
+        for (uint32_t dataByte = 0; dataByte < dataByteLength; ++dataByte) {
+            uint8_t byte;
+            WASM_PARSER_FAIL_IF(!parseUInt8(byte), "can't get ", dataByte, "th data byte from ", segmentNumber, "th Data segment");
+            segment->byte(dataByte) = byte;
+        }
+    }
+    return { };
+}
+
+auto SectionParser::parseCustom() -> PartialResult
+{
+    CustomSection section;
+    uint32_t customSectionNumber = m_info->customSections.size() + 1;
+    uint32_t nameLen;
+    WASM_PARSER_FAIL_IF(!m_info->customSections.tryReserveCapacity(customSectionNumber), "can't allocate enough memory for ", customSectionNumber, "th custom section");
+    WASM_PARSER_FAIL_IF(!parseVarUInt32(nameLen), "can't get ", customSectionNumber, "th custom section's name length");
+    WASM_PARSER_FAIL_IF(!consumeUTF8String(section.name, nameLen), "nameLen get ", customSectionNumber, "th custom section's name of length ", nameLen);
+
+    uint32_t payloadBytes = length() - m_offset;
+    WASM_PARSER_FAIL_IF(!section.payload.tryReserveCapacity(payloadBytes), "can't allocate enough memory for ", customSectionNumber, "th custom section's ", payloadBytes, " bytes");
+    for (uint32_t byteNumber = 0; byteNumber < payloadBytes; ++byteNumber) {
+        uint8_t byte;
+        WASM_PARSER_FAIL_IF(!parseUInt8(byte), "can't get ", byteNumber, "th data byte from ", customSectionNumber, "th custom section");
+        section.payload.uncheckedAppend(byte);
+    }
+
+    Name nameName = { 'n', 'a', 'm', 'e' };
+    if (section.name == nameName) {
+        NameSectionParser nameSectionParser(section.payload.begin(), section.payload.size(), m_info);
+        if (auto nameSection = nameSectionParser.parse())
+            m_info->nameSection = WTFMove(*nameSection);
+    }
+
+    m_info->customSections.uncheckedAppend(WTFMove(section));
+
+    return { };
+}
+
+} } // namespace JSC::Wasm
+
+#endif // ENABLE(WEBASSEMBLY)
diff --git a/Source/JavaScriptCore/wasm/WasmSectionParser.h b/Source/JavaScriptCore/wasm/WasmSectionParser.h
new file mode 100644 (file)
index 0000000..78d0283
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2018 Yusuke Suzuki <yusukesuzuki@slowstart.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "WasmFormat.h"
+#include "WasmOps.h"
+#include "WasmParser.h"
+#include <wtf/Optional.h>
+#include <wtf/Vector.h>
+
+namespace JSC { namespace Wasm {
+
+class SectionParser final : public Parser<void> {
+public:
+    SectionParser(const uint8_t* data, size_t size, size_t offsetInSource, ModuleInformation& info)
+        : Parser(data, size)
+        , m_offsetInSource(offsetInSource)
+        , m_info(info)
+    {
+    }
+
+#define WASM_SECTION_DECLARE_PARSER(NAME, ID, DESCRIPTION) PartialResult WARN_UNUSED_RETURN parse ## NAME();
+    FOR_EACH_KNOWN_WASM_SECTION(WASM_SECTION_DECLARE_PARSER)
+#undef WASM_SECTION_DECLARE_PARSER
+
+    PartialResult WARN_UNUSED_RETURN parseCustom();
+
+private:
+    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("WebAssembly.Module doesn't parse at byte "_s, String::number(m_offset + m_offsetInSource), ": "_s, makeString(args)...));
+    }
+
+    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&, Type& initExprType);
+
+    size_t m_offsetInSource;
+    Ref<ModuleInformation> m_info;
+};
+
+} } // namespace JSC::Wasm
+
+#endif // ENABLE(WEBASSEMBLY)
diff --git a/Source/JavaScriptCore/wasm/WasmStreamingParser.cpp b/Source/JavaScriptCore/wasm/WasmStreamingParser.cpp
new file mode 100644 (file)
index 0000000..9f37260
--- /dev/null
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2018 Yusuke Suzuki <yusukesuzuki@slowstart.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WasmStreamingParser.h"
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "WasmModuleParser.h"
+#include "WasmSectionParser.h"
+#include <wtf/UnalignedAccess.h>
+
+namespace JSC { namespace Wasm {
+
+namespace WasmStreamingParserInternal {
+static constexpr bool verbose = false;
+}
+
+#define WASM_STREAMING_PARSER_FAIL_IF_HELPER_FAILS(helper) \
+    do { \
+        auto helperResult = helper; \
+        if (UNLIKELY(!helperResult)) { \
+            m_errorMessage = helperResult.error(); \
+            return State::FatalError; \
+        } \
+    } while (0)
+
+ALWAYS_INLINE std::optional<uint8_t> parseUInt7(const uint8_t* data, size_t& offset, size_t size)
+{
+    if (offset >= size)
+        return false;
+    uint8_t result = data[offset++];
+    if (result < 0x80)
+        return result;
+    return std::nullopt;
+}
+
+template <typename ...Args>
+NEVER_INLINE auto WARN_UNUSED_RETURN StreamingParser::fail(Args... args) -> State
+{
+    using namespace FailureHelper; // See ADL comment in namespace above.
+    m_errorMessage = makeString("WebAssembly.Module doesn't parse at byte "_s, String::number(m_offset), ": "_s, makeString(args)...);
+    dataLogLnIf(WasmStreamingParserInternal::verbose, m_errorMessage);
+    return State::FatalError;
+}
+
+StreamingParser::StreamingParser(ModuleInformation& info)
+    : m_info(info)
+{
+    dataLogLnIf(WasmStreamingParserInternal::verbose, "starting validation");
+}
+
+auto StreamingParser::parseModuleHeader(Vector<uint8_t>&& data) -> State
+{
+    ASSERT(data.size() == moduleHeaderSize);
+    dataLogLnIf(WasmStreamingParserInternal::verbose, "header validation");
+    WASM_PARSER_FAIL_IF(data[0] != '\0' || data[1] != 'a' || data[2] != 's' || data[3] != 'm', "modules doesn't start with '\\0asm'");
+    uint32_t versionNumber = WTF::unalignedLoad<uint32_t>(data.data() + 4);
+    WASM_PARSER_FAIL_IF(versionNumber != expectedVersionNumber, "unexpected version number ", versionNumber, " expected ", expectedVersionNumber);
+    return State::SectionID;
+}
+
+auto StreamingParser::parseSectionID(Vector<uint8_t>&& data) -> State
+{
+    ASSERT(data.size() == sectionIDSize);
+    size_t offset = 0;
+    auto result = parseUInt7(data.data(), offset, data.size());
+    WASM_PARSER_FAIL_IF(!result, "can't get section byte");
+
+    Section section = Section::Custom;
+    WASM_PARSER_FAIL_IF(!decodeSection(*result, section), "invalid section");
+    ASSERT(section != Section::Begin);
+    WASM_PARSER_FAIL_IF(!validateOrder(m_previousKnownSection, section), "invalid section order, ", m_previousKnownSection, " followed by ", section);
+    m_section = section;
+    if (isKnownSection(section))
+        m_previousKnownSection = section;
+    return State::SectionSize;
+}
+
+auto StreamingParser::parseSectionSize(uint32_t sectionLength) -> State
+{
+    m_sectionLength = sectionLength;
+    if (m_section == Section::Code)
+        return State::CodeSectionSize;
+    return State::SectionPayload;
+}
+
+auto StreamingParser::parseCodeSectionSize(uint32_t functionCount) -> State
+{
+    m_functionCount = functionCount;
+    m_functionIndex = 0;
+    m_codeOffset = m_offset;
+
+    WASM_PARSER_FAIL_IF(functionCount == std::numeric_limits<uint32_t>::max(), "Code section's count is too big ", functionCount);
+    WASM_PARSER_FAIL_IF(functionCount != m_info->functions.size(), "Code section count ", functionCount, " exceeds the declared number of functions ", m_info->functions.size());
+
+    if (m_functionIndex == m_functionCount) {
+        WASM_PARSER_FAIL_IF((m_codeOffset + m_sectionLength) != m_nextOffset, "parsing ended before the end of ", m_section, " section");
+        return State::SectionID;
+    }
+    return State::FunctionSize;
+}
+
+auto StreamingParser::parseFunctionSize(uint32_t functionSize) -> State
+{
+    m_functionSize = functionSize;
+    WASM_PARSER_FAIL_IF(functionSize > maxFunctionSize, "Code function's size ", functionSize, " is too big");
+    return State::FunctionPayload;
+}
+
+auto StreamingParser::parseFunctionPayload(Vector<uint8_t>&& data) -> State
+{
+    auto& function = m_info->functions[m_functionIndex];
+    function.start = m_offset;
+    function.end = m_offset + m_functionSize;
+    function.data = WTFMove(data);
+    dataLogLnIf(WasmStreamingParserInternal::verbose, "Processing function starting at: ", function.start, " and ending at: ", function.end);
+    ++m_functionIndex;
+    if (m_functionIndex == m_functionCount) {
+        WASM_PARSER_FAIL_IF((m_codeOffset + m_sectionLength) != (m_offset + m_functionSize), "parsing ended before the end of ", m_section, " section");
+        return State::SectionID;
+    }
+    return State::FunctionSize;
+}
+
+auto StreamingParser::parseSectionPayload(Vector<uint8_t>&& data) -> State
+{
+    SectionParser parser(data.data(), data.size(), m_offset, m_info.get());
+    switch (m_section) {
+#define WASM_SECTION_PARSE(NAME, ID, DESCRIPTION) \
+    case Section::NAME: { \
+        WASM_STREAMING_PARSER_FAIL_IF_HELPER_FAILS(parser.parse ## NAME()); \
+        break; \
+    }
+    FOR_EACH_KNOWN_WASM_SECTION(WASM_SECTION_PARSE)
+#undef WASM_SECTION_PARSE
+
+    case Section::Custom: {
+        WASM_STREAMING_PARSER_FAIL_IF_HELPER_FAILS(parser.parseCustom());
+        break;
+    }
+
+    case Section::Begin: {
+        RELEASE_ASSERT_NOT_REACHED();
+        break;
+    }
+    }
+
+    WASM_PARSER_FAIL_IF(parser.length() != parser.offset(), "parsing ended before the end of ", m_section, " section");
+
+    return State::SectionID;
+}
+
+auto StreamingParser::consume(const uint8_t* bytes, size_t bytesSize, size_t& offsetInBytes, size_t requiredSize) -> std::optional<Vector<uint8_t>>
+{
+    if (m_remaining.size() == requiredSize) {
+        Vector<uint8_t> result = WTFMove(m_remaining);
+        m_nextOffset += requiredSize;
+        return WTFMove(result);
+    }
+
+    if (m_remaining.size() > requiredSize) {
+        Vector<uint8_t> result(requiredSize);
+        memcpy(result.data(), m_remaining.data(), requiredSize);
+        m_remaining.remove(0, requiredSize);
+        m_nextOffset += requiredSize;
+        return WTFMove(result);
+    }
+
+    ASSERT(m_remaining.size() < requiredSize);
+    size_t bytesRemainingSize = bytesSize - offsetInBytes;
+    size_t totalDataSize = m_remaining.size() + bytesRemainingSize;
+    if (totalDataSize < requiredSize) {
+        m_remaining.append(bytes + offsetInBytes, bytesRemainingSize);
+        offsetInBytes = bytesSize;
+        return std::nullopt;
+    }
+
+    size_t usedSize = requiredSize - m_remaining.size();
+    m_remaining.append(bytes + offsetInBytes, usedSize);
+    offsetInBytes += usedSize;
+    Vector<uint8_t> result = WTFMove(m_remaining);
+    m_nextOffset += requiredSize;
+    return WTFMove(result);
+}
+
+auto StreamingParser::consumeVarUInt32(const uint8_t* bytes, size_t bytesSize, size_t& offsetInBytes, IsEndOfStream isEndOfStream) -> Expected<uint32_t, State>
+{
+    constexpr size_t maxSize = WTF::LEBDecoder::maxByteLength<uint32_t>();
+    size_t bytesRemainingSize = bytesSize - offsetInBytes;
+    size_t totalDataSize = m_remaining.size() + bytesRemainingSize;
+    if (m_remaining.size() >= maxSize) {
+        // Do nothing.
+    } else if (totalDataSize >= maxSize) {
+        size_t usedSize = maxSize - m_remaining.size();
+        m_remaining.append(bytes + offsetInBytes, usedSize);
+        offsetInBytes += usedSize;
+    } else {
+        m_remaining.append(bytes + offsetInBytes, bytesRemainingSize);
+        offsetInBytes += bytesRemainingSize;
+        // If the given bytes are the end of the stream, we try to parse VarUInt32
+        // with the current remaining data since VarUInt32 may not require `maxSize`.
+        if (isEndOfStream == IsEndOfStream::No)
+            return makeUnexpected(m_state);
+    }
+
+    size_t offset = 0;
+    uint32_t result = 0;
+    if (!WTF::LEBDecoder::decodeUInt32(m_remaining.data(), m_remaining.size(), offset, result))
+        return makeUnexpected(State::FatalError);
+    size_t consumedSize = offset;
+    m_remaining.remove(0, consumedSize);
+    m_nextOffset += consumedSize;
+    return result;
+}
+
+auto StreamingParser::addBytes(const uint8_t* bytes, size_t bytesSize, IsEndOfStream isEndOfStream) -> State
+{
+    if (m_state == State::FatalError)
+        return m_state;
+
+    m_totalSize += bytesSize;
+    if (UNLIKELY(m_totalSize.hasOverflowed() || m_totalSize.unsafeGet() > maxModuleSize)) {
+        m_state = fail("module size is too large, maximum ", maxModuleSize);
+        return m_state;
+    }
+
+    if (UNLIKELY(Options::useEagerWebAssemblyModuleHashing()))
+        m_hasher.addBytes(bytes, bytesSize);
+
+    size_t offsetInBytes = 0;
+    while (true) {
+        ASSERT(offsetInBytes <= bytesSize);
+        switch (m_state) {
+        case State::ModuleHeader: {
+            auto result = consume(bytes, bytesSize, offsetInBytes, moduleHeaderSize);
+            if (!result)
+                return m_state;
+            m_state = parseModuleHeader(WTFMove(*result));
+            break;
+        }
+
+        case State::SectionID: {
+            auto result = consume(bytes, bytesSize, offsetInBytes, sectionIDSize);
+            if (!result)
+                return m_state;
+            m_state = parseSectionID(WTFMove(*result));
+            break;
+        }
+
+        case State::SectionSize: {
+            auto result = consumeVarUInt32(bytes, bytesSize, offsetInBytes, isEndOfStream);
+            if (!result) {
+                if (result.error() == State::FatalError)
+                    m_state = failOnState(m_state);
+                else
+                    m_state = result.error();
+                return m_state;
+            }
+            m_state = parseSectionSize(*result);
+            break;
+        }
+
+        case State::SectionPayload: {
+            auto result = consume(bytes, bytesSize, offsetInBytes, m_sectionLength);
+            if (!result)
+                return m_state;
+            m_state = parseSectionPayload(WTFMove(*result));
+            break;
+        }
+
+        case State::CodeSectionSize: {
+            auto result = consumeVarUInt32(bytes, bytesSize, offsetInBytes, isEndOfStream);
+            if (!result) {
+                if (result.error() == State::FatalError)
+                    m_state = failOnState(m_state);
+                else
+                    m_state = result.error();
+                return m_state;
+            }
+            m_state = parseCodeSectionSize(*result);
+            break;
+        }
+
+        case State::FunctionSize: {
+            auto result = consumeVarUInt32(bytes, bytesSize, offsetInBytes, isEndOfStream);
+            if (!result) {
+                if (result.error() == State::FatalError)
+                    m_state = failOnState(m_state);
+                else
+                    m_state = result.error();
+                return m_state;
+            }
+            m_state = parseFunctionSize(*result);
+            break;
+        }
+
+        case State::FunctionPayload: {
+            auto result = consume(bytes, bytesSize, offsetInBytes, m_functionSize);
+            if (!result)
+                return m_state;
+            m_state = parseFunctionPayload(WTFMove(*result));
+            break;
+        }
+
+        case State::Finished:
+        case State::FatalError:
+            return m_state;
+        }
+
+        m_offset = m_nextOffset;
+    }
+}
+
+auto StreamingParser::failOnState(State) -> State
+{
+    switch (m_state) {
+    case State::ModuleHeader:
+        return fail("expected a module of at least ", moduleHeaderSize, " bytes");
+    case State::SectionID:
+        return fail("can't get section byte");
+    case State::SectionSize:
+        return fail("can't get ", m_section, " section's length");
+    case State::SectionPayload:
+        return fail(m_section, " section of size ", m_sectionLength, " would overflow Module's size");
+    case State::CodeSectionSize:
+        return fail("can't get Code section's count");
+    case State::FunctionSize:
+        return fail("can't get ", m_functionIndex, "th Code function's size");
+    case State::FunctionPayload:
+        return fail("Code function's size ", m_functionSize, " exceeds the module's remaining size");
+    case State::Finished:
+    case State::FatalError:
+        return m_state;
+    }
+    return m_state;
+}
+
+auto StreamingParser::finalize() -> State
+{
+    addBytes(nullptr, 0, IsEndOfStream::Yes);
+    switch (m_state) {
+    case State::ModuleHeader:
+    case State::SectionSize:
+    case State::SectionPayload:
+    case State::CodeSectionSize:
+    case State::FunctionSize:
+    case State::FunctionPayload:
+        m_state = failOnState(m_state);
+        break;
+
+    case State::Finished:
+    case State::FatalError:
+        break;
+
+    case State::SectionID:
+        if (m_remaining.isEmpty()) {
+            if (UNLIKELY(Options::useEagerWebAssemblyModuleHashing()))
+                m_info->nameSection->setHash(m_hasher.computeHexDigest());
+            m_state = State::Finished;
+        } else
+            m_state = failOnState(State::SectionID);
+        break;
+    }
+    return m_state;
+}
+
+} } // namespace JSC::Wasm
+
+#endif // ENABLE(WEBASSEMBLY)
diff --git a/Source/JavaScriptCore/wasm/WasmStreamingParser.h b/Source/JavaScriptCore/wasm/WasmStreamingParser.h
new file mode 100644 (file)
index 0000000..1fbb6b2
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 Yusuke Suzuki <yusukesuzuki@slowstart.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "WasmModuleInformation.h"
+#include "WasmParser.h"
+#include "WasmSections.h"
+#include <wtf/Expected.h>
+#include <wtf/Optional.h>
+#include <wtf/SHA1.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+namespace JSC { namespace Wasm {
+
+class StreamingParserClient {
+};
+
+class StreamingParser {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    // The layout of the Wasm module is the following.
+    //
+    // Module:       [ Header ][ Section ]*
+    // Section:      [ ID ][ SizeOfPayload ][ Payload ]
+    // Code Section: [ ID ][ SizeOfPayload ][                   Payload of Code Section                   ]
+    //                                      [ NumberOfFunctions ]([ SizeOfFunction ][ PayloadOfFunction ])*
+    //
+    // Basically we can parse Wasm sections by repeatedly (1) reading the size of the payload, and (2) reading the payload based on the size read in (1).
+    // So this streaming parser handles Section as the unit for incremental parsing. The exception is the Code section. The Code section is large since it
+    // includes all the functions in the wasm module. Since we would like to compile each function and parse the Code section concurrently, the streaming
+    // parser specially handles the Code section. In the Code section, the streaming parser uses Function as the unit for incremental parsing.
+    enum class State : uint8_t {
+        ModuleHeader,
+        SectionID,
+        SectionSize,
+        SectionPayload,
+        CodeSectionSize,
+        FunctionSize,
+        FunctionPayload,
+        Finished,
+        FatalError,
+    };
+
+    enum class IsEndOfStream { Yes, No };
+
+    StreamingParser(ModuleInformation&);
+
+    State addBytes(const uint8_t* bytes, size_t length) { return addBytes(bytes, length, IsEndOfStream::No); }
+    State finalize();
+
+    const String& errorMessage() const { return m_errorMessage; }
+
+private:
+    static constexpr unsigned moduleHeaderSize = 8;
+    static constexpr unsigned sectionIDSize = 1;
+
+    State addBytes(const uint8_t* bytes, size_t length, IsEndOfStream);
+
+    State parseModuleHeader(Vector<uint8_t>&&);
+    State parseSectionID(Vector<uint8_t>&&);
+    State parseSectionSize(uint32_t);
+    State parseSectionPayload(Vector<uint8_t>&&);
+
+    State parseCodeSectionSize(uint32_t);
+    State parseFunctionSize(uint32_t);
+    State parseFunctionPayload(Vector<uint8_t>&&);
+
+    std::optional<Vector<uint8_t>> consume(const uint8_t* bytes, size_t, size_t&, size_t);
+    Expected<uint32_t, State> consumeVarUInt32(const uint8_t* bytes, size_t, size_t&, IsEndOfStream);
+
+    template <typename ...Args> NEVER_INLINE State WARN_UNUSED_RETURN fail(Args...);
+
+    State failOnState(State);
+
+    Ref<ModuleInformation> m_info;
+    Vector<uint8_t> m_remaining;
+    String m_errorMessage;
+
+    CheckedSize m_totalSize { 0 };
+    size_t m_offset { 0 };
+    size_t m_nextOffset { 0 };
+    size_t m_codeOffset { 0 };
+
+    SHA1 m_hasher;
+
+    uint32_t m_sectionLength { 0 };
+
+    uint32_t m_functionCount { 0 };
+    uint32_t m_functionIndex { 0 };
+
+    uint32_t m_functionSize { 0 };
+
+    State m_state { State::ModuleHeader };
+    Section m_section { Section::Begin };
+    Section m_previousKnownSection { Section::Begin };
+};
+
+
+} } // namespace JSC::Wasm
+
+#endif // ENABLE(WEBASSEMBLY)
index 23fef22..aebce0c 100644 (file)
@@ -140,11 +140,6 @@ void JSWebAssemblyModule::visitChildren(JSCell* cell, SlotVisitor& visitor)
         visitor.append(thisObject->m_codeBlocks[i]);
 }
 
-const Vector<uint8_t>& JSWebAssemblyModule::source() const
-{
-    return moduleInformation().source;
-}
-
 } // namespace JSC
 
 #endif // ENABLE(WEBASSEMBLY)
index 17bed61..d393006 100644 (file)
@@ -68,8 +68,6 @@ public:
     JSWebAssemblyCodeBlock* codeBlock(Wasm::MemoryMode mode);
     void setCodeBlock(VM&, Wasm::MemoryMode, JSWebAssemblyCodeBlock*);
 
-    const Vector<uint8_t>& source() const;
-
     JS_EXPORT_PRIVATE Wasm::Module& module();
 
 private:
index 96d4c14..dcadc0d 100644 (file)
@@ -294,10 +294,10 @@ static EncodedJSValue JSC_HOST_CALL webAssemblyValidateFunc(ExecState* exec)
     size_t byteSize;
     std::tie(base, byteSize) = getWasmBufferFromValue(exec, exec->argument(0));
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    BBQPlan plan(&vm.wasmContext, base, byteSize, BBQPlan::Validation, Plan::dontFinalize());
+    BBQPlan plan(&vm.wasmContext, BBQPlan::Validation, Plan::dontFinalize());
     // FIXME: We might want to throw an OOM exception here if we detect that something will OOM.
     // https://bugs.webkit.org/show_bug.cgi?id=166015
-    return JSValue::encode(jsBoolean(plan.parseAndValidateModule()));
+    return JSValue::encode(jsBoolean(plan.parseAndValidateModule(base, byteSize)));
 }
 
 EncodedJSValue JSC_HOST_CALL webAssemblyCompileStreamingInternal(ExecState* exec)
index c29ff36..96cac60 100644 (file)
@@ -1,3 +1,17 @@
+2018-08-27  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
+
+        [WebAssembly] Parse wasm modules in a streaming fashion
+        https://bugs.webkit.org/show_bug.cgi?id=188943
+
+        Reviewed by Mark Lam.
+
+        Add maxByteLength function to get the maximum size for T.
+
+        * wtf/LEBDecoder.h:
+        (WTF::LEBDecoder::maxByteLength):
+        (WTF::LEBDecoder::decodeUInt):
+        (WTF::LEBDecoder::decodeInt):
+
 2018-08-27  Keith Rollin  <krollin@apple.com>
 
         Unreviewed build fix -- disable LTO for production builds
index 9462770..99a5c09 100644 (file)
 namespace WTF { namespace LEBDecoder {
 
 template<typename T>
-inline bool WARN_UNUSED_RETURN decodeUInt(const uint8_t* bytes, size_t length, size_t& offset, T& result)
+inline constexpr size_t maxByteLength()
 {
     const size_t numBits = sizeof(T) * CHAR_BIT;
-    const size_t maxByteLength = (numBits - 1) / 7 + 1; // numBits / 7 rounding up.
+    return (numBits - 1) / 7 + 1; // numBits / 7 rounding up.
+}
+
+template<typename T>
+inline bool WARN_UNUSED_RETURN decodeUInt(const uint8_t* bytes, size_t length, size_t& offset, T& result)
+{
     if (length <= offset)
         return false;
     result = 0;
     unsigned shift = 0;
-    size_t last = std::min(maxByteLength, length - offset) - 1;
+    size_t last = std::min(maxByteLength<T>(), length - offset) - 1;
     for (unsigned i = 0; true; ++i) {
         uint8_t byte = bytes[offset++];
         result |= static_cast<T>(byte & 0x7f) << shift;
@@ -61,13 +66,11 @@ inline bool WARN_UNUSED_RETURN decodeUInt(const uint8_t* bytes, size_t length, s
 template<typename T>
 inline bool WARN_UNUSED_RETURN decodeInt(const uint8_t* bytes, size_t length, size_t& offset, T& result)
 {
-    const size_t numBits = sizeof(T) * CHAR_BIT;
-    const size_t maxByteLength = (numBits - 1) / 7 + 1; // numBits / 7 rounding up.
     if (length <= offset)
         return false;
     result = 0;
     unsigned shift = 0;
-    size_t last = std::min(maxByteLength, length - offset) - 1;
+    size_t last = std::min(maxByteLength<T>(), length - offset) - 1;
     uint8_t byte;
     for (unsigned i = 0; true; ++i) {
         byte = bytes[offset++];
@@ -80,6 +83,7 @@ inline bool WARN_UNUSED_RETURN decodeInt(const uint8_t* bytes, size_t length, si
     }
 
     using UnsignedT = typename std::make_unsigned<T>::type;
+    const size_t numBits = sizeof(T) * CHAR_BIT;
     if (shift < numBits && (byte & 0x40))
         result = static_cast<T>(static_cast<UnsignedT>(result) | (static_cast<UnsignedT>(-1) << shift));
     return true;