fix(javascript): preserve runtime path bytes and Electron ownership (#4941)

* fix(javascript): preserve literal percent runtime paths

* fix(javascript): ignore resolve prefixes before the final anchor

* fix(electron): retain application roots for loadFile declarations

* fix(electron): resolve loadFile from enclosing application package

* fix(electron): retain outer application root across package scopes

* fix(electron): derive application ownership from inventoried packages

* fix(electron): retain resolved package entrypoint ownership

* fix(electron): preserve competing application ownership

* fix(electron): retain invalid and omitted runtime ownership

* fix(electron): preserve omitted module ownership and runtime paths

* fix(electron): preserve proven roots before confining page paths

* fix(electron): retain possible owners behind missing imports

* fix(electron): distinguish absent imports and owned page candidates

* fix(electron): keep unavailable JSON out of owner gaps

* fix(electron): retain exact loader formats and default entry status

* fix(electron): retain unavailable entrypoint ownership candidates

* test(ghidra): preserve Windows Node probe prerequisites

* test(ghidra): assert cancellation during profile shutdown

* fix(electron): separate examined imports from frontend ownership

* fix(electron): retain unavailable directory entrypoint owners

* test(jeb): retain Linux ownership prerequisite in isolation

* fix(javascript): retain unchecked loader ownership and literal main paths

* fix(javascript): preserve runtime paths and call-site module aliases

* fix(javascript): bound module alias traversal and preserve projections

* fix(javascript): retain host module and nested artifact path boundaries

* fix(javascript): preserve ESM and archive module loader provenance

* test(javascript): distinguish unmapped containers from missing exports

* fix: retain default module aliases and Electron builtin imports

* fix: preserve host file URL and inward ASAR ownership

* fix: preserve CommonJS file URL classification in CI fixtures

* fix: retain bound module loaders and Windows entrypoint uncertainty

* fix: retain ASAR uncertainty and invalidate changed Electron app roots

* fix(javascript): retain ownership uncertainty across loader boundaries

* fix(javascript): preserve loader sources and bound alias queries

* fix(javascript): retain destructured loader sources

* fix(javascript): preserve loader uncertainty at resolution boundaries

* fix(javascript): preserve callable and package launch ownership

* fix(javascript): preserve captured values across member reads

* test(process): assert tagged filesystem cancellation failures

* fix(javascript): preserve inventory bases and invocation read timing

* fix(javascript): preserve ownership across indirect and unread loads

* test(javascript): order fixture directory creation on Windows

* test(javascript): share ordered filesystem fixture writer

* fix(javascript): unify loader provenance and package validation

* fix(javascript): preserve captured Node require loader values

* fix(javascript): preserve loader invocation authority

* fix(javascript): verify generic loader invocation authority

* fix(javascript): preserve conditional loader method alternatives

* fix(javascript): require proven factories for source association

* fix(javascript): honor replaced Electron app path setters

* fix(javascript): prove path and assignment source ownership

* fix(javascript): retain native delegate eligibility through shared writes

* fix(process): settle stale baseline errors after confirmed absence

* fix(javascript): preserve assignment and path boundary evidence

* fix(javascript): prove source-directory resolve anchors

* fix: preserve application path provenance and portable schemas

* fix(javascript): retain CommonJS exported setter effects

* fix: isolate Linux identity observations and retain native module exports

* fix(javascript): preserve published app and helper provenance

* fix(javascript): normalize typed stores and native path fixtures
This commit is contained in:
ShuaixinHuang
2026-10-20 17:25:19 +08:00
committed by GitHub
parent d532b18123
commit e9fc8fb30c
149 changed files with 16881 additions and 988 deletions
@@ -1,9 +1,14 @@
import type { JavaScriptOpaqueScopeEffects } from "./javascriptSemanticOpaqueEffects.js";
import type { SemanticIterationSource } from "./javascriptSemanticIterationSources.js";
import * as t from "@babel/types";
import {
semanticStaticPropertyKey,
unwrapJavaScriptExpression,
} from "./javascriptAstValues.js";
import {
semanticBindingAtRead,
semanticEffectTiming,
} from "./javascriptSemanticMutationInitializers.js";
import type {
JavaScriptSemanticPropertyPath,
@@ -29,10 +34,16 @@ export interface JavaScriptSemanticInitializer {
readonly node: t.Node;
readonly projection: readonly (string | number | null)[];
readonly entryBody?: t.BlockStatement;
readonly iterationSources?: readonly {
readonly node: t.Node;
readonly projection: JavaScriptSemanticPropertyPath;
}[];
}
/** Additional references from destructuring copies and possible defaults. */
interface JavaScriptSemanticReferenceInitializer extends JavaScriptSemanticInitializer {
/** This source is a value replacement, rather than a declaration fallback. */
readonly assignment?: true;
readonly copyKind?: "object-rest" | "array-rest";
readonly copyProjectionOffset?: number;
readonly copyExcludedKeys?: readonly string[];
@@ -45,6 +56,12 @@ interface JavaScriptSemanticReferenceInitializer extends JavaScriptSemanticIniti
export interface JavaScriptSemanticMemberEffect {
readonly path: JavaScriptSemanticPropertyPath;
readonly node: t.Node | undefined;
/** Selected RHS values retained by the shared pattern-store owner. */
readonly assignedSources?: readonly SemanticIterationSource[];
/** Binding values whose receiver identities were selected for this effect. */
readonly receiverInitializers?: readonly JavaScriptSemanticInitializer[];
/** Capture of this binding used while following an aliased effect receiver. */
readonly receiverAt?: t.Node;
}
export type JavaScriptSemanticEffectTiming = "before" | "after" | "unknown";
@@ -101,6 +118,7 @@ export interface JavaScriptSemanticScopeState {
/** Shared state for semantic collection and evaluation. */
export interface JavaScriptSemanticAnalysisState {
arrayIterationUnknown: boolean;
readonly program: t.Program;
readonly opaqueEffects: JavaScriptOpaqueScopeEffects;
/** Effective loader for static imports in this source artifact. */
@@ -150,6 +168,8 @@ export interface JavaScriptSemanticAnalysisState {
readonly ambientWrites: SemanticPropertyPathCoverage;
/** Ambient object identities passed to calls whose effects are not evaluated. */
readonly ambientEscapes: SemanticPropertyPathCoverage;
/** Capture sites keep later opaque effects from changing earlier copies. */
readonly ambientEscapeEffects: JavaScriptSemanticMemberEffect[];
/** Source sites distinguish replacement before an ambient identity capture from later writes. */
readonly ambientWriteEffects: JavaScriptSemanticMemberEffect[];
}
@@ -537,7 +557,52 @@ export const isUnmodifiedCommonJsWrapperBinding = (
state: JavaScriptSemanticAnalysisState,
): boolean => {
const binding = commonJsWrapperBindingDeclaration(node, name, state);
return binding !== undefined && binding.initializers.length === 0;
return (
binding !== undefined &&
!semanticBindingHasPriorValueWrite(node, binding, state)
);
};
/** Retained value and pattern stores share one source-ordered write boundary. */
export const semanticBindingHasPriorValueWrite = (
site: t.Node,
binding: JavaScriptSemanticBindingState,
state: JavaScriptSemanticAnalysisState,
): boolean => {
if (binding.definitions.some(({ kind }) => kind === "function")) return true;
const writes = state.bindingWrites.get(binding.bindingId) ?? [];
const read = semanticBindingAtRead(binding, site, state.parentsByNode);
const retained =
read.status === "ordered"
? new Set([
...read.binding.initializers,
...read.binding.referenceInitializers,
])
: undefined;
const representedWrites = new Set<t.Node>();
const retainedWrites = new Set<t.Node>();
for (const source of [
...binding.initializers,
...binding.referenceInitializers,
]) {
let owner: t.Node | undefined = source.node;
while (owner !== undefined && !writes.includes(owner))
owner = state.parentsByNode.get(owner);
if (owner !== undefined) {
representedWrites.add(owner);
if (retained === undefined || retained.has(source))
retainedWrites.add(owner);
} else if (
(retained === undefined || retained.has(source)) &&
semanticEffectTiming(site, source.node, state) !== "after"
)
return true;
}
return writes.some(
(write) =>
(!representedWrites.has(write) || retainedWrites.has(write)) &&
semanticEffectTiming(site, write, state) !== "after",
);
};
/** Program `var` redeclarations retain wrapper identity until a source write. */
@@ -550,7 +615,9 @@ export const commonJsWrapperBindingDeclaration = (
// specifier resolution mode does not establish that distinction.
if (
state.program.sourceType !== "script" ||
!["module", "exports", "require"].includes(name) ||
!["module", "exports", "require", "__dirname", "__filename"].includes(
name,
) ||
!t.isIdentifier(node, { name })
)
return undefined;