Update Promises to the https://github.com/domenic/promises-unwrapping spec
https://bugs.webkit.org/show_bug.cgi?id=120954
Reviewed by Filip Pizlo.
Source/JavaScriptCore:
Update Promises to the revised spec. Notable changes:
- JSPromiseResolver is gone.
- TaskContext has been renamed Microtask and now has a virtual run() function.
- Instead of using custom InternalFunction subclasses, JSFunctions are used
with PrivateName properties for internal slots.
* CMakeLists.txt:
* DerivedSources.make:
* GNUmakefile.list.am:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
* JavaScriptCore.xcodeproj/project.pbxproj:
* interpreter/CallFrame.h:
(JSC::ExecState::promiseConstructorTable):
* runtime/CommonIdentifiers.cpp:
(JSC::CommonIdentifiers::CommonIdentifiers):
* runtime/CommonIdentifiers.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::reset):
(JSC::JSGlobalObject::visitChildren):
(JSC::JSGlobalObject::queueMicrotask):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::promiseConstructor):
(JSC::JSGlobalObject::promisePrototype):
(JSC::JSGlobalObject::promiseStructure):
* runtime/JSPromise.cpp:
(JSC::JSPromise::create):
(JSC::JSPromise::JSPromise):
(JSC::JSPromise::finishCreation):
(JSC::JSPromise::visitChildren):
(JSC::JSPromise::reject):
(JSC::JSPromise::resolve):
(JSC::JSPromise::appendResolveReaction):
(JSC::JSPromise::appendRejectReaction):
(JSC::triggerPromiseReactions):
* runtime/JSPromise.h:
(JSC::JSPromise::status):
(JSC::JSPromise::result):
(JSC::JSPromise::constructor):
* runtime/JSPromiseCallback.cpp: Removed.
* runtime/JSPromiseCallback.h: Removed.
* runtime/JSPromiseConstructor.cpp:
(JSC::constructPromise):
(JSC::JSPromiseConstructor::getCallData):
(JSC::JSPromiseConstructorFuncCast):
(JSC::JSPromiseConstructorFuncResolve):
(JSC::JSPromiseConstructorFuncReject):
* runtime/JSPromiseConstructor.h:
* runtime/JSPromiseDeferred.cpp: Added.
(JSC::JSPromiseDeferred::create):
(JSC::JSPromiseDeferred::JSPromiseDeferred):
(JSC::JSPromiseDeferred::finishCreation):
(JSC::JSPromiseDeferred::visitChildren):
(JSC::createJSPromiseDeferredFromConstructor):
(JSC::updateDeferredFromPotentialThenable):
* runtime/JSPromiseDeferred.h: Added.
(JSC::JSPromiseDeferred::createStructure):
(JSC::JSPromiseDeferred::promise):
(JSC::JSPromiseDeferred::resolve):
(JSC::JSPromiseDeferred::reject):
* runtime/JSPromiseFunctions.cpp: Added.
(JSC::deferredConstructionFunction):
(JSC::createDeferredConstructionFunction):
(JSC::identifyFunction):
(JSC::createIdentifyFunction):
(JSC::promiseAllCountdownFunction):
(JSC::createPromiseAllCountdownFunction):
(JSC::promiseResolutionHandlerFunction):
(JSC::createPromiseResolutionHandlerFunction):
(JSC::rejectPromiseFunction):
(JSC::createRejectPromiseFunction):
(JSC::resolvePromiseFunction):
(JSC::createResolvePromiseFunction):
(JSC::throwerFunction):
(JSC::createThrowerFunction):
* runtime/JSPromiseFunctions.h: Added.
* runtime/JSPromisePrototype.cpp:
(JSC::JSPromisePrototypeFuncThen):
(JSC::JSPromisePrototypeFuncCatch):
* runtime/JSPromiseReaction.cpp: Added.
(JSC::createExecutePromiseReactionMicroTask):
(JSC::ExecutePromiseReactionMicroTask::run):
(JSC::JSPromiseReaction::create):
(JSC::JSPromiseReaction::JSPromiseReaction):
(JSC::JSPromiseReaction::finishCreation):
(JSC::JSPromiseReaction::visitChildren):
* runtime/JSPromiseReaction.h: Added.
(JSC::JSPromiseReaction::createStructure):
(JSC::JSPromiseReaction::deferred):
(JSC::JSPromiseReaction::handler):
* runtime/JSPromiseResolver.cpp: Removed.
* runtime/JSPromiseResolver.h: Removed.
* runtime/JSPromiseResolverConstructor.cpp: Removed.
* runtime/JSPromiseResolverConstructor.h: Removed.
* runtime/JSPromiseResolverPrototype.cpp: Removed.
* runtime/JSPromiseResolverPrototype.h: Removed.
* runtime/Microtask.h: Added.
* runtime/VM.cpp:
(JSC::VM::VM):
(JSC::VM::~VM):
* runtime/VM.h:
Source/WebCore:
* ForwardingHeaders/runtime/JSPromiseDeferred.h: Added.
* ForwardingHeaders/runtime/JSPromiseResolver.h: Removed.
* bindings/js/JSDOMGlobalObjectTask.cpp:
(WebCore::JSGlobalObjectTask::JSGlobalObjectTask):
* bindings/js/JSDOMGlobalObjectTask.h:
* bindings/js/JSDOMPromise.cpp:
(WebCore::DeferredWrapper::DeferredWrapper):
(WebCore::DeferredWrapper::promise):
(WebCore::DeferredWrapper::resolve):
(WebCore::DeferredWrapper::reject):
* bindings/js/JSDOMPromise.h:
(WebCore::DeferredWrapper::resolve):
(WebCore::DeferredWrapper::reject):
(WebCore::DeferredWrapper::resolve<String>):
(WebCore::DeferredWrapper::resolve<bool>):
(WebCore::char>>):
(WebCore::DeferredWrapper::reject<String>):
* bindings/js/JSDOMWindowBase.cpp:
(WebCore::JSDOMWindowBase::queueTaskToEventLoop):
* bindings/js/JSDOMWindowBase.h:
* bindings/js/JSSubtleCryptoCustom.cpp:
(WebCore::JSSubtleCrypto::encrypt):
(WebCore::JSSubtleCrypto::decrypt):
(WebCore::JSSubtleCrypto::sign):
(WebCore::JSSubtleCrypto::verify):
(WebCore::JSSubtleCrypto::digest):
(WebCore::JSSubtleCrypto::generateKey):
(WebCore::JSSubtleCrypto::importKey):
(WebCore::JSSubtleCrypto::exportKey):
(WebCore::JSSubtleCrypto::wrapKey):
(WebCore::JSSubtleCrypto::unwrapKey):
* bindings/js/JSWorkerGlobalScopeBase.cpp:
(WebCore::JSWorkerGlobalScopeBase::queueTaskToEventLoop):
* bindings/js/JSWorkerGlobalScopeBase.h:
LayoutTests:
* crypto/subtle/argument-conversion.html:
* crypto/subtle/resources/common.js:
* crypto/subtle/sha-1.html:
* crypto/subtle/sha-224.html:
* crypto/subtle/sha-256.html:
* crypto/subtle/sha-384.html:
* crypto/subtle/sha-512.html:
* js/dom/Promise-already-fulfilled-expected.txt: Removed.
* js/dom/Promise-already-fulfilled.html: Removed.
* js/dom/Promise-already-rejected.html:
* js/dom/Promise-already-resolved.html:
* js/dom/Promise-catch-expected.txt:
* js/dom/Promise-catch-in-workers-expected.txt:
* js/dom/Promise-catch.html:
* js/dom/Promise-chain.html:
* js/dom/Promise-exception-expected.txt:
* js/dom/Promise-exception.html:
* js/dom/Promise-expected.txt:
* js/dom/Promise-fulfill-expected.txt: Removed.
* js/dom/Promise-fulfill-in-workers-expected.txt: Removed.
* js/dom/Promise-fulfill-in-workers.html: Removed.
* js/dom/Promise-fulfill.html: Removed.
* js/dom/Promise-init-callback-receiver-expected.txt: Added.
* js/dom/Promise-init-callback-receiver.html: Added.
* js/dom/Promise-init-expected.txt:
* js/dom/Promise-init-in-workers-expected.txt:
* js/dom/Promise-init.html:
* js/dom/Promise-onFulfilled-deep-expected.txt: Added.
* js/dom/Promise-onFulfilled-deep.html: Added.
* js/dom/Promise-onRejected-deep-expected.txt: Added.
* js/dom/Promise-onRejected-deep.html: Added.
* js/dom/Promise-reject.html:
* js/dom/Promise-resolve-chain.html:
* js/dom/Promise-resolve-expected.txt:
* js/dom/Promise-resolve-in-workers-expected.txt:
* js/dom/Promise-resolve-state-expected.txt: Added.
* js/dom/Promise-resolve-state-in-workers-expected.txt: Added.
* js/dom/Promise-resolve-state-in-workers.html: Added.
* js/dom/Promise-resolve-state.html: Added.
* js/dom/Promise-resolve-with-itself-expected.txt: Added.
* js/dom/Promise-resolve-with-itself.html: Added.
* js/dom/Promise-resolve-with-then-exception.html:
* js/dom/Promise-resolve-with-then-fulfill-expected.txt:
* js/dom/Promise-resolve-with-then-fulfill.html:
* js/dom/Promise-resolve-with-then-reject-expected.txt:
* js/dom/Promise-resolve-with-then-reject.html:
* js/dom/Promise-resolve.html:
* js/dom/Promise-simple-expected.txt:
* js/dom/Promise-simple-fulfill-expected.txt: Removed.
* js/dom/Promise-simple-fulfill-inside-callback-expected.txt: Removed.
* js/dom/Promise-simple-fulfill-inside-callback.html: Removed.
* js/dom/Promise-simple-fulfill.html: Removed.
* js/dom/Promise-simple-in-workers-expected.txt:
* js/dom/Promise-simple-resolve-expected.txt: Added.
* js/dom/Promise-simple-resolve.html: Added.
* js/dom/Promise-simple.html:
* js/dom/Promise-static-all-expected.txt: Added.
* js/dom/Promise-static-all.html: Added.
* js/dom/Promise-static-cast-expected.txt: Added.
* js/dom/Promise-static-cast.html: Added.
* js/dom/Promise-static-fulfill-expected.txt: Removed.
* js/dom/Promise-static-fulfill.html: Removed.
* js/dom/Promise-static-race-expected.txt: Added.
* js/dom/Promise-static-race.html: Added.
* js/dom/Promise-static-resolve.html:
* js/dom/Promise-then-callback-receiver-expected.txt: Added.
* js/dom/Promise-then-callback-receiver.html: Added.
* js/dom/Promise-then-expected.txt:
* js/dom/Promise-then-in-workers-expected.txt:
* js/dom/Promise-then-without-callbacks.html:
* js/dom/Promise-then.html:
* js/dom/Promise-types-expected.txt:
* js/dom/Promise-types.html:
* js/dom/Promise.html:
* js/resources/Promise-catch-in-workers.js:
* js/resources/Promise-fulfill-in-workers.js: Removed.
* js/resources/Promise-init-in-workers.js:
* js/resources/Promise-reject-in-workers.js:
* js/resources/Promise-resolve-in-workers.js:
* js/resources/Promise-resolve-state-in-workers.js: Added.
* js/resources/Promise-simple-in-workers.js:
* js/resources/Promise-then-in-workers.js:
* js/resources/Promise-then-without-callbacks-in-workers.js:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@161241 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/runtime/JSPromise.cpp b/Source/JavaScriptCore/runtime/JSPromise.cpp
index 80e17ea..9f182c9 100644
--- a/Source/JavaScriptCore/runtime/JSPromise.cpp
+++ b/Source/JavaScriptCore/runtime/JSPromise.cpp
@@ -31,48 +31,22 @@
#include "Error.h"
#include "JSCJSValueInlines.h"
#include "JSCellInlines.h"
-#include "JSPromiseResolver.h"
+#include "JSPromiseConstructor.h"
+#include "JSPromiseReaction.h"
+#include "Microtask.h"
#include "SlotVisitorInlines.h"
-#include "StrongInlines.h"
#include "StructureInlines.h"
namespace JSC {
-class JSPromiseTaskContext : public TaskContext {
-public:
- static PassRefPtr<JSPromiseTaskContext> create(VM& vm, JSPromise* promise)
- {
- return adoptRef(new JSPromiseTaskContext(vm, promise));
- }
-
- JSPromise& promise() const { return *m_promise.get(); }
-
-private:
- JSPromiseTaskContext(VM& vm, JSPromise* promise)
- {
- m_promise.set(vm, promise);
- }
-
- Strong<JSPromise> m_promise;
-};
+static void triggerPromiseReactions(VM&, JSGlobalObject*, Vector<WriteBarrier<JSPromiseReaction>>&, JSValue);
const ClassInfo JSPromise::s_info = { "Promise", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSPromise) };
-JSPromise* JSPromise::create(VM& vm, Structure* structure)
-{
- JSPromise* promise = new (NotNull, allocateCell<JSPromise>(vm.heap)) JSPromise(vm, structure);
- promise->finishCreation(vm);
- return promise;
-}
-
-JSPromise* JSPromise::createWithResolver(VM& vm, JSGlobalObject* globalObject)
+JSPromise* JSPromise::create(VM& vm, JSGlobalObject* globalObject, JSPromiseConstructor* constructor)
{
JSPromise* promise = new (NotNull, allocateCell<JSPromise>(vm.heap)) JSPromise(vm, globalObject->promiseStructure());
- promise->finishCreation(vm);
-
- JSPromiseResolver* resolver = JSPromiseResolver::create(vm, globalObject->promiseResolverStructure(), promise);
- promise->setResolver(vm, resolver);
-
+ promise->finishCreation(vm, constructor);
return promise;
}
@@ -83,14 +57,16 @@
JSPromise::JSPromise(VM& vm, Structure* structure)
: JSDestructibleObject(vm, structure)
- , m_state(Pending)
+ , m_status(Status::Unresolved)
{
}
-void JSPromise::finishCreation(VM& vm)
+void JSPromise::finishCreation(VM& vm, JSPromiseConstructor* constructor)
{
Base::finishCreation(vm);
ASSERT(inherits(info()));
+
+ m_constructor.set(vm, this, constructor);
}
void JSPromise::destroy(JSCell* cell)
@@ -107,137 +83,87 @@
Base::visitChildren(thisObject, visitor);
- visitor.append(&thisObject->m_resolver);
visitor.append(&thisObject->m_result);
-
- for (size_t i = 0; i < thisObject->m_fulfillCallbacks.size(); ++i)
- visitor.append(&thisObject->m_fulfillCallbacks[i]);
- for (size_t i = 0; i < thisObject->m_rejectCallbacks.size(); ++i)
- visitor.append(&thisObject->m_rejectCallbacks[i]);
+ visitor.append(&thisObject->m_constructor);
+ visitor.append(thisObject->m_resolveReactions.begin(), thisObject->m_resolveReactions.end());
+ visitor.append(thisObject->m_rejectReactions.begin(), thisObject->m_rejectReactions.end());
}
-void JSPromise::setResolver(VM& vm, JSPromiseResolver* resolver)
+void JSPromise::reject(VM& vm, JSValue reason)
{
- m_resolver.set(vm, this, resolver);
-}
-
-JSPromiseResolver* JSPromise::resolver() const
-{
- return m_resolver.get();
-}
-
-void JSPromise::setResult(VM& vm, JSValue result)
-{
- m_result.set(vm, this, result);
-}
-
-JSValue JSPromise::result() const
-{
- return m_result.get();
-}
-
-void JSPromise::setState(JSPromise::State state)
-{
- ASSERT(m_state == Pending);
- m_state = state;
-}
-
-JSPromise::State JSPromise::state() const
-{
- return m_state;
-}
-
-void JSPromise::appendCallbacks(ExecState* exec, InternalFunction* fulfillCallback, InternalFunction* rejectCallback)
-{
- // 1. Append fulfillCallback to promise's fulfill callbacks.
- m_fulfillCallbacks.append(WriteBarrier<InternalFunction>(exec->vm(), this, fulfillCallback));
-
- // 2. Append rejectCallback to promise' reject callbacks.
- m_rejectCallbacks.append(WriteBarrier<InternalFunction>(exec->vm(), this, rejectCallback));
-
- // 3. If promise's state is fulfilled, queue a task to process promise's fulfill callbacks with promise's result.
- if (m_state == Fulfilled) {
- queueTaskToProcessFulfillCallbacks(exec);
+ // 1. If the value of promise's internal slot [[PromiseStatus]] is not "unresolved", return.
+ if (m_status != Status::Unresolved)
return;
- }
- // 4. If promise's state is rejected, queue a task to process promise's reject callbacks with promise's result.
- if (m_state == Rejected) {
- queueTaskToProcessRejectCallbacks(exec);
+ DeferGC deferGC(vm.heap);
+
+ // 2. Let 'reactions' be the value of promise's [[RejectReactions]] internal slot.
+ Vector<WriteBarrier<JSPromiseReaction>> reactions;
+ reactions.swap(m_rejectReactions);
+
+ // 3. Set the value of promise's [[Result]] internal slot to reason.
+ m_result.set(vm, this, reason);
+
+ // 4. Set the value of promise's [[ResolveReactions]] internal slot to undefined.
+ m_resolveReactions.clear();
+
+ // 5. Set the value of promise's [[RejectReactions]] internal slot to undefined.
+ // NOTE: Handled by the swap above.
+
+ // 6. Set the value of promise's [[PromiseStatus]] internal slot to "has-rejection".
+ m_status = Status::HasRejection;
+
+ // 7. Return the result of calling TriggerPromiseReactions(reactions, reason).
+ triggerPromiseReactions(vm, globalObject(), reactions, reason);
+}
+
+void JSPromise::resolve(VM& vm, JSValue resolution)
+{
+ // 1. If the value of promise's internal slot [[PromiseStatus]] is not "unresolved", return.
+ if (m_status != Status::Unresolved)
return;
- }
+
+ DeferGC deferGC(vm.heap);
+
+ // 2. Let 'reactions' be the value of promise's [[ResolveReactions]] internal slot.
+ Vector<WriteBarrier<JSPromiseReaction>> reactions;
+ reactions.swap(m_resolveReactions);
+
+ // 3. Set the value of promise's [[Result]] internal slot to resolution.
+ m_result.set(vm, this, resolution);
+
+ // 4. Set the value of promise's [[ResolveReactions]] internal slot to undefined.
+ // NOTE: Handled by the swap above.
+
+ // 5. Set the value of promise's [[RejectReactions]] internal slot to undefined.
+ m_rejectReactions.clear();
+
+ // 6. Set the value of promise's [[PromiseStatus]] internal slot to "has-resolution".
+ m_status = Status::HasResolution;
+
+ // 7. Return the result of calling TriggerPromiseReactions(reactions, resolution).
+ triggerPromiseReactions(vm, globalObject(), reactions, resolution);
}
-void JSPromise::queueTaskToProcessFulfillCallbacks(ExecState* exec)
+void JSPromise::appendResolveReaction(VM& vm, JSPromiseReaction* reaction)
{
- JSGlobalObject* globalObject = this->globalObject();
- if (globalObject->globalObjectMethodTable()->queueTaskToEventLoop)
- globalObject->globalObjectMethodTable()->queueTaskToEventLoop(globalObject, processFulfillCallbacksForTask, JSPromiseTaskContext::create(exec->vm(), this));
- else
- WTFLogAlways("ERROR: Event loop not supported.");
+ m_resolveReactions.append(WriteBarrier<JSPromiseReaction>(vm, this, reaction));
}
-void JSPromise::queueTaskToProcessRejectCallbacks(ExecState* exec)
+void JSPromise::appendRejectReaction(VM& vm, JSPromiseReaction* reaction)
{
- JSGlobalObject* globalObject = this->globalObject();
- if (globalObject->globalObjectMethodTable()->queueTaskToEventLoop)
- globalObject->globalObjectMethodTable()->queueTaskToEventLoop(globalObject, processRejectCallbacksForTask, JSPromiseTaskContext::create(exec->vm(), this));
- else
- WTFLogAlways("ERROR: Event loop not supported.");
+ m_rejectReactions.append(WriteBarrier<JSPromiseReaction>(vm, this, reaction));
}
-void JSPromise::processFulfillCallbacksForTask(ExecState* exec, TaskContext* taskContext)
+void triggerPromiseReactions(VM& vm, JSGlobalObject* globalObject, Vector<WriteBarrier<JSPromiseReaction>>& reactions, JSValue argument)
{
- JSPromiseTaskContext* promiseTaskContext = static_cast<JSPromiseTaskContext*>(taskContext);
- JSPromise& promise = promiseTaskContext->promise();
-
- promise.processFulfillCallbacksWithValue(exec, promise.result());
-}
-
-void JSPromise::processRejectCallbacksForTask(ExecState* exec, TaskContext* taskContext)
-{
- JSPromiseTaskContext* promiseTaskContext = static_cast<JSPromiseTaskContext*>(taskContext);
- JSPromise& promise = promiseTaskContext->promise();
-
- promise.processRejectCallbacksWithValue(exec, promise.result());
-}
-
-void JSPromise::processFulfillCallbacksWithValue(ExecState* exec, JSValue value)
-{
- ASSERT(m_state == Fulfilled);
-
- for (size_t i = 0; i < m_fulfillCallbacks.size(); ++i) {
- JSValue callback = m_fulfillCallbacks[i].get();
-
- CallData callData;
- CallType callType = JSC::getCallData(callback, callData);
- ASSERT(callType != CallTypeNone);
-
- MarkedArgumentBuffer arguments;
- arguments.append(value);
- call(exec, callback, callType, callData, this, arguments);
+ // 1. Repeat for each reaction in reactions, in original insertion order
+ for (auto& reaction : reactions) {
+ // i. Call QueueMicrotask(ExecutePromiseReaction, (reaction, argument)).
+ globalObject->queueMicrotask(createExecutePromiseReactionMicrotask(vm, reaction.get(), argument));
}
- m_fulfillCallbacks.clear();
-}
-
-void JSPromise::processRejectCallbacksWithValue(ExecState* exec, JSValue value)
-{
- ASSERT(m_state == Rejected);
-
- for (size_t i = 0; i < m_rejectCallbacks.size(); ++i) {
- JSValue callback = m_rejectCallbacks[i].get();
-
- CallData callData;
- CallType callType = JSC::getCallData(callback, callData);
- ASSERT(callType != CallTypeNone);
-
- MarkedArgumentBuffer arguments;
- arguments.append(value);
- call(exec, callback, callType, callData, this, arguments);
- }
-
- m_rejectCallbacks.clear();
+ // 2. Return.
}
} // namespace JSC