tsc-ts 0.1.0 → 0.1.2

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.
Files changed (81) hide show
  1. package/README.md +7 -1
  2. package/dist/api/compiler/ast.d.ts +15 -3
  3. package/dist/api/compiler/ast.js +36 -15
  4. package/dist/api/compiler/checker-classes.js +2 -1
  5. package/dist/api/compiler/checker-contextual.d.ts +20 -0
  6. package/dist/api/compiler/checker-contextual.js +111 -1
  7. package/dist/api/compiler/checker-expressions.js +14 -1
  8. package/dist/api/compiler/checker-grammar.d.ts +39 -4
  9. package/dist/api/compiler/checker-grammar.js +239 -75
  10. package/dist/api/compiler/checker-heritage.js +20 -14
  11. package/dist/api/compiler/checker-jsx.js +2 -1
  12. package/dist/api/compiler/checker-modules.js +4 -2
  13. package/dist/api/compiler/checker-source-file.js +1 -1
  14. package/dist/api/compiler/checker-type-factory.js +70 -19
  15. package/dist/api/compiler/checker-type-nodes.d.ts +1 -1
  16. package/dist/api/compiler/checker-type-nodes.js +5 -5
  17. package/dist/api/compiler/checker-unused.js +15 -10
  18. package/dist/api/compiler/checker.d.ts +7 -1
  19. package/dist/api/compiler/checker.js +14 -4
  20. package/dist/api/compiler/compile-program.d.ts +5 -0
  21. package/dist/api/compiler/compile-program.js +8 -0
  22. package/dist/api/compiler/expr-record.d.ts +11 -0
  23. package/dist/api/compiler/expr-record.js +53 -0
  24. package/dist/api/compiler/file-loader.d.ts +5 -0
  25. package/dist/api/compiler/file-loader.js +7 -0
  26. package/dist/api/compiler/identifier-name.d.ts +9 -0
  27. package/dist/api/compiler/identifier-name.js +10 -0
  28. package/dist/api/compiler/jsdoc.js +4 -2
  29. package/dist/api/compiler/parser.d.ts +2 -1
  30. package/dist/api/compiler/parser.js +10 -8
  31. package/dist/api/compiler/printer/factory.js +3 -2
  32. package/dist/api/compiler/printer/printer.js +3 -2
  33. package/dist/api/compiler/program-checker.d.ts +18 -0
  34. package/dist/api/compiler/program-checker.js +27 -0
  35. package/dist/api/compiler/reparser.js +4 -3
  36. package/dist/api/compiler/scanner.d.ts +8 -0
  37. package/dist/api/compiler/scanner.js +58 -42
  38. package/dist/api/compiler/shared-results.d.ts +376 -143
  39. package/dist/api/compiler/shared-results.js +1550 -773
  40. package/dist/api/compiler/transformers/declarations.js +2 -1
  41. package/package.json +15 -10
  42. package/src/compiler/ast.ts +39 -15
  43. package/src/compiler/cgroup.ts +157 -0
  44. package/src/compiler/checker-classes.ts +2 -1
  45. package/src/compiler/checker-contextual.ts +104 -0
  46. package/src/compiler/checker-expressions.ts +16 -1
  47. package/src/compiler/checker-grammar.ts +247 -88
  48. package/src/compiler/checker-heritage.ts +31 -17
  49. package/src/compiler/checker-jsx.ts +2 -1
  50. package/src/compiler/checker-modules.ts +4 -2
  51. package/src/compiler/checker-source-file.ts +1 -1
  52. package/src/compiler/checker-type-factory.ts +67 -21
  53. package/src/compiler/checker-type-nodes.ts +5 -5
  54. package/src/compiler/checker-unused.ts +13 -8
  55. package/src/compiler/checker.ts +15 -4
  56. package/src/compiler/compile-program.ts +8 -0
  57. package/src/compiler/decoder.ts +2 -1
  58. package/src/compiler/doctor-report.ts +133 -0
  59. package/src/compiler/expr-record.ts +59 -0
  60. package/src/compiler/file-loader.ts +8 -0
  61. package/src/compiler/identifier-name.ts +13 -0
  62. package/src/compiler/jsdoc.ts +4 -2
  63. package/src/compiler/parallel-check.ts +364 -146
  64. package/src/compiler/parallel-emit.ts +3 -3
  65. package/src/compiler/parse-thread.ts +24 -2
  66. package/src/compiler/parser.ts +10 -8
  67. package/src/compiler/printer/factory.ts +3 -2
  68. package/src/compiler/printer/printer.ts +4 -2
  69. package/src/compiler/program-checker.ts +28 -0
  70. package/src/compiler/reparser.ts +7 -3
  71. package/src/compiler/run-config.ts +1383 -0
  72. package/src/compiler/run-record-read.ts +87 -0
  73. package/src/compiler/run-records.ts +575 -0
  74. package/src/compiler/scanner.ts +57 -41
  75. package/src/compiler/shared-results.ts +1626 -896
  76. package/src/compiler/thread-entry.ts +3 -1
  77. package/src/compiler/transformers/declarations.ts +2 -1
  78. package/src/compiler/tsc-build.ts +64 -6
  79. package/src/compiler/tsc-cli.ts +33 -4
  80. package/src/compiler/tsc-compile.ts +223 -9
  81. package/src/compiler/tsc-watch.ts +25 -4
@@ -1,5 +1,5 @@
1
- // Results one checker thread computed, as portable records other checker threads translate into their own types
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
- /** An index signature of an `AnonymousObject` record. */
31
- export class PortableIndexInfo {
32
- keyType;
33
- valueType;
34
- isReadonly;
35
- declaration;
36
- components;
37
- constructor(keyType, valueType, isReadonly, declaration, components) {
38
- this.keyType = keyType;
39
- this.valueType = valueType;
40
- this.isReadonly = isReadonly;
41
- this.declaration = declaration;
42
- this.components = components;
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
- export class PortableRecord {
46
- kind;
47
- flags;
48
- num;
49
- text;
50
- symbol;
51
- node;
52
- refs;
53
- nums;
54
- nodes;
55
- texts;
56
- /** For a symbol a checker merged: the id of a symbol it merged, which keys every checker's merged symbol. */
57
- mergedId;
58
- /** The alias: its symbol and the records of its type arguments. */
59
- aliasSymbol;
60
- aliasMergedId;
61
- aliasRefs;
62
- indexInfos;
63
- /** The producer's type id, which keys the consumer's translations of the producer's types. */
64
- id;
65
- constructor(kind, id, flags, num, text, symbol, node, refs) {
66
- this.kind = kind;
67
- this.id = id;
68
- this.flags = flags;
69
- this.num = num;
70
- this.text = text;
71
- this.symbol = symbol;
72
- this.node = node;
73
- this.refs = refs;
74
- this.nums = noNumbers;
75
- this.nodes = noNodes;
76
- this.texts = noTexts;
77
- this.mergedId = -1;
78
- this.aliasSymbol = undefined;
79
- this.aliasMergedId = -1;
80
- this.aliasRefs = noNumbers;
81
- this.indexInfos = noIndexInfos;
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
- records = [];
154
- indexes = new Map();
188
+ writer;
155
189
  /**
156
- * The index of `records[0]`. A checker's exporter keeps the indexes of the types it exported before
157
- * (`ThreadSharing.post`): their records are in its store already, and a result's records are only the new ones.
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
- /** Appends `record`; its index. */
164
- push(record) {
165
- const index = this.base + this.records.length;
166
- this.records.push(record);
167
- return index;
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
- constructor(c, initial) {
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
- /** Whether `node` is one every checker shares; fails if not. */
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 || node.id <= this.nodeLimit)
237
+ if (node === undefined)
181
238
  return true;
182
- this.fail('node of the thread');
183
- return false;
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
- * Sets the symbol of `record`: a symbol of the program, which every checker shares, or a symbol the checker merged,
191
- * by the id of a symbol it merged. Other symbols are the checker's own.
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
- symbolOf(record, symbol, alias) {
194
- let mergedId = -1;
195
- let shared = symbol;
196
- if ((symbol.flags & 33554432 /* SymbolFlags.Transient */) === 0 && getSymbolId(symbol) > this.symbolLimit) {
197
- this.fail('symbol of the thread');
198
- return false;
199
- }
200
- if ((symbol.flags & 33554432 /* SymbolFlags.Transient */) !== 0) {
201
- let origins = this.mergedOrigins;
202
- if (origins === undefined) {
203
- const cache = this.originsCache;
204
- if (cache !== undefined && cache.size === this.c.mergedSymbols.size)
205
- origins = cache.origins;
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
- origins = new Map();
208
- for (const [id, target] of this.c.mergedSymbols)
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
- this.mergedOrigins = origins;
298
+ node = declaration;
217
299
  }
218
- const origin = origins.get(symbol);
219
- if (origin === undefined) {
220
- this.fail('checker symbol ' + symbol.name);
221
- return false;
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
- record.aliasSymbol = shared;
228
- record.aliasMergedId = mergedId;
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
- record.symbol = shared;
232
- record.mergedId = mergedId;
322
+ this.naming = naming;
323
+ this.namingMerged = merged;
324
+ this.namingNode = node;
233
325
  }
234
326
  return true;
235
327
  }
236
- /** `new PortableRecord` with the symbol `symbol` (see `symbolOf`); undefined if it is the checker's own. */
237
- withSymbol(kind, t, symbol, num, refs) {
238
- const record = new PortableRecord(kind, t.id, 0, num, '', undefined, undefined, refs);
239
- return this.symbolOf(record, symbol, false) ? record : undefined;
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
- addWithSymbol(kind, t, symbol, num, refs) {
242
- const record = this.withSymbol(kind, t, symbol, num, refs);
243
- return record === undefined ? -1 : this.add(t, record);
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
- fail(reason) {
246
- if (this.failure === undefined)
247
- this.failure = reason;
248
- return -1;
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
- add(t, record) {
251
- if (!this.sharedNode(record.node))
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
- for (const node of record.nodes)
254
- if (!this.sharedNode(node))
255
- return -1;
256
- for (const info of record.indexInfos) {
257
- if (!this.sharedNode(info.declaration))
258
- return -1;
259
- for (const node of info.components)
260
- if (!this.sharedNode(node))
261
- return -1;
262
- }
263
- const index = this.push(record);
264
- this.indexes.set(t, index);
265
- this.addedTypes.push(t);
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.add(t, new PortableRecord(0 /* RecordKind.Initial */, t.id, 0, initial, '', undefined, undefined, noNumbers));
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.addWithSymbol(4 /* RecordKind.UniqueSymbol */, t, t.symbol, 0, noNumbers);
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.addWithSymbol(5 /* RecordKind.Declared */, t, symbol, 0, noNumbers);
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.addWithSymbol(6 /* RecordKind.ThisType */, t, symbol, 0, noNumbers);
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.addWithSymbol(5 /* RecordKind.Declared */, t, t.symbol, 0, noNumbers);
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
- const refs = this.types(t.types);
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
- return this.add(t, new PortableRecord(14 /* RecordKind.Index */, t.id, 0, t.indexFlags, '', undefined, undefined, [target]));
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 record = new PortableRecord(15 /* RecordKind.IndexedAccess */, t.id, 0, this.c.indexedAccessKeyFlags.get(t) ?? access.accessFlags, '', undefined, undefined, refs);
343
- return this.add(t, this.aliasOf(record, t.alias));
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 record = new PortableRecord(17 /* RecordKind.Template */, t.id, 0, 0, '', undefined, undefined, refs);
353
- record.texts = template.texts.slice();
354
- return this.add(t, record);
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.addWithSymbol(18 /* RecordKind.StringMapping */, t, t.symbol, 0, [target]);
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.add(t, new PortableRecord(20 /* RecordKind.NoInfer */, t.id, 0, 0, '', undefined, undefined, [base]));
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.add(t, new PortableRecord(19 /* RecordKind.Substitution */, t.id, 0, 0, '', undefined, undefined, refs));
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.add(t, new PortableRecord(2 /* RecordKind.FreshLiteral */, t.id, 0, 0, '', undefined, undefined, [index]));
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.addWithSymbol(3 /* RecordKind.EnumLiteral */, t, t.symbol, 0, noNumbers);
553
+ return this.symbolType(3 /* RecordKind.EnumLiteral */, t, t.symbol, noRefs);
391
554
  }
392
- return this.add(t, new PortableRecord(1 /* RecordKind.Literal */, t.id, t.flags, t.numberValue, t.stringValue, undefined, undefined, noNumbers));
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
- if (t.origin !== undefined) {
398
- const o = t.origin;
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.push(new PortableRecord(kind, -1, o.objectFlags, -2, '', undefined, undefined, parts));
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 record = new PortableRecord(12 /* RecordKind.Union */, t.id, t.objectFlags, origin, '', undefined, undefined, refs);
413
- return this.add(t, this.aliasOf(record, t.alias));
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.addWithSymbol(5 /* RecordKind.Declared */, t, symbol, 0, noNumbers);
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.add(t, new PortableRecord(9 /* RecordKind.Reference */, t.id, 0, 0, '', undefined, undefined, refs));
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.addWithSymbol(5 /* RecordKind.Declared */, t, symbol, 0, noNumbers);
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.addWithSymbol(7 /* RecordKind.SymbolType */, t, symbol, 0, noNumbers);
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.add(t, new PortableRecord(8 /* RecordKind.NodeType */, t.id, 0, 0, '', undefined, declaration, noNumbers));
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 = new PortableRecord(22 /* RecordKind.AnonymousObject */, t.id, t.objectFlags & ~lazyObjectFlags(), 0, '', undefined, undefined, noNumbers);
475
- if (!this.symbolOf(shell, t.symbol, false))
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
- infos.push(new PortableIndexInfo(key, value, info.isReadonly, info.declaration, info.components.slice()));
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 = new PortableRecord(23 /* RecordKind.CompleteObject */, -1, 0, index, '', undefined, undefined, members);
493
- complete.indexInfos = infos;
494
- this.push(complete);
495
- return index;
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 known = this.properties.get(prop);
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 record = new PortableRecord(24 /* RecordKind.Property */, -1, prop.flags, prop.checkFlags, prop.name, program, prop.valueDeclaration, [type, name, target]);
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 record.nodes)
764
+ for (const node of declarations)
539
765
  if (!this.sharedNode(node))
540
766
  return -1;
541
- const index = this.push(record);
542
- this.properties.set(prop, index);
543
- this.addedProperties.push(prop);
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 record = new PortableRecord(10 /* RecordKind.TupleTarget */, t.id, 0, t.readonly ? 1 : 0, '', undefined, undefined, noNumbers);
548
- const nums = [];
549
- const nodes = [];
550
- for (const info of t.elementInfos) {
551
- nums.push(info.flags);
552
- nodes.push(info.labeledDeclaration);
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
- record.nums = nums;
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.add(t, new PortableRecord(8 /* RecordKind.NodeType */, t.id, 0, 0, '', undefined, declaration, noNumbers));
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
- const record = new PortableRecord(11 /* RecordKind.Instantiation */, t.id, 0, args.length, '', undefined, declaration, refs);
595
- const index = this.add(t, this.aliasOf(record, t.alias));
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 (index >= 0 && this.failure === undefined)
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 records = this.records.length;
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.records.length = records;
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.add(t, new PortableRecord(8 /* RecordKind.NodeType */, t.id, 0, 0, '', undefined, root.node, noNumbers));
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 record = new PortableRecord(16 /* RecordKind.Conditional */, t.id, 0, keyed.tag, '', undefined, keyed.root.node, refs);
664
- return this.add(t, this.aliasOf(record, keyed.alias));
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 record = new PortableRecord(21 /* RecordKind.DistributedConditional */, t.id, 0, distributed.tag, '', undefined, root.node, refs);
673
- return this.add(t, this.aliasOf(record, distributed.alias));
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 record = new PortableRecord(16 /* RecordKind.Conditional */, t.id, 0, tag, '', undefined, root.node, refs);
679
- return this.add(t, this.aliasOf(record, t.alias));
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
- translated = new Map();
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
- /** A property symbol made as the producer made it. */
703
- property(r, types, symbols) {
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 prop = c.newSymbolWithCheckFlags(r.flags & ~33554432 /* SymbolFlags.Transient */, r.text, r.num);
706
- setDeclarations(prop, r.nodes);
707
- prop.valueDeclaration = r.node;
708
- if (r.aliasSymbol !== undefined || r.aliasMergedId >= 0)
709
- prop.parent = r.aliasMergedId >= 0 ? c.mergedSymbols.get(r.aliasMergedId) : r.aliasSymbol;
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, types[r.refs[0]]);
712
- if (r.refs[1] >= 0)
713
- c.symbolNameTypes.set(id, types[r.refs[1]]);
714
- const target = r.refs[2] >= 0 ? symbols[r.refs[2]] : r.symbol;
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(r, types, symbols) {
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 t = types[r.num];
723
- if ((t.objectFlags & 2097152 /* ObjectFlags.MembersResolved */) !== 0)
724
- return;
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 (const i of r.refs) {
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 infos = r.indexInfos.map((info) => new IndexInfo(types[info.keyType], types[info.valueType], info.isReadonly, info.declaration, info.components));
731
- setStructuredTypeMembers(c, t, members, [], [], infos);
732
- keepPropertiesOnly(c, t, members, scannedLiteralPropertiesLimit);
733
- }
734
- record(r, types) {
735
- if (r.id >= 0) {
736
- const known = this.translated.get(r.id);
737
- if (known !== undefined)
738
- return known;
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
- const t = this.build(r, types);
741
- if (r.id >= 0 && !this.failed)
742
- this.translated.set(r.id, t);
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
- alias(r, types) {
766
- if (r.aliasSymbol === undefined && r.aliasMergedId < 0)
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 own = r.kind === 11 /* RecordKind.Instantiation */ ? r.refs.slice(0, 1 + r.num) : r.refs;
780
- const refs = own.map((i) => types[i]);
781
- switch (r.kind) {
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[r.num];
784
- case 1 /* RecordKind.Literal */:
785
- if ((r.flags & 1024 /* TypeFlags.StringLiteral */) !== 0)
786
- return getStringLiteralType(c, r.text);
787
- if ((r.flags & 2048 /* TypeFlags.NumberLiteral */) !== 0)
788
- return getNumberLiteralType(c, r.num);
789
- return getBigIntLiteralType(c, r.text);
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, this.symbol(r));
1267
+ return getDeclaredTypeOfSymbol(c, symbol);
795
1268
  case 6 /* RecordKind.ThisType */:
796
- return getDeclaredTypeOfSymbol(c, this.symbol(r)).thisType;
1269
+ return getDeclaredTypeOfSymbol(c, symbol).thisType;
797
1270
  case 4 /* RecordKind.UniqueSymbol */:
798
- return getESSymbolLikeTypeForNode(c, this.symbol(r).valueDeclaration);
1271
+ return getESSymbolLikeTypeForNode(c, symbol.valueDeclaration);
799
1272
  case 7 /* RecordKind.SymbolType */:
800
- return getTypeOfSymbol(c, this.symbol(r));
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 c.errorType;
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 < r.nums.length; i++)
817
- infos.push(new TupleElementInfo(r.nums[i], r.nodes[i]));
818
- return getTupleTargetType(c, infos, r.num === 1);
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, r.node, target);
823
- return instantiateTypeWithAlias(c, target, newTypeMapper(typeParameters, refs.slice(1, 1 + r.num)), this.alias(r, types));
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
- if (r.num === -2)
827
- return newUnionType(c, r.flags & 0 /* ObjectFlags.None */, refs);
828
- const origin = r.num >= 0 ? types[r.num] : undefined;
829
- return getUnionTypeFromSortedList(c, refs, r.flags & (32768 /* ObjectFlags.PrimitiveUnion */ | 33554432 /* ObjectFlags.ContainsIntersections */), this.alias(r, types), origin);
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
- if (r.num === -2)
1312
+ const flags = f[p++];
1313
+ if ((header & 2048 /* RecordHeader.Constituent */) !== 0)
833
1314
  return newIntersectionType(c, 0 /* ObjectFlags.None */, refs);
834
- const alias = this.alias(r, types);
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, (r.flags & 134217728 /* ObjectFlags.IsConstrainedTypeVariable */) | getPropagatingFlagsOfTypes(refs, 12 /* TypeFlags.Nullable */), refs);
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], r.num);
1326
+ return getIndexTypeEx(c, refs[0], f[p]);
846
1327
  case 15 /* RecordKind.IndexedAccess */:
847
- return getIndexedAccessTypeEx(c, refs[0], refs[1], r.num, undefined, this.alias(r, types));
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
- this.aliasOfNode(r.node);
1330
+ const tag = f[p++];
1331
+ this.aliasOfNode(node);
850
1332
  if (this.failed)
851
- return c.errorType;
852
- const declared = c.resolvedTypes.get(r.node.id) ?? getTypeFromTypeNode(c, r.node);
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), r.num === 1, this.alias(r, types));
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
- this.aliasOfNode(r.node);
1339
+ const tag = f[p++];
1340
+ this.aliasOfNode(node);
858
1341
  if (this.failed)
859
- return c.errorType;
860
- const declared = (c.resolvedTypes.get(r.node.id) ?? getTypeFromTypeNode(c, r.node));
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 = this.alias(r, types);
864
- getConditionalTypeInstantiation(c, declared, newTypeMapper(root.outerTypeParameters, args), r.num === 1, alias);
865
- const member = c.distributedByKey?.get(distributedKey(root.node.id, args, alias, r.num, refs[refs.length - 1]));
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.failed = true;
868
- return member ?? c.errorType;
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, this.symbol(r), refs[0]);
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
- return newObjectType(c, r.flags | 16 /* ObjectFlags.Anonymous */, this.symbol(r));
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 c.errorType;
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
- // ---- Sharing between checker threads (TSC_TS_SHARE=1)
917
- //
918
- // Checker threads take files from the shared queue as without sharing, and record which checker took each file
919
- // (`ThreadSharingData.takers`). When a checker needs the value type of a variable or property, or the return type of a
920
- // declared signature, declared in a file another checker took, it looks the declaration up in a table all threads
921
- // share (`ResultTable`): a result another checker posted is translated, unless that checker's computation reported
922
- // diagnostics in a file this one checks or may check; otherwise the checker computes the result itself. After
923
- // computing a result that took long enough and met no resolution cycle, a checker exports it into its store (each type
924
- // once) and posts it. A checker that took results for declarations of a file no checker took yet never checks that
925
- // file: it hands the file on to another checker (`forbidden`, `handoff`), which then computes them itself, as the
926
- // checker of a file always does.
927
- /** Whether checker threads share results (TSC_TS_SHARE=1). */
928
- export const shareThreads = process.env.TSC_TS_SHARE === '1';
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 (32 divided by the checkers by default). */
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
- packFailed = 0;
1187
- /** Of `packFailed`, the results that didn't fit in this checker's logs. */
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 ? '' : file.fileName);
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 ? '' : file.fileName);
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
- // A posted result pays when enough checkers need it, so the threshold falls as checkers are added: 8 ms with 4
1299
- // checkers, 2 ms with 16.
1300
- this.minMs = shareMinValue === undefined ? 32 / data.count : Number(shareMinValue);
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
- this.data.index +
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), owned ' +
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 exported ' +
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
- '), not packed ' +
1354
- this.packFailed +
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[2 * this.data.index] +
2033
+ this.used[usedStride * me] +
1359
2034
  ' numbers, ' +
1360
- this.used[2 * this.data.index + 1] +
2035
+ this.used[usedStride * me + 1] +
1361
2036
  ' code units)' +
1362
- (shareVerifyValue === '1' ? '; verified ' + this.verified + ' (' + this.verifiedIdentity + ' equal as text)' : ''));
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
- if (index === undefined)
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 === undefined)
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
- forbidsLastNamed(fileName) {
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 count = numbers[offset + 1];
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(readText(strings, numbers[offset + 2 + 2 * i], numbers[offset + 3 + 2 * i]));
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
- exporter = new Exporter(c, this.initialIndexes);
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
- const root = exporter.type(t);
1462
- if (exporter.failure !== undefined) {
1463
- const failure = exporter.failure;
1464
- forgetExport(exporter);
1465
- return failure;
1466
- }
1467
- const me = this.data.index;
1468
- const numbers = this.numberLogs[me];
1469
- const strings = this.stringLogs[me];
1470
- const table = this.recordTables[me];
1471
- const offset = this.used[2 * me];
1472
- const p = new RecordPacker(c.fileIndex, strings, this.used[2 * me + 1]);
1473
- const starts = [];
1474
- for (const r of exporter.records) {
1475
- starts.push(p.f.length);
1476
- if (!packRecord(p, r)) {
1477
- forgetExport(exporter);
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
- const header = p.f.length;
1482
- p.f.push(root, diagnosticFiles.length);
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 fileName of posted.files)
1522
- if (fileName === '' || this.ownsFileNamed(fileName) || this.forbidsLastNamed(fileName))
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
- const t = reader.translate(posted.root);
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
- for (const fileName of posted.files)
1538
- this.forbidNamed(fileName);
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
- constructor(root, files) {
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.records = [];
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
- // A quick computation is cheaper to repeat than to export, post and translate.
1587
- if (ms < sharing.minMs) {
1588
- sharing.belowMin++;
1589
- sharing.belowMinMs += ms;
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 (typeof state === 'string') {
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.packFailed++;
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(getSourceFileOfNode(symbol.valueDeclaration))) {
1640
- const slot = table.find(id);
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(symbol.valueDeclaration)) {
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', symbol.valueDeclaration, own, sharing.translatePosted(producer, posted, sharing.typeFiles, id, file));
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', symbol.valueDeclaration, producer);
2415
+ traceTranslation(c, 'types', declaration, producer);
1656
2416
  if (sharing.depth === 1)
1657
2417
  sharing.kind = 2;
1658
- sharing.forbid(getSourceFileOfNode(symbol.valueDeclaration));
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(getSourceFileOfNode(declaration))) {
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(getSourceFileOfNode(declaration));
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
- /** Packs records into a checker's store: their numbers into `f`, their strings into its string log from `unitAt`. */
1763
- class RecordPacker {
1764
- f = [];
1765
- fileIndex;
1766
- strings;
1767
- unitAt;
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
- text(t) {
1776
- if (this.unitAt + t.length > this.strings.length) {
1777
- this.full = true;
1778
- this.f.push(0, 0);
1779
- return;
1780
- }
1781
- this.f.push(this.unitAt, t.length);
1782
- for (let j = 0; j < t.length; j++)
1783
- this.strings[this.unitAt++] = t.charCodeAt(j);
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
- list(values) {
1786
- this.f.push(values.length);
1787
- for (const v of values)
1788
- this.f.push(v);
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
- /** A double as the two int32 halves of its bits. */
1791
- double(v) {
1792
- const words = doubleWords();
1793
- words.f[0] = v;
1794
- this.f.push(words.i[0], words.i[1]);
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
- node(node) {
1797
- if (node === undefined) {
1798
- this.f.push(-1);
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
- this.f.push(index, node.pos, node.end, node.kind, node.id);
1806
- return true;
1807
- }
1808
- nodes(list) {
1809
- this.f.push(list.length);
1810
- for (const node of list)
1811
- if (!this.node(node))
1812
- return false;
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
- /** A symbol of the program (by the node it is the symbol of, or the export symbol of), or none. */
1816
- symbol(symbol) {
1817
- if (symbol === undefined) {
1818
- this.f.push(-1);
1819
- return true;
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 declaration = symbol.declaration;
1822
- if (declaration === undefined)
1823
- return false;
1824
- const local = getSymbol(declaration);
1825
- if (local === symbol)
1826
- this.f.push(0);
1827
- else if (local !== undefined && local.exportSymbol === symbol)
1828
- this.f.push(1);
1829
- else
1830
- return false;
1831
- return this.node(declaration);
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
- /** Writes `r`; false if a node or symbol in it can't be named across threads. */
1835
- function packRecord(p, r) {
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
- return true;
1861
- }
1862
- /** Reads records from a checker's store. */
1863
- class RecordUnpacker {
1864
- c;
1865
- f;
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
- next() {
1878
- return this.f[this.at++];
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
- double() {
2704
+ doubleAt(p) {
1881
2705
  const words = doubleWords();
1882
- words.i[0] = this.next();
1883
- words.i[1] = this.next();
2706
+ words.i[0] = this.numbers[p];
2707
+ words.i[1] = this.numbers[p + 1];
1884
2708
  return words.f[0];
1885
2709
  }
1886
- text() {
1887
- const start = this.next();
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
- list() {
1892
- const n = this.next();
1893
- const values = [];
1894
- for (let i = 0; i < n; i++)
1895
- values.push(this.next());
1896
- return values;
1897
- }
1898
- node() {
1899
- const fileIndex = this.next();
1900
- if (fileIndex < 0)
1901
- return undefined;
1902
- const pos = this.next();
1903
- const end = this.next();
1904
- const kind = this.next();
1905
- const id = this.next();
1906
- let node = this.nodesById.get(id);
1907
- if (node === undefined) {
1908
- const file = this.c.files[fileIndex];
1909
- node = file === undefined ? undefined : findNode(file, pos, end, kind, id);
1910
- if (node === undefined) {
1911
- this.failed = true;
1912
- return undefined;
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 result
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
- table;
2016
- unpacker;
2017
- /** By record: the record, its translation and whether it is translated. */
2018
- records = [];
2019
- types = [];
2020
- symbols = [];
2021
- built = [];
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.table = sharing.recordTables[producer];
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
- ensure(index) {
2030
- const placeholder = this.sharing.c.errorType;
2031
- while (this.types.length <= index) {
2032
- this.types.push(placeholder);
2033
- this.symbols.push(undefined);
2034
- this.records.push(undefined);
2035
- this.built.push(0);
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
- /** Record `index`, read on first use; undefined if a node or symbol in it isn't in this program. */
2039
- record(index) {
2040
- this.ensure(index);
2041
- const known = this.records[index];
2042
- if (known !== undefined)
2043
- return known;
2044
- const u = this.unpacker;
2045
- u.at = this.table[2 * index] - 1;
2046
- u.failed = false;
2047
- const start = performance.now();
2048
- const record = unpackRecord(u);
2049
- this.sharing.unpackMs += performance.now() - start;
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
- const r = this.record(i);
2068
- if (r === undefined)
2817
+ if (needed.size >= budget) {
2818
+ this.stopped = true;
2069
2819
  return undefined;
2070
- if ((r.mergedId >= 0 && !c.mergedSymbols.has(r.mergedId)) ||
2071
- (r.aliasMergedId >= 0 && !c.mergedSymbols.has(r.aliasMergedId)))
2820
+ }
2821
+ if (!reader.begin(i) || !this.present(reader))
2072
2822
  return undefined;
2073
2823
  needed.add(i);
2074
- for (const j of r.refs)
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.built[i] === 1)
2837
+ if (this.isBuilt(i))
2097
2838
  continue;
2098
- const r = this.records[i];
2099
- if (r.kind === 8 /* RecordKind.NodeType */ ||
2100
- r.kind === 16 /* RecordKind.Conditional */ ||
2101
- r.kind === 21 /* RecordKind.DistributedConditional */)
2102
- translator.aliasOfNode(r.node);
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.built[i] === 1)
2855
+ if (this.isBuilt(i))
2110
2856
  continue;
2111
- const r = this.records[i];
2112
- if (r.kind === 24 /* RecordKind.Property */)
2113
- this.symbols[i] = translator.property(r, this.types, this.symbols);
2114
- else if (r.kind === 23 /* RecordKind.CompleteObject */)
2115
- translator.complete(r, this.types, this.symbols);
2116
- else
2117
- this.types[i] = translator.record(r, this.types);
2118
- if (translator.failed) {
2119
- translator.failed = false;
2120
- return undefined;
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[root];
2903
+ return this.types.get(root);
2127
2904
  }
2128
2905
  }