1497b5c33722becc3a9ef7e51d11e184904056af
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSPromiseResolver.cpp
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 "JSPromiseResolver.h"
28
29 #if ENABLE(PROMISES)
30
31 #include "JSCJSValueInlines.h"
32 #include "JSCellInlines.h"
33 #include "JSGlobalObject.h"
34 #include "JSPromise.h"
35 #include "JSPromiseCallback.h"
36 #include "SlotVisitorInlines.h"
37 #include "StructureInlines.h"
38
39 namespace JSC {
40
41 const ClassInfo JSPromiseResolver::s_info = { "PromiseResolver", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSPromiseResolver) };
42
43 JSPromiseResolver* JSPromiseResolver::create(VM& vm, Structure* structure, JSPromise* promise)
44 {
45     JSPromiseResolver* object = new (NotNull, allocateCell<JSPromiseResolver>(vm.heap)) JSPromiseResolver(vm, structure);
46     object->finishCreation(vm, promise);
47     return object;
48 }
49
50 Structure* JSPromiseResolver::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
51 {
52     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
53 }
54
55 JSPromiseResolver::JSPromiseResolver(VM& vm, Structure* structure)
56     : JSNonFinalObject(vm, structure)
57     , m_isResolved(false)
58 {
59 }
60
61 void JSPromiseResolver::finishCreation(VM& vm, JSPromise* promise)
62 {
63     Base::finishCreation(vm);
64     ASSERT(inherits(info()));
65     m_promise.set(vm, this, promise);
66 }
67
68 void JSPromiseResolver::visitChildren(JSCell* cell, SlotVisitor& visitor)
69 {
70     JSPromiseResolver* thisObject = jsCast<JSPromiseResolver*>(cell);
71     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
72     COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
73     ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
74
75     Base::visitChildren(thisObject, visitor);
76     visitor.append(&thisObject->m_promise);
77 }
78
79 JSPromise* JSPromiseResolver::promise() const
80 {
81     return m_promise.get();
82 }
83
84 void JSPromiseResolver::fulfillIfNotResolved(ExecState* exec, JSValue value)
85 {
86     if (!m_isResolved) {
87         m_isResolved = true;
88         fulfill(exec, value);
89     }
90 }
91
92 void JSPromiseResolver::resolveIfNotResolved(ExecState* exec, JSValue value)
93 {
94     if (!m_isResolved) {
95         m_isResolved = true;
96         resolve(exec, value);
97     }
98 }
99
100 void JSPromiseResolver::rejectIfNotResolved(ExecState* exec, JSValue value)
101 {
102     if (!m_isResolved) {
103         m_isResolved = true;
104         reject(exec, value);
105     }
106 }
107
108 void JSPromiseResolver::fulfill(ExecState* exec, JSValue value, ResolverMode mode)
109 {
110     // 1. Let promise be the context object's associated promise.
111     // 2. Set promise's state to fulfilled.
112     m_promise->setState(JSPromise::Fulfilled);
113
114     // 3. Set promise's result to value.
115     m_promise->setResult(exec->vm(), value);
116
117     // 4. If the synchronous flag is set, process promise's fulfill callbacks with value.
118     if (mode == ResolveSynchronously) {
119         m_promise->processFulfillCallbacksWithValue(exec, value);
120         return;
121     }
122     
123     // 5. Otherwise, the synchronous flag is unset, queue a task to process promise's fulfill callbacks with value.
124     m_promise->queueTaskToProcessFulfillCallbacks(exec);
125 }
126
127 void JSPromiseResolver::resolve(ExecState* exec, JSValue value, ResolverMode mode)
128 {
129     // 1. Let then be null.
130     JSValue then = jsNull();
131     
132     // 2. If value is a JavaScript Object, set then to the result of calling the JavaScript [[Get]] internal
133     //    method of value with property name then.
134     if (value.isObject()) {
135         then = value.get(exec, exec->propertyNames().then);
136
137         // 3. If calling the [[Get]] internal method threw an exception, catch it and run reject with the thrown
138         //    exception and the synchronous flag if set, and then terminate these steps.
139         if (exec->hadException()) {
140             JSValue exception = exec->exception();
141             exec->clearException();
142
143             reject(exec, exception, mode);
144             return;
145         }
146
147         // 4. If JavaScript IsCallable(then) is true, run these substeps and then terminate these steps:
148         CallData callData;
149         CallType callType = JSC::getCallData(then, callData);
150         if (callType != CallTypeNone) {
151             // 4.1. Let fulfillCallback be a promise callback for the context object and its resolve algorithm.
152             JSPromiseCallback* fulfillCallback = JSPromiseCallback::create(exec, globalObject(), globalObject()->promiseCallbackStructure(), this, JSPromiseCallback::Resolve);
153
154             // 4.2. Let rejectCallback be a promise callback for the context object and its reject algorithm.
155             JSPromiseCallback* rejectCallback = JSPromiseCallback::create(exec, globalObject(), globalObject()->promiseCallbackStructure(), this, JSPromiseCallback::Reject);
156             
157             // 4.3. Call the JavaScript [[Call]] internal method of then with this value value and fulfillCallback
158             //      and rejectCallback as arguments.
159             MarkedArgumentBuffer thenArguments;
160             thenArguments.append(fulfillCallback);
161             thenArguments.append(rejectCallback);
162             call(exec, then, callType, callData, value, thenArguments);
163
164             // 4.4 If calling the [[Call]] internal method threw an exception, catch it and run context object's
165             //     reject with the thrown exception and the synchronous flag if set.
166             if (exec->hadException()) {
167                 JSValue exception = exec->exception();
168                 exec->clearException();
169
170                 reject(exec, exception, mode);
171                 return;
172             }
173             return;
174         }
175     }
176
177     // 5. Run context object's fulfill with value and the synchronous flag if set.
178     fulfill(exec, value, mode);
179 }
180
181 void JSPromiseResolver::reject(ExecState* exec, JSValue value, ResolverMode mode)
182 {
183     // 1. Let promise be the context object's associated promise.
184     // 2. Set promise's state to rejected.
185     m_promise->setState(JSPromise::Rejected);
186
187     // 3. Set promise's result to value.
188     m_promise->setResult(exec->vm(), value);
189
190     // 4. If the synchronous flag is set, process promise's reject callbacks with value.
191     if (mode == ResolveSynchronously) {
192         m_promise->processRejectCallbacksWithValue(exec, value);
193         return;
194     }
195     
196     // 5. Otherwise, the synchronous flag is unset, queue a task to process promise's reject callbacks with value.
197     m_promise->queueTaskToProcessRejectCallbacks(exec);
198 }
199
200 } // namespace JSC
201
202 #endif // ENABLE(PROMISES)