blob: cbc42eb9fd9230f46e6f849ff1a7959bd1079bec [file] [log] [blame]
/*
* Copyright (C) 2013 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. AND ITS CONTRIBUTORS ``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 ITS 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 "JSPromiseDeferred.h"
#include "Error.h"
#include "JSCJSValueInlines.h"
#include "JSCellInlines.h"
#include "JSPromise.h"
#include "JSPromiseConstructor.h"
#include "JSPromiseFunctions.h"
#include "SlotVisitorInlines.h"
namespace JSC {
const ClassInfo JSPromiseDeferred::s_info = { "JSPromiseDeferred", 0, 0, 0, CREATE_METHOD_TABLE(JSPromiseDeferred) };
JSPromiseDeferred* JSPromiseDeferred::create(ExecState* exec, JSGlobalObject* globalObject)
{
VM& vm = exec->vm();
JSFunction* resolver = createDeferredConstructionFunction(vm, globalObject);
JSPromise* promise = constructPromise(exec, globalObject, resolver);
JSValue resolve = resolver->get(exec, vm.propertyNames->resolvePrivateName);
JSValue reject = resolver->get(exec, vm.propertyNames->rejectPrivateName);
return JSPromiseDeferred::create(vm, promise, resolve, reject);
}
JSPromiseDeferred* JSPromiseDeferred::create(VM& vm, JSObject* promise, JSValue resolve, JSValue reject)
{
JSPromiseDeferred* deferred = new (NotNull, allocateCell<JSPromiseDeferred>(vm.heap)) JSPromiseDeferred(vm);
deferred->finishCreation(vm, promise, resolve, reject);
return deferred;
}
JSPromiseDeferred::JSPromiseDeferred(VM& vm)
: Base(vm, vm.promiseDeferredStructure.get())
{
}
void JSPromiseDeferred::finishCreation(VM& vm, JSObject* promise, JSValue resolve, JSValue reject)
{
Base::finishCreation(vm);
m_promise.set(vm, this, promise);
m_resolve.set(vm, this, resolve);
m_reject.set(vm, this, reject);
}
void JSPromiseDeferred::visitChildren(JSCell* cell, SlotVisitor& visitor)
{
JSPromiseDeferred* thisObject = jsCast<JSPromiseDeferred*>(cell);
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
Base::visitChildren(thisObject, visitor);
visitor.append(&thisObject->m_promise);
visitor.append(&thisObject->m_resolve);
visitor.append(&thisObject->m_reject);
}
JSValue createJSPromiseDeferredFromConstructor(ExecState* exec, JSValue C)
{
// -- This implements the GetDeferred(C) abstract operation --
// 1. If IsConstructor(C) is false, throw a TypeError.
if (!C.isObject())
return throwTypeError(exec);
ConstructData constructData;
ConstructType constructType = getConstructData(C, constructData);
if (constructType == ConstructTypeNone)
return throwTypeError(exec);
VM& vm = exec->vm();
// 2. Let 'resolver' be a new built-in function object as defined in Deferred Construction Functions.
JSFunction* resolver = createDeferredConstructionFunction(vm, asObject(C)->globalObject());
// 3. Let 'promise' be the result of calling the [[Construct]] internal method of 'C' with
// an argument list containing the single item resolver.
MarkedArgumentBuffer constructArguments;
constructArguments.append(resolver);
JSObject* promise = construct(exec, C, constructType, constructData, constructArguments);
// 4. ReturnIfAbrupt(promise).
if (exec->hadException())
return jsUndefined();
// 5. Let 'resolve' be the value of resolver's [[Resolve]] internal slot.
JSValue resolve = resolver->get(exec, vm.propertyNames->resolvePrivateName);
// 6. If IsCallable(resolve) is false, throw a TypeError.
CallData resolveCallData;
CallType resolveCallType = getCallData(resolve, resolveCallData);
if (resolveCallType == CallTypeNone)
return throwTypeError(exec);
// 7. Let 'reject' be the value of resolver's [[Reject]] internal slot.
JSValue reject = resolver->get(exec, vm.propertyNames->rejectPrivateName);
// 8. If IsCallable(reject) is false, throw a TypeError.
CallData rejectCallData;
CallType rejectCallType = getCallData(reject, rejectCallData);
if (rejectCallType == CallTypeNone)
return throwTypeError(exec);
// 9. Return the Deferred { [[Promise]]: promise, [[Resolve]]: resolve, [[Reject]]: reject }.
return JSPromiseDeferred::create(exec->vm(), promise, resolve, reject);
}
ThenableStatus updateDeferredFromPotentialThenable(ExecState* exec, JSValue x, JSPromiseDeferred* deferred)
{
// 1. If Type(x) is not Object, return "not a thenable".
if (!x.isObject())
return NotAThenable;
// 2. Let 'then' be the result of calling Get(x, "then").
JSValue thenValue = x.get(exec, exec->vm().propertyNames->then);
// 3. If then is an abrupt completion,
if (exec->hadException()) {
// i. Let 'rejectResult' be the result of calling the [[Call]] internal method of
// deferred.[[Reject]] with undefined as thisArgument and a List containing
// then.[[value]] as argumentsList.
JSValue exception = exec->exception();
exec->clearException();
performDeferredReject(exec, deferred, exception);
// ii. ReturnIfAbrupt(rejectResult).
// NOTE: Nothing to do.
// iii. Return.
return WasAThenable;
}
// 4. Let 'then' be then.[[value]].
// Note: Nothing to do.
// 5. If IsCallable(then) is false, return "not a thenable".
CallData thenCallData;
CallType thenCallType = getCallData(thenValue, thenCallData);
if (thenCallType == CallTypeNone)
return NotAThenable;
// 6. Let 'thenCallResult' be the result of calling the [[Call]] internal method of
// 'then' passing x as thisArgument and a List containing deferred.[[Resolve]] and
// deferred.[[Reject]] as argumentsList.
MarkedArgumentBuffer thenArguments;
thenArguments.append(deferred->resolve());
thenArguments.append(deferred->reject());
call(exec, thenValue, thenCallType, thenCallData, x, thenArguments);
// 7. If 'thenCallResult' is an abrupt completion,
if (exec->hadException()) {
// i. Let 'rejectResult' be the result of calling the [[Call]] internal method of
// deferred.[[Reject]] with undefined as thisArgument and a List containing
// thenCallResult.[[value]] as argumentsList.
JSValue exception = exec->exception();
exec->clearException();
performDeferredReject(exec, deferred, exception);
// ii. ReturnIfAbrupt(rejectResult).
// NOTE: Nothing to do.
}
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