2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "bindings/v8/custom/V8PromiseCustom.h"
34 #include "V8Promise.h"
35 #include "bindings/v8/ScopedPersistent.h"
36 #include "bindings/v8/ScriptFunctionCall.h"
37 #include "bindings/v8/ScriptState.h"
38 #include "bindings/v8/V8Binding.h"
39 #include "bindings/v8/V8HiddenValue.h"
40 #include "bindings/v8/V8PerIsolateData.h"
41 #include "bindings/v8/V8ScriptRunner.h"
42 #include "bindings/v8/WrapperTypeInfo.h"
43 #include "core/dom/Document.h"
44 #include "core/dom/ExecutionContextTask.h"
45 #include "core/frame/DOMWindow.h"
46 #include "core/frame/UseCounter.h"
47 #include "core/inspector/InspectorPromiseInstrumentation.h"
48 #include "core/workers/WorkerGlobalScope.h"
49 #include "platform/Task.h"
50 #include "wtf/Deque.h"
51 #include "wtf/Functional.h"
52 #include "wtf/Noncopyable.h"
53 #include "wtf/PassOwnPtr.h"
56 #define TONATIVE_VOID_EMPTY(type, var, value) \
61 if (UNLIKELY(block.HasCaught())) { \
70 v8::Local<v8::ObjectTemplate> cachedObjectTemplate(void* domTemplateKey, int internalFieldCount, v8::Isolate* isolate)
72 V8PerIsolateData* data = V8PerIsolateData::from(isolate);
73 v8::Handle<v8::FunctionTemplate> functionDescriptor = data->existingDOMTemplate(domTemplateKey);
74 if (!functionDescriptor.IsEmpty())
75 return functionDescriptor->InstanceTemplate();
77 functionDescriptor = v8::FunctionTemplate::New(isolate);
78 v8::Local<v8::ObjectTemplate> instanceTemplate = functionDescriptor->InstanceTemplate();
79 instanceTemplate->SetInternalFieldCount(internalFieldCount);
80 data->setDOMTemplate(domTemplateKey, functionDescriptor);
81 return instanceTemplate;
84 v8::Local<v8::ObjectTemplate> promiseAllEnvironmentObjectTemplate(v8::Isolate* isolate)
86 static int domTemplateKey; // This address is used for a key to look up the dom template.
87 return cachedObjectTemplate(&domTemplateKey, V8PromiseCustom::PromiseAllEnvironmentFieldCount, isolate);
90 v8::Local<v8::ObjectTemplate> primitiveWrapperObjectTemplate(v8::Isolate* isolate)
92 static int domTemplateKey; // This address is used for a key to look up the dom template.
93 return cachedObjectTemplate(&domTemplateKey, V8PromiseCustom::PrimitiveWrapperFieldCount, isolate);
96 v8::Local<v8::ObjectTemplate> internalObjectTemplate(v8::Isolate* isolate)
98 static int domTemplateKey; // This address is used for a key to look up the dom template.
99 return cachedObjectTemplate(&domTemplateKey, V8PromiseCustom::InternalFieldCount, isolate);
102 void promiseResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
104 ASSERT(!info.Data().IsEmpty());
105 v8::Local<v8::Object> promise = info.Data().As<v8::Object>();
106 v8::Local<v8::Value> result = v8::Undefined(info.GetIsolate());
107 if (info.Length() > 0)
110 V8PromiseCustom::resolve(promise, result, info.GetIsolate());
113 void promiseRejectCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
115 ASSERT(!info.Data().IsEmpty());
116 v8::Local<v8::Object> promise = info.Data().As<v8::Object>();
117 v8::Local<v8::Value> result = v8::Undefined(info.GetIsolate());
118 if (info.Length() > 0)
121 V8PromiseCustom::reject(promise, result, info.GetIsolate());
124 void promiseAllFulfillCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
126 v8::Isolate* isolate = info.GetIsolate();
127 ASSERT(!info.Data().IsEmpty());
128 v8::Local<v8::Object> environment = info.Data().As<v8::Object>();
129 v8::Local<v8::Value> result = v8::Undefined(isolate);
130 if (info.Length() > 0)
133 v8::Local<v8::Object> promise = environment->GetInternalField(V8PromiseCustom::PromiseAllEnvironmentPromiseIndex).As<v8::Object>();
134 v8::Local<v8::Object> countdownWrapper = environment->GetInternalField(V8PromiseCustom::PromiseAllEnvironmentCountdownIndex).As<v8::Object>();
135 v8::Local<v8::Integer> index = environment->GetInternalField(V8PromiseCustom::PromiseAllEnvironmentIndexIndex).As<v8::Integer>();
136 v8::Local<v8::Array> results = environment->GetInternalField(V8PromiseCustom::PromiseAllEnvironmentResultsIndex).As<v8::Array>();
138 results->Set(index->Value(), result);
140 v8::Local<v8::Integer> countdown = countdownWrapper->GetInternalField(V8PromiseCustom::PrimitiveWrapperPrimitiveIndex).As<v8::Integer>();
141 ASSERT(countdown->Value() >= 1);
142 if (countdown->Value() == 1) {
143 V8PromiseCustom::resolve(promise, results, isolate);
146 countdownWrapper->SetInternalField(V8PromiseCustom::PrimitiveWrapperPrimitiveIndex, v8::Integer::New(isolate, countdown->Value() - 1));
149 v8::Local<v8::Object> promiseAllEnvironment(v8::Handle<v8::Object> promise, v8::Handle<v8::Object> countdownWrapper, int index, v8::Handle<v8::Array> results, v8::Isolate* isolate)
151 v8::Local<v8::ObjectTemplate> objectTemplate = promiseAllEnvironmentObjectTemplate(isolate);
152 v8::Local<v8::Object> environment = objectTemplate->NewInstance();
153 if (environment.IsEmpty())
154 return v8::Local<v8::Object>();
156 environment->SetInternalField(V8PromiseCustom::PromiseAllEnvironmentPromiseIndex, promise);
157 environment->SetInternalField(V8PromiseCustom::PromiseAllEnvironmentCountdownIndex, countdownWrapper);
158 environment->SetInternalField(V8PromiseCustom::PromiseAllEnvironmentIndexIndex, v8::Integer::New(isolate, index));
159 environment->SetInternalField(V8PromiseCustom::PromiseAllEnvironmentResultsIndex, results);
163 // Clear |internal|'s derived array.
164 void clearDerived(v8::Handle<v8::Object> internal, v8::Isolate* isolate)
166 internal->SetInternalField(V8PromiseCustom::InternalFulfillCallbackIndex, v8::Array::New(isolate));
167 internal->SetInternalField(V8PromiseCustom::InternalRejectCallbackIndex, v8::Array::New(isolate));
168 internal->SetInternalField(V8PromiseCustom::InternalDerivedPromiseIndex, v8::Array::New(isolate));
171 // Add a tuple (|derivedPromise|, |onFulfilled|, |onRejected|) to
172 // |internal|'s derived array.
173 // |internal| must be a Promise internal object.
174 // |derivedPromise| must be a Promise instance.
175 // |onFulfilled| and |onRejected| can be an empty value respectively.
176 void addToDerived(v8::Handle<v8::Object> internal, v8::Handle<v8::Object> derivedPromise, v8::Handle<v8::Function> onFulfilled, v8::Handle<v8::Function> onRejected, v8::Isolate* isolate)
178 v8::Local<v8::Array> fulfillCallbacks = internal->GetInternalField(V8PromiseCustom::InternalFulfillCallbackIndex).As<v8::Array>();
179 v8::Local<v8::Array> rejectCallbacks = internal->GetInternalField(V8PromiseCustom::InternalRejectCallbackIndex).As<v8::Array>();
180 v8::Local<v8::Array> derivedPromises = internal->GetInternalField(V8PromiseCustom::InternalDerivedPromiseIndex).As<v8::Array>();
182 if (onFulfilled.IsEmpty()) {
183 fulfillCallbacks->Set(fulfillCallbacks->Length(), v8::Undefined(isolate));
185 fulfillCallbacks->Set(fulfillCallbacks->Length(), onFulfilled);
188 if (onRejected.IsEmpty()) {
189 rejectCallbacks->Set(rejectCallbacks->Length(), v8::Undefined(isolate));
191 rejectCallbacks->Set(rejectCallbacks->Length(), onRejected);
194 ASSERT(!derivedPromise.IsEmpty());
195 derivedPromises->Set(derivedPromises->Length(), derivedPromise);
197 // Since they are treated as a tuple,
198 // we need to guaranteed that the length of these arrays are same.
199 ASSERT(fulfillCallbacks->Length() == rejectCallbacks->Length() && rejectCallbacks->Length() == derivedPromises->Length());
202 // Set a |promise|'s state and result that correspond to the state.
203 // |promise| must be a Promise instance.
204 void setStateForPromise(v8::Handle<v8::Object> promise, V8PromiseCustom::PromiseState state, v8::Handle<v8::Value> value, v8::Isolate* isolate)
206 ASSERT(!value.IsEmpty());
207 ASSERT(state == V8PromiseCustom::Pending || state == V8PromiseCustom::Fulfilled || state == V8PromiseCustom::Rejected || state == V8PromiseCustom::Following);
208 v8::Local<v8::Object> internal = V8PromiseCustom::getInternal(promise);
209 internal->SetInternalField(V8PromiseCustom::InternalStateIndex, v8::Integer::New(isolate, state));
210 internal->SetInternalField(V8PromiseCustom::InternalResultIndex, value);
211 ExecutionContext* context = currentExecutionContext(isolate);
212 if (InspectorInstrumentation::isPromiseTrackerEnabled(context))
213 InspectorInstrumentation::didUpdatePromiseState(context, ScriptObject(ScriptState::current(isolate), promise), state, ScriptValue(ScriptState::current(isolate), value));
216 class TaskPerformScopeForInstrumentation {
218 TaskPerformScopeForInstrumentation(ExecutionContext* context, ExecutionContextTask* task)
219 : m_cookie(InspectorInstrumentation::willPerformPromiseTask(context, task))
223 ~TaskPerformScopeForInstrumentation()
225 InspectorInstrumentation::didPerformPromiseTask(m_cookie);
229 InspectorInstrumentationCookie m_cookie;
232 class CallHandlerTask FINAL : public ExecutionContextTask {
234 CallHandlerTask(v8::Handle<v8::Object> promise, v8::Handle<v8::Function> handler, v8::Handle<v8::Value> argument, V8PromiseCustom::PromiseState originatorState, v8::Isolate* isolate, ExecutionContext* context)
235 : m_promise(isolate, promise)
236 , m_handler(isolate, handler)
237 , m_argument(isolate, argument)
238 , m_scriptState(ScriptState::current(isolate))
240 ASSERT(!m_promise.isEmpty());
241 ASSERT(!m_handler.isEmpty());
242 ASSERT(!m_argument.isEmpty());
243 InspectorInstrumentation::didPostPromiseTask(context, this, originatorState == V8PromiseCustom::Fulfilled);
245 virtual ~CallHandlerTask() { }
247 virtual void performTask(ExecutionContext*) OVERRIDE;
250 ScopedPersistent<v8::Object> m_promise;
251 ScopedPersistent<v8::Function> m_handler;
252 ScopedPersistent<v8::Value> m_argument;
253 RefPtr<ScriptState> m_scriptState;
256 void CallHandlerTask::performTask(ExecutionContext* context)
258 TaskPerformScopeForInstrumentation performTaskScope(context, this);
261 if (context->activeDOMObjectsAreStopped())
264 ScriptState::Scope scope(m_scriptState.get());
265 v8::Isolate* isolate = m_scriptState->isolate();
266 v8::Handle<v8::Value> info[] = { m_argument.newLocal(isolate) };
267 v8::TryCatch trycatch;
268 v8::Local<v8::Value> value = V8ScriptRunner::callFunction(m_handler.newLocal(isolate), context, v8::Undefined(isolate), WTF_ARRAY_LENGTH(info), info, isolate);
269 if (value.IsEmpty()) {
270 V8PromiseCustom::reject(m_promise.newLocal(isolate), trycatch.Exception(), isolate);
272 V8PromiseCustom::resolve(m_promise.newLocal(isolate), value, isolate);
276 class UpdateDerivedTask FINAL : public ExecutionContextTask {
278 UpdateDerivedTask(v8::Handle<v8::Object> promise, v8::Handle<v8::Function> onFulfilled, v8::Handle<v8::Function> onRejected, v8::Handle<v8::Object> originatorValueObject, v8::Isolate* isolate, ExecutionContext* context)
279 : m_promise(isolate, promise)
280 , m_onFulfilled(isolate, onFulfilled)
281 , m_onRejected(isolate, onRejected)
282 , m_originatorValueObject(isolate, originatorValueObject)
283 , m_scriptState(ScriptState::current(isolate))
285 ASSERT(!m_promise.isEmpty());
286 ASSERT(!m_originatorValueObject.isEmpty());
287 InspectorInstrumentation::didPostPromiseTask(context, this, true);
289 virtual ~UpdateDerivedTask() { }
291 virtual void performTask(ExecutionContext*) OVERRIDE;
294 ScopedPersistent<v8::Object> m_promise;
295 ScopedPersistent<v8::Function> m_onFulfilled;
296 ScopedPersistent<v8::Function> m_onRejected;
297 ScopedPersistent<v8::Object> m_originatorValueObject;
298 RefPtr<ScriptState> m_scriptState;
301 void UpdateDerivedTask::performTask(ExecutionContext* context)
303 TaskPerformScopeForInstrumentation performTaskScope(context, this);
306 if (context->activeDOMObjectsAreStopped())
309 ScriptState::Scope scope(m_scriptState.get());
310 v8::Isolate* isolate = m_scriptState->isolate();
311 v8::Local<v8::Object> originatorValueObject = m_originatorValueObject.newLocal(isolate);
312 v8::Local<v8::Value> coercedAlready = V8HiddenValue::getHiddenValue(isolate, originatorValueObject, V8HiddenValue::thenableHiddenPromise(isolate));
313 if (!coercedAlready.IsEmpty() && coercedAlready->IsObject()) {
314 ASSERT(V8PromiseCustom::isPromise(coercedAlready.As<v8::Object>(), isolate));
315 V8PromiseCustom::updateDerivedFromPromise(m_promise.newLocal(isolate), m_onFulfilled.newLocal(isolate), m_onRejected.newLocal(isolate), coercedAlready.As<v8::Object>(), isolate);
319 v8::Local<v8::Value> then;
320 v8::TryCatch trycatch;
321 then = originatorValueObject->Get(v8AtomicString(isolate, "then"));
322 if (then.IsEmpty()) {
323 // If calling the [[Get]] internal method threw an exception, catch it and run updateDerivedFromReason.
324 V8PromiseCustom::updateDerivedFromReason(m_promise.newLocal(isolate), m_onRejected.newLocal(isolate), trycatch.Exception(), isolate);
328 if (then->IsFunction()) {
329 ASSERT(then->IsObject());
330 v8::Local<v8::Object> coerced = V8PromiseCustom::coerceThenable(originatorValueObject, then.As<v8::Function>(), isolate);
331 // If the stack is exhausted coerced can be empty, but it is impossible
332 // because this function is executed on a fresh stack.
333 ASSERT(!coerced.IsEmpty());
334 V8PromiseCustom::updateDerivedFromPromise(m_promise.newLocal(isolate), m_onFulfilled.newLocal(isolate), m_onRejected.newLocal(isolate), coerced, isolate);
338 V8PromiseCustom::updateDerivedFromValue(m_promise.newLocal(isolate), m_onFulfilled.newLocal(isolate), originatorValueObject, isolate);
341 // Since Promises state propagation routines are executed recursively, they cause
343 // (e.g. UpdateDerived -> UpdateDerivedFromValue -> SetValue ->
344 // PropagateToDerived -> UpdateDerived)
346 // To fix that, we introduce PromisePropagator. It holds the stack. When
347 // propagating the result to the derived tuples, we append the derived tuples
348 // to the stack. After that, we drain the stack to propagate the result to
349 // the stored tuples.
351 // PromisePropagator should be held on the stack and should not be held
352 // as other object's member. PromisePropagator holds Derived tuples. Since
353 // Derived tuples hold persistent handles to JS objects, retaining
354 // PromisePropagator in the heap causes memory leaks.
356 class PromisePropagator {
357 WTF_MAKE_NONCOPYABLE(PromisePropagator);
359 PromisePropagator() { }
361 void performPropagation(v8::Isolate*);
363 void setValue(v8::Handle<v8::Object> promise, v8::Handle<v8::Value>, v8::Isolate*);
364 void setReason(v8::Handle<v8::Object> promise, v8::Handle<v8::Value>, v8::Isolate*);
365 void propagateToDerived(v8::Handle<v8::Object> promise, v8::Isolate*);
366 void updateDerived(v8::Handle<v8::Object> derivedPromise, v8::Handle<v8::Function> onFulfilled, v8::Handle<v8::Function> onRejected, v8::Handle<v8::Object> originator, v8::Isolate*);
367 void updateDerivedFromValue(v8::Handle<v8::Object> derivedPromise, v8::Handle<v8::Function> onFulfilled, v8::Handle<v8::Value>, v8::Isolate*);
368 void updateDerivedFromReason(v8::Handle<v8::Object> derivedPromise, v8::Handle<v8::Function> onRejected, v8::Handle<v8::Value>, v8::Isolate*);
369 void updateDerivedFromPromise(v8::Handle<v8::Object> derivedPromise, v8::Handle<v8::Function> onFulfilled, v8::Handle<v8::Function> onRejected, v8::Handle<v8::Object> promise, v8::Isolate*);
373 WTF_MAKE_NONCOPYABLE(Derived);
375 Derived(v8::Handle<v8::Object> promise, v8::Handle<v8::Function> onFulfilled, v8::Handle<v8::Function> onRejected, v8::Handle<v8::Object> originator, v8::Isolate* isolate)
376 : m_promise(isolate, promise)
377 , m_onFulfilled(isolate, onFulfilled)
378 , m_onRejected(isolate, onRejected)
379 , m_originator(isolate, originator)
381 ASSERT(!m_promise.isEmpty());
382 ASSERT(!m_originator.isEmpty());
385 static PassOwnPtr<Derived> create(v8::Handle<v8::Object> promise, v8::Handle<v8::Function> onFulfilled, v8::Handle<v8::Function> onRejected, v8::Handle<v8::Object> originator, v8::Isolate* isolate)
387 return adoptPtr(new Derived(promise, onFulfilled, onRejected, originator, isolate));
390 v8::Local<v8::Object> promise(v8::Isolate* isolate) const { return m_promise.newLocal(isolate); }
391 v8::Local<v8::Function> onFulfilled(v8::Isolate* isolate) const { return m_onFulfilled.newLocal(isolate); }
392 v8::Local<v8::Function> onRejected(v8::Isolate* isolate) const { return m_onRejected.newLocal(isolate); }
393 v8::Local<v8::Object> originator(v8::Isolate* isolate) const { return m_originator.newLocal(isolate); }
396 ScopedPersistent<v8::Object> m_promise;
397 ScopedPersistent<v8::Function> m_onFulfilled;
398 ScopedPersistent<v8::Function> m_onRejected;
399 ScopedPersistent<v8::Object> m_originator;
402 Deque<OwnPtr<Derived> > m_derivedStack;
405 void PromisePropagator::performPropagation(v8::Isolate* isolate)
407 while (!m_derivedStack.isEmpty()) {
408 v8::HandleScope handleScope(isolate);
409 OwnPtr<Derived> derived = m_derivedStack.takeLast();
410 updateDerived(derived->promise(isolate), derived->onFulfilled(isolate), derived->onRejected(isolate), derived->originator(isolate), isolate);
414 void PromisePropagator::setValue(v8::Handle<v8::Object> promise, v8::Handle<v8::Value> value, v8::Isolate* isolate)
416 ASSERT(V8PromiseCustom::getState(V8PromiseCustom::getInternal(promise)) != V8PromiseCustom::Fulfilled && V8PromiseCustom::getState(V8PromiseCustom::getInternal(promise)) != V8PromiseCustom::Rejected);
417 setStateForPromise(promise, V8PromiseCustom::Fulfilled, value, isolate);
418 propagateToDerived(promise, isolate);
421 void PromisePropagator::setReason(v8::Handle<v8::Object> promise, v8::Handle<v8::Value> reason, v8::Isolate* isolate)
423 ASSERT(V8PromiseCustom::getState(V8PromiseCustom::getInternal(promise)) != V8PromiseCustom::Fulfilled && V8PromiseCustom::getState(V8PromiseCustom::getInternal(promise)) != V8PromiseCustom::Rejected);
424 setStateForPromise(promise, V8PromiseCustom::Rejected, reason, isolate);
425 propagateToDerived(promise, isolate);
428 void PromisePropagator::propagateToDerived(v8::Handle<v8::Object> promise, v8::Isolate* isolate)
430 v8::Local<v8::Object> internal = V8PromiseCustom::getInternal(promise);
431 ASSERT(V8PromiseCustom::getState(internal) == V8PromiseCustom::Fulfilled || V8PromiseCustom::getState(internal) == V8PromiseCustom::Rejected);
432 v8::Local<v8::Array> fulfillCallbacks = internal->GetInternalField(V8PromiseCustom::InternalFulfillCallbackIndex).As<v8::Array>();
433 v8::Local<v8::Array> rejectCallbacks = internal->GetInternalField(V8PromiseCustom::InternalRejectCallbackIndex).As<v8::Array>();
434 v8::Local<v8::Array> derivedPromises = internal->GetInternalField(V8PromiseCustom::InternalDerivedPromiseIndex).As<v8::Array>();
435 // Since they are treated as a tuple,
436 // we need to guaranteed that the length of these arrays are same.
437 ASSERT(fulfillCallbacks->Length() == rejectCallbacks->Length() && rejectCallbacks->Length() == derivedPromises->Length());
439 // Append Derived tuple to the stack in reverse order.
440 for (uint32_t count = 0, length = derivedPromises->Length(); count < length; ++count) {
441 uint32_t i = length - count - 1;
442 v8::Local<v8::Object> derivedPromise = derivedPromises->Get(i).As<v8::Object>();
444 v8::Local<v8::Function> onFulfilled, onRejected;
445 v8::Local<v8::Value> onFulfilledValue = fulfillCallbacks->Get(i);
446 if (onFulfilledValue->IsFunction()) {
447 onFulfilled = onFulfilledValue.As<v8::Function>();
449 v8::Local<v8::Value> onRejectedValue = rejectCallbacks->Get(i);
450 if (onRejectedValue->IsFunction()) {
451 onRejected = onRejectedValue.As<v8::Function>();
454 m_derivedStack.append(Derived::create(derivedPromise, onFulfilled, onRejected, promise, isolate));
456 clearDerived(internal, isolate);
459 void PromisePropagator::updateDerivedFromValue(v8::Handle<v8::Object> derivedPromise, v8::Handle<v8::Function> onFulfilled, v8::Handle<v8::Value> value, v8::Isolate* isolate)
461 if (!onFulfilled.IsEmpty()) {
462 V8PromiseCustom::callHandler(derivedPromise, onFulfilled, value, V8PromiseCustom::Fulfilled, isolate);
464 setValue(derivedPromise, value, isolate);
468 void PromisePropagator::updateDerivedFromReason(v8::Handle<v8::Object> derivedPromise, v8::Handle<v8::Function> onRejected, v8::Handle<v8::Value> reason, v8::Isolate* isolate)
470 if (!onRejected.IsEmpty()) {
471 V8PromiseCustom::callHandler(derivedPromise, onRejected, reason, V8PromiseCustom::Rejected, isolate);
473 setReason(derivedPromise, reason, isolate);
477 void PromisePropagator::updateDerived(v8::Handle<v8::Object> derivedPromise, v8::Handle<v8::Function> onFulfilled, v8::Handle<v8::Function> onRejected, v8::Handle<v8::Object> originator, v8::Isolate* isolate)
479 v8::Local<v8::Object> originatorInternal = V8PromiseCustom::getInternal(originator);
480 V8PromiseCustom::PromiseState originatorState = V8PromiseCustom::getState(originatorInternal);
481 ASSERT(originatorState == V8PromiseCustom::Fulfilled || originatorState == V8PromiseCustom::Rejected);
482 v8::Local<v8::Value> originatorValue = originatorInternal->GetInternalField(V8PromiseCustom::InternalResultIndex);
483 if (originatorState == V8PromiseCustom::Fulfilled) {
484 if (originatorValue->IsObject()) {
485 ExecutionContext* executionContext = currentExecutionContext(isolate);
486 ASSERT(executionContext && executionContext->isContextThread());
487 executionContext->postTask(adoptPtr(new UpdateDerivedTask(derivedPromise, onFulfilled, onRejected, originatorValue.As<v8::Object>(), isolate, executionContext)));
489 updateDerivedFromValue(derivedPromise, onFulfilled, originatorValue, isolate);
492 updateDerivedFromReason(derivedPromise, onRejected, originatorValue, isolate);
496 void PromisePropagator::updateDerivedFromPromise(v8::Handle<v8::Object> derivedPromise, v8::Handle<v8::Function> onFulfilled, v8::Handle<v8::Function> onRejected, v8::Handle<v8::Object> promise, v8::Isolate* isolate)
498 v8::Local<v8::Object> internal = V8PromiseCustom::getInternal(promise);
499 V8PromiseCustom::PromiseState state = V8PromiseCustom::getState(internal);
500 if (state == V8PromiseCustom::Fulfilled || state == V8PromiseCustom::Rejected) {
501 updateDerived(derivedPromise, onFulfilled, onRejected, promise, isolate);
503 addToDerived(internal, derivedPromise, onFulfilled, onRejected, isolate);
505 ExecutionContext* context = currentExecutionContext(isolate);
506 if (InspectorInstrumentation::isPromiseTrackerEnabled(context)) {
507 ScriptState* scriptState = ScriptState::current(isolate);
508 InspectorInstrumentation::didUpdatePromiseParent(context, ScriptObject(scriptState, derivedPromise), ScriptObject(scriptState, promise));
514 void V8Promise::constructorCustom(const v8::FunctionCallbackInfo<v8::Value>& info)
516 v8SetReturnValue(info, v8::Local<v8::Value>());
517 v8::Isolate* isolate = info.GetIsolate();
518 ExecutionContext* executionContext = callingExecutionContext(isolate);
519 UseCounter::count(executionContext, UseCounter::PromiseConstructor);
520 if (!info.Length() || !info[0]->IsFunction()) {
521 throwTypeError("Promise constructor takes a function argument", isolate);
524 v8::Local<v8::Function> init = info[0].As<v8::Function>();
525 TONATIVE_VOID_EMPTY(v8::Local<v8::Object>, promise, V8PromiseCustom::createPromise(info.Holder(), isolate));
526 TONATIVE_VOID_EMPTY(v8::Handle<v8::Value>, resolve, createClosure(promiseResolveCallback, promise, isolate));
527 TONATIVE_VOID_EMPTY(v8::Handle<v8::Value>, reject, createClosure(promiseRejectCallback, promise, isolate));
528 v8::Handle<v8::Value> argv[] = { resolve, reject };
529 v8::TryCatch trycatch;
530 if (V8ScriptRunner::callFunction(init, currentExecutionContext(isolate), v8::Undefined(isolate), WTF_ARRAY_LENGTH(argv), argv, isolate).IsEmpty()) {
531 // An exception is thrown. Reject the promise if its resolved flag is unset.
532 V8PromiseCustom::reject(promise, trycatch.Exception(), isolate);
534 v8SetReturnValue(info, promise);
538 void V8Promise::thenMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& info)
540 v8::Isolate* isolate = info.GetIsolate();
541 v8::Local<v8::Function> onFulfilled, onRejected;
542 if (info.Length() > 0 && info[0]->IsFunction())
543 onFulfilled = info[0].As<v8::Function>();
544 if (info.Length() > 1 && info[1]->IsFunction())
545 onRejected = info[1].As<v8::Function>();
546 TONATIVE_VOID_EMPTY(v8::Local<v8::Value>, newPromise, V8PromiseCustom::then(info.Holder(), onFulfilled, onRejected, isolate));
547 v8SetReturnValue(info, newPromise);
550 void V8Promise::castMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& info)
552 v8::Isolate* isolate = info.GetIsolate();
553 ExecutionContext* executionContext = callingExecutionContext(isolate);
554 UseCounter::count(executionContext, UseCounter::PromiseCast);
555 v8::Local<v8::Value> result = v8::Undefined(isolate);
556 if (info.Length() > 0)
559 TONATIVE_VOID_EMPTY(v8::Local<v8::Value>, cast, V8PromiseCustom::toPromise(result, isolate));
560 v8SetReturnValue(info, cast);
563 void V8Promise::catchMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& info)
565 v8::Isolate* isolate = info.GetIsolate();
566 v8::Local<v8::Function> onFulfilled, onRejected;
568 if (info.Length() > 0 && !info[0]->IsUndefined()) {
569 if (!info[0]->IsFunction()) {
570 v8SetReturnValue(info, throwTypeError("onRejected must be a function or undefined", isolate));
573 onRejected = info[0].As<v8::Function>();
575 TONATIVE_VOID_EMPTY(v8::Local<v8::Value>, newPromise, V8PromiseCustom::then(info.Holder(), onFulfilled, onRejected, isolate));
576 v8SetReturnValue(info, newPromise);
579 void V8Promise::resolveMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& info)
581 v8::Isolate* isolate = info.GetIsolate();
582 ExecutionContext* executionContext = callingExecutionContext(isolate);
583 UseCounter::count(executionContext, UseCounter::PromiseResolve);
584 v8::Local<v8::Value> result = v8::Undefined(isolate);
585 if (info.Length() > 0)
588 TONATIVE_VOID_EMPTY(v8::Local<v8::Object>, promise, V8PromiseCustom::createPromise(info.Holder(), isolate));
589 V8PromiseCustom::resolve(promise, result, isolate);
590 v8SetReturnValue(info, promise);
593 void V8Promise::rejectMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& info)
595 v8::Isolate* isolate = info.GetIsolate();
596 ExecutionContext* executionContext = callingExecutionContext(isolate);
597 UseCounter::count(executionContext, UseCounter::PromiseReject);
598 v8::Local<v8::Value> result = v8::Undefined(isolate);
599 if (info.Length() > 0)
602 TONATIVE_VOID_EMPTY(v8::Local<v8::Object>, promise, V8PromiseCustom::createPromise(info.Holder(), isolate));
603 V8PromiseCustom::reject(promise, result, isolate);
604 v8SetReturnValue(info, promise);
607 void V8Promise::raceMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& info)
609 v8::Isolate* isolate = info.GetIsolate();
610 TONATIVE_VOID_EMPTY(v8::Local<v8::Object>, promise, V8PromiseCustom::createPromise(info.Holder(), isolate));
612 if (!info.Length() || !info[0]->IsArray()) {
613 v8SetReturnValue(info, promise);
617 // FIXME: Now we limit the iterable type to the Array type.
618 v8::Local<v8::Array> iterable = info[0].As<v8::Array>();
619 TONATIVE_VOID_EMPTY(v8::Local<v8::Function>, onFulfilled, createClosure(promiseResolveCallback, promise, isolate));
620 TONATIVE_VOID_EMPTY(v8::Local<v8::Function>, onRejected, createClosure(promiseRejectCallback, promise, isolate));
622 for (unsigned i = 0, length = iterable->Length(); i < length; ++i) {
623 // Array-holes should not be skipped by for-of iteration semantics.
624 TONATIVE_VOID_EMPTY(v8::Local<v8::Value>, nextValue, iterable->Get(i));
625 TONATIVE_VOID_EMPTY(v8::Local<v8::Object>, nextPromise, V8PromiseCustom::toPromise(nextValue, isolate));
626 TONATIVE_VOID_EMPTY(v8::Local<v8::Value>, unused, V8PromiseCustom::then(nextPromise, onFulfilled, onRejected, isolate));
628 v8SetReturnValue(info, promise);
631 void V8Promise::allMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& info)
633 v8::Isolate* isolate = info.GetIsolate();
634 TONATIVE_VOID_EMPTY(v8::Local<v8::Object>, promise, V8PromiseCustom::createPromise(info.Holder(), isolate));
635 v8::Local<v8::Array> results = v8::Array::New(info.GetIsolate());
637 if (!info.Length() || !info[0]->IsArray()) {
638 V8PromiseCustom::resolve(promise, results, isolate);
639 v8SetReturnValue(info, promise);
643 // FIXME: Now we limit the iterable type to the Array type.
644 v8::Local<v8::Array> iterable = info[0].As<v8::Array>();
646 if (!iterable->Length()) {
647 V8PromiseCustom::resolve(promise, results, isolate);
648 v8SetReturnValue(info, promise);
652 v8::Local<v8::ObjectTemplate> objectTemplate = primitiveWrapperObjectTemplate(isolate);
653 TONATIVE_VOID_EMPTY(v8::Local<v8::Object>, countdownWrapper, objectTemplate->NewInstance());
654 countdownWrapper->SetInternalField(V8PromiseCustom::PrimitiveWrapperPrimitiveIndex, v8::Integer::New(isolate, iterable->Length()));
656 TONATIVE_VOID_EMPTY(v8::Local<v8::Function>, onRejected, createClosure(promiseRejectCallback, promise, isolate));
657 for (unsigned i = 0, length = iterable->Length(); i < length; ++i) {
658 // Array-holes should not be skipped by for-of iteration semantics.
659 TONATIVE_VOID_EMPTY(v8::Local<v8::Object>, environment, promiseAllEnvironment(promise, countdownWrapper, i, results, isolate));
660 TONATIVE_VOID_EMPTY(v8::Local<v8::Function>, onFulfilled, createClosure(promiseAllFulfillCallback, environment, isolate));
661 TONATIVE_VOID_EMPTY(v8::Local<v8::Value>, nextValue, iterable->Get(i));
662 TONATIVE_VOID_EMPTY(v8::Local<v8::Object>, nextPromise, V8PromiseCustom::toPromise(nextValue, isolate));
663 TONATIVE_VOID_EMPTY(v8::Local<v8::Value>, unused, V8PromiseCustom::then(nextPromise, onFulfilled, onRejected, isolate));
665 v8SetReturnValue(info, promise);
669 // -- V8PromiseCustom --
670 v8::Local<v8::Object> V8PromiseCustom::createPromise(v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
672 v8::Local<v8::ObjectTemplate> internalTemplate = internalObjectTemplate(isolate);
673 v8::Local<v8::Object> internal = internalTemplate->NewInstance();
674 if (internal.IsEmpty())
675 return v8::Local<v8::Object>();
676 v8::Local<v8::Object> promise = V8DOMWrapper::createWrapper(creationContext, &V8Promise::wrapperTypeInfo, 0, isolate);
678 clearDerived(internal, isolate);
679 promise->SetInternalField(v8DOMWrapperObjectIndex, internal);
681 ExecutionContext* context = currentExecutionContext(isolate);
682 if (InspectorInstrumentation::isPromiseTrackerEnabled(context))
683 InspectorInstrumentation::didCreatePromise(context, ScriptObject(ScriptState::current(isolate), promise));
685 setStateForPromise(promise, Pending, v8::Undefined(isolate), isolate);
689 v8::Local<v8::Object> V8PromiseCustom::getInternal(v8::Handle<v8::Object> promise)
691 v8::Local<v8::Value> value = promise->GetInternalField(v8DOMWrapperObjectIndex);
692 return value.As<v8::Object>();
695 V8PromiseCustom::PromiseState V8PromiseCustom::getState(v8::Handle<v8::Object> internal)
697 v8::Handle<v8::Value> value = internal->GetInternalField(V8PromiseCustom::InternalStateIndex);
698 uint32_t number = toInt32(value);
699 ASSERT(number == Pending || number == Fulfilled || number == Rejected || number == Following);
700 return static_cast<PromiseState>(number);
703 bool V8PromiseCustom::isPromise(v8::Handle<v8::Value> maybePromise, v8::Isolate* isolate)
705 return V8Promise::domTemplate(isolate)->HasInstance(maybePromise);
708 v8::Local<v8::Object> V8PromiseCustom::toPromise(v8::Handle<v8::Value> maybePromise, v8::Isolate* isolate)
710 // FIXME: Currently we don't check [[PromiseConstructor]] since we limit
711 // the creation of the promise objects only from the Blink Promise
713 if (isPromise(maybePromise, isolate))
714 return maybePromise.As<v8::Object>();
716 v8::Local<v8::Object> promise = createPromise(v8::Handle<v8::Object>(), isolate);
717 if (promise.IsEmpty())
718 return v8::Local<v8::Object>();
719 resolve(promise, maybePromise, isolate);
723 void V8PromiseCustom::resolve(v8::Handle<v8::Object> promise, v8::Handle<v8::Value> result, v8::Isolate* isolate)
725 ASSERT(!result.IsEmpty());
726 v8::Local<v8::Object> internal = getInternal(promise);
727 PromiseState state = getState(internal);
728 if (state != Pending)
731 if (isPromise(result, isolate)) {
732 v8::Local<v8::Object> valuePromise = result.As<v8::Object>();
733 v8::Local<v8::Object> valueInternal = getInternal(valuePromise);
734 PromiseState valueState = getState(valueInternal);
735 if (promise->SameValue(valuePromise)) {
736 v8::Local<v8::Value> reason = V8ThrowException::createTypeError("Resolve a promise with itself", isolate);
737 setReason(promise, reason, isolate);
738 } else if (valueState == Following) {
739 v8::Local<v8::Object> valuePromiseFollowing = valueInternal->GetInternalField(InternalResultIndex).As<v8::Object>();
740 setStateForPromise(promise, Following, valuePromiseFollowing, isolate);
741 addToDerived(getInternal(valuePromiseFollowing), promise, v8::Handle<v8::Function>(), v8::Handle<v8::Function>(), isolate);
742 } else if (valueState == Fulfilled) {
743 setValue(promise, valueInternal->GetInternalField(InternalResultIndex), isolate);
744 } else if (valueState == Rejected) {
745 setReason(promise, valueInternal->GetInternalField(InternalResultIndex), isolate);
747 ASSERT(valueState == Pending);
748 setStateForPromise(promise, Following, valuePromise, isolate);
749 addToDerived(valueInternal, promise, v8::Handle<v8::Function>(), v8::Handle<v8::Function>(), isolate);
752 setValue(promise, result, isolate);
756 void V8PromiseCustom::reject(v8::Handle<v8::Object> promise, v8::Handle<v8::Value> reason, v8::Isolate* isolate)
758 v8::Local<v8::Object> internal = getInternal(promise);
759 PromiseState state = getState(internal);
760 if (state != Pending)
762 setReason(promise, reason, isolate);
765 v8::Local<v8::Object> V8PromiseCustom::then(v8::Handle<v8::Object> promise, v8::Handle<v8::Function> onFulfilled, v8::Handle<v8::Function> onRejected, v8::Isolate* isolate)
767 v8::Handle<v8::Object> internal = getInternal(promise);
768 while (getState(internal) == Following) {
769 promise = internal->GetInternalField(InternalResultIndex).As<v8::Object>();
770 internal = getInternal(promise);
772 // FIXME: Currently we don't lookup "constructor" property since we limit
773 // the creation of the promise objects only from the Blink Promise
775 v8::Local<v8::Object> derivedPromise = createPromise(v8::Handle<v8::Object>(), isolate);
776 if (derivedPromise.IsEmpty())
777 return v8::Local<v8::Object>();
778 updateDerivedFromPromise(derivedPromise, onFulfilled, onRejected, promise, isolate);
779 return derivedPromise;
782 void V8PromiseCustom::setValue(v8::Handle<v8::Object> promise, v8::Handle<v8::Value> value, v8::Isolate* isolate)
784 PromisePropagator propagator;
785 propagator.setValue(promise, value, isolate);
786 propagator.performPropagation(isolate);
789 void V8PromiseCustom::setReason(v8::Handle<v8::Object> promise, v8::Handle<v8::Value> reason, v8::Isolate* isolate)
791 PromisePropagator propagator;
792 propagator.setReason(promise, reason, isolate);
793 propagator.performPropagation(isolate);
796 void V8PromiseCustom::propagateToDerived(v8::Handle<v8::Object> promise, v8::Isolate* isolate)
798 PromisePropagator propagator;
799 propagator.propagateToDerived(promise, isolate);
800 propagator.performPropagation(isolate);
803 void V8PromiseCustom::updateDerived(v8::Handle<v8::Object> derivedPromise, v8::Handle<v8::Function> onFulfilled, v8::Handle<v8::Function> onRejected, v8::Handle<v8::Object> originator, v8::Isolate* isolate)
805 PromisePropagator propagator;
806 propagator.updateDerived(derivedPromise, onFulfilled, onRejected, originator, isolate);
807 propagator.performPropagation(isolate);
810 void V8PromiseCustom::updateDerivedFromValue(v8::Handle<v8::Object> derivedPromise, v8::Handle<v8::Function> onFulfilled, v8::Handle<v8::Value> value, v8::Isolate* isolate)
812 PromisePropagator propagator;
813 propagator.updateDerivedFromValue(derivedPromise, onFulfilled, value, isolate);
814 propagator.performPropagation(isolate);
817 void V8PromiseCustom::updateDerivedFromReason(v8::Handle<v8::Object> derivedPromise, v8::Handle<v8::Function> onRejected, v8::Handle<v8::Value> reason, v8::Isolate* isolate)
819 PromisePropagator propagator;
820 propagator.updateDerivedFromReason(derivedPromise, onRejected, reason, isolate);
821 propagator.performPropagation(isolate);
824 void V8PromiseCustom::updateDerivedFromPromise(v8::Handle<v8::Object> derivedPromise, v8::Handle<v8::Function> onFulfilled, v8::Handle<v8::Function> onRejected, v8::Handle<v8::Object> promise, v8::Isolate* isolate)
826 PromisePropagator propagator;
827 propagator.updateDerivedFromPromise(derivedPromise, onFulfilled, onRejected, promise, isolate);
828 propagator.performPropagation(isolate);
831 v8::Local<v8::Object> V8PromiseCustom::coerceThenable(v8::Handle<v8::Object> thenable, v8::Handle<v8::Function> then, v8::Isolate* isolate)
833 ASSERT(!thenable.IsEmpty());
834 ASSERT(!then.IsEmpty());
835 v8::Local<v8::Object> promise = createPromise(v8::Handle<v8::Object>(), isolate);
836 if (promise.IsEmpty())
837 return v8::Local<v8::Object>();
838 v8::Handle<v8::Value> onFulfilled = createClosure(promiseResolveCallback, promise, isolate);
839 if (onFulfilled.IsEmpty())
840 return v8::Local<v8::Object>();
841 v8::Handle<v8::Value> onRejected = createClosure(promiseRejectCallback, promise, isolate);
842 if (onRejected.IsEmpty())
843 return v8::Local<v8::Object>();
844 v8::Handle<v8::Value> argv[] = { onFulfilled, onRejected };
846 v8::TryCatch trycatch;
847 if (V8ScriptRunner::callFunction(then, currentExecutionContext(isolate), thenable, WTF_ARRAY_LENGTH(argv), argv, isolate).IsEmpty()) {
848 reject(promise, trycatch.Exception(), isolate);
850 V8HiddenValue::setHiddenValue(isolate, thenable, V8HiddenValue::thenableHiddenPromise(isolate), promise);
854 void V8PromiseCustom::callHandler(v8::Handle<v8::Object> promise, v8::Handle<v8::Function> handler, v8::Handle<v8::Value> argument, PromiseState originatorState, v8::Isolate* isolate)
856 ASSERT(originatorState == Fulfilled || originatorState == Rejected);
857 ExecutionContext* executionContext = currentExecutionContext(isolate);
858 ASSERT(executionContext && executionContext->isContextThread());
859 executionContext->postTask(adoptPtr(new CallHandlerTask(promise, handler, argument, originatorState, isolate, executionContext)));
862 } // namespace WebCore