From c1237d146ddca8ebdfb455cbdc1d2b25f2dc29d9 Mon Sep 17 00:00:00 2001 From: bendtherules Date: Tue, 7 Apr 2026 14:02:15 +0530 Subject: [PATCH] plan: mark section ids from code --- .opencode/plans/1775540058831-quick-planet.md | 224 ++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 .opencode/plans/1775540058831-quick-planet.md diff --git a/.opencode/plans/1775540058831-quick-planet.md b/.opencode/plans/1775540058831-quick-planet.md new file mode 100644 index 0000000..af2ab29 --- /dev/null +++ b/.opencode/plans/1775540058831-quick-planet.md @@ -0,0 +1,224 @@ +--- +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. + +```typescript +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 + +```typescript +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: + +```typescript +/** 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 + +```json +"#self/ask262Debug": "./src/ask262-debug.mts" +``` + +## 3. `engine262/tsconfig.base.json` — Add path mapping + +```json +"#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: + +```typescript +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): + +```typescript +{ + name: 'resolve-self', + resolveId(source) { + if (source === '#self' || source.startsWith('#self/')) return { id: '#self' }; + }, +}, +``` + +## 5. `engine262/src/index.mts` — Add re-export + +```typescript +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. + +```typescript +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 + +```typescript +// 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 + +1. `cd engine262 && npx tsc -b .` — types compile +2. `cd engine262 && npm run lint` — no ESLint violations +3. Verify transform output: check that functions with URL comments have `ask262Debug.mark(...)` injected at start of body +4. Verify multi-section: check `ArrayProto_every` gets both section IDs +5. Run `npm run build:engine` — rollup resolves `#self/ask262Debug` correctly +6. Inspect `ask262Debug.marks` after running a test to verify marks are populated with correct data