Files
android-dev-ext/src/threads.js
adelphes 0b5be3020a Fixed exception details not being displayed
Make sure the exception scoperef is allocated before locals are retrieved.
2017-03-02 16:12:27 +00:00

130 lines
5.0 KiB
JavaScript

'use strict'
const { AndroidVariables } = require('./variables');
const $ = require('./jq-promise');
/*
Class used to manage a single thread reported by JDWP
*/
class AndroidThread {
constructor(session, threadid, vscode_threadid) {
// the AndroidDebugSession instance
this.session = session;
// the Android debugger instance
this.dbgr = session.dbgr;
// the java thread id (hex string)
this.threadid = threadid;
// the vscode thread id (number)
this.vscode_threadid = vscode_threadid;
// the (Java) name of the thread
this.name = null;
// the thread break info
this.paused = null;
// the timeout during a step which, if it expires, we allow other threads to break
this.stepTimeout = null;
}
threadNotSuspendedError() {
return new Error(`Thread ${this.vscode_threadid} not suspended`);
}
addStackFrameVariable(frame, level) {
if (!this.paused) throw this.threadNotSuspendedError();
var frameId = (this.vscode_threadid * 1e9) + (level * 1e6);
var stack_frame_var = {
frame, frameId,
locals: null,
}
return this.paused.stack_frame_vars[frameId] = stack_frame_var;
}
allocateExceptionScopeReference(frameId) {
if (!this.paused) return;
if (!this.paused.last_exception) return;
this.paused.last_exception.frameId = frameId;
this.paused.last_exception.scopeRef = frameId + 1;
}
getVariables(variablesReference) {
if (!this.paused)
return $.Deferred().rejectWith(this, [this.threadNotSuspendedError()]);
// is this reference a stack frame
var stack_frame_var = this.paused.stack_frame_vars[variablesReference];
if (stack_frame_var) {
// frame locals request
return this._ensureLocals(stack_frame_var).then(varref => this.paused.stack_frame_vars[varref].locals.getVariables(varref));
}
// is this refrence an exception scope
if (this.paused.last_exception && variablesReference === this.paused.last_exception.scopeRef) {
var stack_frame_var = this.paused.stack_frame_vars[this.paused.last_exception.frameId];
return this._ensureLocals(stack_frame_var).then(varref => this.paused.stack_frame_vars[varref].locals.getVariables(this.paused.last_exception.scopeRef));
}
// work out which stack frame this reference is for
var frameId = Math.trunc(variablesReference/1e6) * 1e6;
var stack_frame_var = this.paused.stack_frame_vars[frameId];
return stack_frame_var.locals.getVariables(variablesReference);
}
_ensureLocals(varinfo) {
if (!this.paused)
return $.Deferred().rejectWith(this, [this.threadNotSuspendedError()]);
// evaluate can call this using frameId as the argument
if (typeof varinfo === 'number')
return this._ensureLocals(this.paused.stack_frame_vars[varinfo]);
// if we're currently processing it (or we've finished), just return the promise
if (this.paused.locals_done[varinfo.frameId])
return this.paused.locals_done[varinfo.frameId];
// create a new promise
var def = this.paused.locals_done[varinfo.frameId] = $.Deferred();
this.dbgr.getlocals(this.threadid, varinfo.frame, {def:def,varinfo:varinfo})
.then((locals,x) => {
// make sure we are still paused...
if (!this.paused)
throw this.threadNotSuspendedError();
// sort the locals by name, except for 'this' which always goes first
locals.sort((a,b) => {
if (a.name === b.name) return 0;
if (a.name === 'this') return -1;
if (b.name === 'this') return +1;
return a.name.localeCompare(b.name);
})
// create a new local variable with the results and resolve the promise
var varinfo = x.varinfo;
varinfo.cached = locals;
x.varinfo.locals = new AndroidVariables(this.session, x.varinfo.frameId + 2); // 0 = stack frame, 1 = exception, 2... others
x.varinfo.locals.setVariable(varinfo.frameId, varinfo);
var last_exception = this.paused.last_exception;
if (last_exception) {
x.varinfo.locals.setVariable(last_exception.scopeRef, last_exception);
}
x.def.resolveWith(this, [varinfo.frameId]);
})
.fail(e => {
x.def.rejectWith(this, [e]);
})
return def;
}
setVariableValue(args) {
var frameId = Math.trunc(args.variablesReference/1e6) * 1e6;
var stack_frame_var = this.paused.stack_frame_vars[frameId];
return this._ensureLocals(stack_frame_var).then(varref => {
return this.paused.stack_frame_vars[varref].locals.setVariableValue(args);
});
}
}
exports.AndroidThread = AndroidThread;