mirror of
https://github.com/morluto/rea
synced 2026-10-26 14:25:49 -04:00
perf(javascript): prune typed-array element reference escapes (#5139)
* perf(javascript): prune typed-array element reference escapes * fix(javascript): retain references from typed-array element properties --------- Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
import * as t from "@babel/types";
|
||||
import { isIntrinsicMapLookup } from "./javascriptSemanticMapOperations.js";
|
||||
import {
|
||||
isIntrinsicTypedArrayConstruction,
|
||||
isTypedArrayNumericIndex,
|
||||
} from "./javascriptSemanticTypedArrays.js";
|
||||
|
||||
import {
|
||||
semanticGlobalProcessOrigin,
|
||||
@@ -1218,6 +1222,8 @@ type MutationObservableOrigin =
|
||||
| "ambient"
|
||||
| "array"
|
||||
| "array-prototype"
|
||||
| "typed-array"
|
||||
| "typed-array-element"
|
||||
| "function"
|
||||
| "opaque";
|
||||
|
||||
@@ -1229,6 +1235,7 @@ interface MutationObserverNode {
|
||||
readonly read?: {
|
||||
readonly source: MutationObserverNode;
|
||||
readonly key: PropertyPath[number];
|
||||
readonly numericIndex: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1283,15 +1290,16 @@ const observableMutationPaths = (
|
||||
const project = (
|
||||
source: MutationObserverNode,
|
||||
path: PropertyPath,
|
||||
numericIndex = false,
|
||||
): MutationObserverNode => {
|
||||
let observer = source;
|
||||
for (const key of path) {
|
||||
const identity = JSON.stringify(
|
||||
const identity = `${numericIndex ? "numeric:" : ""}${JSON.stringify(
|
||||
key === null || typeof key === "object" ? key : String(key),
|
||||
);
|
||||
)}`;
|
||||
let selected = observer.projections?.get(identity);
|
||||
if (selected === undefined) {
|
||||
selected = create({ source: observer, key });
|
||||
selected = create({ source: observer, key, numericIndex });
|
||||
(observer.projections ??= new Map()).set(identity, selected);
|
||||
(observer.dependents ??= new Set()).add(selected);
|
||||
schedule(selected);
|
||||
@@ -1352,6 +1360,11 @@ const observableMutationPaths = (
|
||||
// Keep class/getter projections distinct so cycle pruning does not
|
||||
// discard observable escapes through a shared getter result.
|
||||
if (
|
||||
t.isNewExpression(expression) &&
|
||||
isIntrinsicTypedArrayConstruction(expression, state)
|
||||
)
|
||||
(observer.origins ??= new Set()).add("typed-array");
|
||||
else if (
|
||||
t.isNewExpression(expression) &&
|
||||
semanticClassCandidates(expression.callee, state).length === 0
|
||||
)
|
||||
@@ -1383,12 +1396,19 @@ const observableMutationPaths = (
|
||||
t.isOptionalMemberExpression(expression)
|
||||
)
|
||||
link(
|
||||
project(expressionNode(expression.object), [
|
||||
semanticStaticPropertyKey(
|
||||
project(
|
||||
expressionNode(expression.object),
|
||||
[
|
||||
semanticStaticPropertyKey(
|
||||
expression.property,
|
||||
expression.computed,
|
||||
),
|
||||
],
|
||||
isTypedArrayNumericIndex(
|
||||
expression.property,
|
||||
expression.computed,
|
||||
),
|
||||
]),
|
||||
),
|
||||
observer,
|
||||
);
|
||||
else
|
||||
@@ -1416,16 +1436,29 @@ const observableMutationPaths = (
|
||||
for (const source of observer.sources ?? [])
|
||||
for (const origin of source.origins ?? []) add(origin);
|
||||
if (observer.read !== undefined) {
|
||||
const { source, key } = observer.read;
|
||||
const { source, key, numericIndex } = observer.read;
|
||||
if (key === null || typeof key === "object") {
|
||||
// Dynamic keys and copied-slot masks retain their original paths.
|
||||
// Enumerating their exclusion combinations would recreate the growth
|
||||
// that the mutation collector's selection union already prevents.
|
||||
if ((source.origins?.size ?? 0) > 0) add("opaque");
|
||||
for (const origin of source.origins ?? [])
|
||||
add(
|
||||
origin === "typed-array" && numericIndex
|
||||
? "typed-array-element"
|
||||
: "opaque",
|
||||
);
|
||||
} else
|
||||
for (const origin of source.origins ?? []) {
|
||||
if (typeof origin === "string") {
|
||||
if (origin === "array-prototype" || origin === "opaque")
|
||||
if (origin === "typed-array") {
|
||||
// Custom properties can hold references; only canonical numeric
|
||||
// indices have the native Number/BigInt/undefined result contract.
|
||||
add(numericIndex ? "typed-array-element" : "opaque");
|
||||
} else if (origin === "typed-array-element") {
|
||||
// Number/BigInt property reads can expose constructors, methods
|
||||
// or objects returned by prototype getters after boxing.
|
||||
add("opaque");
|
||||
} else if (origin === "array-prototype" || origin === "opaque")
|
||||
add(origin);
|
||||
else if (
|
||||
origin === "ambient" &&
|
||||
@@ -1477,7 +1510,12 @@ const observableMutationPaths = (
|
||||
if (effect === "escape") {
|
||||
observer = project(observer, path);
|
||||
settle();
|
||||
return (observer.origins?.size ?? 0) === 0 ? null : path;
|
||||
const origins = observer.origins;
|
||||
return origins === undefined ||
|
||||
origins.size === 0 ||
|
||||
(origins.size === 1 && origins.has("typed-array-element"))
|
||||
? null
|
||||
: path;
|
||||
}
|
||||
if ((observer.origins?.size ?? 0) === 0)
|
||||
return path.length === 0 ? path : [null];
|
||||
|
||||
Reference in New Issue
Block a user