Skip to content

Commit 20a4c02

Browse files
szuendDevtools-frontend LUCI CQ
authored andcommitted
Include ScopeKind in the Acorn scope parser
We need to differentiate between function/block scopes to be able to construct the "Scopes proposal" scopes from AST and mappings. R=bmeurer@chromium.org Bug: 449575926 Change-Id: I1114ab5b6e743d59d778a80a193a9df7f04c0d16 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7002917 Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Commit-Queue: Simon Zünd <szuend@chromium.org>
1 parent 6c3c535 commit 20a4c02

File tree

4 files changed

+27
-15
lines changed

4 files changed

+27
-15
lines changed

front_end/core/sdk/ScopeTreeCache.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ describe('ScopeTreeCache', () => {
2121
});
2222

2323
it('requests the scope tree once for a script', async () => {
24-
const scopeTree = {start: 0, end: 20, variables: [], children: []};
24+
const scopeTree =
25+
{start: 0, end: 20, kind: Formatter.FormatterWorkerPool.ScopeKind.GLOBAL, variables: [], children: []};
2526
javaScriptScopeTreeStub.returns(Promise.resolve(scopeTree));
2627

2728
const actualScopeTree1 = await scopeTreeForScript(script);
@@ -44,7 +45,8 @@ describe('ScopeTreeCache', () => {
4445
});
4546

4647
it('requests the scope tree once for a script, even if the first request is not done yet', async () => {
47-
const scopeTree = {start: 0, end: 20, variables: [], children: []};
48+
const scopeTree =
49+
{start: 0, end: 20, kind: Formatter.FormatterWorkerPool.ScopeKind.GLOBAL, variables: [], children: []};
4850
const {promise: scopeTreePromise, resolve: scopeTreeResolve} =
4951
Promise.withResolvers<Formatter.FormatterWorkerPool.ScopeTreeNode|null>();
5052
javaScriptScopeTreeStub.returns(scopeTreePromise);

front_end/entrypoints/formatter_worker/FormatterActions.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,16 @@ export const enum DefinitionKind {
4444
FIXED = 3,
4545
}
4646

47+
export const enum ScopeKind {
48+
BLOCK = 1,
49+
FUNCTION = 2,
50+
GLOBAL = 3,
51+
}
52+
4753
export interface ScopeTreeNode {
4854
variables: Array<{name: string, kind: DefinitionKind, offsets: number[]}>;
4955
start: number;
5056
end: number;
57+
kind: ScopeKind;
5158
children: ScopeTreeNode[];
5259
}

front_end/entrypoints/formatter_worker/ScopeParser.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import * as Acorn from '../../third_party/acorn/acorn.js';
66

77
import {ECMA_VERSION} from './AcornTokenizer.js';
8-
import {DefinitionKind, type ScopeTreeNode} from './FormatterActions.js';
8+
import {DefinitionKind, ScopeKind, type ScopeTreeNode} from './FormatterActions.js';
99

1010
export function parseScopes(expression: string, sourceType: 'module'|'script' = 'script'): Scope|null {
1111
// Parse the expression and find variables and scopes.
@@ -36,12 +36,14 @@ export class Scope {
3636
readonly parent: Scope|null;
3737
readonly start: number;
3838
readonly end: number;
39+
readonly kind: ScopeKind;
3940
readonly children: Scope[] = [];
4041

41-
constructor(start: number, end: number, parent: Scope|null) {
42+
constructor(start: number, end: number, parent: Scope|null, kind: ScopeKind) {
4243
this.start = start;
4344
this.end = end;
4445
this.parent = parent;
46+
this.kind = kind;
4547
if (parent) {
4648
parent.children.push(this);
4749
}
@@ -61,6 +63,7 @@ export class Scope {
6163
start: this.start,
6264
end: this.end,
6365
variables,
66+
kind: this.kind,
6467
children,
6568
};
6669
}
@@ -137,7 +140,7 @@ export class ScopeVariableAnalysis {
137140

138141
constructor(node: Acorn.ESTree.Node) {
139142
this.#rootNode = node;
140-
this.#rootScope = new Scope(node.start, node.end, null);
143+
this.#rootScope = new Scope(node.start, node.end, null, ScopeKind.GLOBAL);
141144
this.#currentScope = this.#rootScope;
142145
}
143146

@@ -169,7 +172,7 @@ export class ScopeVariableAnalysis {
169172
node.elements.forEach(item => this.#processNode(item));
170173
break;
171174
case 'ArrowFunctionExpression': {
172-
this.#pushScope(node.start, node.end);
175+
this.#pushScope(node.start, node.end, ScopeKind.FUNCTION);
173176
node.params.forEach(this.#processNodeAsDefinition.bind(this, DefinitionKind.VAR, false));
174177
if (node.body.type === 'BlockStatement') {
175178
// Include the body of the arrow function in the same scope as the arguments.
@@ -188,7 +191,7 @@ export class ScopeVariableAnalysis {
188191
this.#processNode(node.right);
189192
break;
190193
case 'BlockStatement':
191-
this.#pushScope(node.start, node.end);
194+
this.#pushScope(node.start, node.end, ScopeKind.BLOCK);
192195
node.body.forEach(this.#processNode.bind(this));
193196
this.#popScope(false);
194197
break;
@@ -202,7 +205,7 @@ export class ScopeVariableAnalysis {
202205
break;
203206
}
204207
case 'CatchClause':
205-
this.#pushScope(node.start, node.end);
208+
this.#pushScope(node.start, node.end, ScopeKind.BLOCK);
206209
this.#processNodeAsDefinition(DefinitionKind.LET, false, node.param);
207210
this.#processNode(node.body);
208211
this.#popScope(false);
@@ -234,14 +237,14 @@ export class ScopeVariableAnalysis {
234237
break;
235238
case 'ForInStatement':
236239
case 'ForOfStatement':
237-
this.#pushScope(node.start, node.end);
240+
this.#pushScope(node.start, node.end, ScopeKind.BLOCK);
238241
this.#processNode(node.left);
239242
this.#processNode(node.right);
240243
this.#processNode(node.body);
241244
this.#popScope(false);
242245
break;
243246
case 'ForStatement':
244-
this.#pushScope(node.start, node.end);
247+
this.#pushScope(node.start, node.end, ScopeKind.BLOCK);
245248
this.#processNode(node.init ?? null);
246249
this.#processNode(node.test ?? null);
247250
this.#processNode(node.update ?? null);
@@ -250,7 +253,7 @@ export class ScopeVariableAnalysis {
250253
break;
251254
case 'FunctionDeclaration':
252255
this.#processNodeAsDefinition(DefinitionKind.VAR, false, node.id);
253-
this.#pushScope(node.id?.end ?? node.start, node.end);
256+
this.#pushScope(node.id?.end ?? node.start, node.end, ScopeKind.FUNCTION);
254257
this.#addVariable('this', node.start, DefinitionKind.FIXED);
255258
this.#addVariable('arguments', node.start, DefinitionKind.FIXED);
256259
node.params.forEach(this.#processNodeAsDefinition.bind(this, DefinitionKind.LET, false));
@@ -259,7 +262,7 @@ export class ScopeVariableAnalysis {
259262
this.#popScope(true);
260263
break;
261264
case 'FunctionExpression':
262-
this.#pushScope(node.id?.end ?? node.start, node.end);
265+
this.#pushScope(node.id?.end ?? node.start, node.end, ScopeKind.FUNCTION);
263266
this.#addVariable('this', node.start, DefinitionKind.FIXED);
264267
this.#addVariable('arguments', node.start, DefinitionKind.FIXED);
265268
node.params.forEach(this.#processNodeAsDefinition.bind(this, DefinitionKind.LET, false));
@@ -421,8 +424,8 @@ export class ScopeVariableAnalysis {
421424
return this.#allNames;
422425
}
423426

424-
#pushScope(start: number, end: number): void {
425-
this.#currentScope = new Scope(start, end, this.#currentScope);
427+
#pushScope(start: number, end: number, kind: ScopeKind): void {
428+
this.#currentScope = new Scope(start, end, this.#currentScope, kind);
426429
}
427430

428431
#popScope(isFunctionContext: boolean): void {

front_end/models/formatter/FormatterWorkerPool.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import * as Common from '../../core/common/common.js';
66
import * as FormatterActions from '../../entrypoints/formatter_worker/FormatterActions.js'; // eslint-disable-line rulesdir/es-modules-import
77

8-
export {DefinitionKind, type ScopeTreeNode} from '../../entrypoints/formatter_worker/FormatterActions.js';
8+
export {DefinitionKind, ScopeKind, type ScopeTreeNode} from '../../entrypoints/formatter_worker/FormatterActions.js';
99

1010
let formatterWorkerPoolInstance: FormatterWorkerPool;
1111

0 commit comments

Comments
 (0)