7.0 KiB
model
| model |
|---|
| mimo-v2-pro |
Plan: ask262Debug Module for engine262
Overview
Runtime execution tracker that records which ECMAScript spec sections are entered during execution. The Babel transform auto-injects ask262Debug.mark() at the start of every function body with URL comments — developer writes nothing. All metadata (sectionId, file, line) resolved at build time.
Files to Change
| File | Action |
|---|---|
engine262/src/ask262-debug.mts |
CREATE — the module |
engine262/src/index.mts |
MODIFY — add re-export |
engine262/tsconfig.base.json |
MODIFY — add #self/ask262Debug path |
engine262/package.json |
MODIFY — add #self/ask262Debug import |
engine262/scripts/rollup.config.mts |
MODIFY — extend resolve-self plugin |
engine262/scripts/transform.mts |
MODIFY — auto-inject mark() into function bodies |
1. scripts/transform.mts — Enhance the Babel plugin
1a. Refactor addSectionFromComments to also inject mark() into body
Current behavior: sets FuncName.section = 'url' after the declaration.
New behavior: also injects ask262Debug.mark(["sec-..."], "file.mts", lineNumber) as the first statement inside the function body.
function addSectionFromComments(path, getName) {
if (!path.node.leadingComments) return;
const sectionIds = [];
let url = '';
for (const c of path.node.leadingComments) {
for (const line of c.value.split('\n')) {
const matches = line.match(/#(sec-[a-zA-Z0-9._%-]+)/g);
if (matches) {
sectionIds.push(...matches.map(m => m.substring(1)));
if (!url) {
const section = line.split(' ').find(l => l.includes('#sec'));
url = section.includes('https') ? section : `https://tc39.es/ecma262/${section}`;
}
}
}
}
if (sectionIds.length === 0) return;
const name = getName();
// 1. Keep existing .section (backward compat)
path.insertAfter(template.ast(`${name}.section = '${url}';`));
// 2. Inject mark() at start of function body
const body = path.node.body;
if (body?.type === 'BlockStatement') {
const relativeFile = getRelativeFilePath(state);
const line = path.node.loc?.start.line ?? 0;
const markCall = template.ast(
'ask262Debug.mark(SECTION_IDS, FILE, LINE);'
.replace('SECTION_IDS', JSON.stringify(sectionIds))
.replace('FILE', JSON.stringify(relativeFile))
.replace('LINE', String(line))
);
body.body.unshift(markCall);
}
}
1b. Add visitors for all function-like nodes
FunctionDeclaration(path) {
addSectionFromComments(path, () => path.node.id!.name);
},
ClassMethod(path) {
addSectionFromComments(path, () => (path.node.key as t.Identifier).name);
},
ObjectMethod(path) {
addSectionFromComments(path, () => (path.node.key as t.Identifier).name);
},
// Existing visitors (VariableDeclaration, ExportNamedDeclaration) unchanged
1c. Multi-section support
Functions with multiple URL comments get all section IDs as an array:
/** https://tc39.es/ecma262/#sec-array.prototype.every */
/** https://tc39.es/ecma262/#sec-%typedarray%.prototype.every */
function* ArrayProto_every(args) { ... }
// Transform injects:
function* ArrayProto_every(args) {
ask262Debug.mark(["sec-array.prototype.every", "sec-%typedarray%.prototype.every"], "intrinsics/ArrayPrototypeShared.mts", 173);
...
}
2. engine262/package.json — Add subpath import
"#self/ask262Debug": "./src/ask262-debug.mts"
3. engine262/tsconfig.base.json — Add path mapping
"#self/ask262Debug": ["./src/ask262-debug.mts"]
4. scripts/rollup.config.mts — Extend resolve-self plugin
Change from exact #self match to prefix #self/ match in both builds:
resolveId(source, _importer, _options) {
if (source === '#self' || source.startsWith('#self/')) {
return { id: './engine262.mjs' } as any; // inspector build
}
return undefined;
},
Add similar plugin to main engine262 build (which currently lacks one):
{
name: 'resolve-self',
resolveId(source) {
if (source === '#self' || source.startsWith('#self/')) return { id: '#self' };
},
},
5. engine262/src/index.mts — Add re-export
export { default as ask262Debug, type MarkData } from './ask262-debug.mts';
6. engine262/src/ask262-debug.mts — The module
Pure data collection. No stack parsing, no node: imports, no decorators.
export interface MarkData {
readonly sectionIds: string[];
readonly fileRelativePath: string;
readonly lineNumber: number;
readonly important: boolean;
}
class Ask262Debug {
marks: MarkData[] = [];
private _importantStack = false;
mark(sectionIds: string[], file: string, line: number) {
this.marks.push({
sectionIds,
fileRelativePath: file,
lineNumber: line,
important: this._importantStack,
});
}
startImportant() {
this._importantStack = true;
}
stopImportant() {
this._importantStack = false;
}
}
const ask262Debug = new Ask262Debug();
export default ask262Debug;
Developer writes
Nothing. The transform auto-injects mark() into every function body with URL comments.
What the transform produces
// Original:
/** https://tc39.es/ecma262/#sec-proxycreate */
export function ProxyCreate(target, handler) {
// ...
}
// Transformed:
export function ProxyCreate(target, handler) {
ask262Debug.mark(["sec-proxycreate"], "abstract-ops/proxy-objects.mts", 540);
// ...
}
ProxyCreate.section = 'https://tc39.es/ecma262/#sec-proxycreate';
Key Design Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Module name | #self/ask262Debug |
Follows existing #self pattern |
| Export style | Singleton class, default export | ask262Debug.marks access |
MarkData type |
Named export | Users can type-annotate |
| All metadata | From transform (build time) | No runtime stack parsing, no node: imports |
mark() injection |
Auto-injected at start of function body | Developer writes nothing |
| Multi-section | Array of section IDs per function | Functions can implement multiple spec sections |
markLine() |
Removed | mark() covers all cases |
.section |
Kept (backward compat) | Used by test262-intrinsics |
important |
Stubbed with boolean flag | startImportant()/stopImportant() |
| Function types | FunctionDeclaration + ClassMethod + ObjectMethod | Covers all URL comment patterns |
Verification
cd engine262 && npx tsc -b .— types compilecd engine262 && npm run lint— no ESLint violations- Verify transform output: check that functions with URL comments have
ask262Debug.mark(...)injected at start of body - Verify multi-section: check
ArrayProto_everygets both section IDs - Run
npm run build:engine— rollup resolves#self/ask262Debugcorrectly - Inspect
ask262Debug.marksafter running a test to verify marks are populated with correct data