Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
733c160d4f |
No files matched your search
@@ -665,3 +665,24 @@ describe("references captured before an intermediate rebind", () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("self aliases in front of copied-slot selections", () => {
|
||||||
|
// Normalization keeps copied-slot selections exact, so a self alias in front
|
||||||
|
// of one must escape its repeated prefix instead of growing the path.
|
||||||
|
it.each([
|
||||||
|
["seed.opts.inner.token", "unknown"],
|
||||||
|
["seed.value.opts.inner.token", "unknown"],
|
||||||
|
["seed.keep", "literal"],
|
||||||
|
])(
|
||||||
|
"escapes the repeated prefix: %s",
|
||||||
|
(read, status) => {
|
||||||
|
const start = performance.now();
|
||||||
|
const value = resultValue(
|
||||||
|
`const seed = { keep: 3, opts: { inner: { token: 1 } }, value: { opts: { inner: { token: 2 } } } }; let e = flag ? seed : seed.value; const t = { ...e.opts }; e = e.value; consume(t); return ${read};`,
|
||||||
|
);
|
||||||
|
expect(performance.now() - start).toBeLessThan(2000);
|
||||||
|
expect(value?.status).toBe(status);
|
||||||
|
},
|
||||||
|
30000,
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -385,7 +385,11 @@ export function* collectSemanticMemberMutationsSteps(
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
repeatedPrefix !== undefined &&
|
repeatedPrefix !== undefined &&
|
||||||
observablePaths.repeatsUnpruned(binding.bindingId, repeatedPrefix)
|
observablePaths.repeatsUnpruned(
|
||||||
|
binding.bindingId,
|
||||||
|
repeatedPrefix,
|
||||||
|
current.path,
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
markValue(
|
markValue(
|
||||||
current.node,
|
current.node,
|
||||||
@@ -1066,6 +1070,10 @@ interface MutationObserverNode {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Copied-slot selections (from spreads and rests) stay exact path keys. */
|
||||||
|
const carriesSelection = (path: PropertyPath): boolean =>
|
||||||
|
path.some((key) => key !== null && typeof key === "object");
|
||||||
|
|
||||||
const observableMutationPaths = (
|
const observableMutationPaths = (
|
||||||
state: JavaScriptSemanticAnalysisState,
|
state: JavaScriptSemanticAnalysisState,
|
||||||
): {
|
): {
|
||||||
@@ -1077,6 +1085,7 @@ const observableMutationPaths = (
|
|||||||
readonly repeatsUnpruned: (
|
readonly repeatsUnpruned: (
|
||||||
bindingId: string,
|
bindingId: string,
|
||||||
prefix: PropertyPath,
|
prefix: PropertyPath,
|
||||||
|
path: PropertyPath,
|
||||||
) => boolean;
|
) => boolean;
|
||||||
} => {
|
} => {
|
||||||
const bindingNodes = new Map<string, MutationObserverNode>();
|
const bindingNodes = new Map<string, MutationObserverNode>();
|
||||||
@@ -1299,9 +1308,7 @@ const observableMutationPaths = (
|
|||||||
effect: ValueEffect,
|
effect: ValueEffect,
|
||||||
): PropertyPath | null => {
|
): PropertyPath | null => {
|
||||||
let observer = bindingNodes.get(bindingId);
|
let observer = bindingNodes.get(bindingId);
|
||||||
if (observer === undefined) return path;
|
if (observer === undefined || carriesSelection(path)) return path;
|
||||||
if (path.some((key) => key !== null && typeof key === "object"))
|
|
||||||
return path;
|
|
||||||
if (effect === "escape") {
|
if (effect === "escape") {
|
||||||
observer = project(observer, path);
|
observer = project(observer, path);
|
||||||
settle();
|
settle();
|
||||||
@@ -1323,11 +1330,15 @@ const observableMutationPaths = (
|
|||||||
// repeating a prefix revisits an origin set, every longer repetition stays
|
// repeating a prefix revisits an origin set, every longer repetition stays
|
||||||
// observable and normalization can never prune it. The answer depends only
|
// observable and normalization can never prune it. The answer depends only
|
||||||
// on the binding and prefix, so every revisit of a long chain shares it.
|
// on the binding and prefix, so every revisit of a long chain shares it.
|
||||||
|
// Normalization keeps copied-slot selections exact, so it never prunes a
|
||||||
|
// path carrying one, however often a prefix repeats in front of it.
|
||||||
const repeated = new Map<string, boolean>();
|
const repeated = new Map<string, boolean>();
|
||||||
const repeatsUnpruned = (
|
const repeatsUnpruned = (
|
||||||
bindingId: string,
|
bindingId: string,
|
||||||
prefix: PropertyPath,
|
prefix: PropertyPath,
|
||||||
|
path: PropertyPath,
|
||||||
): boolean => {
|
): boolean => {
|
||||||
|
if (carriesSelection(path)) return true;
|
||||||
const key = JSON.stringify([bindingId, prefix]);
|
const key = JSON.stringify([bindingId, prefix]);
|
||||||
const cached = repeated.get(key);
|
const cached = repeated.get(key);
|
||||||
if (cached !== undefined) return cached;
|
if (cached !== undefined) return cached;
|
||||||
|
|||||||
Reference in new issue
Block a user