[compiler] Represent array accesses with PropertyLoad (#32287)

Prior to this PR, our HIR represented property access with numeric
literals (e.g. `myVar[0]`) as ComputedLoads. This means that they were
subject to some deopts (most notably, not being easily dedupable /
hoistable as dependencies).

Now, `PropertyLoad`, `PropertyStore`, etc reference numeric and string
literals (although not yet string literals that aren't valid babel
identifiers). The difference between PropertyLoad and ComputedLoad is
fuzzy now (maybe we should rename these).
- PropertyLoad: property keys are string and numeric literals, only when
the string literals are valid babel identifiers
- ComputedLoad: non-valid babel identifier string literals (rare) and
other non-literal expressions

The biggest feature from this PR is that it trivially enables
array-indicing expressions as dependencies. The compiler can also
specify global and imported types for arrays (e.g. return value of
`useState`)


I'm happy to close this if it complicates more than it helps --
alternative options are to entirely rely on instruction reordering-based
approaches like ReactiveGraphIR or make dependency-specific parsing +
hoisting logic more robust.
This commit is contained in:
mofeiZ 2025-02-18 11:54:20 -07:00 committed by GitHub
parent 19cc5af41e
commit a9575dcf62
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 328 additions and 259 deletions

View File

@ -36,12 +36,14 @@ import {
ObjectProperty,
ObjectPropertyKey,
Place,
PropertyLiteral,
ReturnTerminal,
SourceLocation,
SpreadPattern,
ThrowTerminal,
Type,
makeInstructionId,
makePropertyLiteral,
makeType,
promoteTemporary,
} from './HIR';
@ -2017,11 +2019,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,
};
@ -2316,11 +2318,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 {
@ -2427,11 +2429,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,
});
@ -3057,7 +3059,7 @@ function lowerArguments(
type LoweredMemberExpression = {
object: Place;
property: Place | string;
property: Place | string | number;
value: InstructionValue;
};
function lowerMemberExpression(
@ -3072,8 +3074,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,
@ -3089,10 +3096,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({
@ -3210,7 +3217,7 @@ function lowerJsxMemberExpression(
return lowerValueToTemporary(builder, {
kind: 'PropertyLoad',
object: objectPlace,
property,
property: makePropertyLiteral(property),
loc,
});
}
@ -3626,8 +3633,25 @@ function lowerAssignment(
const lvalue = lvaluePath as NodePath<t.MemberExpression>;
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,
@ -3636,13 +3660,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()) {

View File

@ -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<string, PropertyPathNode>;
optionalProperties: Map<string, PropertyPathNode>;
properties: Map<PropertyLiteral, PropertyPathNode>;
optionalProperties: Map<PropertyLiteral, PropertyPathNode>;
parent: null;
// Recorded to make later computations simpler
fullPath: ReactiveScopeDependency;
@ -183,8 +184,8 @@ type RootNode = {
type PropertyPathNode =
| {
properties: Map<string, PropertyPathNode>;
optionalProperties: Map<string, PropertyPathNode>;
properties: Map<PropertyLiteral, PropertyPathNode>;
optionalProperties: Map<PropertyLiteral, PropertyPathNode>;
parent: PropertyPathNode;
fullPath: ReactiveScopeDependency;
hasOptional: boolean;

View File

@ -16,6 +16,7 @@ import {
DependencyPathEntry,
Instruction,
Terminal,
PropertyLiteral,
} from './HIR';
import {printIdentifier} from './PrintHIR';
@ -157,7 +158,7 @@ function matchOptionalTestBlock(
blocks: ReadonlyMap<BlockId, BasicBlock>,
): {
consequentId: IdentifierId;
property: string;
property: PropertyLiteral;
propertyId: IdentifierId;
storeLocalInstr: Instruction;
consequentGoto: BlockId;

View File

@ -10,6 +10,7 @@ import {
DependencyPathEntry,
GeneratedSource,
Identifier,
PropertyLiteral,
ReactiveScopeDependency,
} from '../HIR';
import {printIdentifier} from '../HIR/PrintHIR';
@ -286,7 +287,7 @@ function merge(
}
type TreeNode<T extends string> = {
properties: Map<string, TreeNode<T>>;
properties: Map<PropertyLiteral, TreeNode<T>>;
accessType: T;
};
type HoistableNode = TreeNode<'Optional' | 'NonNull'>;
@ -343,7 +344,7 @@ function printSubtree(
function makeOrMergeProperty(
node: DependencyNode,
property: string,
property: PropertyLiteral,
accessType: PropertyAccessType,
): DependencyNode {
let child = node.properties.get(property);

View File

@ -937,7 +937,7 @@ export type InstructionValue =
| {
kind: 'PropertyStore';
object: Place;
property: string;
property: PropertyLiteral;
value: Place;
loc: SourceLocation;
}
@ -947,7 +947,7 @@ export type InstructionValue =
| {
kind: 'PropertyDelete';
object: Place;
property: string;
property: PropertyLiteral;
loc: SourceLocation;
}
@ -1121,7 +1121,7 @@ export type StoreLocal = {
export type PropertyLoad = {
kind: 'PropertyLoad';
object: Place;
property: string;
property: PropertyLiteral;
loc: SourceLocation;
};
@ -1502,7 +1502,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<DependencyPathEntry>;
export type ReactiveScopeDependency = {
identifier: Identifier;

View File

@ -22,6 +22,7 @@ import {
TInstruction,
FunctionExpression,
ObjectMethod,
PropertyLiteral,
} from './HIR';
import {
collectHoistablePropertyLoads,
@ -321,7 +322,7 @@ function collectTemporariesSidemapImpl(
function getProperty(
object: Place,
propertyName: string,
propertyName: PropertyLiteral,
optional: boolean,
temporaries: ReadonlyMap<IdentifierId, ReactiveScopeDependency>,
): ReactiveScopeDependency {
@ -519,7 +520,11 @@ class Context {
);
}
visitProperty(object: Place, property: string, optional: boolean): void {
visitProperty(
object: Place,
property: PropertyLiteral,
optional: boolean,
): void {
const nextDependency = getProperty(
object,
property,

View File

@ -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 = {

View File

@ -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<PropertyLoad>,
});
}

View File

@ -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,
};

View File

@ -21,6 +21,7 @@ import {
InstructionKind,
JsxAttribute,
makeInstructionId,
makePropertyLiteral,
ObjectProperty,
Phi,
Place,
@ -446,7 +447,7 @@ function createSymbolProperty(
value: {
kind: 'PropertyLoad',
object: {...symbolInstruction.lvalue},
property: 'for',
property: makePropertyLiteral('for'),
loc: instr.value.loc,
},
loc: instr.loc,

View File

@ -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);

View File

@ -1453,15 +1453,20 @@ function codegenDependency(
if (dependency.path.length !== 0) {
const hasOptional = dependency.path.some(path => path.optional);
for (const path of dependency.path) {
const property =
typeof path.property === 'string'
? t.identifier(path.property)
: t.numericLiteral(path.property);
const isComputed = typeof path.property !== 'string';
if (hasOptional) {
object = t.optionalMemberExpression(
object,
t.identifier(path.property),
false,
property,
isComputed,
path.optional,
);
} else {
object = t.memberExpression(object, t.identifier(path.property));
object = t.memberExpression(object, property, isComputed);
}
}
}
@ -1962,38 +1967,37 @@ function codegenInstructionValue(
value = node;
break;
}
case 'PropertyStore': {
value = t.assignmentExpression(
'=',
t.memberExpression(
codegenPlaceToExpression(cx, instrValue.object),
t.identifier(instrValue.property),
),
codegenPlaceToExpression(cx, instrValue.value),
);
break;
}
case 'PropertyLoad': {
const object = codegenPlaceToExpression(cx, instrValue.object);
case 'PropertyStore':
case 'PropertyLoad':
case 'PropertyDelete': {
let memberExpr;
/*
* We currently only lower single chains of optional memberexpr.
* (See BuildHIR.ts for more detail.)
*/
value = t.memberExpression(
object,
t.identifier(instrValue.property),
undefined,
);
break;
}
case 'PropertyDelete': {
value = t.unaryExpression(
'delete',
t.memberExpression(
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,
);
}
if (instrValue.kind === 'PropertyStore') {
value = t.assignmentExpression(
'=',
memberExpr,
codegenPlaceToExpression(cx, instrValue.value),
);
} else if (instrValue.kind === 'PropertyLoad') {
value = memberExpr;
} else {
value = t.unaryExpression('delete', memberExpr);
}
break;
}
case 'ComputedStore': {

View File

@ -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<State> {
value: {
kind: 'PropertyLoad',
object: {...symbolTemp},
property: 'for',
property: makePropertyLiteral('for'),
loc,
},
},

View File

@ -12,6 +12,7 @@ import {
IdentifierId,
InstructionId,
Place,
PropertyLiteral,
ReactiveBlock,
ReactiveFunction,
ReactiveInstruction,
@ -64,13 +65,13 @@ type KindMap = Map<IdentifierId, CreateUpdate>;
class Visitor extends ReactiveFunctionVisitor<CreateUpdate> {
map: KindMap = new Map();
aliases: DisjointSet<IdentifierId>;
paths: Map<IdentifierId, Map<string, IdentifierId>>;
paths: Map<IdentifierId, Map<PropertyLiteral, IdentifierId>>;
env: Environment;
constructor(
env: Environment,
aliases: DisjointSet<IdentifierId>,
paths: Map<IdentifierId, Map<string, IdentifierId>>,
paths: Map<IdentifierId, Map<PropertyLiteral, IdentifierId>>,
) {
super();
this.aliases = aliases;
@ -218,9 +219,9 @@ export default function pruneInitializationDependencies(
}
function update(
map: Map<IdentifierId, Map<string, IdentifierId>>,
map: Map<IdentifierId, Map<PropertyLiteral, IdentifierId>>,
key: IdentifierId,
path: string,
path: PropertyLiteral,
value: IdentifierId,
): void {
const inner = map.get(key) ?? new Map();
@ -230,7 +231,7 @@ function update(
class AliasVisitor extends ReactiveFunctionVisitor {
scopeIdentifiers: DisjointSet<IdentifierId> = new DisjointSet<IdentifierId>();
scopePaths: Map<IdentifierId, Map<string, IdentifierId>> = new Map();
scopePaths: Map<IdentifierId, Map<PropertyLiteral, IdentifierId>> = new Map();
override visitInstruction(instr: ReactiveInstruction): void {
if (
@ -271,11 +272,14 @@ class AliasVisitor extends ReactiveFunctionVisitor {
function getAliases(
fn: ReactiveFunction,
): [DisjointSet<IdentifierId>, Map<IdentifierId, Map<string, IdentifierId>>] {
): [
DisjointSet<IdentifierId>,
Map<IdentifierId, Map<PropertyLiteral, IdentifierId>>,
] {
const visitor = new AliasVisitor();
visitReactiveFunction(fn, visitor, null);
let disjoint = visitor.scopeIdentifiers;
let scopePaths = new Map<IdentifierId, Map<string, IdentifierId>>();
let scopePaths = new Map<IdentifierId, Map<PropertyLiteral, IdentifierId>>();
for (const [key, value] of visitor.scopePaths) {
for (const [path, id] of value) {
update(

View File

@ -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);
}

View File

@ -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: {

View File

@ -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;

View File

@ -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);

View File

@ -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 = {

View File

@ -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 = {
```

View File

@ -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}]

View File

@ -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 = {

View File

@ -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 = {

View File

@ -94,69 +94,67 @@ export function Component(t0) {
} else {
t6 = $[8];
}
const t7 = items_0[0];
let t8;
if ($[9] !== t6 || $[10] !== t7) {
t8 = <SharedRuntime.ValidateMemoization inputs={t6} output={t7} />;
$[9] = t6;
$[10] = t7;
$[11] = t8;
let t7;
if ($[9] !== items_0[0] || $[10] !== t6) {
t7 = <SharedRuntime.ValidateMemoization inputs={t6} output={items_0[0]} />;
$[9] = items_0[0];
$[10] = t6;
$[11] = t7;
} else {
t8 = $[11];
t7 = $[11];
}
let t8;
if ($[12] !== b) {
t8 = [b];
$[12] = b;
$[13] = t8;
} else {
t8 = $[13];
}
let t9;
if ($[12] !== b) {
t9 = [b];
$[12] = b;
$[13] = t9;
if ($[14] !== items_0[1] || $[15] !== t8) {
t9 = <SharedRuntime.ValidateMemoization inputs={t8} output={items_0[1]} />;
$[14] = items_0[1];
$[15] = t8;
$[16] = t9;
} else {
t9 = $[13];
t9 = $[16];
}
const t10 = items_0[1];
let t11;
if ($[14] !== t10 || $[15] !== t9) {
t11 = <SharedRuntime.ValidateMemoization inputs={t9} output={t10} />;
$[14] = t10;
$[15] = t9;
$[16] = t11;
} else {
t11 = $[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 = <SharedRuntime.ValidateMemoization inputs={t12} output={items_0} />;
let t11;
if ($[20] !== items_0 || $[21] !== t10) {
t11 = <SharedRuntime.ValidateMemoization inputs={t10} output={items_0} />;
$[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 = {

View File

@ -94,69 +94,67 @@ export function Component(t0) {
} else {
t6 = $[8];
}
const t7 = items_0[0];
let t8;
if ($[9] !== t6 || $[10] !== t7) {
t8 = <ValidateMemoization inputs={t6} output={t7} />;
$[9] = t6;
$[10] = t7;
$[11] = t8;
let t7;
if ($[9] !== items_0[0] || $[10] !== t6) {
t7 = <ValidateMemoization inputs={t6} output={items_0[0]} />;
$[9] = items_0[0];
$[10] = t6;
$[11] = t7;
} else {
t8 = $[11];
t7 = $[11];
}
let t8;
if ($[12] !== b) {
t8 = [b];
$[12] = b;
$[13] = t8;
} else {
t8 = $[13];
}
let t9;
if ($[12] !== b) {
t9 = [b];
$[12] = b;
$[13] = t9;
if ($[14] !== items_0[1] || $[15] !== t8) {
t9 = <ValidateMemoization inputs={t8} output={items_0[1]} />;
$[14] = items_0[1];
$[15] = t8;
$[16] = t9;
} else {
t9 = $[13];
t9 = $[16];
}
const t10 = items_0[1];
let t11;
if ($[14] !== t10 || $[15] !== t9) {
t11 = <ValidateMemoization inputs={t9} output={t10} />;
$[14] = t10;
$[15] = t9;
$[16] = t11;
} else {
t11 = $[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 = <ValidateMemoization inputs={t12} output={items_0} />;
let t11;
if ($[20] !== items_0 || $[21] !== t10) {
t11 = <ValidateMemoization inputs={t10} output={items_0} />;
$[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 = {