X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/4be4e30906bcb8ee30b4d189205cb70bad6707ce..81345200c95645a1b0d2635520f96ad55dfde63f:/runtime/JSPromiseDeferred.cpp diff --git a/runtime/JSPromiseDeferred.cpp b/runtime/JSPromiseDeferred.cpp new file mode 100644 index 0000000..67fd550 --- /dev/null +++ b/runtime/JSPromiseDeferred.cpp @@ -0,0 +1,251 @@ +/* + * 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" + +#if ENABLE(PROMISES) + +#include "Error.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSPromise.h" +#include "JSPromiseConstructor.h" +#include "JSPromiseFunctions.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.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(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(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 + +#endif // ENABLE(PROMISES)