| 1 | // @flow @enableTransitivelyFreezeFunctionExpressions:false @enableNewMutationAliasingModel |
| 2 | import {arrayPush, setPropertyByKey, Stringify} from 'shared-runtime'; |
| 3 | |
| 4 | /** |
| 5 | * Repro of a bug fixed in the new aliasing model. |
| 6 | * |
| 7 | * 1. `InferMutableRanges` derives the mutable range of identifiers and their |
| 8 | * aliases from `LoadLocal`, `PropertyLoad`, etc |
| 9 | * - After this pass, y's mutable range only extends to `arrayPush(x, y)` |
| 10 | * - We avoid assigning mutable ranges to loads after y's mutable range, as |
| 11 | * these are working with an immutable value. As a result, `LoadLocal y` and |
| 12 | * `PropertyLoad y` do not get mutable ranges |
| 13 | * 2. `InferReactiveScopeVariables` extends mutable ranges and creates scopes, |
| 14 | * as according to the 'co-mutation' of different values |
| 15 | * - Here, we infer that |
| 16 | * - `arrayPush(y, x)` might alias `x` and `y` to each other |
| 17 | * - `setPropertyKey(x, ...)` may mutate both `x` and `y` |
| 18 | * - This pass correctly extends the mutable range of `y` |
| 19 | * - Since we didn't run `InferMutableRange` logic again, the LoadLocal / |
| 20 | * PropertyLoads still don't have a mutable range |
| 21 | * |
| 22 | * Note that the this bug is an edge case. Compiler output is only invalid for: |
| 23 | * - function expressions with |
| 24 | * `enableTransitivelyFreezeFunctionExpressions:false` |
| 25 | * - functions that throw and get retried without clearing the memocache |
| 26 | * |
| 27 | * Found differences in evaluator results |
| 28 | * Non-forget (expected): |
| 29 | * (kind: ok) |
| 30 | * <div>{"cb":{"kind":"Function","result":10},"shouldInvokeFns":true}</div> |
| 31 | * <div>{"cb":{"kind":"Function","result":11},"shouldInvokeFns":true}</div> |
| 32 | * Forget: |
| 33 | * (kind: ok) |
| 34 | * <div>{"cb":{"kind":"Function","result":10},"shouldInvokeFns":true}</div> |
| 35 | * <div>{"cb":{"kind":"Function","result":10},"shouldInvokeFns":true}</div> |
| 36 | */ |
| 37 | function useFoo({a, b}: {a: number, b: number}) { |
| 38 | const x = []; |
| 39 | const y = {value: a}; |
| 40 | |
| 41 | arrayPush(x, y); // x and y co-mutate |
| 42 | const y_alias = y; |
| 43 | const cb = () => y_alias.value; |
| 44 | setPropertyByKey(x[0], 'value', b); // might overwrite y.value |
| 45 | return <Stringify cb={cb} shouldInvokeFns={true} />; |
| 46 | } |
| 47 | |
| 48 | export const FIXTURE_ENTRYPOINT = { |
| 49 | fn: useFoo, |
| 50 | params: [{a: 2, b: 10}], |
| 51 | sequentialRenders: [ |
| 52 | {a: 2, b: 10}, |
| 53 | {a: 2, b: 11}, |
| 54 | ], |
| 55 | }; |