[GH-ISSUE #2655] fix(javascript): mark shared values uncertain after prototype getter writes #956

Closed
opened 2026-10-15 18:29:52 +00:00 by bsakaguchi · 1 comment
Owner

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

Summary

A function can write to a shared object through a getter inherited from an object-literal prototype, then have its return value described as the object's old constant. The source analysis retains literal 1 after an unconditional write of 2. The exported return-shape projection retains the same obsolete value. This was observed through production source parsing and return projection with inert input.

Steps to reproduce

On the source baseline below, use this JavaScript as the contents of app.js:

export function result() {
  const shared = { x: 1 };
  const proto = { get v() { return shared; } };
  const box = { __proto__: proto };
  box.v.x = 2;
  return shared.x;
}

The following source test reproduces the observed analyzer and export-projection result without executing app.js. Save it as src/domain/javascript/prototypeGetter.review.test.ts in a checkout with its locked dependencies available, then run npm run test:local -- src/domain/javascript/prototypeGetter.review.test.ts. This compact test restates the production calls exercised by the independent focused test; its exact filename and command were not used in that run. Its assertion accepts uncertainty or a correctly recovered value and fails on the observed stale literal.

import { createHash } from "node:crypto";
import { expect, it } from "vitest";
import { analyzeJavaScriptArtifactFiles } from "../../application/javascript/JavaScriptArtifactAnalysis.js";
import { projectJavaScriptModuleSemantics } from "./javascriptModuleSemanticIr.js";
import { projectJavaScriptExportReturnShapes } from "./javascriptExportReturnShapeProjection.js";
import { completeApplicationCoverage } from "./javascriptApplicationEvidenceSchemas.js";

it("preserves uncertainty after an inherited getter result is written", () => {
  const source = `export function result() {
    const shared = { x: 1 };
    const proto = { get v() { return shared; } };
    const box = { __proto__: proto };
    box.v.x = 2;
    return shared.x;
  }`;
  const digest = createHash("sha256").update(source).digest("hex");
  const analysis = analyzeJavaScriptArtifactFiles({
    files: [{
      path: "app.js", container_sha256: digest, sha256: digest,
      bytes: Buffer.byteLength(source), inventory_artifact_id: `art_${digest}`,
      kind: "javascript", unpacked: false,
      text: { included: true, value: source },
    }],
    containers: [], text_bytes_read: Buffer.byteLength(source),
    invalid_utf8_files: 0,
  });
  expect(analysis.parse_failures).toBe(0);
  const ir = analysis.files[0]?.semantic?.ir;
  if (!ir) throw new Error("Missing semantic analysis");
  expect(ir.coverage.status).toBe("complete");
  const module = projectJavaScriptModuleSemantics(ir);
  const link = module.moduleLinks.find(link => link.exportedName === "result");
  if (!link) throw new Error("Missing export");
  const projection = projectJavaScriptExportReturnShapes({
    ir: module, link, modulePath: "app.js",
    baseCoverage: completeApplicationCoverage(),
  });
  if (!projection) throw new Error("Missing return projection");
  console.log(JSON.stringify(projection.properties));
  const value = module.callables.find(c => c.callableId === link.callableId)
    ?.returnSites[0]?.value;
  expect(value?.status === "unknown" ||
    (value?.status === "literal" && value.value === 2)).toBe(true);
});

The supported public workflow is rea analyze-javascript-application <fixture-directory> --artifact-format directory --json. Public-entry-point applicability was checked in source under normal worker/resource admission. The CLI, MCP transport and isolated source worker were not run. The fixture program itself was not executed, and this is not a released-package runtime reproduction.

Expected behavior

An explicit write through a shared getter result must invalidate the earlier constant, or recover the correct new value. This fixture contains a fixed prototype setter, fixed property names, immutable references and a direct return after the write. The getter returns shared, so the write targets shared.x. Existing member-write analysis owns uncertainty for such writes; an unresolved reference must not leave an obsolete constant in a later static return fact. Supporting every prototype shape or recovering runtime execution is not required to restore this contract.

Actual behavior

The independent focused source test found no parser errors and retained an exact link to the exported callable. Both the direct write above and the variation const saved = box.v; saved.x = 2; return shared.x; produced { "status": "literal", "value": 1 }. Module projection preserved the result, and export return-shape projection produced this field:

{ "path": "", "presence": "present", "state": "literal", "value": 1, "reason": null }

The return-shape coverage was complete and projection_complete was true. Those fields describe retained syntax coverage, not executed runtime behavior. General static-analysis limitations are still attached to the observation.

The own-getter mutation control produced unknown. Reading the inherited getter without writing, and writing through an own data property that shadows the inherited getter, each retained literal 1 correctly. One focused test covered these five fixtures and passed by asserting the observed characterization results.

Evidence

  • Expected source: src/domain/javascript/javascriptSemanticMemberMutations.ts:46 owns uncertainty from property writes; docs/tool-design.md:55 requires unknown coverage to remain unknown. javascriptSemanticAnalysis.member_mutations.test.ts and javascriptSemanticAnalysis.getter_receivers.test.ts corroborate the shared getter-result write contract. These controls do not establish a separate promise of complete prototype resolution.
  • Failure source: The independent focused source test exercised production analyzeJavaScriptArtifactFiles, projectJavaScriptModuleSemantics and projectJavaScriptExportReturnShapes and observed the obsolete literal and projected field shown above. Relevant evidence locators are src/domain/javascript/javascriptSemanticObjectReferences.ts:404, src/domain/javascript/javascriptSemanticValues.ts:552 and src/domain/javascript/javascriptExportReturnShapeProjection.ts:24.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

The public directory request and retained export observation are documented by src/application/javascript/JavaScriptApplicationService.ts:32 and src/application/javascript/JavaScriptModuleRelationships.ts:164. Their applicability to the readable UTF-8 fixture was checked in source only. Normal worker/resource admission is required; worker startup, transport and resource-exhaustion behavior were not exercised.

Related closed getter reports are #1494 and #1665. Their existing fixes and confirmation comments address other getter shapes. They are historical references; this report's failure evidence is the independent source test.

Restoration check

Reanalyze the direct-write and saved-alias fixtures. The exported return field must be unknown, or a correctly recovered literal 2, instead of literal 1. Preserve the own-getter mutation control's uncertainty and the no-write and shadowed-property controls' known 1. A blanket invalidation of all getter reads would fail the no-write control. No particular resolver algorithm is prescribed.

Runtime or environment

Source revision a2d8b94dd50dab8d966239dd63ab84718aa8b4b4, Node 24.18.0, npm 11.16.0, locked source dependencies. No installed released runtime was tested.

Current upstream source recheck at 3ae826ceba6f25b08bce50bcb51001209bc16b57 confirms continued presence in the unchanged mutation and return-projection path; no fresh source test, CLI/MCP launch, or released-runtime run was performed.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The assigned, bounded source shape yields an obsolete constant in a retained static return-value fact. The defect changes analysis meaning, but this verification does not demonstrate severe downstream harm or failure of the complete application-analysis workflow.
  • Workaround status: unverified
  • Workaround basis: The passing own-getter control shows a syntax-dependent failure, but rewriting analyzed application source is not established as a practical workaround. No alternative public analysis route was tested for this fixture.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2655 ## Summary A function can write to a shared object through a getter inherited from an object-literal prototype, then have its return value described as the object's old constant. The source analysis retains `literal 1` after an unconditional write of `2`. The exported return-shape projection retains the same obsolete value. This was observed through production source parsing and return projection with inert input. ## Steps to reproduce On the source baseline below, use this JavaScript as the contents of `app.js`: ```js export function result() { const shared = { x: 1 }; const proto = { get v() { return shared; } }; const box = { __proto__: proto }; box.v.x = 2; return shared.x; } ``` The following source test reproduces the observed analyzer and export-projection result without executing `app.js`. Save it as `src/domain/javascript/prototypeGetter.review.test.ts` in a checkout with its locked dependencies available, then run `npm run test:local -- src/domain/javascript/prototypeGetter.review.test.ts`. This compact test restates the production calls exercised by the independent focused test; its exact filename and command were not used in that run. Its assertion accepts uncertainty or a correctly recovered value and fails on the observed stale literal. ```ts import { createHash } from "node:crypto"; import { expect, it } from "vitest"; import { analyzeJavaScriptArtifactFiles } from "../../application/javascript/JavaScriptArtifactAnalysis.js"; import { projectJavaScriptModuleSemantics } from "./javascriptModuleSemanticIr.js"; import { projectJavaScriptExportReturnShapes } from "./javascriptExportReturnShapeProjection.js"; import { completeApplicationCoverage } from "./javascriptApplicationEvidenceSchemas.js"; it("preserves uncertainty after an inherited getter result is written", () => { const source = `export function result() { const shared = { x: 1 }; const proto = { get v() { return shared; } }; const box = { __proto__: proto }; box.v.x = 2; return shared.x; }`; const digest = createHash("sha256").update(source).digest("hex"); const analysis = analyzeJavaScriptArtifactFiles({ files: [{ path: "app.js", container_sha256: digest, sha256: digest, bytes: Buffer.byteLength(source), inventory_artifact_id: `art_${digest}`, kind: "javascript", unpacked: false, text: { included: true, value: source }, }], containers: [], text_bytes_read: Buffer.byteLength(source), invalid_utf8_files: 0, }); expect(analysis.parse_failures).toBe(0); const ir = analysis.files[0]?.semantic?.ir; if (!ir) throw new Error("Missing semantic analysis"); expect(ir.coverage.status).toBe("complete"); const module = projectJavaScriptModuleSemantics(ir); const link = module.moduleLinks.find(link => link.exportedName === "result"); if (!link) throw new Error("Missing export"); const projection = projectJavaScriptExportReturnShapes({ ir: module, link, modulePath: "app.js", baseCoverage: completeApplicationCoverage(), }); if (!projection) throw new Error("Missing return projection"); console.log(JSON.stringify(projection.properties)); const value = module.callables.find(c => c.callableId === link.callableId) ?.returnSites[0]?.value; expect(value?.status === "unknown" || (value?.status === "literal" && value.value === 2)).toBe(true); }); ``` The supported public workflow is `rea analyze-javascript-application <fixture-directory> --artifact-format directory --json`. Public-entry-point applicability was checked in source under normal worker/resource admission. The CLI, MCP transport and isolated source worker were not run. The fixture program itself was not executed, and this is not a released-package runtime reproduction. ## Expected behavior An explicit write through a shared getter result must invalidate the earlier constant, or recover the correct new value. This fixture contains a fixed prototype setter, fixed property names, immutable references and a direct return after the write. The getter returns `shared`, so the write targets `shared.x`. Existing member-write analysis owns uncertainty for such writes; an unresolved reference must not leave an obsolete constant in a later static return fact. Supporting every prototype shape or recovering runtime execution is not required to restore this contract. ## Actual behavior The independent focused source test found no parser errors and retained an exact link to the exported callable. Both the direct write above and the variation `const saved = box.v; saved.x = 2; return shared.x;` produced `{ "status": "literal", "value": 1 }`. Module projection preserved the result, and export return-shape projection produced this field: ```json { "path": "", "presence": "present", "state": "literal", "value": 1, "reason": null } ``` The return-shape coverage was `complete` and `projection_complete` was `true`. Those fields describe retained syntax coverage, not executed runtime behavior. General static-analysis limitations are still attached to the observation. The own-getter mutation control produced `unknown`. Reading the inherited getter without writing, and writing through an own data property that shadows the inherited getter, each retained `literal 1` correctly. One focused test covered these five fixtures and passed by asserting the observed characterization results. ## Evidence - Expected source: `src/domain/javascript/javascriptSemanticMemberMutations.ts:46` owns uncertainty from property writes; `docs/tool-design.md:55` requires unknown coverage to remain unknown. `javascriptSemanticAnalysis.member_mutations.test.ts` and `javascriptSemanticAnalysis.getter_receivers.test.ts` corroborate the shared getter-result write contract. These controls do not establish a separate promise of complete prototype resolution. - Failure source: The independent focused source test exercised production `analyzeJavaScriptArtifactFiles`, `projectJavaScriptModuleSemantics` and `projectJavaScriptExportReturnShapes` and observed the obsolete literal and projected field shown above. Relevant evidence locators are `src/domain/javascript/javascriptSemanticObjectReferences.ts:404`, `src/domain/javascript/javascriptSemanticValues.ts:552` and `src/domain/javascript/javascriptExportReturnShapeProjection.ts:24`. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete The public directory request and retained export observation are documented by `src/application/javascript/JavaScriptApplicationService.ts:32` and `src/application/javascript/JavaScriptModuleRelationships.ts:164`. Their applicability to the readable UTF-8 fixture was checked in source only. Normal worker/resource admission is required; worker startup, transport and resource-exhaustion behavior were not exercised. Related closed getter reports are [#1494](https://github.com/morluto/rea/issues/1494) and [#1665](https://github.com/morluto/rea/issues/1665). Their existing fixes and confirmation comments address other getter shapes. They are historical references; this report's failure evidence is the independent source test. ## Restoration check Reanalyze the direct-write and saved-alias fixtures. The exported return field must be `unknown`, or a correctly recovered literal `2`, instead of literal `1`. Preserve the own-getter mutation control's uncertainty and the no-write and shadowed-property controls' known `1`. A blanket invalidation of all getter reads would fail the no-write control. No particular resolver algorithm is prescribed. ## Runtime or environment Source revision `a2d8b94dd50dab8d966239dd63ab84718aa8b4b4`, Node 24.18.0, npm 11.16.0, locked source dependencies. No installed released runtime was tested. Current upstream source recheck at `3ae826ceba6f25b08bce50bcb51001209bc16b57` confirms continued presence in the unchanged mutation and return-projection path; no fresh source test, CLI/MCP launch, or released-runtime run was performed. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: The assigned, bounded source shape yields an obsolete constant in a retained static return-value fact. The defect changes analysis meaning, but this verification does not demonstrate severe downstream harm or failure of the complete application-analysis workflow. - Workaround status: unverified - Workaround basis: The passing own-getter control shows a syntax-dependent failure, but rewriting analyzed application source is not established as a practical workaround. No alternative public analysis route was tested for this fixture.
bsakaguchi 2026-10-15 18:29:52 +00:00
Author
Owner

@morluto commented on GitHub (Oct 14, 2026):

Addressed on main in 896696b2e761e34c267977bb5c6982c607a622d0.

Object-literal prototype getter references propagate shared write uncertainty; an own property still shadows the inherited getter.

Validation: Affected suites passed in the merged 65-file run (1169 passed, 2 explicit skips), with full static/architecture checks and documentation checks passing. Final installed-package verification is recorded separately in the audit.

Verification scope: Controlled source and production-boundary verification on macOS with pinned Node 24.18.0; real external engines and Windows hosts were not independently rerun. Closure covers the described source defect, not broader provider conformance or performance.

<!-- gh-comment-id:6159195423 --> @morluto commented on GitHub (Oct 14, 2026): Addressed on main in [896696b2e761e34c267977bb5c6982c607a622d0](https://github.com/morluto/rea/commit/896696b2e761e34c267977bb5c6982c607a622d0). Object-literal prototype getter references propagate shared write uncertainty; an own property still shadows the inherited getter. Validation: Affected suites passed in the merged 65-file run (1169 passed, 2 explicit skips), with full static/architecture checks and documentation checks passing. Final installed-package verification is recorded separately in the audit. Verification scope: Controlled source and production-boundary verification on macOS with pinned Node 24.18.0; real external engines and Windows hosts were not independently rerun. Closure covers the described source defect, not broader provider conformance or performance.
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#956
No description provided.