WebAssembly: Make WebAssembly.instantiate/compile truly asynchronous
authorkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 28 Mar 2017 23:12:11 +0000 (23:12 +0000)
committerkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 28 Mar 2017 23:12:11 +0000 (23:12 +0000)
https://bugs.webkit.org/show_bug.cgi?id=169187

Reviewed by Saam Barati.

JSTests:

* wasm/assert.js:
* wasm/js-api/Module-compile.js:
(async.testPromiseAPI):
* wasm/js-api/web-assembly-compile-parallel.js: Added.
(async.throwExn):
(async.test):
* wasm/js-api/web-assembly-instantiate-parallel.js: Added.
(async.test):
* wasm/js-api/web-assembly-instantiate.js:
(assert.eq.async.test):
(assert.eq):
(assert.asyncTest.async.test):
(assert.asyncTest):
(assert.truthy.async.test): Deleted.
(assert.truthy): Deleted.

Source/JavaScriptCore:

This patch allows WebAssembly compilations to happen asynchronously.
To do so, it refactors how much of the compilation happens and adds
new infrastructure for async promises.

First, there is a new class, PromiseDeferredTimer that lives on
the VM.  PromiseDeferredTimer will manage the life-cycle of async
pending promises and any dependencies that promise
needs. PromiseDeferredTimer automagically releases the pending
promise and dependencies once the JSPromiseDeferred is resolved or
rejected. Additionally, PromiseDeferredTimer provides a mechanism
to poll the run-loop whenever the async task needs to synchronize
with the JS thread. Normally, that will be whenever the async task
finishes. In the case of Web Assembly we also use this feature for
the compile + instantiate case, where we might have more work
after the first async task completes (more on that later).

The next class is Wasm::Worklist, which is used to manage Wasm
compilation tasks. The worklist class works similarly to the
DFG/FTL Worklists. It has a pool of threads that it manages. One
interesting aspect of Wasm Worklist is that it can synchronously
compile a plan that is already potentially running
asynchronously. This can occur if a user calls
WebAssembly.instantiate() then new WebAssembly.instantiate() on
the same module. In that case the Wasm Worklist will bump the
priority of the running pending Plan and block the JS thread.

This patch also makes some of the Wasm Plan code cleaner. Since we
now defer all compilation to instantiation time, we no longer need
to guess at which memory we are going to get. Also, Wasm Plans now
track the work they have done with a state enum.

Finally, this patch makes renamed HeapTimer to JSRunLoopTimer. It
also adds changes test262AsyncTest to a more generic testing
infrastructure. Now, in addition to the old functionality, you can
call asyncTest() with the number of tests you expect. When the jsc
CLI exits, it will guarantee that asyncTestPassed() is called that
many times.

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* heap/GCActivityCallback.h:
* heap/IncrementalSweeper.cpp:
(JSC::IncrementalSweeper::scheduleTimer):
(JSC::IncrementalSweeper::IncrementalSweeper):
* heap/IncrementalSweeper.h:
* heap/StopIfNecessaryTimer.cpp:
(JSC::StopIfNecessaryTimer::StopIfNecessaryTimer):
* heap/StopIfNecessaryTimer.h:
* heap/StrongInlines.h:
* jsc.cpp:
(GlobalObject::finishCreation):
(printInternal):
(functionAsyncTestStart):
(functionAsyncTestPassed):
(functionTestWasmModuleFunctions):
(CommandLine::parseArguments):
(runJSC):
* runtime/JSPromiseDeferred.cpp:
(JSC::JSPromiseDeferred::resolve):
(JSC::JSPromiseDeferred::reject):
* runtime/JSPromiseDeferred.h:
(JSC::JSPromiseDeferred::promiseAsyncPending):
* runtime/JSRunLoopTimer.cpp: Renamed from Source/JavaScriptCore/heap/HeapTimer.cpp.
(JSC::JSRunLoopTimer::JSRunLoopTimer):
(JSC::JSRunLoopTimer::setRunLoop):
(JSC::JSRunLoopTimer::~JSRunLoopTimer):
(JSC::JSRunLoopTimer::timerDidFire):
(JSC::JSRunLoopTimer::scheduleTimer):
(JSC::JSRunLoopTimer::cancelTimer):
(JSC::JSRunLoopTimer::invalidate):
* runtime/JSRunLoopTimer.h: Copied from Source/JavaScriptCore/heap/HeapTimer.h.
* runtime/Options.h:
* runtime/PromiseDeferredTimer.cpp: Added.
(JSC::PromiseDeferredTimer::PromiseDeferredTimer):
(JSC::PromiseDeferredTimer::doWork):
(JSC::PromiseDeferredTimer::runRunLoop):
(JSC::PromiseDeferredTimer::addPendingPromise):
(JSC::PromiseDeferredTimer::cancelPendingPromise):
(JSC::PromiseDeferredTimer::scheduleWorkSoon):
(JSC::PromiseDeferredTimer::scheduleBlockedTask):
* runtime/PromiseDeferredTimer.h: Renamed from Source/JavaScriptCore/heap/HeapTimer.h.
(JSC::PromiseDeferredTimer::stopRunningTasks):
* runtime/VM.cpp:
(JSC::VM::VM):
(JSC::VM::~VM):
* runtime/VM.h:
* wasm/JSWebAssembly.cpp:
(JSC::reject):
(JSC::webAssemblyCompileFunc):
(JSC::resolve):
(JSC::instantiate):
(JSC::compileAndInstantiate):
(JSC::webAssemblyInstantiateFunc):
(JSC::webAssemblyValidateFunc):
* wasm/WasmB3IRGenerator.cpp:
(JSC::Wasm::B3IRGenerator::B3IRGenerator):
(JSC::Wasm::B3IRGenerator::emitCheckAndPreparePointer):
(JSC::Wasm::B3IRGenerator::memoryKind):
(JSC::Wasm::parseAndCompile):
* wasm/WasmB3IRGenerator.h:
* wasm/WasmFormat.h:
(JSC::Wasm::ModuleInformation::internalFunctionCount):
* wasm/WasmFunctionParser.h:
* wasm/WasmMemory.h:
* wasm/WasmMemoryInformation.cpp:
(JSC::Wasm::MemoryInformation::MemoryInformation):
* wasm/WasmMemoryInformation.h:
(JSC::Wasm::MemoryInformation::maximum):
(JSC::Wasm::MemoryInformation::hasReservedMemory): Deleted.
(JSC::Wasm::MemoryInformation::takeReservedMemory): Deleted.
(JSC::Wasm::MemoryInformation::mode): Deleted.
* wasm/WasmModuleParser.cpp:
* wasm/WasmModuleParser.h:
(JSC::Wasm::ModuleParser::ModuleParser):
* wasm/WasmPlan.cpp:
(JSC::Wasm::Plan::Plan):
(JSC::Wasm::Plan::stateString):
(JSC::Wasm::Plan::moveToState):
(JSC::Wasm::Plan::fail):
(JSC::Wasm::Plan::parseAndValidateModule):
(JSC::Wasm::Plan::prepare):
(JSC::Wasm::Plan::ThreadCountHolder::ThreadCountHolder):
(JSC::Wasm::Plan::ThreadCountHolder::~ThreadCountHolder):
(JSC::Wasm::Plan::compileFunctions):
(JSC::Wasm::Plan::complete):
(JSC::Wasm::Plan::waitForCompletion):
(JSC::Wasm::Plan::cancel):
(JSC::Wasm::Plan::run): Deleted.
(JSC::Wasm::Plan::initializeCallees): Deleted.
* wasm/WasmPlan.h:
(JSC::Wasm::Plan::dontFinalize):
(JSC::Wasm::Plan::exports):
(JSC::Wasm::Plan::internalFunctionCount):
(JSC::Wasm::Plan::takeModuleInformation):
(JSC::Wasm::Plan::takeCallLinkInfos):
(JSC::Wasm::Plan::takeWasmExitStubs):
(JSC::Wasm::Plan::setModeAndPromise):
(JSC::Wasm::Plan::mode):
(JSC::Wasm::Plan::pendingPromise):
(JSC::Wasm::Plan::vm):
(JSC::Wasm::Plan::errorMessage):
(JSC::Wasm::Plan::failed):
(JSC::Wasm::Plan::hasWork):
(JSC::Wasm::Plan::hasBeenPrepared):
* wasm/WasmPlanInlines.h: Copied from Source/JavaScriptCore/wasm/WasmB3IRGenerator.h.
(JSC::Wasm::Plan::initializeCallees):
* wasm/WasmValidate.cpp:
* wasm/WasmWorklist.cpp: Added.
(JSC::Wasm::Worklist::priorityString):
(JSC::Wasm::Worklist::QueueElement::setToNextPriority):
(JSC::Wasm::Worklist::iterate):
(JSC::Wasm::Worklist::enqueue):
(JSC::Wasm::Worklist::completePlanSynchronously):
(JSC::Wasm::Worklist::stopAllPlansForVM):
(JSC::Wasm::Worklist::Worklist):
(JSC::Wasm::Worklist::~Worklist):
(JSC::Wasm::existingWorklistOrNull):
(JSC::Wasm::ensureWorklist):
* wasm/WasmWorklist.h: Added.
(JSC::Wasm::Worklist::nextTicket):
(JSC::Wasm::Worklist::Comparator::operator()):
* wasm/js/JSWebAssemblyCallee.h:
* wasm/js/JSWebAssemblyCodeBlock.cpp:
(JSC::JSWebAssemblyCodeBlock::JSWebAssemblyCodeBlock):
(JSC::JSWebAssemblyCodeBlock::initialize):
(JSC::JSWebAssemblyCodeBlock::isSafeToRun):
* wasm/js/JSWebAssemblyCodeBlock.h:
(JSC::JSWebAssemblyCodeBlock::create):
(JSC::JSWebAssemblyCodeBlock::initialized):
(JSC::JSWebAssemblyCodeBlock::plan):
(JSC::JSWebAssemblyCodeBlock::runnable):
(JSC::JSWebAssemblyCodeBlock::errorMessage):
(JSC::JSWebAssemblyCodeBlock::callees):
* wasm/js/JSWebAssemblyHelpers.h:
(JSC::createSourceBufferFromValue):
* wasm/js/JSWebAssemblyInstance.cpp:
(JSC::JSWebAssemblyInstance::finishCreation):
(JSC::JSWebAssemblyInstance::visitChildren):
(JSC::JSWebAssemblyInstance::addUnitializedCodeBlock):
(JSC::JSWebAssemblyInstance::finalizeCreation):
(JSC::JSWebAssemblyInstance::create):
(JSC::JSWebAssemblyInstance::setMemory): Deleted.
* wasm/js/JSWebAssemblyInstance.h:
(JSC::JSWebAssemblyInstance::codeBlock):
(JSC::JSWebAssemblyInstance::initialized):
(JSC::JSWebAssemblyInstance::module):
(JSC::JSWebAssemblyInstance::importFunction):
(JSC::JSWebAssemblyInstance::setMemory):
(JSC::JSWebAssemblyInstance::table):
(JSC::JSWebAssemblyInstance::importFunctions):
(JSC::JSWebAssemblyInstance::setImportFunction): Deleted.
(JSC::JSWebAssemblyInstance::setTable): Deleted.
* wasm/js/JSWebAssemblyModule.cpp:
(JSC::JSWebAssemblyModule::createStub):
(JSC::JSWebAssemblyModule::JSWebAssemblyModule):
(JSC::JSWebAssemblyModule::finishCreation):
(JSC::JSWebAssemblyModule::setCodeBlock):
(JSC::JSWebAssemblyModule::buildCodeBlock): Deleted.
(JSC::JSWebAssemblyModule::create): Deleted.
(JSC::JSWebAssemblyModule::codeBlock): Deleted.
* wasm/js/JSWebAssemblyModule.h:
(JSC::JSWebAssemblyModule::moduleInformation):
(JSC::JSWebAssemblyModule::codeBlock):
(JSC::JSWebAssemblyModule::source):
(JSC::JSWebAssemblyModule::takeReservedMemory): Deleted.
(JSC::JSWebAssemblyModule::codeBlockFor): Deleted.
* wasm/js/WebAssemblyInstanceConstructor.cpp:
(JSC::constructJSWebAssemblyInstance):
(JSC::WebAssemblyInstanceConstructor::createInstance): Deleted.
* wasm/js/WebAssemblyModuleConstructor.cpp:
(JSC::WebAssemblyModuleConstructor::createModule):
* wasm/js/WebAssemblyModulePrototype.cpp:
(JSC::webAssemblyModuleProtoImports):
(JSC::webAssemblyModuleProtoExports):
* wasm/js/WebAssemblyModuleRecord.cpp:
(JSC::WebAssemblyModuleRecord::finishCreation):
(JSC::WebAssemblyModuleRecord::link):
(JSC::WebAssemblyModuleRecord::evaluate):
* wasm/js/WebAssemblyModuleRecord.h:

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

54 files changed:
JSTests/ChangeLog
JSTests/wasm/assert.js
JSTests/wasm/js-api/Module-compile.js
JSTests/wasm/js-api/web-assembly-compile-parallel.js [new file with mode: 0644]
JSTests/wasm/js-api/web-assembly-instantiate-parallel.js [new file with mode: 0644]
JSTests/wasm/js-api/web-assembly-instantiate.js
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/heap/GCActivityCallback.h
Source/JavaScriptCore/heap/IncrementalSweeper.cpp
Source/JavaScriptCore/heap/IncrementalSweeper.h
Source/JavaScriptCore/heap/StopIfNecessaryTimer.cpp
Source/JavaScriptCore/heap/StopIfNecessaryTimer.h
Source/JavaScriptCore/heap/StrongInlines.h
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/runtime/JSPromiseDeferred.cpp
Source/JavaScriptCore/runtime/JSPromiseDeferred.h
Source/JavaScriptCore/runtime/JSRunLoopTimer.cpp [moved from Source/JavaScriptCore/heap/HeapTimer.cpp with 73% similarity]
Source/JavaScriptCore/runtime/JSRunLoopTimer.h [moved from Source/JavaScriptCore/heap/HeapTimer.h with 91% similarity]
Source/JavaScriptCore/runtime/Options.h
Source/JavaScriptCore/runtime/PromiseDeferredTimer.cpp [new file with mode: 0644]
Source/JavaScriptCore/runtime/PromiseDeferredTimer.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/VM.h
Source/JavaScriptCore/wasm/JSWebAssembly.cpp
Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
Source/JavaScriptCore/wasm/WasmB3IRGenerator.h
Source/JavaScriptCore/wasm/WasmFormat.h
Source/JavaScriptCore/wasm/WasmFunctionParser.h
Source/JavaScriptCore/wasm/WasmMemory.h
Source/JavaScriptCore/wasm/WasmMemoryInformation.cpp
Source/JavaScriptCore/wasm/WasmMemoryInformation.h
Source/JavaScriptCore/wasm/WasmModuleParser.cpp
Source/JavaScriptCore/wasm/WasmModuleParser.h
Source/JavaScriptCore/wasm/WasmPlan.cpp
Source/JavaScriptCore/wasm/WasmPlan.h
Source/JavaScriptCore/wasm/WasmPlanInlines.h [new file with mode: 0644]
Source/JavaScriptCore/wasm/WasmValidate.cpp
Source/JavaScriptCore/wasm/WasmWorklist.cpp [new file with mode: 0644]
Source/JavaScriptCore/wasm/WasmWorklist.h [new file with mode: 0644]
Source/JavaScriptCore/wasm/js/JSWebAssemblyCallee.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyCodeBlock.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyCodeBlock.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyHelpers.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h
Source/JavaScriptCore/wasm/js/WebAssemblyInstanceConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyModuleConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyModulePrototype.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.h

index affa06d..f71dbaf 100644 (file)
@@ -1,3 +1,26 @@
+2017-03-28  Keith Miller  <keith_miller@apple.com>
+
+        WebAssembly: Make WebAssembly.instantiate/compile truly asynchronous
+        https://bugs.webkit.org/show_bug.cgi?id=169187
+
+        Reviewed by Saam Barati.
+
+        * wasm/assert.js:
+        * wasm/js-api/Module-compile.js:
+        (async.testPromiseAPI):
+        * wasm/js-api/web-assembly-compile-parallel.js: Added.
+        (async.throwExn):
+        (async.test):
+        * wasm/js-api/web-assembly-instantiate-parallel.js: Added.
+        (async.test):
+        * wasm/js-api/web-assembly-instantiate.js:
+        (assert.eq.async.test):
+        (assert.eq):
+        (assert.asyncTest.async.test):
+        (assert.asyncTest):
+        (assert.truthy.async.test): Deleted.
+        (assert.truthy): Deleted.
+
 2017-03-28  JF Bastien  <jfbastien@apple.com>
 
         WebAssembly: implement Module imports/exports
index e9db0a9..e794287 100644 (file)
@@ -144,3 +144,24 @@ export {
     _throws as throws,
     _instanceof as instanceof,
 };
+
+const asyncTestImpl = (promise, thenFunc, catchFunc) => {
+    asyncTestStart(1);
+    promise.then(thenFunc).catch(catchFunc);
+};
+
+const printExn = (e) => {
+    print("Failed: ", e);
+    print(e.stack);
+};
+
+export const asyncTest = (promise) => asyncTestImpl(promise, asyncTestPassed, printExn);
+export const asyncTestEq = (promise, expected) => {
+    const thenCheck = (value) => {
+        if (value === expected)
+            return asyncTestPassed();
+        print("Failed: got ", value, " but expected ", expected);
+
+    }
+    asyncTestImpl(promise, thenCheck, printExn);
+};
index f343c6e..6324302 100644 (file)
@@ -1,7 +1,6 @@
 import * as assert from '../assert.js';
 import Builder from '../Builder.js';
 
-let done = false;
 async function testPromiseAPI() {
     {
         // Can't declare more than one memory.
@@ -14,39 +13,30 @@ async function testPromiseAPI() {
             .Code()
             .End();
 
-        let threw = false;
         try {
             await WebAssembly.compile(builder.WebAssembly().get());
         } catch(e) {
-            threw = true;
             assert.truthy(e instanceof WebAssembly.CompileError);
-            assert.truthy(e.message === "WebAssembly.Module doesn't parse at byte 34 / 43: Memory section cannot exist if an Import has a memory (evaluating 'WebAssembly.compile(builder.WebAssembly().get())')");
+            assert.truthy(e.message === "WebAssembly.Module doesn't parse at byte 34 / 43: Memory section cannot exist if an Import has a memory");
         }
-        assert.truthy(threw);
     }
 
     {
-        let threw = false;
         try {
             await WebAssembly.compile();
         } catch(e) {
-            threw = true;
             assert.truthy(e instanceof TypeError);
             assert.eq(e.message, "first argument must be an ArrayBufferView or an ArrayBuffer (evaluating 'WebAssembly.compile()')");
         }
-        assert.truthy(threw);
     }
 
     {
-        let threw = false;
         try {
             await WebAssembly.compile(20);
         } catch(e) {
-            threw = true;
             assert.truthy(e instanceof TypeError);
             assert.eq(e.message, "first argument must be an ArrayBufferView or an ArrayBuffer (evaluating 'WebAssembly.compile(20)')");
         }
-        assert.truthy(threw);
     }
 
     {
@@ -61,10 +51,6 @@ async function testPromiseAPI() {
         let module = await WebAssembly.compile(builder.WebAssembly().get());
         assert.truthy(module instanceof WebAssembly.Module);
     }
-
-    done = true;
 }
 
-testPromiseAPI();
-drainMicrotasks();
-assert.truthy(done);
+assert.asyncTest(testPromiseAPI());
diff --git a/JSTests/wasm/js-api/web-assembly-compile-parallel.js b/JSTests/wasm/js-api/web-assembly-compile-parallel.js
new file mode 100644 (file)
index 0000000..12feaec
--- /dev/null
@@ -0,0 +1,58 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+async function throwExn() {
+    throw new Error();
+}
+
+async function test() {
+    const loopDepth = 10;
+    const numCompilations = 1;
+    const numVars = 30;
+    const params = [];
+    params.length = numVars;
+    params.fill("i32");
+
+    let builder = (new Builder())
+          .Type().End()
+          .Function().End()
+          .Export()
+              .Function("foo")
+          .End()
+          .Code()
+          .Function("foo", { params, ret: "i32" });
+
+    const makeLoop = (builder, depth) => {
+        if (depth === 0)
+            return builder;
+
+        builder = builder
+            .Loop("i32", (b) => {
+                  b.GetLocal(0)
+                    .I32Const(1)
+                    .I32Sub()
+                    .TeeLocal(0)
+                    .GetLocal(0)
+                    .I32Eqz()
+                    .BrIf(1);
+
+                return makeLoop(b, depth - 1).Br(0);
+            });
+        return builder
+
+    }
+
+    builder = makeLoop(builder, loopDepth);
+    builder = builder.End().End();
+
+    const bin = builder.WebAssembly().get();
+
+    let compilations = [];
+    for (let i = 0; i < numCompilations; ++i) {
+        compilations.push(WebAssembly.compile(bin));
+    }
+
+    await Promise.all(compilations);
+}
+
+assert.asyncTest(test());
diff --git a/JSTests/wasm/js-api/web-assembly-instantiate-parallel.js b/JSTests/wasm/js-api/web-assembly-instantiate-parallel.js
new file mode 100644 (file)
index 0000000..b447bca
--- /dev/null
@@ -0,0 +1,75 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+async function test() {
+    const loopDepth = 100;
+    const numCompilations = 1;
+    const numVars = 30;
+    const params = [];
+    params.length = numVars;
+    params.fill("i32");
+
+    let builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Memory("imp", "memory", { initial: 0 })
+        .End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+        .Function("foo", { params, ret: "i32" });
+
+    const makeLoop = (builder, depth) => {
+        if (depth === 0)
+            return builder;
+
+        builder = builder
+            .Loop("i32", (b) => {
+                b.GetLocal(0)
+                    .I32Const(1)
+                    .I32Sub()
+                    .TeeLocal(0)
+                    .GetLocal(0)
+                    .I32Eqz()
+                    .BrIf(1);
+
+                return makeLoop(b, depth - 1).Br(0);
+            });
+        return builder;
+    }
+
+    builder = makeLoop(builder, loopDepth);
+    builder = builder.End().End();
+
+    const bin = builder.WebAssembly().get();
+    const memory = new WebAssembly.Memory({ initial: 0 });
+    const importObject = { "imp": { "memory": memory } };
+
+    // Compile a bunch of instances in parallel.
+    let compilations = [];
+    for (let i = 0; i < numCompilations; ++i) {
+        compilations.push(WebAssembly.instantiate(bin, importObject));
+    }
+
+    let [inst] = await Promise.all(compilations);
+    let module = inst.module;
+
+    // Compile a bunch of instances from modules in parallel.
+    compilations = [];
+    for (let i = 0; i < numCompilations; ++i) {
+        compilations.push(WebAssembly.instantiate(module, importObject));
+    }
+
+    await Promise.all(compilations);
+
+    // Compile an instance from a module in parallel, have sync compilation steal it.
+    compilations = [];
+    compilations.push(WebAssembly.instantiate(module, importObject));
+    inst = new WebAssembly.Instance(module, importObject);
+
+    await Promise.all(compilations);
+}
+
+assert.asyncTest(test());
index a93bc60..fb4887a 100644 (file)
@@ -19,18 +19,14 @@ assert.eq(WebAssembly.instantiate.length, 1);
 
     const bin = builder.WebAssembly().get();
 
-    let done = false;
     async function test() {
         let {module, instance} = await WebAssembly.instantiate(bin);
         assert.truthy(module instanceof WebAssembly.Module);
         assert.truthy(instance instanceof WebAssembly.Instance);
         assert.eq(instance.exports.foo(20), 1);
-        done = true;
     }
 
-    test();
-    drainMicrotasks();
-    assert.truthy(done);
+    assert.asyncTest(test());
 }
 
 {
@@ -48,19 +44,15 @@ assert.eq(WebAssembly.instantiate.length, 1);
 
     const bin = builder.WebAssembly().get();
 
-    let done = false;
     async function test() {
         try {
             let {module, instance} = await WebAssembly.instantiate(bin, null);
         } catch(e) {
             assert.eq(e.message, "second argument to WebAssembly.instantiate must be undefined or an Object (evaluating 'WebAssembly.instantiate(bin, null)')");
         }
-        done = true;
     }
 
-    test();
-    drainMicrotasks();
-    assert.truthy(done);
+    assert.asyncTest(test());
 }
 
 {
@@ -78,20 +70,16 @@ assert.eq(WebAssembly.instantiate.length, 1);
 
     const bin = builder.WebAssembly().get();
 
-    let done = false;
     async function test() {
         try {
             let {module, instance} = await WebAssembly.instantiate(bin);
         } catch(e) {
             assert.truthy(e instanceof WebAssembly.CompileError);
-            assert.eq(e.message, "WebAssembly.Module doesn't validate: control flow returns with unexpected type, in function at index 0 (evaluating 'WebAssembly.instantiate(bin)')");
+            assert.eq(e.message, "WebAssembly.Module doesn't validate: control flow returns with unexpected type, in function at index 0");
         }
-        done = true;
     }
 
-    test();
-    drainMicrotasks();
-    assert.truthy(done);
+    assert.asyncTest(test());
 }
 
 {
@@ -110,19 +98,15 @@ assert.eq(WebAssembly.instantiate.length, 1);
 
     const bin = builder.WebAssembly().get();
 
-    let done = false;
     async function test() {
         try {
             let {module, instance} = await WebAssembly.instantiate(bin, {imp: {memory: 20}});
         } catch(e) {
-            assert.eq(e.message, "Memory import is not an instance of WebAssembly.Memory (evaluating 'WebAssembly.instantiate(bin, {imp: {memory: 20}})')");
+            assert.eq(e.message, "Memory import is not an instance of WebAssembly.Memory");
         }
-        done = true;
     }
 
-    test();
-    drainMicrotasks();
-    assert.truthy(done);
+    assert.asyncTest(test());
 }
 
 {
@@ -141,20 +125,16 @@ assert.eq(WebAssembly.instantiate.length, 1);
 
     const bin = builder.WebAssembly().get();
 
-    let done = false;
     async function test() {
         try {
             const module = new WebAssembly.Module(bin);
             let instance = await WebAssembly.instantiate(bin, {imp: {memory: 20}});
         } catch(e) {
-            assert.eq(e.message, "Memory import is not an instance of WebAssembly.Memory (evaluating 'WebAssembly.instantiate(bin, {imp: {memory: 20}})')");
+            assert.eq(e.message, "Memory import is not an instance of WebAssembly.Memory");
         }
-        done = true;
     }
 
-    test();
-    drainMicrotasks();
-    assert.truthy(done);
+    assert.asyncTest(test());
 }
 
 {
@@ -172,18 +152,14 @@ assert.eq(WebAssembly.instantiate.length, 1);
 
     const bin = builder.WebAssembly().get();
 
-    let done = false;
     async function test() {
         let module = new WebAssembly.Module(bin);
         let instance = await WebAssembly.instantiate(module);
         assert.truthy(instance instanceof WebAssembly.Instance);
         assert.eq(instance.exports.foo(20), 1);
-        done = true;
     }
 
-    test();
-    drainMicrotasks();
-    assert.truthy(done);
+    assert.asyncTest(test());
 }
 
 {
@@ -202,7 +178,6 @@ assert.eq(WebAssembly.instantiate.length, 1);
 
     const bin = builder.WebAssembly().get();
 
-    let done = false;
     async function test() {
         try {
             await WebAssembly.instantiate(25);
@@ -210,10 +185,7 @@ assert.eq(WebAssembly.instantiate.length, 1);
             // FIXME: Better error message here.
             assert.eq(e.message, "first argument must be an ArrayBufferView or an ArrayBuffer (evaluating 'WebAssembly.instantiate(25)')");
         }
-        done = true;
     }
 
-    test();
-    drainMicrotasks();
-    assert.truthy(done);
+    assert.asyncTest(test());
 }
index 5af714e..7a3ca01 100644 (file)
@@ -503,7 +503,6 @@ set(JavaScriptCore_SOURCES
     heap/HeapProfiler.cpp
     heap/HeapSnapshot.cpp
     heap/HeapSnapshotBuilder.cpp
-    heap/HeapTimer.cpp
     heap/IncrementalSweeper.cpp
     heap/JITStubRoutineSet.cpp
     heap/LargeAllocation.cpp
@@ -802,6 +801,7 @@ set(JavaScriptCore_SOURCES
     runtime/JSPropertyNameEnumerator.cpp
     runtime/JSPropertyNameIterator.cpp
     runtime/JSProxy.cpp
+    runtime/JSRunLoopTimer.cpp
     runtime/JSScope.cpp
     runtime/JSScriptFetcher.cpp
     runtime/JSSegmentedVariableObject.cpp
@@ -851,6 +851,7 @@ set(JavaScriptCore_SOURCES
     runtime/Operations.cpp
     runtime/Options.cpp
     runtime/ProgramExecutable.cpp
+    runtime/PromiseDeferredTimer.cpp
     runtime/PropertyDescriptor.cpp
     runtime/PropertySlot.cpp
     runtime/PropertyTable.cpp
@@ -944,6 +945,7 @@ set(JavaScriptCore_SOURCES
     wasm/WasmPlan.cpp
     wasm/WasmSignature.cpp
     wasm/WasmValidate.cpp
+    wasm/WasmWorklist.cpp
 
     wasm/js/JSWebAssemblyCallee.cpp
     wasm/js/JSWebAssemblyCodeBlock.cpp
index 46f0932..e33a6cc 100644 (file)
@@ -1,3 +1,230 @@
+2017-03-28  Keith Miller  <keith_miller@apple.com>
+
+        WebAssembly: Make WebAssembly.instantiate/compile truly asynchronous
+        https://bugs.webkit.org/show_bug.cgi?id=169187
+
+        Reviewed by Saam Barati.
+
+        This patch allows WebAssembly compilations to happen asynchronously.
+        To do so, it refactors how much of the compilation happens and adds
+        new infrastructure for async promises.
+
+        First, there is a new class, PromiseDeferredTimer that lives on
+        the VM.  PromiseDeferredTimer will manage the life-cycle of async
+        pending promises and any dependencies that promise
+        needs. PromiseDeferredTimer automagically releases the pending
+        promise and dependencies once the JSPromiseDeferred is resolved or
+        rejected. Additionally, PromiseDeferredTimer provides a mechanism
+        to poll the run-loop whenever the async task needs to synchronize
+        with the JS thread. Normally, that will be whenever the async task
+        finishes. In the case of Web Assembly we also use this feature for
+        the compile + instantiate case, where we might have more work
+        after the first async task completes (more on that later).
+
+        The next class is Wasm::Worklist, which is used to manage Wasm
+        compilation tasks. The worklist class works similarly to the
+        DFG/FTL Worklists. It has a pool of threads that it manages. One
+        interesting aspect of Wasm Worklist is that it can synchronously
+        compile a plan that is already potentially running
+        asynchronously. This can occur if a user calls
+        WebAssembly.instantiate() then new WebAssembly.instantiate() on
+        the same module. In that case the Wasm Worklist will bump the
+        priority of the running pending Plan and block the JS thread.
+
+        This patch also makes some of the Wasm Plan code cleaner. Since we
+        now defer all compilation to instantiation time, we no longer need
+        to guess at which memory we are going to get. Also, Wasm Plans now
+        track the work they have done with a state enum.
+
+        Finally, this patch makes renamed HeapTimer to JSRunLoopTimer. It
+        also adds changes test262AsyncTest to a more generic testing
+        infrastructure. Now, in addition to the old functionality, you can
+        call asyncTest() with the number of tests you expect. When the jsc
+        CLI exits, it will guarantee that asyncTestPassed() is called that
+        many times.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * heap/GCActivityCallback.h:
+        * heap/IncrementalSweeper.cpp:
+        (JSC::IncrementalSweeper::scheduleTimer):
+        (JSC::IncrementalSweeper::IncrementalSweeper):
+        * heap/IncrementalSweeper.h:
+        * heap/StopIfNecessaryTimer.cpp:
+        (JSC::StopIfNecessaryTimer::StopIfNecessaryTimer):
+        * heap/StopIfNecessaryTimer.h:
+        * heap/StrongInlines.h:
+        * jsc.cpp:
+        (GlobalObject::finishCreation):
+        (printInternal):
+        (functionAsyncTestStart):
+        (functionAsyncTestPassed):
+        (functionTestWasmModuleFunctions):
+        (CommandLine::parseArguments):
+        (runJSC):
+        * runtime/JSPromiseDeferred.cpp:
+        (JSC::JSPromiseDeferred::resolve):
+        (JSC::JSPromiseDeferred::reject):
+        * runtime/JSPromiseDeferred.h:
+        (JSC::JSPromiseDeferred::promiseAsyncPending):
+        * runtime/JSRunLoopTimer.cpp: Renamed from Source/JavaScriptCore/heap/HeapTimer.cpp.
+        (JSC::JSRunLoopTimer::JSRunLoopTimer):
+        (JSC::JSRunLoopTimer::setRunLoop):
+        (JSC::JSRunLoopTimer::~JSRunLoopTimer):
+        (JSC::JSRunLoopTimer::timerDidFire):
+        (JSC::JSRunLoopTimer::scheduleTimer):
+        (JSC::JSRunLoopTimer::cancelTimer):
+        (JSC::JSRunLoopTimer::invalidate):
+        * runtime/JSRunLoopTimer.h: Copied from Source/JavaScriptCore/heap/HeapTimer.h.
+        * runtime/Options.h:
+        * runtime/PromiseDeferredTimer.cpp: Added.
+        (JSC::PromiseDeferredTimer::PromiseDeferredTimer):
+        (JSC::PromiseDeferredTimer::doWork):
+        (JSC::PromiseDeferredTimer::runRunLoop):
+        (JSC::PromiseDeferredTimer::addPendingPromise):
+        (JSC::PromiseDeferredTimer::cancelPendingPromise):
+        (JSC::PromiseDeferredTimer::scheduleWorkSoon):
+        (JSC::PromiseDeferredTimer::scheduleBlockedTask):
+        * runtime/PromiseDeferredTimer.h: Renamed from Source/JavaScriptCore/heap/HeapTimer.h.
+        (JSC::PromiseDeferredTimer::stopRunningTasks):
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+        (JSC::VM::~VM):
+        * runtime/VM.h:
+        * wasm/JSWebAssembly.cpp:
+        (JSC::reject):
+        (JSC::webAssemblyCompileFunc):
+        (JSC::resolve):
+        (JSC::instantiate):
+        (JSC::compileAndInstantiate):
+        (JSC::webAssemblyInstantiateFunc):
+        (JSC::webAssemblyValidateFunc):
+        * wasm/WasmB3IRGenerator.cpp:
+        (JSC::Wasm::B3IRGenerator::B3IRGenerator):
+        (JSC::Wasm::B3IRGenerator::emitCheckAndPreparePointer):
+        (JSC::Wasm::B3IRGenerator::memoryKind):
+        (JSC::Wasm::parseAndCompile):
+        * wasm/WasmB3IRGenerator.h:
+        * wasm/WasmFormat.h:
+        (JSC::Wasm::ModuleInformation::internalFunctionCount):
+        * wasm/WasmFunctionParser.h:
+        * wasm/WasmMemory.h:
+        * wasm/WasmMemoryInformation.cpp:
+        (JSC::Wasm::MemoryInformation::MemoryInformation):
+        * wasm/WasmMemoryInformation.h:
+        (JSC::Wasm::MemoryInformation::maximum):
+        (JSC::Wasm::MemoryInformation::hasReservedMemory): Deleted.
+        (JSC::Wasm::MemoryInformation::takeReservedMemory): Deleted.
+        (JSC::Wasm::MemoryInformation::mode): Deleted.
+        * wasm/WasmModuleParser.cpp:
+        * wasm/WasmModuleParser.h:
+        (JSC::Wasm::ModuleParser::ModuleParser):
+        * wasm/WasmPlan.cpp:
+        (JSC::Wasm::Plan::Plan):
+        (JSC::Wasm::Plan::stateString):
+        (JSC::Wasm::Plan::moveToState):
+        (JSC::Wasm::Plan::fail):
+        (JSC::Wasm::Plan::parseAndValidateModule):
+        (JSC::Wasm::Plan::prepare):
+        (JSC::Wasm::Plan::ThreadCountHolder::ThreadCountHolder):
+        (JSC::Wasm::Plan::ThreadCountHolder::~ThreadCountHolder):
+        (JSC::Wasm::Plan::compileFunctions):
+        (JSC::Wasm::Plan::complete):
+        (JSC::Wasm::Plan::waitForCompletion):
+        (JSC::Wasm::Plan::cancel):
+        (JSC::Wasm::Plan::run): Deleted.
+        (JSC::Wasm::Plan::initializeCallees): Deleted.
+        * wasm/WasmPlan.h:
+        (JSC::Wasm::Plan::dontFinalize):
+        (JSC::Wasm::Plan::exports):
+        (JSC::Wasm::Plan::internalFunctionCount):
+        (JSC::Wasm::Plan::takeModuleInformation):
+        (JSC::Wasm::Plan::takeCallLinkInfos):
+        (JSC::Wasm::Plan::takeWasmExitStubs):
+        (JSC::Wasm::Plan::setModeAndPromise):
+        (JSC::Wasm::Plan::mode):
+        (JSC::Wasm::Plan::pendingPromise):
+        (JSC::Wasm::Plan::vm):
+        (JSC::Wasm::Plan::errorMessage):
+        (JSC::Wasm::Plan::failed):
+        (JSC::Wasm::Plan::hasWork):
+        (JSC::Wasm::Plan::hasBeenPrepared):
+        * wasm/WasmPlanInlines.h: Copied from Source/JavaScriptCore/wasm/WasmB3IRGenerator.h.
+        (JSC::Wasm::Plan::initializeCallees):
+        * wasm/WasmValidate.cpp:
+        * wasm/WasmWorklist.cpp: Added.
+        (JSC::Wasm::Worklist::priorityString):
+        (JSC::Wasm::Worklist::QueueElement::setToNextPriority):
+        (JSC::Wasm::Worklist::iterate):
+        (JSC::Wasm::Worklist::enqueue):
+        (JSC::Wasm::Worklist::completePlanSynchronously):
+        (JSC::Wasm::Worklist::stopAllPlansForVM):
+        (JSC::Wasm::Worklist::Worklist):
+        (JSC::Wasm::Worklist::~Worklist):
+        (JSC::Wasm::existingWorklistOrNull):
+        (JSC::Wasm::ensureWorklist):
+        * wasm/WasmWorklist.h: Added.
+        (JSC::Wasm::Worklist::nextTicket):
+        (JSC::Wasm::Worklist::Comparator::operator()):
+        * wasm/js/JSWebAssemblyCallee.h:
+        * wasm/js/JSWebAssemblyCodeBlock.cpp:
+        (JSC::JSWebAssemblyCodeBlock::JSWebAssemblyCodeBlock):
+        (JSC::JSWebAssemblyCodeBlock::initialize):
+        (JSC::JSWebAssemblyCodeBlock::isSafeToRun):
+        * wasm/js/JSWebAssemblyCodeBlock.h:
+        (JSC::JSWebAssemblyCodeBlock::create):
+        (JSC::JSWebAssemblyCodeBlock::initialized):
+        (JSC::JSWebAssemblyCodeBlock::plan):
+        (JSC::JSWebAssemblyCodeBlock::runnable):
+        (JSC::JSWebAssemblyCodeBlock::errorMessage):
+        (JSC::JSWebAssemblyCodeBlock::callees):
+        * wasm/js/JSWebAssemblyHelpers.h:
+        (JSC::createSourceBufferFromValue):
+        * wasm/js/JSWebAssemblyInstance.cpp:
+        (JSC::JSWebAssemblyInstance::finishCreation):
+        (JSC::JSWebAssemblyInstance::visitChildren):
+        (JSC::JSWebAssemblyInstance::addUnitializedCodeBlock):
+        (JSC::JSWebAssemblyInstance::finalizeCreation):
+        (JSC::JSWebAssemblyInstance::create):
+        (JSC::JSWebAssemblyInstance::setMemory): Deleted.
+        * wasm/js/JSWebAssemblyInstance.h:
+        (JSC::JSWebAssemblyInstance::codeBlock):
+        (JSC::JSWebAssemblyInstance::initialized):
+        (JSC::JSWebAssemblyInstance::module):
+        (JSC::JSWebAssemblyInstance::importFunction):
+        (JSC::JSWebAssemblyInstance::setMemory):
+        (JSC::JSWebAssemblyInstance::table):
+        (JSC::JSWebAssemblyInstance::importFunctions):
+        (JSC::JSWebAssemblyInstance::setImportFunction): Deleted.
+        (JSC::JSWebAssemblyInstance::setTable): Deleted.
+        * wasm/js/JSWebAssemblyModule.cpp:
+        (JSC::JSWebAssemblyModule::createStub):
+        (JSC::JSWebAssemblyModule::JSWebAssemblyModule):
+        (JSC::JSWebAssemblyModule::finishCreation):
+        (JSC::JSWebAssemblyModule::setCodeBlock):
+        (JSC::JSWebAssemblyModule::buildCodeBlock): Deleted.
+        (JSC::JSWebAssemblyModule::create): Deleted.
+        (JSC::JSWebAssemblyModule::codeBlock): Deleted.
+        * wasm/js/JSWebAssemblyModule.h:
+        (JSC::JSWebAssemblyModule::moduleInformation):
+        (JSC::JSWebAssemblyModule::codeBlock):
+        (JSC::JSWebAssemblyModule::source):
+        (JSC::JSWebAssemblyModule::takeReservedMemory): Deleted.
+        (JSC::JSWebAssemblyModule::codeBlockFor): Deleted.
+        * wasm/js/WebAssemblyInstanceConstructor.cpp:
+        (JSC::constructJSWebAssemblyInstance):
+        (JSC::WebAssemblyInstanceConstructor::createInstance): Deleted.
+        * wasm/js/WebAssemblyModuleConstructor.cpp:
+        (JSC::WebAssemblyModuleConstructor::createModule):
+        * wasm/js/WebAssemblyModulePrototype.cpp:
+        (JSC::webAssemblyModuleProtoImports):
+        (JSC::webAssemblyModuleProtoExports):
+        * wasm/js/WebAssemblyModuleRecord.cpp:
+        (JSC::WebAssemblyModuleRecord::finishCreation):
+        (JSC::WebAssemblyModuleRecord::link):
+        (JSC::WebAssemblyModuleRecord::evaluate):
+        * wasm/js/WebAssemblyModuleRecord.h:
+
 2017-03-28  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         WebAssembly: add fallback to use pinned register to load/store state
index 6eca5ff..f0070fa 100644 (file)
                52C952B919A28A1C0069B386 /* TypeProfiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52C952B819A28A1C0069B386 /* TypeProfiler.cpp */; };
                52F6C35D1E71EB080081F4CC /* WebAssemblyWrapperFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52F6C35B1E71EB080081F4CC /* WebAssemblyWrapperFunction.cpp */; };
                52F6C35E1E71EB080081F4CC /* WebAssemblyWrapperFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 52F6C35C1E71EB080081F4CC /* WebAssemblyWrapperFunction.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               530FB3021E7A0B6E003C19DD /* WasmWorklist.h in Headers */ = {isa = PBXBuildFile; fileRef = 530FB3011E7A0B6E003C19DD /* WasmWorklist.h */; };
+               530FB3041E7A1146003C19DD /* WasmWorklist.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 530FB3031E7A1146003C19DD /* WasmWorklist.cpp */; };
                531374BD1D5CE67600AF7A0B /* WasmPlan.h in Headers */ = {isa = PBXBuildFile; fileRef = 531374BC1D5CE67600AF7A0B /* WasmPlan.h */; };
                531374BF1D5CE95000AF7A0B /* WasmPlan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 531374BE1D5CE95000AF7A0B /* WasmPlan.cpp */; };
                533B15DF1DC7F463004D500A /* WasmOps.h in Headers */ = {isa = PBXBuildFile; fileRef = 533B15DE1DC7F463004D500A /* WasmOps.h */; settings = {ATTRIBUTES = (Private, ); }; };
                5341FC701DAC33E500E7E4D7 /* B3WasmBoundsCheckValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5341FC6F1DAC33E500E7E4D7 /* B3WasmBoundsCheckValue.cpp */; };
                5341FC721DAC343C00E7E4D7 /* B3WasmBoundsCheckValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 5341FC711DAC343C00E7E4D7 /* B3WasmBoundsCheckValue.h */; };
+               534638711E70CF3D00F12AC1 /* JSRunLoopTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 534638701E70CF3D00F12AC1 /* JSRunLoopTimer.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               534638731E70D01500F12AC1 /* JSRunLoopTimer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 534638721E70D01500F12AC1 /* JSRunLoopTimer.cpp */; };
+               534638751E70DDEC00F12AC1 /* PromiseDeferredTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 534638741E70DDEC00F12AC1 /* PromiseDeferredTimer.h */; };
+               534638771E71E06E00F12AC1 /* PromiseDeferredTimer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 534638761E71E06E00F12AC1 /* PromiseDeferredTimer.cpp */; };
                53486BB71C1795C300F6F3AF /* JSTypedArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 53486BB61C1795C300F6F3AF /* JSTypedArray.h */; settings = {ATTRIBUTES = (Public, ); }; };
                53486BBB1C18E84500F6F3AF /* JSTypedArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53486BBA1C18E84500F6F3AF /* JSTypedArray.cpp */; };
                534902851C7276B70012BCB8 /* TypedArrayCTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 534902821C7242C80012BCB8 /* TypedArrayCTest.cpp */; };
                53F40E951D5A7AEF0099A1B6 /* WasmModuleParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F40E941D5A7AEF0099A1B6 /* WasmModuleParser.h */; };
                53F40E971D5A7BEC0099A1B6 /* WasmModuleParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53F40E961D5A7BEC0099A1B6 /* WasmModuleParser.cpp */; };
                53F6BF6D1C3F060A00F41E5D /* InternalFunctionAllocationProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F6BF6C1C3F060A00F41E5D /* InternalFunctionAllocationProfile.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               53F8D2001E8387D400D21116 /* WasmPlanInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F8D1FF1E8387D400D21116 /* WasmPlanInlines.h */; };
                53FA2AE11CF37F3F0022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 53FA2AE01CF37F3F0022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.h */; settings = {ATTRIBUTES = (Private, ); }; };
                53FA2AE31CF380390022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53FA2AE21CF380390022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp */; };
                53FD04D31D7AB277003287D3 /* WasmCallingConvention.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53FD04D11D7AB187003287D3 /* WasmCallingConvention.cpp */; };
                C2CF39C116E15A8100DD69BE /* JSAPIWrapperObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = C2CF39BF16E15A8100DD69BE /* JSAPIWrapperObject.mm */; };
                C2CF39C216E15A8100DD69BE /* JSAPIWrapperObject.h in Headers */ = {isa = PBXBuildFile; fileRef = C2CF39C016E15A8100DD69BE /* JSAPIWrapperObject.h */; };
                C2DA778318E259990066FCB6 /* HeapInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = C2DA778218E259990066FCB6 /* HeapInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               C2E526BD1590EF000054E48D /* HeapTimer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2E526BB1590EF000054E48D /* HeapTimer.cpp */; };
-               C2E526BE1590EF000054E48D /* HeapTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = C2E526BC1590EF000054E48D /* HeapTimer.h */; settings = {ATTRIBUTES = (Private, ); }; };
                C2F0F2D116BAEEE900187C19 /* StructureRareData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2F0F2D016BAEEE900187C19 /* StructureRareData.cpp */; };
                C2FCAE1017A9C24E0034C735 /* BytecodeBasicBlock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2FCAE0C17A9C24E0034C735 /* BytecodeBasicBlock.cpp */; };
                C2FCAE1117A9C24E0034C735 /* BytecodeBasicBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = C2FCAE0D17A9C24E0034C735 /* BytecodeBasicBlock.h */; settings = {ATTRIBUTES = (Private, ); }; };
                52C952B819A28A1C0069B386 /* TypeProfiler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TypeProfiler.cpp; sourceTree = "<group>"; };
                52F6C35B1E71EB080081F4CC /* WebAssemblyWrapperFunction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WebAssemblyWrapperFunction.cpp; path = js/WebAssemblyWrapperFunction.cpp; sourceTree = "<group>"; };
                52F6C35C1E71EB080081F4CC /* WebAssemblyWrapperFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebAssemblyWrapperFunction.h; path = js/WebAssemblyWrapperFunction.h; sourceTree = "<group>"; };
+               530FB3011E7A0B6E003C19DD /* WasmWorklist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmWorklist.h; sourceTree = "<group>"; };
+               530FB3031E7A1146003C19DD /* WasmWorklist.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmWorklist.cpp; sourceTree = "<group>"; };
                531374BC1D5CE67600AF7A0B /* WasmPlan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmPlan.h; sourceTree = "<group>"; };
                531374BE1D5CE95000AF7A0B /* WasmPlan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmPlan.cpp; sourceTree = "<group>"; };
                533B15DE1DC7F463004D500A /* WasmOps.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmOps.h; sourceTree = "<group>"; };
                5341FC6F1DAC33E500E7E4D7 /* B3WasmBoundsCheckValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = B3WasmBoundsCheckValue.cpp; path = b3/B3WasmBoundsCheckValue.cpp; sourceTree = "<group>"; };
                5341FC711DAC343C00E7E4D7 /* B3WasmBoundsCheckValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3WasmBoundsCheckValue.h; path = b3/B3WasmBoundsCheckValue.h; sourceTree = "<group>"; };
+               534638701E70CF3D00F12AC1 /* JSRunLoopTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSRunLoopTimer.h; sourceTree = "<group>"; };
+               534638721E70D01500F12AC1 /* JSRunLoopTimer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSRunLoopTimer.cpp; sourceTree = "<group>"; };
+               534638741E70DDEC00F12AC1 /* PromiseDeferredTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PromiseDeferredTimer.h; sourceTree = "<group>"; };
+               534638761E71E06E00F12AC1 /* PromiseDeferredTimer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PromiseDeferredTimer.cpp; sourceTree = "<group>"; };
                53486BB61C1795C300F6F3AF /* JSTypedArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSTypedArray.h; sourceTree = "<group>"; };
                53486BBA1C18E84500F6F3AF /* JSTypedArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSTypedArray.cpp; sourceTree = "<group>"; };
                534902821C7242C80012BCB8 /* TypedArrayCTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TypedArrayCTest.cpp; path = API/tests/TypedArrayCTest.cpp; sourceTree = "<group>"; };
                53F40E941D5A7AEF0099A1B6 /* WasmModuleParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmModuleParser.h; sourceTree = "<group>"; };
                53F40E961D5A7BEC0099A1B6 /* WasmModuleParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmModuleParser.cpp; sourceTree = "<group>"; };
                53F6BF6C1C3F060A00F41E5D /* InternalFunctionAllocationProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InternalFunctionAllocationProfile.h; sourceTree = "<group>"; };
+               53F8D1FF1E8387D400D21116 /* WasmPlanInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmPlanInlines.h; sourceTree = "<group>"; };
                53FA2AE01CF37F3F0022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLIntPrototypeLoadAdaptiveStructureWatchpoint.h; sourceTree = "<group>"; };
                53FA2AE21CF380390022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp; sourceTree = "<group>"; };
                53FD04D11D7AB187003287D3 /* WasmCallingConvention.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmCallingConvention.cpp; sourceTree = "<group>"; };
                C2CF39BF16E15A8100DD69BE /* JSAPIWrapperObject.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = JSAPIWrapperObject.mm; sourceTree = "<group>"; };
                C2CF39C016E15A8100DD69BE /* JSAPIWrapperObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSAPIWrapperObject.h; sourceTree = "<group>"; };
                C2DA778218E259990066FCB6 /* HeapInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeapInlines.h; sourceTree = "<group>"; };
-               C2E526BB1590EF000054E48D /* HeapTimer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HeapTimer.cpp; sourceTree = "<group>"; };
-               C2E526BC1590EF000054E48D /* HeapTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeapTimer.h; sourceTree = "<group>"; };
                C2F0F2D016BAEEE900187C19 /* StructureRareData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StructureRareData.cpp; sourceTree = "<group>"; };
                C2FCAE0C17A9C24E0034C735 /* BytecodeBasicBlock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BytecodeBasicBlock.cpp; sourceTree = "<group>"; };
                C2FCAE0D17A9C24E0034C735 /* BytecodeBasicBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BytecodeBasicBlock.h; sourceTree = "<group>"; };
                                A54C2AAF1C6544D100A18D78 /* HeapSnapshot.h */,
                                A5311C341C77CEAC00E6B1B6 /* HeapSnapshotBuilder.cpp */,
                                A5311C351C77CEAC00E6B1B6 /* HeapSnapshotBuilder.h */,
-                               C2E526BB1590EF000054E48D /* HeapTimer.cpp */,
-                               C2E526BC1590EF000054E48D /* HeapTimer.h */,
                                0FADE6721D4D23BC00768457 /* HeapUtil.h */,
                                C25F8BCB157544A900245B71 /* IncrementalSweeper.cpp */,
                                C25F8BCC157544A900245B71 /* IncrementalSweeper.h */,
                                53F40E8C1D5901F20099A1B6 /* WasmParser.h */,
                                531374BE1D5CE95000AF7A0B /* WasmPlan.cpp */,
                                531374BC1D5CE67600AF7A0B /* WasmPlan.h */,
+                               53F8D1FF1E8387D400D21116 /* WasmPlanInlines.h */,
                                53F40E841D58F9770099A1B6 /* WasmSections.h */,
                                AD7438BE1E04579200FD0C2A /* WasmSignature.cpp */,
                                AD7438BF1E04579200FD0C2A /* WasmSignature.h */,
                                53FF7F9A1DBFD2B900A26CCC /* WasmValidate.cpp */,
                                53FF7F981DBFCD9000A26CCC /* WasmValidate.h */,
+                               530FB3031E7A1146003C19DD /* WasmWorklist.cpp */,
+                               530FB3011E7A0B6E003C19DD /* WasmWorklist.h */,
                        );
                        path = wasm;
                        sourceTree = "<group>";
                                14A1563010966365006FA260 /* DateInstanceCache.h */,
                                BCD203470E17135E002C7E82 /* DatePrototype.cpp */,
                                BCD203480E17135E002C7E82 /* DatePrototype.h */,
+                               534638761E71E06E00F12AC1 /* PromiseDeferredTimer.cpp */,
+                               534638741E70DDEC00F12AC1 /* PromiseDeferredTimer.h */,
                                169948EDE68D4054B01EF797 /* DefinePropertyAttributes.h */,
                                0FE0500F1AA9091100D33B33 /* DirectArguments.cpp */,
                                0FE050101AA9091100D33B33 /* DirectArguments.h */,
                                0F7C39FC1C8F659500480151 /* RegExpObjectInlines.h */,
                                BCD202BF0E1706A7002C7E82 /* RegExpPrototype.cpp */,
                                BCD202C00E1706A7002C7E82 /* RegExpPrototype.h */,
+                               534638721E70D01500F12AC1 /* JSRunLoopTimer.cpp */,
+                               534638701E70CF3D00F12AC1 /* JSRunLoopTimer.h */,
                                70B0A9D01A9B66200001306A /* RuntimeFlags.h */,
                                527773DD1AAF83AC00BDE7E8 /* RuntimeType.cpp */,
                                52C0611D1AA51E1B00B4ADBA /* RuntimeType.h */,
                                0FEC85061BDACDAC0080FF74 /* B3CheckSpecial.h in Headers */,
                                0FEC85081BDACDAC0080FF74 /* B3CheckValue.h in Headers */,
                                0FEC850A1BDACDAC0080FF74 /* B3Common.h in Headers */,
+                               530FB3021E7A0B6E003C19DD /* WasmWorklist.h in Headers */,
                                0FEC850C1BDACDAC0080FF74 /* B3Commutativity.h in Headers */,
                                0F338E0C1BF0276C0013C88F /* B3Compilation.h in Headers */,
                                7919B7801E03559C005BEED8 /* B3Compile.h in Headers */,
                                BC18C3ED0E16F5CD00B34460 /* CallData.h in Headers */,
                                0F64B27A1A7957B2006E4E66 /* CallEdge.h in Headers */,
                                1429D8DE0ED2205B00B89619 /* CallFrame.h in Headers */,
+                               534638751E70DDEC00F12AC1 /* PromiseDeferredTimer.h in Headers */,
                                62EC9BB71B7EB07C00303AD1 /* CallFrameShuffleData.h in Headers */,
                                0FF4B4BD1E88449A00DBBE86 /* AirRegLiveness.h in Headers */,
                                62D755D71B84FB4A001801FA /* CallFrameShuffler.h in Headers */,
                                E3FF75331D9CEA1800C7E16D /* DOMJITGetterSetter.h in Headers */,
                                E35CA1541DBC3A5C00F83516 /* DOMJITHeapRange.h in Headers */,
                                E3C08E3C1DA41B810039478F /* DOMJITPatchpoint.h in Headers */,
+                               534638711E70CF3D00F12AC1 /* JSRunLoopTimer.h in Headers */,
                                FE6F56DE1E64EAD600D17801 /* VMTraps.h in Headers */,
                                E37AD83C1DA4928600F3D412 /* DOMJITPatchpointParams.h in Headers */,
                                E37AD83D1DA4928600F3D412 /* DOMJITReg.h in Headers */,
                                A5398FAB1C750DA40060A963 /* HeapProfiler.h in Headers */,
                                A54C2AB11C6544F200A18D78 /* HeapSnapshot.h in Headers */,
                                A5311C361C77CEC500E6B1B6 /* HeapSnapshotBuilder.h in Headers */,
-                               C2E526BE1590EF000054E48D /* HeapTimer.h in Headers */,
                                0FD0E5EA1E43D34D0006AB08 /* GCConductor.h in Headers */,
                                0FADE6731D4D23BE00768457 /* HeapUtil.h in Headers */,
                                0F4680D514BBD24B00BFE272 /* HostCallReturnValue.h in Headers */,
                                BC18C45A0E16F5CD00B34460 /* RegExp.h in Headers */,
                                A1712B3F11C7B228007A5315 /* RegExpCache.h in Headers */,
                                BCD202C20E1706A7002C7E82 /* RegExpConstructor.h in Headers */,
+                               53F8D2001E8387D400D21116 /* WasmPlanInlines.h in Headers */,
                                BCD202D60E170708002C7E82 /* RegExpConstructor.lut.h in Headers */,
                                0F7C39FB1C8F629300480151 /* RegExpInlines.h in Headers */,
                                A1712B4111C7B235007A5315 /* RegExpKey.h in Headers */,
                                0FEC850F1BDACDAC0080FF74 /* B3Const64Value.cpp in Sources */,
                                0FF4B4CA1E889D7B00DBBE86 /* B3VariableLiveness.cpp in Sources */,
                                0FEC85111BDACDAC0080FF74 /* B3ConstDoubleValue.cpp in Sources */,
+                               530FB3041E7A1146003C19DD /* WasmWorklist.cpp in Sources */,
                                43422A621C158E6A00E2EB98 /* B3ConstFloatValue.cpp in Sources */,
                                0F338DF51BE93D550013C88F /* B3ConstrainedValue.cpp in Sources */,
                                0F338E0D1BF0276C0013C88F /* B3DataSection.cpp in Sources */,
                                14AD91171DCA97FD0014F9FE /* EvalCodeBlock.cpp in Sources */,
                                147341E21DC2CE9600AA29BA /* EvalExecutable.cpp in Sources */,
                                A54982031891D0B00081E5B8 /* EventLoop.cpp in Sources */,
+                               534638731E70D01500F12AC1 /* JSRunLoopTimer.cpp in Sources */,
                                FE1C0FFF1B194FD100B53FCA /* Exception.cpp in Sources */,
                                FE80C19B1D776A98008510C0 /* ExceptionEventLocation.cpp in Sources */,
                                0F12DE0F1979D5FD0006FF4E /* ExceptionFuzz.cpp in Sources */,
                                A5398FAC1C750DA60060A963 /* HeapProfiler.cpp in Sources */,
                                A54C2AB01C6544EE00A18D78 /* HeapSnapshot.cpp in Sources */,
                                A5311C371C77CECA00E6B1B6 /* HeapSnapshotBuilder.cpp in Sources */,
-                               C2E526BD1590EF000054E48D /* HeapTimer.cpp in Sources */,
                                0F4680D414BBD24900BFE272 /* HostCallReturnValue.cpp in Sources */,
                                DC2143081CA32E58000A8869 /* ICStats.cpp in Sources */,
                                147F39CE107EC37600427A48 /* Identifier.cpp in Sources */,
                                992ABCF91BEA9BD2006403A0 /* RemoteAutomationTarget.cpp in Sources */,
                                992F56B41E4E84A40035953B /* RemoteConnectionToTargetCocoa.mm in Sources */,
                                998ED6741BED70AC00DD8017 /* RemoteControllableTarget.cpp in Sources */,
+                               534638771E71E06E00F12AC1 /* PromiseDeferredTimer.cpp in Sources */,
                                A594558F18245EFD00CC3843 /* RemoteInspectionTarget.cpp in Sources */,
                                995566861E4E8B0F00AAE13C /* RemoteInspector.cpp in Sources */,
                                992F56B51E4E84A80035953B /* RemoteInspectorCocoa.mm in Sources */,
index ddee258..af47588 100644 (file)
@@ -28,7 +28,7 @@
 
 #pragma once
 
-#include "HeapTimer.h"
+#include "JSRunLoopTimer.h"
 #include <wtf/RefPtr.h>
 
 #if USE(CF)
@@ -40,8 +40,9 @@ namespace JSC {
 class FullGCActivityCallback;
 class Heap;
 
-class JS_EXPORT_PRIVATE GCActivityCallback : public HeapTimer {
+class JS_EXPORT_PRIVATE GCActivityCallback : public JSRunLoopTimer {
 public:
+    using Base = JSRunLoopTimer;
     static RefPtr<FullGCActivityCallback> createFullTimer(Heap*);
     static RefPtr<GCActivityCallback> createEdenTimer(Heap*);
 
@@ -70,21 +71,21 @@ protected:
 
 #if USE(CF)
     GCActivityCallback(VM* vm)
-        : HeapTimer(vm)
+        : Base(vm)
         , m_enabled(true)
         , m_delay(s_decade)
     {
     }
 #elif USE(GLIB)
     GCActivityCallback(VM* vm)
-        : HeapTimer(vm)
+        : Base(vm)
         , m_enabled(true)
         , m_delay(s_decade)
     {
     }
 #else
     GCActivityCallback(VM* vm)
-        : HeapTimer(vm)
+        : Base(vm)
         , m_enabled(true)
     {
     }
index cfe89b8..8cbfa9e 100644 (file)
@@ -41,11 +41,11 @@ static const double sweepTimeMultiplier = 1.0 / sweepTimeTotal;
 
 void IncrementalSweeper::scheduleTimer()
 {
-    HeapTimer::scheduleTimer(sweepTimeSlice * sweepTimeMultiplier);
+    Base::scheduleTimer(sweepTimeSlice * sweepTimeMultiplier);
 }
 
 IncrementalSweeper::IncrementalSweeper(Heap* heap)
-    : HeapTimer(heap->vm())
+    : Base(heap->vm())
     , m_currentAllocator(nullptr)
 {
 }
index 34fa88c..76d454f 100644 (file)
@@ -25,7 +25,7 @@
 
 #pragma once
 
-#include "HeapTimer.h"
+#include "JSRunLoopTimer.h"
 #include <wtf/Vector.h>
 
 namespace JSC {
@@ -33,8 +33,9 @@ namespace JSC {
 class Heap;
 class MarkedAllocator;
 
-class IncrementalSweeper : public HeapTimer {
+class IncrementalSweeper : public JSRunLoopTimer {
 public:
+    using Base = JSRunLoopTimer;
     JS_EXPORT_PRIVATE explicit IncrementalSweeper(Heap*);
 
     JS_EXPORT_PRIVATE void startSweeping();
index 6e3176c..940d39d 100644 (file)
@@ -31,7 +31,7 @@
 namespace JSC {
 
 StopIfNecessaryTimer::StopIfNecessaryTimer(VM* vm)
-    : HeapTimer(vm)
+    : Base(vm)
 {
 }
 
index a683184..e1836a1 100644 (file)
 
 #pragma once
 
-#include "HeapTimer.h"
+#include "JSRunLoopTimer.h"
 
 namespace JSC {
 
 class Heap;
 
-class StopIfNecessaryTimer : public HeapTimer {
+class StopIfNecessaryTimer : public JSRunLoopTimer {
 public:
+    using Base = JSRunLoopTimer;
     explicit StopIfNecessaryTimer(VM*);
     
     void doWork() override;
index 6b8197c..205fb04 100644 (file)
@@ -25,6 +25,7 @@
 
 #pragma once
 
+#include "JSCJSValueInlines.h"
 #include "VM.h"
 
 namespace JSC {
index ad4a314..dd6cafd 100644 (file)
@@ -62,6 +62,7 @@
 #include "ObjectConstructor.h"
 #include "ParserError.h"
 #include "ProfilerDatabase.h"
+#include "PromiseDeferredTimer.h"
 #include "ProtoCallFrame.h"
 #include "ReleaseHeapAccessScope.h"
 #include "SamplingProfiler.h"
@@ -73,8 +74,9 @@
 #include "TestRunnerUtils.h"
 #include "TypeProfilerLog.h"
 #include "WasmFaultSignalHandler.h"
-#include "WasmPlan.h"
 #include "WasmMemory.h"
+#include "WasmPlanInlines.h"
+#include "WasmWorklist.h"
 #include <locale.h>
 #include <math.h>
 #include <stdio.h>
@@ -907,8 +909,8 @@ const ClassInfo DOMJITGetterComplex::s_info = { "DOMJITGetterComplex", &Base::s_
 const ClassInfo DOMJITFunctionObject::s_info = { "DOMJITFunctionObject", &Base::s_info, nullptr, CREATE_METHOD_TABLE(DOMJITFunctionObject) };
 const ClassInfo RuntimeArray::s_info = { "RuntimeArray", &Base::s_info, nullptr, CREATE_METHOD_TABLE(RuntimeArray) };
 const ClassInfo SimpleObject::s_info = { "SimpleObject", &Base::s_info, nullptr, CREATE_METHOD_TABLE(SimpleObject) };
-static bool test262AsyncPassed { false };
-static bool test262AsyncTest { false };
+static unsigned asyncTestPasses { 0 };
+static unsigned asyncTestExpectedPasses { 0 };
 
 ElementHandleOwner* Element::handleOwner()
 {
@@ -1077,6 +1079,8 @@ static EncodedJSValue JSC_HOST_CALL functionSamplingProfilerStackTraces(ExecStat
 #endif
 
 static EncodedJSValue JSC_HOST_CALL functionMaxArguments(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionAsyncTestStart(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionAsyncTestPassed(ExecState*);
 
 #if ENABLE(WEBASSEMBLY)
 static EncodedJSValue JSC_HOST_CALL functionTestWasmModuleFunctions(ExecState*);
@@ -1349,6 +1353,9 @@ protected:
 
         addFunction(vm, "maxArguments", functionMaxArguments, 0);
 
+        addFunction(vm, "asyncTestStart", functionAsyncTestStart, 1);
+        addFunction(vm, "asyncTestPassed", functionAsyncTestPassed, 1);
+
 #if ENABLE(WEBASSEMBLY)
         addFunction(vm, "testWasmModuleFunctions", functionTestWasmModuleFunctions, 0);
 #endif
@@ -1732,11 +1739,12 @@ static EncodedJSValue printInternal(ExecState* exec, FILE* out)
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
-    if (test262AsyncTest) {
+    if (asyncTestExpectedPasses) {
         JSValue value = exec->argument(0);
-        if (value.isString() && WTF::equal(asString(value)->value(exec).impl(), "Test262:AsyncTestComplete"))
-            test262AsyncPassed = true;
-        return JSValue::encode(jsUndefined());
+        if (value.isString() && WTF::equal(asString(value)->value(exec).impl(), "Test262:AsyncTestComplete")) {
+            asyncTestPasses++;
+            return JSValue::encode(jsUndefined());
+        }
     }
 
     for (unsigned i = 0; i < exec->argumentCount(); ++i) {
@@ -3049,6 +3057,25 @@ EncodedJSValue JSC_HOST_CALL functionMaxArguments(ExecState*)
     return JSValue::encode(jsNumber(JSC::maxArguments));
 }
 
+EncodedJSValue JSC_HOST_CALL functionAsyncTestStart(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSValue numberOfAsyncPasses = exec->argument(0);
+    if (!numberOfAsyncPasses.isUInt32())
+        return throwVMError(exec, scope, ASCIILiteral("Expected first argument to a uint32"));
+
+    asyncTestExpectedPasses += numberOfAsyncPasses.asUInt32();
+    return encodedJSUndefined();
+}
+
+EncodedJSValue JSC_HOST_CALL functionAsyncTestPassed(ExecState*)
+{
+    asyncTestPasses++;
+    return encodedJSUndefined();
+}
+
 #if ENABLE(WEBASSEMBLY)
 
 static CString valueWithTypeOfWasmValue(ExecState* exec, VM& vm, JSValue value, JSValue wasmValue)
@@ -3159,21 +3186,22 @@ static EncodedJSValue JSC_HOST_CALL functionTestWasmModuleFunctions(ExecState* e
     if (exec->argumentCount() != functionCount + 2)
         CRASH();
 
-    Wasm::Plan plan(&vm, static_cast<uint8_t*>(source->vector()), source->length());
-    plan.run();
-    if (plan.failed()) {
-        dataLogLn("failed to parse module: ", plan.errorMessage());
+    Ref<Wasm::Plan> plan = adoptRef(*new Wasm::Plan(vm, static_cast<uint8_t*>(source->vector()), source->length(), Wasm::Plan::FullCompile, Wasm::Plan::dontFinalize));
+    Wasm::ensureWorklist().enqueue(plan.copyRef());
+    Wasm::ensureWorklist().completePlanSynchronously(plan.get());
+    if (plan->failed()) {
+        dataLogLn("failed to parse module: ", plan->errorMessage());
         CRASH();
     }
 
-    if (plan.internalFunctionCount() != functionCount)
+    if (plan->internalFunctionCount() != functionCount)
         CRASH();
 
     MarkedArgumentBuffer callees;
     MarkedArgumentBuffer keepAlive;
     {
         unsigned lastIndex = UINT_MAX;
-        plan.initializeCallees(exec->lexicalGlobalObject(),
+        plan->initializeCallees(
             [&] (unsigned calleeIndex, JSWebAssemblyCallee* jsEntrypointCallee, JSWebAssemblyCallee* wasmEntrypointCallee) {
                 RELEASE_ASSERT(!calleeIndex || (calleeIndex - 1 == lastIndex));
                 callees.append(jsEntrypointCallee);
@@ -3181,7 +3209,7 @@ static EncodedJSValue JSC_HOST_CALL functionTestWasmModuleFunctions(ExecState* e
                 lastIndex = calleeIndex;
             });
     }
-    std::unique_ptr<Wasm::ModuleInformation> moduleInformation = plan.takeModuleInformation();
+    std::unique_ptr<Wasm::ModuleInformation> moduleInformation = plan->takeModuleInformation();
     RELEASE_ASSERT(!moduleInformation->memory);
 
     for (uint32_t i = 0; i < functionCount; ++i) {
@@ -3666,7 +3694,7 @@ void CommandLine::parseArguments(int argc, char** argv)
         }
 
         if (!strcmp(arg, "--test262-async")) {
-            test262AsyncTest = true;
+            asyncTestExpectedPasses++;
             continue;
         }
 
@@ -3738,48 +3766,64 @@ int runJSC(CommandLine options, const Func& func)
     Worker worker(Workers::singleton());
     
     VM& vm = VM::create(LargeHeap).leakRef();
-    JSLockHolder locker(&vm);
-
     int result;
-    if (options.m_profile && !vm.m_perBytecodeProfiler)
-        vm.m_perBytecodeProfiler = std::make_unique<Profiler::Database>(vm);
+    bool success;
+    {
+        JSLockHolder locker(vm);
 
-    GlobalObject* globalObject = GlobalObject::create(vm, GlobalObject::createStructure(vm, jsNull()), options.m_arguments);
-    globalObject->setRemoteDebuggingEnabled(options.m_enableRemoteDebugging);
-    bool success = func(vm, globalObject);
-    if (options.m_interactive && success)
-        runInteractive(globalObject);
+        if (options.m_profile && !vm.m_perBytecodeProfiler)
+            vm.m_perBytecodeProfiler = std::make_unique<Profiler::Database>(vm);
 
-    vm.drainMicrotasks();
-    result = success && (test262AsyncTest == test262AsyncPassed) ? 0 : 3;
+        GlobalObject* globalObject = GlobalObject::create(vm, GlobalObject::createStructure(vm, jsNull()), options.m_arguments);
+        globalObject->setRemoteDebuggingEnabled(options.m_enableRemoteDebugging);
+        success = func(vm, globalObject);
+        if (options.m_interactive && success)
+            runInteractive(globalObject);
+
+        vm.drainMicrotasks();
+    }
+#if USE(CF)
+    vm.promiseDeferredTimer->runRunLoop();
+#endif
 
-    if (options.m_exitCode)
-        printf("jsc exiting %d\n", result);
+    result = success && (asyncTestExpectedPasses == asyncTestPasses) ? 0 : 3;
+
+    if (options.m_exitCode) {
+        printf("jsc exiting %d", result);
+        if (asyncTestExpectedPasses != asyncTestPasses)
+            printf(" because expected: %d async test passes but got: %d async test passes", asyncTestExpectedPasses, asyncTestPasses);
+        printf("\n");
+    }
 
     if (options.m_profile) {
+        JSLockHolder locker(vm);
         if (!vm.m_perBytecodeProfiler->save(options.m_profilerOutput.utf8().data()))
             fprintf(stderr, "could not save profiler output.\n");
     }
 
 #if ENABLE(JIT)
-    if (Options::useExceptionFuzz())
-        printf("JSC EXCEPTION FUZZ: encountered %u checks.\n", numberOfExceptionFuzzChecks());
-    bool fireAtEnabled =
-    Options::fireExecutableAllocationFuzzAt() || Options::fireExecutableAllocationFuzzAtOrAfter();
-    if (Options::useExecutableAllocationFuzz() && (!fireAtEnabled || Options::verboseExecutableAllocationFuzz()))
-        printf("JSC EXECUTABLE ALLOCATION FUZZ: encountered %u checks.\n", numberOfExecutableAllocationFuzzChecks());
-    if (Options::useOSRExitFuzz()) {
-        printf("JSC OSR EXIT FUZZ: encountered %u static checks.\n", numberOfStaticOSRExitFuzzChecks());
-        printf("JSC OSR EXIT FUZZ: encountered %u dynamic checks.\n", numberOfOSRExitFuzzChecks());
-    }
-
-    auto compileTimeStats = JIT::compileTimeStats();
-    Vector<CString> compileTimeKeys;
-    for (auto& entry : compileTimeStats)
-        compileTimeKeys.append(entry.key);
-    std::sort(compileTimeKeys.begin(), compileTimeKeys.end());
-    for (CString key : compileTimeKeys)
-        printf("%40s: %.3lf ms\n", key.data(), compileTimeStats.get(key));
+    {
+        JSLockHolder locker(vm);
+        if (Options::useExceptionFuzz())
+            printf("JSC EXCEPTION FUZZ: encountered %u checks.\n", numberOfExceptionFuzzChecks());
+        bool fireAtEnabled =
+        Options::fireExecutableAllocationFuzzAt() || Options::fireExecutableAllocationFuzzAtOrAfter();
+        if (Options::useExecutableAllocationFuzz() && (!fireAtEnabled || Options::verboseExecutableAllocationFuzz()))
+            printf("JSC EXECUTABLE ALLOCATION FUZZ: encountered %u checks.\n", numberOfExecutableAllocationFuzzChecks());
+        if (Options::useOSRExitFuzz()) {
+            printf("JSC OSR EXIT FUZZ: encountered %u static checks.\n", numberOfStaticOSRExitFuzzChecks());
+            printf("JSC OSR EXIT FUZZ: encountered %u dynamic checks.\n", numberOfOSRExitFuzzChecks());
+        }
+
+        
+        auto compileTimeStats = JIT::compileTimeStats();
+        Vector<CString> compileTimeKeys;
+        for (auto& entry : compileTimeStats)
+            compileTimeKeys.append(entry.key);
+        std::sort(compileTimeKeys.begin(), compileTimeKeys.end());
+        for (CString key : compileTimeKeys)
+            printf("%40s: %.3lf ms\n", key.data(), compileTimeStats.get(key));
+    }
 #endif
 
     if (Options::gcAtEnd()) {
index 6967e03..1da0601 100644 (file)
@@ -33,6 +33,7 @@
 #include "JSObjectInlines.h"
 #include "JSPromise.h"
 #include "JSPromiseConstructor.h"
+#include "PromiseDeferredTimer.h"
 
 namespace JSC {
 
@@ -99,11 +100,15 @@ static inline void callFunction(ExecState* exec, JSValue function, JSValue value
 void JSPromiseDeferred::resolve(ExecState* exec, JSValue value)
 {
     callFunction(exec, m_resolve.get(), value);
+    bool wasPending = exec->vm().promiseDeferredTimer->cancelPendingPromise(this);
+    ASSERT_UNUSED(wasPending, wasPending == m_promiseIsAsyncPending);
 }
 
 void JSPromiseDeferred::reject(ExecState* exec, JSValue reason)
 {
     callFunction(exec, m_reject.get(), reason);
+    bool wasPending = exec->vm().promiseDeferredTimer->cancelPendingPromise(this);
+    ASSERT_UNUSED(wasPending, wasPending == m_promiseIsAsyncPending);
 }
 
 void JSPromiseDeferred::reject(ExecState* exec, Exception* reason)
index 26f4a82..0922a94 100644 (file)
@@ -56,6 +56,10 @@ public:
     JS_EXPORT_PRIVATE void reject(ExecState*, JSValue);
     JS_EXPORT_PRIVATE void reject(ExecState*, Exception*);
 
+#ifndef NDEBUG
+    void promiseAsyncPending() { m_promiseIsAsyncPending = true; }
+#endif
+
 protected:
     JSPromiseDeferred(VM&, Structure*);
     void finishCreation(VM&, JSObject*, JSValue, JSValue);
@@ -64,6 +68,10 @@ protected:
 private:
     JSPromiseDeferred(VM&);
 
+#ifndef NDEBUG
+    bool m_promiseIsAsyncPending { false };
+#endif
+
     WriteBarrier<JSObject> m_promise;
     WriteBarrier<Unknown> m_resolve;
     WriteBarrier<Unknown> m_reject;
similarity index 73%
rename from Source/JavaScriptCore/heap/HeapTimer.cpp
rename to Source/JavaScriptCore/runtime/JSRunLoopTimer.cpp
index 6396128..afc1391 100644 (file)
  * 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. 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "config.h"
-#include "HeapTimer.h"
+#include "JSRunLoopTimer.h"
 
 #include "GCActivityCallback.h"
 #include "IncrementalSweeper.h"
+#include "JSCInlines.h"
 #include "JSObject.h"
 #include "JSString.h"
-#include "JSCInlines.h"
+
 #include <wtf/MainThread.h>
 #include <wtf/Threading.h>
 
 namespace JSC {
 
 #if USE(CF)
-    
-const CFTimeInterval HeapTimer::s_decade = 60 * 60 * 24 * 365 * 10;
 
-HeapTimer::HeapTimer(VM* vm)
+const CFTimeInterval JSRunLoopTimer::s_decade = 60 * 60 * 24 * 365 * 10;
+
+JSRunLoopTimer::JSRunLoopTimer(VM* vm)
     : m_vm(vm)
     , m_apiLock(&vm->apiLock())
 {
     setRunLoop(vm->heap.runLoop());
 }
 
-void HeapTimer::setRunLoop(CFRunLoopRef runLoop)
+void JSRunLoopTimer::setRunLoop(CFRunLoopRef runLoop)
 {
     if (m_runLoop) {
         CFRunLoopRemoveTimer(m_runLoop.get(), m_timer.get(), kCFRunLoopCommonModes);
@@ -59,24 +60,24 @@ void HeapTimer::setRunLoop(CFRunLoopRef runLoop)
         m_runLoop.clear();
         m_timer.clear();
     }
-    
+
     if (runLoop) {
         m_runLoop = runLoop;
         memset(&m_context, 0, sizeof(CFRunLoopTimerContext));
         m_context.info = this;
-        m_timer = adoptCF(CFRunLoopTimerCreate(kCFAllocatorDefault, s_decade, s_decade, 0, 0, HeapTimer::timerDidFire, &m_context));
+        m_timer = adoptCF(CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + s_decade, s_decade, 0, 0, JSRunLoopTimer::timerDidFire, &m_context));
         CFRunLoopAddTimer(m_runLoop.get(), m_timer.get(), kCFRunLoopCommonModes);
     }
 }
 
-HeapTimer::~HeapTimer()
+JSRunLoopTimer::~JSRunLoopTimer()
 {
     setRunLoop(0);
 }
 
-void HeapTimer::timerDidFire(CFRunLoopTimerRef, void* contextPtr)
+void JSRunLoopTimer::timerDidFire(CFRunLoopTimerRef, void* contextPtr)
 {
-    HeapTimer* timer = static_cast<HeapTimer*>(contextPtr);
+    JSRunLoopTimer* timer = static_cast<JSRunLoopTimer*>(contextPtr);
     timer->m_apiLock->lock();
 
     RefPtr<VM> vm = timer->m_apiLock->vm();
@@ -94,13 +95,13 @@ void HeapTimer::timerDidFire(CFRunLoopTimerRef, void* contextPtr)
     timer->m_apiLock->unlock();
 }
 
-void HeapTimer::scheduleTimer(double intervalInSeconds)
+void JSRunLoopTimer::scheduleTimer(double intervalInSeconds)
 {
     CFRunLoopTimerSetNextFireDate(m_timer.get(), CFAbsoluteTimeGetCurrent() + intervalInSeconds);
     m_isScheduled = true;
 }
 
-void HeapTimer::cancelTimer()
+void JSRunLoopTimer::cancelTimer()
 {
     CFRunLoopTimerSetNextFireDate(m_timer.get(), CFAbsoluteTimeGetCurrent() + s_decade);
     m_isScheduled = false;
@@ -108,9 +109,9 @@ void HeapTimer::cancelTimer()
 
 #elif USE(GLIB)
 
-const long HeapTimer::s_decade = 60 * 60 * 24 * 365 * 10;
+const long JSRunLoopTimer::s_decade = 60 * 60 * 24 * 365 * 10;
 
-static GSourceFuncs heapTimerSourceFunctions = {
+static GSourceFuncs JSRunLoopTimerSourceFunctions = {
     nullptr, // prepare
     nullptr, // check
     // dispatch
@@ -123,27 +124,27 @@ static GSourceFuncs heapTimerSourceFunctions = {
     nullptr, // closure_marshall
 };
 
-HeapTimer::HeapTimer(VM* vm)
+JSRunLoopTimer::JSRunLoopTimer(VM* vm)
     : m_vm(vm)
     , m_apiLock(&vm->apiLock())
-    , m_timer(adoptGRef(g_source_new(&heapTimerSourceFunctions, sizeof(GSource))))
+    , m_timer(adoptGRef(g_source_new(&JSRunLoopTimerSourceFunctions, sizeof(GSource))))
 {
-    g_source_set_name(m_timer.get(), "[JavaScriptCore] HeapTimer");
+    g_source_set_name(m_timer.get(), "[JavaScriptCore] JSRunLoopTimer");
     g_source_set_callback(m_timer.get(), [](gpointer userData) -> gboolean {
-        auto& heapTimer = *static_cast<HeapTimer*>(userData);
-        g_source_set_ready_time(heapTimer.m_timer.get(), g_get_monotonic_time() + HeapTimer::s_decade * G_USEC_PER_SEC);
-        heapTimer.timerDidFire();
+        auto& runLoopTimer = *static_cast<JSRunLoopTimer*>(userData);
+        g_source_set_ready_time(runLoopTimer.m_timer.get(), g_get_monotonic_time() + JSRunLoopTimer::s_decade * G_USEC_PER_SEC);
+        runLoopTimer.timerDidFire();
         return G_SOURCE_CONTINUE;
     }, this, nullptr);
     g_source_attach(m_timer.get(), g_main_context_get_thread_default());
 }
 
-HeapTimer::~HeapTimer()
+JSRunLoopTimer::~JSRunLoopTimer()
 {
     g_source_destroy(m_timer.get());
 }
 
-void HeapTimer::timerDidFire()
+void JSRunLoopTimer::timerDidFire()
 {
     m_apiLock->lock();
 
@@ -161,39 +162,38 @@ void HeapTimer::timerDidFire()
     m_apiLock->unlock();
 }
 
-void HeapTimer::scheduleTimer(double intervalInSeconds)
+void JSRunLoopTimer::scheduleTimer(double intervalInSeconds)
 {
     g_source_set_ready_time(m_timer.get(), g_get_monotonic_time() + intervalInSeconds * G_USEC_PER_SEC);
     m_isScheduled = true;
 }
 
-void HeapTimer::cancelTimer()
+void JSRunLoopTimer::cancelTimer()
 {
     g_source_set_ready_time(m_timer.get(), g_get_monotonic_time() + s_decade * G_USEC_PER_SEC);
     m_isScheduled = false;
 }
 #else
-HeapTimer::HeapTimer(VM* vm)
+JSRunLoopTimer::JSRunLoopTimer(VM* vm)
     : m_vm(vm)
 {
 }
 
-HeapTimer::~HeapTimer()
+JSRunLoopTimer::~JSRunLoopTimer()
 {
 }
 
-void HeapTimer::invalidate()
+void JSRunLoopTimer::invalidate()
 {
 }
 
-void HeapTimer::scheduleTimer(double)
+void JSRunLoopTimer::scheduleTimer(double)
 {
 }
 
-void HeapTimer::cancelTimer()
+void JSRunLoopTimer::cancelTimer()
 {
 }
 #endif
     
-
 } // namespace JSC
similarity index 91%
rename from Source/JavaScriptCore/heap/HeapTimer.h
rename to Source/JavaScriptCore/runtime/JSRunLoopTimer.h
index 72b471f..83ecb9c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2015-2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2015-2017 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -20,7 +20,7 @@
  * 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. 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #pragma once
@@ -44,14 +44,14 @@ namespace JSC {
 class JSLock;
 class VM;
 
-class HeapTimer : public ThreadSafeRefCounted<HeapTimer> {
+class JSRunLoopTimer : public ThreadSafeRefCounted<JSRunLoopTimer> {
 public:
-    HeapTimer(VM*);
+    JSRunLoopTimer(VM*);
 #if USE(CF)
     static void timerDidFire(CFRunLoopTimerRef, void*);
 #endif
-    
-    JS_EXPORT_PRIVATE virtual ~HeapTimer();
+
+    JS_EXPORT_PRIVATE virtual ~JSRunLoopTimer();
     virtual void doWork() = 0;
 
     void scheduleTimer(double intervalInSeconds);
@@ -61,7 +61,7 @@ public:
 #if USE(CF)
     JS_EXPORT_PRIVATE void setRunLoop(CFRunLoopRef);
 #endif // USE(CF)
-    
+
 protected:
     VM* m_vm;
 
@@ -72,7 +72,7 @@ protected:
 
     RetainPtr<CFRunLoopTimerRef> m_timer;
     RetainPtr<CFRunLoopRef> m_runLoop;
-    
+
     CFRunLoopTimerContext m_context;
 
     Lock m_shutdownMutex;
@@ -85,5 +85,5 @@ protected:
 private:
     void invalidate();
 };
-
+    
 } // namespace JSC
index da996ea..a0d9109 100644 (file)
@@ -252,6 +252,7 @@ typedef const char* optionString;
     v(unsigned, numberOfFTLCompilerThreads, computeNumberOfWorkerThreads(8, 2) - 1, Normal, nullptr) \
     v(int32, priorityDeltaOfDFGCompilerThreads, computePriorityDeltaOfWorkerThreads(-1, 0), Normal, nullptr) \
     v(int32, priorityDeltaOfFTLCompilerThreads, computePriorityDeltaOfWorkerThreads(-2, 0), Normal, nullptr) \
+    v(int32, priorityDeltaOfWasmCompilerThreads, computePriorityDeltaOfWorkerThreads(-1, 0), Normal, nullptr) \
     \
     v(bool, useProfiler, false, Normal, nullptr) \
     v(bool, disassembleBaselineForProfiler, true, Normal, nullptr) \
@@ -428,6 +429,9 @@ typedef const char* optionString;
     v(bool, useCodeCache, true, Normal, "If false, the unlinked byte code cache will not be used.") \
     \
     v(bool, useWebAssembly, true, Normal, "Expose the WebAssembly global object.") \
+    \
+    v(bool, failToCompileWebAssemblyCode, false, Normal, "If true, no Wasm::Plan will sucessfully compile a function.") \
+    \
     v(bool, simulateWebAssemblyLowMemory, false, Normal, "If true, the Memory object won't mmap the full 'maximum' range and instead will allocate the minimum required amount.") \
     v(bool, useWebAssemblyFastMemory, true, Normal, "If true, we will try to use a 32-bit address space with a signal handler to bounds check wasm memory.") \
     v(bool, useWebAssemblyFastTLS, true, Normal, "If true, we will try to use fast thread-local storage if available on the current platform.")
diff --git a/Source/JavaScriptCore/runtime/PromiseDeferredTimer.cpp b/Source/JavaScriptCore/runtime/PromiseDeferredTimer.cpp
new file mode 100644 (file)
index 0000000..74af691
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * 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 "PromiseDeferredTimer.h"
+
+#include "JSPromiseDeferred.h"
+#include "StrongInlines.h"
+#include "VM.h"
+
+#include <wtf/Locker.h>
+
+namespace JSC {
+
+static const bool verbose = false;
+
+PromiseDeferredTimer::PromiseDeferredTimer(VM& vm)
+    : Base(&vm)
+{
+}
+
+void PromiseDeferredTimer::doWork()
+{
+    ASSERT(m_vm->currentThreadIsHoldingAPILock());
+    LockHolder locker(m_taskLock);
+    cancelTimer();
+    if (!m_runTasks)
+        return;
+
+    while (!m_tasks.isEmpty()) {
+        JSPromiseDeferred* ticket;
+        Task task;
+        std::tie(ticket, task) = m_tasks.takeLast();
+        dataLogLnIf(verbose, "Doing work on promise: ", RawPointer(ticket));
+
+        // We may have already canceled these promises.
+        if (m_pendingPromises.contains(ticket)) {
+            task();
+            m_vm->drainMicrotasks();
+        }
+
+        auto waitingTasks = m_blockedTasks.take(ticket);
+        for (const Task& unblockedTask : waitingTasks) {
+            unblockedTask();
+            m_vm->drainMicrotasks();
+        }
+    }
+
+#if USE(CF)
+    if (m_pendingPromises.isEmpty() && m_shouldStopRunLoopWhenAllPromisesFinish)
+        CFRunLoopStop(m_runLoop.get());
+#endif
+}
+
+#if USE(CF)
+void PromiseDeferredTimer::runRunLoop()
+{
+    ASSERT(!m_vm->currentThreadIsHoldingAPILock());
+    ASSERT(CFRunLoopGetCurrent() == m_runLoop.get());
+    m_shouldStopRunLoopWhenAllPromisesFinish = true;
+    if (m_pendingPromises.size())
+        CFRunLoopRun();
+}
+#endif
+
+void PromiseDeferredTimer::addPendingPromise(JSPromiseDeferred* ticket, Vector<Strong<JSCell>>&& dependencies)
+{
+    ASSERT(m_vm->currentThreadIsHoldingAPILock());
+    for (unsigned i = 0; i < dependencies.size(); ++i)
+        ASSERT(dependencies[i].get() != ticket);
+
+    auto result = m_pendingPromises.add(ticket, Vector<Strong<JSCell>>());
+    if (result.isNewEntry) {
+        dataLogLnIf(verbose, "Adding new pending promise: ", RawPointer(ticket));
+        dependencies.append(Strong<JSCell>(*m_vm, ticket));
+        result.iterator->value = WTFMove(dependencies);
+    } else {
+        dataLogLnIf(verbose, "Adding new dependencies for promise: ", RawPointer(ticket));
+        result.iterator->value.appendVector(dependencies);
+    }
+
+#ifndef NDEBUG
+    ticket->promiseAsyncPending();
+#endif
+}
+
+bool PromiseDeferredTimer::cancelPendingPromise(JSPromiseDeferred* ticket)
+{
+    ASSERT(m_vm->currentThreadIsHoldingAPILock());
+    bool result = m_pendingPromises.remove(ticket);
+
+    if (result)
+        dataLogLnIf(verbose, "Canceling promise: ", RawPointer(ticket));
+
+    auto blockedTasks = m_blockedTasks.take(ticket);
+    for (const Task& task : blockedTasks)
+        task();
+    return result;
+}
+
+void PromiseDeferredTimer::scheduleWorkSoon(JSPromiseDeferred* ticket, Task&& task)
+{
+    LockHolder locker(m_taskLock);
+    m_tasks.append(std::make_tuple(ticket, WTFMove(task)));
+    if (!isScheduled())
+        scheduleTimer(0);
+}
+
+void PromiseDeferredTimer::scheduleBlockedTask(JSPromiseDeferred* blockingTicket, Task&& task)
+{
+    ASSERT(m_vm->currentThreadIsHoldingAPILock());
+    ASSERT(m_pendingPromises.contains(blockingTicket));
+    auto result = m_blockedTasks.add(blockingTicket, Vector<Task>());
+    result.iterator->value.append(WTFMove(task));
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/PromiseDeferredTimer.h b/Source/JavaScriptCore/runtime/PromiseDeferredTimer.h
new file mode 100644 (file)
index 0000000..cc3c33e
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * 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
+
+#include "JSRunLoopTimer.h"
+#include "Strong.h"
+#include "WriteBarrier.h"
+
+#include <wtf/HashMap.h>
+#include <wtf/Lock.h>
+#include <wtf/SharedTask.h>
+#include <wtf/Vector.h>
+
+namespace JSC {
+
+class JSPromiseDeferred;
+class VM;
+class JSCell;
+
+class PromiseDeferredTimer : public JSRunLoopTimer {
+public:
+    using Base = JSRunLoopTimer;
+
+    PromiseDeferredTimer(VM&);
+
+    void doWork() override;
+
+    void addPendingPromise(JSPromiseDeferred*, Vector<Strong<JSCell>>&& dependencies);
+    // JSPromiseDeferred should handle canceling when the promise is resolved or rejected.
+    bool cancelPendingPromise(JSPromiseDeferred*);
+
+    typedef std::function<void()> Task;
+    void scheduleWorkSoon(JSPromiseDeferred*, Task&&);
+
+    // Blocked tasks should only be registered while holding the JS API lock. If we didn't require holding the
+    // JS API lock then there might be a race where the promise you are waiting on is run before your task is
+    // registered.
+    void scheduleBlockedTask(JSPromiseDeferred*, Task&&);
+
+    void stopRunningTasks() { m_runTasks = false; }
+#if USE(CF)
+    JS_EXPORT_PRIVATE void runRunLoop();
+#endif
+
+private:
+    HashMap<JSPromiseDeferred*, Vector<Strong<JSCell>>> m_pendingPromises;
+    Lock m_taskLock;
+    bool m_runTasks { true };
+#if USE(CF)
+    bool m_shouldStopRunLoopWhenAllPromisesFinish { false };
+#endif
+    Vector<std::tuple<JSPromiseDeferred*, Task>> m_tasks;
+    HashMap<JSPromiseDeferred*, Vector<Task>> m_blockedTasks;
+};
+
+} // namespace JSC
index d108153..022b02e 100644 (file)
@@ -85,6 +85,7 @@
 #include "Parser.h"
 #include "ProfilerDatabase.h"
 #include "ProgramCodeBlock.h"
+#include "PromiseDeferredTimer.h"
 #include "PropertyMapHashTable.h"
 #include "RegExpCache.h"
 #include "RegExpObject.h"
 #include "UnlinkedCodeBlock.h"
 #include "VMEntryScope.h"
 #include "VMInspector.h"
+#include "WasmWorklist.h"
 #include "Watchdog.h"
 #include "WeakGCMapInlines.h"
 #include "WeakMapData.h"
@@ -177,6 +179,7 @@ VM::VM(VMType vmType, HeapType heapType)
     , clientData(0)
     , topVMEntryFrame(nullptr)
     , topCallFrame(CallFrame::noCaller())
+    , promiseDeferredTimer(std::make_unique<PromiseDeferredTimer>(*this))
     , m_atomicStringTable(vmType == Default ? wtfThreadData().atomicStringTable() : new AtomicStringTable)
     , propertyNames(nullptr)
     , emptyList(new ArgList)
@@ -355,6 +358,11 @@ VM::VM(VMType vmType, HeapType heapType)
 
 VM::~VM()
 {
+    promiseDeferredTimer->stopRunningTasks();
+#if ENABLE(WEBASSEMBLY)
+    if (Wasm::existingWorklistOrNull())
+        Wasm::ensureWorklist().stopAllPlansForVM(*this);
+#endif
     if (UNLIKELY(m_watchdog))
         m_watchdog->willDestroyVM(this);
     m_traps.willDestroyVM();
index d67e71b..26ca730 100644 (file)
@@ -106,6 +106,7 @@ class JSObject;
 class JSWebAssemblyInstance;
 class LLIntOffsetsExtractor;
 class NativeExecutable;
+class PromiseDeferredTimer;
 class RegExpCache;
 class Register;
 class RegisterAtOffsetList;
@@ -379,6 +380,7 @@ public:
     std::once_flag m_wasmSignatureInformationOnceFlag;
     std::unique_ptr<Wasm::SignatureInformation> m_wasmSignatureInformation;
 #endif
+    std::unique_ptr<PromiseDeferredTimer> promiseDeferredTimer;
     
     JSCell* currentlyDestructingCallbackObject;
     const ClassInfo* currentlyDestructingCallbackObjectClassInfo;
index fcdc8cd..997ee2f 100644 (file)
 #include "JSCInlines.h"
 #include "JSPromiseDeferred.h"
 #include "JSWebAssemblyHelpers.h"
+#include "JSWebAssemblyInstance.h"
+#include "JSWebAssemblyModule.h"
 #include "ObjectConstructor.h"
+#include "PromiseDeferredTimer.h"
+#include "StrongInlines.h"
 #include "WasmPlan.h"
+#include "WasmWorklist.h"
 #include "WebAssemblyModuleConstructor.h"
 
+using JSC::Wasm::Plan;
+
 namespace JSC {
 
 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSWebAssembly);
@@ -45,45 +52,161 @@ EncodedJSValue JSC_HOST_CALL webAssemblyValidateFunc(ExecState*);
 EncodedJSValue JSC_HOST_CALL webAssemblyCompileFunc(ExecState*);
 EncodedJSValue JSC_HOST_CALL webAssemblyInstantiateFunc(ExecState*);
 
+static EncodedJSValue reject(ExecState* exec, CatchScope& catchScope, JSPromiseDeferred* promise)
+{
+    Exception* exception = catchScope.exception();
+    ASSERT(exception);
+    catchScope.clearException();
+    promise->reject(exec, exception->value());
+    return JSValue::encode(promise->promise());
+}
+
 EncodedJSValue JSC_HOST_CALL webAssemblyCompileFunc(ExecState* exec)
 {
     VM& vm = exec->vm();
-    auto catchScope = DECLARE_CATCH_SCOPE(vm);
+    auto scope = DECLARE_CATCH_SCOPE(vm);
+    auto* globalObject = exec->lexicalGlobalObject();
 
-    JSPromiseDeferred* promise = JSPromiseDeferred::create(exec, exec->lexicalGlobalObject());
-    RETURN_IF_EXCEPTION(catchScope, encodedJSValue());
+    JSPromiseDeferred* promise = JSPromiseDeferred::create(exec, globalObject);
+    RETURN_IF_EXCEPTION(scope, { });
 
-    // FIXME: Make this truly asynchronous:
-    // https://bugs.webkit.org/show_bug.cgi?id=166016
-    JSValue module = WebAssemblyModuleConstructor::createModule(exec, exec->argument(0), exec->lexicalGlobalObject()->WebAssemblyModuleStructure());
-    if (Exception* exception = catchScope.exception()) {
-        catchScope.clearException();
-        promise->reject(exec, exception->value());
-        return JSValue::encode(promise->promise());
-    }
+    RefPtr<ArrayBuffer> source = createSourceBufferFromValue(vm, exec, exec->argument(0));
+    RETURN_IF_EXCEPTION(scope, { });
+
+    Vector<Strong<JSCell>> dependencies;
+    dependencies.append(Strong<JSCell>(vm, globalObject));
+    vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
+
+    Ref<Plan> plan = adoptRef(*new Plan(vm, *source, Plan::Validation, [source, promise, globalObject] (Plan& p) mutable {
+        RefPtr<Plan> plan = makeRef(p);
+        plan->vm().promiseDeferredTimer->scheduleWorkSoon(promise, [source, promise, globalObject, plan = WTFMove(plan)] () mutable {
+            VM& vm = plan->vm();
+            auto scope = DECLARE_CATCH_SCOPE(vm);
+            ExecState* exec = globalObject->globalExec();
+            JSValue module = JSWebAssemblyModule::createStub(vm, exec, globalObject->WebAssemblyModuleStructure(), WTFMove(source), WTFMove(plan));
+            if (scope.exception()) {
+                reject(exec, scope, promise);
+                return;
+            }
 
-    promise->resolve(exec, module);
+            promise->resolve(exec, module);
+        });
+    }));
+
+    Wasm::ensureWorklist().enqueue(WTFMove(plan));
     return JSValue::encode(promise->promise());
 }
 
+enum class Resolve { WithInstance, WithModuleAndInstance };
+static void resolve(VM& vm, ExecState* exec, JSPromiseDeferred* promise, JSWebAssemblyInstance* instance, JSWebAssemblyModule* module, Resolve entries)
+{
+    auto scope = DECLARE_CATCH_SCOPE(vm);
+    instance->finalizeCreation(vm, exec);
+    if (scope.exception()) {
+        reject(exec, scope, promise);
+        return;
+    }
+
+    if (entries == Resolve::WithInstance)
+        promise->resolve(exec, instance);
+    else {
+        JSObject* result = constructEmptyObject(exec);
+        result->putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("module")), module);
+        result->putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("instance")), instance);
+        promise->resolve(exec, result);
+    }
+}
+
+static void instantiate(VM& vm, ExecState* exec, JSPromiseDeferred* promise, JSWebAssemblyModule* module, JSObject* importObject, Resolve entries)
+{
+    auto scope = DECLARE_CATCH_SCOPE(vm);
+    // In order to avoid potentially recompiling a module. We first gather all the import/memory information prior to compiling code.
+    JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, exec, module, importObject, exec->lexicalGlobalObject()->WebAssemblyInstanceStructure());
+    if (scope.exception()) {
+        reject(exec, scope, promise);
+        return;
+    }
+
+    // There are three possible cases:
+    // 1) The instance already has an initialized CodeBlock, so we have no more work to do.
+    // 2) The instance has no CodeBlock, so we need to make one and compile the code for it.
+    // 3) The instance already has an uninitialized CodeBlock, so someone else is compiling code and we just need to wait for them.
+
+    if (instance->initialized()) {
+        resolve(vm, exec, promise, instance, module, entries);
+        return;
+    }
+
+    Vector<Strong<JSCell>> dependencies;
+    // The instance keeps the module alive.
+    dependencies.append(Strong<JSCell>(vm, instance));
+    vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
+
+    if (instance->codeBlock()) {
+        vm.promiseDeferredTimer->scheduleBlockedTask(instance->codeBlock()->plan().pendingPromise(), [&vm, promise, instance, module, entries] () {
+            auto* globalObject = instance->globalObject();
+            ExecState* exec = globalObject->globalExec();
+            resolve(vm, exec, promise, instance, module, entries);
+        });
+        return;
+    }
+    ASSERT(!instance->codeBlock());
+
+    // FIXME: This re-parses the module header, which shouldn't be necessary.
+    // https://bugs.webkit.org/show_bug.cgi?id=170205
+    Ref<Plan> plan = adoptRef(*new Plan(vm, module->source(), Plan::FullCompile, [promise, instance, module, entries] (Plan& p) {
+        RefPtr<Plan> plan = makeRef(p);
+        plan->vm().promiseDeferredTimer->scheduleWorkSoon(promise, [promise, instance, module, entries, plan = WTFMove(plan)] () {
+            VM& vm = plan->vm();
+            ExecState* exec = instance->globalObject()->globalExec();
+            resolve(vm, exec, promise, instance, module, entries);
+        });
+    }));
+
+    instance->addUnitializedCodeBlock(vm, plan.copyRef());
+    plan->setModeAndPromise(instance->memoryMode(), promise);
+    Wasm::ensureWorklist().enqueue(WTFMove(plan));
+}
+
+static void compileAndInstantiate(VM& vm, ExecState* exec, JSPromiseDeferred* promise, JSValue buffer, JSObject* importObject)
+{
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    RefPtr<ArrayBuffer> source = createSourceBufferFromValue(vm, exec, buffer);
+    RETURN_IF_EXCEPTION(scope, void());
+
+    auto* globalObject = exec->lexicalGlobalObject();
+
+    Vector<Strong<JSCell>> dependencies;
+    dependencies.append(Strong<JSCell>(vm, importObject));
+    vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
+
+    Ref<Plan> plan = adoptRef(*new Plan(vm, *source, Plan::Validation, [source, promise, importObject, globalObject] (Plan& p) mutable {
+        RefPtr<Plan> plan = makeRef(p);
+        plan->vm().promiseDeferredTimer->scheduleWorkSoon(promise, [source, promise, importObject, globalObject, plan = WTFMove(plan)] () mutable {
+            VM& vm = plan->vm();
+            auto scope = DECLARE_CATCH_SCOPE(vm);
+            ExecState* exec = globalObject->globalExec();
+            JSWebAssemblyModule* module = JSWebAssemblyModule::createStub(vm, exec, globalObject->WebAssemblyModuleStructure(), WTFMove(source), plan.copyRef());
+            if (scope.exception()) {
+                reject(exec, scope, promise);
+                return;
+            }
+
+            instantiate(vm, exec, promise, module, importObject, Resolve::WithModuleAndInstance);
+        });
+    }));
+
+    Wasm::ensureWorklist().enqueue(WTFMove(plan));
+}
+
 EncodedJSValue JSC_HOST_CALL webAssemblyInstantiateFunc(ExecState* exec)
 {
     VM& vm = exec->vm();
     auto catchScope = DECLARE_CATCH_SCOPE(vm);
 
-    // FIXME: Make this API truly asynchronous: https://bugs.webkit.org/show_bug.cgi?id=169187
-
     JSPromiseDeferred* promise = JSPromiseDeferred::create(exec, exec->lexicalGlobalObject());
     RETURN_IF_EXCEPTION(catchScope, encodedJSValue());
 
-    auto reject = [&] () {
-        Exception* exception = catchScope.exception();
-        ASSERT(exception);
-        catchScope.clearException();
-        promise->reject(exec, exception->value());
-        return JSValue::encode(promise->promise());
-    };
-
     JSValue importArgument = exec->argument(1);
     JSObject* importObject = importArgument.getObject();
     if (!importArgument.isUndefined() && !importObject) {
@@ -93,29 +216,10 @@ EncodedJSValue JSC_HOST_CALL webAssemblyInstantiateFunc(ExecState* exec)
     }
 
     JSValue firstArgument = exec->argument(0);
-    JSValue module;
-    bool firstArgumentIsModule = false;
-    if (firstArgument.inherits(vm, JSWebAssemblyModule::info())) {
-        firstArgumentIsModule = true;
-        module = firstArgument;
-    } else {
-        module = WebAssemblyModuleConstructor::createModule(exec, firstArgument, exec->lexicalGlobalObject()->WebAssemblyModuleStructure());
-        if (catchScope.exception())
-            return reject();
-    }
-
-    JSWebAssemblyInstance* instance = WebAssemblyInstanceConstructor::createInstance(exec, jsCast<JSWebAssemblyModule*>(module), importObject, exec->lexicalGlobalObject()->WebAssemblyInstanceStructure());
-    if (catchScope.exception())
-        return reject();
-
-    if (firstArgumentIsModule)
-        promise->resolve(exec, instance);
-    else {
-        JSObject* result = constructEmptyObject(exec);
-        result->putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("module")), module);
-        result->putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("instance")), instance);
-        promise->resolve(exec, result);
-    }
+    if (auto* module = jsDynamicCast<JSWebAssemblyModule*>(vm, firstArgument))
+        instantiate(vm, exec, promise, module, importObject, Resolve::WithInstance);
+    else
+        compileAndInstantiate(vm, exec, promise, firstArgument, importObject);
 
     return JSValue::encode(promise->promise());
 }
@@ -129,7 +233,7 @@ EncodedJSValue JSC_HOST_CALL webAssemblyValidateFunc(ExecState* exec)
     size_t byteSize;
     uint8_t* base = getWasmBufferFromValue(exec, exec->argument(0), byteOffset, byteSize);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    Wasm::Plan plan(&vm, base + byteOffset, byteSize);
+    Wasm::Plan plan(vm, base + byteOffset, byteSize, Plan::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()));
index 0b02ec3..87d9a42 100644 (file)
@@ -157,7 +157,7 @@ public:
             return fail(__VA_ARGS__);             \
     } while (0)
 
-    B3IRGenerator(VM&, const ModuleInformation&, Procedure&, WasmInternalFunction*, Vector<UnlinkedWasmToWasmCall>&);
+    B3IRGenerator(VM&, const ModuleInformation&, Procedure&, WasmInternalFunction*, Vector<UnlinkedWasmToWasmCall>&, MemoryMode);
 
     PartialResult WARN_UNUSED_RETURN addArguments(const Signature*);
     PartialResult WARN_UNUSED_RETURN addLocal(Type, uint32_t);
@@ -225,6 +225,7 @@ private:
 
     VM& m_vm;
     const ModuleInformation& m_info;
+    MemoryMode m_mode;
     Procedure& m_proc;
     BasicBlock* m_currentBlock;
     Vector<Variable*> m_locals;
@@ -290,9 +291,10 @@ void B3IRGenerator::restoreWasmContext(Procedure& proc, BasicBlock* block, Value
     });
 }
 
-B3IRGenerator::B3IRGenerator(VM& vm, const ModuleInformation& info, Procedure& procedure, WasmInternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls)
+B3IRGenerator::B3IRGenerator(VM& vm, const ModuleInformation& info, Procedure& procedure, WasmInternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, MemoryMode mode)
     : m_vm(vm)
     , m_info(info)
+    , m_mode(mode)
     , m_proc(procedure)
     , m_unlinkedWasmToWasmCalls(unlinkedWasmToWasmCalls)
 {
@@ -520,7 +522,7 @@ auto B3IRGenerator::setGlobal(uint32_t index, ExpressionType value) -> PartialRe
 inline Value* B3IRGenerator::emitCheckAndPreparePointer(ExpressionType pointer, uint32_t offset, uint32_t sizeOfOperation)
 {
     ASSERT(m_memoryBaseGPR);
-    if (m_info.memory.mode() == MemoryMode::BoundsChecking) {
+    if (m_mode == MemoryMode::BoundsChecking) {
         ASSERT(m_memorySizeGPR);
         ASSERT(sizeOfOperation + offset > offset);
         m_currentBlock->appendNew<WasmBoundsCheckValue>(m_proc, Origin(), pointer, m_memorySizeGPR, sizeOfOperation + offset - 1);
@@ -556,7 +558,7 @@ inline uint32_t sizeOfLoadOp(LoadOpType op)
 
 inline B3::Kind B3IRGenerator::memoryKind(B3::Opcode memoryOp)
 {
-    if (m_info.memory.mode() == MemoryMode::Signaling)
+    if (m_mode == MemoryMode::Signaling)
         return trapping(memoryOp);
     return memoryOp;
 }
@@ -1277,7 +1279,7 @@ static void createJSToWasmWrapper(CompilationContext& compilationContext, WasmIn
     jit.ret();
 }
 
-Expected<std::unique_ptr<WasmInternalFunction>, String> parseAndCompile(VM& vm, CompilationContext& compilationContext, const uint8_t* functionStart, size_t functionLength, const Signature* signature, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ModuleInformation& info, const Vector<SignatureIndex>& moduleSignatureIndicesToUniquedSignatureIndices, unsigned optLevel)
+Expected<std::unique_ptr<WasmInternalFunction>, String> parseAndCompile(VM& vm, CompilationContext& compilationContext, const uint8_t* functionStart, size_t functionLength, const Signature* signature, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ModuleInformation& info, const Vector<SignatureIndex>& moduleSignatureIndicesToUniquedSignatureIndices, MemoryMode mode, unsigned optLevel)
 {
     auto result = std::make_unique<WasmInternalFunction>();
 
@@ -1285,7 +1287,7 @@ Expected<std::unique_ptr<WasmInternalFunction>, String> parseAndCompile(VM& vm,
     compilationContext.wasmEntrypointJIT = std::make_unique<CCallHelpers>(&vm);
 
     Procedure procedure;
-    B3IRGenerator context(vm, info, procedure, result.get(), unlinkedWasmToWasmCalls);
+    B3IRGenerator context(vm, info, procedure, result.get(), unlinkedWasmToWasmCalls, mode);
     FunctionParser<B3IRGenerator> parser(&vm, context, functionStart, functionLength, signature, info, moduleSignatureIndicesToUniquedSignatureIndices);
     WASM_FAIL_IF_HELPER_FAILS(parser.parse());
 
index 249d578..de56a1c 100644 (file)
@@ -31,6 +31,7 @@
 #include "CCallHelpers.h"
 #include "VM.h"
 #include "WasmFormat.h"
+#include "WasmMemory.h"
 #include <wtf/Expected.h>
 
 extern "C" void dumpProcedure(void*);
@@ -47,7 +48,7 @@ struct CompilationContext {
     CCallHelpers::Call jsEntrypointToWasmEntrypointCall;
 };
 
-Expected<std::unique_ptr<WasmInternalFunction>, String> parseAndCompile(VM&, CompilationContext&, const uint8_t*, size_t, const Signature*, Vector<UnlinkedWasmToWasmCall>&, const ModuleInformation&, const Vector<SignatureIndex>&, unsigned optLevel = 1);
+Expected<std::unique_ptr<WasmInternalFunction>, String> parseAndCompile(VM&, CompilationContext&, const uint8_t*, size_t, const Signature*, Vector<UnlinkedWasmToWasmCall>&, const ModuleInformation&, const Vector<SignatureIndex>&, MemoryMode, unsigned optLevel = 1);
 
 } } // namespace JSC::Wasm
 
index b59676a..c482529 100644 (file)
@@ -102,14 +102,14 @@ static inline const char* makeString(ExternalKind kind)
 }
 
 struct Import {
-    Identifier module;
-    Identifier field;
+    String module;
+    String field;
     ExternalKind kind;
     unsigned kindIndex; // Index in the vector of the corresponding kind.
 };
 
 struct Export {
-    Identifier field;
+    String field;
     ExternalKind kind;
     unsigned kindIndex; // Index in the vector of the corresponding kind.
 };
@@ -260,6 +260,7 @@ struct ModuleInformation {
     }
 
     uint32_t importFunctionCount() const { return importFunctionSignatureIndices.size(); }
+    uint32_t internalFunctionCount() const { return internalFunctionSignatureIndices.size(); }
 
     ~ModuleInformation();
 };
index f2b9411..d420560 100644 (file)
@@ -115,7 +115,7 @@ auto FunctionParser<Context>::parse() -> Result
         WASM_PARSER_FAIL_IF(!parseVarUInt32(numberOfLocals), "can't get Function's number of locals in group ", i);
         WASM_PARSER_FAIL_IF(numberOfLocals == std::numeric_limits<uint32_t>::max(), "Function section's ", i, "th local group count is too big ", numberOfLocals);
         WASM_PARSER_FAIL_IF(!parseValueType(typeOfLocal), "can't get Function local's type in group ", i);
-        WASM_PARSER_FAIL_IF(!m_context.addLocal(typeOfLocal, numberOfLocals), "can't add ", numberOfLocals, " Function locals from group ", i);
+        WASM_TRY_ADD_TO_CONTEXT(addLocal(typeOfLocal, numberOfLocals));
     }
 
     WASM_FAIL_IF_HELPER_FAILS(parseBody());
@@ -267,7 +267,7 @@ auto FunctionParser<Context>::parseExpression(OpType op) -> PartialResult
         uint32_t index;
         ExpressionType result;
         WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for get_local");
-        WASM_PARSER_FAIL_IF(!m_context.getLocal(index, result), "can't get_local at index", index);
+        WASM_TRY_ADD_TO_CONTEXT(getLocal(index, result));
         m_expressionStack.append(result);
         return { };
     }
@@ -304,7 +304,7 @@ auto FunctionParser<Context>::parseExpression(OpType op) -> PartialResult
         WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get set_global's index");
         WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "set_global value");
         WASM_TRY_ADD_TO_CONTEXT(setGlobal(index, value));
-        return m_context.setGlobal(index, value);
+        return { };
     }
 
     case Call: {
index d13d0c6..44da0df 100644 (file)
@@ -45,7 +45,7 @@ class VM;
 namespace Wasm {
 
 // FIXME: We should support other modes. see: https://bugs.webkit.org/show_bug.cgi?id=162693
-enum class MemoryMode {
+enum class MemoryMode : uint8_t {
     BoundsChecking,
     Signaling,
     NumberOfMemoryModes
index 58ad07a..5b33201 100644 (file)
@@ -88,7 +88,7 @@ PinnedRegisterInfo::PinnedRegisterInfo(Vector<PinnedSizeRegisterInfo>&& sizeRegi
 {
 }
 
-MemoryInformation::MemoryInformation(VM& vm, PageCount initial, PageCount maximum, std::optional<MemoryMode> recompileMode, bool isImport)
+MemoryInformation::MemoryInformation(PageCount initial, PageCount maximum, bool isImport)
     : m_initial(initial)
     , m_maximum(maximum)
     , m_isImport(isImport)
@@ -96,28 +96,6 @@ MemoryInformation::MemoryInformation(VM& vm, PageCount initial, PageCount maximu
     RELEASE_ASSERT(!!m_initial);
     RELEASE_ASSERT(!m_maximum || m_maximum >= m_initial);
     ASSERT(!!*this);
-
-    if (!recompileMode) {
-        if (!isImport) {
-            if (maximum && maximum.bytes() == 0) {
-                m_reservedMemory = Memory::create(vm, initial, maximum, MemoryMode::BoundsChecking);
-                RELEASE_ASSERT(m_reservedMemory);
-                RELEASE_ASSERT(m_reservedMemory->maximum());
-                RELEASE_ASSERT(m_reservedMemory->maximum().bytes() == 0);
-                m_mode = m_reservedMemory->mode();
-                return;
-            }
-
-            m_reservedMemory = Memory::create(vm, initial, maximum, MemoryMode::Signaling);
-            if (m_reservedMemory) {
-                ASSERT(!!*m_reservedMemory);
-                m_mode = m_reservedMemory->mode();
-                return;
-            }
-        }
-        m_mode = Memory::lastAllocatedMode();
-    } else
-        m_mode = *recompileMode;
 }
 
 } } // namespace JSC::Wasm
index 248796d..0d0eaed 100644 (file)
@@ -67,22 +67,17 @@ public:
         ASSERT(!*this);
     }
 
-    MemoryInformation(VM&, PageCount initial, PageCount maximum, std::optional<MemoryMode>, bool isImport);
+    MemoryInformation(PageCount initial, PageCount maximum, bool isImport);
 
     PageCount initial() const { return m_initial; }
     PageCount maximum() const { return m_maximum; }
-    bool hasReservedMemory() const { return m_reservedMemory; }
-    RefPtr<Memory> takeReservedMemory() { ASSERT(hasReservedMemory()); return m_reservedMemory.release(); }
-    MemoryMode mode() const { return m_mode; }
     bool isImport() const { return m_isImport; }
 
     explicit operator bool() const { return !!m_initial; }
 
 private:
-    RefPtr<Memory> m_reservedMemory;
     PageCount m_initial { };
     PageCount m_maximum { };
-    MemoryMode m_mode { MemoryMode::BoundsChecking };
     bool m_isImport { false };
 };
 
index 21d1d0c..e736bbf 100644 (file)
@@ -162,11 +162,11 @@ auto ModuleParser::parseImport() -> PartialResult
 
         WASM_PARSER_FAIL_IF(!parseVarUInt32(moduleLen), "can't get ", importNumber, "th Import's module name length");
         WASM_PARSER_FAIL_IF(!consumeUTF8String(moduleString, moduleLen), "can't get ", importNumber, "th Import's module name of length ", moduleLen);
-        imp.module = Identifier::fromString(m_vm, moduleString);
+        imp.module = moduleString;
 
         WASM_PARSER_FAIL_IF(!parseVarUInt32(fieldLen), "can't get ", importNumber, "th Import's field name length in module '", moduleString, "'");
         WASM_PARSER_FAIL_IF(!consumeUTF8String(fieldString, fieldLen), "can't get ", importNumber, "th Import's field name of length ", moduleLen, " in module '", moduleString, "'");
-        imp.field = Identifier::fromString(m_vm, fieldString);
+        imp.field = fieldString;
 
         WASM_PARSER_FAIL_IF(!parseExternalKind(imp.kind), "can't get ", importNumber, "th Import's kind in module '", moduleString, "' field '", fieldString, "'");
         switch (imp.kind) {
@@ -316,7 +316,7 @@ auto ModuleParser::parseMemoryHelper(bool isImport) -> PartialResult
     ASSERT(initialPageCount);
     ASSERT(!maximumPageCount || maximumPageCount >= initialPageCount);
 
-    m_result.module->memory = MemoryInformation(*m_vm, initialPageCount, maximumPageCount, m_mode, isImport);
+    m_result.module->memory = MemoryInformation(initialPageCount, maximumPageCount, isImport);
     return { };
 }
 
@@ -376,7 +376,7 @@ auto ModuleParser::parseExport() -> PartialResult
         WASM_PARSER_FAIL_IF(!consumeUTF8String(fieldString, fieldLen), "can't get ", exportNumber, "th Export's field name of length ", fieldLen);
         WASM_PARSER_FAIL_IF(exportNames.contains(fieldString), "duplicate export: '", fieldString, "'");
         exportNames.add(fieldString);
-        exp.field = Identifier::fromString(m_vm, fieldString);
+        exp.field = fieldString;
 
         WASM_PARSER_FAIL_IF(!parseExternalKind(exp.kind), "can't get ", exportNumber, "th Export's kind, named '", fieldString, "'");
         WASM_PARSER_FAIL_IF(!parseVarUInt32(exp.kindIndex), "can't get ", exportNumber, "th Export's kind index, named '", fieldString, "'");
index c9618b5..50a2893 100644 (file)
@@ -44,9 +44,9 @@ struct ModuleParserResult {
 class ModuleParser : public Parser<ModuleParserResult> {
 public:
 
-    ModuleParser(VM* vm, const uint8_t* sourceBuffer, size_t sourceLength, std::optional<MemoryMode> mode)
+
+    ModuleParser(VM* vm, const uint8_t* sourceBuffer, size_t sourceLength)
         : Parser(vm, sourceBuffer, sourceLength)
-        , m_mode(mode)
     {
     }
 
@@ -66,7 +66,6 @@ private:
     PartialResult WARN_UNUSED_RETURN parseInitExpr(uint8_t&, uint64_t&, Type& initExprType);
 
     ModuleParserResult m_result;
-    std::optional<MemoryMode> m_mode { std::nullopt };
     bool m_hasTable { false };
 };
 
index b654249..cec9e7f 100644 (file)
@@ -42,7 +42,6 @@
 #include <wtf/DataLog.h>
 #include <wtf/Locker.h>
 #include <wtf/MonotonicTime.h>
-#include <wtf/NumberOfCores.h>
 #include <wtf/StdLibExtras.h>
 #include <wtf/SystemTracing.h>
 #include <wtf/text/StringBuilder.h>
 namespace JSC { namespace Wasm {
 
 static const bool verbose = false;
-    
-Plan::Plan(VM* vm, Vector<uint8_t> source)
-    : Plan(vm, source.data(), source.size())
+
+Plan::Plan(VM& vm, ArrayBuffer& source, AsyncWork work, CompletionTask&& task)
+    : Plan(vm, reinterpret_cast<uint8_t*>(source.data()), source.byteLength(), work, WTFMove(task))
+{
+}
+
+Plan::Plan(VM& vm, Vector<uint8_t>& source, AsyncWork work, CompletionTask&& task)
+    : Plan(vm, source.data(), source.size(), work, WTFMove(task))
 {
 }
 
-Plan::Plan(VM* vm, const uint8_t* source, size_t sourceLength)
+Plan::Plan(VM& vm, const uint8_t* source, size_t sourceLength, AsyncWork work, CompletionTask&& task)
     : m_vm(vm)
+    , m_completionTask(task)
     , m_source(source)
     , m_sourceLength(sourceLength)
+    , m_asyncWork(work)
 {
 }
 
-bool Plan::parseAndValidateModule(std::optional<MemoryMode> recompileMode)
+const char* Plan::stateString(State state)
+{
+    switch (state) {
+    case State::Initial: return "Initial";
+    case State::Validated: return "Validated";
+    case State::Prepared: return "Prepared";
+    case State::Compiled: return "Compiled";
+    case State::Completed: return "Completed";
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+void Plan::moveToState(State state)
 {
+    ASSERT(state > m_state);
+    dataLogLnIf(verbose, "moving to state: ", stateString(state), " from state: ", stateString(m_state));
+    m_state = state;
+}
+
+void Plan::fail(const AbstractLocker& locker, String&& errorMessage)
+{
+    dataLogLnIf(verbose, "failing with message: ", errorMessage);
+    m_errorMessage = WTFMove(errorMessage);
+    complete(locker);
+}
+
+bool Plan::parseAndValidateModule()
+{
+    ASSERT(m_state == State::Initial);
+    dataLogLnIf(verbose, "starting validation");
     MonotonicTime startTime;
     if (verbose || Options::reportCompileTimes())
         startTime = MonotonicTime::now();
 
     {
-        ModuleParser moduleParser(m_vm, m_source, m_sourceLength, recompileMode);
+        ModuleParser moduleParser(&m_vm, m_source, m_sourceLength);
         auto parseResult = moduleParser.parse();
         if (!parseResult) {
-            m_errorMessage = parseResult.error();
+            fail(holdLock(m_lock), WTFMove(parseResult.error()));
             return false;
         }
         m_moduleInformation = WTFMove(parseResult->module);
@@ -82,52 +116,48 @@ bool Plan::parseAndValidateModule(std::optional<MemoryMode> recompileMode)
     }
 
     for (unsigned functionIndex = 0; functionIndex < m_functionLocationInBinary.size(); ++functionIndex) {
-        if (verbose)
-            dataLogLn("Processing function starting at: ", m_functionLocationInBinary[functionIndex].start, " and ending at: ", m_functionLocationInBinary[functionIndex].end);
+        dataLogLnIf(verbose, "Processing function starting at: ", m_functionLocationInBinary[functionIndex].start, " and ending at: ", m_functionLocationInBinary[functionIndex].end);
         const uint8_t* functionStart = m_source + m_functionLocationInBinary[functionIndex].start;
         size_t functionLength = m_functionLocationInBinary[functionIndex].end - m_functionLocationInBinary[functionIndex].start;
         ASSERT(functionLength <= m_sourceLength);
         SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
-        const Signature* signature = SignatureInformation::get(m_vm, signatureIndex);
+        const Signature* signature = SignatureInformation::get(&m_vm, signatureIndex);
 
-        auto validationResult = validateFunction(m_vm, functionStart, functionLength, signature, *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices);
+        auto validationResult = validateFunction(&m_vm, functionStart, functionLength, signature, *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices);
         if (!validationResult) {
             if (verbose) {
                 for (unsigned i = 0; i < functionLength; ++i)
                     dataLog(RawPointer(reinterpret_cast<void*>(functionStart[i])), ", ");
                 dataLogLn();
             }
-            m_errorMessage = makeString(validationResult.error(), ", in function at index ", String::number(functionIndex)); // FIXME make this an Expected.
+            fail(holdLock(m_lock), makeString(validationResult.error(), ", in function at index ", String::number(functionIndex))); // FIXME make this an Expected.
             return false;
         }
     }
 
     if (verbose || Options::reportCompileTimes())
         dataLogLn("Took ", (MonotonicTime::now() - startTime).microseconds(), " us to validate module");
+    if (m_asyncWork == Validation)
+        complete(holdLock(m_lock));
+    else
+        moveToState(State::Validated);
     return true;
 }
 
-// We are creating a bunch of threads that touch the main thread's stack. This will make ASAN unhappy.
-// The reason this is OK is that we guarantee that the main thread doesn't continue until all threads
-// that could touch its stack are done executing.
-SUPPRESS_ASAN 
-void Plan::run(std::optional<MemoryMode> recompileMode)
+void Plan::prepare()
 {
-    if (!parseAndValidateModule(recompileMode))
-        return;
+    ASSERT(m_state == State::Validated);
+    dataLogLnIf(verbose, "Starting preparation");
 
     TraceScope traceScope(WebAssemblyCompileStart, WebAssemblyCompileEnd);
 
-    if (recompileMode)
-        ASSERT(m_moduleInformation->memory.mode() == recompileMode);
-
     auto tryReserveCapacity = [this] (auto& vector, size_t size, const char* what) {
         if (UNLIKELY(!vector.tryReserveCapacity(size))) {
             StringBuilder builder;
             builder.appendLiteral("Failed allocating enough space for ");
             builder.appendNumber(size);
             builder.append(what);
-            m_errorMessage = builder.toString();
+            fail(holdLock(m_lock), builder.toString());
             return false;
         }
         return true;
@@ -148,128 +178,149 @@ void Plan::run(std::optional<MemoryMode> recompileMode)
         if (import->kind != ExternalKind::Function)
             continue;
         unsigned importFunctionIndex = m_wasmExitStubs.size();
-        if (verbose)
-            dataLogLn("Processing import function number ", importFunctionIndex, ": ", import->module, ": ", import->field);
+        dataLogLnIf(verbose, "Processing import function number ", importFunctionIndex, ": ", import->module, ": ", import->field);
         SignatureIndex signatureIndex = m_moduleInformation->importFunctionSignatureIndices.at(import->kindIndex);
-        m_wasmExitStubs.uncheckedAppend(exitStubGenerator(m_vm, m_callLinkInfos, signatureIndex, importFunctionIndex));
+        m_wasmExitStubs.uncheckedAppend(exitStubGenerator(&m_vm, m_callLinkInfos, signatureIndex, importFunctionIndex));
     }
 
-    m_currentIndex = 0;
+    moveToState(State::Prepared);
+}
 
-    auto doWork = [this] {
-        while (true) {
-            uint32_t functionIndex;
-            {
-                auto locker = holdLock(m_lock);
-                if (m_currentIndex >= m_functionLocationInBinary.size())
-                    return;
-                functionIndex = m_currentIndex;
-                ++m_currentIndex;
-            }
+// We don't have a semaphore class... and this does kinda interesting things.
+class Plan::ThreadCountHolder {
+public:
+    ThreadCountHolder(Plan& plan)
+        : m_plan(plan)
+    {
+        LockHolder locker(m_plan.m_lock);
+        m_plan.m_numberOfActiveThreads++;
+    }
 
-            const uint8_t* functionStart = m_source + m_functionLocationInBinary[functionIndex].start;
-            size_t functionLength = m_functionLocationInBinary[functionIndex].end - m_functionLocationInBinary[functionIndex].start;
-            ASSERT(functionLength <= m_sourceLength);
-            SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
-            const Signature* signature = SignatureInformation::get(m_vm, signatureIndex);
-            unsigned functionIndexSpace = m_wasmExitStubs.size() + functionIndex;
-            ASSERT_UNUSED(functionIndexSpace, m_moduleInformation->signatureIndexFromFunctionIndexSpace(functionIndexSpace) == signatureIndex);
-            ASSERT(validateFunction(m_vm, functionStart, functionLength, signature, *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices));
-
-            m_unlinkedWasmToWasmCalls[functionIndex] = Vector<UnlinkedWasmToWasmCall>();
-            auto parseAndCompileResult = parseAndCompile(*m_vm, m_compilationContexts[functionIndex], functionStart, functionLength, signature, m_unlinkedWasmToWasmCalls[functionIndex], *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices);
-
-            if (UNLIKELY(!parseAndCompileResult)) {
-                auto locker = holdLock(m_lock);
-                if (!m_errorMessage) {
-                    // Multiple compiles could fail simultaneously. We arbitrarily choose the first.
-                    m_errorMessage = makeString(parseAndCompileResult.error(), ", in function at index ", String::number(functionIndex)); // FIXME make this an Expected.
-                }
-                m_currentIndex = m_functionLocationInBinary.size();
+    ~ThreadCountHolder()
+    {
+        LockHolder locker(m_plan.m_lock);
+        m_plan.m_numberOfActiveThreads--;
 
-                // We will terminate on the next execution.
-                continue; 
-            }
+        if (!m_plan.m_numberOfActiveThreads)
+            m_plan.complete(locker);
+    }
 
-            m_wasmInternalFunctions[functionIndex] = WTFMove(*parseAndCompileResult);
-        }
-    };
+    Plan& m_plan;
+};
 
-    MonotonicTime startTime;
-    if (verbose || Options::reportCompileTimes())
-        startTime = MonotonicTime::now();
+void Plan::compileFunctions()
+{
+    ASSERT(m_state >= State::Prepared);
+    dataLogLnIf(verbose, "Starting compilation");
 
-    uint32_t threadCount = Options::useConcurrentJIT() ? WTF::numberOfProcessorCores() : 1;
-    uint32_t numWorkerThreads = threadCount - 1;
-    Vector<ThreadIdentifier> threads;
-    threads.reserveCapacity(numWorkerThreads);
-    for (uint32_t i = 0; i < numWorkerThreads; i++)
-        threads.uncheckedAppend(createThread("jsc.wasm-b3-compilation.thread", doWork));
+    if (m_state >= State::Compiled)
+        return;
+    ThreadCountHolder holder(*this);
+    while (true) {
+        uint32_t functionIndex;
+        {
+            auto locker = holdLock(m_lock);
+            if (m_currentIndex >= m_functionLocationInBinary.size())
+                return;
+            functionIndex = m_currentIndex;
+            ++m_currentIndex;
+            if (m_currentIndex == m_functionLocationInBinary.size())
+                moveToState(State::Compiled);
+        }
 
-    doWork(); // Let the main thread do some work too.
+        const uint8_t* functionStart = m_source + m_functionLocationInBinary[functionIndex].start;
+        size_t functionLength = m_functionLocationInBinary[functionIndex].end - m_functionLocationInBinary[functionIndex].start;
+        ASSERT(functionLength <= m_sourceLength);
+        SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
+        const Signature* signature = SignatureInformation::get(&m_vm, signatureIndex);
+        unsigned functionIndexSpace = m_wasmExitStubs.size() + functionIndex;
+        ASSERT_UNUSED(functionIndexSpace, m_moduleInformation->signatureIndexFromFunctionIndexSpace(functionIndexSpace) == signatureIndex);
+        ASSERT(validateFunction(&m_vm, functionStart, functionLength, signature, *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices));
+
+        m_unlinkedWasmToWasmCalls[functionIndex] = Vector<UnlinkedWasmToWasmCall>();
+        auto parseAndCompileResult = parseAndCompile(m_vm, m_compilationContexts[functionIndex], functionStart, functionLength, signature, m_unlinkedWasmToWasmCalls[functionIndex], *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices, m_mode);
+
+        if (UNLIKELY(!parseAndCompileResult)) {
+            auto locker = holdLock(m_lock);
+            if (!m_errorMessage) {
+                // 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 = m_functionLocationInBinary.size();
+            return;
+        }
 
-    for (uint32_t i = 0; i < numWorkerThreads; i++)
-        waitForThreadCompletion(threads[i]);
+        m_wasmInternalFunctions[functionIndex] = WTFMove(*parseAndCompileResult);
+    }
+}
 
-    for (uint32_t functionIndex = 0; functionIndex < m_functionLocationInBinary.size(); functionIndex++) {
-        {
-            CompilationContext& context = m_compilationContexts[functionIndex];
-            SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
-            String signatureDescription = SignatureInformation::get(m_vm, signatureIndex)->toString();
+void Plan::complete(const AbstractLocker&)
+{
+    ASSERT(m_state != State::Compiled || m_currentIndex >= m_functionLocationInBinary.size());
+    dataLogLnIf(verbose, "Starting Completion");
+
+    if (m_state == State::Compiled) {
+        for (uint32_t functionIndex = 0; functionIndex < m_functionLocationInBinary.size(); functionIndex++) {
             {
-                LinkBuffer linkBuffer(*m_vm, *context.wasmEntrypointJIT, nullptr);
-                m_wasmInternalFunctions[functionIndex]->wasmEntrypoint.compilation =
+                CompilationContext& context = m_compilationContexts[functionIndex];
+                SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
+                String signatureDescription = SignatureInformation::get(&m_vm, signatureIndex)->toString();
+                {
+                    LinkBuffer linkBuffer(m_vm, *context.wasmEntrypointJIT, nullptr);
+                    m_wasmInternalFunctions[functionIndex]->wasmEntrypoint.compilation =
                     std::make_unique<B3::Compilation>(FINALIZE_CODE(linkBuffer, ("WebAssembly function[%i] %s", functionIndex, signatureDescription.ascii().data())), WTFMove(context.wasmEntrypointByproducts));
-            }
+                }
 
-            {
-                LinkBuffer linkBuffer(*m_vm, *context.jsEntrypointJIT, nullptr);
-                linkBuffer.link(context.jsEntrypointToWasmEntrypointCall, FunctionPtr(m_wasmInternalFunctions[functionIndex]->wasmEntrypoint.compilation->code().executableAddress()));
+                {
+                    LinkBuffer linkBuffer(m_vm, *context.jsEntrypointJIT, nullptr);
+                    linkBuffer.link(context.jsEntrypointToWasmEntrypointCall, FunctionPtr(m_wasmInternalFunctions[functionIndex]->wasmEntrypoint.compilation->code().executableAddress()));
 
-                m_wasmInternalFunctions[functionIndex]->jsToWasmEntrypoint.compilation =
+                    m_wasmInternalFunctions[functionIndex]->jsToWasmEntrypoint.compilation =
                     std::make_unique<B3::Compilation>(FINALIZE_CODE(linkBuffer, ("JavaScript->WebAssembly entrypoint[%i] %s", functionIndex, signatureDescription.ascii().data())), WTFMove(context.jsEntrypointByproducts));
+                }
             }
-        }
-    }
 
-    if (verbose || Options::reportCompileTimes()) {
-        dataLogLn("Took ", (MonotonicTime::now() - startTime).microseconds(),
-            " us to compile and link the module");
-    }
+        }
 
-    // Patch the call sites for each WebAssembly function.
-    for (auto& unlinked : m_unlinkedWasmToWasmCalls) {
-        for (auto& call : unlinked) {
-            void* executableAddress;
-            if (m_moduleInformation->isImportedFunctionFromFunctionIndexSpace(call.functionIndex)) {
-                // FIXME imports could have been linked in B3, instead of generating a patchpoint. This condition should be replaced by a RELEASE_ASSERT. https://bugs.webkit.org/show_bug.cgi?id=166462
-                executableAddress = call.target == UnlinkedWasmToWasmCall::Target::ToJs
+        for (auto& unlinked : m_unlinkedWasmToWasmCalls) {
+            for (auto& call : unlinked) {
+                void* executableAddress;
+                if (m_moduleInformation->isImportedFunctionFromFunctionIndexSpace(call.functionIndex)) {
+                    // FIXME imports could have been linked in B3, instead of generating a patchpoint. This condition should be replaced by a RELEASE_ASSERT. https://bugs.webkit.org/show_bug.cgi?id=166462
+                    executableAddress = call.target == UnlinkedWasmToWasmCall::Target::ToJs
                     ? m_wasmExitStubs.at(call.functionIndex).wasmToJs.code().executableAddress()
                     : m_wasmExitStubs.at(call.functionIndex).wasmToWasm.code().executableAddress();
-            } else {
-                ASSERT(call.target != UnlinkedWasmToWasmCall::Target::ToJs);
-                executableAddress = m_wasmInternalFunctions.at(call.functionIndex - m_wasmExitStubs.size())->wasmEntrypoint.compilation->code().executableAddress();
+                } else {
+                    ASSERT(call.target != UnlinkedWasmToWasmCall::Target::ToJs);
+                    executableAddress = m_wasmInternalFunctions.at(call.functionIndex - m_wasmExitStubs.size())->wasmEntrypoint.compilation->code().executableAddress();
+                }
+                MacroAssembler::repatchCall(call.callLocation, CodeLocationLabel(executableAddress));
             }
-            MacroAssembler::repatchCall(call.callLocation, CodeLocationLabel(executableAddress));
         }
     }
 
-    m_failed = false;
+    if (m_state != State::Completed) {
+        moveToState(State::Completed);
+        m_completionTask(*this);
+        m_completed.notifyAll();
+    }
 }
 
-void Plan::initializeCallees(JSGlobalObject* globalObject, std::function<void(unsigned, JSWebAssemblyCallee*, JSWebAssemblyCallee*)> callback)
+void Plan::waitForCompletion()
 {
-    ASSERT(!failed());
-    for (unsigned internalFunctionIndex = 0; internalFunctionIndex < m_wasmInternalFunctions.size(); ++internalFunctionIndex) {
-        WasmInternalFunction* function = m_wasmInternalFunctions[internalFunctionIndex].get();
-
-        JSWebAssemblyCallee* jsEntrypointCallee = JSWebAssemblyCallee::create(globalObject->vm(), WTFMove(function->jsToWasmEntrypoint));
-        MacroAssembler::repatchPointer(function->jsToWasmCalleeMoveLocation, jsEntrypointCallee);
-
-        JSWebAssemblyCallee* wasmEntrypointCallee = JSWebAssemblyCallee::create(globalObject->vm(), WTFMove(function->wasmEntrypoint));
-        MacroAssembler::repatchPointer(function->wasmCalleeMoveLocation, wasmEntrypointCallee);
+    LockHolder locker(m_lock);
+    if (m_state != State::Completed) {
+        // FIXME: We should have a wait conditionally so we don't have to hold the lock to complete / fail.
+        m_completed.wait(m_lock);
+    }
+}
 
-        callback(internalFunctionIndex, jsEntrypointCallee, wasmEntrypointCallee);
+void Plan::cancel()
+{
+    LockHolder locker(m_lock);
+    if (m_state != State::Completed) {
+        m_currentIndex = m_functionLocationInBinary.size();
+        fail(locker, ASCIILiteral("WebAssembly Plan was canceled. If you see this error message please file a bug at bugs.webkit.org!"));
     }
 }
 
index 3544fd4..9db14ea 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -40,61 +40,96 @@ namespace JSC {
 class CallLinkInfo;
 class JSGlobalObject;
 class JSWebAssemblyCallee;
+class JSPromiseDeferred;
 
 namespace Wasm {
 
-class Plan {
+class Plan : public ThreadSafeRefCounted<Plan> {
 public:
-    JS_EXPORT_PRIVATE Plan(VM*, Vector<uint8_t>);
-    JS_EXPORT_PRIVATE Plan(VM*, const uint8_t*, size_t);
+    static void dontFinalize(Plan&) { }
+    typedef std::function<void(Plan&)> CompletionTask;
+    enum AsyncWork : uint8_t { FullCompile, Validation };
+    // Note: CompletionTask should not hold a reference to the Plan otherwise there will be a reference cycle.
+    Plan(VM&, ArrayBuffer&, AsyncWork, CompletionTask&&);
+    JS_EXPORT_PRIVATE Plan(VM&, Vector<uint8_t>&, AsyncWork, CompletionTask&&);
+    JS_EXPORT_PRIVATE Plan(VM&, const uint8_t*, size_t, AsyncWork, CompletionTask&&);
     JS_EXPORT_PRIVATE ~Plan();
 
-    bool parseAndValidateModule(std::optional<MemoryMode> = std::nullopt);
+    bool parseAndValidateModule();
 
-    JS_EXPORT_PRIVATE void run(std::optional<MemoryMode> = std::nullopt);
+    JS_EXPORT_PRIVATE void prepare();
+    void compileFunctions();
 
-    JS_EXPORT_PRIVATE void initializeCallees(JSGlobalObject*, std::function<void(unsigned, JSWebAssemblyCallee*, JSWebAssemblyCallee*)>);
-
-    bool WARN_UNUSED_RETURN failed() const { return m_failed; }
-    const String& errorMessage() const
-    {
-        RELEASE_ASSERT(failed());
-        return m_errorMessage;
-    }
+    template<typename Functor>
+    void initializeCallees(const Functor&);
 
     Vector<Export>& exports() const
     {
-        RELEASE_ASSERT(!failed());
+        RELEASE_ASSERT(!failed() && !hasWork());
         return m_moduleInformation->exports;
     }
 
     size_t internalFunctionCount() const
     {
-        RELEASE_ASSERT(!failed());
-        return m_wasmInternalFunctions.size();
+        RELEASE_ASSERT(!failed() && !hasWork());
+        return m_functionLocationInBinary.size();
     }
 
     std::unique_ptr<ModuleInformation>&& takeModuleInformation()
     {
-        RELEASE_ASSERT(!failed());
+        RELEASE_ASSERT(!failed() && !hasWork());
         return WTFMove(m_moduleInformation);
     }
 
     Bag<CallLinkInfo>&& takeCallLinkInfos()
     {
-        RELEASE_ASSERT(!failed());
+        RELEASE_ASSERT(!failed() && !hasWork());
         return WTFMove(m_callLinkInfos);
     }
 
     Vector<WasmExitStubs>&& takeWasmExitStubs()
     {
-        RELEASE_ASSERT(!failed());
+        RELEASE_ASSERT(!failed() && !hasWork());
         return WTFMove(m_wasmExitStubs);
     }
 
-    MemoryMode mode() const { return m_moduleInformation->memory.mode(); }
+    void setModeAndPromise(MemoryMode mode, JSPromiseDeferred* promise)
+    {
+        m_mode = mode;
+        m_pendingPromise = promise;
+    }
+    MemoryMode mode() const { return m_mode; }
+    JSPromiseDeferred* pendingPromise() { return m_pendingPromise; }
+    VM& vm() const { return m_vm; }
+
+    enum class State : uint8_t {
+        Initial,
+        Validated,
+        Prepared,
+        Compiled,
+        Completed // We should only move to Completed if we are holding the lock.
+    };
+
+    const String& errorMessage() const { return m_errorMessage; }
+
+    bool WARN_UNUSED_RETURN failed() const { return !errorMessage().isNull(); }
+    bool hasWork() const { return m_state < State::Compiled; }
+    bool hasBeenPrepared() const { return m_state >= State::Prepared; }
+
+    void waitForCompletion();
+    void cancel();
 
 private:
+    class ThreadCountHolder;
+    friend class ThreadCountHolder;
+
+    void complete(const AbstractLocker&);
+
+    void moveToState(State);
+    void fail(const AbstractLocker&, String&& errorMessage);
+
+    const char* stateString(State);
+
     std::unique_ptr<ModuleInformation> m_moduleInformation;
     Vector<FunctionLocationInBinary> m_functionLocationInBinary;
     Vector<SignatureIndex> m_moduleSignatureIndicesToUniquedSignatureIndices;
@@ -103,16 +138,24 @@ private:
     Vector<std::unique_ptr<WasmInternalFunction>> m_wasmInternalFunctions;
     Vector<CompilationContext> m_compilationContexts;
 
-    VM* m_vm;
+    VM& m_vm;
+    JSPromiseDeferred* m_pendingPromise { nullptr };
+    CompletionTask m_completionTask;
+
     Vector<Vector<UnlinkedWasmToWasmCall>> m_unlinkedWasmToWasmCalls;
     const uint8_t* m_source;
     const size_t m_sourceLength;
-    bool m_failed { true };
     String m_errorMessage;
-    uint32_t m_currentIndex;
+    MemoryMode m_mode { MemoryMode::BoundsChecking };
     Lock m_lock;
+    Condition m_completed;
+    State m_state { State::Initial };
+    const AsyncWork m_asyncWork;
+    uint8_t m_numberOfActiveThreads { 0 };
+    uint32_t m_currentIndex { 0 };
 };
 
+
 } } // namespace JSC::Wasm
 
 #endif // ENABLE(WEBASSEMBLY)
diff --git a/Source/JavaScriptCore/wasm/WasmPlanInlines.h b/Source/JavaScriptCore/wasm/WasmPlanInlines.h
new file mode 100644 (file)
index 0000000..4820c12
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * 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 "JSWebAssemblyCallee.h"
+#include "WasmPlan.h"
+
+namespace JSC { namespace Wasm {
+
+template<typename Functor>
+void Plan::initializeCallees(const Functor& callback)
+{
+    ASSERT(!failed());
+    for (unsigned internalFunctionIndex = 0; internalFunctionIndex < m_wasmInternalFunctions.size(); ++internalFunctionIndex) {
+        WasmInternalFunction* function = m_wasmInternalFunctions[internalFunctionIndex].get();
+
+        JSWebAssemblyCallee* jsEntrypointCallee = JSWebAssemblyCallee::create(m_vm, WTFMove(function->jsToWasmEntrypoint));
+        MacroAssembler::repatchPointer(function->jsToWasmCalleeMoveLocation, jsEntrypointCallee);
+
+        JSWebAssemblyCallee* wasmEntrypointCallee = JSWebAssemblyCallee::create(m_vm, WTFMove(function->wasmEntrypoint));
+        MacroAssembler::repatchPointer(function->wasmCalleeMoveLocation, wasmEntrypointCallee);
+
+        callback(internalFunctionIndex, jsEntrypointCallee, wasmEntrypointCallee);
+    }
+}
+
+} } // namespace JSC::Wasm
+
+#endif // ENABLE(WEBASSEMBLY)
index 75d3826..9238594 100644 (file)
@@ -271,7 +271,7 @@ auto Validate::checkBranchTarget(ControlType& target, const ExpressionList& expr
         if (target.signature() == Void)
             return { };
 
-        WASM_VALIDATOR_FAIL_IF(expressionStack.isEmpty(), "branch to block on empty expression stack");
+        WASM_VALIDATOR_FAIL_IF(expressionStack.isEmpty(), target.type() == BlockType::TopLevel ? "branch out of function" : "branch to block", " on empty expression stack, but expected ", target.signature());
         WASM_VALIDATOR_FAIL_IF(target.signature() != expressionStack.last(), "branch's stack type doesn't match block's type");
 
         return { };
diff --git a/Source/JavaScriptCore/wasm/WasmWorklist.cpp b/Source/JavaScriptCore/wasm/WasmWorklist.cpp
new file mode 100644 (file)
index 0000000..9e176f7
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * 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 "WasmWorklist.h"
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "WasmPlan.h"
+
+#include <wtf/NumberOfCores.h>
+
+namespace JSC { namespace Wasm {
+
+static const bool verbose = false;
+
+const char* Worklist::priorityString(Priority priority)
+{
+    switch (priority) {
+    case Priority::Preparation: return "Preparation";
+    case Priority::Shutdown: return "Shutdown";
+    case Priority::Compilation: return "Compilation";
+    case Priority::Synchronous: return "Synchronous";
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+// The Thread class is designed to prevent threads from blocking when there is still work
+// in the queue. Wasm's Plans have some phases, Validiation, Preparation, and Completion,
+// that can only be done by one thread, and another phase, Compilation, that can be done
+// many threads. In order to stop a thread from wasting time we remove any plan that is
+// is currently in a single threaded state from the work queue so other plans can run.
+class Worklist::Thread final : public AutomaticThread {
+public:
+    using Base = AutomaticThread;
+    Thread(const AbstractLocker& locker, Worklist& work)
+        : Base(locker, work.m_lock, work.m_planEnqueued)
+        , worklist(work)
+    {
+
+    }
+
+protected:
+    PollResult poll(const AbstractLocker&) override
+    {
+        auto& queue = worklist.m_queue;
+        synchronize.notifyAll();
+
+        while (!queue.empty()) {
+
+            Priority priority = queue.top().priority;
+            if (priority == Worklist::Priority::Shutdown)
+                return PollResult::Stop;
+
+            element = queue.top();
+            // Only one thread should validate/prepare.
+            if (!queue.top().plan->hasBeenPrepared()) {
+                queue.pop();
+                return PollResult::Work;
+            }
+
+            if (element.plan->hasWork())
+                return PollResult::Work;
+
+            // There must be a another thread linking this plan so we can deque and see if there is other work.
+            queue.pop();
+            element = QueueElement();
+        }
+        return PollResult::Wait;
+    }
+
+    WorkResult work() override
+    {
+        auto complete = [&] () {
+            element = QueueElement();
+            return WorkResult::Continue;
+        };
+
+        Plan* plan = element.plan.get();
+        ASSERT(plan);
+        if (!plan->hasBeenPrepared()) {
+            plan->parseAndValidateModule();
+            if (!plan->hasWork())
+                return complete();
+            
+            plan->prepare();
+
+            LockHolder locker(*worklist.m_lock);
+            element.setToNextPriority();
+            worklist.m_queue.push(element);
+            worklist.m_planEnqueued->notifyAll(locker);
+        }
+
+        // FIXME: this should check in occasionally to see if there are new, higher priority e.g. synchronous, plans that need to be run.
+        // https://bugs.webkit.org/show_bug.cgi?id=170204
+        plan->compileFunctions();
+        ASSERT(!plan->hasWork());
+
+        {
+            LockHolder locker(*worklist.m_lock);
+            auto queue = worklist.m_queue;
+            // Another thread may have removed our plan from the queue already.
+            if (!queue.empty() && queue.top().plan == element.plan)
+                queue.pop();
+        }
+
+        return complete();
+    }
+
+public:
+    Condition synchronize;
+    Worklist& worklist;
+    // We can only modify element when holding the lock. A synchronous compile might look at each thread's tasks in order to boost the priority.
+    QueueElement element;
+};
+
+void Worklist::QueueElement::setToNextPriority()
+{
+    switch (priority) {
+    case Priority::Preparation:
+        priority = Priority::Compilation;
+        return;
+    case Priority::Synchronous:
+        return;
+    default:
+        break;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+enum class IterateResult { UpdateAndBreak, DropAndContinue, Continue };
+
+template<typename Functor>
+void Worklist::iterate(const AbstractLocker&, const Functor& func)
+{
+    // std::priority_queue does not have an iterator or decrease_key... :( While this is gross, this function
+    // shouldn't be called very often and the queue should be small.
+    // FIXME: we should have our own PriorityQueue that doesn't suck.
+    // https://bugs.webkit.org/show_bug.cgi?id=170145
+    std::vector<QueueElement> elements;
+    while (!m_queue.empty()) {
+        QueueElement element = m_queue.top();
+        m_queue.pop();
+        IterateResult result = func(element);
+        if (result == IterateResult::UpdateAndBreak) {
+            elements.push_back(element);
+            break;
+        }
+
+        if (result == IterateResult::Continue)
+            elements.push_back(element);
+        continue;
+    }
+    
+    for (auto element : elements)
+        m_queue.push(element);
+}
+
+void Worklist::enqueue(Ref<Plan> plan)
+{
+    LockHolder locker(*m_lock);
+
+    if (!ASSERT_DISABLED) {
+        iterate(locker, [&] (QueueElement& element) {
+            ASSERT_UNUSED(element, element.plan.get() != &plan.get());
+            return IterateResult::Continue;
+        });
+    }
+
+    // If we don't have a promise it must be synchronous so we should boost the priority.
+    Priority priority = plan->pendingPromise() ? Priority::Preparation : Priority::Synchronous;
+    dataLogLnIf(verbose, "Enqueuing plan with priority: ", priorityString(priority));
+    m_queue.push({ priority, nextTicket(),  WTFMove(plan) });
+    m_planEnqueued->notifyOne(locker);
+}
+
+void Worklist::completePlanSynchronously(Plan& plan)
+{
+    {
+        LockHolder locker(*m_lock);
+        iterate(locker, [&] (QueueElement& element) {
+            if (element.plan == &plan) {
+                element.priority = Priority::Synchronous;
+                return IterateResult::UpdateAndBreak;
+            }
+            return IterateResult::Continue;
+        });
+
+        for (auto& thread : m_threads) {
+            if (thread->element.plan == &plan)
+                thread->element.priority = Priority::Synchronous;
+        }
+    }
+
+    plan.waitForCompletion();
+}
+
+void Worklist::stopAllPlansForVM(VM& vm)
+{
+    LockHolder locker(*m_lock);
+    iterate(locker, [&] (QueueElement& element) {
+        if (&element.plan->vm() == &vm) {
+            element.plan->cancel();
+            return IterateResult::DropAndContinue;
+        }
+        return IterateResult::Continue;
+    });
+
+    for (auto& thread : m_threads) {
+        if (thread->element.plan && &thread->element.plan->vm() == &vm) {
+            // We don't have to worry about the deadlocking since the thread can't block without clearing the plan and must hold the lock to do so.
+            thread->element.plan->cancel();
+            thread->synchronize.wait(*m_lock);
+        }
+    }
+}
+
+Worklist::Worklist()
+    : m_lock(Box<Lock>::create())
+    , m_planEnqueued(AutomaticThreadCondition::create())
+{
+    unsigned numberOfCompilationThreads = Options::useConcurrentJIT() ? WTF::numberOfProcessorCores() : 1;
+    m_threads.reserveCapacity(numberOfCompilationThreads);
+    LockHolder locker(*m_lock);
+    for (unsigned i = 0; i < numberOfCompilationThreads; i++)
+        m_threads.uncheckedAppend(std::make_unique<Worklist::Thread>(locker, *this));
+}
+
+Worklist::~Worklist()
+{
+    {
+        LockHolder locker(*m_lock);
+        m_queue.push({ Priority::Shutdown, nextTicket(), nullptr });
+        m_planEnqueued->notifyAll(locker);
+    }
+    for (unsigned i = 0; i < m_threads.size(); ++i)
+        m_threads[i]->join();
+}
+
+static Worklist* globalWorklist;
+
+Worklist* existingWorklistOrNull() { return globalWorklist; }
+Worklist& ensureWorklist()
+{
+    static std::once_flag initializeWorklist;
+    std::call_once(initializeWorklist, [] {
+        globalWorklist = new Worklist();
+    });
+    return *globalWorklist;
+}
+
+} } // namespace JSC::Wasm
+
+#endif // ENABLE(WEBASSEMBLY)
diff --git a/Source/JavaScriptCore/wasm/WasmWorklist.h b/Source/JavaScriptCore/wasm/WasmWorklist.h
new file mode 100644 (file)
index 0000000..bcaf1ef
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * 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 "VM.h"
+
+#include <queue>
+
+#include <wtf/AutomaticThread.h>
+#include <wtf/Vector.h>
+
+namespace JSC {
+
+class JSPromiseDeferred;
+
+namespace Wasm {
+
+class Plan;
+
+class Worklist {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    Worklist();
+    ~Worklist();
+
+    JS_EXPORT_PRIVATE void enqueue(Ref<Plan>);
+    void stopAllPlansForVM(VM&);
+
+    JS_EXPORT_PRIVATE void completePlanSynchronously(Plan&);
+
+    void activatePlan(JSPromiseDeferred*, Plan*);
+    void deactivePlan(JSPromiseDeferred*, Plan*);
+
+private:
+    class Thread;
+    friend class Thread;
+
+    typedef uint64_t Ticket;
+    Ticket nextTicket() { return m_lastGrantedTicket++; }
+
+    enum class Priority {
+        Shutdown,
+        Synchronous,
+        Compilation,
+        Preparation
+    };
+    const char* priorityString(Priority);
+
+    struct QueueElement {
+        Priority priority;
+        Ticket ticket;
+        RefPtr<Plan> plan;
+
+        void setToNextPriority();
+    };
+
+    struct Comparator {
+        bool operator()(const QueueElement& left, const QueueElement& right) const
+        {
+            if (left.priority == right.priority)
+                return left.ticket > right.ticket;
+            return left.priority > right.priority;
+        }
+    };
+
+    template<typename Functor>
+    void iterate(const AbstractLocker&, const Functor&);
+
+    Box<Lock> m_lock;
+    RefPtr<AutomaticThreadCondition> m_planEnqueued;
+    // Technically, this could overflow but that's unlikely. Even if it did, we will just compile things of the same
+    // Priority it the wrong order, which isn't wrong, just suboptimal.
+    Ticket m_lastGrantedTicket { 0 };
+    // FIXME: This should use WTF::Vector but WTF::Vector does not support random access iterator.
+    std::priority_queue<QueueElement, std::vector<QueueElement>, Comparator> m_queue;
+    HashMap<JSPromiseDeferred*, Plan*> m_activePlans;
+    Vector<std::unique_ptr<Thread>> m_threads;
+};
+
+Worklist* existingWorklistOrNull();
+JS_EXPORT_PRIVATE Worklist& ensureWorklist();
+
+} } // namespace JSC::Wasm
+
+#endif // ENABLE(WEBASSEMBLY)
index b8777da..a8a3dca 100644 (file)
@@ -60,8 +60,8 @@ public:
     RegisterAtOffsetList* calleeSaveRegisters() { return &m_entrypoint.calleeSaveRegisters; }
 
 private:
-    void finishCreation(VM&, Wasm::Entrypoint&&);
-    JSWebAssemblyCallee(VM&);
+    JS_EXPORT_PRIVATE void finishCreation(VM&, Wasm::Entrypoint&&);
+    JS_EXPORT_PRIVATE JSWebAssemblyCallee(VM&);
 
     Wasm::Entrypoint m_entrypoint;
 };
index ff87116..ae5d921 100644 (file)
 #if ENABLE(WEBASSEMBLY)
 
 #include "JSCInlines.h"
+#include "JSWebAssemblyLinkError.h"
 #include "JSWebAssemblyMemory.h"
 #include "JSWebAssemblyModule.h"
+#include "WasmPlanInlines.h"
 
 namespace JSC {
 
 const ClassInfo JSWebAssemblyCodeBlock::s_info = { "WebAssemblyCodeBlock", nullptr, 0, CREATE_METHOD_TABLE(JSWebAssemblyCodeBlock) };
 
-JSWebAssemblyCodeBlock::JSWebAssemblyCodeBlock(VM& vm, JSWebAssemblyModule* owner, Bag<CallLinkInfo>&& callLinkInfos, Vector<Wasm::WasmExitStubs>&& wasmExitStubs, Wasm::MemoryMode mode, unsigned calleeCount)
+JSWebAssemblyCodeBlock::JSWebAssemblyCodeBlock(VM& vm, JSWebAssemblyModule* owner,  Wasm::MemoryMode mode, Ref<Wasm::Plan>&& plan, unsigned calleeCount)
     : Base(vm, vm.webAssemblyCodeBlockStructure.get())
-    , m_callLinkInfos(WTFMove(callLinkInfos))
-    , m_wasmExitStubs(WTFMove(wasmExitStubs))
+    , m_plan(WTFMove(plan))
     , m_mode(mode)
     , m_calleeCount(calleeCount)
 {
     m_module.set(vm, this, owner);
+    ASSERT(!module()->codeBlock(mode));
+    module()->setCodeBlock(vm, mode, this);
+
     memset(callees(), 0, m_calleeCount * sizeof(WriteBarrier<JSWebAssemblyCallee>) * 2);
 }
 
+void JSWebAssemblyCodeBlock::initialize()
+{
+    if (initialized())
+        return;
+
+    VM& vm = plan().vm();
+    ASSERT(vm.currentThreadIsHoldingAPILock());
+    RELEASE_ASSERT(!plan().hasWork());
+
+    if (plan().failed()) {
+        m_errorMessage = plan().errorMessage();
+        m_plan = nullptr;
+        return;
+    }
+
+    RELEASE_ASSERT(plan().mode() == mode());
+    ASSERT(plan().internalFunctionCount() == m_calleeCount);
+
+    m_callLinkInfos = plan().takeCallLinkInfos();
+    m_wasmExitStubs = plan().takeWasmExitStubs();
+
+    plan().initializeCallees([&] (unsigned calleeIndex, JSWebAssemblyCallee* jsEntrypointCallee, JSWebAssemblyCallee* wasmEntrypointCallee) {
+        setJSEntrypointCallee(vm, calleeIndex, jsEntrypointCallee);
+        setWasmEntrypointCallee(vm, calleeIndex, wasmEntrypointCallee);
+    });
+
+    m_plan = nullptr;
+}
+
 void JSWebAssemblyCodeBlock::destroy(JSCell* cell)
 {
     static_cast<JSWebAssemblyCodeBlock*>(cell)->JSWebAssemblyCodeBlock::~JSWebAssemblyCodeBlock();
 }
 
-bool JSWebAssemblyCodeBlock::isSafeToRun(JSWebAssemblyMemory* memory)
+bool JSWebAssemblyCodeBlock::isSafeToRun(JSWebAssemblyMemory* memory) const
 {
+    if (!runnable())
+        return false;
+
     Wasm::MemoryMode codeMode = mode();
     Wasm::MemoryMode memoryMode = memory->memory().mode();
     switch (codeMode) {
index fe8c3f3..08abdbc 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "JSCell.h"
 #include "JSWebAssemblyCallee.h"
+#include "PromiseDeferredTimer.h"
 #include "UnconditionalFinalizer.h"
 #include "WasmFormat.h"
 #include <wtf/Bag.h>
@@ -39,14 +40,18 @@ namespace JSC {
 class JSWebAssemblyModule;
 class JSWebAssemblyMemory;
 
+namespace Wasm {
+class Plan;
+}
+
 class JSWebAssemblyCodeBlock : public JSCell {
 public:
     typedef JSCell Base;
     static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
 
-    static JSWebAssemblyCodeBlock* create(VM& vm, JSWebAssemblyModule* owner, Bag<CallLinkInfo>&& callLinkInfos, Vector<Wasm::WasmExitStubs>&& exitStubs, Wasm::MemoryMode mode, unsigned calleeCount)
+    static JSWebAssemblyCodeBlock* create(VM& vm, JSWebAssemblyModule* owner, Wasm::MemoryMode mode, Ref<Wasm::Plan>&& plan, unsigned calleeCount)
     {
-        auto* result = new (NotNull, allocateCell<JSWebAssemblyCodeBlock>(vm.heap, allocationSize(calleeCount))) JSWebAssemblyCodeBlock(vm, owner, std::forward<Bag<CallLinkInfo>>(callLinkInfos), std::forward<Vector<Wasm::WasmExitStubs>>(exitStubs), mode, calleeCount);
+        auto* result = new (NotNull, allocateCell<JSWebAssemblyCodeBlock>(vm.heap, allocationSize(calleeCount))) JSWebAssemblyCodeBlock(vm, owner, mode, WTFMove(plan), calleeCount);
         result->finishCreation(vm);
         return result;
     }
@@ -59,7 +64,16 @@ public:
     unsigned functionImportCount() const { return m_wasmExitStubs.size(); }
     Wasm::MemoryMode mode() const { return m_mode; }
     JSWebAssemblyModule* module() const { return m_module.get(); }
-    bool isSafeToRun(JSWebAssemblyMemory*);
+
+    // Don't call intialize directly, this should be called for you, as needed, by JSWebAssemblyInstance::finalizeCreation.
+    void initialize();
+    bool initialized() const { return !m_plan; }
+
+    Wasm::Plan& plan() const { ASSERT(!initialized()); return *m_plan; }
+
+    bool runnable() const { return initialized() && !m_errorMessage; }
+    String& errorMessage() { ASSERT(!runnable()); return m_errorMessage; }
+    bool isSafeToRun(JSWebAssemblyMemory*) const;
 
     JSWebAssemblyCallee* jsEntrypointCalleeFromFunctionIndexSpace(unsigned functionIndexSpace)
     {
@@ -89,10 +103,7 @@ public:
         callees()[calleeIndex + m_calleeCount].set(vm, this, callee);
     }
 
-    WriteBarrier<JSWebAssemblyCallee>* callees()
-    {
-        return bitwise_cast<WriteBarrier<JSWebAssemblyCallee>*>(bitwise_cast<char*>(this) + offsetOfCallees());
-    }
+    WriteBarrier<JSWebAssemblyCallee>* callees() { return bitwise_cast<WriteBarrier<JSWebAssemblyCallee>*>(bitwise_cast<char*>(this) + offsetOfCallees()); }
 
     void* wasmToJsCallStubForImport(unsigned importIndex)
     {
@@ -101,7 +112,7 @@ public:
     }
 
 private:
-    JSWebAssemblyCodeBlock(VM&, JSWebAssemblyModule*, Bag<CallLinkInfo>&&, Vector<Wasm::WasmExitStubs>&&, Wasm::MemoryMode, unsigned calleeCount);
+    JSWebAssemblyCodeBlock(VM&, JSWebAssemblyModule*, Wasm::MemoryMode, Ref<Wasm::Plan>&&, unsigned calleeCount);
     DECLARE_EXPORT_INFO;
     static const bool needsDestruction = true;
     static void destroy(JSCell*);
@@ -125,6 +136,9 @@ private:
     UnconditionalFinalizer m_unconditionalFinalizer;
     Bag<CallLinkInfo> m_callLinkInfos;
     Vector<Wasm::WasmExitStubs> m_wasmExitStubs;
+    // The plan that is compiling this code block.
+    RefPtr<Wasm::Plan> m_plan;
+    String m_errorMessage;
     Wasm::MemoryMode m_mode;
     unsigned m_calleeCount;
 };
index f03310e..578a69f 100644 (file)
@@ -73,6 +73,22 @@ ALWAYS_INLINE uint8_t* getWasmBufferFromValue(ExecState* exec, JSValue value, si
     return arrayBufferView ? static_cast<uint8_t*>(arrayBufferView->vector()) : static_cast<uint8_t*>(arrayBuffer->impl()->data());
 }
 
+ALWAYS_INLINE RefPtr<ArrayBuffer> createSourceBufferFromValue(VM& vm, ExecState* exec, JSValue value)
+{
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+    size_t byteOffset;
+    size_t byteSize;
+    uint8_t* data = getWasmBufferFromValue(exec, value, byteOffset, byteSize);
+    RETURN_IF_EXCEPTION(throwScope, nullptr);
+
+    auto buffer = ArrayBuffer::tryCreate(data + byteOffset, byteSize);
+    if (buffer)
+        return buffer;
+
+    throwException(exec, throwScope, createOutOfMemoryError(exec));
+    return nullptr;
+}
+
 ALWAYS_INLINE bool isWebAssemblyHostFunction(VM& vm, JSObject* object, WebAssemblyFunction*& wasmFunction, WebAssemblyWrapperFunction*& wasmWrapperFunction)
 {
     if (object->inherits(vm, WebAssemblyFunction::info())) {
index e3bd740..e8589f3 100644 (file)
 #include "JSCInlines.h"
 #include "JSModuleEnvironment.h"
 #include "JSModuleNamespaceObject.h"
+#include "JSWebAssemblyHelpers.h"
+#include "JSWebAssemblyLinkError.h"
 #include "JSWebAssemblyMemory.h"
 #include "JSWebAssemblyModule.h"
+#include "WebAssemblyModuleRecord.h"
 #include "WebAssemblyToJSCallee.h"
 #include <wtf/StdLibExtras.h>
 
 namespace JSC {
 
-void JSWebAssemblyInstance::setMemory(VM& vm, ExecState* exec, JSWebAssemblyMemory* memory)
-{
-    auto scope = DECLARE_THROW_SCOPE(vm);
-    // We create stub memories even for modules that should eventually get a memory so we want to avoid recompling there.
-    if (memory->memory()) {
-        auto codeBlock = m_codeBlock->module()->codeBlock(vm, exec, memory);
-        RETURN_IF_EXCEPTION(scope,);
-        m_codeBlock.set(vm, this, codeBlock);
-    }
-    m_memory.set(vm, this, memory);
-}
-
-JSWebAssemblyInstance* JSWebAssemblyInstance::create(VM& vm, Structure* structure, JSWebAssemblyModule* module, JSModuleNamespaceObject* moduleNamespaceObject)
-{
-    // FIXME: These objects could be pretty big we should try to throw OOM here.
-    auto* instance = new (NotNull, allocateCell<JSWebAssemblyInstance>(vm.heap, allocationSize(module->moduleInformation().importFunctionSignatureIndices.size()))) JSWebAssemblyInstance(vm, structure, module->moduleInformation().importFunctionSignatureIndices.size());
-    instance->finishCreation(vm, module, moduleNamespaceObject);
-    return instance;
-}
+const ClassInfo JSWebAssemblyInstance::s_info = { "WebAssembly.Instance", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWebAssemblyInstance) };
 
 Structure* JSWebAssemblyInstance::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
 {
@@ -76,11 +61,11 @@ void JSWebAssemblyInstance::finishCreation(VM& vm, JSWebAssemblyModule* module,
     Base::finishCreation(vm);
     ASSERT(inherits(vm, info()));
 
+    m_module.set(vm, this, module);
     const size_t extraMemorySize = module->moduleInformation().globals.size() * sizeof(Register);
     m_globals = MallocPtr<uint64_t>::malloc(extraMemorySize);
     heap()->reportExtraMemoryAllocated(extraMemorySize);
 
-    m_codeBlock.set(vm, this, module->codeBlock());
     m_moduleNamespaceObject.set(vm, this, moduleNamespaceObject);
     m_callee.set(vm, this, module->callee());
     putDirect(vm, Identifier::fromString(&vm, "exports"), moduleNamespaceObject, None);
@@ -97,6 +82,7 @@ void JSWebAssemblyInstance::visitChildren(JSCell* cell, SlotVisitor& visitor)
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
 
     Base::visitChildren(thisObject, visitor);
+    visitor.append(thisObject->m_module);
     visitor.append(thisObject->m_codeBlock);
     visitor.append(thisObject->m_moduleNamespaceObject);
     visitor.append(thisObject->m_memory);
@@ -104,10 +90,271 @@ void JSWebAssemblyInstance::visitChildren(JSCell* cell, SlotVisitor& visitor)
     visitor.append(thisObject->m_callee);
     visitor.reportExtraMemoryVisited(thisObject->module()->moduleInformation().globals.size());
     for (unsigned i = 0; i < thisObject->m_numImportFunctions; ++i)
-        visitor.append(*thisObject->importFunction(i));
+        visitor.append(thisObject->importFunctions()[i]);
+}
+
+void JSWebAssemblyInstance::addUnitializedCodeBlock(VM& vm, Ref<Wasm::Plan> plan)
+{
+    auto* codeBlock = JSWebAssemblyCodeBlock::create(vm, module(), memoryMode(), WTFMove(plan), module()->moduleInformation().internalFunctionCount());
+    m_codeBlock.set(vm, this, codeBlock);
+    ASSERT(!codeBlock->initialized());
+}
+
+void JSWebAssemblyInstance::finalizeCreation(VM& vm, ExecState* exec)
+{
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    codeBlock()->initialize();
+
+    if (!codeBlock()->runnable()) {
+        throwException(exec, scope, JSWebAssemblyLinkError::create(exec, vm, globalObject()->WebAssemblyLinkErrorStructure(), codeBlock()->errorMessage()));
+        return;
+    }
+    RELEASE_ASSERT(codeBlock()->isSafeToRun(memory()));
+
+    auto* moduleRecord = jsCast<WebAssemblyModuleRecord*>(m_moduleNamespaceObject->moduleRecord());
+    moduleRecord->link(exec, module(), this);
+    RETURN_IF_EXCEPTION(scope, void());
+
+    JSValue startResult = moduleRecord->evaluate(exec);
+    UNUSED_PARAM(startResult);
+    RETURN_IF_EXCEPTION(scope, void());
+}
+
+JSWebAssemblyInstance* JSWebAssemblyInstance::create(VM& vm, ExecState* exec, JSWebAssemblyModule* jsModule, JSObject* importObject, Structure* instanceStructure)
+{
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+    auto* globalObject = exec->lexicalGlobalObject();
+
+    const Wasm::ModuleInformation& moduleInformation = jsModule->moduleInformation();
+
+    auto exception = [&] (JSObject* error) {
+        throwException(exec, throwScope, error);
+        return nullptr;
+    };
+
+    // If the list of module.imports is not empty and Type(importObject) is not Object, a TypeError is thrown.
+    if (moduleInformation.imports.size() && !importObject)
+        return exception(createTypeError(exec, ASCIILiteral("can't make WebAssembly.Instance because there is no imports Object and the WebAssembly.Module requires imports")));
+
+    Identifier moduleKey = Identifier::fromUid(PrivateName(PrivateName::Description, "WebAssemblyInstance"));
+    WebAssemblyModuleRecord* moduleRecord = WebAssemblyModuleRecord::create(exec, vm, globalObject->webAssemblyModuleRecordStructure(), moduleKey, moduleInformation);
+    RETURN_IF_EXCEPTION(throwScope, nullptr);
+
+    JSModuleNamespaceObject* moduleNamespace = moduleRecord->getModuleNamespace(exec);
+    // FIXME: These objects could be pretty big we should try to throw OOM here.
+    auto* instance = new (NotNull, allocateCell<JSWebAssemblyInstance>(vm.heap, allocationSize(moduleInformation.importFunctionCount()))) JSWebAssemblyInstance(vm, instanceStructure, moduleInformation.importFunctionCount());
+    instance->finishCreation(vm, jsModule, moduleNamespace);
+    RETURN_IF_EXCEPTION(throwScope, nullptr);
+
+    // Let funcs, memories and tables be initially-empty lists of callable JavaScript objects, WebAssembly.Memory objects and WebAssembly.Table objects, respectively.
+    // Let imports be an initially-empty list of external values.
+    unsigned numImportFunctions = 0;
+    unsigned numImportGlobals = 0;
+
+    bool hasMemoryImport = false;
+    bool hasTableImport = false;
+    // For each import i in module.imports:
+    for (auto& import : moduleInformation.imports) {
+        // 1. Let o be the resultant value of performing Get(importObject, i.module_name).
+        JSValue importModuleValue = importObject->get(exec, Identifier::fromString(&vm, import.module));
+        RETURN_IF_EXCEPTION(throwScope, nullptr);
+        // 2. If Type(o) is not Object, throw a TypeError.
+        if (!importModuleValue.isObject())
+            return exception(createTypeError(exec, ASCIILiteral("import must be an object"), defaultSourceAppender, runtimeTypeForValue(importModuleValue)));
+
+        // 3. Let v be the value of performing Get(o, i.item_name)
+        JSObject* object = jsCast<JSObject*>(importModuleValue);
+        JSValue value = object->get(exec, Identifier::fromString(&vm, import.field));
+        RETURN_IF_EXCEPTION(throwScope, nullptr);
+
+        switch (import.kind) {
+        case Wasm::ExternalKind::Function: {
+            // 4. If i is a function import:
+            // i. If IsCallable(v) is false, throw a WebAssembly.LinkError.
+            if (!value.isFunction())
+                return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("import function must be callable")));
+
+            JSObject* function = jsCast<JSObject*>(value);
+            // ii. If v is an Exported Function Exotic Object:
+            WebAssemblyFunction* wasmFunction;
+            WebAssemblyWrapperFunction* wasmWrapperFunction;
+            if (isWebAssemblyHostFunction(vm, function, wasmFunction, wasmWrapperFunction)) {
+                // a. If the signature of v does not match the signature of i, throw a WebAssembly.LinkError.
+                Wasm::SignatureIndex importedSignatureIndex;
+                if (wasmFunction)
+                    importedSignatureIndex = wasmFunction->signatureIndex();
+                else {
+                    importedSignatureIndex = wasmWrapperFunction->signatureIndex();
+                    // b. Let closure be v.[[Closure]].
+                    function = wasmWrapperFunction->function();
+                }
+                Wasm::SignatureIndex expectedSignatureIndex = moduleInformation.importFunctionSignatureIndices[import.kindIndex];
+                if (importedSignatureIndex != expectedSignatureIndex)
+                    return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("imported function's signature doesn't match the provided WebAssembly function's signature")));
+            }
+            // iii. Otherwise:
+            // a. Let closure be a new host function of the given signature which calls v by coercing WebAssembly arguments to JavaScript arguments via ToJSValue and returns the result, if any, by coercing via ToWebAssemblyValue.
+            // Note: done as part of Plan compilation.
+            // iv. Append v to funcs.
+            // Note: adding the JSCell to the instance list fulfills closure requirements b. above (the WebAssembly.Instance wil be kept alive) and v. below (the JSFunction).
+
+            ASSERT(numImportFunctions == import.kindIndex);
+            instance->importFunctions()[numImportFunctions++].set(vm, instance, function);
+            // v. Append closure to imports.
+            break;
+        }
+        case Wasm::ExternalKind::Table: {
+            RELEASE_ASSERT(!hasTableImport); // This should be guaranteed by a validation failure.
+            // 7. Otherwise (i is a table import):
+            hasTableImport = true;
+            JSWebAssemblyTable* table = jsDynamicCast<JSWebAssemblyTable*>(vm, value);
+            // i. If v is not a WebAssembly.Table object, throw a WebAssembly.LinkError.
+            if (!table)
+                return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Table import is not an instance of WebAssembly.Table")));
+
+            uint32_t expectedInitial = moduleInformation.tableInformation.initial();
+            uint32_t actualInitial = table->size();
+            if (actualInitial < expectedInitial)
+                return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Table import provided an 'initial' that is too small")));
+
+            if (std::optional<uint32_t> expectedMaximum = moduleInformation.tableInformation.maximum()) {
+                std::optional<uint32_t> actualMaximum = table->maximum();
+                if (!actualMaximum)
+                    return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Table import does not have a 'maximum' but the module requires that it does")));
+                if (*actualMaximum > *expectedMaximum)
+                    return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Imported Table's 'maximum' is larger than the module's expected 'maximum'")));
+            }
+
+            // ii. Append v to tables.
+            // iii. Append v.[[Table]] to imports.
+            instance->m_table.set(vm, instance, table);
+            break;
+        }
+
+        case Wasm::ExternalKind::Memory: {
+            // 6. If i is a memory import:
+            RELEASE_ASSERT(!hasMemoryImport); // This should be guaranteed by a validation failure.
+            RELEASE_ASSERT(moduleInformation.memory);
+            hasMemoryImport = true;
+            JSWebAssemblyMemory* memory = jsDynamicCast<JSWebAssemblyMemory*>(vm, value);
+            // i. If v is not a WebAssembly.Memory object, throw a WebAssembly.LinkError.
+            if (!memory)
+                return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory import is not an instance of WebAssembly.Memory")));
+
+            Wasm::PageCount declaredInitial = moduleInformation.memory.initial();
+            Wasm::PageCount importedInitial = memory->memory().initial();
+            if (importedInitial < declaredInitial)
+                return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory import provided an 'initial' that is smaller than the module's declared 'initial' import memory size")));
+
+            if (Wasm::PageCount declaredMaximum = moduleInformation.memory.maximum()) {
+                Wasm::PageCount importedMaximum = memory->memory().maximum();
+                if (!importedMaximum)
+                    return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory import did not have a 'maximum' but the module requires that it does")));
+
+                if (importedMaximum > declaredMaximum)
+                    return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory import provided a 'maximum' that is larger than the module's declared 'maximum' import memory size")));
+            }
+
+            // ii. Append v to memories.
+            // iii. Append v.[[Memory]] to imports.
+            ASSERT(!instance->m_memory);
+            instance->m_memory.set(vm, instance, memory);
+            RETURN_IF_EXCEPTION(throwScope, nullptr);
+            break;
+        }
+        case Wasm::ExternalKind::Global: {
+            // 5. If i is a global import:
+            // i. If i is not an immutable global, throw a TypeError.
+            ASSERT(moduleInformation.globals[import.kindIndex].mutability == Wasm::Global::Immutable);
+            // ii. If the global_type of i is i64 or Type(v) is not Number, throw a WebAssembly.LinkError.
+            if (moduleInformation.globals[import.kindIndex].type == Wasm::I64)
+                return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("imported global cannot be an i64")));
+            if (!value.isNumber())
+                return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("imported global must be a number")));
+            // iii. Append ToWebAssemblyValue(v) to imports.
+            ASSERT(numImportGlobals == import.kindIndex);
+            switch (moduleInformation.globals[import.kindIndex].type) {
+            case Wasm::I32:
+                instance->setGlobal(numImportGlobals++, value.toInt32(exec));
+                break;
+            case Wasm::F32:
+                instance->setGlobal(numImportGlobals++, bitwise_cast<uint32_t>(value.toFloat(exec)));
+                break;
+            case Wasm::F64:
+                instance->setGlobal(numImportGlobals++, bitwise_cast<uint64_t>(value.asNumber()));
+                break;
+            default:
+                RELEASE_ASSERT_NOT_REACHED();
+            }
+            ASSERT(!throwScope.exception());
+            break;
+        }
+        }
+    }
+
+    {
+        if (!!moduleInformation.memory && moduleInformation.memory.isImport()) {
+            // We should either have a Memory import or we should have thrown an exception.
+            RELEASE_ASSERT(hasMemoryImport);
+        }
+
+        if (moduleInformation.memory && !hasMemoryImport) {
+            RELEASE_ASSERT(!moduleInformation.memory.isImport());
+            // We create a memory when it's a memory definition.
+            RefPtr<Wasm::Memory> memory = Wasm::Memory::create(vm, moduleInformation.memory.initial(), moduleInformation.memory.maximum());
+            if (!memory)
+                return exception(createOutOfMemoryError(exec));
+
+            instance->m_memory.set(vm, instance,
+                JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), memory.releaseNonNull()));
+            RETURN_IF_EXCEPTION(throwScope, nullptr);
+        }
+    }
+
+    {
+        if (!!moduleInformation.tableInformation && moduleInformation.tableInformation.isImport()) {
+            // We should either have a Table import or we should have thrown an exception.
+            RELEASE_ASSERT(hasTableImport);
+        }
+
+        if (!!moduleInformation.tableInformation && !hasTableImport) {
+            RELEASE_ASSERT(!moduleInformation.tableInformation.isImport());
+            // We create a Table when it's a Table definition.
+            JSWebAssemblyTable* table = JSWebAssemblyTable::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyTableStructure(),
+                moduleInformation.tableInformation.initial(), moduleInformation.tableInformation.maximum());
+            // We should always be able to allocate a JSWebAssemblyTable we've defined.
+            // If it's defined to be too large, we should have thrown a validation error.
+            ASSERT(!throwScope.exception());
+            ASSERT(table);
+            instance->m_table.set(vm, instance, table);
+        }
+    }
+    
+    if (!instance->memory()) {
+        // Make sure we have a dummy memory, so that wasm -> wasm thunks avoid checking for a nullptr Memory when trying to set pinned registers.
+        instance->m_memory.set(vm, instance, JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), adoptRef(*(new Wasm::Memory()))));
+    }
+    
+    // Globals
+    {
+        ASSERT(numImportGlobals == moduleInformation.firstInternalGlobal);
+        for (size_t globalIndex = numImportGlobals; globalIndex < moduleInformation.globals.size(); ++globalIndex) {
+            const auto& global = moduleInformation.globals[globalIndex];
+            ASSERT(global.initializationType != Wasm::Global::IsImport);
+            if (global.initializationType == Wasm::Global::FromGlobalImport) {
+                ASSERT(global.initialBitsOrImportNumber < numImportGlobals);
+                instance->setGlobal(globalIndex, instance->loadI64Global(global.initialBitsOrImportNumber));
+            } else
+                instance->setGlobal(globalIndex, global.initialBitsOrImportNumber);
+        }
+    }
+
+    ASSERT(!instance->codeBlock());
+    instance->m_codeBlock.setMayBeNull(vm, instance, jsModule->codeBlock(instance->memoryMode()));
+
+    return instance;
 }
 
-const ClassInfo JSWebAssemblyInstance::s_info = { "WebAssembly.Instance", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWebAssemblyInstance) };
 
 } // namespace JSC
 
index 42aa1b3..cedc94f 100644 (file)
@@ -39,51 +39,34 @@ class JSModuleNamespaceObject;
 class JSWebAssemblyModule;
 class WebAssemblyToJSCallee;
 
+namespace Wasm {
+class Plan;
+}
+
+
 class JSWebAssemblyInstance : public JSDestructibleObject {
 public:
     typedef JSDestructibleObject Base;
 
-
-    static JSWebAssemblyInstance* create(VM&, Structure*, JSWebAssemblyModule*, JSModuleNamespaceObject*);
+    static JSWebAssemblyInstance* create(VM&, ExecState*, JSWebAssemblyModule*, JSObject* importObject, Structure*);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
     DECLARE_INFO;
 
-    JSWebAssemblyModule* module() const
-    {
-        ASSERT(m_codeBlock);
-        return m_codeBlock->module();
-    }
+    JSWebAssemblyCodeBlock* codeBlock() const { return m_codeBlock.get(); }
+    bool initialized() const { return codeBlock() && codeBlock()->initialized(); }
+    void addUnitializedCodeBlock(VM&, Ref<Wasm::Plan>);
+    void finalizeCreation(VM&, ExecState*);
 
-    JSWebAssemblyCodeBlock* codeBlock() const
-    {
-        ASSERT(m_codeBlock);
-        return m_codeBlock.get();
-    }
+    JSWebAssemblyModule* module() const { return m_module.get(); }
 
-    WriteBarrier<JSObject>* importFunction(unsigned idx)
-    {
-        RELEASE_ASSERT(idx < m_numImportFunctions);
-        return &importFunctions()[idx];
-    }
-
-    WriteBarrier<JSObject>* importFunctions()
-    {
-        return bitwise_cast<WriteBarrier<JSObject>*>(bitwise_cast<char*>(this) + offsetOfImportFunctions());
-    }
-
-    void setImportFunction(VM& vm, JSObject* value, unsigned idx)
-    {
-        importFunction(idx)->set(vm, this, value);
-    }
+    JSObject* importFunction(unsigned idx) { RELEASE_ASSERT(idx < m_numImportFunctions); return importFunctions()[idx].get(); }
 
     JSWebAssemblyMemory* memory() { return m_memory.get(); }
-    // Calling this might trigger a recompile.
-    void setMemory(VM&, ExecState*, JSWebAssemblyMemory*);
+    void setMemory(VM& vm, JSWebAssemblyMemory* value) { ASSERT(!memory()); m_memory.set(vm, this, value); }
     Wasm::MemoryMode memoryMode() { return memory()->memory().mode(); }
 
     JSWebAssemblyTable* table() { return m_table.get(); }
-    void setTable(VM& vm, JSWebAssemblyTable* table) { m_table.set(vm, this, table); }
 
     int32_t loadI32Global(unsigned i) const { return m_globals.get()[i]; }
     int64_t loadI64Global(unsigned i) const { return m_globals.get()[i]; }
@@ -91,11 +74,6 @@ public:
     double loadF64Global(unsigned i) const { return bitwise_cast<double>(loadI64Global(i)); }
     void setGlobal(unsigned i, int64_t bits) { m_globals.get()[i] = bits; }
 
-    static size_t offsetOfImportFunction(unsigned idx)
-    {
-        return offsetOfImportFunctions() + sizeof(WriteBarrier<JSCell>) * idx;
-    }
-
     static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_memory); }
     static ptrdiff_t offsetOfTable() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_table); }
     static ptrdiff_t offsetOfCallee() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_callee); }
@@ -115,6 +93,9 @@ protected:
     }
 
 private:
+    WriteBarrier<JSObject>* importFunctions() { return bitwise_cast<WriteBarrier<JSObject>*>(bitwise_cast<char*>(this) + offsetOfImportFunctions()); }
+
+    WriteBarrier<JSWebAssemblyModule> m_module;
     WriteBarrier<JSWebAssemblyCodeBlock> m_codeBlock;
     WriteBarrier<JSModuleNamespaceObject> m_moduleNamespaceObject;
     WriteBarrier<JSWebAssemblyMemory> m_memory;
index 939437e..4e365cf 100644 (file)
@@ -43,35 +43,18 @@ namespace JSC {
 
 const ClassInfo JSWebAssemblyModule::s_info = { "WebAssembly.Module", &Base::s_info, nullptr, CREATE_METHOD_TABLE(JSWebAssemblyModule) };
 
-JSWebAssemblyCodeBlock* JSWebAssemblyModule::buildCodeBlock(VM& vm, ExecState* exec, Wasm::Plan& plan, std::optional<Wasm::MemoryMode> mode)
+JSWebAssemblyModule* JSWebAssemblyModule::createStub(VM& vm, ExecState* exec, Structure* structure, RefPtr<ArrayBuffer>&& source, RefPtr<Wasm::Plan>&& plan)
 {
+    ASSERT(!plan->hasWork());
     auto scope = DECLARE_THROW_SCOPE(vm);
-    // On failure, a new WebAssembly.CompileError is thrown.
-    plan.run(mode);
-    if (plan.failed()) {
-        throwException(exec, scope, createJSWebAssemblyCompileError(exec, vm, plan.errorMessage()));
+    if (plan->failed()) {
+        throwException(exec, scope, JSWebAssemblyCompileError::create(exec, vm, structure->globalObject()->WebAssemblyCompileErrorStructure(), plan->errorMessage()));
         return nullptr;
     }
-    if (mode)
-        ASSERT(*mode == plan.mode());
-
-    unsigned calleeCount = plan.internalFunctionCount();
-    auto* codeBlock = JSWebAssemblyCodeBlock::create(vm, this, plan.takeCallLinkInfos(), plan.takeWasmExitStubs(), plan.mode(), calleeCount);
-
-    plan.initializeCallees(exec->jsCallee()->globalObject(),
-        [&] (unsigned calleeIndex, JSWebAssemblyCallee* jsEntrypointCallee, JSWebAssemblyCallee* wasmEntrypointCallee) {
-            codeBlock->setJSEntrypointCallee(vm, calleeIndex, jsEntrypointCallee);
-            codeBlock->setWasmEntrypointCallee(vm, calleeIndex, wasmEntrypointCallee);
-        });
-    return codeBlock;
-}
-
-JSWebAssemblyModule* JSWebAssemblyModule::create(VM& vm, ExecState* exec, Structure* structure, uint8_t* source, size_t byteSize)
-{
-    auto* instance = new (NotNull, allocateCell<JSWebAssemblyModule>(vm.heap)) JSWebAssemblyModule(vm, structure);
 
-    instance->finishCreation(vm, exec, source, byteSize);
-    return instance;
+    auto* module = new (NotNull, allocateCell<JSWebAssemblyModule>(vm.heap)) JSWebAssemblyModule(vm, structure, WTFMove(source));
+    module->finishCreation(vm, WTFMove(plan));
+    return module;
 }
 
 Structure* JSWebAssemblyModule::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
@@ -79,62 +62,41 @@ Structure* JSWebAssemblyModule::createStructure(VM& vm, JSGlobalObject* globalOb
     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
 }
 
-JSWebAssemblyModule::JSWebAssemblyModule(VM& vm, Structure* structure)
+JSWebAssemblyModule::JSWebAssemblyModule(VM& vm, Structure* structure, RefPtr<ArrayBuffer>&& source)
     : Base(vm, structure)
+    , m_sourceBuffer(source.releaseNonNull())
 {
 }
 
-JSWebAssemblyCodeBlock* JSWebAssemblyModule::codeBlock(VM& vm, ExecState* exec, JSWebAssemblyMemory* memory)
-{
-    Wasm::MemoryMode mode = memory->memory().mode();
-
-    for (unsigned i = 0; i < Wasm::NumberOfMemoryModes; ++i) {
-        if (m_codeBlocks[i] && m_codeBlocks[i]->isSafeToRun(memory))
-            return m_codeBlocks[i].get();
-    }
-
-    ASSERT(!codeBlockFor(mode));
-    auto scope = DECLARE_THROW_SCOPE(vm);
-    // We don't have a code block for this mode, we need to recompile...
-    Wasm::Plan plan(&vm, static_cast<uint8_t*>(m_sourceBuffer->data()), m_sourceBuffer->byteLength());
-
-    auto* codeBlock = buildCodeBlock(vm, exec, plan, mode);
-    RETURN_IF_EXCEPTION(scope, nullptr);
-
-    ASSERT(plan.exports().size() == m_exportSymbolTable->size());
-    if (!ASSERT_DISABLED) {
-        for (auto& exp : plan.exports())
-            ASSERT_UNUSED(exp, m_exportSymbolTable->contains(exp.field.impl()));
-    }
-
-    ASSERT(mode == codeBlock->mode());
-    codeBlockFor(mode).set(vm, this, codeBlock);
-    return codeBlock;
-}
-
-void JSWebAssemblyModule::finishCreation(VM& vm, ExecState* exec, uint8_t* source, size_t byteSize)
+void JSWebAssemblyModule::finishCreation(VM& vm, RefPtr<Wasm::Plan>&& plan)
 {
     Base::finishCreation(vm);
     ASSERT(inherits(vm, info()));
 
-    auto scope = DECLARE_THROW_SCOPE(vm);
-    Wasm::Plan plan(&vm, source, byteSize);
+    std::unique_ptr<Wasm::ModuleInformation> moduleInformation = plan->takeModuleInformation();
+    for (auto& exp : moduleInformation->exports) {
+        ASSERT(exp.field.isSafeToSendToAnotherThread());
+        exp.field = AtomicString(exp.field);
+    }
+    for (auto& imp : moduleInformation->imports) {
+        ASSERT(imp.field.isSafeToSendToAnotherThread());
+        imp.field = AtomicString(imp.field);
+        ASSERT(imp.module.isSafeToSendToAnotherThread());
+        imp.module = AtomicString(imp.module);
+    }
 
-    auto* codeBlock = buildCodeBlock(vm, exec, plan);
-    RETURN_IF_EXCEPTION(scope,);
+    m_moduleInformation = WTFMove(moduleInformation);
 
     // On success, a new WebAssembly.Module object is returned with [[Module]] set to the validated Ast.module.
     SymbolTable* exportSymbolTable = SymbolTable::create(vm);
-    for (auto& exp : plan.exports()) {
+    for (auto& exp : m_moduleInformation->exports) {
         auto offset = exportSymbolTable->takeNextScopeOffset(NoLockingNecessary);
-        exportSymbolTable->set(NoLockingNecessary, exp.field.impl(), SymbolTableEntry(VarOffset(offset)));
+        ASSERT(exp.field.impl()->isAtomic());
+        exportSymbolTable->set(NoLockingNecessary, static_cast<AtomicStringImpl*>(exp.field.impl()), SymbolTableEntry(VarOffset(offset)));
     }
 
-    m_sourceBuffer = ArrayBuffer::create(source, byteSize);
-    m_moduleInformation = plan.takeModuleInformation();
     m_exportSymbolTable.set(vm, this, exportSymbolTable);
     m_callee.set(vm, this, WebAssemblyToJSCallee::create(vm, vm.webAssemblyToJSCalleeStructure.get(), this));
-    codeBlockFor(codeBlock->mode()).set(vm, this, codeBlock);
 }
 
 void JSWebAssemblyModule::destroy(JSCell* cell)
@@ -142,6 +104,11 @@ void JSWebAssemblyModule::destroy(JSCell* cell)
     static_cast<JSWebAssemblyModule*>(cell)->JSWebAssemblyModule::~JSWebAssemblyModule();
 }
 
+void JSWebAssemblyModule::setCodeBlock(VM& vm, Wasm::MemoryMode mode, JSWebAssemblyCodeBlock* codeBlock)
+{
+    m_codeBlocks[static_cast<size_t>(mode)].set(vm, this, codeBlock);
+}
+
 void JSWebAssemblyModule::visitChildren(JSCell* cell, SlotVisitor& visitor)
 {
     JSWebAssemblyModule* thisObject = jsCast<JSWebAssemblyModule*>(cell);
index 0414104..549ae82 100644 (file)
@@ -49,13 +49,12 @@ class JSWebAssemblyModule : public JSDestructibleObject {
 public:
     typedef JSDestructibleObject Base;
 
-    static JSWebAssemblyModule* create(VM&, ExecState*, Structure*, uint8_t* source, size_t byteSize);
+    static JSWebAssemblyModule* createStub(VM&, ExecState*, Structure*, RefPtr<ArrayBuffer>&& source, RefPtr<Wasm::Plan>&&);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
     DECLARE_INFO;
 
     const Wasm::ModuleInformation& moduleInformation() const { return *m_moduleInformation.get(); }
-    RefPtr<Wasm::Memory> takeReservedMemory() { return m_moduleInformation->memory.takeReservedMemory(); }
     SymbolTable* exportSymbolTable() const { return m_exportSymbolTable.get(); }
     Wasm::SignatureIndex signatureIndexFromFunctionIndexSpace(unsigned functionIndexSpace) const
     {
@@ -63,22 +62,22 @@ public:
     }
     WebAssemblyToJSCallee* callee() const { return m_callee.get(); }
 
-    // Returns the code block that this module was originally compiled expecting to use. This won't need to recompile.
-    JSWebAssemblyCodeBlock* codeBlock() { return codeBlockFor(m_moduleInformation->memory.mode()).get(); }
-    // Returns the appropriate code block for the given memory, possibly triggering a recompile.
-    JSWebAssemblyCodeBlock* codeBlock(VM&, ExecState*, JSWebAssemblyMemory*);
+    JSWebAssemblyCodeBlock* codeBlock(Wasm::MemoryMode mode) { return m_codeBlocks[static_cast<size_t>(mode)].get(); }
+
+    ArrayBuffer& source() const { return m_sourceBuffer.get(); }
 
 private:
-    WriteBarrier<JSWebAssemblyCodeBlock>& codeBlockFor(Wasm::MemoryMode mode) { return m_codeBlocks[static_cast<size_t>(mode)]; }
-    JSWebAssemblyCodeBlock* buildCodeBlock(VM&, ExecState*, Wasm::Plan&, std::optional<Wasm::MemoryMode> mode = std::nullopt);
+    friend class JSWebAssemblyCodeBlock;
+
+    void setCodeBlock(VM&, Wasm::MemoryMode, JSWebAssemblyCodeBlock*);
 
-    JSWebAssemblyModule(VM&, Structure*);
-    void finishCreation(VM&, ExecState*, uint8_t* source, size_t byteSize);
+    JSWebAssemblyModule(VM&, Structure*, RefPtr<ArrayBuffer>&&);
+    void finishCreation(VM&, RefPtr<Wasm::Plan>&&);
     static void destroy(JSCell*);
     static void visitChildren(JSCell*, SlotVisitor&);
 
-    RefPtr<ArrayBuffer> m_sourceBuffer;
-    std::unique_ptr<Wasm::ModuleInformation> m_moduleInformation;
+    Ref<ArrayBuffer> m_sourceBuffer;
+    std::unique_ptr<const Wasm::ModuleInformation> m_moduleInformation;
     WriteBarrier<SymbolTable> m_exportSymbolTable;
     WriteBarrier<JSWebAssemblyCodeBlock> m_codeBlocks[Wasm::NumberOfMemoryModes];
     WriteBarrier<WebAssemblyToJSCallee> m_callee;
index 16fc8d2..041dd35 100644 (file)
@@ -37,6 +37,8 @@
 #include "JSWebAssemblyLinkError.h"
 #include "JSWebAssemblyMemory.h"
 #include "JSWebAssemblyModule.h"
+#include "WasmPlan.h"
+#include "WasmWorklist.h"
 #include "WebAssemblyFunction.h"
 #include "WebAssemblyInstancePrototype.h"
 #include "WebAssemblyModuleRecord.h"
@@ -45,8 +47,6 @@
 
 namespace JSC {
 
-static const bool verbose = false;
-
 const ClassInfo WebAssemblyInstanceConstructor::s_info = { "Function", &Base::s_info, &constructorTableWebAssemblyInstance, CREATE_METHOD_TABLE(WebAssemblyInstanceConstructor) };
 
 /* Source for WebAssemblyInstanceConstructor.lut.h
@@ -54,269 +54,52 @@ const ClassInfo WebAssemblyInstanceConstructor::s_info = { "Function", &Base::s_
  @end
  */
 
+using Wasm::Plan;
+
 static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* exec)
 {
     auto& vm = exec->vm();
-    auto throwScope = DECLARE_THROW_SCOPE(vm);
+    auto scope = DECLARE_THROW_SCOPE(vm);
 
     // If moduleObject is not a WebAssembly.Module instance, a TypeError is thrown.
-    JSWebAssemblyModule* jsModule = jsDynamicCast<JSWebAssemblyModule*>(vm, exec->argument(0));
-    if (!jsModule)
-        return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("first argument to WebAssembly.Instance must be a WebAssembly.Module"), defaultSourceAppender, runtimeTypeForValue(exec->argument(0)))));
+    JSWebAssemblyModule* module = jsDynamicCast<JSWebAssemblyModule*>(vm, exec->argument(0));
+    if (!module)
+        return JSValue::encode(throwException(exec, scope, createTypeError(exec, ASCIILiteral("first argument to WebAssembly.Instance must be a WebAssembly.Module"), defaultSourceAppender, runtimeTypeForValue(exec->argument(0)))));
 
     // If the importObject parameter is not undefined and Type(importObject) is not Object, a TypeError is thrown.
     JSValue importArgument = exec->argument(1);
     JSObject* importObject = importArgument.getObject();
     if (!importArgument.isUndefined() && !importObject)
-        return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("second argument to WebAssembly.Instance must be undefined or an Object"), defaultSourceAppender, runtimeTypeForValue(importArgument))));
+        return JSValue::encode(throwException(exec, scope, createTypeError(exec, ASCIILiteral("second argument to WebAssembly.Instance must be undefined or an Object"), defaultSourceAppender, runtimeTypeForValue(importArgument))));
     
     Structure* instanceStructure = InternalFunction::createSubclassStructure(exec, exec->newTarget(), exec->lexicalGlobalObject()->WebAssemblyInstanceStructure());
-    RETURN_IF_EXCEPTION(throwScope, { });
-
-    throwScope.release();
-    return JSValue::encode(WebAssemblyInstanceConstructor::createInstance(exec, jsModule, importObject, instanceStructure));
-}
-
-JSWebAssemblyInstance* WebAssemblyInstanceConstructor::createInstance(ExecState* exec, JSWebAssemblyModule* jsModule, JSObject* importObject, Structure* instanceStructure)
-{
-    auto& vm = exec->vm();
-    auto throwScope = DECLARE_THROW_SCOPE(vm);
-    auto* globalObject = exec->lexicalGlobalObject();
-
-    const Wasm::ModuleInformation& moduleInformation = jsModule->moduleInformation();
-
-    auto exception = [&] (JSObject* error) {
-        throwException(exec, throwScope, error);
-        return nullptr;
-    };
-
-    // If the list of module.imports is not empty and Type(importObject) is not Object, a TypeError is thrown.
-    if (moduleInformation.imports.size() && !importObject)
-        return exception(createTypeError(exec, ASCIILiteral("can't make WebAssembly.Instance because there is no imports Object and the WebAssembly.Module requires imports")));
-
-    Identifier moduleKey = Identifier::fromUid(PrivateName(PrivateName::Description, "WebAssemblyInstance"));
-    WebAssemblyModuleRecord* moduleRecord = WebAssemblyModuleRecord::create(exec, vm, globalObject->webAssemblyModuleRecordStructure(), moduleKey, moduleInformation);
-    RETURN_IF_EXCEPTION(throwScope, nullptr);
-
-
-    JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, instanceStructure, jsModule, moduleRecord->getModuleNamespace(exec));
-    RETURN_IF_EXCEPTION(throwScope, nullptr);
-
-    // Let funcs, memories and tables be initially-empty lists of callable JavaScript objects, WebAssembly.Memory objects and WebAssembly.Table objects, respectively.
-    // Let imports be an initially-empty list of external values.
-    unsigned numImportFunctions = 0;
-    unsigned numImportGlobals = 0;
-
-    bool hasMemoryImport = false;
-    bool hasTableImport = false;
-    // For each import i in module.imports:
-    for (auto& import : moduleInformation.imports) {
-        // 1. Let o be the resultant value of performing Get(importObject, i.module_name).
-        JSValue importModuleValue = importObject->get(exec, import.module);
-        RETURN_IF_EXCEPTION(throwScope, nullptr);
-        // 2. If Type(o) is not Object, throw a TypeError.
-        if (!importModuleValue.isObject())
-            return exception(createTypeError(exec, ASCIILiteral("import must be an object"), defaultSourceAppender, runtimeTypeForValue(importModuleValue)));
-
-        // 3. Let v be the value of performing Get(o, i.item_name)
-        JSObject* object = jsCast<JSObject*>(importModuleValue);
-        JSValue value = object->get(exec, import.field);
-        RETURN_IF_EXCEPTION(throwScope, nullptr);
-
-        switch (import.kind) {
-        case Wasm::ExternalKind::Function: {
-            // 4. If i is a function import:
-            // i. If IsCallable(v) is false, throw a WebAssembly.LinkError.
-            if (!value.isFunction())
-                return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("import function must be callable")));
-
-            JSObject* function = jsCast<JSObject*>(value);
-            // ii. If v is an Exported Function Exotic Object:
-            WebAssemblyFunction* wasmFunction;
-            WebAssemblyWrapperFunction* wasmWrapperFunction;
-            if (isWebAssemblyHostFunction(vm, function, wasmFunction, wasmWrapperFunction)) {
-                // a. If the signature of v does not match the signature of i, throw a WebAssembly.LinkError.
-                Wasm::SignatureIndex importedSignatureIndex;
-                if (wasmFunction)
-                    importedSignatureIndex = wasmFunction->signatureIndex();
-                else {
-                    importedSignatureIndex = wasmWrapperFunction->signatureIndex();
-                    // b. Let closure be v.[[Closure]].
-                    function = wasmWrapperFunction->function();
-                }
-                Wasm::SignatureIndex expectedSignatureIndex = moduleInformation.importFunctionSignatureIndices[import.kindIndex];
-                if (importedSignatureIndex != expectedSignatureIndex)
-                    return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("imported function's signature doesn't match the provided WebAssembly function's signature")));
-            }
-            // iii. Otherwise:
-            // a. Let closure be a new host function of the given signature which calls v by coercing WebAssembly arguments to JavaScript arguments via ToJSValue and returns the result, if any, by coercing via ToWebAssemblyValue.
-            // Note: done as part of Plan compilation.
-            // iv. Append v to funcs.
-            // Note: adding the JSCell to the instance list fulfills closure requirements b. above (the WebAssembly.Instance wil be kept alive) and v. below (the JSFunction).
-
-            ASSERT(numImportFunctions == import.kindIndex);
-            instance->setImportFunction(vm, function, numImportFunctions++);
-            // v. Append closure to imports.
-            break;
-        }
-        case Wasm::ExternalKind::Table: {
-            RELEASE_ASSERT(!hasTableImport); // This should be guaranteed by a validation failure.
-            // 7. Otherwise (i is a table import):
-            hasTableImport = true;
-            JSWebAssemblyTable* table = jsDynamicCast<JSWebAssemblyTable*>(vm, value);
-            // i. If v is not a WebAssembly.Table object, throw a WebAssembly.LinkError.
-            if (!table)
-                return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Table import is not an instance of WebAssembly.Table")));
-
-            uint32_t declaredInitial = moduleInformation.tableInformation.initial();
-            uint32_t importedInitial = table->size();
-            if (importedInitial < declaredInitial)
-                return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Table import provided an 'initial' that is too small")));
-
-            if (std::optional<uint32_t> declaredMaximum = moduleInformation.tableInformation.maximum()) {
-                std::optional<uint32_t> importedMaximum = table->maximum();
-                if (!importedMaximum)
-                    return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Table import does not have a 'maximum' but the module requires that it does")));
-                if (*importedMaximum > *declaredMaximum)
-                    return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Imported Table's 'maximum' is larger than the module's expected 'maximum'")));
-            }
-
-            // ii. Append v to tables.
-            // iii. Append v.[[Table]] to imports.
-            instance->setTable(vm, table);
-            break;
-        }
-        case Wasm::ExternalKind::Memory: {
-            // 6. If i is a memory import:
-            RELEASE_ASSERT(!hasMemoryImport); // This should be guaranteed by a validation failure.
-            RELEASE_ASSERT(moduleInformation.memory);
-            hasMemoryImport = true;
-            JSWebAssemblyMemory* memory = jsDynamicCast<JSWebAssemblyMemory*>(vm, value);
-            // i. If v is not a WebAssembly.Memory object, throw a WebAssembly.LinkError.
-            if (!memory)
-                return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory import is not an instance of WebAssembly.Memory")));
-
-            Wasm::PageCount declaredInitial = moduleInformation.memory.initial();
-            Wasm::PageCount importedInitial = memory->memory().initial();
-            if (importedInitial < declaredInitial)
-                return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory import provided an 'initial' that is smaller than the module's declared 'initial' import memory size")));
-
-            if (Wasm::PageCount declaredMaximum = moduleInformation.memory.maximum()) {
-                Wasm::PageCount importedMaximum = memory->memory().maximum();
-                if (!importedMaximum)
-                    return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory import did not have a 'maximum' but the module requires that it does")));
-
-                if (importedMaximum > declaredMaximum)
-                    return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory import provided a 'maximum' that is larger than the module's declared 'maximum' import memory size")));
-            }
-
-            // ii. Append v to memories.
-            // iii. Append v.[[Memory]] to imports.
-            instance->setMemory(vm, exec, memory);
-            RETURN_IF_EXCEPTION(throwScope, nullptr);
-            break;
-        }
-        case Wasm::ExternalKind::Global: {
-            // 5. If i is a global import:
-            // i. If i is not an immutable global, throw a TypeError.
-            ASSERT(moduleInformation.globals[import.kindIndex].mutability == Wasm::Global::Immutable);
-            // ii. If the global_type of i is i64 or Type(v) is not Number, throw a WebAssembly.LinkError.
-            if (moduleInformation.globals[import.kindIndex].type == Wasm::I64)
-                return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("imported global cannot be an i64")));
-            if (!value.isNumber())
-                return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("imported global must be a number")));
-            // iii. Append ToWebAssemblyValue(v) to imports.
-            ASSERT(numImportGlobals == import.kindIndex);
-            switch (moduleInformation.globals[import.kindIndex].type) {
-            case Wasm::I32:
-                instance->setGlobal(numImportGlobals++, value.toInt32(exec));
-                break;
-            case Wasm::F32:
-                instance->setGlobal(numImportGlobals++, bitwise_cast<uint32_t>(value.toFloat(exec)));
-                break;
-            case Wasm::F64:
-                instance->setGlobal(numImportGlobals++, bitwise_cast<uint64_t>(value.asNumber()));
-                break;
-            default:
-                RELEASE_ASSERT_NOT_REACHED();
-            }
-            ASSERT(!throwScope.exception());
-            break;
-        }
-        }
-    }
-
-    {
-        if (!!moduleInformation.memory && moduleInformation.memory.isImport()) {
-            // We should either have a Memory import or we should have thrown an exception.
-            RELEASE_ASSERT(hasMemoryImport);
-        }
-
-        if (moduleInformation.memory && !hasMemoryImport) {
-            RELEASE_ASSERT(!moduleInformation.memory.isImport());
-            // We create a memory when it's a memory definition.
-            RefPtr<Wasm::Memory> memory;
-            if (moduleInformation.memory.hasReservedMemory())
-                memory = jsModule->takeReservedMemory();
-            else {
-                memory = Wasm::Memory::create(vm, moduleInformation.memory.initial(), moduleInformation.memory.maximum());
-                if (!memory)
-                    return exception(createOutOfMemoryError(exec));
-            }
-            instance->setMemory(vm, exec,
-                JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), memory.releaseNonNull()));
-            RETURN_IF_EXCEPTION(throwScope, nullptr);
+    RETURN_IF_EXCEPTION(scope, { });
+
+    JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, exec, module, importObject, instanceStructure);
+    RETURN_IF_EXCEPTION(scope, { });
+
+    // There are three possible cases:
+    // 1) The instance already has an initialized CodeBlock (runnable), so we just need to finalizeCreation.
+    // 2) The instance has no CodeBlock, so we need to make one and compile the code for it.
+    // 3) The instance already has an uninitialized CodeBlock, so we need to wait for the compilation to finish.
+
+    if (!instance->initialized()) {
+        if (instance->codeBlock())
+            Wasm::ensureWorklist().completePlanSynchronously(instance->codeBlock()->plan());
+        else {
+            Ref<Wasm::Plan> plan = adoptRef(*new Plan(vm, module->source(), Plan::FullCompile, Plan::dontFinalize));
+            plan->setModeAndPromise(instance->memoryMode(), nullptr);
+            instance->addUnitializedCodeBlock(vm, plan.copyRef());
+
+            auto& worklist = Wasm::ensureWorklist();
+            worklist.enqueue(plan.copyRef());
+            worklist.completePlanSynchronously(plan.get());
         }
     }
 
-    {
-        if (!!moduleInformation.tableInformation && moduleInformation.tableInformation.isImport()) {
-            // We should either have a Table import or we should have thrown an exception.
-            RELEASE_ASSERT(hasTableImport);
-        }
-
-        if (!!moduleInformation.tableInformation && !hasTableImport) {
-            RELEASE_ASSERT(!moduleInformation.tableInformation.isImport());
-            // We create a Table when it's a Table definition.
-            JSWebAssemblyTable* table = JSWebAssemblyTable::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyTableStructure(),
-                moduleInformation.tableInformation.initial(), moduleInformation.tableInformation.maximum());
-            // We should always be able to allocate a JSWebAssemblyTable we've defined.
-            // If it's defined to be too large, we should have thrown a validation error.
-            ASSERT(!throwScope.exception());
-            ASSERT(table); 
-            instance->setTable(vm, table);
-        }
-    }
-
-    if (!instance->memory()) {
-        // Make sure we have a dummy memory, so that wasm -> wasm thunks avoid checking for a nullptr Memory when trying to set pinned registers.
-        instance->setMemory(vm, exec, JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), adoptRef(*(new Wasm::Memory()))));
-    }
-
-    // Globals
-    {
-        ASSERT(numImportGlobals == moduleInformation.firstInternalGlobal);
-        for (size_t globalIndex = numImportGlobals; globalIndex < moduleInformation.globals.size(); ++globalIndex) {
-            const auto& global = moduleInformation.globals[globalIndex];
-            ASSERT(global.initializationType != Wasm::Global::IsImport);
-            if (global.initializationType == Wasm::Global::FromGlobalImport) {
-                ASSERT(global.initialBitsOrImportNumber < numImportGlobals);
-                instance->setGlobal(globalIndex, instance->loadI64Global(global.initialBitsOrImportNumber));
-            } else
-                instance->setGlobal(globalIndex, global.initialBitsOrImportNumber);
-        }
-    }
-
-    moduleRecord->link(exec, instance);
-    RETURN_IF_EXCEPTION(throwScope, nullptr);
-
-    if (verbose)
-        moduleRecord->dump();
-    JSValue startResult = moduleRecord->evaluate(exec);
-    UNUSED_PARAM(startResult);
-    RETURN_IF_EXCEPTION(throwScope, nullptr);
-
-    return instance;
+    instance->finalizeCreation(vm, exec);
+    RETURN_IF_EXCEPTION(scope, { });
+    return JSValue::encode(instance);
 }
 
 static EncodedJSValue JSC_HOST_CALL callJSWebAssemblyInstance(ExecState* exec)
index 59709f5..3599622 100644 (file)
@@ -75,13 +75,13 @@ JSValue WebAssemblyModuleConstructor::createModule(ExecState* exec, JSValue buff
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
-    size_t byteOffset;
-    size_t byteSize;
-    uint8_t* base = getWasmBufferFromValue(exec, buffer, byteOffset, byteSize);
+    RefPtr<ArrayBuffer> source = createSourceBufferFromValue(vm, exec, buffer);
     RETURN_IF_EXCEPTION(scope, { });
 
-    scope.release();
-    return JSWebAssemblyModule::create(vm, exec, structure, base + byteOffset, byteSize);
+    RefPtr<Wasm::Plan> plan = adoptRef(new Wasm::Plan(vm, *source, Wasm::Plan::Validation, Wasm::Plan::dontFinalize));
+    if (!plan->parseAndValidateModule())
+        return throwException(exec, scope, JSWebAssemblyCompileError::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyCompileErrorStructure(), plan->errorMessage()));
+    return JSWebAssemblyModule::createStub(vm, exec, structure, WTFMove(source), WTFMove(plan));
 }
 
 WebAssemblyModuleConstructor* WebAssemblyModuleConstructor::create(VM& vm, Structure* structure, WebAssemblyModulePrototype* thisPrototype)
index aa2f376..cea97dc 100644 (file)
@@ -110,8 +110,8 @@ EncodedJSValue JSC_HOST_CALL webAssemblyModuleProtoImports(ExecState* exec)
         for (const Wasm::Import& imp : imports) {
             JSObject* obj = constructEmptyObject(exec);
             RETURN_IF_EXCEPTION(throwScope, { });
-            obj->putDirect(vm, module, jsString(exec, imp.module.string()));
-            obj->putDirect(vm, name, jsString(exec, imp.field.string()));
+            obj->putDirect(vm, module, jsString(exec, imp.module));
+            obj->putDirect(vm, name, jsString(exec, imp.field));
             obj->putDirect(vm, kind, jsString(exec, String(makeString(imp.kind))));
             result->push(exec, obj);
             RETURN_IF_EXCEPTION(throwScope, { });
@@ -141,7 +141,7 @@ EncodedJSValue JSC_HOST_CALL webAssemblyModuleProtoExports(ExecState* exec)
         for (const Wasm::Export& exp : exports) {
             JSObject* obj = constructEmptyObject(exec);
             RETURN_IF_EXCEPTION(throwScope, { });
-            obj->putDirect(vm, name, jsString(exec, exp.field.string()));
+            obj->putDirect(vm, name, jsString(exec, exp.field));
             obj->putDirect(vm, kind, jsString(exec, String(makeString(exp.kind))));
             result->push(exec, obj);
             RETURN_IF_EXCEPTION(throwScope, { });
index fffee5c..fb561d9 100644 (file)
@@ -73,8 +73,10 @@ void WebAssemblyModuleRecord::finishCreation(ExecState* exec, VM& vm, const Wasm
 {
     Base::finishCreation(exec, vm);
     ASSERT(inherits(vm, info()));
-    for (const auto& exp : moduleInformation.exports)
-        addExportEntry(ExportEntry::createLocal(exp.field, exp.field));
+    for (const auto& exp : moduleInformation.exports) {
+        Identifier field = Identifier::fromString(&vm, exp.field);
+        addExportEntry(ExportEntry::createLocal(field, field));
+    }
 }
 
 void WebAssemblyModuleRecord::visitChildren(JSCell* cell, SlotVisitor& visitor)
@@ -85,14 +87,13 @@ void WebAssemblyModuleRecord::visitChildren(JSCell* cell, SlotVisitor& visitor)
     visitor.append(thisObject->m_startFunction);
 }
 
-void WebAssemblyModuleRecord::link(ExecState* exec, JSWebAssemblyInstance* instance)
+void WebAssemblyModuleRecord::link(ExecState* exec, JSWebAssemblyModule* module, JSWebAssemblyInstance* instance)
 {
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
     UNUSED_PARAM(scope);
     auto* globalObject = exec->lexicalGlobalObject();
 
-    JSWebAssemblyModule* module = instance->module();
     JSWebAssemblyCodeBlock* codeBlock = instance->codeBlock();
     const Wasm::ModuleInformation& moduleInformation = module->moduleInformation();
 
@@ -112,7 +113,7 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSWebAssemblyInstance* insta
             //   ii. (Note: At most one wrapper is created for any closure, so func is unique, even if there are multiple occurrances in the list. Moreover, if the item was an import that is already an Exported Function Exotic Object, then the original function object will be found. For imports that are regular JS functions, a new wrapper will be created.)
             if (exp.kindIndex < functionImportCount) {
                 unsigned functionIndex = exp.kindIndex;
-                JSObject* functionImport = instance->importFunction(functionIndex)->get();
+                JSObject* functionImport = instance->importFunction(functionIndex);
                 if (isWebAssemblyHostFunction(vm, functionImport))
                     exportedValue = functionImport;
                 else {
@@ -128,7 +129,7 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSWebAssemblyInstance* insta
                 JSWebAssemblyCallee* wasmEntrypointCallee = codeBlock->wasmEntrypointCalleeFromFunctionIndexSpace(exp.kindIndex);
                 Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(exp.kindIndex);
                 const Wasm::Signature* signature = Wasm::SignatureInformation::get(&vm, signatureIndex);
-                WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->argumentCount(), exp.field.string(), instance, jsEntrypointCallee, wasmEntrypointCallee, signatureIndex);
+                WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->argumentCount(), exp.field, instance, jsEntrypointCallee, wasmEntrypointCallee, signatureIndex);
                 exportedValue = function;
             }
             break;
@@ -179,7 +180,7 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSWebAssemblyInstance* insta
         bool shouldThrowReadOnlyError = false;
         bool ignoreReadOnlyErrors = true;
         bool putResult = false;
-        symbolTablePutTouchWatchpointSet(moduleEnvironment, exec, exp.field, exportedValue, shouldThrowReadOnlyError, ignoreReadOnlyErrors, putResult);
+        symbolTablePutTouchWatchpointSet(moduleEnvironment, exec, Identifier::fromString(&vm, exp.field), exportedValue, shouldThrowReadOnlyError, ignoreReadOnlyErrors, putResult);
         RELEASE_ASSERT(putResult);
     }
 
@@ -192,7 +193,7 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSWebAssemblyInstance* insta
         ASSERT(!signature->argumentCount());
         ASSERT(signature->returnType() == Wasm::Void);
         if (startFunctionIndexSpace < codeBlock->functionImportCount()) {
-            JSObject* startFunction = instance->importFunction(startFunctionIndexSpace)->get();
+            JSObject* startFunction = instance->importFunction(startFunctionIndexSpace);
             m_startFunction.set(vm, this, startFunction);
         } else {
             JSWebAssemblyCallee* jsEntrypointCallee = codeBlock->jsEntrypointCalleeFromFunctionIndexSpace(startFunctionIndexSpace);
@@ -251,7 +252,7 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec)
                 uint32_t functionIndex = element.functionIndices[i];
                 Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(functionIndex);
                 if (functionIndex < codeBlock->functionImportCount()) {
-                    JSObject* functionImport = jsCast<JSObject*>(m_instance->importFunction(functionIndex)->get());
+                    JSObject* functionImport = jsCast<JSObject*>(m_instance->importFunction(functionIndex));
                     if (isWebAssemblyHostFunction(vm, functionImport)) {
                         WebAssemblyFunction* wasmFunction = jsDynamicCast<WebAssemblyFunction*>(vm, functionImport);
                         // If we ever import a WebAssemblyWrapperFunction, we set the import as the unwrapped value.
index 5dceb42..2b8372e 100644 (file)
@@ -33,6 +33,7 @@
 namespace JSC {
 
 class JSWebAssemblyInstance;
+class JSWebAssemblyModule;
 class WebAssemblyFunction;
 
 // Based on the WebAssembly.Instance specification
@@ -47,7 +48,7 @@ public:
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
     static WebAssemblyModuleRecord* create(ExecState*, VM&, Structure*, const Identifier&, const Wasm::ModuleInformation&);
 
-    void link(ExecState*, JSWebAssemblyInstance*);
+    void link(ExecState*, JSWebAssemblyModule*, JSWebAssemblyInstance*);
     JS_EXPORT_PRIVATE JSValue evaluate(ExecState*);
 
 private: