JSPromiseConstructor.cpp [plain text]
#include "config.h"
#include "JSPromiseConstructor.h"
#if ENABLE(PROMISES)
#include "Error.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"
#include "StructureInlines.h"
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) };
JSPromiseConstructor* JSPromiseConstructor::create(VM& vm, Structure* structure, JSPromisePrototype* promisePrototype)
{
JSPromiseConstructor* constructor = new (NotNull, allocateCell<JSPromiseConstructor>(vm.heap)) JSPromiseConstructor(vm, structure);
constructor->finishCreation(vm, promisePrototype);
return constructor;
}
Structure* JSPromiseConstructor::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
{
return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
}
JSPromiseConstructor::JSPromiseConstructor(VM& vm, Structure* structure)
: InternalFunction(vm, structure)
{
}
void JSPromiseConstructor::finishCreation(VM& vm, JSPromisePrototype* promisePrototype)
{
Base::finishCreation(vm, "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)
{
JSValue resolver = exec->argument(0);
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()));
JSFunction* resolve = createResolvePromiseFunction(vm, globalObject);
resolve->putDirect(vm, vm.propertyNames->promisePrivateName, promise);
JSFunction* reject = createRejectPromiseFunction(vm, globalObject);
reject->putDirect(vm, vm.propertyNames->promisePrivateName, promise);
MarkedArgumentBuffer arguments;
arguments.append(resolve);
arguments.append(reject);
call(exec, resolver, callType, callData, jsUndefined(), arguments);
if (exec->hadException()) {
JSValue exception = exec->exception();
exec->clearException();
promise->reject(vm, exception);
}
return JSValue::encode(promise);
}
ConstructType JSPromiseConstructor::getConstructData(JSCell*, ConstructData& constructData)
{
constructData.native.function = constructPromise;
return ConstructTypeHost;
}
CallType JSPromiseConstructor::getCallData(JSCell*, CallData& callData)
{
callData.native.function = constructPromise;
return CallTypeHost;
}
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)
{
JSValue x = exec->argument(0);
JSValue C = exec->thisValue();
JSPromise* promise = jsDynamicCast<JSPromise*>(x);
if (promise) {
JSValue constructor = promise->constructor();
if (sameValue(exec, constructor, C))
return JSValue::encode(x);
}
JSValue deferredValue = createJSPromiseDeferredFromConstructor(exec, C);
if (exec->hadException())
return JSValue::encode(jsUndefined());
JSPromiseDeferred* deferred = jsCast<JSPromiseDeferred*>(deferredValue);
performDeferredResolve(exec, deferred, x);
if (exec->hadException())
return JSValue::encode(jsUndefined());
return JSValue::encode(deferred->promise());
}
EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncResolve(ExecState* exec)
{
JSValue x = exec->argument(0);
JSValue C = exec->thisValue();
JSValue deferredValue = createJSPromiseDeferredFromConstructor(exec, C);
if (exec->hadException())
return JSValue::encode(jsUndefined());
JSPromiseDeferred* deferred = jsCast<JSPromiseDeferred*>(deferredValue);
performDeferredResolve(exec, deferred, x);
if (exec->hadException())
return JSValue::encode(jsUndefined());
return JSValue::encode(deferred->promise());
}
EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncReject(ExecState* exec)
{
JSValue r = exec->argument(0);
JSValue C = exec->thisValue();
JSValue deferredValue = createJSPromiseDeferredFromConstructor(exec, C);
if (exec->hadException())
return JSValue::encode(jsUndefined());
JSPromiseDeferred* deferred = jsCast<JSPromiseDeferred*>(deferredValue);
performDeferredReject(exec, deferred, r);
if (exec->hadException())
return JSValue::encode(jsUndefined());
return JSValue::encode(deferred->promise());
}
EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncRace(ExecState* exec)
{
JSValue iterable = exec->argument(0);
VM& vm = exec->vm();
JSValue C = exec->thisValue();
JSValue deferredValue = createJSPromiseDeferredFromConstructor(exec, C);
if (exec->hadException())
return JSValue::encode(jsUndefined());
JSPromiseDeferred* deferred = jsCast<JSPromiseDeferred*>(deferredValue);
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);
if (exec->hadException())
return JSValue::encode(abruptRejection(exec, deferred));
do {
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);
if (exec->hadException())
return JSValue::encode(abruptRejection(exec, deferred));
if (next == vm.iterationTerminator.get())
return JSValue::encode(deferred->promise());
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);
if (exec->hadException())
return JSValue::encode(abruptRejection(exec, deferred));
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);
if (exec->hadException())
return JSValue::encode(abruptRejection(exec, deferred));
} while (true);
}
EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncAll(ExecState* exec)
{
JSValue iterable = exec->argument(0);
VM& vm = exec->vm();
JSValue C = exec->thisValue();
JSValue deferredValue = createJSPromiseDeferredFromConstructor(exec, C);
if (exec->hadException())
return JSValue::encode(jsUndefined());
JSObject* thisObject = asObject(C);
JSPromiseDeferred* deferred = jsCast<JSPromiseDeferred*>(deferredValue);
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);
if (exec->hadException())
return JSValue::encode(abruptRejection(exec, deferred));
JSArray* values = constructEmptyArray(exec, nullptr, thisObject->globalObject());
NumberObject* countdownHolder = constructNumber(exec, thisObject->globalObject(), JSValue(0));
unsigned index = 0;
do {
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);
if (exec->hadException())
return JSValue::encode(abruptRejection(exec, deferred));
if (next == vm.iterationTerminator.get()) {
if (!index) {
performDeferredResolve(exec, deferred, values);
if (exec->hadException())
return JSValue::encode(jsUndefined());
}
return JSValue::encode(deferred->promise());
}
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);
if (exec->hadException())
return JSValue::encode(abruptRejection(exec, deferred));
JSFunction* countdownFunction = createPromiseAllCountdownFunction(vm, thisObject->globalObject());
countdownFunction->putDirect(vm, vm.propertyNames->indexPrivateName, JSValue(index));
countdownFunction->putDirect(vm, vm.propertyNames->valuesPrivateName, values);
countdownFunction->putDirect(vm, vm.propertyNames->deferredPrivateName, deferred);
countdownFunction->putDirect(vm, vm.propertyNames->countdownHolderPrivateName, countdownHolder);
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);
if (exec->hadException())
return JSValue::encode(abruptRejection(exec, deferred));
index++;
uint32_t newCountdownValue = countdownHolder->internalValue().asUInt32() + 1;
countdownHolder->setInternalValue(vm, JSValue(newCountdownValue));
} while (true);
}
JSPromise* constructPromise(ExecState* exec, JSGlobalObject* globalObject, JSFunction* resolver)
{
JSPromiseConstructor* promiseConstructor = globalObject->promiseConstructor();
ConstructData constructData;
ConstructType constructType = getConstructData(promiseConstructor, constructData);
ASSERT(constructType != ConstructTypeNone);
MarkedArgumentBuffer arguments;
arguments.append(resolver);
return jsCast<JSPromise*>(construct(exec, promiseConstructor, constructType, constructData, arguments));
}
}
#endif // ENABLE(PROMISES)