mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 05:13:58 -07:00
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
223 lines
6.1 KiB
JavaScript
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;
|