From 78caf8d5fea07efc36b693db89d4524345b7b7b1 Mon Sep 17 00:00:00 2001 From: Alexey Kozyatinskiy Date: Wed, 9 Aug 2017 22:43:50 -0700 Subject: [PATCH] [inspector] resolve async evaluation on context destroyed On context destroyed we discard corresponded injected-script and won't be able to wrap async evaluation result, so we can resolve callback with an error right now. R=dgozman@chromium.org Bug: none Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel Change-Id: Ib62f255297f306ad9f2c96a2a5b80e4b5aa33475 Reviewed-on: https://chromium-review.googlesource.com/604213 Commit-Queue: Aleksey Kozyatinskiy Reviewed-by: Dmitry Gozman Cr-Commit-Position: refs/heads/master@{#47267} --- src/inspector/injected-script.cc | 218 +++++++++++++++- src/inspector/injected-script.h | 24 ++ src/inspector/v8-runtime-agent-impl.cc | 237 +++--------------- .../runtime/evaluate-async-expected.txt | 26 ++ test/inspector/runtime/evaluate-async.js | 118 ++++++--- 5 files changed, 383 insertions(+), 240 deletions(-) diff --git a/src/inspector/injected-script.cc b/src/inspector/injected-script.cc index 160fd39256..309cac7d9c 100644 --- a/src/inspector/injected-script.cc +++ b/src/inspector/injected-script.cc @@ -49,7 +49,7 @@ namespace v8_inspector { namespace { static const char privateKeyName[] = "v8-inspector#injectedScript"; -} +} // namespace using protocol::Array; using protocol::Runtime::PropertyDescriptor; @@ -57,6 +57,179 @@ using protocol::Runtime::InternalPropertyDescriptor; using protocol::Runtime::RemoteObject; using protocol::Maybe; +class InjectedScript::ProtocolPromiseHandler { + public: + static bool add(V8InspectorSessionImpl* session, + v8::Local context, + v8::Local promise, + const String16& notPromiseError, int executionContextId, + const String16& objectGroup, bool returnByValue, + bool generatePreview, EvaluateCallback* callback) { + V8InspectorImpl* inspector = session->inspector(); + ProtocolPromiseHandler* handler = + new ProtocolPromiseHandler(session, executionContextId, objectGroup, + returnByValue, generatePreview, callback); + v8::Local wrapper = handler->m_wrapper.Get(inspector->isolate()); + v8::Local thenCallbackFunction = + v8::Function::New(context, thenCallback, wrapper, 0, + v8::ConstructorBehavior::kThrow) + .ToLocalChecked(); + if (promise->Then(context, thenCallbackFunction).IsEmpty()) { + callback->sendFailure(Response::InternalError()); + return false; + } + v8::Local catchCallbackFunction = + v8::Function::New(context, catchCallback, wrapper, 0, + v8::ConstructorBehavior::kThrow) + .ToLocalChecked(); + if (promise->Catch(context, catchCallbackFunction).IsEmpty()) { + callback->sendFailure(Response::InternalError()); + return false; + } + return true; + } + + private: + static void thenCallback(const v8::FunctionCallbackInfo& info) { + ProtocolPromiseHandler* handler = static_cast( + info.Data().As()->Value()); + DCHECK(handler); + v8::Local value = + info.Length() > 0 + ? info[0] + : v8::Local::Cast(v8::Undefined(info.GetIsolate())); + handler->thenCallback(value); + delete handler; + } + + static void catchCallback(const v8::FunctionCallbackInfo& info) { + ProtocolPromiseHandler* handler = static_cast( + info.Data().As()->Value()); + DCHECK(handler); + v8::Local value = + info.Length() > 0 + ? info[0] + : v8::Local::Cast(v8::Undefined(info.GetIsolate())); + handler->catchCallback(value); + delete handler; + } + + ProtocolPromiseHandler(V8InspectorSessionImpl* session, + int executionContextId, const String16& objectGroup, + bool returnByValue, bool generatePreview, + EvaluateCallback* callback) + : m_inspector(session->inspector()), + m_sessionId(session->sessionId()), + m_contextGroupId(session->contextGroupId()), + m_executionContextId(executionContextId), + m_objectGroup(objectGroup), + m_returnByValue(returnByValue), + m_generatePreview(generatePreview), + m_callback(std::move(callback)), + m_wrapper(m_inspector->isolate(), + v8::External::New(m_inspector->isolate(), this)) { + m_wrapper.SetWeak(this, cleanup, v8::WeakCallbackType::kParameter); + } + + static void cleanup( + const v8::WeakCallbackInfo& data) { + if (!data.GetParameter()->m_wrapper.IsEmpty()) { + data.GetParameter()->m_wrapper.Reset(); + data.SetSecondPassCallback(cleanup); + } else { + data.GetParameter()->m_callback->sendFailure( + Response::Error("Promise was collected")); + delete data.GetParameter(); + } + } + + void thenCallback(v8::Local result) { + V8InspectorSessionImpl* session = + m_inspector->sessionById(m_contextGroupId, m_sessionId); + if (!session) return; + InjectedScript::ContextScope scope(session, m_executionContextId); + Response response = scope.initialize(); + if (!response.isSuccess()) return; + if (m_objectGroup == "console") { + scope.injectedScript()->setLastEvaluationResult(result); + } + std::unique_ptr callback = + scope.injectedScript()->takeEvaluateCallback(m_callback); + if (!callback) return; + std::unique_ptr wrappedValue; + response = scope.injectedScript()->wrapObject( + result, m_objectGroup, m_returnByValue, m_generatePreview, + &wrappedValue); + if (!response.isSuccess()) { + callback->sendFailure(response); + return; + } + callback->sendSuccess(std::move(wrappedValue), + Maybe()); + } + + void catchCallback(v8::Local result) { + V8InspectorSessionImpl* session = + m_inspector->sessionById(m_contextGroupId, m_sessionId); + if (!session) return; + InjectedScript::ContextScope scope(session, m_executionContextId); + Response response = scope.initialize(); + if (!response.isSuccess()) return; + std::unique_ptr callback = + scope.injectedScript()->takeEvaluateCallback(m_callback); + if (!callback) return; + std::unique_ptr wrappedValue; + response = scope.injectedScript()->wrapObject( + result, m_objectGroup, m_returnByValue, m_generatePreview, + &wrappedValue); + if (!response.isSuccess()) { + callback->sendFailure(response); + return; + } + String16 message; + std::unique_ptr stack; + v8::Isolate* isolate = session->inspector()->isolate(); + if (result->IsNativeError()) { + message = " " + toProtocolString( + result->ToDetailString(isolate->GetCurrentContext()) + .ToLocalChecked()); + v8::Local stackTrace = v8::debug::GetDetailedStackTrace( + isolate, v8::Local::Cast(result)); + if (!stackTrace.IsEmpty()) { + stack = m_inspector->debugger()->createStackTrace(stackTrace); + } + } + if (!stack) { + stack = m_inspector->debugger()->captureStackTrace(true); + } + std::unique_ptr exceptionDetails = + protocol::Runtime::ExceptionDetails::create() + .setExceptionId(m_inspector->nextExceptionId()) + .setText("Uncaught (in promise)" + message) + .setLineNumber(stack && !stack->isEmpty() ? stack->topLineNumber() + : 0) + .setColumnNumber( + stack && !stack->isEmpty() ? stack->topColumnNumber() : 0) + .setException(wrappedValue->clone()) + .build(); + if (stack) + exceptionDetails->setStackTrace(stack->buildInspectorObjectImpl()); + if (stack && !stack->isEmpty()) + exceptionDetails->setScriptId(toString16(stack->topScriptId())); + callback->sendSuccess(std::move(wrappedValue), std::move(exceptionDetails)); + } + + V8InspectorImpl* m_inspector; + int m_sessionId; + int m_contextGroupId; + int m_executionContextId; + String16 m_objectGroup; + bool m_returnByValue; + bool m_generatePreview; + EvaluateCallback* m_callback; + v8::Global m_wrapper; +}; + std::unique_ptr InjectedScript::create( InspectedContext* inspectedContext, int sessionId) { v8::Isolate* isolate = inspectedContext->isolate(); @@ -122,7 +295,7 @@ InjectedScript::InjectedScript(InspectedContext* context, m_value(context->isolate(), object), m_sessionId(sessionId) {} -InjectedScript::~InjectedScript() {} +InjectedScript::~InjectedScript() { discardEvaluateCallbacks(); } Response InjectedScript::getProperties( v8::Local object, const String16& groupName, bool ownProperties, @@ -269,6 +442,47 @@ std::unique_ptr InjectedScript::wrapTable( &errors); } +void InjectedScript::addPromiseCallback( + V8InspectorSessionImpl* session, v8::MaybeLocal value, + const String16& notPromiseError, const String16& objectGroup, + bool returnByValue, bool generatePreview, + std::unique_ptr callback) { + if (value.IsEmpty()) { + callback->sendFailure(Response::InternalError()); + return; + } + if (!value.ToLocalChecked()->IsPromise()) { + callback->sendFailure(Response::Error(notPromiseError)); + return; + } + v8::MicrotasksScope microtasksScope(m_context->isolate(), + v8::MicrotasksScope::kRunMicrotasks); + if (ProtocolPromiseHandler::add(session, m_context->context(), + value.ToLocalChecked().As(), + notPromiseError, m_context->contextId(), + objectGroup, returnByValue, generatePreview, + callback.get())) { + m_evaluateCallbacks.insert(callback.release()); + } +} + +void InjectedScript::discardEvaluateCallbacks() { + for (auto& callback : m_evaluateCallbacks) { + callback->sendFailure(Response::Error("Execution context was destroyed.")); + delete callback; + } + m_evaluateCallbacks.clear(); +} + +std::unique_ptr InjectedScript::takeEvaluateCallback( + EvaluateCallback* callback) { + auto it = m_evaluateCallbacks.find(callback); + if (it == m_evaluateCallbacks.end()) return nullptr; + std::unique_ptr value(*it); + m_evaluateCallbacks.erase(it); + return value; +} + Response InjectedScript::findObject(const RemoteObjectId& objectId, v8::Local* outObject) const { auto it = m_idToWrappedObject.find(objectId.id()); diff --git a/src/inspector/injected-script.h b/src/inspector/injected-script.h index a064c7303d..52ed7ce558 100644 --- a/src/inspector/injected-script.h +++ b/src/inspector/injected-script.h @@ -32,6 +32,7 @@ #define V8_INSPECTOR_INJECTEDSCRIPT_H_ #include +#include #include "src/base/macros.h" #include "src/inspector/inspected-context.h" @@ -52,6 +53,16 @@ class V8InspectorSessionImpl; using protocol::Maybe; using protocol::Response; +class EvaluateCallback { + public: + virtual void sendSuccess( + std::unique_ptr result, + protocol::Maybe + exceptionDetails) = 0; + virtual void sendFailure(const protocol::DispatchResponse& response) = 0; + virtual ~EvaluateCallback() {} +}; + class InjectedScript final { public: static std::unique_ptr create(InspectedContext*, @@ -86,6 +97,13 @@ class InjectedScript final { std::unique_ptr wrapTable( v8::Local table, v8::Local columns) const; + void addPromiseCallback(V8InspectorSessionImpl* session, + v8::MaybeLocal value, + const String16& notPromiseError, + const String16& objectGroup, bool returnByValue, + bool generatePreview, + std::unique_ptr callback); + Response findObject(const RemoteObjectId&, v8::Local*) const; String16 objectGroupName(const RemoteObjectId&) const; void releaseObjectGroup(const String16&); @@ -191,6 +209,11 @@ class InjectedScript final { v8::Local commandLineAPI(); void unbindObject(int id); + class ProtocolPromiseHandler; + void discardEvaluateCallbacks(); + std::unique_ptr takeEvaluateCallback( + EvaluateCallback* callback); + InspectedContext* m_context; v8::Global m_value; int m_sessionId; @@ -200,6 +223,7 @@ class InjectedScript final { std::unordered_map> m_idToWrappedObject; std::unordered_map m_idToObjectGroupName; std::unordered_map> m_nameToObjectGroup; + std::unordered_set m_evaluateCallbacks; DISALLOW_COPY_AND_ASSIGN(InjectedScript); }; diff --git a/src/inspector/v8-runtime-agent-impl.cc b/src/inspector/v8-runtime-agent-impl.cc index 009113eb8e..6255eac444 100644 --- a/src/inspector/v8-runtime-agent-impl.cc +++ b/src/inspector/v8-runtime-agent-impl.cc @@ -58,192 +58,37 @@ using protocol::Runtime::RemoteObject; namespace { -template -class ProtocolPromiseHandler { +template +class EvaluateCallbackWrapper : public EvaluateCallback { public: - static void add(V8InspectorSessionImpl* session, - v8::Local context, - v8::MaybeLocal value, - const String16& notPromiseError, int executionContextId, - const String16& objectGroup, bool returnByValue, - bool generatePreview, std::unique_ptr callback) { - if (value.IsEmpty()) { - callback->sendFailure(Response::InternalError()); - return; - } - if (!value.ToLocalChecked()->IsPromise()) { - callback->sendFailure(Response::Error(notPromiseError)); - return; - } - V8InspectorImpl* inspector = session->inspector(); - v8::MicrotasksScope microtasks_scope(inspector->isolate(), - v8::MicrotasksScope::kRunMicrotasks); - v8::Local promise = - v8::Local::Cast(value.ToLocalChecked()); - Callback* rawCallback = callback.get(); - ProtocolPromiseHandler* handler = new ProtocolPromiseHandler( - session, executionContextId, objectGroup, returnByValue, - generatePreview, std::move(callback)); - v8::Local wrapper = handler->m_wrapper.Get(inspector->isolate()); - - v8::Local thenCallbackFunction = - v8::Function::New(context, thenCallback, wrapper, 0, - v8::ConstructorBehavior::kThrow) - .ToLocalChecked(); - if (promise->Then(context, thenCallbackFunction).IsEmpty()) { - rawCallback->sendFailure(Response::InternalError()); - return; - } - v8::Local catchCallbackFunction = - v8::Function::New(context, catchCallback, wrapper, 0, - v8::ConstructorBehavior::kThrow) - .ToLocalChecked(); - if (promise->Catch(context, catchCallbackFunction).IsEmpty()) { - rawCallback->sendFailure(Response::InternalError()); - return; - } + static std::unique_ptr wrap( + std::unique_ptr callback) { + return std::unique_ptr( + new EvaluateCallbackWrapper(std::move(callback))); + } + void sendSuccess(std::unique_ptr result, + protocol::Maybe + exceptionDetails) override { + return m_callback->sendSuccess(std::move(result), + std::move(exceptionDetails)); + } + void sendFailure(const protocol::DispatchResponse& response) override { + return m_callback->sendFailure(response); } private: - static void thenCallback(const v8::FunctionCallbackInfo& info) { - ProtocolPromiseHandler* handler = - static_cast*>( - info.Data().As()->Value()); - DCHECK(handler); - v8::Local value = - info.Length() > 0 - ? info[0] - : v8::Local::Cast(v8::Undefined(info.GetIsolate())); - std::unique_ptr wrappedValue( - handler->wrapObject(value, true)); - if (!wrappedValue) return; - handler->m_callback->sendSuccess( - std::move(wrappedValue), Maybe()); - } + explicit EvaluateCallbackWrapper(std::unique_ptr callback) + : m_callback(std::move(callback)) {} - static void catchCallback(const v8::FunctionCallbackInfo& info) { - ProtocolPromiseHandler* handler = - static_cast*>( - info.Data().As()->Value()); - DCHECK(handler); - v8::Local value = - info.Length() > 0 - ? info[0] - : v8::Local::Cast(v8::Undefined(info.GetIsolate())); - - std::unique_ptr wrappedValue( - handler->wrapObject(value, false)); - if (!wrappedValue) return; - - String16 message; - std::unique_ptr stack; - if (value->IsNativeError()) { - message = - " " + - toProtocolString( - value->ToDetailString(info.GetIsolate()->GetCurrentContext()) - .ToLocalChecked()); - v8::Local stackTrace = v8::debug::GetDetailedStackTrace( - info.GetIsolate(), v8::Local::Cast(value)); - if (!stackTrace.IsEmpty()) { - stack = handler->m_inspector->debugger()->createStackTrace(stackTrace); - } - } - if (!stack) { - stack = handler->m_inspector->debugger()->captureStackTrace(true); - } - std::unique_ptr exceptionDetails = - protocol::Runtime::ExceptionDetails::create() - .setExceptionId(handler->m_inspector->nextExceptionId()) - .setText("Uncaught (in promise)" + message) - .setLineNumber(stack && !stack->isEmpty() ? stack->topLineNumber() - : 0) - .setColumnNumber( - stack && !stack->isEmpty() ? stack->topColumnNumber() : 0) - .setException(wrappedValue->clone()) - .build(); - if (stack) - exceptionDetails->setStackTrace(stack->buildInspectorObjectImpl()); - if (stack && !stack->isEmpty()) - exceptionDetails->setScriptId(toString16(stack->topScriptId())); - handler->m_callback->sendSuccess(std::move(wrappedValue), - std::move(exceptionDetails)); - } - - ProtocolPromiseHandler(V8InspectorSessionImpl* session, - int executionContextId, const String16& objectGroup, - bool returnByValue, bool generatePreview, - std::unique_ptr callback) - : m_inspector(session->inspector()), - m_sessionId(session->sessionId()), - m_contextGroupId(session->contextGroupId()), - m_executionContextId(executionContextId), - m_objectGroup(objectGroup), - m_returnByValue(returnByValue), - m_generatePreview(generatePreview), - m_callback(std::move(callback)), - m_wrapper(m_inspector->isolate(), - v8::External::New(m_inspector->isolate(), this)) { - m_wrapper.SetWeak(this, cleanup, v8::WeakCallbackType::kParameter); - } - - static void cleanup( - const v8::WeakCallbackInfo>& data) { - if (!data.GetParameter()->m_wrapper.IsEmpty()) { - data.GetParameter()->m_wrapper.Reset(); - data.SetSecondPassCallback(cleanup); - } else { - data.GetParameter()->m_callback->sendFailure( - Response::Error("Promise was collected")); - delete data.GetParameter(); - } - } - - std::unique_ptr wrapObject( - v8::Local value, bool success) { - V8InspectorSessionImpl* session = - m_inspector->sessionById(m_contextGroupId, m_sessionId); - if (!session) { - m_callback->sendFailure(Response::Error("No session")); - return nullptr; - } - InjectedScript::ContextScope scope(session, m_executionContextId); - Response response = scope.initialize(); - if (!response.isSuccess()) { - m_callback->sendFailure(response); - return nullptr; - } - if (success && m_objectGroup == "console") { - scope.injectedScript()->setLastEvaluationResult(value); - } - std::unique_ptr wrappedValue; - response = scope.injectedScript()->wrapObject( - value, m_objectGroup, m_returnByValue, m_generatePreview, - &wrappedValue); - if (!response.isSuccess()) { - m_callback->sendFailure(response); - return nullptr; - } - return wrappedValue; - } - - V8InspectorImpl* m_inspector; - int m_sessionId; - int m_contextGroupId; - int m_executionContextId; - String16 m_objectGroup; - bool m_returnByValue; - bool m_generatePreview; - std::unique_ptr m_callback; - v8::Global m_wrapper; + std::unique_ptr m_callback; }; -template +template bool wrapEvaluateResultAsync(InjectedScript* injectedScript, v8::MaybeLocal maybeResultValue, const v8::TryCatch& tryCatch, const String16& objectGroup, bool returnByValue, - bool generatePreview, Callback* callback) { + bool generatePreview, ProtocolCallback* callback) { std::unique_ptr result; Maybe exceptionDetails; @@ -344,12 +189,11 @@ void V8RuntimeAgentImpl::evaluate( generatePreview.fromMaybe(false), callback.get()); return; } - ProtocolPromiseHandler::add( - m_session, scope.context(), maybeResultValue, - "Result of the evaluation is not a promise", - scope.injectedScript()->context()->contextId(), objectGroup.fromMaybe(""), - returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), - std::move(callback)); + scope.injectedScript()->addPromiseCallback( + m_session, maybeResultValue, "Result of the evaluation is not a promise", + objectGroup.fromMaybe(""), returnByValue.fromMaybe(false), + generatePreview.fromMaybe(false), + EvaluateCallbackWrapper::wrap(std::move(callback))); } void V8RuntimeAgentImpl::awaitPromise( @@ -362,12 +206,11 @@ void V8RuntimeAgentImpl::awaitPromise( callback->sendFailure(response); return; } - ProtocolPromiseHandler::add( - m_session, scope.context(), scope.object(), - "Could not find promise with given id", - scope.injectedScript()->context()->contextId(), scope.objectGroupName(), - returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), - std::move(callback)); + scope.injectedScript()->addPromiseCallback( + m_session, scope.object(), "Could not find promise with given id", + scope.objectGroupName(), returnByValue.fromMaybe(false), + generatePreview.fromMaybe(false), + EvaluateCallbackWrapper::wrap(std::move(callback))); } void V8RuntimeAgentImpl::callFunctionOn( @@ -460,12 +303,12 @@ void V8RuntimeAgentImpl::callFunctionOn( return; } - ProtocolPromiseHandler::add( - m_session, scope.context(), maybeResultValue, - "Result of the function call is not a promise", - scope.injectedScript()->context()->contextId(), scope.objectGroupName(), + scope.injectedScript()->addPromiseCallback( + m_session, maybeResultValue, + "Result of the function call is not a promise", scope.objectGroupName(), returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), - std::move(callback)); + EvaluateCallbackWrapper::wrap( + std::move(callback))); } Response V8RuntimeAgentImpl::getProperties( @@ -669,12 +512,12 @@ void V8RuntimeAgentImpl::runScript( generatePreview.fromMaybe(false), callback.get()); return; } - ProtocolPromiseHandler::add( - m_session, scope.context(), maybeResultValue.ToLocalChecked(), + scope.injectedScript()->addPromiseCallback( + m_session, maybeResultValue.ToLocalChecked(), "Result of the script execution is not a promise", - scope.injectedScript()->context()->contextId(), objectGroup.fromMaybe(""), - returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), - std::move(callback)); + objectGroup.fromMaybe(""), returnByValue.fromMaybe(false), + generatePreview.fromMaybe(false), + EvaluateCallbackWrapper::wrap(std::move(callback))); } void V8RuntimeAgentImpl::restore() { diff --git a/test/inspector/runtime/evaluate-async-expected.txt b/test/inspector/runtime/evaluate-async-expected.txt index e1be517bf5..38171c4000 100644 --- a/test/inspector/runtime/evaluate-async-expected.txt +++ b/test/inspector/runtime/evaluate-async-expected.txt @@ -203,3 +203,29 @@ Running test: testLastEvaluatedResult } } } + +Running test: testRuntimeDisable +Resolving promise.. +{ + id : + result : { + result : { + className : Object + description : Object + objectId : + type : object + } + } +} +Promise resolved + +Running test: testImmediatelyResolvedAfterAfterContextDestroyed +Destroying context.. +{ + error : { + code : -32000 + message : Execution context was destroyed. + } + id : +} +Context destroyed diff --git a/test/inspector/runtime/evaluate-async.js b/test/inspector/runtime/evaluate-async.js index 4dfcf22972..02da68745c 100644 --- a/test/inspector/runtime/evaluate-async.js +++ b/test/inspector/runtime/evaluate-async.js @@ -30,73 +30,109 @@ function throwSyntaxError() } `); -InspectorTest.runTestSuite([ - function testResolvedPromise(next) +InspectorTest.runAsyncTestSuite([ + async function testResolvedPromise() { - Protocol.Runtime.evaluate({ expression: "Promise.resolve(239)", awaitPromise: true, generatePreview: true }) - .then(result => InspectorTest.logMessage(result)) - .then(() => next()); + InspectorTest.logMessage(await Protocol.Runtime.evaluate({ + expression: "Promise.resolve(239)", + awaitPromise: true, + generatePreview: true + })); }, - function testRejectedPromise(next) + async function testRejectedPromise() { - Protocol.Runtime.evaluate({ expression: "Promise.reject(239)", awaitPromise: true }) - .then(result => InspectorTest.logMessage(result)) - .then(() => next()); + InspectorTest.logMessage(await Protocol.Runtime.evaluate({ + expression: "Promise.reject(239)", + awaitPromise: true + })); }, - function testRejectedPromiseWithError(next) + async function testRejectedPromiseWithError() { Protocol.Runtime.enable(); - Protocol.Runtime.evaluate({ expression: "Promise.resolve().then(throwError)", awaitPromise: true }) - .then(result => InspectorTest.logMessage(result)) - .then(Protocol.Runtime.disable) - .then(() => next()); + InspectorTest.logMessage(await Protocol.Runtime.evaluate({ + expression: "Promise.resolve().then(throwError)", + awaitPromise: true + })); + await Protocol.Runtime.disable(); }, - function testRejectedPromiseWithSyntaxError(next) + async function testRejectedPromiseWithSyntaxError() { Protocol.Runtime.enable(); - Protocol.Runtime.evaluate({ expression: "Promise.resolve().then(throwSyntaxError)", awaitPromise: true }) - .then(result => InspectorTest.logMessage(result)) - .then(Protocol.Runtime.disable) - .then(() => next()); + InspectorTest.logMessage(await Protocol.Runtime.evaluate({ + expression: "Promise.resolve().then(throwSyntaxError)", + awaitPromise: true + })); + await Protocol.Runtime.disable(); }, - function testPrimitiveValueInsteadOfPromise(next) + async function testPrimitiveValueInsteadOfPromise() { - Protocol.Runtime.evaluate({ expression: "true", awaitPromise: true }) - .then(result => InspectorTest.logMessage(result)) - .then(() => next()); + InspectorTest.logMessage(await Protocol.Runtime.evaluate({ + expression: "true", + awaitPromise: true + })); }, - function testObjectInsteadOfPromise(next) + async function testObjectInsteadOfPromise() { - Protocol.Runtime.evaluate({ expression: "({})", awaitPromise: true }) - .then(result => InspectorTest.logMessage(result)) - .then(() => next()); + InspectorTest.logMessage(await Protocol.Runtime.evaluate({ + expression: "({})", + awaitPromise: true + })); }, - function testPendingPromise(next) + async function testPendingPromise() { - Protocol.Runtime.evaluate({ expression: "createPromiseAndScheduleResolve()", awaitPromise: true, returnByValue: true }) - .then(result => InspectorTest.logMessage(result)) - .then(() => next()); + InspectorTest.logMessage(await Protocol.Runtime.evaluate({ + expression: "createPromiseAndScheduleResolve()", + awaitPromise: true, + returnByValue: true + })); }, - function testExceptionInEvaluate(next) + async function testExceptionInEvaluate() { - Protocol.Runtime.evaluate({ expression: "throw 239", awaitPromise: true }) - .then(result => InspectorTest.logMessage(result)) - .then(() => next()); + InspectorTest.logMessage(await Protocol.Runtime.evaluate({ + expression: "throw 239", + awaitPromise: true + })); }, - function testLastEvaluatedResult(next) + async function testLastEvaluatedResult() { - Protocol.Runtime.evaluate({ expression: 'Promise.resolve(42)', awaitPromise: true, objectGroup: 'console' }) - .then(result => InspectorTest.logMessage(result)) - .then(() => Protocol.Runtime.evaluate({ expression: '$_', includeCommandLineAPI: true })) - .then(result => InspectorTest.logMessage(result)) - .then(() => next()); + InspectorTest.logMessage(await Protocol.Runtime.evaluate({ + expression: 'Promise.resolve(42)', + awaitPromise: true, + objectGroup: 'console' + })); + InspectorTest.logMessage(await Protocol.Runtime.evaluate({ + expression: '$_', + includeCommandLineAPI: true + })); }, + + async function testRuntimeDisable() + { + await Protocol.Runtime.enable(); + Protocol.Runtime.evaluate({ + expression: 'new Promise(r1 => r = r1)', + awaitPromise: true }).then(InspectorTest.logMessage); + await Protocol.Runtime.disable(); + InspectorTest.log('Resolving promise..'); + await Protocol.Runtime.evaluate({expression: 'r({a:1})'}); + InspectorTest.log('Promise resolved'); + }, + + async function testImmediatelyResolvedAfterAfterContextDestroyed() + { + Protocol.Runtime.evaluate({ + expression: 'a = new Promise(() => 42)', + awaitPromise: true }).then(InspectorTest.logMessage); + InspectorTest.log('Destroying context..'); + await Protocol.Runtime.evaluate({expression: 'inspector.fireContextDestroyed()'}); + InspectorTest.log('Context destroyed'); + } ]);