Skip to content

fix(javascript): resolve module-level Electron options in functions - #8331

Closed
ShuaixinHuang wants to merge 6 commits into
morluto:mainfrom
ShuaixinHuang:codex/fix-electron-module-options
Closed

ShuaixinHuang wants to merge 6 commits into
morluto:mainfrom
ShuaixinHuang:codex/fix-electron-module-options

Conversation

@ShuaixinHuang

@ShuaixinHuang ShuaixinHuang commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Supersedes #8250 at the author's explicit request to resubmit the existing PRs. This replacement reuses the identical published head cc2bb1242a5611527bfb99935bc7dbcf85e902f7 and full three-file patch. Prior discussion and historical verification remain at #8250 (earlier #8087). No code changes or new local test runs were made for this resubmission. This new PR requires its own hosted CI. The owner has specifically confirmed review of this replacement PR's final diff, evidence, limitations and unresolved P1, accepted full responsibility, and approved it for initial maintainer review. The P1 remains a before-merge blocker.

Outstanding review follow-up — not fixed in this published revision

The known P1 review finding remains unresolved in this head. In a classic script, an early globalThis.createWindow() followed by a later bare call can make module-constant initialization appear definite even though the early activation encounters the temporal dead zone. The previously reported 115-test verification below does not establish that this additional case is fixed.

A separate local draft follow-up has 67 focused tests and three build tasks passing, plus check:fast; expanded verification and commit/push are not complete. That draft is not part of this PR's published commit or diff, and those draft results are not claimed as a fix in this revision. This finding remains a before-merge blocker; the original review is preserved rather than marked resolved.

Supersedes #8087 at the author's request. This replacement reuses verified head cc2bb12 and preserves the original discussion and validation record at #8087. Historical revision-specific validation below remains at its stated execution points. This new PR must pass its own hosted CI.

Problem and resulting behavior

Fixes #7227. Electron static analysis loses module-level immutable webPreferences read inside a later activation. Retain those settings when initialization precedes the function's module-level uses, while preserving uncertainty when a hoisted function can run earlier.

For example, this source must remain dynamic rather than report a proven nodeIntegration: true:

const { BrowserWindow } = require("electron");
createWindow();
const webPreferences = { nodeIntegration: true };
function createWindow() {
  return new BrowserWindow({ webPreferences });
}

Moving the call after the const initializer yields object-literal with the literal setting, including when the function declaration appears before the initializer.

Owning change and scope

Initialization coverage stays in javascriptSemanticMutationInitializers.ts; Electron consumers use that fact without reinterpreting declaration order. A hoisted declaration's position does not prove activation timing. The owner indexes module-level function references once per immutable module body and treats earlier aliases, indirect activations, live exports and unproven timing conservatively. This is a bounded reference-order check, not a general call graph or runtime execution proof.

The existing ancestry cache also retains the enclosing function, avoiding repeated walks from deep reads. Initialization coverage remains separate from cross-function capture ordering: the latter stays unresolved so named child-process argv shapes retain their existing uncertainty. No public schema, provider, execution authority, deadline, resource ceiling or assertion was weakened.

Validation at the current follow-up

Windows, Node 24.22.1 / npm 11.16.0:

  • Exact preceding head fb2532ad61ceb4cce7d233131fc1edd4305925ff, with the new source regressions: 4 failed / 47 passed. Three cases expose early direct, aliased and forwarding calls; the fourth exposes a late-call positive control.
  • Inert Node VM controls reproduce a temporal-dead-zone ReferenceError for the three early calls. They substitute a stub BrowserWindow and do not run Electron.
  • Final committed follow-up 9bafc838629358fb8ebc0ce86abefa5c82c8a0ab: 112/112 passed across six complete files: Electron options 51, existing scope-depth/resource controls 14, binding initializers 13, argument shapes 30, existing child-process compiled CLI/stdio MCP 2, and new Electron initialization compiled CLI/stdio MCP 2. Three build prerequisite tasks passed.
  • Both public adapters report dynamic/empty preferences for the early-call source and object-literal/literal true for the late-call source. Source ranges distinguish the two files. Fixtures remain inert.
  • Test discovery passed: all 2,866 test files discovered exactly once. Normal precommit passed; typecheck/lint passed 2/2. Normal prepush typecheck/lint passed 2/2, 26.986 seconds; the normal fast-forward push to this PR's existing fork branch completed.

The earlier head's independent hosted CI passed, but it did not cover this hoisted-declaration review case. The new head requires its own hosted CI; an earlier green run does not validate it. Local checks use synthetic JavaScript through production static analysis and real local compiled CLI/stdio MCP. No real Electron application or Linux/macOS execution is claimed.

Loop ownership review follow-up

Commit cc2bb1242a5611527bfb99935bc7dbcf85e902f7 keeps enclosing-function identity in its own immutable ancestry cache. Loop ordering remains uncertain; entering a loop no longer erases which function owns the read. This retains initialized module-level Electron settings inside for-of, for and while loops while keeping early calls dynamic. No downstream reinterpretation or call graph expansion was added.

On exact preceding head 9bafc838, the three new loop source cases fail while the existing 51 pass. With the correction, six complete files pass 115/115 (Electron 54, scope-depth 14, initializers 13, shapes 30, existing child-process CLI/MCP 2, Electron initialization CLI/MCP 2). The public adapter cases include both early and late calls inside loops and outside loops, with source provenance. All three committed files match the tested bytes exactly after normal precommit hooks. Three build tasks passed; normal prepush typecheck/lint passed 2/2, 46.166 seconds. No assertions or resource controls were relaxed.

The earlier 9bafc838 own hosted workflow 41009833080 completed successfully; the new cc2bb124 head requires independent hosted validation. Validation uses synthetic inert JavaScript and real local compiled CLI/stdio MCP on Windows, Node 24.22.1 / npm 11.16.0; no real Electron application or Linux/macOS run is claimed.

Author readiness

Human owner: @ShuaixinHuang

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths. The known early-global-call P1 below still needs its published regression/fix.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-31T15:26:24.514182Z
We couldn’t complete this review. Comment @codex review to try again.
cc2bb12 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@greptile-apps

greptile-apps Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] The change reaches shared JavaScript static reports through a narrowly gated binding rule.

Fix the receiver check before merging so unrelated member calls do not erase known Electron settings.

Findings

  1. P1 Known Electron settings disappear ▶
Fix with agent prompt
### Issue 1
src/domain/javascript/javascriptSemanticMutationInitializers.ts:893-900
The new check treats locally bound `window`, `self`, and `globalThis` objects as the global object. For example, `const window = { createWindow() {} }; window.createWindow();` before the preferences initializer is unrelated to the module's `createWindow` function. If the real function is called after initialization, this unrelated member call still becomes its first recorded read. REA then drops the known preferences and reports `dynamic`.

Check the receiver's binding before counting its property access as a reference to the module function.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

The PR resolves module-level Electron options inside functions while keeping uncertain initialization unknown.

  • Electron analysis resolves webPreferences after module initialization.

Reviews (2) · Last reviewed commit: "fix(javascript): account for classic glo..." · Reviewed by Greptile

Comment on lines +893 to +900
if (
t.isThisExpression(receiver) ||
(t.isIdentifier(receiver) &&
(receiver.name === "globalThis" ||
receiver.name === "window" ||
receiver.name === "self"))
)
name = semanticStaticPropertyKey(node.property, node.computed);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Known Electron settings disappear

The new check treats locally bound window, self, and globalThis objects as the global object. For example, const window = { createWindow() {} }; window.createWindow(); before the preferences initializer is unrelated to the module's createWindow function. If the real function is called after initialization, this unrelated member call still becomes its first recorded read. REA then drops the known preferences and reports dynamic.

Check the receiver's binding before counting its property access as a reference to the module function.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/domain/javascript/javascriptSemanticMutationInitializers.ts
Line: 893-900

Comment:
**Known Electron settings disappear**

The new check treats locally bound `window`, `self`, and `globalThis` objects as the global object. For example, `const window = { createWindow() {} }; window.createWindow();` before the preferences initializer is unrelated to the module's `createWindow` function. If the real function is called after initialization, this unrelated member call still becomes its first recorded read. REA then drops the known preferences and reports `dynamic`.

Check the receiver's binding before counting its property access as a reference to the module function.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Module-level webPreferences constants are reported as dynamic when the window is created inside a function

1 participant