Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ca2911799 |
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {Effect, ValueKind, ValueReason} from './HIR';
|
||||
import {Effect, makeIdentifierId, ValueKind, ValueReason} from './HIR';
|
||||
import {
|
||||
BUILTIN_SHAPES,
|
||||
BuiltInArrayId,
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
addFunction,
|
||||
addHook,
|
||||
addObject,
|
||||
signatureArgument,
|
||||
} from './ObjectShape';
|
||||
import {BuiltInType, ObjectType, PolyType} from './Types';
|
||||
import {TypeConfig} from './TypeSchema';
|
||||
@@ -645,35 +646,35 @@ const REACT_APIS: Array<[string, BuiltInType]> = [
|
||||
hookKind: 'useEffect',
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
aliasing: {
|
||||
receiver: '@receiver',
|
||||
receiver: makeIdentifierId(0),
|
||||
params: [],
|
||||
rest: '@rest',
|
||||
returns: '@returns',
|
||||
temporaries: ['@effect'],
|
||||
rest: makeIdentifierId(1),
|
||||
returns: makeIdentifierId(2),
|
||||
temporaries: [signatureArgument(3)],
|
||||
effects: [
|
||||
// Freezes the function and deps
|
||||
{
|
||||
kind: 'Freeze',
|
||||
value: '@rest',
|
||||
value: signatureArgument(1),
|
||||
reason: ValueReason.Effect,
|
||||
},
|
||||
// Internally creates an effect object that captures the function and deps
|
||||
{
|
||||
kind: 'Create',
|
||||
into: '@effect',
|
||||
into: signatureArgument(3),
|
||||
value: ValueKind.Frozen,
|
||||
reason: ValueReason.KnownReturnSignature,
|
||||
},
|
||||
// The effect stores the function and dependencies
|
||||
{
|
||||
kind: 'Capture',
|
||||
from: '@rest',
|
||||
into: '@effect',
|
||||
from: signatureArgument(1),
|
||||
into: signatureArgument(3),
|
||||
},
|
||||
// Returns undefined
|
||||
{
|
||||
kind: 'Create',
|
||||
into: '@returns',
|
||||
into: signatureArgument(2),
|
||||
value: ValueKind.Primitive,
|
||||
reason: ValueReason.KnownReturnSignature,
|
||||
},
|
||||
@@ -905,7 +906,6 @@ export function installTypeConfig(
|
||||
noAlias: typeConfig.noAlias === true,
|
||||
mutableOnlyIfOperandsAreMutable:
|
||||
typeConfig.mutableOnlyIfOperandsAreMutable === true,
|
||||
aliasing: typeConfig.aliasing,
|
||||
});
|
||||
}
|
||||
case 'hook': {
|
||||
@@ -923,7 +923,6 @@ export function installTypeConfig(
|
||||
),
|
||||
returnValueKind: typeConfig.returnValueKind ?? ValueKind.Frozen,
|
||||
noAlias: typeConfig.noAlias === true,
|
||||
aliasing: typeConfig.aliasing,
|
||||
});
|
||||
}
|
||||
case 'object': {
|
||||
|
||||
@@ -1453,20 +1453,6 @@ export const ValueKindSchema = z.enum([
|
||||
ValueKind.Context,
|
||||
]);
|
||||
|
||||
export const ValueReasonSchema = z.enum([
|
||||
ValueReason.Context,
|
||||
ValueReason.Effect,
|
||||
ValueReason.Global,
|
||||
ValueReason.HookCaptured,
|
||||
ValueReason.HookReturn,
|
||||
ValueReason.JsxCaptured,
|
||||
ValueReason.KnownReturnSignature,
|
||||
ValueReason.Other,
|
||||
ValueReason.ReactiveFunctionArgument,
|
||||
ValueReason.ReducerState,
|
||||
ValueReason.State,
|
||||
]);
|
||||
|
||||
// The effect with which a value is modified.
|
||||
export enum Effect {
|
||||
// Default value: not allowed after lifetime inference
|
||||
|
||||
@@ -6,18 +6,14 @@
|
||||
*/
|
||||
|
||||
import {CompilerError} from '../CompilerError';
|
||||
import {AliasingEffect, AliasingSignature} from '../Inference/AliasingEffects';
|
||||
import {assertExhaustive} from '../Utils/utils';
|
||||
import {AliasingSignature} from '../Inference/AliasingEffects';
|
||||
import {
|
||||
Effect,
|
||||
GeneratedSource,
|
||||
Hole,
|
||||
makeDeclarationId,
|
||||
makeIdentifierId,
|
||||
makeInstructionId,
|
||||
Place,
|
||||
SourceLocation,
|
||||
SpreadPattern,
|
||||
ValueKind,
|
||||
ValueReason,
|
||||
} from './HIR';
|
||||
@@ -29,7 +25,6 @@ import {
|
||||
PolyType,
|
||||
PrimitiveType,
|
||||
} from './Types';
|
||||
import {AliasingEffectConfig, AliasingSignatureConfig} from './TypeSchema';
|
||||
|
||||
/*
|
||||
* This file exports types and defaults for JavaScript object shapes. These are
|
||||
@@ -58,20 +53,13 @@ function createAnonId(): string {
|
||||
export function addFunction(
|
||||
registry: ShapeRegistry,
|
||||
properties: Iterable<[string, BuiltInType | PolyType]>,
|
||||
fn: Omit<FunctionSignature, 'hookKind' | 'aliasing'> & {
|
||||
aliasing?: AliasingSignatureConfig | null | undefined;
|
||||
},
|
||||
fn: Omit<FunctionSignature, 'hookKind'>,
|
||||
id: string | null = null,
|
||||
isConstructor: boolean = false,
|
||||
): FunctionType {
|
||||
const shapeId = id ?? createAnonId();
|
||||
const aliasing =
|
||||
fn.aliasing != null
|
||||
? parseAliasingSignatureConfig(fn.aliasing, '<builtin>', GeneratedSource)
|
||||
: null;
|
||||
addShape(registry, shapeId, properties, {
|
||||
...fn,
|
||||
aliasing,
|
||||
hookKind: null,
|
||||
});
|
||||
return {
|
||||
@@ -89,18 +77,11 @@ export function addFunction(
|
||||
*/
|
||||
export function addHook(
|
||||
registry: ShapeRegistry,
|
||||
fn: Omit<FunctionSignature, 'aliasing'> & {
|
||||
hookKind: HookKind;
|
||||
aliasing?: AliasingSignatureConfig | null | undefined;
|
||||
},
|
||||
fn: FunctionSignature & {hookKind: HookKind},
|
||||
id: string | null = null,
|
||||
): FunctionType {
|
||||
const shapeId = id ?? createAnonId();
|
||||
const aliasing =
|
||||
fn.aliasing != null
|
||||
? parseAliasingSignatureConfig(fn.aliasing, '<builtin>', GeneratedSource)
|
||||
: null;
|
||||
addShape(registry, shapeId, [], {...fn, aliasing});
|
||||
addShape(registry, shapeId, [], fn);
|
||||
return {
|
||||
kind: 'Function',
|
||||
return: fn.returnType,
|
||||
@@ -109,129 +90,6 @@ export function addHook(
|
||||
};
|
||||
}
|
||||
|
||||
function parseAliasingSignatureConfig(
|
||||
typeConfig: AliasingSignatureConfig,
|
||||
moduleName: string,
|
||||
loc: SourceLocation,
|
||||
): AliasingSignature {
|
||||
const lifetimes = new Map<string, Place>();
|
||||
function define(temp: string): Place {
|
||||
CompilerError.invariant(!lifetimes.has(temp), {
|
||||
reason: `Invalid type configuration for module`,
|
||||
description: `Expected aliasing signature to have unique names for receiver, params, rest, returns, and temporaries in module '${moduleName}'`,
|
||||
loc,
|
||||
});
|
||||
const place = signatureArgument(lifetimes.size);
|
||||
lifetimes.set(temp, place);
|
||||
return place;
|
||||
}
|
||||
function lookup(temp: string): Place {
|
||||
const place = lifetimes.get(temp);
|
||||
CompilerError.invariant(place != null, {
|
||||
reason: `Invalid type configuration for module`,
|
||||
description: `Expected aliasing signature effects to reference known names from receiver/params/rest/returns/temporaries, but '${temp}' is not a known name in '${moduleName}'`,
|
||||
loc,
|
||||
});
|
||||
return place;
|
||||
}
|
||||
const receiver = define(typeConfig.receiver);
|
||||
const params = typeConfig.params.map(define);
|
||||
const rest = typeConfig.rest != null ? define(typeConfig.rest) : null;
|
||||
const returns = define(typeConfig.returns);
|
||||
const temporaries = typeConfig.temporaries.map(define);
|
||||
const effects = typeConfig.effects.map(
|
||||
(effect: AliasingEffectConfig): AliasingEffect => {
|
||||
switch (effect.kind) {
|
||||
case 'CreateFrom':
|
||||
case 'Capture':
|
||||
case 'Alias':
|
||||
case 'Assign': {
|
||||
const from = lookup(effect.from);
|
||||
const into = lookup(effect.into);
|
||||
return {
|
||||
kind: effect.kind,
|
||||
from,
|
||||
into,
|
||||
};
|
||||
}
|
||||
case 'Mutate':
|
||||
case 'MutateTransitiveConditionally': {
|
||||
const value = lookup(effect.value);
|
||||
return {kind: effect.kind, value};
|
||||
}
|
||||
case 'Create': {
|
||||
const into = lookup(effect.into);
|
||||
return {
|
||||
kind: 'Create',
|
||||
into,
|
||||
reason: effect.reason,
|
||||
value: effect.value,
|
||||
};
|
||||
}
|
||||
case 'Freeze': {
|
||||
const value = lookup(effect.value);
|
||||
return {
|
||||
kind: 'Freeze',
|
||||
value,
|
||||
reason: effect.reason,
|
||||
};
|
||||
}
|
||||
case 'Impure': {
|
||||
const place = lookup(effect.place);
|
||||
return {
|
||||
kind: 'Impure',
|
||||
place,
|
||||
error: CompilerError.throwTodo({
|
||||
reason: 'Support impure effect declarations',
|
||||
loc: GeneratedSource,
|
||||
}),
|
||||
};
|
||||
}
|
||||
case 'Apply': {
|
||||
const receiver = lookup(effect.receiver);
|
||||
const fn = lookup(effect.function);
|
||||
const args: Array<Place | SpreadPattern | Hole> = effect.args.map(
|
||||
arg => {
|
||||
if (typeof arg === 'string') {
|
||||
return lookup(arg);
|
||||
} else if (arg.kind === 'Spread') {
|
||||
return {kind: 'Spread', place: lookup(arg.place)};
|
||||
} else {
|
||||
return arg;
|
||||
}
|
||||
},
|
||||
);
|
||||
const into = lookup(effect.into);
|
||||
return {
|
||||
kind: 'Apply',
|
||||
receiver,
|
||||
function: fn,
|
||||
mutatesFunction: effect.mutatesFunction,
|
||||
args,
|
||||
into,
|
||||
loc,
|
||||
signature: null,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
effect,
|
||||
`Unexpected effect kind '${(effect as any).kind}'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
return {
|
||||
receiver: receiver.identifier.id,
|
||||
params: params.map(p => p.identifier.id),
|
||||
rest: rest != null ? rest.identifier.id : null,
|
||||
returns: returns.identifier.id,
|
||||
temporaries,
|
||||
effects,
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an object to an existing ShapeRegistry.
|
||||
*
|
||||
@@ -334,7 +192,8 @@ export type FunctionSignature = {
|
||||
|
||||
canonicalName?: string;
|
||||
|
||||
aliasing?: AliasingSignature | null | undefined;
|
||||
aliasing?: AliasingSignature | null;
|
||||
todo_aliasing?: AliasingSignature | null;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -461,24 +320,24 @@ addObject(BUILTIN_SHAPES, BuiltInArrayId, [
|
||||
calleeEffect: Effect.Store,
|
||||
returnValueKind: ValueKind.Primitive,
|
||||
aliasing: {
|
||||
receiver: '@receiver',
|
||||
receiver: makeIdentifierId(0),
|
||||
params: [],
|
||||
rest: '@rest',
|
||||
returns: '@returns',
|
||||
rest: makeIdentifierId(1),
|
||||
returns: makeIdentifierId(2),
|
||||
temporaries: [],
|
||||
effects: [
|
||||
// Push directly mutates the array itself
|
||||
{kind: 'Mutate', value: '@receiver'},
|
||||
{kind: 'Mutate', value: signatureArgument(0)},
|
||||
// The arguments are captured into the array
|
||||
{
|
||||
kind: 'Capture',
|
||||
from: '@rest',
|
||||
into: '@receiver',
|
||||
from: signatureArgument(1),
|
||||
into: signatureArgument(0),
|
||||
},
|
||||
// Returns the new length, a primitive
|
||||
{
|
||||
kind: 'Create',
|
||||
into: '@returns',
|
||||
into: signatureArgument(2),
|
||||
value: ValueKind.Primitive,
|
||||
reason: ValueReason.KnownReturnSignature,
|
||||
},
|
||||
@@ -515,56 +374,58 @@ addObject(BUILTIN_SHAPES, BuiltInArrayId, [
|
||||
noAlias: true,
|
||||
mutableOnlyIfOperandsAreMutable: true,
|
||||
aliasing: {
|
||||
receiver: '@receiver',
|
||||
params: ['@callback'],
|
||||
receiver: makeIdentifierId(0),
|
||||
params: [makeIdentifierId(1)],
|
||||
rest: null,
|
||||
returns: '@returns',
|
||||
returns: makeIdentifierId(2),
|
||||
temporaries: [
|
||||
// Temporary representing captured items of the receiver
|
||||
'@item',
|
||||
signatureArgument(3),
|
||||
// Temporary representing the result of the callback
|
||||
'@callbackReturn',
|
||||
signatureArgument(4),
|
||||
/*
|
||||
* Undefined `this` arg to the callback. Note the signature does not
|
||||
* support passing an explicit thisArg second param
|
||||
*/
|
||||
'@thisArg',
|
||||
signatureArgument(5),
|
||||
],
|
||||
effects: [
|
||||
// Map creates a new mutable array
|
||||
{
|
||||
kind: 'Create',
|
||||
into: '@returns',
|
||||
into: signatureArgument(2),
|
||||
value: ValueKind.Mutable,
|
||||
reason: ValueReason.KnownReturnSignature,
|
||||
},
|
||||
// The first arg to the callback is an item extracted from the receiver array
|
||||
{
|
||||
kind: 'CreateFrom',
|
||||
from: '@receiver',
|
||||
into: '@item',
|
||||
from: signatureArgument(0),
|
||||
into: signatureArgument(3),
|
||||
},
|
||||
// The undefined this for the callback
|
||||
{
|
||||
kind: 'Create',
|
||||
into: '@thisArg',
|
||||
into: signatureArgument(5),
|
||||
value: ValueKind.Primitive,
|
||||
reason: ValueReason.KnownReturnSignature,
|
||||
},
|
||||
// calls the callback, returning the result into a temporary
|
||||
{
|
||||
kind: 'Apply',
|
||||
receiver: '@thisArg',
|
||||
args: ['@item', {kind: 'Hole'}, '@receiver'],
|
||||
function: '@callback',
|
||||
into: '@callbackReturn',
|
||||
receiver: signatureArgument(5),
|
||||
args: [signatureArgument(3), {kind: 'Hole'}, signatureArgument(0)],
|
||||
function: signatureArgument(1),
|
||||
into: signatureArgument(4),
|
||||
signature: null,
|
||||
mutatesFunction: false,
|
||||
loc: GeneratedSource,
|
||||
},
|
||||
// captures the result of the callback into the return array
|
||||
{
|
||||
kind: 'Capture',
|
||||
from: '@callbackReturn',
|
||||
into: '@returns',
|
||||
from: signatureArgument(4),
|
||||
into: signatureArgument(2),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -716,28 +577,28 @@ addObject(BUILTIN_SHAPES, BuiltInSetId, [
|
||||
// returnValueKind is technically dependent on the ValueKind of the set itself
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
aliasing: {
|
||||
receiver: '@receiver',
|
||||
receiver: makeIdentifierId(0),
|
||||
params: [],
|
||||
rest: '@rest',
|
||||
returns: '@returns',
|
||||
rest: makeIdentifierId(1),
|
||||
returns: makeIdentifierId(2),
|
||||
temporaries: [],
|
||||
effects: [
|
||||
// Set.add returns the receiver Set
|
||||
{
|
||||
kind: 'Assign',
|
||||
from: '@receiver',
|
||||
into: '@returns',
|
||||
from: signatureArgument(0),
|
||||
into: signatureArgument(2),
|
||||
},
|
||||
// Set.add mutates the set itself
|
||||
{
|
||||
kind: 'Mutate',
|
||||
value: '@receiver',
|
||||
value: signatureArgument(0),
|
||||
},
|
||||
// Captures the rest params into the set
|
||||
{
|
||||
kind: 'Capture',
|
||||
from: '@rest',
|
||||
into: '@receiver',
|
||||
from: signatureArgument(1),
|
||||
into: signatureArgument(0),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -1442,30 +1303,30 @@ export const DefaultNonmutatingHook = addHook(
|
||||
hookKind: 'Custom',
|
||||
returnValueKind: ValueKind.Frozen,
|
||||
aliasing: {
|
||||
receiver: '@receiver',
|
||||
receiver: makeIdentifierId(0),
|
||||
params: [],
|
||||
rest: '@rest',
|
||||
returns: '@returns',
|
||||
rest: makeIdentifierId(1),
|
||||
returns: makeIdentifierId(2),
|
||||
temporaries: [],
|
||||
effects: [
|
||||
// Freeze the arguments
|
||||
{
|
||||
kind: 'Freeze',
|
||||
value: '@rest',
|
||||
value: signatureArgument(1),
|
||||
reason: ValueReason.HookCaptured,
|
||||
},
|
||||
// Returns a frozen value
|
||||
{
|
||||
kind: 'Create',
|
||||
into: '@returns',
|
||||
into: signatureArgument(2),
|
||||
value: ValueKind.Frozen,
|
||||
reason: ValueReason.HookReturn,
|
||||
},
|
||||
// May alias any arguments into the return
|
||||
{
|
||||
kind: 'Alias',
|
||||
from: '@rest',
|
||||
into: '@returns',
|
||||
from: signatureArgument(1),
|
||||
into: signatureArgument(2),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -8,12 +8,7 @@
|
||||
import {isValidIdentifier} from '@babel/types';
|
||||
import {z} from 'zod';
|
||||
import {Effect, ValueKind} from '..';
|
||||
import {
|
||||
EffectSchema,
|
||||
ValueKindSchema,
|
||||
ValueReason,
|
||||
ValueReasonSchema,
|
||||
} from './HIR';
|
||||
import {EffectSchema, ValueKindSchema} from './HIR';
|
||||
|
||||
export type ObjectPropertiesConfig = {[key: string]: TypeConfig};
|
||||
export const ObjectPropertiesSchema: z.ZodType<ObjectPropertiesConfig> = z
|
||||
@@ -36,194 +31,6 @@ export const ObjectTypeSchema: z.ZodType<ObjectTypeConfig> = z.object({
|
||||
properties: ObjectPropertiesSchema.nullable(),
|
||||
});
|
||||
|
||||
export const LifetimeIdSchema = z.string().refine(id => id.startsWith('@'), {
|
||||
message: "Placeholder names must start with '@'",
|
||||
});
|
||||
|
||||
export type FreezeEffectConfig = {
|
||||
kind: 'Freeze';
|
||||
value: string;
|
||||
reason: ValueReason;
|
||||
};
|
||||
|
||||
export const FreezeEffectSchema: z.ZodType<FreezeEffectConfig> = z.object({
|
||||
kind: z.literal('Freeze'),
|
||||
value: LifetimeIdSchema,
|
||||
reason: ValueReasonSchema,
|
||||
});
|
||||
|
||||
export type MutateEffectConfig = {
|
||||
kind: 'Mutate';
|
||||
value: string;
|
||||
};
|
||||
|
||||
export const MutateEffectSchema: z.ZodType<MutateEffectConfig> = z.object({
|
||||
kind: z.literal('Mutate'),
|
||||
value: LifetimeIdSchema,
|
||||
});
|
||||
|
||||
export type MutateTransitiveConditionallyConfig = {
|
||||
kind: 'MutateTransitiveConditionally';
|
||||
value: string;
|
||||
};
|
||||
|
||||
export const MutateTransitiveConditionallySchema: z.ZodType<MutateTransitiveConditionallyConfig> =
|
||||
z.object({
|
||||
kind: z.literal('MutateTransitiveConditionally'),
|
||||
value: LifetimeIdSchema,
|
||||
});
|
||||
|
||||
export type CreateEffectConfig = {
|
||||
kind: 'Create';
|
||||
into: string;
|
||||
value: ValueKind;
|
||||
reason: ValueReason;
|
||||
};
|
||||
|
||||
export const CreateEffectSchema: z.ZodType<CreateEffectConfig> = z.object({
|
||||
kind: z.literal('Create'),
|
||||
into: LifetimeIdSchema,
|
||||
value: ValueKindSchema,
|
||||
reason: ValueReasonSchema,
|
||||
});
|
||||
|
||||
export type AssignEffectConfig = {
|
||||
kind: 'Assign';
|
||||
from: string;
|
||||
into: string;
|
||||
};
|
||||
|
||||
export const AssignEffectSchema: z.ZodType<AssignEffectConfig> = z.object({
|
||||
kind: z.literal('Assign'),
|
||||
from: LifetimeIdSchema,
|
||||
into: LifetimeIdSchema,
|
||||
});
|
||||
|
||||
export type AliasEffectConfig = {
|
||||
kind: 'Alias';
|
||||
from: string;
|
||||
into: string;
|
||||
};
|
||||
|
||||
export const AliasEffectSchema: z.ZodType<AliasEffectConfig> = z.object({
|
||||
kind: z.literal('Alias'),
|
||||
from: LifetimeIdSchema,
|
||||
into: LifetimeIdSchema,
|
||||
});
|
||||
|
||||
export type CaptureEffectConfig = {
|
||||
kind: 'Capture';
|
||||
from: string;
|
||||
into: string;
|
||||
};
|
||||
|
||||
export const CaptureEffectSchema: z.ZodType<CaptureEffectConfig> = z.object({
|
||||
kind: z.literal('Capture'),
|
||||
from: LifetimeIdSchema,
|
||||
into: LifetimeIdSchema,
|
||||
});
|
||||
|
||||
export type CreateFromEffectConfig = {
|
||||
kind: 'CreateFrom';
|
||||
from: string;
|
||||
into: string;
|
||||
};
|
||||
|
||||
export const CreateFromEffectSchema: z.ZodType<CreateFromEffectConfig> =
|
||||
z.object({
|
||||
kind: z.literal('CreateFrom'),
|
||||
from: LifetimeIdSchema,
|
||||
into: LifetimeIdSchema,
|
||||
});
|
||||
|
||||
export type ApplyArgConfig =
|
||||
| string
|
||||
| {kind: 'Spread'; place: string}
|
||||
| {kind: 'Hole'};
|
||||
|
||||
export const ApplyArgSchema: z.ZodType<ApplyArgConfig> = z.union([
|
||||
LifetimeIdSchema,
|
||||
z.object({
|
||||
kind: z.literal('Spread'),
|
||||
place: LifetimeIdSchema,
|
||||
}),
|
||||
z.object({
|
||||
kind: z.literal('Hole'),
|
||||
}),
|
||||
]);
|
||||
|
||||
export type ApplyEffectConfig = {
|
||||
kind: 'Apply';
|
||||
receiver: string;
|
||||
function: string;
|
||||
mutatesFunction: boolean;
|
||||
args: Array<ApplyArgConfig>;
|
||||
into: string;
|
||||
};
|
||||
|
||||
export const ApplyEffectSchema: z.ZodType<ApplyEffectConfig> = z.object({
|
||||
kind: z.literal('Apply'),
|
||||
receiver: LifetimeIdSchema,
|
||||
function: LifetimeIdSchema,
|
||||
mutatesFunction: z.boolean(),
|
||||
args: z.array(ApplyArgSchema),
|
||||
into: LifetimeIdSchema,
|
||||
});
|
||||
|
||||
export type ImpureEffectConfig = {
|
||||
kind: 'Impure';
|
||||
place: string;
|
||||
};
|
||||
|
||||
export const ImpureEffectSchema: z.ZodType<ImpureEffectConfig> = z.object({
|
||||
kind: z.literal('Impure'),
|
||||
place: LifetimeIdSchema,
|
||||
});
|
||||
|
||||
export type AliasingEffectConfig =
|
||||
| FreezeEffectConfig
|
||||
| CreateEffectConfig
|
||||
| CreateFromEffectConfig
|
||||
| AssignEffectConfig
|
||||
| AliasEffectConfig
|
||||
| CaptureEffectConfig
|
||||
| ImpureEffectConfig
|
||||
| MutateEffectConfig
|
||||
| MutateTransitiveConditionallyConfig
|
||||
| ApplyEffectConfig;
|
||||
|
||||
export const AliasingEffectSchema: z.ZodType<AliasingEffectConfig> = z.union([
|
||||
FreezeEffectSchema,
|
||||
CreateEffectSchema,
|
||||
CreateFromEffectSchema,
|
||||
AssignEffectSchema,
|
||||
AliasEffectSchema,
|
||||
CaptureEffectSchema,
|
||||
ImpureEffectSchema,
|
||||
MutateEffectSchema,
|
||||
MutateTransitiveConditionallySchema,
|
||||
ApplyEffectSchema,
|
||||
]);
|
||||
|
||||
export type AliasingSignatureConfig = {
|
||||
receiver: string;
|
||||
params: Array<string>;
|
||||
rest: string | null;
|
||||
returns: string;
|
||||
effects: Array<AliasingEffectConfig>;
|
||||
temporaries: Array<string>;
|
||||
};
|
||||
|
||||
export const AliasingSignatureSchema: z.ZodType<AliasingSignatureConfig> =
|
||||
z.object({
|
||||
receiver: LifetimeIdSchema,
|
||||
params: z.array(LifetimeIdSchema),
|
||||
rest: LifetimeIdSchema.nullable(),
|
||||
returns: LifetimeIdSchema,
|
||||
effects: z.array(AliasingEffectSchema),
|
||||
temporaries: z.array(LifetimeIdSchema),
|
||||
});
|
||||
|
||||
export type FunctionTypeConfig = {
|
||||
kind: 'function';
|
||||
positionalParams: Array<Effect>;
|
||||
@@ -235,7 +42,6 @@ export type FunctionTypeConfig = {
|
||||
mutableOnlyIfOperandsAreMutable?: boolean | null | undefined;
|
||||
impure?: boolean | null | undefined;
|
||||
canonicalName?: string | null | undefined;
|
||||
aliasing?: AliasingSignatureConfig | null | undefined;
|
||||
};
|
||||
export const FunctionTypeSchema: z.ZodType<FunctionTypeConfig> = z.object({
|
||||
kind: z.literal('function'),
|
||||
@@ -248,7 +54,6 @@ export const FunctionTypeSchema: z.ZodType<FunctionTypeConfig> = z.object({
|
||||
mutableOnlyIfOperandsAreMutable: z.boolean().nullable().optional(),
|
||||
impure: z.boolean().nullable().optional(),
|
||||
canonicalName: z.string().nullable().optional(),
|
||||
aliasing: AliasingSignatureSchema.nullable().optional(),
|
||||
});
|
||||
|
||||
export type HookTypeConfig = {
|
||||
@@ -258,7 +63,6 @@ export type HookTypeConfig = {
|
||||
returnType: TypeConfig;
|
||||
returnValueKind?: ValueKind | null | undefined;
|
||||
noAlias?: boolean | null | undefined;
|
||||
aliasing?: AliasingSignatureConfig | null | undefined;
|
||||
};
|
||||
export const HookTypeSchema: z.ZodType<HookTypeConfig> = z.object({
|
||||
kind: z.literal('hook'),
|
||||
@@ -267,7 +71,6 @@ export const HookTypeSchema: z.ZodType<HookTypeConfig> = z.object({
|
||||
returnType: z.lazy(() => TypeSchema),
|
||||
returnValueKind: ValueKindSchema.nullable().optional(),
|
||||
noAlias: z.boolean().nullable().optional(),
|
||||
aliasing: AliasingSignatureSchema.nullable().optional(),
|
||||
});
|
||||
|
||||
export type BuiltInTypeConfig =
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
import {CompilerErrorDetailOptions} from '../CompilerError';
|
||||
import {
|
||||
FunctionExpression,
|
||||
GeneratedSource,
|
||||
Hole,
|
||||
IdentifierId,
|
||||
ObjectMethod,
|
||||
@@ -19,7 +18,6 @@ import {
|
||||
ValueReason,
|
||||
} from '../HIR';
|
||||
import {FunctionSignature} from '../HIR/ObjectShape';
|
||||
import {printSourceLocation} from '../HIR/PrintHIR';
|
||||
|
||||
/**
|
||||
* `AliasingEffect` describes a set of "effects" that an instruction/terminal has on one or
|
||||
@@ -202,19 +200,10 @@ export function hashEffect(effect: AliasingEffect): string {
|
||||
return [effect.kind, effect.value.identifier.id, effect.reason].join(':');
|
||||
}
|
||||
case 'Impure':
|
||||
case 'Render': {
|
||||
return [effect.kind, effect.place.identifier.id].join(':');
|
||||
}
|
||||
case 'Render':
|
||||
case 'MutateFrozen':
|
||||
case 'MutateGlobal': {
|
||||
return [
|
||||
effect.kind,
|
||||
effect.place.identifier.id,
|
||||
effect.error.severity,
|
||||
effect.error.reason,
|
||||
effect.error.description,
|
||||
printSourceLocation(effect.error.loc ?? GeneratedSource),
|
||||
].join(':');
|
||||
return [effect.kind, effect.place.identifier.id].join(':');
|
||||
}
|
||||
case 'Mutate':
|
||||
case 'MutateConditionally':
|
||||
|
||||
@@ -22,7 +22,7 @@ import {inferMutableRanges} from './InferMutableRanges';
|
||||
import inferReferenceEffects from './InferReferenceEffects';
|
||||
import {assertExhaustive} from '../Utils/utils';
|
||||
import {inferMutationAliasingEffects} from './InferMutationAliasingEffects';
|
||||
import {inferFunctionExpressionAliasingEffectsSignature} from './InferFunctionExpressionAliasingEffectsSignature';
|
||||
import {inferMutationAliasingFunctionEffects} from './InferMutationAliasingFunctionEffects';
|
||||
import {inferMutationAliasingRanges} from './InferMutationAliasingRanges';
|
||||
|
||||
export default function analyseFunctions(func: HIRFunction): void {
|
||||
@@ -71,7 +71,7 @@ function lowerWithMutationAliasing(fn: HIRFunction): void {
|
||||
inferMutationAliasingRanges(fn, {isFunctionExpression: true});
|
||||
rewriteInstructionKindsBasedOnReassignment(fn);
|
||||
inferReactiveScopeVariables(fn);
|
||||
const effects = inferFunctionExpressionAliasingEffectsSignature(fn);
|
||||
const effects = inferMutationAliasingFunctionEffects(fn);
|
||||
fn.env.logger?.debugLogIRs?.({
|
||||
kind: 'hir',
|
||||
name: 'AnalyseFunction (inner)',
|
||||
|
||||
@@ -38,7 +38,6 @@ import {
|
||||
import {
|
||||
eachInstructionValueLValue,
|
||||
eachInstructionValueOperand,
|
||||
eachTerminalOperand,
|
||||
eachTerminalSuccessor,
|
||||
} from '../HIR/visitors';
|
||||
import {Ok, Result} from '../Utils/Result';
|
||||
@@ -50,14 +49,12 @@ import {
|
||||
} from './InferReferenceEffects';
|
||||
import {
|
||||
assertExhaustive,
|
||||
getOrInsertDefault,
|
||||
getOrInsertWith,
|
||||
Set_isSuperset,
|
||||
} from '../Utils/utils';
|
||||
import {
|
||||
printAliasingEffect,
|
||||
printAliasingSignature,
|
||||
printFunction,
|
||||
printIdentifier,
|
||||
printInstruction,
|
||||
printInstructionValue,
|
||||
@@ -197,15 +194,12 @@ export function inferMutationAliasingEffects(
|
||||
let count = 0;
|
||||
while (queuedStates.size !== 0) {
|
||||
count++;
|
||||
if (count > 100) {
|
||||
if (count > 1000) {
|
||||
console.log(
|
||||
'oops infinite loop',
|
||||
fn.id,
|
||||
typeof fn.loc !== 'symbol' ? fn.loc?.filename : null,
|
||||
);
|
||||
if (DEBUG) {
|
||||
console.log(printFunction(fn));
|
||||
}
|
||||
throw new Error('infinite loop');
|
||||
}
|
||||
for (const [blockId, block] of fn.body.blocks) {
|
||||
@@ -217,11 +211,6 @@ export function inferMutationAliasingEffects(
|
||||
|
||||
statesByBlock.set(blockId, incomingState);
|
||||
const state = incomingState.clone();
|
||||
if (DEBUG) {
|
||||
console.log('*************');
|
||||
console.log(`bb${block.id}`);
|
||||
console.log('*************');
|
||||
}
|
||||
inferBlock(context, state, block);
|
||||
|
||||
for (const nextBlockId of eachTerminalSuccessor(block.terminal)) {
|
||||
@@ -232,19 +221,8 @@ export function inferMutationAliasingEffects(
|
||||
return Ok(undefined);
|
||||
}
|
||||
|
||||
function findHoistedContextDeclarations(
|
||||
fn: HIRFunction,
|
||||
): Map<DeclarationId, Place | null> {
|
||||
const hoisted = new Map<DeclarationId, Place | null>();
|
||||
function visit(place: Place): void {
|
||||
if (
|
||||
hoisted.has(place.identifier.declarationId) &&
|
||||
hoisted.get(place.identifier.declarationId) == null
|
||||
) {
|
||||
// If this is the first load of the value, store the location
|
||||
hoisted.set(place.identifier.declarationId, place);
|
||||
}
|
||||
}
|
||||
function findHoistedContextDeclarations(fn: HIRFunction): Set<DeclarationId> {
|
||||
const hoisted = new Set<DeclarationId>();
|
||||
for (const block of fn.body.blocks.values()) {
|
||||
for (const instr of block.instructions) {
|
||||
if (instr.value.kind === 'DeclareContext') {
|
||||
@@ -254,17 +232,10 @@ function findHoistedContextDeclarations(
|
||||
kind == InstructionKind.HoistedFunction ||
|
||||
kind == InstructionKind.HoistedLet
|
||||
) {
|
||||
hoisted.set(instr.value.lvalue.place.identifier.declarationId, null);
|
||||
}
|
||||
} else {
|
||||
for (const operand of eachInstructionValueOperand(instr.value)) {
|
||||
visit(operand);
|
||||
hoisted.add(instr.value.lvalue.place.identifier.declarationId);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const operand of eachTerminalOperand(block.terminal)) {
|
||||
visit(operand);
|
||||
}
|
||||
}
|
||||
return hoisted;
|
||||
}
|
||||
@@ -274,40 +245,21 @@ class Context {
|
||||
instructionSignatureCache: Map<Instruction, InstructionSignature> = new Map();
|
||||
effectInstructionValueCache: Map<AliasingEffect, InstructionValue> =
|
||||
new Map();
|
||||
applySignatureCache: Map<
|
||||
AliasingSignature,
|
||||
Map<AliasingEffect, Array<AliasingEffect> | null>
|
||||
> = new Map();
|
||||
catchHandlers: Map<BlockId, Place> = new Map();
|
||||
functionSignatureCache: Map<FunctionExpression, AliasingSignature> =
|
||||
new Map();
|
||||
isFuctionExpression: boolean;
|
||||
fn: HIRFunction;
|
||||
hoistedContextDeclarations: Map<DeclarationId, Place | null>;
|
||||
hoistedContextDeclarations: Set<DeclarationId>;
|
||||
|
||||
constructor(
|
||||
isFunctionExpression: boolean,
|
||||
fn: HIRFunction,
|
||||
hoistedContextDeclarations: Map<DeclarationId, Place | null>,
|
||||
hoistedContextDeclarations: Set<DeclarationId>,
|
||||
) {
|
||||
this.isFuctionExpression = isFunctionExpression;
|
||||
this.fn = fn;
|
||||
this.hoistedContextDeclarations = hoistedContextDeclarations;
|
||||
}
|
||||
|
||||
cacheApplySignature(
|
||||
signature: AliasingSignature,
|
||||
effect: Extract<AliasingEffect, {kind: 'Apply'}>,
|
||||
f: () => Array<AliasingEffect> | null,
|
||||
): Array<AliasingEffect> | null {
|
||||
const inner = getOrInsertDefault(
|
||||
this.applySignatureCache,
|
||||
signature,
|
||||
new Map(),
|
||||
);
|
||||
return getOrInsertWith(inner, effect, f);
|
||||
}
|
||||
|
||||
internEffect(effect: AliasingEffect): AliasingEffect {
|
||||
const hash = hashEffect(effect);
|
||||
let interned = this.internedEffects.get(hash);
|
||||
@@ -381,13 +333,11 @@ function inferBlock(
|
||||
state.appendAlias(handlerParam, instr.lvalue);
|
||||
const kind = state.kind(instr.lvalue).kind;
|
||||
if (kind === ValueKind.Mutable || kind == ValueKind.Context) {
|
||||
effects.push(
|
||||
context.internEffect({
|
||||
kind: 'Alias',
|
||||
from: instr.lvalue,
|
||||
into: handlerParam,
|
||||
}),
|
||||
);
|
||||
effects.push({
|
||||
kind: 'Alias',
|
||||
from: instr.lvalue,
|
||||
into: handlerParam,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -396,11 +346,11 @@ function inferBlock(
|
||||
} else if (terminal.kind === 'return') {
|
||||
if (!context.isFuctionExpression) {
|
||||
terminal.effects = [
|
||||
context.internEffect({
|
||||
{
|
||||
kind: 'Freeze',
|
||||
value: terminal.value,
|
||||
reason: ValueReason.JsxCaptured,
|
||||
}),
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -577,21 +527,20 @@ function applyEffect(
|
||||
break;
|
||||
}
|
||||
case ValueKind.Frozen: {
|
||||
applyEffect(
|
||||
context,
|
||||
state,
|
||||
{
|
||||
kind: 'ImmutableCapture',
|
||||
from: effect.from,
|
||||
into: effect.into,
|
||||
},
|
||||
aliased,
|
||||
effects,
|
||||
);
|
||||
effects.push({
|
||||
kind: 'ImmutableCapture',
|
||||
from: effect.from,
|
||||
into: effect.into,
|
||||
});
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
effects.push(effect);
|
||||
effects.push({
|
||||
// OK: recording information flow
|
||||
kind: 'CreateFrom', // prev Alias
|
||||
from: effect.from,
|
||||
into: effect.into,
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -690,17 +639,11 @@ function applyEffect(
|
||||
}
|
||||
case ValueKind.Frozen: {
|
||||
isMutableReferenceType = false;
|
||||
applyEffect(
|
||||
context,
|
||||
state,
|
||||
{
|
||||
kind: 'ImmutableCapture',
|
||||
from: effect.from,
|
||||
into: effect.into,
|
||||
},
|
||||
aliased,
|
||||
effects,
|
||||
);
|
||||
effects.push({
|
||||
kind: 'ImmutableCapture',
|
||||
from: effect.from,
|
||||
into: effect.into,
|
||||
});
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -722,17 +665,11 @@ function applyEffect(
|
||||
const fromKind = fromValue.kind;
|
||||
switch (fromKind) {
|
||||
case ValueKind.Frozen: {
|
||||
applyEffect(
|
||||
context,
|
||||
state,
|
||||
{
|
||||
kind: 'ImmutableCapture',
|
||||
from: effect.from,
|
||||
into: effect.into,
|
||||
},
|
||||
aliased,
|
||||
effects,
|
||||
);
|
||||
effects.push({
|
||||
kind: 'ImmutableCapture',
|
||||
from: effect.from,
|
||||
into: effect.into,
|
||||
});
|
||||
let value = context.effectInstructionValueCache.get(effect);
|
||||
if (value == null) {
|
||||
value = {
|
||||
@@ -790,33 +727,23 @@ function applyEffect(
|
||||
* We're calling a locally declared function, we already know it's effects!
|
||||
* We just have to substitute in the args for the params
|
||||
*/
|
||||
const functionExpr = functionValues[0];
|
||||
let signature = context.functionSignatureCache.get(functionExpr);
|
||||
if (signature == null) {
|
||||
signature = buildSignatureFromFunctionExpression(
|
||||
state.env,
|
||||
functionExpr,
|
||||
);
|
||||
context.functionSignatureCache.set(functionExpr, signature);
|
||||
}
|
||||
const signature = buildSignatureFromFunctionExpression(
|
||||
state.env,
|
||||
functionValues[0],
|
||||
);
|
||||
if (DEBUG) {
|
||||
console.log(
|
||||
`constructed alias signature:\n${printAliasingSignature(signature)}`,
|
||||
);
|
||||
}
|
||||
const signatureEffects = context.cacheApplySignature(
|
||||
const signatureEffects = computeEffectsForSignature(
|
||||
state.env,
|
||||
signature,
|
||||
effect,
|
||||
() =>
|
||||
computeEffectsForSignature(
|
||||
state.env,
|
||||
signature,
|
||||
effect.into,
|
||||
effect.receiver,
|
||||
effect.args,
|
||||
functionExpr.loweredFunc.func.context,
|
||||
effect.loc,
|
||||
),
|
||||
effect.into,
|
||||
effect.receiver,
|
||||
effect.args,
|
||||
functionValues[0].loweredFunc.func.context,
|
||||
effect.loc,
|
||||
);
|
||||
if (signatureEffects != null) {
|
||||
if (DEBUG) {
|
||||
@@ -835,24 +762,18 @@ function applyEffect(
|
||||
break;
|
||||
}
|
||||
}
|
||||
let signatureEffects = null;
|
||||
if (effect.signature?.aliasing != null) {
|
||||
const signature = effect.signature.aliasing;
|
||||
signatureEffects = context.cacheApplySignature(
|
||||
effect.signature.aliasing,
|
||||
effect,
|
||||
() =>
|
||||
computeEffectsForSignature(
|
||||
const signatureEffects =
|
||||
effect.signature?.aliasing != null
|
||||
? computeEffectsForSignature(
|
||||
state.env,
|
||||
signature,
|
||||
effect.signature.aliasing,
|
||||
effect.into,
|
||||
effect.receiver,
|
||||
effect.args,
|
||||
[],
|
||||
effect.loc,
|
||||
),
|
||||
);
|
||||
}
|
||||
)
|
||||
: null;
|
||||
if (signatureEffects != null) {
|
||||
if (DEBUG) {
|
||||
console.log('apply aliasing signature effects');
|
||||
@@ -980,89 +901,48 @@ function applyEffect(
|
||||
console.log(prettyFormat(state.debugAbstractValue(value)));
|
||||
}
|
||||
|
||||
let reason: string;
|
||||
let description: string | null = null;
|
||||
|
||||
if (
|
||||
mutationKind === 'mutate-frozen' &&
|
||||
context.hoistedContextDeclarations.has(
|
||||
effect.value.identifier.declarationId,
|
||||
)
|
||||
) {
|
||||
const description =
|
||||
reason = `This variable is accessed before it is declared, which prevents the earlier access from updating when this value changes over time`;
|
||||
if (
|
||||
effect.value.identifier.name !== null &&
|
||||
effect.value.identifier.name.kind === 'named'
|
||||
? `Variable \`${effect.value.identifier.name.value}\` is accessed before it is declared`
|
||||
: null;
|
||||
const hoistedAccess = context.hoistedContextDeclarations.get(
|
||||
effect.value.identifier.declarationId,
|
||||
);
|
||||
if (hoistedAccess != null && hoistedAccess.loc != effect.value.loc) {
|
||||
applyEffect(
|
||||
context,
|
||||
state,
|
||||
{
|
||||
kind: 'MutateFrozen',
|
||||
place: effect.value,
|
||||
error: {
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: `This variable is accessed before it is declared, which may prevent it from updating as the assigned value changes over time`,
|
||||
description,
|
||||
loc: hoistedAccess.loc,
|
||||
suggestions: null,
|
||||
},
|
||||
},
|
||||
aliased,
|
||||
effects,
|
||||
);
|
||||
) {
|
||||
description = `Move the declaration of \`${effect.value.identifier.name.value}\` to before it is first referenced`;
|
||||
}
|
||||
|
||||
applyEffect(
|
||||
context,
|
||||
state,
|
||||
{
|
||||
kind: 'MutateFrozen',
|
||||
place: effect.value,
|
||||
error: {
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: `This variable is accessed before it is declared, which prevents the earlier access from updating when this value changes over time`,
|
||||
description,
|
||||
loc: effect.value.loc,
|
||||
suggestions: null,
|
||||
},
|
||||
},
|
||||
aliased,
|
||||
effects,
|
||||
);
|
||||
} else {
|
||||
const reason = getWriteErrorReason({
|
||||
reason = getWriteErrorReason({
|
||||
kind: value.kind,
|
||||
reason: value.reason,
|
||||
context: new Set(),
|
||||
});
|
||||
const description =
|
||||
if (
|
||||
effect.value.identifier.name !== null &&
|
||||
effect.value.identifier.name.kind === 'named'
|
||||
? `Found mutation of \`${effect.value.identifier.name.value}\``
|
||||
: null;
|
||||
applyEffect(
|
||||
context,
|
||||
state,
|
||||
{
|
||||
kind:
|
||||
value.kind === ValueKind.Frozen
|
||||
? 'MutateFrozen'
|
||||
: 'MutateGlobal',
|
||||
place: effect.value,
|
||||
error: {
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason,
|
||||
description,
|
||||
loc: effect.value.loc,
|
||||
suggestions: null,
|
||||
},
|
||||
},
|
||||
aliased,
|
||||
effects,
|
||||
);
|
||||
) {
|
||||
description = `Found mutation of \`${effect.value.identifier.name.value}\``;
|
||||
}
|
||||
}
|
||||
|
||||
effects.push({
|
||||
kind:
|
||||
value.kind === ValueKind.Frozen ? 'MutateFrozen' : 'MutateGlobal',
|
||||
place: effect.value,
|
||||
error: {
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason,
|
||||
description,
|
||||
loc: effect.value.loc,
|
||||
suggestions: null,
|
||||
},
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import {AliasingEffect} from './AliasingEffects';
|
||||
* This function *also* propagates more ambient-style effects (MutateFrozen, MutateGlobal, Impure, Render)
|
||||
* from instructions within the function up to the function itself.
|
||||
*/
|
||||
export function inferFunctionExpressionAliasingEffectsSignature(
|
||||
export function inferMutationAliasingFunctionEffects(
|
||||
fn: HIRFunction,
|
||||
): Array<AliasingEffect> | null {
|
||||
const effects: Array<AliasingEffect> = [];
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
import {assertExhaustive, getOrInsertWith} from '../Utils/utils';
|
||||
import {printFunction} from '../HIR';
|
||||
import {printIdentifier, printPlace} from '../HIR/PrintHIR';
|
||||
import {MutationKind} from './InferFunctionExpressionAliasingEffectsSignature';
|
||||
import {MutationKind} from './InferMutationAliasingFunctionEffects';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
@@ -38,15 +38,13 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Error
|
||||
|
||||
```
|
||||
17 | * $2 = Function context=setState
|
||||
18 | */
|
||||
> 19 | useEffect(() => setState(2), []);
|
||||
| ^^^^^^^^ InvalidReact: This variable is accessed before it is declared, which may prevent it from updating as the assigned value changes over time. Variable `setState` is accessed before it is declared (19:19)
|
||||
|
||||
InvalidReact: This variable is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. Variable `setState` is accessed before it is declared (21:21)
|
||||
19 | useEffect(() => setState(2), []);
|
||||
20 |
|
||||
21 | const [state, setState] = useState(0);
|
||||
> 21 | const [state, setState] = useState(0);
|
||||
| ^^^^^^^^ InvalidReact: This variable is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. Move the declaration of `setState` to before it is first referenced (21:21)
|
||||
22 | return <Stringify state={state} />;
|
||||
23 | }
|
||||
24 |
|
||||
```
|
||||
|
||||
|
||||
@@ -57,62 +57,67 @@ import { Stringify } from "shared-runtime";
|
||||
* - cb1 is not assumed to be called since it's only used as a call operand
|
||||
*/
|
||||
function useFoo(t0) {
|
||||
const $ = _c(13);
|
||||
const { arr1, arr2 } = t0;
|
||||
const $ = _c(14);
|
||||
let arr1;
|
||||
let arr2;
|
||||
let t1;
|
||||
if ($[0] !== arr1[0]) {
|
||||
t1 = (e) => arr1[0].value + e.value;
|
||||
$[0] = arr1[0];
|
||||
$[1] = t1;
|
||||
if ($[0] !== t0) {
|
||||
({ arr1, arr2 } = t0);
|
||||
let t2;
|
||||
if ($[4] !== arr1[0]) {
|
||||
t2 = (e) => arr1[0].value + e.value;
|
||||
$[4] = arr1[0];
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
const cb1 = t2;
|
||||
t1 = () => arr1.map(cb1);
|
||||
$[0] = t0;
|
||||
$[1] = arr1;
|
||||
$[2] = arr2;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
arr1 = $[1];
|
||||
arr2 = $[2];
|
||||
t1 = $[3];
|
||||
}
|
||||
const cb1 = t1;
|
||||
const getArrMap1 = t1;
|
||||
let t2;
|
||||
if ($[2] !== arr1 || $[3] !== cb1) {
|
||||
t2 = () => arr1.map(cb1);
|
||||
$[2] = arr1;
|
||||
$[3] = cb1;
|
||||
$[4] = t2;
|
||||
if ($[6] !== arr2) {
|
||||
t2 = (e_0) => arr2[0].value + e_0.value;
|
||||
$[6] = arr2;
|
||||
$[7] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
t2 = $[7];
|
||||
}
|
||||
const getArrMap1 = t2;
|
||||
const cb2 = t2;
|
||||
let t3;
|
||||
if ($[5] !== arr2) {
|
||||
t3 = (e_0) => arr2[0].value + e_0.value;
|
||||
$[5] = arr2;
|
||||
$[6] = t3;
|
||||
if ($[8] !== arr1 || $[9] !== cb2) {
|
||||
t3 = () => arr1.map(cb2);
|
||||
$[8] = arr1;
|
||||
$[9] = cb2;
|
||||
$[10] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
t3 = $[10];
|
||||
}
|
||||
const cb2 = t3;
|
||||
const getArrMap2 = t3;
|
||||
let t4;
|
||||
if ($[7] !== arr1 || $[8] !== cb2) {
|
||||
t4 = () => arr1.map(cb2);
|
||||
$[7] = arr1;
|
||||
$[8] = cb2;
|
||||
$[9] = t4;
|
||||
} else {
|
||||
t4 = $[9];
|
||||
}
|
||||
const getArrMap2 = t4;
|
||||
let t5;
|
||||
if ($[10] !== getArrMap1 || $[11] !== getArrMap2) {
|
||||
t5 = (
|
||||
if ($[11] !== getArrMap1 || $[12] !== getArrMap2) {
|
||||
t4 = (
|
||||
<Stringify
|
||||
getArrMap1={getArrMap1}
|
||||
getArrMap2={getArrMap2}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
$[10] = getArrMap1;
|
||||
$[11] = getArrMap2;
|
||||
$[12] = t5;
|
||||
$[11] = getArrMap1;
|
||||
$[12] = getArrMap2;
|
||||
$[13] = t4;
|
||||
} else {
|
||||
t5 = $[12];
|
||||
t4 = $[13];
|
||||
}
|
||||
return t5;
|
||||
return t4;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -58,62 +58,67 @@ import { Stringify } from "shared-runtime";
|
||||
* - cb1 is not assumed to be called since it's only used as a call operand
|
||||
*/
|
||||
function useFoo(t0) {
|
||||
const $ = _c(13);
|
||||
const { arr1, arr2 } = t0;
|
||||
const $ = _c(14);
|
||||
let arr1;
|
||||
let arr2;
|
||||
let t1;
|
||||
if ($[0] !== arr1[0]) {
|
||||
t1 = (e) => arr1[0].value + e.value;
|
||||
$[0] = arr1[0];
|
||||
$[1] = t1;
|
||||
if ($[0] !== t0) {
|
||||
({ arr1, arr2 } = t0);
|
||||
let t2;
|
||||
if ($[4] !== arr1[0]) {
|
||||
t2 = (e) => arr1[0].value + e.value;
|
||||
$[4] = arr1[0];
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
const cb1 = t2;
|
||||
t1 = () => arr1.map(cb1);
|
||||
$[0] = t0;
|
||||
$[1] = arr1;
|
||||
$[2] = arr2;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
arr1 = $[1];
|
||||
arr2 = $[2];
|
||||
t1 = $[3];
|
||||
}
|
||||
const cb1 = t1;
|
||||
const getArrMap1 = t1;
|
||||
let t2;
|
||||
if ($[2] !== arr1 || $[3] !== cb1) {
|
||||
t2 = () => arr1.map(cb1);
|
||||
$[2] = arr1;
|
||||
$[3] = cb1;
|
||||
$[4] = t2;
|
||||
if ($[6] !== arr2) {
|
||||
t2 = (e_0) => arr2[0].value + e_0.value;
|
||||
$[6] = arr2;
|
||||
$[7] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
t2 = $[7];
|
||||
}
|
||||
const getArrMap1 = t2;
|
||||
const cb2 = t2;
|
||||
let t3;
|
||||
if ($[5] !== arr2) {
|
||||
t3 = (e_0) => arr2[0].value + e_0.value;
|
||||
$[5] = arr2;
|
||||
$[6] = t3;
|
||||
if ($[8] !== arr1 || $[9] !== cb2) {
|
||||
t3 = () => arr1.map(cb2);
|
||||
$[8] = arr1;
|
||||
$[9] = cb2;
|
||||
$[10] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
t3 = $[10];
|
||||
}
|
||||
const cb2 = t3;
|
||||
const getArrMap2 = t3;
|
||||
let t4;
|
||||
if ($[7] !== arr1 || $[8] !== cb2) {
|
||||
t4 = () => arr1.map(cb2);
|
||||
$[7] = arr1;
|
||||
$[8] = cb2;
|
||||
$[9] = t4;
|
||||
} else {
|
||||
t4 = $[9];
|
||||
}
|
||||
const getArrMap2 = t4;
|
||||
let t5;
|
||||
if ($[10] !== getArrMap1 || $[11] !== getArrMap2) {
|
||||
t5 = (
|
||||
if ($[11] !== getArrMap1 || $[12] !== getArrMap2) {
|
||||
t4 = (
|
||||
<Stringify
|
||||
getArrMap1={getArrMap1}
|
||||
getArrMap2={getArrMap2}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
$[10] = getArrMap1;
|
||||
$[11] = getArrMap2;
|
||||
$[12] = t5;
|
||||
$[11] = getArrMap1;
|
||||
$[12] = getArrMap2;
|
||||
$[13] = t4;
|
||||
} else {
|
||||
t5 = $[12];
|
||||
t4 = $[13];
|
||||
}
|
||||
return t5;
|
||||
return t4;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -31,15 +31,13 @@ function Component({content, refetch}) {
|
||||
## Error
|
||||
|
||||
```
|
||||
9 | // TDZ violation!
|
||||
10 | const onRefetch = useCallback(() => {
|
||||
> 11 | refetch(data);
|
||||
| ^^^^ InvalidReact: This variable is accessed before it is declared, which may prevent it from updating as the assigned value changes over time. Variable `data` is accessed before it is declared (11:11)
|
||||
|
||||
InvalidReact: This variable is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. Variable `data` is accessed before it is declared (19:19)
|
||||
12 | }, [refetch]);
|
||||
13 |
|
||||
14 | // The context variable gets frozen here since it's passed to a hook
|
||||
17 | // This has to error: onRefetch needs to memoize with `content` as a
|
||||
18 | // dependency, but the dependency comes later
|
||||
> 19 | const {data = null} = content;
|
||||
| ^^^^^^^^^^^ InvalidReact: This variable is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. Move the declaration of `data` to before it is first referenced (19:19)
|
||||
20 |
|
||||
21 | return <Foo data={data} onSubmit={onSubmit} />;
|
||||
22 | }
|
||||
```
|
||||
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @flow @enableNewMutationAliasingModel
|
||||
|
||||
import fbt from 'fbt';
|
||||
|
||||
component Component() {
|
||||
const sections = Object.keys(items);
|
||||
|
||||
for (let i = 0; i < sections.length; i += 3) {
|
||||
chunks.push(
|
||||
sections.slice(i, i + 3).map(section => {
|
||||
return <Child />;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return <Child />;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
|
||||
import fbt from "fbt";
|
||||
|
||||
function Component() {
|
||||
const $ = _c(1);
|
||||
const sections = Object.keys(items);
|
||||
for (let i = 0; i < sections.length; i = i + 3, i) {
|
||||
chunks.push(sections.slice(i, i + 3).map(_temp));
|
||||
}
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <Child />;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
function _temp(section) {
|
||||
return <Child />;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -1,17 +0,0 @@
|
||||
// @flow @enableNewMutationAliasingModel
|
||||
|
||||
import fbt from 'fbt';
|
||||
|
||||
component Component() {
|
||||
const sections = Object.keys(items);
|
||||
|
||||
for (let i = 0; i < sections.length; i += 3) {
|
||||
chunks.push(
|
||||
sections.slice(i, i + 3).map(section => {
|
||||
return <Child />;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return <Child />;
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @flow @enableNewMutationAliasingModel
|
||||
|
||||
import {identity, Stringify, useFragment} from 'shared-runtime';
|
||||
|
||||
component Example() {
|
||||
const data = useFragment();
|
||||
|
||||
const {a, b} = identity(data);
|
||||
|
||||
const el = <Stringify tooltip={b} />;
|
||||
|
||||
identity(a.at(0));
|
||||
|
||||
return <Stringify icon={el} />;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
|
||||
import { identity, Stringify, useFragment } from "shared-runtime";
|
||||
|
||||
function Example() {
|
||||
const $ = _c(2);
|
||||
const data = useFragment();
|
||||
let t0;
|
||||
if ($[0] !== data) {
|
||||
const { a, b } = identity(data);
|
||||
|
||||
const el = <Stringify tooltip={b} />;
|
||||
|
||||
identity(a.at(0));
|
||||
|
||||
t0 = <Stringify icon={el} />;
|
||||
$[0] = data;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -1,15 +0,0 @@
|
||||
// @flow @enableNewMutationAliasingModel
|
||||
|
||||
import {identity, Stringify, useFragment} from 'shared-runtime';
|
||||
|
||||
component Example() {
|
||||
const data = useFragment();
|
||||
|
||||
const {a, b} = identity(data);
|
||||
|
||||
const el = <Stringify tooltip={b} />;
|
||||
|
||||
identity(a.at(0));
|
||||
|
||||
return <Stringify icon={el} />;
|
||||
}
|
||||
@@ -30,47 +30,60 @@ import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire @enabl
|
||||
import { fire } from "react";
|
||||
|
||||
function Component(t0) {
|
||||
const $ = _c(9);
|
||||
const { bar, baz } = t0;
|
||||
let t1;
|
||||
if ($[0] !== bar) {
|
||||
t1 = () => {
|
||||
console.log(bar);
|
||||
};
|
||||
$[0] = bar;
|
||||
$[1] = t1;
|
||||
const $ = _c(13);
|
||||
let bar;
|
||||
let baz;
|
||||
let foo;
|
||||
if ($[0] !== t0) {
|
||||
({ bar, baz } = t0);
|
||||
let t1;
|
||||
if ($[4] !== bar) {
|
||||
t1 = () => {
|
||||
console.log(bar);
|
||||
};
|
||||
$[4] = bar;
|
||||
$[5] = t1;
|
||||
} else {
|
||||
t1 = $[5];
|
||||
}
|
||||
foo = t1;
|
||||
$[0] = t0;
|
||||
$[1] = bar;
|
||||
$[2] = baz;
|
||||
$[3] = foo;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
bar = $[1];
|
||||
baz = $[2];
|
||||
foo = $[3];
|
||||
}
|
||||
const foo = t1;
|
||||
const t2 = useFire(foo);
|
||||
const t3 = useFire(baz);
|
||||
let t4;
|
||||
if ($[2] !== bar || $[3] !== t2 || $[4] !== t3) {
|
||||
t4 = () => {
|
||||
t2(bar);
|
||||
t3(bar);
|
||||
};
|
||||
$[2] = bar;
|
||||
$[3] = t2;
|
||||
$[4] = t3;
|
||||
$[5] = t4;
|
||||
} else {
|
||||
t4 = $[5];
|
||||
}
|
||||
useEffect(t4);
|
||||
let t5;
|
||||
if ($[6] !== bar || $[7] !== t2) {
|
||||
t5 = () => {
|
||||
const t1 = useFire(foo);
|
||||
const t2 = useFire(baz);
|
||||
let t3;
|
||||
if ($[6] !== bar || $[7] !== t1 || $[8] !== t2) {
|
||||
t3 = () => {
|
||||
t1(bar);
|
||||
t2(bar);
|
||||
};
|
||||
$[6] = bar;
|
||||
$[7] = t2;
|
||||
$[8] = t5;
|
||||
$[7] = t1;
|
||||
$[8] = t2;
|
||||
$[9] = t3;
|
||||
} else {
|
||||
t5 = $[8];
|
||||
t3 = $[9];
|
||||
}
|
||||
useEffect(t5);
|
||||
useEffect(t3);
|
||||
let t4;
|
||||
if ($[10] !== bar || $[11] !== t1) {
|
||||
t4 = () => {
|
||||
t1(bar);
|
||||
};
|
||||
$[10] = bar;
|
||||
$[11] = t1;
|
||||
$[12] = t4;
|
||||
} else {
|
||||
t4 = $[12];
|
||||
}
|
||||
useEffect(t4);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableNewMutationAliasingModel
|
||||
|
||||
import {
|
||||
identity,
|
||||
makeObject_Primitives,
|
||||
typedIdentity,
|
||||
useIdentity,
|
||||
ValidateMemoization,
|
||||
} from 'shared-runtime';
|
||||
|
||||
function Component({a, b}) {
|
||||
// create a mutable value with input `a`
|
||||
const x = makeObject_Primitives(a);
|
||||
|
||||
// freeze the value
|
||||
useIdentity(x);
|
||||
|
||||
// known to pass-through via aliasing signature
|
||||
const x2 = typedIdentity(x);
|
||||
|
||||
// Unknown function so we assume it conditionally mutates,
|
||||
// but x2 is frozen so this downgrades to a read.
|
||||
// x should *not* take b as a dependency
|
||||
identity(x2, b);
|
||||
|
||||
return <ValidateMemoization inputs={[a]} output={x} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: 0, b: 0}],
|
||||
sequentialRenders: [
|
||||
{a: 0, b: 0},
|
||||
{a: 1, b: 0},
|
||||
{a: 1, b: 1},
|
||||
{a: 0, b: 1},
|
||||
{a: 0, b: 0},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel
|
||||
|
||||
import {
|
||||
identity,
|
||||
makeObject_Primitives,
|
||||
typedIdentity,
|
||||
useIdentity,
|
||||
ValidateMemoization,
|
||||
} from "shared-runtime";
|
||||
|
||||
function Component(t0) {
|
||||
const $ = _c(7);
|
||||
const { a, b } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a) {
|
||||
t1 = makeObject_Primitives(a);
|
||||
$[0] = a;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const x = t1;
|
||||
|
||||
useIdentity(x);
|
||||
|
||||
const x2 = typedIdentity(x);
|
||||
|
||||
identity(x2, b);
|
||||
let t2;
|
||||
if ($[2] !== a) {
|
||||
t2 = [a];
|
||||
$[2] = a;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
let t3;
|
||||
if ($[4] !== t2 || $[5] !== x) {
|
||||
t3 = <ValidateMemoization inputs={t2} output={x} />;
|
||||
$[4] = t2;
|
||||
$[5] = x;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ a: 0, b: 0 }],
|
||||
sequentialRenders: [
|
||||
{ a: 0, b: 0 },
|
||||
{ a: 1, b: 0 },
|
||||
{ a: 1, b: 1 },
|
||||
{ a: 0, b: 1 },
|
||||
{ a: 0, b: 0 },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"inputs":[0],"output":{"a":0,"b":"value1","c":true}}</div>
|
||||
<div>{"inputs":[1],"output":{"a":0,"b":"value1","c":true}}</div>
|
||||
<div>{"inputs":[1],"output":{"a":0,"b":"value1","c":true}}</div>
|
||||
<div>{"inputs":[0],"output":{"a":0,"b":"value1","c":true}}</div>
|
||||
<div>{"inputs":[0],"output":{"a":0,"b":"value1","c":true}}</div>
|
||||
@@ -1,39 +0,0 @@
|
||||
// @enableNewMutationAliasingModel
|
||||
|
||||
import {
|
||||
identity,
|
||||
makeObject_Primitives,
|
||||
typedIdentity,
|
||||
useIdentity,
|
||||
ValidateMemoization,
|
||||
} from 'shared-runtime';
|
||||
|
||||
function Component({a, b}) {
|
||||
// create a mutable value with input `a`
|
||||
const x = makeObject_Primitives(a);
|
||||
|
||||
// freeze the value
|
||||
useIdentity(x);
|
||||
|
||||
// known to pass-through via aliasing signature
|
||||
const x2 = typedIdentity(x);
|
||||
|
||||
// Unknown function so we assume it conditionally mutates,
|
||||
// but x2 is frozen so this downgrades to a read.
|
||||
// x should *not* take b as a dependency
|
||||
identity(x2, b);
|
||||
|
||||
return <ValidateMemoization inputs={[a]} output={x} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: 0, b: 0}],
|
||||
sequentialRenders: [
|
||||
{a: 0, b: 0},
|
||||
{a: 1, b: 0},
|
||||
{a: 1, b: 1},
|
||||
{a: 0, b: 1},
|
||||
{a: 0, b: 0},
|
||||
],
|
||||
};
|
||||
@@ -1,112 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableNewMutationAliasingModel
|
||||
|
||||
import {
|
||||
identity,
|
||||
makeObject_Primitives,
|
||||
typedIdentity,
|
||||
useIdentity,
|
||||
ValidateMemoization,
|
||||
} from 'shared-runtime';
|
||||
|
||||
function Component({a, b}) {
|
||||
// create a mutable value with input `a`
|
||||
const x = makeObject_Primitives(a);
|
||||
|
||||
// known to pass-through via aliasing signature
|
||||
const x2 = typedIdentity(x);
|
||||
|
||||
// Unknown function so we assume it conditionally mutates,
|
||||
// and x is still mutable so
|
||||
identity(x2, b);
|
||||
|
||||
return <ValidateMemoization inputs={[a, b]} output={x} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: 0, b: 0}],
|
||||
sequentialRenders: [
|
||||
{a: 0, b: 0},
|
||||
{a: 1, b: 0},
|
||||
{a: 1, b: 1},
|
||||
{a: 0, b: 1},
|
||||
{a: 0, b: 0},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel
|
||||
|
||||
import {
|
||||
identity,
|
||||
makeObject_Primitives,
|
||||
typedIdentity,
|
||||
useIdentity,
|
||||
ValidateMemoization,
|
||||
} from "shared-runtime";
|
||||
|
||||
function Component(t0) {
|
||||
const $ = _c(9);
|
||||
const { a, b } = t0;
|
||||
let x;
|
||||
if ($[0] !== a || $[1] !== b) {
|
||||
x = makeObject_Primitives(a);
|
||||
|
||||
const x2 = typedIdentity(x);
|
||||
|
||||
identity(x2, b);
|
||||
$[0] = a;
|
||||
$[1] = b;
|
||||
$[2] = x;
|
||||
} else {
|
||||
x = $[2];
|
||||
}
|
||||
let t1;
|
||||
if ($[3] !== a || $[4] !== b) {
|
||||
t1 = [a, b];
|
||||
$[3] = a;
|
||||
$[4] = b;
|
||||
$[5] = t1;
|
||||
} else {
|
||||
t1 = $[5];
|
||||
}
|
||||
let t2;
|
||||
if ($[6] !== t1 || $[7] !== x) {
|
||||
t2 = <ValidateMemoization inputs={t1} output={x} />;
|
||||
$[6] = t1;
|
||||
$[7] = x;
|
||||
$[8] = t2;
|
||||
} else {
|
||||
t2 = $[8];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ a: 0, b: 0 }],
|
||||
sequentialRenders: [
|
||||
{ a: 0, b: 0 },
|
||||
{ a: 1, b: 0 },
|
||||
{ a: 1, b: 1 },
|
||||
{ a: 0, b: 1 },
|
||||
{ a: 0, b: 0 },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"inputs":[0,0],"output":{"a":0,"b":"value1","c":true}}</div>
|
||||
<div>{"inputs":[1,0],"output":{"a":0,"b":"value1","c":true}}</div>
|
||||
<div>{"inputs":[1,1],"output":{"a":0,"b":"value1","c":true}}</div>
|
||||
<div>{"inputs":[0,1],"output":{"a":0,"b":"value1","c":true}}</div>
|
||||
<div>{"inputs":[0,0],"output":{"a":0,"b":"value1","c":true}}</div>
|
||||
@@ -1,35 +0,0 @@
|
||||
// @enableNewMutationAliasingModel
|
||||
|
||||
import {
|
||||
identity,
|
||||
makeObject_Primitives,
|
||||
typedIdentity,
|
||||
useIdentity,
|
||||
ValidateMemoization,
|
||||
} from 'shared-runtime';
|
||||
|
||||
function Component({a, b}) {
|
||||
// create a mutable value with input `a`
|
||||
const x = makeObject_Primitives(a);
|
||||
|
||||
// known to pass-through via aliasing signature
|
||||
const x2 = typedIdentity(x);
|
||||
|
||||
// Unknown function so we assume it conditionally mutates,
|
||||
// and x is still mutable so
|
||||
identity(x2, b);
|
||||
|
||||
return <ValidateMemoization inputs={[a, b]} output={x} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: 0, b: 0}],
|
||||
sequentialRenders: [
|
||||
{a: 0, b: 0},
|
||||
{a: 1, b: 0},
|
||||
{a: 1, b: 1},
|
||||
{a: 0, b: 1},
|
||||
{a: 0, b: 0},
|
||||
],
|
||||
};
|
||||
@@ -30,47 +30,60 @@ import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire
|
||||
import { fire } from "react";
|
||||
|
||||
function Component(t0) {
|
||||
const $ = _c(9);
|
||||
const { bar, baz } = t0;
|
||||
let t1;
|
||||
if ($[0] !== bar) {
|
||||
t1 = () => {
|
||||
console.log(bar);
|
||||
};
|
||||
$[0] = bar;
|
||||
$[1] = t1;
|
||||
const $ = _c(13);
|
||||
let bar;
|
||||
let baz;
|
||||
let foo;
|
||||
if ($[0] !== t0) {
|
||||
({ bar, baz } = t0);
|
||||
let t1;
|
||||
if ($[4] !== bar) {
|
||||
t1 = () => {
|
||||
console.log(bar);
|
||||
};
|
||||
$[4] = bar;
|
||||
$[5] = t1;
|
||||
} else {
|
||||
t1 = $[5];
|
||||
}
|
||||
foo = t1;
|
||||
$[0] = t0;
|
||||
$[1] = bar;
|
||||
$[2] = baz;
|
||||
$[3] = foo;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
bar = $[1];
|
||||
baz = $[2];
|
||||
foo = $[3];
|
||||
}
|
||||
const foo = t1;
|
||||
const t2 = useFire(foo);
|
||||
const t3 = useFire(baz);
|
||||
let t4;
|
||||
if ($[2] !== bar || $[3] !== t2 || $[4] !== t3) {
|
||||
t4 = () => {
|
||||
t2(bar);
|
||||
t3(bar);
|
||||
};
|
||||
$[2] = bar;
|
||||
$[3] = t2;
|
||||
$[4] = t3;
|
||||
$[5] = t4;
|
||||
} else {
|
||||
t4 = $[5];
|
||||
}
|
||||
useEffect(t4);
|
||||
let t5;
|
||||
if ($[6] !== bar || $[7] !== t2) {
|
||||
t5 = () => {
|
||||
const t1 = useFire(foo);
|
||||
const t2 = useFire(baz);
|
||||
let t3;
|
||||
if ($[6] !== bar || $[7] !== t1 || $[8] !== t2) {
|
||||
t3 = () => {
|
||||
t1(bar);
|
||||
t2(bar);
|
||||
};
|
||||
$[6] = bar;
|
||||
$[7] = t2;
|
||||
$[8] = t5;
|
||||
$[7] = t1;
|
||||
$[8] = t2;
|
||||
$[9] = t3;
|
||||
} else {
|
||||
t5 = $[8];
|
||||
t3 = $[9];
|
||||
}
|
||||
useEffect(t5);
|
||||
useEffect(t3);
|
||||
let t4;
|
||||
if ($[10] !== bar || $[11] !== t1) {
|
||||
t4 = () => {
|
||||
t1(bar);
|
||||
};
|
||||
$[10] = bar;
|
||||
$[11] = t1;
|
||||
$[12] = t4;
|
||||
} else {
|
||||
t4 = $[12];
|
||||
}
|
||||
useEffect(t4);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,22 +69,6 @@ export function makeSharedRuntimeTypeProvider({
|
||||
returnValueKind: ValueKindEnum.Mutable,
|
||||
noAlias: true,
|
||||
},
|
||||
typedIdentity: {
|
||||
kind: 'function',
|
||||
positionalParams: [EffectEnum.Read],
|
||||
restParam: null,
|
||||
calleeEffect: EffectEnum.Read,
|
||||
returnType: {kind: 'type', name: 'Any'},
|
||||
returnValueKind: ValueKindEnum.Mutable,
|
||||
aliasing: {
|
||||
receiver: '@receiver',
|
||||
params: ['@value'],
|
||||
rest: null,
|
||||
returns: '@return',
|
||||
temporaries: [],
|
||||
effects: [{kind: 'Assign', from: '@value', into: '@return'}],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
} else if (moduleName === 'ReactCompilerTest') {
|
||||
|
||||
@@ -396,8 +396,4 @@ export function typedLog(...values: Array<any>): void {
|
||||
console.log(...values);
|
||||
}
|
||||
|
||||
export function typedIdentity<T>(value: T): T {
|
||||
return value;
|
||||
}
|
||||
|
||||
export default typedLog;
|
||||
|
||||
Reference in New Issue
Block a user