@opencode/codemode 0.0.0-beta-19422 → 0.0.0-beta-19500

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.
@@ -1,11 +1,12 @@
1
1
  import { Cause, Deferred, Effect, Exit } from "effect";
2
2
  import { ToolRuntimeError, toProgram } from "../data.js";
3
3
  import { ToolReference } from "../tool-runtime.js";
4
- import { AsyncIteratorSymbol, CodeModeFunction, CodeModeGenerator, ComputedValue, GeneratorMethodReference, GeneratorReturn, IntrinsicReference, InterpreterRuntimeError, isRecord, IteratorSymbol, IteratorSymbols, OptionalShortCircuit, PromiseInstanceMethodReference, ProgramThrow, unsupportedSyntax, } from "./model.js";
4
+ import { AsyncIteratorSymbol, CodeModeGenerator, ComputedValue, GeneratorMethodReference, GeneratorReturn, IntrinsicReference, InterpreterRuntimeError, IteratorSymbol, OptionalShortCircuit, PromiseInstanceMethodReference, ProgramThrow, unsupportedSyntax, } from "./model.js";
5
5
  import { caughtErrorValue } from "./errors.js";
6
6
  import { globals } from "./globals.js";
7
7
  import { HostFunction, HostNamespace } from "./host.js";
8
8
  import { invokeIntrinsic } from "./methods.js";
9
+ import { assign, get, has, ownKeys, parseArrayIndex, ProgramArray, ProgramFunction, ProgramObject, record, remove, set, } from "./objects.js";
9
10
  import { preserveConsumerError } from "./runner.js";
10
11
  import { invokePromiseInstanceMethod, PromiseRuntime, resolvePromise, resolvePromiseValue } from "./promises.js";
11
12
  import { containsOpaqueReference, describeValue, isRuntimeReference, rejectCircularInsertion, typeofValue, } from "./references.js";
@@ -16,15 +17,22 @@ import { numberMethods } from "../stdlib/number.js";
16
17
  import { constructRegExp, regexpMethods, regexpProperties } from "../stdlib/regexp.js";
17
18
  import { stringMethods } from "../stdlib/string.js";
18
19
  import { uriArgument, urlMethods, urlProperties, urlSearchParamsMethods, urlWritableProperties } from "../stdlib/url.js";
19
- import { coerceToNumber, coerceToString, compoundOperators } from "../stdlib/value.js";
20
+ import { enumerableSource } from "../stdlib/object.js";
21
+ import { coerceToNumber, coerceToString, compoundOperators, errorBrandName } from "../stdlib/value.js";
20
22
  import { Values } from "../values.js";
21
- const MAX_ARRAY_LENGTH = 4_294_967_295;
22
- const parseArrayIndex = (key) => {
23
- const property = String(key);
24
- if (!/^(0|[1-9]\d*)$/.test(property))
25
- return undefined;
26
- const index = Number(property);
27
- return index < MAX_ARRAY_LENGTH ? index : undefined;
23
+ // What a loop does with its body's result: exit with a StatementResult, or undefined to keep iterating.
24
+ // Unlabelled break ends this loop; a label the loop does not carry propagates outward.
25
+ const loopExit = (result, labels) => {
26
+ if (result.kind === "return")
27
+ return result;
28
+ if (result.kind === "break") {
29
+ if (result.label !== undefined && !labels?.has(result.label))
30
+ return result;
31
+ return { kind: "none" };
32
+ }
33
+ if (result.kind === "continue" && result.label !== undefined && !labels?.has(result.label))
34
+ return result;
35
+ return undefined;
28
36
  };
29
37
  const calleeDescription = (callee) => {
30
38
  if (callee?.type === "Identifier")
@@ -42,6 +50,33 @@ const calleeDescription = (callee) => {
42
50
  }
43
51
  return "The called value";
44
52
  };
53
+ const constructorName = (value) => {
54
+ if (typeof value === "string")
55
+ return "String";
56
+ if (typeof value === "number")
57
+ return "Number";
58
+ if (typeof value === "boolean")
59
+ return "Boolean";
60
+ if (value instanceof ProgramArray)
61
+ return "Array";
62
+ if (value instanceof Values.Date)
63
+ return "Date";
64
+ if (value instanceof Values.RegExp)
65
+ return "RegExp";
66
+ if (value instanceof Values.Map)
67
+ return "Map";
68
+ if (value instanceof Values.Set)
69
+ return "Set";
70
+ if (value instanceof Values.URL)
71
+ return "URL";
72
+ if (value instanceof Values.URLSearchParams)
73
+ return "URLSearchParams";
74
+ if (value instanceof Values.Promise)
75
+ return "Promise";
76
+ if (!(value instanceof ProgramObject) || value instanceof ProgramFunction)
77
+ return undefined;
78
+ return errorBrandName(value) ?? "Object";
79
+ };
45
80
  const instanceofValue = (lhs, rhs, node) => {
46
81
  if (rhs instanceof HostFunction && rhs.instanceOf !== undefined)
47
82
  return rhs.instanceOf(lhs);
@@ -72,6 +107,55 @@ const collectPatternNames = (pattern, out = []) => {
72
107
  }
73
108
  return out;
74
109
  };
110
+ // `var` names declared anywhere in a function body except inside nested functions, which own theirs.
111
+ // Memoized per body: a function's var names never change, and hoisting runs on every call.
112
+ const varNames = new WeakMap();
113
+ const collectVarNames = (node, out = []) => {
114
+ if (!node)
115
+ return out;
116
+ switch (node.type) {
117
+ case "VariableDeclaration":
118
+ if (node.kind === "var")
119
+ for (const declaration of node.declarations)
120
+ collectPatternNames(declaration.id, out);
121
+ break;
122
+ case "BlockStatement":
123
+ for (const statement of node.body)
124
+ collectVarNames(statement, out);
125
+ break;
126
+ case "IfStatement":
127
+ collectVarNames(node.consequent, out);
128
+ collectVarNames(node.alternate, out);
129
+ break;
130
+ case "ForStatement":
131
+ if (node.init?.type === "VariableDeclaration")
132
+ collectVarNames(node.init, out);
133
+ collectVarNames(node.body, out);
134
+ break;
135
+ case "ForInStatement":
136
+ case "ForOfStatement":
137
+ if (node.left.type === "VariableDeclaration")
138
+ collectVarNames(node.left, out);
139
+ collectVarNames(node.body, out);
140
+ break;
141
+ case "WhileStatement":
142
+ case "DoWhileStatement":
143
+ case "LabeledStatement":
144
+ collectVarNames(node.body, out);
145
+ break;
146
+ case "SwitchStatement":
147
+ for (const item of node.cases)
148
+ for (const statement of item.consequent)
149
+ collectVarNames(statement, out);
150
+ break;
151
+ case "TryStatement":
152
+ collectVarNames(node.block, out);
153
+ collectVarNames(node.handler?.body, out);
154
+ collectVarNames(node.finalizer, out);
155
+ break;
156
+ }
157
+ return out;
158
+ };
75
159
  const loopDeclaration = (left, statement) => {
76
160
  if (left.type !== "VariableDeclaration")
77
161
  return undefined;
@@ -90,12 +174,6 @@ const isOpaqueMemberReference = (value) => value instanceof ToolReference ||
90
174
  value instanceof PromiseInstanceMethodReference ||
91
175
  value instanceof IntrinsicReference ||
92
176
  value instanceof GeneratorMethodReference;
93
- const copyIteratorSymbols = (source, target, consumed) => {
94
- for (const symbol of IteratorSymbols) {
95
- if (!consumed?.has(symbol) && Object.hasOwn(source, symbol))
96
- Reflect.set(target, symbol, Reflect.get(source, symbol));
97
- }
98
- };
99
177
  const promiseResolutionNode = { type: "PromiseResolution", start: 0, end: 0 };
100
178
  /** One program execution: the tool bridge, promise scheduler, captured logs, and the global scope built once. */
101
179
  export class Runtime {
@@ -105,8 +183,10 @@ export class Runtime {
105
183
  promises;
106
184
  logs;
107
185
  runner;
186
+ /** Built-in globals by name, unaffected by program shadowing. */
187
+ builtins;
108
188
  root;
109
- constructor(executeTool, search, toolKeys, promises, logs = []) {
189
+ constructor(executeTool, search, toolKeys, promises, logs = [], extraGlobals = () => []) {
110
190
  this.executeTool = executeTool;
111
191
  this.search = search;
112
192
  this.toolKeys = toolKeys;
@@ -121,7 +201,8 @@ export class Runtime {
121
201
  settlePromise: (promise) => this.root.settlePromise(promise),
122
202
  syncIterator: (value, node) => this.root.syncIterator(value, node),
123
203
  };
124
- for (const [name, value] of globals(this))
204
+ this.builtins = new Map([...globals(this), ...extraGlobals(this)]);
205
+ for (const [name, value] of this.builtins)
125
206
  globalScope.set(name, { mutable: false, value });
126
207
  }
127
208
  run(program) {
@@ -145,6 +226,7 @@ class Frame {
145
226
  return Effect.gen(function* () {
146
227
  self.predeclareLexical(program.body);
147
228
  self.hoistFunctions(program.body);
229
+ self.hoistVars(program.body);
148
230
  let value = undefined;
149
231
  for (const [index, statement] of program.body.entries()) {
150
232
  if (index === program.body.length - 1 && statement.type === "ExpressionStatement") {
@@ -242,8 +324,15 @@ class Frame {
242
324
  return { kind: "none" };
243
325
  }).pipe(Effect.ensuring(Effect.sync(() => self.scopes.pop())));
244
326
  }
245
- createFunction(node) {
246
- return new CodeModeFunction(node.params, node.body, this.scopes.capture(), node.async, node.generator);
327
+ createFunction(node, name = node.type === "ArrowFunctionExpression" ? "" : (node.id?.name ?? "")) {
328
+ return new ProgramFunction(name, node.params, node.body, this.scopes.capture(), node.async, node.generator);
329
+ }
330
+ // NamedEvaluation: an anonymous function definition takes the name of what it is assigned to.
331
+ evaluateNamed(node, name) {
332
+ if (node.type === "ArrowFunctionExpression" || (node.type === "FunctionExpression" && !node.id)) {
333
+ return Effect.sync(() => this.createFunction(node, name));
334
+ }
335
+ return this.evaluateExpression(node);
247
336
  }
248
337
  hoistFunctions(statements) {
249
338
  for (const node of statements) {
@@ -252,6 +341,19 @@ class Frame {
252
341
  this.scopes.declare(node.id.name, this.createFunction(node), true, node);
253
342
  }
254
343
  }
344
+ // Hoisted `var` bindings start undefined, or copy a same-named parameter. Function bodies hoist
345
+ // into their own scope above the parameters so closures in parameter defaults keep seeing outer names.
346
+ hoistVars(statements, parameters) {
347
+ const names = varNames.get(statements) ??
348
+ statements.reduce((out, statement) => collectVarNames(statement, out), []);
349
+ varNames.set(statements, names);
350
+ const scope = this.scopes.current();
351
+ for (const name of names) {
352
+ if (scope.has(name))
353
+ continue;
354
+ scope.set(name, { mutable: true, value: parameters?.get(name)?.value, initialized: true });
355
+ }
356
+ }
255
357
  predeclareLexical(statements) {
256
358
  for (const statement of statements) {
257
359
  if (statement.type !== "VariableDeclaration")
@@ -287,7 +389,9 @@ class Frame {
287
389
  self.scopes.push();
288
390
  return yield* Effect.gen(function* () {
289
391
  const cases = node.cases;
290
- self.predeclareLexical(cases.flatMap((branch) => branch.consequent));
392
+ const statements = cases.flatMap((branch) => branch.consequent);
393
+ self.predeclareLexical(statements);
394
+ self.hoistFunctions(statements);
291
395
  let defaultIndex;
292
396
  let selected;
293
397
  for (const [index, branch] of cases.entries()) {
@@ -328,20 +432,9 @@ class Frame {
328
432
  const self = this;
329
433
  return Effect.gen(function* () {
330
434
  while (yield* self.evaluateExpression(node.test)) {
331
- const result = yield* self.evaluateStatement(node.body);
332
- if (result.kind === "continue") {
333
- if (result.label !== undefined && !labels?.has(result.label))
334
- return result;
335
- continue;
336
- }
337
- if (result.kind === "break") {
338
- if (result.label !== undefined && !labels?.has(result.label))
339
- return result;
340
- return { kind: "none" };
341
- }
342
- if (result.kind === "return") {
343
- return result;
344
- }
435
+ const exit = loopExit(yield* self.evaluateStatement(node.body), labels);
436
+ if (exit !== undefined)
437
+ return exit;
345
438
  }
346
439
  return { kind: "none" };
347
440
  });
@@ -350,20 +443,9 @@ class Frame {
350
443
  const self = this;
351
444
  return Effect.gen(function* () {
352
445
  do {
353
- const result = yield* self.evaluateStatement(node.body);
354
- if (result.kind === "continue") {
355
- if (result.label !== undefined && !labels?.has(result.label))
356
- return result;
357
- continue;
358
- }
359
- if (result.kind === "break") {
360
- if (result.label !== undefined && !labels?.has(result.label))
361
- return result;
362
- return { kind: "none" };
363
- }
364
- if (result.kind === "return") {
365
- return result;
366
- }
446
+ const exit = loopExit(yield* self.evaluateStatement(node.body), labels);
447
+ if (exit !== undefined)
448
+ return exit;
367
449
  } while (yield* self.evaluateExpression(node.test));
368
450
  return { kind: "none" };
369
451
  });
@@ -398,24 +480,13 @@ class Frame {
398
480
  };
399
481
  nextIteration();
400
482
  while (testNode ? yield* self.evaluateExpression(testNode) : true) {
401
- const result = yield* self.evaluateStatement(node.body);
402
- if (result.kind === "return") {
403
- return result;
404
- }
405
- if (result.kind === "break") {
406
- if (result.label !== undefined && !labels?.has(result.label))
407
- return result;
408
- return { kind: "none" };
409
- }
410
- if (result.kind === "continue" && result.label !== undefined && !labels?.has(result.label))
411
- return result;
483
+ const exit = loopExit(yield* self.evaluateStatement(node.body), labels);
484
+ if (exit !== undefined)
485
+ return exit;
412
486
  nextIteration();
413
487
  if (updateNode) {
414
488
  yield* self.evaluateExpression(updateNode);
415
489
  }
416
- if (result.kind === "continue") {
417
- continue;
418
- }
419
490
  }
420
491
  return { kind: "none" };
421
492
  }).pipe(Effect.ensuring(Effect.sync(() => self.scopes.pop())));
@@ -446,18 +517,20 @@ class Frame {
446
517
  }
447
518
  const assignment = left.type === "VariableDeclaration" ? undefined : left;
448
519
  const evaluateBody = (value) => Effect.gen(function* () {
449
- if (declared) {
520
+ if (declared?.lexical) {
450
521
  self.scopes.push();
451
- if (declared.lexical)
452
- self.predeclarePattern(declared.pattern, declared.mutable, left);
453
- yield* self.declarePattern(declared.pattern, value, declared.mutable, left, declared.lexical);
522
+ self.predeclarePattern(declared.pattern, declared.mutable, left);
523
+ yield* self.declarePattern(declared.pattern, value, declared.mutable, left, true);
524
+ }
525
+ else if (declared) {
526
+ yield* self.assignPattern(declared.pattern, value, left);
454
527
  }
455
528
  else if (assignment) {
456
529
  yield* self.assignPattern(assignment, value, left);
457
530
  }
458
531
  return yield* self.evaluateStatement(node.body);
459
532
  }).pipe(Effect.ensuring(Effect.sync(() => {
460
- if (declared)
533
+ if (declared?.lexical)
461
534
  self.scopes.pop();
462
535
  })));
463
536
  while (true) {
@@ -475,20 +548,10 @@ class Frame {
475
548
  }
476
549
  return yield* Effect.failCause(bodyExit.cause);
477
550
  }
478
- const result = bodyExit.value;
479
- if (result.kind === "return") {
551
+ const exit = loopExit(bodyExit.value, labels);
552
+ if (exit !== undefined) {
480
553
  yield* close();
481
- return result;
482
- }
483
- if (result.kind === "break") {
484
- yield* close();
485
- if (result.label !== undefined && !labels?.has(result.label))
486
- return result;
487
- return { kind: "none" };
488
- }
489
- if (result.kind === "continue" && result.label !== undefined && !labels?.has(result.label)) {
490
- yield* close();
491
- return result;
554
+ return exit;
492
555
  }
493
556
  }
494
557
  }).pipe(Effect.ensuring(Effect.sync(() => {
@@ -512,8 +575,8 @@ class Frame {
512
575
  });
513
576
  }
514
577
  syncIterator(value, node) {
515
- const iterator = Array.isArray(value)
516
- ? value[Symbol.iterator]()
578
+ const iterator = value instanceof ProgramArray
579
+ ? value.items[Symbol.iterator]()
517
580
  : typeof value === "string"
518
581
  ? value[Symbol.iterator]()
519
582
  : value instanceof Values.Map
@@ -527,7 +590,10 @@ class Frame {
527
590
  return Effect.succeed({
528
591
  next: Effect.sync(() => {
529
592
  const step = iterator.next();
530
- return { done: Boolean(step.done), value: step.value };
593
+ return {
594
+ done: Boolean(step.done),
595
+ value: Array.isArray(step.value) ? new ProgramArray(step.value) : step.value,
596
+ };
531
597
  }),
532
598
  close: Effect.void,
533
599
  });
@@ -550,10 +616,10 @@ class Frame {
550
616
  asynchronous: value.asynchronous,
551
617
  });
552
618
  }
553
- if (!isRecord(value) || isRuntimeReference(value))
619
+ if (!(value instanceof ProgramObject))
554
620
  return Effect.undefined;
555
- const asyncMethod = allowAsync ? Reflect.get(value, AsyncIteratorSymbol) : undefined;
556
- const method = asyncMethod ?? Reflect.get(value, IteratorSymbol);
621
+ const asyncMethod = allowAsync ? get(value, AsyncIteratorSymbol) : undefined;
622
+ const method = asyncMethod ?? get(value, IteratorSymbol);
557
623
  if (method === undefined || method === null)
558
624
  return Effect.undefined;
559
625
  const self = this;
@@ -563,7 +629,7 @@ class Frame {
563
629
  iterator: object,
564
630
  next: object instanceof CodeModeGenerator
565
631
  ? new GeneratorMethodReference(object, "next")
566
- : self.requireIteratorMethod(object.next, "Iterator next", node),
632
+ : self.requireIteratorMethod(get(object, "next"), "Iterator next", node),
567
633
  asynchronous: asyncMethod !== undefined && asyncMethod !== null,
568
634
  };
569
635
  });
@@ -573,7 +639,7 @@ class Frame {
573
639
  return Effect.gen(function* () {
574
640
  if (iterator.asynchronous) {
575
641
  const object = self.requireIteratorObject(yield* self.awaitValue(yield* self.invokeCallable(iterator.next, [], node)), "Iterator next() result", node);
576
- return { done: Boolean(object.done), value: object.value };
642
+ return { done: Boolean(get(object, "done")), value: get(object, "value") };
577
643
  }
578
644
  const called = yield* Effect.exit(self.invokeCallable(iterator.next, [], node));
579
645
  if (!Exit.isSuccess(called)) {
@@ -583,7 +649,7 @@ class Frame {
583
649
  }
584
650
  const captured = yield* Effect.exit(Effect.sync(() => {
585
651
  const object = self.requireIteratorObject(called.value, "Iterator next() result", node);
586
- return { done: Boolean(object.done), value: object.value };
652
+ return { done: Boolean(get(object, "done")), value: get(object, "value") };
587
653
  }));
588
654
  if (!Exit.isSuccess(captured)) {
589
655
  if (awaiting)
@@ -601,7 +667,7 @@ class Frame {
601
667
  closeIterator(iterator, node, awaiting = true) {
602
668
  const close = iterator.iterator instanceof CodeModeGenerator
603
669
  ? new GeneratorMethodReference(iterator.iterator, "return")
604
- : iterator.iterator.return;
670
+ : get(iterator.iterator, "return");
605
671
  if (close === undefined || close === null)
606
672
  return iterator.asynchronous || !awaiting ? Effect.void : Effect.yieldNow;
607
673
  const self = this;
@@ -617,7 +683,7 @@ class Frame {
617
683
  yield* Effect.yieldNow;
618
684
  return yield* Effect.failCause(called.cause);
619
685
  }
620
- const captured = yield* Effect.exit(Effect.sync(() => self.requireIteratorObject(called.value, "Iterator return() result", node).value));
686
+ const captured = yield* Effect.exit(Effect.sync(() => get(self.requireIteratorObject(called.value, "Iterator return() result", node), "value")));
621
687
  if (!Exit.isSuccess(captured)) {
622
688
  if (awaiting)
623
689
  yield* Effect.yieldNow;
@@ -628,7 +694,7 @@ class Frame {
628
694
  });
629
695
  }
630
696
  requireIteratorObject(value, context, node) {
631
- if (isRecord(value) && !isRuntimeReference(value))
697
+ if (value instanceof ProgramObject)
632
698
  return value;
633
699
  throw new InterpreterRuntimeError(`${context} must be an object.`, node).as("TypeError");
634
700
  }
@@ -642,17 +708,13 @@ class Frame {
642
708
  return value;
643
709
  throw new InterpreterRuntimeError(`${context} must be a function.`, node).as("TypeError");
644
710
  }
645
- enumerableKeys(value) {
646
- if (value instanceof ToolReference) {
711
+ // for...in over null/undefined iterates nothing, like JS.
712
+ enumerableKeys(value, node) {
713
+ if (value instanceof ToolReference)
647
714
  return [...this.runtime.toolKeys(value.path)];
648
- }
649
- if (Array.isArray(value)) {
650
- return Object.keys(value);
651
- }
652
- if (value !== null && typeof value === "object" && !isRuntimeReference(value)) {
653
- return Object.keys(value);
654
- }
655
- return undefined;
715
+ if (value === null || value === undefined)
716
+ return [];
717
+ return ownKeys(enumerableSource("for...in", value, node)).filter((key) => typeof key === "string");
656
718
  }
657
719
  evaluateForInStatement(node, labels) {
658
720
  const left = node.left;
@@ -664,43 +726,32 @@ class Frame {
664
726
  if (declared?.lexical)
665
727
  self.predeclarePattern(declared.pattern, declared.mutable, left);
666
728
  const right = yield* self.evaluateExpression(node.right);
667
- const keys = self.enumerableKeys(right);
668
- if (keys === undefined) {
669
- throw new InterpreterRuntimeError("for...in requires a plain object, array, or tools reference. Use for...of for arrays/strings/Maps/Sets, or Object.keys(value) for a key list.", node);
670
- }
729
+ const keys = self.enumerableKeys(right, node.right);
671
730
  if (left.type !== "Identifier" && left.type !== "VariableDeclaration") {
672
731
  throw new InterpreterRuntimeError("Unsupported for...in binding.", left);
673
732
  }
674
733
  const assignmentName = left.type === "Identifier" ? left.name : undefined;
675
734
  for (const key of keys) {
676
735
  const result = yield* Effect.gen(function* () {
677
- if (declared) {
736
+ if (declared?.lexical) {
678
737
  self.scopes.push();
679
- if (declared.lexical)
680
- self.predeclarePattern(declared.pattern, declared.mutable, left);
681
- yield* self.declarePattern(declared.pattern, key, declared.mutable, left, declared.lexical);
738
+ self.predeclarePattern(declared.pattern, declared.mutable, left);
739
+ yield* self.declarePattern(declared.pattern, key, declared.mutable, left, true);
740
+ }
741
+ else if (declared) {
742
+ yield* self.assignPattern(declared.pattern, key, left);
682
743
  }
683
744
  else if (assignmentName) {
684
745
  self.scopes.set(assignmentName, key, left);
685
746
  }
686
747
  return yield* self.evaluateStatement(node.body);
687
748
  }).pipe(Effect.ensuring(Effect.sync(() => {
688
- if (declared)
749
+ if (declared?.lexical)
689
750
  self.scopes.pop();
690
751
  })));
691
- if (result.kind === "return") {
692
- return result;
693
- }
694
- if (result.kind === "break") {
695
- if (result.label !== undefined && !labels?.has(result.label))
696
- return result;
697
- return { kind: "none" };
698
- }
699
- if (result.kind === "continue") {
700
- if (result.label !== undefined && !labels?.has(result.label))
701
- return result;
702
- continue;
703
- }
752
+ const exit = loopExit(result, labels);
753
+ if (exit !== undefined)
754
+ return exit;
704
755
  }
705
756
  return { kind: "none" };
706
757
  }).pipe(Effect.ensuring(Effect.sync(() => {
@@ -781,8 +832,16 @@ class Frame {
781
832
  throw new InterpreterRuntimeError("Unsupported variable declaration shape.", declaration);
782
833
  }
783
834
  const init = declaration.init;
784
- const value = init ? yield* self.evaluateExpression(init) : undefined;
785
- yield* self.declarePattern(declaration.id, value, kind !== "const", declaration, kind !== "var");
835
+ // `var x` alone is a no-op: the binding was hoisted on function entry.
836
+ const id = declaration.id;
837
+ const evaluate = (init) => id.type === "Identifier" ? self.evaluateNamed(init, id.name) : self.evaluateExpression(init);
838
+ if (kind === "var") {
839
+ if (init)
840
+ yield* self.assignPattern(id, yield* evaluate(init), declaration);
841
+ continue;
842
+ }
843
+ const value = init ? yield* evaluate(init) : undefined;
844
+ yield* self.declarePattern(declaration.id, value, kind !== "const", declaration, true);
786
845
  }
787
846
  });
788
847
  }
@@ -798,23 +857,19 @@ class Frame {
798
857
  return;
799
858
  }
800
859
  if (pattern.type === "AssignmentPattern") {
801
- const resolved = value === undefined ? yield* self.evaluateExpression(pattern.right) : value;
860
+ const resolved = value === undefined ? yield* self.evaluateDefault(pattern) : value;
802
861
  yield* self.declarePattern(pattern.left, resolved, mutable, node, initialize);
803
862
  return;
804
863
  }
805
864
  if (pattern.type === "ObjectPattern") {
806
- if (value === null || typeof value !== "object" || isRuntimeReference(value)) {
865
+ if (!(value instanceof ProgramObject)) {
807
866
  throw new InterpreterRuntimeError(`Object destructuring requires a data object or array value, received ${describeValue(value)}.`, pattern, "InvalidDataValue");
808
867
  }
809
868
  const consumed = new Set();
810
869
  for (const property of pattern.properties) {
811
870
  if (property.type === "RestElement") {
812
- const rest = Object.create(null);
813
- for (const [key, item] of Object.entries(value)) {
814
- if (!consumed.has(key))
815
- rest[key] = item;
816
- }
817
- copyIteratorSymbols(value, rest, consumed);
871
+ const rest = new ProgramObject();
872
+ assign(rest, value, consumed);
818
873
  yield* self.declarePattern(property.argument, rest, mutable, property, initialize);
819
874
  continue;
820
875
  }
@@ -842,30 +897,25 @@ class Frame {
842
897
  return;
843
898
  }
844
899
  if (pattern.type === "AssignmentPattern") {
845
- const resolved = value === undefined ? yield* self.evaluateExpression(pattern.right) : value;
900
+ const resolved = value === undefined ? yield* self.evaluateDefault(pattern) : value;
846
901
  yield* self.assignPattern(pattern.left, resolved, node);
847
902
  return;
848
903
  }
849
904
  if (pattern.type === "ObjectPattern") {
850
- if (value === null || typeof value !== "object" || isRuntimeReference(value)) {
905
+ if (!(value instanceof ProgramObject)) {
851
906
  throw new InterpreterRuntimeError(`Object destructuring requires a data object or array value, received ${describeValue(value)}.`, pattern, "InvalidDataValue");
852
907
  }
853
- const source = value;
854
908
  const consumed = new Set();
855
909
  for (const property of pattern.properties) {
856
910
  if (property.type === "RestElement") {
857
- const rest = Object.create(null);
858
- for (const [key, item] of Object.entries(source)) {
859
- if (!consumed.has(key))
860
- rest[key] = item;
861
- }
862
- copyIteratorSymbols(source, rest, consumed);
911
+ const rest = new ProgramObject();
912
+ assign(rest, value, consumed);
863
913
  yield* self.assignPattern(property.argument, rest, property);
864
914
  continue;
865
915
  }
866
916
  const key = yield* self.destructuringPropertyKey(property);
867
917
  consumed.add(typeof key === "symbol" ? key : String(key));
868
- yield* self.assignPattern(property.value, self.destructuringPropertyValue(source, key), property);
918
+ yield* self.assignPattern(property.value, self.destructuringPropertyValue(value, key), property);
869
919
  }
870
920
  return;
871
921
  }
@@ -875,6 +925,11 @@ class Frame {
875
925
  throw new InterpreterRuntimeError(`Unsupported assignment pattern '${pattern.type}'.`, node);
876
926
  });
877
927
  }
928
+ evaluateDefault(pattern) {
929
+ return pattern.left.type === "Identifier"
930
+ ? this.evaluateNamed(pattern.right, pattern.left.name)
931
+ : this.evaluateExpression(pattern.right);
932
+ }
878
933
  destructureArrayPattern(pattern, value, consume) {
879
934
  const self = this;
880
935
  return Effect.gen(function* () {
@@ -887,7 +942,7 @@ class Frame {
887
942
  if (done) {
888
943
  if (element === null)
889
944
  continue;
890
- yield* consume(element.type === "RestElement" ? element.argument : element, element.type === "RestElement" ? [] : undefined, element);
945
+ yield* consume(element.type === "RestElement" ? element.argument : element, element.type === "RestElement" ? new ProgramArray() : undefined, element);
891
946
  if (element.type === "RestElement")
892
947
  return;
893
948
  continue;
@@ -906,7 +961,7 @@ class Frame {
906
961
  if (!done)
907
962
  rest.push(next.value);
908
963
  }
909
- yield* consume(element.argument, rest, element);
964
+ yield* consume(element.argument, new ProgramArray(rest), element);
910
965
  return;
911
966
  }
912
967
  const consumed = consume(element, step.done ? undefined : step.value, pattern);
@@ -931,14 +986,10 @@ class Frame {
931
986
  throw unsupportedSyntax(keyNode.type, keyNode);
932
987
  }
933
988
  destructuringPropertyValue(source, key) {
934
- if (!Array.isArray(source))
935
- return Reflect.get(source, key);
936
- if (key === "length")
937
- return source.length;
938
- if (typeof key === "number")
939
- return source[key];
940
- if (Object.hasOwn(source, key))
941
- return Reflect.get(source, key);
989
+ if (!(source instanceof ProgramArray))
990
+ return get(source, key);
991
+ if (has(source, key))
992
+ return get(source, key);
942
993
  if (typeof key === "string" && arrayMethods.has(key))
943
994
  return new IntrinsicReference(source, key);
944
995
  return undefined;
@@ -1013,7 +1064,7 @@ class Frame {
1013
1064
  // unsupported syntax. Built-ins like Number are real constructors in JS, so do not claim
1014
1065
  // otherwise; say `new` is unsupported for them and point at the plain call.
1015
1066
  const name = calleeDescription(node.callee);
1016
- const message = callee instanceof CodeModeFunction
1067
+ const message = callee instanceof ProgramFunction
1017
1068
  ? `${name} cannot be constructed: user-defined constructors and classes are not supported. Call it as a function that returns a plain object instead.`
1018
1069
  : callee instanceof HostFunction
1019
1070
  ? `new ${name}(...) is not supported; call ${name}(...) without new instead.`
@@ -1043,6 +1094,9 @@ class Frame {
1043
1094
  return lhs === rhs;
1044
1095
  if (operator === "!==")
1045
1096
  return lhs !== rhs;
1097
+ if (operator === "in" && rhs instanceof ProgramObject && !containsOpaqueReference(lhs)) {
1098
+ return has(rhs, lhs !== null && typeof lhs === "object" ? coerceToString(lhs) : lhs);
1099
+ }
1046
1100
  if (containsOpaqueReference(lhs) || containsOpaqueReference(rhs)) {
1047
1101
  throw new InterpreterRuntimeError("Binary operators require data values.", node, "InvalidDataValue");
1048
1102
  }
@@ -1095,11 +1149,10 @@ class Frame {
1095
1149
  case ">>>":
1096
1150
  return l >>> r;
1097
1151
  case "in":
1098
- if (rhs === null || typeof rhs !== "object") {
1152
+ if (!(rhs instanceof ProgramObject)) {
1099
1153
  throw new InterpreterRuntimeError("The 'in' operator requires a data object on the right-hand side.", node);
1100
1154
  }
1101
- // Never expose properties inherited from host prototypes.
1102
- return Object.hasOwn(rhs, coerceOperand(lhs));
1155
+ return has(rhs, coerceOperand(lhs));
1103
1156
  default:
1104
1157
  throw new InterpreterRuntimeError(`Unsupported binary operator '${operator}'.`, node);
1105
1158
  }
@@ -1178,7 +1231,7 @@ class Frame {
1178
1231
  const next = toProgram(self.applyCompoundAssignment(operator, current, rightValue, node), "Assignment result");
1179
1232
  return self.scopes.set(name, next, left);
1180
1233
  }
1181
- const rightValue = yield* self.evaluateExpression(node.right);
1234
+ const rightValue = yield* self.evaluateNamed(node.right, name);
1182
1235
  return self.scopes.set(name, rightValue, left);
1183
1236
  }
1184
1237
  if (left.type === "MemberExpression") {
@@ -1201,7 +1254,7 @@ class Frame {
1201
1254
  const current = self.scopes.get(name, left);
1202
1255
  if (!shouldAssign(current))
1203
1256
  return current;
1204
- const rightValue = yield* self.evaluateExpression(node.right);
1257
+ const rightValue = yield* self.evaluateNamed(node.right, name);
1205
1258
  return self.scopes.set(name, rightValue, left);
1206
1259
  });
1207
1260
  }
@@ -1275,7 +1328,7 @@ class Frame {
1275
1328
  }
1276
1329
  return yield* self.createToolCallPromise(callable.path, args);
1277
1330
  }
1278
- if (callable instanceof CodeModeFunction) {
1331
+ if (callable instanceof ProgramFunction) {
1279
1332
  return yield* self.invokeFunction(callable, args);
1280
1333
  }
1281
1334
  if (callable instanceof GeneratorMethodReference) {
@@ -1334,12 +1387,14 @@ class Frame {
1334
1387
  }
1335
1388
  for (const [index, parameter] of fn.parameters.entries()) {
1336
1389
  if (parameter.type === "RestElement") {
1337
- yield* invocation.declarePattern(parameter.argument, args.slice(index), true, parameter, true);
1390
+ yield* invocation.declarePattern(parameter.argument, new ProgramArray(args.slice(index)), true, parameter, true);
1338
1391
  break;
1339
1392
  }
1340
1393
  yield* invocation.declarePattern(parameter, args[index], true, parameter, true);
1341
1394
  }
1342
1395
  if (fn.body.type === "BlockStatement") {
1396
+ invocation.scopes.push();
1397
+ invocation.hoistVars(fn.body.body, paramScope);
1343
1398
  const result = yield* invocation.evaluateStatement(fn.body);
1344
1399
  return result.kind === "return" ? result.value : undefined;
1345
1400
  }
@@ -1349,12 +1404,7 @@ class Frame {
1349
1404
  return Effect.succeed(this.createGenerator(invocation, run, fn.async));
1350
1405
  if (!fn.async)
1351
1406
  return run;
1352
- // The initial yield assigns the promise before the body can self-resolve.
1353
- const box = {};
1354
- return Effect.map(this.createPromise(Effect.flatMap(run, (value) => resolvePromiseValue(invocation.runtime.runner, value, fn.body, box))), (promise) => {
1355
- box.promise = promise;
1356
- return promise;
1357
- });
1407
+ return this.runtime.promises.createWithSelf((self) => Effect.flatMap(run, (value) => resolvePromiseValue(invocation.runtime.runner, value, fn.body, self)));
1358
1408
  }
1359
1409
  createGenerator(invocation, run, asynchronous) {
1360
1410
  const state = { started: false, completed: false, draining: false, pending: [], pendingIndex: 0 };
@@ -1379,13 +1429,13 @@ class Frame {
1379
1429
  if (state.completed) {
1380
1430
  if (kind === "throw")
1381
1431
  return Effect.fail(new ProgramThrow(value));
1382
- return Effect.succeed({ value: kind === "return" ? value : undefined, done: true });
1432
+ return Effect.succeed(record({ value: kind === "return" ? value : undefined, done: true }));
1383
1433
  }
1384
1434
  if (!state.started && kind !== "next") {
1385
1435
  state.completed = true;
1386
1436
  if (kind === "throw")
1387
1437
  return Effect.fail(new ProgramThrow(value));
1388
- return Effect.succeed({ value, done: true });
1438
+ return Effect.succeed(record({ value, done: true }));
1389
1439
  }
1390
1440
  state.pending.push(request);
1391
1441
  if (state.available) {
@@ -1405,7 +1455,7 @@ class Frame {
1405
1455
  const active = state.active;
1406
1456
  state.active = undefined;
1407
1457
  if (active) {
1408
- Deferred.doneUnsafe(active.response, Exit.isSuccess(exit) ? Exit.succeed({ value: exit.value, done: true }) : exit);
1458
+ Deferred.doneUnsafe(active.response, Exit.isSuccess(exit) ? Exit.succeed(record({ value: exit.value, done: true })) : exit);
1409
1459
  }
1410
1460
  yield* invocation.completeGeneratorRequests(state, asynchronous);
1411
1461
  state.completed = true;
@@ -1429,10 +1479,10 @@ class Frame {
1429
1479
  }
1430
1480
  if (asynchronous && pending.kind === "return") {
1431
1481
  const resolved = yield* Effect.exit(self.awaitValue(pending.value));
1432
- Deferred.doneUnsafe(pending.response, Exit.isSuccess(resolved) ? Exit.succeed({ value: resolved.value, done: true }) : resolved);
1482
+ Deferred.doneUnsafe(pending.response, Exit.isSuccess(resolved) ? Exit.succeed(record({ value: resolved.value, done: true })) : resolved);
1433
1483
  continue;
1434
1484
  }
1435
- Deferred.doneUnsafe(pending.response, Exit.succeed({ value: pending.kind === "return" ? pending.value : undefined, done: true }));
1485
+ Deferred.doneUnsafe(pending.response, Exit.succeed(record({ value: pending.kind === "return" ? pending.value : undefined, done: true })));
1436
1486
  }
1437
1487
  });
1438
1488
  }
@@ -1473,7 +1523,7 @@ class Frame {
1473
1523
  const state = this.generatorState;
1474
1524
  if (!state?.active)
1475
1525
  throw new InterpreterRuntimeError("Generator has no active request.", node);
1476
- Deferred.doneUnsafe(state.active.response, Exit.succeed({ value, done: false }));
1526
+ Deferred.doneUnsafe(state.active.response, Exit.succeed(record({ value, done: false })));
1477
1527
  state.active = undefined;
1478
1528
  return Effect.flatMap(this.takeGeneratorRequest(state), (request) => {
1479
1529
  state.active = request;
@@ -1489,7 +1539,7 @@ class Frame {
1489
1539
  delegateYield(value, node) {
1490
1540
  const self = this;
1491
1541
  return Effect.gen(function* () {
1492
- if (Array.isArray(value) ||
1542
+ if (value instanceof ProgramArray ||
1493
1543
  typeof value === "string" ||
1494
1544
  value instanceof Values.Map ||
1495
1545
  value instanceof Values.Set ||
@@ -1526,7 +1576,7 @@ class Frame {
1526
1576
  ? iterator.next
1527
1577
  : iterator.iterator instanceof CodeModeGenerator
1528
1578
  ? new GeneratorMethodReference(iterator.iterator, kind)
1529
- : iterator.iterator[kind];
1579
+ : get(iterator.iterator, kind);
1530
1580
  if (method === undefined || method === null) {
1531
1581
  if (kind === "return")
1532
1582
  return yield* Effect.fail(new GeneratorReturn(input));
@@ -1535,10 +1585,10 @@ class Frame {
1535
1585
  }
1536
1586
  const called = yield* self.invokeCallable(self.requireIteratorMethod(method, `Iterator ${kind}`, node), [input], node);
1537
1587
  const result = self.requireIteratorObject(iterator.asynchronous ? yield* self.awaitValue(called) : called, `Iterator ${kind}() result`, node);
1538
- const done = Boolean(result.done);
1588
+ const done = Boolean(get(result, "done"));
1539
1589
  const resultValue = self.generatorAsync && !iterator.asynchronous
1540
- ? yield* self.awaitAsyncFromSyncValue(iterator, result.value, node, kind !== "return" && !done)
1541
- : result.value;
1590
+ ? yield* self.awaitAsyncFromSyncValue(iterator, get(result, "value"), node, kind !== "return" && !done)
1591
+ : get(result, "value");
1542
1592
  if (done) {
1543
1593
  if (kind === "return")
1544
1594
  return yield* Effect.fail(new GeneratorReturn(resultValue));
@@ -1560,20 +1610,15 @@ class Frame {
1560
1610
  });
1561
1611
  }
1562
1612
  evaluateObjectExpression(node) {
1563
- const objectValue = Object.create(null);
1613
+ const objectValue = new ProgramObject();
1564
1614
  const self = this;
1565
1615
  return Effect.gen(function* () {
1566
1616
  for (const property of node.properties) {
1567
1617
  if (property.type === "SpreadElement") {
1568
1618
  const spread = yield* self.evaluateExpression(property.argument);
1569
- if (spread === null || spread === undefined || Values.isValue(spread))
1619
+ if (spread === null || spread === undefined)
1570
1620
  continue;
1571
- if (typeof spread !== "object" || Array.isArray(spread) || isRuntimeReference(spread)) {
1572
- throw new InterpreterRuntimeError(`Object spread requires a data object, received ${describeValue(spread)}.`, property, "InvalidDataValue");
1573
- }
1574
- for (const [key, value] of Object.entries(spread))
1575
- objectValue[key] = value;
1576
- copyIteratorSymbols(spread, objectValue);
1621
+ assign(objectValue, enumerableSource("Object spread", spread, property));
1577
1622
  continue;
1578
1623
  }
1579
1624
  if (property.kind !== "init") {
@@ -1593,7 +1638,12 @@ class Frame {
1593
1638
  else {
1594
1639
  throw new InterpreterRuntimeError("Unsupported object property key shape.", keyNode);
1595
1640
  }
1596
- Reflect.set(objectValue, key, yield* self.evaluateExpression(property.value));
1641
+ const name = key === IteratorSymbol
1642
+ ? "[Symbol.iterator]"
1643
+ : key === AsyncIteratorSymbol
1644
+ ? "[Symbol.asyncIterator]"
1645
+ : String(key);
1646
+ set(objectValue, key, yield* self.evaluateNamed(property.value, name));
1597
1647
  }
1598
1648
  return objectValue;
1599
1649
  });
@@ -1624,7 +1674,7 @@ class Frame {
1624
1674
  values.push(yield* self.evaluateExpression(element));
1625
1675
  }
1626
1676
  }
1627
- return values;
1677
+ return new ProgramArray(values);
1628
1678
  });
1629
1679
  }
1630
1680
  evaluateTemplateLiteral(node) {
@@ -1686,6 +1736,14 @@ class Frame {
1686
1736
  // Unknown static members read as undefined so feature detection works like native JS.
1687
1737
  return new ComputedValue(objectValue.member(key, propertyNode));
1688
1738
  }
1739
+ // Values have no prototype chain, so `.constructor` resolves to the owning built-in directly.
1740
+ if (operation === "read" &&
1741
+ key === "constructor" &&
1742
+ !(objectValue instanceof ProgramObject && has(objectValue, key))) {
1743
+ const name = constructorName(objectValue);
1744
+ if (name !== undefined)
1745
+ return new ComputedValue(self.runtime.builtins.get(name));
1746
+ }
1689
1747
  if (typeof objectValue === "string") {
1690
1748
  if (key === "length")
1691
1749
  return new ComputedValue(objectValue.length);
@@ -1765,28 +1823,16 @@ class Frame {
1765
1823
  }
1766
1824
  return new ComputedValue(undefined);
1767
1825
  }
1826
+ if (objectValue instanceof ProgramObject)
1827
+ return { target: objectValue, key };
1768
1828
  if (isRuntimeReference(objectValue)) {
1769
1829
  throw new InterpreterRuntimeError(`Cannot read properties of ${describeValue(objectValue)}; only data values expose properties.`, objectNode, "InvalidDataValue");
1770
1830
  }
1771
- if (typeof objectValue !== "object" || objectValue === null) {
1772
- throw new InterpreterRuntimeError("Cannot access a property on a non-object value.", objectNode);
1773
- }
1774
- if (Array.isArray(objectValue)) {
1775
- if (operation === "delete")
1776
- return { target: objectValue, key };
1777
- const index = typeof key === "symbol" ? undefined : parseArrayIndex(key);
1778
- if (key !== "length" && !(typeof key === "string" && arrayMethods.has(key)) && index === undefined) {
1779
- if (typeof key === "string" && Object.hasOwn(objectValue, key)) {
1780
- return new ComputedValue(objectValue[key]);
1781
- }
1782
- return new ComputedValue(undefined);
1783
- }
1784
- return { target: objectValue, key: index ?? key };
1785
- }
1786
- return { target: objectValue, key };
1831
+ throw new InterpreterRuntimeError("Cannot access a property on a non-object value.", objectNode);
1787
1832
  });
1788
1833
  }
1789
1834
  readMember(node) {
1835
+ const self = this;
1790
1836
  return Effect.map(this.getMemberReference(node), (reference) => {
1791
1837
  if (reference === OptionalShortCircuit)
1792
1838
  return OptionalShortCircuit;
@@ -1794,19 +1840,15 @@ class Frame {
1794
1840
  return reference.value;
1795
1841
  if (reference === undefined || isOpaqueMemberReference(reference))
1796
1842
  return reference;
1797
- if (Array.isArray(reference.target)) {
1798
- if (reference.key === "length")
1799
- return reference.target.length;
1800
- if (typeof reference.key === "string")
1801
- return new IntrinsicReference(reference.target, reference.key);
1802
- return Reflect.get(reference.target, reference.key);
1803
- }
1804
- if (reference.target instanceof Values.RegExp)
1805
- return reference.target.lastIndex;
1806
- if (reference.target instanceof Values.URL) {
1807
- return Reflect.get(reference.target.url, reference.key);
1808
- }
1809
- return Reflect.get(reference.target, reference.key);
1843
+ const value = self.readReferenceValue(reference, reference.key);
1844
+ if (value === undefined &&
1845
+ reference.target instanceof ProgramArray &&
1846
+ typeof reference.key === "string" &&
1847
+ arrayMethods.has(reference.key) &&
1848
+ !has(reference.target, reference.key)) {
1849
+ return new IntrinsicReference(reference.target, reference.key);
1850
+ }
1851
+ return value;
1810
1852
  });
1811
1853
  }
1812
1854
  writeMember(node, value) {
@@ -1829,27 +1871,20 @@ class Frame {
1829
1871
  if (reference.target instanceof Values.RegExp) {
1830
1872
  return Reflect.deleteProperty(reference.target.regex, reference.key);
1831
1873
  }
1832
- return Reflect.deleteProperty(reference.target, reference.key);
1874
+ return remove(reference.target, reference.key);
1833
1875
  });
1834
1876
  }
1835
1877
  // Resolve side-effecting object and key expressions exactly once.
1836
1878
  modifyMember(node, compute) {
1837
1879
  const self = this;
1838
1880
  return Effect.gen(function* () {
1839
- const reference = yield* self.getMemberReference(node);
1881
+ const reference = yield* self.getMemberReference(node, "write");
1840
1882
  if (reference === OptionalShortCircuit ||
1841
1883
  reference instanceof ComputedValue ||
1842
1884
  reference === undefined ||
1843
1885
  isOpaqueMemberReference(reference)) {
1844
1886
  throw new InterpreterRuntimeError("Only data fields may be assigned.", node);
1845
1887
  }
1846
- if (Array.isArray(reference.target)) {
1847
- if (reference.key === "length")
1848
- throw new InterpreterRuntimeError("Array length cannot be assigned.", node);
1849
- if (typeof reference.key === "string" && arrayMethods.has(reference.key)) {
1850
- throw new InterpreterRuntimeError("Array methods cannot be assigned.", node);
1851
- }
1852
- }
1853
1888
  const key = reference.key;
1854
1889
  const { write, next, result } = yield* compute(self.readReferenceValue(reference, key));
1855
1890
  if (write)
@@ -1863,18 +1898,9 @@ class Frame {
1863
1898
  }
1864
1899
  if (reference.target instanceof Values.RegExp)
1865
1900
  return reference.target.lastIndex;
1866
- return Reflect.get(reference.target, key);
1901
+ return get(reference.target, key);
1867
1902
  }
1868
1903
  assignToReference(reference, key, next, node) {
1869
- if (Array.isArray(reference.target)) {
1870
- const target = reference.target;
1871
- if (typeof key !== "number" || parseArrayIndex(key) === undefined) {
1872
- throw new InterpreterRuntimeError("Array assignment index must be a valid array index.", node, "InvalidDataValue");
1873
- }
1874
- rejectCircularInsertion(target, next, "Array assignment result", node);
1875
- target[key] = next;
1876
- return;
1877
- }
1878
1904
  if (reference.target instanceof Values.URL) {
1879
1905
  const property = key;
1880
1906
  if (!urlWritableProperties.has(property)) {
@@ -1896,8 +1922,12 @@ class Frame {
1896
1922
  return;
1897
1923
  }
1898
1924
  const target = reference.target;
1899
- rejectCircularInsertion(target, next, "Object assignment result", node);
1900
- Reflect.set(target, key, next);
1925
+ rejectCircularInsertion(target, next, target instanceof ProgramArray ? "Array assignment result" : "Object assignment result", node);
1926
+ if (set(target, key, next))
1927
+ return;
1928
+ if (target instanceof ProgramArray)
1929
+ throw new InterpreterRuntimeError("Invalid array length", node).as("RangeError");
1930
+ throw new InterpreterRuntimeError(`Cannot assign to read only property '${String(key)}'.`, node).as("TypeError");
1901
1931
  }
1902
1932
  toPropertyKey(value, node) {
1903
1933
  if (typeof value === "string" || typeof value === "number") {