1/*
2 * Copyright (C) 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "JSPromiseDeferred.h"
28
29#if ENABLE(PROMISES)
30
31#include "Error.h"
32#include "JSCJSValueInlines.h"
33#include "JSCellInlines.h"
34#include "JSPromise.h"
35#include "JSPromiseConstructor.h"
36#include "JSPromiseFunctions.h"
37#include "SlotVisitorInlines.h"
38#include "StructureInlines.h"
39
40namespace JSC {
41
42const ClassInfo JSPromiseDeferred::s_info = { "JSPromiseDeferred", 0, 0, 0, CREATE_METHOD_TABLE(JSPromiseDeferred) };
43
44JSPromiseDeferred* JSPromiseDeferred::create(ExecState* exec, JSGlobalObject* globalObject)
45{
46    VM& vm = exec->vm();
47
48    JSFunction* resolver = createDeferredConstructionFunction(vm, globalObject);
49
50    JSPromise* promise = constructPromise(exec, globalObject, resolver);
51    JSValue resolve = resolver->get(exec, vm.propertyNames->resolvePrivateName);
52    JSValue reject = resolver->get(exec, vm.propertyNames->rejectPrivateName);
53
54    return JSPromiseDeferred::create(vm, promise, resolve, reject);
55}
56
57JSPromiseDeferred* JSPromiseDeferred::create(VM& vm, JSObject* promise, JSValue resolve, JSValue reject)
58{
59    JSPromiseDeferred* deferred = new (NotNull, allocateCell<JSPromiseDeferred>(vm.heap)) JSPromiseDeferred(vm);
60    deferred->finishCreation(vm, promise, resolve, reject);
61    return deferred;
62}
63
64JSPromiseDeferred::JSPromiseDeferred(VM& vm)
65    : Base(vm, vm.promiseDeferredStructure.get())
66{
67}
68
69void JSPromiseDeferred::finishCreation(VM& vm, JSObject* promise, JSValue resolve, JSValue reject)
70{
71    Base::finishCreation(vm);
72    m_promise.set(vm, this, promise);
73    m_resolve.set(vm, this, resolve);
74    m_reject.set(vm, this, reject);
75}
76
77void JSPromiseDeferred::visitChildren(JSCell* cell, SlotVisitor& visitor)
78{
79    JSPromiseDeferred* thisObject = jsCast<JSPromiseDeferred*>(cell);
80    ASSERT_GC_OBJECT_INHERITS(thisObject, info());
81    COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
82    ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
83
84    Base::visitChildren(thisObject, visitor);
85
86    visitor.append(&thisObject->m_promise);
87    visitor.append(&thisObject->m_resolve);
88    visitor.append(&thisObject->m_reject);
89}
90
91JSValue createJSPromiseDeferredFromConstructor(ExecState* exec, JSValue C)
92{
93    // -- This implements the GetDeferred(C) abstract operation --
94
95    // 1. If IsConstructor(C) is false, throw a TypeError.
96    if (!C.isObject())
97        return throwTypeError(exec);
98
99    ConstructData constructData;
100    ConstructType constructType = getConstructData(C, constructData);
101    if (constructType == ConstructTypeNone)
102        return throwTypeError(exec);
103
104    VM& vm = exec->vm();
105
106    // 2. Let 'resolver' be a new built-in function object as defined in Deferred Construction Functions.
107    JSFunction* resolver = createDeferredConstructionFunction(vm, asObject(C)->globalObject());
108
109    // 3. Let 'promise' be the result of calling the [[Construct]] internal method of 'C' with
110    //    an argument list containing the single item resolver.
111    MarkedArgumentBuffer constructArguments;
112    constructArguments.append(resolver);
113    JSObject* promise = construct(exec, C, constructType, constructData, constructArguments);
114
115    // 4. ReturnIfAbrupt(promise).
116    if (exec->hadException())
117        return jsUndefined();
118
119    // 5. Let 'resolve' be the value of resolver's [[Resolve]] internal slot.
120    JSValue resolve = resolver->get(exec, vm.propertyNames->resolvePrivateName);
121
122    // 6. If IsCallable(resolve) is false, throw a TypeError.
123    CallData resolveCallData;
124    CallType resolveCallType = getCallData(resolve, resolveCallData);
125    if (resolveCallType == CallTypeNone)
126        return throwTypeError(exec);
127
128    // 7. Let 'reject' be the value of resolver's [[Reject]] internal slot.
129    JSValue reject = resolver->get(exec, vm.propertyNames->rejectPrivateName);
130
131    // 8. If IsCallable(reject) is false, throw a TypeError.
132    CallData rejectCallData;
133    CallType rejectCallType = getCallData(reject, rejectCallData);
134    if (rejectCallType == CallTypeNone)
135        return throwTypeError(exec);
136
137    // 9. Return the Deferred { [[Promise]]: promise, [[Resolve]]: resolve, [[Reject]]: reject }.
138    return JSPromiseDeferred::create(exec->vm(), promise, resolve, reject);
139}
140
141ThenableStatus updateDeferredFromPotentialThenable(ExecState* exec, JSValue x, JSPromiseDeferred* deferred)
142{
143    // 1. If Type(x) is not Object, return "not a thenable".
144    if (!x.isObject())
145        return NotAThenable;
146
147    // 2. Let 'then' be the result of calling Get(x, "then").
148    JSValue thenValue = x.get(exec, exec->vm().propertyNames->then);
149
150    // 3. If then is an abrupt completion,
151    if (exec->hadException()) {
152        // i. Let 'rejectResult' be the result of calling the [[Call]] internal method of
153        //    deferred.[[Reject]] with undefined as thisArgument and a List containing
154        //    then.[[value]] as argumentsList.
155        JSValue exception = exec->exception();
156        exec->clearException();
157
158        performDeferredReject(exec, deferred, exception);
159
160        // ii. ReturnIfAbrupt(rejectResult).
161        // NOTE: Nothing to do.
162
163        // iii. Return.
164        return WasAThenable;
165    }
166
167    // 4. Let 'then' be then.[[value]].
168    // Note: Nothing to do.
169
170    // 5. If IsCallable(then) is false, return "not a thenable".
171    CallData thenCallData;
172    CallType thenCallType = getCallData(thenValue, thenCallData);
173    if (thenCallType == CallTypeNone)
174        return NotAThenable;
175
176    // 6. Let 'thenCallResult' be the result of calling the [[Call]] internal method of
177    //    'then' passing x as thisArgument and a List containing deferred.[[Resolve]] and
178    //    deferred.[[Reject]] as argumentsList.
179    MarkedArgumentBuffer thenArguments;
180    thenArguments.append(deferred->resolve());
181    thenArguments.append(deferred->reject());
182
183    call(exec, thenValue, thenCallType, thenCallData, x, thenArguments);
184
185    // 7. If 'thenCallResult' is an abrupt completion,
186    if (exec->hadException()) {
187        // i. Let 'rejectResult' be the result of calling the [[Call]] internal method of
188        //    deferred.[[Reject]] with undefined as thisArgument and a List containing
189        //    thenCallResult.[[value]] as argumentsList.
190        JSValue exception = exec->exception();
191        exec->clearException();
192
193        performDeferredReject(exec, deferred, exception);
194
195        // ii. ReturnIfAbrupt(rejectResult).
196        // NOTE: Nothing to do.
197    }
198
199    return WasAThenable;
200}
201
202void performDeferredResolve(ExecState* exec, JSPromiseDeferred* deferred, JSValue argument)
203{
204    JSValue deferredResolve = deferred->resolve();
205
206    CallData resolveCallData;
207    CallType resolveCallType = getCallData(deferredResolve, resolveCallData);
208    ASSERT(resolveCallType != CallTypeNone);
209
210    MarkedArgumentBuffer arguments;
211    arguments.append(argument);
212
213    call(exec, deferredResolve, resolveCallType, resolveCallData, jsUndefined(), arguments);
214}
215
216void performDeferredReject(ExecState* exec, JSPromiseDeferred* deferred, JSValue argument)
217{
218    JSValue deferredReject = deferred->reject();
219
220    CallData rejectCallData;
221    CallType rejectCallType = getCallData(deferredReject, rejectCallData);
222    ASSERT(rejectCallType != CallTypeNone);
223
224    MarkedArgumentBuffer arguments;
225    arguments.append(argument);
226
227    call(exec, deferredReject, rejectCallType, rejectCallData, jsUndefined(), arguments);
228}
229
230JSValue abruptRejection(ExecState* exec, JSPromiseDeferred* deferred)
231{
232    ASSERT(exec->hadException());
233    JSValue argument = exec->exception();
234    exec->clearException();
235
236    // i. Let 'rejectResult' be the result of calling the [[Call]] internal method
237    // of deferred.[[Reject]] with undefined as thisArgument and a List containing
238    // argument.[[value]] as argumentsList.
239    performDeferredReject(exec, deferred, argument);
240
241    // ii. ReturnIfAbrupt(rejectResult).
242    if (exec->hadException())
243        return jsUndefined();
244
245    // iii. Return deferred.[[Promise]].
246    return deferred->promise();
247}
248
249} // namespace JSC
250
251#endif // ENABLE(PROMISES)
252