8
import type { NodePath } from "@babel/traverse";
9
import type * as t from "@babel/types";
10
import { CompilerError } from "../CompilerError";
11
-import { Set_union } from "../Utils/utils";
11
+import { getOrInsertDefault } from "../Utils/utils";
12
import { GeneratedSource } from "./HIR";
13
14
+type IdentifierInfo = {
15
+ reassigned: boolean;
16
+ reassignedByInnerFn: boolean;
17
+ referencedByInnerFn: boolean;
18
+};
19
+const DEFAULT_IDENTIFIER_INFO: IdentifierInfo = {
20
+ reassigned: false,
21
+ reassignedByInnerFn: false,
22
+ referencedByInnerFn: false,
23
+};
24
+
25
type BabelFunction =
26
| NodePath<t.FunctionDeclaration>
27
| NodePath<t.FunctionExpression>
29
| NodePath<t.ObjectMethod>;
30
type FindContextIdentifierState = {
31
currentFn: Array<BabelFunction>;
21
- reassigned: Set<t.Identifier>;
22
- referenced: Set<t.Identifier>;
32
+ identifiers: Map<t.Identifier, IdentifierInfo>;
33
};
34
35
const withFunctionScope = {
49
): Set<t.Identifier> {
50
const state: FindContextIdentifierState = {
51
currentFn: [],
42
- reassigned: new Set(),
43
- referenced: new Set(),
52
+ identifiers: new Map(),
53
};
54
55
func.traverse<FindContextIdentifierState>(
63
state: FindContextIdentifierState
64
): void {
65
const left = path.get("left");
57
- handleAssignment(state.reassigned, left);
66
+ const currentFn = state.currentFn.at(-1) ?? null;
67
+ handleAssignment(currentFn, state.identifiers, left);
68
},
69
Identifier(
70
path: NodePath<t.Identifier>,
71
state: FindContextIdentifierState
72
): void {
63
- const currentFn = state.currentFn.at(-1);
64
- if (currentFn !== undefined)
65
- handleIdentifier(currentFn, state.referenced, path);
73
+ const currentFn = state.currentFn.at(-1) ?? null;
74
+ if (path.isReferencedIdentifier()) {
75
+ handleIdentifier(currentFn, state.identifiers, path);
76
+ }
77
},
78
},
79
state
80
);
70
- return Set_union(state.reassigned, state.referenced);
81
+
82
+ const result = new Set<t.Identifier>();
83
+ for (const [id, info] of state.identifiers.entries()) {
84
+ if (info.reassignedByInnerFn) {
85
+ result.add(id);
86
+ } else if (info.reassigned && info.referencedByInnerFn) {
87
+ result.add(id);
88
+ }
89
+ }
90
+ return result;
91
}
92
93
function handleIdentifier(
74
- currentFn: BabelFunction,
75
- referenced: Set<t.Identifier>,
94
+ currentFn: BabelFunction | null,
95
+ identifiers: Map<t.Identifier, IdentifierInfo>,
96
path: NodePath<t.Identifier>
97
): void {
98
const name = path.node.name;
99
const binding = path.scope.getBinding(name);
80
- const bindingAboveLambdaScope = currentFn.scope.parent.getBinding(name);
100
+ if (binding == null) {
101
+ return;
102
+ }
103
+ const identifier = getOrInsertDefault(identifiers, binding.identifier, {
104
+ ...DEFAULT_IDENTIFIER_INFO,
105
+ });
106
82
- if (binding != null && binding === bindingAboveLambdaScope) {
83
- referenced.add(binding.identifier);
107
+ if (currentFn != null) {
108
+ const bindingAboveLambdaScope = currentFn.scope.parent.getBinding(name);
109
+
110
+ if (binding === bindingAboveLambdaScope) {
111
+ identifier.referencedByInnerFn = true;
112
+ }
113
}
114
}
115
116
function handleAssignment(
88
- reassigned: Set<t.Identifier>,
117
+ currentFn: BabelFunction | null,
118
+ identifiers: Map<t.Identifier, IdentifierInfo>,
119
lvalPath: NodePath<t.LVal>
120
): void {
121
/*
128
const path = lvalPath as NodePath<t.Identifier>;
129
const name = path.node.name;
130
const binding = path.scope.getBinding(name);
101
- if (binding != null) {
102
- reassigned.add(binding.identifier);
131
+ if (binding == null) {
132
+ break;
133
+ }
134
+ const state = getOrInsertDefault(identifiers, binding.identifier, {
135
+ ...DEFAULT_IDENTIFIER_INFO,
136
+ });
137
+ state.reassigned = true;
138
+
139
+ if (currentFn != null) {
140
+ const bindingAboveLambdaScope = currentFn.scope.parent.getBinding(name);
141
+
142
+ if (binding === bindingAboveLambdaScope) {
143
+ state.reassignedByInnerFn = true;
144
+ }
145
}
146
break;
147
}
149
const path = lvalPath as NodePath<t.ArrayPattern>;
150
for (const element of path.get("elements")) {
151
if (nonNull(element)) {
110
- handleAssignment(reassigned, element);
152
+ handleAssignment(currentFn, identifiers, element);
153
}
154
}
155
break;
165
loc: valuePath.node.loc ?? GeneratedSource,
166
suggestions: null,
167
});
126
- handleAssignment(reassigned, valuePath);
168
+ handleAssignment(currentFn, identifiers, valuePath);
169
} else {
170
CompilerError.invariant(property.isRestElement(), {
171
reason: `[FindContextIdentifiers] Invalid assumptions for babel types.`,
173
loc: property.node.loc ?? GeneratedSource,
174
suggestions: null,
175
});
134
- handleAssignment(reassigned, property);
176
+ handleAssignment(currentFn, identifiers, property);
177
}
178
}
179
break;
181
case "AssignmentPattern": {
182
const path = lvalPath as NodePath<t.AssignmentPattern>;
183
const left = path.get("left");
142
- handleAssignment(reassigned, left);
184
+ handleAssignment(currentFn, identifiers, left);
185
break;
186
}
187
case "RestElement": {
188
const path = lvalPath as NodePath<t.RestElement>;
147
- handleAssignment(reassigned, path.get("argument"));
189
+ handleAssignment(currentFn, identifiers, path.get("argument"));
190
break;
191
}
192
case "MemberExpression": {