-
Notifications
You must be signed in to change notification settings - Fork 46.9k
/
AlignMethodCallScopes.ts
80 lines (75 loc) · 2.54 KB
/
AlignMethodCallScopes.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {
HIRFunction,
IdentifierId,
ReactiveScope,
makeInstructionId,
} from '../HIR';
import DisjointSet from '../Utils/DisjointSet';
/**
* Ensures that method call instructions have scopes such that either:
* - Both the MethodCall and its property have the same scope
* - OR neither has a scope
*/
export function alignMethodCallScopes(fn: HIRFunction): void {
const scopeMapping = new Map<IdentifierId, ReactiveScope | null>();
const mergedScopes = new DisjointSet<ReactiveScope>();
for (const [, block] of fn.body.blocks) {
for (const instr of block.instructions) {
const {lvalue, value} = instr;
if (value.kind === 'MethodCall') {
const lvalueScope = lvalue.identifier.scope;
const propertyScope = value.property.identifier.scope;
if (lvalueScope !== null) {
if (propertyScope !== null) {
// Both have a scope: merge the scopes
mergedScopes.union([lvalueScope, propertyScope]);
} else {
/*
* Else the call itself has a scope but not the property,
* record that this property should be in this scope
*/
scopeMapping.set(value.property.identifier.id, lvalueScope);
}
} else if (propertyScope !== null) {
// else this property does not need a scope
scopeMapping.set(value.property.identifier.id, null);
}
} else if (
value.kind === 'FunctionExpression' ||
value.kind === 'ObjectMethod'
) {
alignMethodCallScopes(value.loweredFunc.func);
}
}
}
mergedScopes.forEach((scope, root) => {
if (scope === root) {
return;
}
root.range.start = makeInstructionId(
Math.min(scope.range.start, root.range.start),
);
root.range.end = makeInstructionId(
Math.max(scope.range.end, root.range.end),
);
});
for (const [, block] of fn.body.blocks) {
for (const instr of block.instructions) {
const mappedScope = scopeMapping.get(instr.lvalue.identifier.id);
if (mappedScope !== undefined) {
instr.lvalue.identifier.scope = mappedScope;
} else if (instr.lvalue.identifier.scope !== null) {
const mergedScope = mergedScopes.find(instr.lvalue.identifier.scope);
if (mergedScope != null) {
instr.lvalue.identifier.scope = mergedScope;
}
}
}
}
}