forked from facebook/react
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Repro function expr hoisting (facebook#29615)
Modified version of @mofeiZ's facebook#29232 with CI passing (had to run prettier) --------- Co-authored-by: Mofei Zhang <feifei0@meta.com>
- Loading branch information
Showing
6 changed files
with
291 additions
and
0 deletions.
There are no files selected for viewing
93 changes: 93 additions & 0 deletions
93
...ler/src/__tests__/fixtures/compiler/bug-invalid-hoisting-functionexpr.expect.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
|
||
## Input | ||
|
||
```javascript | ||
import { Stringify } from "shared-runtime"; | ||
|
||
/** | ||
* We currently hoist the accessed properties of function expressions, | ||
* regardless of control flow. This is simply because we wrote support for | ||
* function expressions before doing a lot of work in PropagateScopeDeps | ||
* to handle conditionally accessed dependencies. | ||
* | ||
* Current evaluator error: | ||
* Found differences in evaluator results | ||
* Non-forget (expected): | ||
* (kind: ok) <div>{"shouldInvokeFns":true,"callback":{"kind":"Function","result":null}}</div> | ||
* Forget: | ||
* (kind: exception) Cannot read properties of null (reading 'prop') | ||
*/ | ||
function Component({ obj, isObjNull }) { | ||
const callback = () => { | ||
if (!isObjNull) { | ||
return obj.prop; | ||
} else { | ||
return null; | ||
} | ||
}; | ||
return <Stringify shouldInvokeFns={true} callback={callback} />; | ||
} | ||
|
||
export const FIXTURE_ENTRYPOINT = { | ||
fn: Component, | ||
params: [{ obj: null, isObjNull: true }], | ||
}; | ||
|
||
``` | ||
|
||
## Code | ||
|
||
```javascript | ||
import { c as _c } from "react/compiler-runtime"; | ||
import { Stringify } from "shared-runtime"; | ||
|
||
/** | ||
* We currently hoist the accessed properties of function expressions, | ||
* regardless of control flow. This is simply because we wrote support for | ||
* function expressions before doing a lot of work in PropagateScopeDeps | ||
* to handle conditionally accessed dependencies. | ||
* | ||
* Current evaluator error: | ||
* Found differences in evaluator results | ||
* Non-forget (expected): | ||
* (kind: ok) <div>{"shouldInvokeFns":true,"callback":{"kind":"Function","result":null}}</div> | ||
* Forget: | ||
* (kind: exception) Cannot read properties of null (reading 'prop') | ||
*/ | ||
function Component(t0) { | ||
const $ = _c(5); | ||
const { obj, isObjNull } = t0; | ||
let t1; | ||
if ($[0] !== isObjNull || $[1] !== obj.prop) { | ||
t1 = () => { | ||
if (!isObjNull) { | ||
return obj.prop; | ||
} else { | ||
return null; | ||
} | ||
}; | ||
$[0] = isObjNull; | ||
$[1] = obj.prop; | ||
$[2] = t1; | ||
} else { | ||
t1 = $[2]; | ||
} | ||
const callback = t1; | ||
let t2; | ||
if ($[3] !== callback) { | ||
t2 = <Stringify shouldInvokeFns={true} callback={callback} />; | ||
$[3] = callback; | ||
$[4] = t2; | ||
} else { | ||
t2 = $[4]; | ||
} | ||
return t2; | ||
} | ||
|
||
export const FIXTURE_ENTRYPOINT = { | ||
fn: Component, | ||
params: [{ obj: null, isObjNull: true }], | ||
}; | ||
|
||
``` | ||
30 changes: 30 additions & 0 deletions
30
...ugin-react-compiler/src/__tests__/fixtures/compiler/bug-invalid-hoisting-functionexpr.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { Stringify } from "shared-runtime"; | ||
|
||
/** | ||
* We currently hoist the accessed properties of function expressions, | ||
* regardless of control flow. This is simply because we wrote support for | ||
* function expressions before doing a lot of work in PropagateScopeDeps | ||
* to handle conditionally accessed dependencies. | ||
* | ||
* Current evaluator error: | ||
* Found differences in evaluator results | ||
* Non-forget (expected): | ||
* (kind: ok) <div>{"shouldInvokeFns":true,"callback":{"kind":"Function","result":null}}</div> | ||
* Forget: | ||
* (kind: exception) Cannot read properties of null (reading 'prop') | ||
*/ | ||
function Component({ obj, isObjNull }) { | ||
const callback = () => { | ||
if (!isObjNull) { | ||
return obj.prop; | ||
} else { | ||
return null; | ||
} | ||
}; | ||
return <Stringify shouldInvokeFns={true} callback={callback} />; | ||
} | ||
|
||
export const FIXTURE_ENTRYPOINT = { | ||
fn: Component, | ||
params: [{ obj: null, isObjNull: true }], | ||
}; |
119 changes: 119 additions & 0 deletions
119
.../src/__tests__/fixtures/compiler/bug-invalid-pruned-scope-leaks-value.expect.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
|
||
## Input | ||
|
||
```javascript | ||
import invariant from "invariant"; | ||
import { | ||
makeObject_Primitives, | ||
mutate, | ||
sum, | ||
useIdentity, | ||
} from "shared-runtime"; | ||
|
||
/** | ||
* Exposes fundamental issue with pruning 'non-reactive' dependencies + flattening | ||
* those scopes. Here, `z`'s original memo block is removed due to the inner hook call. | ||
* However, we also infer that `z` is non-reactive and does not need to be a memo | ||
* dependency. | ||
* | ||
* Current evaluator error: | ||
* Found differences in evaluator results | ||
* Non-forget (expected): | ||
* (kind: ok) [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}] | ||
* [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}] | ||
* [5,{"a":0,"b":"value1","c":true,"wat0":"joe"}] | ||
* Forget: | ||
* (kind: ok) [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}] | ||
* [[ (exception in render) Invariant Violation: oh no! ]] | ||
* [5,{"a":0,"b":"value1","c":true,"wat0":"joe"}] | ||
*/ | ||
|
||
function MyApp({ count }) { | ||
const z = makeObject_Primitives(); | ||
const x = useIdentity(2); | ||
const y = sum(x, count); | ||
mutate(z); | ||
const thing = [y, z]; | ||
if (thing[1] !== z) { | ||
invariant(false, "oh no!"); | ||
} | ||
return thing; | ||
} | ||
|
||
export const FIXTURE_ENTRYPOINT = { | ||
fn: MyApp, | ||
params: [{ count: 2 }], | ||
sequentialRenders: [{ count: 2 }, { count: 2 }, { count: 3 }], | ||
}; | ||
|
||
``` | ||
|
||
## Code | ||
|
||
```javascript | ||
import { c as _c } from "react/compiler-runtime"; | ||
import invariant from "invariant"; | ||
import { | ||
makeObject_Primitives, | ||
mutate, | ||
sum, | ||
useIdentity, | ||
} from "shared-runtime"; | ||
|
||
/** | ||
* Exposes fundamental issue with pruning 'non-reactive' dependencies + flattening | ||
* those scopes. Here, `z`'s original memo block is removed due to the inner hook call. | ||
* However, we also infer that `z` is non-reactive and does not need to be a memo | ||
* dependency. | ||
* | ||
* Current evaluator error: | ||
* Found differences in evaluator results | ||
* Non-forget (expected): | ||
* (kind: ok) [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}] | ||
* [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}] | ||
* [5,{"a":0,"b":"value1","c":true,"wat0":"joe"}] | ||
* Forget: | ||
* (kind: ok) [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}] | ||
* [[ (exception in render) Invariant Violation: oh no! ]] | ||
* [5,{"a":0,"b":"value1","c":true,"wat0":"joe"}] | ||
*/ | ||
|
||
function MyApp(t0) { | ||
const $ = _c(5); | ||
const { count } = t0; | ||
const z = makeObject_Primitives(); | ||
const x = useIdentity(2); | ||
let t1; | ||
if ($[0] !== x || $[1] !== count) { | ||
t1 = sum(x, count); | ||
$[0] = x; | ||
$[1] = count; | ||
$[2] = t1; | ||
} else { | ||
t1 = $[2]; | ||
} | ||
const y = t1; | ||
mutate(z); | ||
let t2; | ||
if ($[3] !== y) { | ||
t2 = [y, z]; | ||
$[3] = y; | ||
$[4] = t2; | ||
} else { | ||
t2 = $[4]; | ||
} | ||
const thing = t2; | ||
if (thing[1] !== z) { | ||
invariant(false, "oh no!"); | ||
} | ||
return thing; | ||
} | ||
|
||
export const FIXTURE_ENTRYPOINT = { | ||
fn: MyApp, | ||
params: [{ count: 2 }], | ||
sequentialRenders: [{ count: 2 }, { count: 2 }, { count: 3 }], | ||
}; | ||
|
||
``` | ||
43 changes: 43 additions & 0 deletions
43
...in-react-compiler/src/__tests__/fixtures/compiler/bug-invalid-pruned-scope-leaks-value.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import invariant from "invariant"; | ||
import { | ||
makeObject_Primitives, | ||
mutate, | ||
sum, | ||
useIdentity, | ||
} from "shared-runtime"; | ||
|
||
/** | ||
* Exposes fundamental issue with pruning 'non-reactive' dependencies + flattening | ||
* those scopes. Here, `z`'s original memo block is removed due to the inner hook call. | ||
* However, we also infer that `z` is non-reactive and does not need to be a memo | ||
* dependency. | ||
* | ||
* Current evaluator error: | ||
* Found differences in evaluator results | ||
* Non-forget (expected): | ||
* (kind: ok) [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}] | ||
* [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}] | ||
* [5,{"a":0,"b":"value1","c":true,"wat0":"joe"}] | ||
* Forget: | ||
* (kind: ok) [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}] | ||
* [[ (exception in render) Invariant Violation: oh no! ]] | ||
* [5,{"a":0,"b":"value1","c":true,"wat0":"joe"}] | ||
*/ | ||
|
||
function MyApp({ count }) { | ||
const z = makeObject_Primitives(); | ||
const x = useIdentity(2); | ||
const y = sum(x, count); | ||
mutate(z); | ||
const thing = [y, z]; | ||
if (thing[1] !== z) { | ||
invariant(false, "oh no!"); | ||
} | ||
return thing; | ||
} | ||
|
||
export const FIXTURE_ENTRYPOINT = { | ||
fn: MyApp, | ||
params: [{ count: 2 }], | ||
sequentialRenders: [{ count: 2 }, { count: 2 }, { count: 3 }], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters