Chromium Code Reviews| Index: Source/bindings/v8/custom/V8PromiseCustom.cpp |
| diff --git a/Source/bindings/v8/custom/V8PromiseCustom.cpp b/Source/bindings/v8/custom/V8PromiseCustom.cpp |
| index b3b278fef7d037fcc10948ce36e59bc83d94bd56..02e2f28e9a4a0e7f5a819c677ee1cf9b9ee78618 100644 |
| --- a/Source/bindings/v8/custom/V8PromiseCustom.cpp |
| +++ b/Source/bindings/v8/custom/V8PromiseCustom.cpp |
| @@ -36,6 +36,7 @@ |
| #include "bindings/v8/ScriptFunctionCall.h" |
| #include "bindings/v8/ScriptState.h" |
| #include "bindings/v8/V8Binding.h" |
| +#include "bindings/v8/V8HiddenPropertyName.h" |
| #include "bindings/v8/V8PerIsolateData.h" |
| #include "bindings/v8/V8ScriptRunner.h" |
| #include "bindings/v8/WrapperTypeInfo.h" |
| @@ -66,13 +67,6 @@ v8::Local<v8::ObjectTemplate> cachedObjectTemplate(void* privateTemplateUniqueKe |
| return instanceTemplate; |
| } |
| -v8::Local<v8::ObjectTemplate> wrapperCallbackEnvironmentObjectTemplate(v8::Isolate* isolate) |
| -{ |
| - // This is only for getting a unique pointer which we can pass to privateTemplate. |
| - static int privateTemplateUniqueKey = 0; |
| - return cachedObjectTemplate(&privateTemplateUniqueKey, V8PromiseCustom::WrapperCallbackEnvironmentFieldCount, isolate); |
| -} |
| - |
| v8::Local<v8::ObjectTemplate> promiseAllEnvironmentObjectTemplate(v8::Isolate* isolate) |
| { |
| // This is only for getting a unique pointer which we can pass to privateTemplate. |
| @@ -94,102 +88,6 @@ v8::Local<v8::ObjectTemplate> internalObjectTemplate(v8::Isolate* isolate) |
| return cachedObjectTemplate(&privateTemplateUniqueKey, V8PromiseCustom::InternalFieldCount, isolate); |
| } |
| -class PromiseTask : public ScriptExecutionContext::Task { |
| -public: |
| - PromiseTask(v8::Handle<v8::Function> callback, v8::Handle<v8::Object> receiver, v8::Handle<v8::Value> result, v8::Isolate* isolate) |
| - : m_callback(isolate, callback) |
| - , m_receiver(isolate, receiver) |
| - , m_result(isolate, result) |
| - { |
| - ASSERT(!m_callback.isEmpty()); |
| - ASSERT(!m_receiver.isEmpty()); |
| - ASSERT(!m_result.isEmpty()); |
| - } |
| - virtual ~PromiseTask() { } |
| - |
| - virtual void performTask(ScriptExecutionContext*) OVERRIDE; |
| - |
| -private: |
| - ScopedPersistent<v8::Function> m_callback; |
| - ScopedPersistent<v8::Object> m_receiver; |
| - ScopedPersistent<v8::Value> m_result; |
| -}; |
| - |
| -void PromiseTask::performTask(ScriptExecutionContext* context) |
| -{ |
| - ASSERT(context); |
| - if (context->activeDOMObjectsAreStopped()) |
| - return; |
| - |
| - ScriptState* state = 0; |
| - if (context->isDocument()) { |
| - state = mainWorldScriptState(static_cast<Document*>(context)->frame()); |
| - } else { |
| - ASSERT(context->isWorkerGlobalScope()); |
| - state = scriptStateFromWorkerGlobalScope(toWorkerGlobalScope(context)); |
| - } |
| - ASSERT(state); |
| - |
| - v8::Isolate* isolate = state->isolate(); |
| - v8::HandleScope handleScope(isolate); |
| - v8::Handle<v8::Context> v8Context = state->context(); |
| - v8::Context::Scope scope(v8Context); |
| - v8::Handle<v8::Value> args[] = { m_result.newLocal(isolate) }; |
| - V8ScriptRunner::callFunction(m_callback.newLocal(isolate), context, m_receiver.newLocal(isolate), WTF_ARRAY_LENGTH(args), args, isolate); |
| -}; |
| - |
| -v8::Handle<v8::Value> postTask(v8::Handle<v8::Function> callback, v8::Handle<v8::Object> receiver, v8::Handle<v8::Value> value, v8::Isolate* isolate) |
| -{ |
| - ScriptExecutionContext* scriptExecutionContext = getScriptExecutionContext(); |
| - ASSERT(scriptExecutionContext && scriptExecutionContext->isContextThread()); |
| - scriptExecutionContext->postTask(adoptPtr(new PromiseTask(callback, receiver, value, isolate))); |
| - return v8::Undefined(isolate); |
| -} |
| - |
| -void wrapperCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
| -{ |
| - v8::Isolate* isolate = args.GetIsolate(); |
| - ASSERT(!args.Data().IsEmpty()); |
| - v8::Local<v8::Object> environment = args.Data().As<v8::Object>(); |
| - v8::Local<v8::Value> result = v8::Undefined(isolate); |
| - if (args.Length() > 0) |
| - result = args[0]; |
| - |
| - v8::Local<v8::Object> promise = environment->GetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentPromiseIndex).As<v8::Object>(); |
| - v8::Local<v8::Function> callback = environment->GetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentCallbackIndex).As<v8::Function>(); |
| - |
| - v8::Local<v8::Value> argv[] = { |
| - result, |
| - }; |
| - v8::TryCatch trycatch; |
| - result = V8ScriptRunner::callFunction(callback, getScriptExecutionContext(), promise, WTF_ARRAY_LENGTH(argv), argv, isolate); |
| - if (result.IsEmpty()) { |
| - V8PromiseCustom::reject(promise, trycatch.Exception(), V8PromiseCustom::Synchronous, isolate); |
| - return; |
| - } |
| - V8PromiseCustom::resolve(promise, result, V8PromiseCustom::Synchronous, isolate); |
| -} |
| - |
| -v8::Local<v8::Object> wrapperCallbackEnvironment(v8::Handle<v8::Object> promise, v8::Handle<v8::Function> callback, v8::Isolate* isolate) |
| -{ |
| - v8::Local<v8::ObjectTemplate> objectTemplate = wrapperCallbackEnvironmentObjectTemplate(isolate); |
| - v8::Local<v8::Object> environment = objectTemplate->NewInstance(); |
| - environment->SetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentPromiseIndex, promise); |
| - environment->SetInternalField(V8PromiseCustom::WrapperCallbackEnvironmentCallbackIndex, callback); |
| - return environment; |
| -} |
| - |
| -void promiseFulfillCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
| -{ |
| - ASSERT(!args.Data().IsEmpty()); |
| - v8::Local<v8::Object> promise = args.Data().As<v8::Object>(); |
| - v8::Local<v8::Value> result = v8::Undefined(args.GetIsolate()); |
| - if (args.Length() > 0) |
| - result = args[0]; |
| - |
| - V8PromiseCustom::fulfill(promise, result, V8PromiseCustom::Synchronous, args.GetIsolate()); |
| -} |
| - |
| void promiseResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
| { |
| ASSERT(!args.Data().IsEmpty()); |
| @@ -198,7 +96,7 @@ void promiseResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
| if (args.Length() > 0) |
| result = args[0]; |
| - V8PromiseCustom::resolve(promise, result, V8PromiseCustom::Synchronous, args.GetIsolate()); |
| + V8PromiseCustom::resolve(promise, result, args.GetIsolate()); |
| } |
| void promiseRejectCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
| @@ -209,17 +107,7 @@ void promiseRejectCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
| if (args.Length() > 0) |
| result = args[0]; |
| - V8PromiseCustom::reject(promise, result, V8PromiseCustom::Synchronous, args.GetIsolate()); |
| -} |
| - |
| -void callCallbacks(v8::Handle<v8::Array> callbacks, v8::Handle<v8::Value> result, V8PromiseCustom::SynchronousMode mode, v8::Isolate* isolate) |
| -{ |
| - v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global(); |
| - for (uint32_t i = 0, length = callbacks->Length(); i < length; ++i) { |
| - v8::Local<v8::Value> value = callbacks->Get(i); |
| - v8::Local<v8::Function> callback = value.As<v8::Function>(); |
| - V8PromiseCustom::call(callback, global, result, mode, isolate); |
| - } |
| + V8PromiseCustom::reject(promise, result, args.GetIsolate()); |
| } |
| void promiseAllFulfillCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
| @@ -241,7 +129,7 @@ void promiseAllFulfillCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
| v8::Local<v8::Integer> countdown = countdownWrapper->GetInternalField(V8PromiseCustom::PrimitiveWrapperPrimitiveIndex).As<v8::Integer>(); |
| ASSERT(countdown->Value() >= 1); |
| if (countdown->Value() == 1) { |
| - V8PromiseCustom::resolve(promise, results, V8PromiseCustom::Synchronous, isolate); |
| + V8PromiseCustom::resolve(promise, results, isolate); |
| return; |
| } |
| countdownWrapper->SetInternalField(V8PromiseCustom::PrimitiveWrapperPrimitiveIndex, v8::Integer::New(countdown->Value() - 1, isolate)); |
| @@ -259,36 +147,164 @@ v8::Local<v8::Object> promiseAllEnvironment(v8::Handle<v8::Object> promise, v8:: |
| return environment; |
| } |
| -void promiseResolve(const v8::FunctionCallbackInfo<v8::Value>& args) |
| +// Clear |internal|'s derived array. |
| +void clearDerived(v8::Handle<v8::Object> internal) |
| { |
| - v8::Local<v8::Object> promise = args.Data().As<v8::Object>(); |
| - ASSERT(!promise.IsEmpty()); |
| - v8::Local<v8::Object> internal = V8PromiseCustom::getInternal(promise); |
| - if (V8PromiseCustom::getState(internal) != V8PromiseCustom::Pending) |
| + internal->SetInternalField(V8PromiseCustom::InternalFulfillCallbackIndex, v8::Array::New()); |
| + internal->SetInternalField(V8PromiseCustom::InternalRejectCallbackIndex, v8::Array::New()); |
| + internal->SetInternalField(V8PromiseCustom::InternalDerivedPromiseIndex, v8::Array::New()); |
| +} |
| + |
| +// Add a tuple (|derivedPromise|, |onFulfilled|, |onRejected|) to |
| +// |internal|'s derived array. |
| +// |internal| must be a Promise internal object. |
| +// |derivedPromise| must be a Promise instance. |
| +// |onFulfilled| and |onRejected| can be an empty value respectively. |
| +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) |
| +{ |
| + v8::Local<v8::Array> fulfillCallbacks = internal->GetInternalField(V8PromiseCustom::InternalFulfillCallbackIndex).As<v8::Array>(); |
| + v8::Local<v8::Array> rejectCallbacks = internal->GetInternalField(V8PromiseCustom::InternalRejectCallbackIndex).As<v8::Array>(); |
| + v8::Local<v8::Array> derivedPromises = internal->GetInternalField(V8PromiseCustom::InternalDerivedPromiseIndex).As<v8::Array>(); |
| + |
| + if (onFulfilled.IsEmpty()) { |
| + fulfillCallbacks->Set(fulfillCallbacks->Length(), v8::Undefined(isolate)); |
| + } else { |
| + fulfillCallbacks->Set(fulfillCallbacks->Length(), onFulfilled); |
| + } |
| + |
| + if (onRejected.IsEmpty()) { |
| + rejectCallbacks->Set(rejectCallbacks->Length(), v8::Undefined(isolate)); |
| + } else { |
| + rejectCallbacks->Set(rejectCallbacks->Length(), onRejected); |
| + } |
| + |
| + ASSERT(!derivedPromise.IsEmpty()); |
| + derivedPromises->Set(derivedPromises->Length(), derivedPromise); |
| + |
| + // Since they are treated as a tuple, |
| + // we need to guaranteed that the length of these arrays are same. |
| + ASSERT(fulfillCallbacks->Length() == rejectCallbacks->Length() && rejectCallbacks->Length() == derivedPromises->Length()); |
| +} |
| + |
| +class CallHandlerTask : public ScriptExecutionContext::Task { |
| +public: |
| + CallHandlerTask(v8::Handle<v8::Object> promise, v8::Handle<v8::Function> handler, v8::Handle<v8::Value> argument, v8::Isolate* isolate) |
| + : m_promise(isolate, promise) |
| + , m_handler(isolate, handler) |
| + , m_argument(isolate, argument) |
| + { |
| + ASSERT(!m_promise.isEmpty()); |
| + ASSERT(!m_handler.isEmpty()); |
| + ASSERT(!m_argument.isEmpty()); |
| + } |
| + virtual ~CallHandlerTask() { } |
| + |
| + virtual void performTask(ScriptExecutionContext*) OVERRIDE; |
| + |
| +private: |
| + ScopedPersistent<v8::Object> m_promise; |
| + ScopedPersistent<v8::Function> m_handler; |
| + ScopedPersistent<v8::Value> m_argument; |
| +}; |
| + |
| +void CallHandlerTask::performTask(ScriptExecutionContext* context) |
| +{ |
| + ASSERT(context); |
| + if (context->activeDOMObjectsAreStopped()) |
| return; |
| - v8::Isolate* isolate = args.GetIsolate(); |
| - V8PromiseCustom::setState(V8PromiseCustom::getInternal(promise), V8PromiseCustom::Following, isolate); |
| - v8::Local<v8::Value> result = v8::Undefined(isolate); |
| - if (args.Length() > 0) |
| - result = args[0]; |
| - V8PromiseCustom::resolve(promise, result, V8PromiseCustom::Asynchronous, isolate); |
| + ScriptState* state = 0; |
| + if (context->isDocument()) { |
| + state = mainWorldScriptState(static_cast<Document*>(context)->frame()); |
| + } else { |
| + ASSERT(context->isWorkerGlobalScope()); |
| + state = scriptStateFromWorkerGlobalScope(toWorkerGlobalScope(context)); |
| + } |
| + ASSERT(state); |
| + |
| + v8::Isolate* isolate = state->isolate(); |
| + v8::HandleScope handleScope(isolate); |
| + v8::Handle<v8::Context> v8Context = state->context(); |
| + v8::Context::Scope scope(v8Context); |
| + v8::Handle<v8::Value> args[] = { m_argument.newLocal(isolate) }; |
| + v8::TryCatch trycatch; |
| + v8::Local<v8::Value> value = V8ScriptRunner::callFunction(m_handler.newLocal(isolate), context, v8::Undefined(isolate), WTF_ARRAY_LENGTH(args), args, isolate); |
|
yusukesuzuki
2013/10/01 03:44:03
Since V8-side change[1] and Blink-side change[2] a
|
| + if (value.IsEmpty()) { |
| + V8PromiseCustom::reject(m_promise.newLocal(isolate), trycatch.Exception(), isolate); |
| + } else { |
| + V8PromiseCustom::resolve(m_promise.newLocal(isolate), value, isolate); |
| + } |
| } |
| -void promiseReject(const v8::FunctionCallbackInfo<v8::Value>& args) |
| +class UpdateDerivedTask : public ScriptExecutionContext::Task { |
| +public: |
| + UpdateDerivedTask(v8::Handle<v8::Object> promise, v8::Handle<v8::Function> onFulfilled, v8::Handle<v8::Function> onRejected, v8::Handle<v8::Object> originatorObject, v8::Isolate* isolate) |
| + : m_promise(isolate, promise) |
| + , m_onFulfilled(isolate, onFulfilled) |
| + , m_onRejected(isolate, onRejected) |
| + , m_originatorObject(isolate, originatorObject) |
| + { |
| + ASSERT(!m_promise.isEmpty()); |
| + ASSERT(!m_onFulfilled.isEmpty()); |
| + ASSERT(!m_onRejected.isEmpty()); |
| + ASSERT(!m_originatorObject.isEmpty()); |
| + } |
| + virtual ~UpdateDerivedTask() { } |
| + |
| + virtual void performTask(ScriptExecutionContext*) OVERRIDE; |
| + |
| +private: |
| + ScopedPersistent<v8::Object> m_promise; |
| + ScopedPersistent<v8::Function> m_onFulfilled; |
| + ScopedPersistent<v8::Function> m_onRejected; |
| + ScopedPersistent<v8::Object> m_originatorObject; |
| +}; |
| + |
| +void UpdateDerivedTask::performTask(ScriptExecutionContext* context) |
| { |
| - v8::Local<v8::Object> promise = args.Data().As<v8::Object>(); |
| - ASSERT(!promise.IsEmpty()); |
| - v8::Local<v8::Object> internal = V8PromiseCustom::getInternal(promise); |
| - if (V8PromiseCustom::getState(internal) != V8PromiseCustom::Pending) |
| + ASSERT(context); |
| + if (context->activeDOMObjectsAreStopped()) |
| return; |
| - v8::Isolate* isolate = args.GetIsolate(); |
| - V8PromiseCustom::setState(V8PromiseCustom::getInternal(promise), V8PromiseCustom::Following, isolate); |
| - v8::Local<v8::Value> result = v8::Undefined(isolate); |
| - if (args.Length() > 0) |
| - result = args[0]; |
| - V8PromiseCustom::reject(promise, result, V8PromiseCustom::Asynchronous, isolate); |
| + ScriptState* state = 0; |
| + if (context->isDocument()) { |
| + state = mainWorldScriptState(static_cast<Document*>(context)->frame()); |
| + } else { |
| + ASSERT(context->isWorkerGlobalScope()); |
| + state = scriptStateFromWorkerGlobalScope(toWorkerGlobalScope(context)); |
| + } |
| + ASSERT(state); |
| + |
| + v8::Isolate* isolate = state->isolate(); |
| + v8::HandleScope handleScope(isolate); |
| + v8::Handle<v8::Context> v8Context = state->context(); |
| + v8::Context::Scope scope(v8Context); |
| + |
| + v8::Local<v8::Object> originatorObject = m_originatorObject.newLocal(isolate); |
| + v8::Local<v8::Value> coercedAlready = originatorObject->GetHiddenValue(V8HiddenPropertyName::thenableHiddenPromise(isolate)); |
| + if (!coercedAlready.IsEmpty() && coercedAlready->IsObject()) { |
| + ASSERT(V8PromiseCustom::isPromise(coercedAlready.As<v8::Object>(), isolate)); |
| + V8PromiseCustom::updateDerivedFromPromise(m_promise.newLocal(isolate), m_onFulfilled.newLocal(isolate), m_onRejected.newLocal(isolate), coercedAlready.As<v8::Object>(), isolate); |
| + return; |
| + } |
| + |
| + v8::Local<v8::Value> then; |
| + v8::TryCatch trycatch; |
| + then = originatorObject->Get(v8::String::NewSymbol("then")); |
| + if (then.IsEmpty()) { |
| + // If calling the [[Get]] internal method threw an exception, catch it and run updateDerivedFromReason. |
| + V8PromiseCustom::updateDerivedFromReason(m_promise.newLocal(isolate), m_onRejected.newLocal(isolate), trycatch.Exception(), isolate); |
| + return; |
| + } |
| + |
| + if (then->IsFunction()) { |
| + ASSERT(then->IsObject()); |
| + v8::Local<v8::Object> coerced = V8PromiseCustom::coerceThenable(originatorObject, then.As<v8::Function>(), isolate); |
| + V8PromiseCustom::updateDerivedFromPromise(m_promise.newLocal(isolate), m_onFulfilled.newLocal(isolate), m_onRejected.newLocal(isolate), coerced, isolate); |
| + return; |
| + } |
| + |
| + V8PromiseCustom::updateDerivedFromValue(m_promise.newLocal(isolate), m_onFulfilled.newLocal(isolate), originatorObject, isolate); |
| } |
| } // namespace |
| @@ -304,14 +320,13 @@ void V8Promise::constructorCustom(const v8::FunctionCallbackInfo<v8::Value>& arg |
| v8::Local<v8::Function> init = args[0].As<v8::Function>(); |
| v8::Local<v8::Object> promise = V8PromiseCustom::createPromise(args.Holder(), isolate); |
| v8::Handle<v8::Value> argv[] = { |
| - createClosure(promiseResolve, promise, isolate), |
| - createClosure(promiseReject, promise, isolate) |
| + createClosure(promiseResolveCallback, promise, isolate), |
| + createClosure(promiseRejectCallback, promise, isolate) |
| }; |
| v8::TryCatch trycatch; |
| - if (V8ScriptRunner::callFunction(init, getScriptExecutionContext(), promise, WTF_ARRAY_LENGTH(argv), argv, isolate).IsEmpty()) { |
| + if (V8ScriptRunner::callFunction(init, getScriptExecutionContext(), v8::Undefined(isolate), WTF_ARRAY_LENGTH(argv), argv, isolate).IsEmpty()) { |
| // An exception is thrown. Reject the promise if its resolved flag is unset. |
| - if (V8PromiseCustom::getState(V8PromiseCustom::getInternal(promise)) == V8PromiseCustom::Pending) |
| - V8PromiseCustom::reject(promise, trycatch.Exception(), V8PromiseCustom::Asynchronous, isolate); |
| + V8PromiseCustom::reject(promise, trycatch.Exception(), isolate); |
| } |
| v8SetReturnValue(args, promise); |
| return; |
| @@ -320,28 +335,23 @@ void V8Promise::constructorCustom(const v8::FunctionCallbackInfo<v8::Value>& arg |
| void V8Promise::thenMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) |
| { |
| v8::Isolate* isolate = args.GetIsolate(); |
| - v8::Local<v8::Function> fulfillWrapper, rejectWrapper; |
| + v8::Local<v8::Function> onFulfilled, onRejected; |
| v8::Local<v8::Object> promise = V8PromiseCustom::createPromise(args.Holder(), isolate); |
| if (args.Length() > 0 && !args[0]->IsUndefined()) { |
| if (!args[0]->IsFunction()) { |
| - v8SetReturnValue(args, throwTypeError("fulfillCallback must be a function or undefined", isolate)); |
| + v8SetReturnValue(args, throwTypeError("onFulfilled must be a function or undefined", isolate)); |
| return; |
| } |
| - fulfillWrapper = createClosure(wrapperCallback, wrapperCallbackEnvironment(promise, args[0].As<v8::Function>(), isolate), isolate); |
| - } else { |
| - fulfillWrapper = createClosure(promiseFulfillCallback, promise, isolate); |
| + onFulfilled = args[0].As<v8::Function>(); |
| } |
| if (args.Length() > 1 && !args[1]->IsUndefined()) { |
| if (!args[1]->IsFunction()) { |
| - v8SetReturnValue(args, throwTypeError("rejectCallback must be a function or undefined", isolate)); |
| + v8SetReturnValue(args, throwTypeError("onRejected must be a function or undefined", isolate)); |
| return; |
| } |
| - rejectWrapper = createClosure(wrapperCallback, wrapperCallbackEnvironment(promise, args[1].As<v8::Function>(), isolate), isolate); |
| - } else { |
| - rejectWrapper = createClosure(promiseRejectCallback, promise, isolate); |
| + onRejected = args[1].As<v8::Function>(); |
| } |
| - V8PromiseCustom::append(args.Holder(), fulfillWrapper, rejectWrapper, isolate); |
| - v8SetReturnValue(args, promise); |
| + v8SetReturnValue(args, V8PromiseCustom::then(args.Holder(), onFulfilled, onRejected, isolate)); |
| } |
| void V8Promise::castMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) |
| @@ -357,21 +367,16 @@ void V8Promise::castMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args |
| void V8Promise::catchMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) |
| { |
| v8::Isolate* isolate = args.GetIsolate(); |
| - v8::Local<v8::Function> fulfillWrapper, rejectWrapper; |
| - v8::Local<v8::Object> promise = V8PromiseCustom::createPromise(args.Holder(), isolate); |
| + v8::Local<v8::Function> onFulfilled, onRejected; |
| if (args.Length() > 0 && !args[0]->IsUndefined()) { |
| if (!args[0]->IsFunction()) { |
| - v8SetReturnValue(args, throwTypeError("rejectCallback must be a function or undefined", isolate)); |
| + v8SetReturnValue(args, throwTypeError("onRejected must be a function or undefined", isolate)); |
| return; |
| } |
| - rejectWrapper = createClosure(wrapperCallback, wrapperCallbackEnvironment(promise, args[0].As<v8::Function>(), isolate), isolate); |
| - } else { |
| - rejectWrapper = createClosure(promiseRejectCallback, promise, isolate); |
| + onRejected = args[0].As<v8::Function>(); |
| } |
| - fulfillWrapper = createClosure(promiseFulfillCallback, promise, isolate); |
| - V8PromiseCustom::append(args.Holder(), fulfillWrapper, rejectWrapper, isolate); |
| - v8SetReturnValue(args, promise); |
| + v8SetReturnValue(args, V8PromiseCustom::then(args.Holder(), onFulfilled, onRejected, isolate)); |
| } |
| void V8Promise::resolveMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) |
| @@ -382,7 +387,7 @@ void V8Promise::resolveMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& a |
| result = args[0]; |
| v8::Local<v8::Object> promise = V8PromiseCustom::createPromise(args.Holder(), isolate); |
| - V8PromiseCustom::resolve(promise, result, V8PromiseCustom::Asynchronous, isolate); |
| + V8PromiseCustom::resolve(promise, result, isolate); |
| v8SetReturnValue(args, promise); |
| } |
| @@ -394,7 +399,7 @@ void V8Promise::rejectMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& ar |
| result = args[0]; |
| v8::Local<v8::Object> promise = V8PromiseCustom::createPromise(args.Holder(), isolate); |
| - V8PromiseCustom::reject(promise, result, V8PromiseCustom::Asynchronous, isolate); |
| + V8PromiseCustom::reject(promise, result, isolate); |
| v8SetReturnValue(args, promise); |
| } |
| @@ -410,14 +415,14 @@ void V8Promise::raceMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args |
| // FIXME: Now we limit the iterable type to the Array type. |
| v8::Local<v8::Array> iterable = args[0].As<v8::Array>(); |
| - v8::Local<v8::Function> fulfillCallback = createClosure(promiseResolveCallback, promise, isolate); |
| - v8::Local<v8::Function> rejectCallback = createClosure(promiseRejectCallback, promise, isolate); |
| + v8::Local<v8::Function> onFulfilled = createClosure(promiseResolveCallback, promise, isolate); |
| + v8::Local<v8::Function> onRejected = createClosure(promiseRejectCallback, promise, isolate); |
| for (unsigned i = 0, length = iterable->Length(); i < length; ++i) { |
| // Array-holes should not be skipped by for-of iteration semantics. |
| V8TRYCATCH_VOID(v8::Local<v8::Value>, nextValue, iterable->Get(i)); |
| v8::Local<v8::Object> nextPromise = V8PromiseCustom::toPromise(nextValue, isolate); |
| - V8PromiseCustom::append(nextPromise, fulfillCallback, rejectCallback, isolate); |
| + V8PromiseCustom::then(nextPromise, onFulfilled, onRejected, isolate); |
| } |
| v8SetReturnValue(args, promise); |
| } |
| @@ -429,7 +434,7 @@ void V8Promise::allMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) |
| v8::Local<v8::Array> results = v8::Array::New(); |
| if (!args.Length() || !args[0]->IsArray()) { |
| - V8PromiseCustom::resolve(promise, results, V8PromiseCustom::Asynchronous, isolate); |
| + V8PromiseCustom::resolve(promise, results, isolate); |
| v8SetReturnValue(args, promise); |
| return; |
| } |
| @@ -438,7 +443,7 @@ void V8Promise::allMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) |
| v8::Local<v8::Array> iterable = args[0].As<v8::Array>(); |
| if (!iterable->Length()) { |
| - V8PromiseCustom::resolve(promise, results, V8PromiseCustom::Asynchronous, isolate); |
| + V8PromiseCustom::resolve(promise, results, isolate); |
| v8SetReturnValue(args, promise); |
| return; |
| } |
| @@ -447,14 +452,14 @@ void V8Promise::allMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) |
| v8::Local<v8::Object> countdownWrapper = objectTemplate->NewInstance(); |
| countdownWrapper->SetInternalField(V8PromiseCustom::PrimitiveWrapperPrimitiveIndex, v8::Integer::New(iterable->Length(), isolate)); |
| - v8::Local<v8::Function> rejectCallback = createClosure(promiseRejectCallback, promise, isolate); |
| + v8::Local<v8::Function> onRejected = createClosure(promiseRejectCallback, promise, isolate); |
| for (unsigned i = 0, length = iterable->Length(); i < length; ++i) { |
| // Array-holes should not be skipped by for-of iteration semantics. |
| v8::Local<v8::Object> environment = promiseAllEnvironment(promise, countdownWrapper, i, results, isolate); |
| - v8::Local<v8::Function> fulfillCallback = createClosure(promiseAllFulfillCallback, environment, isolate); |
| + v8::Local<v8::Function> onFulfilled = createClosure(promiseAllFulfillCallback, environment, isolate); |
| V8TRYCATCH_VOID(v8::Local<v8::Value>, nextValue, iterable->Get(i)); |
| v8::Local<v8::Object> nextPromise = V8PromiseCustom::toPromise(nextValue, isolate); |
| - V8PromiseCustom::append(nextPromise, fulfillCallback, rejectCallback, isolate); |
| + V8PromiseCustom::then(nextPromise, onFulfilled, onRejected, isolate); |
| } |
| v8SetReturnValue(args, promise); |
| } |
| @@ -467,164 +472,221 @@ v8::Local<v8::Object> V8PromiseCustom::createPromise(v8::Handle<v8::Object> crea |
| v8::Local<v8::Object> internal = internalTemplate->NewInstance(); |
| v8::Local<v8::Object> promise = V8DOMWrapper::createWrapper(creationContext, &V8Promise::info, 0, isolate); |
| - clearInternal(internal, V8PromiseCustom::Pending, v8::Undefined(isolate), isolate); |
| + clearDerived(internal); |
| + setState(internal, Pending, v8::Undefined(isolate), isolate); |
| promise->SetInternalField(v8DOMWrapperObjectIndex, internal); |
| return promise; |
| } |
| -void V8PromiseCustom::fulfill(v8::Handle<v8::Object> promise, v8::Handle<v8::Value> result, SynchronousMode mode, v8::Isolate* isolate) |
| +v8::Local<v8::Object> V8PromiseCustom::getInternal(v8::Handle<v8::Object> promise) |
| { |
| - v8::Local<v8::Object> internal = getInternal(promise); |
| - PromiseState state = getState(internal); |
| - if (state == Fulfilled || state == Rejected) |
| - return; |
| - |
| - ASSERT(state == Pending || state == Following); |
| - v8::Local<v8::Array> callbacks = internal->GetInternalField(V8PromiseCustom::InternalFulfillCallbackIndex).As<v8::Array>(); |
| - clearInternal(internal, Fulfilled, result, isolate); |
| + v8::Local<v8::Value> value = promise->GetInternalField(v8DOMWrapperObjectIndex); |
| + return value.As<v8::Object>(); |
| +} |
| - callCallbacks(callbacks, result, mode, isolate); |
| +V8PromiseCustom::PromiseState V8PromiseCustom::getState(v8::Handle<v8::Object> internal) |
| +{ |
| + v8::Handle<v8::Value> value = internal->GetInternalField(V8PromiseCustom::InternalStateIndex); |
| + bool ok = false; |
| + uint32_t number = toInt32(value, ok); |
| + ASSERT(ok && (number == Pending || number == Fulfilled || number == Rejected || number == Following)); |
| + return static_cast<PromiseState>(number); |
| } |
| -void V8PromiseCustom::resolve(v8::Handle<v8::Object> promise, v8::Handle<v8::Value> result, SynchronousMode mode, v8::Isolate* isolate) |
| +void V8PromiseCustom::setState(v8::Handle<v8::Object> internal, PromiseState state, v8::Handle<v8::Value> value, v8::Isolate* isolate) |
| { |
| - ASSERT(!result.IsEmpty()); |
| + ASSERT(!value.IsEmpty()); |
| + ASSERT(state == Pending || state == Fulfilled || state == Rejected || state == Following); |
| + internal->SetInternalField(InternalStateIndex, v8::Integer::New(state, isolate)); |
| + internal->SetInternalField(InternalResultIndex, value); |
| +} |
| - v8::Local<v8::Value> then; |
| - if (result->IsObject()) { |
| - v8::TryCatch trycatch; |
| - then = result.As<v8::Object>()->Get(v8::String::NewSymbol("then")); |
| - if (then.IsEmpty()) { |
| - // If calling the [[Get]] internal method threw an exception, catch it and run reject. |
| - reject(promise, trycatch.Exception(), mode, isolate); |
| - return; |
| - } |
| - } |
| +bool V8PromiseCustom::isPromise(v8::Handle<v8::Value> maybePromise, v8::Isolate* isolate) |
| +{ |
| + WrapperWorldType currentWorldType = worldType(isolate); |
| + return V8Promise::GetTemplate(isolate, currentWorldType)->HasInstance(maybePromise); |
| +} |
| - if (!then.IsEmpty() && then->IsFunction()) { |
| - ASSERT(result->IsObject()); |
| - v8::TryCatch trycatch; |
| - v8::Handle<v8::Value> argv[] = { |
| - createClosure(promiseResolveCallback, promise, isolate), |
| - createClosure(promiseRejectCallback, promise, isolate), |
| - }; |
| - if (V8ScriptRunner::callFunction(then.As<v8::Function>(), getScriptExecutionContext(), result.As<v8::Object>(), WTF_ARRAY_LENGTH(argv), argv, isolate).IsEmpty()) |
| - reject(promise, trycatch.Exception(), mode, isolate); |
| - return; |
| - } |
| +v8::Local<v8::Object> V8PromiseCustom::toPromise(v8::Handle<v8::Value> maybePromise, v8::Isolate* isolate) |
| +{ |
| + // FIXME: Currently we dones't check [[PromiseConstructor]] since we limits |
| + // the creation of the promise objects only from the Blink Promise |
| + // constructor. |
| + if (isPromise(maybePromise, isolate)) |
| + return maybePromise.As<v8::Object>(); |
| - fulfill(promise, result, mode, isolate); |
| + v8::Local<v8::Object> promise = createPromise(v8::Handle<v8::Object>(), isolate); |
| + resolve(promise, maybePromise, isolate); |
| + return promise; |
| } |
| -void V8PromiseCustom::reject(v8::Handle<v8::Object> promise, v8::Handle<v8::Value> result, SynchronousMode mode, v8::Isolate* isolate) |
| +void V8PromiseCustom::resolve(v8::Handle<v8::Object> promise, v8::Handle<v8::Value> result, v8::Isolate* isolate) |
| { |
| + ASSERT(!result.IsEmpty()); |
| v8::Local<v8::Object> internal = getInternal(promise); |
| PromiseState state = getState(internal); |
| - if (state == Fulfilled || state == Rejected) |
| + if (state != Pending) |
| return; |
| - ASSERT(state == Pending || state == Following); |
| - v8::Local<v8::Array> callbacks = internal->GetInternalField(V8PromiseCustom::InternalRejectCallbackIndex).As<v8::Array>(); |
| - clearInternal(internal, Rejected, result, isolate); |
| - |
| - callCallbacks(callbacks, result, mode, isolate); |
| + if (isPromise(result, isolate)) { |
| + v8::Local<v8::Object> valuePromise = result.As<v8::Object>(); |
| + v8::Local<v8::Object> valueInternal = getInternal(valuePromise); |
| + PromiseState valueState = getState(valueInternal); |
| + if (promise->SameValue(valuePromise)) { |
| + v8::Local<v8::Value> reason = V8ThrowException::createTypeError("Resolve a promise with itself", isolate); |
| + setReason(promise, reason, isolate); |
| + } else if (valueState == Following) { |
| + v8::Local<v8::Object> valuePromiseFollowing = valueInternal->GetInternalField(InternalResultIndex).As<v8::Object>(); |
| + setState(internal, Following, valuePromiseFollowing, isolate); |
| + addToDerived(getInternal(valuePromiseFollowing), promise, v8::Handle<v8::Function>(), v8::Handle<v8::Function>(), isolate); |
| + } else if (valueState == Fulfilled) { |
| + setValue(promise, valueInternal->GetInternalField(InternalResultIndex), isolate); |
| + } else if (valueState == Rejected) { |
| + setReason(promise, valueInternal->GetInternalField(InternalResultIndex), isolate); |
| + } else { |
| + ASSERT(valueState == Pending); |
| + setState(internal, Following, valuePromise, isolate); |
| + addToDerived(valueInternal, promise, v8::Handle<v8::Function>(), v8::Handle<v8::Function>(), isolate); |
| + } |
| + } else { |
| + setValue(promise, result, isolate); |
| + } |
| } |
| -void V8PromiseCustom::append(v8::Handle<v8::Object> promise, v8::Handle<v8::Function> fulfillCallback, v8::Handle<v8::Function> rejectCallback, v8::Isolate* isolate) |
| +void V8PromiseCustom::reject(v8::Handle<v8::Object> promise, v8::Handle<v8::Value> reason, v8::Isolate* isolate) |
| { |
| - // fulfillCallback and rejectCallback can be empty. |
| v8::Local<v8::Object> internal = getInternal(promise); |
| - |
| PromiseState state = getState(internal); |
| - if (state == Fulfilled) { |
| - if (!fulfillCallback.IsEmpty()) { |
| - v8::Local<v8::Value> result = internal->GetInternalField(V8PromiseCustom::InternalResultIndex); |
| - v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global(); |
| - call(fulfillCallback, global, result, Asynchronous, isolate); |
| - } |
| - return; |
| - } |
| - if (state == Rejected) { |
| - if (!rejectCallback.IsEmpty()) { |
| - v8::Local<v8::Value> result = internal->GetInternalField(V8PromiseCustom::InternalResultIndex); |
| - v8::Local<v8::Object> global = isolate->GetCurrentContext()->Global(); |
| - call(rejectCallback, global, result, Asynchronous, isolate); |
| - } |
| + if (state != Pending) |
| return; |
| - } |
| - |
| - ASSERT(state == Pending || state == Following); |
| - if (!fulfillCallback.IsEmpty()) { |
| - v8::Local<v8::Array> callbacks = internal->GetInternalField(InternalFulfillCallbackIndex).As<v8::Array>(); |
| - callbacks->Set(callbacks->Length(), fulfillCallback); |
| - } |
| - if (!rejectCallback.IsEmpty()) { |
| - v8::Local<v8::Array> callbacks = internal->GetInternalField(InternalRejectCallbackIndex).As<v8::Array>(); |
| - callbacks->Set(callbacks->Length(), rejectCallback); |
| - } |
| + setReason(promise, reason, isolate); |
| } |
| -v8::Local<v8::Object> V8PromiseCustom::getInternal(v8::Handle<v8::Object> promise) |
| +v8::Local<v8::Object> V8PromiseCustom::then(v8::Handle<v8::Object> promise, v8::Handle<v8::Function> onFulfilled, v8::Handle<v8::Function> onRejected, v8::Isolate* isolate) |
| { |
| - v8::Local<v8::Value> value = promise->GetInternalField(v8DOMWrapperObjectIndex); |
| - return value.As<v8::Object>(); |
| + v8::Handle<v8::Object> internal = getInternal(promise); |
| + if (getState(internal) == Following) |
| + return then(internal->GetInternalField(InternalResultIndex).As<v8::Object>(), onFulfilled, onRejected, isolate); |
| + // FIXME: Currently we dones't lookup "constructor" property since we limits |
| + // the creation of the promise objects only from the Blink Promise |
| + // constructor. |
| + v8::Local<v8::Object> derivedPromise = createPromise(v8::Handle<v8::Object>(), isolate); |
| + updateDerivedFromPromise(derivedPromise, onFulfilled, onRejected, promise, isolate); |
| + return derivedPromise; |
| } |
| -void V8PromiseCustom::clearInternal(v8::Handle<v8::Object> internal, PromiseState state, v8::Handle<v8::Value> value, v8::Isolate* isolate) |
| +void V8PromiseCustom::setValue(v8::Handle<v8::Object> promise, v8::Handle<v8::Value> value, v8::Isolate* isolate) |
| { |
| - setState(internal, state, isolate); |
| - internal->SetInternalField(V8PromiseCustom::InternalResultIndex, value); |
| - internal->SetInternalField(V8PromiseCustom::InternalFulfillCallbackIndex, v8::Array::New()); |
| - internal->SetInternalField(V8PromiseCustom::InternalRejectCallbackIndex, v8::Array::New()); |
| + v8::Local<v8::Object> internal = getInternal(promise); |
| + ASSERT(getState(internal) != Fulfilled && getState(internal) != Rejected); |
| + setState(internal, Fulfilled, value, isolate); |
| + propagateToDerived(promise, isolate); |
| } |
| -V8PromiseCustom::PromiseState V8PromiseCustom::getState(v8::Handle<v8::Object> internal) |
| +void V8PromiseCustom::setReason(v8::Handle<v8::Object> promise, v8::Handle<v8::Value> reason, v8::Isolate* isolate) |
| { |
| - v8::Handle<v8::Value> value = internal->GetInternalField(V8PromiseCustom::InternalStateIndex); |
| - bool ok = false; |
| - uint32_t number = toInt32(value, ok); |
| - ASSERT(ok && (number == Pending || number == Fulfilled || number == Rejected || number == Following)); |
| - return static_cast<PromiseState>(number); |
| + v8::Local<v8::Object> internal = getInternal(promise); |
| + ASSERT(getState(internal) != Fulfilled && getState(internal) != Rejected); |
| + setState(internal, Rejected, reason, isolate); |
| + propagateToDerived(promise, isolate); |
| } |
| -void V8PromiseCustom::setState(v8::Handle<v8::Object> internal, PromiseState state, v8::Isolate* isolate) |
| +void V8PromiseCustom::propagateToDerived(v8::Handle<v8::Object> promise, v8::Isolate* isolate) |
| { |
| - ASSERT(state == Pending || state == Fulfilled || state == Rejected || state == Following); |
| - internal->SetInternalField(V8PromiseCustom::InternalStateIndex, v8::Integer::New(state, isolate)); |
| + v8::Local<v8::Object> internal = getInternal(promise); |
| + ASSERT(getState(internal) == Fulfilled || getState(internal) == Rejected); |
| + v8::Local<v8::Array> fulfillCallbacks = internal->GetInternalField(InternalFulfillCallbackIndex).As<v8::Array>(); |
| + v8::Local<v8::Array> rejectCallbacks = internal->GetInternalField(InternalRejectCallbackIndex).As<v8::Array>(); |
| + v8::Local<v8::Array> derivedPromises = internal->GetInternalField(InternalDerivedPromiseIndex).As<v8::Array>(); |
| + for (uint32_t i = 0, length = derivedPromises->Length(); i < length; ++i) { |
| + v8::Local<v8::Object> derivedPromise = derivedPromises->Get(i).As<v8::Object>(); |
| + |
| + v8::Local<v8::Function> onFulfilled, onRejected; |
| + v8::Local<v8::Value> onFulfilledValue = fulfillCallbacks->Get(i); |
| + if (onFulfilledValue->IsFunction()) { |
| + onFulfilled = onFulfilledValue.As<v8::Function>(); |
| + } |
| + v8::Local<v8::Value> onRejectedValue = rejectCallbacks->Get(i); |
| + if (onRejectedValue->IsFunction()) { |
| + onRejected = onRejectedValue.As<v8::Function>(); |
| + } |
| + |
| + updateDerived(derivedPromise, onFulfilled, onRejected, promise, isolate); |
| + } |
| + clearDerived(internal); |
| +} |
| + |
| +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) |
| +{ |
| + v8::Local<v8::Object> originatorInternal = getInternal(originator); |
| + ASSERT(getState(originatorInternal) == Fulfilled || getState(originatorInternal) == Rejected); |
| + PromiseState originatorState = getState(originatorInternal); |
| + v8::Local<v8::Value> originatorValue = originatorInternal->GetInternalField(InternalResultIndex); |
| + if (originatorState == Fulfilled) { |
| + if (originatorValue->IsObject()) { |
| + ScriptExecutionContext* scriptExecutionContext = getScriptExecutionContext(); |
| + ASSERT(scriptExecutionContext && scriptExecutionContext->isContextThread()); |
| + scriptExecutionContext->postTask(adoptPtr(new UpdateDerivedTask(derivedPromise, onFulfilled, onRejected, originatorValue.As<v8::Object>(), isolate))); |
| + } else { |
| + updateDerivedFromValue(derivedPromise, onFulfilled, originatorValue, isolate); |
| + } |
| + } else { |
| + updateDerivedFromReason(derivedPromise, onRejected, originatorValue, isolate); |
| + } |
| } |
| -void V8PromiseCustom::call(v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, v8::Handle<v8::Value> result, SynchronousMode mode, v8::Isolate* isolate) |
| +void V8PromiseCustom::updateDerivedFromValue(v8::Handle<v8::Object> derivedPromise, v8::Handle<v8::Function> onFulfilled, v8::Handle<v8::Value> value, v8::Isolate* isolate) |
| { |
| - if (mode == Synchronous) { |
| - v8::Context::Scope scope(isolate->GetCurrentContext()); |
| - // If an exception is thrown, catch it and do nothing. |
| - v8::TryCatch trycatch; |
| - v8::Handle<v8::Value> args[] = { result }; |
| - V8ScriptRunner::callFunction(function, getScriptExecutionContext(), receiver, WTF_ARRAY_LENGTH(args), args, isolate); |
| + if (!onFulfilled.IsEmpty()) { |
| + callHandler(derivedPromise, onFulfilled, value, isolate); |
| } else { |
| - ASSERT(mode == Asynchronous); |
| - postTask(function, receiver, result, isolate); |
| + setValue(derivedPromise, value, isolate); |
| } |
| } |
| -bool V8PromiseCustom::isPromise(v8::Handle<v8::Value> maybePromise, v8::Isolate* isolate) |
| +void V8PromiseCustom::updateDerivedFromReason(v8::Handle<v8::Object> derivedPromise, v8::Handle<v8::Function> onRejected, v8::Handle<v8::Value> reason, v8::Isolate* isolate) |
| { |
| - WrapperWorldType currentWorldType = worldType(isolate); |
| - return V8Promise::GetTemplate(isolate, currentWorldType)->HasInstance(maybePromise); |
| + if (!onRejected.IsEmpty()) { |
| + callHandler(derivedPromise, onRejected, reason, isolate); |
| + } else { |
| + setReason(derivedPromise, reason, isolate); |
| + } |
| } |
| -v8::Local<v8::Object> V8PromiseCustom::toPromise(v8::Handle<v8::Value> maybePromise, v8::Isolate* isolate) |
| +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) |
| { |
| - // FIXME: Currently we dones't check [[PromiseConstructor]] since we limits |
| - // the creation of the promise objects only from the Blink Promise |
| - // constructor. |
| - if (isPromise(maybePromise, isolate)) |
| - return maybePromise.As<v8::Object>(); |
| + v8::Local<v8::Object> internal = getInternal(promise); |
| + PromiseState state = getState(internal); |
| + if (state == Fulfilled || state == Rejected) { |
| + updateDerived(derivedPromise, onFulfilled, onRejected, promise, isolate); |
| + } else { |
| + addToDerived(internal, derivedPromise, onFulfilled, onRejected, isolate); |
| + } |
| +} |
| +v8::Local<v8::Object> V8PromiseCustom::coerceThenable(v8::Handle<v8::Object> thenable, v8::Handle<v8::Function> then, v8::Isolate* isolate) |
| +{ |
| + ASSERT(!thenable.IsEmpty()); |
| + ASSERT(!then.IsEmpty()); |
| v8::Local<v8::Object> promise = createPromise(v8::Handle<v8::Object>(), isolate); |
| - resolve(promise, maybePromise, Asynchronous, isolate); |
| + v8::Handle<v8::Value> argv[] = { |
| + createClosure(promiseResolveCallback, promise, isolate), |
| + createClosure(promiseRejectCallback, promise, isolate) |
| + }; |
| + v8::TryCatch trycatch; |
| + if (V8ScriptRunner::callFunction(then, getScriptExecutionContext(), thenable, WTF_ARRAY_LENGTH(argv), argv, isolate).IsEmpty()) { |
| + reject(promise, trycatch.Exception(), isolate); |
| + } |
| + thenable->SetHiddenValue(V8HiddenPropertyName::thenableHiddenPromise(isolate), promise); |
| return promise; |
| } |
| +void V8PromiseCustom::callHandler(v8::Handle<v8::Object> promise, v8::Handle<v8::Function> handler, v8::Handle<v8::Value> argument, v8::Isolate* isolate) |
| +{ |
| + ScriptExecutionContext* scriptExecutionContext = getScriptExecutionContext(); |
| + ASSERT(scriptExecutionContext && scriptExecutionContext->isContextThread()); |
| + scriptExecutionContext->postTask(adoptPtr(new CallHandlerTask(promise, handler, argument, isolate))); |
| +} |
| + |
| } // namespace WebCore |