poe-code 6.0.0 → 7.0.1
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/dist/index.js +307 -937
- package/dist/index.js.map +4 -4
- package/dist/metafile.json +1 -1
- package/package.json +1 -1
- package/packages/safejs/dist/interp/async.d.ts +2 -1
- package/packages/safejs/dist/interp/async.js +25 -24
- package/packages/safejs/dist/interp/interpreter.js +7 -5
- package/packages/safejs/dist/interp/patterns.d.ts +1 -0
- package/packages/safejs/dist/interp/patterns.js +1 -1
- package/packages/safejs/dist/interp/var-hoist.js +5 -135
- package/packages/safejs/dist/lint/index.js +0 -4
- package/packages/safejs/dist/lint/rules/AS001.js +4 -4
- package/packages/safejs/dist/lint/rules/AS003.js +47 -7
- package/packages/safejs/dist/lint/rules/AS006-007.js +48 -7
- package/packages/safejs/dist/parse/bindings.d.ts +6 -0
- package/packages/safejs/dist/parse/bindings.js +90 -0
- package/packages/safejs/dist/parse/parser.js +70 -37
- package/packages/safejs/dist/snapshot/dump-format.d.ts +1 -1
- package/packages/safejs/dist/snapshot/dump-format.js +1 -1
- package/packages/safejs/dist/lint/rules/AS-await-non-promise.d.ts +0 -13
- package/packages/safejs/dist/lint/rules/AS-await-non-promise.js +0 -301
- package/packages/safejs/dist/lint/rules/AS008.d.ts +0 -13
- package/packages/safejs/dist/lint/rules/AS008.js +0 -386
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ArrowFunctionExpression, AwaitExpression, FunctionDeclaration, FunctionExpression, ParseResult, SourceSpan } from "../parse.js";
|
|
1
|
+
import type { ArrowFunctionExpression, AwaitExpression, BlockStatement, FunctionDeclaration, FunctionExpression, ParseResult, SourceSpan } from "../parse.js";
|
|
2
2
|
import { type GeneratorCompletion } from "./generator.js";
|
|
3
3
|
import { type OtelSpan } from "../observability/otel.js";
|
|
4
4
|
import type { Budget } from "./budget.js";
|
|
@@ -25,6 +25,7 @@ export type AsyncEvaluationContext = {
|
|
|
25
25
|
onSuspend?: () => void;
|
|
26
26
|
captureReplayState?: () => unknown;
|
|
27
27
|
rootNode?: ParseResult;
|
|
28
|
+
functionBody?: BlockStatement;
|
|
28
29
|
restoredLoopIterations: Map<number, LoopIterationSnapshot>;
|
|
29
30
|
resumeTarget?: {
|
|
30
31
|
nodeId?: number;
|
|
@@ -5,6 +5,7 @@ import { getThenable, resolveSandboxValue } from "./promise.js";
|
|
|
5
5
|
import { runAsyncPrefix, suspendJob } from "./jobs.js";
|
|
6
6
|
import { awaitSandboxValue } from "./cancel.js";
|
|
7
7
|
import { hoistVarDeclarations } from "./var-hoist.js";
|
|
8
|
+
import { boundIdentifiers, containsParameterExpression, hoistedVarDeclarations } from "../parse/bindings.js";
|
|
8
9
|
import { createSandboxArguments, createSandboxClosure, createSandboxGenerator, createSandboxPromise, allocateProducedSandboxValue, isSandboxPromise } from "./values.js";
|
|
9
10
|
export function emitResumeBreakpoint(context, breakpoint) {
|
|
10
11
|
context.onYield?.({
|
|
@@ -112,6 +113,7 @@ function createGeneratorClosure(node, context, evaluateNode) {
|
|
|
112
113
|
const channel = createGeneratorChannel(async (generatorYield) => {
|
|
113
114
|
const result = await evaluateNode(node.body, {
|
|
114
115
|
...closureContext,
|
|
116
|
+
functionBody: node.body,
|
|
115
117
|
generatorYield: (value, yieldNodeId) => {
|
|
116
118
|
generator.state = "suspended";
|
|
117
119
|
return generatorYield(value, yieldNodeId);
|
|
@@ -166,6 +168,7 @@ async function executeClosure(node, args, thisValue, context, evaluateNode) {
|
|
|
166
168
|
const scope = await createClosureScope(node, args, thisValue, context, evaluateNode);
|
|
167
169
|
const result = await evaluateNode(node.body, {
|
|
168
170
|
...context,
|
|
171
|
+
functionBody: node.body.type === "BlockStatement" ? node.body : undefined,
|
|
169
172
|
scope
|
|
170
173
|
});
|
|
171
174
|
if (result.kind === "error") {
|
|
@@ -187,16 +190,32 @@ async function createClosureScope(node, args, thisValue, context, evaluateNode)
|
|
|
187
190
|
scope.declare("arguments", "let", createSandboxArguments(args));
|
|
188
191
|
}
|
|
189
192
|
await bindParameters(node.params, args, scope, context, evaluateNode);
|
|
190
|
-
|
|
191
|
-
|
|
193
|
+
const bodyScope = node.params.some(containsParameterExpression)
|
|
194
|
+
? scope.child({}, { functionBoundary: true })
|
|
195
|
+
: scope;
|
|
196
|
+
hoistVarDeclarations(node.body, bodyScope);
|
|
197
|
+
if (bodyScope !== scope) {
|
|
198
|
+
for (const declaration of hoistedVarDeclarations([node.body])) {
|
|
199
|
+
for (const declarator of declaration.declarations) {
|
|
200
|
+
for (const identifier of boundIdentifiers(declarator.id)) {
|
|
201
|
+
if (!scope.hasOwnBinding(identifier.name))
|
|
202
|
+
continue;
|
|
203
|
+
const parameter = scope.lookup(identifier.name);
|
|
204
|
+
if (parameter.found)
|
|
205
|
+
bodyScope.assign(identifier.name, parameter.value);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return bodyScope;
|
|
192
211
|
}
|
|
193
212
|
function isBlockBody(body) {
|
|
194
213
|
return body.type === "BlockStatement";
|
|
195
214
|
}
|
|
196
215
|
async function bindParameters(params, args, scope, context, evaluateNode) {
|
|
197
216
|
for (const param of params) {
|
|
198
|
-
for (const
|
|
199
|
-
scope.predeclare(name, "
|
|
217
|
+
for (const identifier of boundIdentifiers(param)) {
|
|
218
|
+
scope.predeclare(identifier.name, "var");
|
|
200
219
|
}
|
|
201
220
|
}
|
|
202
221
|
for (let index = 0; index < params.length; index += 1) {
|
|
@@ -204,7 +223,7 @@ async function bindParameters(params, args, scope, context, evaluateNode) {
|
|
|
204
223
|
if (param.type === "RestElement") {
|
|
205
224
|
const rest = args.slice(index);
|
|
206
225
|
context.budget.allocateArrayLength(rest.length);
|
|
207
|
-
const binding = await bindPattern(param, rest, { kind: "
|
|
226
|
+
const binding = await bindPattern(param, rest, { kind: "var", initialize: true }, scope, {
|
|
208
227
|
evaluate: (defaultNode) => evaluateNode(defaultNode, { ...context, scope })
|
|
209
228
|
});
|
|
210
229
|
if (!binding.ok) {
|
|
@@ -217,7 +236,7 @@ async function bindParameters(params, args, scope, context, evaluateNode) {
|
|
|
217
236
|
}
|
|
218
237
|
return;
|
|
219
238
|
}
|
|
220
|
-
const binding = await bindPattern(param, args[index], { kind: "
|
|
239
|
+
const binding = await bindPattern(param, args[index], { kind: "var", initialize: true }, scope, {
|
|
221
240
|
evaluate: (defaultNode) => evaluateNode(defaultNode, { ...context, scope })
|
|
222
241
|
});
|
|
223
242
|
if (!binding.ok) {
|
|
@@ -230,24 +249,6 @@ async function bindParameters(params, args, scope, context, evaluateNode) {
|
|
|
230
249
|
}
|
|
231
250
|
}
|
|
232
251
|
}
|
|
233
|
-
function getParameterBindingNames(pattern) {
|
|
234
|
-
switch (pattern.type) {
|
|
235
|
-
case "Identifier":
|
|
236
|
-
return [pattern.name];
|
|
237
|
-
case "MemberExpression":
|
|
238
|
-
return [];
|
|
239
|
-
case "AssignmentPattern":
|
|
240
|
-
return getParameterBindingNames(pattern.left);
|
|
241
|
-
case "RestElement":
|
|
242
|
-
return getParameterBindingNames(pattern.argument);
|
|
243
|
-
case "ArrayPattern":
|
|
244
|
-
return pattern.elements.flatMap((element) => element === null ? [] : getParameterBindingNames(element));
|
|
245
|
-
case "ObjectPattern":
|
|
246
|
-
return pattern.properties.flatMap((property) => property.type === "RestElement"
|
|
247
|
-
? getParameterBindingNames(property)
|
|
248
|
-
: getParameterBindingNames(property.value));
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
252
|
export function normalizeClosureResult(result, budget) {
|
|
252
253
|
if (isSandboxPromise(result) &&
|
|
253
254
|
(result.synchronousPrefix !== undefined || result.hostCall !== undefined)) {
|
|
@@ -803,7 +803,9 @@ function isParseResult(value) {
|
|
|
803
803
|
Object.hasOwn(value, "span"));
|
|
804
804
|
}
|
|
805
805
|
function createBlockContext(node, context) {
|
|
806
|
-
const scope = node === context.rootNode ||
|
|
806
|
+
const scope = node === context.rootNode ||
|
|
807
|
+
node === context.functionBody ||
|
|
808
|
+
context.generatorResume !== undefined
|
|
807
809
|
? context.scope
|
|
808
810
|
: context.scope.child();
|
|
809
811
|
const blockContext = {
|
|
@@ -816,15 +818,15 @@ function createBlockContext(node, context) {
|
|
|
816
818
|
return blockContext;
|
|
817
819
|
}
|
|
818
820
|
function predeclareBlockBindings(node, context) {
|
|
819
|
-
predeclareStatementListBindings(node.body, context);
|
|
821
|
+
predeclareStatementListBindings(node.body, context, node === context.functionBody || node === context.rootNode);
|
|
820
822
|
}
|
|
821
|
-
function predeclareStatementListBindings(statements, context) {
|
|
823
|
+
function predeclareStatementListBindings(statements, context, functionBody = false) {
|
|
822
824
|
const { scope } = context;
|
|
823
825
|
const names = new Set();
|
|
824
826
|
for (const statement of statements) {
|
|
825
827
|
if (statement.type === "FunctionDeclaration") {
|
|
826
828
|
const name = statement.id.name;
|
|
827
|
-
if (names.has(name)) {
|
|
829
|
+
if (names.has(name) && !(functionBody && scope.getOwnBindingKind(name) === "var")) {
|
|
828
830
|
throw new Error(`Cannot redeclare binding '${name}' in the same scope.`);
|
|
829
831
|
}
|
|
830
832
|
const closure = createInterpretedClosure(statement, context, evaluateNode);
|
|
@@ -838,7 +840,7 @@ function predeclareStatementListBindings(statements, context) {
|
|
|
838
840
|
throw new Error(`Cannot redeclare binding '${name}' in the same scope.`);
|
|
839
841
|
}
|
|
840
842
|
names.add(name);
|
|
841
|
-
scope.declare(name, "
|
|
843
|
+
scope.declare(name, functionBody ? "var" : "let", closure);
|
|
842
844
|
continue;
|
|
843
845
|
}
|
|
844
846
|
if (statement.type !== "VariableDeclaration" || statement.kind === "var") {
|
|
@@ -20,7 +20,7 @@ export async function bindPattern(pattern, value, target, scope, context) {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
function bindIdentifier(pattern, value, target, scope) {
|
|
23
|
-
if ("assign" in target || target.kind === "var") {
|
|
23
|
+
if ("assign" in target || (target.kind === "var" && target.initialize !== true)) {
|
|
24
24
|
if ("assign" in target) {
|
|
25
25
|
const binding = scope.lookup(pattern.name);
|
|
26
26
|
if (!binding.found) {
|
|
@@ -1,140 +1,10 @@
|
|
|
1
|
+
import { boundIdentifiers, hoistedVarDeclarations } from "../parse/bindings.js";
|
|
1
2
|
export function hoistVarDeclarations(node, scope) {
|
|
2
|
-
|
|
3
|
-
for (const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
return;
|
|
7
|
-
}
|
|
8
|
-
switch (node.type) {
|
|
9
|
-
case "VariableDeclaration":
|
|
10
|
-
case "IfStatement":
|
|
11
|
-
case "ForStatement":
|
|
12
|
-
case "ForInStatement":
|
|
13
|
-
case "ForOfStatement":
|
|
14
|
-
case "WhileStatement":
|
|
15
|
-
case "DoWhileStatement":
|
|
16
|
-
case "TryStatement":
|
|
17
|
-
case "SwitchStatement":
|
|
18
|
-
case "ExportNamedDeclaration":
|
|
19
|
-
hoistVarStatement(node, scope);
|
|
20
|
-
return;
|
|
21
|
-
default:
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
function hoistVarStatement(statement, scope) {
|
|
26
|
-
switch (statement.type) {
|
|
27
|
-
case "VariableDeclaration":
|
|
28
|
-
declareVarBindings(statement, scope);
|
|
29
|
-
return;
|
|
30
|
-
case "BlockStatement":
|
|
31
|
-
hoistVarDeclarations(statement, scope);
|
|
32
|
-
return;
|
|
33
|
-
case "IfStatement":
|
|
34
|
-
hoistVarStatement(statement.consequent, scope);
|
|
35
|
-
if (statement.alternate !== undefined) {
|
|
36
|
-
hoistVarStatement(statement.alternate, scope);
|
|
37
|
-
}
|
|
38
|
-
return;
|
|
39
|
-
case "ForStatement":
|
|
40
|
-
if (statement.init?.type === "VariableDeclaration") {
|
|
41
|
-
declareVarBindings(statement.init, scope);
|
|
42
|
-
}
|
|
43
|
-
hoistVarStatement(statement.body, scope);
|
|
44
|
-
return;
|
|
45
|
-
case "ForInStatement":
|
|
46
|
-
case "ForOfStatement":
|
|
47
|
-
if (statement.left.type === "VariableDeclaration") {
|
|
48
|
-
declareVarBindings(statement.left, scope);
|
|
49
|
-
}
|
|
50
|
-
hoistVarStatement(statement.body, scope);
|
|
51
|
-
return;
|
|
52
|
-
case "WhileStatement":
|
|
53
|
-
case "DoWhileStatement":
|
|
54
|
-
hoistVarStatement(statement.body, scope);
|
|
55
|
-
return;
|
|
56
|
-
case "TryStatement":
|
|
57
|
-
hoistVarDeclarations(statement.block, scope);
|
|
58
|
-
if (statement.handler !== undefined) {
|
|
59
|
-
hoistVarDeclarations(statement.handler.body, scope);
|
|
60
|
-
}
|
|
61
|
-
if (statement.finalizer !== undefined) {
|
|
62
|
-
hoistVarDeclarations(statement.finalizer, scope);
|
|
3
|
+
for (const declaration of hoistedVarDeclarations([node])) {
|
|
4
|
+
for (const declarator of declaration.declarations) {
|
|
5
|
+
for (const identifier of boundIdentifiers(declarator.id)) {
|
|
6
|
+
scope.declareVar(identifier.name);
|
|
63
7
|
}
|
|
64
|
-
return;
|
|
65
|
-
case "SwitchStatement":
|
|
66
|
-
for (const switchCase of statement.cases) {
|
|
67
|
-
for (const consequent of switchCase.consequent) {
|
|
68
|
-
hoistVarStatement(consequent, scope);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return;
|
|
72
|
-
case "ExportNamedDeclaration":
|
|
73
|
-
hoistVarStatement(statement.declaration, scope);
|
|
74
|
-
return;
|
|
75
|
-
case "FunctionDeclaration":
|
|
76
|
-
case "ExportDefaultDeclaration":
|
|
77
|
-
case "ImportDeclaration":
|
|
78
|
-
case "BreakStatement":
|
|
79
|
-
case "ContinueStatement":
|
|
80
|
-
case "EmptyStatement":
|
|
81
|
-
case "ExpressionStatement":
|
|
82
|
-
case "ReturnStatement":
|
|
83
|
-
case "ThrowStatement":
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
function declareVarBindings(declaration, scope) {
|
|
88
|
-
if (declaration.kind !== "var") {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
for (const declarator of declaration.declarations) {
|
|
92
|
-
for (const name of getPatternBindingNames(declarator.id)) {
|
|
93
|
-
scope.declareVar(name);
|
|
94
8
|
}
|
|
95
9
|
}
|
|
96
10
|
}
|
|
97
|
-
function getPatternBindingNames(pattern) {
|
|
98
|
-
switch (pattern.type) {
|
|
99
|
-
case "Identifier":
|
|
100
|
-
return [pattern.name];
|
|
101
|
-
case "ArrayPattern":
|
|
102
|
-
return pattern.elements.flatMap((element) => element === null || element.type === "MemberExpression"
|
|
103
|
-
? []
|
|
104
|
-
: getNestedPatternBindingNames(element));
|
|
105
|
-
case "ObjectPattern":
|
|
106
|
-
return pattern.properties.flatMap((property) => property.type === "RestElement"
|
|
107
|
-
? property.argument.type === "MemberExpression"
|
|
108
|
-
? []
|
|
109
|
-
: getNestedPatternBindingNames(property.argument)
|
|
110
|
-
: property.value.type === "MemberExpression"
|
|
111
|
-
? []
|
|
112
|
-
: getNestedPatternBindingNames(property.value));
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
function getNestedPatternBindingNames(pattern) {
|
|
116
|
-
switch (pattern.type) {
|
|
117
|
-
case "Identifier":
|
|
118
|
-
return [pattern.name];
|
|
119
|
-
case "AssignmentPattern":
|
|
120
|
-
return pattern.left.type === "MemberExpression"
|
|
121
|
-
? []
|
|
122
|
-
: getNestedPatternBindingNames(pattern.left);
|
|
123
|
-
case "RestElement":
|
|
124
|
-
return pattern.argument.type === "MemberExpression"
|
|
125
|
-
? []
|
|
126
|
-
: getNestedPatternBindingNames(pattern.argument);
|
|
127
|
-
case "ArrayPattern":
|
|
128
|
-
return pattern.elements.flatMap((element) => element === null || element.type === "MemberExpression"
|
|
129
|
-
? []
|
|
130
|
-
: getNestedPatternBindingNames(element));
|
|
131
|
-
case "ObjectPattern":
|
|
132
|
-
return pattern.properties.flatMap((property) => property.type === "RestElement"
|
|
133
|
-
? property.argument.type === "MemberExpression"
|
|
134
|
-
? []
|
|
135
|
-
: getNestedPatternBindingNames(property.argument)
|
|
136
|
-
: property.value.type === "MemberExpression"
|
|
137
|
-
? []
|
|
138
|
-
: getNestedPatternBindingNames(property.value));
|
|
139
|
-
}
|
|
140
|
-
}
|
|
@@ -3,14 +3,12 @@ import { AS003 } from "./rules/AS003.js";
|
|
|
3
3
|
import { AS004 } from "./rules/AS004.js";
|
|
4
4
|
import { AS005 } from "./rules/AS005.js";
|
|
5
5
|
import { AS006_007 } from "./rules/AS006-007.js";
|
|
6
|
-
import { AS008 } from "./rules/AS008.js";
|
|
7
6
|
import { AS009 } from "./rules/AS009.js";
|
|
8
7
|
import { AS010 } from "./rules/AS010.js";
|
|
9
8
|
import { AS011 } from "./rules/AS011.js";
|
|
10
9
|
import { AS013 } from "./rules/AS013.js";
|
|
11
10
|
import { AS015 } from "./rules/AS015.js";
|
|
12
11
|
import { AS_ASYNC_NOT_NEEDED } from "./rules/AS-async-not-needed.js";
|
|
13
|
-
import { AS_AWAIT_NON_PROMISE } from "./rules/AS-await-non-promise.js";
|
|
14
12
|
import { AS_DESTRUCTURE_NULL_DEFAULT } from "./rules/AS-destructure-null-default.js";
|
|
15
13
|
import { AS_EXPORT_IMPORT_META } from "./rules/AS-export-import-meta.js";
|
|
16
14
|
import { AS_FLOATING_PROMISE } from "./rules/AS-floating-promise.js";
|
|
@@ -35,14 +33,12 @@ const RULES = [
|
|
|
35
33
|
AS_UNUSED_IMPORT,
|
|
36
34
|
AS006_007,
|
|
37
35
|
AS_MISSING_ASYNC,
|
|
38
|
-
AS008,
|
|
39
36
|
AS009,
|
|
40
37
|
AS010,
|
|
41
38
|
AS011,
|
|
42
39
|
AS013,
|
|
43
40
|
AS015,
|
|
44
41
|
AS_IMPORT_CYCLE,
|
|
45
|
-
AS_AWAIT_NON_PROMISE,
|
|
46
42
|
AS_FLOATING_PROMISE,
|
|
47
43
|
AS_ASYNC_NOT_NEEDED,
|
|
48
44
|
AS_JSDOC_TYPE,
|
|
@@ -156,7 +156,10 @@ class AS001Scanner {
|
|
|
156
156
|
(previousToken.value === "." || previousToken.value === "?.");
|
|
157
157
|
const isPropertyKey = nextSignificantChar === ":" && !canStartStatement;
|
|
158
158
|
const isMemberName = isMemberNameToken(token, previousToken, previousPreviousToken, braceContextStack, nextSignificantChar);
|
|
159
|
-
if (canStartStatement &&
|
|
159
|
+
if (canStartStatement &&
|
|
160
|
+
nextSignificantChar === ":" &&
|
|
161
|
+
token.value !== "default" &&
|
|
162
|
+
!this.isLoopLabelStart()) {
|
|
160
163
|
this.report("label", token.start, this.positionWithinSource(token.start.offset + token.value.length));
|
|
161
164
|
}
|
|
162
165
|
else if (!isMemberProperty && !isPropertyKey && !isMemberName) {
|
|
@@ -214,9 +217,6 @@ class AS001Scanner {
|
|
|
214
217
|
case "class":
|
|
215
218
|
case "eval":
|
|
216
219
|
case "Function":
|
|
217
|
-
case "switch":
|
|
218
|
-
case "this":
|
|
219
|
-
case "var":
|
|
220
220
|
case "with":
|
|
221
221
|
this.report(token.value, token.start, token.end);
|
|
222
222
|
return;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parseModule } from "../../parse/parser.js";
|
|
2
2
|
import { KNOWN_RUNTIME_GLOBALS } from "./known-globals.js";
|
|
3
|
+
import { hoistedVarDeclarations } from "../../parse/bindings.js";
|
|
3
4
|
export function AS003(source, options = {}) {
|
|
4
5
|
return new AS003Scanner(options.filename ?? "<input>", new Set([...KNOWN_RUNTIME_GLOBALS, ...(options.allowedGlobals ?? [])])).scan(source);
|
|
5
6
|
}
|
|
@@ -52,6 +53,9 @@ class AS003Scanner {
|
|
|
52
53
|
case "TryStatement":
|
|
53
54
|
this.visitTryStatement(node);
|
|
54
55
|
return;
|
|
56
|
+
case "SwitchStatement":
|
|
57
|
+
this.visitSwitchStatement(node);
|
|
58
|
+
return;
|
|
55
59
|
case "VariableDeclaration":
|
|
56
60
|
this.visitVariableDeclaration(node);
|
|
57
61
|
return;
|
|
@@ -75,8 +79,14 @@ class AS003Scanner {
|
|
|
75
79
|
return;
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
|
-
visitBlock(node) {
|
|
79
|
-
this.
|
|
82
|
+
visitBlock(node, functionBody = false) {
|
|
83
|
+
const bindings = this.collectBlockBindings(node.body);
|
|
84
|
+
if (functionBody) {
|
|
85
|
+
for (const declaration of hoistedVarDeclarations(node.body)) {
|
|
86
|
+
bindings.push(...this.collectDeclarationBindings(declaration));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
this.withScope(bindings, () => {
|
|
80
90
|
for (const statement of node.body) {
|
|
81
91
|
this.visitStatement(statement);
|
|
82
92
|
}
|
|
@@ -93,7 +103,9 @@ class AS003Scanner {
|
|
|
93
103
|
}
|
|
94
104
|
}
|
|
95
105
|
visitForStatement(node) {
|
|
96
|
-
const bindings = node.init?.type === "VariableDeclaration"
|
|
106
|
+
const bindings = node.init?.type === "VariableDeclaration" && node.init.kind !== "var"
|
|
107
|
+
? this.collectDeclarationBindings(node.init)
|
|
108
|
+
: [];
|
|
97
109
|
this.withScope(bindings, () => {
|
|
98
110
|
if (node.init !== undefined) {
|
|
99
111
|
if (node.init.type === "VariableDeclaration") {
|
|
@@ -113,7 +125,9 @@ class AS003Scanner {
|
|
|
113
125
|
});
|
|
114
126
|
}
|
|
115
127
|
visitForOfStatement(node) {
|
|
116
|
-
const bindings = node.left.type === "VariableDeclaration"
|
|
128
|
+
const bindings = node.left.type === "VariableDeclaration" && node.left.kind !== "var"
|
|
129
|
+
? this.collectDeclarationBindings(node.left)
|
|
130
|
+
: [];
|
|
117
131
|
this.withScope(bindings, () => {
|
|
118
132
|
if (node.left.type === "VariableDeclaration") {
|
|
119
133
|
this.visitVariableDeclaration(node.left);
|
|
@@ -129,6 +143,17 @@ class AS003Scanner {
|
|
|
129
143
|
this.visitExpression(node.test);
|
|
130
144
|
this.visitStatement(node.body);
|
|
131
145
|
}
|
|
146
|
+
visitSwitchStatement(node) {
|
|
147
|
+
this.visitExpression(node.discriminant);
|
|
148
|
+
this.withScope(this.collectBlockBindings(node.cases.flatMap((switchCase) => switchCase.consequent)), () => {
|
|
149
|
+
for (const switchCase of node.cases) {
|
|
150
|
+
if (switchCase.test !== undefined)
|
|
151
|
+
this.visitExpression(switchCase.test);
|
|
152
|
+
for (const statement of switchCase.consequent)
|
|
153
|
+
this.visitStatement(statement);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
132
157
|
visitTryStatement(node) {
|
|
133
158
|
this.visitBlock(node.block);
|
|
134
159
|
if (node.handler !== undefined) {
|
|
@@ -202,8 +227,20 @@ class AS003Scanner {
|
|
|
202
227
|
this.visitAssignmentExpression(node);
|
|
203
228
|
return;
|
|
204
229
|
case "CallExpression":
|
|
230
|
+
case "NewExpression":
|
|
205
231
|
this.visitCallExpression(node);
|
|
206
232
|
return;
|
|
233
|
+
case "SequenceExpression":
|
|
234
|
+
for (const expression of node.expressions)
|
|
235
|
+
this.visitExpression(expression);
|
|
236
|
+
return;
|
|
237
|
+
case "UpdateExpression":
|
|
238
|
+
this.visitExpression(node.argument);
|
|
239
|
+
return;
|
|
240
|
+
case "TaggedTemplateExpression":
|
|
241
|
+
this.visitExpression(node.tag);
|
|
242
|
+
this.visitTemplateLiteral(node.quasi);
|
|
243
|
+
return;
|
|
207
244
|
case "TemplateLiteral":
|
|
208
245
|
this.visitTemplateLiteral(node);
|
|
209
246
|
return;
|
|
@@ -221,7 +258,7 @@ class AS003Scanner {
|
|
|
221
258
|
this.visitBindingElement(parameter);
|
|
222
259
|
}
|
|
223
260
|
if (node.body.type === "BlockStatement") {
|
|
224
|
-
this.visitBlock(node.body);
|
|
261
|
+
this.visitBlock(node.body, true);
|
|
225
262
|
}
|
|
226
263
|
else {
|
|
227
264
|
this.visitExpression(node.body);
|
|
@@ -442,6 +479,9 @@ class AS003Scanner {
|
|
|
442
479
|
}
|
|
443
480
|
collectModuleBindings(body) {
|
|
444
481
|
const bindings = [];
|
|
482
|
+
for (const declaration of hoistedVarDeclarations(body)) {
|
|
483
|
+
bindings.push(...this.collectDeclarationBindings(declaration));
|
|
484
|
+
}
|
|
445
485
|
for (const statement of body) {
|
|
446
486
|
if (statement.type === "ImportDeclaration") {
|
|
447
487
|
bindings.push(...this.collectImportBindings(statement));
|
|
@@ -451,7 +491,7 @@ class AS003Scanner {
|
|
|
451
491
|
bindings.push({ kind: "let", name: statement.id.name });
|
|
452
492
|
continue;
|
|
453
493
|
}
|
|
454
|
-
if (statement.type === "VariableDeclaration") {
|
|
494
|
+
if (statement.type === "VariableDeclaration" && statement.kind !== "var") {
|
|
455
495
|
bindings.push(...this.collectDeclarationBindings(statement));
|
|
456
496
|
continue;
|
|
457
497
|
}
|
|
@@ -467,7 +507,7 @@ class AS003Scanner {
|
|
|
467
507
|
if (statement.type === "FunctionDeclaration") {
|
|
468
508
|
bindings.push({ kind: "let", name: statement.id.name });
|
|
469
509
|
}
|
|
470
|
-
if (statement.type === "VariableDeclaration") {
|
|
510
|
+
if (statement.type === "VariableDeclaration" && statement.kind !== "var") {
|
|
471
511
|
bindings.push(...this.collectDeclarationBindings(statement));
|
|
472
512
|
}
|
|
473
513
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { parseModule } from "../../parse/parser.js";
|
|
2
|
+
import { hoistedVarDeclarations } from "../../parse/bindings.js";
|
|
2
3
|
export function AS006_007(source, options = {}) {
|
|
3
4
|
return new AS006007Scanner(options.filename ?? "<input>").scan(source);
|
|
4
5
|
}
|
|
@@ -58,6 +59,9 @@ class AS006007Scanner {
|
|
|
58
59
|
case "TryStatement":
|
|
59
60
|
this.visitTryStatement(node);
|
|
60
61
|
return;
|
|
62
|
+
case "SwitchStatement":
|
|
63
|
+
this.visitSwitchStatement(node);
|
|
64
|
+
return;
|
|
61
65
|
case "VariableDeclaration":
|
|
62
66
|
this.visitVariableDeclaration(node);
|
|
63
67
|
return;
|
|
@@ -75,8 +79,14 @@ class AS006007Scanner {
|
|
|
75
79
|
return;
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
|
-
visitBlock(node) {
|
|
79
|
-
this.
|
|
82
|
+
visitBlock(node, functionBody = false) {
|
|
83
|
+
const bindings = this.collectBlockBindings(node.body);
|
|
84
|
+
if (functionBody) {
|
|
85
|
+
for (const declaration of hoistedVarDeclarations(node.body)) {
|
|
86
|
+
bindings.push(...this.collectDeclarationBindings(declaration));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
this.withScope(bindings, () => {
|
|
80
90
|
for (const statement of node.body) {
|
|
81
91
|
this.visitStatement(statement);
|
|
82
92
|
}
|
|
@@ -90,7 +100,9 @@ class AS006007Scanner {
|
|
|
90
100
|
}
|
|
91
101
|
}
|
|
92
102
|
visitForStatement(node) {
|
|
93
|
-
const bindings = node.init?.type === "VariableDeclaration"
|
|
103
|
+
const bindings = node.init?.type === "VariableDeclaration" && node.init.kind !== "var"
|
|
104
|
+
? this.collectDeclarationBindings(node.init)
|
|
105
|
+
: [];
|
|
94
106
|
this.withScope(bindings, () => {
|
|
95
107
|
if (node.init !== undefined) {
|
|
96
108
|
if (node.init.type === "VariableDeclaration") {
|
|
@@ -110,7 +122,9 @@ class AS006007Scanner {
|
|
|
110
122
|
});
|
|
111
123
|
}
|
|
112
124
|
visitForOfStatement(node) {
|
|
113
|
-
const bindings = node.left.type === "VariableDeclaration"
|
|
125
|
+
const bindings = node.left.type === "VariableDeclaration" && node.left.kind !== "var"
|
|
126
|
+
? this.collectDeclarationBindings(node.left)
|
|
127
|
+
: [];
|
|
114
128
|
this.withScope(bindings, () => {
|
|
115
129
|
if (node.left.type === "VariableDeclaration") {
|
|
116
130
|
this.visitVariableDeclaration(node.left);
|
|
@@ -143,6 +157,18 @@ class AS006007Scanner {
|
|
|
143
157
|
this.visitBlock(node.body);
|
|
144
158
|
});
|
|
145
159
|
}
|
|
160
|
+
visitSwitchStatement(node) {
|
|
161
|
+
this.visitExpression(node.discriminant);
|
|
162
|
+
const statements = node.cases.flatMap((switchCase) => switchCase.consequent);
|
|
163
|
+
this.withScope(this.collectBlockBindings(statements), () => {
|
|
164
|
+
for (const switchCase of node.cases) {
|
|
165
|
+
if (switchCase.test !== undefined)
|
|
166
|
+
this.visitExpression(switchCase.test);
|
|
167
|
+
for (const statement of switchCase.consequent)
|
|
168
|
+
this.visitStatement(statement);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
146
172
|
visitThrowStatement(node) {
|
|
147
173
|
this.visitExpression(node.argument);
|
|
148
174
|
}
|
|
@@ -202,8 +228,20 @@ class AS006007Scanner {
|
|
|
202
228
|
this.visitAssignmentExpression(node);
|
|
203
229
|
return;
|
|
204
230
|
case "CallExpression":
|
|
231
|
+
case "NewExpression":
|
|
205
232
|
this.visitCallExpression(node);
|
|
206
233
|
return;
|
|
234
|
+
case "SequenceExpression":
|
|
235
|
+
for (const expression of node.expressions)
|
|
236
|
+
this.visitExpression(expression);
|
|
237
|
+
return;
|
|
238
|
+
case "UpdateExpression":
|
|
239
|
+
this.visitExpression(node.argument);
|
|
240
|
+
return;
|
|
241
|
+
case "TaggedTemplateExpression":
|
|
242
|
+
this.visitExpression(node.tag);
|
|
243
|
+
this.visitTemplateLiteral(node.quasi);
|
|
244
|
+
return;
|
|
207
245
|
case "TemplateLiteral":
|
|
208
246
|
this.visitTemplateLiteral(node);
|
|
209
247
|
return;
|
|
@@ -222,7 +260,7 @@ class AS006007Scanner {
|
|
|
222
260
|
this.visitBindingElement(parameter);
|
|
223
261
|
}
|
|
224
262
|
if (node.body.type === "BlockStatement") {
|
|
225
|
-
this.visitBlock(node.body);
|
|
263
|
+
this.visitBlock(node.body, true);
|
|
226
264
|
return;
|
|
227
265
|
}
|
|
228
266
|
this.visitExpression(node.body);
|
|
@@ -436,6 +474,9 @@ class AS006007Scanner {
|
|
|
436
474
|
}
|
|
437
475
|
collectModuleBindings(body) {
|
|
438
476
|
const bindings = [];
|
|
477
|
+
for (const declaration of hoistedVarDeclarations(body)) {
|
|
478
|
+
bindings.push(...this.collectDeclarationBindings(declaration));
|
|
479
|
+
}
|
|
439
480
|
for (const statement of body) {
|
|
440
481
|
if (statement.type === "ImportDeclaration") {
|
|
441
482
|
bindings.push(...this.collectImportBindings(statement));
|
|
@@ -445,7 +486,7 @@ class AS006007Scanner {
|
|
|
445
486
|
bindings.push(this.createBinding(statement.id, "let"));
|
|
446
487
|
continue;
|
|
447
488
|
}
|
|
448
|
-
if (statement.type === "VariableDeclaration") {
|
|
489
|
+
if (statement.type === "VariableDeclaration" && statement.kind !== "var") {
|
|
449
490
|
bindings.push(...this.collectDeclarationBindings(statement));
|
|
450
491
|
continue;
|
|
451
492
|
}
|
|
@@ -461,7 +502,7 @@ class AS006007Scanner {
|
|
|
461
502
|
if (statement.type === "FunctionDeclaration") {
|
|
462
503
|
bindings.push(this.createBinding(statement.id, "let"));
|
|
463
504
|
}
|
|
464
|
-
if (statement.type === "VariableDeclaration") {
|
|
505
|
+
if (statement.type === "VariableDeclaration" && statement.kind !== "var") {
|
|
465
506
|
bindings.push(...this.collectDeclarationBindings(statement));
|
|
466
507
|
}
|
|
467
508
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ArrayPattern, AssignmentPattern, Identifier, MemberExpression, ObjectPattern, ParseResult, RestElement, VariableDeclaration } from "./parser.js";
|
|
2
|
+
type BindingPattern = Identifier | ArrayPattern | ObjectPattern | AssignmentPattern | RestElement | MemberExpression;
|
|
3
|
+
export declare function boundIdentifiers(pattern: BindingPattern): Iterable<Identifier>;
|
|
4
|
+
export declare function containsParameterExpression(pattern: BindingPattern): boolean;
|
|
5
|
+
export declare function hoistedVarDeclarations(nodes: readonly ParseResult[]): Iterable<VariableDeclaration>;
|
|
6
|
+
export {};
|