Compare commits
8 Commits
pr34125
...
mofeiZ/30/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc43aea269 | ||
|
|
70fb3f6849 | ||
|
|
d2ed23a07f | ||
|
|
3556fcd3ad | ||
|
|
af227d3e75 | ||
|
|
6b10e016a2 | ||
|
|
1c8e944358 | ||
|
|
9f35794e90 |
@@ -57,7 +57,6 @@ import {
|
||||
mergeReactiveScopesThatInvalidateTogether,
|
||||
promoteUsedTemporaries,
|
||||
propagateEarlyReturns,
|
||||
propagateScopeDependencies,
|
||||
pruneHoistedContexts,
|
||||
pruneNonEscapingScopes,
|
||||
pruneNonReactiveDependencies,
|
||||
@@ -343,14 +342,12 @@ function* runWithEnvironment(
|
||||
});
|
||||
assertTerminalSuccessorsExist(hir);
|
||||
assertTerminalPredsExist(hir);
|
||||
if (env.config.enablePropagateDepsInHIR) {
|
||||
propagateScopeDependenciesHIR(hir);
|
||||
yield log({
|
||||
kind: 'hir',
|
||||
name: 'PropagateScopeDependenciesHIR',
|
||||
value: hir,
|
||||
});
|
||||
}
|
||||
propagateScopeDependenciesHIR(hir);
|
||||
yield log({
|
||||
kind: 'hir',
|
||||
name: 'PropagateScopeDependenciesHIR',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
if (env.config.inlineJsxTransform) {
|
||||
inlineJsxTransform(hir, env.config.inlineJsxTransform);
|
||||
@@ -378,15 +375,6 @@ function* runWithEnvironment(
|
||||
});
|
||||
assertScopeInstructionsWithinScopes(reactiveFunction);
|
||||
|
||||
if (!env.config.enablePropagateDepsInHIR) {
|
||||
propagateScopeDependencies(reactiveFunction);
|
||||
yield log({
|
||||
kind: 'reactive',
|
||||
name: 'PropagateScopeDependencies',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
}
|
||||
|
||||
pruneNonEscapingScopes(reactiveFunction);
|
||||
yield log({
|
||||
kind: 'reactive',
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
import {NodePath, Scope} from '@babel/traverse';
|
||||
import * as t from '@babel/types';
|
||||
import {Expression} from '@babel/types';
|
||||
import invariant from 'invariant';
|
||||
import {
|
||||
CompilerError,
|
||||
@@ -3186,7 +3185,13 @@ function lowerJsxMemberExpression(
|
||||
loc: object.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
objectPlace = lowerIdentifier(builder, object);
|
||||
|
||||
const kind = getLoadKind(builder, object);
|
||||
objectPlace = lowerValueToTemporary(builder, {
|
||||
kind: kind,
|
||||
place: lowerIdentifier(builder, object),
|
||||
loc: exprPath.node.loc ?? GeneratedSource,
|
||||
});
|
||||
}
|
||||
const property = exprPath.get('property').node.name;
|
||||
return lowerValueToTemporary(builder, {
|
||||
@@ -3359,7 +3364,7 @@ function lowerFunction(
|
||||
>,
|
||||
): LoweredFunction | null {
|
||||
const componentScope: Scope = builder.parentFunction.scope;
|
||||
const captured = gatherCapturedDeps(builder, expr, componentScope);
|
||||
const capturedContext = gatherCapturedContext(expr, componentScope);
|
||||
|
||||
/*
|
||||
* TODO(gsn): In the future, we could only pass in the context identifiers
|
||||
@@ -3373,7 +3378,7 @@ function lowerFunction(
|
||||
expr,
|
||||
builder.environment,
|
||||
builder.bindings,
|
||||
[...builder.context, ...captured.identifiers],
|
||||
[...builder.context, ...capturedContext],
|
||||
builder.parentFunction,
|
||||
);
|
||||
let loweredFunc: HIRFunction;
|
||||
@@ -3386,7 +3391,6 @@ function lowerFunction(
|
||||
loweredFunc = lowering.unwrap();
|
||||
return {
|
||||
func: loweredFunc,
|
||||
dependencies: captured.refs,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4060,14 +4064,6 @@ function lowerAssignment(
|
||||
}
|
||||
}
|
||||
|
||||
function isValidDependency(path: NodePath<t.MemberExpression>): boolean {
|
||||
const parent: NodePath<t.Node> = path.parentPath;
|
||||
return (
|
||||
!path.node.computed &&
|
||||
!(parent.isCallExpression() && parent.get('callee') === path)
|
||||
);
|
||||
}
|
||||
|
||||
function captureScopes({from, to}: {from: Scope; to: Scope}): Set<Scope> {
|
||||
let scopes: Set<Scope> = new Set();
|
||||
while (from) {
|
||||
@@ -4082,8 +4078,7 @@ function captureScopes({from, to}: {from: Scope; to: Scope}): Set<Scope> {
|
||||
return scopes;
|
||||
}
|
||||
|
||||
function gatherCapturedDeps(
|
||||
builder: HIRBuilder,
|
||||
function gatherCapturedContext(
|
||||
fn: NodePath<
|
||||
| t.FunctionExpression
|
||||
| t.ArrowFunctionExpression
|
||||
@@ -4091,10 +4086,8 @@ function gatherCapturedDeps(
|
||||
| t.ObjectMethod
|
||||
>,
|
||||
componentScope: Scope,
|
||||
): {identifiers: Array<t.Identifier>; refs: Array<Place>} {
|
||||
const capturedIds: Map<t.Identifier, number> = new Map();
|
||||
const capturedRefs: Set<Place> = new Set();
|
||||
const seenPaths: Set<string> = new Set();
|
||||
): Array<t.Identifier> {
|
||||
const capturedIds = new Set<t.Identifier>();
|
||||
|
||||
/*
|
||||
* Capture all the scopes from the parent of this function up to and including
|
||||
@@ -4105,33 +4098,11 @@ function gatherCapturedDeps(
|
||||
to: componentScope,
|
||||
});
|
||||
|
||||
function addCapturedId(bindingIdentifier: t.Identifier): number {
|
||||
if (!capturedIds.has(bindingIdentifier)) {
|
||||
const index = capturedIds.size;
|
||||
capturedIds.set(bindingIdentifier, index);
|
||||
return index;
|
||||
} else {
|
||||
return capturedIds.get(bindingIdentifier)!;
|
||||
}
|
||||
}
|
||||
|
||||
function handleMaybeDependency(
|
||||
path:
|
||||
| NodePath<t.MemberExpression>
|
||||
| NodePath<t.Identifier>
|
||||
| NodePath<t.JSXOpeningElement>,
|
||||
path: NodePath<t.Identifier> | NodePath<t.JSXOpeningElement>,
|
||||
): void {
|
||||
// Base context variable to depend on
|
||||
let baseIdentifier: NodePath<t.Identifier> | NodePath<t.JSXIdentifier>;
|
||||
/*
|
||||
* Base expression to depend on, which (for now) may contain non side-effectful
|
||||
* member expressions
|
||||
*/
|
||||
let dependency:
|
||||
| NodePath<t.MemberExpression>
|
||||
| NodePath<t.JSXMemberExpression>
|
||||
| NodePath<t.Identifier>
|
||||
| NodePath<t.JSXIdentifier>;
|
||||
if (path.isJSXOpeningElement()) {
|
||||
const name = path.get('name');
|
||||
if (!(name.isJSXMemberExpression() || name.isJSXIdentifier())) {
|
||||
@@ -4147,115 +4118,20 @@ function gatherCapturedDeps(
|
||||
'Invalid logic in gatherCapturedDeps',
|
||||
);
|
||||
baseIdentifier = current;
|
||||
|
||||
/*
|
||||
* Get the expression to depend on, which may involve PropertyLoads
|
||||
* for member expressions
|
||||
*/
|
||||
let currentDep:
|
||||
| NodePath<t.JSXMemberExpression>
|
||||
| NodePath<t.Identifier>
|
||||
| NodePath<t.JSXIdentifier> = baseIdentifier;
|
||||
|
||||
while (true) {
|
||||
const nextDep: null | NodePath<t.Node> = currentDep.parentPath;
|
||||
if (nextDep && nextDep.isJSXMemberExpression()) {
|
||||
currentDep = nextDep;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
dependency = currentDep;
|
||||
} else if (path.isMemberExpression()) {
|
||||
// Calculate baseIdentifier
|
||||
let currentId: NodePath<Expression> = path;
|
||||
while (currentId.isMemberExpression()) {
|
||||
currentId = currentId.get('object');
|
||||
}
|
||||
if (!currentId.isIdentifier()) {
|
||||
return;
|
||||
}
|
||||
baseIdentifier = currentId;
|
||||
|
||||
/*
|
||||
* Get the expression to depend on, which may involve PropertyLoads
|
||||
* for member expressions
|
||||
*/
|
||||
let currentDep:
|
||||
| NodePath<t.MemberExpression>
|
||||
| NodePath<t.Identifier>
|
||||
| NodePath<t.JSXIdentifier> = baseIdentifier;
|
||||
|
||||
while (true) {
|
||||
const nextDep: null | NodePath<t.Node> = currentDep.parentPath;
|
||||
if (
|
||||
nextDep &&
|
||||
nextDep.isMemberExpression() &&
|
||||
isValidDependency(nextDep)
|
||||
) {
|
||||
currentDep = nextDep;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dependency = currentDep;
|
||||
} else {
|
||||
baseIdentifier = path;
|
||||
dependency = path;
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip dependency path, as we already tried to recursively add it (+ all subexpressions)
|
||||
* as a dependency.
|
||||
*/
|
||||
dependency.skip();
|
||||
path.skip();
|
||||
|
||||
// Add the base identifier binding as a dependency.
|
||||
const binding = baseIdentifier.scope.getBinding(baseIdentifier.node.name);
|
||||
if (binding === undefined || !pureScopes.has(binding.scope)) {
|
||||
return;
|
||||
}
|
||||
const idKey = String(addCapturedId(binding.identifier));
|
||||
|
||||
// Add the expression (potentially a memberexpr path) as a dependency.
|
||||
let exprKey = idKey;
|
||||
if (dependency.isMemberExpression()) {
|
||||
let pathTokens = [];
|
||||
let current: NodePath<Expression> = dependency;
|
||||
while (current.isMemberExpression()) {
|
||||
const property = current.get('property') as NodePath<t.Identifier>;
|
||||
pathTokens.push(property.node.name);
|
||||
current = current.get('object');
|
||||
}
|
||||
|
||||
exprKey += '.' + pathTokens.reverse().join('.');
|
||||
} else if (dependency.isJSXMemberExpression()) {
|
||||
let pathTokens = [];
|
||||
let current: NodePath<t.JSXMemberExpression | t.JSXIdentifier> =
|
||||
dependency;
|
||||
while (current.isJSXMemberExpression()) {
|
||||
const property = current.get('property');
|
||||
pathTokens.push(property.node.name);
|
||||
current = current.get('object');
|
||||
}
|
||||
}
|
||||
|
||||
if (!seenPaths.has(exprKey)) {
|
||||
let loweredDep: Place;
|
||||
if (dependency.isJSXIdentifier()) {
|
||||
loweredDep = lowerValueToTemporary(builder, {
|
||||
kind: 'LoadLocal',
|
||||
place: lowerIdentifier(builder, dependency),
|
||||
loc: path.node.loc ?? GeneratedSource,
|
||||
});
|
||||
} else if (dependency.isJSXMemberExpression()) {
|
||||
loweredDep = lowerJsxMemberExpression(builder, dependency);
|
||||
} else {
|
||||
loweredDep = lowerExpressionToTemporary(builder, dependency);
|
||||
}
|
||||
capturedRefs.add(loweredDep);
|
||||
seenPaths.add(exprKey);
|
||||
if (binding !== undefined && pureScopes.has(binding.scope)) {
|
||||
capturedIds.add(binding.identifier);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4286,13 +4162,13 @@ function gatherCapturedDeps(
|
||||
return;
|
||||
} else if (path.isJSXElement()) {
|
||||
handleMaybeDependency(path.get('openingElement'));
|
||||
} else if (path.isMemberExpression() || path.isIdentifier()) {
|
||||
} else if (path.isIdentifier()) {
|
||||
handleMaybeDependency(path);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return {identifiers: [...capturedIds.keys()], refs: [...capturedRefs]};
|
||||
return [...capturedIds.keys()];
|
||||
}
|
||||
|
||||
function notNull<T>(value: T | null): value is T {
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
Set_union,
|
||||
getOrInsertDefault,
|
||||
} from '../Utils/utils';
|
||||
import {collectOptionalChainSidemap} from './CollectOptionalChainDependencies';
|
||||
import {
|
||||
BasicBlock,
|
||||
BlockId,
|
||||
@@ -21,7 +20,6 @@ import {
|
||||
ReactiveScopeDependency,
|
||||
ScopeId,
|
||||
} from './HIR';
|
||||
import {collectTemporariesSidemap} from './PropagateScopeDependenciesHIR';
|
||||
|
||||
/**
|
||||
* Helper function for `PropagateScopeDependencies`. Uses control flow graph
|
||||
@@ -90,11 +88,6 @@ export function collectHoistablePropertyLoads(
|
||||
): ReadonlyMap<BlockId, BlockInfo> {
|
||||
const registry = new PropertyPathRegistry();
|
||||
|
||||
const functionExpressionLoads = collectFunctionExpressionFakeLoads(fn);
|
||||
const actuallyEvaluatedTemporaries = new Map(
|
||||
[...temporaries].filter(([id]) => !functionExpressionLoads.has(id)),
|
||||
);
|
||||
|
||||
/**
|
||||
* Due to current limitations of mutable range inference, there are edge cases in
|
||||
* which we infer known-immutable values (e.g. props or hook params) to have a
|
||||
@@ -112,7 +105,7 @@ export function collectHoistablePropertyLoads(
|
||||
}
|
||||
}
|
||||
const nodes = collectNonNullsInBlocks(fn, {
|
||||
temporaries: actuallyEvaluatedTemporaries,
|
||||
temporaries,
|
||||
knownImmutableIdentifiers,
|
||||
hoistableFromOptionals,
|
||||
registry,
|
||||
@@ -348,19 +341,15 @@ function collectNonNullsInBlocks(
|
||||
assumedNonNullObjects.add(maybeNonNull);
|
||||
}
|
||||
if (
|
||||
instr.value.kind === 'FunctionExpression' &&
|
||||
(instr.value.kind === 'FunctionExpression' ||
|
||||
instr.value.kind === 'ObjectMethod') &&
|
||||
!fn.env.config.enableTreatFunctionDepsAsConditional
|
||||
) {
|
||||
const innerFn = instr.value.loweredFunc;
|
||||
const innerTemporaries = collectTemporariesSidemap(
|
||||
innerFn.func,
|
||||
new Set(),
|
||||
);
|
||||
const innerOptionals = collectOptionalChainSidemap(innerFn.func);
|
||||
const innerHoistableMap = collectHoistablePropertyLoads(
|
||||
innerFn.func,
|
||||
innerTemporaries,
|
||||
innerOptionals.hoistableObjects,
|
||||
context.temporaries,
|
||||
context.hoistableFromOptionals,
|
||||
context.nestedFnImmutableContext ??
|
||||
new Set(
|
||||
innerFn.func.context
|
||||
@@ -582,27 +571,3 @@ function reduceMaybeOptionalChains(
|
||||
}
|
||||
} while (changed);
|
||||
}
|
||||
|
||||
function collectFunctionExpressionFakeLoads(
|
||||
fn: HIRFunction,
|
||||
): Set<IdentifierId> {
|
||||
const sources = new Map<IdentifierId, IdentifierId>();
|
||||
const functionExpressionReferences = new Set<IdentifierId>();
|
||||
|
||||
for (const [_, block] of fn.body.blocks) {
|
||||
for (const {lvalue, value} of block.instructions) {
|
||||
if (value.kind === 'FunctionExpression') {
|
||||
for (const reference of value.loweredFunc.dependencies) {
|
||||
let curr: IdentifierId | undefined = reference.identifier.id;
|
||||
while (curr != null) {
|
||||
functionExpressionReferences.add(curr);
|
||||
curr = sources.get(curr);
|
||||
}
|
||||
}
|
||||
} else if (value.kind === 'PropertyLoad') {
|
||||
sources.set(lvalue.identifier.id, value.object.identifier.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return functionExpressionReferences;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {CompilerError} from '..';
|
||||
import {getOrInsertDefault} from '../Utils/utils';
|
||||
import {assertNonNull} from './CollectHoistablePropertyLoads';
|
||||
import {
|
||||
BlockId,
|
||||
@@ -22,25 +23,14 @@ export function collectOptionalChainSidemap(
|
||||
fn: HIRFunction,
|
||||
): OptionalChainSidemap {
|
||||
const context: OptionalTraversalContext = {
|
||||
currFn: fn,
|
||||
blocks: fn.body.blocks,
|
||||
seenOptionals: new Set(),
|
||||
processedInstrsInOptional: new Set(),
|
||||
processedInstrsInOptional: new Map(),
|
||||
temporariesReadInOptional: new Map(),
|
||||
hoistableObjects: new Map(),
|
||||
};
|
||||
for (const [_, block] of fn.body.blocks) {
|
||||
if (
|
||||
block.terminal.kind === 'optional' &&
|
||||
!context.seenOptionals.has(block.id)
|
||||
) {
|
||||
traverseOptionalBlock(
|
||||
block as TBasicBlock<OptionalTerminal>,
|
||||
context,
|
||||
null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
traverseFunction(fn, context);
|
||||
return {
|
||||
temporariesReadInOptional: context.temporariesReadInOptional,
|
||||
processedInstrsInOptional: context.processedInstrsInOptional,
|
||||
@@ -97,7 +87,10 @@ export type OptionalChainSidemap = {
|
||||
* $5 = MethodCall $2.$4() <--- here, we want to take a dep on $2 and $4!
|
||||
* ```
|
||||
*/
|
||||
processedInstrsInOptional: ReadonlySet<InstructionId>;
|
||||
processedInstrsInOptional: ReadonlyMap<
|
||||
HIRFunction,
|
||||
ReadonlySet<InstructionId>
|
||||
>;
|
||||
/**
|
||||
* Records optional chains for which we can safely evaluate non-optional
|
||||
* PropertyLoads. e.g. given `a?.b.c`, we can evaluate any load from `a?.b` at
|
||||
@@ -115,16 +108,47 @@ export type OptionalChainSidemap = {
|
||||
};
|
||||
|
||||
type OptionalTraversalContext = {
|
||||
currFn: HIRFunction;
|
||||
blocks: ReadonlyMap<BlockId, BasicBlock>;
|
||||
|
||||
// Track optional blocks to avoid outer calls into nested optionals
|
||||
seenOptionals: Set<BlockId>;
|
||||
|
||||
processedInstrsInOptional: Set<InstructionId>;
|
||||
processedInstrsInOptional: Map<HIRFunction, Set<InstructionId>>;
|
||||
temporariesReadInOptional: Map<IdentifierId, ReactiveScopeDependency>;
|
||||
hoistableObjects: Map<BlockId, ReactiveScopeDependency>;
|
||||
};
|
||||
|
||||
function traverseFunction(
|
||||
fn: HIRFunction,
|
||||
context: OptionalTraversalContext,
|
||||
): void {
|
||||
for (const [_, block] of fn.body.blocks) {
|
||||
for (const instr of block.instructions) {
|
||||
if (
|
||||
instr.value.kind === 'FunctionExpression' ||
|
||||
instr.value.kind === 'ObjectMethod'
|
||||
) {
|
||||
traverseFunction(instr.value.loweredFunc.func, {
|
||||
...context,
|
||||
currFn: instr.value.loweredFunc.func,
|
||||
blocks: instr.value.loweredFunc.func.body.blocks,
|
||||
seenOptionals: new Set(),
|
||||
});
|
||||
}
|
||||
}
|
||||
if (
|
||||
block.terminal.kind === 'optional' &&
|
||||
!context.seenOptionals.has(block.id)
|
||||
) {
|
||||
traverseOptionalBlock(
|
||||
block as TBasicBlock<OptionalTerminal>,
|
||||
context,
|
||||
null,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Match the consequent and alternate blocks of an optional.
|
||||
* @returns propertyload computed by the consequent block, or null if the
|
||||
@@ -369,10 +393,13 @@ function traverseOptionalBlock(
|
||||
},
|
||||
],
|
||||
};
|
||||
context.processedInstrsInOptional.add(
|
||||
matchConsequentResult.storeLocalInstrId,
|
||||
const processedInstrsInOptional = getOrInsertDefault(
|
||||
context.processedInstrsInOptional,
|
||||
context.currFn,
|
||||
new Set(),
|
||||
);
|
||||
context.processedInstrsInOptional.add(test.id);
|
||||
processedInstrsInOptional.add(matchConsequentResult.storeLocalInstrId);
|
||||
processedInstrsInOptional.add(test.id);
|
||||
context.temporariesReadInOptional.set(
|
||||
matchConsequentResult.consequentId,
|
||||
load,
|
||||
|
||||
@@ -230,8 +230,6 @@ const EnvironmentConfigSchema = z.object({
|
||||
*/
|
||||
enableUseTypeAnnotations: z.boolean().default(false),
|
||||
|
||||
enablePropagateDepsInHIR: z.boolean().default(false),
|
||||
|
||||
/**
|
||||
* Enables inference of optional dependency chains. Without this flag
|
||||
* a property chain such as `props?.items?.foo` will infer as a dep on
|
||||
|
||||
@@ -722,7 +722,6 @@ export type ObjectProperty = {
|
||||
};
|
||||
|
||||
export type LoweredFunction = {
|
||||
dependencies: Array<Place>;
|
||||
func: HIRFunction;
|
||||
};
|
||||
|
||||
|
||||
@@ -538,9 +538,6 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
|
||||
.split('\n')
|
||||
.map(line => ` ${line}`)
|
||||
.join('\n');
|
||||
const deps = instrValue.loweredFunc.dependencies
|
||||
.map(dep => printPlace(dep))
|
||||
.join(',');
|
||||
const context = instrValue.loweredFunc.func.context
|
||||
.map(dep => printPlace(dep))
|
||||
.join(',');
|
||||
@@ -557,7 +554,7 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
|
||||
})
|
||||
.join(', ') ?? '';
|
||||
const type = printType(instrValue.loweredFunc.func.returnType).trim();
|
||||
value = `${kind} ${name} @deps[${deps}] @context[${context}] @effects[${effects}]${type !== '' ? ` return${type}` : ''}:\n${fn}`;
|
||||
value = `${kind} ${name} @context[${context}] @effects[${effects}]${type !== '' ? ` return${type}` : ''}:\n${fn}`;
|
||||
break;
|
||||
}
|
||||
case 'TaggedTemplateExpression': {
|
||||
|
||||
@@ -16,6 +16,8 @@ import {
|
||||
DeclarationId,
|
||||
areEqualPaths,
|
||||
IdentifierId,
|
||||
BasicBlock,
|
||||
BlockId,
|
||||
} from './HIR';
|
||||
import {
|
||||
collectHoistablePropertyLoads,
|
||||
@@ -37,7 +39,11 @@ import {collectOptionalChainSidemap} from './CollectOptionalChainDependencies';
|
||||
export function propagateScopeDependenciesHIR(fn: HIRFunction): void {
|
||||
const usedOutsideDeclaringScope =
|
||||
findTemporariesUsedOutsideDeclaringScope(fn);
|
||||
const temporaries = collectTemporariesSidemap(fn, usedOutsideDeclaringScope);
|
||||
const temporaries = collectTemporariesSidemap(
|
||||
fn,
|
||||
usedOutsideDeclaringScope,
|
||||
new Map(),
|
||||
);
|
||||
const {
|
||||
temporariesReadInOptional,
|
||||
processedInstrsInOptional,
|
||||
@@ -214,8 +220,9 @@ function findTemporariesUsedOutsideDeclaringScope(
|
||||
export function collectTemporariesSidemap(
|
||||
fn: HIRFunction,
|
||||
usedOutsideDeclaringScope: ReadonlySet<DeclarationId>,
|
||||
temporaries: Map<IdentifierId, ReactiveScopeDependency>,
|
||||
isInnerFn: boolean = false,
|
||||
): ReadonlyMap<IdentifierId, ReactiveScopeDependency> {
|
||||
const temporaries = new Map<IdentifierId, ReactiveScopeDependency>();
|
||||
for (const [_, block] of fn.body.blocks) {
|
||||
for (const instr of block.instructions) {
|
||||
const {value, lvalue} = instr;
|
||||
@@ -224,23 +231,54 @@ export function collectTemporariesSidemap(
|
||||
);
|
||||
|
||||
if (value.kind === 'PropertyLoad' && !usedOutside) {
|
||||
const property = getProperty(
|
||||
value.object,
|
||||
value.property,
|
||||
false,
|
||||
temporaries,
|
||||
);
|
||||
temporaries.set(lvalue.identifier.id, property);
|
||||
if (isInnerFn) {
|
||||
const source = temporaries.get(value.object.identifier.id);
|
||||
if (source) {
|
||||
// only add inner function loads if their source is valid
|
||||
const property = getProperty(
|
||||
value.object,
|
||||
value.property,
|
||||
false,
|
||||
temporaries,
|
||||
);
|
||||
temporaries.set(lvalue.identifier.id, property);
|
||||
}
|
||||
} else {
|
||||
const property = getProperty(
|
||||
value.object,
|
||||
value.property,
|
||||
false,
|
||||
temporaries,
|
||||
);
|
||||
temporaries.set(lvalue.identifier.id, property);
|
||||
}
|
||||
} else if (
|
||||
value.kind === 'LoadLocal' &&
|
||||
lvalue.identifier.name == null &&
|
||||
value.place.identifier.name !== null &&
|
||||
!usedOutside
|
||||
) {
|
||||
temporaries.set(lvalue.identifier.id, {
|
||||
identifier: value.place.identifier,
|
||||
path: [],
|
||||
});
|
||||
if (
|
||||
!isInnerFn ||
|
||||
fn.context.some(
|
||||
context => context.identifier.id === value.place.identifier.id,
|
||||
)
|
||||
) {
|
||||
temporaries.set(lvalue.identifier.id, {
|
||||
identifier: value.place.identifier,
|
||||
path: [],
|
||||
});
|
||||
}
|
||||
} else if (
|
||||
value.kind === 'FunctionExpression' ||
|
||||
value.kind === 'ObjectMethod'
|
||||
) {
|
||||
collectTemporariesSidemap(
|
||||
value.loweredFunc.func,
|
||||
usedOutsideDeclaringScope,
|
||||
temporaries,
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -310,6 +348,8 @@ class Context {
|
||||
#temporaries: ReadonlyMap<IdentifierId, ReactiveScopeDependency>;
|
||||
#temporariesUsedOutsideScope: ReadonlySet<DeclarationId>;
|
||||
|
||||
innerFnContext: HIRFunction | null = null;
|
||||
|
||||
constructor(
|
||||
temporariesUsedOutsideScope: ReadonlySet<DeclarationId>,
|
||||
temporaries: ReadonlyMap<IdentifierId, ReactiveScopeDependency>,
|
||||
@@ -374,6 +414,14 @@ class Context {
|
||||
|
||||
// Checks if identifier is a valid dependency in the current scope
|
||||
#checkValidDependency(maybeDependency: ReactiveScopeDependency): boolean {
|
||||
if (
|
||||
this.innerFnContext != null &&
|
||||
!this.innerFnContext.context.some(
|
||||
context => context.identifier.id === maybeDependency.identifier.id,
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
// ref.current access is not a valid dep
|
||||
if (
|
||||
isUseRefType(maybeDependency.identifier) &&
|
||||
@@ -575,7 +623,10 @@ function collectDependencies(
|
||||
fn: HIRFunction,
|
||||
usedOutsideDeclaringScope: ReadonlySet<DeclarationId>,
|
||||
temporaries: ReadonlyMap<IdentifierId, ReactiveScopeDependency>,
|
||||
processedInstrsInOptional: ReadonlySet<InstructionId>,
|
||||
processedInstrsInOptional: ReadonlyMap<
|
||||
HIRFunction,
|
||||
ReadonlySet<InstructionId>
|
||||
>,
|
||||
): Map<ReactiveScope, Array<ReactiveScopeDependency>> {
|
||||
const context = new Context(usedOutsideDeclaringScope, temporaries);
|
||||
|
||||
@@ -594,8 +645,13 @@ function collectDependencies(
|
||||
}
|
||||
|
||||
const scopeTraversal = new ScopeBlockTraversal();
|
||||
// TODO: make this less hacky
|
||||
const st: Array<[HIRFunction, BlockId, BasicBlock]> = [...fn.body.blocks].map(
|
||||
([id, block]) => [fn, id, block],
|
||||
);
|
||||
|
||||
for (const [blockId, block] of fn.body.blocks) {
|
||||
while (st.length != 0) {
|
||||
const [currFn, blockId, block] = st.shift()!;
|
||||
scopeTraversal.recordScopes(block);
|
||||
const scopeBlockInfo = scopeTraversal.blockInfos.get(blockId);
|
||||
if (scopeBlockInfo?.kind === 'begin') {
|
||||
@@ -604,6 +660,9 @@ function collectDependencies(
|
||||
context.exitScope(scopeBlockInfo.scope, scopeBlockInfo?.pruned);
|
||||
}
|
||||
|
||||
// TODO: make this less hacky
|
||||
context.innerFnContext = currFn === fn ? null : currFn;
|
||||
|
||||
// Record referenced optional chains in phis
|
||||
for (const phi of block.phis) {
|
||||
for (const operand of phi.operands) {
|
||||
@@ -614,16 +673,37 @@ function collectDependencies(
|
||||
}
|
||||
}
|
||||
for (const instr of block.instructions) {
|
||||
if (!processedInstrsInOptional.has(instr.id)) {
|
||||
if (
|
||||
instr.value.kind === 'FunctionExpression' ||
|
||||
instr.value.kind === 'ObjectMethod'
|
||||
) {
|
||||
/**
|
||||
* Push the outermost nested fn context, as these are guaranteed to reference
|
||||
* component / hook identifiers (i.e. eligible dependencies)
|
||||
*/
|
||||
const innerFn = instr.value.loweredFunc.func;
|
||||
const outermostNestedFnContext = currFn === fn ? innerFn : currFn;
|
||||
const x: Array<[HIRFunction, BlockId, BasicBlock]> = [
|
||||
...innerFn.body.blocks.entries(),
|
||||
].map(([id, block]) => [outermostNestedFnContext, id, block]);
|
||||
st.unshift(...x);
|
||||
|
||||
context.declare(instr.lvalue.identifier, {
|
||||
id: instr.id,
|
||||
scope: context.currentScope,
|
||||
});
|
||||
} else if (!processedInstrsInOptional.get(currFn)?.has(instr.id)) {
|
||||
// instruction ids are function-relative
|
||||
handleInstruction(instr, context);
|
||||
}
|
||||
}
|
||||
|
||||
if (!processedInstrsInOptional.has(block.terminal.id)) {
|
||||
if (!processedInstrsInOptional.get(currFn)?.has(block.terminal.id)) {
|
||||
for (const place of eachTerminalOperand(block.terminal)) {
|
||||
context.visitOperand(place);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return context.deps;
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ export function* eachInstructionValueOperand(
|
||||
}
|
||||
case 'ObjectMethod':
|
||||
case 'FunctionExpression': {
|
||||
yield* instrValue.loweredFunc.dependencies;
|
||||
yield* instrValue.loweredFunc.func.context;
|
||||
break;
|
||||
}
|
||||
case 'TaggedTemplateExpression': {
|
||||
@@ -517,8 +517,9 @@ export function mapInstructionValueOperands(
|
||||
}
|
||||
case 'ObjectMethod':
|
||||
case 'FunctionExpression': {
|
||||
instrValue.loweredFunc.dependencies =
|
||||
instrValue.loweredFunc.dependencies.map(d => fn(d));
|
||||
instrValue.loweredFunc.func.context =
|
||||
instrValue.loweredFunc.func.context.map(d => fn(d));
|
||||
|
||||
break;
|
||||
}
|
||||
case 'TaggedTemplateExpression': {
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
Effect,
|
||||
HIRFunction,
|
||||
Identifier,
|
||||
IdentifierName,
|
||||
LoweredFunction,
|
||||
Place,
|
||||
isRefOrRefValue,
|
||||
@@ -41,20 +40,6 @@ export class IdentifierState {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
declareProperty(lvalue: Place, object: Place, property: string): void {
|
||||
const objectDependency = this.properties.get(object.identifier);
|
||||
let nextDependency: Dependency;
|
||||
if (objectDependency === undefined) {
|
||||
nextDependency = {identifier: object.identifier, path: [property]};
|
||||
} else {
|
||||
nextDependency = {
|
||||
identifier: objectDependency.identifier,
|
||||
path: [...objectDependency.path, property],
|
||||
};
|
||||
}
|
||||
this.properties.set(lvalue.identifier, nextDependency);
|
||||
}
|
||||
|
||||
declareTemporary(lvalue: Place, value: Place): void {
|
||||
const resolved: Dependency = this.properties.get(value.identifier) ?? {
|
||||
identifier: value.identifier,
|
||||
@@ -73,25 +58,10 @@ export default function analyseFunctions(func: HIRFunction): void {
|
||||
case 'ObjectMethod':
|
||||
case 'FunctionExpression': {
|
||||
lower(instr.value.loweredFunc.func);
|
||||
infer(instr.value.loweredFunc, state, func.context);
|
||||
break;
|
||||
}
|
||||
case 'PropertyLoad': {
|
||||
state.declareProperty(
|
||||
instr.lvalue,
|
||||
instr.value.object,
|
||||
instr.value.property,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'ComputedLoad': {
|
||||
/*
|
||||
* The path is set to an empty string as the path doesn't really
|
||||
* matter for a computed load.
|
||||
*/
|
||||
state.declareProperty(instr.lvalue, instr.value.object, '');
|
||||
infer(instr.value.loweredFunc, func.context);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'LoadLocal':
|
||||
case 'LoadContext': {
|
||||
if (instr.lvalue.identifier.name === null) {
|
||||
@@ -115,11 +85,8 @@ function lower(func: HIRFunction): void {
|
||||
logHIRFunction('AnalyseFunction (inner)', func);
|
||||
}
|
||||
|
||||
function infer(
|
||||
loweredFunc: LoweredFunction,
|
||||
state: IdentifierState,
|
||||
context: Array<Place>,
|
||||
): void {
|
||||
// infer loweredFunc (inner) with outer function context
|
||||
function infer(loweredFunc: LoweredFunction, context: Array<Place>): void {
|
||||
const mutations = new Map<string, Effect>();
|
||||
for (const operand of loweredFunc.func.context) {
|
||||
if (
|
||||
@@ -130,15 +97,13 @@ function infer(
|
||||
}
|
||||
}
|
||||
|
||||
for (const dep of loweredFunc.dependencies) {
|
||||
let name: IdentifierName | null = null;
|
||||
|
||||
if (state.properties.has(dep.identifier)) {
|
||||
const receiver = state.properties.get(dep.identifier)!;
|
||||
name = receiver.identifier.name;
|
||||
} else {
|
||||
name = dep.identifier.name;
|
||||
}
|
||||
for (const dep of loweredFunc.func.context) {
|
||||
CompilerError.invariant(dep.identifier.name !== null, {
|
||||
reason: 'context refs should always have a name',
|
||||
description: null,
|
||||
loc: dep.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
|
||||
if (isRefOrRefValue(dep.identifier)) {
|
||||
/*
|
||||
@@ -149,8 +114,8 @@ function infer(
|
||||
* render
|
||||
*/
|
||||
dep.effect = Effect.Capture;
|
||||
} else if (name !== null) {
|
||||
const effect = mutations.get(name.value);
|
||||
} else {
|
||||
const effect = mutations.get(dep.identifier.name.value);
|
||||
if (effect !== undefined) {
|
||||
dep.effect = effect === Effect.Unknown ? Effect.Capture : effect;
|
||||
}
|
||||
@@ -176,7 +141,6 @@ function infer(
|
||||
const effect = mutations.get(place.identifier.name.value);
|
||||
if (effect !== undefined) {
|
||||
place.effect = effect === Effect.Unknown ? Effect.Capture : effect;
|
||||
loweredFunc.dependencies.push(place);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,22 +61,6 @@ export function inferMutableContextVariables(fn: HIRFunction): void {
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
for (const instr of block.instructions) {
|
||||
switch (instr.value.kind) {
|
||||
case 'PropertyLoad': {
|
||||
state.declareProperty(
|
||||
instr.lvalue,
|
||||
instr.value.object,
|
||||
instr.value.property,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'ComputedLoad': {
|
||||
/*
|
||||
* The path is set to an empty string as the path doesn't really
|
||||
* matter for a computed load.
|
||||
*/
|
||||
state.declareProperty(instr.lvalue, instr.value.object, '');
|
||||
break;
|
||||
}
|
||||
case 'LoadLocal':
|
||||
case 'LoadContext': {
|
||||
if (instr.lvalue.identifier.name === null) {
|
||||
|
||||
@@ -58,6 +58,14 @@ export function deadCodeElimination(fn: HIRFunction): void {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constant propagation and DCE may have deleted or rewritten instructions
|
||||
* that reference context variables.
|
||||
*/
|
||||
retainWhere(fn.context, contextVar =>
|
||||
state.isIdOrNameUsed(contextVar.identifier),
|
||||
);
|
||||
}
|
||||
|
||||
class State {
|
||||
|
||||
@@ -270,7 +270,6 @@ function emitSelectorFn(env: Environment, keys: Array<string>): Instruction {
|
||||
name: null,
|
||||
loweredFunc: {
|
||||
func: fn,
|
||||
dependencies: [],
|
||||
},
|
||||
type: 'ArrowFunctionExpression',
|
||||
loc: GeneratedSource,
|
||||
|
||||
@@ -24,7 +24,6 @@ export function outlineFunctions(
|
||||
}
|
||||
if (
|
||||
value.kind === 'FunctionExpression' &&
|
||||
value.loweredFunc.dependencies.length === 0 &&
|
||||
value.loweredFunc.func.context.length === 0 &&
|
||||
// TODO: handle outlining named functions
|
||||
value.loweredFunc.func.id === null &&
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,6 @@ export {mergeReactiveScopesThatInvalidateTogether} from './MergeReactiveScopesTh
|
||||
export {printReactiveFunction} from './PrintReactiveFunction';
|
||||
export {promoteUsedTemporaries} from './PromoteUsedTemporaries';
|
||||
export {propagateEarlyReturns} from './PropagateEarlyReturns';
|
||||
export {propagateScopeDependencies} from './PropagateScopeDependencies';
|
||||
export {pruneAllReactiveScopes} from './PruneAllReactiveScopes';
|
||||
export {pruneHoistedContexts} from './PruneHoistedContexts';
|
||||
export {pruneNonEscapingScopes} from './PruneNonEscapingScopes';
|
||||
|
||||
@@ -44,48 +44,44 @@ import { c as _c } from "react/compiler-runtime"; // @validateRefAccessDuringRen
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
function Component() {
|
||||
const $ = _c(6);
|
||||
const $ = _c(5);
|
||||
const ref = useRef(null);
|
||||
const [state, setState] = useState(false);
|
||||
let t0;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = () => {};
|
||||
|
||||
t1 = [];
|
||||
t0 = [];
|
||||
$[0] = t0;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
t1 = $[1];
|
||||
}
|
||||
useEffect(t0, t1);
|
||||
useEffect(_temp, t0);
|
||||
let t1;
|
||||
let t2;
|
||||
let t3;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = () => {
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = () => {
|
||||
setState(true);
|
||||
};
|
||||
t3 = [];
|
||||
t2 = [];
|
||||
$[1] = t1;
|
||||
$[2] = t2;
|
||||
$[3] = t3;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
t2 = $[2];
|
||||
t3 = $[3];
|
||||
}
|
||||
useEffect(t2, t3);
|
||||
useEffect(t1, t2);
|
||||
|
||||
const t4 = String(state);
|
||||
let t5;
|
||||
if ($[4] !== t4) {
|
||||
t5 = <Child key={t4} ref={ref} />;
|
||||
const t3 = String(state);
|
||||
let t4;
|
||||
if ($[3] !== t3) {
|
||||
t4 = <Child key={t3} ref={ref} />;
|
||||
$[3] = t3;
|
||||
$[4] = t4;
|
||||
$[5] = t5;
|
||||
} else {
|
||||
t5 = $[5];
|
||||
t4 = $[4];
|
||||
}
|
||||
return t5;
|
||||
return t4;
|
||||
}
|
||||
function _temp() {}
|
||||
|
||||
function Child(t0) {
|
||||
const { ref } = t0;
|
||||
|
||||
@@ -28,7 +28,7 @@ function Component() {
|
||||
t0 = () => {
|
||||
"worklet";
|
||||
|
||||
setCount((count_0) => count_0 + 1);
|
||||
setCount(_temp);
|
||||
};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
@@ -45,6 +45,9 @@ function Component() {
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
function _temp(count_0) {
|
||||
return count_0 + 1;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ function Component(t0) {
|
||||
const $ = _c(5);
|
||||
const { obj, isObjNull } = t0;
|
||||
let t1;
|
||||
if ($[0] !== isObjNull || $[1] !== obj.prop) {
|
||||
if ($[0] !== isObjNull || $[1] !== obj) {
|
||||
t1 = () => {
|
||||
if (!isObjNull) {
|
||||
return obj.prop;
|
||||
@@ -67,7 +67,7 @@ function Component(t0) {
|
||||
}
|
||||
};
|
||||
$[0] = isObjNull;
|
||||
$[1] = obj.prop;
|
||||
$[1] = obj;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
|
||||
@@ -38,16 +38,24 @@ import { identity } from "shared-runtime";
|
||||
* try-catch block, as that might throw
|
||||
*/
|
||||
function useFoo(maybeNullObject) {
|
||||
const $ = _c(2);
|
||||
const $ = _c(4);
|
||||
let y;
|
||||
if ($[0] !== maybeNullObject.value.inner) {
|
||||
if ($[0] !== maybeNullObject) {
|
||||
y = [];
|
||||
try {
|
||||
y.push(identity(maybeNullObject.value.inner));
|
||||
let t0;
|
||||
if ($[2] !== maybeNullObject.value.inner) {
|
||||
t0 = identity(maybeNullObject.value.inner);
|
||||
$[2] = maybeNullObject.value.inner;
|
||||
$[3] = t0;
|
||||
} else {
|
||||
t0 = $[3];
|
||||
}
|
||||
y.push(t0);
|
||||
} catch {
|
||||
y.push("null");
|
||||
}
|
||||
$[0] = maybeNullObject.value.inner;
|
||||
$[0] = maybeNullObject;
|
||||
$[1] = y;
|
||||
} else {
|
||||
y = $[1];
|
||||
|
||||
@@ -55,11 +55,7 @@ function getNativeLogFunction(level) {
|
||||
if (arguments.length === 1 && typeof arguments[0] === "string") {
|
||||
str = arguments[0];
|
||||
} else {
|
||||
str = Array.prototype.map
|
||||
.call(arguments, function (arg) {
|
||||
return inspect(arg, { depth: 10 });
|
||||
})
|
||||
.join(", ");
|
||||
str = Array.prototype.map.call(arguments, _temp).join(", ");
|
||||
}
|
||||
|
||||
const firstArg = arguments[0];
|
||||
@@ -92,6 +88,9 @@ function getNativeLogFunction(level) {
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
function _temp(arg) {
|
||||
return inspect(arg, { depth: 10 });
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -2,39 +2,55 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function component(foo, bar) {
|
||||
import {mutate} from 'shared-runtime';
|
||||
|
||||
function Component({foo, bar}) {
|
||||
let x = {foo};
|
||||
let y = {bar};
|
||||
const f0 = function () {
|
||||
let a = {y};
|
||||
let a = [y];
|
||||
let b = x;
|
||||
a.x = b;
|
||||
// this writes y.x = x
|
||||
a[0].x = b;
|
||||
};
|
||||
f0();
|
||||
mutate(y);
|
||||
mutate(y.x);
|
||||
return y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{foo: 3, bar: 4}],
|
||||
sequentialRenders: [
|
||||
{foo: 3, bar: 4},
|
||||
{foo: 3, bar: 5},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function component(foo, bar) {
|
||||
import { mutate } from "shared-runtime";
|
||||
|
||||
function Component(t0) {
|
||||
const $ = _c(3);
|
||||
const { foo, bar } = t0;
|
||||
let y;
|
||||
if ($[0] !== foo || $[1] !== bar) {
|
||||
const x = { foo };
|
||||
y = { bar };
|
||||
const f0 = function () {
|
||||
const a = { y };
|
||||
const a = [y];
|
||||
const b = x;
|
||||
a.x = b;
|
||||
|
||||
a[0].x = b;
|
||||
};
|
||||
|
||||
f0();
|
||||
mutate(y);
|
||||
mutate(y.x);
|
||||
$[0] = foo;
|
||||
$[1] = bar;
|
||||
$[2] = y;
|
||||
@@ -44,5 +60,17 @@ function component(foo, bar) {
|
||||
return y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ foo: 3, bar: 4 }],
|
||||
sequentialRenders: [
|
||||
{ foo: 3, bar: 4 },
|
||||
{ foo: 3, bar: 5 },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Eval output
|
||||
(kind: ok) {"bar":4,"x":{"foo":3,"wat0":"joe"}}
|
||||
{"bar":5,"x":{"foo":3,"wat0":"joe"}}
|
||||
@@ -1,12 +1,24 @@
|
||||
function component(foo, bar) {
|
||||
import {mutate} from 'shared-runtime';
|
||||
|
||||
function Component({foo, bar}) {
|
||||
let x = {foo};
|
||||
let y = {bar};
|
||||
const f0 = function () {
|
||||
let a = {y};
|
||||
let a = [y];
|
||||
let b = x;
|
||||
a.x = b;
|
||||
// this writes y.x = x
|
||||
a[0].x = b;
|
||||
};
|
||||
f0();
|
||||
mutate(y);
|
||||
mutate(y.x);
|
||||
return y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{foo: 3, bar: 4}],
|
||||
sequentialRenders: [
|
||||
{foo: 3, bar: 4},
|
||||
{foo: 3, bar: 5},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -26,29 +26,19 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function component(a, b) {
|
||||
const $ = _c(5);
|
||||
let t0;
|
||||
if ($[0] !== b) {
|
||||
t0 = { b };
|
||||
$[0] = b;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const y = t0;
|
||||
const $ = _c(2);
|
||||
let z;
|
||||
if ($[2] !== a || $[3] !== y.b) {
|
||||
if ($[0] !== a) {
|
||||
z = { a };
|
||||
const x = function () {
|
||||
z.a = 2;
|
||||
};
|
||||
|
||||
x();
|
||||
$[2] = a;
|
||||
$[3] = y.b;
|
||||
$[4] = z;
|
||||
$[0] = a;
|
||||
$[1] = z;
|
||||
} else {
|
||||
z = $[4];
|
||||
z = $[1];
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
@@ -2,21 +2,28 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function component(a, b) {
|
||||
import {mutate} from 'shared-runtime';
|
||||
|
||||
function Component({a, b}) {
|
||||
let z = {a};
|
||||
let y = {b};
|
||||
let y = {b: {b}};
|
||||
let x = function () {
|
||||
z.a = 2;
|
||||
console.log(y.b);
|
||||
mutate(y.b);
|
||||
};
|
||||
x();
|
||||
return z;
|
||||
return [y, z];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: component,
|
||||
params: ['TodoAdd'],
|
||||
isComponent: 'TodoAdd',
|
||||
fn: Component,
|
||||
params: [{a: 2, b: 3}],
|
||||
sequentialRenders: [
|
||||
{a: 2, b: 3},
|
||||
{a: 2, b: 3},
|
||||
{a: 4, b: 3},
|
||||
{a: 4, b: 5},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
@@ -25,32 +32,46 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function component(a, b) {
|
||||
import { mutate } from "shared-runtime";
|
||||
|
||||
function Component(t0) {
|
||||
const $ = _c(3);
|
||||
let z;
|
||||
const { a, b } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a || $[1] !== b) {
|
||||
z = { a };
|
||||
const y = { b };
|
||||
const z = { a };
|
||||
const y = { b: { b } };
|
||||
const x = function () {
|
||||
z.a = 2;
|
||||
console.log(y.b);
|
||||
mutate(y.b);
|
||||
};
|
||||
|
||||
x();
|
||||
t1 = [y, z];
|
||||
$[0] = a;
|
||||
$[1] = b;
|
||||
$[2] = z;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
z = $[2];
|
||||
t1 = $[2];
|
||||
}
|
||||
return z;
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: component,
|
||||
params: ["TodoAdd"],
|
||||
isComponent: "TodoAdd",
|
||||
fn: Component,
|
||||
params: [{ a: 2, b: 3 }],
|
||||
sequentialRenders: [
|
||||
{ a: 2, b: 3 },
|
||||
{ a: 2, b: 3 },
|
||||
{ a: 4, b: 3 },
|
||||
{ a: 4, b: 5 },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [{"b":{"b":3,"wat0":"joe"}},{"a":2}]
|
||||
[{"b":{"b":3,"wat0":"joe"}},{"a":2}]
|
||||
[{"b":{"b":3,"wat0":"joe"}},{"a":2}]
|
||||
[{"b":{"b":5,"wat0":"joe"}},{"a":2}]
|
||||
@@ -1,16 +1,23 @@
|
||||
function component(a, b) {
|
||||
import {mutate} from 'shared-runtime';
|
||||
|
||||
function Component({a, b}) {
|
||||
let z = {a};
|
||||
let y = {b};
|
||||
let y = {b: {b}};
|
||||
let x = function () {
|
||||
z.a = 2;
|
||||
console.log(y.b);
|
||||
mutate(y.b);
|
||||
};
|
||||
x();
|
||||
return z;
|
||||
return [y, z];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: component,
|
||||
params: ['TodoAdd'],
|
||||
isComponent: 'TodoAdd',
|
||||
fn: Component,
|
||||
params: [{a: 2, b: 3}],
|
||||
sequentialRenders: [
|
||||
{a: 2, b: 3},
|
||||
{a: 2, b: 3},
|
||||
{a: 4, b: 3},
|
||||
{a: 4, b: 5},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component({a, b}) {
|
||||
let z = {a};
|
||||
let y = {b};
|
||||
let x = function () {
|
||||
z.a = 2;
|
||||
return Math.max(y.b, 0);
|
||||
};
|
||||
x();
|
||||
return z;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: 2, b: 3}],
|
||||
sequentialRenders: [
|
||||
{a: 2, b: 3},
|
||||
{a: 2, b: 3},
|
||||
{a: 4, b: 3},
|
||||
{a: 4, b: 5},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(t0) {
|
||||
const $ = _c(5);
|
||||
const { a, b } = t0;
|
||||
let z;
|
||||
if ($[0] !== a || $[1] !== b) {
|
||||
z = { a };
|
||||
let t1;
|
||||
if ($[3] !== b) {
|
||||
t1 = { b };
|
||||
$[3] = b;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
const y = t1;
|
||||
const x = function () {
|
||||
z.a = 2;
|
||||
return Math.max(y.b, 0);
|
||||
};
|
||||
|
||||
x();
|
||||
$[0] = a;
|
||||
$[1] = b;
|
||||
$[2] = z;
|
||||
} else {
|
||||
z = $[2];
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ a: 2, b: 3 }],
|
||||
sequentialRenders: [
|
||||
{ a: 2, b: 3 },
|
||||
{ a: 2, b: 3 },
|
||||
{ a: 4, b: 3 },
|
||||
{ a: 4, b: 5 },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) {"a":2}
|
||||
{"a":2}
|
||||
{"a":2}
|
||||
{"a":2}
|
||||
@@ -0,0 +1,21 @@
|
||||
function Component({a, b}) {
|
||||
let z = {a};
|
||||
let y = {b};
|
||||
let x = function () {
|
||||
z.a = 2;
|
||||
return Math.max(y.b, 0);
|
||||
};
|
||||
x();
|
||||
return z;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: 2, b: 3}],
|
||||
sequentialRenders: [
|
||||
{a: 2, b: 3},
|
||||
{a: 2, b: 3},
|
||||
{a: 4, b: 3},
|
||||
{a: 4, b: 5},
|
||||
],
|
||||
};
|
||||
@@ -34,7 +34,6 @@ function component(a) {
|
||||
const x = { a };
|
||||
y = {};
|
||||
|
||||
y;
|
||||
y = x;
|
||||
|
||||
mutate(y);
|
||||
|
||||
@@ -31,7 +31,6 @@ function bar(a) {
|
||||
const x = [a];
|
||||
y = {};
|
||||
|
||||
y;
|
||||
y = x[0][1];
|
||||
$[0] = a;
|
||||
$[1] = y;
|
||||
|
||||
@@ -37,8 +37,6 @@ function bar(a, b) {
|
||||
let t;
|
||||
t = {};
|
||||
|
||||
y;
|
||||
t;
|
||||
y = x[0][1];
|
||||
t = x[1][0];
|
||||
$[0] = a;
|
||||
|
||||
@@ -31,7 +31,6 @@ function bar(a) {
|
||||
const x = [a];
|
||||
y = {};
|
||||
|
||||
y;
|
||||
y = x[0].a[1];
|
||||
$[0] = a;
|
||||
$[1] = y;
|
||||
|
||||
@@ -30,7 +30,6 @@ function bar(a) {
|
||||
const x = [a];
|
||||
y = {};
|
||||
|
||||
y;
|
||||
y = x[0];
|
||||
$[0] = a;
|
||||
$[1] = y;
|
||||
|
||||
@@ -25,7 +25,6 @@ function component(a) {
|
||||
const x = { a };
|
||||
y = 1;
|
||||
|
||||
y;
|
||||
y = x;
|
||||
|
||||
mutate(y);
|
||||
|
||||
@@ -38,9 +38,8 @@ function useTest() {
|
||||
|
||||
const t1 = (w = 42);
|
||||
const t2 = w;
|
||||
|
||||
w;
|
||||
let t3;
|
||||
|
||||
w = 999;
|
||||
t3 = 2;
|
||||
t0 = makeArray(t1, t2, t3);
|
||||
|
||||
@@ -33,9 +33,14 @@ import { c as _c } from "react/compiler-runtime"; /**
|
||||
* props.b *does* influence `a`
|
||||
*/
|
||||
function Component(props) {
|
||||
const $ = _c(2);
|
||||
const $ = _c(5);
|
||||
let a;
|
||||
if ($[0] !== props) {
|
||||
if (
|
||||
$[0] !== props.a ||
|
||||
$[1] !== props.b ||
|
||||
$[2] !== props.c ||
|
||||
$[3] !== props.d
|
||||
) {
|
||||
a = [];
|
||||
a.push(props.a);
|
||||
bb0: {
|
||||
@@ -47,10 +52,13 @@ function Component(props) {
|
||||
}
|
||||
|
||||
a.push(props.d);
|
||||
$[0] = props;
|
||||
$[1] = a;
|
||||
$[0] = props.a;
|
||||
$[1] = props.b;
|
||||
$[2] = props.c;
|
||||
$[3] = props.d;
|
||||
$[4] = a;
|
||||
} else {
|
||||
a = $[1];
|
||||
a = $[4];
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
@@ -70,10 +70,10 @@ import { c as _c } from "react/compiler-runtime"; /**
|
||||
* props.b does *not* influence `a`
|
||||
*/
|
||||
function ComponentA(props) {
|
||||
const $ = _c(3);
|
||||
const $ = _c(5);
|
||||
let a_DEBUG;
|
||||
let t0;
|
||||
if ($[0] !== props) {
|
||||
if ($[0] !== props.a || $[1] !== props.b || $[2] !== props.d) {
|
||||
t0 = Symbol.for("react.early_return_sentinel");
|
||||
bb0: {
|
||||
a_DEBUG = [];
|
||||
@@ -85,12 +85,14 @@ function ComponentA(props) {
|
||||
|
||||
a_DEBUG.push(props.d);
|
||||
}
|
||||
$[0] = props;
|
||||
$[1] = a_DEBUG;
|
||||
$[2] = t0;
|
||||
$[0] = props.a;
|
||||
$[1] = props.b;
|
||||
$[2] = props.d;
|
||||
$[3] = a_DEBUG;
|
||||
$[4] = t0;
|
||||
} else {
|
||||
a_DEBUG = $[1];
|
||||
t0 = $[2];
|
||||
a_DEBUG = $[3];
|
||||
t0 = $[4];
|
||||
}
|
||||
if (t0 !== Symbol.for("react.early_return_sentinel")) {
|
||||
return t0;
|
||||
@@ -102,9 +104,14 @@ function ComponentA(props) {
|
||||
* props.b *does* influence `a`
|
||||
*/
|
||||
function ComponentB(props) {
|
||||
const $ = _c(2);
|
||||
const $ = _c(5);
|
||||
let a;
|
||||
if ($[0] !== props) {
|
||||
if (
|
||||
$[0] !== props.a ||
|
||||
$[1] !== props.b ||
|
||||
$[2] !== props.c ||
|
||||
$[3] !== props.d
|
||||
) {
|
||||
a = [];
|
||||
a.push(props.a);
|
||||
if (props.b) {
|
||||
@@ -112,10 +119,13 @@ function ComponentB(props) {
|
||||
}
|
||||
|
||||
a.push(props.d);
|
||||
$[0] = props;
|
||||
$[1] = a;
|
||||
$[0] = props.a;
|
||||
$[1] = props.b;
|
||||
$[2] = props.c;
|
||||
$[3] = props.d;
|
||||
$[4] = a;
|
||||
} else {
|
||||
a = $[1];
|
||||
a = $[4];
|
||||
}
|
||||
return a;
|
||||
}
|
||||
@@ -124,10 +134,15 @@ function ComponentB(props) {
|
||||
* props.b *does* influence `a`, but only in a way that is never observable
|
||||
*/
|
||||
function ComponentC(props) {
|
||||
const $ = _c(3);
|
||||
const $ = _c(6);
|
||||
let a;
|
||||
let t0;
|
||||
if ($[0] !== props) {
|
||||
if (
|
||||
$[0] !== props.a ||
|
||||
$[1] !== props.b ||
|
||||
$[2] !== props.c ||
|
||||
$[3] !== props.d
|
||||
) {
|
||||
t0 = Symbol.for("react.early_return_sentinel");
|
||||
bb0: {
|
||||
a = [];
|
||||
@@ -140,12 +155,15 @@ function ComponentC(props) {
|
||||
|
||||
a.push(props.d);
|
||||
}
|
||||
$[0] = props;
|
||||
$[1] = a;
|
||||
$[2] = t0;
|
||||
$[0] = props.a;
|
||||
$[1] = props.b;
|
||||
$[2] = props.c;
|
||||
$[3] = props.d;
|
||||
$[4] = a;
|
||||
$[5] = t0;
|
||||
} else {
|
||||
a = $[1];
|
||||
t0 = $[2];
|
||||
a = $[4];
|
||||
t0 = $[5];
|
||||
}
|
||||
if (t0 !== Symbol.for("react.early_return_sentinel")) {
|
||||
return t0;
|
||||
@@ -157,10 +175,15 @@ function ComponentC(props) {
|
||||
* props.b *does* influence `a`
|
||||
*/
|
||||
function ComponentD(props) {
|
||||
const $ = _c(3);
|
||||
const $ = _c(6);
|
||||
let a;
|
||||
let t0;
|
||||
if ($[0] !== props) {
|
||||
if (
|
||||
$[0] !== props.a ||
|
||||
$[1] !== props.b ||
|
||||
$[2] !== props.c ||
|
||||
$[3] !== props.d
|
||||
) {
|
||||
t0 = Symbol.for("react.early_return_sentinel");
|
||||
bb0: {
|
||||
a = [];
|
||||
@@ -173,12 +196,15 @@ function ComponentD(props) {
|
||||
|
||||
a.push(props.d);
|
||||
}
|
||||
$[0] = props;
|
||||
$[1] = a;
|
||||
$[2] = t0;
|
||||
$[0] = props.a;
|
||||
$[1] = props.b;
|
||||
$[2] = props.c;
|
||||
$[3] = props.d;
|
||||
$[4] = a;
|
||||
$[5] = t0;
|
||||
} else {
|
||||
a = $[1];
|
||||
t0 = $[2];
|
||||
a = $[4];
|
||||
t0 = $[5];
|
||||
}
|
||||
if (t0 !== Symbol.for("react.early_return_sentinel")) {
|
||||
return t0;
|
||||
|
||||
@@ -36,9 +36,9 @@ function mayMutate() {}
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function ComponentA(props) {
|
||||
const $ = _c(2);
|
||||
const $ = _c(4);
|
||||
let t0;
|
||||
if ($[0] !== props) {
|
||||
if ($[0] !== props.p0 || $[1] !== props.p1 || $[2] !== props.p2) {
|
||||
const a = [];
|
||||
const b = [];
|
||||
if (b) {
|
||||
@@ -49,18 +49,20 @@ function ComponentA(props) {
|
||||
}
|
||||
|
||||
t0 = <Foo a={a} b={b} />;
|
||||
$[0] = props;
|
||||
$[1] = t0;
|
||||
$[0] = props.p0;
|
||||
$[1] = props.p1;
|
||||
$[2] = props.p2;
|
||||
$[3] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
t0 = $[3];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
function ComponentB(props) {
|
||||
const $ = _c(2);
|
||||
const $ = _c(4);
|
||||
let t0;
|
||||
if ($[0] !== props) {
|
||||
if ($[0] !== props.p0 || $[1] !== props.p1 || $[2] !== props.p2) {
|
||||
const a = [];
|
||||
const b = [];
|
||||
if (mayMutate(b)) {
|
||||
@@ -71,10 +73,12 @@ function ComponentB(props) {
|
||||
}
|
||||
|
||||
t0 = <Foo a={a} b={b} />;
|
||||
$[0] = props;
|
||||
$[1] = t0;
|
||||
$[0] = props.p0;
|
||||
$[1] = props.p1;
|
||||
$[2] = props.p2;
|
||||
$[3] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
t0 = $[3];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
@@ -19,10 +19,10 @@ function foo() {
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function foo() {
|
||||
const $ = _c(1);
|
||||
|
||||
const getJSX = _temp;
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const getJSX = () => <Child x={GLOBAL_IS_X} />;
|
||||
|
||||
t0 = getJSX();
|
||||
$[0] = t0;
|
||||
} else {
|
||||
@@ -31,6 +31,9 @@ function foo() {
|
||||
const result = t0;
|
||||
return result;
|
||||
}
|
||||
function _temp() {
|
||||
return <Child x={GLOBAL_IS_X} />;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -23,13 +23,14 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
```javascript
|
||||
function foo() {
|
||||
const f = () => {
|
||||
console.log(42);
|
||||
};
|
||||
const f = _temp;
|
||||
|
||||
f();
|
||||
return 42;
|
||||
}
|
||||
function _temp() {
|
||||
console.log(42);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: foo,
|
||||
|
||||
@@ -18,12 +18,10 @@ function Component(props) {
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(1);
|
||||
|
||||
const onEvent = _temp;
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const onEvent = () => {
|
||||
console.log(42);
|
||||
};
|
||||
|
||||
t0 = <Foo onEvent={onEvent} />;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
@@ -31,6 +29,9 @@ function Component(props) {
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
function _temp() {
|
||||
console.log(42);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -34,9 +34,8 @@ function Component(props) {
|
||||
let Component;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
Component = Stringify;
|
||||
|
||||
Component;
|
||||
let t0;
|
||||
|
||||
t0 = Component;
|
||||
Component = t0;
|
||||
$[0] = Component;
|
||||
|
||||
@@ -31,9 +31,9 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(5);
|
||||
const $ = _c(7);
|
||||
let t0;
|
||||
if ($[0] !== props) {
|
||||
if ($[0] !== props.cond || $[1] !== props.a || $[2] !== props.b) {
|
||||
t0 = Symbol.for("react.early_return_sentinel");
|
||||
bb0: {
|
||||
const x = [];
|
||||
@@ -41,12 +41,12 @@ function Component(props) {
|
||||
x.push(props.a);
|
||||
if (props.b) {
|
||||
let t1;
|
||||
if ($[2] !== props.b) {
|
||||
if ($[4] !== props.b) {
|
||||
t1 = [props.b];
|
||||
$[2] = props.b;
|
||||
$[3] = t1;
|
||||
$[4] = props.b;
|
||||
$[5] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
t1 = $[5];
|
||||
}
|
||||
const y = t1;
|
||||
x.push(y);
|
||||
@@ -58,20 +58,22 @@ function Component(props) {
|
||||
break bb0;
|
||||
} else {
|
||||
let t1;
|
||||
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = foo();
|
||||
$[4] = t1;
|
||||
$[6] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
t1 = $[6];
|
||||
}
|
||||
t0 = t1;
|
||||
break bb0;
|
||||
}
|
||||
}
|
||||
$[0] = props;
|
||||
$[1] = t0;
|
||||
$[0] = props.cond;
|
||||
$[1] = props.a;
|
||||
$[2] = props.b;
|
||||
$[3] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
t0 = $[3];
|
||||
}
|
||||
if (t0 !== Symbol.for("react.early_return_sentinel")) {
|
||||
return t0;
|
||||
|
||||
@@ -45,9 +45,9 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import { makeArray } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(6);
|
||||
let t0;
|
||||
if ($[0] !== props) {
|
||||
if ($[0] !== props.cond || $[1] !== props.a || $[2] !== props.b) {
|
||||
t0 = Symbol.for("react.early_return_sentinel");
|
||||
bb0: {
|
||||
const x = [];
|
||||
@@ -57,21 +57,23 @@ function Component(props) {
|
||||
break bb0;
|
||||
} else {
|
||||
let t1;
|
||||
if ($[2] !== props.b) {
|
||||
if ($[4] !== props.b) {
|
||||
t1 = makeArray(props.b);
|
||||
$[2] = props.b;
|
||||
$[3] = t1;
|
||||
$[4] = props.b;
|
||||
$[5] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
t1 = $[5];
|
||||
}
|
||||
t0 = t1;
|
||||
break bb0;
|
||||
}
|
||||
}
|
||||
$[0] = props;
|
||||
$[1] = t0;
|
||||
$[0] = props.cond;
|
||||
$[1] = props.a;
|
||||
$[2] = props.b;
|
||||
$[3] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
t0 = $[3];
|
||||
}
|
||||
if (t0 !== Symbol.for("react.early_return_sentinel")) {
|
||||
return t0;
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees @enableOptionalDependencies
|
||||
import {ValidateMemoization} from 'shared-runtime';
|
||||
function Component(props) {
|
||||
const data = useMemo(() => {
|
||||
const x = [];
|
||||
x.push(props?.items);
|
||||
if (props.cond) {
|
||||
x.push(props?.items);
|
||||
}
|
||||
return x;
|
||||
}, [props?.items, props.cond]);
|
||||
return (
|
||||
<ValidateMemoization inputs={[props?.items, props.cond]} output={data} />
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
2 | import {ValidateMemoization} from 'shared-runtime';
|
||||
3 | function Component(props) {
|
||||
> 4 | const data = useMemo(() => {
|
||||
| ^^^^^^^
|
||||
> 5 | const x = [];
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 6 | x.push(props?.items);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 7 | if (props.cond) {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 8 | x.push(props?.items);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 9 | }
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 10 | return x;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 11 | }, [props?.items, props.cond]);
|
||||
| ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected (4:11)
|
||||
12 | return (
|
||||
13 | <ValidateMemoization inputs={[props?.items, props.cond]} output={data} />
|
||||
14 | );
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees @enableOptionalDependencies
|
||||
import {ValidateMemoization} from 'shared-runtime';
|
||||
function Component(props) {
|
||||
const data = useMemo(() => {
|
||||
const x = [];
|
||||
x.push(props?.items);
|
||||
if (props.cond) {
|
||||
x.push(props.items);
|
||||
}
|
||||
return x;
|
||||
}, [props?.items, props.cond]);
|
||||
return (
|
||||
<ValidateMemoization inputs={[props?.items, props.cond]} output={data} />
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
2 | import {ValidateMemoization} from 'shared-runtime';
|
||||
3 | function Component(props) {
|
||||
> 4 | const data = useMemo(() => {
|
||||
| ^^^^^^^
|
||||
> 5 | const x = [];
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 6 | x.push(props?.items);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 7 | if (props.cond) {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 8 | x.push(props.items);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 9 | }
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 10 | return x;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 11 | }, [props?.items, props.cond]);
|
||||
| ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected (4:11)
|
||||
12 | return (
|
||||
13 | <ValidateMemoization inputs={[props?.items, props.cond]} output={data} />
|
||||
14 | );
|
||||
```
|
||||
|
||||
|
||||
@@ -24,13 +24,17 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Error
|
||||
|
||||
```
|
||||
4 | }
|
||||
5 | return baz(); // OK: FuncDecls are HoistableDeclarations that have both declaration and value hoisting
|
||||
6 | function baz() {
|
||||
> 6 | function baz() {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
> 7 | return bar();
|
||||
| ^^^ Todo: Support functions with unreachable code that may contain hoisted declarations (7:7)
|
||||
8 | }
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 8 | }
|
||||
| ^^^^ Todo: Support functions with unreachable code that may contain hoisted declarations (6:8)
|
||||
9 | }
|
||||
10 |
|
||||
11 | export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ function Component() {
|
||||
4 | // NOTE: `i` is a context variable because it's reassigned and also referenced
|
||||
5 | // within a closure, the `onClick` handler of each item
|
||||
> 6 | for (let i = MIN; i <= MAX; i += INCREMENT) {
|
||||
| ^^^^^^^^^^^ Todo: Support for loops where the index variable is a context variable. `i` is a context variable (6:6)
|
||||
| ^ InvalidReact: Updating a value used previously in JSX is not allowed. Consider moving the mutation before the JSX. Found mutation of `i` (6:6)
|
||||
7 | items.push(<Stringify key={i} onClick={() => data.set(i)} />);
|
||||
8 | }
|
||||
9 | return items;
|
||||
|
||||
@@ -25,7 +25,7 @@ export const FIXTURE_ENTRYPONT = {
|
||||
1 | function useFoo(props: {value: {x: string; y: string} | null}) {
|
||||
2 | const value = props.value;
|
||||
> 3 | return createArray(value?.x, value?.y)?.join(', ');
|
||||
| ^^^^^^^^ Todo: Unexpected terminal kind `optional` for optional test block (3:3)
|
||||
| ^^^^^^^^ Todo: Unexpected terminal kind `optional` for optional fallthrough block (3:3)
|
||||
4 | }
|
||||
5 |
|
||||
6 | function createArray<T>(...args: Array<T>): Array<T> {
|
||||
|
||||
@@ -22,7 +22,7 @@ function Component(props) {
|
||||
7 | return hasErrors;
|
||||
8 | }
|
||||
> 9 | return hasErrors();
|
||||
| ^^^^^^^^^ Invariant: [hoisting] Expected value for identifier to be initialized. hasErrors_0$16 (9:9)
|
||||
| ^^^^^^^^^ Invariant: [hoisting] Expected value for identifier to be initialized. hasErrors_0$14 (9:9)
|
||||
10 | }
|
||||
11 |
|
||||
```
|
||||
|
||||
@@ -34,7 +34,7 @@ function Component() {
|
||||
t0 = function update() {
|
||||
"worklet";
|
||||
|
||||
setCount((count_0) => count_0 + 1);
|
||||
setCount(_temp);
|
||||
};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
@@ -51,6 +51,9 @@ function Component() {
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
function _temp(count_0) {
|
||||
return count_0 + 1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableTreatFunctionDepsAsConditional @enablePropagateDepsInHIR:false
|
||||
// @enableTreatFunctionDepsAsConditional
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function Component({props}) {
|
||||
@@ -20,7 +20,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableTreatFunctionDepsAsConditional @enablePropagateDepsInHIR:false
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableTreatFunctionDepsAsConditional
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Component(t0) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @enableTreatFunctionDepsAsConditional @enablePropagateDepsInHIR:false
|
||||
// @enableTreatFunctionDepsAsConditional
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function Component({props}) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableTreatFunctionDepsAsConditional @enablePropagateDepsInHIR:false
|
||||
// @enableTreatFunctionDepsAsConditional
|
||||
function Component(props) {
|
||||
function getLength() {
|
||||
return props.bar.length;
|
||||
@@ -21,15 +21,15 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableTreatFunctionDepsAsConditional @enablePropagateDepsInHIR:false
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableTreatFunctionDepsAsConditional
|
||||
function Component(props) {
|
||||
const $ = _c(5);
|
||||
let t0;
|
||||
if ($[0] !== props) {
|
||||
if ($[0] !== props.bar) {
|
||||
t0 = function getLength() {
|
||||
return props.bar.length;
|
||||
};
|
||||
$[0] = props;
|
||||
$[0] = props.bar;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @enableTreatFunctionDepsAsConditional @enablePropagateDepsInHIR:false
|
||||
// @enableTreatFunctionDepsAsConditional
|
||||
function Component(props) {
|
||||
function getLength() {
|
||||
return props.bar.length;
|
||||
|
||||
@@ -26,9 +26,9 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(2);
|
||||
const $ = _c(3);
|
||||
let items;
|
||||
if ($[0] !== props) {
|
||||
if ($[0] !== props.cond || $[1] !== props.a) {
|
||||
let t0;
|
||||
if (props.cond) {
|
||||
t0 = [];
|
||||
@@ -38,10 +38,11 @@ function Component(props) {
|
||||
items = t0;
|
||||
|
||||
items?.push(props.a);
|
||||
$[0] = props;
|
||||
$[1] = items;
|
||||
$[0] = props.cond;
|
||||
$[1] = props.a;
|
||||
$[2] = items;
|
||||
} else {
|
||||
items = $[1];
|
||||
items = $[2];
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
@@ -33,11 +33,11 @@ function useFoo(t0) {
|
||||
const $ = _c(2);
|
||||
const { a } = t0;
|
||||
let x;
|
||||
if ($[0] !== a.b.c.d) {
|
||||
if ($[0] !== a.b.c.d.e) {
|
||||
x = [];
|
||||
x.push(a?.b.c?.d.e);
|
||||
x.push(a.b?.c.d?.e);
|
||||
$[0] = a.b.c.d;
|
||||
$[0] = a.b.c.d.e;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Throw} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Note: this is disabled in the evaluator due to different devmode errors.
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) <invalidtag val="[object Object]"></invalidtag>
|
||||
* logs: ['Warning: <%s /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.%s','invalidTag']
|
||||
*
|
||||
* Forget:
|
||||
* (kind: ok) <invalidtag val="[object Object]"></invalidtag>
|
||||
* logs: [
|
||||
* 'Warning: <%s /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.%s','invalidTag',
|
||||
* 'Warning: The tag <%s> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.%s','invalidTag',
|
||||
* ]
|
||||
*/
|
||||
function useFoo() {
|
||||
const invalidTag = Throw;
|
||||
/**
|
||||
* Need to be careful to not parse `invalidTag` as a localVar (i.e. render
|
||||
* Throw). Note that the jsx transform turns this into a string tag:
|
||||
* `jsx("invalidTag"...
|
||||
*/
|
||||
return <invalidTag val={{val: 2}} />;
|
||||
}
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Throw } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Note: this is disabled in the evaluator due to different devmode errors.
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) <invalidtag val="[object Object]"></invalidtag>
|
||||
* logs: ['Warning: <%s /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.%s','invalidTag']
|
||||
*
|
||||
* Forget:
|
||||
* (kind: ok) <invalidtag val="[object Object]"></invalidtag>
|
||||
* logs: [
|
||||
* 'Warning: <%s /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.%s','invalidTag',
|
||||
* 'Warning: The tag <%s> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.%s','invalidTag',
|
||||
* ]
|
||||
*/
|
||||
function useFoo() {
|
||||
const $ = _c(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <invalidTag val={{ val: 2 }} />;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import {Throw} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Note: this is disabled in the evaluator due to different devmode errors.
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) <invalidtag val="[object Object]"></invalidtag>
|
||||
* logs: ['Warning: <%s /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.%s','invalidTag']
|
||||
*
|
||||
* Forget:
|
||||
* (kind: ok) <invalidtag val="[object Object]"></invalidtag>
|
||||
* logs: [
|
||||
* 'Warning: <%s /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.%s','invalidTag',
|
||||
* 'Warning: The tag <%s> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.%s','invalidTag',
|
||||
* ]
|
||||
*/
|
||||
function useFoo() {
|
||||
const invalidTag = Throw;
|
||||
/**
|
||||
* Need to be careful to not parse `invalidTag` as a localVar (i.e. render
|
||||
* Throw). Note that the jsx transform turns this into a string tag:
|
||||
* `jsx("invalidTag"...
|
||||
*/
|
||||
return <invalidTag val={{val: 2}} />;
|
||||
}
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [],
|
||||
};
|
||||
@@ -27,11 +27,10 @@ import * as SharedRuntime from "shared-runtime";
|
||||
function useFoo(t0) {
|
||||
const $ = _c(1);
|
||||
const { cond } = t0;
|
||||
const MyLocal = SharedRuntime;
|
||||
if (cond) {
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = <MyLocal.Text value={4} />;
|
||||
t1 = <SharedRuntime.Text value={4} />;
|
||||
$[0] = t1;
|
||||
} else {
|
||||
t1 = $[0];
|
||||
|
||||
@@ -22,10 +22,9 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import * as SharedRuntime from "shared-runtime";
|
||||
function useFoo() {
|
||||
const $ = _c(1);
|
||||
const MyLocal = SharedRuntime;
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <MyLocal.Text value={4} />;
|
||||
t0 = <SharedRuntime.Text value={4} />;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
|
||||
@@ -25,10 +25,10 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
function useFoo() {
|
||||
const $ = _c(1);
|
||||
|
||||
const callback = _temp;
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const callback = () => <Stringify value={4} />;
|
||||
|
||||
t0 = callback();
|
||||
$[0] = t0;
|
||||
} else {
|
||||
@@ -36,6 +36,9 @@ function useFoo() {
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
function _temp() {
|
||||
return <Stringify value={4} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import * as SharedRuntime from 'shared-runtime';
|
||||
import {invoke} from 'shared-runtime';
|
||||
function useComponentFactory({name}) {
|
||||
const localVar = SharedRuntime;
|
||||
const cb = () => <localVar.Stringify>hello world {name}</localVar.Stringify>;
|
||||
return invoke(cb);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useComponentFactory,
|
||||
params: [{name: 'sathya'}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import * as SharedRuntime from "shared-runtime";
|
||||
import { invoke } from "shared-runtime";
|
||||
function useComponentFactory(t0) {
|
||||
const $ = _c(4);
|
||||
const { name } = t0;
|
||||
let t1;
|
||||
if ($[0] !== name) {
|
||||
t1 = () => (
|
||||
<SharedRuntime.Stringify>hello world {name}</SharedRuntime.Stringify>
|
||||
);
|
||||
$[0] = name;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const cb = t1;
|
||||
let t2;
|
||||
if ($[2] !== cb) {
|
||||
t2 = invoke(cb);
|
||||
$[2] = cb;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useComponentFactory,
|
||||
params: [{ name: "sathya" }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"children":["hello world ","sathya"]}</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
import * as SharedRuntime from 'shared-runtime';
|
||||
import {invoke} from 'shared-runtime';
|
||||
function useComponentFactory({name}) {
|
||||
const localVar = SharedRuntime;
|
||||
const cb = () => <localVar.Stringify>hello world {name}</localVar.Stringify>;
|
||||
return invoke(cb);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useComponentFactory,
|
||||
params: [{name: 'sathya'}],
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import * as SharedRuntime from 'shared-runtime';
|
||||
function Component({name}) {
|
||||
const localVar = SharedRuntime;
|
||||
return <localVar.Stringify>hello world {name}</localVar.Stringify>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{name: 'sathya'}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import * as SharedRuntime from "shared-runtime";
|
||||
function Component(t0) {
|
||||
const $ = _c(2);
|
||||
const { name } = t0;
|
||||
let t1;
|
||||
if ($[0] !== name) {
|
||||
t1 = <SharedRuntime.Stringify>hello world {name}</SharedRuntime.Stringify>;
|
||||
$[0] = name;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ name: "sathya" }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"children":["hello world ","sathya"]}</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
import * as SharedRuntime from 'shared-runtime';
|
||||
function Component({name}) {
|
||||
const localVar = SharedRuntime;
|
||||
return <localVar.Stringify>hello world {name}</localVar.Stringify>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{name: 'sathya'}],
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import * as SharedRuntime from 'shared-runtime';
|
||||
function Component({name}) {
|
||||
return <SharedRuntime.Stringify>hello world {name}</SharedRuntime.Stringify>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{name: 'sathya'}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import * as SharedRuntime from "shared-runtime";
|
||||
function Component(t0) {
|
||||
const $ = _c(2);
|
||||
const { name } = t0;
|
||||
let t1;
|
||||
if ($[0] !== name) {
|
||||
t1 = <SharedRuntime.Stringify>hello world {name}</SharedRuntime.Stringify>;
|
||||
$[0] = name;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ name: "sathya" }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"children":["hello world ","sathya"]}</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
import * as SharedRuntime from 'shared-runtime';
|
||||
function Component({name}) {
|
||||
return <SharedRuntime.Stringify>hello world {name}</SharedRuntime.Stringify>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{name: 'sathya'}],
|
||||
};
|
||||
@@ -25,11 +25,10 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import * as SharedRuntime from "shared-runtime";
|
||||
function useFoo() {
|
||||
const $ = _c(1);
|
||||
const MyLocal = SharedRuntime;
|
||||
|
||||
const callback = _temp;
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const callback = () => <MyLocal.Text value={4} />;
|
||||
|
||||
t0 = callback();
|
||||
$[0] = t0;
|
||||
} else {
|
||||
@@ -37,6 +36,9 @@ function useFoo() {
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
function _temp() {
|
||||
return <SharedRuntime.Text value={4} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
|
||||
@@ -26,7 +26,6 @@ function f(a) {
|
||||
const $ = _c(4);
|
||||
let x;
|
||||
if ($[0] !== a) {
|
||||
x;
|
||||
x = { a };
|
||||
$[0] = a;
|
||||
$[1] = x;
|
||||
|
||||
@@ -27,7 +27,6 @@ function f(a) {
|
||||
const $ = _c(2);
|
||||
let x;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
x;
|
||||
x = {};
|
||||
$[0] = x;
|
||||
} else {
|
||||
|
||||
@@ -32,7 +32,7 @@ function Component() {
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = () => {
|
||||
setState((s) => s + 1);
|
||||
setState(_temp);
|
||||
};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
@@ -61,6 +61,9 @@ function Component() {
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
function _temp(s) {
|
||||
return s + 1;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ function Component(props) {
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = (e) => {
|
||||
setX((currentX) => currentX + null);
|
||||
setX(_temp);
|
||||
};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
@@ -48,6 +48,9 @@ function Component(props) {
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
function _temp(currentX) {
|
||||
return currentX + null;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
|
||||
@@ -120,29 +120,29 @@ function useFoo(t0) {
|
||||
}
|
||||
const x = t1;
|
||||
let t2;
|
||||
if ($[2] !== prop2?.inner) {
|
||||
if ($[2] !== prop2?.inner.value) {
|
||||
t2 = identity(prop2?.inner.value)?.toString();
|
||||
$[2] = prop2?.inner;
|
||||
$[2] = prop2?.inner.value;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
const y = t2;
|
||||
let t3;
|
||||
if ($[4] !== prop3 || $[5] !== prop4) {
|
||||
if ($[4] !== prop3 || $[5] !== prop4?.inner) {
|
||||
t3 = prop3?.fn(prop4?.inner.value).toString();
|
||||
$[4] = prop3;
|
||||
$[5] = prop4;
|
||||
$[5] = prop4?.inner;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
const z = t3;
|
||||
let t4;
|
||||
if ($[7] !== prop5 || $[8] !== prop6) {
|
||||
if ($[7] !== prop5 || $[8] !== prop6?.inner) {
|
||||
t4 = prop5?.fn(prop6?.inner.value)?.toString();
|
||||
$[7] = prop5;
|
||||
$[8] = prop6;
|
||||
$[8] = prop6?.inner;
|
||||
$[9] = t4;
|
||||
} else {
|
||||
t4 = $[9];
|
||||
|
||||
@@ -29,9 +29,9 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import { makeObject_Primitives } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(2);
|
||||
const $ = _c(3);
|
||||
let t0;
|
||||
if ($[0] !== props) {
|
||||
if ($[0] !== props.cond || $[1] !== props.value) {
|
||||
t0 = Symbol.for("react.early_return_sentinel");
|
||||
bb0: {
|
||||
const object = makeObject_Primitives();
|
||||
@@ -45,10 +45,11 @@ function Component(props) {
|
||||
break bb0;
|
||||
}
|
||||
}
|
||||
$[0] = props;
|
||||
$[1] = t0;
|
||||
$[0] = props.cond;
|
||||
$[1] = props.value;
|
||||
$[2] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
t0 = $[2];
|
||||
}
|
||||
if (t0 !== Symbol.for("react.early_return_sentinel")) {
|
||||
return t0;
|
||||
|
||||
@@ -40,15 +40,15 @@ function useHook(t0) {
|
||||
t1 = $[1];
|
||||
}
|
||||
let t2;
|
||||
if ($[2] !== b || $[3] !== t1) {
|
||||
if ($[2] !== t1 || $[3] !== b) {
|
||||
t2 = {
|
||||
x: t1,
|
||||
y() {
|
||||
return [b];
|
||||
},
|
||||
};
|
||||
$[2] = b;
|
||||
$[3] = t1;
|
||||
$[2] = t1;
|
||||
$[3] = b;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
|
||||
@@ -40,7 +40,7 @@ function useHook(t0) {
|
||||
const { value } = t0;
|
||||
const [state] = useState(false);
|
||||
let t1;
|
||||
if ($[0] !== value || $[1] !== state) {
|
||||
if ($[0] !== state || $[1] !== value) {
|
||||
t1 = {
|
||||
getX() {
|
||||
return {
|
||||
@@ -52,8 +52,8 @@ function useHook(t0) {
|
||||
};
|
||||
},
|
||||
};
|
||||
$[0] = value;
|
||||
$[1] = state;
|
||||
$[0] = state;
|
||||
$[1] = value;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees @enableOptionalDependencies
|
||||
import {ValidateMemoization} from 'shared-runtime';
|
||||
function Component(props) {
|
||||
const data = useMemo(() => {
|
||||
const x = [];
|
||||
x.push(props?.items);
|
||||
if (props.cond) {
|
||||
x.push(props?.items);
|
||||
}
|
||||
return x;
|
||||
}, [props?.items, props.cond]);
|
||||
return (
|
||||
<ValidateMemoization inputs={[props?.items, props.cond]} output={data} />
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees @enableOptionalDependencies
|
||||
import { ValidateMemoization } from "shared-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(9);
|
||||
|
||||
props?.items;
|
||||
let t0;
|
||||
let x;
|
||||
if ($[0] !== props?.items || $[1] !== props.cond) {
|
||||
x = [];
|
||||
x.push(props?.items);
|
||||
if (props.cond) {
|
||||
x.push(props?.items);
|
||||
}
|
||||
$[0] = props?.items;
|
||||
$[1] = props.cond;
|
||||
$[2] = x;
|
||||
} else {
|
||||
x = $[2];
|
||||
}
|
||||
t0 = x;
|
||||
const data = t0;
|
||||
|
||||
const t1 = props?.items;
|
||||
let t2;
|
||||
if ($[3] !== t1 || $[4] !== props.cond) {
|
||||
t2 = [t1, props.cond];
|
||||
$[3] = t1;
|
||||
$[4] = props.cond;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
let t3;
|
||||
if ($[6] !== t2 || $[7] !== data) {
|
||||
t3 = <ValidateMemoization inputs={t2} output={data} />;
|
||||
$[6] = t2;
|
||||
$[7] = data;
|
||||
$[8] = t3;
|
||||
} else {
|
||||
t3 = $[8];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -1,74 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees @enableOptionalDependencies
|
||||
import {ValidateMemoization} from 'shared-runtime';
|
||||
function Component(props) {
|
||||
const data = useMemo(() => {
|
||||
const x = [];
|
||||
x.push(props?.items);
|
||||
if (props.cond) {
|
||||
x.push(props.items);
|
||||
}
|
||||
return x;
|
||||
}, [props?.items, props.cond]);
|
||||
return (
|
||||
<ValidateMemoization inputs={[props?.items, props.cond]} output={data} />
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees @enableOptionalDependencies
|
||||
import { ValidateMemoization } from "shared-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(9);
|
||||
|
||||
props?.items;
|
||||
let t0;
|
||||
let x;
|
||||
if ($[0] !== props?.items || $[1] !== props.cond) {
|
||||
x = [];
|
||||
x.push(props?.items);
|
||||
if (props.cond) {
|
||||
x.push(props.items);
|
||||
}
|
||||
$[0] = props?.items;
|
||||
$[1] = props.cond;
|
||||
$[2] = x;
|
||||
} else {
|
||||
x = $[2];
|
||||
}
|
||||
t0 = x;
|
||||
const data = t0;
|
||||
|
||||
const t1 = props?.items;
|
||||
let t2;
|
||||
if ($[3] !== t1 || $[4] !== props.cond) {
|
||||
t2 = [t1, props.cond];
|
||||
$[3] = t1;
|
||||
$[4] = props.cond;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
let t3;
|
||||
if ($[6] !== t2 || $[7] !== data) {
|
||||
t3 = <ValidateMemoization inputs={t2} output={data} />;
|
||||
$[6] = t2;
|
||||
$[7] = data;
|
||||
$[8] = t3;
|
||||
} else {
|
||||
t3 = $[8];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -30,10 +30,10 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(6);
|
||||
let y;
|
||||
let t0;
|
||||
if ($[0] !== props) {
|
||||
if ($[0] !== props.cond || $[1] !== props.a || $[2] !== props.b) {
|
||||
t0 = Symbol.for("react.early_return_sentinel");
|
||||
bb0: {
|
||||
const x = [];
|
||||
@@ -43,11 +43,11 @@ function Component(props) {
|
||||
break bb0;
|
||||
} else {
|
||||
let t1;
|
||||
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = foo();
|
||||
$[3] = t1;
|
||||
$[5] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
t1 = $[5];
|
||||
}
|
||||
y = t1;
|
||||
if (props.b) {
|
||||
@@ -56,12 +56,14 @@ function Component(props) {
|
||||
}
|
||||
}
|
||||
}
|
||||
$[0] = props;
|
||||
$[1] = y;
|
||||
$[2] = t0;
|
||||
$[0] = props.cond;
|
||||
$[1] = props.a;
|
||||
$[2] = props.b;
|
||||
$[3] = y;
|
||||
$[4] = t0;
|
||||
} else {
|
||||
y = $[1];
|
||||
t0 = $[2];
|
||||
y = $[3];
|
||||
t0 = $[4];
|
||||
}
|
||||
if (t0 !== Symbol.for("react.early_return_sentinel")) {
|
||||
return t0;
|
||||
|
||||
@@ -49,7 +49,7 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import { makeArray } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(3);
|
||||
const $ = _c(6);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = {};
|
||||
@@ -59,7 +59,12 @@ function Component(props) {
|
||||
}
|
||||
const x = t0;
|
||||
let t1;
|
||||
if ($[1] !== props) {
|
||||
if (
|
||||
$[1] !== props.cond ||
|
||||
$[2] !== props.cond2 ||
|
||||
$[3] !== props.value ||
|
||||
$[4] !== props.value2
|
||||
) {
|
||||
let y;
|
||||
if (props.cond) {
|
||||
if (props.cond2) {
|
||||
@@ -74,10 +79,13 @@ function Component(props) {
|
||||
y.push(x);
|
||||
|
||||
t1 = [x, y];
|
||||
$[1] = props;
|
||||
$[2] = t1;
|
||||
$[1] = props.cond;
|
||||
$[2] = props.cond2;
|
||||
$[3] = props.value;
|
||||
$[4] = props.value2;
|
||||
$[5] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
t1 = $[5];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(3);
|
||||
const $ = _c(4);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = {};
|
||||
@@ -46,7 +46,7 @@ function Component(props) {
|
||||
}
|
||||
const x = t0;
|
||||
let t1;
|
||||
if ($[1] !== props) {
|
||||
if ($[1] !== props.cond || $[2] !== props.value) {
|
||||
let y;
|
||||
if (props.cond) {
|
||||
y = [props.value];
|
||||
@@ -57,10 +57,11 @@ function Component(props) {
|
||||
y.push(x);
|
||||
|
||||
t1 = [x, y];
|
||||
$[1] = props;
|
||||
$[2] = t1;
|
||||
$[1] = props.cond;
|
||||
$[2] = props.value;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @debug
|
||||
function Component(props) {
|
||||
const $ = _c(3);
|
||||
const $ = _c(4);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = {};
|
||||
@@ -42,7 +42,7 @@ function Component(props) {
|
||||
}
|
||||
const x = t0;
|
||||
let t1;
|
||||
if ($[1] !== props) {
|
||||
if ($[1] !== props.cond || $[2] !== props.a) {
|
||||
let y;
|
||||
if (props.cond) {
|
||||
y = {};
|
||||
@@ -53,10 +53,11 @@ function Component(props) {
|
||||
y.x = x;
|
||||
|
||||
t1 = [x, y];
|
||||
$[1] = props;
|
||||
$[2] = t1;
|
||||
$[1] = props.cond;
|
||||
$[2] = props.a;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useCallback} from 'react';
|
||||
|
||||
function Component({propA, propB}) {
|
||||
return useCallback(() => {
|
||||
if (propA) {
|
||||
return {
|
||||
value: propB.x.y,
|
||||
};
|
||||
}
|
||||
}, [propA, propB.x.y]);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{propA: 1, propB: {x: {y: []}}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
3 |
|
||||
4 | function Component({propA, propB}) {
|
||||
> 5 | return useCallback(() => {
|
||||
| ^^^^^^^
|
||||
> 6 | if (propA) {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
> 7 | return {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
> 8 | value: propB.x.y,
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
> 9 | };
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
> 10 | }
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
> 11 | }, [propA, propB.x.y]);
|
||||
| ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected (5:11)
|
||||
12 | }
|
||||
13 |
|
||||
14 | export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useCallback} from 'react';
|
||||
import {identity, mutate} from 'shared-runtime';
|
||||
|
||||
function useHook(propA, propB) {
|
||||
return useCallback(() => {
|
||||
const x = {};
|
||||
if (identity(null) ?? propA.a) {
|
||||
mutate(x);
|
||||
return {
|
||||
value: propB.x.y,
|
||||
};
|
||||
}
|
||||
}, [propA.a, propB.x.y]);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useHook,
|
||||
params: [{a: 1}, {x: {y: 3}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
4 |
|
||||
5 | function useHook(propA, propB) {
|
||||
> 6 | return useCallback(() => {
|
||||
| ^^^^^^^
|
||||
> 7 | const x = {};
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 8 | if (identity(null) ?? propA.a) {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 9 | mutate(x);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 10 | return {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 11 | value: propB.x.y,
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 12 | };
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 13 | }
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
> 14 | }, [propA.a, propB.x.y]);
|
||||
| ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected (6:14)
|
||||
|
||||
CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected (6:14)
|
||||
15 | }
|
||||
16 |
|
||||
17 | export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useCallback} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* TODO: we're currently bailing out because `contextVar` is a context variable
|
||||
* and not recorded into the PropagateScopeDeps LoadLocal / PropertyLoad
|
||||
* sidemap. Previously, we were able to avoid this as `BuildHIR` hoisted
|
||||
* `LoadContext` and `PropertyLoad` instructions into the outer function, which
|
||||
* we took as eligible dependencies.
|
||||
*
|
||||
* One solution is to simply record `LoadContext` identifiers into the
|
||||
* temporaries sidemap when the instruction occurs *after* the context
|
||||
* variable's mutable range.
|
||||
*/
|
||||
function Foo(props) {
|
||||
let contextVar;
|
||||
if (props.cond) {
|
||||
contextVar = {val: 2};
|
||||
} else {
|
||||
contextVar = {};
|
||||
}
|
||||
|
||||
const cb = useCallback(() => [contextVar.val], [contextVar.val]);
|
||||
|
||||
return <Stringify cb={cb} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{cond: true}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
22 | }
|
||||
23 |
|
||||
> 24 | const cb = useCallback(() => [contextVar.val], [contextVar.val]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected (24:24)
|
||||
25 |
|
||||
26 | return <Stringify cb={cb} shouldInvokeFns={true} />;
|
||||
27 | }
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useCallback} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* TODO: we're currently bailing out because `contextVar` is a context variable
|
||||
* and not recorded into the PropagateScopeDeps LoadLocal / PropertyLoad
|
||||
* sidemap. Previously, we were able to avoid this as `BuildHIR` hoisted
|
||||
* `LoadContext` and `PropertyLoad` instructions into the outer function, which
|
||||
* we took as eligible dependencies.
|
||||
*
|
||||
* One solution is to simply record `LoadContext` identifiers into the
|
||||
* temporaries sidemap when the instruction occurs *after* the context
|
||||
* variable's mutable range.
|
||||
*/
|
||||
function Foo(props) {
|
||||
let contextVar;
|
||||
if (props.cond) {
|
||||
contextVar = {val: 2};
|
||||
} else {
|
||||
contextVar = {};
|
||||
}
|
||||
|
||||
const cb = useCallback(() => [contextVar.val], [contextVar.val]);
|
||||
|
||||
return <Stringify cb={cb} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{cond: true}],
|
||||
};
|
||||
@@ -1,81 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useCallback} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function Foo(props) {
|
||||
let contextVar;
|
||||
if (props.cond) {
|
||||
contextVar = {val: 2};
|
||||
} else {
|
||||
contextVar = {};
|
||||
}
|
||||
|
||||
const cb = useCallback(() => [contextVar.val], [contextVar.val]);
|
||||
|
||||
return <Stringify cb={cb} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{cond: true}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
|
||||
import { useCallback } from "react";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Foo(props) {
|
||||
const $ = _c(6);
|
||||
let contextVar;
|
||||
if ($[0] !== props.cond) {
|
||||
if (props.cond) {
|
||||
contextVar = { val: 2 };
|
||||
} else {
|
||||
contextVar = {};
|
||||
}
|
||||
$[0] = props.cond;
|
||||
$[1] = contextVar;
|
||||
} else {
|
||||
contextVar = $[1];
|
||||
}
|
||||
|
||||
const t0 = contextVar;
|
||||
let t1;
|
||||
if ($[2] !== t0.val) {
|
||||
t1 = () => [contextVar.val];
|
||||
$[2] = t0.val;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
contextVar;
|
||||
const cb = t1;
|
||||
let t2;
|
||||
if ($[4] !== cb) {
|
||||
t2 = <Stringify cb={cb} shouldInvokeFns={true} />;
|
||||
$[4] = cb;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{ cond: true }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"cb":{"kind":"Function","result":[2]},"shouldInvokeFns":true}</div>
|
||||
@@ -1,21 +0,0 @@
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useCallback} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function Foo(props) {
|
||||
let contextVar;
|
||||
if (props.cond) {
|
||||
contextVar = {val: 2};
|
||||
} else {
|
||||
contextVar = {};
|
||||
}
|
||||
|
||||
const cb = useCallback(() => [contextVar.val], [contextVar.val]);
|
||||
|
||||
return <Stringify cb={cb} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{cond: true}],
|
||||
};
|
||||
@@ -45,18 +45,16 @@ function Foo(props) {
|
||||
} else {
|
||||
x = $[1];
|
||||
}
|
||||
|
||||
const t0 = x;
|
||||
let t1;
|
||||
if ($[2] !== t0) {
|
||||
t1 = () => [x];
|
||||
$[2] = t0;
|
||||
$[3] = t1;
|
||||
let t0;
|
||||
if ($[2] !== x) {
|
||||
t0 = () => [x];
|
||||
$[2] = x;
|
||||
$[3] = t0;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
t0 = $[3];
|
||||
}
|
||||
x;
|
||||
const cb = t1;
|
||||
const cb = t0;
|
||||
return cb;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useCallback} from 'react';
|
||||
|
||||
function Component({propA, propB}) {
|
||||
return useCallback(() => {
|
||||
if (propA) {
|
||||
return {
|
||||
value: propB.x.y,
|
||||
};
|
||||
}
|
||||
}, [propA, propB.x.y]);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{propA: 1, propB: {x: {y: []}}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
|
||||
import { useCallback } from "react";
|
||||
|
||||
function Component(t0) {
|
||||
const $ = _c(3);
|
||||
const { propA, propB } = t0;
|
||||
let t1;
|
||||
if ($[0] !== propA || $[1] !== propB.x.y) {
|
||||
t1 = () => {
|
||||
if (propA) {
|
||||
return { value: propB.x.y };
|
||||
}
|
||||
};
|
||||
$[0] = propA;
|
||||
$[1] = propB.x.y;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ propA: 1, propB: { x: { y: [] } } }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) "[[ function params=0 ]]"
|
||||
@@ -70,28 +70,26 @@ function useBar(t0, cond) {
|
||||
if (cond) {
|
||||
x = b;
|
||||
}
|
||||
|
||||
const t2 = x;
|
||||
let t3;
|
||||
if ($[1] !== a || $[2] !== t2) {
|
||||
t3 = () => [a, x];
|
||||
$[1] = a;
|
||||
$[2] = t2;
|
||||
$[3] = t3;
|
||||
let t2;
|
||||
if ($[1] !== x || $[2] !== a) {
|
||||
t2 = () => [a, x];
|
||||
$[1] = x;
|
||||
$[2] = a;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t3 = $[3];
|
||||
t2 = $[3];
|
||||
}
|
||||
x;
|
||||
const cb = t3;
|
||||
let t4;
|
||||
const cb = t2;
|
||||
let t3;
|
||||
if ($[4] !== cb) {
|
||||
t4 = <Stringify cb={cb} shouldInvoke={true} />;
|
||||
t3 = <Stringify cb={cb} shouldInvoke={true} />;
|
||||
$[4] = cb;
|
||||
$[5] = t4;
|
||||
$[5] = t3;
|
||||
} else {
|
||||
t4 = $[5];
|
||||
t3 = $[5];
|
||||
}
|
||||
return t4;
|
||||
return t3;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useCallback} from 'react';
|
||||
import {identity, mutate} from 'shared-runtime';
|
||||
|
||||
function useHook(propA, propB) {
|
||||
return useCallback(() => {
|
||||
const x = {};
|
||||
if (identity(null) ?? propA.a) {
|
||||
mutate(x);
|
||||
return {
|
||||
value: propB.x.y,
|
||||
};
|
||||
}
|
||||
}, [propA.a, propB.x.y]);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useHook,
|
||||
params: [{a: 1}, {x: {y: 3}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
|
||||
import { useCallback } from "react";
|
||||
import { identity, mutate } from "shared-runtime";
|
||||
|
||||
function useHook(propA, propB) {
|
||||
const $ = _c(3);
|
||||
let t0;
|
||||
if ($[0] !== propA.a || $[1] !== propB.x.y) {
|
||||
t0 = () => {
|
||||
const x = {};
|
||||
if (identity(null) ?? propA.a) {
|
||||
mutate(x);
|
||||
return { value: propB.x.y };
|
||||
}
|
||||
};
|
||||
$[0] = propA.a;
|
||||
$[1] = propB.x.y;
|
||||
$[2] = t0;
|
||||
} else {
|
||||
t0 = $[2];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useHook,
|
||||
params: [{ a: 1 }, { x: { y: 3 } }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) "[[ function params=0 ]]"
|
||||
@@ -35,7 +35,6 @@ function useFoo(arr1, arr2) {
|
||||
if ($[0] !== arr1 || $[1] !== arr2) {
|
||||
const x = [arr1];
|
||||
|
||||
y;
|
||||
(y = x.concat(arr2)), y;
|
||||
$[0] = arr1;
|
||||
$[1] = arr2;
|
||||
|
||||
@@ -34,9 +34,9 @@ function useFoo(t0) {
|
||||
const $ = _c(2);
|
||||
const { a } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a.b) {
|
||||
if ($[0] !== a.b?.c.d?.e) {
|
||||
t1 = <Stringify fn={() => a.b?.c.d?.e} shouldInvokeFns={true} />;
|
||||
$[0] = a.b;
|
||||
$[0] = a.b?.c.d?.e;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
|
||||
@@ -29,36 +29,38 @@ import { c as _c } from "react/compiler-runtime";
|
||||
const FooContext = React.createContext({ current: null });
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(5);
|
||||
const $ = _c(7);
|
||||
React.useContext(FooContext);
|
||||
const ref = React.useRef();
|
||||
const [x, setX] = React.useState(false);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
if ($[0] !== ref) {
|
||||
t0 = () => {
|
||||
setX(true);
|
||||
ref.current = true;
|
||||
};
|
||||
$[0] = t0;
|
||||
$[0] = ref;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
t0 = $[1];
|
||||
}
|
||||
const onClick = t0;
|
||||
let t1;
|
||||
if ($[1] !== props.children) {
|
||||
if ($[2] !== props.children) {
|
||||
t1 = React.cloneElement(props.children);
|
||||
$[1] = props.children;
|
||||
$[2] = t1;
|
||||
$[2] = props.children;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
t1 = $[3];
|
||||
}
|
||||
let t2;
|
||||
if ($[3] !== t1) {
|
||||
if ($[4] !== onClick || $[5] !== t1) {
|
||||
t2 = <div onClick={onClick}>{t1}</div>;
|
||||
$[3] = t1;
|
||||
$[4] = t2;
|
||||
$[4] = onClick;
|
||||
$[5] = t1;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
t2 = $[6];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user