mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 01:48:18 +00:00
added support for exception breakpoints
This commit is contained in:
@@ -139,6 +139,8 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
this._locals_done = null;
|
this._locals_done = null;
|
||||||
// the fifo queue of evaluations (watches, hover, etc)
|
// the fifo queue of evaluations (watches, hover, etc)
|
||||||
this._evals_queue = [];
|
this._evals_queue = [];
|
||||||
|
// the last (current) exception info
|
||||||
|
this._last_exception = null;
|
||||||
|
|
||||||
// since we want to send breakpoint events, we will assign an id to every event
|
// since we want to send breakpoint events, we will assign an id to every event
|
||||||
// so that the frontend can match events with breakpoints.
|
// so that the frontend can match events with breakpoints.
|
||||||
@@ -166,6 +168,12 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
// This debug adapter implements the configurationDoneRequest.
|
// This debug adapter implements the configurationDoneRequest.
|
||||||
response.body.supportsConfigurationDoneRequest = true;
|
response.body.supportsConfigurationDoneRequest = true;
|
||||||
|
|
||||||
|
// we support some exception options
|
||||||
|
response.body.exceptionBreakpointFilters = [
|
||||||
|
{ label:'All Exceptions', filter:'all', default:false },
|
||||||
|
{ label:'Uncaught Exceptions', filter:'uncaught', default:true },
|
||||||
|
];
|
||||||
|
|
||||||
this.sendResponse(response);
|
this.sendResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,6 +284,7 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
this.dbgr.on('bpstatechange', this, this.onBreakpointStateChange)
|
this.dbgr.on('bpstatechange', this, this.onBreakpointStateChange)
|
||||||
.on('bphit', this, this.onBreakpointHit)
|
.on('bphit', this, this.onBreakpointHit)
|
||||||
.on('step', this, this.onStep)
|
.on('step', this, this.onStep)
|
||||||
|
.on('exception', this, this.onException)
|
||||||
.on('disconnect', this, this.onDebuggerDisconnect);
|
.on('disconnect', this, this.onDebuggerDisconnect);
|
||||||
this.waitForConfigurationDone = $.Deferred();
|
this.waitForConfigurationDone = $.Deferred();
|
||||||
// - tell the client we're initialised and ready for breakpoint info, etc
|
// - tell the client we're initialised and ready for breakpoint info, etc
|
||||||
@@ -406,6 +415,7 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
package: pkgname,
|
package: pkgname,
|
||||||
package_path: fpn,
|
package_path: fpn,
|
||||||
srcroot: path.join(app_root,src_folder),
|
srcroot: path.join(app_root,src_folder),
|
||||||
|
public_classes: subfiles.filter(sf => /^[a-zA-Z_$][a-zA-Z0-9_$]*\.java$/.test(sf)).map(sf => sf.match(/^(.*)\.java$/)[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// add the subfiles to the list to process
|
// add the subfiles to the list to process
|
||||||
@@ -566,6 +576,20 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
this.sendResponse(response);
|
this.sendResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setExceptionBreakPointsRequest(response /*: SetExceptionBreakpointsResponse*/, args /*: SetExceptionBreakpointsArguments*/) {
|
||||||
|
this.dbgr.clearBreakOnExceptions({response,args})
|
||||||
|
.then(x => {
|
||||||
|
if (x.args.filters.includes('all')) {
|
||||||
|
x.set = this.dbgr.setBreakOnExceptions('both', x);
|
||||||
|
} else if (x.args.filters.includes('uncaught')) {
|
||||||
|
x.set = this.dbgr.setBreakOnExceptions('uncaught', x);
|
||||||
|
} else {
|
||||||
|
x.set = $.Deferred().resolveWith(this, [x]);
|
||||||
|
}
|
||||||
|
x.set.then(x => this.sendResponse(x.response));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
threadsRequest(response/*: DebugProtocol.ThreadsResponse*/) {
|
threadsRequest(response/*: DebugProtocol.ThreadsResponse*/) {
|
||||||
|
|
||||||
this.dbgr.allthreads(response)
|
this.dbgr.allthreads(response)
|
||||||
@@ -633,9 +657,13 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scopesRequest(response/*: DebugProtocol.ScopesResponse*/, args/*: DebugProtocol.ScopesArguments*/) {
|
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 = {
|
response.body = {
|
||||||
scopes: [new Scope("Local", args.frameId, false)]
|
scopes: scopes
|
||||||
};
|
};
|
||||||
this.sendResponse(response);
|
this.sendResponse(response);
|
||||||
}
|
}
|
||||||
@@ -826,6 +854,17 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
};
|
};
|
||||||
this.sendResponse(response);
|
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 {
|
else {
|
||||||
// frame locals request
|
// frame locals request
|
||||||
this.dbgr.getlocals(varinfo.frame.threadid, varinfo.frame, response)
|
this.dbgr.getlocals(varinfo.frame.threadid, varinfo.frame, response)
|
||||||
@@ -843,6 +882,7 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
continueRequest(response/*: DebugProtocol.ContinueResponse*/, args/*: DebugProtocol.ContinueArguments*/) {
|
continueRequest(response/*: DebugProtocol.ContinueResponse*/, args/*: DebugProtocol.ContinueArguments*/) {
|
||||||
D('Continue');
|
D('Continue');
|
||||||
this._variableHandles = {};
|
this._variableHandles = {};
|
||||||
|
this._last_exception = null;
|
||||||
// sometimes, the device is so quick that a breakpoint is hit
|
// sometimes, the device is so quick that a breakpoint is hit
|
||||||
// before we've completed the resume promise chain.
|
// before we've completed the resume promise chain.
|
||||||
// so tell the client that we've resumed now and just send a StoppedEvent
|
// so tell the client that we've resumed now and just send a StoppedEvent
|
||||||
@@ -879,13 +919,12 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
doStep(which, response, args) {
|
doStep(which, response, args) {
|
||||||
D('step '+which);
|
D('step '+which);
|
||||||
this._variableHandles = {};
|
this._variableHandles = {};
|
||||||
|
this._last_exception = null;
|
||||||
|
this._running = true;
|
||||||
|
this._locals_done = $.Deferred();
|
||||||
var threadid = ('000000000000000' + args.threadId.toString(16)).slice(-16);
|
var threadid = ('000000000000000' + args.threadId.toString(16)).slice(-16);
|
||||||
this.dbgr.step(which, threadid)
|
this.dbgr.step(which, threadid);
|
||||||
.then(() => {
|
this.sendResponse(response);
|
||||||
this._running = true;
|
|
||||||
this._locals_done = $.Deferred();
|
|
||||||
this.sendResponse(response);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stepInRequest(response/*: DebugProtocol.NextResponse*/, args/*: DebugProtocol.StepInArguments*/) {
|
stepInRequest(response/*: DebugProtocol.NextResponse*/, args/*: DebugProtocol.StepInArguments*/) {
|
||||||
@@ -900,6 +939,24 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
this.doStep('out', response, args);
|
this.doStep('out', response, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the debugger if an exception event is triggered
|
||||||
|
*/
|
||||||
|
onException(e) {
|
||||||
|
D('exception hit: ' + JSON.stringify(e.throwlocation));
|
||||||
|
// it's possible for the debugger to send multiple exception notifications, depending on the package filters
|
||||||
|
// , so just ignore them if we've already stopped
|
||||||
|
if (!this._running) return;
|
||||||
|
this._running = false;
|
||||||
|
this._last_exception = {
|
||||||
|
exception: e.event.exception,
|
||||||
|
threadid: e.throwlocation.threadid,
|
||||||
|
varref: ++this._nextObjVarRef,
|
||||||
|
};
|
||||||
|
this._variableHandles[this._last_exception.varref] = this._last_exception;
|
||||||
|
this.sendEvent(new StoppedEvent("exception", parseInt(e.throwlocation.threadid,16)));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by VSCode to perform watch, console and hover evaluations
|
* Called by VSCode to perform watch, console and hover evaluations
|
||||||
*/
|
*/
|
||||||
|
|||||||
180
src/debugger.js
180
src/debugger.js
@@ -11,6 +11,7 @@ function Debugger() {
|
|||||||
this.connection = null;
|
this.connection = null;
|
||||||
this.ons = {};
|
this.ons = {};
|
||||||
this.breakpoints = { all: [], enabled: {}, bysrcloc: {} };
|
this.breakpoints = { all: [], enabled: {}, bysrcloc: {} };
|
||||||
|
this.exception_ids = [];
|
||||||
this.JDWP = new _JDWP();
|
this.JDWP = new _JDWP();
|
||||||
this.session = null;
|
this.session = null;
|
||||||
this.globals = Debugger.globals;
|
this.globals = Debugger.globals;
|
||||||
@@ -810,6 +811,100 @@ Debugger.prototype = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getExceptionLocal: function (ex_ref_value, extra) {
|
||||||
|
var x = {
|
||||||
|
ex_ref_value: ex_ref_value,
|
||||||
|
extra: extra
|
||||||
|
};
|
||||||
|
return this.session.adbclient.jdwp_command({
|
||||||
|
ths: this,
|
||||||
|
extra: x,
|
||||||
|
cmd: this.JDWP.Commands.GetObjectType(ex_ref_value),
|
||||||
|
})
|
||||||
|
.then((typeref, x) => this.session.adbclient.jdwp_command({
|
||||||
|
ths: this,
|
||||||
|
extra: x,
|
||||||
|
cmd: this.JDWP.Commands.signature(typeref)
|
||||||
|
}))
|
||||||
|
.then((type, x) => {
|
||||||
|
x.type = type;
|
||||||
|
return this.gettypedebuginfo(type.signature, x)
|
||||||
|
})
|
||||||
|
.then((dbgtype, x) => {
|
||||||
|
return this._ensurefields(dbgtype[x.type.signature], x)
|
||||||
|
})
|
||||||
|
.then((typeinfo, x) => {
|
||||||
|
return this._mapvalues('exception', [{ name: '{ex}', type: x.type }], [x.ex_ref_value], {}, x);
|
||||||
|
})
|
||||||
|
.then((res, x) => {
|
||||||
|
return $.Deferred().resolveWith(this, [res[0], x.extra])
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
invokeMethod: function (objectid, threadid, type_signature, method_name, method_sig, args, extra) {
|
||||||
|
var x = { objectid, threadid, type_signature, method_name, method_sig, args, extra };
|
||||||
|
x.return_type_signature = method_sig.match(/\)(.*)/)[1];
|
||||||
|
return this.gettypedebuginfo(x.return_type_signature)
|
||||||
|
.then(dbgtypes => {
|
||||||
|
x.return_type = dbgtypes[x.return_type_signature].type;
|
||||||
|
return this.gettypedebuginfo(x.type_signature);
|
||||||
|
})
|
||||||
|
.then(dbgtype => this._ensuremethods(dbgtype[x.type_signature]))
|
||||||
|
.then(typeinfo => {
|
||||||
|
// resolving the methods only resolves the non-inherited methods
|
||||||
|
// if we can't find a matching method, we need to search the super types
|
||||||
|
var o = {
|
||||||
|
dbgr:this,
|
||||||
|
def:$.Deferred(),
|
||||||
|
x: x,
|
||||||
|
find_method(typeinfo) {
|
||||||
|
for (var mid in typeinfo.methods) {
|
||||||
|
var m = typeinfo.methods[mid];
|
||||||
|
if ((m.name === this.x.method_name) && ((m.genericsig||m.sig) === this.x.method_sig)) {
|
||||||
|
this.def.resolveWith(this, [typeinfo, m, this.x]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// search the supertype
|
||||||
|
if (typeinfo.type.signature==='Ljava/lang/Object;') {
|
||||||
|
this.def.rejectWith(this, [new Error('No such method: ' + this.x.method_name + ' ' + this.x.method_sig)]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dbgr._ensuresuper(typeinfo)
|
||||||
|
.then(typeinfo => {
|
||||||
|
return this.dbgr.gettypedebuginfo(typeinfo.super.signature, typeinfo.super.signature)
|
||||||
|
})
|
||||||
|
.then((dbgtype, sig) => {
|
||||||
|
return this.dbgr._ensuremethods(dbgtype[sig])
|
||||||
|
})
|
||||||
|
.then(typeinfo => {
|
||||||
|
this.find_method(typeinfo)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
o.find_method(typeinfo);
|
||||||
|
return o.def;
|
||||||
|
})
|
||||||
|
.then((typeinfo, method, x) => {
|
||||||
|
return this.session.adbclient.jdwp_command({
|
||||||
|
ths: this,
|
||||||
|
extra: x,
|
||||||
|
cmd: this.JDWP.Commands.InvokeMethod(x.objectid, x.threadid, typeinfo.info.typeid, method.methodid, x.args),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((res, x) => {
|
||||||
|
if (/^0+$/.test(res.exception))
|
||||||
|
return this._mapvalues('return', [{ name:'{return}', type:x.return_type }], [res.return_value], {}, x);
|
||||||
|
// todo - handle reutrn exceptions
|
||||||
|
})
|
||||||
|
.then((res, x) => $.Deferred().resolveWith(this, [res[0], x.extra])); // res = {return_value, exception}
|
||||||
|
},
|
||||||
|
|
||||||
|
invokeToString(objectid, threadid, type_signature, extra) {
|
||||||
|
return this.invokeMethod(objectid, threadid, type_signature || 'Ljava/lang/Object;', 'toString', '()Ljava/lang/String;', [], extra);
|
||||||
|
},
|
||||||
|
|
||||||
getstringchars: function (stringref, extra) {
|
getstringchars: function (stringref, extra) {
|
||||||
return this.session.adbclient.jdwp_command({
|
return this.session.adbclient.jdwp_command({
|
||||||
ths: this,
|
ths: this,
|
||||||
@@ -1343,6 +1438,89 @@ Debugger.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
clearBreakOnExceptions: function(extra) {
|
||||||
|
var o = {
|
||||||
|
dbgr: this,
|
||||||
|
def: $.Deferred(),
|
||||||
|
extra: extra,
|
||||||
|
next() {
|
||||||
|
if (!this.dbgr.exception_ids.length) {
|
||||||
|
return this.def.resolveWith(this.dbgr, [this.extra]); // done
|
||||||
|
}
|
||||||
|
// clear next pattern
|
||||||
|
this.dbgr.session.adbclient.jdwp_command({
|
||||||
|
cmd: this.dbgr.JDWP.Commands.ClearExceptionBreak(this.dbgr.exception_ids.pop())
|
||||||
|
})
|
||||||
|
.then(() => this.next())
|
||||||
|
.fail(e => this.def.rejectWith(this, [e]))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
o.next();
|
||||||
|
return o.def;
|
||||||
|
},
|
||||||
|
|
||||||
|
setBreakOnExceptions: function(which, extra) {
|
||||||
|
var onevent = {
|
||||||
|
data: {
|
||||||
|
dbgr: this,
|
||||||
|
},
|
||||||
|
fn: function (e) {
|
||||||
|
this._findcmllocation(this.session.classes, e.event.throwlocation)
|
||||||
|
.then(tloc => {
|
||||||
|
this._findcmllocation(this.session.classes, e.event.catchlocation)
|
||||||
|
.then(cloc => {
|
||||||
|
var eventdata = {
|
||||||
|
event: e.event,
|
||||||
|
throwlocation: Object.assign({ threadid: e.event.threadid }, tloc),
|
||||||
|
catchlocation: Object.assign({ threadid: e.event.threadid }, cloc),
|
||||||
|
};
|
||||||
|
this.session.stoppedlocation = Object.assign({}, eventdata.throwlocation);
|
||||||
|
this._trigger('exception', eventdata);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}.bind(this)
|
||||||
|
};
|
||||||
|
|
||||||
|
var c = false, u = false;
|
||||||
|
switch (which) {
|
||||||
|
case 'caught': c = true; break;
|
||||||
|
case 'uncaught': u = true; break;
|
||||||
|
case 'both': c = u = true; break;
|
||||||
|
default: throw new Error('Invalid exception option');
|
||||||
|
}
|
||||||
|
// when setting up the exceptions, we filter by packages containing public classes in the current session
|
||||||
|
// - each filter needs a separate call (I think), so we do this as an asynchronous list
|
||||||
|
var pkgs = this.session.build.packages;
|
||||||
|
var pkgs_to_monitor = Object.keys(pkgs).filter(pkgname => pkgs[pkgname].public_classes.length);
|
||||||
|
var o = {
|
||||||
|
dbgr: this,
|
||||||
|
pkgs: pkgs_to_monitor,
|
||||||
|
caught: c,
|
||||||
|
uncaught: u,
|
||||||
|
onevent: onevent,
|
||||||
|
cmds:[],
|
||||||
|
def: $.Deferred(),
|
||||||
|
extra: extra,
|
||||||
|
next() {
|
||||||
|
if (!this.pkgs.length) {
|
||||||
|
this.def.resolveWith(this.dbgr, [this.extra]); // done
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// setup next pattern
|
||||||
|
this.dbgr.session.adbclient.jdwp_command({
|
||||||
|
cmd: this.dbgr.JDWP.Commands.SetExceptionBreak(this.pkgs.shift() + '.*', this.caught, this.uncaught, this.onevent),
|
||||||
|
})
|
||||||
|
.then(x => {
|
||||||
|
this.dbgr.exception_ids.push(x.id);
|
||||||
|
this.next();
|
||||||
|
})
|
||||||
|
.fail(e => this.def.rejectWith(this, [e]))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
o.next();
|
||||||
|
return o.def;
|
||||||
|
},
|
||||||
|
|
||||||
_loadclzinfo: function (signature) {
|
_loadclzinfo: function (signature) {
|
||||||
return this.gettypedebuginfo(signature)
|
return this.gettypedebuginfo(signature)
|
||||||
.then(function (classes) {
|
.then(function (classes) {
|
||||||
@@ -1429,6 +1607,8 @@ Debugger.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_findmethodasync: function (classes, location) {
|
_findmethodasync: function (classes, location) {
|
||||||
|
// some locations are null (which causes the jdwp command to fail)
|
||||||
|
if (/^0+$/.test(location.cid)) return $.Deferred().resolveWith(this, [null]);
|
||||||
var m = this._findmethod(classes, location.cid, location.mid);
|
var m = this._findmethod(classes, location.cid, location.mid);
|
||||||
if (m) return $.Deferred().resolveWith(this, [m]);
|
if (m) return $.Deferred().resolveWith(this, [m]);
|
||||||
// convert the classid to a type signature
|
// convert the classid to a type signature
|
||||||
|
|||||||
90
src/jdwp.js
90
src/jdwp.js
@@ -97,7 +97,7 @@ function _JDWP() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.errorcode != 0) {
|
if (this.errorcode != 0) {
|
||||||
console.error("Command failed: error " + this.errorcode);
|
console.error("Command failed: error " + this.errorcode, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.errorcode && this.command && this.command.replydecodefn) {
|
if (!this.errorcode && this.command && this.command.replydecodefn) {
|
||||||
@@ -131,6 +131,15 @@ function _JDWP() {
|
|||||||
var DataCoder = {
|
var DataCoder = {
|
||||||
_idsizes:null,
|
_idsizes:null,
|
||||||
|
|
||||||
|
nullRefValue: function() {
|
||||||
|
if (!this._idsizes._nullreftypeid) {
|
||||||
|
var x = '00', len = this._idsizes.reftypeidsize * 2; // each byte needs 2 chars
|
||||||
|
while (x.length < len) x += x;
|
||||||
|
this._idsizes._nullreftypeid = x.slice(0, len); // should be power of 2, but just in case...
|
||||||
|
}
|
||||||
|
return this._idsizes._nullreftypeid;
|
||||||
|
},
|
||||||
|
|
||||||
decodeString: function(o) {
|
decodeString: function(o) {
|
||||||
var rd = o.data;
|
var rd = o.data;
|
||||||
var utf8len=(rd[o.idx++]<<24)+(rd[o.idx++]<<16)+(rd[o.idx++]<<8)+(rd[o.idx++]);
|
var utf8len=(rd[o.idx++]<<24)+(rd[o.idx++]<<16)+(rd[o.idx++]<<8)+(rd[o.idx++]);
|
||||||
@@ -144,7 +153,7 @@ function _JDWP() {
|
|||||||
var rd = o.data;
|
var rd = o.data;
|
||||||
var res1=(rd[o.idx++]<<24)+(rd[o.idx++]<<16)+(rd[o.idx++]<<8)+(rd[o.idx++]);
|
var res1=(rd[o.idx++]<<24)+(rd[o.idx++]<<16)+(rd[o.idx++]<<8)+(rd[o.idx++]);
|
||||||
var res2=(rd[o.idx++]<<24)+(rd[o.idx++]<<16)+(rd[o.idx++]<<8)+(rd[o.idx++]);
|
var res2=(rd[o.idx++]<<24)+(rd[o.idx++]<<16)+(rd[o.idx++]<<8)+(rd[o.idx++]);
|
||||||
return intToHex(res1,8)+intToHex(res2,8);
|
return intToHex(res1>>>0,8)+intToHex(res2>>>0,8); // >>> 0 ensures +ve value
|
||||||
},
|
},
|
||||||
decodeInt: function(o) {
|
decodeInt: function(o) {
|
||||||
var rd = o.data;
|
var rd = o.data;
|
||||||
@@ -241,6 +250,9 @@ function _JDWP() {
|
|||||||
decodeStatus : function(o) {
|
decodeStatus : function(o) {
|
||||||
return this.mapflags(this.decodeInt(o), ['verified','prepared','initialized','error']);
|
return this.mapflags(this.decodeInt(o), ['verified','prepared','initialized','error']);
|
||||||
},
|
},
|
||||||
|
decodeTaggedObjectID : function(o) {
|
||||||
|
return this.decodeValue(o);
|
||||||
|
},
|
||||||
decodeValue : function(o) {
|
decodeValue : function(o) {
|
||||||
var rd = o.data;
|
var rd = o.data;
|
||||||
return this.tagtodecoder(rd[o.idx++]).call(this, o);
|
return this.tagtodecoder(rd[o.idx++]).call(this, o);
|
||||||
@@ -346,6 +358,13 @@ function _JDWP() {
|
|||||||
event.threadid = this.decodeORef(o);
|
event.threadid = this.decodeORef(o);
|
||||||
event.location = this.decodeLocation(o);
|
event.location = this.decodeLocation(o);
|
||||||
break;
|
break;
|
||||||
|
case 4: // exception
|
||||||
|
event.reqid = this.decodeInt(o);
|
||||||
|
event.threadid = this.decodeORef(o);
|
||||||
|
event.throwlocation = this.decodeLocation(o);
|
||||||
|
event.exception = this.decodeTaggedObjectID(o);
|
||||||
|
event.catchlocation = this.decodeLocation(o); // 0 = uncaught
|
||||||
|
break;
|
||||||
case 8: // classprepare
|
case 8: // classprepare
|
||||||
event.reqid = this.decodeInt(o);
|
event.reqid = this.decodeInt(o);
|
||||||
event.threadid = this.decodeORef(o);
|
event.threadid = this.decodeORef(o);
|
||||||
@@ -804,6 +823,19 @@ function _JDWP() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
GetObjectType:function(objectid) {
|
||||||
|
return new Command('GetObjectType:'+objectid, 9, 1,
|
||||||
|
function() {
|
||||||
|
var res=[];
|
||||||
|
DataCoder.encodeRef(res, objectid);
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
function(o) {
|
||||||
|
DataCoder.decodeRefType(o);
|
||||||
|
return DataCoder.decodeTRef(o);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
GetFieldValues:function(objectid, fields) {
|
GetFieldValues:function(objectid, fields) {
|
||||||
return new Command('GetFieldValues:'+objectid, 9, 2,
|
return new Command('GetFieldValues:'+objectid, 9, 2,
|
||||||
function() {
|
function() {
|
||||||
@@ -842,6 +874,27 @@ function _JDWP() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
InvokeMethod:function(objectid, threadid, classid, methodid, args) {
|
||||||
|
return new Command('InvokeMethod:'+[objectid, threadid, classid, methodid, args].join(','), 9, 6,
|
||||||
|
function() {
|
||||||
|
var res=[];
|
||||||
|
DataCoder.encodeRef(res, objectid);
|
||||||
|
DataCoder.encodeRef(res, threadid);
|
||||||
|
DataCoder.encodeRef(res, classid);
|
||||||
|
DataCoder.encodeRef(res, methodid);
|
||||||
|
DataCoder.encodeInt(res, args.length);
|
||||||
|
args.forEach(arg => DataCoder.encodeValue(res, arg.type, arg.value));
|
||||||
|
DataCoder.encodeInt(res, 1); // INVOKE_SINGLE_THREADED
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
function(o) {
|
||||||
|
return {
|
||||||
|
return_value: DataCoder.decodeValue(o),
|
||||||
|
exception: DataCoder.decodeTaggedObjectID(o),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
GetArrayLength:function(arrobjid) {
|
GetArrayLength:function(arrobjid) {
|
||||||
return new Command('GetArrayLength:'+arrobjid, 13, 1,
|
return new Command('GetArrayLength:'+arrobjid, 13, 1,
|
||||||
function() {
|
function() {
|
||||||
@@ -1015,6 +1068,39 @@ function _JDWP() {
|
|||||||
onevent
|
onevent
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
ClearExceptionBreak:function(requestid) {
|
||||||
|
// kind(4=exception)
|
||||||
|
return this.ClearEvent("exception",4,requestid);
|
||||||
|
},
|
||||||
|
SetExceptionBreak:function(pattern, caught, uncaught, onevent) {
|
||||||
|
// a wrapper around SetEventRequest
|
||||||
|
var mods = [{
|
||||||
|
modkind:8, // exceptiononly
|
||||||
|
reftypeid: DataCoder.nullRefValue(), // exception class
|
||||||
|
caught: caught,
|
||||||
|
uncaught: uncaught,
|
||||||
|
}];
|
||||||
|
pattern && mods.unshift({
|
||||||
|
modkind:5, // classmatch
|
||||||
|
pattern: pattern,
|
||||||
|
});
|
||||||
|
// kind(4=exception)
|
||||||
|
// suspendpolicy(0=none,1=event-thread,2=all)
|
||||||
|
return this.SetEventRequest("exception",4,2,mods,
|
||||||
|
function(m, i, res) {
|
||||||
|
res.push(m.modkind);
|
||||||
|
switch(m.modkind) {
|
||||||
|
case 5: DataCoder.encodeString(res, m.pattern); break;
|
||||||
|
case 8:
|
||||||
|
DataCoder.encodeRef(res, m.reftypeid);
|
||||||
|
DataCoder.encodeBoolean(res, m.caught);
|
||||||
|
DataCoder.encodeBoolean(res, m.uncaught);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onevent
|
||||||
|
);
|
||||||
|
},
|
||||||
allclasses:function() {
|
allclasses:function() {
|
||||||
// not supported by android
|
// not supported by android
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user