@samitouri / QOS-React-2 / commits / 4a36b83796

Ref validation allows passing refs as props

Updates ValidateNoRefAccessInRender to allow passing refs to JSX, but continue disallowing passing ref values (so `ref` is okay but not `ref.current`)

Joe Savona committed Jun 20, 2023 at 10:11 UTC 4a36b8379611bfaf4bc628eb45542bc3946961cb
5 files changed +70 -3
compiler/forget/packages/babel-plugin-react-forget/src/HIR/ValidateNoRefAccesInRender.ts
+11 -3
@@ -49,10 +49,18 @@ export function validateNoRefAccessInRender(fn: HIRFunction): void {
49 // for these instructions so they ensure we have a complete analysis.
50 break;
51 }
52 + case "JsxExpression": {
53 + // It's okay to pass refs to JSX, but not ref *values*
54 + for (const operand of eachInstructionValueOperand(instr.value)) {
55 + validateNonRefValue(error, operand);
56 + }
57 + break;
58 + }
59 case "FunctionExpression": {
53 - // For now we assume *all* function expressions are safe, eventually we can
54 - // be more precise and disallow ref access in functions that may be called
55 - // during render
60 + // functions are allowed to capture refs, so long as the function is not called
61 + // during render. see AnalyzeFunctions for how we ensure that functions which
62 + // capture refs get assigned a mutable range so we know here whether the function
63 + // is called or not
64 const mutableRange = instr.lvalue.identifier.mutableRange;
65 if (mutableRange.end > mutableRange.start + 1) {
66 for (const operand of eachInstructionValueOperand(instr.value)) {
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/allow-passing-refs-as-props.expect.md new
+32
@@ -0,0 +1,32 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const ref = useRef(null);
7 + return <Foo ref={ref} />;
8 +}
9 +
10 +```
11 +
12 +## Code
13 +
14 +```javascript
15 +import { unstable_useMemoCache as useMemoCache } from "react";
16 +function Component(props) {
17 + const $ = useMemoCache(2);
18 + const ref = useRef(null);
19 + const c_0 = $[0] !== ref;
20 + let t0;
21 + if (c_0) {
22 + t0 = <Foo ref={ref} />;
23 + $[0] = ref;
24 + $[1] = t0;
25 + } else {
26 + t0 = $[1];
27 + }
28 + return t0;
29 +}
30 +
31 +```
32 +
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/allow-passing-refs-as-props.js new
+4
@@ -0,0 +1,4 @@
1 +function Component(props) {
2 + const ref = useRef(null);
3 + return <Foo ref={ref} />;
4 +}
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-ref-value-as-props.expect.md new
+19
@@ -0,0 +1,19 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const ref = useRef(null);
7 + return <Foo ref={ref.current} />;
8 +}
9 +
10 +```
11 +
12 +
13 +## Error
14 +
15 +```
16 +[ReactForget] InvalidInput: Ref values (the `current` property) may not be accessed during render. Cannot access ref value at freeze $20:TObject<BuiltInRefValue> (3:3)
17 +```
18 +
19 +
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-ref-value-as-props.js new
+4
@@ -0,0 +1,4 @@
1 +function Component(props) {
2 + const ref = useRef(null);
3 + return <Foo ref={ref.current} />;
4 +}