@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.
- package/dist/avm2/amf.d.ts +1 -1
- package/dist/avm2/amf.d.ts.map +1 -1
- package/dist/avm2/amf.js +4 -3
- package/dist/avm2/amf.js.map +1 -1
- package/dist/avm2/as3-types.d.ts +25 -0
- package/dist/avm2/as3-types.d.ts.map +1 -0
- package/dist/avm2/as3-types.js +9 -0
- package/dist/avm2/as3-types.js.map +1 -0
- package/dist/avm2/builtin-types.generated.d.ts +553 -0
- package/dist/avm2/builtin-types.generated.d.ts.map +1 -0
- package/dist/avm2/builtin-types.generated.js +11 -0
- package/dist/avm2/builtin-types.generated.js.map +1 -0
- 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/classes.d.ts.map +1 -1
- package/dist/avm2/classes.js +10 -3
- package/dist/avm2/classes.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 +20 -1
- package/dist/avm2/errors.js.map +1 -1
- package/dist/avm2/index.d.ts +4 -0
- package/dist/avm2/index.d.ts.map +1 -1
- package/dist/avm2/index.js +3 -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/array.js +1 -1
- package/dist/avm2/natives/array.js.map +1 -1
- package/dist/avm2/natives/bytearray.d.ts.map +1 -1
- package/dist/avm2/natives/bytearray.js +6 -11
- package/dist/avm2/natives/bytearray.js.map +1 -1
- package/dist/avm2/natives/describe.js +6 -1
- package/dist/avm2/natives/describe.js.map +1 -1
- package/dist/avm2/natives/object.d.ts.map +1 -1
- package/dist/avm2/natives/object.js +5 -2
- package/dist/avm2/natives/object.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/natives/vector.js +1 -1
- package/dist/avm2/natives/vector.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 +101 -1
- package/dist/avm2/runtime.d.ts.map +1 -1
- package/dist/avm2/runtime.js +351 -45
- package/dist/avm2/runtime.js.map +1 -1
- package/dist/avm2/traits.d.ts +13 -0
- package/dist/avm2/traits.d.ts.map +1 -1
- package/dist/avm2/traits.js +23 -0
- package/dist/avm2/traits.js.map +1 -1
- package/package.json +2 -2
- package/src/avm2/amf.ts +4 -3
- package/src/avm2/as3-types.ts +41 -0
- package/src/avm2/builtin-types.generated.ts +619 -0
- package/src/avm2/cache-fill.ts +5 -7
- package/src/avm2/classes.ts +10 -3
- package/src/avm2/descriptors.ts +6 -2
- package/src/avm2/errors.ts +21 -1
- package/src/avm2/index.ts +6 -0
- package/src/avm2/names.ts +13 -0
- package/src/avm2/natives/array.ts +1 -1
- package/src/avm2/natives/bytearray.ts +6 -14
- package/src/avm2/natives/describe.ts +8 -1
- package/src/avm2/natives/object.ts +5 -2
- package/src/avm2/natives/proxy.ts +20 -4
- package/src/avm2/natives/vector.ts +1 -1
- package/src/avm2/options.ts +5 -0
- package/src/avm2/property-cache.ts +57 -11
- package/src/avm2/runtime.ts +407 -53
- package/src/avm2/traits.ts +27 -1
package/src/avm2/errors.ts
CHANGED
|
@@ -11,6 +11,14 @@ import type { Runtime } from "./runtime.js";
|
|
|
11
11
|
/** Thrown for an AS3 exception that is an Error the runtime made, before its class existed. */
|
|
12
12
|
export class AsError extends Error {}
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Thrown where a SWF asks for something swf2es has not got (Runtime.unsupported).
|
|
16
|
+
* Uncaught, it stops the script as any error of the runtime's own does; an
|
|
17
|
+
* AS3 catch gets it as an AS3 error (Runtime.caught), since content written
|
|
18
|
+
* for several runtimes guards what one of them may lack with try and catch.
|
|
19
|
+
*/
|
|
20
|
+
export class UnsupportedError extends Error {}
|
|
21
|
+
|
|
14
22
|
/**
|
|
15
23
|
* Where an AS3 error was made, as JavaScript stack lines from the first
|
|
16
24
|
* compiled method's (their names begin with `$`), the runtime's and the
|
|
@@ -29,6 +37,16 @@ export function stackOf(error: Value): string | null {
|
|
|
29
37
|
return frames.slice(Math.max(0, first)).join("\n");
|
|
30
38
|
}
|
|
31
39
|
|
|
40
|
+
/**
|
|
41
|
+
* What an error the runtime made says in full, its text and arguments as
|
|
42
|
+
* debugger mode words them, for a host to show: the release player's
|
|
43
|
+
* message, which the SWF sees, is the number alone. Null for any other value.
|
|
44
|
+
*/
|
|
45
|
+
export function detailOf(error: Value): string | null {
|
|
46
|
+
const detail = (error as { $detail?: unknown } | null)?.$detail;
|
|
47
|
+
return typeof detail === "string" ? detail : null;
|
|
48
|
+
}
|
|
49
|
+
|
|
32
50
|
/**
|
|
33
51
|
* Error `id`'s message as AS3 sees it: its number, as the release player
|
|
34
52
|
* and avmshell give it, or with its text and arguments in debugger mode.
|
|
@@ -49,6 +67,8 @@ export function describe(rt: Runtime, v: Value): string {
|
|
|
49
67
|
return rt.toString(v);
|
|
50
68
|
}
|
|
51
69
|
|
|
70
|
+
// Its class's name without the package, as "Object@0", the address left out.
|
|
52
71
|
const name: string = v.$traits.name;
|
|
53
|
-
|
|
72
|
+
const colons = name.lastIndexOf("::");
|
|
73
|
+
return `${colons < 0 ? name : name.slice(colons + 2)}@0`;
|
|
54
74
|
}
|
package/src/avm2/index.ts
CHANGED
|
@@ -4,10 +4,14 @@ import type { RuntimeOptions } from "./options.js";
|
|
|
4
4
|
import { Runtime } from "./runtime.js";
|
|
5
5
|
|
|
6
6
|
export { Reader as Amf3Reader, Writer as Amf3Writer } from "./amf.js";
|
|
7
|
+
// The classes' declarations, for natives to reach AS3 objects by (Runtime.prop).
|
|
8
|
+
export type { MethodName, PropertyName, Ref, WritableName } from "./as3-types.js";
|
|
7
9
|
/** avmplus' builtin.abc (MPL-2.0), which every runtime loads first. */
|
|
8
10
|
export { builtinLibrary } from "./builtin-library.js";
|
|
11
|
+
export type { BuiltinClasses, BuiltinStatics } from "./builtin-types.generated.js";
|
|
9
12
|
export type { Abc, AsObject, CompileUnit, FoundDefinition, Method, Value } from "./descriptors.js";
|
|
10
13
|
export { type Domain, frameScripts, frameSites, siteScript, stackFrames } from "./domain.js";
|
|
14
|
+
export { UnsupportedError } from "./errors.js";
|
|
11
15
|
export { FloatValue, hasFloatLibrary } from "./float.js";
|
|
12
16
|
export { floatLibrary } from "./float-library.js";
|
|
13
17
|
export type { ClassHook, NativesProvider } from "./hooks.js";
|
|
@@ -67,6 +71,8 @@ export { invalidate as clearPropertyCaches } from "./property-cache.js";
|
|
|
67
71
|
export { Runtime } from "./runtime.js";
|
|
68
72
|
// methodKey: where a method sits on its traits, for a player that makes a closure of its own.
|
|
69
73
|
export { instanceVarField, methodKey, setStaticVar } from "./traits.js";
|
|
74
|
+
// avmplus' UTF-8, for a player's bytes that must read and write as ByteArray's do.
|
|
75
|
+
export { fromUtf8, utf8 } from "./utf8.js";
|
|
70
76
|
|
|
71
77
|
/** A runtime with the builtins' natives, for modules compiled from builtin.abc and after. */
|
|
72
78
|
export function createRuntime(options: RuntimeOptions = {}): Runtime {
|
package/src/avm2/names.ts
CHANGED
|
@@ -85,6 +85,11 @@ export const CONSTANT_MultinameA = 0x0e;
|
|
|
85
85
|
export const CONSTANT_MultinameL = 0x1b;
|
|
86
86
|
export const CONSTANT_MultinameLA = 0x1c;
|
|
87
87
|
|
|
88
|
+
// What a name made at run time was made of (Multiname.made).
|
|
89
|
+
export const MADE_Unknown = 0;
|
|
90
|
+
export const MADE_Number = 1;
|
|
91
|
+
export const MADE_Value = 2;
|
|
92
|
+
|
|
88
93
|
/**
|
|
89
94
|
* A name as a property lookup uses it: its namespaces, each with the API
|
|
90
95
|
* version it sees bindings at, and its local name, null for any name.
|
|
@@ -121,6 +126,14 @@ export class Multiname {
|
|
|
121
126
|
cls: AsObject | null = null;
|
|
122
127
|
/** Fixed at construction so indexed access needs no getter in its hot path. */
|
|
123
128
|
readonly elementName: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* What made it, where the code's own value did at run time: a number
|
|
131
|
+
* read or written as an element (MADE_Number), which avmplus hands a
|
|
132
|
+
* Proxy as its string, or a string or any other value (MADE_Value), which
|
|
133
|
+
* it hands it as a QName. MADE_Unknown for a name of the code's own and
|
|
134
|
+
* one the runtime made.
|
|
135
|
+
*/
|
|
136
|
+
made = MADE_Unknown;
|
|
124
137
|
|
|
125
138
|
constructor(
|
|
126
139
|
readonly kind: number,
|
|
@@ -760,13 +760,11 @@ export function byteArrayNatives(rt: Runtime): Natives {
|
|
|
760
760
|
return rt.defaultObjectEncoding;
|
|
761
761
|
}
|
|
762
762
|
|
|
763
|
+
// Unchecked, unlike an instance's: avmshell and Flash take any uint, a
|
|
764
|
+
// new ByteArray starts with it, and writeObject reads an encoding below
|
|
765
|
+
// 3 as AMF0 (#1508 in avmshell), any other as AMF3.
|
|
763
766
|
static set defaultObjectEncoding(v: Value) {
|
|
764
|
-
|
|
765
|
-
if (e !== kAMF0 && e !== kAMF3) {
|
|
766
|
-
throw rt.error("ArgumentError", 2008, "objectEncoding");
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
rt.defaultObjectEncoding = e;
|
|
767
|
+
rt.defaultObjectEncoding = rt.toUint(v);
|
|
770
768
|
}
|
|
771
769
|
|
|
772
770
|
get length() {
|
|
@@ -1215,13 +1213,11 @@ export function byteArrayNatives(rt: Runtime): Natives {
|
|
|
1215
1213
|
},
|
|
1216
1214
|
};
|
|
1217
1215
|
|
|
1216
|
+
// AMF3 whatever their objectEncoding, as ObjectOutputObject::writeObject
|
|
1217
|
+
// and ObjectInputObject::readObject: avmshell and Flash alike.
|
|
1218
1218
|
class ObjectOutputNatives {
|
|
1219
1219
|
writeObject(this: AsObject, v: Value): void {
|
|
1220
1220
|
const stream: Writer = this.$amf;
|
|
1221
|
-
if (stream.objectEncoding !== 3) {
|
|
1222
|
-
throw rt.unsupported("AMF0");
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
1221
|
stream.value(v);
|
|
1226
1222
|
}
|
|
1227
1223
|
}
|
|
@@ -1229,10 +1225,6 @@ export function byteArrayNatives(rt: Runtime): Natives {
|
|
|
1229
1225
|
class ObjectInputNatives {
|
|
1230
1226
|
readObject(this: AsObject): Value {
|
|
1231
1227
|
const stream: Reader = this.$amf;
|
|
1232
|
-
if (stream.objectEncoding !== 3) {
|
|
1233
|
-
throw rt.unsupported("AMF0");
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
1228
|
return stream.value();
|
|
1237
1229
|
}
|
|
1238
1230
|
|
|
@@ -121,7 +121,14 @@ function chooseTraits(rt: Runtime, v: Value): Traits | string {
|
|
|
121
121
|
return "null";
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
// An int atom: -0 is a double.
|
|
125
|
+
if (
|
|
126
|
+
typeof v === "number" &&
|
|
127
|
+
(v | 0) === v &&
|
|
128
|
+
v >= -(1 << 28) &&
|
|
129
|
+
v < 1 << 28 &&
|
|
130
|
+
!Object.is(v, -0)
|
|
131
|
+
) {
|
|
125
132
|
return rt.traitsOfType("int");
|
|
126
133
|
}
|
|
127
134
|
|
|
@@ -191,9 +191,12 @@ function shortName(qualified: string): string {
|
|
|
191
191
|
|
|
192
192
|
export function qualifiedClassName(rt: Runtime, v: Value): string {
|
|
193
193
|
switch (typeof v) {
|
|
194
|
-
// As TypeDescriber::chooseTraits: an int is one that fits avmplus' 29-bit
|
|
194
|
+
// As TypeDescriber::chooseTraits: an int is one that fits avmplus' 29-bit
|
|
195
|
+
// int atom, which -0, always a double, never is.
|
|
195
196
|
case "number":
|
|
196
|
-
return (v | 0) === v && v >= -(1 << 28) && v < 1 << 28
|
|
197
|
+
return (v | 0) === v && v >= -(1 << 28) && v < 1 << 28 && !Object.is(v, -0)
|
|
198
|
+
? "int"
|
|
199
|
+
: "Number";
|
|
197
200
|
case "string":
|
|
198
201
|
return "String";
|
|
199
202
|
case "boolean":
|
|
@@ -7,7 +7,16 @@
|
|
|
7
7
|
|
|
8
8
|
import type { AsObject, Value } from "../descriptors.js";
|
|
9
9
|
import type { ClassHook, PropertyHook } from "../hooks.js";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
MADE_Number,
|
|
12
|
+
MADE_Unknown,
|
|
13
|
+
type Multiname,
|
|
14
|
+
NS_Public,
|
|
15
|
+
namespace,
|
|
16
|
+
publicNs,
|
|
17
|
+
publicQname,
|
|
18
|
+
qname,
|
|
19
|
+
} from "../names.js";
|
|
11
20
|
import type { Runtime } from "../runtime.js";
|
|
12
21
|
import type { Natives } from "./define.js";
|
|
13
22
|
|
|
@@ -15,8 +24,11 @@ const FLASH_PROXY = namespace(NS_Public, "http://www.adobe.com/2006/actionscript
|
|
|
15
24
|
|
|
16
25
|
/**
|
|
17
26
|
* The name a flash_proxy method gets for `mn`: a QName that was the name
|
|
18
|
-
* stays one
|
|
19
|
-
*
|
|
27
|
+
* stays one; a number read or written as an element comes as its string,
|
|
28
|
+
* as avmplus' getUintProperty and getpropertylate_d intern it, whatever
|
|
29
|
+
* the number; a string or another value of the code's comes as a QName,
|
|
30
|
+
* "0" too, as does any other name, in its namespace. A name whose maker
|
|
31
|
+
* is not known is told by its string: an index's is the string.
|
|
20
32
|
*/
|
|
21
33
|
function nameFor(rt: Runtime, mn: Multiname): Value {
|
|
22
34
|
if (mn.key !== undefined && (mn.key as AsObject)?.$local !== undefined) {
|
|
@@ -24,7 +36,11 @@ function nameFor(rt: Runtime, mn: Multiname): Value {
|
|
|
24
36
|
}
|
|
25
37
|
|
|
26
38
|
const name = mn.name;
|
|
27
|
-
if (
|
|
39
|
+
if (
|
|
40
|
+
name !== null &&
|
|
41
|
+
!mn.attribute &&
|
|
42
|
+
(mn.made === MADE_Number || (mn.made === MADE_Unknown && isIndex(name)))
|
|
43
|
+
) {
|
|
28
44
|
return name;
|
|
29
45
|
}
|
|
30
46
|
|
|
@@ -330,7 +330,7 @@ for (const [kind, convert, fill, elementsOf, store] of VECTORS) {
|
|
|
330
330
|
|
|
331
331
|
const source = args[0];
|
|
332
332
|
if (!isObject(source)) {
|
|
333
|
-
throw rt.error("TypeError", 1034, rt.describe(source), cls.$it.
|
|
333
|
+
throw rt.error("TypeError", 1034, rt.describe(source), cls.$it.errorName);
|
|
334
334
|
}
|
|
335
335
|
|
|
336
336
|
if (source.$traits === cls.$it) {
|
package/src/avm2/options.ts
CHANGED
|
@@ -8,6 +8,11 @@ import type { Runtime } from "./runtime.js";
|
|
|
8
8
|
export interface RuntimeOptions {
|
|
9
9
|
/** Where trace and print write a line. */
|
|
10
10
|
print?: (line: string) => void;
|
|
11
|
+
/**
|
|
12
|
+
* Told what is not supported that the code asked for and caught, once
|
|
13
|
+
* for each: caught, it reaches no one else.
|
|
14
|
+
*/
|
|
15
|
+
onUnsupported?: (message: string) => void;
|
|
11
16
|
/**
|
|
12
17
|
* Behave as the debugger player: error messages carry avmplus' text
|
|
13
18
|
* ("Error #1009: Cannot access ..."), and System.isDebugger is true. By
|
|
@@ -11,8 +11,13 @@
|
|
|
11
11
|
// entry still looks for the property on the object, which may have lost it,
|
|
12
12
|
// then on its prototypes, which may have gained or lost it, and leaves
|
|
13
13
|
// what neither has to the full lookup.
|
|
14
|
+
//
|
|
15
|
+
// A name looked up on more traits than its cache keeps, as `x` is on every
|
|
16
|
+
// class with an x, stops replacing entries; what it finds on traits it has
|
|
17
|
+
// no entry for is kept on those traits instead, by name, so that the one
|
|
18
|
+
// class it is mostly read on is not left to the full lookup.
|
|
14
19
|
|
|
15
|
-
import type { Method, TypeRef } from "./descriptors.js";
|
|
20
|
+
import type { AsObject, Method, TypeRef } from "./descriptors.js";
|
|
16
21
|
import type { Multiname } from "./names.js";
|
|
17
22
|
import type { Traits } from "./traits.js";
|
|
18
23
|
|
|
@@ -25,7 +30,7 @@ export const IC_GetSet = 5;
|
|
|
25
30
|
export const IC_Method = 6;
|
|
26
31
|
/** A method a property hook resolves the name of for get and set, as XML's: callproperty only. */
|
|
27
32
|
export const IC_Call = 7;
|
|
28
|
-
/** No binding: a dynamic property, the object's own or else a prototype's, if either has it. */
|
|
33
|
+
/** No binding: a dynamic property, the object's own (a dynamic object's) or else a prototype's, if either has it. */
|
|
29
34
|
export const IC_Dynamic = 8;
|
|
30
35
|
|
|
31
36
|
/** Entries a name keeps, for as many traits, before it replaces the oldest. */
|
|
@@ -35,7 +40,7 @@ const ENTRIES = 8;
|
|
|
35
40
|
* keeps would replace one on every miss, and still miss as often. It keeps
|
|
36
41
|
* those it has until the epoch changes.
|
|
37
42
|
*/
|
|
38
|
-
|
|
43
|
+
const REPLACEMENTS = 16;
|
|
39
44
|
|
|
40
45
|
/**
|
|
41
46
|
* One traits' entry for a name. A name's first entry heads its list and
|
|
@@ -70,6 +75,8 @@ export function epoch(): number {
|
|
|
70
75
|
|
|
71
76
|
/** The caches filled in this epoch, which invalidate empties so that they keep no traits or code. */
|
|
72
77
|
const filled: PropertyCache[] = [];
|
|
78
|
+
/** The traits given entries of their own in this epoch (Traits.manyTraitsEntries). */
|
|
79
|
+
const filledTraits: Traits[] = [];
|
|
73
80
|
|
|
74
81
|
/** Note that `head`'s cache is being filled in this epoch. */
|
|
75
82
|
function filling(head: PropertyCache): void {
|
|
@@ -88,6 +95,11 @@ export function invalidate(): void {
|
|
|
88
95
|
}
|
|
89
96
|
|
|
90
97
|
filled.length = 0;
|
|
98
|
+
for (let i = 0; i < filledTraits.length; i++) {
|
|
99
|
+
filledTraits[i].manyTraitsEntries = null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
filledTraits.length = 0;
|
|
91
103
|
current++;
|
|
92
104
|
}
|
|
93
105
|
|
|
@@ -98,11 +110,12 @@ export const UNFILLED = new PropertyCache();
|
|
|
98
110
|
|
|
99
111
|
/** The entry of `mn`'s cache for objects with traits `t`, or null. */
|
|
100
112
|
export function cached(t: Traits | undefined, mn: Multiname): PropertyCache | null {
|
|
101
|
-
|
|
102
|
-
if (t === undefined ||
|
|
113
|
+
const head = mn.cache;
|
|
114
|
+
if (t === undefined || head.epoch !== epoch()) {
|
|
103
115
|
return null;
|
|
104
116
|
}
|
|
105
117
|
|
|
118
|
+
let e: PropertyCache | null = head;
|
|
106
119
|
do {
|
|
107
120
|
if (e.traits === t) {
|
|
108
121
|
return e;
|
|
@@ -111,16 +124,20 @@ export function cached(t: Traits | undefined, mn: Multiname): PropertyCache | nu
|
|
|
111
124
|
e = e.next;
|
|
112
125
|
} while (e !== null);
|
|
113
126
|
|
|
127
|
+
if (head.replaced === REPLACEMENTS && t.manyTraitsEntries !== null) {
|
|
128
|
+
return t.manyTraitsEntries.get(mn) ?? null;
|
|
129
|
+
}
|
|
130
|
+
|
|
114
131
|
return null;
|
|
115
132
|
}
|
|
116
133
|
|
|
117
134
|
/**
|
|
118
|
-
* The entry of
|
|
119
|
-
* one while the list is shorter than ENTRIES, else the
|
|
120
|
-
* replaced REPLACEMENTS; then
|
|
121
|
-
* first.
|
|
135
|
+
* The entry of `mn`'s cache, `head`, to fill for traits `t`: its own, a
|
|
136
|
+
* free one, a new one while the list is shorter than ENTRIES, else the
|
|
137
|
+
* oldest, until it has replaced REPLACEMENTS; then one `t` keeps for `mn`.
|
|
138
|
+
* A cache of an earlier epoch is emptied first.
|
|
122
139
|
*/
|
|
123
|
-
export function entryFor(head: PropertyCache, t: Traits): PropertyCache
|
|
140
|
+
export function entryFor(head: PropertyCache, t: Traits, mn: Multiname): PropertyCache {
|
|
124
141
|
if (head.epoch !== epoch()) {
|
|
125
142
|
for (let e: PropertyCache | null = head; e !== null; e = e.next) {
|
|
126
143
|
e.traits = null;
|
|
@@ -150,7 +167,7 @@ export function entryFor(head: PropertyCache, t: Traits): PropertyCache | null {
|
|
|
150
167
|
}
|
|
151
168
|
|
|
152
169
|
if (head.replaced === REPLACEMENTS) {
|
|
153
|
-
return
|
|
170
|
+
return traitsEntry(t, mn);
|
|
154
171
|
}
|
|
155
172
|
|
|
156
173
|
head.replaced++;
|
|
@@ -158,3 +175,32 @@ export function entryFor(head: PropertyCache, t: Traits): PropertyCache | null {
|
|
|
158
175
|
head.victim = victim.next ?? head;
|
|
159
176
|
return victim;
|
|
160
177
|
}
|
|
178
|
+
|
|
179
|
+
/** The entry `t` keeps for `mn`, a name its cache has no room for. */
|
|
180
|
+
function traitsEntry(t: Traits, mn: Multiname): PropertyCache {
|
|
181
|
+
let entries = t.manyTraitsEntries;
|
|
182
|
+
if (entries === null) {
|
|
183
|
+
entries = new Map();
|
|
184
|
+
t.manyTraitsEntries = entries;
|
|
185
|
+
filledTraits.push(t);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
let e = entries.get(mn);
|
|
189
|
+
if (e === undefined) {
|
|
190
|
+
e = new PropertyCache();
|
|
191
|
+
entries.set(mn, e);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return e;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Which of a scope chain's objects binds each name findProperty looked up
|
|
199
|
+
* on it, the innermost that does, or null where none does, in the epoch it
|
|
200
|
+
* was found in: a chain's objects never change, nor do their traits'
|
|
201
|
+
* bindings within an epoch. Only for a chain without a with scope, which
|
|
202
|
+
* has a name by its dynamic properties too.
|
|
203
|
+
*/
|
|
204
|
+
export class ScopeFinds extends Map<Multiname, AsObject | null> {
|
|
205
|
+
epoch = current;
|
|
206
|
+
}
|