22
BasicBlock,
23
isUseRefType,
24
SourceLocation,
25
+ ArrayExpression,
26
} from '../HIR';
27
import {eachInstructionLValue, eachInstructionOperand} from '../HIR/visitors';
28
import {isMutable} from '../ReactiveScopes/InferReactiveScopeVariables';
37
isStateSource: boolean;
38
};
39
40
+type EffectMetadata = {
41
+ effect: HIRFunction;
42
+ dependencies: ArrayExpression;
43
+};
44
+
45
type ValidationContext = {
46
readonly functions: Map<IdentifierId, FunctionExpression>;
47
+ readonly candidateDependencies: Map<IdentifierId, ArrayExpression>;
48
readonly errors: CompilerError;
49
readonly derivationCache: DerivationCache;
43
- readonly effects: Set<HIRFunction>;
50
+ readonly effectsCache: Map<IdentifierId, EffectMetadata>;
51
readonly setStateLoads: Map<IdentifierId, IdentifierId | null>;
52
readonly setStateUsages: Map<IdentifierId, Set<SourceLocation>>;
53
};
182
fn: HIRFunction,
183
): Result<void, CompilerError> {
184
const functions: Map<IdentifierId, FunctionExpression> = new Map();
185
+ const candidateDependencies: Map<IdentifierId, ArrayExpression> = new Map();
186
const derivationCache = new DerivationCache();
187
const errors = new CompilerError();
180
- const effects: Set<HIRFunction> = new Set();
188
+ const effectsCache: Map<IdentifierId, EffectMetadata> = new Map();
189
190
const setStateLoads: Map<IdentifierId, IdentifierId> = new Map();
191
const setStateUsages: Map<IdentifierId, Set<SourceLocation>> = new Map();
192
193
const context: ValidationContext = {
194
functions,
195
+ candidateDependencies,
196
errors,
197
derivationCache,
189
- effects,
198
+ effectsCache,
199
setStateLoads,
200
setStateUsages,
201
};
238
isFirstPass = false;
239
} while (context.derivationCache.snapshot());
240
232
- for (const effect of effects) {
233
- validateEffect(effect, context);
241
+ for (const [, effect] of effectsCache) {
242
+ validateEffect(effect.effect, effect.dependencies, context);
243
}
244
245
return errors.asResult();
363
value.args[1].kind === 'Identifier'
364
) {
365
const effectFunction = context.functions.get(value.args[0].identifier.id);
357
- if (effectFunction != null) {
358
- context.effects.add(effectFunction.loweredFunc.func);
366
+ const deps = context.candidateDependencies.get(
367
+ value.args[1].identifier.id,
368
+ );
369
+ if (effectFunction != null && deps != null) {
370
+ context.effectsCache.set(value.args[0].identifier.id, {
371
+ effect: effectFunction.loweredFunc.func,
372
+ dependencies: deps,
373
+ });
374
}
375
} else if (isUseStateType(lvalue.identifier) && value.args.length > 0) {
376
typeOfValue = 'fromState';
382
);
383
return;
384
}
385
+ } else if (value.kind === 'ArrayExpression') {
386
+ context.candidateDependencies.set(lvalue.identifier.id, value);
387
}
388
389
for (const operand of eachInstructionOperand(instr)) {
613
614
function validateEffect(
615
effectFunction: HIRFunction,
616
+ dependencies: ArrayExpression,
617
context: ValidationContext,
618
): void {
619
const seenBlocks: Set<BlockId> = new Set();
630
Set<SourceLocation>
631
> = new Map();
632
633
+ // Consider setStates in the effect's dependency array as being part of effectSetStateUsages
634
+ for (const dep of dependencies.elements) {
635
+ if (dep.kind === 'Identifier') {
636
+ const root = getRootSetState(dep.identifier.id, context.setStateLoads);
637
+ if (root !== null) {
638
+ effectSetStateUsages.set(root, new Set([dep.loc]));
639
+ }
640
+ }
641
+ }
642
+
643
let cleanUpFunctionDeps: Set<IdentifierId> | undefined;
644
645
const globals: Set<IdentifierId> = new Set();