poe-code 5.0.4 → 7.0.0

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.
Files changed (52) hide show
  1. package/dist/index.js +3457 -3542
  2. package/dist/index.js.map +4 -4
  3. package/dist/metafile.json +1 -1
  4. package/package.json +1 -1
  5. package/packages/safejs/dist/interp/async.d.ts +2 -1
  6. package/packages/safejs/dist/interp/async.js +56 -33
  7. package/packages/safejs/dist/interp/cancel.d.ts +7 -2
  8. package/packages/safejs/dist/interp/cancel.js +128 -92
  9. package/packages/safejs/dist/interp/exceptions.js +7 -3
  10. package/packages/safejs/dist/interp/globals/collections.js +2 -0
  11. package/packages/safejs/dist/interp/globals/console-json.js +4 -0
  12. package/packages/safejs/dist/interp/globals/error.js +1 -5
  13. package/packages/safejs/dist/interp/globals/math.js +2 -4
  14. package/packages/safejs/dist/interp/globals/misc.js +5 -0
  15. package/packages/safejs/dist/interp/globals/object-array.js +25 -0
  16. package/packages/safejs/dist/interp/globals/regex.js +1 -1
  17. package/packages/safejs/dist/interp/host-bridge.js +8 -5
  18. package/packages/safejs/dist/interp/interpreter.js +33 -37
  19. package/packages/safejs/dist/interp/iteration.d.ts +3 -3
  20. package/packages/safejs/dist/interp/iteration.js +20 -9
  21. package/packages/safejs/dist/interp/methods/array.js +1 -0
  22. package/packages/safejs/dist/interp/methods/function.d.ts +1 -1
  23. package/packages/safejs/dist/interp/methods/function.js +17 -1
  24. package/packages/safejs/dist/interp/methods/generator.js +1 -0
  25. package/packages/safejs/dist/interp/methods/map.js +1 -0
  26. package/packages/safejs/dist/interp/methods/number.js +1 -0
  27. package/packages/safejs/dist/interp/methods/regex.js +1 -0
  28. package/packages/safejs/dist/interp/methods/set.js +1 -0
  29. package/packages/safejs/dist/interp/methods/string.js +1 -0
  30. package/packages/safejs/dist/interp/patterns.d.ts +1 -0
  31. package/packages/safejs/dist/interp/patterns.js +1 -1
  32. package/packages/safejs/dist/interp/promise-tracker.d.ts +12 -2
  33. package/packages/safejs/dist/interp/promise-tracker.js +65 -7
  34. package/packages/safejs/dist/interp/promise.d.ts +7 -5
  35. package/packages/safejs/dist/interp/promise.js +531 -148
  36. package/packages/safejs/dist/interp/values.d.ts +6 -0
  37. package/packages/safejs/dist/interp/values.js +25 -5
  38. package/packages/safejs/dist/interp/var-hoist.js +5 -135
  39. package/packages/safejs/dist/lint/index.js +0 -4
  40. package/packages/safejs/dist/lint/rules/AS001.js +4 -9
  41. package/packages/safejs/dist/lint/rules/AS003.js +47 -7
  42. package/packages/safejs/dist/lint/rules/AS006-007.js +48 -7
  43. package/packages/safejs/dist/parse/bindings.d.ts +6 -0
  44. package/packages/safejs/dist/parse/bindings.js +90 -0
  45. package/packages/safejs/dist/parse/parser.js +70 -37
  46. package/packages/safejs/dist/snapshot/dump-format.d.ts +1 -1
  47. package/packages/safejs/dist/snapshot/dump-format.js +1 -1
  48. package/packages/safejs/dist/snapshot/replay-data.js +3 -0
  49. package/packages/safejs/dist/lint/rules/AS-await-non-promise.d.ts +0 -13
  50. package/packages/safejs/dist/lint/rules/AS-await-non-promise.js +0 -301
  51. package/packages/safejs/dist/lint/rules/AS008.d.ts +0 -13
  52. package/packages/safejs/dist/lint/rules/AS008.js +0 -386
@@ -52,6 +52,9 @@ export type SandboxCallContext = {
52
52
  };
53
53
  export type SandboxClosure = {
54
54
  readonly async?: true;
55
+ readonly sandbox?: true;
56
+ readonly boundTarget?: SandboxClosure;
57
+ readonly cancellationSignal?: AbortSignal;
55
58
  readonly kind: "fn";
56
59
  readonly name?: string;
57
60
  readonly properties?: SandboxObject;
@@ -82,6 +85,9 @@ type CopyFromSandboxOptions = {
82
85
  };
83
86
  export declare function createSandboxClosure(input: {
84
87
  async?: boolean;
88
+ sandbox?: boolean;
89
+ boundTarget?: SandboxClosure;
90
+ cancellationSignal?: AbortSignal;
85
91
  call: (args: readonly SandboxValue[], context?: SandboxCallContext) => SandboxValue | Promise<SandboxValue>;
86
92
  construct?: (args: readonly SandboxValue[], context?: SandboxCallContext) => SandboxValue | Promise<SandboxValue>;
87
93
  name?: string;
@@ -2,6 +2,7 @@ import { bindOtelSpan, getBoundOtelSpan } from "../observability/otel.js";
2
2
  import { SandboxError } from "./budget.js";
3
3
  import { observeSandboxPromise, trackSandboxPromise } from "./promise-tracker.js";
4
4
  import { promiseReplayContext } from "./promise-replay.js";
5
+ import { invokeCancelableClosure, readPromiseCancellation, registerPromiseCancellation } from "./cancel.js";
5
6
  import { parseRegex } from "./regex/parse.js";
6
7
  import { assertSandboxDataDepth } from "../graph-depth.js";
7
8
  import { copySandboxArgumentProperties, createSandboxArguments, getSandboxArgumentEntries, isSandboxArguments } from "./arguments.js";
@@ -17,15 +18,30 @@ const sandboxRetainedValues = Symbol("SandboxRetainedValues");
17
18
  export function createSandboxClosure(input) {
18
19
  const closure = {
19
20
  kind: "fn",
20
- call: input.call,
21
+ call: (args, context) => invokeCancelableClosure(closure, input.call, args, context),
21
22
  name: input.name,
22
- ...(input.construct === undefined ? {} : { construct: input.construct }),
23
+ ...(input.construct === undefined
24
+ ? {}
25
+ : {
26
+ construct: (args, context) => invokeCancelableClosure(closure, input.construct, args, context, true)
27
+ }),
23
28
  ...(input.async === true ? { async: true } : {})
24
29
  };
25
30
  Object.defineProperty(closure, sandboxClosureBrand, {
26
31
  enumerable: false,
27
32
  value: true
28
33
  });
34
+ if (input.sandbox === true) {
35
+ Object.defineProperty(closure, "sandbox", { value: true });
36
+ }
37
+ if (input.boundTarget !== undefined) {
38
+ Object.defineProperty(closure, "boundTarget", { value: input.boundTarget });
39
+ }
40
+ if (input.cancellationSignal !== undefined) {
41
+ Object.defineProperty(closure, "cancellationSignal", {
42
+ value: input.cancellationSignal
43
+ });
44
+ }
29
45
  if (input.properties !== undefined) {
30
46
  Object.defineProperty(closure, "properties", {
31
47
  enumerable: false,
@@ -40,11 +56,14 @@ export function createSandboxClosure(input) {
40
56
  return Object.freeze(closure);
41
57
  }
42
58
  export function createSandboxPromise(promise, metadata = {}) {
59
+ const original = metadata.trackReplay === false
60
+ ? promise
61
+ : (promiseReplayContext.getStore()?.track(promise) ?? promise);
43
62
  const sandboxPromise = {
44
63
  kind: "promise",
45
- promise: metadata.trackReplay === false
46
- ? promise
47
- : (promiseReplayContext.getStore()?.track(promise) ?? promise)
64
+ get promise() {
65
+ return readPromiseCancellation(sandboxPromise, original);
66
+ }
48
67
  };
49
68
  Object.defineProperty(sandboxPromise, sandboxPromiseBrand, {
50
69
  enumerable: false,
@@ -70,6 +89,7 @@ export function createSandboxPromise(promise, metadata = {}) {
70
89
  });
71
90
  }
72
91
  trackSandboxPromise(sandboxPromise);
92
+ registerPromiseCancellation(sandboxPromise);
73
93
  return Object.freeze(sandboxPromise);
74
94
  }
75
95
  export function createSandboxGenerator(channel, metadata = undefined) {
@@ -1,140 +1,10 @@
1
+ import { boundIdentifiers, hoistedVarDeclarations } from "../parse/bindings.js";
1
2
  export function hoistVarDeclarations(node, scope) {
2
- if (node.type === "BlockStatement") {
3
- for (const statement of node.body) {
4
- hoistVarStatement(statement, scope);
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 && nextSignificantChar === ":" && !this.isLoopLabelStart()) {
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,17 +217,9 @@ 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;
223
- case "new":
224
- if (!this.source.slice(this.skipTriviaFrom(token.end.offset)).startsWith("RegExp")) {
225
- this.report(token.value, token.start, token.end);
226
- }
227
- return;
228
223
  default:
229
224
  return;
230
225
  }
@@ -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.withScope(this.collectBlockBindings(node.body), () => {
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" ? this.collectDeclarationBindings(node.init) : [];
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" ? this.collectDeclarationBindings(node.left) : [];
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.withScope(this.collectBlockBindings(node.body), () => {
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" ? this.collectDeclarationBindings(node.init) : [];
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" ? this.collectDeclarationBindings(node.left) : [];
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 {};
@@ -0,0 +1,90 @@
1
+ export function* boundIdentifiers(pattern) {
2
+ switch (pattern.type) {
3
+ case "Identifier":
4
+ yield pattern;
5
+ return;
6
+ case "AssignmentPattern":
7
+ yield* boundIdentifiers(pattern.left);
8
+ return;
9
+ case "RestElement":
10
+ yield* boundIdentifiers(pattern.argument);
11
+ return;
12
+ case "ArrayPattern":
13
+ for (const element of pattern.elements) {
14
+ if (element !== null)
15
+ yield* boundIdentifiers(element);
16
+ }
17
+ return;
18
+ case "ObjectPattern":
19
+ for (const property of pattern.properties) {
20
+ yield* boundIdentifiers(property.type === "RestElement" ? property : property.value);
21
+ }
22
+ return;
23
+ case "MemberExpression":
24
+ return;
25
+ }
26
+ }
27
+ export function containsParameterExpression(pattern) {
28
+ switch (pattern.type) {
29
+ case "AssignmentPattern":
30
+ case "MemberExpression":
31
+ return true;
32
+ case "RestElement":
33
+ return containsParameterExpression(pattern.argument);
34
+ case "ArrayPattern":
35
+ return pattern.elements.some((element) => element !== null && containsParameterExpression(element));
36
+ case "ObjectPattern":
37
+ return pattern.properties.some((property) => property.type === "RestElement"
38
+ ? containsParameterExpression(property.argument)
39
+ : property.computed || containsParameterExpression(property.value));
40
+ case "Identifier":
41
+ return false;
42
+ }
43
+ }
44
+ export function* hoistedVarDeclarations(nodes) {
45
+ for (const node of nodes) {
46
+ switch (node.type) {
47
+ case "VariableDeclaration":
48
+ if (node.kind === "var")
49
+ yield node;
50
+ break;
51
+ case "BlockStatement":
52
+ yield* hoistedVarDeclarations(node.body);
53
+ break;
54
+ case "IfStatement":
55
+ yield* hoistedVarDeclarations([node.consequent]);
56
+ if (node.alternate !== undefined)
57
+ yield* hoistedVarDeclarations([node.alternate]);
58
+ break;
59
+ case "ForStatement":
60
+ if (node.init?.type === "VariableDeclaration")
61
+ yield* hoistedVarDeclarations([node.init]);
62
+ yield* hoistedVarDeclarations([node.body]);
63
+ break;
64
+ case "ForInStatement":
65
+ case "ForOfStatement":
66
+ if (node.left.type === "VariableDeclaration")
67
+ yield* hoistedVarDeclarations([node.left]);
68
+ yield* hoistedVarDeclarations([node.body]);
69
+ break;
70
+ case "WhileStatement":
71
+ case "DoWhileStatement":
72
+ yield* hoistedVarDeclarations([node.body]);
73
+ break;
74
+ case "TryStatement":
75
+ yield* hoistedVarDeclarations([node.block]);
76
+ if (node.handler !== undefined)
77
+ yield* hoistedVarDeclarations([node.handler.body]);
78
+ if (node.finalizer !== undefined)
79
+ yield* hoistedVarDeclarations([node.finalizer]);
80
+ break;
81
+ case "SwitchStatement":
82
+ for (const switchCase of node.cases)
83
+ yield* hoistedVarDeclarations(switchCase.consequent);
84
+ break;
85
+ case "ExportNamedDeclaration":
86
+ yield* hoistedVarDeclarations([node.declaration]);
87
+ break;
88
+ }
89
+ }
90
+ }