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