Version 1.1 improvements (#88)

* fix 0 alignment in binary xml decoding

* output reason for APK manifest read failure

* try and match package name against process name
when determining which pid to attach

* make post launch pause user-configurable

* code tidy, jsdocs and types

* more types in expression parse classes

* fix issue with expandable objects not evaluating

* update build task example

* fix package/type evaluation

* improve handling of targetDevice and processID combinations

* show full call stack by default

* implement a queue for evaluations

* improve performance of retrieving single fields

* check root term identifiers against this fields
This commit is contained in:
Dave Holoway
2020-04-24 19:03:39 +01:00
committed by GitHub
parent a4ce09d309
commit 6439e1b8b7
14 changed files with 381 additions and 138 deletions

View File

@@ -511,8 +511,20 @@ async function evaluate_identifier(dbgr, locals, identifier) {
if (local) {
return local;
}
// check if the identifier is an unqualified member of the current 'this' context
const this_context = locals.find(l => l.name === 'this');
if (this_context) {
try {
const member = await evaluate_member(dbgr, new MemberExpression(identifier), this_context);
return member;
} catch {
// not a member of this - just continue
}
}
// if it's not a local, it could be the start of a package name or a type
const classes = await dbgr.getAllClasses();
const classes = Array.from(dbgr.session.loadedClasses);
return evaluate_qualified_type_name(dbgr, identifier, classes);
}
@@ -520,17 +532,17 @@ async function evaluate_identifier(dbgr, locals, identifier) {
*
* @param {Debugger} dbgr
* @param {string} dotted_name
* @param {*[]} classes
* @param {string[]} classes
*/
async function evaluate_qualified_type_name(dbgr, dotted_name, classes) {
const exact_class_matcher = new RegExp(`^L(java/lang/)?${dotted_name.replace(/\./g,'[$/]')};$`);
const exact_class = classes.find(c => exact_class_matcher.test(c.type.signature));
const exact_class = classes.find(signature => exact_class_matcher.test(signature));
if (exact_class) {
return dbgr.getTypeValue(exact_class.type.signature);
return dbgr.getTypeValue(exact_class);
}
const class_matcher = new RegExp(`^L(java/lang/)?${dotted_name.replace('.','[$/]')}/`);
const matching_classes = classes.filter(c => class_matcher.test(c.type.signature));
const matching_classes = classes.filter(signature => class_matcher.test(signature));
if (matching_classes.length === 0) {
// the dotted name doesn't match any packages
throw new Error(`'${dotted_name}' is not a package, type or variable name`);
@@ -623,7 +635,7 @@ async function evaluate_qualifiers(dbgr, locals, thread, value, qualified_terms)
i++;
continue;
}
value = await evaluate_member(dbgr, locals, thread, term, value);
value = await evaluate_member(dbgr, term, value);
continue;
}
if (term instanceof ArrayIndexExpression) {
@@ -822,12 +834,10 @@ async function evaluate_methodcall(dbgr, locals, thread, method_name, m, obj_loc
/**
* @param {Debugger} dbgr
* @param {DebuggerValue[]} locals
* @param {AndroidThread} thread
* @param {MemberExpression} member
* @param {DebuggerValue} value
*/
async function evaluate_member(dbgr, locals, thread, member, value) {
async function evaluate_member(dbgr, member, value) {
if (!JavaType.isReference(value.type)) {
throw new Error('TypeError: value is not a reference type');
}
@@ -952,7 +962,7 @@ async function evaluate_cast(dbgr, locals, thread, cast_type, rhs) {
* @param {Debugger} dbgr
* @param {{allowFormatSpecifier:boolean}} [options]
*/
async function evaluate(expression, thread, locals, dbgr, options) {
async function evaluate_one_expression(expression, thread, locals, dbgr, options) {
D('evaluate: ' + expression);
await dbgr.ensureConnected();
@@ -994,6 +1004,45 @@ async function evaluate(expression, thread, locals, dbgr, options) {
}
}
/**
*
*/
const queuedExpressions = [];
/**
* @param {string} expression
* @param {AndroidThread} thread
* @param {DebuggerValue[]} locals
* @param {Debugger} dbgr
* @param {{allowFormatSpecifier:boolean}} [options]
*/
async function evaluate(expression, thread, locals, dbgr, options) {
return new Promise(async (resolve, reject) => {
const queue_length = queuedExpressions.push({
expression, thread, locals, dbgr, options,
resolve, reject
});
if (queue_length > 1) {
return;
}
// run the queue
while (queuedExpressions.length) {
const {
expression, thread, locals, dbgr, options,
resolve, reject
} = queuedExpressions[0];
try {
const res = await evaluate_one_expression(expression, thread, locals, dbgr, options);
resolve(res);
} catch (err) {
reject(err);
}
queuedExpressions.shift();
}
});
}
module.exports = {
evaluate,
}