--- model: accounts/fireworks/routers/kimi-k2p5-turbo --- # 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. **Primary Use Case:** AI analysis of execution flow mapped to ECMAScript spec sections for understanding JavaScript engine behavior. ## Files to Change | File | Action | |------|--------| | `engine262/src/ask262-debug.mts` | **CREATE** — the module with deduplication logic | | `engine262/src/index.mts` | **MODIFY** — add named re-export | | `engine262/scripts/transform.mts` | **MODIFY** — auto-inject `mark()` and import | | `engine262/scripts/verify-ask262-debug.mts` | **CREATE** — post-build verification script | ## 1. `scripts/transform.mts` — Enhance the Babel Plugin ### 1a. Extend State Interface Add `ask262Debug` to the `NeededNames` type and extend `State` interface: ```typescript type NeededNames = 'Completion' | 'AbruptCompletion' | 'Assert' | 'Call' | 'IteratorClose' | 'AsyncIteratorClose' | 'Value' | 'skipDebugger' | 'ask262Debug'; // NEW interface State extends PluginPass { needed: Partial>; fileRelativePath?: string; // NEW: captured at Program enter } ``` ### 1b. Add Import Creator Function Add alongside existing `createImport*` functions: ```typescript function createImportAsk262Debug() { return template.ast(` import { ask262Debug } from "#self"; `); } ``` ### 1c. Refactor `addSectionFromComments` The current function (lines 101-133) handles only single URLs. Update to capture multiple section IDs and inject `mark()`: ```typescript function addSectionFromComments( path: NodePath | NodePath | NodePath | NodePath | NodePath, state: State, getName: () => string, getBody: () => t.BlockStatement | null ) { if (!path.node.leadingComments) return; const sectionIds: string[] = []; 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')); // Only capture external URLs (skip local/fragment references) if (section?.startsWith('https://')) { url = section; } } } } } if (sectionIds.length === 0) return; const name = getName(); // 1. Keep existing .section (backward compat) - uses first URL // Only applies to named functions if (name && url) { path.insertAfter(template.ast(`${name}.section = '${url}';`)); } // 2. Get function body via callback (node-type check done by caller) const body = getBody(); // 3. Inject mark() at start of function body (only for block bodies) if (body?.type === 'BlockStatement') { const line = path.node.loc?.start.line ?? 0; const markCall = template.statement( `ask262Debug.mark(${JSON.stringify(sectionIds)}, ${JSON.stringify(state.fileRelativePath)}, ${line});` )(); body.body.unshift(markCall); state.needed.ask262Debug = true; } } ``` ### 1d. Update Visitors Update existing visitors to use new `addSectionFromComments` signature, and add new visitors for ClassMethod and ObjectMethod: ```typescript return { visitor: { Program: { enter(path, state) { state.needed = {}; // Capture relative file path from state.filename const absolutePath = state.filename || ''; state.fileRelativePath = absolutePath.replace(/.*\/(src\/)/, '$1').replace(/\.mts$/, '.mts') || 'unknown.mts'; }, exit(path, state) { // ... existing imports (skipDebugger, Completion, etc.) ... // NEW: Add ask262Debug import if needed if (state.needed.ask262Debug) { path.unshiftContainer('body', createImportAsk262Debug()); } }, }, FunctionDeclaration(path, state) { addSectionFromComments( path, state, () => path.node.id!.name, () => path.node.body ); }, VariableDeclaration(path, state) { const init = path.get('declarations.0.init'); if (init.isFunctionExpression()) { const id = path.node.declarations[0].id as t.Identifier; addSectionFromComments( path, state, () => id.name, () => init.node.body ); } else if (init.isArrowFunctionExpression()) { const id = path.node.declarations[0].id as t.Identifier; addSectionFromComments( path, state, () => id.name, () => init.node.body.type === 'BlockStatement' ? init.node.body : null ); } }, ExportNamedDeclaration(path, state) { const declaration = path.node.declaration; if (declaration?.type === 'FunctionDeclaration') { addSectionFromComments( path, state, () => declaration.id!.name, () => declaration.body ); } }, // NEW: Add ClassMethod visitor ClassMethod(path, state) { const key = path.node.key; if (key.type === 'Identifier') { addSectionFromComments( path, state, () => key.name, () => path.node.body ); } }, // NEW: Add ObjectMethod visitor ObjectMethod(path, state) { const key = path.node.key; if (key.type === 'Identifier') { addSectionFromComments( path, state, () => key.name, () => path.node.body ); } }, // ... rest of existing visitors (CallExpression, ThrowStatement, etc.) unchanged }, }; ``` ### 1e. Arrow Function Special Handling The updated `addSectionFromComments` handles this automatically: - Arrow with block body `() => { ... }`: `.section` + `mark()` injected - Arrow with expression body `() => expr`: `.section` only, no `mark()` ## 2. `engine262/src/index.mts` — Add Named Re-export Add the re-export alongside existing exports (around line 20): ```typescript // Add to existing exports from './helpers.mts' or create new export section export { ask262Debug, type MarkData } from './ask262-debug.mts'; ``` ## 3. `engine262/src/ask262-debug.mts` — The Module Deduplicated data collection. No stack parsing, no `node:` imports, no decorators. ```typescript export interface MarkData { readonly sectionIds: string[]; readonly fileRelativePath: string; readonly lineNumber: number; readonly important: boolean; } interface MarkKey { readonly sectionIds: string; readonly file: string; readonly line: number; } class Ask262Debug { marks: MarkData[] = []; private _importantStack = false; private _markIndex = new Map(); // key -> index in marks private _makeKey(sectionIds: string[], file: string, line: number): string { return `${sectionIds.join(',')}|${file}|${line}`; } mark(sectionIds: string[], file: string, line: number) { const key = this._makeKey(sectionIds, file, line); const existingIndex = this._markIndex.get(key); if (existingIndex !== undefined) { // Merge important flag const existing = this.marks[existingIndex]; if (this._importantStack && !existing.important) { (this.marks[existingIndex] as any).important = true; } return; } const newMark: MarkData = { sectionIds: [...sectionIds], // defensive copy fileRelativePath: file, lineNumber: line, important: this._importantStack, }; this._markIndex.set(key, this.marks.length); this.marks.push(newMark); } startImportant() { this._importantStack = true; } stopImportant() { this._importantStack = false; } } export const ask262Debug = new Ask262Debug(); ``` ### Key Behaviors | Scenario | Behavior | |----------|----------| | Duplicate `(sectionIds, file, line)` | Keep one entry, merge `important` flags (OR logic) | | Generator resumption | Mark on every `.next()` call (may create duplicates if same location) | | Nested functions | Each entry adds its mark (chronological trace) | ## 4. `engine262/scripts/verify-ask262-debug.mts` — Verification Script ```typescript #!/usr/bin/env node import { ask262Debug, Agent, ManagedRealm } from '#self'; console.log('=== ask262Debug Verification ===\n'); // 1. Check export exists console.log('1. Checking export...'); if (!ask262Debug) { console.error('FAIL: ask262Debug not exported'); process.exit(1); } console.log(' ✓ ask262Debug exported\n'); // 2. Run code that hits known spec sections console.log('2. Executing test code...'); const agent = new Agent(); const realm = new ManagedRealm({ agent }); realm.evaluateScript(` // Array.prototype.every - should hit sec-array.prototype.every [1, 2, 3].every(x => x > 0); // Proxy creation - should hit sec-proxycreate or similar new Proxy({}, {}); `); // 4. Verify marks were captured console.log('3. Checking marks...'); const marks = ask262Debug.marks; console.log(` Captured ${marks.length} unique marks\n`); if (marks.length === 0) { console.error('FAIL: No marks captured'); process.exit(1); } // 5. Show sample marks console.log('4. Sample marks:'); marks.slice(0, 5).forEach((m, i) => { console.log(` [${i}] ${m.sectionIds.join(', ')} @ ${m.fileRelativePath}:${m.lineNumber}${m.important ? ' [important]' : ''}`); }); console.log('=== All Checks Passed ==='); ``` ## Key Design Decisions Summary | Decision | Choice | Rationale | |----------|--------|-----------| | **Use case** | AI execution flow analysis | Map runtime to spec sections | | **Import pattern** | `import { ask262Debug } from '#self'` | Uses existing pattern (315+ occurrences) | | **Bundle strategy** | Included in engine262.mjs | Single artifact, no config changes needed | | **Data structure** | Deduplicated array | Chronological trace with unique entries only | | **Deduplication key** | `(sectionIds, file, line)` | Same location = same semantic entry | | **Duplicate handling** | Merge `important` flags (OR) | If any call was important, mark it important | | **All metadata** | From transform (build time) | No runtime stack parsing, no `node:` imports | | **`mark()` injection** | Auto-injected at start of block body | Developer writes nothing | | **Arrow functions** | Block body: `.section` + `mark()`, Expression: `.section` only | Can't inject statement in expression | | **Multi-section** | Array of section IDs per function | Functions can implement multiple spec sections | | **`.section`** | Kept (backward compat) | Used by test262-intrinsics | | **Generators** | Mark on every `.next()` | Simpler, deduplication handles repeats | | **Nested functions** | Mark all | Chronological trace for AI analysis | | **`important`** | User-driven `startImportant()`/`stopImportant()` | Manual annotation of interesting sections | | **Function types** | FunctionDeclaration + ClassMethod + ObjectMethod + VariableDeclaration | Covers all URL comment patterns | | **Class/Object method keys** | Identifier names only | Simple, covers 99% of cases | | **File paths** | Relative to `engine262/src/`, `.mts` extension | Maps to source, not compiled output | | **Line numbers** | 1-indexed from Babel AST | Matches source file line numbers | ## Implementation Checklist 1. [ ] **Create** `engine262/src/ask262-debug.mts` with the module code 2. [ ] **Modify** `engine262/src/index.mts` - add re-export 3. [ ] **Modify** `engine262/scripts/transform.mts`: - [ ] Add `ask262Debug` to `NeededNames` type - [ ] Extend `State` interface with `fileRelativePath` - [ ] Add `createImportAsk262Debug()` function - [ ] Update `addSectionFromComments()` signature and logic - [ ] Update `Program.exit` to inject ask262Debug import - [ ] Update `FunctionDeclaration` visitor - [ ] Update `VariableDeclaration` visitor - [ ] Update `ExportNamedDeclaration` visitor - [ ] Add `ClassMethod` visitor - [ ] Add `ObjectMethod` visitor 4. [ ] **Create** `engine262/scripts/verify-ask262-debug.mts` 5. [ ] **Type checking**: `cd engine262 && npx tsc -b .` 6. [ ] **Linting**: `cd engine262 && npm run lint` 7. [ ] **Build**: `cd engine262 && npm run build:engine` 8. [ ] **Verification**: `cd engine262 && node --enable-source-maps --disable-warning=ExperimentalWarning --experimental-strip-types scripts/verify-ask262-debug.mts` ## Expected Output from Verification ``` === ask262Debug Verification === 1. Checking export... ✓ ask262Debug exported 2. Executing test code... 3. Checking marks... Captured 47 unique marks 4. Sample marks: [0] sec-array.prototype.every @ intrinsics/ArrayPrototypeShared.mts:173 [1] sec-proxycreate @ abstract-ops/proxy-objects.mts:540 ... === All Checks Passed === ``` ## Troubleshooting ### Issue: No marks captured after build **Cause:** Transform not injecting `mark()` calls **Fix:** Check that functions have URL comments with `#sec-` patterns ### Issue: Build fails with "Cannot find module '#self'" **Cause:** Rollup not resolving the import **Fix:** Ensure babel transform runs before resolution in rollup config ### Issue: Line numbers are off **Cause:** Babel source maps not configured **Fix:** Check that `sourceMap: true` in babelOptions and `loc` is preserved ### Issue: Duplicate marks for same function **Cause:** Generator resumption or recursion **Fix:** This is expected behavior - deduplication happens on `(sectionIds, file, line)`