mirror of
https://github.com/morluto/rea.git
synced 2026-11-06 06:46:10 +00:00
fix(javascript): retain proven browser global receivers
This commit is contained in:
1 parent
673a659074
commit
45d0c3e299
5 files changed
+20
-4
No files matched your search
@@ -736,12 +736,21 @@ export function* indexJavaScriptAmbientWritesSteps(
|
||||
retainSemanticAmbientEffect(state, "write", resolved, node);
|
||||
}
|
||||
}
|
||||
for (const { node, target, browserReceiver, projection } of escapes)
|
||||
for (const path of ambientWrites(target, node, true, projection).paths)
|
||||
for (const { node, target, browserReceiver, projection } of escapes) {
|
||||
const resolved = ambientWrites(target, node, true, projection);
|
||||
if (
|
||||
browserReceiver &&
|
||||
resolved.ownership === "ambient" &&
|
||||
resolved.paths.length > 0 &&
|
||||
resolved.paths.every((path) => path.length === 1 && path[0] === "global")
|
||||
)
|
||||
state.retainedGlobalCallReceivers.add(target);
|
||||
for (const path of resolved.paths)
|
||||
// Only proven browser APIs keep their global receiver from user code.
|
||||
if (!(browserReceiver && path.length === 1 && path[0] === "global")) {
|
||||
retainSemanticAmbientEffect(state, "escape", path, node);
|
||||
}
|
||||
}
|
||||
// A function may call a browser method replaced later in source order.
|
||||
if (
|
||||
[...browserReceiverMethods].some((method) =>
|
||||
|
||||
@@ -1055,6 +1055,7 @@ const createState = (
|
||||
ambientBindingWrites: new Map(),
|
||||
ambientWrites: new SemanticPropertyPathCoverage(),
|
||||
escapingReferenceStores: new WeakSet(),
|
||||
retainedGlobalCallReceivers: new WeakSet(),
|
||||
ambientEscapes: new SemanticPropertyPathCoverage(),
|
||||
ambientEscapeEffects: [],
|
||||
ambientWriteEffects: [],
|
||||
|
||||
@@ -1445,8 +1445,9 @@ export function* collectSemanticMemberMutationsSteps(
|
||||
)
|
||||
return;
|
||||
if (
|
||||
t.isMemberExpression(expression) ||
|
||||
t.isOptionalMemberExpression(expression)
|
||||
(t.isMemberExpression(expression) ||
|
||||
t.isOptionalMemberExpression(expression)) &&
|
||||
!state.retainedGlobalCallReceivers.has(expression.object)
|
||||
)
|
||||
markEscaped(expression.object, mutation);
|
||||
};
|
||||
|
||||
@@ -171,6 +171,8 @@ export interface JavaScriptSemanticAnalysisState {
|
||||
readonly ambientBindingWrites: Map<string, t.Node[]>;
|
||||
/** Source-local writes, tagged by global, import.meta or CJS module identity. */
|
||||
readonly ambientWrites: SemanticPropertyPathCoverage;
|
||||
/** Browser receivers proven to be only the ambient global identity. */
|
||||
readonly retainedGlobalCallReceivers: WeakSet<t.Node>;
|
||||
/** Store targets whose ownership is unknown or ambient, as classified by ambient indexing. */
|
||||
readonly escapingReferenceStores: WeakSet<t.MemberExpression>;
|
||||
/** Ambient object identities passed to calls whose effects are not evaluated. */
|
||||
|
||||
@@ -34,6 +34,7 @@ it.each([
|
||||
'function inspect() { const records = new Map(); return records.get("/accounts"); }',
|
||||
'const inspect = () => { const records = new Map(); const alias = records; return alias.delete("/accounts"); }',
|
||||
'const browser = window; browser.open("about:blank"); new Map().get("/accounts")',
|
||||
'const records = new Map(); const browser = window; browser.open("about:blank"); records.get("/accounts")',
|
||||
'const box = {}; box.value = { name: "Alice" }; inspect(box); new Map().get("/accounts")',
|
||||
'patch(() => ({ name: "Alice" })); new Map().get("/accounts")',
|
||||
'const box = {}; box.prototype = Map.prototype; new Map().get("/accounts")',
|
||||
@@ -54,6 +55,8 @@ it.each([
|
||||
|
||||
it.each([
|
||||
'client.get("/accounts")',
|
||||
'const records = new Map(); const browser = window; browser.open(window); records.get("/accounts")',
|
||||
'const records = new Map(); function invoke() { window.open(); } window.open = patch; invoke(); records.get("/accounts")',
|
||||
'Object.getPrototypeOf = () => Map.prototype; Object.getPrototypeOf({}).get = fetch; new Map().get("/accounts")',
|
||||
'Reflect.getPrototypeOf = () => Map.prototype; Reflect.getPrototypeOf({}).get = fetch; new Map().get("/accounts")',
|
||||
'const records = new Map(); records.get = request; records.get("/accounts")',
|
||||
|
||||
Reference in new issue
Block a user