]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - runtime/JSPromiseConstructor.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / runtime / JSPromiseConstructor.cpp
index 402619633fa06fd254b3f2a7a8cc71e807aaf911..6895ed82fad8077cd7cfc30441e469104cb2ef2c 100644 (file)
 #if ENABLE(PROMISES)
 
 #include "Error.h"
+#include "Exception.h"
+#include "IteratorOperations.h"
+#include "JSCBuiltins.h"
 #include "JSCJSValueInlines.h"
 #include "JSCellInlines.h"
 #include "JSPromise.h"
-#include "JSPromiseDeferred.h"
-#include "JSPromiseFunctions.h"
 #include "JSPromisePrototype.h"
 #include "Lookup.h"
 #include "NumberObject.h"
@@ -43,22 +44,16 @@ namespace JSC {
 
 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSPromiseConstructor);
 
-static EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncCast(ExecState*);
-static EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncResolve(ExecState*);
-static EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncReject(ExecState*);
-static EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncRace(ExecState*);
-static EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncAll(ExecState*);
 }
 
 #include "JSPromiseConstructor.lut.h"
 
 namespace JSC {
 
-const ClassInfo JSPromiseConstructor::s_info = { "Function", &InternalFunction::s_info, 0, ExecState::promiseConstructorTable, CREATE_METHOD_TABLE(JSPromiseConstructor) };
+const ClassInfo JSPromiseConstructor::s_info = { "Function", &InternalFunction::s_info, &promiseConstructorTable, CREATE_METHOD_TABLE(JSPromiseConstructor) };
 
 /* Source for JSPromiseConstructor.lut.h
 @begin promiseConstructorTable
-  cast            JSPromiseConstructorFuncCast                DontEnum|Function 1
   resolve         JSPromiseConstructorFuncResolve             DontEnum|Function 1
   reject          JSPromiseConstructorFuncReject              DontEnum|Function 1
   race            JSPromiseConstructorFuncRace                DontEnum|Function 1
@@ -85,65 +80,27 @@ JSPromiseConstructor::JSPromiseConstructor(VM& vm, Structure* structure)
 
 void JSPromiseConstructor::finishCreation(VM& vm, JSPromisePrototype* promisePrototype)
 {
-    Base::finishCreation(vm, "Promise");
+    Base::finishCreation(vm, ASCIILiteral("Promise"));
     putDirectWithoutTransition(vm, vm.propertyNames->prototype, promisePrototype, DontEnum | DontDelete | ReadOnly);
     putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete);
 }
 
 static EncodedJSValue JSC_HOST_CALL constructPromise(ExecState* exec)
 {
-    // NOTE: We ignore steps 1-4 as they only matter if you support subclassing, which we do not yet.
-    // 1. Let promise be the this value.
-    // 2. If Type(promise) is not Object, then throw a TypeError exception.
-    // 3. If promise does not have a [[PromiseStatus]] internal slot, then throw a TypeError exception.
-    // 4. If promise's [[PromiseStatus]] internal slot is not undefined, then throw a TypeError exception.
-
-    JSValue resolver = exec->argument(0);
-
-    // 5. IsCallable(resolver) is false, then throw a TypeError exception
-    CallData callData;
-    CallType callType = getCallData(resolver, callData);
-    if (callType == CallTypeNone)
-        return JSValue::encode(throwTypeError(exec, ASCIILiteral("Promise constructor takes a function argument")));
-
     VM& vm = exec->vm();
     JSGlobalObject* globalObject = exec->callee()->globalObject();
 
-    JSPromise* promise = JSPromise::create(vm, globalObject, jsCast<JSPromiseConstructor*>(exec->callee()));
-
-    // NOTE: Steps 6-8 are handled by JSPromise::create().
-    // 6. Set promise's [[PromiseStatus]] internal slot to "unresolved".
-    // 7. Set promise's [[ResolveReactions]] internal slot to a new empty List.
-    // 8. Set promise's [[RejectReactions]] internal slot to a new empty List.
-    
-    // 9. Let 'resolve' be a new built-in function object as defined in Resolve Promise Functions.
-    JSFunction* resolve = createResolvePromiseFunction(vm, globalObject);
-
-    // 10. Set the [[Promise]] internal slot of 'resolve' to 'promise'.
-    resolve->putDirect(vm, vm.propertyNames->promisePrivateName, promise);
-    
-    // 11. Let 'reject' be a new built-in function object as defined in Reject Promise Functions
-    JSFunction* reject = createRejectPromiseFunction(vm, globalObject);
+    JSPromise* promise = JSPromise::create(vm, globalObject);
 
-    // 12. Set the [[Promise]] internal slot of 'reject' to 'promise'.
-    reject->putDirect(vm, vm.propertyNames->promisePrivateName, promise);
+    JSFunction* initializePromise = globalObject->initializePromiseFunction();
+    CallData callData;
+    CallType callType = getCallData(initializePromise, callData);
+    ASSERT(callType != CallTypeNone);
 
-    // 13. Let 'result' be the result of calling the [[Call]] internal method of resolver with
-    //     undefined as thisArgument and a List containing resolve and reject as argumentsList.
     MarkedArgumentBuffer arguments;
-    arguments.append(resolve);
-    arguments.append(reject);
-    call(exec, resolver, callType, callData, jsUndefined(), arguments);
-
-    // 14. If result is an abrupt completion, call PromiseReject(promise, result.[[value]]).
-    if (exec->hadException()) {
-        JSValue exception = exec->exception();
-        exec->clearException();
-
-        promise->reject(vm, exception);
-    }
+    arguments.append(exec->argument(0));
+    call(exec, initializePromise, callType, callData, promise, arguments);
 
-    // 15. Return promise.
     return JSValue::encode(promise);
 }
 
@@ -161,380 +118,7 @@ CallType JSPromiseConstructor::getCallData(JSCell*, CallData& callData)
 
 bool JSPromiseConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
 {
-    return getStaticFunctionSlot<InternalFunction>(exec, ExecState::promiseConstructorTable(exec->vm()), jsCast<JSPromiseConstructor*>(object), propertyName, slot);
-}
-
-EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncCast(ExecState* exec)
-{
-    // -- Promise.cast(x) --
-    JSValue x = exec->argument(0);
-
-    // 1. Let 'C' be the this value.
-    JSValue C = exec->thisValue();
-
-    // 2. If IsPromise(x) is true,
-    JSPromise* promise = jsDynamicCast<JSPromise*>(x);
-    if (promise) {
-        // i. Let 'constructor' be the value of x's [[PromiseConstructor]] internal slot.
-        JSValue constructor = promise->constructor();
-        // ii. If SameValue(constructor, C) is true, return x.
-        if (sameValue(exec, constructor, C))
-            return JSValue::encode(x);
-    }
-
-    // 3. Let 'deferred' be the result of calling GetDeferred(C).
-    JSValue deferredValue = createJSPromiseDeferredFromConstructor(exec, C);
-    
-    // 4. ReturnIfAbrupt(deferred).
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-
-    JSPromiseDeferred* deferred = jsCast<JSPromiseDeferred*>(deferredValue);
-
-    // 5. Let 'resolveResult' be the result of calling the [[Call]] internal method
-    //    of deferred.[[Resolve]] with undefined as thisArgument and a List containing x
-    //    as argumentsList.
-    performDeferredResolve(exec, deferred, x);
-
-    // 6. ReturnIfAbrupt(resolveResult).
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-
-    // 7. Return deferred.[[Promise]].
-    return JSValue::encode(deferred->promise());
-}
-
-EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncResolve(ExecState* exec)
-{
-    // -- Promise.resolve(x) --
-    JSValue x = exec->argument(0);
-
-    // 1. Let 'C' be the this value.
-    JSValue C = exec->thisValue();
-
-    // 2. Let 'deferred' be the result of calling GetDeferred(C).
-    JSValue deferredValue = createJSPromiseDeferredFromConstructor(exec, C);
-
-    // 3. ReturnIfAbrupt(deferred).
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-
-    JSPromiseDeferred* deferred = jsCast<JSPromiseDeferred*>(deferredValue);
-
-    // 4. Let 'resolveResult' be the result of calling the [[Call]] internal method
-    //    of deferred.[[Resolve]] with undefined as thisArgument and a List containing x
-    //    as argumentsList.
-    performDeferredResolve(exec, deferred, x);
-    
-    // 5. ReturnIfAbrupt(resolveResult).
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-
-    // 6. Return deferred.[[Promise]].
-    return JSValue::encode(deferred->promise());
-}
-
-EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncReject(ExecState* exec)
-{
-    // -- Promise.reject(x) --
-    JSValue r = exec->argument(0);
-
-    // 1. Let 'C' be the this value.
-    JSValue C = exec->thisValue();
-
-    // 2. Let 'deferred' be the result of calling GetDeferred(C).
-    JSValue deferredValue = createJSPromiseDeferredFromConstructor(exec, C);
-
-    // 3. ReturnIfAbrupt(deferred).
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-
-    JSPromiseDeferred* deferred = jsCast<JSPromiseDeferred*>(deferredValue);
-
-    // 4. Let 'rejectResult' be the result of calling the [[Call]] internal method
-    //    of deferred.[[Reject]] with undefined as thisArgument and a List containing r
-    //    as argumentsList.
-    performDeferredReject(exec, deferred, r);
-
-    // 5. ReturnIfAbrupt(resolveResult).
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-
-    // 6. Return deferred.[[Promise]].
-    return JSValue::encode(deferred->promise());
-}
-
-EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncRace(ExecState* exec)
-{
-    // -- Promise.race(iterable) --
-    JSValue iterable = exec->argument(0);
-    VM& vm = exec->vm();
-
-    // 1. Let 'C' be the this value.
-    JSValue C = exec->thisValue();
-
-    // 2. Let 'deferred' be the result of calling GetDeferred(C).
-    JSValue deferredValue = createJSPromiseDeferredFromConstructor(exec, C);
-
-    // 3. ReturnIfAbrupt(deferred).
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-
-    JSPromiseDeferred* deferred = jsCast<JSPromiseDeferred*>(deferredValue);
-
-    // 4. Let 'iterator' be the result of calling GetIterator(iterable).
-    JSValue iteratorFunction = iterable.get(exec, vm.propertyNames->iteratorPrivateName);
-    if (exec->hadException())
-        return JSValue::encode(abruptRejection(exec, deferred));
-
-    CallData iteratorFunctionCallData;
-    CallType iteratorFunctionCallType = getCallData(iteratorFunction, iteratorFunctionCallData);
-    if (iteratorFunctionCallType == CallTypeNone) {
-        throwTypeError(exec);
-        return JSValue::encode(abruptRejection(exec, deferred));
-    }
-
-    ArgList iteratorFunctionArguments;
-    JSValue iterator = call(exec, iteratorFunction, iteratorFunctionCallType, iteratorFunctionCallData, iterable, iteratorFunctionArguments);
-
-    // 5. RejectIfAbrupt(iterator, deferred).
-    if (exec->hadException())
-        return JSValue::encode(abruptRejection(exec, deferred));
-
-    // 6. Repeat
-    do {
-        // i. Let 'next' be the result of calling IteratorStep(iterator).
-        JSValue nextFunction = iterator.get(exec, exec->vm().propertyNames->iteratorNextPrivateName);
-        if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
-
-        CallData nextFunctionCallData;
-        CallType nextFunctionCallType = getCallData(nextFunction, nextFunctionCallData);
-        if (nextFunctionCallType == CallTypeNone) {
-            throwTypeError(exec);
-            return JSValue::encode(abruptRejection(exec, deferred));
-        }
-
-        MarkedArgumentBuffer nextFunctionArguments;
-        nextFunctionArguments.append(jsUndefined());
-        JSValue next = call(exec, nextFunction, nextFunctionCallType, nextFunctionCallData, iterator, nextFunctionArguments);
-        
-        // ii. RejectIfAbrupt(next, deferred).
-        if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
-    
-        // iii. If 'next' is false, return deferred.[[Promise]].
-        // Note: We implement this as an iterationTerminator
-        if (next == vm.iterationTerminator.get())
-            return JSValue::encode(deferred->promise());
-        
-        // iv. Let 'nextValue' be the result of calling IteratorValue(next).
-        // v. RejectIfAbrupt(nextValue, deferred).
-        // Note: 'next' is already the value, so there is nothing to do here.
-
-        // vi. Let 'nextPromise' be the result of calling Invoke(C, "cast", (nextValue)).
-        JSValue castFunction = C.get(exec, vm.propertyNames->cast);
-        if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
-
-        CallData castFunctionCallData;
-        CallType castFunctionCallType = getCallData(castFunction, castFunctionCallData);
-        if (castFunctionCallType == CallTypeNone) {
-            throwTypeError(exec);
-            return JSValue::encode(abruptRejection(exec, deferred));
-        }
-
-        MarkedArgumentBuffer castFunctionArguments;
-        castFunctionArguments.append(next);
-        JSValue nextPromise = call(exec, castFunction, castFunctionCallType, castFunctionCallData, C, castFunctionArguments);
-
-        // vii. RejectIfAbrupt(nextPromise, deferred).
-        if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
-
-        // viii. Let 'result' be the result of calling Invoke(nextPromise, "then", (deferred.[[Resolve]], deferred.[[Reject]])).
-        JSValue thenFunction = nextPromise.get(exec, vm.propertyNames->then);
-        if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
-
-        CallData thenFunctionCallData;
-        CallType thenFunctionCallType = getCallData(thenFunction, thenFunctionCallData);
-        if (thenFunctionCallType == CallTypeNone) {
-            throwTypeError(exec);
-            return JSValue::encode(abruptRejection(exec, deferred));
-        }
-
-        MarkedArgumentBuffer thenFunctionArguments;
-        thenFunctionArguments.append(deferred->resolve());
-        thenFunctionArguments.append(deferred->reject());
-
-        call(exec, thenFunction, thenFunctionCallType, thenFunctionCallData, nextPromise, thenFunctionArguments);
-
-        // ix. RejectIfAbrupt(result, deferred).
-        if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
-    } while (true);
-}
-
-EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncAll(ExecState* exec)
-{
-    // -- Promise.all(iterable) --
-
-    JSValue iterable = exec->argument(0);
-    VM& vm = exec->vm();
-
-    // 1. Let 'C' be the this value.
-    JSValue C = exec->thisValue();
-
-    // 2. Let 'deferred' be the result of calling GetDeferred(C).
-    JSValue deferredValue = createJSPromiseDeferredFromConstructor(exec, C);
-
-    // 3. ReturnIfAbrupt(deferred).
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-
-    // NOTE: A non-abrupt completion of createJSPromiseDeferredFromConstructor implies that
-    // C and deferredValue are objects.
-    JSObject* thisObject = asObject(C);
-    JSPromiseDeferred* deferred = jsCast<JSPromiseDeferred*>(deferredValue);
-
-    // 4. Let 'iterator' be the result of calling GetIterator(iterable).
-    JSValue iteratorFunction = iterable.get(exec, vm.propertyNames->iteratorPrivateName);
-    if (exec->hadException())
-        return JSValue::encode(abruptRejection(exec, deferred));
-
-    CallData iteratorFunctionCallData;
-    CallType iteratorFunctionCallType = getCallData(iteratorFunction, iteratorFunctionCallData);
-    if (iteratorFunctionCallType == CallTypeNone) {
-        throwTypeError(exec);
-        return JSValue::encode(abruptRejection(exec, deferred));
-    }
-
-    ArgList iteratorFunctionArguments;
-    JSValue iterator = call(exec, iteratorFunction, iteratorFunctionCallType, iteratorFunctionCallData, iterable, iteratorFunctionArguments);
-
-    // 5. RejectIfAbrupt(iterator, deferred).
-    if (exec->hadException())
-        return JSValue::encode(abruptRejection(exec, deferred));
-
-    // 6. Let 'values' be the result of calling ArrayCreate(0).
-    JSArray* values = constructEmptyArray(exec, nullptr, thisObject->globalObject());
-    
-    // 7. Let 'countdownHolder' be Record { [[Countdown]]: 0 }.
-    NumberObject* countdownHolder = constructNumber(exec, thisObject->globalObject(), JSValue(0));
-    
-    // 8. Let 'index' be 0.
-    unsigned index = 0;
-    
-    // 9. Repeat.
-    do {
-        // i. Let 'next' be the result of calling IteratorStep(iterator).
-        JSValue nextFunction = iterator.get(exec, exec->vm().propertyNames->iteratorNextPrivateName);
-        if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
-
-        CallData nextFunctionCallData;
-        CallType nextFunctionCallType = getCallData(nextFunction, nextFunctionCallData);
-        if (nextFunctionCallType == CallTypeNone) {
-            throwTypeError(exec);
-            return JSValue::encode(abruptRejection(exec, deferred));
-        }
-
-        MarkedArgumentBuffer nextFunctionArguments;
-        nextFunctionArguments.append(jsUndefined());
-        JSValue next = call(exec, nextFunction, nextFunctionCallType, nextFunctionCallData, iterator, nextFunctionArguments);
-        
-        // ii. RejectIfAbrupt(next, deferred).
-        if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
-
-        // iii. If 'next' is false,
-        // Note: We implement this as an iterationTerminator
-        if (next == vm.iterationTerminator.get()) {
-            // a. If 'index' is 0,
-            if (!index) {
-                // a. Let 'resolveResult' be 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);
-
-                // b. ReturnIfAbrupt(resolveResult).
-                if (exec->hadException())
-                    return JSValue::encode(jsUndefined());
-            }
-            
-            // b. Return deferred.[[Promise]].
-            return JSValue::encode(deferred->promise());
-        }
-        
-        // iv. Let 'nextValue' be the result of calling IteratorValue(next).
-        // v. RejectIfAbrupt(nextValue, deferred).
-        // Note: 'next' is already the value, so there is nothing to do here.
-
-        // vi. Let 'nextPromise' be the result of calling Invoke(C, "cast", (nextValue)).
-        JSValue castFunction = C.get(exec, vm.propertyNames->cast);
-        if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
-
-        CallData castFunctionCallData;
-        CallType castFunctionCallType = getCallData(castFunction, castFunctionCallData);
-        if (castFunctionCallType == CallTypeNone) {
-            throwTypeError(exec);
-            return JSValue::encode(abruptRejection(exec, deferred));
-        }
-
-        MarkedArgumentBuffer castFunctionArguments;
-        castFunctionArguments.append(next);
-        JSValue nextPromise = call(exec, castFunction, castFunctionCallType, castFunctionCallData, C, castFunctionArguments);
-
-        // vii. RejectIfAbrupt(nextPromise, deferred).
-        if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
-
-        // viii. Let 'countdownFunction' be a new built-in function object as defined in Promise.all Countdown Functions.
-        JSFunction* countdownFunction = createPromiseAllCountdownFunction(vm, thisObject->globalObject());
-        
-        // ix. Set the [[Index]] internal slot of 'countdownFunction' to 'index'.
-        countdownFunction->putDirect(vm, vm.propertyNames->indexPrivateName, JSValue(index));
-
-        // x. Set the [[Values]] internal slot of 'countdownFunction' to 'values'.
-        countdownFunction->putDirect(vm, vm.propertyNames->valuesPrivateName, values);
-
-        // xi. Set the [[Deferred]] internal slot of 'countdownFunction' to 'deferred'.
-        countdownFunction->putDirect(vm, vm.propertyNames->deferredPrivateName, deferred);
-
-        // xii. Set the [[CountdownHolder]] internal slot of 'countdownFunction' to 'countdownHolder'.
-        countdownFunction->putDirect(vm, vm.propertyNames->countdownHolderPrivateName, countdownHolder);
-
-        // xiii. Let 'result' be the result of calling Invoke(nextPromise, "then", (countdownFunction, deferred.[[Reject]])).
-        JSValue thenFunction = nextPromise.get(exec, vm.propertyNames->then);
-        if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
-
-        CallData thenFunctionCallData;
-        CallType thenFunctionCallType = getCallData(thenFunction, thenFunctionCallData);
-        if (thenFunctionCallType == CallTypeNone) {
-            throwTypeError(exec);
-            return JSValue::encode(abruptRejection(exec, deferred));
-        }
-
-        MarkedArgumentBuffer thenFunctionArguments;
-        thenFunctionArguments.append(countdownFunction);
-        thenFunctionArguments.append(deferred->reject());
-
-        call(exec, thenFunction, thenFunctionCallType, thenFunctionCallData, nextPromise, thenFunctionArguments);
-
-        // xiv. RejectIfAbrupt(result, deferred).
-        if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
-
-        // xv. Set index to index + 1.
-        index++;
-
-        // xvi. Set countdownHolder.[[Countdown]] to countdownHolder.[[Countdown]] + 1.
-        uint32_t newCountdownValue = countdownHolder->internalValue().asUInt32() + 1;
-        countdownHolder->setInternalValue(vm, JSValue(newCountdownValue));
-    } while (true);
+    return getStaticFunctionSlot<InternalFunction>(exec, promiseConstructorTable, jsCast<JSPromiseConstructor*>(object), propertyName, slot);
 }
 
 JSPromise* constructPromise(ExecState* exec, JSGlobalObject* globalObject, JSFunction* resolver)