72
env: Environment,
73
// Bindings captured from the outer function, in case lower() is called recursively (for lambdas)
74
bindings: Bindings | null = null,
75
- capturedRefs: Array<t.Identifier> = [],
75
+ capturedRefs: Map<t.Identifier, SourceLocation> = new Map(),
76
): Result<HIRFunction, CompilerError> {
77
const builder = new HIRBuilder(env, {
78
bindings,
80
});
81
const context: HIRFunction['context'] = [];
82
83
- for (const ref of capturedRefs ?? []) {
83
+ for (const [ref, loc] of capturedRefs ?? []) {
84
context.push({
85
kind: 'Identifier',
86
identifier: builder.resolveBinding(ref),
87
effect: Effect.Unknown,
88
reactive: false,
89
- loc: ref.loc ?? GeneratedSource,
89
+ loc,
90
});
91
}
92
3439
* This isn't a problem in practice because use Babel's scope analysis to
3440
* identify the correct references.
3441
*/
3442
- const lowering = lower(expr, builder.environment, builder.bindings, [
3443
- ...builder.context,
3444
- ...capturedContext,
3445
- ]);
3442
+ const lowering = lower(
3443
+ expr,
3444
+ builder.environment,
3445
+ builder.bindings,
3446
+ new Map([...builder.context, ...capturedContext]),
3447
+ );
3448
let loweredFunc: HIRFunction;
3449
if (lowering.isErr()) {
3450
lowering
4162
return scopes;
4163
}
4164
4165
+/**
4166
+ * Returns a mapping of "context" identifiers — references to free variables that
4167
+ * will become part of the function expression's `context` array — along with the
4168
+ * source location of their first reference within the function.
4169
+ */
4170
function gatherCapturedContext(
4171
fn: NodePath<
4172
| t.FunctionExpression
4175
| t.ObjectMethod
4176
>,
4177
componentScope: Scope,
4171
-): Array<t.Identifier> {
4172
- const capturedIds = new Set<t.Identifier>();
4178
+): Map<t.Identifier, SourceLocation> {
4179
+ const capturedIds = new Map<t.Identifier, SourceLocation>();
4180
4181
/*
4182
* Capture all the scopes from the parent of this function up to and including
4219
4220
// Add the base identifier binding as a dependency.
4221
const binding = baseIdentifier.scope.getBinding(baseIdentifier.node.name);
4215
- if (binding !== undefined && pureScopes.has(binding.scope)) {
4216
- capturedIds.add(binding.identifier);
4222
+ if (
4223
+ binding !== undefined &&
4224
+ pureScopes.has(binding.scope) &&
4225
+ !capturedIds.has(binding.identifier)
4226
+ ) {
4227
+ capturedIds.set(
4228
+ binding.identifier,
4229
+ path.node.loc ?? binding.identifier.loc ?? GeneratedSource,
4230
+ );
4231
}
4232
}
4233
4264
},
4265
});
4266
4253
- return [...capturedIds.keys()];
4267
+ return capturedIds;
4268
}
4269
4270
function notNull<T>(value: T | null): value is T {