[PR #2778] [MERGED] fix(javascript): retain getter receivers through local aliases #5387

Closed
opened 2026-10-31 01:06:15 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/2778
Author: @To3akaRin
Created: 10/13/2026
Status: ✅ Merged
Merged: 10/13/2026
Merged by: @morluto

Base: main ← Head: codex/getter-local-receiver-aliases


📝 Commits (3)

  • 2459269 fix(javascript): retain getter receivers through local aliases
  • fb2fb22 fix(javascript): resolve projected private getter receivers
  • 67ca246 fix: reconcile latest main for PR #2778

📊 Changes

7 files changed (+940 additions, -111 deletions)

View changed files

📝 docs/javascript-application-workflows.md (+12 -0)
➕ src/domain/javascript/javascriptSemanticAnalysis.getter_aliases.test.ts (+351 -0)
📝 src/domain/javascript/javascriptSemanticMemberMutations.ts (+23 -46)
📝 src/domain/javascript/javascriptSemanticMutationInitializers.ts (+56 -5)
📝 src/domain/javascript/javascriptSemanticObjectReferences.ts (+360 -60)
➕ src/domain/javascript/javascriptSemanticReferenceSources.ts (+53 -0)
➕ tests/acceptance/applications/getterLocalAliasesCli.test.ts (+85 -0)

📄 Description

Summary

Fixes #2653. A getter such as get v() { const self = this; return self.s; } could return a shared object that is then written through box.v.x = 2, while REA still exported the old literal 1 for shared.x. The mutation now retains the getter receiver through local aliases and reports that return value as unknown.

Change and scope

  • Resolve getter-local alias initializers and destructuring projections in an iterative worklist, retaining lexical binding identity, capture order, and the original receiver. Plain and chained assignments use their value references. Private-field aliases keep receiver projections separate from result paths. Receiver projections follow data fields initialized from this or earlier self fields, preserving instance identity, a static field's declaring class, and initializer-versus-getter read timing.
  • Reuse the existing initializer-position logic for reads and the existing destructuring-source checks. Move the latter into one shared owner so excluded defaults are not interpreted differently by getter and ordinary mutation analysis.
  • Deduplicate alias selections and terminate cycles. When the same binding/capture repeatedly grows its selected path, resolve its roots as escapes through the existing mutation pass rather than enumerating loop iterations or dropping deeper possible writes.
  • Preserve no-write and unrelated-property controls, shadowed bindings, alias replacement, and the distinction between getter this and outer or nested ordinary-function receivers. Document the supported behavior.

This is an ordinary inert-source correctness fix within the existing analysis boundary; an advance capability-scope decision is not applicable. It is independent of #2765 (container ownership) and does not implement prototype getter lookup from #2655. #2092's unknown-key class-getter expansion change was inspected and does not fix these local alias cases; this branch does not depend on it or #2094.

Contract and boundary impact

  • Earliest changed stage: getter result references and source-local mutation propagation, before module retention and export return-shape projection.
  • CLI/MCP schemas, tool catalogs, package metadata, dependencies, installation, and provider authority: unchanged.
  • Exported Evidence now retains value_status: "unknown" and a root field with state: "unknown", value: null for the affected writes. The no-write control retains literal 1. Existing source ranges, inference limitations, and coverage semantics remain in place.
  • Effects remain reading and parsing caller-selected source. The fixture programs are never executed, and no target process, instrumentation, network, or file-mutation capability is added.

Evidence and regression coverage

On the unchanged base f0f09950f34d96e3b1c62fd04821af5e301f80c7, six source regressions returned the stale literal and both new public CLI/stdio MCP tests failed on that same exported value. The corresponding precision controls passed. Regression tests now also cover chained assignment, lexical private fields, an excluded destructuring default, a 2,000-binding alias chain, growing and non-growing cycles, and unrelated receiver properties.

Additional regressions distinguish positive projected private-field writes (#s = shared) from unrelated-child writes (#s = { peer: shared }) and no-write controls. They cover self-field chains, static and instance inheritance, initialization order, and private-result suffixes. Field-projection traversal rejects structurally repeating expansions while allowing repeated fields that consume a finite path; a cyclic-inheritance input has a termination regression. Growing local alias cycles retain deeper possible writes. The source suite and public transports pass.

Validation performed

On macOS with Node 24.19.0 and npm 11.17.0 (the repository pins Node 24.18.0 / npm 11.16.0; that exact pair was not used locally):

  • npm run test:local -- src/domain/javascript/javascriptSemanticAnalysis.getter_aliases.test.ts src/domain/javascript/javascriptSemanticAnalysis.getter_receivers.test.ts src/domain/javascript/javascriptSemanticAnalysis.member_mutations.test.ts src/domain/javascript/javascriptSemanticAnalysis.reference_lifetimes.test.ts src/domain/javascript/javascriptSemanticMutationInitializers.test.ts — 5 files, 366 tests passed.
  • npm run test:focused -- tests/acceptance/applications/getterLocalAliasesCli.test.ts tests/acceptance/applications/getterReceiverMutationsCli.test.ts — fresh runtime/skill build; 4 real CLI/stdio MCP tests passed.
  • npm run check — typecheck, lint, formatting, unused-code checks, metadata, module boundaries, and architecture guards passed.
  • npm run docs:check — generated documentation checks and 2 README consistency tests passed.
  • npm run verify:test-discovery — all 1,057 test files discovered exactly once.
  • npm run jscpd — passed at 2.98% duplicated lines.
  • git diff --check — passed.

Limitations

This does not execute getters or infer the new runtime value. Growing cycles conservatively escape their recovered roots, which can make additional descendants of those roots unknown; unrelated receiver properties remain separate. Receiver identity recovery is limited to known data-field this chains; it does not execute arbitrary getters, calls, constructors, or static blocks. Rest/copy aliases and arbitrary local-call results retain their existing handling. No real Electron process, Windows/Linux host, or released-package workflow was exercised, and no performance improvement is claimed.

Before merge

  • Concrete issue, focused outcome, and contribution scope established.
  • Changed source behavior and real public CLI/MCP Evidence covered.
  • Related documentation updated; no generated outputs or credentials committed.
  • Observations, static inferences, uncertainty, and remaining verification limits remain distinguishable.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/2778 **Author:** [@To3akaRin](https://github.com/To3akaRin) **Created:** 10/13/2026 **Status:** ✅ Merged **Merged:** 10/13/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `codex/getter-local-receiver-aliases` --- ### 📝 Commits (3) - [`2459269`](https://github.com/morluto/rea/commit/245926977d2f784b5604afcc89092fcc94395a5c) fix(javascript): retain getter receivers through local aliases - [`fb2fb22`](https://github.com/morluto/rea/commit/fb2fb22c1f0edf22aeca9f1d19a6f6cfd1d6f1f3) fix(javascript): resolve projected private getter receivers - [`67ca246`](https://github.com/morluto/rea/commit/67ca2467c3699fe247f6b68f9d960297e83ac591) fix: reconcile latest main for PR #2778 ### 📊 Changes **7 files changed** (+940 additions, -111 deletions) <details> <summary>View changed files</summary> 📝 `docs/javascript-application-workflows.md` (+12 -0) ➕ `src/domain/javascript/javascriptSemanticAnalysis.getter_aliases.test.ts` (+351 -0) 📝 `src/domain/javascript/javascriptSemanticMemberMutations.ts` (+23 -46) 📝 `src/domain/javascript/javascriptSemanticMutationInitializers.ts` (+56 -5) 📝 `src/domain/javascript/javascriptSemanticObjectReferences.ts` (+360 -60) ➕ `src/domain/javascript/javascriptSemanticReferenceSources.ts` (+53 -0) ➕ `tests/acceptance/applications/getterLocalAliasesCli.test.ts` (+85 -0) </details> ### 📄 Description ## Summary Fixes #2653. A getter such as `get v() { const self = this; return self.s; }` could return a shared object that is then written through `box.v.x = 2`, while REA still exported the old literal `1` for `shared.x`. The mutation now retains the getter receiver through local aliases and reports that return value as unknown. ## Change and scope - Resolve getter-local alias initializers and destructuring projections in an iterative worklist, retaining lexical binding identity, capture order, and the original receiver. Plain and chained assignments use their value references. Private-field aliases keep receiver projections separate from result paths. Receiver projections follow data fields initialized from `this` or earlier self fields, preserving instance identity, a static field's declaring class, and initializer-versus-getter read timing. - Reuse the existing initializer-position logic for reads and the existing destructuring-source checks. Move the latter into one shared owner so excluded defaults are not interpreted differently by getter and ordinary mutation analysis. - Deduplicate alias selections and terminate cycles. When the same binding/capture repeatedly grows its selected path, resolve its roots as escapes through the existing mutation pass rather than enumerating loop iterations or dropping deeper possible writes. - Preserve no-write and unrelated-property controls, shadowed bindings, alias replacement, and the distinction between getter `this` and outer or nested ordinary-function receivers. Document the supported behavior. This is an ordinary inert-source correctness fix within the existing analysis boundary; an advance capability-scope decision is not applicable. It is independent of #2765 (container ownership) and does not implement prototype getter lookup from #2655. #2092's unknown-key class-getter expansion change was inspected and does not fix these local alias cases; this branch does not depend on it or #2094. ## Contract and boundary impact - Earliest changed stage: getter result references and source-local mutation propagation, before module retention and export return-shape projection. - CLI/MCP schemas, tool catalogs, package metadata, dependencies, installation, and provider authority: unchanged. - Exported Evidence now retains `value_status: "unknown"` and a root field with `state: "unknown", value: null` for the affected writes. The no-write control retains literal `1`. Existing source ranges, inference limitations, and coverage semantics remain in place. - Effects remain reading and parsing caller-selected source. The fixture programs are never executed, and no target process, instrumentation, network, or file-mutation capability is added. ## Evidence and regression coverage On the unchanged base `f0f09950f34d96e3b1c62fd04821af5e301f80c7`, six source regressions returned the stale literal and both new public CLI/stdio MCP tests failed on that same exported value. The corresponding precision controls passed. Regression tests now also cover chained assignment, lexical private fields, an excluded destructuring default, a 2,000-binding alias chain, growing and non-growing cycles, and unrelated receiver properties. Additional regressions distinguish positive projected private-field writes (`#s = shared`) from unrelated-child writes (`#s = { peer: shared }`) and no-write controls. They cover self-field chains, static and instance inheritance, initialization order, and private-result suffixes. Field-projection traversal rejects structurally repeating expansions while allowing repeated fields that consume a finite path; a cyclic-inheritance input has a termination regression. Growing local alias cycles retain deeper possible writes. The source suite and public transports pass. ## Validation performed On macOS with Node 24.19.0 and npm 11.17.0 (the repository pins Node 24.18.0 / npm 11.16.0; that exact pair was not used locally): - `npm run test:local -- src/domain/javascript/javascriptSemanticAnalysis.getter_aliases.test.ts src/domain/javascript/javascriptSemanticAnalysis.getter_receivers.test.ts src/domain/javascript/javascriptSemanticAnalysis.member_mutations.test.ts src/domain/javascript/javascriptSemanticAnalysis.reference_lifetimes.test.ts src/domain/javascript/javascriptSemanticMutationInitializers.test.ts` — 5 files, 366 tests passed. - `npm run test:focused -- tests/acceptance/applications/getterLocalAliasesCli.test.ts tests/acceptance/applications/getterReceiverMutationsCli.test.ts` — fresh runtime/skill build; 4 real CLI/stdio MCP tests passed. - `npm run check` — typecheck, lint, formatting, unused-code checks, metadata, module boundaries, and architecture guards passed. - `npm run docs:check` — generated documentation checks and 2 README consistency tests passed. - `npm run verify:test-discovery` — all 1,057 test files discovered exactly once. - `npm run jscpd` — passed at 2.98% duplicated lines. - `git diff --check` — passed. ## Limitations This does not execute getters or infer the new runtime value. Growing cycles conservatively escape their recovered roots, which can make additional descendants of those roots unknown; unrelated receiver properties remain separate. Receiver identity recovery is limited to known data-field `this` chains; it does not execute arbitrary getters, calls, constructors, or static blocks. Rest/copy aliases and arbitrary local-call results retain their existing handling. No real Electron process, Windows/Linux host, or released-package workflow was exercised, and no performance improvement is claimed. ## Before merge - [x] Concrete issue, focused outcome, and contribution scope established. - [x] Changed source behavior and real public CLI/MCP Evidence covered. - [x] Related documentation updated; no generated outputs or credentials committed. - [x] Observations, static inferences, uncertainty, and remaining verification limits remain distinguishable. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:06:15 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#5387
No description provided.