chrome-devtools-frontend 1.0.1001476 → 1.0.1003469
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/gni/devtools_grd_files.gni +5 -6
- package/front_end/core/common/ParsedURL.ts +3 -3
- package/front_end/core/host/InspectorFrontendHost.ts +30 -1
- package/front_end/core/host/InspectorFrontendHostAPI.ts +1 -0
- package/front_end/core/host/UserMetrics.ts +17 -1
- package/front_end/core/i18n/locales/en-US.json +24 -63
- package/front_end/core/i18n/locales/en-XL.json +24 -63
- package/front_end/core/protocol_client/InspectorBackend.ts +4 -0
- package/front_end/core/root/Runtime.ts +7 -3
- package/front_end/core/sdk/ServiceWorkerManager.ts +6 -5
- package/front_end/core/sdk/TracingModel.ts +5 -4
- package/front_end/devtools_compatibility.js +1 -0
- package/front_end/entrypoints/formatter_worker/FormatterActions.ts +14 -0
- package/front_end/entrypoints/formatter_worker/ScopeParser.ts +491 -0
- package/front_end/entrypoints/formatter_worker/Substitute.ts +4 -440
- package/front_end/entrypoints/formatter_worker/formatter_worker.ts +2 -0
- package/front_end/entrypoints/main/MainImpl.ts +1 -0
- package/front_end/generated/InspectorBackendCommands.js +42 -12
- package/front_end/generated/SupportedCSSProperties.js +3 -5
- package/front_end/generated/protocol-mapping.d.ts +5 -1
- package/front_end/generated/protocol-proxy-api.d.ts +4 -1
- package/front_end/generated/protocol.ts +68 -14
- package/front_end/models/bindings/CompilerScriptMapping.ts +1 -1
- package/front_end/models/issues_manager/AttributionReportingIssue.ts +6 -34
- package/front_end/models/issues_manager/DeprecationIssue.ts +84 -172
- package/front_end/models/issues_manager/Issue.ts +8 -4
- package/front_end/models/issues_manager/descriptions/arInvalidHeader.md +3 -0
- package/front_end/models/persistence/NetworkPersistenceManager.ts +20 -0
- package/front_end/models/timeline_model/TimelineModel.ts +2 -49
- package/front_end/panels/application/AppManifestView.ts +3 -3
- package/front_end/panels/application/ApplicationPanelCacheSection.ts +3 -1
- package/front_end/panels/application/ApplicationPanelSidebar.ts +11 -6
- package/front_end/panels/application/ApplicationPanelTreeElement.ts +2 -2
- package/front_end/panels/application/BackgroundServiceView.ts +5 -4
- package/front_end/panels/application/ResourcesPanel.ts +1 -1
- package/front_end/panels/console/ConsoleViewMessage.ts +6 -3
- package/front_end/panels/css_overview/components/CSSOverviewStartView.ts +3 -2
- package/front_end/panels/elements/StylePropertyTreeElement.ts +19 -13
- package/front_end/panels/elements/StylesSidebarPane.ts +74 -5
- package/front_end/panels/elements/stylesSidebarPane.css +3 -0
- package/front_end/panels/issues/AffectedResourcesView.ts +4 -3
- package/front_end/panels/issues/AffectedSourcesView.ts +2 -1
- package/front_end/panels/issues/AttributionReportingIssueDetailsView.ts +9 -38
- package/front_end/panels/issues/IssueView.ts +1 -1
- package/front_end/panels/lighthouse/LighthouseController.ts +6 -3
- package/front_end/panels/lighthouse/LighthouseReporterTypes.ts +2 -1
- package/front_end/panels/lighthouse/lighthousePanel.css +4 -0
- package/front_end/panels/network/NetworkLogView.ts +32 -0
- package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +2 -2
- package/front_end/panels/profiler/HeapSnapshotView.ts +2 -1
- package/front_end/panels/profiler/ProfileDataGrid.ts +1 -1
- package/front_end/panels/security/SecurityPanel.ts +6 -5
- package/front_end/panels/settings/SettingsScreen.ts +2 -3
- package/front_end/panels/sources/SourcesNavigator.ts +1 -1
- package/front_end/panels/sources/SourcesPanel.ts +1 -1
- package/front_end/panels/timeline/PerformanceModel.ts +2 -6
- package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +5 -26
- package/front_end/panels/timeline/TimelineUIUtils.ts +14 -12
- package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +1036 -1088
- package/front_end/third_party/lighthouse/locales/en-US.json +244 -4
- package/front_end/third_party/lighthouse/locales/en-XL.json +244 -4
- package/front_end/third_party/lighthouse/report/bundle.d.ts +4 -22
- package/front_end/third_party/lighthouse/report/bundle.js +23 -366
- package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +1 -1
- package/front_end/ui/components/docs/linkifier/simple-url.ts +2 -1
- package/front_end/ui/components/docs/panel_feedback/basic.ts +3 -2
- package/front_end/ui/components/docs/panel_feedback/button.ts +2 -1
- package/front_end/ui/components/linkifier/LinkifierImpl.ts +4 -3
- package/front_end/ui/components/linkifier/LinkifierUtils.ts +2 -3
- package/front_end/ui/components/panel_feedback/FeedbackButton.ts +4 -6
- package/front_end/ui/components/panel_feedback/PanelFeedback.ts +5 -4
- package/front_end/ui/components/request_link_icon/RequestLinkIcon.ts +3 -3
- package/front_end/ui/components/text_editor/javascript.ts +6 -15
- package/front_end/ui/legacy/EmptyWidget.ts +2 -1
- package/front_end/ui/legacy/InspectorView.ts +29 -0
- package/front_end/ui/legacy/UIUtils.ts +4 -4
- package/front_end/ui/legacy/XLink.ts +12 -13
- package/front_end/ui/legacy/components/color_picker/ContrastDetails.ts +2 -4
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +1 -1
- package/front_end/ui/legacy/components/perf_ui/LineLevelProfile.ts +0 -2
- package/front_end/ui/legacy/components/utils/ImagePreview.ts +3 -7
- package/front_end/ui/legacy/components/utils/Linkifier.ts +23 -23
- package/front_end/ui/legacy/toolbar.css +1 -1
- package/package.json +1 -1
- package/scripts/whitespaces.txt +1 -0
- package/front_end/models/issues_manager/descriptions/arInvalidAttributionSourceEventId.md +0 -3
- package/front_end/models/issues_manager/descriptions/arInvalidAttributionSourceExpiry.md +0 -4
- package/front_end/models/issues_manager/descriptions/arInvalidAttributionSourcePriority.md +0 -4
@@ -416,6 +416,7 @@
|
|
416
416
|
RecordingEdited: 'DevTools.RecordingEdited',
|
417
417
|
RecordingExported: 'DevTools.RecordingExported',
|
418
418
|
RecordingReplayFinished: 'DevTools.RecordingReplayFinished',
|
419
|
+
RecordingReplaySpeed: 'DevTools.RecordingReplaySpeed',
|
419
420
|
RecordingReplayStarted: 'DevTools.RecordingReplayStarted',
|
420
421
|
RecordingToggled: 'DevTools.RecordingToggled',
|
421
422
|
SyncSetting: 'DevTools.SyncSetting',
|
@@ -22,3 +22,17 @@ export interface FormatResult {
|
|
22
22
|
content: string;
|
23
23
|
mapping: FormatMapping;
|
24
24
|
}
|
25
|
+
|
26
|
+
export const enum DefinitionKind {
|
27
|
+
None = 0,
|
28
|
+
Let = 1,
|
29
|
+
Var = 2,
|
30
|
+
Fixed = 3,
|
31
|
+
}
|
32
|
+
|
33
|
+
export interface ScopeTreeNode {
|
34
|
+
variables: {name: string, kind: DefinitionKind, offsets: number[]}[];
|
35
|
+
start: number;
|
36
|
+
end: number;
|
37
|
+
children: ScopeTreeNode[];
|
38
|
+
}
|
@@ -0,0 +1,491 @@
|
|
1
|
+
// Copyright 2022 The Chromium Authors. All rights reserved.
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
3
|
+
// found in the LICENSE file.
|
4
|
+
|
5
|
+
import * as Acorn from '../../third_party/acorn/acorn.js';
|
6
|
+
|
7
|
+
import {ECMA_VERSION} from './AcornTokenizer.js';
|
8
|
+
import type {ScopeTreeNode} from './FormatterActions.js';
|
9
|
+
import {DefinitionKind} from './FormatterActions.js';
|
10
|
+
|
11
|
+
export function parseScopes(expression: string): Scope|null {
|
12
|
+
// Parse the expression and find variables and scopes.
|
13
|
+
let root: Acorn.ESTree.Node|null = null;
|
14
|
+
try {
|
15
|
+
root = Acorn.parse(expression, {ecmaVersion: ECMA_VERSION, allowAwaitOutsideFunction: true, ranges: false}) as
|
16
|
+
Acorn.ESTree.Node;
|
17
|
+
} catch {
|
18
|
+
return null;
|
19
|
+
}
|
20
|
+
return new ScopeVariableAnalysis(root).run();
|
21
|
+
}
|
22
|
+
|
23
|
+
export interface Use {
|
24
|
+
offset: number;
|
25
|
+
scope: Scope;
|
26
|
+
isShorthandAssignmentProperty: boolean;
|
27
|
+
}
|
28
|
+
|
29
|
+
export interface VariableUses {
|
30
|
+
definitionKind: DefinitionKind;
|
31
|
+
uses: Use[];
|
32
|
+
}
|
33
|
+
|
34
|
+
export class Scope {
|
35
|
+
readonly variables = new Map<string, VariableUses>();
|
36
|
+
readonly parent: Scope|null;
|
37
|
+
readonly start: number;
|
38
|
+
readonly end: number;
|
39
|
+
readonly children: Scope[] = [];
|
40
|
+
|
41
|
+
constructor(start: number, end: number, parent: Scope|null) {
|
42
|
+
this.start = start;
|
43
|
+
this.end = end;
|
44
|
+
this.parent = parent;
|
45
|
+
if (parent) {
|
46
|
+
parent.children.push(this);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
export(): ScopeTreeNode {
|
51
|
+
const variables = [];
|
52
|
+
for (const variable of this.variables) {
|
53
|
+
const offsets = [];
|
54
|
+
for (const use of variable[1].uses) {
|
55
|
+
offsets.push(use.offset);
|
56
|
+
}
|
57
|
+
variables.push({name: variable[0], kind: variable[1].definitionKind, offsets});
|
58
|
+
}
|
59
|
+
const children = this.children.map(c => c.export());
|
60
|
+
return {
|
61
|
+
start: this.start,
|
62
|
+
end: this.end,
|
63
|
+
variables,
|
64
|
+
children,
|
65
|
+
};
|
66
|
+
}
|
67
|
+
|
68
|
+
addVariable(name: string, offset: number, definitionKind: DefinitionKind, isShorthandAssignmentProperty: boolean):
|
69
|
+
void {
|
70
|
+
const variable = this.variables.get(name);
|
71
|
+
const use = {offset, scope: this, isShorthandAssignmentProperty};
|
72
|
+
if (!variable) {
|
73
|
+
this.variables.set(name, {definitionKind, uses: [use]});
|
74
|
+
return;
|
75
|
+
}
|
76
|
+
if (variable.definitionKind === DefinitionKind.None) {
|
77
|
+
variable.definitionKind = definitionKind;
|
78
|
+
}
|
79
|
+
variable.uses.push(use);
|
80
|
+
}
|
81
|
+
|
82
|
+
findBinders(name: string): VariableUses[] {
|
83
|
+
const result = [];
|
84
|
+
let scope: Scope|null = this;
|
85
|
+
while (scope !== null) {
|
86
|
+
const defUse = scope.variables.get(name);
|
87
|
+
if (defUse && defUse.definitionKind !== DefinitionKind.None) {
|
88
|
+
result.push(defUse);
|
89
|
+
}
|
90
|
+
scope = scope.parent;
|
91
|
+
}
|
92
|
+
return result;
|
93
|
+
}
|
94
|
+
|
95
|
+
#mergeChildDefUses(name: string, defUses: VariableUses): void {
|
96
|
+
const variable = this.variables.get(name);
|
97
|
+
if (!variable) {
|
98
|
+
this.variables.set(name, defUses);
|
99
|
+
return;
|
100
|
+
}
|
101
|
+
variable.uses.push(...defUses.uses);
|
102
|
+
if (defUses.definitionKind === DefinitionKind.Var) {
|
103
|
+
console.assert(variable.definitionKind !== DefinitionKind.Let);
|
104
|
+
if (variable.definitionKind === DefinitionKind.None) {
|
105
|
+
variable.definitionKind = defUses.definitionKind;
|
106
|
+
}
|
107
|
+
} else {
|
108
|
+
console.assert(defUses.definitionKind === DefinitionKind.None);
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
finalizeToParent(isFunctionScope: boolean): void {
|
113
|
+
if (!this.parent) {
|
114
|
+
console.error('Internal error: wrong nesting in scope analysis.');
|
115
|
+
throw new Error('Internal error');
|
116
|
+
}
|
117
|
+
|
118
|
+
// Move all unbound variables to the parent (also move var-bound variables
|
119
|
+
// if the parent is not a function).
|
120
|
+
const keysToRemove = [];
|
121
|
+
for (const [name, defUse] of this.variables.entries()) {
|
122
|
+
if (defUse.definitionKind === DefinitionKind.None ||
|
123
|
+
(defUse.definitionKind === DefinitionKind.Var && !isFunctionScope)) {
|
124
|
+
this.parent.#mergeChildDefUses(name, defUse);
|
125
|
+
keysToRemove.push(name);
|
126
|
+
}
|
127
|
+
}
|
128
|
+
keysToRemove.forEach(k => this.variables.delete(k));
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
export class ScopeVariableAnalysis {
|
133
|
+
readonly #rootScope: Scope;
|
134
|
+
readonly #allNames = new Set<string>();
|
135
|
+
#currentScope: Scope;
|
136
|
+
readonly #rootNode: Acorn.ESTree.Node;
|
137
|
+
|
138
|
+
constructor(node: Acorn.ESTree.Node) {
|
139
|
+
this.#rootNode = node;
|
140
|
+
this.#rootScope = new Scope(node.start, node.end, null);
|
141
|
+
this.#currentScope = this.#rootScope;
|
142
|
+
}
|
143
|
+
|
144
|
+
run(): Scope {
|
145
|
+
this.#processNode(this.#rootNode);
|
146
|
+
return this.#rootScope;
|
147
|
+
}
|
148
|
+
|
149
|
+
#processNode(node: Acorn.ESTree.Node|null): void {
|
150
|
+
if (node === null) {
|
151
|
+
return;
|
152
|
+
}
|
153
|
+
switch (node.type) {
|
154
|
+
case 'AwaitExpression':
|
155
|
+
this.#processNode(node.argument);
|
156
|
+
break;
|
157
|
+
case 'ArrayExpression':
|
158
|
+
node.elements.forEach(item => this.#processNode(item));
|
159
|
+
break;
|
160
|
+
case 'ExpressionStatement':
|
161
|
+
this.#processNode(node.expression);
|
162
|
+
break;
|
163
|
+
case 'Program':
|
164
|
+
console.assert(this.#currentScope === this.#rootScope);
|
165
|
+
node.body.forEach(item => this.#processNode(item));
|
166
|
+
console.assert(this.#currentScope === this.#rootScope);
|
167
|
+
break;
|
168
|
+
case 'ArrayPattern':
|
169
|
+
node.elements.forEach(item => this.#processNode(item));
|
170
|
+
break;
|
171
|
+
case 'ArrowFunctionExpression': {
|
172
|
+
this.#pushScope(node.start, node.end);
|
173
|
+
node.params.forEach(this.#processNodeAsDefinition.bind(this, DefinitionKind.Var));
|
174
|
+
this.#processNode(node.body);
|
175
|
+
this.#popScope(true);
|
176
|
+
break;
|
177
|
+
}
|
178
|
+
case 'AssignmentExpression':
|
179
|
+
case 'AssignmentPattern':
|
180
|
+
case 'BinaryExpression':
|
181
|
+
case 'LogicalExpression':
|
182
|
+
this.#processNode(node.left);
|
183
|
+
this.#processNode(node.right);
|
184
|
+
break;
|
185
|
+
case 'BlockStatement':
|
186
|
+
this.#pushScope(node.start, node.end);
|
187
|
+
node.body.forEach(this.#processNode.bind(this));
|
188
|
+
this.#popScope(false);
|
189
|
+
break;
|
190
|
+
case 'CallExpression':
|
191
|
+
this.#processNode(node.callee);
|
192
|
+
node.arguments.forEach(this.#processNode.bind(this));
|
193
|
+
break;
|
194
|
+
case 'VariableDeclaration': {
|
195
|
+
const definitionKind = node.kind === 'var' ? DefinitionKind.Var : DefinitionKind.Let;
|
196
|
+
node.declarations.forEach(this.#processVariableDeclarator.bind(this, definitionKind));
|
197
|
+
break;
|
198
|
+
}
|
199
|
+
case 'CatchClause':
|
200
|
+
this.#pushScope(node.start, node.end);
|
201
|
+
this.#processNodeAsDefinition(DefinitionKind.Let, node.param);
|
202
|
+
node.body.body.forEach(this.#processNode.bind(this));
|
203
|
+
this.#popScope(false);
|
204
|
+
break;
|
205
|
+
case 'ClassBody':
|
206
|
+
node.body.forEach(this.#processNode.bind(this));
|
207
|
+
break;
|
208
|
+
case 'ClassDeclaration':
|
209
|
+
this.#processNodeAsDefinition(DefinitionKind.Let, node.id);
|
210
|
+
this.#pushScope(node.start, node.end);
|
211
|
+
this.#processNode(node.superClass ?? null);
|
212
|
+
this.#processNode(node.body);
|
213
|
+
this.#popScope(false);
|
214
|
+
break;
|
215
|
+
case 'ClassExpression':
|
216
|
+
this.#pushScope(node.start, node.end);
|
217
|
+
// Intentionally ignore the id.
|
218
|
+
this.#processNode(node.superClass ?? null);
|
219
|
+
this.#processNode(node.body);
|
220
|
+
this.#popScope(false);
|
221
|
+
break;
|
222
|
+
case 'ChainExpression':
|
223
|
+
this.#processNode(node.expression);
|
224
|
+
break;
|
225
|
+
case 'ConditionalExpression':
|
226
|
+
this.#processNode(node.test);
|
227
|
+
this.#processNode(node.consequent);
|
228
|
+
this.#processNode(node.alternate);
|
229
|
+
break;
|
230
|
+
case 'DoWhileStatement':
|
231
|
+
this.#processNode(node.body);
|
232
|
+
this.#processNode(node.test);
|
233
|
+
break;
|
234
|
+
case 'ForInStatement':
|
235
|
+
case 'ForOfStatement':
|
236
|
+
this.#pushScope(node.start, node.end);
|
237
|
+
this.#processNode(node.left);
|
238
|
+
this.#processNode(node.right);
|
239
|
+
this.#processNode(node.body);
|
240
|
+
this.#popScope(false);
|
241
|
+
break;
|
242
|
+
case 'ForStatement':
|
243
|
+
this.#pushScope(node.start, node.end);
|
244
|
+
this.#processNode(node.init ?? null);
|
245
|
+
this.#processNode(node.test ?? null);
|
246
|
+
this.#processNode(node.update ?? null);
|
247
|
+
this.#processNode(node.body);
|
248
|
+
this.#popScope(false);
|
249
|
+
break;
|
250
|
+
case 'FunctionDeclaration':
|
251
|
+
this.#processNodeAsDefinition(DefinitionKind.Var, node.id);
|
252
|
+
this.#pushScope(node.id?.end ?? node.start, node.end);
|
253
|
+
this.#addVariable('this', node.start, DefinitionKind.Fixed);
|
254
|
+
this.#addVariable('arguments', node.start, DefinitionKind.Fixed);
|
255
|
+
node.params.forEach(this.#processNodeAsDefinition.bind(this, DefinitionKind.Let));
|
256
|
+
this.#processNode(node.body);
|
257
|
+
this.#popScope(true);
|
258
|
+
break;
|
259
|
+
case 'FunctionExpression':
|
260
|
+
// Id is intentionally ignored in function expressions.
|
261
|
+
this.#pushScope(node.start, node.end);
|
262
|
+
this.#addVariable('this', node.start, DefinitionKind.Fixed);
|
263
|
+
this.#addVariable('arguments', node.start, DefinitionKind.Fixed);
|
264
|
+
node.params.forEach(this.#processNodeAsDefinition.bind(this, DefinitionKind.Let));
|
265
|
+
this.#processNode(node.body);
|
266
|
+
this.#popScope(true);
|
267
|
+
break;
|
268
|
+
case 'Identifier':
|
269
|
+
this.#addVariable(node.name, node.start);
|
270
|
+
break;
|
271
|
+
case 'IfStatement':
|
272
|
+
this.#processNode(node.test);
|
273
|
+
this.#processNode(node.consequent);
|
274
|
+
this.#processNode(node.alternate ?? null);
|
275
|
+
break;
|
276
|
+
case 'LabeledStatement':
|
277
|
+
this.#processNode(node.body);
|
278
|
+
break;
|
279
|
+
case 'MetaProperty':
|
280
|
+
break;
|
281
|
+
case 'MethodDefinition':
|
282
|
+
if (node.computed) {
|
283
|
+
this.#processNode(node.key);
|
284
|
+
}
|
285
|
+
this.#processNode(node.value);
|
286
|
+
break;
|
287
|
+
case 'NewExpression':
|
288
|
+
this.#processNode(node.callee);
|
289
|
+
node.arguments.forEach(this.#processNode.bind(this));
|
290
|
+
break;
|
291
|
+
case 'MemberExpression':
|
292
|
+
this.#processNode(node.object);
|
293
|
+
if (node.computed) {
|
294
|
+
this.#processNode(node.property);
|
295
|
+
}
|
296
|
+
break;
|
297
|
+
case 'ObjectExpression':
|
298
|
+
node.properties.forEach(this.#processNode.bind(this));
|
299
|
+
break;
|
300
|
+
case 'ObjectPattern':
|
301
|
+
node.properties.forEach(this.#processNode.bind(this));
|
302
|
+
break;
|
303
|
+
case 'PrivateIdentifier':
|
304
|
+
break;
|
305
|
+
case 'PropertyDefinition':
|
306
|
+
if (node.computed) {
|
307
|
+
this.#processNode(node.key);
|
308
|
+
}
|
309
|
+
this.#processNode(node.value ?? null);
|
310
|
+
break;
|
311
|
+
case 'Property':
|
312
|
+
if (node.shorthand) {
|
313
|
+
console.assert(node.value === node.key);
|
314
|
+
console.assert(node.value.type === 'Identifier');
|
315
|
+
this.#addVariable((node.value as Acorn.ESTree.Identifier).name, node.value.start, DefinitionKind.None, true);
|
316
|
+
} else {
|
317
|
+
if (node.computed) {
|
318
|
+
this.#processNode(node.key);
|
319
|
+
}
|
320
|
+
this.#processNode(node.value);
|
321
|
+
}
|
322
|
+
break;
|
323
|
+
case 'RestElement':
|
324
|
+
this.#processNodeAsDefinition(DefinitionKind.Let, node.argument);
|
325
|
+
break;
|
326
|
+
case 'ReturnStatement':
|
327
|
+
this.#processNode(node.argument ?? null);
|
328
|
+
break;
|
329
|
+
case 'SequenceExpression':
|
330
|
+
node.expressions.forEach(this.#processNode.bind(this));
|
331
|
+
break;
|
332
|
+
case 'SpreadElement':
|
333
|
+
this.#processNode(node.argument);
|
334
|
+
break;
|
335
|
+
case 'SwitchCase':
|
336
|
+
this.#processNode(node.test ?? null);
|
337
|
+
node.consequent.forEach(this.#processNode.bind(this));
|
338
|
+
break;
|
339
|
+
case 'SwitchStatement':
|
340
|
+
this.#processNode(node.discriminant);
|
341
|
+
node.cases.forEach(this.#processNode.bind(this));
|
342
|
+
break;
|
343
|
+
case 'TaggedTemplateExpression':
|
344
|
+
this.#processNode(node.tag);
|
345
|
+
this.#processNode(node.quasi);
|
346
|
+
break;
|
347
|
+
case 'TemplateLiteral':
|
348
|
+
node.expressions.forEach(this.#processNode.bind(this));
|
349
|
+
break;
|
350
|
+
case 'ThisExpression':
|
351
|
+
this.#addVariable('this', node.start);
|
352
|
+
break;
|
353
|
+
case 'ThrowStatement':
|
354
|
+
this.#processNode(node.argument);
|
355
|
+
break;
|
356
|
+
case 'TryStatement':
|
357
|
+
this.#processNode(node.block);
|
358
|
+
this.#processNode(node.handler ?? null);
|
359
|
+
this.#processNode(node.finalizer ?? null);
|
360
|
+
break;
|
361
|
+
case 'WithStatement':
|
362
|
+
this.#processNode(node.object);
|
363
|
+
// TODO jarin figure how to treat the with body.
|
364
|
+
this.#processNode(node.body);
|
365
|
+
break;
|
366
|
+
case 'YieldExpression':
|
367
|
+
this.#processNode(node.argument ?? null);
|
368
|
+
break;
|
369
|
+
case 'UnaryExpression':
|
370
|
+
case 'UpdateExpression':
|
371
|
+
this.#processNode(node.argument);
|
372
|
+
break;
|
373
|
+
case 'WhileStatement':
|
374
|
+
this.#processNode(node.test);
|
375
|
+
this.#processNode(node.body);
|
376
|
+
break;
|
377
|
+
|
378
|
+
// Ignore, no expressions involved.
|
379
|
+
case 'BreakStatement':
|
380
|
+
case 'ContinueStatement':
|
381
|
+
case 'DebuggerStatement':
|
382
|
+
case 'EmptyStatement':
|
383
|
+
case 'Literal':
|
384
|
+
case 'Super':
|
385
|
+
case 'TemplateElement':
|
386
|
+
break;
|
387
|
+
// Ignore, cannot be used outside of a module.
|
388
|
+
case 'ImportDeclaration':
|
389
|
+
case 'ImportDefaultSpecifier':
|
390
|
+
case 'ImportNamespaceSpecifier':
|
391
|
+
case 'ImportSpecifier':
|
392
|
+
case 'ImportExpression':
|
393
|
+
case 'ExportAllDeclaration':
|
394
|
+
case 'ExportDefaultDeclaration':
|
395
|
+
case 'ExportNamedDeclaration':
|
396
|
+
case 'ExportSpecifier':
|
397
|
+
break;
|
398
|
+
|
399
|
+
case 'VariableDeclarator':
|
400
|
+
console.error('Should not encounter VariableDeclarator in general traversal.');
|
401
|
+
break;
|
402
|
+
}
|
403
|
+
}
|
404
|
+
|
405
|
+
getFreeVariables(): Map<string, Use[]> {
|
406
|
+
const result = new Map<string, Use[]>();
|
407
|
+
for (const [name, defUse] of this.#rootScope.variables) {
|
408
|
+
if (defUse.definitionKind !== DefinitionKind.None) {
|
409
|
+
// Skip bound variables.
|
410
|
+
continue;
|
411
|
+
}
|
412
|
+
result.set(name, defUse.uses);
|
413
|
+
}
|
414
|
+
return result;
|
415
|
+
}
|
416
|
+
|
417
|
+
getAllNames(): Set<string> {
|
418
|
+
return this.#allNames;
|
419
|
+
}
|
420
|
+
|
421
|
+
#pushScope(start: number, end: number): void {
|
422
|
+
this.#currentScope = new Scope(start, end, this.#currentScope);
|
423
|
+
}
|
424
|
+
|
425
|
+
#popScope(isFunctionContext: boolean): void {
|
426
|
+
if (this.#currentScope.parent === null) {
|
427
|
+
console.error('Internal error: wrong nesting in scope analysis.');
|
428
|
+
throw new Error('Internal error');
|
429
|
+
}
|
430
|
+
this.#currentScope.finalizeToParent(isFunctionContext);
|
431
|
+
this.#currentScope = this.#currentScope.parent;
|
432
|
+
}
|
433
|
+
|
434
|
+
#addVariable(
|
435
|
+
name: string, offset: number, definitionKind: DefinitionKind = DefinitionKind.None,
|
436
|
+
isShorthandAssignmentProperty: boolean = false): void {
|
437
|
+
this.#allNames.add(name);
|
438
|
+
this.#currentScope.addVariable(name, offset, definitionKind, isShorthandAssignmentProperty);
|
439
|
+
}
|
440
|
+
|
441
|
+
#processNodeAsDefinition(
|
442
|
+
definitionKind: DefinitionKind.Let|DefinitionKind.Var,
|
443
|
+
node: Acorn.ESTree.Pattern|Acorn.ESTree.AssignmentProperty|null): void {
|
444
|
+
if (node === null) {
|
445
|
+
return;
|
446
|
+
}
|
447
|
+
switch (node.type) {
|
448
|
+
case 'ArrayPattern':
|
449
|
+
node.elements.forEach(this.#processNodeAsDefinition.bind(this, definitionKind));
|
450
|
+
break;
|
451
|
+
case 'AssignmentPattern':
|
452
|
+
this.#processNodeAsDefinition(definitionKind, node.left);
|
453
|
+
this.#processNode(node.right);
|
454
|
+
break;
|
455
|
+
case 'Identifier':
|
456
|
+
this.#addVariable(node.name, node.start, definitionKind);
|
457
|
+
break;
|
458
|
+
case 'MemberExpression':
|
459
|
+
this.#processNode(node.object);
|
460
|
+
if (node.computed) {
|
461
|
+
this.#processNode(node.property);
|
462
|
+
}
|
463
|
+
break;
|
464
|
+
case 'ObjectPattern':
|
465
|
+
node.properties.forEach(this.#processNodeAsDefinition.bind(this, definitionKind));
|
466
|
+
break;
|
467
|
+
case 'Property':
|
468
|
+
// This is AssignmentProperty inside an object pattern.
|
469
|
+
if (node.shorthand) {
|
470
|
+
console.assert(node.value === node.key);
|
471
|
+
console.assert(node.value.type === 'Identifier');
|
472
|
+
this.#addVariable((node.value as Acorn.ESTree.Identifier).name, node.value.start, definitionKind, true);
|
473
|
+
} else {
|
474
|
+
if (node.computed) {
|
475
|
+
this.#processNode(node.key);
|
476
|
+
}
|
477
|
+
this.#processNodeAsDefinition(definitionKind, node.value);
|
478
|
+
}
|
479
|
+
break;
|
480
|
+
case 'RestElement':
|
481
|
+
this.#processNodeAsDefinition(definitionKind, node.argument);
|
482
|
+
break;
|
483
|
+
}
|
484
|
+
}
|
485
|
+
|
486
|
+
#processVariableDeclarator(
|
487
|
+
definitionKind: DefinitionKind.Let|DefinitionKind.Var, decl: Acorn.ESTree.VariableDeclarator): void {
|
488
|
+
this.#processNodeAsDefinition(definitionKind, decl.id);
|
489
|
+
this.#processNode(decl.init ?? null);
|
490
|
+
}
|
491
|
+
}
|