17
areEqualPaths,
18
IdentifierId,
19
Terminal,
20
+ InstructionValue,
21
+ LoadContext,
22
+ TInstruction,
23
+ FunctionExpression,
24
+ ObjectMethod,
25
} from './HIR';
26
import {
27
collectHoistablePropertyLoads,
228
fn,
229
usedOutsideDeclaringScope,
230
temporaries,
226
- false,
231
+ null,
232
);
233
return temporaries;
234
}
235
236
+function isLoadContextMutable(
237
+ instrValue: InstructionValue,
238
+ id: InstructionId,
239
+): instrValue is LoadContext {
240
+ if (instrValue.kind === 'LoadContext') {
241
+ CompilerError.invariant(instrValue.place.identifier.scope != null, {
242
+ reason:
243
+ '[PropagateScopeDependencies] Expected all context variables to be assigned a scope',
244
+ loc: instrValue.loc,
245
+ });
246
+ return id >= instrValue.place.identifier.scope.range.end;
247
+ }
248
+ return false;
249
+}
250
/**
251
* Recursive collect a sidemap of all `LoadLocal` and `PropertyLoads` with a
252
* function and all nested functions.
258
fn: HIRFunction,
259
usedOutsideDeclaringScope: ReadonlySet<DeclarationId>,
260
temporaries: Map<IdentifierId, ReactiveScopeDependency>,
242
- isInnerFn: boolean,
261
+ innerFnContext: {instrId: InstructionId} | null,
262
): void {
263
for (const [_, block] of fn.body.blocks) {
245
- for (const instr of block.instructions) {
246
- const {value, lvalue} = instr;
264
+ for (const {value, lvalue, id: origInstrId} of block.instructions) {
265
+ const instrId =
266
+ innerFnContext != null ? innerFnContext.instrId : origInstrId;
267
const usedOutside = usedOutsideDeclaringScope.has(
268
lvalue.identifier.declarationId,
269
);
270
271
if (value.kind === 'PropertyLoad' && !usedOutside) {
252
- if (!isInnerFn || temporaries.has(value.object.identifier.id)) {
272
+ if (
273
+ innerFnContext == null ||
274
+ temporaries.has(value.object.identifier.id)
275
+ ) {
276
/**
277
* All dependencies of a inner / nested function must have a base
278
* identifier from the outermost component / hook. This is because the
288
temporaries.set(lvalue.identifier.id, property);
289
}
290
} else if (
268
- value.kind === 'LoadLocal' &&
291
+ (value.kind === 'LoadLocal' || isLoadContextMutable(value, instrId)) &&
292
lvalue.identifier.name == null &&
293
value.place.identifier.name !== null &&
294
!usedOutside
295
) {
296
if (
274
- !isInnerFn ||
297
+ innerFnContext == null ||
298
fn.context.some(
299
context => context.identifier.id === value.place.identifier.id,
300
)
312
value.loweredFunc.func,
313
usedOutsideDeclaringScope,
314
temporaries,
292
- true,
315
+ innerFnContext ?? {instrId},
316
);
317
}
318
}
387
* Tracks the traversal state. See Context.declare for explanation of why this
388
* is needed.
389
*/
367
- inInnerFn: boolean = false;
390
+ #innerFnContext: {outerInstrId: InstructionId} | null = null;
391
392
constructor(
393
temporariesUsedOutsideScope: ReadonlySet<DeclarationId>,
457
* by root identifier mutable ranges).
458
*/
459
declare(identifier: Identifier, decl: Decl): void {
437
- if (this.inInnerFn) return;
460
+ if (this.#innerFnContext != null) return;
461
if (!this.#declarations.has(identifier.declarationId)) {
462
this.#declarations.set(identifier.declarationId, decl);
463
}
600
currentScope.reassignments.add(place.identifier);
601
}
602
}
580
- enterInnerFn<T>(cb: () => T): T {
581
- const wasInInnerFn = this.inInnerFn;
582
- this.inInnerFn = true;
603
+ enterInnerFn<T>(
604
+ innerFn: TInstruction<FunctionExpression> | TInstruction<ObjectMethod>,
605
+ cb: () => T,
606
+ ): T {
607
+ const prevContext = this.#innerFnContext;
608
+ this.#innerFnContext = this.#innerFnContext ?? {outerInstrId: innerFn.id};
609
const result = cb();
584
- this.inInnerFn = wasInInnerFn;
610
+ this.#innerFnContext = prevContext;
611
return result;
612
}
613
750
* Recursively visit the inner function to extract dependencies there
751
*/
752
const innerFn = instr.value.loweredFunc.func;
727
- context.enterInnerFn(() => {
728
- handleFunction(innerFn);
729
- });
753
+ context.enterInnerFn(
754
+ instr as
755
+ | TInstruction<FunctionExpression>
756
+ | TInstruction<ObjectMethod>,
757
+ () => {
758
+ handleFunction(innerFn);
759
+ },
760
+ );
761
} else {
762
handleInstruction(instr, context);
763
}