From 84e03b11c4b57edb43fb1ebc6707396980ba3146 Mon Sep 17 00:00:00 2001 From: adelphes Date: Thu, 26 Jan 2017 12:18:43 +0000 Subject: [PATCH] Improved exception scope display. The exception object is now expanded at the root scope level with the message displayed as an extra field --- src/debugMain.js | 50 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/src/debugMain.js b/src/debugMain.js index df205af..3576ee5 100644 --- a/src/debugMain.js +++ b/src/debugMain.js @@ -182,6 +182,7 @@ class AndroidDebugSession extends DebugSession { this._evals_queue = []; // the last (current) exception info this._last_exception = null; + this._exmsg_var_name = ':msg'; // the special name given to exception message fields // since we want to send breakpoint events, we will assign an id to every event // so that the frontend can match events with breakpoints. @@ -719,13 +720,21 @@ class AndroidDebugSession extends DebugSession { scopesRequest(response/*: DebugProtocol.ScopesResponse*/, args/*: DebugProtocol.ScopesArguments*/) { var scopes = [new Scope("Local", args.frameId, false)]; - if (this._last_exception) { - scopes.push(new Scope("Exception", this._last_exception.varref, false)); - } - response.body = { scopes: scopes }; + + if (this._last_exception && !this._last_exception.objvar) { + this.dbgr.getExceptionLocal(this._last_exception.exception, {response,scopes}) + .then((ex_local,x) => { + this._last_exception.objvar = ex_local; + // put the exception first - otherwise it can get lost if there's a lot of locals + x.scopes.unshift(new Scope("Exception: "+ex_local.type.typename, this._last_exception.varref, false)); + this.sendResponse(x.response); + }) + .fail(e => { this.sendResponse(response); }); + return; + } this.sendResponse(response); } @@ -846,6 +855,17 @@ class AndroidDebugSession extends DebugSession { x.supertype = supertype; return this.dbgr.getfieldvalues(x.varinfo.objvar, x); }) + .then((fields, x) => { + // add an extra msg field for exceptions + if (!x.varinfo.exception) return; + x.fields = fields; + return this.dbgr.invokeToString(x.varinfo.objvar.value, x.varinfo.threadid, varinfo.objvar.type.signature, x) + .then((call,x) => { + call.name = this._exmsg_var_name; + x.fields.unshift(call); + return $.Deferred().resolveWith(this, [x.fields, x]); + }); + }) .then((fields, x) => { // ignore supertypes of Object x.supertype && x.supertype.signature!=='Ljava/lang/Object;' && fields.unshift({ @@ -928,17 +948,6 @@ class AndroidDebugSession extends DebugSession { }; this.sendResponse(response); } - else if (varinfo.exception) { - this.dbgr.getExceptionLocal(varinfo.exception, {varinfo,response}) - .then((ex_local,x) => { - x.ex_local = ex_local; - return this.dbgr.invokeToString(ex_local.value, x.varinfo.threadid, ex_local.type.signature, x); - }) - .then((call,x) => { - call.name = '{msg}'; - return_mapped_vars(x.varinfo.cached = [call,x.ex_local], x.response); - }); - } else { // frame locals request this.dbgr.getlocals(varinfo.frame.threadid, varinfo.frame, response) @@ -1045,6 +1054,10 @@ class AndroidDebugSession extends DebugSession { } var destvar = v.cached.find(v => v.name===args.name); + if (!destvar || !/^(field|local|arrelem)$/.test(destvar.vtype)) { + failSetVariableRequest(response, `The value is read-only and cannot be updated.`); + return; + } // be nice and remove any superfluous whitespace var value = args.value.trim(); @@ -1263,6 +1276,13 @@ class AndroidDebugSession extends DebugSession { return this.sendResponseAndDoNext(response, '(running)'); } + // special case for evaluating exception messages + // - this is called if the user uses "Copy value" from the locals + if (args.expression===this._exmsg_var_name && this._last_exception && this._last_exception.cached) { + var msglocal = this._last_exception.cached.find(v => v.name===this._exmsg_var_name); + if (msglocal) return this.sendResponseAndDoNext(response, msglocal.string); + } + var parse_array_or_fncall = function(e) { var arg, res = {arr:[], call:null}; // pre-call array indexes