@swf2es/runtime 0.1.14 → 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/avm2/cache-fill.d.ts +4 -1
- package/dist/avm2/cache-fill.d.ts.map +1 -1
- package/dist/avm2/cache-fill.js +5 -6
- package/dist/avm2/cache-fill.js.map +1 -1
- package/dist/avm2/descriptors.d.ts +6 -1
- package/dist/avm2/descriptors.d.ts.map +1 -1
- package/dist/avm2/descriptors.js.map +1 -1
- package/dist/avm2/errors.d.ts +14 -0
- package/dist/avm2/errors.d.ts.map +1 -1
- package/dist/avm2/errors.js +17 -0
- package/dist/avm2/errors.js.map +1 -1
- package/dist/avm2/index.d.ts +1 -0
- package/dist/avm2/index.d.ts.map +1 -1
- package/dist/avm2/index.js +1 -0
- package/dist/avm2/index.js.map +1 -1
- package/dist/avm2/names.d.ts +11 -0
- package/dist/avm2/names.d.ts.map +1 -1
- package/dist/avm2/names.js +12 -0
- package/dist/avm2/names.js.map +1 -1
- package/dist/avm2/natives/proxy.d.ts.map +1 -1
- package/dist/avm2/natives/proxy.js +9 -4
- package/dist/avm2/natives/proxy.js.map +1 -1
- package/dist/avm2/options.d.ts +5 -0
- package/dist/avm2/options.d.ts.map +1 -1
- package/dist/avm2/options.js.map +1 -1
- package/dist/avm2/property-cache.d.ts +17 -13
- package/dist/avm2/property-cache.d.ts.map +1 -1
- package/dist/avm2/property-cache.js +50 -10
- package/dist/avm2/property-cache.js.map +1 -1
- package/dist/avm2/runtime.d.ts +62 -0
- package/dist/avm2/runtime.d.ts.map +1 -1
- package/dist/avm2/runtime.js +245 -25
- package/dist/avm2/runtime.js.map +1 -1
- package/dist/avm2/traits.d.ts +7 -0
- package/dist/avm2/traits.d.ts.map +1 -1
- package/dist/avm2/traits.js +6 -0
- package/dist/avm2/traits.js.map +1 -1
- package/package.json +2 -2
- package/src/avm2/cache-fill.ts +5 -7
- package/src/avm2/descriptors.ts +6 -2
- package/src/avm2/errors.ts +18 -0
- package/src/avm2/index.ts +1 -0
- package/src/avm2/names.ts +13 -0
- package/src/avm2/natives/proxy.ts +20 -4
- package/src/avm2/options.ts +5 -0
- package/src/avm2/property-cache.ts +57 -11
- package/src/avm2/runtime.ts +280 -26
- package/src/avm2/traits.ts +7 -1
package/src/avm2/runtime.ts
CHANGED
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
pairOf,
|
|
48
48
|
walkless,
|
|
49
49
|
} from "./enumeration.js";
|
|
50
|
-
import { AsError, describe, errorMessage, stackOf } from "./errors.js";
|
|
50
|
+
import { AsError, describe, detailOf, errorMessage, stackOf, UnsupportedError } from "./errors.js";
|
|
51
51
|
import { FloatValue, floatValue } from "./float.js";
|
|
52
52
|
import { type ClassHook, hookedBinding, type NativesProvider, type PropertyHook } from "./hooks.js";
|
|
53
53
|
import {
|
|
@@ -70,6 +70,8 @@ import {
|
|
|
70
70
|
CONSTANT_RTQnameA,
|
|
71
71
|
CONSTANT_RTQnameL,
|
|
72
72
|
CONSTANT_RTQnameLA,
|
|
73
|
+
MADE_Number,
|
|
74
|
+
MADE_Value,
|
|
73
75
|
Multiname,
|
|
74
76
|
Namespace,
|
|
75
77
|
NS_Private,
|
|
@@ -113,7 +115,7 @@ import {
|
|
|
113
115
|
IC_Slot,
|
|
114
116
|
invalidate,
|
|
115
117
|
NO_CACHE,
|
|
116
|
-
|
|
118
|
+
ScopeFinds,
|
|
117
119
|
UNFILLED,
|
|
118
120
|
} from "./property-cache.js";
|
|
119
121
|
import {
|
|
@@ -153,6 +155,18 @@ class ScopePack {
|
|
|
153
155
|
) {}
|
|
154
156
|
}
|
|
155
157
|
|
|
158
|
+
/** Whether a chain or a pack, `scopes` with bits `w`, has a with scope, in a pack in it too. */
|
|
159
|
+
function hasWith(scopes: Value[], w: number): boolean {
|
|
160
|
+
for (let i = 0; i < scopes.length; i++) {
|
|
161
|
+
const o = scopes[i];
|
|
162
|
+
if (o instanceof ScopePack ? hasWith(o.scopes, o.w) : (w >> i) & 1) {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
|
|
156
170
|
/** The value of `o`'s dynamic property `name`, or NOT_FOUND. */
|
|
157
171
|
function dynamicValue(o: AsObject, name: string): Value {
|
|
158
172
|
const d: Map<string, Value> | null | undefined = o.$d;
|
|
@@ -167,6 +181,7 @@ function dynamicValue(o: AsObject, name: string): Value {
|
|
|
167
181
|
export class Runtime {
|
|
168
182
|
readonly print: (line: string) => void;
|
|
169
183
|
readonly debugger: boolean;
|
|
184
|
+
private readonly onUnsupported: ((message: string) => void) | undefined;
|
|
170
185
|
readonly compileAbc:
|
|
171
186
|
| ((abc: Uint8Array, unit: CompileUnit) => ((rt: Runtime) => Abc) | number)
|
|
172
187
|
| null;
|
|
@@ -249,7 +264,7 @@ export class Runtime {
|
|
|
249
264
|
/** Special traits: an activation's or a catch scope's, by descriptor. */
|
|
250
265
|
private readonly scopeTraits = new WeakMap<object, Traits>();
|
|
251
266
|
readonly specialized = new Map<AsObject, AsObject>();
|
|
252
|
-
readonly empty: Scope = Object.assign([], { w: 0 });
|
|
267
|
+
readonly empty: Scope = Object.assign([], { w: 0, found: undefined });
|
|
253
268
|
|
|
254
269
|
/**
|
|
255
270
|
* `natives` are the natives by name, or a provider that makes them for
|
|
@@ -264,6 +279,7 @@ export class Runtime {
|
|
|
264
279
|
) {
|
|
265
280
|
this.print = options.print ?? defaultPrint;
|
|
266
281
|
this.debugger = options.debugger ?? false;
|
|
282
|
+
this.onUnsupported = options.onUnsupported;
|
|
267
283
|
this.compileAbc = options.compileAbc ?? null;
|
|
268
284
|
this.files = options.files ?? memoryFiles();
|
|
269
285
|
this.workerHost = options.workers ?? null;
|
|
@@ -429,6 +445,7 @@ export class Runtime {
|
|
|
429
445
|
// Its string only once a lookup needs it: a Dictionary does not.
|
|
430
446
|
const keyed = Multiname.keyed(kind, namespaces, versions, part, mn.attribute, this.keyName);
|
|
431
447
|
keyed.domain = mn.domain;
|
|
448
|
+
keyed.made = MADE_Value;
|
|
432
449
|
return keyed;
|
|
433
450
|
} else {
|
|
434
451
|
name = typeof part === "string" ? part : this.toString(part);
|
|
@@ -437,6 +454,42 @@ export class Runtime {
|
|
|
437
454
|
|
|
438
455
|
const named = new Multiname(kind, namespaces, versions, name, mn.attribute);
|
|
439
456
|
named.domain = mn.domain;
|
|
457
|
+
// A number here is one no element access made: a Proxy tells it by its string.
|
|
458
|
+
if (mn.runtimeName && typeof last !== "number") {
|
|
459
|
+
named.made = MADE_Value;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return named;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* The name runtimeName makes of `mn`, a MultinameL, and a string the
|
|
467
|
+
* code itself holds, `obj["name"]`: a module makes it once as it loads
|
|
468
|
+
* and keeps it as it keeps `mn`, so it has a cache as a module's own
|
|
469
|
+
* names have, however many such names the module has and however long
|
|
470
|
+
* the string is.
|
|
471
|
+
*/
|
|
472
|
+
literalName(mn: Multiname, name: string): Multiname {
|
|
473
|
+
const named = new Multiname(mn.kind, mn.namespaces, mn.versions, name, mn.attribute);
|
|
474
|
+
named.domain = mn.domain;
|
|
475
|
+
named.made = MADE_Value;
|
|
476
|
+
named.cache = UNFILLED;
|
|
477
|
+
|
|
478
|
+
return named;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* The name number `i` makes of `mn` in an element's read or write, which
|
|
483
|
+
* avmplus' JIT hands an object as a number (getpropertylate_i and _d), a
|
|
484
|
+
* Proxy as its string: where the name has no public namespace, it is a
|
|
485
|
+
* name as any other.
|
|
486
|
+
*/
|
|
487
|
+
private indexName(mn: Multiname, i: number): Multiname {
|
|
488
|
+
const named = this.runtimeName(mn, i);
|
|
489
|
+
if (mn.kind === CONSTANT_MultinameL && mn.elementName) {
|
|
490
|
+
named.made = MADE_Number;
|
|
491
|
+
}
|
|
492
|
+
|
|
440
493
|
return named;
|
|
441
494
|
}
|
|
442
495
|
|
|
@@ -462,6 +515,7 @@ export class Runtime {
|
|
|
462
515
|
|
|
463
516
|
const named = new Multiname(base.kind, base.namespaces, base.versions, name, base.attribute);
|
|
464
517
|
named.domain = base.domain;
|
|
518
|
+
named.made = MADE_Value;
|
|
465
519
|
if (!cache) {
|
|
466
520
|
cache = { names: new Map(), misses: 0 };
|
|
467
521
|
this.stringNames.set(base, cache);
|
|
@@ -578,6 +632,7 @@ export class Runtime {
|
|
|
578
632
|
const traits = new Traits("global", this.objectTraits);
|
|
579
633
|
traits.dynamic = true;
|
|
580
634
|
traits.isGlobal = true;
|
|
635
|
+
traits.abc = script.abc;
|
|
581
636
|
traits.describe(script.desc.traits);
|
|
582
637
|
const g = traits.instance();
|
|
583
638
|
// As the script's initializer, its methods run in an empty scope chain,
|
|
@@ -671,6 +726,7 @@ export class Runtime {
|
|
|
671
726
|
scope(outer: Scope, locals: Value[], withs: number): Scope {
|
|
672
727
|
const chain = outer.concat(locals) as Scope;
|
|
673
728
|
chain.w = outer.w | (withs << outer.length);
|
|
729
|
+
chain.found = undefined;
|
|
674
730
|
return chain;
|
|
675
731
|
}
|
|
676
732
|
|
|
@@ -686,6 +742,7 @@ export class Runtime {
|
|
|
686
742
|
const packed = chain.slice(0, at) as Scope;
|
|
687
743
|
packed.push(new ScopePack(chain.slice(at), chain.w >>> at) as never);
|
|
688
744
|
packed.w = (chain.w & ((1 << at) - 1)) | (1 << at);
|
|
745
|
+
packed.found = undefined;
|
|
689
746
|
return packed;
|
|
690
747
|
}
|
|
691
748
|
|
|
@@ -724,6 +781,47 @@ export class Runtime {
|
|
|
724
781
|
}
|
|
725
782
|
}
|
|
726
783
|
|
|
784
|
+
const found = this.outerScopeOf(outer, mn);
|
|
785
|
+
if (found !== null) {
|
|
786
|
+
return found;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
return this.global(mn, outer.length ? outer[0] : locals[0], strict);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
/**
|
|
793
|
+
* The innermost object of a captured chain that has `mn`, or null: kept
|
|
794
|
+
* on a chain without a with scope (ScopeFinds), as a function whose
|
|
795
|
+
* chain the compiler could not type, such as one made where chains of
|
|
796
|
+
* different lengths meet, looks its names up each time it runs.
|
|
797
|
+
*/
|
|
798
|
+
private outerScopeOf(outer: Scope, mn: Multiname): AsObject | null {
|
|
799
|
+
let finds = outer.found;
|
|
800
|
+
if (finds === undefined) {
|
|
801
|
+
finds = hasWith(outer, outer.w) ? null : new ScopeFinds();
|
|
802
|
+
outer.found = finds;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// A name made at run time is a new one each time: keeping it would only grow the map.
|
|
806
|
+
if (finds === null || mn.cache === NO_CACHE) {
|
|
807
|
+
return this.searchOuter(outer, mn);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
if (finds.epoch !== epoch()) {
|
|
811
|
+
finds.clear();
|
|
812
|
+
finds.epoch = epoch();
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
let o = finds.get(mn);
|
|
816
|
+
if (o === undefined) {
|
|
817
|
+
o = this.searchOuter(outer, mn);
|
|
818
|
+
finds.set(mn, o);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
return o;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
private searchOuter(outer: Scope, mn: Multiname): AsObject | null {
|
|
727
825
|
for (let i = outer.length - 1; i >= 0; i--) {
|
|
728
826
|
const o = outer[i];
|
|
729
827
|
if (o instanceof ScopePack) {
|
|
@@ -736,7 +834,7 @@ export class Runtime {
|
|
|
736
834
|
}
|
|
737
835
|
}
|
|
738
836
|
|
|
739
|
-
return
|
|
837
|
+
return null;
|
|
740
838
|
}
|
|
741
839
|
|
|
742
840
|
findPropertyStrict(mn: Multiname, outer: Scope, locals: Value[], withs: number): AsObject {
|
|
@@ -887,16 +985,24 @@ export class Runtime {
|
|
|
887
985
|
traitsOf(v: Value): Traits {
|
|
888
986
|
switch (typeof v) {
|
|
889
987
|
case "object":
|
|
890
|
-
case "function":
|
|
988
|
+
case "function": {
|
|
891
989
|
if (v === null) {
|
|
892
990
|
throw this.error("TypeError", 1009);
|
|
893
991
|
}
|
|
894
992
|
|
|
993
|
+
// An object's own first: instanceof walks the whole prototype
|
|
994
|
+
// chain of an object, and a class's is as deep as its bases.
|
|
995
|
+
const traits = v.$traits;
|
|
996
|
+
if (traits !== undefined) {
|
|
997
|
+
return traits;
|
|
998
|
+
}
|
|
999
|
+
|
|
895
1000
|
return v instanceof FloatValue
|
|
896
1001
|
? this.builtinTraits("float")
|
|
897
1002
|
: v instanceof Namespace
|
|
898
1003
|
? this.builtinTraits("Namespace")
|
|
899
|
-
:
|
|
1004
|
+
: traits;
|
|
1005
|
+
}
|
|
900
1006
|
// As avmplus' toVTable: every number is a Number at run time, int or
|
|
901
1007
|
// not; int's and uint's traits only bind early, from static types.
|
|
902
1008
|
case "number":
|
|
@@ -918,12 +1024,19 @@ export class Runtime {
|
|
|
918
1024
|
private receiverTraits(v: Value): Traits | undefined {
|
|
919
1025
|
switch (typeof v) {
|
|
920
1026
|
case "object":
|
|
921
|
-
case "function":
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1027
|
+
case "function": {
|
|
1028
|
+
if (v === null) {
|
|
1029
|
+
return undefined;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// A float and a Namespace have none of their own (see traitsOf).
|
|
1033
|
+
const traits: Traits | undefined = v.$traits;
|
|
1034
|
+
if (traits !== undefined) {
|
|
1035
|
+
return traits;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
return v instanceof FloatValue ? this.builtinTraits("float") : undefined;
|
|
1039
|
+
}
|
|
927
1040
|
case "string":
|
|
928
1041
|
return this.stringTraits;
|
|
929
1042
|
case "number":
|
|
@@ -1126,7 +1239,7 @@ export class Runtime {
|
|
|
1126
1239
|
}
|
|
1127
1240
|
}
|
|
1128
1241
|
|
|
1129
|
-
return this.getProperty(o, this.
|
|
1242
|
+
return this.getProperty(o, this.indexName(mn, i));
|
|
1130
1243
|
}
|
|
1131
1244
|
|
|
1132
1245
|
/** obj[i] = v for a number i, as avmplus' setUintProperty. */
|
|
@@ -1140,13 +1253,103 @@ export class Runtime {
|
|
|
1140
1253
|
return;
|
|
1141
1254
|
}
|
|
1142
1255
|
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1256
|
+
const a: Value[] | undefined = o.$a;
|
|
1257
|
+
if (a !== undefined) {
|
|
1258
|
+
if (a !== SEALED_ELEMENTS && i !== 0xffffffff) {
|
|
1259
|
+
a[i] = v;
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
} else if (traits?.dynamic && traits.properties === null && !traits.refusesNames) {
|
|
1263
|
+
// As ScriptObject::setUintProperty: an object with no elements of its own keeps the
|
|
1264
|
+
// number among its dynamic properties, with no binding asked for.
|
|
1265
|
+
const d: Map<string, Value> | null | undefined = o.$d;
|
|
1266
|
+
if (d) {
|
|
1267
|
+
d.set(`${i}`, v);
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1146
1270
|
}
|
|
1147
1271
|
}
|
|
1148
1272
|
|
|
1149
|
-
this.setProperty(o, this.
|
|
1273
|
+
this.setProperty(o, this.indexName(mn, i), v);
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
/**
|
|
1277
|
+
* obj[k] for a key whose type the compiler does not know, by a name with
|
|
1278
|
+
* no namespace from the stack, as avmplus' getprop_index and
|
|
1279
|
+
* getpropertyHelper: a number is an index (getIndexed), an object a
|
|
1280
|
+
* Dictionary has as a key is read by itself, and anything else, a QName
|
|
1281
|
+
* and a Dictionary's absent key among them, is looked up by the name it
|
|
1282
|
+
* makes.
|
|
1283
|
+
*/
|
|
1284
|
+
getKeyed(o: Value, mn: Multiname, k: Value): Value {
|
|
1285
|
+
let r: Value;
|
|
1286
|
+
if (typeof k === "number") {
|
|
1287
|
+
r = this.getIndexed(o, mn, k);
|
|
1288
|
+
return r;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
if (typeof k === "object" && k !== null) {
|
|
1292
|
+
const keys: DictionaryKeys | undefined = o?.$keys;
|
|
1293
|
+
if (keys !== undefined) {
|
|
1294
|
+
if (!mn.elementName) {
|
|
1295
|
+
r = this.getProperty(o, this.runtimeName(mn, this.unkeyed(k)));
|
|
1296
|
+
return r;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
// What is there was put there by a name with the object as its key, which a QName's is not.
|
|
1300
|
+
const v = keys.get(k);
|
|
1301
|
+
if (v !== undefined) {
|
|
1302
|
+
return v;
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
r = this.getProperty(o, this.runtimeName(mn, k));
|
|
1308
|
+
return r;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
/**
|
|
1312
|
+
* An object given as the key of a Dictionary by a name with no public
|
|
1313
|
+
* namespace, as the name is made of it: avmplus' JIT asks a Dictionary
|
|
1314
|
+
* for an object only through getprop_index and setprop_index, which such
|
|
1315
|
+
* a name does not reach, so the object is its string, as on any other
|
|
1316
|
+
* receiver (initMultinameLate); a QName names what it names. Its
|
|
1317
|
+
* interpreter asks the Dictionary by any name, as a name made without
|
|
1318
|
+
* this does: the compiler leaves script and class initializers to that.
|
|
1319
|
+
*/
|
|
1320
|
+
private unkeyed(k: AsObject): Value {
|
|
1321
|
+
return k.$traits !== undefined && k.$local === undefined ? this.toString(k) : k;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
/** obj[k] = v for a key whose type the compiler does not know, as avmplus' setprop_index and setpropertyHelper. */
|
|
1325
|
+
setKeyed(o: Value, mn: Multiname, k: Value, v: Value): void {
|
|
1326
|
+
if (typeof k === "number") {
|
|
1327
|
+
this.setIndexed(o, mn, k, v);
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
if (typeof k === "object" && k !== null) {
|
|
1332
|
+
const keys: DictionaryKeys | undefined = o?.$keys;
|
|
1333
|
+
if (keys !== undefined) {
|
|
1334
|
+
if (!mn.elementName) {
|
|
1335
|
+
this.setProperty(o, this.runtimeName(mn, this.unkeyed(k)), v);
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
if (keys.replace(k, v)) {
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
// A new key: an object of traits, which a float and a Namespace are not, and no QName,
|
|
1344
|
+
// which names what it names. Those are added, or not, by the name they make.
|
|
1345
|
+
if (k.$traits !== undefined && k.$local === undefined) {
|
|
1346
|
+
keys.add(k, v);
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
this.setProperty(o, this.runtimeName(mn, k), v);
|
|
1150
1353
|
}
|
|
1151
1354
|
|
|
1152
1355
|
/**
|
|
@@ -1385,11 +1588,7 @@ export class Runtime {
|
|
|
1385
1588
|
/** fillCache (cache-fill.ts), for a name that caches on traits a cache knows `o` by. */
|
|
1386
1589
|
private fill(o: Value, traits: Traits, b: number, mn: Multiname): void {
|
|
1387
1590
|
const head = mn.cache;
|
|
1388
|
-
if (
|
|
1389
|
-
head === NO_CACHE ||
|
|
1390
|
-
(head.replaced === REPLACEMENTS && head.epoch === epoch()) ||
|
|
1391
|
-
this.receiverTraits(o) !== traits
|
|
1392
|
-
) {
|
|
1591
|
+
if (head === NO_CACHE || this.receiverTraits(o) !== traits) {
|
|
1393
1592
|
return;
|
|
1394
1593
|
}
|
|
1395
1594
|
|
|
@@ -1424,6 +1623,8 @@ export class Runtime {
|
|
|
1424
1623
|
f.$length = (method as CountedMethod).$length ?? method.length;
|
|
1425
1624
|
f.$id = (method as Method & { $id?: number }).$id ?? 0;
|
|
1426
1625
|
f.$closure = true;
|
|
1626
|
+
// Whose method it is, for moduleOf.
|
|
1627
|
+
f.$of = traits;
|
|
1427
1628
|
if (typeof o === "object") {
|
|
1428
1629
|
byMethod.set(method, f);
|
|
1429
1630
|
}
|
|
@@ -1740,8 +1941,7 @@ export class Runtime {
|
|
|
1740
1941
|
if (
|
|
1741
1942
|
typeof o === "object" &&
|
|
1742
1943
|
o !== null &&
|
|
1743
|
-
!(o instanceof Namespace)
|
|
1744
|
-
!(o instanceof FloatValue)
|
|
1944
|
+
(o.$traits !== undefined || !(o instanceof Namespace || o instanceof FloatValue))
|
|
1745
1945
|
) {
|
|
1746
1946
|
return o.$p;
|
|
1747
1947
|
}
|
|
@@ -2242,6 +2442,21 @@ export class Runtime {
|
|
|
2242
2442
|
return f;
|
|
2243
2443
|
}
|
|
2244
2444
|
|
|
2445
|
+
/**
|
|
2446
|
+
* The module a function's code is from, as avmplus has a method's code
|
|
2447
|
+
* context: a function's by the global object its scope begins with, a
|
|
2448
|
+
* bound method's by its receiver's class; null for a native, a class's
|
|
2449
|
+
* own method, or anything but a function.
|
|
2450
|
+
*/
|
|
2451
|
+
moduleOf(f: Value): object | null {
|
|
2452
|
+
const o = f as AsObject | null;
|
|
2453
|
+
if (o?.$of) {
|
|
2454
|
+
return (o.$of as Traits).abc;
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
return o?.$global ? this.traitsOf(o.$global).abc : null;
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2245
2460
|
/** A Function object calling `f`, with `global` as its receiver when it has none. */
|
|
2246
2461
|
newFunctionObject(f: Method, global: AsObject | null): AsObject {
|
|
2247
2462
|
const o = this.functionTraits.instance();
|
|
@@ -3124,7 +3339,13 @@ export class Runtime {
|
|
|
3124
3339
|
return new AsError(`${name}: ${message}`);
|
|
3125
3340
|
}
|
|
3126
3341
|
|
|
3127
|
-
|
|
3342
|
+
const made = this.constructClass(cls, [message, id]);
|
|
3343
|
+
// What the number stands for, kept beside the message for a host's report (detailOf).
|
|
3344
|
+
if (!this.debugger) {
|
|
3345
|
+
Object.defineProperty(made, "$detail", { value: errorMessage(true, id, args) });
|
|
3346
|
+
}
|
|
3347
|
+
|
|
3348
|
+
return made;
|
|
3128
3349
|
}
|
|
3129
3350
|
|
|
3130
3351
|
/** See stackOf in errors.ts. */
|
|
@@ -3132,6 +3353,11 @@ export class Runtime {
|
|
|
3132
3353
|
return stackOf(error);
|
|
3133
3354
|
}
|
|
3134
3355
|
|
|
3356
|
+
/** See detailOf in errors.ts. */
|
|
3357
|
+
detailOf(error: Value): string | null {
|
|
3358
|
+
return detailOf(error);
|
|
3359
|
+
}
|
|
3360
|
+
|
|
3135
3361
|
/** See errorMessage in errors.ts. */
|
|
3136
3362
|
errorMessage(id: number, args: Value[] = []): string {
|
|
3137
3363
|
return errorMessage(this.debugger, id, args);
|
|
@@ -3162,6 +3388,10 @@ export class Runtime {
|
|
|
3162
3388
|
return this.error("RangeError", 1506);
|
|
3163
3389
|
}
|
|
3164
3390
|
|
|
3391
|
+
if (e instanceof UnsupportedError) {
|
|
3392
|
+
return this.unsupportedCaught(e);
|
|
3393
|
+
}
|
|
3394
|
+
|
|
3165
3395
|
if (e instanceof Error && !(e instanceof AsError)) {
|
|
3166
3396
|
throw e;
|
|
3167
3397
|
}
|
|
@@ -3169,6 +3399,30 @@ export class Runtime {
|
|
|
3169
3399
|
return e;
|
|
3170
3400
|
}
|
|
3171
3401
|
|
|
3402
|
+
/** What a catch was told of already (unsupportedCaught). */
|
|
3403
|
+
private readonly unsupportedTold = new Set<string>();
|
|
3404
|
+
|
|
3405
|
+
/**
|
|
3406
|
+
* What is not supported as the AS3 error a catch gets: an
|
|
3407
|
+
* IllegalOperationError with its message, or an Error where the player's
|
|
3408
|
+
* classes are not loaded; said once to the host, as the code may say nothing.
|
|
3409
|
+
*/
|
|
3410
|
+
private unsupportedCaught(e: UnsupportedError): Value {
|
|
3411
|
+
if (!this.unsupportedTold.has(e.message)) {
|
|
3412
|
+
this.unsupportedTold.add(e.message);
|
|
3413
|
+
this.onUnsupported?.(e.message);
|
|
3414
|
+
}
|
|
3415
|
+
|
|
3416
|
+
let cls: AsObject;
|
|
3417
|
+
try {
|
|
3418
|
+
cls = this.resolve(this.cls(namespace(NS_Public, "flash.errors"), "IllegalOperationError"));
|
|
3419
|
+
} catch {
|
|
3420
|
+
cls = this.builtinClass("Error");
|
|
3421
|
+
}
|
|
3422
|
+
|
|
3423
|
+
return this.constructClass(cls, [e.message, 0]);
|
|
3424
|
+
}
|
|
3425
|
+
|
|
3172
3426
|
catches(e: Value, type: TypeRef): boolean {
|
|
3173
3427
|
return this.isInstanceOf(e, this.traitsOfType(type));
|
|
3174
3428
|
}
|
|
@@ -3178,7 +3432,7 @@ export class Runtime {
|
|
|
3178
3432
|
}
|
|
3179
3433
|
|
|
3180
3434
|
unsupported(what: string): Error {
|
|
3181
|
-
return new
|
|
3435
|
+
return new UnsupportedError(`swf2es: ${what} is not supported yet`);
|
|
3182
3436
|
}
|
|
3183
3437
|
|
|
3184
3438
|
// Class aliases and the classes AMF makes (amf-classes.ts).
|
package/src/avm2/traits.ts
CHANGED
|
@@ -16,7 +16,7 @@ import type { Domain } from "./domain.js";
|
|
|
16
16
|
import { DynamicProperties } from "./dynamic-properties.js";
|
|
17
17
|
import type { ClassHook, IndexHook, PropertyHook } from "./hooks.js";
|
|
18
18
|
import type { Multiname, Namespace } from "./names.js";
|
|
19
|
-
import { invalidate } from "./property-cache.js";
|
|
19
|
+
import { invalidate, type PropertyCache } from "./property-cache.js";
|
|
20
20
|
|
|
21
21
|
// Binding kinds, as the compiler encodes them: kind | id << 3.
|
|
22
22
|
export const BIND_Method = 1;
|
|
@@ -160,6 +160,12 @@ export class Traits {
|
|
|
160
160
|
interfaceKeys: Map<Traits, (string | undefined)[]> | null = null;
|
|
161
161
|
lastInterface: Traits | null = null;
|
|
162
162
|
lastInterfaceKeys: (string | undefined)[] = [];
|
|
163
|
+
/**
|
|
164
|
+
* The cache entries for these traits of names looked up on more traits
|
|
165
|
+
* than their own caches keep (property-cache.ts), by name; emptied with
|
|
166
|
+
* every cache.
|
|
167
|
+
*/
|
|
168
|
+
manyTraitsEntries: Map<Multiname, PropertyCache> | null = null;
|
|
163
169
|
|
|
164
170
|
constructor(
|
|
165
171
|
readonly name: string,
|