0ad/binaries/data/mods/mod/tools/dap/managers/frame.js
Dunedan 93ce94655d
Use @stylistic/brace-style for eslint
Up to now `eslint-plugin-brace-rules` was used to enforce a common brace
style for JavaScript code. This plugin was however updated the last time
over 9 years ago and will be incompatible with ESLint v10, as that
[removes `context.getSourceCode()`][1], the plugin relies on.

To keep the eslint config working with ESLint v10, this replaces
`eslint-plugin-brace-rules` with the [`@stylistic/brace-style`][2] rule
from `@stylistic/eslint-plugin`, a package we already use.

While `@stylistic/brace-style` doesn't offer an option to format braces
in exactly the same way as before, the "allman" style seems to be the
one closest to the existing code.

[1]: https://eslint.org/blog/2025/11/eslint-v10.0.0-alpha.0-released/#removed-deprecated-rule-context-members
[2]: https://eslint.style/rules/brace-style
2026-01-12 21:33:52 +01:00

223 lines
6.1 KiB
JavaScript

import { Plugin } from 'tools/dap/plugin.js';
class FrameManager extends Plugin
{
constructor(jsDebugger, dapHandler)
{
super('FrameManager', 'manager');
this.logger.debug('Setting up FrameManager');
this.jsDebugger = jsDebugger;
jsDebugger.registerHookName('onContinue', this.name);
jsDebugger.registerHookName('onNext', this.name);
jsDebugger.registerHookName('onStepIn', this.name);
jsDebugger.registerHookName('onStepOut', this.name);
jsDebugger.on('onDebuggerDetached', () =>
{
this.logger.debug('Debugger detached');
let frame = jsDebugger.currentFrame;
while (frame)
{
this.cleanFrameDebugger(frame);
frame = frame.onStack && !frame.terminated ? frame.older : null;
}
this.breakpoints = [];
jsDebugger.currentFrame = undefined;
}, this.name);
jsDebugger.on('onDebuggerStatement', (frame) =>
{
jsDebugger.stopInframe(frame, () =>
{
this.logger.debug(`Paused on debugger statement in frame: ${frame.script.url}`);
jsDebugger.pushEvent('stopped', {
'reason': 'debugger',
'threadId': 1,
'text': "Paused on debugger statement"
}, this.name);
});
}, this.name);
jsDebugger.on('onEnterFrame', (frame) =>
{
if (!frame || !frame.older || !frame.older.stepIn || frame.onStep !== undefined)
return;
this.logger.debug(`Stepping in frame: ${frame.script.url}`);
this.hookFrameDebugger(frame, "stepIn", "Paused on stepIn");
}, this.name);
dapHandler.registerCommand('stackTrace', (req) =>
{
this.logger.debug('Handling stackTrace command');
if (!jsDebugger.currentFrame)
{
this.logger.error('No current frame available');
return dapHandler.errorResponse(req, 'No current frame available');
}
const stackFrames = [];
let frame = jsDebugger.currentFrame;
while (frame)
{
const location = frame.script.getOffsetMetadata(frame.offset);
const sourceReferenceIndex = jsDebugger.sourcesReferences.findIndex((src) => src.path === frame.script.url);
stackFrames.push({
'id': stackFrames.length + 1,
'name': frame.script.displayName || this.extractScriptName(frame.script.url),
'source': { 'path': frame.script.url, 'sourceReference': sourceReferenceIndex + 1 },
'line': location.lineNumber + (1 - frame.script.source.startLine),
'column': location.columnNumber
});
frame = frame.onStack && !frame.terminated ? frame.older : null;
}
return dapHandler.successResponse(req, { 'stackFrames': stackFrames });
});
dapHandler.registerCommand('continue', (req) =>
{
this.logger.debug('Handling continue command');
let frame = jsDebugger.currentFrame;
while (frame)
{
this.cleanFrameDebugger(frame);
frame = frame.older;
}
jsDebugger.triggerHook('onContinue', jsDebugger.currentFrame);
Engine.EndWaitingForMessage();
return dapHandler.successResponse(req, { 'allThreadsContinued': true });
});
dapHandler.registerCommand('next', (req) =>
{
this.logger.debug('Handling next command');
if (!jsDebugger.currentFrame)
{
this.logger.error('No current frame available');
return dapHandler.errorResponse(req, 'No current frame available');
}
this.hookFrameDebugger(jsDebugger.currentFrame, "next", "Paused on next");
jsDebugger.currentFrame.stepIn = false;
jsDebugger.currentFrame.stepOut = true;
jsDebugger.currentFrame.stepOver = true;
jsDebugger.triggerHook('onNext', jsDebugger.currentFrame);
Engine.EndWaitingForMessage();
return dapHandler.successResponse(req, undefined);
});
dapHandler.registerCommand('stepIn', (req) =>
{
this.logger.debug('Handling stepIn command');
if (!jsDebugger.currentFrame)
{
this.logger.error('No current frame available');
return dapHandler.errorResponse(req, 'No current frame available');
}
this.hookFrameDebugger(jsDebugger.currentFrame, "stepIn", "Paused on stepIn");
jsDebugger.currentFrame.stepIn = true;
jsDebugger.currentFrame.stepOut = false;
jsDebugger.currentFrame.stepOver = false;
jsDebugger.triggerHook('onStepIn', jsDebugger.currentFrame);
Engine.EndWaitingForMessage();
return dapHandler.successResponse(req, undefined);
});
dapHandler.registerCommand('stepOut', (req) =>
{
this.logger.debug('Handling stepOut command');
if (!jsDebugger.currentFrame)
{
this.logger.error('No current frame available');
return dapHandler.errorResponse(req, 'No current frame available');
}
jsDebugger.currentFrame.stepIn = false;
jsDebugger.currentFrame.stepOut = true;
jsDebugger.currentFrame.stepOver = false;
this.hookFrameDebugger(jsDebugger.currentFrame, "stepOut", "Paused on stepOut");
jsDebugger.triggerHook('onStepOut', jsDebugger.currentFrame);
Engine.EndWaitingForMessage();
return dapHandler.successResponse(req, undefined);
});
}
extractScriptName(url)
{
if (!url)
return "[No Name]";
const parts = url.split('\\');
return parts[parts.length - 1] || "[No Name]";
}
hookFrameDebugger(frame, reason, msg)
{
if (!frame)
return;
const that = this;
if (frame.onStep === undefined)
{
frame.onStep = function()
{
if (this.stepOut === true && this.stepOver !== true)
return;
const currentLocation = this.script.getOffsetLocation(frame.offset);
if (this.currentLocation?.lineNumber === currentLocation.lineNumber)
return;
that.jsDebugger.stopInframe(this, () =>
{
that.jsDebugger.pushEvent('stopped', {
'reason': reason,
'threadId': 1,
'text': msg
}, that.name);
});
that.logger.debug("Client continue from step");
};
}
if (frame.onPop === undefined)
{
frame.onPop = function()
{
if (this.stepIn || this.stepOut)
that.hookFrameDebugger(this.older, reason, msg);
that.cleanFrameDebugger(this);
};
}
}
cleanFrameDebugger(frame)
{
if (!frame)
return;
if (frame.onStep)
frame.onStep = undefined;
if (frame.onPop)
frame.onPop = undefined;
frame.stepIn = undefined;
frame.stepOut = undefined;
frame.stepOver = undefined;
if (!frame.onStack && frame.terminated)
return;
this.logger.debug(`Cleaned frame: ${frame.script.url}`);
}
}
export default FrameManager;