@temperlang/core 0.3.0 → 0.4.0
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/async.js +71 -0
- package/bitvector.js +103 -0
- package/check-type.js +65 -0
- package/core.js +107 -0
- package/date.js +45 -0
- package/deque.js +67 -0
- package/float.js +74 -0
- package/index.js +12 -1254
- package/interface.js +56 -0
- package/listed.js +289 -0
- package/mapped.js +216 -0
- package/package.json +1 -1
- package/pair.js +69 -0
- package/regex.js +91 -76
- package/string.js +198 -0
- package/interface-types.js +0 -121
package/regex.js
CHANGED
|
@@ -1,71 +1,68 @@
|
|
|
1
|
-
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
export const regexCompiledFound = (_, compiled, text) => {
|
|
4
|
+
compiled.lastIndex = 0;
|
|
2
5
|
return compiled.test(text);
|
|
3
|
-
}
|
|
6
|
+
};
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
8
|
+
/**
|
|
9
|
+
* @param {RegExp} compiled
|
|
10
|
+
* @param {string} text
|
|
11
|
+
* @param {number} begin
|
|
12
|
+
*/
|
|
13
|
+
export const regexCompiledFind = (_, compiled, text, begin, regexRefs) => {
|
|
14
|
+
const match = regexCompiledFindEx(_, compiled, text, begin, regexRefs);
|
|
7
15
|
if (match === undefined) {
|
|
8
16
|
// We could just fail on `undefined.groups`, but that seems accidental.
|
|
9
17
|
throw Error();
|
|
10
18
|
}
|
|
11
|
-
return match
|
|
12
|
-
}
|
|
19
|
+
return match;
|
|
20
|
+
};
|
|
13
21
|
|
|
14
22
|
/**
|
|
15
23
|
* @param {RegExp} compiled
|
|
16
24
|
* @param {string} text
|
|
25
|
+
* @param {number} begin
|
|
17
26
|
*/
|
|
18
|
-
function regexCompiledFindEx(_, compiled, text, regexRefs) {
|
|
27
|
+
function regexCompiledFindEx(_, compiled, text, begin, regexRefs) {
|
|
28
|
+
compiled.lastIndex = begin;
|
|
19
29
|
const match = compiled.exec(text);
|
|
20
30
|
if (match === null) {
|
|
21
31
|
return undefined;
|
|
22
32
|
}
|
|
23
|
-
const {
|
|
33
|
+
const {
|
|
34
|
+
// @ts-ignore
|
|
35
|
+
groups: groupsMaybe,
|
|
36
|
+
// @ts-ignore
|
|
37
|
+
indices: { groups: indexGroupsMaybe },
|
|
38
|
+
} = match;
|
|
24
39
|
const groups = groupsMaybe || {};
|
|
25
|
-
const indexGroups = indexGroupsMaybe ||
|
|
40
|
+
const indexGroups = indexGroupsMaybe || {};
|
|
26
41
|
// Find the begin indices in code points for all matched groups.
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
if (defaultFull) {
|
|
30
|
-
rawBegins.push({ name: "full", index: match.index });
|
|
31
|
-
}
|
|
32
|
-
for (const entry of Object.entries(indexGroups)) {
|
|
33
|
-
const [name, indices] = entry;
|
|
34
|
-
if (indices !== undefined) {
|
|
35
|
-
rawBegins.push({ name, index: indices[0] });
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
const begins = codePointIndices(text, rawBegins);
|
|
39
|
-
// Form the matches.
|
|
40
|
-
const Group = regexRefs.group.constructor;
|
|
42
|
+
const Match = regexRefs.match.constructor;
|
|
43
|
+
const Group = regexRefs.match.full.constructor;
|
|
41
44
|
const resultGroups = new Map();
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
// TODO(tjp, regex): Can we presume we often receive them in sorted order?
|
|
56
|
-
unitNameIndexArray.sort((a, b) => a.index - b.index);
|
|
57
|
-
let unitCount = 0;
|
|
58
|
-
let codeCount = 0;
|
|
59
|
-
const codeIndices = {};
|
|
60
|
-
for (const unitNameIndex of unitNameIndexArray) {
|
|
61
|
-
const { name, index: unitIndex } = unitNameIndex;
|
|
62
|
-
while (unitCount < unitIndex) {
|
|
63
|
-
unitCount += text.codePointAt(unitCount) > 0xFFFF ? 2 : 1;
|
|
64
|
-
codeCount += 1;
|
|
45
|
+
const fullText = match[0];
|
|
46
|
+
const fullBegin = match.index;
|
|
47
|
+
const full = new Group(
|
|
48
|
+
"full",
|
|
49
|
+
fullText,
|
|
50
|
+
fullBegin,
|
|
51
|
+
fullBegin + fullText.length
|
|
52
|
+
);
|
|
53
|
+
for (const name of Object.keys(indexGroups)) {
|
|
54
|
+
const indices = indexGroups[name];
|
|
55
|
+
const text = groups[name];
|
|
56
|
+
if (text === undefined) {
|
|
57
|
+
continue;
|
|
65
58
|
}
|
|
66
|
-
|
|
59
|
+
const groupBegin = indices[0];
|
|
60
|
+
resultGroups.set(
|
|
61
|
+
name,
|
|
62
|
+
new Group(name, text, groupBegin, groupBegin + text.length)
|
|
63
|
+
);
|
|
67
64
|
}
|
|
68
|
-
return
|
|
65
|
+
return new Match(full, resultGroups);
|
|
69
66
|
}
|
|
70
67
|
|
|
71
68
|
/**
|
|
@@ -74,36 +71,56 @@ function codePointIndices(text, unitNameIndexArray) {
|
|
|
74
71
|
* @param {(groups: Map<string, any>) => string} format
|
|
75
72
|
* @returns {string}
|
|
76
73
|
*/
|
|
77
|
-
export
|
|
78
|
-
_,
|
|
79
|
-
compiled,
|
|
80
|
-
text,
|
|
81
|
-
format,
|
|
82
|
-
regexRefs
|
|
83
|
-
) {
|
|
74
|
+
export const regexCompiledReplace = (_, compiled, text, format, regexRefs) => {
|
|
84
75
|
// Simple string replace doesn't provide all match group details if we want to
|
|
85
76
|
// make our interface consistent, so we have to do this manually here.
|
|
86
77
|
// The hope is that we can optimize a bunch out when we have compile-time
|
|
87
78
|
// contant patterns and customized match result types.
|
|
88
|
-
let
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
79
|
+
let result = "";
|
|
80
|
+
let begin = 0;
|
|
81
|
+
let keepBegin = begin;
|
|
82
|
+
do {
|
|
83
|
+
let match = regexCompiledFindEx(_, compiled, text, begin, regexRefs);
|
|
84
|
+
if (match === undefined) {
|
|
85
|
+
if (result == "") {
|
|
86
|
+
// Manually handle no match case for our manual replace logic.
|
|
87
|
+
return text;
|
|
88
|
+
} else {
|
|
89
|
+
result += text.substring(keepBegin);
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
result += text.slice(keepBegin, match.full.begin);
|
|
94
|
+
result += format(match);
|
|
95
|
+
keepBegin = match.full.end;
|
|
96
|
+
begin = Math.max(keepBegin, begin + 1);
|
|
97
|
+
} while (begin <= text.length); // `<=` to see string end
|
|
98
|
+
return result;
|
|
99
|
+
};
|
|
98
100
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
101
|
+
/**
|
|
102
|
+
* @param {RegExp} compiled
|
|
103
|
+
* @param {string} text
|
|
104
|
+
* @returns {Readonly<string[]>}
|
|
105
|
+
*/
|
|
106
|
+
export const regexCompiledSplit = (_, compiled, text) => {
|
|
107
|
+
return Object.freeze(text.split(compiled));
|
|
108
|
+
};
|
|
102
109
|
|
|
103
|
-
|
|
110
|
+
/**
|
|
111
|
+
* @param {RegExp} _
|
|
112
|
+
* @param {string} formatted
|
|
113
|
+
* @returns {RegExp}
|
|
114
|
+
*/
|
|
115
|
+
export const regexCompileFormatted = (_, formatted) => {
|
|
116
|
+
return new RegExp(formatted, "dgu"); // d:hasIndices, g:global, u:unicode
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export const regexFormatterAdjustCodeSet = (self, codeSet, regexRefs) => {
|
|
104
120
|
if (codeSet.negated) {
|
|
105
121
|
let maxCode = codeSet.items.reduce(
|
|
106
|
-
(maxCode, item) => Math.max(maxCode, self.maxCode(item)) ?? 0,
|
|
122
|
+
(maxCode, item) => Math.max(maxCode, self.maxCode(item)) ?? 0,
|
|
123
|
+
0
|
|
107
124
|
);
|
|
108
125
|
if (maxCode < MIN_SUPPLEMENTAL_CP) {
|
|
109
126
|
// Add a bonus explicit surrogate pair to encourage js code points.
|
|
@@ -115,20 +132,18 @@ export function regexFormatterAdjustCodeSet(self, codeSet, regexRefs) {
|
|
|
115
132
|
}
|
|
116
133
|
return new regexRefs.orObject.constructor([
|
|
117
134
|
codeSetBonus,
|
|
118
|
-
new codeSet.constructor(
|
|
119
|
-
[codeSetBonus].concat(codeSet.items), true
|
|
120
|
-
),
|
|
135
|
+
new codeSet.constructor([codeSetBonus].concat(codeSet.items), true),
|
|
121
136
|
]);
|
|
122
137
|
}
|
|
123
138
|
}
|
|
124
139
|
return codeSet;
|
|
125
|
-
}
|
|
140
|
+
};
|
|
126
141
|
|
|
127
|
-
export
|
|
142
|
+
export const regexFormatterPushCodeTo = (_, out, code, insideCodeSet) => {
|
|
128
143
|
// Ignore insideCodeSet for now.
|
|
129
144
|
// TODO(tjp, regex): Get fancier, including with work in Temper.
|
|
130
145
|
out.push(`\\u{${code.toString(16)}}`);
|
|
131
|
-
}
|
|
146
|
+
};
|
|
132
147
|
|
|
133
148
|
// Cached later for some approximate efficiency.
|
|
134
149
|
let codeSetBonus = null;
|
package/string.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { requireIsSafeInteger } from "./check-type.js";
|
|
2
|
+
import {bubble} from "./core.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Implements extension method String::fromCodePoints
|
|
6
|
+
* @param {number[]} codePoints
|
|
7
|
+
* @returns {string}
|
|
8
|
+
*/
|
|
9
|
+
export const stringFromCodePoints = (codePoints) => {
|
|
10
|
+
// TODO Append in batches if codePoints is long?
|
|
11
|
+
return String.fromCodePoint(...codePoints);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Implements extension method String::isEmpty
|
|
16
|
+
* @param {string} s
|
|
17
|
+
* @returns {boolean}
|
|
18
|
+
*/
|
|
19
|
+
export const stringIsEmpty = (s) => {
|
|
20
|
+
return s === "";
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Implements extension method String::split
|
|
25
|
+
* @param {string} s
|
|
26
|
+
* @param {string | undefined} separator
|
|
27
|
+
* @returns {string[]}
|
|
28
|
+
*/
|
|
29
|
+
export const stringSplit = (s, separator) => {
|
|
30
|
+
return separator ? s.split(separator).map((s) => s) : Array.from(s);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Implements extension method String::toFloat64
|
|
35
|
+
* @param {string} s
|
|
36
|
+
* @returns {number}
|
|
37
|
+
*/
|
|
38
|
+
export const stringToFloat64 = (s) => {
|
|
39
|
+
// TODO Consider JSON.parse + bonus constants instead? Faster or not?
|
|
40
|
+
if (!/^\s*-?(?:\d+(?:\.\d+)?(?:[eE][-+]?\d+)?|NaN|Infinity)\s*$/.test(s)) {
|
|
41
|
+
bubble();
|
|
42
|
+
}
|
|
43
|
+
return Number(s);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Implements extension method String::toInt
|
|
48
|
+
* @param {string} s
|
|
49
|
+
* @param {number?} radix
|
|
50
|
+
* @returns {number}
|
|
51
|
+
*/
|
|
52
|
+
export const stringToInt = (s, radix) => {
|
|
53
|
+
// This currently maybe allocates for trim and then also for check.
|
|
54
|
+
// TODO Avoid that with manual char checks? Arbitrary base makes regex harder.
|
|
55
|
+
s = s.trim();
|
|
56
|
+
radix = radix ?? 10;
|
|
57
|
+
const result = parseInt(s, radix);
|
|
58
|
+
requireIsSafeInteger(result);
|
|
59
|
+
const trimmed = s.slice(0, s.length - 1);
|
|
60
|
+
if (parseInt(trimmed, radix) === result) {
|
|
61
|
+
bubble();
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param {string} s
|
|
68
|
+
* @param {number} begin
|
|
69
|
+
* @param {number} end
|
|
70
|
+
* @returns {number}
|
|
71
|
+
*/
|
|
72
|
+
export const stringCountBetween = (s, begin, end) => {
|
|
73
|
+
let count = 0;
|
|
74
|
+
for (let i = begin; i < end; ++i) {
|
|
75
|
+
let cp = s.codePointAt(i);
|
|
76
|
+
if (cp !== undefined) {
|
|
77
|
+
count += 1;
|
|
78
|
+
i += !!(cp >>> 16); // skip over trailing surrogate
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return count;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param {string} s
|
|
86
|
+
* @param {number} i
|
|
87
|
+
* @returns {number}
|
|
88
|
+
*/
|
|
89
|
+
export const stringGet = (s, i) => {
|
|
90
|
+
const c = s.codePointAt(i);
|
|
91
|
+
if (c === undefined) {
|
|
92
|
+
throw new Error();
|
|
93
|
+
}
|
|
94
|
+
return c;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @param {string} s
|
|
99
|
+
* @param {number} begin
|
|
100
|
+
* @param {number} end
|
|
101
|
+
* @param {number} minCount
|
|
102
|
+
* @returns {boolean}
|
|
103
|
+
*/
|
|
104
|
+
export const stringHasAtLeast = (s, begin, end, minCount) => {
|
|
105
|
+
let { length } = s;
|
|
106
|
+
begin = Math.min(begin, length);
|
|
107
|
+
end = Math.max(begin, Math.min(end, length));
|
|
108
|
+
let nUtf16 = end - begin;
|
|
109
|
+
if (nUtf16 < minCount) { return false; }
|
|
110
|
+
if (nUtf16 >= minCount * 2) { return true; }
|
|
111
|
+
// Fall back to an early-outing version of countBetween.
|
|
112
|
+
let count = 0;
|
|
113
|
+
for (let i = begin; i < end; ++i) {
|
|
114
|
+
let cp = s.codePointAt(i);
|
|
115
|
+
if (cp !== undefined) {
|
|
116
|
+
count += 1;
|
|
117
|
+
i += !!(cp >>> 16); // skip over trailing surrogate
|
|
118
|
+
if (count >= minCount) { return true; }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return count >= minCount;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @param {string} s
|
|
126
|
+
* @param {number} i
|
|
127
|
+
* @returns {number}
|
|
128
|
+
*/
|
|
129
|
+
export const stringNext = (s, i) => {
|
|
130
|
+
let iNext = Math.min(s.length, i);
|
|
131
|
+
let cp = s.codePointAt(i);
|
|
132
|
+
if (cp !== undefined) {
|
|
133
|
+
iNext += 1 + !!(cp >>> 16);
|
|
134
|
+
}
|
|
135
|
+
return iNext;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* @param {string} s
|
|
140
|
+
* @param {number} i
|
|
141
|
+
* @returns {number}
|
|
142
|
+
*/
|
|
143
|
+
export const stringPrev = (s, i) => {
|
|
144
|
+
let iPrev = Math.min(s.length, i);
|
|
145
|
+
if (iPrev) {
|
|
146
|
+
iPrev -= 1;
|
|
147
|
+
if (iPrev && s.codePointAt(iPrev - 1) >>> 16) {
|
|
148
|
+
iPrev -= 1;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return iPrev;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @param {string} s
|
|
156
|
+
* @param {(number) => void} f
|
|
157
|
+
*/
|
|
158
|
+
export const stringForEach = (s, f) => {
|
|
159
|
+
let { length } = s;
|
|
160
|
+
for (let i = 0; i < length; ++i) {
|
|
161
|
+
let cp = s.codePointAt(i);
|
|
162
|
+
f(cp);
|
|
163
|
+
if (cp >>> 16) {
|
|
164
|
+
++i; // Skip both surrogates
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
export const stringIndexNone = -1;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Casts a *StringIndexOption* to a *StringIndex*
|
|
173
|
+
*
|
|
174
|
+
* @param {number} i a string index or no string index
|
|
175
|
+
* @returns {number} i if it's a valid StringIndex.
|
|
176
|
+
*/
|
|
177
|
+
export const requireStringIndex = (i) => {
|
|
178
|
+
if (i >= 0) { return i; }
|
|
179
|
+
throw new TypeError(`Expected StringIndex, not ${i}`);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Casts a *StringIndexOption* to a *NoStringIndex*.
|
|
184
|
+
* @param {number} i a string index or no string index
|
|
185
|
+
* @returns {number} i if it's valid NoStringIndex.
|
|
186
|
+
*/
|
|
187
|
+
export const requireNoStringIndex = (i) => {
|
|
188
|
+
if (i < 0) { return i; }
|
|
189
|
+
throw new TypeError(`Expected NoStringIndex, not ${i}`);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* @param {string[]} s
|
|
194
|
+
* @param {number} c
|
|
195
|
+
*/
|
|
196
|
+
export const stringBuilderAppendCodePoint = (s, c) => {
|
|
197
|
+
s[0] += String.fromCodePoint(c);
|
|
198
|
+
}
|
package/interface-types.js
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview
|
|
3
|
-
* Declares *InterfaceType* which allows exposing a Temper `interface` type
|
|
4
|
-
* as a type that user types can extend and which inter-operates with
|
|
5
|
-
* `instanceof`.
|
|
6
|
-
*
|
|
7
|
-
* User code may do
|
|
8
|
-
*
|
|
9
|
-
* MyClass.implementedBy(AnInterface);
|
|
10
|
-
*
|
|
11
|
-
* to declare that `class MyClass implements AnInterface { ... }`.
|
|
12
|
-
*
|
|
13
|
-
* After that, members defined on *AnInterface* will be available via
|
|
14
|
-
* *MyClass*'s prototype and `new MyClass(...) instanceof AnInterface` will
|
|
15
|
-
* be true.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
export class InterfaceType {
|
|
19
|
-
constructor(name, members, supers, inheritanceDepth) {
|
|
20
|
-
/** Diagnostic string. The name of the interface. */
|
|
21
|
-
this.name = name;
|
|
22
|
-
/**
|
|
23
|
-
* A list of [kind, propertyKey, value] triples that should be added to
|
|
24
|
-
* classes that implement this modulo overriding.
|
|
25
|
-
* Kind has type ("m" | "g" | "s") where "m" indicates a normal method
|
|
26
|
-
* whose name is key, "g" a getter for the property named key,
|
|
27
|
-
* and "s" a setter.
|
|
28
|
-
*/
|
|
29
|
-
this.members = [...members];
|
|
30
|
-
/** interface types that are super-types of this */
|
|
31
|
-
this.supers = [...supers];
|
|
32
|
-
/**
|
|
33
|
-
* Computed by Temper compiler so that sub-type members can clobber
|
|
34
|
-
* super-type members but not otherwise.
|
|
35
|
-
*/
|
|
36
|
-
this.inheritanceDepth = inheritanceDepth;
|
|
37
|
-
/** Presence `in` a value indicates sub-type. */
|
|
38
|
-
this.interfaceTag = Symbol(this.toString());
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
toString() { return `interface ${this.name}`; }
|
|
42
|
-
|
|
43
|
-
/** Invoked when user code does `value instanceof anInterfaceType`. */
|
|
44
|
-
[Symbol.hasInstance](x) {
|
|
45
|
-
return x && typeof x === 'object' && this.interfaceTag in x;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Called to declare a class implements this InterfaceType. */
|
|
49
|
-
implementedBy(classType) {
|
|
50
|
-
const memberMaps = InterfaceType.#memberMapsFor(classType);
|
|
51
|
-
classType.prototype[this.interfaceTag] = void 0;
|
|
52
|
-
const inheritanceDepth = this.inheritanceDepth;
|
|
53
|
-
for (const [kind, key, value] of this.members) {
|
|
54
|
-
const memberMap = memberMaps[kind];
|
|
55
|
-
const depth = memberMap.get(key);
|
|
56
|
-
if (typeof depth === 'undefined' || inheritanceDepth > depth) {
|
|
57
|
-
memberMap.set(key, inheritanceDepth);
|
|
58
|
-
const proto = classType.prototype;
|
|
59
|
-
const descriptor = Object.getOwnPropertyDescriptor(proto, key)
|
|
60
|
-
|| { configurable: true };
|
|
61
|
-
switch (kind) {
|
|
62
|
-
case 'm':
|
|
63
|
-
descriptor.value = value;
|
|
64
|
-
break;
|
|
65
|
-
case 'g':
|
|
66
|
-
descriptor.get = value;
|
|
67
|
-
break;
|
|
68
|
-
case 's':
|
|
69
|
-
descriptor.set = value;
|
|
70
|
-
break;
|
|
71
|
-
}
|
|
72
|
-
Object.defineProperty(proto, key, descriptor);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
for (const superType of this.supers) {
|
|
76
|
-
superType.implementedBy(classType);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
static #memberMapsKey = Symbol('memberMaps');
|
|
81
|
-
|
|
82
|
-
static #memberMapsFor(classType) {
|
|
83
|
-
const memberMapsKey = InterfaceType.#memberMapsKey;
|
|
84
|
-
// Do not reuse super-class's member map
|
|
85
|
-
let maps = Object.hasOwnProperty.call(classType, memberMapsKey)
|
|
86
|
-
? classType[memberMapsKey] : null;
|
|
87
|
-
if (!maps) {
|
|
88
|
-
maps = { m: new Map(), g: new Map(), s: new Map() };
|
|
89
|
-
classType[memberMapsKey] = maps;
|
|
90
|
-
// Walk prototypes. Even though Temper does not allow class
|
|
91
|
-
// inheritance, this code may be used by JavaScript types that
|
|
92
|
-
// do, so flatten class members so that interfaces don't clobber
|
|
93
|
-
// class members.
|
|
94
|
-
// Except we treat interfaces as sub-types of Object, so stop
|
|
95
|
-
// before Object.prototype. This allows an interface to override
|
|
96
|
-
// Object members like toString, valueOf, etc.
|
|
97
|
-
let prototype = classType.prototype;
|
|
98
|
-
while (prototype && prototype !== Object.prototype) {
|
|
99
|
-
for (const keys of [
|
|
100
|
-
Object.getOwnPropertyNames(prototype),
|
|
101
|
-
Object.getOwnPropertySymbols(prototype),
|
|
102
|
-
]) {
|
|
103
|
-
for (const key of keys) {
|
|
104
|
-
const descriptor = Object.getOwnPropertyDescriptor(prototype, key);
|
|
105
|
-
if ('value' in descriptor) {
|
|
106
|
-
maps.m.set(key, Number.MAX_SAFE_INTEGER);
|
|
107
|
-
}
|
|
108
|
-
if (descriptor.get) {
|
|
109
|
-
maps.g.set(key, Number.MAX_SAFE_INTEGER);
|
|
110
|
-
}
|
|
111
|
-
if (descriptor.set) {
|
|
112
|
-
maps.s.set(key, Number.MAX_SAFE_INTEGER);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
prototype = Object.getPrototypeOf(prototype);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return maps;
|
|
120
|
-
}
|
|
121
|
-
}
|