Files
ask262/.opencode/plans/1775540058831-quick-planet.md
T

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

  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