@@ -17792,6 +17792,11 @@ function isUseStateType(id) {
1779217792function isRefOrRefValue(id) {
1779317793    return isUseRefType(id) || isRefValueType(id);
1779417794}
17795+ function isRefOrRefLikeMutableType(type) {
17796+     return (type.kind === 'Object' &&
17797+         (type.shapeId === 'BuiltInUseRefId' ||
17798+             type.shapeId == 'ReanimatedSharedValueId'));
17799+ }
1779517800function isSetStateType(id) {
1779617801    return id.type.kind === 'Function' && id.type.shapeId === 'BuiltInSetState';
1779717802}
@@ -29584,6 +29589,8 @@ const BuiltInPropsId = 'BuiltInProps';
2958429589const BuiltInArrayId = 'BuiltInArray';
2958529590const BuiltInSetId = 'BuiltInSet';
2958629591const BuiltInMapId = 'BuiltInMap';
29592+ const BuiltInWeakSetId = 'BuiltInWeakSet';
29593+ const BuiltInWeakMapId = 'BuiltInWeakMap';
2958729594const BuiltInFunctionId = 'BuiltInFunction';
2958829595const BuiltInJsxId = 'BuiltInJsx';
2958929596const BuiltInObjectId = 'BuiltInObject';
@@ -29605,6 +29612,7 @@ const BuiltInUseTransitionId = 'BuiltInUseTransition';
2960529612const BuiltInStartTransitionId = 'BuiltInStartTransition';
2960629613const BuiltInFireId = 'BuiltInFire';
2960729614const BuiltInFireFunctionId = 'BuiltInFireFunction';
29615+ const ReanimatedSharedValueId = 'ReanimatedSharedValueId';
2960829616const BUILTIN_SHAPES = new Map();
2960929617addObject(BUILTIN_SHAPES, BuiltInPropsId, [
2961029618    ['ref', { kind: 'Object', shapeId: BuiltInUseRefId }],
@@ -30024,6 +30032,80 @@ addObject(BUILTIN_SHAPES, BuiltInMapId, [
3002430032        }),
3002530033    ],
3002630034]);
30035+ addObject(BUILTIN_SHAPES, BuiltInWeakSetId, [
30036+     [
30037+         'add',
30038+         addFunction(BUILTIN_SHAPES, [], {
30039+             positionalParams: [Effect.Capture],
30040+             restParam: null,
30041+             returnType: { kind: 'Object', shapeId: BuiltInWeakSetId },
30042+             calleeEffect: Effect.Store,
30043+             returnValueKind: ValueKind.Mutable,
30044+         }),
30045+     ],
30046+     [
30047+         'delete',
30048+         addFunction(BUILTIN_SHAPES, [], {
30049+             positionalParams: [Effect.Read],
30050+             restParam: null,
30051+             returnType: PRIMITIVE_TYPE,
30052+             calleeEffect: Effect.Store,
30053+             returnValueKind: ValueKind.Primitive,
30054+         }),
30055+     ],
30056+     [
30057+         'has',
30058+         addFunction(BUILTIN_SHAPES, [], {
30059+             positionalParams: [Effect.Read],
30060+             restParam: null,
30061+             returnType: PRIMITIVE_TYPE,
30062+             calleeEffect: Effect.Read,
30063+             returnValueKind: ValueKind.Primitive,
30064+         }),
30065+     ],
30066+ ]);
30067+ addObject(BUILTIN_SHAPES, BuiltInWeakMapId, [
30068+     [
30069+         'delete',
30070+         addFunction(BUILTIN_SHAPES, [], {
30071+             positionalParams: [Effect.Read],
30072+             restParam: null,
30073+             returnType: PRIMITIVE_TYPE,
30074+             calleeEffect: Effect.Store,
30075+             returnValueKind: ValueKind.Primitive,
30076+         }),
30077+     ],
30078+     [
30079+         'get',
30080+         addFunction(BUILTIN_SHAPES, [], {
30081+             positionalParams: [Effect.Read],
30082+             restParam: null,
30083+             returnType: { kind: 'Poly' },
30084+             calleeEffect: Effect.Capture,
30085+             returnValueKind: ValueKind.Mutable,
30086+         }),
30087+     ],
30088+     [
30089+         'has',
30090+         addFunction(BUILTIN_SHAPES, [], {
30091+             positionalParams: [Effect.Read],
30092+             restParam: null,
30093+             returnType: PRIMITIVE_TYPE,
30094+             calleeEffect: Effect.Read,
30095+             returnValueKind: ValueKind.Primitive,
30096+         }),
30097+     ],
30098+     [
30099+         'set',
30100+         addFunction(BUILTIN_SHAPES, [], {
30101+             positionalParams: [Effect.Capture, Effect.Capture],
30102+             restParam: null,
30103+             returnType: { kind: 'Object', shapeId: BuiltInWeakMapId },
30104+             calleeEffect: Effect.Store,
30105+             returnValueKind: ValueKind.Mutable,
30106+         }),
30107+     ],
30108+ ]);
3002730109addObject(BUILTIN_SHAPES, BuiltInUseStateId, [
3002830110    ['0', { kind: 'Poly' }],
3002930111    [
@@ -37957,6 +38039,26 @@ const TYPED_GLOBALS = [
3795738039            returnValueKind: ValueKind.Mutable,
3795838040        }, null, true),
3795938041    ],
38042+     [
38043+         'WeakMap',
38044+         addFunction(DEFAULT_SHAPES, [], {
38045+             positionalParams: [Effect.ConditionallyMutateIterator],
38046+             restParam: null,
38047+             returnType: { kind: 'Object', shapeId: BuiltInWeakMapId },
38048+             calleeEffect: Effect.Read,
38049+             returnValueKind: ValueKind.Mutable,
38050+         }, null, true),
38051+     ],
38052+     [
38053+         'WeakSet',
38054+         addFunction(DEFAULT_SHAPES, [], {
38055+             positionalParams: [Effect.ConditionallyMutateIterator],
38056+             restParam: null,
38057+             returnType: { kind: 'Object', shapeId: BuiltInWeakSetId },
38058+             calleeEffect: Effect.Read,
38059+             returnValueKind: ValueKind.Mutable,
38060+         }, null, true),
38061+     ],
3796038062];
3796138063const REACT_APIS = [
3796238064    [
@@ -38282,7 +38384,7 @@ function getReanimatedModuleType(registry) {
3828238384            addHook(registry, {
3828338385                positionalParams: [],
3828438386                restParam: Effect.Freeze,
38285-                 returnType: { kind: 'Poly'  },
38387+                 returnType: { kind: 'Object', shapeId: ReanimatedSharedValueId  },
3828638388                returnValueKind: ValueKind.Mutable,
3828738389                noAlias: true,
3828838390                calleeEffect: Effect.Read,
@@ -38426,6 +38528,7 @@ const EnvironmentConfigSchema = zod.z.object({
3842638528    validateNoCapitalizedCalls: zod.z.nullable(zod.z.array(zod.z.string())).default(null),
3842738529    validateBlocklistedImports: zod.z.nullable(zod.z.array(zod.z.string())).default(null),
3842838530    validateNoImpureFunctionsInRender: zod.z.boolean().default(false),
38531+     validateNoFreezingKnownMutableFunctions: zod.z.boolean().default(false),
3842938532    enableAssumeHooksFollowRulesOfReact: zod.z.boolean().default(true),
3843038533    enableTransitivelyFreezeFunctionExpressions: zod.z.boolean().default(true),
3843138534    enableEmitFreeze: ExternalFunctionSchema.nullable().default(null),
@@ -48389,6 +48492,49 @@ class RewriteBlockIds extends ReactiveFunctionVisitor {
4838948492    }
4839048493}
4839148494
48495+ function inferAliasForUncalledFunctions(fn, aliases) {
48496+     var _a;
48497+     for (const block of fn.body.blocks.values()) {
48498+         instrs: for (const instr of block.instructions) {
48499+             const { lvalue, value } = instr;
48500+             if (value.kind !== 'ObjectMethod' &&
48501+                 value.kind !== 'FunctionExpression') {
48502+                 continue;
48503+             }
48504+             const range = lvalue.identifier.mutableRange;
48505+             if (range.end > range.start + 1) {
48506+                 continue;
48507+             }
48508+             for (const operand of eachInstructionValueOperand(value)) {
48509+                 if (isMutable(instr, operand)) {
48510+                     continue instrs;
48511+                 }
48512+             }
48513+             const operands = new Set();
48514+             for (const effect of (_a = value.loweredFunc.func.effects) !== null && _a !== void 0 ? _a : []) {
48515+                 if (effect.kind !== 'ContextMutation') {
48516+                     continue;
48517+                 }
48518+                 if (effect.effect === Effect.Store || effect.effect === Effect.Mutate) {
48519+                     for (const operand of effect.places) {
48520+                         if (isMutableEffect(operand.effect, operand.loc) &&
48521+                             !isRefOrRefLikeMutableType(operand.identifier.type)) {
48522+                             operands.add(operand.identifier);
48523+                         }
48524+                     }
48525+                 }
48526+             }
48527+             if (operands.size !== 0) {
48528+                 operands.add(lvalue.identifier);
48529+                 aliases.union([...operands]);
48530+                 for (const operand of operands) {
48531+                     operand.mutableRange.end = makeInstructionId(instr.id + 1);
48532+                 }
48533+             }
48534+         }
48535+     }
48536+ }
48537+ 
4839248538function inferAliases(func) {
4839348539    const aliases = new DisjointSet();
4839448540    for (const [_, block] of func.body.blocks) {
@@ -48630,6 +48776,7 @@ function inferMutableRanges(ir) {
4863048776    while (true) {
4863148777        inferMutableRangesForAlias(ir, aliases);
4863248778        inferAliasForPhis(ir, aliases);
48779+         inferAliasForUncalledFunctions(ir, aliases);
4863348780        const nextAliases = aliases.canonicalize();
4863448781        if (areEqualMaps(prevAliases, nextAliases)) {
4863548782            break;
@@ -55140,6 +55287,76 @@ function validateStaticComponents(fn) {
5514055287    return error.asResult();
5514155288}
5514255289
55290+ function validateNoFreezingKnownMutableFunctions(fn) {
55291+     var _a;
55292+     const errors = new CompilerError();
55293+     const contextMutationEffects = new Map();
55294+     function visitOperand(operand) {
55295+         if (operand.effect === Effect.Freeze) {
55296+             const effect = contextMutationEffects.get(operand.identifier.id);
55297+             if (effect != null) {
55298+                 errors.push({
55299+                     reason: `This argument is a function which modifies local variables when called, which can bypass memoization and cause the UI not to update`,
55300+                     description: `Functions that are returned from hooks, passed as arguments to hooks, or passed as props to components may not mutate local variables`,
55301+                     loc: operand.loc,
55302+                     severity: ErrorSeverity.InvalidReact,
55303+                 });
55304+                 errors.push({
55305+                     reason: `The function modifies a local variable here`,
55306+                     loc: effect.loc,
55307+                     severity: ErrorSeverity.InvalidReact,
55308+                 });
55309+             }
55310+         }
55311+     }
55312+     for (const block of fn.body.blocks.values()) {
55313+         for (const instr of block.instructions) {
55314+             const { lvalue, value } = instr;
55315+             switch (value.kind) {
55316+                 case 'LoadLocal': {
55317+                     const effect = contextMutationEffects.get(value.place.identifier.id);
55318+                     if (effect != null) {
55319+                         contextMutationEffects.set(lvalue.identifier.id, effect);
55320+                     }
55321+                     break;
55322+                 }
55323+                 case 'StoreLocal': {
55324+                     const effect = contextMutationEffects.get(value.value.identifier.id);
55325+                     if (effect != null) {
55326+                         contextMutationEffects.set(lvalue.identifier.id, effect);
55327+                         contextMutationEffects.set(value.lvalue.place.identifier.id, effect);
55328+                     }
55329+                     break;
55330+                 }
55331+                 case 'FunctionExpression': {
55332+                     const knownMutation = ((_a = value.loweredFunc.func.effects) !== null && _a !== void 0 ? _a : []).find(effect => {
55333+                         return (effect.kind === 'ContextMutation' &&
55334+                             (effect.effect === Effect.Store ||
55335+                                 effect.effect === Effect.Mutate) &&
55336+                             Iterable_some(effect.places, place => {
55337+                                 return (isMutableEffect(place.effect, place.loc) &&
55338+                                     !isRefOrRefLikeMutableType(place.identifier.type));
55339+                             }));
55340+                     });
55341+                     if (knownMutation && knownMutation.kind === 'ContextMutation') {
55342+                         contextMutationEffects.set(lvalue.identifier.id, knownMutation);
55343+                     }
55344+                     break;
55345+                 }
55346+                 default: {
55347+                     for (const operand of eachInstructionValueOperand(value)) {
55348+                         visitOperand(operand);
55349+                     }
55350+                 }
55351+             }
55352+         }
55353+         for (const operand of eachTerminalOperand(block.terminal)) {
55354+             visitOperand(operand);
55355+         }
55356+     }
55357+     return errors.asResult();
55358+ }
55359+ 
5514355360function run(func, config, fnType, mode, programContext, logger, filename, code) {
5514455361    var _a, _b;
5514555362    const contextIdentifiers = findContextIdentifiers(func);
@@ -55244,6 +55461,9 @@ function runWithEnvironment(func, env) {
5524455461        if (env.config.validateNoImpureFunctionsInRender) {
5524555462            validateNoImpureFunctionsInRender(hir).unwrap();
5524655463        }
55464+         if (env.config.validateNoFreezingKnownMutableFunctions) {
55465+             validateNoFreezingKnownMutableFunctions(hir).unwrap();
55466+         }
5524755467    }
5524855468    inferReactivePlaces(hir);
5524955469    log({ kind: 'hir', name: 'InferReactivePlaces', value: hir });
0 commit comments