Commit 49ad05f6 authored by Alexey Kozyatinskiy's avatar Alexey Kozyatinskiy Committed by Commit Bot

[inspector] improved exception details for Runtime.evaluate with awaitPromise

Fetch message and stack trace from error object if Promise is rejected with native JS error.

Bug: v8:6249
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng
Change-Id: Icf84205eb9d16e860600b7cc916ddcb1922be040
Reviewed-on: https://chromium-review.googlesource.com/563096Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46483}
parent 0a61361e
......@@ -9844,6 +9844,18 @@ int debug::GetStackFrameId(v8::Local<v8::StackFrame> frame) {
return Utils::OpenHandle(*frame)->id();
}
v8::Local<v8::StackTrace> debug::GetDetailedStackTrace(
Isolate* v8_isolate, v8::Local<v8::Object> v8_error) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
i::Handle<i::JSReceiver> error = Utils::OpenHandle(*v8_error);
if (!error->IsJSObject()) {
return v8::Local<v8::StackTrace>();
}
i::Handle<i::FixedArray> stack_trace =
isolate->GetDetailedStackTrace(i::Handle<i::JSObject>::cast(error));
return Utils::StackTraceToLocal(stack_trace);
}
MaybeLocal<debug::Script> debug::GeneratorObject::Script() {
i::Handle<i::JSGeneratorObject> obj = Utils::OpenHandle(this);
i::Object* maybe_script = obj->function()->shared()->script();
......
......@@ -216,6 +216,9 @@ V8_EXPORT_PRIVATE void SetConsoleDelegate(Isolate* isolate,
int GetStackFrameId(v8::Local<v8::StackFrame> frame);
v8::Local<v8::StackTrace> GetDetailedStackTrace(Isolate* isolate,
v8::Local<v8::Object> error);
/**
* Native wrapper around v8::internal::JSGeneratorObject object.
*/
......
......@@ -135,12 +135,27 @@ class ProtocolPromiseHandler {
handler->wrapObject(value));
if (!wrappedValue) return;
std::unique_ptr<V8StackTraceImpl> stack =
handler->m_inspector->debugger()->captureStackTrace(true);
String16 message;
std::unique_ptr<V8StackTraceImpl> stack;
if (value->IsNativeError()) {
message =
" " +
toProtocolString(
value->ToDetailString(info.GetIsolate()->GetCurrentContext())
.ToLocalChecked());
v8::Local<v8::StackTrace> stackTrace = v8::debug::GetDetailedStackTrace(
info.GetIsolate(), v8::Local<v8::Object>::Cast(value));
if (!stackTrace.IsEmpty()) {
stack = handler->m_inspector->debugger()->createStackTrace(stackTrace);
}
}
if (!stack) {
stack = handler->m_inspector->debugger()->captureStackTrace(true);
}
std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails =
protocol::Runtime::ExceptionDetails::create()
.setExceptionId(handler->m_inspector->nextExceptionId())
.setText("Uncaught (in promise)")
.setText("Uncaught (in promise)" + message)
.setLineNumber(stack && !stack->isEmpty() ? stack->topLineNumber()
: 0)
.setColumnNumber(
......
......@@ -99,6 +99,56 @@ Running test: testRejectedPromiseWithStack
}
}
Running test: testRejectedPromiseWithError
{
id : <messageId>
result : {
exceptionDetails : {
columnNumber : 0
exception : {
className : Error
description : Error: MyError at rejectPromiseWithAnError (test.js:25:20) at <anonymous>:1:1
objectId : <objectId>
subtype : error
type : object
}
exceptionId : <exceptionId>
lineNumber : 0
stackTrace : {
callFrames : [
]
parent : {
callFrames : [
[0] : {
columnNumber : 4
functionName : rejectPromiseWithAnError
lineNumber : 24
scriptId : <scriptId>
url : test.js
}
[1] : {
columnNumber : 0
functionName :
lineNumber : 0
scriptId : <scriptId>
url :
}
]
description : Promise.reject
}
}
text : Uncaught (in promise) Error: MyError
}
result : {
className : Error
description : Error: MyError at rejectPromiseWithAnError (test.js:25:20) at <anonymous>:1:1
objectId : <objectId>
subtype : error
type : object
}
}
}
Running test: testPendingPromise
{
id : <messageId>
......
......@@ -28,6 +28,18 @@ function rejectPromise()
rejectCallback = undefined;
}
function rejectPromiseWithAnError()
{
rejectCallback(new Error('MyError'));
resolveCallback = undefined;
rejectCallback = undefined;
}
function throwError()
{
throw new Error('MyError');
}
//# sourceURL=test.js`);
Protocol.Debugger.enable()
......@@ -68,6 +80,21 @@ function testSuite()
}
},
function testRejectedPromiseWithError(next)
{
Protocol.Runtime.evaluate({ expression: "createPromise()"})
.then(result => scheduleRejectAndAwaitPromise(result))
.then(result => InspectorTest.logMessage(result))
.then(() => next());
function scheduleRejectAndAwaitPromise(result)
{
var promise = Protocol.Runtime.awaitPromise({ promiseObjectId: result.result.result.objectId });
Protocol.Runtime.evaluate({ expression: "rejectPromiseWithAnError()" });
return promise;
}
},
function testPendingPromise(next)
{
Protocol.Runtime.evaluate({ expression: "createPromise()"})
......
......@@ -35,6 +35,98 @@ Running test: testRejectedPromise
}
}
Running test: testRejectedPromiseWithError
{
id : <messageId>
result : {
exceptionDetails : {
columnNumber : 11
exception : {
className : Error
description : Error: MyError at foo (<anonymous>:13:11) at throwError (<anonymous>:15:3) at <anonymous>
objectId : <objectId>
subtype : error
type : object
}
exceptionId : <exceptionId>
lineNumber : 13
scriptId : <scriptId>
stackTrace : {
callFrames : [
[0] : {
columnNumber : 10
functionName : foo
lineNumber : 12
scriptId : <scriptId>
url :
}
[1] : {
columnNumber : 2
functionName : throwError
lineNumber : 14
scriptId : <scriptId>
url :
}
]
}
text : Uncaught (in promise) Error: MyError
}
result : {
className : Error
description : Error: MyError at foo (<anonymous>:13:11) at throwError (<anonymous>:15:3) at <anonymous>
objectId : <objectId>
subtype : error
type : object
}
}
}
Running test: testRejectedPromiseWithSyntaxError
{
id : <messageId>
result : {
exceptionDetails : {
columnNumber : 5
exception : {
className : SyntaxError
description : SyntaxError: Unexpected token } at foo (<anonymous>:21:5) at throwSyntaxError (<anonymous>:23:3) at <anonymous>
objectId : <objectId>
subtype : error
type : object
}
exceptionId : <exceptionId>
lineNumber : 21
scriptId : <scriptId>
stackTrace : {
callFrames : [
[0] : {
columnNumber : 4
functionName : foo
lineNumber : 20
scriptId : <scriptId>
url :
}
[1] : {
columnNumber : 2
functionName : throwSyntaxError
lineNumber : 22
scriptId : <scriptId>
url :
}
]
}
text : Uncaught (in promise) SyntaxError: Unexpected token }
}
result : {
className : SyntaxError
description : SyntaxError: Unexpected token } at foo (<anonymous>:21:5) at throwSyntaxError (<anonymous>:23:3) at <anonymous>
objectId : <objectId>
subtype : error
type : object
}
}
}
Running test: testPrimitiveValueInsteadOfPromise
{
error : {
......
......@@ -11,7 +11,24 @@ function createPromiseAndScheduleResolve()
var promise = new Promise((resolve) => resolveCallback = resolve);
setTimeout(resolveCallback.bind(null, { a : 239 }), 0);
return promise;
}`);
}
function throwError()
{
function foo() {
throw new Error('MyError');
}
foo();
}
function throwSyntaxError()
{
function foo() {
eval('}');
}
foo();
}
`);
InspectorTest.runTestSuite([
function testResolvedPromise(next)
......@@ -28,6 +45,24 @@ InspectorTest.runTestSuite([
.then(() => next());
},
function testRejectedPromiseWithError(next)
{
Protocol.Runtime.enable();
Protocol.Runtime.evaluate({ expression: "Promise.resolve().then(throwError)", awaitPromise: true })
.then(result => InspectorTest.logMessage(result))
.then(Protocol.Runtime.disable)
.then(() => next());
},
function testRejectedPromiseWithSyntaxError(next)
{
Protocol.Runtime.enable();
Protocol.Runtime.evaluate({ expression: "Promise.resolve().then(throwSyntaxError)", awaitPromise: true })
.then(result => InspectorTest.logMessage(result))
.then(Protocol.Runtime.disable)
.then(() => next());
},
function testPrimitiveValueInsteadOfPromise(next)
{
Protocol.Runtime.evaluate({ expression: "true", awaitPromise: true })
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment