@scriptc/compiler 0.2.8 → 0.2.10

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 (275) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +1 -0
  2. package/dist/backend/cycle-analysis.d.ts +56 -0
  3. package/dist/backend/cycle-analysis.js +222 -28
  4. package/dist/backend/cycle-analysis.js.map +1 -1
  5. package/dist/backend/cycle-analysis.test.js +232 -15
  6. package/dist/backend/cycle-analysis.test.js.map +1 -1
  7. package/dist/backend/cycle-mutability.d.ts +27 -6
  8. package/dist/backend/cycle-mutability.js +110 -17
  9. package/dist/backend/cycle-mutability.js.map +1 -1
  10. package/dist/backend/immutability.d.ts +57 -0
  11. package/dist/backend/immutability.js +178 -0
  12. package/dist/backend/immutability.js.map +1 -0
  13. package/dist/backend/immutability.test.d.ts +1 -0
  14. package/dist/backend/immutability.test.js +131 -0
  15. package/dist/backend/immutability.test.js.map +1 -0
  16. package/dist/backend/llvm/alloc.d.ts +13 -3
  17. package/dist/backend/llvm/alloc.js +24 -10
  18. package/dist/backend/llvm/alloc.js.map +1 -1
  19. package/dist/backend/llvm/alloc.test.js +5 -5
  20. package/dist/backend/llvm/alloc.test.js.map +1 -1
  21. package/dist/backend/llvm/borrowed-inputs.d.ts +9 -2
  22. package/dist/backend/llvm/borrowed-inputs.js +31 -3
  23. package/dist/backend/llvm/borrowed-inputs.js.map +1 -1
  24. package/dist/backend/llvm/byte-numbers.js +2 -3
  25. package/dist/backend/llvm/byte-numbers.js.map +1 -1
  26. package/dist/backend/llvm/classes.d.ts +28 -0
  27. package/dist/backend/llvm/classes.js +51 -10
  28. package/dist/backend/llvm/classes.js.map +1 -1
  29. package/dist/backend/llvm/classes.test.js +129 -1
  30. package/dist/backend/llvm/classes.test.js.map +1 -1
  31. package/dist/backend/llvm/common.d.ts +30 -0
  32. package/dist/backend/llvm/common.js +41 -0
  33. package/dist/backend/llvm/common.js.map +1 -1
  34. package/dist/backend/llvm/dense-array-access.js +6 -5
  35. package/dist/backend/llvm/dense-array-access.js.map +1 -1
  36. package/dist/backend/llvm/dense-array-access.test.js +10 -2
  37. package/dist/backend/llvm/dense-array-access.test.js.map +1 -1
  38. package/dist/backend/llvm/dyn.d.ts +6 -4
  39. package/dist/backend/llvm/dyn.js +30 -14
  40. package/dist/backend/llvm/dyn.js.map +1 -1
  41. package/dist/backend/llvm/emitter.d.ts +109 -5
  42. package/dist/backend/llvm/emitter.js +716 -112
  43. package/dist/backend/llvm/emitter.js.map +1 -1
  44. package/dist/backend/llvm/emitter.test.js +54 -3
  45. package/dist/backend/llvm/emitter.test.js.map +1 -1
  46. package/dist/backend/llvm/expr-bytes.js +35 -28
  47. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  48. package/dist/backend/llvm/expr-calls.js +14 -6
  49. package/dist/backend/llvm/expr-calls.js.map +1 -1
  50. package/dist/backend/llvm/expr-containers.js +31 -8
  51. package/dist/backend/llvm/expr-containers.js.map +1 -1
  52. package/dist/backend/llvm/expr-context.d.ts +2 -0
  53. package/dist/backend/llvm/expr-control.js +46 -5
  54. package/dist/backend/llvm/expr-control.js.map +1 -1
  55. package/dist/backend/llvm/expr-dynamic.js +3 -0
  56. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  57. package/dist/backend/llvm/expr-primitives.js +30 -3
  58. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  59. package/dist/backend/llvm/expr-primitives.test.js +53 -0
  60. package/dist/backend/llvm/expr-primitives.test.js.map +1 -1
  61. package/dist/backend/llvm/expr-stream-bridges.d.ts +3 -0
  62. package/dist/backend/llvm/expr-stream-bridges.js +76 -14
  63. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  64. package/dist/backend/llvm/lib-filesystem.js +18 -2
  65. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  66. package/dist/backend/llvm/lib-shared.js +1 -0
  67. package/dist/backend/llvm/lib-shared.js.map +1 -1
  68. package/dist/backend/llvm/local-array-reads.d.ts +17 -6
  69. package/dist/backend/llvm/local-array-reads.js +157 -45
  70. package/dist/backend/llvm/local-array-reads.js.map +1 -1
  71. package/dist/backend/llvm/loop-array-borrows.d.ts +5 -2
  72. package/dist/backend/llvm/loop-array-borrows.js +27 -8
  73. package/dist/backend/llvm/loop-array-borrows.js.map +1 -1
  74. package/dist/backend/llvm/map-number-lookup.d.ts +7 -0
  75. package/dist/backend/llvm/map-number-lookup.js +18 -1
  76. package/dist/backend/llvm/map-number-lookup.js.map +1 -1
  77. package/dist/backend/llvm/map-read-lifetimes.d.ts +5 -0
  78. package/dist/backend/llvm/map-read-lifetimes.js +14 -4
  79. package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
  80. package/dist/backend/llvm/map-read-lifetimes.test.js +5 -0
  81. package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
  82. package/dist/backend/llvm/reference-effects.js +34 -1
  83. package/dist/backend/llvm/reference-effects.js.map +1 -1
  84. package/dist/backend/llvm/reference-writes.d.ts +124 -0
  85. package/dist/backend/llvm/reference-writes.js +1090 -0
  86. package/dist/backend/llvm/reference-writes.js.map +1 -0
  87. package/dist/backend/llvm/shapes.d.ts +22 -8
  88. package/dist/backend/llvm/shapes.js +79 -23
  89. package/dist/backend/llvm/shapes.js.map +1 -1
  90. package/dist/backend/llvm/shapes.test.js +5 -5
  91. package/dist/backend/llvm/shapes.test.js.map +1 -1
  92. package/dist/backend/llvm/string-lifetimes.test.js +39 -10
  93. package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
  94. package/dist/backend/llvm/string-reads.d.ts +3 -0
  95. package/dist/backend/llvm/string-reads.js +45 -11
  96. package/dist/backend/llvm/string-reads.js.map +1 -1
  97. package/dist/backend/llvm/string-slices.js +1 -1
  98. package/dist/backend/llvm/switch-dispatch.d.ts +2 -2
  99. package/dist/backend/llvm/switch-dispatch.js +35 -22
  100. package/dist/backend/llvm/switch-dispatch.js.map +1 -1
  101. package/dist/backend/llvm/switch-dispatch.test.js +28 -10
  102. package/dist/backend/llvm/switch-dispatch.test.js.map +1 -1
  103. package/dist/backend/llvm/trunc.d.ts +8 -0
  104. package/dist/backend/llvm/trunc.js +28 -0
  105. package/dist/backend/llvm/trunc.js.map +1 -0
  106. package/dist/backend/llvm/walk-borrows.d.ts +8 -1
  107. package/dist/backend/llvm/walk-borrows.js +15 -4
  108. package/dist/backend/llvm/walk-borrows.js.map +1 -1
  109. package/dist/backend/loop-polls.d.ts +14 -0
  110. package/dist/backend/loop-polls.js +100 -0
  111. package/dist/backend/loop-polls.js.map +1 -0
  112. package/dist/backend/loop-polls.test.d.ts +1 -0
  113. package/dist/backend/loop-polls.test.js +86 -0
  114. package/dist/backend/loop-polls.test.js.map +1 -0
  115. package/dist/backend/may-throw.d.ts +36 -2
  116. package/dist/backend/may-throw.js +328 -13
  117. package/dist/backend/may-throw.js.map +1 -1
  118. package/dist/backend/may-throw.test.js +169 -1
  119. package/dist/backend/may-throw.test.js.map +1 -1
  120. package/dist/backend/native-codegen.d.ts +1 -3
  121. package/dist/backend/native-codegen.js +4 -32
  122. package/dist/backend/native-codegen.js.map +1 -1
  123. package/dist/backend/publication.d.ts +5 -0
  124. package/dist/backend/publication.js +10 -0
  125. package/dist/backend/publication.js.map +1 -1
  126. package/dist/backend/runtime-abi.d.ts +2 -2
  127. package/dist/backend/runtime-abi.js +2 -2
  128. package/dist/backend/runtime-abi.js.map +1 -1
  129. package/dist/backend/runtime-pack-targets.test.js +1 -1
  130. package/dist/backend/runtime-pack-targets.test.js.map +1 -1
  131. package/dist/backend/runtime-pack.test.js +1 -1
  132. package/dist/backend/runtime-pack.test.js.map +1 -1
  133. package/dist/cli/command.js +8 -1
  134. package/dist/cli/command.js.map +1 -1
  135. package/dist/cli/output-options.d.ts +1 -0
  136. package/dist/cli/output-options.js +3 -0
  137. package/dist/cli/output-options.js.map +1 -1
  138. package/dist/cli/usage.d.ts +5 -1
  139. package/dist/cli/usage.js +6 -0
  140. package/dist/cli/usage.js.map +1 -1
  141. package/dist/compile-types.d.ts +5 -0
  142. package/dist/contracts/check.d.ts +8 -0
  143. package/dist/contracts/check.js +1577 -0
  144. package/dist/contracts/check.js.map +1 -0
  145. package/dist/executable/executable-cache.d.ts +2 -0
  146. package/dist/executable/executable-cache.js +3 -0
  147. package/dist/executable/executable-cache.js.map +1 -1
  148. package/dist/executable/executable-cache.test.js +1 -0
  149. package/dist/executable/executable-cache.test.js.map +1 -1
  150. package/dist/executable/prepare.js +9 -0
  151. package/dist/executable/prepare.js.map +1 -1
  152. package/dist/frontend/contract-types.d.ts +50 -0
  153. package/dist/frontend/contract-types.js +232 -0
  154. package/dist/frontend/contract-types.js.map +1 -0
  155. package/dist/frontend/lowering/array-values.js +37 -1
  156. package/dist/frontend/lowering/array-values.js.map +1 -1
  157. package/dist/frontend/lowering/builtins/strings.js +4 -1
  158. package/dist/frontend/lowering/builtins/strings.js.map +1 -1
  159. package/dist/frontend/lowering/call-arguments.js +96 -4
  160. package/dist/frontend/lowering/call-arguments.js.map +1 -1
  161. package/dist/frontend/lowering/class-method-mutations.js +88 -0
  162. package/dist/frontend/lowering/class-method-mutations.js.map +1 -1
  163. package/dist/frontend/lowering/coercions/unions.d.ts +12 -0
  164. package/dist/frontend/lowering/coercions/unions.js +63 -0
  165. package/dist/frontend/lowering/coercions/unions.js.map +1 -1
  166. package/dist/frontend/lowering/containers/bytes.js +15 -4
  167. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  168. package/dist/frontend/lowering/expressions/optional-chains.js +4 -1
  169. package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
  170. package/dist/frontend/lowering/expressions/union-fields.d.ts +19 -0
  171. package/dist/frontend/lowering/expressions/union-fields.js +236 -0
  172. package/dist/frontend/lowering/expressions/union-fields.js.map +1 -0
  173. package/dist/frontend/lowering/lower-calls.js +21 -4
  174. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  175. package/dist/frontend/lowering/lower-exprs.d.ts +6 -0
  176. package/dist/frontend/lowering/lower-exprs.js +245 -4
  177. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  178. package/dist/frontend/lowering/lower-island.js +5 -19
  179. package/dist/frontend/lowering/lower-island.js.map +1 -1
  180. package/dist/frontend/lowering/lower-stmts.js +11 -1
  181. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  182. package/dist/frontend/lowering/lowerer.d.ts +14 -0
  183. package/dist/frontend/lowering/lowerer.js +221 -8
  184. package/dist/frontend/lowering/lowerer.js.map +1 -1
  185. package/dist/frontend/lowering/nan-coded-reads.d.ts +161 -0
  186. package/dist/frontend/lowering/nan-coded-reads.js +1342 -0
  187. package/dist/frontend/lowering/nan-coded-reads.js.map +1 -0
  188. package/dist/frontend/lowering/narrowing-stability.d.ts +100 -0
  189. package/dist/frontend/lowering/narrowing-stability.js +1174 -0
  190. package/dist/frontend/lowering/narrowing-stability.js.map +1 -0
  191. package/dist/frontend/lowering/narrowing-stability.test.d.ts +1 -0
  192. package/dist/frontend/lowering/narrowing-stability.test.js +150 -0
  193. package/dist/frontend/lowering/narrowing-stability.test.js.map +1 -0
  194. package/dist/frontend/lowering/runtime-optional-bounds.d.ts +14 -2
  195. package/dist/frontend/lowering/runtime-optional-bounds.js +17 -11
  196. package/dist/frontend/lowering/runtime-optional-bounds.js.map +1 -1
  197. package/dist/frontend/lowering/runtime-optional-bounds.test.js +28 -0
  198. package/dist/frontend/lowering/runtime-optional-bounds.test.js.map +1 -1
  199. package/dist/frontend/lowering/surfaces.js +8 -0
  200. package/dist/frontend/lowering/surfaces.js.map +1 -1
  201. package/dist/frontend/program.js +1 -1
  202. package/dist/frontend/type-mapper.js +43 -0
  203. package/dist/frontend/type-mapper.js.map +1 -1
  204. package/dist/index.d.ts +1 -1
  205. package/dist/index.js +20 -8
  206. package/dist/index.js.map +1 -1
  207. package/dist/ir/builtin-signatures.d.ts +1 -1
  208. package/dist/ir/builtin-signatures.js +2 -0
  209. package/dist/ir/builtin-signatures.js.map +1 -1
  210. package/dist/ir/constant-globals.d.ts +14 -0
  211. package/dist/ir/constant-globals.js +81 -0
  212. package/dist/ir/constant-globals.js.map +1 -0
  213. package/dist/ir/constant-globals.test.d.ts +1 -0
  214. package/dist/ir/constant-globals.test.js +97 -0
  215. package/dist/ir/constant-globals.test.js.map +1 -0
  216. package/dist/ir/final-fields.d.ts +35 -0
  217. package/dist/ir/final-fields.js +187 -0
  218. package/dist/ir/final-fields.js.map +1 -0
  219. package/dist/ir/final-fields.test.d.ts +1 -0
  220. package/dist/ir/final-fields.test.js +106 -0
  221. package/dist/ir/final-fields.test.js.map +1 -0
  222. package/dist/ir/int32-slots.d.ts +35 -0
  223. package/dist/ir/int32-slots.js +217 -104
  224. package/dist/ir/int32-slots.js.map +1 -1
  225. package/dist/ir/int32-slots.test.js +27 -0
  226. package/dist/ir/int32-slots.test.js.map +1 -1
  227. package/dist/ir/integer-loops.d.ts +5 -0
  228. package/dist/ir/integer-loops.js +23 -4
  229. package/dist/ir/integer-loops.js.map +1 -1
  230. package/dist/ir/integer-loops.test.js +43 -1
  231. package/dist/ir/integer-loops.test.js.map +1 -1
  232. package/dist/ir/integer-ranges.d.ts +20 -0
  233. package/dist/ir/integer-ranges.js +178 -21
  234. package/dist/ir/integer-ranges.js.map +1 -1
  235. package/dist/ir/integer-ranges.test.js +148 -3
  236. package/dist/ir/integer-ranges.test.js.map +1 -1
  237. package/dist/ir/ir.d.ts +38 -1
  238. package/dist/ir/ir.js +4 -0
  239. package/dist/ir/ir.js.map +1 -1
  240. package/dist/ir/serialize.d.ts +17 -3
  241. package/dist/ir/serialize.js +198 -39
  242. package/dist/ir/serialize.js.map +1 -1
  243. package/dist/ir/serialize.test.js +65 -1
  244. package/dist/ir/serialize.test.js.map +1 -1
  245. package/dist/ir/unshare.d.ts +14 -0
  246. package/dist/ir/unshare.js +60 -0
  247. package/dist/ir/unshare.js.map +1 -0
  248. package/dist/ir/unshare.test.d.ts +1 -0
  249. package/dist/ir/unshare.test.js +115 -0
  250. package/dist/ir/unshare.test.js.map +1 -0
  251. package/dist/library/prepare.js +9 -0
  252. package/dist/library/prepare.js.map +1 -1
  253. package/dist/native/cache.d.ts +8 -1
  254. package/dist/native/cache.js +38 -1
  255. package/dist/native/cache.js.map +1 -1
  256. package/dist/native/cache.test.js +11 -0
  257. package/dist/native/cache.test.js.map +1 -1
  258. package/dist/native/compiler.js +4 -3
  259. package/dist/native/compiler.js.map +1 -1
  260. package/dist/native/prepare.d.ts +3 -1
  261. package/dist/native/prepare.js +63 -28
  262. package/dist/native/prepare.js.map +1 -1
  263. package/dist/native/prepare.test.js +23 -1
  264. package/dist/native/prepare.test.js.map +1 -1
  265. package/dist/native/text-file.d.ts +10 -0
  266. package/dist/native/text-file.js +41 -0
  267. package/dist/native/text-file.js.map +1 -0
  268. package/dist/text-parts.d.ts +5 -0
  269. package/dist/text-parts.js +36 -0
  270. package/dist/text-parts.js.map +1 -0
  271. package/package.json +20 -20
  272. package/surface-manifest.json +7 -1
  273. package/dist/frontend/lowering/expressions/union-field-write.d.ts +0 -8
  274. package/dist/frontend/lowering/expressions/union-field-write.js +0 -108
  275. package/dist/frontend/lowering/expressions/union-field-write.js.map +0 -1
@@ -0,0 +1,1174 @@
1
+ import * as ts from "../ts7/adapter.js";
2
+ import { InternalCompilerError } from "../../errors.js";
3
+ import { typeEquals } from "../../ir/ir.js";
4
+ /** Whether TypeScript's control-flow narrowing of a mutable reference still
5
+ * holds when the narrowed read executes.
6
+ *
7
+ * TypeScript narrows property references (`o.p`, `this.a.b`) and bindings
8
+ * after assignments and tests, and keeps that narrowing across calls: a
9
+ * callback, method or getter that clears or replaces the property (or a
10
+ * closure that reassigns the binding) leaves the reference typed by its old
11
+ * value. Lowering bridges a narrowed union read with an UNCHECKED payload
12
+ * extraction and folds `?.`, truthiness and `typeof` by the narrowed type,
13
+ * so such a read would interpret the wrong arm. This analysis decides when
14
+ * the narrowing is provably current:
15
+ *
16
+ * - every property in the reference is construction-only (no store to its
17
+ * name anywhere in the program except `this.name = ...` directly in a
18
+ * constructor body, no accessor of that name, no reflective or computed
19
+ * store into class instances) and the root binding is never assigned
20
+ * from another function; or
21
+ * - nothing between the earliest occurrence of the same reference in the
22
+ * use's function and the use can run other code or store the property
23
+ * through another reference: no call or construction (except standard
24
+ * library operations that cannot call back), tagged template, `await`,
25
+ * `yield`, user accessor, user iteration or user conversion hook, and no
26
+ * store to a property of the same name through a different reference.
27
+ * A loop around the use that starts inside that span contributes its
28
+ * whole body, since its back edge reaches the use again.
29
+ *
30
+ * TypeScript never carries a property narrowing into a nested function,
31
+ * and every narrowing (an assignment, test, predicate or assertion of the
32
+ * reference) is an occurrence of the reference, so a narrowed use always
33
+ * has one earlier in its own function. Spurious occurrences only move the
34
+ * span start earlier, which keeps the answer conservative. */
35
+ export class NarrowingStability {
36
+ lowerer;
37
+ files = null;
38
+ programFacts = null;
39
+ facts = new Map();
40
+ verdicts = new Map();
41
+ effects = new Map();
42
+ capturedMutable = new Map();
43
+ identifierWrites = new Map();
44
+ widened = new Map();
45
+ plainContainers = new Map();
46
+ constructor(lowerer) {
47
+ this.lowerer = lowerer;
48
+ }
49
+ setFiles(files) {
50
+ this.files = files;
51
+ this.programFacts = null;
52
+ }
53
+ /** The declared type a narrowed read in an absence or type test (or in a
54
+ * slot that accepts that declared type) must observe instead of the
55
+ * checker's stale narrowing, or null to keep the checker's type. */
56
+ widenedType(node) {
57
+ if (!ts.isPropertyAccessExpression(node) && !ts.isIdentifier(node))
58
+ return null;
59
+ // Program-level queries before the program's files are split concern
60
+ // declarations, never narrowed reads in function bodies.
61
+ if (this.files === null)
62
+ return null;
63
+ const position = widePosition(node);
64
+ if (position === null)
65
+ return null;
66
+ const cached = this.widened.get(node);
67
+ if (cached !== undefined)
68
+ return cached;
69
+ let result = null;
70
+ try {
71
+ result = this.computeWidened(node, position);
72
+ }
73
+ finally {
74
+ this.widened.set(node, result);
75
+ }
76
+ return result;
77
+ }
78
+ /** The checker narrows this reference below its declared type, and the
79
+ * narrowing may be stale at runtime: its read must check the stored
80
+ * value instead of trusting the narrowed type. */
81
+ untrusted(node, narrowed) {
82
+ // Explicit assertions (`x!`, `x as T`) are the program's own claim and
83
+ // keep their established stance; only flow narrowing is in question.
84
+ if (this.files === null)
85
+ return false;
86
+ if (ts.isPropertyAccessExpression(node)) {
87
+ const declared = this.declaredPropertyType(node);
88
+ if (declared === null)
89
+ return false;
90
+ const declaredIr = this.lowerer.mapTypeOf(declared);
91
+ if (declaredIr === null || typeEquals(declaredIr, narrowed))
92
+ return false;
93
+ return !this.trusted(node);
94
+ }
95
+ if (ts.isIdentifier(node)) {
96
+ const symbol = this.bindingOf(node);
97
+ if (!symbol || !this.isCapturedMutable(symbol))
98
+ return false;
99
+ return !this.trusted(node);
100
+ }
101
+ return false;
102
+ }
103
+ bindingOf(node) {
104
+ const symbol = this.lowerer.resolveValueSymbol(node);
105
+ if (symbol && symbol.flags & ts.SymbolFlags.Alias)
106
+ return this.lowerer.checker.getAliasedSymbol(symbol);
107
+ return symbol;
108
+ }
109
+ computeWidened(node, position) {
110
+ const checker = this.lowerer.checker;
111
+ let declared;
112
+ if (ts.isPropertyAccessExpression(node)) {
113
+ declared = this.declaredPropertyType(node);
114
+ }
115
+ else {
116
+ const symbol = this.bindingOf(node);
117
+ declared = symbol && this.isCapturedMutable(symbol) ? checker.getTypeOfSymbol(symbol) : null;
118
+ }
119
+ if (declared === null)
120
+ return null;
121
+ const narrowedTs = checker.getTypeAtLocation(node);
122
+ if (narrowedTs === declared)
123
+ return null;
124
+ const declaredIr = this.lowerer.mapTypeOf(declared);
125
+ const narrowedIr = this.lowerer.mapTypeOf(narrowedTs);
126
+ if (declaredIr === null || narrowedIr === null || typeEquals(declaredIr, narrowedIr))
127
+ return null;
128
+ // A union narrowed to some of its arms, or a class narrowed to a
129
+ // subclass by instanceof (whose members dispatch on the instance).
130
+ const classNarrowing = declaredIr.kind === "object" && narrowedIr.kind === "object";
131
+ if (declaredIr.kind !== "union" && !classNarrowing)
132
+ return null;
133
+ if (position === "member") {
134
+ // A member the declared type itself supports (every arm has it, so
135
+ // no arm is nullish) reads the current value's member, as Node does.
136
+ // Calls dispatch only through a single class.
137
+ const member = memberUse(node);
138
+ if (member === null || (member.call && !classNarrowing))
139
+ return null;
140
+ if (!checker.getPropertyOfType(declared, member.access.name.text))
141
+ return null;
142
+ }
143
+ else if (position === "chain") {
144
+ if (classNarrowing)
145
+ return null;
146
+ // A chain body is lowered for the checker's present receiver type:
147
+ // only a narrowing that removed nothing but null/undefined can be
148
+ // restored to the guarded declared receiver.
149
+ const present = this.lowerer.mapTypeOf(checker.getNonNullableType(declared));
150
+ if (present === null || !typeEquals(present, narrowedIr))
151
+ return null;
152
+ }
153
+ else if (position === "context") {
154
+ if (!this.argumentSlotIsGeneric(node))
155
+ return null;
156
+ const contextual = checker.getContextualType(node);
157
+ if (contextual === undefined)
158
+ return null;
159
+ if (!(contextual.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) &&
160
+ !checker.isTypeAssignableTo(declared, contextual)) {
161
+ // A slot that takes the declared type minus null/undefined after an
162
+ // absence test (`if (acc !== null) return acc`): the checker's
163
+ // narrowing can also hold an arm an assignment chose before the
164
+ // call that stored another one. Restore every present arm, the way
165
+ // a binding narrowed only by the test reads.
166
+ if (classNarrowing)
167
+ return null;
168
+ const present = checker.getNonNullableType(declared);
169
+ if (present === declared ||
170
+ !checker.isTypeAssignableTo(narrowedTs, present) ||
171
+ !checker.isTypeAssignableTo(present, contextual))
172
+ return null;
173
+ return this.trusted(node) ? null : present;
174
+ }
175
+ }
176
+ return this.trusted(node) ? null : declared;
177
+ }
178
+ /** A read in a call or construction argument (directly or inside a literal
179
+ * argument) reaches the callee's own lowering with the declared type. User
180
+ * callees coerce any value of their parameter type, and so do the
181
+ * standard library's generic slots: a parameter typed by a type parameter
182
+ * (`push(...items: T[])`, `Map.set(key: K, value: V)`) and the console
183
+ * methods, which format any value. Other standard-library lowerings are
184
+ * specialized to the argument types programs pass and can refuse the
185
+ * declared union (`JSON.stringify` of a possibly-undefined value,
186
+ * `clearInterval` of a nullable handle). Those arguments keep the checker's
187
+ * narrowing, and maybeNarrow reads them through the checked extraction. */
188
+ argumentSlotIsGeneric(node) {
189
+ const slot = enclosingArgument(node);
190
+ if (slot === null)
191
+ return true;
192
+ const { call, argument } = slot;
193
+ const checker = this.lowerer.checker;
194
+ const signature = checker.getResolvedSignature(call);
195
+ const declaration = signature ? checker.signatureDeclaration(signature) : undefined;
196
+ if (!declaration || !this.lowerer.isStdlibFile(declaration.getSourceFile()))
197
+ return true;
198
+ const callee = skipOuter(call.expression);
199
+ if (ts.isPropertyAccessExpression(callee) &&
200
+ this.lowerer.isStdlibGlobal(skipOuter(callee.expression), "console"))
201
+ return true;
202
+ const index = call.arguments?.indexOf(argument) ?? -1;
203
+ const params = declaration.parameters ?? [];
204
+ if (index < 0 || params.length === 0)
205
+ return false;
206
+ const param = params[Math.min(index, params.length - 1)];
207
+ if (index >= params.length && param.dotDotDotToken === undefined)
208
+ return false;
209
+ return param.type !== undefined && this.mentionsTypeParameter(param.type);
210
+ }
211
+ mentionsTypeParameter(node) {
212
+ if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName)) {
213
+ const symbol = this.lowerer.checker.getSymbolAtLocation(node.typeName);
214
+ if (symbol && symbol.flags & ts.SymbolFlags.TypeParameter)
215
+ return true;
216
+ }
217
+ return (ts.forEachChild(node, (child) => this.mentionsTypeParameter(child) || undefined) ?? false);
218
+ }
219
+ /** The type of the accessed property as declared on the receiver's
220
+ * (instantiated) type, before any flow narrowing of this reference. */
221
+ declaredPropertyType(node) {
222
+ const checker = this.lowerer.checker;
223
+ const receiver = checker.getTypeAtLocation(node.expression);
224
+ const property = checker.getPropertyOfType(receiver, node.name.text) ?? checker.getSymbolAtLocation(node.name);
225
+ if (!property)
226
+ return null;
227
+ return checker.getTypeOfSymbol(property);
228
+ }
229
+ trusted(node) {
230
+ const cached = this.verdicts.get(node);
231
+ if (cached !== undefined)
232
+ return cached;
233
+ const verdict = this.computeTrusted(node);
234
+ this.verdicts.set(node, verdict);
235
+ return verdict;
236
+ }
237
+ computeTrusted(node) {
238
+ const chain = referenceChain(node);
239
+ if (chain === null)
240
+ return true;
241
+ const program = this.program();
242
+ // A getter answers a fresh value on every read: neither its own result
243
+ // nor anything read through it keeps a narrowing.
244
+ for (const link of chain.links) {
245
+ if (!program.accessors.has(link.name))
246
+ continue;
247
+ const symbol = this.lowerer.checker.getSymbolAtLocation(ts.isPropertyAccessExpression(link.node) ? link.node.name : link.node);
248
+ if (!symbol ||
249
+ (symbol.flags & (ts.SymbolFlags.GetAccessor | ts.SymbolFlags.SetAccessor)) !== 0)
250
+ return false;
251
+ }
252
+ const rootStable = chain.root.kind === ts.SyntaxKind.ThisKeyword ||
253
+ (ts.isIdentifier(chain.root) && !this.rootCapturedMutable(chain.root));
254
+ if (rootStable &&
255
+ chain.links.length > 0 &&
256
+ chain.links.every((link) => this.constructionOnly(link.name)))
257
+ return true;
258
+ if (chain.links.length === 0 && rootStable)
259
+ return true;
260
+ return this.currentSinceNarrowing(node, chain);
261
+ }
262
+ /** The root binding can change between the narrowing and the use, or
263
+ * names a namespace whose exported bindings are assigned as variables
264
+ * (invisible to the member-store census). */
265
+ rootCapturedMutable(root) {
266
+ const symbol = this.bindingOf(root);
267
+ if (symbol === null)
268
+ return true;
269
+ if (symbol.flags & (ts.SymbolFlags.Module | ts.SymbolFlags.Enum))
270
+ return true;
271
+ return this.isCapturedMutable(symbol);
272
+ }
273
+ /** No intervening code between the reference's earliest occurrence in
274
+ * the use's function and the use can change it. */
275
+ currentSinceNarrowing(node, chain) {
276
+ const boundary = flowBoundary(node);
277
+ const facts = this.functionFacts(boundary);
278
+ const last = chain.links[chain.links.length - 1];
279
+ const refKey = last ? `.${last.name}` : chain.root.text;
280
+ const candidates = facts.refs.get(refKey) ?? [];
281
+ const useStart = node.getStart();
282
+ let start = -1;
283
+ for (const candidate of candidates) {
284
+ if (candidate === node)
285
+ continue;
286
+ const candidateStart = candidate.getStart();
287
+ if (candidateStart >= useStart)
288
+ break;
289
+ if (referenceKey(candidate) !== chain.key)
290
+ continue;
291
+ start = candidateStart;
292
+ // An assignment narrows once it completes: code its right side runs
293
+ // happens before the stored value exists.
294
+ const parent = candidate.parent;
295
+ if (ts.isBinaryExpression(parent) &&
296
+ parent.left === candidate &&
297
+ parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
298
+ parent.end <= useStart)
299
+ start = parent.end;
300
+ break;
301
+ }
302
+ if (start < 0)
303
+ return false;
304
+ // A loop around the use that starts after the narrowing reaches the
305
+ // use again through its back edge: its whole body (and header) runs in
306
+ // between.
307
+ let end = useStart;
308
+ for (const loop of facts.loops) {
309
+ const loopStart = loop.getStart();
310
+ if (loopStart >= start && loopStart <= useStart && loop.end >= node.end)
311
+ end = Math.max(end, loop.end);
312
+ }
313
+ // Effects and stores are ordered by where they take effect (a call
314
+ // after its arguments, a store after its right side). Code enclosing
315
+ // the use takes effect after it unless a loop brings the use around.
316
+ for (const { node: effect } of between(facts.effects, start, end)) {
317
+ if (this.isEffect(effect))
318
+ return false;
319
+ }
320
+ if (chain.links.length === 0)
321
+ return true;
322
+ const names = new Set(chain.links.map((link) => link.name));
323
+ for (const { node: write } of between(facts.writes, start, end)) {
324
+ const name = accessName(write);
325
+ if (name === null) {
326
+ if (!this.isPlainContainerWrite(write))
327
+ return false;
328
+ continue;
329
+ }
330
+ if (!names.has(name))
331
+ continue;
332
+ const key = referenceKey(write);
333
+ // A store through the same reference (or a prefix of it) is a flow
334
+ // assignment the checker already accounts for.
335
+ if (key !== null && (key === chain.key || chain.key.startsWith(`${key}.`)))
336
+ continue;
337
+ return false;
338
+ }
339
+ return true;
340
+ }
341
+ isPlainContainerWrite(write) {
342
+ if (!ts.isElementAccessExpression(write))
343
+ return false;
344
+ const cached = this.plainContainers.get(write);
345
+ if (cached !== undefined)
346
+ return cached;
347
+ const checker = this.lowerer.checker;
348
+ const type = checker.getTypeAtLocation(write.expression);
349
+ const plain = isPlainContainerType(checker, type);
350
+ this.plainContainers.set(write, plain);
351
+ return plain;
352
+ }
353
+ constructionOnly(name) {
354
+ const program = this.program();
355
+ return !program.wildcard && !program.mutable.has(name) && !program.accessors.has(name);
356
+ }
357
+ /** A `let`/`var`/parameter binding assigned from a function other than
358
+ * the one that declares it: any call may change it. */
359
+ isCapturedMutable(symbol) {
360
+ const cached = this.capturedMutable.get(symbol);
361
+ if (cached !== undefined)
362
+ return cached;
363
+ let result = false;
364
+ try {
365
+ const decl = this.lowerer.checker.valueDeclarationOf(symbol);
366
+ const mutableBinding = decl !== undefined &&
367
+ ((ts.isVariableDeclaration(decl) &&
368
+ ts.isVariableDeclarationList(decl.parent) &&
369
+ (decl.parent.flags & ts.NodeFlags.Const) === 0) ||
370
+ ts.isParameter(decl));
371
+ if (decl && mutableBinding) {
372
+ const home = flowBoundary(decl);
373
+ const writes = this.writesIn(decl.getSourceFile()).get(symbol.name) ?? [];
374
+ for (const write of writes) {
375
+ if (flowBoundary(write) === home)
376
+ continue;
377
+ if (this.lowerer.resolveValueSymbol(write) === symbol) {
378
+ result = true;
379
+ break;
380
+ }
381
+ }
382
+ }
383
+ }
384
+ finally {
385
+ this.capturedMutable.set(symbol, result);
386
+ }
387
+ return result;
388
+ }
389
+ writesIn(file) {
390
+ let writes = this.identifierWrites.get(file);
391
+ if (writes)
392
+ return writes;
393
+ const found = new Map();
394
+ writes = found;
395
+ this.identifierWrites.set(file, found);
396
+ const add = (target) => {
397
+ forEachWriteTarget(target, (t) => {
398
+ if (!ts.isIdentifier(t))
399
+ return;
400
+ let list = found.get(t.text);
401
+ if (!list)
402
+ found.set(t.text, (list = []));
403
+ list.push(t);
404
+ });
405
+ };
406
+ ts.walkPreorder(file, (node) => {
407
+ const target = writeTargetOf(node);
408
+ if (target)
409
+ add(target);
410
+ });
411
+ return writes;
412
+ }
413
+ program() {
414
+ if (this.programFacts)
415
+ return this.programFacts;
416
+ if (this.files === null) {
417
+ throw new InternalCompilerError("narrowing stability queried before the program files");
418
+ }
419
+ const facts = {
420
+ mutable: new Set(),
421
+ accessors: new Set(),
422
+ wildcard: this.lowerer.dynamic,
423
+ conversion: false,
424
+ iteration: false,
425
+ };
426
+ this.programFacts = facts;
427
+ for (const file of this.files) {
428
+ if (file.isDeclarationFile)
429
+ continue;
430
+ ts.walkPreorder(file, (node) => {
431
+ if (ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node)) {
432
+ const name = memberName(node.name);
433
+ if (name !== null)
434
+ facts.accessors.add(name);
435
+ else
436
+ facts.wildcard = true;
437
+ }
438
+ if (ts.isMethodDeclaration(node) ||
439
+ ts.isPropertyDeclaration(node) ||
440
+ ts.isPropertyAssignment(node) ||
441
+ ts.isGetAccessorDeclaration(node)) {
442
+ const name = memberName(node.name);
443
+ if (name === "toString" || name === "valueOf" || name === "toJSON")
444
+ facts.conversion = true;
445
+ if (ts.isComputedPropertyName(node.name)) {
446
+ const text = node.name.expression.getText();
447
+ if (text.includes("toPrimitive"))
448
+ facts.conversion = true;
449
+ if (text.includes("iterator") || text.includes("asyncIterator"))
450
+ facts.iteration = true;
451
+ }
452
+ }
453
+ if ((ts.isFunctionDeclaration(node) ||
454
+ ts.isFunctionExpression(node) ||
455
+ ts.isMethodDeclaration(node)) &&
456
+ node.asteriskToken) {
457
+ facts.iteration = true;
458
+ }
459
+ if (ts.isCallExpression(node) && reflectiveStore(node))
460
+ facts.wildcard = true;
461
+ const target = writeTargetOf(node);
462
+ if (target) {
463
+ forEachWriteTarget(target, (t) => {
464
+ if (ts.isIdentifier(t))
465
+ return;
466
+ const name = accessName(t);
467
+ if (name === null) {
468
+ if (!this.isPlainContainerWrite(t))
469
+ facts.wildcard = true;
470
+ return;
471
+ }
472
+ if (isConstructorThisStore(t))
473
+ return;
474
+ facts.mutable.add(name);
475
+ });
476
+ }
477
+ });
478
+ }
479
+ return facts;
480
+ }
481
+ functionFacts(boundary) {
482
+ let facts = this.facts.get(boundary);
483
+ if (facts)
484
+ return facts;
485
+ const program = this.program();
486
+ const found = { effects: [], writes: [], loops: [], refs: new Map() };
487
+ facts = found;
488
+ this.facts.set(boundary, found);
489
+ const addRef = (key, node) => {
490
+ let list = found.refs.get(key);
491
+ if (!list)
492
+ found.refs.set(key, (list = []));
493
+ list.push(node);
494
+ };
495
+ const effect = (node, at = node.end) => {
496
+ found.effects.push({ node, at });
497
+ };
498
+ ts.walkPreorder(boundary, (node) => {
499
+ if (node !== boundary && isFlowBoundary(node))
500
+ return "skip";
501
+ if (ts.isClassLikeDeclaration(node) && node !== boundary) {
502
+ effect(node);
503
+ return "skip";
504
+ }
505
+ switch (node.kind) {
506
+ case ts.SyntaxKind.CallExpression:
507
+ case ts.SyntaxKind.NewExpression:
508
+ case ts.SyntaxKind.TaggedTemplateExpression:
509
+ case ts.SyntaxKind.Decorator:
510
+ case ts.SyntaxKind.AwaitExpression:
511
+ case ts.SyntaxKind.YieldExpression:
512
+ case ts.SyntaxKind.SpreadElement:
513
+ effect(node);
514
+ break;
515
+ case ts.SyntaxKind.ForOfStatement:
516
+ // The iterator steps between the iterable and the body.
517
+ effect(node, node.expression.end);
518
+ found.loops.push(node);
519
+ break;
520
+ case ts.SyntaxKind.ForStatement:
521
+ case ts.SyntaxKind.ForInStatement:
522
+ case ts.SyntaxKind.WhileStatement:
523
+ case ts.SyntaxKind.DoStatement:
524
+ found.loops.push(node);
525
+ break;
526
+ case ts.SyntaxKind.ArrayBindingPattern:
527
+ case ts.SyntaxKind.ArrayLiteralExpression:
528
+ if (program.iteration)
529
+ effect(node, node.parent?.end ?? node.end);
530
+ break;
531
+ case ts.SyntaxKind.TemplateSpan:
532
+ if (program.conversion)
533
+ effect(node, node.expression.end);
534
+ break;
535
+ case ts.SyntaxKind.BinaryExpression:
536
+ if (program.conversion)
537
+ effect(node);
538
+ break;
539
+ }
540
+ if (ts.isPropertyAccessExpression(node)) {
541
+ addRef(`.${node.name.text}`, node);
542
+ if (program.accessors.has(node.name.text))
543
+ effect(node);
544
+ }
545
+ else if (ts.isElementAccessExpression(node)) {
546
+ const name = accessName(node);
547
+ if (name !== null) {
548
+ addRef(`.${name}`, node);
549
+ if (program.accessors.has(name))
550
+ effect(node);
551
+ }
552
+ }
553
+ else if (ts.isIdentifier(node) && isValueIdentifier(node)) {
554
+ addRef(node.text, node);
555
+ }
556
+ const target = writeTargetOf(node);
557
+ if (target) {
558
+ forEachWriteTarget(target, (t) => {
559
+ if (ts.isPropertyAccessExpression(t) || ts.isElementAccessExpression(t))
560
+ found.writes.push({ node: t, at: node.end });
561
+ });
562
+ }
563
+ return undefined;
564
+ });
565
+ found.effects.sort((a, b) => a.at - b.at);
566
+ found.writes.sort((a, b) => a.at - b.at);
567
+ return facts;
568
+ }
569
+ /** Whether evaluating this candidate can run code other than standard
570
+ * library operations that cannot call back into the program. */
571
+ isEffect(node) {
572
+ const cached = this.effects.get(node);
573
+ if (cached !== undefined)
574
+ return cached;
575
+ let result = true;
576
+ try {
577
+ result = this.computeEffect(node);
578
+ }
579
+ finally {
580
+ this.effects.set(node, result);
581
+ }
582
+ return result;
583
+ }
584
+ computeEffect(node) {
585
+ const checker = this.lowerer.checker;
586
+ if (ts.isCallExpression(node))
587
+ return !this.isInertLibraryCall(node);
588
+ if (ts.isNewExpression(node))
589
+ return !this.isInertConstruction(node);
590
+ if (ts.isForOfStatement(node)) {
591
+ if (node.awaitModifier)
592
+ return true;
593
+ return this.program().iteration && !this.isBuiltinIterable(node.expression);
594
+ }
595
+ if (ts.isSpreadElement(node))
596
+ return !this.isBuiltinIterable(node.expression);
597
+ if (ts.isArrayBindingPattern(node)) {
598
+ const owner = node.parent;
599
+ return !(ts.isVariableDeclaration(owner) &&
600
+ owner.initializer !== undefined &&
601
+ this.isBuiltinIterable(owner.initializer));
602
+ }
603
+ if (ts.isArrayLiteralExpression(node)) {
604
+ const parent = node.parent;
605
+ if (!ts.isBinaryExpression(parent) ||
606
+ parent.left !== node ||
607
+ parent.operatorToken.kind !== ts.SyntaxKind.EqualsToken)
608
+ return false;
609
+ return !this.isBuiltinIterable(parent.right);
610
+ }
611
+ if (ts.isTemplateSpan(node))
612
+ return !this.isPrimitive(node.expression);
613
+ if (ts.isBinaryExpression(node)) {
614
+ if (!CONVERTING_OPERATORS.has(node.operatorToken.kind))
615
+ return false;
616
+ return !this.isPrimitive(node.left) || !this.isPrimitive(node.right);
617
+ }
618
+ if (ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node)) {
619
+ const symbol = checker.getSymbolAtLocation(ts.isPropertyAccessExpression(node) ? node.name : node.argumentExpression);
620
+ if (!symbol)
621
+ return true;
622
+ return ((symbol.flags & (ts.SymbolFlags.GetAccessor | ts.SymbolFlags.SetAccessor)) !== 0 &&
623
+ !this.lowerer.isStdlibSymbol(symbol));
624
+ }
625
+ if (ts.isClassLikeDeclaration(node)) {
626
+ if (node.modifiers?.some((m) => m.kind === ts.SyntaxKind.Decorator))
627
+ return true;
628
+ for (const clause of node.heritageClauses ?? []) {
629
+ for (const heritage of clause.types) {
630
+ const e = heritage.expression;
631
+ if (!ts.isIdentifier(e) && !ts.isPropertyAccessExpression(e))
632
+ return true;
633
+ }
634
+ }
635
+ return node.members.some((member) => ts.isClassStaticBlockDeclaration(member) ||
636
+ (member.name !== undefined && ts.isComputedPropertyName(member.name)) ||
637
+ member.modifiers?.some((m) => m.kind === ts.SyntaxKind.Decorator) === true ||
638
+ (ts.isPropertyDeclaration(member) &&
639
+ member.initializer !== undefined &&
640
+ member.modifiers?.some((m) => m.kind === ts.SyntaxKind.StaticKeyword) === true));
641
+ }
642
+ return true;
643
+ }
644
+ isInertLibraryCall(call) {
645
+ if (call.expression.kind === ts.SyntaxKind.SuperKeyword)
646
+ return false;
647
+ if (call.expression.kind === ts.SyntaxKind.ImportKeyword)
648
+ return false;
649
+ if (!this.inertArguments(call.arguments))
650
+ return false;
651
+ const callee = skipOuter(call.expression);
652
+ if (ts.isIdentifier(callee)) {
653
+ if (!INERT_GLOBAL_FUNCTIONS.has(callee.text))
654
+ return false;
655
+ if (!this.lowerer.isStdlibGlobal(callee, callee.text))
656
+ return false;
657
+ return call.arguments.every((arg) => this.isPrimitive(arg));
658
+ }
659
+ if (!ts.isPropertyAccessExpression(callee))
660
+ return false;
661
+ const method = callee.name.text;
662
+ const receiver = skipOuter(callee.expression);
663
+ if (ts.isIdentifier(receiver)) {
664
+ const members = INERT_NAMESPACE_MEMBERS.get(receiver.text);
665
+ if (members !== undefined && this.lowerer.isStdlibGlobal(receiver, receiver.text))
666
+ return members === "*" || members.has(method);
667
+ }
668
+ const checker = this.lowerer.checker;
669
+ const type = checker.getTypeAtLocation(callee.expression);
670
+ if (type.flags & PRIMITIVE_FLAGS)
671
+ return true;
672
+ if (checker.isArrayType(type) || checker.isTupleType(type))
673
+ return !ARRAY_CONVERTING_METHODS.has(method);
674
+ const symbol = type.getSymbol();
675
+ return (symbol !== undefined &&
676
+ BUILTIN_COLLECTIONS.has(symbol.name) &&
677
+ this.lowerer.isStdlibSymbol(symbol));
678
+ }
679
+ isInertConstruction(node) {
680
+ const callee = skipOuter(node.expression);
681
+ if (!ts.isIdentifier(callee) || !INERT_CONSTRUCTORS.has(callee.text))
682
+ return false;
683
+ if (!this.lowerer.isStdlibGlobal(callee, callee.text))
684
+ return false;
685
+ const args = node.arguments ?? [];
686
+ if (!this.inertArguments(args))
687
+ return false;
688
+ return args.every((arg) => this.isPrimitive(arg) || this.isBuiltinIterable(arg));
689
+ }
690
+ /** No argument is a function the callee could invoke. */
691
+ inertArguments(args) {
692
+ const checker = this.lowerer.checker;
693
+ for (const arg of args) {
694
+ const e = skipOuter(arg);
695
+ if (ts.isArrowFunction(e) || ts.isFunctionExpression(e))
696
+ return false;
697
+ if (ts.isSpreadElement(e))
698
+ continue;
699
+ if (ts.isStringLiteral(e) ||
700
+ ts.isNumericLiteral(e) ||
701
+ ts.isNoSubstitutionTemplateLiteral(e) ||
702
+ e.kind === ts.SyntaxKind.TrueKeyword ||
703
+ e.kind === ts.SyntaxKind.FalseKeyword)
704
+ continue;
705
+ const type = checker.getTypeAtLocation(e);
706
+ if (type.flags & PRIMITIVE_FLAGS)
707
+ continue;
708
+ if (type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown))
709
+ return false;
710
+ if (checker.getCallSignatures(type).length > 0)
711
+ return false;
712
+ if (this.program().conversion && !(type.flags & ts.TypeFlags.Object))
713
+ return false;
714
+ }
715
+ return true;
716
+ }
717
+ isPrimitive(node) {
718
+ return (this.lowerer.checker.getTypeAtLocation(node).flags & PRIMITIVE_FLAGS) !== 0;
719
+ }
720
+ isBuiltinIterable(node) {
721
+ const checker = this.lowerer.checker;
722
+ const type = checker.getTypeAtLocation(node);
723
+ if (type.flags & ts.TypeFlags.StringLike)
724
+ return true;
725
+ return checker.isArrayType(type) || checker.isTupleType(type);
726
+ }
727
+ }
728
+ const PRIMITIVE_FLAGS = ts.TypeFlags.StringLike |
729
+ ts.TypeFlags.NumberLike |
730
+ ts.TypeFlags.BooleanLike |
731
+ ts.TypeFlags.BigIntLike |
732
+ ts.TypeFlags.Undefined |
733
+ ts.TypeFlags.Null |
734
+ ts.TypeFlags.Void;
735
+ const CONVERTING_OPERATORS = new Set([
736
+ ts.SyntaxKind.PlusToken,
737
+ ts.SyntaxKind.MinusToken,
738
+ ts.SyntaxKind.AsteriskToken,
739
+ ts.SyntaxKind.SlashToken,
740
+ ts.SyntaxKind.PercentToken,
741
+ ts.SyntaxKind.AsteriskAsteriskToken,
742
+ ts.SyntaxKind.LessThanToken,
743
+ ts.SyntaxKind.GreaterThanToken,
744
+ ts.SyntaxKind.LessThanEqualsToken,
745
+ ts.SyntaxKind.GreaterThanEqualsToken,
746
+ ts.SyntaxKind.EqualsEqualsToken,
747
+ ts.SyntaxKind.ExclamationEqualsToken,
748
+ ts.SyntaxKind.AmpersandToken,
749
+ ts.SyntaxKind.BarToken,
750
+ ts.SyntaxKind.CaretToken,
751
+ ts.SyntaxKind.LessThanLessThanToken,
752
+ ts.SyntaxKind.GreaterThanGreaterThanToken,
753
+ ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken,
754
+ ts.SyntaxKind.PlusEqualsToken,
755
+ ts.SyntaxKind.MinusEqualsToken,
756
+ ts.SyntaxKind.AsteriskEqualsToken,
757
+ ts.SyntaxKind.SlashEqualsToken,
758
+ ts.SyntaxKind.PercentEqualsToken,
759
+ ]);
760
+ /** Global functions that never call back when given primitive arguments. */
761
+ const INERT_GLOBAL_FUNCTIONS = new Set([
762
+ "parseInt",
763
+ "parseFloat",
764
+ "isNaN",
765
+ "isFinite",
766
+ "encodeURIComponent",
767
+ "decodeURIComponent",
768
+ "encodeURI",
769
+ "decodeURI",
770
+ "String",
771
+ "Number",
772
+ "Boolean",
773
+ ]);
774
+ /** Standard namespaces whose listed members never call back into the
775
+ * program when given no function argument. */
776
+ const INERT_NAMESPACE_MEMBERS = new Map([
777
+ ["Math", "*"],
778
+ ["console", "*"],
779
+ [
780
+ "Number",
781
+ new Set(["isInteger", "isFinite", "isNaN", "isSafeInteger", "parseFloat", "parseInt"]),
782
+ ],
783
+ ["String", new Set(["fromCharCode", "fromCodePoint"])],
784
+ [
785
+ "Object",
786
+ new Set(["keys", "values", "entries", "hasOwn", "getPrototypeOf", "is", "getOwnPropertyNames"]),
787
+ ],
788
+ ["Array", new Set(["isArray"])],
789
+ ["Date", new Set(["now"])],
790
+ ]);
791
+ /** Array methods that convert their elements through user hooks. */
792
+ const ARRAY_CONVERTING_METHODS = new Set([
793
+ "join",
794
+ "toString",
795
+ "toLocaleString",
796
+ "sort",
797
+ "toSorted",
798
+ ]);
799
+ const BUILTIN_COLLECTIONS = new Set([
800
+ "Map",
801
+ "Set",
802
+ "WeakMap",
803
+ "WeakSet",
804
+ "ReadonlyMap",
805
+ "ReadonlySet",
806
+ ]);
807
+ const INERT_CONSTRUCTORS = new Set([
808
+ "Map",
809
+ "Set",
810
+ "WeakMap",
811
+ "WeakSet",
812
+ "Array",
813
+ "Error",
814
+ "TypeError",
815
+ "RangeError",
816
+ "SyntaxError",
817
+ "Date",
818
+ "RegExp",
819
+ "Uint8Array",
820
+ "Int8Array",
821
+ "Uint16Array",
822
+ "Int16Array",
823
+ "Uint32Array",
824
+ "Int32Array",
825
+ "Float32Array",
826
+ "Float64Array",
827
+ "BigInt64Array",
828
+ "BigUint64Array",
829
+ ]);
830
+ /** Where a stale narrowed read can keep its declared representation: the
831
+ * receiver of `?.`, an absence/type/truthiness test, or a slot whose
832
+ * contextual type may accept the declared type. */
833
+ function widePosition(node) {
834
+ let child = node;
835
+ let parent = node.parent;
836
+ while (ts.isParenthesizedExpression(parent)) {
837
+ child = parent;
838
+ parent = parent.parent;
839
+ }
840
+ if ((ts.isPropertyAccessExpression(parent) ||
841
+ ts.isElementAccessExpression(parent) ||
842
+ ts.isCallExpression(parent)) &&
843
+ parent.expression === child) {
844
+ if (parent.questionDotToken !== undefined)
845
+ return "chain";
846
+ return memberUse(node) !== null ? "member" : null;
847
+ }
848
+ if (ts.isBinaryExpression(parent)) {
849
+ switch (parent.operatorToken.kind) {
850
+ case ts.SyntaxKind.QuestionQuestionToken:
851
+ case ts.SyntaxKind.BarBarToken:
852
+ case ts.SyntaxKind.AmpersandAmpersandToken:
853
+ case ts.SyntaxKind.InstanceOfKeyword:
854
+ return parent.left === child ? "test" : null;
855
+ case ts.SyntaxKind.EqualsEqualsEqualsToken:
856
+ case ts.SyntaxKind.ExclamationEqualsEqualsToken:
857
+ case ts.SyntaxKind.EqualsEqualsToken:
858
+ case ts.SyntaxKind.ExclamationEqualsToken:
859
+ return "test";
860
+ case ts.SyntaxKind.EqualsToken:
861
+ return parent.right === child ? "context" : null;
862
+ default:
863
+ return null;
864
+ }
865
+ }
866
+ if (ts.isPrefixUnaryExpression(parent))
867
+ return parent.operator === ts.SyntaxKind.ExclamationToken ? "test" : null;
868
+ if (ts.isTypeOfExpression(parent))
869
+ return "test";
870
+ if (ts.isTemplateSpan(parent))
871
+ return "test";
872
+ if ((ts.isIfStatement(parent) || ts.isWhileStatement(parent) || ts.isDoStatement(parent)) &&
873
+ parent.expression === child)
874
+ return "test";
875
+ if (ts.isConditionalExpression(parent) && parent.condition === child)
876
+ return "test";
877
+ if (ts.isForStatement(parent) && parent.condition === child)
878
+ return "test";
879
+ if ((ts.isCallExpression(parent) || ts.isNewExpression(parent)) &&
880
+ parent.arguments?.some((arg) => arg === child))
881
+ return "context";
882
+ // Only slots whose type IS the contextual type: an array or object
883
+ // literal (or an unannotated arrow body) infers its own type from the
884
+ // narrowed element instead, unless the literal itself sits in a typed
885
+ // slot (`const xs: Expr[] = [x]`, `{ value }` passed as a parameter).
886
+ if (ts.isReturnStatement(parent))
887
+ return "context";
888
+ if ((ts.isArrayLiteralExpression(parent) ||
889
+ (ts.isPropertyAssignment(parent) && parent.initializer === child) ||
890
+ ts.isShorthandPropertyAssignment(parent)) &&
891
+ typedLiteral(parent))
892
+ return "context";
893
+ if (ts.isVariableDeclaration(parent) && parent.initializer === child && parent.type)
894
+ return "context";
895
+ return null;
896
+ }
897
+ /** The call or construction whose argument holds `node`, directly or as an
898
+ * element or property of literal arguments, with that argument. */
899
+ function enclosingArgument(node) {
900
+ let child = node;
901
+ let parent = node.parent;
902
+ while (ts.isParenthesizedExpression(parent) ||
903
+ ts.isArrayLiteralExpression(parent) ||
904
+ ts.isObjectLiteralExpression(parent) ||
905
+ ts.isShorthandPropertyAssignment(parent) ||
906
+ (ts.isPropertyAssignment(parent) && parent.initializer === child)) {
907
+ child = parent;
908
+ parent = parent.parent;
909
+ }
910
+ if (!(ts.isCallExpression(parent) || ts.isNewExpression(parent)))
911
+ return null;
912
+ const argument = parent.arguments?.find((arg) => arg === child);
913
+ return argument ? { call: parent, argument } : null;
914
+ }
915
+ /** The array or object literal holding `parent` (an element's literal or a
916
+ * property assignment) is itself lowered against a declared slot type. */
917
+ function typedLiteral(parent) {
918
+ let literal = ts.isArrayLiteralExpression(parent) ? parent : parent.parent;
919
+ while (literal) {
920
+ let slot = literal.parent;
921
+ while (slot && ts.isParenthesizedExpression(slot)) {
922
+ literal = slot;
923
+ slot = slot.parent;
924
+ }
925
+ if (!slot)
926
+ return false;
927
+ if (ts.isVariableDeclaration(slot))
928
+ return slot.initializer === literal && !!slot.type;
929
+ if (ts.isCallExpression(slot) || ts.isNewExpression(slot))
930
+ return slot.arguments?.some((arg) => arg === literal) ?? false;
931
+ if (ts.isBinaryExpression(slot))
932
+ return slot.operatorToken.kind === ts.SyntaxKind.EqualsToken && slot.right === literal;
933
+ if (ts.isReturnStatement(slot)) {
934
+ let fn = slot.parent;
935
+ while (fn && !ts.isFunctionLike(fn))
936
+ fn = fn.parent;
937
+ return fn !== undefined && ts.isFunctionLike(fn) && fn.type !== undefined;
938
+ }
939
+ if (ts.isArrayLiteralExpression(slot)) {
940
+ literal = slot;
941
+ continue;
942
+ }
943
+ if (ts.isPropertyAssignment(slot) && slot.initializer === literal) {
944
+ literal = slot.parent;
945
+ continue;
946
+ }
947
+ return false;
948
+ }
949
+ return false;
950
+ }
951
+ /** The plain member read or method call `node` is the receiver of
952
+ * (`node.name`, `node.name(...)`; not a store target or optional step). */
953
+ function memberUse(node) {
954
+ let child = node;
955
+ let parent = node.parent;
956
+ while (ts.isParenthesizedExpression(parent)) {
957
+ child = parent;
958
+ parent = parent.parent;
959
+ }
960
+ if (!ts.isPropertyAccessExpression(parent) || parent.expression !== child)
961
+ return null;
962
+ if (parent.questionDotToken !== undefined)
963
+ return null;
964
+ const outer = parent.parent;
965
+ if (outer && writeTargetOf(outer) === parent)
966
+ return null;
967
+ const call = outer !== undefined && ts.isCallExpression(outer) && outer.expression === parent;
968
+ if (call && outer.questionDotToken !== undefined)
969
+ return null;
970
+ return { access: parent, call };
971
+ }
972
+ /** The function-like body, class member initializer or file whose flow
973
+ * contains `node`. */
974
+ function flowBoundary(node) {
975
+ let n = node.parent;
976
+ while (n && !isFlowBoundary(n))
977
+ n = n.parent;
978
+ return n ?? node.getSourceFile();
979
+ }
980
+ function isFlowBoundary(node) {
981
+ return (ts.isFunctionLike(node) ||
982
+ ts.isClassStaticBlockDeclaration(node) ||
983
+ ts.isPropertyDeclaration(node) ||
984
+ ts.isSourceFile(node));
985
+ }
986
+ function skipOuter(node) {
987
+ let e = node;
988
+ while (ts.isParenthesizedExpression(e) ||
989
+ ts.isNonNullExpression(e) ||
990
+ ts.isAsExpression(e) ||
991
+ ts.isTypeAssertion(e) ||
992
+ ts.isSatisfiesExpression(e)) {
993
+ e = e.expression;
994
+ }
995
+ return e;
996
+ }
997
+ /** A member access's name when it is static (`o.p`, `o["p"]`, `o[0]`). */
998
+ function accessName(node) {
999
+ if (ts.isPropertyAccessExpression(node))
1000
+ return node.name.text;
1001
+ if (ts.isElementAccessExpression(node)) {
1002
+ const arg = node.argumentExpression;
1003
+ if (ts.isStringLiteral(arg) || ts.isNoSubstitutionTemplateLiteral(arg))
1004
+ return arg.text;
1005
+ if (ts.isNumericLiteral(arg))
1006
+ return String(Number(arg.text));
1007
+ }
1008
+ return null;
1009
+ }
1010
+ /** The normalized spelling TypeScript's reference matching would equate:
1011
+ * parentheses, non-null and type assertions erased, `o["p"]` as `o.p`. */
1012
+ function referenceKey(node) {
1013
+ const e = skipOuter(node);
1014
+ if (ts.isIdentifier(e))
1015
+ return e.text;
1016
+ if (e.kind === ts.SyntaxKind.ThisKeyword)
1017
+ return "this";
1018
+ if (ts.isPropertyAccessExpression(e) || ts.isElementAccessExpression(e)) {
1019
+ const name = accessName(e);
1020
+ if (name === null)
1021
+ return null;
1022
+ const receiver = referenceKey(e.expression);
1023
+ return receiver === null ? null : `${receiver}.${name}`;
1024
+ }
1025
+ return null;
1026
+ }
1027
+ function referenceChain(node) {
1028
+ const key = referenceKey(node);
1029
+ if (key === null)
1030
+ return null;
1031
+ const links = [];
1032
+ let e = skipOuter(node);
1033
+ while (ts.isPropertyAccessExpression(e) || ts.isElementAccessExpression(e)) {
1034
+ links.unshift({ name: accessName(e), node: e });
1035
+ e = skipOuter(e.expression);
1036
+ }
1037
+ return { key, root: e, links };
1038
+ }
1039
+ function memberName(name) {
1040
+ if (ts.isIdentifier(name) || ts.isStringLiteral(name))
1041
+ return name.text;
1042
+ if (ts.isNumericLiteral(name))
1043
+ return String(Number(name.text));
1044
+ if (ts.isPrivateIdentifier(name))
1045
+ return name.text;
1046
+ return null;
1047
+ }
1048
+ /** An identifier that reads or declares a binding (not a member name). */
1049
+ function isValueIdentifier(node) {
1050
+ const parent = node.parent;
1051
+ if (ts.isPropertyAccessExpression(parent) && parent.name === node)
1052
+ return false;
1053
+ if ((ts.isPropertyAssignment(parent) ||
1054
+ ts.isPropertyDeclaration(parent) ||
1055
+ ts.isMethodDeclaration(parent) ||
1056
+ ts.isPropertySignature(parent) ||
1057
+ ts.isGetAccessorDeclaration(parent) ||
1058
+ ts.isSetAccessorDeclaration(parent)) &&
1059
+ parent.name === node)
1060
+ return false;
1061
+ return true;
1062
+ }
1063
+ /** The store target expression of an assignment-like node. */
1064
+ function writeTargetOf(node) {
1065
+ if (ts.isBinaryExpression(node)) {
1066
+ const op = node.operatorToken.kind;
1067
+ if (op >= ts.SyntaxKind.FirstAssignment && op <= ts.SyntaxKind.LastAssignment)
1068
+ return node.left;
1069
+ return null;
1070
+ }
1071
+ if ((ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) &&
1072
+ (node.operator === ts.SyntaxKind.PlusPlusToken ||
1073
+ node.operator === ts.SyntaxKind.MinusMinusToken))
1074
+ return node.operand;
1075
+ if (ts.isDeleteExpression(node))
1076
+ return node.expression;
1077
+ if ((ts.isForOfStatement(node) || ts.isForInStatement(node)) &&
1078
+ !ts.isVariableDeclarationList(node.initializer))
1079
+ return node.initializer;
1080
+ return null;
1081
+ }
1082
+ /** Each stored-to reference in a target, through destructuring patterns. */
1083
+ function forEachWriteTarget(target, visit) {
1084
+ const e = skipOuter(target);
1085
+ if (ts.isIdentifier(e) || ts.isPropertyAccessExpression(e) || ts.isElementAccessExpression(e)) {
1086
+ visit(e);
1087
+ return;
1088
+ }
1089
+ if (ts.isArrayLiteralExpression(e)) {
1090
+ for (const element of e.elements) {
1091
+ forEachWriteTarget(ts.isSpreadElement(element) ? element.expression : element, visit);
1092
+ }
1093
+ return;
1094
+ }
1095
+ if (ts.isObjectLiteralExpression(e)) {
1096
+ for (const property of e.properties) {
1097
+ if (ts.isPropertyAssignment(property))
1098
+ forEachWriteTarget(property.initializer, visit);
1099
+ else if (ts.isShorthandPropertyAssignment(property) && ts.isIdentifier(property.name))
1100
+ visit(property.name);
1101
+ else if (ts.isSpreadAssignment(property))
1102
+ forEachWriteTarget(property.expression, visit);
1103
+ }
1104
+ return;
1105
+ }
1106
+ if (ts.isBinaryExpression(e) && e.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
1107
+ // A destructuring default: `[a = 1] = xs`.
1108
+ forEachWriteTarget(e.left, visit);
1109
+ }
1110
+ }
1111
+ /** `this.p = ...` directly in a constructor body, before the object can
1112
+ * be observed by code that narrowed it. */
1113
+ function isConstructorThisStore(target) {
1114
+ if (!ts.isPropertyAccessExpression(target) && !ts.isElementAccessExpression(target))
1115
+ return false;
1116
+ if (skipOuter(target.expression).kind !== ts.SyntaxKind.ThisKeyword)
1117
+ return false;
1118
+ return ts.isConstructorDeclaration(flowBoundary(target));
1119
+ }
1120
+ /** `Object.assign(target, ...)` and similar reflective stores into an
1121
+ * existing object. */
1122
+ function reflectiveStore(call) {
1123
+ const callee = skipOuter(call.expression);
1124
+ if (!ts.isPropertyAccessExpression(callee) || !ts.isIdentifier(callee.expression))
1125
+ return false;
1126
+ const owner = callee.expression.text;
1127
+ const method = callee.name.text;
1128
+ const reflective = (owner === "Object" &&
1129
+ (method === "assign" ||
1130
+ method === "defineProperty" ||
1131
+ method === "defineProperties" ||
1132
+ method === "setPrototypeOf")) ||
1133
+ (owner === "Reflect" &&
1134
+ (method === "set" ||
1135
+ method === "defineProperty" ||
1136
+ method === "deleteProperty" ||
1137
+ method === "setPrototypeOf"));
1138
+ if (!reflective)
1139
+ return false;
1140
+ const target = call.arguments[0];
1141
+ return target === undefined || !ts.isObjectLiteralExpression(skipOuter(target));
1142
+ }
1143
+ function isPlainContainerType(checker, type) {
1144
+ if (type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown))
1145
+ return false;
1146
+ const parts = type.isUnionType() ? ts.constituentTypes(type) : [type];
1147
+ return parts.every((part) => {
1148
+ if (part.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Null))
1149
+ return true;
1150
+ if (checker.isArrayType(part) || checker.isTupleType(part) || checker.isArrayLikeType(part))
1151
+ return true;
1152
+ const symbol = part.getSymbol();
1153
+ if (symbol && symbol.flags & ts.SymbolFlags.Class)
1154
+ return false;
1155
+ return checker.getIndexInfosOfType(part).length > 0;
1156
+ });
1157
+ }
1158
+ /** Entries of an `at`-ordered list taking effect in `(start, end]`. */
1159
+ function between(entries, start, end) {
1160
+ let lo = 0;
1161
+ let hi = entries.length;
1162
+ while (lo < hi) {
1163
+ const mid = (lo + hi) >> 1;
1164
+ if (entries[mid].at <= start)
1165
+ lo = mid + 1;
1166
+ else
1167
+ hi = mid;
1168
+ }
1169
+ const out = [];
1170
+ for (let i = lo; i < entries.length && entries[i].at <= end; i++)
1171
+ out.push(entries[i]);
1172
+ return out;
1173
+ }
1174
+ //# sourceMappingURL=narrowing-stability.js.map