@swf2es/runtime 0.1.12 → 0.1.14
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/names.d.ts +1 -1
- package/dist/avm2/names.d.ts.map +1 -1
- package/dist/avm2/names.js +1 -1
- package/dist/avm2/names.js.map +1 -1
- package/dist/avm2/natives/bytearray.d.ts.map +1 -1
- package/dist/avm2/natives/bytearray.js +22 -0
- package/dist/avm2/natives/bytearray.js.map +1 -1
- package/dist/avm2/property-cache.d.ts +1 -1
- package/dist/avm2/property-cache.d.ts.map +1 -1
- package/dist/avm2/property-cache.js +1 -1
- package/dist/avm2/property-cache.js.map +1 -1
- package/dist/avm2/runtime.d.ts +22 -2
- package/dist/avm2/runtime.d.ts.map +1 -1
- package/dist/avm2/runtime.js +127 -10
- package/dist/avm2/runtime.js.map +1 -1
- package/package.json +2 -2
- package/src/avm2/names.ts +1 -1
- package/src/avm2/natives/bytearray.ts +26 -0
- package/src/avm2/property-cache.ts +1 -1
- package/src/avm2/runtime.ts +134 -10
package/dist/avm2/runtime.js
CHANGED
|
@@ -41,6 +41,19 @@ export { NOT_FOUND } from "./descriptors.js";
|
|
|
41
41
|
* checks for it, and fails as a sealed object does.
|
|
42
42
|
*/
|
|
43
43
|
export const SEALED_ELEMENTS = Object.freeze([]);
|
|
44
|
+
/**
|
|
45
|
+
* The scopes a contested function's chain has past those all its creators'
|
|
46
|
+
* chains share, in one entry of the chain (Runtime.packScope): `w` has a bit
|
|
47
|
+
* for each that is a with scope, as a Scope's has.
|
|
48
|
+
*/
|
|
49
|
+
class ScopePack {
|
|
50
|
+
scopes;
|
|
51
|
+
w;
|
|
52
|
+
constructor(scopes, w) {
|
|
53
|
+
this.scopes = scopes;
|
|
54
|
+
this.w = w;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
44
57
|
/** The value of `o`'s dynamic property `name`, or NOT_FOUND. */
|
|
45
58
|
function dynamicValue(o, name) {
|
|
46
59
|
const d = o.$d;
|
|
@@ -171,6 +184,8 @@ export class Runtime {
|
|
|
171
184
|
/** Empty the inline caches, so that they keep no traits or code of what is being let go of. */
|
|
172
185
|
forgetCaches() {
|
|
173
186
|
invalidate();
|
|
187
|
+
this.stringNames = new WeakMap();
|
|
188
|
+
this.stringNamesEpoch = epoch();
|
|
174
189
|
}
|
|
175
190
|
// Names.
|
|
176
191
|
ns(kind, uri) {
|
|
@@ -227,15 +242,27 @@ export class Runtime {
|
|
|
227
242
|
typeName(_N, _V, _S, base, param) {
|
|
228
243
|
return new TypeName(base, [param]);
|
|
229
244
|
}
|
|
230
|
-
/**
|
|
231
|
-
|
|
245
|
+
/**
|
|
246
|
+
* A multiname with its runtime namespace and name, from the stack: `a`
|
|
247
|
+
* the namespace where it has one at run time, and then `b` the name,
|
|
248
|
+
* else `a` the name. Two parameters, not a rest: a lookup by a name made
|
|
249
|
+
* at run time calls this each time, and a rest is an array each call.
|
|
250
|
+
*/
|
|
251
|
+
runtimeName(mn, a, b) {
|
|
252
|
+
if (!mn.runtimeNs &&
|
|
253
|
+
mn.runtimeName &&
|
|
254
|
+
mn.cache !== NO_CACHE &&
|
|
255
|
+
typeof a === "string" &&
|
|
256
|
+
a.length <= 128) {
|
|
257
|
+
return this.stringName(mn, a);
|
|
258
|
+
}
|
|
232
259
|
let namespaces = mn.namespaces;
|
|
233
260
|
let versions = mn.versions;
|
|
234
|
-
let
|
|
261
|
+
let last = a;
|
|
235
262
|
if (mn.runtimeNs) {
|
|
236
|
-
|
|
237
|
-
namespaces = [this.namespaceOf(ns)];
|
|
263
|
+
namespaces = [this.namespaceOf(a)];
|
|
238
264
|
versions = [255];
|
|
265
|
+
last = b;
|
|
239
266
|
}
|
|
240
267
|
const kind = mn.kind === CONSTANT_RTQname ||
|
|
241
268
|
mn.kind === CONSTANT_RTQnameA ||
|
|
@@ -245,7 +272,7 @@ export class Runtime {
|
|
|
245
272
|
: mn.kind;
|
|
246
273
|
let name = mn.name;
|
|
247
274
|
if (mn.runtimeName) {
|
|
248
|
-
const part =
|
|
275
|
+
const part = last;
|
|
249
276
|
if (part?.$local !== undefined && !(part instanceof Namespace)) {
|
|
250
277
|
// A Proxy's QName of a multiname names its namespaces still.
|
|
251
278
|
if (part.$mn) {
|
|
@@ -272,6 +299,44 @@ export class Runtime {
|
|
|
272
299
|
named.domain = mn.domain;
|
|
273
300
|
return named;
|
|
274
301
|
}
|
|
302
|
+
stringNames = new WeakMap();
|
|
303
|
+
stringNamesEpoch = -1;
|
|
304
|
+
/** Repeated string keys share lookup work, without retaining arbitrary keys or retired domains. */
|
|
305
|
+
stringName(base, name) {
|
|
306
|
+
const current = epoch();
|
|
307
|
+
if (this.stringNamesEpoch !== current) {
|
|
308
|
+
this.stringNames = new WeakMap();
|
|
309
|
+
this.stringNamesEpoch = current;
|
|
310
|
+
}
|
|
311
|
+
let cache = this.stringNames.get(base);
|
|
312
|
+
if (cache && cache.misses >= 0) {
|
|
313
|
+
const known = cache.names.get(name);
|
|
314
|
+
if (known) {
|
|
315
|
+
cache.misses = 0;
|
|
316
|
+
return known;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const named = new Multiname(base.kind, base.namespaces, base.versions, name, base.attribute);
|
|
320
|
+
named.domain = base.domain;
|
|
321
|
+
if (!cache) {
|
|
322
|
+
cache = { names: new Map(), misses: 0 };
|
|
323
|
+
this.stringNames.set(base, cache);
|
|
324
|
+
}
|
|
325
|
+
// Replacing filled inline caches would retain their heads until the next invalidation.
|
|
326
|
+
if (cache.misses < 0) {
|
|
327
|
+
return named;
|
|
328
|
+
}
|
|
329
|
+
if (cache.names.size < 64) {
|
|
330
|
+
named.cache = UNFILLED;
|
|
331
|
+
cache.names.set(name, named);
|
|
332
|
+
}
|
|
333
|
+
else if (++cache.misses >= 64) {
|
|
334
|
+
// A stream of unrelated keys should stop paying for a map lookup on every access.
|
|
335
|
+
cache.names.clear();
|
|
336
|
+
cache.misses = -1;
|
|
337
|
+
}
|
|
338
|
+
return named;
|
|
339
|
+
}
|
|
275
340
|
/** An object's string, as the name of an object that is not a Dictionary. */
|
|
276
341
|
keyName = (key) => this.toString(key);
|
|
277
342
|
/** The runtime namespace a Namespace value stands for. */
|
|
@@ -427,6 +492,20 @@ export class Runtime {
|
|
|
427
492
|
chain.w = outer.w | (withs << outer.length);
|
|
428
493
|
return chain;
|
|
429
494
|
}
|
|
495
|
+
/**
|
|
496
|
+
* `chain` for a contested function whose creators' chains differ in
|
|
497
|
+
* length (the codegen's Scope.merged): its first `at` entries, which all
|
|
498
|
+
* of them have, then one entry holding the rest, a ScopePack, searched in
|
|
499
|
+
* its place. The function's chain is then as long whichever creator made
|
|
500
|
+
* it, and what it creates finds its own scopes where it was compiled to.
|
|
501
|
+
* Called by compiled code.
|
|
502
|
+
*/
|
|
503
|
+
packScope(chain, at) {
|
|
504
|
+
const packed = chain.slice(0, at);
|
|
505
|
+
packed.push(new ScopePack(chain.slice(at), chain.w >>> at));
|
|
506
|
+
packed.w = (chain.w & ((1 << at) - 1)) | (1 << at);
|
|
507
|
+
return packed;
|
|
508
|
+
}
|
|
430
509
|
/**
|
|
431
510
|
* The global object of the script that defines `mn`, run first if it has
|
|
432
511
|
* not run. A module reads `mn.global` before calling this: once set it
|
|
@@ -454,8 +533,15 @@ export class Runtime {
|
|
|
454
533
|
}
|
|
455
534
|
}
|
|
456
535
|
for (let i = outer.length - 1; i >= 0; i--) {
|
|
457
|
-
|
|
458
|
-
|
|
536
|
+
const o = outer[i];
|
|
537
|
+
if (o instanceof ScopePack) {
|
|
538
|
+
const found = this.packHas(o, mn);
|
|
539
|
+
if (found !== undefined) {
|
|
540
|
+
return found;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
else if (this.scopeHas(o, mn, (outer.w >> i) & 1)) {
|
|
544
|
+
return o;
|
|
459
545
|
}
|
|
460
546
|
}
|
|
461
547
|
return this.global(mn, outer.length ? outer[0] : locals[0], strict);
|
|
@@ -506,6 +592,22 @@ export class Runtime {
|
|
|
506
592
|
}
|
|
507
593
|
return global;
|
|
508
594
|
}
|
|
595
|
+
/** The innermost scope of a pack that has `mn`, a pack in it searched in turn; undefined if none has. */
|
|
596
|
+
packHas(pack, mn) {
|
|
597
|
+
for (let i = pack.scopes.length - 1; i >= 0; i--) {
|
|
598
|
+
const o = pack.scopes[i];
|
|
599
|
+
if (o instanceof ScopePack) {
|
|
600
|
+
const found = this.packHas(o, mn);
|
|
601
|
+
if (found !== undefined) {
|
|
602
|
+
return found;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
else if (this.scopeHas(o, mn, (pack.w >> i) & 1)) {
|
|
606
|
+
return o;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return undefined;
|
|
610
|
+
}
|
|
509
611
|
scopeHas(o, mn, isWith) {
|
|
510
612
|
if (isWith) {
|
|
511
613
|
return this.withHas(o, mn);
|
|
@@ -756,8 +858,23 @@ export class Runtime {
|
|
|
756
858
|
return traits.getIndex(o, i, this);
|
|
757
859
|
}
|
|
758
860
|
const a = o.$a;
|
|
759
|
-
if (a !== undefined
|
|
760
|
-
|
|
861
|
+
if (a !== undefined) {
|
|
862
|
+
if (i in a) {
|
|
863
|
+
return a[i];
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
else if (traits?.dynamic && traits.properties === null && !traits.refusesNames) {
|
|
867
|
+
// As ScriptObject::getUintProperty: an object with no elements of its own looks the
|
|
868
|
+
// number up among its dynamic properties first, with no binding asked for; a table
|
|
869
|
+
// keyed by numbers, as a Dictionary of handles, is read so on every access. One not
|
|
870
|
+
// there, or there as undefined, is looked up in full.
|
|
871
|
+
const d = o.$d;
|
|
872
|
+
if (d) {
|
|
873
|
+
const v = d.get(`${i}`);
|
|
874
|
+
if (v !== undefined) {
|
|
875
|
+
return v;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
761
878
|
}
|
|
762
879
|
}
|
|
763
880
|
return this.getProperty(o, this.runtimeName(mn, i));
|