[GH-ISSUE #2653] fix(javascript): getter-local receiver aliases retain stale return literals #954

Closed
opened 2026-10-15 18:29:51 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 13, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/2653

Summary

An exported function can visibly write to a shared object's property through a getter and still receive a static return shape of literal 1 for that property. The initialized receiver alias inside the getter is enough to trigger this result. REA's documented conservative treatment of accessors requires the affected value to remain uncertain unless its post-write value is modeled correctly. The observed producer and export projection instead retain the pre-write literal with complete return-shape coverage.

Steps to reproduce

The source analysis and production export projection below were executed against the source baseline in Runtime or environment. The input remained inert text throughout verification.

Save this source as app.mjs in a readable directory named getter-alias-input:

export function result() {
  const shared = { x: 1 };
  const box = {
    s: shared,
    get v() {
      const self = this;
      return self.s;
    },
  };
  box.v.x = 2;
  return shared.x;
}

For a source-checkout reproduction without launching the application or analysis worker, save the following test as src/domain/javascript/getterReceiverAlias.review.test.ts and run the command below. It invokes the semantic analyzer and the same module compaction and return projection used by application analysis. All fixture programs are strings; the test never executes them.

import { expect, it } from "vitest";
import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js";
import { onlyCallable } from "./javascriptSemanticAnalysis.fixture.js";
import { projectJavaScriptModuleSemantics } from "./javascriptModuleSemanticIr.js";
import { projectJavaScriptExportReturnShapes } from "./javascriptExportReturnShapeProjection.js";
import { completeApplicationCoverage } from "./javascriptApplicationEvidenceSchemas.js";

it("observes stale getter-alias evidence and discriminating controls", () => {
  for (const [getter, write, expected] of [
    ["const self = this; return self.s;", "box.v.x = 2;", "literal"],
    ["return this.s;", "box.v.x = 2;", "unknown"],
    ["const self = this; return self.s;", "", "literal"],
  ]) {
    const ir = analyzeJavaScriptSemantics(`export function result() {
      const shared = { x: 1 };
      const box = { s: shared, get v() { ${getter} } };
      ${write}
      return shared.x;
    }`);
    const value = onlyCallable(ir, "result").returnSites[0]?.value;
    expect(value?.status).toBe(expected);
    if (expected === "literal")
      expect(value).toMatchObject({ status: "literal", value: 1 });
    expect(ir.coverage.status).toBe("complete");
    const module = projectJavaScriptModuleSemantics(ir);
    const link = module.moduleLinks.find(l => l.exportedName === "result");
    if (!link) throw new Error("Missing export link");
    const projection = projectJavaScriptExportReturnShapes({
      ir: module, link, modulePath: "app.mjs",
      baseCoverage: completeApplicationCoverage(),
    });
    expect(projection?.properties.static_return_shapes)
      .toMatchObject([{ value_status: expected }]);
    expect(projection?.properties.return_shape_coverage)
      .toMatchObject({ status: "complete", projection_complete: true });
    console.log(JSON.stringify({ getter, write, value,
      projection: projection?.properties }));
  }
});
npm run test:focused -- src/domain/javascript/getterReceiverAlias.review.test.ts

The public entry command below is provided for a subsequent end-to-end check. It was not run in this verification; its connection to the observed producer and projection was checked in source.

node scripts/rea.mjs analyze-javascript-application getter-alias-input --artifact-format directory --json

Locate the graph observation whose semantic_role is export-return-shapes and exported_name is result. Use a stable ordinary UTF-8 file, default source selection, no cancellation and sufficient analysis/transfer capacity. No package metadata or external module resolution is necessary for this export.

Expected behavior

The documented export return-shape analysis retains conservative uncertainty for accessors and aliases. For this source, box.v invokes the getter with box as this; self.s therefore refers to shared, and the assignment writes its x property before the return. That reasoning follows the visible source; it is not a runtime execution observation. A value_status of unknown, or a correctly modeled literal 2, satisfies the contract. A definite literal 1 does not.

Actual behavior

The initialized receiver alias produces the following value in the semantic analyzer and retains it through production module compaction and export projection:

{
  "value": { "status": "literal", "value": 1 },
  "static_return_shapes": [
    {
      "value_status": "literal",
      "fields": [
        { "path": "", "presence": "present", "state": "literal", "value": 1, "reason": null }
      ]
    }
  ],
  "return_shape_coverage": {
    "status": "complete",
    "retained_return_sites": 1,
    "omitted_return_sites": 0,
    "omitted_fields": 0,
    "omitted_property_coverage": 0,
    "projection_complete": true
  }
}

This excerpt omits source ranges and other projection metadata. Changing only the getter body to return this.s yields unknown after the same write. Removing the write from the alias case preserves the correct pre-write literal 1. The supplied assignment-alias and destructured-receiver variants also retained literal 1 through the same projection; the supplied getter calling a receiver method retained literal 1 at the semantic analyzer. These observations do not establish their frequency in real applications.

The six-case focused observation run passed all six cases. Its assertions deliberately record the current wrong result and controls, rather than claiming a regression fix. An earlier run failed because the verification test queried a nonexistent IR property; correcting that observation check produced the passing runs. No CLI, MCP, isolated analysis worker, installed-package runtime or fixture program was executed.

Evidence

  • Expected source: docs/javascript-application-workflows.md:185-203 documents direct return evaluation, alias uncertainty and conservative accessor treatment. src/domain/javascript/javascriptSemanticAnalysis.getter_receivers.test.ts and tests/acceptance/applications/getterReceiverMutationsCli.test.ts corroborate that writes through returned getter references must not publish stale literals.
  • Failure source: Fresh inert observation tests invoked analyzeJavaScriptSemantics, projectJavaScriptModuleSemantics and projectJavaScriptExportReturnShapes and observed the value/projection above. Relevant receiver translation is src/domain/javascript/javascriptSemanticObjectReferences.ts:310-402; ordinary alias propagation is src/domain/javascript/javascriptSemanticMemberMutations.ts:100-314 and 651-715.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

The runtime observations establish the inert producer, module compaction and export projection results. Public command reachability was checked statically, and the public command was not run. Reproduction is complete at the source-level correctness boundary.

The supported public command accepts an ordinary readable directory containing the fixture. Public applicability was checked in source at src/cli/electronCommands.ts:188, src/application/javascript/JavaScriptApplicationService.ts:38 and src/application/javascript/JavaScriptModuleRelationships.ts:164; it was not verified by an end-to-end launch.

Related prior work is the closed getter-receiver report. Its closure verifies direct this, direct receiver-property, private-field and super-getter inputs. It is reference material, not evidence that this getter-local alias case is fixed. Related: #1494.

Restoration check

Reanalyze the exact alias source and require its exported return value to be unknown or correctly modeled as literal 2. Preserve the direct-receiver write control's uncertainty and the no-write control's literal 1. Confirm that the result survives module compaction and appears in public application Evidence. Apply the same check to the observed assignment-alias and destructured-receiver variants. Complete coverage describes retained return sites and must not be used as proof of runtime execution.

Runtime or environment

Source checkout a2d8b94dd5, tested with the pinned Node 24.18.0 / npm 11.16.0 toolchain. No claim is made about independently testing the published package or another revision. Current upstream 3ae826ceba remains affected by source recheck of the unchanged getter-receiver translation, mutation analysis, module compaction and export projection; no new runtime reproduction was run.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The demonstrated inputs publish an incorrect static literal through the export return-shape projection. The result can mislead source analysis, but no production incident or severe broader impact was observed.
  • Workaround status: unverified
  • Workaround basis: The direct-receiver control avoids this result, but rewriting the analyzed artifact changes its identity and is not an established general workaround for an analyst who needs Evidence about the original input.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2653 ## Summary An exported function can visibly write to a shared object's property through a getter and still receive a static return shape of literal 1 for that property. The initialized receiver alias inside the getter is enough to trigger this result. REA's documented conservative treatment of accessors requires the affected value to remain uncertain unless its post-write value is modeled correctly. The observed producer and export projection instead retain the pre-write literal with complete return-shape coverage. ## Steps to reproduce The source analysis and production export projection below were executed against the source baseline in Runtime or environment. The input remained inert text throughout verification. Save this source as app.mjs in a readable directory named getter-alias-input: ```js export function result() { const shared = { x: 1 }; const box = { s: shared, get v() { const self = this; return self.s; }, }; box.v.x = 2; return shared.x; } ``` For a source-checkout reproduction without launching the application or analysis worker, save the following test as src/domain/javascript/getterReceiverAlias.review.test.ts and run the command below. It invokes the semantic analyzer and the same module compaction and return projection used by application analysis. All fixture programs are strings; the test never executes them. ```ts import { expect, it } from "vitest"; import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js"; import { onlyCallable } from "./javascriptSemanticAnalysis.fixture.js"; import { projectJavaScriptModuleSemantics } from "./javascriptModuleSemanticIr.js"; import { projectJavaScriptExportReturnShapes } from "./javascriptExportReturnShapeProjection.js"; import { completeApplicationCoverage } from "./javascriptApplicationEvidenceSchemas.js"; it("observes stale getter-alias evidence and discriminating controls", () => { for (const [getter, write, expected] of [ ["const self = this; return self.s;", "box.v.x = 2;", "literal"], ["return this.s;", "box.v.x = 2;", "unknown"], ["const self = this; return self.s;", "", "literal"], ]) { const ir = analyzeJavaScriptSemantics(`export function result() { const shared = { x: 1 }; const box = { s: shared, get v() { ${getter} } }; ${write} return shared.x; }`); const value = onlyCallable(ir, "result").returnSites[0]?.value; expect(value?.status).toBe(expected); if (expected === "literal") expect(value).toMatchObject({ status: "literal", value: 1 }); expect(ir.coverage.status).toBe("complete"); const module = projectJavaScriptModuleSemantics(ir); const link = module.moduleLinks.find(l => l.exportedName === "result"); if (!link) throw new Error("Missing export link"); const projection = projectJavaScriptExportReturnShapes({ ir: module, link, modulePath: "app.mjs", baseCoverage: completeApplicationCoverage(), }); expect(projection?.properties.static_return_shapes) .toMatchObject([{ value_status: expected }]); expect(projection?.properties.return_shape_coverage) .toMatchObject({ status: "complete", projection_complete: true }); console.log(JSON.stringify({ getter, write, value, projection: projection?.properties })); } }); ``` ```sh npm run test:focused -- src/domain/javascript/getterReceiverAlias.review.test.ts ``` The public entry command below is provided for a subsequent end-to-end check. It was not run in this verification; its connection to the observed producer and projection was checked in source. ```sh node scripts/rea.mjs analyze-javascript-application getter-alias-input --artifact-format directory --json ``` Locate the graph observation whose semantic_role is export-return-shapes and exported_name is result. Use a stable ordinary UTF-8 file, default source selection, no cancellation and sufficient analysis/transfer capacity. No package metadata or external module resolution is necessary for this export. ## Expected behavior The documented export return-shape analysis retains conservative uncertainty for accessors and aliases. For this source, box.v invokes the getter with box as this; self.s therefore refers to shared, and the assignment writes its x property before the return. That reasoning follows the visible source; it is not a runtime execution observation. A value_status of unknown, or a correctly modeled literal 2, satisfies the contract. A definite literal 1 does not. ## Actual behavior The initialized receiver alias produces the following value in the semantic analyzer and retains it through production module compaction and export projection: ```json { "value": { "status": "literal", "value": 1 }, "static_return_shapes": [ { "value_status": "literal", "fields": [ { "path": "", "presence": "present", "state": "literal", "value": 1, "reason": null } ] } ], "return_shape_coverage": { "status": "complete", "retained_return_sites": 1, "omitted_return_sites": 0, "omitted_fields": 0, "omitted_property_coverage": 0, "projection_complete": true } } ``` This excerpt omits source ranges and other projection metadata. Changing only the getter body to return this.s yields unknown after the same write. Removing the write from the alias case preserves the correct pre-write literal 1. The supplied assignment-alias and destructured-receiver variants also retained literal 1 through the same projection; the supplied getter calling a receiver method retained literal 1 at the semantic analyzer. These observations do not establish their frequency in real applications. The six-case focused observation run passed all six cases. Its assertions deliberately record the current wrong result and controls, rather than claiming a regression fix. An earlier run failed because the verification test queried a nonexistent IR property; correcting that observation check produced the passing runs. No CLI, MCP, isolated analysis worker, installed-package runtime or fixture program was executed. ## Evidence - Expected source: docs/javascript-application-workflows.md:185-203 documents direct return evaluation, alias uncertainty and conservative accessor treatment. src/domain/javascript/javascriptSemanticAnalysis.getter_receivers.test.ts and tests/acceptance/applications/getterReceiverMutationsCli.test.ts corroborate that writes through returned getter references must not publish stale literals. - Failure source: Fresh inert observation tests invoked analyzeJavaScriptSemantics, projectJavaScriptModuleSemantics and projectJavaScriptExportReturnShapes and observed the value/projection above. Relevant receiver translation is src/domain/javascript/javascriptSemanticObjectReferences.ts:310-402; ordinary alias propagation is src/domain/javascript/javascriptSemanticMemberMutations.ts:100-314 and 651-715. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete The runtime observations establish the inert producer, module compaction and export projection results. Public command reachability was checked statically, and the public command was not run. Reproduction is complete at the source-level correctness boundary. The supported public command accepts an ordinary readable directory containing the fixture. Public applicability was checked in source at `src/cli/electronCommands.ts:188`, `src/application/javascript/JavaScriptApplicationService.ts:38` and `src/application/javascript/JavaScriptModuleRelationships.ts:164`; it was not verified by an end-to-end launch. Related prior work is [the closed getter-receiver report](https://github.com/morluto/rea/issues/1665). Its closure verifies direct this, direct receiver-property, private-field and super-getter inputs. It is reference material, not evidence that this getter-local alias case is fixed. Related: #1494. ## Restoration check Reanalyze the exact alias source and require its exported return value to be unknown or correctly modeled as literal 2. Preserve the direct-receiver write control's uncertainty and the no-write control's literal 1. Confirm that the result survives module compaction and appears in public application Evidence. Apply the same check to the observed assignment-alias and destructured-receiver variants. Complete coverage describes retained return sites and must not be used as proof of runtime execution. ## Runtime or environment Source checkout a2d8b94dd50dab8d966239dd63ab84718aa8b4b4, tested with the pinned Node 24.18.0 / npm 11.16.0 toolchain. No claim is made about independently testing the published package or another revision. Current upstream 3ae826ceba6f25b08bce50bcb51001209bc16b57 remains affected by source recheck of the unchanged getter-receiver translation, mutation analysis, module compaction and export projection; no new runtime reproduction was run. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: The demonstrated inputs publish an incorrect static literal through the export return-shape projection. The result can mislead source analysis, but no production incident or severe broader impact was observed. - Workaround status: unverified - Workaround basis: The direct-receiver control avoids this result, but rewriting the analyzed artifact changes its identity and is not an established general workaround for an analyst who needs Evidence about the original input.
bsakaguchi 2026-10-15 18:29:51 +00:00
  • closed this issue
  • added the
    bug
    label
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#954
No description provided.