identify breakpoint source locations by package and filename instead of type name

This commit is contained in:
Dave Holoway
2021-05-30 19:31:33 +01:00
parent 33dd93da0c
commit b5701dae7d
3 changed files with 15 additions and 4 deletions

View File

@@ -610,12 +610,16 @@ class DebuggerBreakpoint {
const cls = splitSourcePath(srcfpn); const cls = splitSourcePath(srcfpn);
this.id = DebuggerBreakpoint.makeBreakpointID(srcfpn, linenum); this.id = DebuggerBreakpoint.makeBreakpointID(srcfpn, linenum);
this.srcfpn = srcfpn; this.srcfpn = srcfpn;
this.file = cls.file;
this.qtype = cls.qtype; this.qtype = cls.qtype;
this.pkg = cls.pkg; this.pkg = cls.pkg;
this.type = cls.type; this.type = cls.type;
this.linenum = linenum; this.linenum = linenum;
this.options = options; this.options = options;
this.sigpattern = new RegExp(`^L${cls.qtype}([$][$a-zA-Z0-9_]+)?;$`), // sigpattern is used to match up source files with class signatures but because
// kotlin allows filenames that differ from class names, we now only match up to
// the package level and use the source name retuned by JDWP to narrow it down futher.
this.sigpattern = new RegExp(`^L${cls.pkg.replace(/[.]/g, '/')}/[^/]+;$`)
this.state = initialState; // set,notloaded,enabled,removed this.state = initialState; // set,notloaded,enabled,removed
this.hitcount = 0; // number of times this bp was hit during execution this.hitcount = 0; // number of times this bp was hit during execution
this.stopcount = 0; // number of times this bp caused a break into the debugger this.stopcount = 0; // number of times this bp caused a break into the debugger

View File

@@ -567,13 +567,15 @@ class Debugger extends EventEmitter {
* @param {DebuggerBreakpoint} bp * @param {DebuggerBreakpoint} bp
*/ */
async initialiseBreakpoint(bp) { async initialiseBreakpoint(bp) {
// try and load the class - if the runtime hasn't loaded it yet, this will just return a TypeNotAvailable instance // try and load the classes in the package - if the runtime hasn't loaded it yet, this will just return a TypeNotAvailable instance
let classes = await Promise.all( let classes = await Promise.all(
[...this.session.loadedClasses] [...this.session.loadedClasses]
.filter(signature => bp.sigpattern.test(signature)) .filter(signature => bp.sigpattern.test(signature))
.map(signature => this.loadClassInfo(signature)) .map(signature => this.loadClassInfo(signature))
); );
let bploc = Debugger.findBreakpointLocation(classes, bp); // find classes with a matching sourcefile name
classes = classes.filter(c => c.src.sourcefile === bp.file);
const bploc = Debugger.findBreakpointLocation(classes, bp);
if (!bploc) { if (!bploc) {
// we couldn't identify a matching location - either the class is not yet loaded or the // we couldn't identify a matching location - either the class is not yet loaded or the
// location doesn't correspond to any code. In case it's the former, make sure we are notified // location doesn't correspond to any code. In case it's the former, make sure we are notified
@@ -1661,6 +1663,7 @@ class Debugger extends EventEmitter {
let bploc = null; let bploc = null;
classes.find(c => classes.find(c =>
bp.sigpattern.test(c.type.signature) bp.sigpattern.test(c.type.signature)
&& bp.file === c.src.sourcefile
&& c.methods.find(m => { && c.methods.find(m => {
const line = m.linetable.lines.find(line => line.linenum === bp.linenum); const line = m.linetable.lines.find(line => line.linenum === bp.linenum);
if (line) { if (line) {

View File

@@ -6,12 +6,16 @@ function hasValidSourceFileExtension(s) {
return /\.(java|kt)$/i.test(s); return /\.(java|kt)$/i.test(s);
} }
/**
* @param {string} filepath
*/
function splitSourcePath(filepath) { function splitSourcePath(filepath) {
const m = filepath.match(/^\/([^/]+(?:\/[^/]+)*)?\/([^./]+)\.(java|kt)$/); const m = filepath.match(/^\/([^/]+(?:\/[^/]+)*)?\/([^./]+)\.(java|kt)$/i);
return { return {
pkg: m[1].replace(/\/+/g, '.'), pkg: m[1].replace(/\/+/g, '.'),
type: m[2], type: m[2],
qtype: `${m[1]}/${m[2]}`, qtype: `${m[1]}/${m[2]}`,
file: `${m[2]}.${m[3]}`,
} }
} }