@swf2es/runtime 0.1.15 → 0.1.17

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 (84) hide show
  1. package/dist/avm2/amf.d.ts +1 -1
  2. package/dist/avm2/amf.d.ts.map +1 -1
  3. package/dist/avm2/amf.js +4 -3
  4. package/dist/avm2/amf.js.map +1 -1
  5. package/dist/avm2/as3-types.d.ts +25 -0
  6. package/dist/avm2/as3-types.d.ts.map +1 -0
  7. package/dist/avm2/as3-types.js +9 -0
  8. package/dist/avm2/as3-types.js.map +1 -0
  9. package/dist/avm2/builtin-types.generated.d.ts +553 -0
  10. package/dist/avm2/builtin-types.generated.d.ts.map +1 -0
  11. package/dist/avm2/builtin-types.generated.js +11 -0
  12. package/dist/avm2/builtin-types.generated.js.map +1 -0
  13. package/dist/avm2/cache-fill.d.ts +4 -1
  14. package/dist/avm2/cache-fill.d.ts.map +1 -1
  15. package/dist/avm2/cache-fill.js +5 -6
  16. package/dist/avm2/cache-fill.js.map +1 -1
  17. package/dist/avm2/classes.d.ts.map +1 -1
  18. package/dist/avm2/classes.js +10 -3
  19. package/dist/avm2/classes.js.map +1 -1
  20. package/dist/avm2/descriptors.d.ts +6 -1
  21. package/dist/avm2/descriptors.d.ts.map +1 -1
  22. package/dist/avm2/descriptors.js.map +1 -1
  23. package/dist/avm2/errors.d.ts +14 -0
  24. package/dist/avm2/errors.d.ts.map +1 -1
  25. package/dist/avm2/errors.js +20 -1
  26. package/dist/avm2/errors.js.map +1 -1
  27. package/dist/avm2/index.d.ts +4 -0
  28. package/dist/avm2/index.d.ts.map +1 -1
  29. package/dist/avm2/index.js +3 -0
  30. package/dist/avm2/index.js.map +1 -1
  31. package/dist/avm2/names.d.ts +11 -0
  32. package/dist/avm2/names.d.ts.map +1 -1
  33. package/dist/avm2/names.js +12 -0
  34. package/dist/avm2/names.js.map +1 -1
  35. package/dist/avm2/natives/array.js +1 -1
  36. package/dist/avm2/natives/array.js.map +1 -1
  37. package/dist/avm2/natives/bytearray.d.ts.map +1 -1
  38. package/dist/avm2/natives/bytearray.js +6 -11
  39. package/dist/avm2/natives/bytearray.js.map +1 -1
  40. package/dist/avm2/natives/describe.js +6 -1
  41. package/dist/avm2/natives/describe.js.map +1 -1
  42. package/dist/avm2/natives/object.d.ts.map +1 -1
  43. package/dist/avm2/natives/object.js +5 -2
  44. package/dist/avm2/natives/object.js.map +1 -1
  45. package/dist/avm2/natives/proxy.d.ts.map +1 -1
  46. package/dist/avm2/natives/proxy.js +9 -4
  47. package/dist/avm2/natives/proxy.js.map +1 -1
  48. package/dist/avm2/natives/vector.js +1 -1
  49. package/dist/avm2/natives/vector.js.map +1 -1
  50. package/dist/avm2/options.d.ts +5 -0
  51. package/dist/avm2/options.d.ts.map +1 -1
  52. package/dist/avm2/options.js.map +1 -1
  53. package/dist/avm2/property-cache.d.ts +17 -13
  54. package/dist/avm2/property-cache.d.ts.map +1 -1
  55. package/dist/avm2/property-cache.js +50 -10
  56. package/dist/avm2/property-cache.js.map +1 -1
  57. package/dist/avm2/runtime.d.ts +101 -1
  58. package/dist/avm2/runtime.d.ts.map +1 -1
  59. package/dist/avm2/runtime.js +351 -45
  60. package/dist/avm2/runtime.js.map +1 -1
  61. package/dist/avm2/traits.d.ts +13 -0
  62. package/dist/avm2/traits.d.ts.map +1 -1
  63. package/dist/avm2/traits.js +23 -0
  64. package/dist/avm2/traits.js.map +1 -1
  65. package/package.json +2 -2
  66. package/src/avm2/amf.ts +4 -3
  67. package/src/avm2/as3-types.ts +41 -0
  68. package/src/avm2/builtin-types.generated.ts +619 -0
  69. package/src/avm2/cache-fill.ts +5 -7
  70. package/src/avm2/classes.ts +10 -3
  71. package/src/avm2/descriptors.ts +6 -2
  72. package/src/avm2/errors.ts +21 -1
  73. package/src/avm2/index.ts +6 -0
  74. package/src/avm2/names.ts +13 -0
  75. package/src/avm2/natives/array.ts +1 -1
  76. package/src/avm2/natives/bytearray.ts +6 -14
  77. package/src/avm2/natives/describe.ts +8 -1
  78. package/src/avm2/natives/object.ts +5 -2
  79. package/src/avm2/natives/proxy.ts +20 -4
  80. package/src/avm2/natives/vector.ts +1 -1
  81. package/src/avm2/options.ts +5 -0
  82. package/src/avm2/property-cache.ts +57 -11
  83. package/src/avm2/runtime.ts +407 -53
  84. package/src/avm2/traits.ts +27 -1
@@ -21,18 +21,18 @@ import { NAMED } from "./dictionary-keys.js";
21
21
  import { compileUnit, Domain, definitionNames, findScript } from "./domain.js";
22
22
  import { isViewRangeError, SharedMemoryView } from "./domain-memory.js";
23
23
  import { continues, dictionaryAt, elementsAt, enumerated, INT_ATOM_LIMIT, nextIndex, ownNames, pairIndex, pairOf, walkless, } from "./enumeration.js";
24
- import { AsError, describe, errorMessage, stackOf } from "./errors.js";
24
+ import { AsError, describe, detailOf, errorMessage, stackOf, UnsupportedError } from "./errors.js";
25
25
  import { FloatValue, floatValue } from "./float.js";
26
26
  import { hookedBinding } from "./hooks.js";
27
27
  import { installMethod, nativeFactory, noBodyFactory, unverifiedFactory, verifyErrorFactory, } from "./method-factories.js";
28
28
  import { Modules } from "./modules.js";
29
- import { arrayIndex, CONSTANT_Multiname, CONSTANT_MultinameA, CONSTANT_MultinameL, CONSTANT_MultinameLA, CONSTANT_Qname, CONSTANT_RTQname, CONSTANT_RTQnameA, CONSTANT_RTQnameL, CONSTANT_RTQnameLA, Multiname, Namespace, NS_Private, NS_Public, namespace, prefixedNamespace, prefixOf, publicNs, publicQname, qname, TypeName, } from "./names.js";
29
+ import { arrayIndex, CONSTANT_Multiname, CONSTANT_MultinameA, CONSTANT_MultinameL, CONSTANT_MultinameLA, CONSTANT_Qname, CONSTANT_RTQname, CONSTANT_RTQnameA, CONSTANT_RTQnameL, CONSTANT_RTQnameLA, MADE_Number, MADE_Value, Multiname, Namespace, NS_Private, NS_Public, namespace, prefixedNamespace, prefixOf, publicNs, publicQname, qname, TypeName, } from "./names.js";
30
30
  import { FloatVector, setFloatElement } from "./natives/float-vector.js";
31
31
  import { storeDouble, storeInt, storeObject, } from "./natives/vector-elements.js";
32
32
  import { escapeAttributeValue, escapeElementValue } from "./natives/xml/escape.js";
33
33
  import { numberToString, stringToNumber } from "./number-strings.js";
34
34
  import { defaultPrint, memoryFiles, } from "./options.js";
35
- import { cached, epoch, IC_Call, IC_Const, IC_Dynamic, IC_Get, IC_GetSet, IC_Method, IC_Set, IC_Slot, invalidate, NO_CACHE, REPLACEMENTS, UNFILLED, } from "./property-cache.js";
35
+ import { cached, epoch, IC_Call, IC_Const, IC_Dynamic, IC_Get, IC_GetSet, IC_Method, IC_Set, IC_Slot, invalidate, NO_CACHE, ScopeFinds, UNFILLED, } from "./property-cache.js";
36
36
  import { BIND_Const, BIND_Get, BIND_GetSet, BIND_Method, BIND_Set, BIND_Var, boundKey, ClassRef, methodKey, slotKey, Traits, untypedSetKey, VectorRef, } from "./traits.js";
37
37
  export { NOT_FOUND } from "./descriptors.js";
38
38
  /**
@@ -54,7 +54,27 @@ class ScopePack {
54
54
  this.w = w;
55
55
  }
56
56
  }
57
+ /** Whether a chain or a pack, `scopes` with bits `w`, has a with scope, in a pack in it too. */
58
+ function hasWith(scopes, w) {
59
+ for (let i = 0; i < scopes.length; i++) {
60
+ const o = scopes[i];
61
+ if (o instanceof ScopePack ? hasWith(o.scopes, o.w) : (w >> i) & 1) {
62
+ return true;
63
+ }
64
+ }
65
+ return false;
66
+ }
57
67
  /** The value of `o`'s dynamic property `name`, or NOT_FOUND. */
68
+ /** Whether k is an int atom avmplus can take as a uint: an integer of 0 to 2^32-1, -0 not, as it is a double's. */
69
+ function uintAtom(k) {
70
+ return typeof k === "number" && k >>> 0 === k && (k !== 0 || 1 / k > 0);
71
+ }
72
+ /** Whether v is an object's atom to avmplus: not null, nor a Namespace or float, whose atoms are their own. */
73
+ function isScriptObject(v) {
74
+ return (typeof v === "object" && v !== null && !(v instanceof Namespace) && !(v instanceof FloatValue));
75
+ }
76
+ /** The name avmplus' getUintProperty looks a number up by: public, at every version. */
77
+ const ELEMENT_NAME = new Multiname(CONSTANT_MultinameL, [publicNs], [255], null, false);
58
78
  function dynamicValue(o, name) {
59
79
  const d = o.$d;
60
80
  if (!d) {
@@ -67,6 +87,7 @@ export class Runtime {
67
87
  classHooks;
68
88
  print;
69
89
  debugger;
90
+ onUnsupported;
70
91
  compileAbc;
71
92
  files;
72
93
  /** See RuntimeOptions.workers and worker. */
@@ -127,6 +148,13 @@ export class Runtime {
127
148
  /** The builtin classes' traits, made before their classes so the bootstrap can refer to them. */
128
149
  objectTraits;
129
150
  classTraits;
151
+ /**
152
+ * What the super operations of a script's methods and of a class's
153
+ * static ones look in, as avmplus' script traits extend Object and a
154
+ * class object's traits Class: their `super` is that class's instance.
155
+ */
156
+ scriptSuper;
157
+ staticSuper;
130
158
  functionTraits;
131
159
  /**
132
160
  * Method closures, by receiver and method, so that o.f === o.f; by the
@@ -147,7 +175,7 @@ export class Runtime {
147
175
  /** Special traits: an activation's or a catch scope's, by descriptor. */
148
176
  scopeTraits = new WeakMap();
149
177
  specialized = new Map();
150
- empty = Object.assign([], { w: 0 });
178
+ empty = Object.assign([], { w: 0, found: undefined });
151
179
  /**
152
180
  * `natives` are the natives by name, or a provider that makes them for
153
181
  * this runtime, so that a natives module can close over its runtime (as a
@@ -158,6 +186,7 @@ export class Runtime {
158
186
  this.classHooks = classHooks;
159
187
  this.print = options.print ?? defaultPrint;
160
188
  this.debugger = options.debugger ?? false;
189
+ this.onUnsupported = options.onUnsupported;
161
190
  this.compileAbc = options.compileAbc ?? null;
162
191
  this.files = options.files ?? memoryFiles();
163
192
  this.workerHost = options.workers ?? null;
@@ -177,6 +206,8 @@ export class Runtime {
177
206
  this.objectTraits.dynamic = true;
178
207
  this.objectTraits.testsValues = true;
179
208
  this.classTraits = new Traits("Class", this.objectTraits);
209
+ this.scriptSuper = { $it: this.objectTraits };
210
+ this.staticSuper = { $it: this.classTraits };
180
211
  this.functionTraits = new Traits("Function", this.objectTraits);
181
212
  this.functionTraits.dynamic = true;
182
213
  this.natives = typeof natives === "function" ? natives(this) : natives;
@@ -289,6 +320,7 @@ export class Runtime {
289
320
  // Its string only once a lookup needs it: a Dictionary does not.
290
321
  const keyed = Multiname.keyed(kind, namespaces, versions, part, mn.attribute, this.keyName);
291
322
  keyed.domain = mn.domain;
323
+ keyed.made = MADE_Value;
292
324
  return keyed;
293
325
  }
294
326
  else {
@@ -297,6 +329,37 @@ export class Runtime {
297
329
  }
298
330
  const named = new Multiname(kind, namespaces, versions, name, mn.attribute);
299
331
  named.domain = mn.domain;
332
+ // A number here is one no element access made: a Proxy tells it by its string.
333
+ if (mn.runtimeName && typeof last !== "number") {
334
+ named.made = MADE_Value;
335
+ }
336
+ return named;
337
+ }
338
+ /**
339
+ * The name runtimeName makes of `mn`, a MultinameL, and a string the
340
+ * code itself holds, `obj["name"]`: a module makes it once as it loads
341
+ * and keeps it as it keeps `mn`, so it has a cache as a module's own
342
+ * names have, however many such names the module has and however long
343
+ * the string is.
344
+ */
345
+ literalName(mn, name) {
346
+ const named = new Multiname(mn.kind, mn.namespaces, mn.versions, name, mn.attribute);
347
+ named.domain = mn.domain;
348
+ named.made = MADE_Value;
349
+ named.cache = UNFILLED;
350
+ return named;
351
+ }
352
+ /**
353
+ * The name number `i` makes of `mn` in an element's read or write, which
354
+ * avmplus' JIT hands an object as a number (getpropertylate_i and _d), a
355
+ * Proxy as its string: where the name has no public namespace, it is a
356
+ * name as any other.
357
+ */
358
+ indexName(mn, i) {
359
+ const named = this.runtimeName(mn, i);
360
+ if (mn.kind === CONSTANT_MultinameL && mn.elementName) {
361
+ named.made = MADE_Number;
362
+ }
300
363
  return named;
301
364
  }
302
365
  stringNames = new WeakMap();
@@ -318,6 +381,7 @@ export class Runtime {
318
381
  }
319
382
  const named = new Multiname(base.kind, base.namespaces, base.versions, name, base.attribute);
320
383
  named.domain = base.domain;
384
+ named.made = MADE_Value;
321
385
  if (!cache) {
322
386
  cache = { names: new Map(), misses: 0 };
323
387
  this.stringNames.set(base, cache);
@@ -417,6 +481,7 @@ export class Runtime {
417
481
  const traits = new Traits("global", this.objectTraits);
418
482
  traits.dynamic = true;
419
483
  traits.isGlobal = true;
484
+ traits.abc = script.abc;
420
485
  traits.describe(script.desc.traits);
421
486
  const g = traits.instance();
422
487
  // As the script's initializer, its methods run in an empty scope chain,
@@ -424,7 +489,9 @@ export class Runtime {
424
489
  // indexes the scopes their closures capture: the global object is
425
490
  // only what they push.
426
491
  for (const [d, factory, id, , params] of script.desc.traits.methods) {
427
- installMethod(this, traits.methodHome(3), d, withId(factory(this.empty, null), id), factory, params);
492
+ const method = withId(factory(this.empty, this.scriptSuper), id);
493
+ this.keepStatic(factory, method);
494
+ installMethod(this, traits.methodHome(3), d, method, factory, params);
428
495
  }
429
496
  // Caches keep the methods.
430
497
  invalidate();
@@ -449,7 +516,7 @@ export class Runtime {
449
516
  if (script.state === 0 || (retry && script.state === 3)) {
450
517
  script.state = 1;
451
518
  try {
452
- script.desc.init(this.empty, null).call(g);
519
+ script.desc.init(this.empty, this.scriptSuper).call(g);
453
520
  }
454
521
  catch (e) {
455
522
  script.state = 3;
@@ -490,6 +557,7 @@ export class Runtime {
490
557
  scope(outer, locals, withs) {
491
558
  const chain = outer.concat(locals);
492
559
  chain.w = outer.w | (withs << outer.length);
560
+ chain.found = undefined;
493
561
  return chain;
494
562
  }
495
563
  /**
@@ -504,6 +572,7 @@ export class Runtime {
504
572
  const packed = chain.slice(0, at);
505
573
  packed.push(new ScopePack(chain.slice(at), chain.w >>> at));
506
574
  packed.w = (chain.w & ((1 << at) - 1)) | (1 << at);
575
+ packed.found = undefined;
507
576
  return packed;
508
577
  }
509
578
  /**
@@ -532,6 +601,40 @@ export class Runtime {
532
601
  return locals[i];
533
602
  }
534
603
  }
604
+ const found = this.outerScopeOf(outer, mn);
605
+ if (found !== null) {
606
+ return found;
607
+ }
608
+ return this.global(mn, outer.length ? outer[0] : locals[0], strict);
609
+ }
610
+ /**
611
+ * The innermost object of a captured chain that has `mn`, or null: kept
612
+ * on a chain without a with scope (ScopeFinds), as a function whose
613
+ * chain the compiler could not type, such as one made where chains of
614
+ * different lengths meet, looks its names up each time it runs.
615
+ */
616
+ outerScopeOf(outer, mn) {
617
+ let finds = outer.found;
618
+ if (finds === undefined) {
619
+ finds = hasWith(outer, outer.w) ? null : new ScopeFinds();
620
+ outer.found = finds;
621
+ }
622
+ // A name made at run time is a new one each time: keeping it would only grow the map.
623
+ if (finds === null || mn.cache === NO_CACHE) {
624
+ return this.searchOuter(outer, mn);
625
+ }
626
+ if (finds.epoch !== epoch()) {
627
+ finds.clear();
628
+ finds.epoch = epoch();
629
+ }
630
+ let o = finds.get(mn);
631
+ if (o === undefined) {
632
+ o = this.searchOuter(outer, mn);
633
+ finds.set(mn, o);
634
+ }
635
+ return o;
636
+ }
637
+ searchOuter(outer, mn) {
535
638
  for (let i = outer.length - 1; i >= 0; i--) {
536
639
  const o = outer[i];
537
640
  if (o instanceof ScopePack) {
@@ -544,7 +647,7 @@ export class Runtime {
544
647
  return o;
545
648
  }
546
649
  }
547
- return this.global(mn, outer.length ? outer[0] : locals[0], strict);
650
+ return null;
548
651
  }
549
652
  findPropertyStrict(mn, outer, locals, withs) {
550
653
  return this.findProperty(mn, outer, locals, withs, true);
@@ -669,15 +772,22 @@ export class Runtime {
669
772
  traitsOf(v) {
670
773
  switch (typeof v) {
671
774
  case "object":
672
- case "function":
775
+ case "function": {
673
776
  if (v === null) {
674
777
  throw this.error("TypeError", 1009);
675
778
  }
779
+ // An object's own first: instanceof walks the whole prototype
780
+ // chain of an object, and a class's is as deep as its bases.
781
+ const traits = v.$traits;
782
+ if (traits !== undefined) {
783
+ return traits;
784
+ }
676
785
  return v instanceof FloatValue
677
786
  ? this.builtinTraits("float")
678
787
  : v instanceof Namespace
679
788
  ? this.builtinTraits("Namespace")
680
- : v.$traits;
789
+ : traits;
790
+ }
681
791
  // As avmplus' toVTable: every number is a Number at run time, int or
682
792
  // not; int's and uint's traits only bind early, from static types.
683
793
  case "number":
@@ -698,12 +808,17 @@ export class Runtime {
698
808
  receiverTraits(v) {
699
809
  switch (typeof v) {
700
810
  case "object":
701
- case "function":
702
- return v instanceof FloatValue
703
- ? this.builtinTraits("float")
704
- : v === null
705
- ? undefined
706
- : v.$traits;
811
+ case "function": {
812
+ if (v === null) {
813
+ return undefined;
814
+ }
815
+ // A float and a Namespace have none of their own (see traitsOf).
816
+ const traits = v.$traits;
817
+ if (traits !== undefined) {
818
+ return traits;
819
+ }
820
+ return v instanceof FloatValue ? this.builtinTraits("float") : undefined;
821
+ }
707
822
  case "string":
708
823
  return this.stringTraits;
709
824
  case "number":
@@ -840,7 +955,7 @@ export class Runtime {
840
955
  mn.kind === CONSTANT_MultinameA ||
841
956
  mn.kind === CONSTANT_MultinameL ||
842
957
  mn.kind === CONSTANT_MultinameLA);
843
- throw this.error("ReferenceError", name === null && nsset ? 1081 : 1069, mn.name ?? "*", traits.name);
958
+ throw this.error("ReferenceError", name === null && nsset ? 1081 : 1069, mn.name ?? "*", traits.errorName);
844
959
  }
845
960
  /**
846
961
  * obj[i] for a number i, as avmplus' getUintProperty: a Vector's,
@@ -877,7 +992,7 @@ export class Runtime {
877
992
  }
878
993
  }
879
994
  }
880
- return this.getProperty(o, this.runtimeName(mn, i));
995
+ return this.getProperty(o, this.indexName(mn, i));
881
996
  }
882
997
  /** obj[i] = v for a number i, as avmplus' setUintProperty. */
883
998
  setIndexed(o, mn, i, v) {
@@ -889,12 +1004,114 @@ export class Runtime {
889
1004
  traits.setIndex(o, i, v, this);
890
1005
  return;
891
1006
  }
892
- if (o.$a !== undefined && o.$a !== SEALED_ELEMENTS && i !== 0xffffffff) {
893
- o.$a[i] = v;
894
- return;
1007
+ const a = o.$a;
1008
+ if (a !== undefined) {
1009
+ if (a !== SEALED_ELEMENTS && i !== 0xffffffff) {
1010
+ a[i] = v;
1011
+ return;
1012
+ }
1013
+ }
1014
+ else if (traits?.dynamic && traits.properties === null && !traits.refusesNames) {
1015
+ // As ScriptObject::setUintProperty: an object with no elements of its own keeps the
1016
+ // number among its dynamic properties, with no binding asked for.
1017
+ const d = o.$d;
1018
+ if (d) {
1019
+ d.set(`${i}`, v);
1020
+ return;
1021
+ }
1022
+ }
1023
+ }
1024
+ this.setProperty(o, this.indexName(mn, i), v);
1025
+ }
1026
+ /**
1027
+ * obj[k] by a runtime name with no public namespace, or an attribute's,
1028
+ * in a script's or a class's initializer, which avmplus interprets: its
1029
+ * getproperty reads an int key of at least 0 off an object by
1030
+ * getUintProperty, whatever namespaces the name has, where its JIT asks
1031
+ * for a public one (getIndexed). Any other key makes a name.
1032
+ */
1033
+ getInterpreted(o, mn, k) {
1034
+ if (uintAtom(k) && isScriptObject(o)) {
1035
+ return this.getIndexed(o, ELEMENT_NAME, k);
1036
+ }
1037
+ return this.getProperty(o, this.runtimeName(mn, k));
1038
+ }
1039
+ /** obj[k] = v as getInterpreted reads it, as avmplus' interpreter's setproperty. */
1040
+ setInterpreted(o, mn, k, v) {
1041
+ if (uintAtom(k) && isScriptObject(o)) {
1042
+ this.setIndexed(o, ELEMENT_NAME, k, v);
1043
+ return;
1044
+ }
1045
+ this.setProperty(o, this.runtimeName(mn, k), v);
1046
+ }
1047
+ /**
1048
+ * obj[k] for a key whose type the compiler does not know, by a name with
1049
+ * no namespace from the stack, as avmplus' getprop_index and
1050
+ * getpropertyHelper: a number is an index (getIndexed), an object a
1051
+ * Dictionary has as a key is read by itself, and anything else, a QName
1052
+ * and a Dictionary's absent key among them, is looked up by the name it
1053
+ * makes.
1054
+ */
1055
+ getKeyed(o, mn, k) {
1056
+ let r;
1057
+ if (typeof k === "number") {
1058
+ r = this.getIndexed(o, mn, k);
1059
+ return r;
1060
+ }
1061
+ if (typeof k === "object" && k !== null) {
1062
+ const keys = o?.$keys;
1063
+ if (keys !== undefined) {
1064
+ if (!mn.elementName) {
1065
+ r = this.getProperty(o, this.runtimeName(mn, this.unkeyed(k)));
1066
+ return r;
1067
+ }
1068
+ // What is there was put there by a name with the object as its key, which a QName's is not.
1069
+ const v = keys.get(k);
1070
+ if (v !== undefined) {
1071
+ return v;
1072
+ }
895
1073
  }
896
1074
  }
897
- this.setProperty(o, this.runtimeName(mn, i), v);
1075
+ r = this.getProperty(o, this.runtimeName(mn, k));
1076
+ return r;
1077
+ }
1078
+ /**
1079
+ * An object given as the key of a Dictionary by a name with no public
1080
+ * namespace, as the name is made of it: avmplus' JIT asks a Dictionary
1081
+ * for an object only through getprop_index and setprop_index, which such
1082
+ * a name does not reach, so the object is its string, as on any other
1083
+ * receiver (initMultinameLate); a QName names what it names. Its
1084
+ * interpreter asks the Dictionary by any name, as a name made without
1085
+ * this does: the compiler leaves script and class initializers to that.
1086
+ */
1087
+ unkeyed(k) {
1088
+ return k.$traits !== undefined && k.$local === undefined ? this.toString(k) : k;
1089
+ }
1090
+ /** obj[k] = v for a key whose type the compiler does not know, as avmplus' setprop_index and setpropertyHelper. */
1091
+ setKeyed(o, mn, k, v) {
1092
+ if (typeof k === "number") {
1093
+ this.setIndexed(o, mn, k, v);
1094
+ return;
1095
+ }
1096
+ if (typeof k === "object" && k !== null) {
1097
+ const keys = o?.$keys;
1098
+ if (keys !== undefined) {
1099
+ if (!mn.elementName) {
1100
+ this.setProperty(o, this.runtimeName(mn, this.unkeyed(k)), v);
1101
+ return;
1102
+ }
1103
+ if (keys.replace(k, v)) {
1104
+ return;
1105
+ }
1106
+ // A new key: an object of traits, which a float and a Namespace are not, and no QName,
1107
+ // which names what it names. Those are added, or not, by the name they make.
1108
+ if (k.$traits !== undefined && k.$local === undefined) {
1109
+ keys.add(k, v);
1110
+ return;
1111
+ }
1112
+ }
1113
+ }
1114
+ this.setProperty(o, this.runtimeName(mn, k), v);
898
1115
  }
899
1116
  /**
900
1117
  * Element i of Vector o set to x, already converted, as
@@ -1084,18 +1301,16 @@ export class Runtime {
1084
1301
  if (slot !== undefined) {
1085
1302
  return o[slot];
1086
1303
  }
1087
- throw this.error("ReferenceError", 1077, mn.name ?? "*", traits.name);
1304
+ throw this.error("ReferenceError", 1077, mn.name ?? "*", traits.errorName);
1088
1305
  }
1089
1306
  default:
1090
- throw this.error("ReferenceError", 1077, mn.name ?? "*", traits.name);
1307
+ throw this.error("ReferenceError", 1077, mn.name ?? "*", traits.errorName);
1091
1308
  }
1092
1309
  }
1093
1310
  /** fillCache (cache-fill.ts), for a name that caches on traits a cache knows `o` by. */
1094
1311
  fill(o, traits, b, mn) {
1095
1312
  const head = mn.cache;
1096
- if (head === NO_CACHE ||
1097
- (head.replaced === REPLACEMENTS && head.epoch === epoch()) ||
1098
- this.receiverTraits(o) !== traits) {
1313
+ if (head === NO_CACHE || this.receiverTraits(o) !== traits) {
1099
1314
  return;
1100
1315
  }
1101
1316
  fillCache(head, o, traits, b, mn);
@@ -1124,6 +1339,8 @@ export class Runtime {
1124
1339
  f.$length = method.$length ?? method.length;
1125
1340
  f.$id = method.$id ?? 0;
1126
1341
  f.$closure = true;
1342
+ // Whose method it is, for moduleOf.
1343
+ f.$of = traits;
1127
1344
  if (typeof o === "object") {
1128
1345
  byMethod.set(method, f);
1129
1346
  }
@@ -1176,7 +1393,7 @@ export class Runtime {
1176
1393
  switch (b & 7) {
1177
1394
  case BIND_Const:
1178
1395
  if (!init) {
1179
- throw this.error("ReferenceError", 1074, mn.name ?? "*", traits.name);
1396
+ throw this.error("ReferenceError", 1074, mn.name ?? "*", traits.errorName);
1180
1397
  }
1181
1398
  o[slotKey(id)] = this.coerce(v, traits.slotType(id));
1182
1399
  return;
@@ -1188,9 +1405,9 @@ export class Runtime {
1188
1405
  (traits.proto[untypedSetKey(id + 1)] ?? traits.proto[methodKey(id + 1)]).call(o, v);
1189
1406
  return;
1190
1407
  case BIND_Method:
1191
- throw this.error("ReferenceError", 1037, mn.name ?? "*", traits.name);
1408
+ throw this.error("ReferenceError", 1037, mn.name ?? "*", traits.errorName);
1192
1409
  default:
1193
- throw this.error("ReferenceError", 1074, mn.name ?? "*", traits.name);
1410
+ throw this.error("ReferenceError", 1074, mn.name ?? "*", traits.errorName);
1194
1411
  }
1195
1412
  }
1196
1413
  const name = mn.dynamicName();
@@ -1212,7 +1429,7 @@ export class Runtime {
1212
1429
  return;
1213
1430
  }
1214
1431
  }
1215
- throw this.error("ReferenceError", 1056, mn.name ?? "*", traits.name);
1432
+ throw this.error("ReferenceError", 1056, mn.name ?? "*", traits.errorName);
1216
1433
  }
1217
1434
  initProperty(o, mn, v) {
1218
1435
  this.setProperty(o, mn, v, true);
@@ -1227,7 +1444,7 @@ export class Runtime {
1227
1444
  }
1228
1445
  // A primitive's property cannot be deleted, as MethodEnv's delproperty has it.
1229
1446
  if (typeof o !== "object" || o instanceof Namespace) {
1230
- throw this.error("ReferenceError", 1120, mn.name ?? "*", this.traitsOf(o).name);
1447
+ throw this.error("ReferenceError", 1120, mn.name ?? "*", this.traitsOf(o).errorName);
1231
1448
  }
1232
1449
  // E4X 11.3.1: delete x[list] is a TypeError, as in delete x.a.(b == 1).
1233
1450
  if (mn.key?.$nodes !== undefined) {
@@ -1388,8 +1605,7 @@ export class Runtime {
1388
1605
  // A namespace is the runtime's own object: its prototype is its class's.
1389
1606
  if (typeof o === "object" &&
1390
1607
  o !== null &&
1391
- !(o instanceof Namespace) &&
1392
- !(o instanceof FloatValue)) {
1608
+ (o.$traits !== undefined || !(o instanceof Namespace || o instanceof FloatValue))) {
1393
1609
  return o.$p;
1394
1610
  }
1395
1611
  return this.traitsOf(o).proto.$p;
@@ -1402,6 +1618,28 @@ export class Runtime {
1402
1618
  publicName(name) {
1403
1619
  return qname(publicNs, this.nameString(name));
1404
1620
  }
1621
+ // Natives' typed reach into AS3 objects (as3-types.ts): getProperty,
1622
+ // setProperty and callProperty of the public name, the lookup natives
1623
+ // made before, with the name checked against the class's declarations
1624
+ // when compiled.
1625
+ /** `o`'s public property `name`: a variable, constant or accessor its class declares. */
1626
+ prop(o, name) {
1627
+ // biome-ignore lint/style/useConst: a const is folded into a tail call (see getProperty)
1628
+ let r;
1629
+ r = this.getProperty(o, this.publicName(name));
1630
+ return r;
1631
+ }
1632
+ /** Sets `o`'s public property `name`, which its class declares writable. */
1633
+ setProp(o, name, v) {
1634
+ this.setProperty(o, this.publicName(name), v);
1635
+ }
1636
+ /** Calls `o`'s public method `name` with `args`. */
1637
+ callProp(o, name, ...args) {
1638
+ // biome-ignore lint/style/useConst: a const is folded into a tail call (see getProperty)
1639
+ let r;
1640
+ r = this.callProperty(o, this.publicName(name), ...args);
1641
+ return r;
1642
+ }
1405
1643
  /** A property name as a string, as ToString makes one. */
1406
1644
  nameString(name) {
1407
1645
  return typeof name === "string" ? name : this.toString(name);
@@ -1615,8 +1853,30 @@ export class Runtime {
1615
1853
  }
1616
1854
  return key;
1617
1855
  }
1618
- callStatic(_abc, m, ..._args) {
1619
- throw this.unsupported(`callstatic ${m}`);
1856
+ /**
1857
+ * The first method each factory made for a class's or a script's
1858
+ * traits, as avmplus' AbcEnv keeps the first MethodEnv of each method
1859
+ * a vtable makes (VTable::makeMethodEnv): what callstatic calls.
1860
+ */
1861
+ staticMethods = new WeakMap();
1862
+ /** Keep `method`, made by `factory`, for callstatic, unless it made one before. */
1863
+ keepStatic(factory, method) {
1864
+ if (!this.staticMethods.has(factory)) {
1865
+ this.staticMethods.set(factory, method);
1866
+ }
1867
+ }
1868
+ /**
1869
+ * callstatic: method `factory` made, called on `o` with its arguments,
1870
+ * which the compiler coerced. avmplus verifies a method when it first
1871
+ * runs and refuses one that calls a method no vtable has made yet
1872
+ * (1107); that is found here only once the call is reached.
1873
+ */
1874
+ callStatic(factory, o, ...args) {
1875
+ const method = this.staticMethods.get(factory);
1876
+ if (method === undefined) {
1877
+ throw this.error("VerifyError", 1107);
1878
+ }
1879
+ return method.apply(o, args);
1620
1880
  }
1621
1881
  // The super operations, as MethodEnv's: by the base class's traits
1622
1882
  // alone, a name they do not bind a ReferenceError, not a dynamic property.
@@ -1629,7 +1889,7 @@ export class Runtime {
1629
1889
  return r;
1630
1890
  }
1631
1891
  if (b === 0) {
1632
- throw this.error("ReferenceError", 1070, mn.name ?? "*", traits.name);
1892
+ throw this.error("ReferenceError", 1070, mn.name ?? "*", traits.errorName);
1633
1893
  }
1634
1894
  r = this.callValue(this.getBound(o, traits, b, mn), o, args, mn);
1635
1895
  return r;
@@ -1638,7 +1898,7 @@ export class Runtime {
1638
1898
  const traits = sup.$it;
1639
1899
  const b = traits.find(mn);
1640
1900
  if (b === 0) {
1641
- throw this.error("ReferenceError", 1069, mn.name ?? "*", traits.name);
1901
+ throw this.error("ReferenceError", 1069, mn.name ?? "*", traits.errorName);
1642
1902
  }
1643
1903
  return this.getBound(o, traits, b, mn);
1644
1904
  }
@@ -1655,11 +1915,11 @@ export class Runtime {
1655
1915
  o[slotKey(id)] = this.coerce(v, traits.slotType(id));
1656
1916
  return;
1657
1917
  case BIND_Method:
1658
- throw this.error("ReferenceError", 1037, mn.name ?? "*", traits.name);
1918
+ throw this.error("ReferenceError", 1037, mn.name ?? "*", traits.errorName);
1659
1919
  case 0:
1660
- throw this.error("ReferenceError", 1056, mn.name ?? "*", traits.name);
1920
+ throw this.error("ReferenceError", 1056, mn.name ?? "*", traits.errorName);
1661
1921
  default:
1662
- throw this.error("ReferenceError", 1074, mn.name ?? "*", traits.name);
1922
+ throw this.error("ReferenceError", 1074, mn.name ?? "*", traits.errorName);
1663
1923
  }
1664
1924
  }
1665
1925
  constructSuper(sup, o, ...args) {
@@ -1809,6 +2069,19 @@ export class Runtime {
1809
2069
  this.functionObjects.set(method, f);
1810
2070
  return f;
1811
2071
  }
2072
+ /**
2073
+ * The module a function's code is from, as avmplus has a method's code
2074
+ * context: a function's by the global object its scope begins with, a
2075
+ * bound method's by its receiver's class; null for a native, a class's
2076
+ * own method, or anything but a function.
2077
+ */
2078
+ moduleOf(f) {
2079
+ const o = f;
2080
+ if (o?.$of) {
2081
+ return o.$of.abc;
2082
+ }
2083
+ return o?.$global ? this.traitsOf(o.$global).abc : null;
2084
+ }
1812
2085
  /** A Function object calling `f`, with `global` as its receiver when it has none. */
1813
2086
  newFunctionObject(f, global) {
1814
2087
  const o = this.functionTraits.instance();
@@ -2044,7 +2317,7 @@ export class Runtime {
2044
2317
  if (v.$traits === traits || this.isInstanceOf(v, traits)) {
2045
2318
  return v;
2046
2319
  }
2047
- throw this.error("TypeError", 1034, this.describe(v), traits.name);
2320
+ throw this.error("TypeError", 1034, this.describe(v), traits.errorName);
2048
2321
  }
2049
2322
  }
2050
2323
  }
@@ -2065,7 +2338,7 @@ export class Runtime {
2065
2338
  if (v.$traits === traits || this.isInstanceOf(v, traits)) {
2066
2339
  return v;
2067
2340
  }
2068
- throw this.error("TypeError", 1034, this.describe(v), traits.name);
2341
+ throw this.error("TypeError", 1034, this.describe(v), traits.errorName);
2069
2342
  }
2070
2343
  coerceString(v) {
2071
2344
  return v === null || v === undefined ? null : typeof v === "string" ? v : this.toString(v);
@@ -2264,7 +2537,7 @@ export class Runtime {
2264
2537
  return v;
2265
2538
  }
2266
2539
  }
2267
- throw this.error("TypeError", 1050, o.$traits.name);
2540
+ throw this.error("TypeError", 1050, o.$traits.errorName);
2268
2541
  }
2269
2542
  add(a, b) {
2270
2543
  if (typeof a === "number" && typeof b === "number") {
@@ -2543,12 +2816,21 @@ export class Runtime {
2543
2816
  catch {
2544
2817
  return new AsError(`${name}: ${message}`);
2545
2818
  }
2546
- return this.constructClass(cls, [message, id]);
2819
+ const made = this.constructClass(cls, [message, id]);
2820
+ // What the number stands for, kept beside the message for a host's report (detailOf).
2821
+ if (!this.debugger) {
2822
+ Object.defineProperty(made, "$detail", { value: errorMessage(true, id, args) });
2823
+ }
2824
+ return made;
2547
2825
  }
2548
2826
  /** See stackOf in errors.ts. */
2549
2827
  stackOf(error) {
2550
2828
  return stackOf(error);
2551
2829
  }
2830
+ /** See detailOf in errors.ts. */
2831
+ detailOf(error) {
2832
+ return detailOf(error);
2833
+ }
2552
2834
  /** See errorMessage in errors.ts. */
2553
2835
  errorMessage(id, args = []) {
2554
2836
  return errorMessage(this.debugger, id, args);
@@ -2573,11 +2855,35 @@ export class Runtime {
2573
2855
  if (isViewRangeError(e)) {
2574
2856
  return this.error("RangeError", 1506);
2575
2857
  }
2858
+ if (e instanceof UnsupportedError) {
2859
+ return this.unsupportedCaught(e);
2860
+ }
2576
2861
  if (e instanceof Error && !(e instanceof AsError)) {
2577
2862
  throw e;
2578
2863
  }
2579
2864
  return e;
2580
2865
  }
2866
+ /** What a catch was told of already (unsupportedCaught). */
2867
+ unsupportedTold = new Set();
2868
+ /**
2869
+ * What is not supported as the AS3 error a catch gets: an
2870
+ * IllegalOperationError with its message, or an Error where the player's
2871
+ * classes are not loaded; said once to the host, as the code may say nothing.
2872
+ */
2873
+ unsupportedCaught(e) {
2874
+ if (!this.unsupportedTold.has(e.message)) {
2875
+ this.unsupportedTold.add(e.message);
2876
+ this.onUnsupported?.(e.message);
2877
+ }
2878
+ let cls;
2879
+ try {
2880
+ cls = this.resolve(this.cls(namespace(NS_Public, "flash.errors"), "IllegalOperationError"));
2881
+ }
2882
+ catch {
2883
+ cls = this.builtinClass("Error");
2884
+ }
2885
+ return this.constructClass(cls, [e.message, 0]);
2886
+ }
2581
2887
  catches(e, type) {
2582
2888
  return this.isInstanceOf(e, this.traitsOfType(type));
2583
2889
  }
@@ -2585,7 +2891,7 @@ export class Runtime {
2585
2891
  return new Error("swf2es: a dispatcher reached no block");
2586
2892
  }
2587
2893
  unsupported(what) {
2588
- return new Error(`swf2es: ${what} is not supported yet`);
2894
+ return new UnsupportedError(`swf2es: ${what} is not supported yet`);
2589
2895
  }
2590
2896
  // Class aliases and the classes AMF makes (amf-classes.ts).
2591
2897
  registerClassAlias(name, cls) {