1# renameVariables23## File4`src/ReactiveScopes/RenameVariables.ts`56## Purpose7This pass ensures that every named variable in the function has a unique name that doesn't conflict with other variables in the same block scope or with global identifiers. After scope construction and temporary promotion, variables from different source scopes may end up in the same reactive block - this pass resolves any naming conflicts.89The pass also converts the `#t{id}` promoted temporary names into clean output names like `t0`, `t1`, etc.1011## Input Invariants12- The ReactiveFunction has been through `promoteUsedTemporaries`13- Variables may have names that conflict with:14 - Other variables in the same or ancestor block scope15 - Global identifiers referenced by the function16 - Promoted temporaries with `#t{id}` or `#T{id}` naming17- The function parameters have names (either from source or promoted)1819## Output Guarantees20- Every named variable has a unique name within its scope21- No variable shadows a global identifier referenced by the function22- Promoted temporaries are renamed to `t0`, `t1`, ... (for regular temps)23- Promoted JSX temporaries are renamed to `T0`, `T1`, ... (for JSX tags)24- Conflicting source names get disambiguated with `$` suffix (e.g., `foo$0`, `foo$1`)25- Returns a `Set<string>` of all unique variable names in the function2627## Algorithm2829### Phase 1: Collect Referenced Globals30Uses `collectReferencedGlobals(fn)` to build a set of all global identifiers referenced by the function. Variable names must not conflict with these.3132### Phase 2: Rename with Scope Stack33The `Scopes` class maintains:3435```typescript36class Scopes {37 #seen: Map<DeclarationId, IdentifierName> = new Map(); // Canonical name for each declaration38 #stack: Array<Map<string, DeclarationId>> = [new Map()]; // Block scope stack39 #globals: Set<string>; // Global names to avoid40 names: Set<ValidIdentifierName> = new Set(); // All assigned names41}42```4344### Renaming Logic45```typescript46visit(identifier: Identifier): void {47 // Skip unnamed identifiers48 if (originalName === null) return;4950 // If we've already named this declaration, reuse that name51 const mappedName = this.#seen.get(identifier.declarationId);52 if (mappedName !== undefined) {53 identifier.name = mappedName;54 return;55 }5657 // Find a unique name58 let name = originalName.value;59 let id = 0;6061 // Promoted temporaries start with t0/T062 if (isPromotedTemporary(originalName.value)) {63 name = `t${id++}`;64 } else if (isPromotedJsxTemporary(originalName.value)) {65 name = `T${id++}`;66 }6768 // Increment until we find a unique name69 while (this.#lookup(name) !== null || this.#globals.has(name)) {70 if (isPromotedTemporary(...)) {71 name = `t${id++}`;72 } else if (isPromotedJsxTemporary(...)) {73 name = `T${id++}`;74 } else {75 name = `${originalName.value}$${id++}`; // foo$0, foo$1, etc.76 }77 }7879 identifier.name = makeIdentifierName(name);80 this.#seen.set(identifier.declarationId, identifier.name);81}82```8384### Scope Management85```typescript86enter(fn: () => void): void {87 this.#stack.push(new Map());88 fn();89 this.#stack.pop();90}9192#lookup(name: string): DeclarationId | null {93 // Search from innermost to outermost scope94 for (let i = this.#stack.length - 1; i >= 0; i--) {95 const entry = this.#stack[i].get(name);96 if (entry !== undefined) return entry;97 }98 return null;99}100```101102### Visitor Pattern103```typescript104class Visitor extends ReactiveFunctionVisitor<Scopes> {105 override visitBlock(block: ReactiveBlock, state: Scopes): void {106 state.enter(() => {107 this.traverseBlock(block, state);108 });109 }110111 override visitScope(scope: ReactiveScopeBlock, state: Scopes): void {112 // Visit scope declarations first113 for (const [_, declaration] of scope.scope.declarations) {114 state.visit(declaration.identifier);115 }116 this.traverseScope(scope, state);117 }118119 override visitPlace(id: InstructionId, place: Place, state: Scopes): void {120 state.visit(place.identifier);121 }122}123```124125## Edge Cases126127### Shadowed Variables128When the compiler merges scopes that had shadowing in the source:129```javascript130function foo() {131 const x = 1;132 {133 const x = 2; // Shadowed in source134 }135}136```137If both `x` declarations end up in the same compiled scope, they become `x` and `x$0`.138139### Global Name Conflicts140If a local variable would conflict with a referenced global:141```javascript142function foo() {143 const Math = 1; // Conflicts with global Math if used144}145```146The local gets renamed to `Math$0` if `Math` global is referenced.147148### Nested Functions149The pass recursively processes nested function expressions, entering a new scope for each function body.150151### Pruned Scopes152Pruned scopes don't create a new block scope in the output - the pass traverses their instructions without entering a new scope level.153154### DeclarationId Consistency155The pass uses `DeclarationId` to track which identifiers refer to the same variable, ensuring all references get the same renamed name.156157## TODOs158None in the source file.159160## Example161162### Fixture: `simple.js`163164**Before RenameVariables:**165```166scope @0 [...] declarations=[#t5$19_@0]167scope @1 [...] dependencies=[#t9$22] declarations=[#t10$23_@1]168```169170**After RenameVariables:**171```172scope @0 [...] declarations=[t0$19_@0]173scope @1 [...] dependencies=[t0$22] declarations=[t1$23_@1]174```175176Key observations:177- `#t5$19_@0` becomes `t0$19_@0` (first temporary in scope)178- `#t9$22` becomes `t0$22` (first temporary in a different block scope)179- `#t10$23_@1` becomes `t1$23_@1` (second temporary in that block)180- The `#t` prefix is removed and sequential numbering is applied181182**Generated Code:**183```javascript184export default function foo(x, y) {185 const $ = _c(4);186 if (x) {187 let t0; // Was #t5188 if ($[0] !== y) {189 t0 = foo(false, y);190 // ...191 }192 return t0;193 }194 const t0 = y * 10; // Was #t9, reuses t0 since different block scope195 let t1; // Was #t10196 // ...197}198```199200The pass produces clean, readable output with minimal variable names while avoiding conflicts.
Findings
✓ No findings reported for this file.