diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts index b59e7381b6365..0589e0208370f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -36,12 +36,14 @@ import { ObjectProperty, ObjectPropertyKey, Place, + PropertyLiteral, ReturnTerminal, SourceLocation, SpreadPattern, ThrowTerminal, Type, makeInstructionId, + makePropertyLiteral, makeType, promoteTemporary, } from './HIR'; @@ -2019,11 +2021,11 @@ function lowerExpression( }); // Save the result back to the property - if (typeof property === 'string') { + if (typeof property === 'string' || typeof property === 'number') { return { kind: 'PropertyStore', object: {...object}, - property, + property: makePropertyLiteral(property), value: {...newValuePlace}, loc: leftExpr.node.loc ?? GeneratedSource, }; @@ -2318,11 +2320,11 @@ function lowerExpression( const argument = expr.get('argument'); if (argument.isMemberExpression()) { const {object, property} = lowerMemberExpression(builder, argument); - if (typeof property === 'string') { + if (typeof property === 'string' || typeof property === 'number') { return { kind: 'PropertyDelete', object, - property, + property: makePropertyLiteral(property), loc: exprLoc, }; } else { @@ -2429,11 +2431,11 @@ function lowerExpression( // Save the result back to the property let newValuePlace; - if (typeof property === 'string') { + if (typeof property === 'string' || typeof property === 'number') { newValuePlace = lowerValueToTemporary(builder, { kind: 'PropertyStore', object: {...object}, - property, + property: makePropertyLiteral(property), value: {...updatedValue}, loc: leftExpr.node.loc ?? GeneratedSource, }); @@ -3059,7 +3061,7 @@ function lowerArguments( type LoweredMemberExpression = { object: Place; - property: Place | string; + property: Place | string | number; value: InstructionValue; }; function lowerMemberExpression( @@ -3074,8 +3076,13 @@ function lowerMemberExpression( const object = loweredObject ?? lowerExpressionToTemporary(builder, objectNode); - if (!expr.node.computed) { - if (!propertyNode.isIdentifier()) { + if (!expr.node.computed || expr.node.property.type === 'NumericLiteral') { + let property: PropertyLiteral; + if (propertyNode.isIdentifier()) { + property = makePropertyLiteral(propertyNode.node.name); + } else if (propertyNode.isNumericLiteral()) { + property = makePropertyLiteral(propertyNode.node.value); + } else { builder.errors.push({ reason: `(BuildHIR::lowerMemberExpression) Handle ${propertyNode.type} property`, severity: ErrorSeverity.Todo, @@ -3091,10 +3098,10 @@ function lowerMemberExpression( const value: InstructionValue = { kind: 'PropertyLoad', object: {...object}, - property: propertyNode.node.name, + property, loc: exprLoc, }; - return {object, property: propertyNode.node.name, value}; + return {object, property, value}; } else { if (!propertyNode.isExpression()) { builder.errors.push({ @@ -3212,7 +3219,7 @@ function lowerJsxMemberExpression( return lowerValueToTemporary(builder, { kind: 'PropertyLoad', object: objectPlace, - property, + property: makePropertyLiteral(property), loc, }); } @@ -3625,8 +3632,25 @@ function lowerAssignment( const lvalue = lvaluePath as NodePath; const property = lvalue.get('property'); const object = lowerExpressionToTemporary(builder, lvalue.get('object')); - if (!lvalue.node.computed) { - if (!property.isIdentifier()) { + if (!lvalue.node.computed || lvalue.get('property').isNumericLiteral()) { + let temporary; + if (property.isIdentifier()) { + temporary = lowerValueToTemporary(builder, { + kind: 'PropertyStore', + object, + property: makePropertyLiteral(property.node.name), + value, + loc, + }); + } else if (property.isNumericLiteral()) { + temporary = lowerValueToTemporary(builder, { + kind: 'PropertyStore', + object, + property: makePropertyLiteral(property.node.value), + value, + loc, + }); + } else { builder.errors.push({ reason: `(BuildHIR::lowerAssignment) Handle ${property.type} properties in MemberExpression`, severity: ErrorSeverity.Todo, @@ -3635,13 +3659,6 @@ function lowerAssignment( }); return {kind: 'UnsupportedNode', node: lvalueNode, loc}; } - const temporary = lowerValueToTemporary(builder, { - kind: 'PropertyStore', - object, - property: property.node.name, - value, - loc, - }); return {kind: 'LoadLocal', place: temporary, loc: temporary.loc}; } else { if (!property.isExpression()) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts index 1fc38fd9da2c5..5e8d65fc96eac 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts @@ -18,6 +18,7 @@ import { IdentifierId, InstructionId, InstructionValue, + PropertyLiteral, ReactiveScopeDependency, ScopeId, } from './HIR'; @@ -172,8 +173,8 @@ export type BlockInfo = { * and make computing sets intersections simpler. */ type RootNode = { - properties: Map; - optionalProperties: Map; + properties: Map; + optionalProperties: Map; parent: null; // Recorded to make later computations simpler fullPath: ReactiveScopeDependency; @@ -183,8 +184,8 @@ type RootNode = { type PropertyPathNode = | { - properties: Map; - optionalProperties: Map; + properties: Map; + optionalProperties: Map; parent: PropertyPathNode; fullPath: ReactiveScopeDependency; hasOptional: boolean; diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectOptionalChainDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectOptionalChainDependencies.ts index e4a7d14570a93..2f7b742f1a86c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectOptionalChainDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectOptionalChainDependencies.ts @@ -16,6 +16,7 @@ import { DependencyPathEntry, Instruction, Terminal, + PropertyLiteral, } from './HIR'; import {printIdentifier} from './PrintHIR'; @@ -157,7 +158,7 @@ function matchOptionalTestBlock( blocks: ReadonlyMap, ): { consequentId: IdentifierId; - property: string; + property: PropertyLiteral; propertyId: IdentifierId; storeLocalInstr: Instruction; consequentGoto: BlockId; diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/DeriveMinimalDependenciesHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/DeriveMinimalDependenciesHIR.ts index b83cf9677cb99..7819ab39b2c69 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/DeriveMinimalDependenciesHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/DeriveMinimalDependenciesHIR.ts @@ -10,6 +10,7 @@ import { DependencyPathEntry, GeneratedSource, Identifier, + PropertyLiteral, ReactiveScopeDependency, } from '../HIR'; import {printIdentifier} from '../HIR/PrintHIR'; @@ -303,7 +304,7 @@ function merge( } type TreeNode = { - properties: Map>; + properties: Map>; accessType: T; }; type HoistableNode = TreeNode<'Optional' | 'NonNull'>; @@ -359,7 +360,7 @@ function printSubtree( function makeOrMergeProperty( node: DependencyNode, - property: string, + property: PropertyLiteral, accessType: PropertyAccessType, ): DependencyNode { let child = node.properties.get(property); diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts index fb7e84b11604a..27cf17fd930b3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts @@ -941,7 +941,7 @@ export type InstructionValue = | { kind: 'PropertyStore'; object: Place; - property: string; + property: PropertyLiteral; value: Place; loc: SourceLocation; } @@ -951,7 +951,7 @@ export type InstructionValue = | { kind: 'PropertyDelete'; object: Place; - property: string; + property: PropertyLiteral; loc: SourceLocation; } @@ -1125,7 +1125,7 @@ export type StoreLocal = { export type PropertyLoad = { kind: 'PropertyLoad'; object: Place; - property: string; + property: PropertyLiteral; loc: SourceLocation; }; @@ -1507,7 +1507,17 @@ export type ReactiveScopeDeclaration = { scope: ReactiveScope; // the scope in which the variable was originally declared }; -export type DependencyPathEntry = {property: string; optional: boolean}; +const opaquePropertyLiteral = Symbol(); +export type PropertyLiteral = (string | number) & { + [opaquePropertyLiteral]: 'PropertyLiteral'; +}; +export function makePropertyLiteral(value: string | number): PropertyLiteral { + return value as PropertyLiteral; +} +export type DependencyPathEntry = { + property: PropertyLiteral; + optional: boolean; +}; export type DependencyPath = Array; export type ReactiveScopeDependency = { identifier: Identifier; diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/PropagateScopeDependenciesHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/PropagateScopeDependenciesHIR.ts index 8046363ad7dd7..07d16b2e5a532 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/PropagateScopeDependenciesHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/PropagateScopeDependenciesHIR.ts @@ -34,6 +34,7 @@ import { ReactiveValue, ReactiveScopeBlock, PrunedReactiveScopeBlock, + PropertyLiteral, } from './HIR'; import { collectHoistablePropertyLoads, @@ -417,7 +418,7 @@ function collectTemporariesSidemapImpl( function getProperty( object: Place, - propertyName: string, + propertyName: PropertyLiteral, optional: boolean, temporaries: ReadonlyMap, ): ReactiveScopeDependency { @@ -618,7 +619,11 @@ class Context { ); } - visitProperty(object: Place, property: string, optional: boolean): void { + visitProperty( + object: Place, + property: PropertyLiteral, + optional: boolean, + ): void { const nextDependency = getProperty( object, property, 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 55cd1531aee93..a67b580a6728b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Types.ts @@ -6,6 +6,7 @@ */ import {CompilerError} from '../CompilerError'; +import {PropertyLiteral} from './HIR'; export type BuiltInType = PrimitiveType | FunctionType | ObjectType; @@ -59,7 +60,7 @@ export type PropType = { kind: 'Property'; objectType: Type; objectName: string; - propertyName: string; + propertyName: PropertyLiteral; }; export type ObjectMethod = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/DropManualMemoization.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/DropManualMemoization.ts index 4dcdc21e15ac5..8d123845c3739 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/DropManualMemoization.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/DropManualMemoization.ts @@ -145,9 +145,10 @@ function collectTemporaries( } case 'PropertyLoad': { if (sidemap.react.has(value.object.identifier.id)) { - if (value.property === 'useMemo' || value.property === 'useCallback') { + const property = value.property; + if (property === 'useMemo' || property === 'useCallback') { sidemap.manualMemos.set(instr.lvalue.identifier.id, { - kind: value.property, + kind: property as 'useMemo' | 'useCallback', loadInstr: instr as TInstruction, }); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/ConstantPropagation.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/ConstantPropagation.ts index a9f62c1986efa..07cd419230ac4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/ConstantPropagation.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/ConstantPropagation.ts @@ -19,6 +19,7 @@ import { Primitive, assertConsistentIdentifiers, assertTerminalSuccessorsExist, + makePropertyLiteral, markInstructionIds, markPredecessors, mergeConsecutiveBlocks, @@ -238,13 +239,14 @@ function evaluateInstruction( if ( property !== null && property.kind === 'Primitive' && - typeof property.value === 'string' && - isValidIdentifier(property.value) + ((typeof property.value === 'string' && + isValidIdentifier(property.value)) || + typeof property.value === 'number') ) { const nextValue: InstructionValue = { kind: 'PropertyLoad', loc: value.loc, - property: property.value, + property: makePropertyLiteral(property.value), object: value.object, }; instr.value = nextValue; @@ -256,13 +258,14 @@ function evaluateInstruction( if ( property !== null && property.kind === 'Primitive' && - typeof property.value === 'string' && - isValidIdentifier(property.value) + ((typeof property.value === 'string' && + isValidIdentifier(property.value)) || + typeof property.value === 'number') ) { const nextValue: InstructionValue = { kind: 'PropertyStore', loc: value.loc, - property: property.value, + property: makePropertyLiteral(property.value), object: value.object, value: value.value, }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/InlineJsxTransform.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/InlineJsxTransform.ts index 999d9d1a9cd70..5a469ad480b82 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/InlineJsxTransform.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/InlineJsxTransform.ts @@ -21,6 +21,7 @@ import { InstructionKind, JsxAttribute, makeInstructionId, + makePropertyLiteral, ObjectProperty, Phi, Place, @@ -444,7 +445,7 @@ function createSymbolProperty( value: { kind: 'PropertyLoad', object: {...symbolInstruction.lvalue}, - property: 'for', + property: makePropertyLiteral('for'), loc: instr.value.loc, }, loc: instr.loc, diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts index 5b700b23b4049..b636c7b1718cc 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/LowerContextAccess.ts @@ -23,6 +23,7 @@ import { isUseContextHookType, makeBlockId, makeInstructionId, + makePropertyLiteral, makeType, markInstructionIds, promoteTemporary, @@ -195,7 +196,7 @@ function emitPropertyLoad( const loadProp: PropertyLoad = { kind: 'PropertyLoad', object, - property, + property: makePropertyLiteral(property), loc: GeneratedSource, }; const element: Place = createTemporaryPlace(env, GeneratedSource); diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts index 53a9cabdf4cd0..79fdaa313b405 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -1967,12 +1967,22 @@ function codegenInstructionValue( break; } case 'PropertyStore': { - value = t.assignmentExpression( - '=', - t.memberExpression( + let memberExpr; + if (typeof instrValue.property === 'string') { + memberExpr = t.memberExpression( codegenPlaceToExpression(cx, instrValue.object), t.identifier(instrValue.property), - ), + ); + } else { + memberExpr = t.memberExpression( + codegenPlaceToExpression(cx, instrValue.object), + t.numericLiteral(instrValue.property), + true, + ); + } + value = t.assignmentExpression( + '=', + memberExpr, codegenPlaceToExpression(cx, instrValue.value), ); break; @@ -1983,21 +1993,36 @@ function codegenInstructionValue( * We currently only lower single chains of optional memberexpr. * (See BuildHIR.ts for more detail.) */ - value = t.memberExpression( - object, - t.identifier(instrValue.property), - undefined, - ); + if (typeof instrValue.property === 'string') { + value = t.memberExpression( + object, + t.identifier(instrValue.property), + undefined, + ); + } else { + value = t.memberExpression( + object, + t.numericLiteral(instrValue.property), + true, + ); + } break; } case 'PropertyDelete': { - value = t.unaryExpression( - 'delete', - t.memberExpression( + let memberexpr; + if (typeof instrValue.property === 'string') { + memberexpr = t.memberExpression( codegenPlaceToExpression(cx, instrValue.object), t.identifier(instrValue.property), - ), - ); + ); + } else { + memberexpr = t.memberExpression( + codegenPlaceToExpression(cx, instrValue.object), + t.numericLiteral(instrValue.property), + true, + ); + } + value = t.unaryExpression('delete', memberexpr); break; } case 'ComputedStore': { diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PropagateEarlyReturns.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PropagateEarlyReturns.ts index b8ba19628451b..522aaf5a5df28 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PropagateEarlyReturns.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PropagateEarlyReturns.ts @@ -17,6 +17,7 @@ import { ReactiveStatement, ReactiveTerminalStatement, makeInstructionId, + makePropertyLiteral, promoteTemporary, } from '../HIR'; import {createTemporaryPlace} from '../HIR/HIRBuilder'; @@ -189,7 +190,7 @@ class Transform extends ReactiveFunctionTransform { value: { kind: 'PropertyLoad', object: {...symbolTemp}, - property: 'for', + property: makePropertyLiteral('for'), loc, }, }, diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneInitializationDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneInitializationDependencies.ts index ad6a3a5eeede3..7387a9d9470d9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneInitializationDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneInitializationDependencies.ts @@ -12,6 +12,7 @@ import { IdentifierId, InstructionId, Place, + PropertyLiteral, ReactiveBlock, ReactiveFunction, ReactiveInstruction, @@ -65,13 +66,13 @@ type KindMap = Map; class Visitor extends ReactiveFunctionVisitor { map: KindMap = new Map(); aliases: DisjointSet; - paths: Map>; + paths: Map>; env: Environment; constructor( env: Environment, aliases: DisjointSet, - paths: Map>, + paths: Map>, ) { super(); this.aliases = aliases; @@ -227,9 +228,9 @@ export default function pruneInitializationDependencies( } function update( - map: Map>, + map: Map>, key: IdentifierId, - path: string, + path: PropertyLiteral, value: IdentifierId, ): void { const inner = map.get(key) ?? new Map(); @@ -239,7 +240,7 @@ function update( class AliasVisitor extends ReactiveFunctionVisitor { scopeIdentifiers: DisjointSet = new DisjointSet(); - scopePaths: Map> = new Map(); + scopePaths: Map> = new Map(); override visitInstruction(instr: ReactiveInstruction): void { if ( @@ -280,11 +281,14 @@ class AliasVisitor extends ReactiveFunctionVisitor { function getAliases( fn: ReactiveFunction, -): [DisjointSet, Map>] { +): [ + DisjointSet, + Map>, +] { const visitor = new AliasVisitor(); visitReactiveFunction(fn, visitor, null); let disjoint = visitor.scopeIdentifiers; - let scopePaths = new Map>(); + let scopePaths = new Map>(); for (const [key, value] of visitor.scopePaths) { for (const [path, id] of value) { update( 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 44bf539e847bb..3054a83c76138 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts @@ -14,6 +14,7 @@ import { Identifier, IdentifierId, Instruction, + makePropertyLiteral, makeType, PropType, Type, @@ -335,7 +336,7 @@ function* generateInstructionTypes( kind: 'Property', objectType: value.value.identifier.type, objectName: getName(names, value.value.identifier.id), - propertyName, + propertyName: makePropertyLiteral(propertyName), }); } else { break; @@ -352,7 +353,7 @@ function* generateInstructionTypes( kind: 'Property', objectType: value.value.identifier.type, objectName: getName(names, value.value.identifier.id), - propertyName: property.key.name, + propertyName: makePropertyLiteral(property.key.name), }); } } @@ -453,10 +454,12 @@ class Unifier { return; } const objectType = this.get(tB.objectType); - const propertyType = this.env.getPropertyType( - objectType, - tB.propertyName, - ); + let propertyType; + if (typeof tB.propertyName === 'number') { + propertyType = null; + } else { + propertyType = this.env.getPropertyType(objectType, tB.propertyName); + } if (propertyType !== null) { this.unify(tA, propertyType); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts index 53640da5020bd..ed23693f61439 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts @@ -257,7 +257,9 @@ export function validateHooksUsage(fn: HIRFunction): void { } case 'PropertyLoad': { const objectKind = getKindForPlace(instr.value.object); - const isHookProperty = isHookName(instr.value.property); + const isHookProperty = + typeof instr.value.property === 'string' && + isHookName(instr.value.property); let kind: Kind; switch (objectKind) { case Kind.Error: { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoCapitalizedCalls.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoCapitalizedCalls.ts index 2988d1cc02430..2f9b0a1c7e2ce 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoCapitalizedCalls.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoCapitalizedCalls.ts @@ -61,7 +61,10 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void { } case 'PropertyLoad': { // Start conservative and disallow all capitalized method calls - if (/^[A-Z]/.test(value.property)) { + if ( + typeof value.property === 'string' && + /^[A-Z]/.test(value.property) + ) { capitalizedProperties.set(lvalue.identifier.id, value.property); } break; diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts index 4db8c700f387f..779207b22e646 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts @@ -285,7 +285,7 @@ function validateNoRefAccessInRenderImpl( } case 'ComputedLoad': case 'PropertyLoad': { - if (typeof instr.value.property !== 'string') { + if (instr.value.kind === 'ComputedLoad') { validateNoDirectRefValueAccess(errors, instr.value.property, env); } const objType = env.get(instr.value.object.identifier.id); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/mixedreadonly-mutating-map.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/mixedreadonly-mutating-map.expect.md index 4867388a864d2..6dd05cbe7c619 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/mixedreadonly-mutating-map.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/mixedreadonly-mutating-map.expect.md @@ -125,22 +125,21 @@ function Component(t0) { } else { t1 = $[2]; } - const t2 = jsx[0]; - let t3; - if ($[3] !== t1 || $[4] !== t2) { - t3 = ( + let t2; + if ($[3] !== jsx[0] || $[4] !== t1) { + t2 = ( <> {t1} - {t2} + {jsx[0]} ); - $[3] = t1; - $[4] = t2; - $[5] = t3; + $[3] = jsx[0]; + $[4] = t1; + $[5] = t2; } else { - t3 = $[5]; + t2 = $[5]; } - return t3; + return t2; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-dep-not-recognized.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-dep-not-recognized.expect.md deleted file mode 100644 index bf22790a51c12..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-dep-not-recognized.expect.md +++ /dev/null @@ -1,40 +0,0 @@ - -## Input - -```javascript -// @validatePreserveExistingMemoizationGuarantees - -import {useMemo} from 'react'; -import {makeArray} from 'shared-runtime'; - -// We currently only recognize "hoistable" values (e.g. variable reads -// and property loads from named variables) in the source depslist. -// This makes validation logic simpler and follows the same constraints -// from the eslint react-hooks-deps plugin. -function Foo(props) { - const x = makeArray(props); - // react-hooks-deps lint would already fail here - return useMemo(() => [x[0]], [x[0]]); -} - -export const FIXTURE_ENTRYPOINT = { - fn: Foo, - params: [{val: 1}], -}; - -``` - - -## Error - -``` - 11 | const x = makeArray(props); - 12 | // react-hooks-deps lint would already fail here -> 13 | return useMemo(() => [x[0]], [x[0]]); - | ^^^^ InvalidReact: Expected the dependency list to be an array of simple expressions (e.g. `x`, `x.y.z`, `x?.y?.z`) (13:13) - 14 | } - 15 | - 16 | export const FIXTURE_ENTRYPOINT = { -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-dep-array-literal-access.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-dep-array-literal-access.expect.md new file mode 100644 index 0000000000000..dafe2b63e35ae --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-dep-array-literal-access.expect.md @@ -0,0 +1,71 @@ + +## Input + +```javascript +// @validatePreserveExistingMemoizationGuarantees + +import {useMemo} from 'react'; +import {makeArray} from 'shared-runtime'; + +// We currently only recognize "hoistable" values (e.g. variable reads +// and property loads from named variables) in the source depslist. +// This makes validation logic simpler and follows the same constraints +// from the eslint react-hooks-deps plugin. +function Foo(props) { + const x = makeArray(props); + // react-hooks-deps lint would already fail here + return useMemo(() => [x[0]], [x[0]]); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{val: 1}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees + +import { useMemo } from "react"; +import { makeArray } from "shared-runtime"; + +// We currently only recognize "hoistable" values (e.g. variable reads +// and property loads from named variables) in the source depslist. +// This makes validation logic simpler and follows the same constraints +// from the eslint react-hooks-deps plugin. +function Foo(props) { + const $ = _c(4); + let t0; + if ($[0] !== props) { + t0 = makeArray(props); + $[0] = props; + $[1] = t0; + } else { + t0 = $[1]; + } + const x = t0; + let t1; + let t2; + if ($[2] !== x[0]) { + t2 = [x[0]]; + $[2] = x[0]; + $[3] = t2; + } else { + t2 = $[3]; + } + t1 = t2; + return t1; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{ val: 1 }], +}; + +``` + +### Eval output +(kind: ok) [{"val":1}] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-dep-not-recognized.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-dep-array-literal-access.ts similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-dep-not-recognized.ts rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-dep-array-literal-access.ts diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-via-aliased-mutation-array.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-via-aliased-mutation-array.expect.md index ee52047fbe982..b9ff24519e50f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-via-aliased-mutation-array.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-via-aliased-mutation-array.expect.md @@ -32,28 +32,20 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(4); - let x; + const $ = _c(2); + let t0; if ($[0] !== props.input) { - x = []; + const x = []; const y = x; y.push(props.input); - $[0] = props.input; - $[1] = x; - } else { - x = $[1]; - } - const t0 = x[0]; - let t1; - if ($[2] !== t0) { - t1 = [t0]; - $[2] = t0; - $[3] = t1; + t0 = [x[0]]; + $[0] = props.input; + $[1] = t0; } else { - t1 = $[3]; + t0 = $[1]; } - return t1; + return t0; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-via-aliased-mutation-lambda.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-via-aliased-mutation-lambda.expect.md index 3a338af5ab58f..ebf30d9d28560 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-via-aliased-mutation-lambda.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/reactivity-via-aliased-mutation-lambda.expect.md @@ -35,32 +35,24 @@ export const FIXTURE_ENTRYPOINT = { ```javascript import { c as _c } from "react/compiler-runtime"; function Component(props) { - const $ = _c(4); - let x; + const $ = _c(2); + let t0; if ($[0] !== props.input) { - x = []; + const x = []; const f = (arg) => { const y = x; y.push(arg); }; f(props.input); - $[0] = props.input; - $[1] = x; - } else { - x = $[1]; - } - const t0 = x[0]; - let t1; - if ($[2] !== t0) { - t1 = [t0]; - $[2] = t0; - $[3] = t1; + t0 = [x[0]]; + $[0] = props.input; + $[1] = t0; } else { - t1 = $[3]; + t0 = $[1]; } - return t1; + return t0; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-provider-store-capture-namespace-import.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-provider-store-capture-namespace-import.expect.md index 5016b0c4dfdfd..87b5d7a09e28d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-provider-store-capture-namespace-import.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-provider-store-capture-namespace-import.expect.md @@ -94,69 +94,67 @@ export function Component(t0) { } else { t6 = $[8]; } - const t7 = items_0[0]; - let t8; - if ($[9] !== t6 || $[10] !== t7) { - t8 = ; - $[9] = t6; - $[10] = t7; - $[11] = t8; + let t7; + if ($[9] !== items_0[0] || $[10] !== t6) { + t7 = ; + $[9] = items_0[0]; + $[10] = t6; + $[11] = t7; } else { - t8 = $[11]; + t7 = $[11]; } - let t9; + let t8; if ($[12] !== b) { - t9 = [b]; + t8 = [b]; $[12] = b; - $[13] = t9; + $[13] = t8; } else { - t9 = $[13]; + t8 = $[13]; } - const t10 = items_0[1]; - let t11; - if ($[14] !== t10 || $[15] !== t9) { - t11 = ; - $[14] = t10; - $[15] = t9; - $[16] = t11; + let t9; + if ($[14] !== items_0[1] || $[15] !== t8) { + t9 = ; + $[14] = items_0[1]; + $[15] = t8; + $[16] = t9; } else { - t11 = $[16]; + t9 = $[16]; } - let t12; + let t10; if ($[17] !== a || $[18] !== b) { - t12 = [a, b]; + t10 = [a, b]; $[17] = a; $[18] = b; - $[19] = t12; + $[19] = t10; } else { - t12 = $[19]; + t10 = $[19]; } - let t13; - if ($[20] !== items_0 || $[21] !== t12) { - t13 = ; + let t11; + if ($[20] !== items_0 || $[21] !== t10) { + t11 = ; $[20] = items_0; - $[21] = t12; - $[22] = t13; + $[21] = t10; + $[22] = t11; } else { - t13 = $[22]; + t11 = $[22]; } - let t14; - if ($[23] !== t11 || $[24] !== t13 || $[25] !== t8) { - t14 = ( + let t12; + if ($[23] !== t11 || $[24] !== t7 || $[25] !== t9) { + t12 = ( <> - {t8} + {t7} + {t9} {t11} - {t13} ); $[23] = t11; - $[24] = t13; - $[25] = t8; - $[26] = t14; + $[24] = t7; + $[25] = t9; + $[26] = t12; } else { - t14 = $[26]; + t12 = $[26]; } - return t14; + return t12; } export const FIXTURE_ENTRYPOINT = { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-provider-store-capture.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-provider-store-capture.expect.md index 3f341fc6650d5..c71e4c2530fce 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-provider-store-capture.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-provider-store-capture.expect.md @@ -94,69 +94,67 @@ export function Component(t0) { } else { t6 = $[8]; } - const t7 = items_0[0]; - let t8; - if ($[9] !== t6 || $[10] !== t7) { - t8 = ; - $[9] = t6; - $[10] = t7; - $[11] = t8; + let t7; + if ($[9] !== items_0[0] || $[10] !== t6) { + t7 = ; + $[9] = items_0[0]; + $[10] = t6; + $[11] = t7; } else { - t8 = $[11]; + t7 = $[11]; } - let t9; + let t8; if ($[12] !== b) { - t9 = [b]; + t8 = [b]; $[12] = b; - $[13] = t9; + $[13] = t8; } else { - t9 = $[13]; + t8 = $[13]; } - const t10 = items_0[1]; - let t11; - if ($[14] !== t10 || $[15] !== t9) { - t11 = ; - $[14] = t10; - $[15] = t9; - $[16] = t11; + let t9; + if ($[14] !== items_0[1] || $[15] !== t8) { + t9 = ; + $[14] = items_0[1]; + $[15] = t8; + $[16] = t9; } else { - t11 = $[16]; + t9 = $[16]; } - let t12; + let t10; if ($[17] !== a || $[18] !== b) { - t12 = [a, b]; + t10 = [a, b]; $[17] = a; $[18] = b; - $[19] = t12; + $[19] = t10; } else { - t12 = $[19]; + t10 = $[19]; } - let t13; - if ($[20] !== items_0 || $[21] !== t12) { - t13 = ; + let t11; + if ($[20] !== items_0 || $[21] !== t10) { + t11 = ; $[20] = items_0; - $[21] = t12; - $[22] = t13; + $[21] = t10; + $[22] = t11; } else { - t13 = $[22]; + t11 = $[22]; } - let t14; - if ($[23] !== t11 || $[24] !== t13 || $[25] !== t8) { - t14 = ( + let t12; + if ($[23] !== t11 || $[24] !== t7 || $[25] !== t9) { + t12 = ( <> - {t8} + {t7} + {t9} {t11} - {t13} ); $[23] = t11; - $[24] = t13; - $[25] = t8; - $[26] = t14; + $[24] = t7; + $[25] = t9; + $[26] = t12; } else { - t14 = $[26]; + t12 = $[26]; } - return t14; + return t12; } export const FIXTURE_ENTRYPOINT = {