/* * 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(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