blob: 80e17ea00189a7c7be5d13bb817bd71e28eb40e1 [file] [log] [blame]
weinig@apple.combd760692013-08-26 19:19:50 +00001/*
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 "JSPromise.h"
28
weinig@apple.comb7188852013-08-29 22:54:38 +000029#if ENABLE(PROMISES)
30
weinig@apple.combd760692013-08-26 19:19:50 +000031#include "Error.h"
32#include "JSCJSValueInlines.h"
33#include "JSCellInlines.h"
34#include "JSPromiseResolver.h"
35#include "SlotVisitorInlines.h"
36#include "StrongInlines.h"
37#include "StructureInlines.h"
38
39namespace JSC {
40
41class JSPromiseTaskContext : public TaskContext {
42public:
43 static PassRefPtr<JSPromiseTaskContext> create(VM& vm, JSPromise* promise)
44 {
45 return adoptRef(new JSPromiseTaskContext(vm, promise));
46 }
47
48 JSPromise& promise() const { return *m_promise.get(); }
49
50private:
51 JSPromiseTaskContext(VM& vm, JSPromise* promise)
52 {
53 m_promise.set(vm, promise);
54 }
55
56 Strong<JSPromise> m_promise;
57};
58
59const ClassInfo JSPromise::s_info = { "Promise", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSPromise) };
60
61JSPromise* JSPromise::create(VM& vm, Structure* structure)
62{
63 JSPromise* promise = new (NotNull, allocateCell<JSPromise>(vm.heap)) JSPromise(vm, structure);
64 promise->finishCreation(vm);
65 return promise;
66}
67
68JSPromise* JSPromise::createWithResolver(VM& vm, JSGlobalObject* globalObject)
69{
70 JSPromise* promise = new (NotNull, allocateCell<JSPromise>(vm.heap)) JSPromise(vm, globalObject->promiseStructure());
71 promise->finishCreation(vm);
72
73 JSPromiseResolver* resolver = JSPromiseResolver::create(vm, globalObject->promiseResolverStructure(), promise);
74 promise->setResolver(vm, resolver);
75
76 return promise;
77}
78
79Structure* JSPromise::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
80{
81 return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
82}
83
84JSPromise::JSPromise(VM& vm, Structure* structure)
85 : JSDestructibleObject(vm, structure)
86 , m_state(Pending)
87{
88}
89
90void JSPromise::finishCreation(VM& vm)
91{
92 Base::finishCreation(vm);
93 ASSERT(inherits(info()));
94}
95
96void JSPromise::destroy(JSCell* cell)
97{
98 static_cast<JSPromise*>(cell)->JSPromise::~JSPromise();
99}
100
101void JSPromise::visitChildren(JSCell* cell, SlotVisitor& visitor)
102{
103 JSPromise* thisObject = jsCast<JSPromise*>(cell);
104 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
105 COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
106 ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
107
108 Base::visitChildren(thisObject, visitor);
109
110 visitor.append(&thisObject->m_resolver);
111 visitor.append(&thisObject->m_result);
112
113 for (size_t i = 0; i < thisObject->m_fulfillCallbacks.size(); ++i)
114 visitor.append(&thisObject->m_fulfillCallbacks[i]);
115 for (size_t i = 0; i < thisObject->m_rejectCallbacks.size(); ++i)
116 visitor.append(&thisObject->m_rejectCallbacks[i]);
117}
118
119void JSPromise::setResolver(VM& vm, JSPromiseResolver* resolver)
120{
121 m_resolver.set(vm, this, resolver);
122}
123
124JSPromiseResolver* JSPromise::resolver() const
125{
126 return m_resolver.get();
127}
128
129void JSPromise::setResult(VM& vm, JSValue result)
130{
131 m_result.set(vm, this, result);
132}
133
134JSValue JSPromise::result() const
135{
136 return m_result.get();
137}
138
139void JSPromise::setState(JSPromise::State state)
140{
141 ASSERT(m_state == Pending);
142 m_state = state;
143}
144
145JSPromise::State JSPromise::state() const
146{
147 return m_state;
148}
149
150void JSPromise::appendCallbacks(ExecState* exec, InternalFunction* fulfillCallback, InternalFunction* rejectCallback)
151{
152 // 1. Append fulfillCallback to promise's fulfill callbacks.
153 m_fulfillCallbacks.append(WriteBarrier<InternalFunction>(exec->vm(), this, fulfillCallback));
154
155 // 2. Append rejectCallback to promise' reject callbacks.
156 m_rejectCallbacks.append(WriteBarrier<InternalFunction>(exec->vm(), this, rejectCallback));
157
158 // 3. If promise's state is fulfilled, queue a task to process promise's fulfill callbacks with promise's result.
159 if (m_state == Fulfilled) {
160 queueTaskToProcessFulfillCallbacks(exec);
161 return;
162 }
163
164 // 4. If promise's state is rejected, queue a task to process promise's reject callbacks with promise's result.
165 if (m_state == Rejected) {
166 queueTaskToProcessRejectCallbacks(exec);
167 return;
168 }
169}
170
171void JSPromise::queueTaskToProcessFulfillCallbacks(ExecState* exec)
172{
173 JSGlobalObject* globalObject = this->globalObject();
174 if (globalObject->globalObjectMethodTable()->queueTaskToEventLoop)
175 globalObject->globalObjectMethodTable()->queueTaskToEventLoop(globalObject, processFulfillCallbacksForTask, JSPromiseTaskContext::create(exec->vm(), this));
176 else
177 WTFLogAlways("ERROR: Event loop not supported.");
178}
179
180void JSPromise::queueTaskToProcessRejectCallbacks(ExecState* exec)
181{
182 JSGlobalObject* globalObject = this->globalObject();
183 if (globalObject->globalObjectMethodTable()->queueTaskToEventLoop)
184 globalObject->globalObjectMethodTable()->queueTaskToEventLoop(globalObject, processRejectCallbacksForTask, JSPromiseTaskContext::create(exec->vm(), this));
185 else
186 WTFLogAlways("ERROR: Event loop not supported.");
187}
188
189void JSPromise::processFulfillCallbacksForTask(ExecState* exec, TaskContext* taskContext)
190{
191 JSPromiseTaskContext* promiseTaskContext = static_cast<JSPromiseTaskContext*>(taskContext);
192 JSPromise& promise = promiseTaskContext->promise();
193
194 promise.processFulfillCallbacksWithValue(exec, promise.result());
195}
196
197void JSPromise::processRejectCallbacksForTask(ExecState* exec, TaskContext* taskContext)
198{
199 JSPromiseTaskContext* promiseTaskContext = static_cast<JSPromiseTaskContext*>(taskContext);
200 JSPromise& promise = promiseTaskContext->promise();
201
202 promise.processRejectCallbacksWithValue(exec, promise.result());
203}
204
205void JSPromise::processFulfillCallbacksWithValue(ExecState* exec, JSValue value)
206{
207 ASSERT(m_state == Fulfilled);
208
209 for (size_t i = 0; i < m_fulfillCallbacks.size(); ++i) {
210 JSValue callback = m_fulfillCallbacks[i].get();
211
212 CallData callData;
213 CallType callType = JSC::getCallData(callback, callData);
214 ASSERT(callType != CallTypeNone);
215
216 MarkedArgumentBuffer arguments;
217 arguments.append(value);
218 call(exec, callback, callType, callData, this, arguments);
219 }
220
221 m_fulfillCallbacks.clear();
222}
223
224void JSPromise::processRejectCallbacksWithValue(ExecState* exec, JSValue value)
225{
226 ASSERT(m_state == Rejected);
227
228 for (size_t i = 0; i < m_rejectCallbacks.size(); ++i) {
229 JSValue callback = m_rejectCallbacks[i].get();
230
231 CallData callData;
232 CallType callType = JSC::getCallData(callback, callData);
233 ASSERT(callType != CallTypeNone);
234
235 MarkedArgumentBuffer arguments;
236 arguments.append(value);
237 call(exec, callback, callType, callData, this, arguments);
238 }
239
240 m_rejectCallbacks.clear();
241}
242
243} // namespace JSC
weinig@apple.comb7188852013-08-29 22:54:38 +0000244
245#endif // ENABLE(PROMISES)