1
import {CompilerError} from '../CompilerError';
2
import {inRange} from '../ReactiveScopes/InferReactiveScopeVariables';
3
+import {printDependency} from '../ReactiveScopes/PrintReactiveFunction';
4
import {
5
Set_equal,
6
Set_filter,
24
} from './HIR';
25
import {collectTemporariesSidemap} from './PropagateScopeDependenciesHIR';
26
27
+const DEBUG_PRINT = false;
28
+
29
/**
30
* Helper function for `PropagateScopeDependencies`. Uses control flow graph
31
* analysis to determine which `Identifier`s can be assumed to be non-null
89
fn: HIRFunction,
90
temporaries: ReadonlyMap<IdentifierId, ReactiveScopeDependency>,
91
hoistableFromOptionals: ReadonlyMap<BlockId, ReactiveScopeDependency>,
89
- nestedFnImmutableContext: ReadonlySet<IdentifierId> | null,
92
): ReadonlyMap<BlockId, BlockInfo> {
93
const registry = new PropertyPathRegistry();
92
-
93
- const functionExpressionLoads = collectFunctionExpressionFakeLoads(fn);
94
- const actuallyEvaluatedTemporaries = new Map(
95
- [...temporaries].filter(([id]) => !functionExpressionLoads.has(id)),
96
- );
97
-
94
/**
95
* Due to current limitations of mutable range inference, there are edge cases in
96
* which we infer known-immutable values (e.g. props or hook params) to have a
107
}
108
}
109
}
114
- const nodes = collectNonNullsInBlocks(fn, {
115
- temporaries: actuallyEvaluatedTemporaries,
110
+ return collectHoistablePropertyLoadsImpl(fn, {
111
+ temporaries,
112
knownImmutableIdentifiers,
113
hoistableFromOptionals,
114
registry,
119
- nestedFnImmutableContext,
115
+ nestedFnImmutableContext: null,
116
});
121
- propagateNonNull(fn, nodes, registry);
117
+}
118
+
119
+type CollectHoistablePropertyLoadsContext = {
120
+ temporaries: ReadonlyMap<IdentifierId, ReactiveScopeDependency>;
121
+ knownImmutableIdentifiers: ReadonlySet<IdentifierId>;
122
+ hoistableFromOptionals: ReadonlyMap<BlockId, ReactiveScopeDependency>;
123
+ registry: PropertyPathRegistry;
124
+ /**
125
+ * (For nested / inner function declarations)
126
+ * Context variables (i.e. captured from an outer scope) that are immutable.
127
+ * Note that this technically could be merged into `knownImmutableIdentifiers`,
128
+ * but are currently kept separate for readability.
129
+ */
130
+ nestedFnImmutableContext: ReadonlySet<IdentifierId> | null;
131
+};
132
+function collectHoistablePropertyLoadsImpl(
133
+ fn: HIRFunction,
134
+ context: CollectHoistablePropertyLoadsContext,
135
+): ReadonlyMap<BlockId, BlockInfo> {
136
+ const functionExpressionLoads = collectFunctionExpressionFakeLoads(fn);
137
+ const actuallyEvaluatedTemporaries = new Map(
138
+ [...context.temporaries].filter(([id]) => !functionExpressionLoads.has(id)),
139
+ );
140
+
141
+ const nodes = collectNonNullsInBlocks(fn, {
142
+ ...context,
143
+ temporaries: actuallyEvaluatedTemporaries,
144
+ });
145
+ propagateNonNull(fn, nodes, context.registry);
146
+
147
+ if (DEBUG_PRINT) {
148
+ console.log('(printing hoistable nodes in blocks)');
149
+ for (const [blockId, node] of nodes) {
150
+ console.log(
151
+ `bb${blockId}: ${[...node.assumedNonNullObjects].map(n => printDependency(n.fullPath)).join(' ')}`,
152
+ );
153
+ }
154
+ }
155
156
return nodes;
157
}
276
277
function getMaybeNonNullInInstruction(
278
instr: InstructionValue,
246
- context: CollectNonNullsInBlocksContext,
279
+ context: CollectHoistablePropertyLoadsContext,
280
): PropertyPathNode | null {
281
let path = null;
282
if (instr.kind === 'PropertyLoad') {
295
function isImmutableAtInstr(
296
identifier: Identifier,
297
instr: InstructionId,
265
- context: CollectNonNullsInBlocksContext,
298
+ context: CollectHoistablePropertyLoadsContext,
299
): boolean {
300
if (context.nestedFnImmutableContext != null) {
301
/**
328
}
329
}
330
298
-type CollectNonNullsInBlocksContext = {
299
- temporaries: ReadonlyMap<IdentifierId, ReactiveScopeDependency>;
300
- knownImmutableIdentifiers: ReadonlySet<IdentifierId>;
301
- hoistableFromOptionals: ReadonlyMap<BlockId, ReactiveScopeDependency>;
302
- registry: PropertyPathRegistry;
303
- /**
304
- * (For nested / inner function declarations)
305
- * Context variables (i.e. captured from an outer scope) that are immutable.
306
- * Note that this technically could be merged into `knownImmutableIdentifiers`,
307
- * but are currently kept separate for readability.
308
- */
309
- nestedFnImmutableContext: ReadonlySet<IdentifierId> | null;
310
-};
331
function collectNonNullsInBlocks(
332
fn: HIRFunction,
313
- context: CollectNonNullsInBlocksContext,
333
+ context: CollectHoistablePropertyLoadsContext,
334
): ReadonlyMap<BlockId, BlockInfo> {
335
/**
336
* Known non-null objects such as functional component props can be safely
378
new Set(),
379
);
380
const innerOptionals = collectOptionalChainSidemap(innerFn.func);
361
- const innerHoistableMap = collectHoistablePropertyLoads(
381
+ const innerHoistableMap = collectHoistablePropertyLoadsImpl(
382
innerFn.func,
363
- innerTemporaries,
364
- innerOptionals.hoistableObjects,
365
- context.nestedFnImmutableContext ??
366
- new Set(
367
- innerFn.func.context
368
- .filter(place =>
369
- isImmutableAtInstr(place.identifier, instr.id, context),
370
- )
371
- .map(place => place.identifier.id),
372
- ),
383
+ {
384
+ ...context,
385
+ temporaries: innerTemporaries, // TODO: remove in later PR
386
+ hoistableFromOptionals: innerOptionals.hoistableObjects, // TODO: remove in later PR
387
+ nestedFnImmutableContext:
388
+ context.nestedFnImmutableContext ??
389
+ new Set(
390
+ innerFn.func.context
391
+ .filter(place =>
392
+ isImmutableAtInstr(place.identifier, instr.id, context),
393
+ )
394
+ .map(place => place.identifier.id),
395
+ ),
396
+ },
397
);
398
const innerHoistables = assertNonNull(
399
innerHoistableMap.get(innerFn.func.body.entry),