From 54e602d891c517c8070ce9255ae0a8921e6fcade Mon Sep 17 00:00:00 2001 From: Mofei Zhang Date: Thu, 13 Mar 2025 11:52:25 -0400 Subject: [PATCH 1/5] [compiler] Patch array and argument spread mutability Array and argument spreads may mutate stateful iterables. Spread sites should have `ConditionallyMutate` effects (e.g. mutate if the ValueKind is mutable, otherwise read). See - [ecma spec (13.2.4.1 Runtime Semantics: ArrayAccumulation. SpreadElement : ... AssignmentExpression)](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-runtime-semantics-arrayaccumulation). - [ecma spec 13.3.8.1 Runtime Semantics: ArgumentListEvaluation](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-runtime-semantics-argumentlistevaluation) Note that - Object and JSX Attribute spreads do not evaluate iterables (srcs [mozilla](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#description), [ecma](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-runtime-semantics-propertydefinitionevaluation)) - An ideal mutability inference system could model known collections (i.e. Arrays or Sets) as a "mutated collection of non-mutable objects" (see `todo-granular-iterator-semantics`), but this is not what we do today. As such, an array / argument spread will always extend the range of built-in arrays, sets, etc - Due to HIR limitations, call expressions with argument spreads may cause unnecessary bailouts and/or scope merging when we know the call itself has `freeze`, `capture`, or `read` semantics (e.g. `useHook(...mutableValue)`) We can deal with this by rewriting these call instructions to (1) create an intermediate array to consume the iterator and (2) capture and spread the array at the callsite --- .../src/Inference/InferReferenceEffects.ts | 113 ++++++++++++------ .../array-spread-later-mutated.expect.md | 62 ++++++++++ .../compiler/array-spread-later-mutated.js | 20 ++++ ...> array-spread-mutable-iterator.expect.md} | 30 +++-- ...or.js => array-spread-mutable-iterator.js} | 0 ...spread-argument-mutable-iterator.expect.md | 42 +++++++ .../call-spread-argument-mutable-iterator.js | 13 ++ ...ok-call-spreads-mutable-iterator.expect.md | 33 +++++ ...todo-hook-call-spreads-mutable-iterator.js | 12 ++ ...wer-property-load-into-temporary.expect.md | 19 +-- ...alls-lower-property-load-into-temporary.js | 5 +- .../packages/snap/src/SproutTodoFilter.ts | 1 - 12 files changed, 283 insertions(+), 67 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-later-mutated.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-later-mutated.js rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{bug-array-spread-mutable-iterator.expect.md => array-spread-mutable-iterator.expect.md} (82%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{bug-array-spread-mutable-iterator.js => array-spread-mutable-iterator.js} (100%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/call-spread-argument-mutable-iterator.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/call-spread-argument-mutable-iterator.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-hook-call-spreads-mutable-iterator.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-hook-call-spreads-mutable-iterator.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts index e6883250705d..bfa082540835 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts @@ -872,11 +872,33 @@ function inferBlock( reason: new Set([ValueReason.Other]), context: new Set(), }; + + for (const element of instrValue.elements) { + if (element.kind === 'Spread') { + state.referenceAndRecordEffects( + freezeActions, + element.place, + isArrayType(element.place.identifier) + ? Effect.Capture + : Effect.ConditionallyMutate, + ValueReason.Other, + ); + } else if (element.kind === 'Identifier') { + state.referenceAndRecordEffects( + freezeActions, + element, + Effect.Capture, + ValueReason.Other, + ); + } else { + let _: 'Hole' = element.kind; + } + } + state.initialize(instrValue, valueKind); + state.define(instr.lvalue, instrValue); + instr.lvalue.effect = Effect.Store; continuation = { - kind: 'initialize', - valueKind, - effect: {kind: Effect.Capture, reason: ValueReason.Other}, - lvalueEffect: Effect.Store, + kind: 'funeffects', }; break; } @@ -1241,21 +1263,12 @@ function inferBlock( for (let i = 0; i < instrValue.args.length; i++) { const arg = instrValue.args[i]; const place = arg.kind === 'Identifier' ? arg : arg.place; - if (effects !== null) { - state.referenceAndRecordEffects( - freezeActions, - place, - effects[i], - ValueReason.Other, - ); - } else { - state.referenceAndRecordEffects( - freezeActions, - place, - Effect.ConditionallyMutate, - ValueReason.Other, - ); - } + state.referenceAndRecordEffects( + freezeActions, + place, + getArgumentEffect(effects != null ? effects[i] : null, arg), + ValueReason.Other, + ); hasCaptureArgument ||= place.effect === Effect.Capture; } if (signature !== null) { @@ -1307,7 +1320,10 @@ function inferBlock( signature !== null ? { kind: signature.returnValueKind, - reason: new Set([ValueReason.Other]), + reason: new Set([ + signature.returnValueReason ?? + ValueReason.KnownReturnSignature, + ]), context: new Set(), } : { @@ -1356,25 +1372,16 @@ function inferBlock( for (let i = 0; i < instrValue.args.length; i++) { const arg = instrValue.args[i]; const place = arg.kind === 'Identifier' ? arg : arg.place; - if (effects !== null) { - /* - * If effects are inferred for an argument, we should fail invalid - * mutating effects - */ - state.referenceAndRecordEffects( - freezeActions, - place, - effects[i], - ValueReason.Other, - ); - } else { - state.referenceAndRecordEffects( - freezeActions, - place, - Effect.ConditionallyMutate, - ValueReason.Other, - ); - } + /* + * If effects are inferred for an argument, we should fail invalid + * mutating effects + */ + state.referenceAndRecordEffects( + freezeActions, + place, + getArgumentEffect(effects != null ? effects[i] : null, arg), + ValueReason.Other, + ); hasCaptureArgument ||= place.effect === Effect.Capture; } if (signature !== null) { @@ -2049,3 +2056,31 @@ function areArgumentsImmutableAndNonMutating( } return true; } + +function getArgumentEffect( + signatureEffect: Effect | null, + arg: Place | SpreadPattern, +): Effect { + if (signatureEffect != null) { + if (arg.kind === 'Identifier') { + return signatureEffect; + } else if ( + signatureEffect === Effect.Mutate || + signatureEffect === Effect.ConditionallyMutate + ) { + return signatureEffect; + } else { + // see call-spread-argument-mutable-iterator test fixture + if (signatureEffect === Effect.Freeze) { + CompilerError.throwTodo({ + reason: 'Support spread syntax for hook arguments', + loc: arg.place.loc, + }); + } + // effects[i] is Effect.Capture | Effect.Read | Effect.Store + return Effect.ConditionallyMutate; + } + } else { + return Effect.ConditionallyMutate; + } +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-later-mutated.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-later-mutated.expect.md new file mode 100644 index 000000000000..a317e22faf92 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-later-mutated.expect.md @@ -0,0 +1,62 @@ + +## Input + +```javascript +function useBar({arg}) { + /** + * Note that mutableIterator is mutated by the later object spread. Therefore, + * `s.values()` should be memoized within the same block as the object spread. + * In terms of compiler internals, they should have the same reactive scope. + */ + const obj = {}; + const s = new Set([obj, 5, 4]); + const mutableIterator = s.values(); + const arr = [...mutableIterator]; + + obj.x = arg; + return arr; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useBar, + params: [{arg: 3}], + sequentialRenders: [{arg: 3}, {arg: 3}, {arg: 4}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +function useBar(t0) { + const $ = _c(2); + const { arg } = t0; + let arr; + if ($[0] !== arg) { + const obj = {}; + const s = new Set([obj, 5, 4]); + const mutableIterator = s.values(); + arr = [...mutableIterator]; + + obj.x = arg; + $[0] = arg; + $[1] = arr; + } else { + arr = $[1]; + } + return arr; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useBar, + params: [{ arg: 3 }], + sequentialRenders: [{ arg: 3 }, { arg: 3 }, { arg: 4 }], +}; + +``` + +### Eval output +(kind: ok) [{"x":3},5,4] +[{"x":3},5,4] +[{"x":4},5,4] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-later-mutated.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-later-mutated.js new file mode 100644 index 000000000000..036ce2ddfc68 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-later-mutated.js @@ -0,0 +1,20 @@ +function useBar({arg}) { + /** + * Note that mutableIterator is mutated by the later object spread. Therefore, + * `s.values()` should be memoized within the same block as the object spread. + * In terms of compiler internals, they should have the same reactive scope. + */ + const obj = {}; + const s = new Set([obj, 5, 4]); + const mutableIterator = s.values(); + const arr = [...mutableIterator]; + + obj.x = arg; + return arr; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useBar, + params: [{arg: 3}], + sequentialRenders: [{arg: 3}, {arg: 3}, {arg: 4}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-mutable-iterator.expect.md similarity index 82% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-mutable-iterator.expect.md index 70f41f40477a..25499af1b020 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-mutable-iterator.expect.md @@ -55,26 +55,20 @@ import { c as _c } from "react/compiler-runtime"; /** function useBar(t0) { "use memo"; - const $ = _c(3); + const $ = _c(2); const { arg } = t0; let t1; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + if ($[0] !== arg) { const s = new Set([1, 5, 4]); - t1 = s.values(); - $[0] = t1; - } else { - t1 = $[0]; - } - const mutableIterator = t1; - let t2; - if ($[1] !== arg) { - t2 = [arg, ...mutableIterator]; - $[1] = arg; - $[2] = t2; + const mutableIterator = s.values(); + + t1 = [arg, ...mutableIterator]; + $[0] = arg; + $[1] = t1; } else { - t2 = $[2]; + t1 = $[1]; } - return t2; + return t1; } export const FIXTURE_ENTRYPOINT = { @@ -84,4 +78,8 @@ export const FIXTURE_ENTRYPOINT = { }; ``` - \ No newline at end of file + +### Eval output +(kind: ok) [3,1,5,4] +[3,1,5,4] +[4,1,5,4] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-mutable-iterator.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-spread-mutable-iterator.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/call-spread-argument-mutable-iterator.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/call-spread-argument-mutable-iterator.expect.md new file mode 100644 index 000000000000..74fb57b6bc2f --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/call-spread-argument-mutable-iterator.expect.md @@ -0,0 +1,42 @@ + +## Input + +```javascript +import {useIdentity} from 'shared-runtime'; + +function useFoo() { + const it = new Set([1, 2]).values(); + useIdentity(); + return Math.max(...it); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{}], + sequentialRenders: [{}, {}], +}; + +``` + +## Code + +```javascript +import { useIdentity } from "shared-runtime"; + +function useFoo() { + const it = new Set([1, 2]).values(); + useIdentity(); + return Math.max(...it); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{}], + sequentialRenders: [{}, {}], +}; + +``` + +### Eval output +(kind: ok) 2 +2 \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/call-spread-argument-mutable-iterator.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/call-spread-argument-mutable-iterator.js new file mode 100644 index 000000000000..1b30f0a46f7c --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/call-spread-argument-mutable-iterator.js @@ -0,0 +1,13 @@ +import {useIdentity} from 'shared-runtime'; + +function useFoo() { + const it = new Set([1, 2]).values(); + useIdentity(); + return Math.max(...it); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{}], + sequentialRenders: [{}, {}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-hook-call-spreads-mutable-iterator.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-hook-call-spreads-mutable-iterator.expect.md new file mode 100644 index 000000000000..5d0af122f214 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-hook-call-spreads-mutable-iterator.expect.md @@ -0,0 +1,33 @@ + +## Input + +```javascript +import {useIdentity} from 'shared-runtime'; + +function Component() { + const items = makeArray(0, 1, 2, null, 4, false, 6); + return useIdentity(...items.values()); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [], + sequentialRenders: [{}, {}], +}; + +``` + + +## Error + +``` + 3 | function Component() { + 4 | const items = makeArray(0, 1, 2, null, 4, false, 6); +> 5 | return useIdentity(...items.values()); + | ^^^^^^^^^^^^^^ Todo: Support spread syntax for hook arguments (5:5) + 6 | } + 7 | + 8 | export const FIXTURE_ENTRYPOINT = { +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-hook-call-spreads-mutable-iterator.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-hook-call-spreads-mutable-iterator.js new file mode 100644 index 000000000000..982fd83dc8a2 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-hook-call-spreads-mutable-iterator.js @@ -0,0 +1,12 @@ +import {useIdentity} from 'shared-runtime'; + +function Component() { + const items = makeArray(0, 1, 2, null, 4, false, 6); + return useIdentity(...items.values()); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [], + sequentialRenders: [{}, {}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-nested-method-calls-lower-property-load-into-temporary.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-nested-method-calls-lower-property-load-into-temporary.expect.md index 877c15e883ff..56bf9e3d6214 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-nested-method-calls-lower-property-load-into-temporary.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-nested-method-calls-lower-property-load-into-temporary.expect.md @@ -4,9 +4,10 @@ ```javascript import {makeArray} from 'shared-runtime'; -function Component(props) { +const other = [0, 1]; +function Component({}) { const items = makeArray(0, 1, 2, null, 4, false, 6); - const max = Math.max(...items.filter(Boolean)); + const max = Math.max(2, items.push(5), ...other); return max; } @@ -21,13 +22,13 @@ export const FIXTURE_ENTRYPOINT = { ## Error ``` - 3 | function Component(props) { - 4 | const items = makeArray(0, 1, 2, null, 4, false, 6); -> 5 | const max = Math.max(...items.filter(Boolean)); - | ^^^^^^^^ Invariant: [Codegen] Internal error: MethodCall::property must be an unpromoted + unmemoized MemberExpression. Got a `Identifier` (5:5) - 6 | return max; - 7 | } - 8 | + 4 | function Component({}) { + 5 | const items = makeArray(0, 1, 2, null, 4, false, 6); +> 6 | const max = Math.max(2, items.push(5), ...other); + | ^^^^^^^^ Invariant: [Codegen] Internal error: MethodCall::property must be an unpromoted + unmemoized MemberExpression. Got a `Identifier` (6:6) + 7 | return max; + 8 | } + 9 | ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-nested-method-calls-lower-property-load-into-temporary.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-nested-method-calls-lower-property-load-into-temporary.js index 475cf383cf1e..b2883c330383 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-nested-method-calls-lower-property-load-into-temporary.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-nested-method-calls-lower-property-load-into-temporary.js @@ -1,8 +1,9 @@ import {makeArray} from 'shared-runtime'; -function Component(props) { +const other = [0, 1]; +function Component({}) { const items = makeArray(0, 1, 2, null, 4, false, 6); - const max = Math.max(...items.filter(Boolean)); + const max = Math.max(2, items.push(5), ...other); return max; } diff --git a/compiler/packages/snap/src/SproutTodoFilter.ts b/compiler/packages/snap/src/SproutTodoFilter.ts index bbed77c35a90..0817bbf89c58 100644 --- a/compiler/packages/snap/src/SproutTodoFilter.ts +++ b/compiler/packages/snap/src/SproutTodoFilter.ts @@ -462,7 +462,6 @@ const skipFilter = new Set([ // bugs 'bug-object-expression-computed-key-modified-during-after-construction-hoisted-sequence-expr', - 'bug-array-spread-mutable-iterator', `bug-capturing-func-maybealias-captured-mutate`, 'bug-aliased-capture-aliased-mutate', 'bug-aliased-capture-mutate', From 7606b290486d7178decfc548b36a3a2a5d1dc3de Mon Sep 17 00:00:00 2001 From: Mofei Zhang Date: Thu, 13 Mar 2025 11:52:25 -0400 Subject: [PATCH 2/5] [compiler][optim] Add shape for Array.from (see title) --- .../src/HIR/Globals.ts | 18 +++- .../array-from-arg1-captures-arg0.expect.md | 91 +++++++++++++++++++ .../compiler/array-from-arg1-captures-arg0.js | 26 ++++++ .../array-from-captures-arg0.expect.md | 88 ++++++++++++++++++ .../compiler/array-from-captures-arg0.js | 26 ++++++ .../array-from-maybemutates-arg0.expect.md | 64 +++++++++++++ .../compiler/array-from-maybemutates-arg0.js | 14 +++ ...todo-granular-iterator-semantics.expect.md | 24 +++-- ...md => type-inference-array-from.expect.md} | 48 ++++++++-- ...y-from.js => type-inference-array-from.js} | 6 ++ 10 files changed, 385 insertions(+), 20 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.js rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{todo-type-inference-array-from.expect.md => type-inference-array-from.expect.md} (71%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{todo-type-inference-array-from.js => type-inference-array-from.js} (87%) diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts index 3fe2e938ce57..df8196c1d7a0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts @@ -119,8 +119,8 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [ ], /* * https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.from - * Array.from(arrayLike, optionalFn, optionalThis) not added because - * the Effect of `arrayLike` is polymorphic i.e. + * Array.from(arrayLike, optionalFn, optionalThis) + * Note that the Effect of `arrayLike` is polymorphic i.e. * - Effect.read if * - it does not have an @iterator property and is array-like * (i.e. has a length property) @@ -128,6 +128,20 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [ * - Effect.mutate if it is a self-mutative iterator (e.g. a generator * function) */ + [ + 'from', + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [ + Effect.ConditionallyMutate, + Effect.ConditionallyMutate, + Effect.ConditionallyMutate, + ], + restParam: Effect.Read, + returnType: {kind: 'Object', shapeId: BuiltInArrayId}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Mutable, + }), + ], [ 'of', // Array.of(element0, ..., elementN) diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.expect.md new file mode 100644 index 000000000000..8892c8d484bf --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.expect.md @@ -0,0 +1,91 @@ + +## Input + +```javascript +import {useIdentity, Stringify} from 'shared-runtime'; + +/** + * TODO: Note that this `Array.from` is inferred to be mutating its first + * argument. This is because React Compiler's typing system does not yet support + * annotating a function with a set of argument match cases + distinct + * definitions (polymorphism). + * + * In this case, we should be able to infer that the `Array.from` call is + * not mutating its 0th argument. + * The 0th argument should be typed as having `effect:Mutate` only when + * (1) it might be a mutable iterable or + * (2) the 1st argument might mutate its callee + */ +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(); + const derived = Array.from(arr, (x, idx) => ({...x, id: idx})); + return {derived.at(-1)}; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { useIdentity, Stringify } from "shared-runtime"; + +/** + * TODO: Note that this `Array.from` is inferred to be mutating its first + * argument. This is because React Compiler's typing system does not yet support + * annotating a function with a set of argument match cases + distinct + * definitions (polymorphism). + * + * In this case, we should be able to infer that the `Array.from` call is + * not mutating its 0th argument. + * The 0th argument should be typed as having `effect:Mutate` only when + * (1) it might be a mutable iterable or + * (2) the 1st argument might mutate its callee + */ +function Component(t0) { + const $ = _c(4); + const { value } = t0; + const arr = [{ value: "foo" }, { value: "bar" }, { value }]; + useIdentity(); + const derived = Array.from(arr, _temp); + let t1; + if ($[0] !== derived) { + t1 = derived.at(-1); + $[0] = derived; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== t1) { + t2 = {t1}; + $[2] = t1; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; +} +function _temp(x, idx) { + return { ...x, id: idx }; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 5 }], + sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }], +}; + +``` + +### Eval output +(kind: ok)
{"children":{"value":5,"id":2}}
+
{"children":{"value":6,"id":2}}
+
{"children":{"value":6,"id":2}}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.js new file mode 100644 index 000000000000..f2b364bc60ee --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-arg1-captures-arg0.js @@ -0,0 +1,26 @@ +import {useIdentity, Stringify} from 'shared-runtime'; + +/** + * TODO: Note that this `Array.from` is inferred to be mutating its first + * argument. This is because React Compiler's typing system does not yet support + * annotating a function with a set of argument match cases + distinct + * definitions (polymorphism). + * + * In this case, we should be able to infer that the `Array.from` call is + * not mutating its 0th argument. + * The 0th argument should be typed as having `effect:Mutate` only when + * (1) it might be a mutable iterable or + * (2) the 1st argument might mutate its callee + */ +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(); + const derived = Array.from(arr, (x, idx) => ({...x, id: idx})); + return {derived.at(-1)}; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.expect.md new file mode 100644 index 000000000000..66d0b4258462 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.expect.md @@ -0,0 +1,88 @@ + +## Input + +```javascript +import {useIdentity, Stringify} from 'shared-runtime'; + +/** + * TODO: Note that this `Array.from` is inferred to be mutating its first + * argument. This is because React Compiler's typing system does not yet support + * annotating a function with a set of argument match cases + distinct + * definitions (polymorphism) + * + * In this case, we should be able to infer that the `Array.from` call is + * not mutating its 0th argument. + * The 0th argument should be typed as having `effect:Mutate` only when + * (1) it might be a mutable iterable or + * (2) the 1st argument might mutate its callee + */ +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(); + const derived = Array.from(arr); + return {derived.at(-1)}; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { useIdentity, Stringify } from "shared-runtime"; + +/** + * TODO: Note that this `Array.from` is inferred to be mutating its first + * argument. This is because React Compiler's typing system does not yet support + * annotating a function with a set of argument match cases + distinct + * definitions (polymorphism) + * + * In this case, we should be able to infer that the `Array.from` call is + * not mutating its 0th argument. + * The 0th argument should be typed as having `effect:Mutate` only when + * (1) it might be a mutable iterable or + * (2) the 1st argument might mutate its callee + */ +function Component(t0) { + const $ = _c(4); + const { value } = t0; + const arr = [{ value: "foo" }, { value: "bar" }, { value }]; + useIdentity(); + const derived = Array.from(arr); + let t1; + if ($[0] !== derived) { + t1 = derived.at(-1); + $[0] = derived; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== t1) { + t2 = {t1}; + $[2] = t1; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 5 }], + sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }], +}; + +``` + +### Eval output +(kind: ok)
{"children":{"value":5}}
+
{"children":{"value":6}}
+
{"children":{"value":6}}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.js new file mode 100644 index 000000000000..c9b09c384de2 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-captures-arg0.js @@ -0,0 +1,26 @@ +import {useIdentity, Stringify} from 'shared-runtime'; + +/** + * TODO: Note that this `Array.from` is inferred to be mutating its first + * argument. This is because React Compiler's typing system does not yet support + * annotating a function with a set of argument match cases + distinct + * definitions (polymorphism) + * + * In this case, we should be able to infer that the `Array.from` call is + * not mutating its 0th argument. + * The 0th argument should be typed as having `effect:Mutate` only when + * (1) it might be a mutable iterable or + * (2) the 1st argument might mutate its callee + */ +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(); + const derived = Array.from(arr); + return {derived.at(-1)}; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.expect.md new file mode 100644 index 000000000000..586124280a3d --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.expect.md @@ -0,0 +1,64 @@ + +## Input + +```javascript +import {mutateAndReturn, Stringify, useIdentity} from 'shared-runtime'; + +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(); + const derived = Array.from(arr, mutateAndReturn); + return {derived.at(-1)}; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { mutateAndReturn, Stringify, useIdentity } from "shared-runtime"; + +function Component(t0) { + const $ = _c(4); + const { value } = t0; + const arr = [{ value: "foo" }, { value: "bar" }, { value }]; + useIdentity(); + const derived = Array.from(arr, mutateAndReturn); + let t1; + if ($[0] !== derived) { + t1 = derived.at(-1); + $[0] = derived; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== t1) { + t2 = {t1}; + $[2] = t1; + $[3] = t2; + } else { + t2 = $[3]; + } + return t2; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: 5 }], + sequentialRenders: [{ value: 5 }, { value: 6 }, { value: 6 }], +}; + +``` + +### Eval output +(kind: ok)
{"children":{"value":5,"wat0":"joe"}}
+
{"children":{"value":6,"wat0":"joe"}}
+
{"children":{"value":6,"wat0":"joe"}}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.js new file mode 100644 index 000000000000..edb4e3712584 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/array-from-maybemutates-arg0.js @@ -0,0 +1,14 @@ +import {mutateAndReturn, Stringify, useIdentity} from 'shared-runtime'; + +function Component({value}) { + const arr = [{value: 'foo'}, {value: 'bar'}, {value}]; + useIdentity(); + const derived = Array.from(arr, mutateAndReturn); + return {derived.at(-1)}; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 5}], + sequentialRenders: [{value: 5}, {value: 6}, {value: 6}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md index 1ba01dc5bf4d..ea3f1d4f3871 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md @@ -68,21 +68,29 @@ function Validate({ x, input }) { } function useFoo(input) { "use memo"; - const $ = _c(3); + const $ = _c(5); const x = Array.from([{}]); useIdentity(); - x.push([input]); let t0; - if ($[0] !== input || $[1] !== x) { - t0 = ; + if ($[0] !== input) { + t0 = [input]; $[0] = input; - $[1] = x; - $[2] = t0; + $[1] = t0; + } else { + t0 = $[1]; + } + x.push(t0); + let t1; + if ($[2] !== input || $[3] !== x) { + t1 = ; + $[2] = input; + $[3] = x; + $[4] = t1; } else { - t0 = $[2]; + t1 = $[4]; } - return t0; + return t1; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference-array-from.expect.md similarity index 71% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference-array-from.expect.md index 6061464afce7..5209fd953ec8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference-array-from.expect.md @@ -37,6 +37,12 @@ function useFoo({val1, val2}) { export const FIXTURE_ENTRYPOINT = { fn: useFoo, params: [{val1: 1, val2: 2}], + params: [ + {val1: 1, val2: 2}, + {val1: 1, val2: 2}, + {val1: 1, val2: 3}, + {val1: 4, val2: 2}, + ], }; ``` @@ -71,29 +77,51 @@ function Validate({ x, val1, val2 }) { } function useFoo(t0) { "use memo"; - const $ = _c(4); + const $ = _c(8); const { val1, val2 } = t0; const x = Array.from([]); useIdentity(); - x.push([val1]); - x.push([val2]); let t1; - if ($[0] !== val1 || $[1] !== val2 || $[2] !== x) { - t1 = ; + if ($[0] !== val1) { + t1 = [val1]; $[0] = val1; - $[1] = val2; - $[2] = x; - $[3] = t1; + $[1] = t1; + } else { + t1 = $[1]; + } + x.push(t1); + let t2; + if ($[2] !== val2) { + t2 = [val2]; + $[2] = val2; + $[3] = t2; + } else { + t2 = $[3]; + } + x.push(t2); + let t3; + if ($[4] !== val1 || $[5] !== val2 || $[6] !== x) { + t3 = ; + $[4] = val1; + $[5] = val2; + $[6] = x; + $[7] = t3; } else { - t1 = $[3]; + t3 = $[7]; } - return t1; + return t3; } export const FIXTURE_ENTRYPOINT = { fn: useFoo, params: [{ val1: 1, val2: 2 }], + params: [ + { val1: 1, val2: 2 }, + { val1: 1, val2: 2 }, + { val1: 1, val2: 3 }, + { val1: 4, val2: 2 }, + ], }; ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference-array-from.js similarity index 87% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference-array-from.js index d1a80a4ea709..dfd4e0e0f19a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-inference-array-from.js @@ -33,4 +33,10 @@ function useFoo({val1, val2}) { export const FIXTURE_ENTRYPOINT = { fn: useFoo, params: [{val1: 1, val2: 2}], + params: [ + {val1: 1, val2: 2}, + {val1: 1, val2: 2}, + {val1: 1, val2: 3}, + {val1: 4, val2: 2}, + ], }; From 8ba22587b544041a5ee08bca191a133252ebd1b5 Mon Sep 17 00:00:00 2001 From: Mofei Zhang Date: Thu, 13 Mar 2025 11:52:25 -0400 Subject: [PATCH 3/5] [compiler][optim] infer mixedReadOnly for numeric and computed properties Expand type inference to infer mixedReadOnly types for numeric and computed property accesses. ```js function Component({idx}) const data = useFragment(...) // we want to type `posts` correctly as Array const posts = data.viewers[idx].posts.slice(0, 5); // ... } ``` --- .../src/HIR/Environment.ts | 43 ++++++++++--- .../src/HIR/Types.ts | 10 ++- .../src/TypeInference/InferTypes.ts | 47 ++++++++++---- .../relay-transitive-mixeddata.expect.md | 61 +++++++++++++++++++ .../compiler/relay-transitive-mixeddata.js | 21 +++++++ 5 files changed, 161 insertions(+), 21 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/relay-transitive-mixeddata.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/relay-transitive-mixeddata.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index b61243f0424a..7dc9e044bfb7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -1126,9 +1126,32 @@ export class Environment { ); } + getFallthroughPropertyType( + receiver: Type, + _property: Type, + ): BuiltInType | PolyType | null { + let shapeId = null; + if (receiver.kind === 'Object' || receiver.kind === 'Function') { + shapeId = receiver.shapeId; + } + + if (shapeId !== null) { + const shape = this.#shapes.get(shapeId); + + CompilerError.invariant(shape !== undefined, { + reason: `[HIR] Forget internal error: cannot resolve shape ${shapeId}`, + description: null, + loc: null, + suggestions: null, + }); + return shape.properties.get('*') ?? null; + } + return null; + } + getPropertyType( receiver: Type, - property: string, + property: string | number, ): BuiltInType | PolyType | null { let shapeId = null; if (receiver.kind === 'Object' || receiver.kind === 'Function') { @@ -1146,17 +1169,19 @@ export class Environment { loc: null, suggestions: null, }); - let value = - shape.properties.get(property) ?? shape.properties.get('*') ?? null; - if (value === null && isHookName(property)) { - value = this.#getCustomHookType(); + if (typeof property === 'string') { + return ( + shape.properties.get(property) ?? + shape.properties.get('*') ?? + (isHookName(property) ? this.#getCustomHookType() : null) + ); + } else { + return shape.properties.get('*') ?? null; } - return value; - } else if (isHookName(property)) { + } else if (typeof property === 'string' && isHookName(property)) { return this.#getCustomHookType(); - } else { - return null; } + return null; } getFunctionSignature(type: FunctionType): FunctionSignature | null { diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts index a67b580a6728..1de81919c391 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts @@ -60,7 +60,15 @@ export type PropType = { kind: 'Property'; objectType: Type; objectName: string; - propertyName: PropertyLiteral; + propertyName: + | { + kind: 'literal'; + value: PropertyLiteral; + } + | { + kind: 'computed'; + value: Type; + }; }; export type ObjectMethod = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts b/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts index 3054a83c7613..02e4e60e4b84 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts @@ -307,11 +307,26 @@ function* generateInstructionTypes( kind: 'Property', objectType: value.object.identifier.type, objectName: getName(names, value.object.identifier.id), - propertyName: value.property, + propertyName: { + kind: 'literal', + value: value.property, + }, }); break; } + case 'ComputedLoad': { + yield equation(left, { + kind: 'Property', + objectType: value.object.identifier.type, + objectName: getName(names, value.object.identifier.id), + propertyName: { + kind: 'computed', + value: value.property.identifier.type, + }, + }); + break; + } case 'MethodCall': { const returnType = makeType(); yield equation(value.property.identifier.type, { @@ -336,7 +351,10 @@ function* generateInstructionTypes( kind: 'Property', objectType: value.value.identifier.type, objectName: getName(names, value.value.identifier.id), - propertyName: makePropertyLiteral(propertyName), + propertyName: { + kind: 'literal', + value: makePropertyLiteral(propertyName), + }, }); } else { break; @@ -353,7 +371,10 @@ function* generateInstructionTypes( kind: 'Property', objectType: value.value.identifier.type, objectName: getName(names, value.value.identifier.id), - propertyName: makePropertyLiteral(property.key.name), + propertyName: { + kind: 'literal', + value: makePropertyLiteral(property.key.name), + }, }); } } @@ -410,7 +431,6 @@ function* generateInstructionTypes( case 'RegExpLiteral': case 'MetaProperty': case 'ComputedStore': - case 'ComputedLoad': case 'Await': case 'GetIterator': case 'IteratorNext': @@ -454,12 +474,13 @@ class Unifier { return; } const objectType = this.get(tB.objectType); - let propertyType; - if (typeof tB.propertyName === 'number') { - propertyType = null; - } else { - propertyType = this.env.getPropertyType(objectType, tB.propertyName); - } + const propertyType = + tB.propertyName.kind === 'literal' + ? this.env.getPropertyType(objectType, tB.propertyName.value) + : this.env.getFallthroughPropertyType( + objectType, + tB.propertyName.value, + ); if (propertyType !== null) { this.unify(tA, propertyType); } @@ -677,7 +698,11 @@ class Unifier { const RefLikeNameRE = /^(?:[a-zA-Z$_][a-zA-Z$_0-9]*)Ref$|^ref$/; function isRefLikeName(t: PropType): boolean { - return RefLikeNameRE.test(t.objectName) && t.propertyName === 'current'; + return ( + t.propertyName.kind === 'literal' && + RefLikeNameRE.test(t.objectName) && + t.propertyName.value === 'current' + ); } function tryUnionTypes(ty1: Type, ty2: Type): Type | null { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/relay-transitive-mixeddata.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/relay-transitive-mixeddata.expect.md new file mode 100644 index 000000000000..5caf2e8e0da0 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/relay-transitive-mixeddata.expect.md @@ -0,0 +1,61 @@ + +## Input + +```javascript +import {useFragment} from 'shared-runtime'; + +/** + * React compiler should infer that the returned value is a primitive and avoid + * memoizing it. + */ +function useRelayData({query, idx}) { + 'use memo'; + const data = useFragment('', query); + return data.a[idx].toString(); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useRelayData, + params: [{query: '', idx: 0}], + sequentialRenders: [ + {query: '', idx: 0}, + {query: '', idx: 0}, + {query: '', idx: 1}, + ], +}; + +``` + +## Code + +```javascript +import { useFragment } from "shared-runtime"; + +/** + * React compiler should infer that the returned value is a primitive and avoid + * memoizing it. + */ +function useRelayData(t0) { + "use memo"; + const { query, idx } = t0; + + const data = useFragment("", query); + return data.a[idx].toString(); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useRelayData, + params: [{ query: "", idx: 0 }], + sequentialRenders: [ + { query: "", idx: 0 }, + { query: "", idx: 0 }, + { query: "", idx: 1 }, + ], +}; + +``` + +### Eval output +(kind: ok) "1" +"1" +"2" \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/relay-transitive-mixeddata.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/relay-transitive-mixeddata.js new file mode 100644 index 000000000000..78708f30c71c --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/relay-transitive-mixeddata.js @@ -0,0 +1,21 @@ +import {useFragment} from 'shared-runtime'; + +/** + * React compiler should infer that the returned value is a primitive and avoid + * memoizing it. + */ +function useRelayData({query, idx}) { + 'use memo'; + const data = useFragment('', query); + return data.a[idx].toString(); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useRelayData, + params: [{query: '', idx: 0}], + sequentialRenders: [ + {query: '', idx: 0}, + {query: '', idx: 0}, + {query: '', idx: 1}, + ], +}; From d2d5350e6522aeedfecf5f963248bdd9d43ddeb6 Mon Sep 17 00:00:00 2001 From: Mofei Zhang Date: Thu, 13 Mar 2025 11:52:25 -0400 Subject: [PATCH 4/5] [compiler][optim] more shapes for mixedreadonly - Add `at`, `indexOf`, and `includes` - Optimize MixedReadOnly which is currently only used by hook return values. Hook return values are typed as Frozen, this change propagates that to return values of aliasing function calls (such as `at`). One potential issue is that developers may pass `enableAssumeHooksFollowRulesOfReact:false` and set `transitiveMixedData`, expecting their transitive mixed data to be mutable. This is a bit of an edge case and already doesn't have clear semantics. --- .../src/HIR/ObjectShape.ts | 58 ++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts index f51768c586b7..22ae261867b3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts @@ -535,6 +535,30 @@ addObject(BUILTIN_SHAPES, BuiltInRefValueId, [ ['*', {kind: 'Object', shapeId: BuiltInRefValueId}], ]); +/** + * MixedReadOnly = + * | primitive + * | simple objects (Record) + * | Array + * + * APIs such as Relay — but also Flux and other data stores — often return a + * union of types with some interesting properties in terms of analysis. + * + * Given this constraint, if data came from Relay, then we should be able to + * infer things like `data.items.map(): Array`. That may seem like a leap at + * first but remember, we assume you're not patching builtins. Thus the only way + * data.items.map can exist and be a function, given the above set of data types + * and builtin JS methods, is if `data.items` was an Array, and `data.items.map` + * is therefore calling Array.prototype.map. Then we know that function returns + * an Array as well. This relies on the fact that map() is being called, so if + * data.items was some other type it would error at runtime - so it's sound. + * + * Note that this shape is currently only used for hook return values, which + * means that it's safe to type aliasing method-call return kinds as `Frozen`. + * + * Also note that all newly created arrays from method-calls (e.g. `.map`) + * have the appropriate mutable `BuiltInArray` shape + */ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [ [ 'toString', @@ -546,6 +570,36 @@ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [ returnValueKind: ValueKind.Primitive, }), ], + [ + 'indexOf', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: {kind: 'Primitive'}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + 'includes', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: {kind: 'Primitive'}, + calleeEffect: Effect.Read, + returnValueKind: ValueKind.Primitive, + }), + ], + [ + 'at', + addFunction(BUILTIN_SHAPES, [], { + positionalParams: [Effect.Read], + restParam: null, + returnType: {kind: 'Object', shapeId: BuiltInMixedReadonlyId}, + calleeEffect: Effect.Capture, + returnValueKind: ValueKind.Frozen, + }), + ], [ 'map', addFunction(BUILTIN_SHAPES, [], { @@ -642,9 +696,9 @@ addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [ addFunction(BUILTIN_SHAPES, [], { positionalParams: [], restParam: Effect.ConditionallyMutate, - returnType: {kind: 'Poly'}, + returnType: {kind: 'Object', shapeId: BuiltInMixedReadonlyId}, calleeEffect: Effect.ConditionallyMutate, - returnValueKind: ValueKind.Mutable, + returnValueKind: ValueKind.Frozen, noAlias: true, mutableOnlyIfOperandsAreMutable: true, }), From c19f052b2d1b167f4a0541d2215148b2f35f7685 Mon Sep 17 00:00:00 2001 From: Mofei Zhang Date: Thu, 13 Mar 2025 11:52:25 -0400 Subject: [PATCH 5/5] [compiler][ez] Move compiler gating tests Move all gating tests to `gating/` --- .../src/HIR/Environment.ts | 2 +- .../arrow-function-expr-gating-test.expect.md | 35 ---------- .../arrow-function-expr-gating-test.js | 4 -- ...codegen-emit-imports-same-source.expect.md | 2 +- .../codegen-instrument-forget-test.expect.md | 4 +- ...gating-test-export-function-and-default.js | 14 ---- .../arrow-function-expr-gating-test.expect.md | 50 +++++++++++++++ .../gating/arrow-function-expr-gating-test.js | 10 +++ ...en-instrument-forget-gating-test.expect.md | 64 +++++++++++++++---- .../codegen-instrument-forget-gating-test.js | 15 ++++- ...component-syntax-ref-gating.flow.expect.md | 0 .../error.component-syntax-ref-gating.flow.js | 0 .../error.gating-hoisting.expect.md | 0 .../{ => gating}/error.gating-hoisting.js | 0 .../error.gating-use-before-decl.expect.md | 0 .../error.gating-use-before-decl.js | 0 ...ccess-function-name-in-component.expect.md | 0 ...ating-access-function-name-in-component.js | 0 ...ng-preserves-function-properties.expect.md | 0 .../gating-preserves-function-properties.tsx | 0 ...ing-test-export-default-function.expect.md | 14 +++- .../gating-test-export-default-function.js | 5 ++ ...test-export-function-and-default.expect.md | 63 ++++++++++++++---- ...gating-test-export-function-and-default.js | 26 ++++++++ .../gating-test-export-function.expect.md | 14 +++- .../gating-test-export-function.js | 5 ++ .../{ => gating}/gating-test.expect.md | 14 +++- .../compiler/{ => gating}/gating-test.js | 5 ++ ...with-hoisted-type-reference.flow.expect.md | 14 +++- ...gating-with-hoisted-type-reference.flow.js | 5 ++ ...ion-expression-React-memo-gating.expect.md | 5 +- ...r-function-expression-React-memo-gating.js | 0 ...expr-export-default-gating-test.expect.md} | 21 +++--- ...i-arrow-expr-export-default-gating-test.js | 11 ++++ ...ti-arrow-expr-export-gating-test.expect.md | 41 ++++++++---- .../multi-arrow-expr-export-gating-test.js | 16 +++++ .../multi-arrow-expr-gating-test.expect.md} | 49 +++++++++----- .../gating/multi-arrow-expr-gating-test.js | 18 ++++++ ...mport-without-compiled-functions.expect.md | 0 ...ating-import-without-compiled-functions.js | 0 ...i-arrow-expr-export-default-gating-test.js | 9 --- .../multi-arrow-expr-export-gating-test.js | 9 --- .../compiler/multi-arrow-expr-gating-test.js | 11 ---- .../packages/snap/src/SproutTodoFilter.ts | 13 +--- 44 files changed, 402 insertions(+), 166 deletions(-) delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.expect.md delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.js delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function-and-default.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.js rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/codegen-instrument-forget-gating-test.expect.md (57%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/codegen-instrument-forget-gating-test.js (52%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/error.component-syntax-ref-gating.flow.expect.md (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/error.component-syntax-ref-gating.flow.js (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/error.gating-hoisting.expect.md (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/error.gating-hoisting.js (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/error.gating-use-before-decl.expect.md (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/error.gating-use-before-decl.js (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-access-function-name-in-component.expect.md (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-access-function-name-in-component.js (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-preserves-function-properties.expect.md (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-preserves-function-properties.tsx (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-test-export-default-function.expect.md (86%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-test-export-default-function.js (76%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-test-export-function-and-default.expect.md (51%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.js rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-test-export-function.expect.md (86%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-test-export-function.js (77%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-test.expect.md (86%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-test.js (75%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-with-hoisted-type-reference.flow.expect.md (79%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/gating-with-hoisted-type-reference.flow.js (69%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/infer-function-expression-React-memo-gating.expect.md (80%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/infer-function-expression-React-memo-gating.js (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{multi-arrow-expr-gating-test.expect.md => gating/multi-arrow-expr-export-default-gating-test.expect.md} (69%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.js rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/multi-arrow-expr-export-gating-test.expect.md (57%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.js rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{multi-arrow-expr-export-default-gating-test.expect.md => gating/multi-arrow-expr-gating-test.expect.md} (50%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.js rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/repro-no-gating-import-without-compiled-functions.expect.md (100%) rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{ => gating}/repro-no-gating-import-without-compiled-functions.js (100%) delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-default-gating-test.js delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-gating-test.js delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-gating-test.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index 7dc9e044bfb7..785240653e0d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -663,7 +663,7 @@ const testComplexConfigDefaults: PartialEnvironmentConfig = { source: 'react-compiler-runtime', importSpecifierName: 'shouldInstrument', }, - globalGating: '__DEV__', + globalGating: 'DEV', }, enableEmitHookGuards: { source: 'react-compiler-runtime', diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.expect.md deleted file mode 100644 index 25feb6fd64d1..000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.expect.md +++ /dev/null @@ -1,35 +0,0 @@ - -## Input - -```javascript -// @gating -const ErrorView = (error, _retry) => ; - -export default ErrorView; - -``` - -## Code - -```javascript -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; -import { c as _c } from "react/compiler-runtime"; // @gating -const ErrorView = isForgetEnabled_Fixtures() - ? (error, _retry) => { - const $ = _c(2); - let t0; - if ($[0] !== error) { - t0 = ; - $[0] = error; - $[1] = t0; - } else { - t0 = $[1]; - } - return t0; - } - : (error, _retry) => ; - -export default ErrorView; - -``` - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.js deleted file mode 100644 index 3debc725be2b..000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.js +++ /dev/null @@ -1,4 +0,0 @@ -// @gating -const ErrorView = (error, _retry) => ; - -export default ErrorView; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-imports-same-source.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-imports-same-source.expect.md index 9a59b36cc0f1..161b42dc6d7d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-imports-same-source.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-emit-imports-same-source.expect.md @@ -21,7 +21,7 @@ import { import { c as _c } from "react/compiler-runtime"; // @enableEmitFreeze @enableEmitInstrumentForget function useFoo(props) { - if (__DEV__ && shouldInstrument) + if (DEV && shouldInstrument) useRenderCounter("useFoo", "/codegen-emit-imports-same-source.ts"); const $ = _c(2); let t0; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-test.expect.md index b5da853b6e5c..319de18794f2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-test.expect.md @@ -28,7 +28,7 @@ import { c as _c } from "react/compiler-runtime"; // @enableEmitInstrumentForget function Bar(props) { "use forget"; - if (__DEV__ && shouldInstrument) + if (DEV && shouldInstrument) useRenderCounter("Bar", "/codegen-instrument-forget-test.ts"); const $ = _c(2); let t0; @@ -48,7 +48,7 @@ function NoForget(props) { function Foo(props) { "use forget"; - if (__DEV__ && shouldInstrument) + if (DEV && shouldInstrument) useRenderCounter("Foo", "/codegen-instrument-forget-test.ts"); const $ = _c(2); let t0; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function-and-default.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function-and-default.js deleted file mode 100644 index abd227e247ea..000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function-and-default.js +++ /dev/null @@ -1,14 +0,0 @@ -// @gating @compilationMode(annotation) -export default function Bar(props) { - 'use forget'; - return
{props.bar}
; -} - -function NoForget(props) { - return {props.noForget}; -} - -export function Foo(props) { - 'use forget'; - return {props.bar}; -} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.expect.md new file mode 100644 index 000000000000..659e2c9ff9e4 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.expect.md @@ -0,0 +1,50 @@ + +## Input + +```javascript +// @gating +import {Stringify} from 'shared-runtime'; +const ErrorView = ({error, _retry}) => ; + +export default ErrorView; + +export const FIXTURE_ENTRYPOINT = { + fn: eval('ErrorView'), + params: [{}], +}; + +``` + +## Code + +```javascript +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; +import { c as _c } from "react/compiler-runtime"; // @gating +import { Stringify } from "shared-runtime"; +const ErrorView = isForgetEnabled_Fixtures() + ? (t0) => { + const $ = _c(2); + const { error } = t0; + let t1; + if ($[0] !== error) { + t1 = ; + $[0] = error; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; + } + : ({ error, _retry }) => ; + +export default ErrorView; + +export const FIXTURE_ENTRYPOINT = { + fn: eval("ErrorView"), + params: [{}], +}; + +``` + +### Eval output +(kind: ok)
{}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.js new file mode 100644 index 000000000000..4c6ab77596ec --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/arrow-function-expr-gating-test.js @@ -0,0 +1,10 @@ +// @gating +import {Stringify} from 'shared-runtime'; +const ErrorView = ({error, _retry}) => ; + +export default ErrorView; + +export const FIXTURE_ENTRYPOINT = { + fn: eval('ErrorView'), + params: [{}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.expect.md similarity index 57% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-gating-test.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.expect.md index fc9247344d56..fe85e38e106a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.expect.md @@ -15,9 +15,22 @@ function NoForget(props) { function Foo(props) { 'use forget'; - return {props.bar}; + if (props.bar < 0) { + return props.children; + } + return ( + + + + ); } +global.DEV = true; +export const FIXTURE_ENTRYPOINT = { + fn: eval('Foo'), + params: [{bar: 2}], +}; + ``` ## Code @@ -29,7 +42,7 @@ import { c as _c } from "react/compiler-runtime"; // @enableEmitInstrumentForget const Bar = isForgetEnabled_Fixtures() ? function Bar(props) { "use forget"; - if (__DEV__ && shouldInstrument) + if (DEV && shouldInstrument) useRenderCounter("Bar", "/codegen-instrument-forget-gating-test.ts"); const $ = _c(2); let t0; @@ -53,23 +66,50 @@ function NoForget(props) { const Foo = isForgetEnabled_Fixtures() ? function Foo(props) { "use forget"; - if (__DEV__ && shouldInstrument) + if (DEV && shouldInstrument) useRenderCounter("Foo", "/codegen-instrument-forget-gating-test.ts"); - const $ = _c(2); - let t0; - if ($[0] !== props.bar) { - t0 = {props.bar}; - $[0] = props.bar; + const $ = _c(3); + if (props.bar < 0) { + return props.children; + } + + const t0 = props.bar - 1; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = ; + $[0] = t1; + } else { + t1 = $[0]; + } + let t2; + if ($[1] !== t0) { + t2 = {t1}; $[1] = t0; + $[2] = t2; } else { - t0 = $[1]; + t2 = $[2]; } - return t0; + return t2; } : function Foo(props) { "use forget"; - return {props.bar}; + if (props.bar < 0) { + return props.children; + } + return ( + + + + ); }; +global.DEV = true; +export const FIXTURE_ENTRYPOINT = { + fn: eval("Foo"), + params: [{ bar: 2 }], +}; + ``` - \ No newline at end of file + +### Eval output +(kind: ok)
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.js similarity index 52% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-gating-test.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.js index dffb8ce79535..6730630ac388 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/codegen-instrument-forget-gating-test.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/codegen-instrument-forget-gating-test.js @@ -11,5 +11,18 @@ function NoForget(props) { function Foo(props) { 'use forget'; - return {props.bar}; + if (props.bar < 0) { + return props.children; + } + return ( + + + + ); } + +global.DEV = true; +export const FIXTURE_ENTRYPOINT = { + fn: eval('Foo'), + params: [{bar: 2}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.component-syntax-ref-gating.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.expect.md similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.component-syntax-ref-gating.flow.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.expect.md diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.component-syntax-ref-gating.flow.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.component-syntax-ref-gating.flow.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.gating-hoisting.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.expect.md similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.gating-hoisting.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.expect.md diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.gating-hoisting.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.gating-hoisting.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.gating-use-before-decl.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.expect.md similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.gating-use-before-decl.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.expect.md diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.gating-use-before-decl.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.gating-use-before-decl.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-access-function-name-in-component.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-access-function-name-in-component.expect.md similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-access-function-name-in-component.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-access-function-name-in-component.expect.md diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-access-function-name-in-component.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-access-function-name-in-component.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-access-function-name-in-component.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-access-function-name-in-component.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-preserves-function-properties.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-preserves-function-properties.expect.md similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-preserves-function-properties.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-preserves-function-properties.expect.md diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-preserves-function-properties.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-preserves-function-properties.tsx similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-preserves-function-properties.tsx rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-preserves-function-properties.tsx diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-default-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.expect.md similarity index 86% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-default-function.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.expect.md index 92830938b592..4e4a35c37557 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-default-function.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.expect.md @@ -17,6 +17,11 @@ function Foo(props) { return {props.bar}; } +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; + ``` ## Code @@ -66,5 +71,12 @@ const Foo = isForgetEnabled_Fixtures() return {props.bar}; }; +export const FIXTURE_ENTRYPOINT = { + fn: eval("Bar"), + params: [{ bar: 2 }], +}; + ``` - \ No newline at end of file + +### Eval output +(kind: ok)
2
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-default-function.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.js similarity index 76% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-default-function.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.js index 5ac847953d58..1ec9a902b244 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-default-function.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-default-function.js @@ -12,3 +12,8 @@ function Foo(props) { 'use forget'; return {props.bar}; } + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function-and-default.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.expect.md similarity index 51% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function-and-default.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.expect.md index ea3d320a3ec4..95c629aa11f9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function-and-default.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.expect.md @@ -12,11 +12,23 @@ function NoForget(props) { return {props.noForget}; } -export function Foo(props) { +function Foo(props) { 'use forget'; - return {props.bar}; + if (props.bar < 0) { + return props.children; + } + return ( + + + + ); } +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; + ``` ## Code @@ -47,25 +59,50 @@ export default Bar; function NoForget(props) { return {props.noForget}; } - -export const Foo = isForgetEnabled_Fixtures() +const Foo = isForgetEnabled_Fixtures() ? function Foo(props) { "use forget"; - const $ = _c(2); - let t0; - if ($[0] !== props.bar) { - t0 = {props.bar}; - $[0] = props.bar; + const $ = _c(3); + if (props.bar < 0) { + return props.children; + } + + const t0 = props.bar - 1; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = ; + $[0] = t1; + } else { + t1 = $[0]; + } + let t2; + if ($[1] !== t0) { + t2 = {t1}; $[1] = t0; + $[2] = t2; } else { - t0 = $[1]; + t2 = $[2]; } - return t0; + return t2; } : function Foo(props) { "use forget"; - return {props.bar}; + if (props.bar < 0) { + return props.children; + } + return ( + + + + ); }; +export const FIXTURE_ENTRYPOINT = { + fn: eval("Bar"), + params: [{ bar: 2 }], +}; + ``` - \ No newline at end of file + +### Eval output +(kind: ok)
2
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.js new file mode 100644 index 000000000000..5b7f7c81b74c --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-and-default.js @@ -0,0 +1,26 @@ +// @gating @compilationMode(annotation) +export default function Bar(props) { + 'use forget'; + return
{props.bar}
; +} + +function NoForget(props) { + return {props.noForget}; +} + +function Foo(props) { + 'use forget'; + if (props.bar < 0) { + return props.children; + } + return ( + + + + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.expect.md similarity index 86% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.expect.md index caaf1b447220..ab31d2939d44 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.expect.md @@ -17,6 +17,11 @@ export function Foo(props) { return {props.bar}; } +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; + ``` ## Code @@ -66,5 +71,12 @@ export const Foo = isForgetEnabled_Fixtures() return {props.bar}; }; +export const FIXTURE_ENTRYPOINT = { + fn: eval("Bar"), + params: [{ bar: 2 }], +}; + ``` - \ No newline at end of file + +### Eval output +(kind: ok)
2
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.js similarity index 77% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.js index 45ce5cbdb56a..5bcf9ac67d76 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test-export-function.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function.js @@ -12,3 +12,8 @@ export function Foo(props) { 'use forget'; return {props.bar}; } + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.expect.md similarity index 86% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.expect.md index 5166dab4184b..e68e4840cf52 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.expect.md @@ -17,6 +17,11 @@ function Foo(props) { return {props.bar}; } +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; + ``` ## Code @@ -65,5 +70,12 @@ const Foo = isForgetEnabled_Fixtures() return {props.bar}; }; +export const FIXTURE_ENTRYPOINT = { + fn: eval("Bar"), + params: [{ bar: 2 }], +}; + ``` - \ No newline at end of file + +### Eval output +(kind: ok)
2
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.js similarity index 75% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.js index bf031d8b1562..3bb92654d97d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-test.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test.js @@ -12,3 +12,8 @@ function Foo(props) { 'use forget'; return {props.bar}; } + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Bar'), + params: [{bar: 2}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-with-hoisted-type-reference.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.expect.md similarity index 79% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-with-hoisted-type-reference.flow.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.expect.md index e87606372da0..26c6e510d147 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-with-hoisted-type-reference.flow.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.expect.md @@ -13,6 +13,11 @@ component Component(value: string) { export default memo(Component); +export const FIXTURE_ENTRYPOINT = { + fn: eval('Component'), + params: [{value: 'foo'}], +}; + ``` ## Code @@ -43,5 +48,12 @@ const Component = isForgetEnabled_Fixtures() export default memo(Component); +export const FIXTURE_ENTRYPOINT = { + fn: eval("Component"), + params: [{ value: "foo" }], +}; + ``` - \ No newline at end of file + +### Eval output +(kind: ok)
foo
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-with-hoisted-type-reference.flow.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.js similarity index 69% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-with-hoisted-type-reference.flow.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.js index 86433ec50d34..1fe640b6934a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating-with-hoisted-type-reference.flow.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-with-hoisted-type-reference.flow.js @@ -8,3 +8,8 @@ component Component(value: string) { } export default memo(Component); + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Component'), + params: [{value: 'foo'}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-function-expression-React-memo-gating.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.expect.md similarity index 80% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-function-expression-React-memo-gating.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.expect.md index 6524c9d20288..4931b87b0176 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-function-expression-React-memo-gating.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.expect.md @@ -35,4 +35,7 @@ export default React.forwardRef( ); ``` - \ No newline at end of file + +### Eval output +(kind: exception) Fixture not implemented +logs: ['forwardRef render functions accept exactly two parameters: props and ref. %s','Did you forget to use the ref parameter?'] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-function-expression-React-memo-gating.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-function-expression-React-memo-gating.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/infer-function-expression-React-memo-gating.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.expect.md similarity index 69% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-gating-test.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.expect.md index 92d3aa72093e..b3dc5011f9ca 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.expect.md @@ -3,17 +3,17 @@ ```javascript // @gating -const ErrorView = (error, _retry) => ; +import {Stringify} from 'shared-runtime'; -const Renderer = props => ( +const ErrorView = (error, _retry) => ; + +export default props => ( ); -export default Renderer; - ``` ## Code @@ -21,12 +21,14 @@ export default Renderer; ```javascript import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; import { c as _c } from "react/compiler-runtime"; // @gating +import { Stringify } from "shared-runtime"; + const ErrorView = isForgetEnabled_Fixtures() ? (error, _retry) => { const $ = _c(2); let t0; if ($[0] !== error) { - t0 = ; + t0 = ; $[0] = error; $[1] = t0; } else { @@ -34,9 +36,9 @@ const ErrorView = isForgetEnabled_Fixtures() } return t0; } - : (error, _retry) => ; + : (error, _retry) => ; -const Renderer = isForgetEnabled_Fixtures() +export default isForgetEnabled_Fixtures() ? (props) => { const $ = _c(1); let t0; @@ -59,7 +61,8 @@ const Renderer = isForgetEnabled_Fixtures() ); -export default Renderer; ``` - \ No newline at end of file + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.js new file mode 100644 index 000000000000..e856a290f7f4 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-default-gating-test.js @@ -0,0 +1,11 @@ +// @gating +import {Stringify} from 'shared-runtime'; + +const ErrorView = (error, _retry) => ; + +export default props => ( + + + + +); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.expect.md similarity index 57% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-gating-test.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.expect.md index 61d87a11cd39..ea2a1c154129 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.expect.md @@ -3,15 +3,22 @@ ```javascript // @gating -const ErrorView = (error, _retry) => ; +import {Stringify} from 'shared-runtime'; + +const ErrorView = (error, _retry) => ; export const Renderer = props => ( - - +
+ - +
); +export const FIXTURE_ENTRYPOINT = { + fn: eval('Renderer'), + params: [{}], +}; + ``` ## Code @@ -19,12 +26,14 @@ export const Renderer = props => ( ```javascript import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; import { c as _c } from "react/compiler-runtime"; // @gating +import { Stringify } from "shared-runtime"; + const ErrorView = isForgetEnabled_Fixtures() ? (error, _retry) => { const $ = _c(2); let t0; if ($[0] !== error) { - t0 = ; + t0 = ; $[0] = error; $[1] = t0; } else { @@ -32,7 +41,7 @@ const ErrorView = isForgetEnabled_Fixtures() } return t0; } - : (error, _retry) => ; + : (error, _retry) => ; export const Renderer = isForgetEnabled_Fixtures() ? (props) => { @@ -40,10 +49,10 @@ export const Renderer = isForgetEnabled_Fixtures() let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = ( - - +
+ - +
); $[0] = t0; } else { @@ -52,11 +61,17 @@ export const Renderer = isForgetEnabled_Fixtures() return t0; } : (props) => ( - - +
+ - +
); +export const FIXTURE_ENTRYPOINT = { + fn: eval("Renderer"), + params: [{}], +}; ``` - \ No newline at end of file + +### Eval output +(kind: ok)
{"error":{}}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.js new file mode 100644 index 000000000000..19152d683934 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-export-gating-test.js @@ -0,0 +1,16 @@ +// @gating +import {Stringify} from 'shared-runtime'; + +const ErrorView = (error, _retry) => ; + +export const Renderer = props => ( +
+ + +
+); + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Renderer'), + params: [{}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-default-gating-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.expect.md similarity index 50% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-default-gating-test.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.expect.md index a9a90eaa21e7..cc5195edb222 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-default-gating-test.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.expect.md @@ -3,15 +3,24 @@ ```javascript // @gating -const ErrorView = (error, _retry) => ; +import {Stringify} from 'shared-runtime'; -export default Renderer = props => ( - - +const ErrorView = (error, _retry) => ; + +const Renderer = props => ( +
+ - +
); +export default Renderer; + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Renderer'), + params: [{}], +}; + ``` ## Code @@ -19,12 +28,14 @@ export default Renderer = props => ( ```javascript import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; import { c as _c } from "react/compiler-runtime"; // @gating +import { Stringify } from "shared-runtime"; + const ErrorView = isForgetEnabled_Fixtures() ? (error, _retry) => { const $ = _c(2); let t0; if ($[0] !== error) { - t0 = ; + t0 = ; $[0] = error; $[1] = t0; } else { @@ -32,18 +43,18 @@ const ErrorView = isForgetEnabled_Fixtures() } return t0; } - : (error, _retry) => ; + : (error, _retry) => ; -export default Renderer = isForgetEnabled_Fixtures() +const Renderer = isForgetEnabled_Fixtures() ? (props) => { const $ = _c(1); let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = ( - - +
+ - +
); $[0] = t0; } else { @@ -52,11 +63,19 @@ export default Renderer = isForgetEnabled_Fixtures() return t0; } : (props) => ( - - +
+ - +
); +export default Renderer; + +export const FIXTURE_ENTRYPOINT = { + fn: eval("Renderer"), + params: [{}], +}; ``` - \ No newline at end of file + +### Eval output +(kind: ok)
{"error":{}}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.js new file mode 100644 index 000000000000..ae4fd8dc94cf --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/multi-arrow-expr-gating-test.js @@ -0,0 +1,18 @@ +// @gating +import {Stringify} from 'shared-runtime'; + +const ErrorView = (error, _retry) => ; + +const Renderer = props => ( +
+ + +
+); + +export default Renderer; + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Renderer'), + params: [{}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-no-gating-import-without-compiled-functions.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/repro-no-gating-import-without-compiled-functions.expect.md similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-no-gating-import-without-compiled-functions.expect.md rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/repro-no-gating-import-without-compiled-functions.expect.md diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-no-gating-import-without-compiled-functions.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/repro-no-gating-import-without-compiled-functions.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-no-gating-import-without-compiled-functions.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/repro-no-gating-import-without-compiled-functions.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-default-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-default-gating-test.js deleted file mode 100644 index 0c07f55b1010..000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-default-gating-test.js +++ /dev/null @@ -1,9 +0,0 @@ -// @gating -const ErrorView = (error, _retry) => ; - -export default Renderer = props => ( - - - - -); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-gating-test.js deleted file mode 100644 index ac71868f33fa..000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-export-gating-test.js +++ /dev/null @@ -1,9 +0,0 @@ -// @gating -const ErrorView = (error, _retry) => ; - -export const Renderer = props => ( - - - - -); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-gating-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-gating-test.js deleted file mode 100644 index 72109d6f8167..000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/multi-arrow-expr-gating-test.js +++ /dev/null @@ -1,11 +0,0 @@ -// @gating -const ErrorView = (error, _retry) => ; - -const Renderer = props => ( - - - - -); - -export default Renderer; diff --git a/compiler/packages/snap/src/SproutTodoFilter.ts b/compiler/packages/snap/src/SproutTodoFilter.ts index 0817bbf89c58..7c487869d333 100644 --- a/compiler/packages/snap/src/SproutTodoFilter.ts +++ b/compiler/packages/snap/src/SproutTodoFilter.ts @@ -222,7 +222,6 @@ const skipFilter = new Set([ 'array-at-mutate-after-capture', 'array-join', 'array-push-effect', - 'arrow-function-expr-gating-test', 'assignment-in-nested-if', 'await-side-effecting-promise', 'await', @@ -335,16 +334,10 @@ const skipFilter = new Set([ 'babel-existing-react-import', 'babel-existing-react-kitchensink-import', 'call', - 'codegen-instrument-forget-gating-test', 'codegen-instrument-forget-test', 'conditional-on-mutable', 'constructor', 'frozen-after-alias', - 'gating-test-export-default-function', - 'gating-test-export-function-and-default', - 'gating-test-export-function', - 'gating-test', - 'gating-with-hoisted-type-reference.flow', 'hook-call', 'hooks-freeze-arguments', 'hooks-freeze-possibly-mutable-arguments', @@ -352,8 +345,6 @@ const skipFilter = new Set([ 'independent', 'interdependent-across-if', 'interdependent', - 'multi-arrow-expr-export-gating-test', - 'multi-arrow-expr-gating-test', 'mutable-liverange-loop', 'sequence-expression', 'ssa-call-jsx-2', @@ -361,7 +352,6 @@ const skipFilter = new Set([ 'ssa-newexpression', 'ssa-shadowing', 'template-literal', - 'multi-arrow-expr-export-default-gating-test', // works, but appears differently when printing // due to optional function argument @@ -407,7 +397,6 @@ const skipFilter = new Set([ 'infer-functions-hook-with-hook-call', 'infer-functions-hook-with-jsx', 'infer-function-expression-component', - 'infer-function-expression-React-memo-gating', 'infer-skip-components-without-hooks-or-jsx', 'class-component-with-render-helper', 'fbt/fbtparam-with-jsx-element-content', @@ -422,7 +411,7 @@ const skipFilter = new Set([ 'transitive-freeze-function-expressions', // nothing to compile/run - 'repro-no-gating-import-without-compiled-functions', + 'gating/repro-no-gating-import-without-compiled-functions', // TODOs 'rules-of-hooks/todo.bail.rules-of-hooks-279ac76f53af',