mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-22 17:39:19 +00:00
Improve expression parsing
Allow ':super' to be used as a member. Search super types when locating field members. Fixes #16
This commit is contained in:
@@ -849,7 +849,7 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
// ignore supertypes of Object
|
// ignore supertypes of Object
|
||||||
x.supertype && x.supertype.signature!=='Ljava/lang/Object;' && fields.unshift({
|
x.supertype && x.supertype.signature!=='Ljava/lang/Object;' && fields.unshift({
|
||||||
vtype:'super',
|
vtype:'super',
|
||||||
name:'super',
|
name:':super',
|
||||||
hasnullvalue:false,
|
hasnullvalue:false,
|
||||||
type: x.supertype,
|
type: x.supertype,
|
||||||
value: x.varinfo.objvar.value,
|
value: x.varinfo.objvar.value,
|
||||||
@@ -1273,10 +1273,12 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
const resolve_evaluation = (value, variablesReference) => $.Deferred().resolveWith(this, [value, variablesReference]);
|
const resolve_evaluation = (value, variablesReference) => $.Deferred().resolveWith(this, [value, variablesReference]);
|
||||||
|
|
||||||
// special case for evaluating exception messages
|
// special case for evaluating exception messages
|
||||||
// - this is called if the user uses "Copy value" from the locals
|
// - this is called if the user tries to evaluate ':msg' from the locals
|
||||||
if (expression===this._exmsg_var_name && this._last_exception && this._last_exception.cached) {
|
if (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);
|
var msglocal = this._last_exception.cached.find(v => v.name===this._exmsg_var_name);
|
||||||
if (msglocal) return resolve_evaluation(msglocal.string);
|
if (msglocal) {
|
||||||
|
return resolve_evaluation(this._local_to_variable(msglocal).value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const parse_array_or_fncall = function(e) {
|
const parse_array_or_fncall = function(e) {
|
||||||
@@ -1330,7 +1332,7 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
while (e.expr[0] === '.') {
|
while (e.expr[0] === '.') {
|
||||||
// member expression
|
// member expression
|
||||||
e.expr = e.expr.slice(1).trim();
|
e.expr = e.expr.slice(1).trim();
|
||||||
var m, member_name = e.expr.match(/^[a-zA-Z_$][a-zA-Z0-9_$]*/);
|
var m, member_name = e.expr.match(/^:?[a-zA-Z_$][a-zA-Z0-9_$]*/); // allow : at start for :super and :msg
|
||||||
if (!member_name) return null;
|
if (!member_name) return null;
|
||||||
res.members.push(m = {member:member_name[0], array_or_fncall:null})
|
res.members.push(m = {member:member_name[0], array_or_fncall:null})
|
||||||
e.expr = e.expr.slice(m.member.length).trim();
|
e.expr = e.expr.slice(m.member.length).trim();
|
||||||
@@ -1403,23 +1405,32 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
const evaluate_member = (m, obj_local) => {
|
const evaluate_member = (m, obj_local) => {
|
||||||
if (!JTYPES.isReference(obj_local.type)) return reject_evaluation('TypeError: value is not a reference type');
|
if (!JTYPES.isReference(obj_local.type)) return reject_evaluation('TypeError: value is not a reference type');
|
||||||
if (obj_local.hasnullvalue) return reject_evaluation('NullPointerException');
|
if (obj_local.hasnullvalue) return reject_evaluation('NullPointerException');
|
||||||
if (m.array_or_fncall.call) return evaluate_methodcall(m, obj_local);
|
var chain;
|
||||||
|
if (m.array_or_fncall.call){
|
||||||
|
chain = evaluate_methodcall(m, obj_local);
|
||||||
|
}
|
||||||
// length is a 'fake' field of arrays, so special-case it
|
// length is a 'fake' field of arrays, so special-case it
|
||||||
if (JTYPES.isArray(obj_local.type) && m.member==='length')
|
else if (JTYPES.isArray(obj_local.type) && m.member==='length') {
|
||||||
return evaluate_number(obj_local.arraylen);
|
chain = evaluate_number(obj_local.arraylen);
|
||||||
return this.dbgr.getfieldvalues(obj_local, m)
|
}
|
||||||
.then((fields,m) => {
|
// we also special-case :super (for object instances)
|
||||||
var field = fields.find(f => f.name === m.member);
|
else if (JTYPES.isObject(obj_local.type) && m.member === ':super') {
|
||||||
if (!field) return reject_evaluation('no such field: '+m.member);
|
chain = this.dbgr.getsuperinstance(obj_local);
|
||||||
|
}
|
||||||
|
// anything else must be a real field
|
||||||
|
else {
|
||||||
|
chain = this.dbgr.getFieldValue(obj_local, m.member, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain.then(local => {
|
||||||
if (m.array_or_fncall.arr.length) {
|
if (m.array_or_fncall.arr.length) {
|
||||||
var q = $.Deferred();
|
var q = $.Deferred();
|
||||||
m.array_or_fncall.arr.reduce((q,index_expr) => {
|
m.array_or_fncall.arr.reduce((q,index_expr) => {
|
||||||
return q.then(function(index_expr,local) { return evaluate_array_element(index_expr,local) }.bind(this,index_expr));
|
return q.then(function(index_expr,local) { return evaluate_array_element(index_expr,local) }.bind(this,index_expr));
|
||||||
}, q);
|
}, q);
|
||||||
return q.resolveWith(this, [field]);
|
return q.resolveWith(this, [local]);
|
||||||
}
|
}
|
||||||
return field;
|
});
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var e = { expr:expression.trim() };
|
var e = { expr:expression.trim() };
|
||||||
|
|||||||
@@ -721,6 +721,15 @@ Debugger.prototype = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getsuperinstance: function (local, extra) {
|
||||||
|
return this.getsupertype(local, {local,extra})
|
||||||
|
.then(function (supertypeinfo, x) {
|
||||||
|
var castobj = Object.assign({}, x.local);
|
||||||
|
castobj.type = supertypeinfo;
|
||||||
|
return $.Deferred().resolveWith(this, [castobj, x.extra]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
createstring: function (string, extra) {
|
createstring: function (string, extra) {
|
||||||
return this.ensureconnected({ string: string, extra: extra })
|
return this.ensureconnected({ string: string, extra: extra })
|
||||||
.then(function (x) {
|
.then(function (x) {
|
||||||
@@ -838,6 +847,25 @@ Debugger.prototype = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getFieldValue: function(objvar, fieldname, includeInherited, extra) {
|
||||||
|
const findfield = x => {
|
||||||
|
return this.getfieldvalues(x.objvar, x)
|
||||||
|
.then((fields, x) => {
|
||||||
|
var field = fields.find(f => f.name === x.fieldname);
|
||||||
|
if (field) return $.Deferred().resolveWith(this,[field,x.extra]);
|
||||||
|
if (!x.includeInherited || x.objvar.type.signature==='Ljava/lang/Object;')
|
||||||
|
return $.Deferred().rejectWith(this,[new Error('No such field: '+x.fieldname), x.extra]);
|
||||||
|
// search supertype
|
||||||
|
return this.getsuperinstance(x.objvar, x)
|
||||||
|
.then((superobjvar,x) => {
|
||||||
|
x.objvar = superobjvar;
|
||||||
|
return x.findfield(x);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return findfield({findfield, objvar, fieldname, includeInherited, extra});
|
||||||
|
},
|
||||||
|
|
||||||
getExceptionLocal: function (ex_ref_value, extra) {
|
getExceptionLocal: function (ex_ref_value, extra) {
|
||||||
var x = {
|
var x = {
|
||||||
ex_ref_value: ex_ref_value,
|
ex_ref_value: ex_ref_value,
|
||||||
|
|||||||
Reference in New Issue
Block a user