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
203 lines
4.2 KiB
JavaScript
203 lines
4.2 KiB
JavaScript
import { logger } from 'tools/dap/logger.js';
|
|
|
|
export class JsDebugger
|
|
{
|
|
constructor()
|
|
{
|
|
this.debugger = new Debugger();
|
|
this.logger = logger.getLogger("SpiderDebugger");
|
|
this.events = [];
|
|
this.sourcesReferences = [];
|
|
this.currentFrame = null;
|
|
this.hooks = {
|
|
'onDebuggerAttached': [],
|
|
'onDebuggerDetached': [],
|
|
'onNewGlobalObject': [],
|
|
'onDebuggerStatement': [],
|
|
'onNewScript': [],
|
|
'onEnterFrame': [],
|
|
'onUncaughtException': [],
|
|
'onStopInFrame': [],
|
|
'onRsumeInFrame': [],
|
|
};
|
|
|
|
this.debugger.uncaughtExceptionHook = (e) =>
|
|
{
|
|
this._runHooks('onUncaughtException', e);
|
|
};
|
|
|
|
this.debugger.onNewGlobalObject = (global) =>
|
|
{
|
|
this._runHooks('onNewGlobalObject', global);
|
|
};
|
|
|
|
this.debugger.onDebuggerStatement = (frame) =>
|
|
{
|
|
this._runHooks('onDebuggerStatement', frame);
|
|
};
|
|
|
|
this.debugger.onNewScript = (script, global) =>
|
|
{
|
|
this._runHooks('onNewScript', { script, global });
|
|
};
|
|
|
|
this.debugger.onEnterFrame = (frame) =>
|
|
{
|
|
this._runHooks('onEnterFrame', frame);
|
|
};
|
|
|
|
this.debuggerAttached = false;
|
|
}
|
|
|
|
_runHooks(event, data)
|
|
{
|
|
this.logger.trace(`Running hook for ${event}`);
|
|
for (const hookInfo of this.hooks[event])
|
|
{
|
|
this.logger.trace(`Running hook for ${hookInfo.source}-${event}`);
|
|
if (typeof hookInfo.callback !== 'function')
|
|
{
|
|
this.logger.warn(`Hook for ${event} is not a function: ${hook.source}`);
|
|
continue;
|
|
}
|
|
|
|
try
|
|
{
|
|
hookInfo.callback(data);
|
|
}
|
|
catch(e)
|
|
{
|
|
this.logger.error(`Error in hook for ${hookInfo.source}-${event}: ${e.message}`);
|
|
this.logger.error(uneval(e.stack));
|
|
}
|
|
}
|
|
}
|
|
|
|
on(event, callback, source)
|
|
{
|
|
if (!event || typeof event !== 'string')
|
|
{
|
|
this.logger.warn('Invalid event name');
|
|
return;
|
|
}
|
|
|
|
if (!this.hooks[event])
|
|
{
|
|
this.logger.warn(`No hooks registered for event: ${event}`);
|
|
return;
|
|
}
|
|
|
|
if (!source || typeof source !== 'string')
|
|
{
|
|
this.logger.warn(`Invalid source name for event ${event}`);
|
|
return;
|
|
}
|
|
if (typeof callback !== 'function')
|
|
{
|
|
this.logger.warn(`Callback for event ${source}:${event} is not a function`);
|
|
return;
|
|
}
|
|
|
|
this.hooks[event].push({ callback, source });
|
|
this.logger.debug(`Hook added for event: ${event}`);
|
|
}
|
|
|
|
get instance()
|
|
{
|
|
return this.debugger;
|
|
}
|
|
|
|
setAttached(attached)
|
|
{
|
|
this.debuggerAttached = attached;
|
|
if (attached)
|
|
{
|
|
this.logger.debug("Debugger attached");
|
|
this._runHooks('onDebuggerAttached', {});
|
|
}
|
|
else
|
|
{
|
|
this.logger.debug("Debugger detached");
|
|
this._runHooks('onDebuggerDetached', {});
|
|
}
|
|
}
|
|
|
|
pushEvent(eventName, eventData, source)
|
|
{
|
|
if (!eventName || typeof eventName !== 'string')
|
|
{
|
|
this.logger.warn('Invalid event name');
|
|
return;
|
|
}
|
|
if (source && typeof source !== 'string')
|
|
{
|
|
this.logger.warn('Invalid source name');
|
|
return;
|
|
}
|
|
|
|
this.logger.debug(`Pushing event: ${source}-${eventName}`);
|
|
this.events.push({
|
|
'type': 'event',
|
|
'event': eventName,
|
|
'body': eventData
|
|
});
|
|
}
|
|
|
|
stopInframe(frame, onHandler)
|
|
{
|
|
if (!frame || !(frame instanceof Debugger.Frame))
|
|
{
|
|
this.logger.error('Invalid frame provided to stopInframe');
|
|
return;
|
|
}
|
|
|
|
this.currentFrame = frame;
|
|
frame.currentLocation = frame.script.getOffsetLocation(frame.offset);
|
|
this.logger.debug(`Stop at ${frame.script.url}:${frame.currentLocation.lineNumber}:${frame.currentLocation.columnNumber}`);
|
|
this.logger.debug(`Frame type: ${frame.type}`);
|
|
|
|
if (onHandler && typeof onHandler === 'function')
|
|
onHandler();
|
|
|
|
this._runHooks('onStopInFrame', frame);
|
|
Engine.WaitForMessage();
|
|
this._runHooks('onRsumeInFrame', frame);
|
|
|
|
this.logger.debug("Client continue");
|
|
}
|
|
|
|
registerHookName(event, source)
|
|
{
|
|
if (!event || typeof event !== 'string')
|
|
{
|
|
this.logger.warn('Invalid event name');
|
|
return;
|
|
}
|
|
|
|
if (!source || typeof source !== 'string')
|
|
{
|
|
this.logger.warn(`Invalid source name for ${event}`);
|
|
return;
|
|
}
|
|
|
|
if (this.hooks[event])
|
|
{
|
|
this.logger.warn(`Hooks already registered for event: ${event}`);
|
|
return;
|
|
}
|
|
|
|
this.hooks[event] = [];
|
|
this.logger.debug(`Hook registered for event: ${event} from source: ${source}`);
|
|
}
|
|
|
|
triggerHook(event, data)
|
|
{
|
|
if (!event || typeof event !== 'string')
|
|
{
|
|
this.logger.warn('Invalid event name');
|
|
return;
|
|
}
|
|
|
|
this._runHooks(event, data);
|
|
}
|
|
}
|