Commit 7817ee1e authored by weinig@apple.com's avatar weinig@apple.com

[JS] Implement Promise.all()

https://bugs.webkit.org/show_bug.cgi?id=126510

Reviewed by Gavin Barraclough.

Source/JavaScriptCore: 

Add Promise.all() implementation and factor out performing resolves and rejects
on deferreds to share a bit of code. Also moves the abruptRejection helper to
JSPromiseDeferred so it can be used in JSPromiseFunctions.

* runtime/CommonIdentifiers.h:
* runtime/JSPromiseConstructor.cpp:
(JSC::JSPromiseConstructorFuncCast):
(JSC::JSPromiseConstructorFuncResolve):
(JSC::JSPromiseConstructorFuncReject):
(JSC::JSPromiseConstructorFuncAll):
* runtime/JSPromiseDeferred.cpp:
(JSC::updateDeferredFromPotentialThenable):
(JSC::performDeferredResolve):
(JSC::performDeferredReject):
(JSC::abruptRejection):
* runtime/JSPromiseDeferred.h:
* runtime/JSPromiseFunctions.cpp:
(JSC::promiseAllCountdownFunction):
(JSC::createPromiseAllCountdownFunction):
* runtime/JSPromiseFunctions.h:
* runtime/JSPromiseReaction.cpp:
(JSC::ExecutePromiseReactionMicrotask::run):

LayoutTests: 

Enabled and fix the existing Promise.all() test case.
- Promise.all() and Promise.all({}) should reject by my reading of the spec.
Also removes the Promise.all() shim used by the crypto tests.

* crypto/subtle/resources/common.js:
* js/dom/Promise-static-all-expected.txt:
* js/dom/Promise-static-all.html:


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@161365 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent ba83364c
2014-01-05 Sam Weinig <sam@webkit.org>
[JS] Implement Promise.all()
https://bugs.webkit.org/show_bug.cgi?id=126510
Reviewed by Gavin Barraclough.
Enabled and fix the existing Promise.all() test case.
- Promise.all() and Promise.all({}) should reject by my reading of the spec.
Also removes the Promise.all() shim used by the crypto tests.
* crypto/subtle/resources/common.js:
* js/dom/Promise-static-all-expected.txt:
* js/dom/Promise-static-all.html:
2014-01-06 Zan Dobersek <zdobersek@igalia.com>
Unreviewed GTK gardening.
......@@ -101,24 +101,5 @@ var Base64URL = {
}
};
if (!Promise.all) {
// A very simple temporary implementation only for WebCrypto tests.
Promise.all = function(promises) {
var results = [];
var resultCount = 0;
var resolver;
var rejector;
function next(result) {
results[resultCount++] = result;
if (resultCount < promises.length)
promises[resultCount].then(next);
else
resolver(results);
}
promises[0].then(next, function() { rejector(null) });
return new Promise(function(resolve, reject) { resolver = resolve; rejector = reject; });
}
}
if (!crypto.subtle)
crypto.subtle = crypto.webkitSubtle;
......@@ -2,6 +2,28 @@ Test Promise.all
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS result is undefined
PASS Promise.all() is rejected.
PASS Promise.all([p1, p2, p3]) is fulfilled.
PASS result.length is 3
PASS result[0] is "p1"
PASS result[1] is "p2"
PASS result[2] is "p3"
PASS Promise.all([p1, p6, p5]) is rejected.
PASS result is "p6"
PASS Promise.all([p9]) is fulfilled.
PASS result.length is 1
PASS result[0] is "p2"
PASS Promise.all([p9,,,]) is fulfilled.
PASS result.length is 3
PASS result[0] is "p2"
PASS result[1] is undefined
PASS result[2] is undefined
PASS Promise.all([p9,42]) is fulfilled.
PASS result.length is 2
PASS result[0] is "p2"
PASS result[1] is 42
PASS Promise.all({}) is rejected.
PASS successfullyParsed is true
TEST COMPLETE
......
......@@ -9,7 +9,6 @@
<script>
description('Test Promise.all');
/*
window.jsTestIsAsync = true;
result = undefined;
......@@ -30,11 +29,9 @@ Promise.all([p1, p2, p5]).then(function(result) {
});
Promise.all().then(function(result) {
testPassed('Promise.all() is fulfilled.');
window.result = result;
shouldBe('result.length', '0');
testFailed('Promise.all() is fulfilled.');
}, function() {
testFailed('Promise.all() is rejected.');
testPassed('Promise.all() is rejected.');
}).then(function() {
return Promise.all([p1, p2, p3]).then(function(result) {
testPassed('Promise.all([p1, p2, p3]) is fulfilled.');
......@@ -90,16 +87,13 @@ Promise.all().then(function(result) {
}).then(function() {
// Not iterable object case.
return Promise.all({}).then(function(result) {
testPassed('Promise.all({}) is fulfilled.');
window.result = result;
shouldBe('result.length', '0');
testFailed('Promise.all({}) is fulfilled.');
}, function(result) {
testFailed('Promise.all({}) is rejected.');
testPassed('Promise.all({}) is rejected.');
});
}).then(finishJSTest, finishJSTest);
shouldBe('result', 'undefined');
*/
</script>
<script src="../../resources/js-test-post.js"></script>
......
2014-01-05 Sam Weinig <sam@webkit.org>
[JS] Implement Promise.all()
https://bugs.webkit.org/show_bug.cgi?id=126510
Reviewed by Gavin Barraclough.
Add Promise.all() implementation and factor out performing resolves and rejects
on deferreds to share a bit of code. Also moves the abruptRejection helper to
JSPromiseDeferred so it can be used in JSPromiseFunctions.
* runtime/CommonIdentifiers.h:
* runtime/JSPromiseConstructor.cpp:
(JSC::JSPromiseConstructorFuncCast):
(JSC::JSPromiseConstructorFuncResolve):
(JSC::JSPromiseConstructorFuncReject):
(JSC::JSPromiseConstructorFuncAll):
* runtime/JSPromiseDeferred.cpp:
(JSC::updateDeferredFromPotentialThenable):
(JSC::performDeferredResolve):
(JSC::performDeferredReject):
(JSC::abruptRejection):
* runtime/JSPromiseDeferred.h:
* runtime/JSPromiseFunctions.cpp:
(JSC::promiseAllCountdownFunction):
(JSC::createPromiseAllCountdownFunction):
* runtime/JSPromiseFunctions.h:
* runtime/JSPromiseReaction.cpp:
(JSC::ExecutePromiseReactionMicrotask::run):
2014-01-06 Filip Pizlo <fpizlo@apple.com>
Get rid of ENABLE(VALUE_PROFILER). It's on all the time now.
......
......@@ -212,7 +212,11 @@
macro(reject) \
macro(promise) \
macro(fulfillmentHandler) \
macro(rejectionHandler)
macro(rejectionHandler) \
macro(index) \
macro(values) \
macro(deferred) \
macro(countdownHolder)
namespace JSC {
......
......@@ -152,16 +152,7 @@ ThenableStatus updateDeferredFromPotentialThenable(ExecState* exec, JSValue x, J
JSValue exception = exec->exception();
exec->clearException();
JSValue deferredReject = deferred->reject();
CallData rejectCallData;
CallType rejectCallType = getCallData(deferredReject, rejectCallData);
ASSERT(rejectCallType != CallTypeNone);
MarkedArgumentBuffer rejectArguments;
rejectArguments.append(exception);
call(exec, deferredReject, rejectCallType, rejectCallData, jsUndefined(), rejectArguments);
performDeferredReject(exec, deferred, exception);
// ii. ReturnIfAbrupt(rejectResult).
// NOTE: Nothing to do.
......@@ -196,16 +187,7 @@ ThenableStatus updateDeferredFromPotentialThenable(ExecState* exec, JSValue x, J
JSValue exception = exec->exception();
exec->clearException();
JSValue deferredReject = deferred->reject();
CallData rejectCallData;
CallType rejectCallType = getCallData(deferredReject, rejectCallData);
ASSERT(rejectCallType != CallTypeNone);
MarkedArgumentBuffer rejectArguments;
rejectArguments.append(exception);
call(exec, deferredReject, rejectCallType, rejectCallData, jsUndefined(), rejectArguments);
performDeferredReject(exec, deferred, exception);
// ii. ReturnIfAbrupt(rejectResult).
// NOTE: Nothing to do.
......@@ -214,4 +196,51 @@ ThenableStatus updateDeferredFromPotentialThenable(ExecState* exec, JSValue x, J
return WasAThenable;
}
void performDeferredResolve(ExecState* exec, JSPromiseDeferred* deferred, JSValue argument)
{
JSValue deferredResolve = deferred->resolve();
CallData resolveCallData;
CallType resolveCallType = getCallData(deferredResolve, resolveCallData);
ASSERT(resolveCallType != CallTypeNone);
MarkedArgumentBuffer arguments;
arguments.append(argument);
call(exec, deferredResolve, resolveCallType, resolveCallData, jsUndefined(), arguments);
}
void performDeferredReject(ExecState* exec, JSPromiseDeferred* deferred, JSValue argument)
{
JSValue deferredReject = deferred->reject();
CallData rejectCallData;
CallType rejectCallType = getCallData(deferredReject, rejectCallData);
ASSERT(rejectCallType != CallTypeNone);
MarkedArgumentBuffer arguments;
arguments.append(argument);
call(exec, deferredReject, rejectCallType, rejectCallData, jsUndefined(), arguments);
}
JSValue abruptRejection(ExecState* exec, JSPromiseDeferred* deferred)
{
ASSERT(exec->hadException());
JSValue argument = exec->exception();
exec->clearException();
// i. Let 'rejectResult' be the result of calling the [[Call]] internal method
// of deferred.[[Reject]] with undefined as thisArgument and a List containing
// argument.[[value]] as argumentsList.
performDeferredReject(exec, deferred, argument);
// ii. ReturnIfAbrupt(rejectResult).
if (exec->hadException())
return jsUndefined();
// iii. Return deferred.[[Promise]].
return deferred->promise();
}
} // namespace JSC
......@@ -70,6 +70,11 @@ enum ThenableStatus {
JSValue createJSPromiseDeferredFromConstructor(ExecState*, JSValue constructor);
ThenableStatus updateDeferredFromPotentialThenable(ExecState*, JSValue, JSPromiseDeferred*);
void performDeferredResolve(ExecState*, JSPromiseDeferred*, JSValue argument);
void performDeferredReject(ExecState*, JSPromiseDeferred*, JSValue argument);
JSValue abruptRejection(ExecState*, JSPromiseDeferred*);
} // namespace JSC
#endif // JSPromiseDeferred_h
......@@ -34,6 +34,7 @@
#include "JSPromise.h"
#include "JSPromiseConstructor.h"
#include "JSPromiseDeferred.h"
#include "NumberObject.h"
namespace JSC {
......@@ -71,6 +72,55 @@ JSFunction* createIdentifyFunction(VM& vm, JSGlobalObject* globalObject)
return JSFunction::create(vm, globalObject, 1, ASCIILiteral("IdentityFunction"), identifyFunction);
}
// Promise.All Countdown Functions
static EncodedJSValue JSC_HOST_CALL promiseAllCountdownFunction(ExecState* exec)
{
JSValue x = exec->argument(0);
VM& vm = exec->vm();
JSObject* F = exec->callee();
// 1. Let 'index' be the value of F's [[Index]] internal slot.
uint32_t index = F->get(exec, vm.propertyNames->indexPrivateName).asUInt32();
// 2. Let 'values' be the value of F's [[Values]] internal slot..
JSArray* values = jsCast<JSArray*>(F->get(exec, vm.propertyNames->valuesPrivateName));
// 3. Let 'deferred' be the value of F's [[Deferred]] internal slot.
JSPromiseDeferred* deferred = jsCast<JSPromiseDeferred*>(F->get(exec, vm.propertyNames->deferredPrivateName));
// 4. Let 'countdownHolder' be the value of F's [[CountdownHolder]] internal slot.
NumberObject* countdownHolder = jsCast<NumberObject*>(F->get(exec, vm.propertyNames->countdownHolderPrivateName));
// 5. Let 'result' be the result of calling the [[DefineOwnProperty]] internal method
// of 'values' with arguments 'index' and Property Descriptor { [[Value]]: x,
// [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.
values->putDirectIndex(exec, index, x);
// 6. RejectIfAbrupt(result, deferred).
if (exec->hadException())
abruptRejection(exec, deferred);
// 7. Set countdownHolder.[[Countdown]] to countdownHolder.[[Countdown]] - 1.
uint32_t newCountdownValue = countdownHolder->internalValue().asUInt32() - 1;
countdownHolder->setInternalValue(vm, JSValue(newCountdownValue));
// 8. If countdownHolder.[[Countdown]] is 0,
if (!newCountdownValue) {
// i. Return the result of calling the [[Call]] internal method of deferred.[[Resolve]]
// with undefined as thisArgument and a List containing 'values' as argumentsList.
performDeferredResolve(exec, deferred, values);
}
// 9. Return.
return JSValue::encode(jsUndefined());
}
JSFunction* createPromiseAllCountdownFunction(VM& vm, JSGlobalObject* globalObject)
{
return JSFunction::create(vm, globalObject, 1, ASCIILiteral("PromiseAllCountdownFunction"), promiseAllCountdownFunction);
}
// Promise Resolution Handler Functions
static EncodedJSValue JSC_HOST_CALL promiseResolutionHandlerFunction(ExecState* exec)
......
......@@ -34,6 +34,7 @@ namespace JSC {
JSFunction* createDeferredConstructionFunction(VM&, JSGlobalObject*);
JSFunction* createIdentifyFunction(VM&, JSGlobalObject*);
JSFunction* createPromiseAllCountdownFunction(VM&, JSGlobalObject*);
JSFunction* createPromiseResolutionHandlerFunction(VM&, JSGlobalObject*);
JSFunction* createRejectPromiseFunction(VM&, JSGlobalObject*);
JSFunction* createResolvePromiseFunction(VM&, JSGlobalObject*);
......
......@@ -89,17 +89,7 @@ void ExecutePromiseReactionMicrotask::run(ExecState* exec)
JSValue exception = exec->exception();
exec->clearException();
JSValue deferredReject = deferred->reject();
CallData rejectCallData;
CallType rejectCallType = getCallData(deferredReject, rejectCallData);
ASSERT(rejectCallType != CallTypeNone);
MarkedArgumentBuffer rejectArguments;
rejectArguments.append(exception);
call(exec, deferredReject, rejectCallType, rejectCallData, jsUndefined(), rejectArguments);
// FIXME: Should we return the result somewhere?
performDeferredReject(exec, deferred, exception);
}
// 5. Let 'handlerResult' be handlerResult.[[value]].
......@@ -109,19 +99,10 @@ void ExecutePromiseReactionMicrotask::run(ExecState* exec)
if (sameValue(exec, handlerResult, deferred->promise())) {
// i. Let 'selfResolutionError' be a newly-created TypeError object.
JSObject* selfResolutionError = createTypeError(exec, ASCIILiteral("Resolve a promise with itself"));
// ii. Return the result of calling the [[Call]] internal method of deferred.[[Reject]] passing
// undefined as thisArgument and a List containing selfResolutionError as argumentsList.
JSValue deferredReject = deferred->reject();
CallData rejectCallData;
CallType rejectCallType = getCallData(deferredReject, rejectCallData);
ASSERT(rejectCallType != CallTypeNone);
MarkedArgumentBuffer rejectArguments;
rejectArguments.append(selfResolutionError);
call(exec, deferredReject, rejectCallType, rejectCallData, jsUndefined(), rejectArguments);
// FIXME: Should we return the result somewhere?
performDeferredReject(exec, deferred, selfResolutionError);
}
// 7. Let 'updateResult' be the result of calling UpdateDeferredFromPotentialThenable(handlerResult, deferred).
......@@ -135,18 +116,7 @@ void ExecutePromiseReactionMicrotask::run(ExecState* exec)
if (updateResult == NotAThenable) {
// i. Return the result of calling the [[Call]] internal method of deferred.[[Resolve]]
// passing undefined as thisArgument and a List containing handlerResult as argumentsList.
JSValue deferredResolve = deferred->resolve();
CallData resolveCallData;
CallType resolveCallType = getCallData(deferredResolve, resolveCallData);
ASSERT(resolveCallType != CallTypeNone);
MarkedArgumentBuffer arguments;
arguments.append(handlerResult);
call(exec, deferredResolve, resolveCallType, resolveCallData, jsUndefined(), arguments);
// FIXME: Should we return the result somewhere?
performDeferredResolve(exec, deferred, handlerResult);
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment