tsc-ts 0.1.0 → 0.1.1
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/README.md +1 -1
- package/dist/api/compiler/ast.d.ts +9 -3
- package/dist/api/compiler/ast.js +17 -12
- package/dist/api/compiler/checker-classes.js +2 -1
- package/dist/api/compiler/checker-contextual.d.ts +20 -0
- package/dist/api/compiler/checker-contextual.js +111 -1
- package/dist/api/compiler/checker-expressions.js +14 -1
- package/dist/api/compiler/checker-jsx.js +2 -1
- package/dist/api/compiler/checker-type-factory.js +53 -12
- package/dist/api/compiler/checker.d.ts +5 -1
- package/dist/api/compiler/checker.js +10 -4
- package/dist/api/compiler/compile-program.d.ts +5 -0
- package/dist/api/compiler/compile-program.js +8 -0
- package/dist/api/compiler/expr-record.d.ts +11 -0
- package/dist/api/compiler/expr-record.js +53 -0
- package/dist/api/compiler/identifier-name.d.ts +9 -0
- package/dist/api/compiler/identifier-name.js +10 -0
- package/dist/api/compiler/jsdoc.js +4 -2
- package/dist/api/compiler/parser.d.ts +2 -1
- package/dist/api/compiler/parser.js +10 -8
- package/dist/api/compiler/printer/factory.js +3 -2
- package/dist/api/compiler/printer/printer.js +3 -2
- package/dist/api/compiler/reparser.js +4 -3
- package/dist/api/compiler/scanner.d.ts +8 -0
- package/dist/api/compiler/scanner.js +58 -42
- package/dist/api/compiler/shared-results.d.ts +376 -143
- package/dist/api/compiler/shared-results.js +1550 -773
- package/dist/api/compiler/transformers/declarations.js +2 -1
- package/package.json +9 -9
- package/src/compiler/ast.ts +19 -12
- package/src/compiler/cgroup.ts +157 -0
- package/src/compiler/checker-classes.ts +2 -1
- package/src/compiler/checker-contextual.ts +104 -0
- package/src/compiler/checker-expressions.ts +16 -1
- package/src/compiler/checker-jsx.ts +2 -1
- package/src/compiler/checker-type-factory.ts +53 -14
- package/src/compiler/checker.ts +11 -4
- package/src/compiler/compile-program.ts +8 -0
- package/src/compiler/decoder.ts +2 -1
- package/src/compiler/doctor-report.ts +133 -0
- package/src/compiler/expr-record.ts +59 -0
- package/src/compiler/identifier-name.ts +13 -0
- package/src/compiler/jsdoc.ts +4 -2
- package/src/compiler/parallel-check.ts +353 -146
- package/src/compiler/parallel-emit.ts +3 -3
- package/src/compiler/parser.ts +10 -8
- package/src/compiler/printer/factory.ts +3 -2
- package/src/compiler/printer/printer.ts +4 -2
- package/src/compiler/reparser.ts +7 -3
- package/src/compiler/run-config.ts +1383 -0
- package/src/compiler/run-record-read.ts +87 -0
- package/src/compiler/run-records.ts +575 -0
- package/src/compiler/scanner.ts +57 -41
- package/src/compiler/shared-results.ts +1626 -896
- package/src/compiler/thread-entry.ts +3 -1
- package/src/compiler/transformers/declarations.ts +2 -1
- package/src/compiler/tsc-build.ts +64 -6
- package/src/compiler/tsc-cli.ts +33 -4
- package/src/compiler/tsc-compile.ts +213 -8
- package/src/compiler/tsc-watch.ts +25 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// Results one checker thread computed, as
|
|
2
|
-
// (TSC_TS_SHARE=1).
|
|
1
|
+
// Results one checker thread computed, as records in its number log that other checker threads translate into their
|
|
2
|
+
// own types (TSC_TS_SHARE=1).
|
|
3
3
|
//
|
|
4
4
|
// A checker exports the result of a declaration-level computation (the value type of a variable or property, or the
|
|
5
5
|
// return type of a declared signature) as records: each type the result reaches through its identity (constituents,
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// template, string mapping and substitution types), down to types every checker can get by itself (intrinsic types,
|
|
8
8
|
// declared types of symbols, types of declarations and type nodes). Another checker translates the records through
|
|
9
9
|
// its own interning caches, so each type still has one object in it.
|
|
10
|
-
import { getSymbol, NodeVisitor } from './ast.js';
|
|
10
|
+
import { getSymbol, getType, NodeVisitor } from './ast.js';
|
|
11
11
|
import { getSourceFileOfNode } from './binder-utilities.js';
|
|
12
12
|
import { getDeclaredTypeOfSymbol } from './checker-declared-types.js';
|
|
13
13
|
import { TupleElementInfo, TypeAlias, getMappedType, IndexInfo, newTypeMapper, } from './checker-types.js';
|
|
@@ -22,69 +22,87 @@ import { findResolutionCycleStartIndex } from './checker-symbols.js';
|
|
|
22
22
|
import { typeToString } from './type-printer.js';
|
|
23
23
|
import { getTypeOfSymbol } from './checker-symbol-types.js';
|
|
24
24
|
import { getReturnTypeOfSignature } from './checker-signatures.js';
|
|
25
|
+
import { LinkTable } from './link-tables.js';
|
|
25
26
|
// Functions, classes, enums and modules have anonymous types that are cheap to create; their members are not. (A
|
|
26
27
|
// function: a top-level constant computed from an enum member of a module in an import cycle doesn't compile natively.)
|
|
27
28
|
function sharedValueFlags() {
|
|
28
29
|
return 3 /* SymbolFlags.Variable */ | 4 /* SymbolFlags.Property */;
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
31
|
+
const noRefs = [];
|
|
32
|
+
/**
|
|
33
|
+
* Writes records into a checker's store: int32 words at a cursor, strings into its string log, and each record's
|
|
34
|
+
* offset in its record table. The cursor is the writer's alone until it publishes a result.
|
|
35
|
+
*/
|
|
36
|
+
export class StoreWriter {
|
|
37
|
+
numbers;
|
|
38
|
+
strings;
|
|
39
|
+
table;
|
|
40
|
+
fileIndex;
|
|
41
|
+
/** The cursor in the number log, and in the string log. */
|
|
42
|
+
at = 0;
|
|
43
|
+
unitAt = 0;
|
|
44
|
+
/** Set when a record or a field didn't fit the store's configured capacity. */
|
|
45
|
+
full = false;
|
|
46
|
+
constructor(numbers, strings, table, fileIndex) {
|
|
47
|
+
this.numbers = numbers;
|
|
48
|
+
this.strings = strings;
|
|
49
|
+
this.table = table;
|
|
50
|
+
this.fileIndex = fileIndex;
|
|
43
51
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
this.
|
|
70
|
-
this.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
this.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
52
|
+
/** Returns to the cursors `at` and `unitAt`. */
|
|
53
|
+
reset(at, unitAt) {
|
|
54
|
+
this.at = at;
|
|
55
|
+
this.unitAt = unitAt;
|
|
56
|
+
this.full = false;
|
|
57
|
+
}
|
|
58
|
+
/** Starts record `index` at the cursor: its header, and its offset in the record table. */
|
|
59
|
+
beginRecord(index, header) {
|
|
60
|
+
if (index < this.table.length)
|
|
61
|
+
this.table[index] = this.at + 1;
|
|
62
|
+
else
|
|
63
|
+
this.full = true;
|
|
64
|
+
this.word(header);
|
|
65
|
+
}
|
|
66
|
+
word(value) {
|
|
67
|
+
if (this.at < this.numbers.length)
|
|
68
|
+
this.numbers[this.at] = value;
|
|
69
|
+
else
|
|
70
|
+
this.full = true;
|
|
71
|
+
this.at++;
|
|
72
|
+
}
|
|
73
|
+
/** A double as the two int32 halves of its bits. */
|
|
74
|
+
double(value) {
|
|
75
|
+
const words = doubleWords();
|
|
76
|
+
words.f[0] = value;
|
|
77
|
+
this.word(words.i[0]);
|
|
78
|
+
this.word(words.i[1]);
|
|
79
|
+
}
|
|
80
|
+
/** A string as its offset and length in the string log. */
|
|
81
|
+
text(value) {
|
|
82
|
+
const length = value.length;
|
|
83
|
+
if (this.unitAt + length <= this.strings.length) {
|
|
84
|
+
this.word(this.unitAt);
|
|
85
|
+
this.word(length);
|
|
86
|
+
for (let j = 0; j < length; j++)
|
|
87
|
+
this.strings[this.unitAt++] = value.charCodeAt(j);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
this.full = true;
|
|
91
|
+
this.word(0);
|
|
92
|
+
this.word(0);
|
|
93
|
+
}
|
|
94
|
+
/** A node as its file, range and id, or -1 for none. The file must be one `fileIndex` has. */
|
|
95
|
+
node(node) {
|
|
96
|
+
if (node === undefined) {
|
|
97
|
+
this.word(-1);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
this.word(this.fileIndex.get(getSourceFileOfNode(node)));
|
|
101
|
+
this.word(node.pos);
|
|
102
|
+
this.word(node.end);
|
|
103
|
+
this.word(node.id);
|
|
82
104
|
}
|
|
83
105
|
}
|
|
84
|
-
const noNumbers = [];
|
|
85
|
-
const noNodes = [];
|
|
86
|
-
const noTexts = [];
|
|
87
|
-
const noIndexInfos = [];
|
|
88
106
|
/** The types every checker creates when it starts, in the same order; marker types are left out. */
|
|
89
107
|
export function initialTypes(c) {
|
|
90
108
|
return [
|
|
@@ -142,127 +160,266 @@ export function initialTypes(c) {
|
|
|
142
160
|
c.typeofType,
|
|
143
161
|
];
|
|
144
162
|
}
|
|
163
|
+
/** The reason an export stops at its budget (`Exporter.limit`), which `post` returns: the result is not posted. */
|
|
164
|
+
export const budgetFailure = 'export budget';
|
|
145
165
|
/** A checker's `Exporter.mergedOrigins`, kept while its merged symbols stay the same. */
|
|
146
166
|
export class MergedOrigins {
|
|
147
167
|
origins = new Map();
|
|
148
168
|
size = -1;
|
|
149
169
|
}
|
|
170
|
+
/** An index signature of an `AnonymousObject` record, waiting for the words of its `CompleteObject`. */
|
|
171
|
+
class IndexInfoWords {
|
|
172
|
+
keyType;
|
|
173
|
+
valueType;
|
|
174
|
+
isReadonly;
|
|
175
|
+
declaration;
|
|
176
|
+
components;
|
|
177
|
+
constructor(keyType, valueType, isReadonly, declaration, components) {
|
|
178
|
+
this.keyType = keyType;
|
|
179
|
+
this.valueType = valueType;
|
|
180
|
+
this.isReadonly = isReadonly;
|
|
181
|
+
this.declaration = declaration;
|
|
182
|
+
this.components = components;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
150
185
|
/** Writes a result as records; `failure` names the first part that has no record kind. */
|
|
151
186
|
export class Exporter {
|
|
152
187
|
c;
|
|
153
|
-
|
|
154
|
-
indexes = new Map();
|
|
188
|
+
writer;
|
|
155
189
|
/**
|
|
156
|
-
* The index of
|
|
157
|
-
* (`ThreadSharing.post`):
|
|
190
|
+
* The index of the first record of the export in progress. A checker's exporter keeps the records of the types it
|
|
191
|
+
* exported before (`ThreadSharing.post`): its store has them already, and a result's records are only the new ones.
|
|
158
192
|
*/
|
|
159
193
|
base = 0;
|
|
194
|
+
/** The number of records the export in progress wrote. */
|
|
195
|
+
count = 0;
|
|
196
|
+
/** The record of each type, by type id, and of each property symbol, by symbol id. */
|
|
197
|
+
indexes = new Map();
|
|
198
|
+
properties = new Map();
|
|
160
199
|
/** The types and properties the export in progress gave indexes, which a failed export forgets again. */
|
|
161
200
|
addedTypes = [];
|
|
162
201
|
addedProperties = [];
|
|
163
|
-
/**
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
202
|
+
/**
|
|
203
|
+
* The most records the export in progress may add (`ThreadSharing.post` prices it with `kappa`); past it the export
|
|
204
|
+
* fails and the result is not posted.
|
|
205
|
+
*/
|
|
206
|
+
limit = Number.POSITIVE_INFINITY;
|
|
169
207
|
initial;
|
|
170
208
|
failure = undefined;
|
|
171
209
|
/** The ids above which nodes and symbols are the checker thread's own (lazily parsed JSDoc), or Infinity. */
|
|
172
210
|
nodeLimit = Infinity;
|
|
173
211
|
symbolLimit = Infinity;
|
|
174
|
-
|
|
212
|
+
/** The ids of the symbols each merged symbol merged (`getMergedSymbol`), built on first use. */
|
|
213
|
+
mergedOrigins = undefined;
|
|
214
|
+
/** Where the checker keeps `mergedOrigins` between exports. */
|
|
215
|
+
originsCache = undefined;
|
|
216
|
+
/** How the record in progress names its symbol and its alias, and the node it carries. */
|
|
217
|
+
naming = 0 /* SymbolNaming.None */;
|
|
218
|
+
namingMerged = -1;
|
|
219
|
+
namingNode = undefined;
|
|
220
|
+
aliasNaming = 0 /* SymbolNaming.None */;
|
|
221
|
+
aliasMerged = -1;
|
|
222
|
+
aliasNode = undefined;
|
|
223
|
+
aliasRefs = noRefs;
|
|
224
|
+
node = undefined;
|
|
225
|
+
constructor(c, initial, writer) {
|
|
175
226
|
this.c = c;
|
|
176
227
|
this.initial = initial;
|
|
228
|
+
this.writer = writer;
|
|
177
229
|
}
|
|
178
|
-
|
|
230
|
+
fail(reason) {
|
|
231
|
+
if (this.failure === undefined)
|
|
232
|
+
this.failure = reason;
|
|
233
|
+
return -1;
|
|
234
|
+
}
|
|
235
|
+
/** Whether `node` is one every checker shares, and can name; fails if not. */
|
|
179
236
|
sharedNode(node) {
|
|
180
|
-
if (node === undefined
|
|
237
|
+
if (node === undefined)
|
|
181
238
|
return true;
|
|
182
|
-
|
|
183
|
-
|
|
239
|
+
if (node.id > this.nodeLimit) {
|
|
240
|
+
this.fail('node of the thread');
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
if (!this.writer.fileIndex.has(getSourceFileOfNode(node))) {
|
|
244
|
+
this.fail('node outside the program');
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
return true;
|
|
184
248
|
}
|
|
185
|
-
/** The ids of the symbols each merged symbol merged (`getMergedSymbol`), built on first use. */
|
|
186
|
-
mergedOrigins = undefined;
|
|
187
|
-
/** Where the checker keeps `mergedOrigins` between exports. */
|
|
188
|
-
originsCache = undefined;
|
|
189
249
|
/**
|
|
190
|
-
*
|
|
191
|
-
*
|
|
250
|
+
* The index of the next record of the store, or -1 if the result is over its export budget; the store's table must
|
|
251
|
+
* have room for it.
|
|
192
252
|
*/
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
253
|
+
nextIndex() {
|
|
254
|
+
const index = this.base + this.count;
|
|
255
|
+
if (index >= this.limit)
|
|
256
|
+
return this.fail(budgetFailure);
|
|
257
|
+
this.count++;
|
|
258
|
+
if (index >= this.writer.table.length)
|
|
259
|
+
this.writer.full = true;
|
|
260
|
+
return index;
|
|
261
|
+
}
|
|
262
|
+
/** Records the type `t` at `index`. */
|
|
263
|
+
memo(t, index) {
|
|
264
|
+
// A consumer's memo is by record index, so a type has one record: VERIFY runs check that it does.
|
|
265
|
+
if (shareVerifyValue !== undefined && this.indexes.has(t.id))
|
|
266
|
+
process.stderr.write('share-verify: type ' + t.id + ' has a record already\n');
|
|
267
|
+
this.indexes.set(t.id, index);
|
|
268
|
+
this.addedTypes.push(t.id);
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Sets how the record in progress names `symbol`: a symbol of the program by the node it is the symbol of, or a
|
|
272
|
+
* symbol the checker merged by the id of a symbol it merged. Fails for a symbol of the thread.
|
|
273
|
+
*/
|
|
274
|
+
nameSymbol(symbol, alias) {
|
|
275
|
+
let naming = 0 /* SymbolNaming.None */;
|
|
276
|
+
let merged = -1;
|
|
277
|
+
let node = undefined;
|
|
278
|
+
if (symbol !== undefined) {
|
|
279
|
+
if ((symbol.flags & 33554432 /* SymbolFlags.Transient */) === 0) {
|
|
280
|
+
if (getSymbolId(symbol) > this.symbolLimit) {
|
|
281
|
+
this.fail('symbol of the thread');
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
const declaration = symbol.declaration;
|
|
285
|
+
if (declaration === undefined) {
|
|
286
|
+
this.fail('symbol without a declaration');
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
const local = getSymbol(declaration);
|
|
290
|
+
if (local === symbol)
|
|
291
|
+
naming = 1 /* SymbolNaming.Node */;
|
|
292
|
+
else if (local !== undefined && local.exportSymbol === symbol)
|
|
293
|
+
naming = 2 /* SymbolNaming.Export */;
|
|
206
294
|
else {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
if (!origins.has(target))
|
|
210
|
-
origins.set(target, id);
|
|
211
|
-
if (cache !== undefined) {
|
|
212
|
-
cache.origins = origins;
|
|
213
|
-
cache.size = this.c.mergedSymbols.size;
|
|
214
|
-
}
|
|
295
|
+
this.fail('symbol not named by its declaration');
|
|
296
|
+
return false;
|
|
215
297
|
}
|
|
216
|
-
|
|
298
|
+
node = declaration;
|
|
217
299
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
300
|
+
else {
|
|
301
|
+
let origins = this.mergedOrigins;
|
|
302
|
+
if (origins === undefined)
|
|
303
|
+
origins = this.buildOrigins();
|
|
304
|
+
const origin = origins.get(symbol);
|
|
305
|
+
if (origin === undefined) {
|
|
306
|
+
this.fail('checker symbol ' + symbol.name);
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
naming = 3 /* SymbolNaming.Merged */;
|
|
310
|
+
merged = origin;
|
|
222
311
|
}
|
|
223
|
-
mergedId = origin;
|
|
224
|
-
shared = undefined;
|
|
225
312
|
}
|
|
226
313
|
if (alias) {
|
|
227
|
-
|
|
228
|
-
|
|
314
|
+
this.aliasNaming = naming;
|
|
315
|
+
this.aliasMerged = merged;
|
|
316
|
+
this.aliasNode = node;
|
|
317
|
+
// The alias's type arguments are recorded after it is named (`aliasBits`). A record that names an alias which
|
|
318
|
+
// has none, such as a property naming its parent, keeps none of the last alias's.
|
|
319
|
+
this.aliasRefs = noRefs;
|
|
229
320
|
}
|
|
230
321
|
else {
|
|
231
|
-
|
|
232
|
-
|
|
322
|
+
this.naming = naming;
|
|
323
|
+
this.namingMerged = merged;
|
|
324
|
+
this.namingNode = node;
|
|
233
325
|
}
|
|
234
326
|
return true;
|
|
235
327
|
}
|
|
236
|
-
/**
|
|
237
|
-
|
|
238
|
-
const
|
|
239
|
-
|
|
328
|
+
/** The ids of the symbols each merged symbol merged, from the checker's `mergedSymbols`. */
|
|
329
|
+
buildOrigins() {
|
|
330
|
+
const cache = this.originsCache;
|
|
331
|
+
let origins;
|
|
332
|
+
if (cache !== undefined && cache.size === this.c.mergedSymbols.size)
|
|
333
|
+
origins = cache.origins;
|
|
334
|
+
else {
|
|
335
|
+
origins = new Map();
|
|
336
|
+
for (const [id, target] of this.c.mergedSymbols)
|
|
337
|
+
if (!origins.has(target))
|
|
338
|
+
origins.set(target, id);
|
|
339
|
+
if (cache !== undefined) {
|
|
340
|
+
cache.origins = origins;
|
|
341
|
+
cache.size = this.c.mergedSymbols.size;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
this.mergedOrigins = origins;
|
|
345
|
+
return origins;
|
|
240
346
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
347
|
+
/**
|
|
348
|
+
* Names the alias of the record in progress, after its type arguments are exported, and returns the header bits that
|
|
349
|
+
* name it; -1 if it can't be named.
|
|
350
|
+
*/
|
|
351
|
+
aliasBits(alias, refs) {
|
|
352
|
+
if (alias === undefined)
|
|
353
|
+
return 0;
|
|
354
|
+
if (!this.nameSymbol(alias.symbol, true))
|
|
355
|
+
return -1;
|
|
356
|
+
this.aliasRefs = refs;
|
|
357
|
+
return 32 /* RecordHeader.HasAlias */ | (this.aliasNaming === 3 /* SymbolNaming.Merged */ ? 1024 /* RecordHeader.AliasMerged */ : 0);
|
|
244
358
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
359
|
+
/**
|
|
360
|
+
* Starts the record at `index`: its header, its refs, its alias, its symbol and its node, all decided by `bits` and
|
|
361
|
+
* the fields set just before. A kind's own fields follow.
|
|
362
|
+
*/
|
|
363
|
+
begin(kind, index, refs, bits) {
|
|
364
|
+
const w = this.writer;
|
|
365
|
+
const n = refs.length;
|
|
366
|
+
w.beginRecord(index, kind | bits | (n < 65535 /* RecordHeader.RefEscape */ ? n << 16 : 65535 /* RecordHeader.RefEscape */ << 16));
|
|
367
|
+
if (n >= 65535 /* RecordHeader.RefEscape */)
|
|
368
|
+
w.word(n);
|
|
369
|
+
for (let i = 0; i < n; i++)
|
|
370
|
+
w.word(refs[i]);
|
|
371
|
+
if ((bits & 32 /* RecordHeader.HasAlias */) !== 0) {
|
|
372
|
+
if ((bits & 1024 /* RecordHeader.AliasMerged */) !== 0)
|
|
373
|
+
w.word(this.aliasMerged);
|
|
374
|
+
else {
|
|
375
|
+
w.word(this.aliasNaming === 2 /* SymbolNaming.Export */ ? 1 : 0);
|
|
376
|
+
w.node(this.aliasNode);
|
|
377
|
+
}
|
|
378
|
+
const aliasRefs = this.aliasRefs;
|
|
379
|
+
w.word(aliasRefs.length);
|
|
380
|
+
for (let i = 0; i < aliasRefs.length; i++)
|
|
381
|
+
w.word(aliasRefs[i]);
|
|
382
|
+
}
|
|
383
|
+
const naming = bits & 192 /* RecordHeader.NamingMask */;
|
|
384
|
+
if (naming === 3 /* SymbolNaming.Merged */ << 6 /* RecordHeader.NamingShift */)
|
|
385
|
+
w.word(this.namingMerged);
|
|
386
|
+
else if (naming !== 0)
|
|
387
|
+
w.node(this.namingNode);
|
|
388
|
+
if ((bits & 256 /* RecordHeader.HasNode */) !== 0)
|
|
389
|
+
w.node(this.node);
|
|
390
|
+
}
|
|
391
|
+
/** The record of `t` of `kind`, with `refs` and no fields of its own; its index. */
|
|
392
|
+
plain(kind, t, refs) {
|
|
393
|
+
const index = this.nextIndex();
|
|
394
|
+
this.memo(t, index);
|
|
395
|
+
this.begin(kind, index, refs, 0);
|
|
396
|
+
return index;
|
|
249
397
|
}
|
|
250
|
-
|
|
251
|
-
|
|
398
|
+
/** The record of the initial type `t`, which is `num`th in `initialTypes`. */
|
|
399
|
+
initialType(t, num) {
|
|
400
|
+
const index = this.nextIndex();
|
|
401
|
+
this.memo(t, index);
|
|
402
|
+
this.begin(0 /* RecordKind.Initial */, index, noRefs, 0);
|
|
403
|
+
this.writer.word(num);
|
|
404
|
+
return index;
|
|
405
|
+
}
|
|
406
|
+
/** The record of the type `t` of `kind` named by `symbol`, with `refs`; its index. */
|
|
407
|
+
symbolType(kind, t, symbol, refs) {
|
|
408
|
+
if (!this.nameSymbol(symbol, false))
|
|
252
409
|
return -1;
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const index = this.
|
|
264
|
-
this.
|
|
265
|
-
this.
|
|
410
|
+
const index = this.nextIndex();
|
|
411
|
+
this.memo(t, index);
|
|
412
|
+
this.begin(kind, index, refs, this.naming << 6 /* RecordHeader.NamingShift */);
|
|
413
|
+
return index;
|
|
414
|
+
}
|
|
415
|
+
/** The record of the type `t` of the type node `node`; its index. */
|
|
416
|
+
nodeType(t, node) {
|
|
417
|
+
if (!this.sharedNode(node))
|
|
418
|
+
return -1;
|
|
419
|
+
this.node = node;
|
|
420
|
+
const index = this.nextIndex();
|
|
421
|
+
this.memo(t, index);
|
|
422
|
+
this.begin(8 /* RecordKind.NodeType */, index, noRefs, 256 /* RecordHeader.HasNode */);
|
|
266
423
|
return index;
|
|
267
424
|
}
|
|
268
425
|
types(list) {
|
|
@@ -274,23 +431,15 @@ export class Exporter {
|
|
|
274
431
|
}
|
|
275
432
|
return refs;
|
|
276
433
|
}
|
|
277
|
-
aliasOf(record, alias) {
|
|
278
|
-
if (alias === undefined)
|
|
279
|
-
return record;
|
|
280
|
-
const refs = this.types(alias.typeArguments);
|
|
281
|
-
this.symbolOf(record, alias.symbol, true);
|
|
282
|
-
record.aliasRefs = refs;
|
|
283
|
-
return record;
|
|
284
|
-
}
|
|
285
434
|
type(t) {
|
|
286
435
|
if (this.failure !== undefined)
|
|
287
436
|
return -1;
|
|
288
|
-
const known = this.indexes.get(t);
|
|
437
|
+
const known = this.indexes.get(t.id);
|
|
289
438
|
if (known !== undefined)
|
|
290
439
|
return known;
|
|
291
|
-
const initial = this.initial.get(t);
|
|
440
|
+
const initial = this.initial.get(t.id);
|
|
292
441
|
if (initial !== undefined)
|
|
293
|
-
return this.
|
|
442
|
+
return this.initialType(t, initial);
|
|
294
443
|
const c = this.c;
|
|
295
444
|
const flags = t.flags;
|
|
296
445
|
if ((flags & 15360 /* TypeFlags.Literal */) !== 0 && (flags & 256 /* TypeFlags.Boolean */) === 0)
|
|
@@ -298,7 +447,7 @@ export class Exporter {
|
|
|
298
447
|
if ((flags & 16384 /* TypeFlags.UniqueESSymbol */) !== 0) {
|
|
299
448
|
if (c.uniqueESSymbolTypes.get(t.symbol) !== t)
|
|
300
449
|
return this.fail('unique symbol');
|
|
301
|
-
return this.
|
|
450
|
+
return this.symbolType(4 /* RecordKind.UniqueSymbol */, t, t.symbol, noRefs);
|
|
302
451
|
}
|
|
303
452
|
if ((flags & 524288 /* TypeFlags.TypeParameter */) !== 0) {
|
|
304
453
|
const symbol = t.symbol;
|
|
@@ -306,41 +455,50 @@ export class Exporter {
|
|
|
306
455
|
return this.fail('type parameter without a symbol');
|
|
307
456
|
const declared = getDeclaredTypeOfSymbol(c, symbol);
|
|
308
457
|
if (declared === t)
|
|
309
|
-
return this.
|
|
458
|
+
return this.symbolType(5 /* RecordKind.Declared */, t, symbol, noRefs);
|
|
310
459
|
if ((declared.objectFlags & 3 /* ObjectFlags.ClassOrInterface */) !== 0 && declared.thisType === t)
|
|
311
|
-
return this.
|
|
460
|
+
return this.symbolType(6 /* RecordKind.ThisType */, t, symbol, noRefs);
|
|
312
461
|
return this.fail('fresh type parameter');
|
|
313
462
|
}
|
|
314
463
|
if (t.symbol !== undefined && (flags & (134217728 /* TypeFlags.Union */ | 65536 /* TypeFlags.Enum */)) !== 0) {
|
|
315
464
|
// An enum type: the declared type of its symbol.
|
|
316
465
|
if (getDeclaredTypeOfSymbol(c, t.symbol) !== t)
|
|
317
466
|
return this.fail('enum type');
|
|
318
|
-
return this.
|
|
467
|
+
return this.symbolType(5 /* RecordKind.Declared */, t, t.symbol, noRefs);
|
|
319
468
|
}
|
|
320
469
|
if ((flags & 134217728 /* TypeFlags.Union */) !== 0)
|
|
321
470
|
return this.union(t);
|
|
322
|
-
if ((flags & 268435456 /* TypeFlags.Intersection */) !== 0)
|
|
323
|
-
|
|
324
|
-
if (this.failure !== undefined)
|
|
325
|
-
return -1;
|
|
326
|
-
const record = new PortableRecord(13 /* RecordKind.Intersection */, t.id, t.objectFlags, 0, '', undefined, undefined, refs);
|
|
327
|
-
return this.add(t, this.aliasOf(record, t.alias));
|
|
328
|
-
}
|
|
471
|
+
if ((flags & 268435456 /* TypeFlags.Intersection */) !== 0)
|
|
472
|
+
return this.intersection(t);
|
|
329
473
|
if ((flags & 1048576 /* TypeFlags.Object */) !== 0)
|
|
330
474
|
return this.object(t);
|
|
331
475
|
if ((flags & 2097152 /* TypeFlags.Index */) !== 0) {
|
|
332
476
|
const target = this.type(t.target);
|
|
333
477
|
if (this.failure !== undefined)
|
|
334
478
|
return -1;
|
|
335
|
-
|
|
479
|
+
const index = this.nextIndex();
|
|
480
|
+
this.memo(t, index);
|
|
481
|
+
this.begin(14 /* RecordKind.Index */, index, [target], 0);
|
|
482
|
+
this.writer.word(t.indexFlags);
|
|
483
|
+
return index;
|
|
336
484
|
}
|
|
337
485
|
if ((flags & 33554432 /* TypeFlags.IndexedAccess */) !== 0) {
|
|
338
486
|
const access = t;
|
|
339
487
|
const refs = this.types([access.objectType, access.indexType]);
|
|
340
488
|
if (this.failure !== undefined)
|
|
341
489
|
return -1;
|
|
342
|
-
const
|
|
343
|
-
|
|
490
|
+
const alias = t.alias;
|
|
491
|
+
const aliasRefs = alias === undefined ? noRefs : this.types(alias.typeArguments);
|
|
492
|
+
if (this.failure !== undefined)
|
|
493
|
+
return -1;
|
|
494
|
+
const bits = this.aliasBits(alias, aliasRefs);
|
|
495
|
+
if (bits < 0)
|
|
496
|
+
return -1;
|
|
497
|
+
const index = this.nextIndex();
|
|
498
|
+
this.memo(t, index);
|
|
499
|
+
this.begin(15 /* RecordKind.IndexedAccess */, index, refs, bits);
|
|
500
|
+
this.writer.word(this.c.indexedAccessKeyFlags.get(t) ?? access.accessFlags);
|
|
501
|
+
return index;
|
|
344
502
|
}
|
|
345
503
|
if ((flags & 67108864 /* TypeFlags.Conditional */) !== 0)
|
|
346
504
|
return this.conditional(t);
|
|
@@ -349,15 +507,20 @@ export class Exporter {
|
|
|
349
507
|
const refs = this.types(template.types);
|
|
350
508
|
if (this.failure !== undefined)
|
|
351
509
|
return -1;
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
510
|
+
const index = this.nextIndex();
|
|
511
|
+
this.memo(t, index);
|
|
512
|
+
this.begin(17 /* RecordKind.Template */, index, refs, 0);
|
|
513
|
+
const texts = template.texts;
|
|
514
|
+
this.writer.word(texts.length);
|
|
515
|
+
for (let i = 0; i < texts.length; i++)
|
|
516
|
+
this.writer.text(texts[i]);
|
|
517
|
+
return index;
|
|
355
518
|
}
|
|
356
519
|
if ((flags & 8388608 /* TypeFlags.StringMapping */) !== 0) {
|
|
357
520
|
const target = this.type(t.target);
|
|
358
521
|
if (this.failure !== undefined)
|
|
359
522
|
return -1;
|
|
360
|
-
return this.
|
|
523
|
+
return this.symbolType(18 /* RecordKind.StringMapping */, t, t.symbol, [target]);
|
|
361
524
|
}
|
|
362
525
|
if ((flags & 16777216 /* TypeFlags.Substitution */) !== 0) {
|
|
363
526
|
const substitution = t;
|
|
@@ -365,12 +528,12 @@ export class Exporter {
|
|
|
365
528
|
const base = this.type(substitution.baseType);
|
|
366
529
|
if (this.failure !== undefined)
|
|
367
530
|
return -1;
|
|
368
|
-
return this.
|
|
531
|
+
return this.plain(20 /* RecordKind.NoInfer */, t, [base]);
|
|
369
532
|
}
|
|
370
533
|
const refs = this.types([substitution.baseType, substitution.constraint]);
|
|
371
534
|
if (this.failure !== undefined)
|
|
372
535
|
return -1;
|
|
373
|
-
return this.
|
|
536
|
+
return this.plain(19 /* RecordKind.Substitution */, t, refs);
|
|
374
537
|
}
|
|
375
538
|
return this.fail('type flags ' + flags);
|
|
376
539
|
}
|
|
@@ -382,35 +545,86 @@ export class Exporter {
|
|
|
382
545
|
const index = this.type(regular);
|
|
383
546
|
if (this.failure !== undefined)
|
|
384
547
|
return -1;
|
|
385
|
-
return this.
|
|
548
|
+
return this.plain(2 /* RecordKind.FreshLiteral */, t, [index]);
|
|
386
549
|
}
|
|
387
550
|
if (t.symbol !== undefined) {
|
|
388
551
|
if (getDeclaredTypeOfSymbol(this.c, t.symbol) !== t)
|
|
389
552
|
return this.fail('enum literal');
|
|
390
|
-
return this.
|
|
553
|
+
return this.symbolType(3 /* RecordKind.EnumLiteral */, t, t.symbol, noRefs);
|
|
391
554
|
}
|
|
392
|
-
|
|
555
|
+
const index = this.nextIndex();
|
|
556
|
+
this.memo(t, index);
|
|
557
|
+
this.begin(1 /* RecordKind.Literal */, index, noRefs, 0);
|
|
558
|
+
const w = this.writer;
|
|
559
|
+
w.word(t.flags);
|
|
560
|
+
if ((t.flags & 2048 /* TypeFlags.NumberLiteral */) !== 0)
|
|
561
|
+
w.double(t.numberValue);
|
|
562
|
+
else
|
|
563
|
+
w.text(t.stringValue);
|
|
564
|
+
return index;
|
|
565
|
+
}
|
|
566
|
+
/** The record of the constituents of a union's or intersection's origin, which is not a type: it has no alias. */
|
|
567
|
+
originRecord(kind, flags, refs) {
|
|
568
|
+
const index = this.nextIndex();
|
|
569
|
+
this.begin(kind, index, refs, 2048 /* RecordHeader.Constituent */);
|
|
570
|
+
this.writer.word(flags);
|
|
571
|
+
// A union's origin field is -1 here, as it is for a union with no origin: the constituents are refs, and the
|
|
572
|
+
// reader scans the field for records this record depends on.
|
|
573
|
+
if (kind === 12 /* RecordKind.Union */)
|
|
574
|
+
this.writer.word(-1);
|
|
575
|
+
return index;
|
|
393
576
|
}
|
|
394
577
|
union(t) {
|
|
395
578
|
const refs = this.types(t.types);
|
|
579
|
+
if (this.failure !== undefined)
|
|
580
|
+
return -1;
|
|
396
581
|
let origin = -1;
|
|
397
|
-
|
|
398
|
-
|
|
582
|
+
const o = t.origin;
|
|
583
|
+
if (o !== undefined) {
|
|
399
584
|
if ((o.flags & 402653184 /* TypeFlags.UnionOrIntersection */) !== 0) {
|
|
400
585
|
// Origins are not interned: a union key spells their constituents.
|
|
401
586
|
const parts = this.types(o.types);
|
|
402
587
|
if (this.failure !== undefined)
|
|
403
588
|
return -1;
|
|
404
589
|
const kind = (o.flags & 134217728 /* TypeFlags.Union */) !== 0 ? 12 /* RecordKind.Union */ : 13 /* RecordKind.Intersection */;
|
|
405
|
-
origin = this.
|
|
590
|
+
origin = this.originRecord(kind, o.objectFlags, parts);
|
|
406
591
|
}
|
|
407
|
-
else
|
|
592
|
+
else {
|
|
408
593
|
origin = this.type(o);
|
|
594
|
+
if (this.failure !== undefined)
|
|
595
|
+
return -1;
|
|
596
|
+
}
|
|
409
597
|
}
|
|
598
|
+
const alias = t.alias;
|
|
599
|
+
const aliasRefs = alias === undefined ? noRefs : this.types(alias.typeArguments);
|
|
410
600
|
if (this.failure !== undefined)
|
|
411
601
|
return -1;
|
|
412
|
-
const
|
|
413
|
-
|
|
602
|
+
const bits = this.aliasBits(alias, aliasRefs);
|
|
603
|
+
if (bits < 0)
|
|
604
|
+
return -1;
|
|
605
|
+
const index = this.nextIndex();
|
|
606
|
+
this.memo(t, index);
|
|
607
|
+
this.begin(12 /* RecordKind.Union */, index, refs, bits);
|
|
608
|
+
this.writer.word(t.objectFlags);
|
|
609
|
+
this.writer.word(origin);
|
|
610
|
+
return index;
|
|
611
|
+
}
|
|
612
|
+
intersection(t) {
|
|
613
|
+
const refs = this.types(t.types);
|
|
614
|
+
if (this.failure !== undefined)
|
|
615
|
+
return -1;
|
|
616
|
+
const alias = t.alias;
|
|
617
|
+
const aliasRefs = alias === undefined ? noRefs : this.types(alias.typeArguments);
|
|
618
|
+
if (this.failure !== undefined)
|
|
619
|
+
return -1;
|
|
620
|
+
const bits = this.aliasBits(alias, aliasRefs);
|
|
621
|
+
if (bits < 0)
|
|
622
|
+
return -1;
|
|
623
|
+
const index = this.nextIndex();
|
|
624
|
+
this.memo(t, index);
|
|
625
|
+
this.begin(13 /* RecordKind.Intersection */, index, refs, bits);
|
|
626
|
+
this.writer.word(t.objectFlags);
|
|
627
|
+
return index;
|
|
414
628
|
}
|
|
415
629
|
object(t) {
|
|
416
630
|
const c = this.c;
|
|
@@ -426,17 +640,17 @@ export class Exporter {
|
|
|
426
640
|
return this.tupleTarget(t);
|
|
427
641
|
if (symbol === undefined || getDeclaredTypeOfSymbol(c, symbol) !== t)
|
|
428
642
|
return this.fail('generic target');
|
|
429
|
-
return this.
|
|
643
|
+
return this.symbolType(5 /* RecordKind.Declared */, t, symbol, noRefs);
|
|
430
644
|
}
|
|
431
645
|
const refs = [this.type(target), ...this.types(reference.resolvedTypeArguments ?? [])];
|
|
432
646
|
if (this.failure !== undefined)
|
|
433
647
|
return -1;
|
|
434
|
-
return this.
|
|
648
|
+
return this.plain(9 /* RecordKind.Reference */, t, refs);
|
|
435
649
|
}
|
|
436
650
|
if ((objectFlags & 3 /* ObjectFlags.ClassOrInterface */) !== 0) {
|
|
437
651
|
if (symbol === undefined || getDeclaredTypeOfSymbol(c, symbol) !== t)
|
|
438
652
|
return this.fail('declared type');
|
|
439
|
-
return this.
|
|
653
|
+
return this.symbolType(5 /* RecordKind.Declared */, t, symbol, noRefs);
|
|
440
654
|
}
|
|
441
655
|
if ((objectFlags & (1024 /* ObjectFlags.ReverseMapped */ | 256 /* ObjectFlags.EvolvingArray */)) !== 0)
|
|
442
656
|
return this.fail('object kind');
|
|
@@ -446,10 +660,10 @@ export class Exporter {
|
|
|
446
660
|
return this.fail('object without a declaration, object flags ' + objectFlags);
|
|
447
661
|
// The anonymous type of a function, class, enum or module; a variable's own type is what is being shared.
|
|
448
662
|
if ((symbol.flags & sharedValueFlags()) === 0 && c.symbolTypes.get(getSymbolId(symbol)) === t)
|
|
449
|
-
return this.
|
|
663
|
+
return this.symbolType(7 /* RecordKind.SymbolType */, t, symbol, noRefs);
|
|
450
664
|
const declaration = (objectFlags & 32 /* ObjectFlags.Mapped */) !== 0 ? t.declaration : symbol.declaration;
|
|
451
665
|
if (declaration !== undefined && c.resolvedTypes.get(declaration.id) === t)
|
|
452
|
-
return this.
|
|
666
|
+
return this.nodeType(t, declaration);
|
|
453
667
|
if ((symbol.flags & 4096 /* SymbolFlags.ObjectLiteral */) !== 0 &&
|
|
454
668
|
(objectFlags & (16 /* ObjectFlags.Anonymous */ | 2097152 /* ObjectFlags.MembersResolved */)) ===
|
|
455
669
|
(16 /* ObjectFlags.Anonymous */ | 2097152 /* ObjectFlags.MembersResolved */))
|
|
@@ -471,10 +685,8 @@ export class Exporter {
|
|
|
471
685
|
const resolved = t;
|
|
472
686
|
if (resolved.callSignatures.length !== 0 || resolved.constructSignatures.length !== 0)
|
|
473
687
|
return this.fail('object literal signatures');
|
|
474
|
-
const shell =
|
|
475
|
-
|
|
476
|
-
return -1;
|
|
477
|
-
const index = this.add(t, shell);
|
|
688
|
+
const shell = this.nextIndex();
|
|
689
|
+
this.memo(t, shell);
|
|
478
690
|
const members = [];
|
|
479
691
|
for (const prop of resolved.properties) {
|
|
480
692
|
members.push(this.property(prop));
|
|
@@ -487,18 +699,38 @@ export class Exporter {
|
|
|
487
699
|
const value = this.type(info.valueType);
|
|
488
700
|
if (this.failure !== undefined)
|
|
489
701
|
return -1;
|
|
490
|
-
|
|
702
|
+
if (!this.sharedNode(info.declaration))
|
|
703
|
+
return -1;
|
|
704
|
+
for (const node of info.components)
|
|
705
|
+
if (!this.sharedNode(node))
|
|
706
|
+
return -1;
|
|
707
|
+
infos.push(new IndexInfoWords(key, value, info.isReadonly, info.declaration, info.components));
|
|
491
708
|
}
|
|
492
|
-
const complete =
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
709
|
+
const complete = this.nextIndex();
|
|
710
|
+
if (!this.nameSymbol(t.symbol, false))
|
|
711
|
+
return -1;
|
|
712
|
+
this.begin(22 /* RecordKind.AnonymousObject */, shell, noRefs, this.naming << 6 /* RecordHeader.NamingShift */);
|
|
713
|
+
const w = this.writer;
|
|
714
|
+
w.word(t.objectFlags & ~lazyObjectFlags());
|
|
715
|
+
w.word(complete);
|
|
716
|
+
this.begin(23 /* RecordKind.CompleteObject */, complete, members, 0);
|
|
717
|
+
w.word(shell);
|
|
718
|
+
w.word(infos.length);
|
|
719
|
+
for (const info of infos) {
|
|
720
|
+
w.word(info.keyType);
|
|
721
|
+
w.word(info.valueType);
|
|
722
|
+
w.word(info.isReadonly ? 1 : 0);
|
|
723
|
+
w.node(info.declaration);
|
|
724
|
+
w.word(info.components.length);
|
|
725
|
+
for (const node of info.components)
|
|
726
|
+
w.node(node);
|
|
727
|
+
}
|
|
728
|
+
return shell;
|
|
496
729
|
}
|
|
497
|
-
/** The records of property symbols, by symbol. */
|
|
498
|
-
properties = new Map();
|
|
499
730
|
/** A property symbol of an object literal type: a checker's own, made from one of the program or from another. */
|
|
500
731
|
property(prop) {
|
|
501
|
-
const
|
|
732
|
+
const id = getSymbolId(prop);
|
|
733
|
+
const known = this.properties.get(id);
|
|
502
734
|
if (known !== undefined)
|
|
503
735
|
return known;
|
|
504
736
|
const c = this.c;
|
|
@@ -506,7 +738,6 @@ export class Exporter {
|
|
|
506
738
|
return this.fail('property of the program');
|
|
507
739
|
if ((prop.checkFlags & ~(8 /* CheckFlags.Readonly */ | 32768 /* CheckFlags.Late */)) !== 0)
|
|
508
740
|
return this.fail('property check flags ' + prop.checkFlags);
|
|
509
|
-
const id = getSymbolId(prop);
|
|
510
741
|
const t = c.symbolTypes.get(id);
|
|
511
742
|
if (t === undefined)
|
|
512
743
|
return this.fail('property without a type');
|
|
@@ -527,33 +758,52 @@ export class Exporter {
|
|
|
527
758
|
else
|
|
528
759
|
program = source;
|
|
529
760
|
}
|
|
530
|
-
const
|
|
531
|
-
record.nodes = getDeclarations(prop).slice();
|
|
532
|
-
if (prop.parent !== undefined && !this.symbolOf(record, prop.parent, true))
|
|
533
|
-
return -1;
|
|
534
|
-
if (program !== undefined && getSymbolId(program) > this.symbolLimit)
|
|
535
|
-
return this.fail('symbol of the thread');
|
|
761
|
+
const declarations = getDeclarations(prop);
|
|
536
762
|
if (!this.sharedNode(prop.valueDeclaration))
|
|
537
763
|
return -1;
|
|
538
|
-
for (const node of
|
|
764
|
+
for (const node of declarations)
|
|
539
765
|
if (!this.sharedNode(node))
|
|
540
766
|
return -1;
|
|
541
|
-
const index = this.
|
|
542
|
-
this.properties.set(
|
|
543
|
-
this.addedProperties.push(
|
|
767
|
+
const index = this.nextIndex();
|
|
768
|
+
this.properties.set(id, index);
|
|
769
|
+
this.addedProperties.push(id);
|
|
770
|
+
if (!this.nameSymbol(program, false))
|
|
771
|
+
return -1;
|
|
772
|
+
const value = prop.valueDeclaration;
|
|
773
|
+
const valueIsFirst = value !== undefined && declarations.length !== 0 && declarations[0] === value;
|
|
774
|
+
this.node = value;
|
|
775
|
+
let bits = (this.naming << 6 /* RecordHeader.NamingShift */) |
|
|
776
|
+
(valueIsFirst ? 512 /* RecordHeader.ValueIsFirst */ : value !== undefined ? 256 /* RecordHeader.HasNode */ : 0);
|
|
777
|
+
if (prop.parent !== undefined) {
|
|
778
|
+
if (!this.nameSymbol(prop.parent, true))
|
|
779
|
+
return -1;
|
|
780
|
+
bits |= 32 /* RecordHeader.HasAlias */ | (this.aliasMerged >= 0 ? 1024 /* RecordHeader.AliasMerged */ : 0);
|
|
781
|
+
}
|
|
782
|
+
this.begin(24 /* RecordKind.Property */, index, [type, name, target], bits);
|
|
783
|
+
const w = this.writer;
|
|
784
|
+
w.word(prop.flags);
|
|
785
|
+
w.word(prop.checkFlags);
|
|
786
|
+
w.text(prop.name);
|
|
787
|
+
w.word(declarations.length);
|
|
788
|
+
for (const node of declarations)
|
|
789
|
+
w.node(node);
|
|
544
790
|
return index;
|
|
545
791
|
}
|
|
546
792
|
tupleTarget(t) {
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
793
|
+
const index = this.nextIndex();
|
|
794
|
+
this.memo(t, index);
|
|
795
|
+
this.begin(10 /* RecordKind.TupleTarget */, index, noRefs, 0);
|
|
796
|
+
const w = this.writer;
|
|
797
|
+
w.word(t.readonly ? 1 : 0);
|
|
798
|
+
const infos = t.elementInfos;
|
|
799
|
+
w.word(infos.length);
|
|
800
|
+
for (const info of infos) {
|
|
801
|
+
if (!this.sharedNode(info.labeledDeclaration))
|
|
802
|
+
return -1;
|
|
803
|
+
w.word(info.flags);
|
|
804
|
+
w.node(info.labeledDeclaration);
|
|
553
805
|
}
|
|
554
|
-
|
|
555
|
-
record.nodes = nodes;
|
|
556
|
-
return this.add(t, record);
|
|
806
|
+
return index;
|
|
557
807
|
}
|
|
558
808
|
/** An instantiated anonymous or mapped type, or a deferred type reference: its target and outer type arguments. */
|
|
559
809
|
instantiation(t) {
|
|
@@ -576,7 +826,7 @@ export class Exporter {
|
|
|
576
826
|
target = t.target;
|
|
577
827
|
}
|
|
578
828
|
if (declaration !== undefined && t.mapper === undefined && c.resolvedTypes.get(declaration.id) === t)
|
|
579
|
-
return this.
|
|
829
|
+
return this.nodeType(t, declaration);
|
|
580
830
|
if (declaration === undefined || target === undefined || t.mapper === undefined)
|
|
581
831
|
return this.fail('instantiation, declaration ' + (declaration?.kind ?? -1) + ' target ' + (target !== undefined));
|
|
582
832
|
const typeParameters = c.nodeLinks(declaration).outerTypeParameters;
|
|
@@ -591,11 +841,29 @@ export class Exporter {
|
|
|
591
841
|
const refs = [this.type(target), ...this.types(args)];
|
|
592
842
|
if (this.failure !== undefined)
|
|
593
843
|
return -1;
|
|
594
|
-
|
|
595
|
-
|
|
844
|
+
// The alias and its type arguments are exported before the record, as the record names them: a consumer builds
|
|
845
|
+
// records in index order, and would find them unbuilt after it.
|
|
846
|
+
const alias = t.alias;
|
|
847
|
+
const aliasRefs = alias === undefined ? noRefs : this.types(alias.typeArguments);
|
|
848
|
+
if (this.failure !== undefined)
|
|
849
|
+
return -1;
|
|
850
|
+
if (!this.sharedNode(declaration))
|
|
851
|
+
return -1;
|
|
852
|
+
// The record is written after the return types of its signatures, which are built right after it: its index is
|
|
853
|
+
// lower than theirs.
|
|
854
|
+
const index = this.nextIndex();
|
|
855
|
+
this.memo(t, index);
|
|
596
856
|
// Only for an export that hasn't failed (its alias may have): optionalType would forget that failure.
|
|
597
|
-
if (
|
|
857
|
+
if (this.failure === undefined)
|
|
598
858
|
this.resolvedReturnTypes(t, refs);
|
|
859
|
+
if (this.failure !== undefined)
|
|
860
|
+
return -1;
|
|
861
|
+
const bits = this.aliasBits(alias, aliasRefs);
|
|
862
|
+
if (bits < 0)
|
|
863
|
+
return -1;
|
|
864
|
+
this.node = declaration;
|
|
865
|
+
this.begin(11 /* RecordKind.Instantiation */, index, refs, bits | 256 /* RecordHeader.HasNode */);
|
|
866
|
+
this.writer.word(args.length);
|
|
599
867
|
return index;
|
|
600
868
|
}
|
|
601
869
|
/**
|
|
@@ -624,7 +892,10 @@ export class Exporter {
|
|
|
624
892
|
optionalType(t) {
|
|
625
893
|
if (this.failure !== undefined)
|
|
626
894
|
return -1;
|
|
627
|
-
const
|
|
895
|
+
const w = this.writer;
|
|
896
|
+
const at = w.at;
|
|
897
|
+
const unitAt = w.unitAt;
|
|
898
|
+
const count = this.count;
|
|
628
899
|
const types = this.addedTypes.length;
|
|
629
900
|
const properties = this.addedProperties.length;
|
|
630
901
|
const index = this.type(t);
|
|
@@ -636,16 +907,23 @@ export class Exporter {
|
|
|
636
907
|
this.properties.delete(this.addedProperties[i]);
|
|
637
908
|
this.addedTypes.length = types;
|
|
638
909
|
this.addedProperties.length = properties;
|
|
639
|
-
this.
|
|
910
|
+
this.count = count;
|
|
911
|
+
w.at = at;
|
|
912
|
+
w.unitAt = unitAt;
|
|
913
|
+
// A store that ran out stays out: the export can't be posted.
|
|
640
914
|
this.failure = undefined;
|
|
641
915
|
return -1;
|
|
642
916
|
}
|
|
917
|
+
/**
|
|
918
|
+
* The record of the conditional type `t`: the conditional type of a type node, or the instantiation of one with
|
|
919
|
+
* outer type arguments; -1 if it can't be exported.
|
|
920
|
+
*/
|
|
643
921
|
conditional(t) {
|
|
644
922
|
const root = t.root;
|
|
645
923
|
if (t.mapper === undefined) {
|
|
646
924
|
if (this.c.resolvedTypes.get(root.node.id) !== t)
|
|
647
925
|
return this.fail('conditional type');
|
|
648
|
-
return this.
|
|
926
|
+
return this.nodeType(t, root.node);
|
|
649
927
|
}
|
|
650
928
|
const args = [];
|
|
651
929
|
for (const tp of root.outerTypeParameters)
|
|
@@ -660,8 +938,21 @@ export class Exporter {
|
|
|
660
938
|
const refs = this.types(keyed.typeArguments);
|
|
661
939
|
if (this.failure !== undefined)
|
|
662
940
|
return -1;
|
|
663
|
-
const
|
|
664
|
-
|
|
941
|
+
const alias = keyed.alias;
|
|
942
|
+
const aliasRefs = alias === undefined ? noRefs : this.types(alias.typeArguments);
|
|
943
|
+
if (this.failure !== undefined)
|
|
944
|
+
return -1;
|
|
945
|
+
const bits = this.aliasBits(alias, aliasRefs);
|
|
946
|
+
if (bits < 0)
|
|
947
|
+
return -1;
|
|
948
|
+
if (!this.sharedNode(keyed.root.node))
|
|
949
|
+
return -1;
|
|
950
|
+
this.node = keyed.root.node;
|
|
951
|
+
const index = this.nextIndex();
|
|
952
|
+
this.memo(t, index);
|
|
953
|
+
this.begin(16 /* RecordKind.Conditional */, index, refs, bits | 256 /* RecordHeader.HasNode */);
|
|
954
|
+
this.writer.word(keyed.tag);
|
|
955
|
+
return index;
|
|
665
956
|
}
|
|
666
957
|
const distributed = this.c.distributedConditionals?.get(t);
|
|
667
958
|
if (distributed === undefined)
|
|
@@ -669,14 +960,40 @@ export class Exporter {
|
|
|
669
960
|
const refs = this.types([...distributed.typeArguments, distributed.constituent]);
|
|
670
961
|
if (this.failure !== undefined)
|
|
671
962
|
return -1;
|
|
672
|
-
const
|
|
673
|
-
|
|
963
|
+
const alias = distributed.alias;
|
|
964
|
+
const aliasRefs = alias === undefined ? noRefs : this.types(alias.typeArguments);
|
|
965
|
+
if (this.failure !== undefined)
|
|
966
|
+
return -1;
|
|
967
|
+
const bits = this.aliasBits(alias, aliasRefs);
|
|
968
|
+
if (bits < 0)
|
|
969
|
+
return -1;
|
|
970
|
+
if (!this.sharedNode(root.node))
|
|
971
|
+
return -1;
|
|
972
|
+
this.node = root.node;
|
|
973
|
+
const index = this.nextIndex();
|
|
974
|
+
this.memo(t, index);
|
|
975
|
+
this.begin(21 /* RecordKind.DistributedConditional */, index, refs, bits | 256 /* RecordHeader.HasNode */);
|
|
976
|
+
this.writer.word(distributed.tag);
|
|
977
|
+
return index;
|
|
674
978
|
}
|
|
675
979
|
const refs = this.types(args);
|
|
676
980
|
if (this.failure !== undefined)
|
|
677
981
|
return -1;
|
|
678
|
-
const
|
|
679
|
-
|
|
982
|
+
const alias = t.alias;
|
|
983
|
+
const aliasRefs = alias === undefined ? noRefs : this.types(alias.typeArguments);
|
|
984
|
+
if (this.failure !== undefined)
|
|
985
|
+
return -1;
|
|
986
|
+
const bits = this.aliasBits(alias, aliasRefs);
|
|
987
|
+
if (bits < 0)
|
|
988
|
+
return -1;
|
|
989
|
+
if (!this.sharedNode(root.node))
|
|
990
|
+
return -1;
|
|
991
|
+
this.node = root.node;
|
|
992
|
+
const index = this.nextIndex();
|
|
993
|
+
this.memo(t, index);
|
|
994
|
+
this.begin(16 /* RecordKind.Conditional */, index, refs, bits | 256 /* RecordHeader.HasNode */);
|
|
995
|
+
this.writer.word(tag);
|
|
996
|
+
return index;
|
|
680
997
|
}
|
|
681
998
|
}
|
|
682
999
|
/** Object flags that checkers compute after creating a type (a function: see `sharedValueFlags`). */
|
|
@@ -689,58 +1006,162 @@ function lazyObjectFlags() {
|
|
|
689
1006
|
268435456 /* ObjectFlags.IdenticalBaseTypeExists */);
|
|
690
1007
|
}
|
|
691
1008
|
/** Translates records into a checker's own types; one translation per producer type. */
|
|
1009
|
+
const noTypes = [];
|
|
1010
|
+
/** A type alias whose symbol and type arguments are read: its declared type first, as instantiating it does. */
|
|
1011
|
+
function typeAliasOf(c, symbol, refs) {
|
|
1012
|
+
// An instantiation of an alias follows its declared type, which records the alias's type parameters.
|
|
1013
|
+
getDeclaredTypeOfSymbol(c, symbol);
|
|
1014
|
+
return new TypeAlias(symbol, refs);
|
|
1015
|
+
}
|
|
1016
|
+
/** Translates the records of another checker's store into this checker's types, reading them where they are. */
|
|
692
1017
|
export class Translator {
|
|
693
1018
|
c;
|
|
694
|
-
|
|
1019
|
+
store;
|
|
695
1020
|
initial;
|
|
696
1021
|
/** Set when a record has no counterpart here; the translation is then abandoned. */
|
|
697
1022
|
failed = false;
|
|
698
|
-
constructor(c) {
|
|
1023
|
+
constructor(c, store) {
|
|
699
1024
|
this.c = c;
|
|
1025
|
+
this.store = store;
|
|
700
1026
|
this.initial = initialTypes(c);
|
|
701
1027
|
}
|
|
702
|
-
/**
|
|
703
|
-
|
|
1028
|
+
/**
|
|
1029
|
+
* The symbol of `node`, named by `how`: the symbol of the node itself, or its export symbol. The record names
|
|
1030
|
+
* symbols of the program this way, and a consumer that hasn't the symbol fails the translation.
|
|
1031
|
+
*/
|
|
1032
|
+
namedSymbol(node, how) {
|
|
1033
|
+
const local = getSymbol(node);
|
|
1034
|
+
return how === 2 /* SymbolNaming.Export */ ? local?.exportSymbol : local;
|
|
1035
|
+
}
|
|
1036
|
+
/** Abandons the record being read: the translation fails, and a later one starts afresh. */
|
|
1037
|
+
abandon() {
|
|
1038
|
+
this.failed = true;
|
|
1039
|
+
return undefined;
|
|
1040
|
+
}
|
|
1041
|
+
/** A property symbol made as the producer made it; undefined if its record can't be read here. */
|
|
1042
|
+
property(index) {
|
|
704
1043
|
const c = this.c;
|
|
705
|
-
const
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
1044
|
+
const store = this.store;
|
|
1045
|
+
const reader = store.reader;
|
|
1046
|
+
if (!reader.begin(index))
|
|
1047
|
+
return this.abandon();
|
|
1048
|
+
const f = reader.numbers;
|
|
1049
|
+
const type = store.types.get(f[reader.refAt]);
|
|
1050
|
+
if (type === undefined)
|
|
1051
|
+
return this.abandon();
|
|
1052
|
+
const nameAt = f[reader.refAt + 1];
|
|
1053
|
+
const targetAt = f[reader.refAt + 2];
|
|
1054
|
+
let parent = undefined;
|
|
1055
|
+
if (reader.aliasNaming >= 0) {
|
|
1056
|
+
if (reader.aliasNaming === 3 /* SymbolNaming.Merged */)
|
|
1057
|
+
parent = c.mergedSymbols.get(reader.aliasMergedId);
|
|
1058
|
+
else {
|
|
1059
|
+
const node = reader.readNode(reader.aliasNodeStart);
|
|
1060
|
+
if (node === undefined)
|
|
1061
|
+
return this.abandon();
|
|
1062
|
+
parent = this.namedSymbol(node, reader.aliasNaming);
|
|
1063
|
+
}
|
|
1064
|
+
if (parent === undefined)
|
|
1065
|
+
return this.abandon();
|
|
1066
|
+
}
|
|
1067
|
+
let source = undefined;
|
|
1068
|
+
if (reader.symbolNaming === 3 /* SymbolNaming.Merged */)
|
|
1069
|
+
source = c.mergedSymbols.get(reader.symbolMergedId);
|
|
1070
|
+
else if (reader.symbolNaming !== 0 /* SymbolNaming.None */) {
|
|
1071
|
+
const node = reader.readNode(reader.symbolNodeStart);
|
|
1072
|
+
if (node === undefined)
|
|
1073
|
+
return this.abandon();
|
|
1074
|
+
source = this.namedSymbol(node, reader.symbolNaming);
|
|
1075
|
+
}
|
|
1076
|
+
if (reader.symbolNaming !== 0 /* SymbolNaming.None */ && source === undefined)
|
|
1077
|
+
return this.abandon();
|
|
1078
|
+
let value = undefined;
|
|
1079
|
+
if (reader.nodeStart >= 0) {
|
|
1080
|
+
value = reader.readNode(reader.nodeStart);
|
|
1081
|
+
if (value === undefined)
|
|
1082
|
+
return this.abandon();
|
|
1083
|
+
}
|
|
1084
|
+
let p = reader.fields;
|
|
1085
|
+
const flags = f[p++];
|
|
1086
|
+
const checkFlags = f[p++];
|
|
1087
|
+
const text = reader.textAt(p);
|
|
1088
|
+
p += 2;
|
|
1089
|
+
const count = f[p++];
|
|
1090
|
+
const declarations = [];
|
|
1091
|
+
for (let i = 0; i < count; i++) {
|
|
1092
|
+
const node = reader.readNode(p);
|
|
1093
|
+
p += reader.nodeWords(p);
|
|
1094
|
+
if (node === undefined)
|
|
1095
|
+
return this.abandon();
|
|
1096
|
+
declarations.push(node);
|
|
1097
|
+
}
|
|
1098
|
+
if ((reader.header & 512 /* RecordHeader.ValueIsFirst */) !== 0)
|
|
1099
|
+
value = declarations[0];
|
|
1100
|
+
const prop = c.newSymbolWithCheckFlags(flags & ~33554432 /* SymbolFlags.Transient */, text, checkFlags);
|
|
1101
|
+
setDeclarations(prop, declarations);
|
|
1102
|
+
prop.valueDeclaration = value;
|
|
1103
|
+
if (parent !== undefined)
|
|
1104
|
+
prop.parent = parent;
|
|
710
1105
|
const id = getSymbolId(prop);
|
|
711
|
-
c.symbolTypes.set(id,
|
|
712
|
-
if (
|
|
713
|
-
|
|
714
|
-
|
|
1106
|
+
c.symbolTypes.set(id, type);
|
|
1107
|
+
if (nameAt >= 0) {
|
|
1108
|
+
const nameType = store.types.get(nameAt);
|
|
1109
|
+
if (nameType === undefined)
|
|
1110
|
+
return this.abandon();
|
|
1111
|
+
c.symbolNameTypes.set(id, nameType);
|
|
1112
|
+
}
|
|
1113
|
+
const target = targetAt >= 0 ? store.symbols.get(targetAt) : source;
|
|
715
1114
|
if (target !== undefined)
|
|
716
1115
|
c.symbolTargets.set(id, target);
|
|
717
1116
|
return prop;
|
|
718
1117
|
}
|
|
719
|
-
/** Sets the members of the `AnonymousObject` record a `CompleteObject` record completes. */
|
|
720
|
-
complete(
|
|
1118
|
+
/** Sets the members of the `AnonymousObject` record a `CompleteObject` record completes; false if it can't read it. */
|
|
1119
|
+
complete(index) {
|
|
721
1120
|
const c = this.c;
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
|
|
1121
|
+
const store = this.store;
|
|
1122
|
+
const reader = store.reader;
|
|
1123
|
+
if (!reader.begin(index))
|
|
1124
|
+
return false;
|
|
1125
|
+
const f = reader.numbers;
|
|
1126
|
+
const refAt = reader.refAt;
|
|
1127
|
+
let p = reader.fields;
|
|
1128
|
+
const shell = store.types.get(f[p++]);
|
|
1129
|
+
if (shell === undefined)
|
|
1130
|
+
return false;
|
|
1131
|
+
if ((shell.objectFlags & 2097152 /* ObjectFlags.MembersResolved */) !== 0)
|
|
1132
|
+
return true;
|
|
725
1133
|
const members = new Map();
|
|
726
|
-
for (
|
|
727
|
-
const prop = symbols[i];
|
|
1134
|
+
for (let i = 0; i < reader.refCount; i++) {
|
|
1135
|
+
const prop = store.symbols.get(f[refAt + i]);
|
|
1136
|
+
if (prop === undefined)
|
|
1137
|
+
return false;
|
|
728
1138
|
members.set(prop.name, prop);
|
|
729
1139
|
}
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
1140
|
+
const count = f[p++];
|
|
1141
|
+
const infos = [];
|
|
1142
|
+
for (let i = 0; i < count; i++) {
|
|
1143
|
+
const keyType = store.types.get(f[p]);
|
|
1144
|
+
const valueType = store.types.get(f[p + 1]);
|
|
1145
|
+
const isReadonly = f[p + 2] === 1;
|
|
1146
|
+
p += 3;
|
|
1147
|
+
const declaration = reader.readNode(p);
|
|
1148
|
+
p += reader.nodeWords(p);
|
|
1149
|
+
const components = [];
|
|
1150
|
+
const componentCount = f[p++];
|
|
1151
|
+
for (let j = 0; j < componentCount; j++) {
|
|
1152
|
+
const node = reader.readNode(p);
|
|
1153
|
+
p += reader.nodeWords(p);
|
|
1154
|
+
if (node === undefined)
|
|
1155
|
+
return false;
|
|
1156
|
+
components.push(node);
|
|
1157
|
+
}
|
|
1158
|
+
if (keyType === undefined || valueType === undefined)
|
|
1159
|
+
return false;
|
|
1160
|
+
infos.push(new IndexInfo(keyType, valueType, isReadonly, declaration, components));
|
|
739
1161
|
}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
return t;
|
|
1162
|
+
setStructuredTypeMembers(c, shell, members, [], [], infos);
|
|
1163
|
+
keepPropertiesOnly(c, shell, members, scannedLiteralPropertiesLimit);
|
|
1164
|
+
return true;
|
|
744
1165
|
}
|
|
745
1166
|
/**
|
|
746
1167
|
* Resolves the declared type of the type alias whose declaration contains the type node `node`, as resolving its
|
|
@@ -762,128 +1183,202 @@ export class Translator {
|
|
|
762
1183
|
return;
|
|
763
1184
|
}
|
|
764
1185
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
return undefined;
|
|
768
|
-
const symbol = r.aliasMergedId >= 0 ? this.c.mergedSymbols.get(r.aliasMergedId) : r.aliasSymbol;
|
|
769
|
-
// An instantiation of an alias follows its declared type, which records the alias's type parameters.
|
|
770
|
-
getDeclaredTypeOfSymbol(this.c, symbol);
|
|
771
|
-
return new TypeAlias(symbol, r.aliasRefs.map((i) => types[i]));
|
|
772
|
-
}
|
|
773
|
-
symbol(r) {
|
|
774
|
-
return r.mergedId >= 0 ? this.c.mergedSymbols.get(r.mergedId) : r.symbol;
|
|
775
|
-
}
|
|
776
|
-
build(r, types) {
|
|
1186
|
+
/** The type record `index`; undefined if it or a record it needs can't be translated here. */
|
|
1187
|
+
record(index) {
|
|
777
1188
|
const c = this.c;
|
|
1189
|
+
const store = this.store;
|
|
1190
|
+
const reader = store.reader;
|
|
1191
|
+
if (!reader.begin(index))
|
|
1192
|
+
return this.abandon();
|
|
1193
|
+
const f = reader.numbers;
|
|
1194
|
+
const header = reader.header;
|
|
1195
|
+
const kind = reader.kind;
|
|
1196
|
+
const n = reader.refCount;
|
|
778
1197
|
// An instantiation's refs end with the return types of its signatures, which are built after it.
|
|
779
|
-
const
|
|
780
|
-
const refs =
|
|
781
|
-
|
|
1198
|
+
const count = kind === 11 /* RecordKind.Instantiation */ ? Math.min(n, 1 + f[reader.fields]) : n;
|
|
1199
|
+
const refs = new Array(count);
|
|
1200
|
+
for (let i = 0; i < count; i++) {
|
|
1201
|
+
const t = store.types.get(f[reader.refAt + i]);
|
|
1202
|
+
if (t === undefined)
|
|
1203
|
+
return this.abandon();
|
|
1204
|
+
refs[i] = t;
|
|
1205
|
+
}
|
|
1206
|
+
let aliasSymbol = undefined;
|
|
1207
|
+
let aliasRefs = noTypes;
|
|
1208
|
+
if (reader.aliasNaming >= 0) {
|
|
1209
|
+
if (reader.aliasNaming === 3 /* SymbolNaming.Merged */) {
|
|
1210
|
+
aliasSymbol = c.mergedSymbols.get(reader.aliasMergedId);
|
|
1211
|
+
if (aliasSymbol === undefined)
|
|
1212
|
+
return this.abandon();
|
|
1213
|
+
}
|
|
1214
|
+
else {
|
|
1215
|
+
const aliasNode = reader.readNode(reader.aliasNodeStart);
|
|
1216
|
+
if (aliasNode === undefined)
|
|
1217
|
+
return this.abandon();
|
|
1218
|
+
aliasSymbol = this.namedSymbol(aliasNode, reader.aliasNaming);
|
|
1219
|
+
if (aliasSymbol === undefined)
|
|
1220
|
+
return this.abandon();
|
|
1221
|
+
}
|
|
1222
|
+
const aliasCount = reader.aliasRefCount;
|
|
1223
|
+
const types = new Array(aliasCount);
|
|
1224
|
+
for (let i = 0; i < aliasCount; i++) {
|
|
1225
|
+
const t = store.types.get(f[reader.aliasRefAt + i]);
|
|
1226
|
+
if (t === undefined)
|
|
1227
|
+
return this.abandon();
|
|
1228
|
+
types[i] = t;
|
|
1229
|
+
}
|
|
1230
|
+
aliasRefs = types;
|
|
1231
|
+
}
|
|
1232
|
+
let symbol = undefined;
|
|
1233
|
+
if (reader.symbolNaming === 3 /* SymbolNaming.Merged */) {
|
|
1234
|
+
symbol = c.mergedSymbols.get(reader.symbolMergedId);
|
|
1235
|
+
if (symbol === undefined)
|
|
1236
|
+
return this.abandon();
|
|
1237
|
+
}
|
|
1238
|
+
else if (reader.symbolNaming !== 0 /* SymbolNaming.None */) {
|
|
1239
|
+
const symbolNode = reader.readNode(reader.symbolNodeStart);
|
|
1240
|
+
if (symbolNode === undefined)
|
|
1241
|
+
return this.abandon();
|
|
1242
|
+
symbol = this.namedSymbol(symbolNode, reader.symbolNaming);
|
|
1243
|
+
if (symbol === undefined)
|
|
1244
|
+
return this.abandon();
|
|
1245
|
+
}
|
|
1246
|
+
let node = undefined;
|
|
1247
|
+
if (reader.nodeStart >= 0) {
|
|
1248
|
+
node = reader.readNode(reader.nodeStart);
|
|
1249
|
+
if (node === undefined)
|
|
1250
|
+
return this.abandon();
|
|
1251
|
+
}
|
|
1252
|
+
let p = reader.fields;
|
|
1253
|
+
switch (kind) {
|
|
782
1254
|
case 0 /* RecordKind.Initial */:
|
|
783
|
-
return this.initial[
|
|
784
|
-
case 1 /* RecordKind.Literal */:
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
return getBigIntLiteralType(c,
|
|
1255
|
+
return this.initial[f[p]];
|
|
1256
|
+
case 1 /* RecordKind.Literal */: {
|
|
1257
|
+
const flags = f[p++];
|
|
1258
|
+
if ((flags & 2048 /* TypeFlags.NumberLiteral */) !== 0)
|
|
1259
|
+
return getNumberLiteralType(c, reader.doubleAt(p));
|
|
1260
|
+
const text = reader.textAt(p);
|
|
1261
|
+
return (flags & 1024 /* TypeFlags.StringLiteral */) !== 0 ? getStringLiteralType(c, text) : getBigIntLiteralType(c, text);
|
|
1262
|
+
}
|
|
790
1263
|
case 2 /* RecordKind.FreshLiteral */:
|
|
791
1264
|
return getFreshTypeOfLiteralType(c, refs[0]);
|
|
792
1265
|
case 3 /* RecordKind.EnumLiteral */:
|
|
793
1266
|
case 5 /* RecordKind.Declared */:
|
|
794
|
-
return getDeclaredTypeOfSymbol(c,
|
|
1267
|
+
return getDeclaredTypeOfSymbol(c, symbol);
|
|
795
1268
|
case 6 /* RecordKind.ThisType */:
|
|
796
|
-
return getDeclaredTypeOfSymbol(c,
|
|
1269
|
+
return getDeclaredTypeOfSymbol(c, symbol).thisType;
|
|
797
1270
|
case 4 /* RecordKind.UniqueSymbol */:
|
|
798
|
-
return getESSymbolLikeTypeForNode(c,
|
|
1271
|
+
return getESSymbolLikeTypeForNode(c, symbol.valueDeclaration);
|
|
799
1272
|
case 7 /* RecordKind.SymbolType */:
|
|
800
|
-
return getTypeOfSymbol(c,
|
|
1273
|
+
return getTypeOfSymbol(c, symbol);
|
|
801
1274
|
case 8 /* RecordKind.NodeType */: {
|
|
802
|
-
const node = r.node;
|
|
803
1275
|
const cached = c.resolvedTypes.get(node.id);
|
|
804
1276
|
if (cached !== undefined)
|
|
805
1277
|
return cached;
|
|
806
1278
|
this.aliasOfNode(node);
|
|
807
1279
|
if (this.failed)
|
|
808
|
-
return
|
|
1280
|
+
return undefined;
|
|
809
1281
|
const t = getTypeFromTypeNode(c, node);
|
|
810
1282
|
return c.resolvedTypes.get(node.id) ?? t;
|
|
811
1283
|
}
|
|
812
1284
|
case 9 /* RecordKind.Reference */:
|
|
813
1285
|
return createTypeReference(c, refs[0], refs.slice(1));
|
|
814
1286
|
case 10 /* RecordKind.TupleTarget */: {
|
|
1287
|
+
const readonly = f[p++] === 1;
|
|
1288
|
+
const infosCount = f[p++];
|
|
815
1289
|
const infos = [];
|
|
816
|
-
for (let i = 0; i <
|
|
817
|
-
|
|
818
|
-
|
|
1290
|
+
for (let i = 0; i < infosCount; i++) {
|
|
1291
|
+
const flags = f[p++];
|
|
1292
|
+
const label = reader.readNode(p);
|
|
1293
|
+
p += reader.nodeWords(p);
|
|
1294
|
+
infos.push(new TupleElementInfo(flags, label));
|
|
1295
|
+
}
|
|
1296
|
+
return getTupleTargetType(c, infos, readonly);
|
|
819
1297
|
}
|
|
820
1298
|
case 11 /* RecordKind.Instantiation */: {
|
|
1299
|
+
const args = f[p++];
|
|
821
1300
|
const target = refs[0];
|
|
822
|
-
const typeParameters = outerTypeParametersOf(c,
|
|
823
|
-
return instantiateTypeWithAlias(c, target, newTypeMapper(typeParameters, refs.slice(1, 1 +
|
|
1301
|
+
const typeParameters = outerTypeParametersOf(c, node, target);
|
|
1302
|
+
return instantiateTypeWithAlias(c, target, newTypeMapper(typeParameters, refs.slice(1, 1 + args)), aliasSymbol === undefined ? undefined : typeAliasOf(c, aliasSymbol, aliasRefs));
|
|
824
1303
|
}
|
|
825
1304
|
case 12 /* RecordKind.Union */: {
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
1305
|
+
const flags = f[p++];
|
|
1306
|
+
const originAt = f[p++];
|
|
1307
|
+
if ((header & 2048 /* RecordHeader.Constituent */) !== 0)
|
|
1308
|
+
return newUnionType(c, flags & 0 /* ObjectFlags.None */, refs);
|
|
1309
|
+
return getUnionTypeFromSortedList(c, refs, flags & (32768 /* ObjectFlags.PrimitiveUnion */ | 33554432 /* ObjectFlags.ContainsIntersections */), aliasSymbol === undefined ? undefined : typeAliasOf(c, aliasSymbol, aliasRefs), originAt >= 0 ? store.types.get(originAt) : undefined);
|
|
830
1310
|
}
|
|
831
1311
|
case 13 /* RecordKind.Intersection */: {
|
|
832
|
-
|
|
1312
|
+
const flags = f[p++];
|
|
1313
|
+
if ((header & 2048 /* RecordHeader.Constituent */) !== 0)
|
|
833
1314
|
return newIntersectionType(c, 0 /* ObjectFlags.None */, refs);
|
|
834
|
-
const alias =
|
|
1315
|
+
const alias = aliasSymbol === undefined ? undefined : typeAliasOf(c, aliasSymbol, aliasRefs);
|
|
835
1316
|
const hash = typeListHash(refs, alias, 0);
|
|
836
1317
|
const existing = c.intersectionTypes.get(hash, refs, alias, 0);
|
|
837
1318
|
if (existing !== undefined)
|
|
838
1319
|
return existing;
|
|
839
|
-
const result = newIntersectionType(c, (
|
|
1320
|
+
const result = newIntersectionType(c, (flags & 134217728 /* ObjectFlags.IsConstrainedTypeVariable */) | getPropagatingFlagsOfTypes(refs, 12 /* TypeFlags.Nullable */), refs);
|
|
840
1321
|
result.alias = alias;
|
|
841
1322
|
c.intersectionTypes.set(hash, refs, alias, 0, result);
|
|
842
1323
|
return result;
|
|
843
1324
|
}
|
|
844
1325
|
case 14 /* RecordKind.Index */:
|
|
845
|
-
return getIndexTypeEx(c, refs[0],
|
|
1326
|
+
return getIndexTypeEx(c, refs[0], f[p]);
|
|
846
1327
|
case 15 /* RecordKind.IndexedAccess */:
|
|
847
|
-
return getIndexedAccessTypeEx(c, refs[0], refs[1],
|
|
1328
|
+
return getIndexedAccessTypeEx(c, refs[0], refs[1], f[p], undefined, aliasSymbol === undefined ? undefined : typeAliasOf(c, aliasSymbol, aliasRefs));
|
|
848
1329
|
case 16 /* RecordKind.Conditional */: {
|
|
849
|
-
|
|
1330
|
+
const tag = f[p++];
|
|
1331
|
+
this.aliasOfNode(node);
|
|
850
1332
|
if (this.failed)
|
|
851
|
-
return
|
|
852
|
-
const declared = c.resolvedTypes.get(
|
|
1333
|
+
return undefined;
|
|
1334
|
+
const declared = c.resolvedTypes.get(node.id) ?? getTypeFromTypeNode(c, node);
|
|
853
1335
|
const root = declared.root;
|
|
854
|
-
return getConditionalTypeInstantiation(c, declared, newTypeMapper(root.outerTypeParameters, refs),
|
|
1336
|
+
return getConditionalTypeInstantiation(c, declared, newTypeMapper(root.outerTypeParameters, refs), tag === 1, aliasSymbol === undefined ? undefined : typeAliasOf(c, aliasSymbol, aliasRefs));
|
|
855
1337
|
}
|
|
856
1338
|
case 21 /* RecordKind.DistributedConditional */: {
|
|
857
|
-
|
|
1339
|
+
const tag = f[p++];
|
|
1340
|
+
this.aliasOfNode(node);
|
|
858
1341
|
if (this.failed)
|
|
859
|
-
return
|
|
860
|
-
const declared = (c.resolvedTypes.get(
|
|
1342
|
+
return undefined;
|
|
1343
|
+
const declared = (c.resolvedTypes.get(node.id) ?? getTypeFromTypeNode(c, node));
|
|
861
1344
|
const root = declared.root;
|
|
862
1345
|
const args = refs.slice(0, refs.length - 1);
|
|
863
|
-
const alias =
|
|
864
|
-
getConditionalTypeInstantiation(c, declared, newTypeMapper(root.outerTypeParameters, args),
|
|
865
|
-
const member = c.distributedByKey?.get(distributedKey(root.node.id, args, alias,
|
|
1346
|
+
const alias = aliasSymbol === undefined ? undefined : typeAliasOf(c, aliasSymbol, aliasRefs);
|
|
1347
|
+
getConditionalTypeInstantiation(c, declared, newTypeMapper(root.outerTypeParameters, args), tag === 1, alias);
|
|
1348
|
+
const member = c.distributedByKey?.get(distributedKey(root.node.id, args, alias, tag, refs[refs.length - 1]));
|
|
866
1349
|
if (member === undefined)
|
|
867
|
-
this.
|
|
868
|
-
return member
|
|
1350
|
+
return this.abandon();
|
|
1351
|
+
return member;
|
|
1352
|
+
}
|
|
1353
|
+
case 17 /* RecordKind.Template */: {
|
|
1354
|
+
const textsCount = f[p++];
|
|
1355
|
+
const texts = [];
|
|
1356
|
+
for (let i = 0; i < textsCount; i++) {
|
|
1357
|
+
texts.push(reader.textAt(p));
|
|
1358
|
+
p += 2;
|
|
1359
|
+
}
|
|
1360
|
+
return getTemplateLiteralType(c, texts, refs);
|
|
869
1361
|
}
|
|
870
|
-
case 17 /* RecordKind.Template */:
|
|
871
|
-
return getTemplateLiteralType(c, r.texts.slice(), refs);
|
|
872
1362
|
case 18 /* RecordKind.StringMapping */:
|
|
873
|
-
return getStringMappingType(c,
|
|
1363
|
+
return getStringMappingType(c, symbol, refs[0]);
|
|
874
1364
|
case 19 /* RecordKind.Substitution */:
|
|
875
1365
|
return getSubstitutionType(c, refs[0], refs[1]);
|
|
876
1366
|
case 20 /* RecordKind.NoInfer */:
|
|
877
1367
|
return getNoInferType(c, refs[0]);
|
|
878
|
-
case 22 /* RecordKind.AnonymousObject */:
|
|
879
|
-
|
|
1368
|
+
case 22 /* RecordKind.AnonymousObject */: {
|
|
1369
|
+
// A translation that failed after this record opened the type still made it, and a record built then may name
|
|
1370
|
+
// it: the completion must fill that type, not a new one beside it.
|
|
1371
|
+
const known = store.types.get(index);
|
|
1372
|
+
if (known !== undefined)
|
|
1373
|
+
return known;
|
|
1374
|
+
return newObjectType(c, f[p] | 16 /* ObjectFlags.Anonymous */, symbol);
|
|
1375
|
+
}
|
|
880
1376
|
case 23 /* RecordKind.CompleteObject */:
|
|
881
1377
|
case 24 /* RecordKind.Property */:
|
|
882
|
-
return
|
|
1378
|
+
return this.abandon();
|
|
883
1379
|
}
|
|
884
1380
|
}
|
|
885
1381
|
}
|
|
886
|
-
/** The outer type parameters an instantiation of `target` (declared at `declaration`) maps, as instantiation computes them. */
|
|
887
1382
|
function outerTypeParametersOf(c, declaration, target) {
|
|
888
1383
|
const known = c.nodeLinks(declaration).outerTypeParameters;
|
|
889
1384
|
if (known !== undefined)
|
|
@@ -913,19 +1408,20 @@ export function isSharedReturnSignature(c, sig) {
|
|
|
913
1408
|
(sig.flags & (16 /* SignatureFlags.IsInnerCallChain */ | 32 /* SignatureFlags.IsOuterCallChain */)) === 0 &&
|
|
914
1409
|
c.resolvedSignatures.get(declaration.id) === sig);
|
|
915
1410
|
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
export const
|
|
1411
|
+
/**
|
|
1412
|
+
* The columns of a checker's row of `ThreadSharingData.stats`: the milliseconds of its outermost
|
|
1413
|
+
* computations by kind (0 owned, 1 computed here for a file another checker took, 2 translated, 3
|
|
1414
|
+
* other), of exporting, translating and unpacking results, the results it posted and translated, and
|
|
1415
|
+
* the milliseconds it saved by translating a result instead of computing it.
|
|
1416
|
+
*/
|
|
1417
|
+
export const shareStatColumns = 10;
|
|
1418
|
+
export const shareStatKinds = 4;
|
|
1419
|
+
export const shareStatExport = 4;
|
|
1420
|
+
export const shareStatTranslation = 5;
|
|
1421
|
+
export const shareStatUnpack = 6;
|
|
1422
|
+
export const shareStatPosted = 7;
|
|
1423
|
+
export const shareStatTranslated = 8;
|
|
1424
|
+
export const shareStatSaved = 9;
|
|
929
1425
|
/**
|
|
930
1426
|
* The size of each checker's logs: numbers (int32s) and string code units (2 bytes each), 8 MB and 2 MB. Only the
|
|
931
1427
|
* pages a checker writes take memory.
|
|
@@ -934,6 +1430,8 @@ export const numberLogSize = 1 << 21;
|
|
|
934
1430
|
export const stringLogSize = 1 << 20;
|
|
935
1431
|
/** The number of records each checker's store can hold. */
|
|
936
1432
|
export const recordTableSize = 1 << 20;
|
|
1433
|
+
/** The int32s each checker gets in `ThreadSharingData.used`, a cache line each, for two counters. */
|
|
1434
|
+
export const usedStride = 16;
|
|
937
1435
|
/** The number of entries of each result table: a power of two. */
|
|
938
1436
|
export const resultTableSize = 1 << 20;
|
|
939
1437
|
/**
|
|
@@ -961,6 +1459,10 @@ export function forbiddenWords(count) {
|
|
|
961
1459
|
export function isForbidden(forbidden, count, file, index) {
|
|
962
1460
|
return (Atomics.load(forbidden, file * forbiddenWords(count) + (index >>> 5)) & (1 << (index & 31))) !== 0;
|
|
963
1461
|
}
|
|
1462
|
+
/** Whether checker `index` of `count` looked for a result for a declaration of file `file` and found none (`wanted`). */
|
|
1463
|
+
export function isWanted(wanted, count, file, index) {
|
|
1464
|
+
return (Atomics.load(wanted, file * forbiddenWords(count) + (index >>> 5)) & (1 << (index & 31))) !== 0;
|
|
1465
|
+
}
|
|
964
1466
|
/**
|
|
965
1467
|
* An open-addressing table in a SharedArrayBuffer, from a key (a symbol or node id) to a result's state: 0 until a
|
|
966
1468
|
* checker posts it, then `postedState`.
|
|
@@ -1011,8 +1513,73 @@ export class ResultTable {
|
|
|
1011
1513
|
Atomics.compareExchange(this.words, 2 * slot + 1, 0, state);
|
|
1012
1514
|
}
|
|
1013
1515
|
}
|
|
1014
|
-
/** TSC_TS_SHARE_MIN_MS: the shortest computation a checker posts, in milliseconds (
|
|
1516
|
+
/** TSC_TS_SHARE_MIN_MS: the shortest computation a checker posts, in milliseconds (`shareFloorTotalMs` split by the checkers by default). */
|
|
1015
1517
|
const shareMinValue = process.env.TSC_TS_SHARE_MIN_MS;
|
|
1518
|
+
/**
|
|
1519
|
+
* The floor, in milliseconds of checker time split over the checkers that share: 1 ms with 4 checkers, 0.25 ms with
|
|
1520
|
+
* 16.
|
|
1521
|
+
*/
|
|
1522
|
+
const shareFloorTotalMs = 4;
|
|
1523
|
+
/** `TSC_TS_SHARE_<name>` as a number, or `fallback`. */
|
|
1524
|
+
function shareNumber(name, fallback) {
|
|
1525
|
+
const value = process.env[name];
|
|
1526
|
+
if (value === undefined)
|
|
1527
|
+
return fallback;
|
|
1528
|
+
const parsed = Number(value);
|
|
1529
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* The posting rule's cost model: a post appends at most `(kappa*T - p0)/e` records (`T` the computation's time), a
|
|
1533
|
+
* take builds at most `lambda*T/c`, `p0` is what a post costs itself, `e` each record a post adds and `c` each record
|
|
1534
|
+
* a take builds, all in microseconds. The price instrumentation's replay (`TSC_TS_SHARE_PRICE`) fits them; the
|
|
1535
|
+
* `TSC_TS_SHARE_*` variables override them for measurement.
|
|
1536
|
+
*
|
|
1537
|
+
* Read where they are used, not once at module evaluation: this module is in an import cycle (it imports
|
|
1538
|
+
* `checker-symbols.js`, which imports `checker.js`, which imports this module), and scriptc rejects code that runs at
|
|
1539
|
+
* module evaluation there (SC1016: a class the cycle reaches can be accessed before its declaration runs), so a
|
|
1540
|
+
* `const kappa = shareNumber(...)` at the top level does not build.
|
|
1541
|
+
*/
|
|
1542
|
+
function shareKappa() {
|
|
1543
|
+
return shareNumber('TSC_TS_SHARE_KAPPA', 0.25);
|
|
1544
|
+
}
|
|
1545
|
+
function shareLambda() {
|
|
1546
|
+
return shareNumber('TSC_TS_SHARE_LAMBDA', 0.3);
|
|
1547
|
+
}
|
|
1548
|
+
function sharePostUs() {
|
|
1549
|
+
return shareNumber('TSC_TS_SHARE_P0_US', 0.3);
|
|
1550
|
+
}
|
|
1551
|
+
function shareExportRecordUs() {
|
|
1552
|
+
return shareNumber('TSC_TS_SHARE_E_US', 1.2);
|
|
1553
|
+
}
|
|
1554
|
+
function shareBuildRecordUs() {
|
|
1555
|
+
return shareNumber('TSC_TS_SHARE_C_US', 6.59);
|
|
1556
|
+
}
|
|
1557
|
+
/**
|
|
1558
|
+
* Whether a run posts cost-bounded is its configuration's choice (`RunConfig.bounded`), not a rule here: off by
|
|
1559
|
+
* default, because a bounded post costs a run that gains nothing by it (T3 server's c16 check was 8-9% slower),
|
|
1560
|
+
* and turned on where the project's own runs measured it faster (`run-config.ts`). A bounded run keeps the 2-8 ms
|
|
1561
|
+
* floor's sharing gate, posts only results another checker demanded, and budgets both the records a post adds and
|
|
1562
|
+
* the records a take builds; an unbounded run keeps Phase 1's rule and posts every result over the floor.
|
|
1563
|
+
*/
|
|
1564
|
+
/** The floor's total in milliseconds before the cost-bounded rule (`shareFloorTotalMs`): 2 ms at 16, 8 ms at 4. */
|
|
1565
|
+
const shareFloorTotalMsPhase1 = 32;
|
|
1566
|
+
/**
|
|
1567
|
+
* The rule of the design's Phase 2: a result is posted when it took at least the floor (`ThreadSharing.minMs`), and a
|
|
1568
|
+
* consumer builds at most `lambda*T/c` records to take one, where `T` is the producer's time for it.
|
|
1569
|
+
*/
|
|
1570
|
+
export class CostBoundedRule {
|
|
1571
|
+
bounded;
|
|
1572
|
+
/** `bounded` false is Phase 1's rule: the same floor, and a take that may build any number of records. */
|
|
1573
|
+
constructor(bounded) {
|
|
1574
|
+
this.bounded = bounded;
|
|
1575
|
+
}
|
|
1576
|
+
posts(sharing, _key, ms, _diagnosticFiles, _fileIndex) {
|
|
1577
|
+
return ms >= sharing.minMs;
|
|
1578
|
+
}
|
|
1579
|
+
buildBudget(ms) {
|
|
1580
|
+
return this.bounded ? Math.floor((shareLambda() * ms * 1000) / shareBuildRecordUs()) : Number.POSITIVE_INFINITY;
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1016
1583
|
// Debugging: TSC_TS_SHARE_BLOCK and TSC_TS_SHARE_ALLOW name parts of file names (comma-separated, each optionally with
|
|
1017
1584
|
// a range of declaration positions, `name@start-end`) whose results are never or only translated, and
|
|
1018
1585
|
// TSC_TS_SHARE_TRACE=1 prints each translation.
|
|
@@ -1020,7 +1587,8 @@ const shareBlockValue = process.env.TSC_TS_SHARE_BLOCK;
|
|
|
1020
1587
|
const shareAllowValue = process.env.TSC_TS_SHARE_ALLOW;
|
|
1021
1588
|
const shareTraceValue = process.env.TSC_TS_SHARE_TRACE;
|
|
1022
1589
|
// Debugging: TSC_TS_SHARE_VERIFY=1 computes each result a checker could translate, translates it after, and prints
|
|
1023
|
-
// the results whose translation prints differently
|
|
1590
|
+
// the results whose translation prints differently; =2 checks that a translation creates types record by record, in
|
|
1591
|
+
// the order the producer created them.
|
|
1024
1592
|
const shareVerifyValue = process.env.TSC_TS_SHARE_VERIFY;
|
|
1025
1593
|
/** Checks a translation against the computation it replaces (TSC_TS_SHARE_VERIFY). */
|
|
1026
1594
|
function verifyTranslation(c, kind, node, own, translated) {
|
|
@@ -1131,7 +1699,8 @@ export class IdBits {
|
|
|
1131
1699
|
}
|
|
1132
1700
|
/**
|
|
1133
1701
|
* The files of the diagnostics of results, by key (a symbol or declaration id), with a bit per key: most results have
|
|
1134
|
-
* none, and the bit answers that without a lookup.
|
|
1702
|
+
* none, and the bit answers that without a lookup. A file is its index in the program (`Checker.fileIndex`), and -1
|
|
1703
|
+
* a diagnostic without a file.
|
|
1135
1704
|
*/
|
|
1136
1705
|
export class FilesByKey {
|
|
1137
1706
|
files = new Map();
|
|
@@ -1151,6 +1720,18 @@ export class ThreadSharing {
|
|
|
1151
1720
|
returns;
|
|
1152
1721
|
/** TSC_TS_SHARE_MIN_MS: the shortest computation this checker posts. */
|
|
1153
1722
|
minMs;
|
|
1723
|
+
/**
|
|
1724
|
+
* Whether this run is cost-bounded, which the run's configuration chose (`RunConfig.bounded`, off by default and
|
|
1725
|
+
* learned from the project's records). Without it the run keeps Phase 1's behavior: no demand bits, no export or
|
|
1726
|
+
* take budget, the 2-8 ms floor.
|
|
1727
|
+
*/
|
|
1728
|
+
bounded;
|
|
1729
|
+
/**
|
|
1730
|
+
* The rule that decides which of this checker's results are posted, and how much a take may build. A small-result
|
|
1731
|
+
* tier stacks on it by constructing its own rule here. Built per checker, not once at module evaluation, for the
|
|
1732
|
+
* reason the cost model's functions give (`shareKappa`).
|
|
1733
|
+
*/
|
|
1734
|
+
rule;
|
|
1154
1735
|
initialIndexes = new Map();
|
|
1155
1736
|
initial;
|
|
1156
1737
|
numberLogs = [];
|
|
@@ -1159,9 +1740,26 @@ export class ThreadSharing {
|
|
|
1159
1740
|
used;
|
|
1160
1741
|
takers;
|
|
1161
1742
|
forbidden;
|
|
1743
|
+
/** The checkers that missed on a key in each file (`ThreadSharingData.wanted`). */
|
|
1744
|
+
wanted;
|
|
1745
|
+
/** The files this checker noted a miss on, so that each is marked in `wanted` once. */
|
|
1746
|
+
wantedFiles = new IdBits();
|
|
1747
|
+
/** The stop switch of every checker (`ThreadSharingData.control`). */
|
|
1748
|
+
control;
|
|
1749
|
+
/** This checker's row of the counters the main thread reads (`ThreadSharingData.stats`). */
|
|
1750
|
+
stats;
|
|
1751
|
+
row;
|
|
1162
1752
|
/** The number of results this checker posted. */
|
|
1163
1753
|
posted = 0;
|
|
1164
1754
|
translated = 0;
|
|
1755
|
+
/**
|
|
1756
|
+
* Set while this checker exports a result into its store. A computation nested in the export walk can reach
|
|
1757
|
+
* another shared result: posting it would write its records in the middle of the record being exported, and an
|
|
1758
|
+
* export that failed would rewind over the posted result's words.
|
|
1759
|
+
*/
|
|
1760
|
+
exporting = false;
|
|
1761
|
+
/** Shared results a nested computation left to the checkers that compute them. */
|
|
1762
|
+
nested = 0;
|
|
1165
1763
|
/** Outermost shared-kind computations: nesting depth, the kind of the current one, and the time by kind. */
|
|
1166
1764
|
depth = 0;
|
|
1167
1765
|
kind = 0;
|
|
@@ -1170,6 +1768,31 @@ export class ThreadSharing {
|
|
|
1170
1768
|
addOutermost(ms) {
|
|
1171
1769
|
this.kindMs[this.kind] += ms;
|
|
1172
1770
|
this.kind = 3;
|
|
1771
|
+
this.writeStats();
|
|
1772
|
+
}
|
|
1773
|
+
/**
|
|
1774
|
+
* Copies this checker's counters into its row of `stats`, the memory the main thread reads them from
|
|
1775
|
+
* while checking runs. Called where a counter changes, a few times per result at most.
|
|
1776
|
+
*/
|
|
1777
|
+
writeStats() {
|
|
1778
|
+
const s = this.stats;
|
|
1779
|
+
const row = this.row;
|
|
1780
|
+
for (let k = 0; k < shareStatKinds; k++)
|
|
1781
|
+
s[row + k] = this.kindMs[k];
|
|
1782
|
+
s[row + shareStatExport] = this.exportMs;
|
|
1783
|
+
s[row + shareStatTranslation] = this.translationMs;
|
|
1784
|
+
// A translation reads its records in place, so there is no separate unpacking time: shareStatUnpack stays 0.
|
|
1785
|
+
s[row + shareStatPosted] = this.posted;
|
|
1786
|
+
s[row + shareStatTranslated] = this.translated;
|
|
1787
|
+
}
|
|
1788
|
+
/** Adds to the milliseconds this checker saved by translating results instead of computing them. */
|
|
1789
|
+
addSaved(ms) {
|
|
1790
|
+
const at = this.row + shareStatSaved;
|
|
1791
|
+
this.stats[at] = this.stats[at] + ms;
|
|
1792
|
+
}
|
|
1793
|
+
/** Whether the main thread stopped translating posted results (control 2): this checker computes them. */
|
|
1794
|
+
stoppedTranslating() {
|
|
1795
|
+
return Atomics.load(this.control, 0) === 2;
|
|
1173
1796
|
}
|
|
1174
1797
|
notShareable = 0;
|
|
1175
1798
|
notShareableMs = 0;
|
|
@@ -1180,17 +1803,26 @@ export class ThreadSharing {
|
|
|
1180
1803
|
verifiedIdentity = 0;
|
|
1181
1804
|
belowMin = 0;
|
|
1182
1805
|
belowMinMs = 0;
|
|
1806
|
+
/** Results whose export passed the budget `kappa*T`, and translations that stopped at `lambda*T/c`. */
|
|
1807
|
+
overBudget = 0;
|
|
1808
|
+
overBudgetMs = 0;
|
|
1809
|
+
stoppedTranslations = 0;
|
|
1810
|
+
/** Results not posted because no checker that may take one missed on a key in the declaration's file. */
|
|
1811
|
+
notDemanded = 0;
|
|
1183
1812
|
exportFailed = 0;
|
|
1184
1813
|
exportFailedMs = 0;
|
|
1185
1814
|
exportFailures = new Map();
|
|
1186
|
-
|
|
1187
|
-
|
|
1815
|
+
/** TSC_TS_SHARE_VERIFY=2: records whose translation created types before an earlier record's. */
|
|
1816
|
+
orderViolations = 0;
|
|
1817
|
+
storeFailed = 0;
|
|
1818
|
+
/** Of `storeFailed`, the results that didn't fit in this checker's logs. */
|
|
1188
1819
|
logFull = 0;
|
|
1189
1820
|
computedPrivately = 0;
|
|
1821
|
+
/** Results this checker computed and kept because the main thread stopped sharing (control 1 or 2). */
|
|
1822
|
+
stopped = 0;
|
|
1190
1823
|
/** How often a posted result couldn't be taken here; and results for declarations of the checker's own files. */
|
|
1191
1824
|
sawDoneUnusable = 0;
|
|
1192
1825
|
owned = 0;
|
|
1193
|
-
unpackMs = 0;
|
|
1194
1826
|
/** The program's nodes results named, by id. */
|
|
1195
1827
|
nodesById = new Map();
|
|
1196
1828
|
origins = new MergedOrigins();
|
|
@@ -1262,13 +1894,13 @@ export class ThreadSharing {
|
|
|
1262
1894
|
if (own === undefined)
|
|
1263
1895
|
own = new Set();
|
|
1264
1896
|
const file = diagnostics[i].file;
|
|
1265
|
-
own.add(file === undefined ?
|
|
1897
|
+
own.add(file === undefined ? -1 : this.fileIndexOfName(file.fileName));
|
|
1266
1898
|
}
|
|
1267
1899
|
for (let i = suggestionsBefore; i < suggestions.length; i++) {
|
|
1268
1900
|
if (own === undefined)
|
|
1269
1901
|
own = new Set();
|
|
1270
1902
|
const file = suggestions[i].file;
|
|
1271
|
-
own.add(file === undefined ?
|
|
1903
|
+
own.add(file === undefined ? -1 : this.fileIndexOfName(file.fileName));
|
|
1272
1904
|
}
|
|
1273
1905
|
// A deferred diagnostic is reported where its file is checked, as the diagnostics it reported are.
|
|
1274
1906
|
for (let i = deferredBefore; i < deferred.length; i++) {
|
|
@@ -1283,6 +1915,22 @@ export class ThreadSharing {
|
|
|
1283
1915
|
}
|
|
1284
1916
|
}
|
|
1285
1917
|
}
|
|
1918
|
+
/**
|
|
1919
|
+
* The index of the program file named `fileName`, or -1: a diagnostic in a file this checker hasn't is one every
|
|
1920
|
+
* checker would report, like a diagnostic without a file.
|
|
1921
|
+
*/
|
|
1922
|
+
fileIndexOfName(fileName) {
|
|
1923
|
+
let files = this.filesByIndex;
|
|
1924
|
+
if (files === undefined) {
|
|
1925
|
+
files = new Map();
|
|
1926
|
+
for (const [file, index] of this.c.fileIndex)
|
|
1927
|
+
files.set(file.fileName, index);
|
|
1928
|
+
this.filesByIndex = files;
|
|
1929
|
+
}
|
|
1930
|
+
return files.get(fileName) ?? -1;
|
|
1931
|
+
}
|
|
1932
|
+
/** The index of each file by name, built on first use. */
|
|
1933
|
+
filesByIndex = undefined;
|
|
1286
1934
|
constructor(c, data) {
|
|
1287
1935
|
this.c = c;
|
|
1288
1936
|
// `tracked` reads the diagnostics a computation added, and deferred, from these lists.
|
|
@@ -1290,17 +1938,27 @@ export class ThreadSharing {
|
|
|
1290
1938
|
c.suggestionDiagnostics.added = [];
|
|
1291
1939
|
c.deferredDiagnosticFiles = [];
|
|
1292
1940
|
this.data = data;
|
|
1941
|
+
this.bounded = data.bounded;
|
|
1942
|
+
this.rule = new CostBoundedRule(this.bounded);
|
|
1293
1943
|
this.used = new Int32Array(data.used);
|
|
1294
1944
|
this.takers = new Int32Array(data.takers);
|
|
1295
1945
|
this.forbidden = new Int32Array(data.forbidden);
|
|
1946
|
+
this.wanted = new Int32Array(data.wanted);
|
|
1947
|
+
this.control = new Int32Array(data.control);
|
|
1948
|
+
this.stats = new Float64Array(data.stats);
|
|
1949
|
+
this.row = data.index * shareStatColumns;
|
|
1296
1950
|
this.types = new ResultTable(data.typeTable);
|
|
1297
1951
|
this.returns = new ResultTable(data.returnTable);
|
|
1298
|
-
//
|
|
1299
|
-
// checkers,
|
|
1300
|
-
|
|
1952
|
+
// The floor, lowered with the rest of the 2-8 ms threshold band when posting is cost-bounded: a posted result pays
|
|
1953
|
+
// when enough checkers need it, so it falls as checkers are added, to 0.25 ms with 16. Below the bounded count the
|
|
1954
|
+
// run keeps the 2-8 ms band, where the posts the extra floor would add are what the c4 gate measured as a cost.
|
|
1955
|
+
this.minMs =
|
|
1956
|
+
shareMinValue === undefined
|
|
1957
|
+
? (this.bounded ? shareFloorTotalMs : shareFloorTotalMsPhase1) / data.count
|
|
1958
|
+
: Number(shareMinValue);
|
|
1301
1959
|
this.initial = initialTypes(c);
|
|
1302
1960
|
for (let i = 0; i < this.initial.length; i++)
|
|
1303
|
-
this.initialIndexes.set(this.initial[i], i);
|
|
1961
|
+
this.initialIndexes.set(this.initial[i].id, i);
|
|
1304
1962
|
for (let k = 0; k < data.count; k++) {
|
|
1305
1963
|
this.numberLogs.push(new Int32Array(data.numberLogs[k]));
|
|
1306
1964
|
this.recordTables.push(new Int32Array(data.recordTables[k]));
|
|
@@ -1310,8 +1968,15 @@ export class ThreadSharing {
|
|
|
1310
1968
|
process.on('exit', () => process.stderr.write(this.summary() + '\n'));
|
|
1311
1969
|
}
|
|
1312
1970
|
summary() {
|
|
1971
|
+
// The stores are written through a local cursor; the shared counters are only for this summary.
|
|
1972
|
+
const writer = this.storeExporter?.writer;
|
|
1973
|
+
const me = this.data.index;
|
|
1974
|
+
if (writer !== undefined) {
|
|
1975
|
+
this.used[usedStride * me] = writer.at;
|
|
1976
|
+
this.used[usedStride * me + 1] = writer.unitAt;
|
|
1977
|
+
}
|
|
1313
1978
|
return ('checker ' +
|
|
1314
|
-
|
|
1979
|
+
me +
|
|
1315
1980
|
': outermost ms owned/computed here/translated/other ' +
|
|
1316
1981
|
this.kindMs.map((v) => v.toFixed(0)).join('/') +
|
|
1317
1982
|
'; posted ' +
|
|
@@ -1322,15 +1987,15 @@ export class ThreadSharing {
|
|
|
1322
1987
|
this.translated +
|
|
1323
1988
|
' (' +
|
|
1324
1989
|
this.translationMs.toFixed(0) +
|
|
1325
|
-
' ms, unpacking ' +
|
|
1326
|
-
this.unpackMs.toFixed(0) +
|
|
1327
1990
|
' ms), export ' +
|
|
1328
1991
|
this.exportMs.toFixed(0) +
|
|
1329
1992
|
' ms, computed here ' +
|
|
1330
1993
|
this.computedPrivately +
|
|
1331
1994
|
' (' +
|
|
1332
1995
|
this.sawDoneUnusable +
|
|
1333
|
-
' posted but not taken
|
|
1996
|
+
' posted but not taken, ' +
|
|
1997
|
+
this.stopped +
|
|
1998
|
+
' after the stop switch), owned ' +
|
|
1334
1999
|
this.owned +
|
|
1335
2000
|
'; not posted: cycles ' +
|
|
1336
2001
|
this.notShareable +
|
|
@@ -1340,7 +2005,15 @@ export class ThreadSharing {
|
|
|
1340
2005
|
this.belowMin +
|
|
1341
2006
|
' (' +
|
|
1342
2007
|
this.belowMinMs.toFixed(0) +
|
|
1343
|
-
' ms), not
|
|
2008
|
+
' ms), not demanded ' +
|
|
2009
|
+
this.notDemanded +
|
|
2010
|
+
', over the export budget ' +
|
|
2011
|
+
this.overBudget +
|
|
2012
|
+
' (' +
|
|
2013
|
+
this.overBudgetMs.toFixed(0) +
|
|
2014
|
+
' ms), takes stopped at their budget ' +
|
|
2015
|
+
this.stoppedTranslations +
|
|
2016
|
+
', not exported ' +
|
|
1344
2017
|
this.exportFailed +
|
|
1345
2018
|
' (' +
|
|
1346
2019
|
this.exportFailedMs.toFixed(0) +
|
|
@@ -1350,16 +2023,21 @@ export class ThreadSharing {
|
|
|
1350
2023
|
.slice(0, 4)
|
|
1351
2024
|
.map(([k, v]) => k + ' ' + v.toFixed(0))
|
|
1352
2025
|
.join('; ') +
|
|
1353
|
-
'),
|
|
1354
|
-
this.
|
|
2026
|
+
'), in an export ' +
|
|
2027
|
+
this.nested +
|
|
2028
|
+
', not stored ' +
|
|
2029
|
+
this.storeFailed +
|
|
1355
2030
|
' (logs full ' +
|
|
1356
2031
|
this.logFull +
|
|
1357
2032
|
', used ' +
|
|
1358
|
-
this.used[
|
|
2033
|
+
this.used[usedStride * me] +
|
|
1359
2034
|
' numbers, ' +
|
|
1360
|
-
this.used[
|
|
2035
|
+
this.used[usedStride * me + 1] +
|
|
1361
2036
|
' code units)' +
|
|
1362
|
-
(shareVerifyValue === '1'
|
|
2037
|
+
(shareVerifyValue === '1'
|
|
2038
|
+
? '; verified ' + this.verified + ' (' + this.verifiedIdentity + ' equal as text)'
|
|
2039
|
+
: '') +
|
|
2040
|
+
(shareVerifyValue === '2' ? '; order violations ' + this.orderViolations : ''));
|
|
1363
2041
|
}
|
|
1364
2042
|
/**
|
|
1365
2043
|
* Whether this checker checks `file`, or may: a result computed for a declaration in it is never taken here. That is
|
|
@@ -1368,17 +2046,60 @@ export class ThreadSharing {
|
|
|
1368
2046
|
*/
|
|
1369
2047
|
owns(file) {
|
|
1370
2048
|
const index = this.c.fileIndex.get(file);
|
|
1371
|
-
|
|
2049
|
+
return index === undefined || this.ownsIndex(index);
|
|
2050
|
+
}
|
|
2051
|
+
/** Whether this checker checks file `index`, or may; -1 names a diagnostic without a file, which it has. */
|
|
2052
|
+
ownsIndex(index) {
|
|
2053
|
+
if (index < 0)
|
|
1372
2054
|
return true;
|
|
1373
2055
|
const taker = Atomics.load(this.takers, index);
|
|
1374
2056
|
if (taker === 0)
|
|
1375
2057
|
return false;
|
|
1376
2058
|
return taker !== -2 && (taker < 0 || taker === this.data.index + 1);
|
|
1377
2059
|
}
|
|
2060
|
+
/**
|
|
2061
|
+
* Notes that this checker looked for a result for a declaration in `file` and found none: a checker posts results
|
|
2062
|
+
* for that file only then (`demanded`). One `Atomics.or` per file and checker.
|
|
2063
|
+
*/
|
|
2064
|
+
want(file) {
|
|
2065
|
+
if (!this.bounded)
|
|
2066
|
+
return;
|
|
2067
|
+
const index = this.c.fileIndex.get(file);
|
|
2068
|
+
if (index === undefined || this.wantedFiles.has(index))
|
|
2069
|
+
return;
|
|
2070
|
+
this.wantedFiles.add(index);
|
|
2071
|
+
const me = this.data.index;
|
|
2072
|
+
Atomics.or(this.wanted, index * forbiddenWords(this.data.count) + (me >>> 5), 1 << (me & 31));
|
|
2073
|
+
}
|
|
2074
|
+
/**
|
|
2075
|
+
* Whether a checker other than this one and the file's taker missed on a key in file `index`: someone other than
|
|
2076
|
+
* the checkers that already compute its declarations would take a result for one of them.
|
|
2077
|
+
*/
|
|
2078
|
+
demanded(index) {
|
|
2079
|
+
const count = this.data.count;
|
|
2080
|
+
const me = this.data.index;
|
|
2081
|
+
const words = forbiddenWords(count);
|
|
2082
|
+
const taker = Atomics.load(this.takers, index);
|
|
2083
|
+
const base = index * words;
|
|
2084
|
+
for (let w = 0; w < words; w++) {
|
|
2085
|
+
let bits = Atomics.load(this.wanted, base + w);
|
|
2086
|
+
if (w === me >>> 5)
|
|
2087
|
+
bits &= ~(1 << (me & 31));
|
|
2088
|
+
if (taker > 0 && w === (taker - 1) >>> 5)
|
|
2089
|
+
bits &= ~(1 << ((taker - 1) & 31));
|
|
2090
|
+
if (bits !== 0)
|
|
2091
|
+
return true;
|
|
2092
|
+
}
|
|
2093
|
+
return false;
|
|
2094
|
+
}
|
|
1378
2095
|
/** After taking a result for a declaration in `file`: if no checker took the file yet, this one never will. */
|
|
1379
2096
|
forbid(file) {
|
|
1380
2097
|
const index = this.c.fileIndex.get(file);
|
|
1381
|
-
if (index
|
|
2098
|
+
if (index !== undefined)
|
|
2099
|
+
this.forbidIndex(index);
|
|
2100
|
+
}
|
|
2101
|
+
forbidIndex(index) {
|
|
2102
|
+
if (index < 0)
|
|
1382
2103
|
return;
|
|
1383
2104
|
const me = this.data.index;
|
|
1384
2105
|
if (Atomics.load(this.takers, index) === 0)
|
|
@@ -1404,40 +2125,16 @@ export class ThreadSharing {
|
|
|
1404
2125
|
}
|
|
1405
2126
|
return true;
|
|
1406
2127
|
}
|
|
1407
|
-
|
|
1408
|
-
this.ownsFileNamed(fileName);
|
|
1409
|
-
const file = this.filesByName.get(fileName);
|
|
1410
|
-
const index = file === undefined ? undefined : this.c.fileIndex.get(file);
|
|
1411
|
-
return index !== undefined && this.forbidsLast(index);
|
|
1412
|
-
}
|
|
1413
|
-
forbidNamed(fileName) {
|
|
1414
|
-
this.ownsFileNamed(fileName);
|
|
1415
|
-
const file = this.filesByName.get(fileName);
|
|
1416
|
-
if (file !== undefined)
|
|
1417
|
-
this.forbid(file);
|
|
1418
|
-
}
|
|
1419
|
-
filesByName = undefined;
|
|
1420
|
-
ownsFileNamed(fileName) {
|
|
1421
|
-
let files = this.filesByName;
|
|
1422
|
-
if (files === undefined) {
|
|
1423
|
-
files = new Map();
|
|
1424
|
-
for (const file of this.c.files)
|
|
1425
|
-
files.set(file.fileName, file);
|
|
1426
|
-
this.filesByName = files;
|
|
1427
|
-
}
|
|
1428
|
-
const file = files.get(fileName);
|
|
1429
|
-
return file === undefined || this.owns(file);
|
|
1430
|
-
}
|
|
1431
|
-
/** The result checker `k` posted at `offset` in its number log. */
|
|
2128
|
+
/** The result checker `k` posted at `offset` in its number log: the record of its type, and the files it named. */
|
|
1432
2129
|
result(k, offset) {
|
|
1433
2130
|
const numbers = this.numberLogs[k];
|
|
1434
|
-
const strings = this.stringLogs[k];
|
|
1435
2131
|
const root = numbers[offset];
|
|
1436
|
-
const
|
|
2132
|
+
const computeMs = numbers[offset + 1] / 1000;
|
|
2133
|
+
const count = numbers[offset + 2];
|
|
1437
2134
|
const files = [];
|
|
1438
2135
|
for (let i = 0; i < count; i++)
|
|
1439
|
-
files.push(
|
|
1440
|
-
return new StoredResult(root, files);
|
|
2136
|
+
files.push(numbers[offset + 3 + i]);
|
|
2137
|
+
return new StoredResult(root, files, computeMs);
|
|
1441
2138
|
}
|
|
1442
2139
|
/** The exporter of this checker's store: it exports each type once. */
|
|
1443
2140
|
storeExporter = undefined;
|
|
@@ -1446,71 +2143,71 @@ export class ThreadSharing {
|
|
|
1446
2143
|
* tables, -1 if it doesn't fit in the logs or a node or symbol in it can't be named across threads, or why it can't
|
|
1447
2144
|
* be exported.
|
|
1448
2145
|
*/
|
|
1449
|
-
post(t, diagnosticFiles) {
|
|
2146
|
+
post(t, diagnosticFiles, computeMs) {
|
|
1450
2147
|
const c = this.c;
|
|
2148
|
+
const me = this.data.index;
|
|
1451
2149
|
let exporter = this.storeExporter;
|
|
1452
2150
|
if (exporter === undefined) {
|
|
1453
|
-
|
|
2151
|
+
const writer = new StoreWriter(this.numberLogs[me], this.stringLogs[me], this.recordTables[me], c.fileIndex);
|
|
2152
|
+
exporter = new Exporter(c, this.initialIndexes, writer);
|
|
1454
2153
|
exporter.nodeLimit = this.data.nodeIds;
|
|
1455
2154
|
exporter.symbolLimit = this.data.symbolIds;
|
|
1456
2155
|
exporter.originsCache = this.origins;
|
|
1457
2156
|
this.storeExporter = exporter;
|
|
1458
2157
|
}
|
|
2158
|
+
// The export budget: a post appends at most `(kappa*T - p0)/e` records, so it costs at most `kappa*T` even when
|
|
2159
|
+
// the result is over it, and an over-budget result is not posted.
|
|
2160
|
+
if (this.bounded) {
|
|
2161
|
+
const maxRecords = Math.floor((shareKappa() * computeMs * 1000 - sharePostUs()) / shareExportRecordUs());
|
|
2162
|
+
if (maxRecords < 0)
|
|
2163
|
+
return budgetFailure;
|
|
2164
|
+
exporter.limit = exporter.base + maxRecords;
|
|
2165
|
+
}
|
|
2166
|
+
else
|
|
2167
|
+
exporter.limit = Number.POSITIVE_INFINITY;
|
|
2168
|
+
const writer = exporter.writer;
|
|
1459
2169
|
exporter.failure = undefined;
|
|
1460
2170
|
exporter.mergedOrigins = undefined;
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
2171
|
+
exporter.base = this.storedRecords;
|
|
2172
|
+
exporter.count = 0;
|
|
2173
|
+
const at = writer.at;
|
|
2174
|
+
const unitAt = writer.unitAt;
|
|
2175
|
+
this.exporting = true;
|
|
2176
|
+
try {
|
|
2177
|
+
const root = exporter.type(t);
|
|
2178
|
+
if (exporter.failure !== undefined) {
|
|
2179
|
+
const failure = exporter.failure;
|
|
2180
|
+
forgetExport(exporter, at, unitAt);
|
|
2181
|
+
return failure;
|
|
2182
|
+
}
|
|
2183
|
+
// The result's own words, at the cursor after the records it added: its root record, how long the result
|
|
2184
|
+
// took to compute (microseconds: a checker that translates it saved that much), and the files its
|
|
2185
|
+
// diagnostics are in.
|
|
2186
|
+
const header = writer.at;
|
|
2187
|
+
writer.word(root);
|
|
2188
|
+
writer.word(Math.round(computeMs * 1000));
|
|
2189
|
+
writer.word(diagnosticFiles.length);
|
|
2190
|
+
for (const index of diagnosticFiles)
|
|
2191
|
+
writer.word(index);
|
|
2192
|
+
if (writer.full) {
|
|
2193
|
+
this.logFull++;
|
|
2194
|
+
forgetExport(exporter, at, unitAt);
|
|
1478
2195
|
return -1;
|
|
1479
2196
|
}
|
|
2197
|
+
const count = exporter.count;
|
|
2198
|
+
exporter.base += count;
|
|
2199
|
+
exporter.count = 0;
|
|
2200
|
+
exporter.addedTypes.length = 0;
|
|
2201
|
+
exporter.addedProperties.length = 0;
|
|
2202
|
+
this.posted++;
|
|
2203
|
+
this.storedRecords += count;
|
|
2204
|
+
this.writeStats();
|
|
2205
|
+
// Only this checker writes its logs and store; the result table's state publishes what it wrote.
|
|
2206
|
+
return postedState(this.data.count, me, header);
|
|
1480
2207
|
}
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
for (const fileName of diagnosticFiles)
|
|
1484
|
-
p.text(fileName);
|
|
1485
|
-
const count = exporter.records.length;
|
|
1486
|
-
let fits = !p.full && offset + p.f.length <= numbers.length && exporter.base + count <= recordTableSize;
|
|
1487
|
-
for (const v of p.f)
|
|
1488
|
-
if ((v | 0) !== v)
|
|
1489
|
-
fits = false;
|
|
1490
|
-
if (!fits) {
|
|
1491
|
-
this.logFull++;
|
|
1492
|
-
forgetExport(exporter);
|
|
1493
|
-
return -1;
|
|
2208
|
+
finally {
|
|
2209
|
+
this.exporting = false;
|
|
1494
2210
|
}
|
|
1495
|
-
for (let i = 0; i < p.f.length; i++)
|
|
1496
|
-
numbers[offset + i] = p.f[i];
|
|
1497
|
-
for (let i = 0; i < count; i++) {
|
|
1498
|
-
const index = exporter.base + i;
|
|
1499
|
-
table[2 * index] = offset + starts[i] + 1;
|
|
1500
|
-
const r = exporter.records[i];
|
|
1501
|
-
if (r.kind === 23 /* RecordKind.CompleteObject */)
|
|
1502
|
-
table[2 * r.num + 1] = index + 1;
|
|
1503
|
-
}
|
|
1504
|
-
// Only this checker writes its logs and store; the result table's state publishes what it wrote.
|
|
1505
|
-
this.used[2 * me] = offset + p.f.length;
|
|
1506
|
-
this.used[2 * me + 1] = p.unitAt;
|
|
1507
|
-
exporter.base += count;
|
|
1508
|
-
exporter.records = [];
|
|
1509
|
-
exporter.addedTypes.length = 0;
|
|
1510
|
-
exporter.addedProperties.length = 0;
|
|
1511
|
-
this.posted++;
|
|
1512
|
-
this.storedRecords += count;
|
|
1513
|
-
return postedState(this.data.count, me, offset + header);
|
|
1514
2211
|
}
|
|
1515
2212
|
/** The records this checker stored. */
|
|
1516
2213
|
storedRecords = 0;
|
|
@@ -1518,8 +2215,8 @@ export class ThreadSharing {
|
|
|
1518
2215
|
readers = [];
|
|
1519
2216
|
/** The translation of `posted` into this checker, or undefined if it can't be taken here. */
|
|
1520
2217
|
translatePosted(producer, posted, files, key, declarationFile) {
|
|
1521
|
-
for (const
|
|
1522
|
-
if (
|
|
2218
|
+
for (const index of posted.files)
|
|
2219
|
+
if (this.ownsIndex(index) || this.forbidsLast(index))
|
|
1523
2220
|
return undefined;
|
|
1524
2221
|
const declarationIndex = this.c.fileIndex.get(declarationFile);
|
|
1525
2222
|
if (declarationIndex !== undefined && this.forbidsLast(declarationIndex))
|
|
@@ -1530,17 +2227,25 @@ export class ThreadSharing {
|
|
|
1530
2227
|
reader = new StoreReader(this, producer);
|
|
1531
2228
|
this.readers[producer] = reader;
|
|
1532
2229
|
}
|
|
1533
|
-
|
|
2230
|
+
// The translation budget: building more records than the rule allows costs more than computing the result again,
|
|
2231
|
+
// where `T` is the time the producer's computation took (`StoredResult.computeMs`). The walk stops at the bound
|
|
2232
|
+
// before anything is built.
|
|
2233
|
+
const t = reader.translate(posted.root, this.rule.buildBudget(posted.computeMs));
|
|
2234
|
+
if (t === undefined && reader.stopped)
|
|
2235
|
+
this.stoppedTranslations++;
|
|
1534
2236
|
this.translationMs += performance.now() - start;
|
|
1535
2237
|
if (t !== undefined) {
|
|
1536
2238
|
this.translated++;
|
|
1537
|
-
|
|
1538
|
-
|
|
2239
|
+
// Translating the result saved the time its producer's computation took (`post`'s header).
|
|
2240
|
+
this.addSaved(posted.computeMs);
|
|
2241
|
+
for (const index of posted.files)
|
|
2242
|
+
this.forbidIndex(index);
|
|
1539
2243
|
if (posted.files.length > 0) {
|
|
1540
2244
|
files.set(key, posted.files);
|
|
1541
2245
|
this.noteFiles(posted.files);
|
|
1542
2246
|
}
|
|
1543
2247
|
}
|
|
2248
|
+
this.writeStats();
|
|
1544
2249
|
return t;
|
|
1545
2250
|
}
|
|
1546
2251
|
}
|
|
@@ -1548,9 +2253,12 @@ export class ThreadSharing {
|
|
|
1548
2253
|
export class StoredResult {
|
|
1549
2254
|
root;
|
|
1550
2255
|
files;
|
|
1551
|
-
|
|
2256
|
+
/** How long computing the result took its producer, in milliseconds. */
|
|
2257
|
+
computeMs;
|
|
2258
|
+
constructor(root, files, computeMs = 0) {
|
|
1552
2259
|
this.root = root;
|
|
1553
2260
|
this.files = files;
|
|
2261
|
+
this.computeMs = computeMs;
|
|
1554
2262
|
}
|
|
1555
2263
|
}
|
|
1556
2264
|
function readText(strings, start, length) {
|
|
@@ -1559,23 +2267,25 @@ function readText(strings, start, length) {
|
|
|
1559
2267
|
text += String.fromCharCode(strings[start + j]);
|
|
1560
2268
|
return text;
|
|
1561
2269
|
}
|
|
1562
|
-
/** Forgets the types and properties a failed export of a store gave indexes. */
|
|
1563
|
-
function forgetExport(exporter) {
|
|
2270
|
+
/** Forgets the types and properties a failed export of a store gave indexes, and its words. */
|
|
2271
|
+
function forgetExport(exporter, at, unitAt) {
|
|
1564
2272
|
for (const t of exporter.addedTypes)
|
|
1565
2273
|
exporter.indexes.delete(t);
|
|
1566
2274
|
for (const prop of exporter.addedProperties)
|
|
1567
2275
|
exporter.properties.delete(prop);
|
|
1568
2276
|
exporter.addedTypes.length = 0;
|
|
1569
2277
|
exporter.addedProperties.length = 0;
|
|
1570
|
-
exporter.
|
|
2278
|
+
exporter.count = 0;
|
|
1571
2279
|
exporter.failure = undefined;
|
|
2280
|
+
exporter.writer.at = at;
|
|
2281
|
+
exporter.writer.unitAt = unitAt;
|
|
1572
2282
|
}
|
|
1573
2283
|
/**
|
|
1574
2284
|
* Runs `compute` for the result keyed `key` in `table`, then exports and posts it if it can be shared and took long
|
|
1575
2285
|
* enough, unless another checker posted it meanwhile. Nothing is written for the others before: a checker that finds
|
|
1576
2286
|
* no result computes its own.
|
|
1577
2287
|
*/
|
|
1578
|
-
function computeAndPost(sharing, table, files, key, symbol, sig) {
|
|
2288
|
+
function computeAndPost(sharing, table, files, key, symbol, sig, declarationFile) {
|
|
1579
2289
|
const t = sharing.computeShared(files, key, symbol, sig);
|
|
1580
2290
|
const ms = sharing.lastMs;
|
|
1581
2291
|
if (!sharing.lastShareable) {
|
|
@@ -1583,10 +2293,48 @@ function computeAndPost(sharing, table, files, key, symbol, sig) {
|
|
|
1583
2293
|
sharing.notShareableMs += ms;
|
|
1584
2294
|
return t;
|
|
1585
2295
|
}
|
|
1586
|
-
|
|
1587
|
-
if (
|
|
1588
|
-
sharing
|
|
1589
|
-
|
|
2296
|
+
let diagnosticFiles;
|
|
2297
|
+
if (sharing.bounded) {
|
|
2298
|
+
// The main thread stopped sharing at its window (control 1 or 2): this result is the checker's own,
|
|
2299
|
+
// and the others compute theirs, as they do when they find none posted.
|
|
2300
|
+
if (Atomics.load(sharing.control, 0) !== 0) {
|
|
2301
|
+
sharing.stopped++;
|
|
2302
|
+
return t;
|
|
2303
|
+
}
|
|
2304
|
+
const index = sharing.c.fileIndex.get(declarationFile);
|
|
2305
|
+
if (index === undefined)
|
|
2306
|
+
return t;
|
|
2307
|
+
// The rule priced this result: a quick computation is cheaper to repeat than to export, post and translate.
|
|
2308
|
+
diagnosticFiles = files.get(key) ?? [];
|
|
2309
|
+
if (!sharing.rule.posts(sharing, key, ms, diagnosticFiles, index)) {
|
|
2310
|
+
sharing.belowMin++;
|
|
2311
|
+
sharing.belowMinMs += ms;
|
|
2312
|
+
return t;
|
|
2313
|
+
}
|
|
2314
|
+
// A result is posted only for a declaration whose file another checker that may take it missed on: a file only
|
|
2315
|
+
// its own checkers read has nobody to take it.
|
|
2316
|
+
if (!sharing.demanded(index)) {
|
|
2317
|
+
sharing.notDemanded++;
|
|
2318
|
+
return t;
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
else {
|
|
2322
|
+
// Below the bounded count the run is Phase 1's, and takes the same path: the floor first, then the stop switch,
|
|
2323
|
+
// and none of the bounded path's lookups (the file index and the diagnostics) is made.
|
|
2324
|
+
if (ms < sharing.minMs) {
|
|
2325
|
+
sharing.belowMin++;
|
|
2326
|
+
sharing.belowMinMs += ms;
|
|
2327
|
+
return t;
|
|
2328
|
+
}
|
|
2329
|
+
if (Atomics.load(sharing.control, 0) !== 0) {
|
|
2330
|
+
sharing.stopped++;
|
|
2331
|
+
return t;
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
// A result computed inside this checker's export walk doesn't have the store to itself: the walk would write over
|
|
2335
|
+
// its records, so it stays with the checkers that compute it.
|
|
2336
|
+
if (sharing.exporting) {
|
|
2337
|
+
sharing.nested++;
|
|
1590
2338
|
return t;
|
|
1591
2339
|
}
|
|
1592
2340
|
const slot = table.slot(key);
|
|
@@ -1594,17 +2342,22 @@ function computeAndPost(sharing, table, files, key, symbol, sig) {
|
|
|
1594
2342
|
return t;
|
|
1595
2343
|
const start = performance.now();
|
|
1596
2344
|
// Diagnostics stay with this checker; the others only learn which files they are in.
|
|
1597
|
-
const state = sharing.post(t, files.get(key) ?? []);
|
|
1598
|
-
if (
|
|
2345
|
+
const state = sharing.post(t, diagnosticFiles ?? files.get(key) ?? [], ms);
|
|
2346
|
+
if (state === budgetFailure) {
|
|
2347
|
+
sharing.overBudget++;
|
|
2348
|
+
sharing.overBudgetMs += ms;
|
|
2349
|
+
}
|
|
2350
|
+
else if (typeof state === 'string') {
|
|
1599
2351
|
sharing.exportFailed++;
|
|
1600
2352
|
sharing.exportFailedMs += ms;
|
|
1601
2353
|
sharing.exportFailures.set(state, (sharing.exportFailures.get(state) ?? 0) + ms);
|
|
1602
2354
|
}
|
|
1603
2355
|
else if (state === -1)
|
|
1604
|
-
sharing.
|
|
2356
|
+
sharing.storeFailed++;
|
|
1605
2357
|
else
|
|
1606
2358
|
table.publish(slot, state);
|
|
1607
2359
|
sharing.exportMs += performance.now() - start;
|
|
2360
|
+
sharing.writeStats();
|
|
1608
2361
|
return t;
|
|
1609
2362
|
}
|
|
1610
2363
|
/** Shares the value type of `symbol` with the other checker threads, computing it with `compute` where needed. */
|
|
@@ -1635,27 +2388,34 @@ function threadTypeOfSymbolWorker(c, symbol) {
|
|
|
1635
2388
|
}
|
|
1636
2389
|
if (id > sharing.data.symbolIds)
|
|
1637
2390
|
return sharing.tracked(sharing.typeFiles, id, symbol, undefined);
|
|
2391
|
+
const declaration = symbol.valueDeclaration;
|
|
2392
|
+
const file = getSourceFileOfNode(declaration);
|
|
2393
|
+
// A declaration with a type annotation: computing its type resolves the annotation, and translating the records
|
|
2394
|
+
// does too (`NodeType` resolves the node), so sharing saves nothing on it and costs a lookup. Below the bounded
|
|
2395
|
+
// count the exclusion is off, as it is in Phase 1.
|
|
2396
|
+
if (sharing.bounded && getType(declaration) !== undefined)
|
|
2397
|
+
return sharing.tracked(sharing.typeFiles, id, symbol, undefined);
|
|
1638
2398
|
const table = sharing.types;
|
|
1639
|
-
if (!sharing.owns(
|
|
1640
|
-
|
|
2399
|
+
if (!sharing.owns(file)) {
|
|
2400
|
+
// The stop switch is read before the table: a checker that stops translating computes the result itself.
|
|
2401
|
+
const slot = sharing.stoppedTranslating() ? -1 : table.find(id);
|
|
1641
2402
|
const state = slot < 0 ? 0 : table.state(slot);
|
|
1642
2403
|
if (state <= -2 &&
|
|
1643
2404
|
findResolutionCycleStartIndex(c, symbol, 0 /* TypeSystemPropertyName.Type */) < 0 &&
|
|
1644
|
-
!translationBlocked(
|
|
2405
|
+
!translationBlocked(declaration)) {
|
|
1645
2406
|
const producer = postedProducer(sharing.data.count, state);
|
|
1646
2407
|
const posted = sharing.result(producer, postedOffset(sharing.data.count, state));
|
|
1647
|
-
const file = getSourceFileOfNode(symbol.valueDeclaration);
|
|
1648
2408
|
if (shareVerifyValue === '1') {
|
|
1649
2409
|
const own = sharing.tracked(sharing.typeFiles, id, symbol, undefined);
|
|
1650
|
-
verifyTranslation(c, 'types',
|
|
2410
|
+
verifyTranslation(c, 'types', declaration, own, sharing.translatePosted(producer, posted, sharing.typeFiles, id, file));
|
|
1651
2411
|
return own;
|
|
1652
2412
|
}
|
|
1653
2413
|
const t = sharing.translatePosted(producer, posted, sharing.typeFiles, id, file);
|
|
1654
2414
|
if (t !== undefined) {
|
|
1655
|
-
traceTranslation(c, 'types',
|
|
2415
|
+
traceTranslation(c, 'types', declaration, producer);
|
|
1656
2416
|
if (sharing.depth === 1)
|
|
1657
2417
|
sharing.kind = 2;
|
|
1658
|
-
sharing.forbid(
|
|
2418
|
+
sharing.forbid(file);
|
|
1659
2419
|
const again = c.symbolTypes.get(id);
|
|
1660
2420
|
if (again !== undefined)
|
|
1661
2421
|
return again;
|
|
@@ -1664,6 +2424,9 @@ function threadTypeOfSymbolWorker(c, symbol) {
|
|
|
1664
2424
|
}
|
|
1665
2425
|
sharing.sawDoneUnusable++;
|
|
1666
2426
|
}
|
|
2427
|
+
// This checker needed a result for one of the file's declarations and has none: the others post its results only
|
|
2428
|
+
// then.
|
|
2429
|
+
sharing.want(file);
|
|
1667
2430
|
sharing.computedPrivately++;
|
|
1668
2431
|
if (sharing.depth === 1)
|
|
1669
2432
|
sharing.kind = 1;
|
|
@@ -1673,7 +2436,7 @@ function threadTypeOfSymbolWorker(c, symbol) {
|
|
|
1673
2436
|
if (sharing.depth === 1)
|
|
1674
2437
|
sharing.kind = 0;
|
|
1675
2438
|
}
|
|
1676
|
-
return computeAndPost(sharing, table, sharing.typeFiles, id, symbol, undefined);
|
|
2439
|
+
return computeAndPost(sharing, table, sharing.typeFiles, id, symbol, undefined, file);
|
|
1677
2440
|
}
|
|
1678
2441
|
/** Notes that the computation in progress used the cached return type of `sig` (see `ThreadSharing.typeFiles`). */
|
|
1679
2442
|
export function noteReturnTypeUse(c, sig) {
|
|
@@ -1703,16 +2466,21 @@ function threadReturnTypeWorker(c, sig) {
|
|
|
1703
2466
|
const key = declaration.id;
|
|
1704
2467
|
if (key > sharing.data.nodeIds)
|
|
1705
2468
|
return sharing.tracked(sharing.returnFiles, key, undefined, sig);
|
|
2469
|
+
const file = getSourceFileOfNode(declaration);
|
|
2470
|
+
// A declaration with a return type annotation: computing the return type resolves the annotation, and translating
|
|
2471
|
+
// the records does too, so sharing saves nothing on it and costs a lookup. Below the bounded count the exclusion is
|
|
2472
|
+
// off, as it is in Phase 1.
|
|
2473
|
+
if (sharing.bounded && getType(declaration) !== undefined)
|
|
2474
|
+
return sharing.tracked(sharing.returnFiles, key, undefined, sig);
|
|
1706
2475
|
const table = sharing.returns;
|
|
1707
|
-
if (!sharing.owns(
|
|
1708
|
-
const slot = table.find(key);
|
|
2476
|
+
if (!sharing.owns(file)) {
|
|
2477
|
+
const slot = sharing.stoppedTranslating() ? -1 : table.find(key);
|
|
1709
2478
|
const state = slot < 0 ? 0 : table.state(slot);
|
|
1710
2479
|
if (state <= -2 &&
|
|
1711
2480
|
findResolutionCycleStartIndex(c, sig, 3 /* TypeSystemPropertyName.ResolvedReturnType */) < 0 &&
|
|
1712
2481
|
!translationBlocked(declaration)) {
|
|
1713
2482
|
const producer = postedProducer(sharing.data.count, state);
|
|
1714
2483
|
const posted = sharing.result(producer, postedOffset(sharing.data.count, state));
|
|
1715
|
-
const file = getSourceFileOfNode(declaration);
|
|
1716
2484
|
if (shareVerifyValue === '1') {
|
|
1717
2485
|
const own = sharing.tracked(sharing.returnFiles, key, undefined, sig);
|
|
1718
2486
|
verifyTranslation(c, 'returns', declaration, own, sharing.translatePosted(producer, posted, sharing.returnFiles, key, file));
|
|
@@ -1723,11 +2491,12 @@ function threadReturnTypeWorker(c, sig) {
|
|
|
1723
2491
|
traceTranslation(c, 'returns', declaration, producer);
|
|
1724
2492
|
if (sharing.depth === 1)
|
|
1725
2493
|
sharing.kind = 2;
|
|
1726
|
-
sharing.forbid(
|
|
2494
|
+
sharing.forbid(file);
|
|
1727
2495
|
return t;
|
|
1728
2496
|
}
|
|
1729
2497
|
sharing.sawDoneUnusable++;
|
|
1730
2498
|
}
|
|
2499
|
+
sharing.want(file);
|
|
1731
2500
|
sharing.computedPrivately++;
|
|
1732
2501
|
if (sharing.depth === 1)
|
|
1733
2502
|
sharing.kind = 1;
|
|
@@ -1737,7 +2506,7 @@ function threadReturnTypeWorker(c, sig) {
|
|
|
1737
2506
|
if (sharing.depth === 1)
|
|
1738
2507
|
sharing.kind = 0;
|
|
1739
2508
|
}
|
|
1740
|
-
return computeAndPost(sharing, table, sharing.returnFiles, key, undefined, sig);
|
|
2509
|
+
return computeAndPost(sharing, table, sharing.returnFiles, key, undefined, sig, file);
|
|
1741
2510
|
}
|
|
1742
2511
|
// ---- Records in a checker's store
|
|
1743
2512
|
//
|
|
@@ -1759,333 +2528,305 @@ function doubleWords() {
|
|
|
1759
2528
|
doubleWordsValue = new DoubleWords();
|
|
1760
2529
|
return doubleWordsValue;
|
|
1761
2530
|
}
|
|
1762
|
-
/**
|
|
1763
|
-
class
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
/** Set when the string log is full. */
|
|
1769
|
-
full = false;
|
|
1770
|
-
constructor(fileIndex, strings, unitAt) {
|
|
1771
|
-
this.fileIndex = fileIndex;
|
|
1772
|
-
this.strings = strings;
|
|
1773
|
-
this.unitAt = unitAt;
|
|
2531
|
+
/** Collects the children of a node (`forEachChild`). */
|
|
2532
|
+
class ChildVisitor extends NodeVisitor {
|
|
2533
|
+
children = [];
|
|
2534
|
+
visitNode(node) {
|
|
2535
|
+
this.children.push(node);
|
|
2536
|
+
return false;
|
|
1774
2537
|
}
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
2538
|
+
}
|
|
2539
|
+
/** The nodes of the program records name, by id, found by descending from their file through the nodes that contain them. */
|
|
2540
|
+
export class NodeFinder {
|
|
2541
|
+
files;
|
|
2542
|
+
nodesById;
|
|
2543
|
+
visitor = undefined;
|
|
2544
|
+
constructor(files, nodesById) {
|
|
2545
|
+
this.files = files;
|
|
2546
|
+
this.nodesById = nodesById;
|
|
1784
2547
|
}
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
2548
|
+
/** The node of file `fileIndex` with the range `pos`-`end` and id `id`; undefined if the program hasn't it. */
|
|
2549
|
+
find(fileIndex, pos, end, id) {
|
|
2550
|
+
const known = this.nodesById.get(id);
|
|
2551
|
+
if (known !== undefined)
|
|
2552
|
+
return known;
|
|
2553
|
+
const file = this.files[fileIndex];
|
|
2554
|
+
if (file === undefined)
|
|
2555
|
+
return undefined;
|
|
2556
|
+
let visitor = this.visitor;
|
|
2557
|
+
if (visitor === undefined)
|
|
2558
|
+
visitor = this.visitor = new ChildVisitor();
|
|
2559
|
+
let node = file;
|
|
2560
|
+
for (;;) {
|
|
2561
|
+
if (node.id === id) {
|
|
2562
|
+
if (node.pos !== pos || node.end !== end)
|
|
2563
|
+
return undefined;
|
|
2564
|
+
this.nodesById.set(id, node);
|
|
2565
|
+
return node;
|
|
2566
|
+
}
|
|
2567
|
+
visitor.children.length = 0;
|
|
2568
|
+
node.forEachChild(visitor);
|
|
2569
|
+
let next = undefined;
|
|
2570
|
+
for (const child of visitor.children)
|
|
2571
|
+
if (child.pos <= pos && end <= child.end) {
|
|
2572
|
+
// The innermost of nested nodes with one range is found by its id further down.
|
|
2573
|
+
if (child.id === id) {
|
|
2574
|
+
if (child.pos !== pos || child.end !== end)
|
|
2575
|
+
return undefined;
|
|
2576
|
+
this.nodesById.set(id, child);
|
|
2577
|
+
return child;
|
|
2578
|
+
}
|
|
2579
|
+
if (next === undefined)
|
|
2580
|
+
next = child;
|
|
2581
|
+
}
|
|
2582
|
+
if (next === undefined)
|
|
2583
|
+
return undefined;
|
|
2584
|
+
node = next;
|
|
2585
|
+
}
|
|
1789
2586
|
}
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
2587
|
+
}
|
|
2588
|
+
/**
|
|
2589
|
+
* Reads the records of a checker's store where it wrote them. `begin` starts a record at its offset in the number log
|
|
2590
|
+
* and finds its parts without decoding its kind's fields: its refs, its alias, its symbol, its node, then the fields
|
|
2591
|
+
* at `fields`.
|
|
2592
|
+
*/
|
|
2593
|
+
export class RecordReader {
|
|
2594
|
+
numbers;
|
|
2595
|
+
strings;
|
|
2596
|
+
table;
|
|
2597
|
+
finder;
|
|
2598
|
+
/** The record in progress: its header and kind, and where its refs are. */
|
|
2599
|
+
header = 0;
|
|
2600
|
+
kind = 0;
|
|
2601
|
+
refAt = 0;
|
|
2602
|
+
refCount = 0;
|
|
2603
|
+
/** Where its alias refs are, if it has an alias; -1 if none. */
|
|
2604
|
+
aliasRefAt = -1;
|
|
2605
|
+
aliasRefCount = 0;
|
|
2606
|
+
/** How its alias is named: `SymbolNaming` and a merged symbol id, or a node at `aliasNodeStart`. */
|
|
2607
|
+
aliasNaming = -1;
|
|
2608
|
+
aliasMergedId = -1;
|
|
2609
|
+
aliasNodeStart = -1;
|
|
2610
|
+
/** How its symbol is named: the kind of naming, a merged symbol id, or a node at `symbolNodeStart`. */
|
|
2611
|
+
symbolNaming = 0;
|
|
2612
|
+
symbolMergedId = -1;
|
|
2613
|
+
symbolNodeStart = -1;
|
|
2614
|
+
/** Where its node is, if it has one; -1 if none. */
|
|
2615
|
+
nodeStart = -1;
|
|
2616
|
+
/** The offset of its kind's own fields. */
|
|
2617
|
+
fields = 0;
|
|
2618
|
+
/** The records a record names, filled by `scan`. */
|
|
2619
|
+
deps = [];
|
|
2620
|
+
constructor(numbers, strings, table, files, nodesById) {
|
|
2621
|
+
this.numbers = numbers;
|
|
2622
|
+
this.strings = strings;
|
|
2623
|
+
this.table = table;
|
|
2624
|
+
this.finder = new NodeFinder(files, nodesById);
|
|
1795
2625
|
}
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
return true;
|
|
1800
|
-
}
|
|
1801
|
-
const file = getSourceFileOfNode(node);
|
|
1802
|
-
const index = this.fileIndex.get(file);
|
|
1803
|
-
if (index === undefined)
|
|
2626
|
+
/** Starts the record at `index`; false if the store hasn't it. */
|
|
2627
|
+
begin(index) {
|
|
2628
|
+
if (index < 0 || index >= this.table.length)
|
|
1804
2629
|
return false;
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
2630
|
+
const start = this.table[index];
|
|
2631
|
+
if (start <= 0)
|
|
2632
|
+
return false;
|
|
2633
|
+
const numbers = this.numbers;
|
|
2634
|
+
const at = start - 1;
|
|
2635
|
+
const header = numbers[at];
|
|
2636
|
+
this.header = header;
|
|
2637
|
+
this.kind = header & 31 /* RecordHeader.KindMask */;
|
|
2638
|
+
let count = (header >>> 16) & RefCountMask;
|
|
2639
|
+
this.refAt = at + 1;
|
|
2640
|
+
if (count === 65535 /* RecordHeader.RefEscape */) {
|
|
2641
|
+
count = numbers[this.refAt];
|
|
2642
|
+
this.refAt++;
|
|
2643
|
+
}
|
|
2644
|
+
this.refCount = count;
|
|
2645
|
+
this.locate();
|
|
1813
2646
|
return true;
|
|
1814
2647
|
}
|
|
1815
|
-
/**
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
2648
|
+
/** Finds the record's alias refs, its symbol and its node, and the fields that follow them. */
|
|
2649
|
+
locate() {
|
|
2650
|
+
const numbers = this.numbers;
|
|
2651
|
+
const header = this.header;
|
|
2652
|
+
let p = this.refAt + this.refCount;
|
|
2653
|
+
this.aliasNaming = -1;
|
|
2654
|
+
this.aliasMergedId = -1;
|
|
2655
|
+
this.aliasNodeStart = -1;
|
|
2656
|
+
this.aliasRefAt = -1;
|
|
2657
|
+
this.aliasRefCount = 0;
|
|
2658
|
+
if ((header & 32 /* RecordHeader.HasAlias */) !== 0) {
|
|
2659
|
+
if ((header & 1024 /* RecordHeader.AliasMerged */) !== 0) {
|
|
2660
|
+
this.aliasNaming = 3 /* SymbolNaming.Merged */;
|
|
2661
|
+
this.aliasMergedId = numbers[p++];
|
|
2662
|
+
}
|
|
2663
|
+
else {
|
|
2664
|
+
this.aliasNaming = numbers[p] === 1 ? 2 /* SymbolNaming.Export */ : 1 /* SymbolNaming.Node */;
|
|
2665
|
+
this.aliasNodeStart = p + 1;
|
|
2666
|
+
p = this.aliasNodeStart + this.nodeWords(this.aliasNodeStart);
|
|
2667
|
+
}
|
|
2668
|
+
this.aliasRefCount = numbers[p];
|
|
2669
|
+
this.aliasRefAt = p + 1;
|
|
2670
|
+
p = this.aliasRefAt + this.aliasRefCount;
|
|
1820
2671
|
}
|
|
1821
|
-
const
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
if (
|
|
1826
|
-
this.
|
|
1827
|
-
else if (
|
|
1828
|
-
this.
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
2672
|
+
const naming = (header & 192 /* RecordHeader.NamingMask */) >>> 6 /* RecordHeader.NamingShift */;
|
|
2673
|
+
this.symbolNaming = naming;
|
|
2674
|
+
this.symbolMergedId = -1;
|
|
2675
|
+
this.symbolNodeStart = -1;
|
|
2676
|
+
if (naming === 3 /* SymbolNaming.Merged */)
|
|
2677
|
+
this.symbolMergedId = numbers[p++];
|
|
2678
|
+
else if (naming !== 0 /* SymbolNaming.None */) {
|
|
2679
|
+
this.symbolNodeStart = p;
|
|
2680
|
+
p += this.nodeWords(p);
|
|
2681
|
+
}
|
|
2682
|
+
this.nodeStart = -1;
|
|
2683
|
+
if ((header & 256 /* RecordHeader.HasNode */) !== 0) {
|
|
2684
|
+
this.nodeStart = p;
|
|
2685
|
+
p += this.nodeWords(p);
|
|
2686
|
+
}
|
|
2687
|
+
this.fields = p;
|
|
1832
2688
|
}
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
p.f.push(r.kind, r.id, r.flags);
|
|
1837
|
-
// A number literal's value is any double, written as its two halves.
|
|
1838
|
-
if (r.kind === 1 /* RecordKind.Literal */)
|
|
1839
|
-
p.double(r.num);
|
|
1840
|
-
else
|
|
1841
|
-
p.f.push(r.num);
|
|
1842
|
-
p.f.push(r.mergedId, r.aliasMergedId);
|
|
1843
|
-
p.text(r.text);
|
|
1844
|
-
if (!p.symbol(r.symbol) || !p.node(r.node) || !p.symbol(r.aliasSymbol))
|
|
1845
|
-
return false;
|
|
1846
|
-
p.list(r.refs);
|
|
1847
|
-
p.list(r.nums);
|
|
1848
|
-
if (!p.nodes(r.nodes))
|
|
1849
|
-
return false;
|
|
1850
|
-
p.f.push(r.texts.length);
|
|
1851
|
-
for (const t of r.texts)
|
|
1852
|
-
p.text(t);
|
|
1853
|
-
p.list(r.aliasRefs);
|
|
1854
|
-
p.f.push(r.indexInfos.length);
|
|
1855
|
-
for (const info of r.indexInfos) {
|
|
1856
|
-
p.f.push(info.keyType, info.valueType, info.isReadonly ? 1 : 0);
|
|
1857
|
-
if (!p.node(info.declaration) || !p.nodes(info.components))
|
|
1858
|
-
return false;
|
|
2689
|
+
/** The words a node at `p` takes: its file index, range and id, or one word for no node. */
|
|
2690
|
+
nodeWords(p) {
|
|
2691
|
+
return this.numbers[p] < 0 ? 1 : 4;
|
|
1859
2692
|
}
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
strings;
|
|
1867
|
-
at = 0;
|
|
1868
|
-
failed = false;
|
|
1869
|
-
/** The nodes found so far, by id. */
|
|
1870
|
-
nodesById;
|
|
1871
|
-
constructor(c, numbers, strings, nodesById) {
|
|
1872
|
-
this.c = c;
|
|
1873
|
-
this.f = numbers;
|
|
1874
|
-
this.strings = strings;
|
|
1875
|
-
this.nodesById = nodesById;
|
|
2693
|
+
/** The node at `p`; undefined if it is none or the program hasn't it. */
|
|
2694
|
+
readNode(p) {
|
|
2695
|
+
if (p < 0 || this.numbers[p] < 0)
|
|
2696
|
+
return undefined;
|
|
2697
|
+
const numbers = this.numbers;
|
|
2698
|
+
return this.finder.find(numbers[p], numbers[p + 1], numbers[p + 2], numbers[p + 3]);
|
|
1876
2699
|
}
|
|
1877
|
-
|
|
1878
|
-
|
|
2700
|
+
/** The record's own node; undefined if it has none or the program hasn't it. */
|
|
2701
|
+
node() {
|
|
2702
|
+
return this.readNode(this.nodeStart);
|
|
1879
2703
|
}
|
|
1880
|
-
|
|
2704
|
+
doubleAt(p) {
|
|
1881
2705
|
const words = doubleWords();
|
|
1882
|
-
words.i[0] = this.
|
|
1883
|
-
words.i[1] = this.
|
|
2706
|
+
words.i[0] = this.numbers[p];
|
|
2707
|
+
words.i[1] = this.numbers[p + 1];
|
|
1884
2708
|
return words.f[0];
|
|
1885
2709
|
}
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
const length = this.next();
|
|
1889
|
-
return readText(this.strings, start, length);
|
|
2710
|
+
textAt(p) {
|
|
2711
|
+
return readText(this.strings, this.numbers[p], this.numbers[p + 1]);
|
|
1890
2712
|
}
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
2713
|
+
/**
|
|
2714
|
+
* Collects in `deps` the indexes of the records the record in progress needs: its refs, its alias refs, and the
|
|
2715
|
+
* types of its own fields that are records (a union's origin, an object literal's completion, a completed object's
|
|
2716
|
+
* members' types). A record is written before the records it needs.
|
|
2717
|
+
*/
|
|
2718
|
+
scan() {
|
|
2719
|
+
const numbers = this.numbers;
|
|
2720
|
+
const deps = this.deps;
|
|
2721
|
+
deps.length = 0;
|
|
2722
|
+
for (let i = 0; i < this.refCount; i++)
|
|
2723
|
+
deps.push(numbers[this.refAt + i]);
|
|
2724
|
+
for (let i = 0; i < this.aliasRefCount; i++)
|
|
2725
|
+
deps.push(numbers[this.aliasRefAt + i]);
|
|
2726
|
+
let p = this.fields;
|
|
2727
|
+
switch (this.kind) {
|
|
2728
|
+
case 12 /* RecordKind.Union */:
|
|
2729
|
+
case 22 /* RecordKind.AnonymousObject */:
|
|
2730
|
+
// The origin of a union, and the completion of an object literal.
|
|
2731
|
+
deps.push(numbers[p + 1]);
|
|
2732
|
+
break;
|
|
2733
|
+
case 23 /* RecordKind.CompleteObject */: {
|
|
2734
|
+
// The object literal completed, then each index signature's key and value types.
|
|
2735
|
+
deps.push(numbers[p]);
|
|
2736
|
+
const count = numbers[p + 1];
|
|
2737
|
+
let q = p + 2;
|
|
2738
|
+
for (let i = 0; i < count; i++) {
|
|
2739
|
+
deps.push(numbers[q], numbers[q + 1]);
|
|
2740
|
+
q += 3;
|
|
2741
|
+
q += this.nodeWords(q);
|
|
2742
|
+
const components = numbers[q++];
|
|
2743
|
+
for (let j = 0; j < components; j++)
|
|
2744
|
+
q += this.nodeWords(q);
|
|
2745
|
+
}
|
|
2746
|
+
break;
|
|
1913
2747
|
}
|
|
1914
|
-
this.nodesById.set(id, node);
|
|
1915
2748
|
}
|
|
1916
|
-
return node;
|
|
1917
|
-
}
|
|
1918
|
-
nodes() {
|
|
1919
|
-
const n = this.next();
|
|
1920
|
-
const list = [];
|
|
1921
|
-
for (let i = 0; i < n; i++)
|
|
1922
|
-
list.push(this.node());
|
|
1923
|
-
return list;
|
|
1924
|
-
}
|
|
1925
|
-
symbol() {
|
|
1926
|
-
const how = this.next();
|
|
1927
|
-
if (how < 0)
|
|
1928
|
-
return undefined;
|
|
1929
|
-
const node = this.node();
|
|
1930
|
-
if (node === undefined)
|
|
1931
|
-
return undefined;
|
|
1932
|
-
const local = getSymbol(node);
|
|
1933
|
-
const symbol = how === 0 ? local : local?.exportSymbol;
|
|
1934
|
-
if (symbol === undefined)
|
|
1935
|
-
this.failed = true;
|
|
1936
|
-
return symbol;
|
|
1937
|
-
}
|
|
1938
|
-
}
|
|
1939
|
-
/** Reads a record; undefined if a node or symbol in it isn't in `u`'s program. */
|
|
1940
|
-
function unpackRecord(u) {
|
|
1941
|
-
const kind = u.next();
|
|
1942
|
-
const id = u.next();
|
|
1943
|
-
const flags = u.next();
|
|
1944
|
-
const num = kind === 1 /* RecordKind.Literal */ ? u.double() : u.next();
|
|
1945
|
-
const mergedId = u.next();
|
|
1946
|
-
const aliasMergedId = u.next();
|
|
1947
|
-
const text = u.text();
|
|
1948
|
-
const symbol = u.symbol();
|
|
1949
|
-
const node = u.node();
|
|
1950
|
-
const aliasSymbol = u.symbol();
|
|
1951
|
-
const refs = u.list();
|
|
1952
|
-
const record = new PortableRecord(kind, id, flags, num, text, symbol, node, refs);
|
|
1953
|
-
record.mergedId = mergedId;
|
|
1954
|
-
record.aliasMergedId = aliasMergedId;
|
|
1955
|
-
record.aliasSymbol = aliasSymbol;
|
|
1956
|
-
record.nums = u.list();
|
|
1957
|
-
record.nodes = u.nodes();
|
|
1958
|
-
const textCount = u.next();
|
|
1959
|
-
const texts = [];
|
|
1960
|
-
for (let k = 0; k < textCount; k++)
|
|
1961
|
-
texts.push(u.text());
|
|
1962
|
-
record.texts = texts;
|
|
1963
|
-
record.aliasRefs = u.list();
|
|
1964
|
-
const infoCount = u.next();
|
|
1965
|
-
const infos = [];
|
|
1966
|
-
for (let k = 0; k < infoCount; k++) {
|
|
1967
|
-
const keyType = u.next();
|
|
1968
|
-
const valueType = u.next();
|
|
1969
|
-
const isReadonly = u.next() === 1;
|
|
1970
|
-
const declaration = u.node();
|
|
1971
|
-
const components = u.nodes();
|
|
1972
|
-
infos.push(new PortableIndexInfo(keyType, valueType, isReadonly, declaration, components));
|
|
1973
|
-
}
|
|
1974
|
-
record.indexInfos = infos;
|
|
1975
|
-
return u.failed ? undefined : record;
|
|
1976
|
-
}
|
|
1977
|
-
/** Collects the children of a node (`forEachChild`). */
|
|
1978
|
-
class ChildCollector extends NodeVisitor {
|
|
1979
|
-
children = [];
|
|
1980
|
-
visitNode(node) {
|
|
1981
|
-
this.children.push(node);
|
|
1982
|
-
return false;
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1985
|
-
/** The node of `file` with the range, kind and id given, found by descending through the nodes that contain it. */
|
|
1986
|
-
function findNode(file, pos, end, kind, id) {
|
|
1987
|
-
let node = file;
|
|
1988
|
-
for (;;) {
|
|
1989
|
-
if (node.id === id)
|
|
1990
|
-
return node.kind === kind && node.pos === pos && node.end === end ? node : undefined;
|
|
1991
|
-
const collector = new ChildCollector();
|
|
1992
|
-
node.forEachChild(collector);
|
|
1993
|
-
let next = undefined;
|
|
1994
|
-
for (const child of collector.children)
|
|
1995
|
-
if (child.pos <= pos && end <= child.end) {
|
|
1996
|
-
// The innermost of nested nodes with one range is found by its id further down.
|
|
1997
|
-
if (child.id === id)
|
|
1998
|
-
return child.kind === kind && child.pos === pos && child.end === end ? child : undefined;
|
|
1999
|
-
if (next === undefined)
|
|
2000
|
-
next = child;
|
|
2001
|
-
}
|
|
2002
|
-
if (next === undefined)
|
|
2003
|
-
return undefined;
|
|
2004
|
-
node = next;
|
|
2005
2749
|
}
|
|
2006
2750
|
}
|
|
2751
|
+
/** The bits of a record header that hold the number of refs, or `RefEscape` if the number is a word of its own. */
|
|
2752
|
+
const RefCountMask = 0xffff;
|
|
2007
2753
|
/**
|
|
2008
|
-
* A consumer's translations of another checker's store: each record is read and translated once, when a
|
|
2009
|
-
* first needs it, in the order of the store, which is the order the producer exported them in.
|
|
2754
|
+
* A consumer's translations of another checker's store: each record is read where it is, and translated once, when a
|
|
2755
|
+
* result first needs it, in the order of the store, which is the order the producer exported them in.
|
|
2010
2756
|
*/
|
|
2011
2757
|
class StoreReader {
|
|
2012
2758
|
sharing;
|
|
2013
2759
|
producer;
|
|
2014
2760
|
translator;
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
types =
|
|
2020
|
-
symbols =
|
|
2021
|
-
|
|
2761
|
+
reader;
|
|
2762
|
+
/** Whether the last `translate` stopped at its budget, having built nothing. */
|
|
2763
|
+
stopped = false;
|
|
2764
|
+
/** By record: the type it translated to, the symbol a property record makes, and whether a completion is done. */
|
|
2765
|
+
types = new LinkTable();
|
|
2766
|
+
symbols = new LinkTable();
|
|
2767
|
+
completed = new LinkTable();
|
|
2022
2768
|
constructor(sharing, producer) {
|
|
2023
2769
|
this.sharing = sharing;
|
|
2024
2770
|
this.producer = producer;
|
|
2025
|
-
this.translator = new Translator(sharing.c);
|
|
2026
|
-
this.
|
|
2027
|
-
this.unpacker = new RecordUnpacker(sharing.c, sharing.numberLogs[producer], sharing.stringLogs[producer], sharing.nodesById);
|
|
2771
|
+
this.translator = new Translator(sharing.c, this);
|
|
2772
|
+
this.reader = new RecordReader(sharing.numberLogs[producer], sharing.stringLogs[producer], sharing.recordTables[producer], sharing.c.files, sharing.nodesById);
|
|
2028
2773
|
}
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2774
|
+
/**
|
|
2775
|
+
* Whether record `index` is translated: a property record made its symbol, an object literal got its members, a
|
|
2776
|
+
* shell whose members come later only counts with them. A translation that fails after it built a shell leaves
|
|
2777
|
+
* the empty type behind, and a later translation of the shell alone would take it for the object.
|
|
2778
|
+
*/
|
|
2779
|
+
isBuilt(index) {
|
|
2780
|
+
if (!this.reader.begin(index))
|
|
2781
|
+
return false;
|
|
2782
|
+
const kind = this.reader.kind;
|
|
2783
|
+
if (kind === 24 /* RecordKind.Property */)
|
|
2784
|
+
return this.symbols.get(index) !== undefined;
|
|
2785
|
+
if (kind === 23 /* RecordKind.CompleteObject */)
|
|
2786
|
+
return this.completed.get(index) !== undefined;
|
|
2787
|
+
if (this.types.get(index) === undefined)
|
|
2788
|
+
return false;
|
|
2789
|
+
if (kind !== 22 /* RecordKind.AnonymousObject */)
|
|
2790
|
+
return true;
|
|
2791
|
+
const completion = this.reader.numbers[this.reader.fields + 1];
|
|
2792
|
+
return completion < 0 || this.completed.get(completion) !== undefined;
|
|
2037
2793
|
}
|
|
2038
|
-
/**
|
|
2039
|
-
|
|
2040
|
-
this.
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
if (record !== undefined)
|
|
2051
|
-
this.records[index] = record;
|
|
2052
|
-
return record;
|
|
2053
|
-
}
|
|
2054
|
-
/** The translation of record `root` and the records it needs; undefined if one can't be translated here. */
|
|
2055
|
-
translate(root) {
|
|
2794
|
+
/** Whether the record in progress names only merged symbols this program has. */
|
|
2795
|
+
present(reader) {
|
|
2796
|
+
const merged = this.sharing.c.mergedSymbols;
|
|
2797
|
+
if (reader.aliasNaming === 3 /* SymbolNaming.Merged */ && !merged.has(reader.aliasMergedId))
|
|
2798
|
+
return false;
|
|
2799
|
+
return reader.symbolNaming !== 3 /* SymbolNaming.Merged */ || merged.has(reader.symbolMergedId);
|
|
2800
|
+
}
|
|
2801
|
+
/**
|
|
2802
|
+
* The translation of record `root` and the records it needs; undefined if one can't be translated here, or if the
|
|
2803
|
+
* records it would build pass `budget`.
|
|
2804
|
+
*/
|
|
2805
|
+
translate(root, budget = Number.POSITIVE_INFINITY) {
|
|
2056
2806
|
const c = this.sharing.c;
|
|
2057
2807
|
const translator = this.translator;
|
|
2808
|
+
const reader = this.reader;
|
|
2809
|
+
translator.failed = false;
|
|
2058
2810
|
const needed = new Set();
|
|
2059
2811
|
const stack = [root];
|
|
2812
|
+
this.stopped = false;
|
|
2060
2813
|
while (stack.length > 0) {
|
|
2061
2814
|
const i = stack.pop();
|
|
2062
|
-
if (needed.has(i))
|
|
2063
|
-
continue;
|
|
2064
|
-
this.ensure(i);
|
|
2065
|
-
if (this.built[i] === 1)
|
|
2815
|
+
if (needed.has(i) || this.isBuilt(i))
|
|
2066
2816
|
continue;
|
|
2067
|
-
|
|
2068
|
-
|
|
2817
|
+
if (needed.size >= budget) {
|
|
2818
|
+
this.stopped = true;
|
|
2069
2819
|
return undefined;
|
|
2070
|
-
|
|
2071
|
-
|
|
2820
|
+
}
|
|
2821
|
+
if (!reader.begin(i) || !this.present(reader))
|
|
2072
2822
|
return undefined;
|
|
2073
2823
|
needed.add(i);
|
|
2074
|
-
|
|
2824
|
+
reader.scan();
|
|
2825
|
+
const deps = reader.deps;
|
|
2826
|
+
for (let k = 0; k < deps.length; k++) {
|
|
2827
|
+
const j = deps[k];
|
|
2075
2828
|
if (j >= 0)
|
|
2076
2829
|
stack.push(j);
|
|
2077
|
-
for (const j of r.aliasRefs)
|
|
2078
|
-
stack.push(j);
|
|
2079
|
-
if (r.kind === 23 /* RecordKind.CompleteObject */ || (r.kind === 12 /* RecordKind.Union */ && r.num >= 0))
|
|
2080
|
-
stack.push(r.num);
|
|
2081
|
-
for (const info of r.indexInfos) {
|
|
2082
|
-
stack.push(info.keyType);
|
|
2083
|
-
stack.push(info.valueType);
|
|
2084
|
-
}
|
|
2085
|
-
if (r.kind === 22 /* RecordKind.AnonymousObject */) {
|
|
2086
|
-
const completion = this.table[2 * i + 1] - 1;
|
|
2087
|
-
if (completion >= 0)
|
|
2088
|
-
stack.push(completion);
|
|
2089
2830
|
}
|
|
2090
2831
|
}
|
|
2091
2832
|
const order = [...needed].sort((a, b) => a - b);
|
|
@@ -2093,36 +2834,72 @@ class StoreReader {
|
|
|
2093
2834
|
// record is translated. A translation can start translations of this store again (resolving a type alias, or
|
|
2094
2835
|
// a type of a symbol), which translate some of these records first.
|
|
2095
2836
|
for (const i of order) {
|
|
2096
|
-
if (this.
|
|
2837
|
+
if (this.isBuilt(i))
|
|
2097
2838
|
continue;
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2839
|
+
reader.begin(i);
|
|
2840
|
+
const kind = reader.kind;
|
|
2841
|
+
if (kind !== 8 /* RecordKind.NodeType */ && kind !== 16 /* RecordKind.Conditional */ && kind !== 21 /* RecordKind.DistributedConditional */)
|
|
2842
|
+
continue;
|
|
2843
|
+
const node = reader.node();
|
|
2844
|
+
if (node === undefined)
|
|
2845
|
+
return undefined;
|
|
2846
|
+
translator.aliasOfNode(node);
|
|
2103
2847
|
}
|
|
2104
2848
|
if (translator.failed) {
|
|
2105
2849
|
translator.failed = false;
|
|
2106
2850
|
return undefined;
|
|
2107
2851
|
}
|
|
2852
|
+
const verifyOrder = shareVerifyValue === '2';
|
|
2853
|
+
let lastNew = -1;
|
|
2108
2854
|
for (const i of order) {
|
|
2109
|
-
if (this.
|
|
2855
|
+
if (this.isBuilt(i))
|
|
2110
2856
|
continue;
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2857
|
+
reader.begin(i);
|
|
2858
|
+
const before = verifyOrder ? c.typeCount : 0;
|
|
2859
|
+
switch (reader.kind) {
|
|
2860
|
+
case 24 /* RecordKind.Property */: {
|
|
2861
|
+
const symbol = translator.property(i);
|
|
2862
|
+
if (symbol === undefined) {
|
|
2863
|
+
translator.failed = false;
|
|
2864
|
+
return undefined;
|
|
2865
|
+
}
|
|
2866
|
+
this.symbols.set(i, symbol);
|
|
2867
|
+
continue;
|
|
2868
|
+
}
|
|
2869
|
+
case 23 /* RecordKind.CompleteObject */:
|
|
2870
|
+
if (!translator.complete(i)) {
|
|
2871
|
+
translator.failed = false;
|
|
2872
|
+
return undefined;
|
|
2873
|
+
}
|
|
2874
|
+
this.completed.set(i, true);
|
|
2875
|
+
continue;
|
|
2876
|
+
default: {
|
|
2877
|
+
const t = translator.record(i);
|
|
2878
|
+
if (t === undefined) {
|
|
2879
|
+
translator.failed = false;
|
|
2880
|
+
return undefined;
|
|
2881
|
+
}
|
|
2882
|
+
this.types.set(i, t);
|
|
2883
|
+
// The types a translation creates, record by record, are created in the order the producer created them.
|
|
2884
|
+
if (verifyOrder && c.typeCount > before) {
|
|
2885
|
+
if (before < lastNew) {
|
|
2886
|
+
const violations = ++this.sharing.orderViolations;
|
|
2887
|
+
if (violations <= 20)
|
|
2888
|
+
process.stderr.write('share-verify: checker ' +
|
|
2889
|
+
this.sharing.data.index +
|
|
2890
|
+
' record ' +
|
|
2891
|
+
i +
|
|
2892
|
+
' created type ' +
|
|
2893
|
+
c.typeCount +
|
|
2894
|
+
' after ' +
|
|
2895
|
+
lastNew +
|
|
2896
|
+
'\n');
|
|
2897
|
+
}
|
|
2898
|
+
lastNew = c.typeCount;
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2121
2901
|
}
|
|
2122
|
-
this.built[i] = 1;
|
|
2123
|
-
// A translated record is not read again.
|
|
2124
|
-
this.records[i] = undefined;
|
|
2125
2902
|
}
|
|
2126
|
-
return this.types
|
|
2903
|
+
return this.types.get(root);
|
|
2127
2904
|
}
|
|
2128
2905
|
}
|