@scriptc/compiler 0.3.4 → 0.3.6

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 (316) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +409 -42
  2. package/dist/backend/llvm/array-search.js +1 -1
  3. package/dist/backend/llvm/array-search.js.map +1 -1
  4. package/dist/backend/llvm/call-lifetimes.d.ts +6 -0
  5. package/dist/backend/llvm/call-lifetimes.js +19 -0
  6. package/dist/backend/llvm/call-lifetimes.js.map +1 -1
  7. package/dist/backend/llvm/constant-callbacks.d.ts +2 -21
  8. package/dist/backend/llvm/constant-callbacks.js +36 -219
  9. package/dist/backend/llvm/constant-callbacks.js.map +1 -1
  10. package/dist/backend/llvm/constant-callbacks.test.js +22 -0
  11. package/dist/backend/llvm/constant-callbacks.test.js.map +1 -1
  12. package/dist/backend/llvm/dense-array-access.d.ts +9 -0
  13. package/dist/backend/llvm/dense-array-access.js +27 -1
  14. package/dist/backend/llvm/dense-array-access.js.map +1 -1
  15. package/dist/backend/llvm/dyn.d.ts +6 -0
  16. package/dist/backend/llvm/dyn.js +71 -7
  17. package/dist/backend/llvm/dyn.js.map +1 -1
  18. package/dist/backend/llvm/emitter.d.ts +120 -2
  19. package/dist/backend/llvm/emitter.js +820 -142
  20. package/dist/backend/llvm/emitter.js.map +1 -1
  21. package/dist/backend/llvm/expr-async.js +17 -7
  22. package/dist/backend/llvm/expr-async.js.map +1 -1
  23. package/dist/backend/llvm/expr-bytes.js +1 -1
  24. package/dist/backend/llvm/expr-callbacks.d.ts +11 -0
  25. package/dist/backend/llvm/expr-callbacks.js +53 -0
  26. package/dist/backend/llvm/expr-callbacks.js.map +1 -1
  27. package/dist/backend/llvm/expr-calls.js +88 -2
  28. package/dist/backend/llvm/expr-calls.js.map +1 -1
  29. package/dist/backend/llvm/expr-containers.js +50 -27
  30. package/dist/backend/llvm/expr-containers.js.map +1 -1
  31. package/dist/backend/llvm/expr-dispatch.js +1 -0
  32. package/dist/backend/llvm/expr-dispatch.js.map +1 -1
  33. package/dist/backend/llvm/expr-dynamic.js +25 -1
  34. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  35. package/dist/backend/llvm/expr-primitives.d.ts +1 -1
  36. package/dist/backend/llvm/expr-primitives.js +20 -1
  37. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  38. package/dist/backend/llvm/expr-stream-bridges.js +4 -0
  39. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  40. package/dist/backend/llvm/integer-clones.d.ts +103 -0
  41. package/dist/backend/llvm/integer-clones.js +1336 -0
  42. package/dist/backend/llvm/integer-clones.js.map +1 -0
  43. package/dist/backend/llvm/integer-clones.test.d.ts +1 -0
  44. package/dist/backend/llvm/integer-clones.test.js +219 -0
  45. package/dist/backend/llvm/integer-clones.test.js.map +1 -0
  46. package/dist/backend/llvm/lib-datagram.js +14 -0
  47. package/dist/backend/llvm/lib-datagram.js.map +1 -1
  48. package/dist/backend/llvm/lib-dispatch.js +32 -1
  49. package/dist/backend/llvm/lib-dispatch.js.map +1 -1
  50. package/dist/backend/llvm/lib-filesystem.js +61 -1
  51. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  52. package/dist/backend/llvm/lib-network.js +21 -3
  53. package/dist/backend/llvm/lib-network.js.map +1 -1
  54. package/dist/backend/llvm/lib-process.js +34 -0
  55. package/dist/backend/llvm/lib-process.js.map +1 -1
  56. package/dist/backend/llvm/lib-shared.js +77 -2
  57. package/dist/backend/llvm/lib-shared.js.map +1 -1
  58. package/dist/backend/llvm/lib-test.js +16 -3
  59. package/dist/backend/llvm/lib-test.js.map +1 -1
  60. package/dist/backend/llvm/lib-tls.js +6 -0
  61. package/dist/backend/llvm/lib-tls.js.map +1 -1
  62. package/dist/backend/llvm/local-array-reads.js +1 -1
  63. package/dist/backend/llvm/local-array-reads.js.map +1 -1
  64. package/dist/backend/llvm/map-entry-fusion.d.ts +28 -0
  65. package/dist/backend/llvm/map-entry-fusion.js +233 -0
  66. package/dist/backend/llvm/map-entry-fusion.js.map +1 -0
  67. package/dist/backend/llvm/map-entry-fusion.test.d.ts +1 -0
  68. package/dist/backend/llvm/map-entry-fusion.test.js +183 -0
  69. package/dist/backend/llvm/map-entry-fusion.test.js.map +1 -0
  70. package/dist/backend/llvm/map-number-lookup.d.ts +2 -0
  71. package/dist/backend/llvm/map-number-lookup.js +1 -1
  72. package/dist/backend/llvm/map-number-lookup.js.map +1 -1
  73. package/dist/backend/llvm/map-number-lookup.test.js +10 -11
  74. package/dist/backend/llvm/map-number-lookup.test.js.map +1 -1
  75. package/dist/backend/llvm/map-read-lifetimes.d.ts +2 -0
  76. package/dist/backend/llvm/map-read-lifetimes.js +3 -0
  77. package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
  78. package/dist/backend/llvm/map-read-lifetimes.test.js +2 -2
  79. package/dist/backend/llvm/nullable-unions.d.ts +6 -3
  80. package/dist/backend/llvm/nullable-unions.js +2 -0
  81. package/dist/backend/llvm/nullable-unions.js.map +1 -1
  82. package/dist/backend/llvm/publish.d.ts +16 -0
  83. package/dist/backend/llvm/publish.js +81 -10
  84. package/dist/backend/llvm/publish.js.map +1 -1
  85. package/dist/backend/llvm/publish.test.d.ts +1 -0
  86. package/dist/backend/llvm/publish.test.js +94 -0
  87. package/dist/backend/llvm/publish.test.js.map +1 -0
  88. package/dist/backend/llvm/record-results.d.ts +1 -1
  89. package/dist/backend/llvm/record-results.js +78 -4
  90. package/dist/backend/llvm/record-results.js.map +1 -1
  91. package/dist/backend/llvm/record-sources.d.ts +7 -0
  92. package/dist/backend/llvm/record-sources.js +14 -0
  93. package/dist/backend/llvm/record-sources.js.map +1 -0
  94. package/dist/backend/llvm/reference-writes.js +1 -0
  95. package/dist/backend/llvm/reference-writes.js.map +1 -1
  96. package/dist/backend/llvm/shapes.d.ts +12 -3
  97. package/dist/backend/llvm/shapes.js +23 -14
  98. package/dist/backend/llvm/shapes.js.map +1 -1
  99. package/dist/backend/llvm/shapes.test.js +5 -3
  100. package/dist/backend/llvm/shapes.test.js.map +1 -1
  101. package/dist/backend/llvm/speculative-integers.d.ts +74 -0
  102. package/dist/backend/llvm/speculative-integers.js +571 -0
  103. package/dist/backend/llvm/speculative-integers.js.map +1 -0
  104. package/dist/backend/llvm/speculative-integers.test.d.ts +1 -0
  105. package/dist/backend/llvm/speculative-integers.test.js +148 -0
  106. package/dist/backend/llvm/speculative-integers.test.js.map +1 -0
  107. package/dist/backend/llvm/string-lifetimes.test.js +75 -1
  108. package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
  109. package/dist/backend/llvm/walkers.d.ts +17 -0
  110. package/dist/backend/llvm/walkers.js +123 -0
  111. package/dist/backend/llvm/walkers.js.map +1 -1
  112. package/dist/backend/may-throw.js +2 -0
  113. package/dist/backend/may-throw.js.map +1 -1
  114. package/dist/backend/native/contracts.d.ts +2 -1
  115. package/dist/backend/native/executable.js +6 -1
  116. package/dist/backend/native/executable.js.map +1 -1
  117. package/dist/backend/native/runtime-inputs.d.ts +1 -1
  118. package/dist/backend/native/runtime-inputs.js +1 -0
  119. package/dist/backend/native/runtime-inputs.js.map +1 -1
  120. package/dist/backend/runtime-abi.d.ts +2 -2
  121. package/dist/backend/runtime-abi.js +2 -2
  122. package/dist/backend/runtime-pack-targets.test.js +1 -1
  123. package/dist/backend/runtime-pack.d.ts +7 -3
  124. package/dist/backend/runtime-pack.js +12 -6
  125. package/dist/backend/runtime-pack.js.map +1 -1
  126. package/dist/backend/runtime-pack.test.js +16 -7
  127. package/dist/backend/runtime-pack.test.js.map +1 -1
  128. package/dist/contracts/check.js +1 -0
  129. package/dist/contracts/check.js.map +1 -1
  130. package/dist/coverage/surface-manifest.js +3 -3
  131. package/dist/coverage/surface-manifest.js.map +1 -1
  132. package/dist/diagnostics/diagnostic.d.ts +1 -1
  133. package/dist/diagnostics/diagnostic.js +6 -2
  134. package/dist/diagnostics/diagnostic.js.map +1 -1
  135. package/dist/frontend/lowering/builtin-calls.js +13 -0
  136. package/dist/frontend/lowering/builtin-calls.js.map +1 -1
  137. package/dist/frontend/lowering/builtins/callable-aliases.js +82 -8
  138. package/dist/frontend/lowering/builtins/callable-aliases.js.map +1 -1
  139. package/dist/frontend/lowering/builtins/child-process-async.d.ts +8 -6
  140. package/dist/frontend/lowering/builtins/child-process-async.js +45 -29
  141. package/dist/frontend/lowering/builtins/child-process-async.js.map +1 -1
  142. package/dist/frontend/lowering/builtins/compression.d.ts +9 -0
  143. package/dist/frontend/lowering/builtins/compression.js +145 -30
  144. package/dist/frontend/lowering/builtins/compression.js.map +1 -1
  145. package/dist/frontend/lowering/builtins/crypto.js +12 -1
  146. package/dist/frontend/lowering/builtins/crypto.js.map +1 -1
  147. package/dist/frontend/lowering/builtins/errors.js +5 -3
  148. package/dist/frontend/lowering/builtins/errors.js.map +1 -1
  149. package/dist/frontend/lowering/builtins/filesystem-calls.d.ts +6 -0
  150. package/dist/frontend/lowering/builtins/filesystem-calls.js +16 -0
  151. package/dist/frontend/lowering/builtins/filesystem-calls.js.map +1 -1
  152. package/dist/frontend/lowering/builtins/filesystem.d.ts +8 -0
  153. package/dist/frontend/lowering/builtins/filesystem.js +199 -2
  154. package/dist/frontend/lowering/builtins/filesystem.js.map +1 -1
  155. package/dist/frontend/lowering/builtins/fs-streams.d.ts +26 -0
  156. package/dist/frontend/lowering/builtins/fs-streams.js +131 -0
  157. package/dist/frontend/lowering/builtins/fs-streams.js.map +1 -0
  158. package/dist/frontend/lowering/builtins/hash-stream.d.ts +12 -0
  159. package/dist/frontend/lowering/builtins/hash-stream.js +80 -0
  160. package/dist/frontend/lowering/builtins/hash-stream.js.map +1 -0
  161. package/dist/frontend/lowering/builtins/module-calls.js +17 -3
  162. package/dist/frontend/lowering/builtins/module-calls.js.map +1 -1
  163. package/dist/frontend/lowering/builtins/process.js +9 -2
  164. package/dist/frontend/lowering/builtins/process.js.map +1 -1
  165. package/dist/frontend/lowering/builtins/readline.d.ts +45 -13
  166. package/dist/frontend/lowering/builtins/readline.js +812 -47
  167. package/dist/frontend/lowering/builtins/readline.js.map +1 -1
  168. package/dist/frontend/lowering/builtins/workers.d.ts +4 -0
  169. package/dist/frontend/lowering/builtins/workers.js +58 -0
  170. package/dist/frontend/lowering/builtins/workers.js.map +1 -1
  171. package/dist/frontend/lowering/class-dynamic-dispatch.js +150 -8
  172. package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
  173. package/dist/frontend/lowering/class-protocols.d.ts +55 -0
  174. package/dist/frontend/lowering/class-protocols.js +735 -0
  175. package/dist/frontend/lowering/class-protocols.js.map +1 -1
  176. package/dist/frontend/lowering/containers/array-from-async.d.ts +18 -0
  177. package/dist/frontend/lowering/containers/array-from-async.js +282 -0
  178. package/dist/frontend/lowering/containers/array-from-async.js.map +1 -0
  179. package/dist/frontend/lowering/containers/bytes.js +33 -12
  180. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  181. package/dist/frontend/lowering/containers/collection-grouping.js +2 -1
  182. package/dist/frontend/lowering/containers/collection-grouping.js.map +1 -1
  183. package/dist/frontend/lowering/containers/collection-methods.js +27 -23
  184. package/dist/frontend/lowering/containers/collection-methods.js.map +1 -1
  185. package/dist/frontend/lowering/devirtualize.js +20 -7
  186. package/dist/frontend/lowering/devirtualize.js.map +1 -1
  187. package/dist/frontend/lowering/divergences.js +188 -15
  188. package/dist/frontend/lowering/divergences.js.map +1 -1
  189. package/dist/frontend/lowering/lower-calls.js +129 -25
  190. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  191. package/dist/frontend/lowering/lower-classes.js +46 -1
  192. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  193. package/dist/frontend/lowering/lower-dgram.js +55 -15
  194. package/dist/frontend/lowering/lower-dgram.js.map +1 -1
  195. package/dist/frontend/lowering/lower-event-emitter.d.ts +9 -1
  196. package/dist/frontend/lowering/lower-event-emitter.js +88 -1
  197. package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
  198. package/dist/frontend/lowering/lower-exprs.d.ts +4 -0
  199. package/dist/frontend/lowering/lower-exprs.js +239 -71
  200. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  201. package/dist/frontend/lowering/lower-inspect.js +49 -3
  202. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  203. package/dist/frontend/lowering/lower-mock.d.ts +11 -1
  204. package/dist/frontend/lowering/lower-mock.js +377 -61
  205. package/dist/frontend/lowering/lower-mock.js.map +1 -1
  206. package/dist/frontend/lowering/lower-modules.js +1 -0
  207. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  208. package/dist/frontend/lowering/lower-server.d.ts +2 -1
  209. package/dist/frontend/lowering/lower-server.js +234 -21
  210. package/dist/frontend/lowering/lower-server.js.map +1 -1
  211. package/dist/frontend/lowering/lower-stmts.js +60 -13
  212. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  213. package/dist/frontend/lowering/lower-stream.d.ts +1 -3
  214. package/dist/frontend/lowering/lower-stream.js +325 -20
  215. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  216. package/dist/frontend/lowering/lower-test.js +147 -48
  217. package/dist/frontend/lowering/lower-test.js.map +1 -1
  218. package/dist/frontend/lowering/lowerer.d.ts +13 -1
  219. package/dist/frontend/lowering/lowerer.js +10 -2
  220. package/dist/frontend/lowering/lowerer.js.map +1 -1
  221. package/dist/frontend/lowering/null-prototype.d.ts +13 -0
  222. package/dist/frontend/lowering/null-prototype.js +33 -0
  223. package/dist/frontend/lowering/null-prototype.js.map +1 -0
  224. package/dist/frontend/lowering/record-sources.d.ts +15 -0
  225. package/dist/frontend/lowering/record-sources.js +46 -0
  226. package/dist/frontend/lowering/record-sources.js.map +1 -0
  227. package/dist/frontend/lowering/surfaces.d.ts +3 -3
  228. package/dist/frontend/lowering/surfaces.js +59 -5
  229. package/dist/frontend/lowering/surfaces.js.map +1 -1
  230. package/dist/frontend/mock-shapes.d.ts +5 -0
  231. package/dist/frontend/mock-shapes.js +27 -2
  232. package/dist/frontend/mock-shapes.js.map +1 -1
  233. package/dist/frontend/program.js +5 -4
  234. package/dist/frontend/program.js.map +1 -1
  235. package/dist/frontend/type-mapper.d.ts +3 -0
  236. package/dist/frontend/type-mapper.js +148 -4
  237. package/dist/frontend/type-mapper.js.map +1 -1
  238. package/dist/index.js +18 -8
  239. package/dist/index.js.map +1 -1
  240. package/dist/ir/analysis.d.ts +10 -0
  241. package/dist/ir/analysis.js +35 -0
  242. package/dist/ir/analysis.js.map +1 -1
  243. package/dist/ir/analysis.test.js +69 -1
  244. package/dist/ir/analysis.test.js.map +1 -1
  245. package/dist/ir/builtin-effects.js +72 -0
  246. package/dist/ir/builtin-effects.js.map +1 -1
  247. package/dist/ir/builtin-signatures.d.ts +1 -1
  248. package/dist/ir/builtin-signatures.js +258 -13
  249. package/dist/ir/builtin-signatures.js.map +1 -1
  250. package/dist/ir/capture-values.d.ts +107 -0
  251. package/dist/ir/capture-values.js +850 -0
  252. package/dist/ir/capture-values.js.map +1 -0
  253. package/dist/ir/capture-values.test.d.ts +1 -0
  254. package/dist/ir/capture-values.test.js +268 -0
  255. package/dist/ir/capture-values.test.js.map +1 -0
  256. package/dist/ir/field-values.d.ts +16 -0
  257. package/dist/ir/field-values.js +128 -0
  258. package/dist/ir/field-values.js.map +1 -0
  259. package/dist/ir/field-values.test.d.ts +1 -0
  260. package/dist/ir/field-values.test.js +160 -0
  261. package/dist/ir/field-values.test.js.map +1 -0
  262. package/dist/ir/final-fields.d.ts +4 -1
  263. package/dist/ir/final-fields.js +11 -3
  264. package/dist/ir/final-fields.js.map +1 -1
  265. package/dist/ir/function-references.test.js +2 -1
  266. package/dist/ir/function-references.test.js.map +1 -1
  267. package/dist/ir/init-only-globals.d.ts +16 -0
  268. package/dist/ir/init-only-globals.js +121 -0
  269. package/dist/ir/init-only-globals.js.map +1 -0
  270. package/dist/ir/init-only-globals.test.d.ts +1 -0
  271. package/dist/ir/init-only-globals.test.js +115 -0
  272. package/dist/ir/init-only-globals.test.js.map +1 -0
  273. package/dist/ir/initializer-bindings.d.ts +18 -1
  274. package/dist/ir/initializer-bindings.js +91 -1
  275. package/dist/ir/initializer-bindings.js.map +1 -1
  276. package/dist/ir/initializer-bindings.test.js +57 -2
  277. package/dist/ir/initializer-bindings.test.js.map +1 -1
  278. package/dist/ir/int32-slots.d.ts +22 -0
  279. package/dist/ir/int32-slots.js +398 -13
  280. package/dist/ir/int32-slots.js.map +1 -1
  281. package/dist/ir/int32-slots.test.js +119 -8
  282. package/dist/ir/int32-slots.test.js.map +1 -1
  283. package/dist/ir/integer-loop-facts.d.ts +27 -1
  284. package/dist/ir/integer-loop-facts.js +239 -7
  285. package/dist/ir/integer-loop-facts.js.map +1 -1
  286. package/dist/ir/integer-ranges.d.ts +8 -5
  287. package/dist/ir/integer-ranges.js +147 -59
  288. package/dist/ir/integer-ranges.js.map +1 -1
  289. package/dist/ir/integer-ranges.test.js +285 -7
  290. package/dist/ir/integer-ranges.test.js.map +1 -1
  291. package/dist/ir/ir.d.ts +22 -1
  292. package/dist/ir/ir.js +4 -0
  293. package/dist/ir/ir.js.map +1 -1
  294. package/dist/ir/property-bags.d.ts +14 -0
  295. package/dist/ir/property-bags.js +121 -0
  296. package/dist/ir/property-bags.js.map +1 -0
  297. package/dist/ir/receiver-readers.d.ts +21 -0
  298. package/dist/ir/receiver-readers.js +217 -0
  299. package/dist/ir/receiver-readers.js.map +1 -0
  300. package/dist/ir/runtime-features.d.ts +3 -1
  301. package/dist/ir/runtime-features.js +102 -7
  302. package/dist/ir/runtime-features.js.map +1 -1
  303. package/dist/ir/runtime-features.test.js +62 -0
  304. package/dist/ir/runtime-features.test.js.map +1 -1
  305. package/dist/ir/traverse.js +4 -0
  306. package/dist/ir/traverse.js.map +1 -1
  307. package/dist/ir/validate.js +71 -3
  308. package/dist/ir/validate.js.map +1 -1
  309. package/dist/ir/validate.test.js +59 -0
  310. package/dist/ir/validate.test.js.map +1 -1
  311. package/dist/library/int-infer.js +3 -0
  312. package/dist/library/int-infer.js.map +1 -1
  313. package/dist/library/ir-policy.js +5 -0
  314. package/dist/library/ir-policy.js.map +1 -1
  315. package/package.json +20 -20
  316. package/surface-manifest.json +57 -19
@@ -8,6 +8,8 @@ export class ClassProtocols {
8
8
  implemented = null;
9
9
  methods = new Map();
10
10
  answers = new Map();
11
+ /** What markIdentityFlows learned about the program, while it runs. */
12
+ facts = null;
11
13
  constructor(checker, sources) {
12
14
  this.checker = checker;
13
15
  this.sources = sources;
@@ -65,8 +67,643 @@ export class ClassProtocols {
65
67
  for (const source of this.sources)
66
68
  if (!source.isDeclarationFile)
67
69
  visit(source);
70
+ this.markIdentityFlows(protocols);
68
71
  return protocols;
69
72
  }
73
+ /** Structural types that receive a value with identity: a program class
74
+ * instance, or a value of a type already marked here. Natively such a
75
+ * type would be a record, and the conversion into it a field copy, so a
76
+ * write or identity check through the record would miss the original
77
+ * (`function bump(o: { n: number }) { o.n++ }` called with a class
78
+ * instance). Marking keeps every value of the type a live, checked view.
79
+ * Flows are scanned syntactically, and only sites whose source carries
80
+ * identity query their target type.
81
+ *
82
+ * A copy differs from the original only when something observes the
83
+ * difference, so a type is marked only when the program can: values of
84
+ * the type are written through, compared by identity (`===`, `switch`,
85
+ * assert), used as Set/Map keys or array search needles, given to
86
+ * whole-object observers (console, JSON, Object, util, structuredClone,
87
+ * spreads, for-in, templates, `unknown` parameters) or tested with
88
+ * instanceof; or a field the type
89
+ * exposes is written on the source class after construction, so a copy
90
+ * could read a stale value. A view of readonly data that nothing
91
+ * observes stays a native record. */
92
+ markIdentityFlows(protocols) {
93
+ const sites = [];
94
+ /** Expressions whose values are observed as whole objects. */
95
+ const observed = [];
96
+ /** Receivers whose type arguments or elements are observed (Set and Map
97
+ * keys, array search haystacks). */
98
+ const observedContainers = [];
99
+ /** Arguments observed when their parameter type accepts any value
100
+ * (`unknown`, `any`, `object`, `{}`, an unresolved type parameter) or
101
+ * the callee is a generic function or class that observes its type
102
+ * parameter. */
103
+ const looseArguments = [];
104
+ /** The call each loose argument belongs to. */
105
+ const looseCalls = [];
106
+ /** Member writes: the receiver and the written name (null: any name). */
107
+ const writes = [];
108
+ /** Annotated callback parameters: the caller's argument (the contextual
109
+ * signature's parameter) flows into the declared parameter type. */
110
+ const callbackParams = [];
111
+ const contextual = (expr) => () => this.checker.getContextualType(expr);
112
+ const flow = (expr) => {
113
+ if (expr !== undefined)
114
+ sites.push({ source: expr, target: contextual(expr) });
115
+ };
116
+ const write = (target) => {
117
+ while (ts.isParenthesizedExpression(target) || ts.isNonNullExpression(target))
118
+ target = target.expression;
119
+ if (ts.isPropertyAccessExpression(target)) {
120
+ // `this.f = v` in a constructor initializes the instance before any
121
+ // view of it can exist.
122
+ if (target.expression.kind !== ts.SyntaxKind.ThisKeyword || !inConstructor(target))
123
+ writes.push({ receiver: target.expression, name: target.name.text });
124
+ }
125
+ else if (ts.isElementAccessExpression(target)) {
126
+ const key = target.argumentExpression;
127
+ writes.push({
128
+ receiver: target.expression,
129
+ name: ts.isStringLiteralLike(key) || ts.isNumericLiteral(key) ? key.text : null,
130
+ });
131
+ }
132
+ };
133
+ const observe = (node) => {
134
+ if (ts.isBinaryExpression(node)) {
135
+ const op = node.operatorToken.kind;
136
+ if ((op === ts.SyntaxKind.EqualsEqualsEqualsToken ||
137
+ op === ts.SyntaxKind.ExclamationEqualsEqualsToken ||
138
+ op === ts.SyntaxKind.EqualsEqualsToken ||
139
+ op === ts.SyntaxKind.ExclamationEqualsToken) &&
140
+ !isNullishLiteral(node.left) &&
141
+ !isNullishLiteral(node.right) &&
142
+ !isPrimitiveLiteral(node.left) &&
143
+ !isPrimitiveLiteral(node.right))
144
+ observed.push(node.left, node.right);
145
+ else if (op === ts.SyntaxKind.InstanceOfKeyword)
146
+ observed.push(node.left);
147
+ else if (op === ts.SyntaxKind.InKeyword)
148
+ observed.push(node.right);
149
+ else if (op >= ts.SyntaxKind.FirstAssignment && op <= ts.SyntaxKind.LastAssignment)
150
+ write(node.left);
151
+ }
152
+ else if ((ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) &&
153
+ (node.operator === ts.SyntaxKind.PlusPlusToken ||
154
+ node.operator === ts.SyntaxKind.MinusMinusToken)) {
155
+ write(node.operand);
156
+ }
157
+ else if (ts.isDeleteExpression(node)) {
158
+ write(node.expression);
159
+ }
160
+ else if (ts.isSpreadAssignment(node) || ts.isTemplateSpan(node)) {
161
+ observed.push(node.expression);
162
+ }
163
+ else if (ts.isForInStatement(node)) {
164
+ observed.push(node.expression);
165
+ }
166
+ else if (ts.isSwitchStatement(node)) {
167
+ // `switch` matches its cases with ===.
168
+ const cases = node.caseBlock.clauses
169
+ .filter(ts.isCaseClause)
170
+ .map((clause) => clause.expression)
171
+ .filter((expr) => !isNullishLiteral(expr) && !isPrimitiveLiteral(expr));
172
+ if (cases.length > 0)
173
+ observed.push(node.expression, ...cases);
174
+ }
175
+ else if (ts.isCallExpression(node) || ts.isNewExpression(node)) {
176
+ const callee = node.expression;
177
+ const args = node.arguments ?? [];
178
+ const member = ts.isPropertyAccessExpression(callee) ? callee.name.text : null;
179
+ const owner = ts.isPropertyAccessExpression(callee) && ts.isIdentifier(callee.expression)
180
+ ? callee.expression.text
181
+ : null;
182
+ if ((owner !== null && OBSERVER_NAMESPACES.has(owner)) ||
183
+ (ts.isIdentifier(callee) &&
184
+ (OBSERVER_FUNCTIONS.has(callee.text) ||
185
+ (ts.isNewExpression(node) && IDENTITY_CONSTRUCTORS.has(callee.text))))) {
186
+ observed.push(...args.filter((arg) => !ts.isSpreadElement(arg)));
187
+ // Object.assign(target, ...) and Object.defineProperty write the target.
188
+ if (owner === "Object" && args[0] !== undefined && OBJECT_WRITERS.has(member ?? ""))
189
+ writes.push({ receiver: args[0], name: null });
190
+ }
191
+ else if (member !== null && IDENTITY_METHODS.has(member)) {
192
+ observed.push(...args.filter((arg) => !ts.isSpreadElement(arg)));
193
+ observedContainers.push(callee.expression);
194
+ }
195
+ else {
196
+ for (const arg of args) {
197
+ if (ts.isSpreadElement(arg))
198
+ continue;
199
+ looseArguments.push(arg);
200
+ looseCalls.push(node);
201
+ }
202
+ }
203
+ }
204
+ };
205
+ const visit = (node) => {
206
+ observe(node);
207
+ if (ts.isCallExpression(node) || ts.isNewExpression(node))
208
+ node.arguments?.forEach(flow);
209
+ else if (ts.isVariableDeclaration(node) && node.type !== undefined)
210
+ flow(node.initializer);
211
+ else if (ts.isPropertyDeclaration(node) && node.type !== undefined)
212
+ flow(node.initializer);
213
+ else if (ts.isParameter(node) && node.type !== undefined)
214
+ flow(node.initializer);
215
+ else if (ts.isReturnStatement(node))
216
+ flow(node.expression);
217
+ else if (ts.isArrowFunction(node) && !ts.isBlock(node.body))
218
+ flow(node.body);
219
+ else if (ts.isYieldExpression(node))
220
+ flow(node.expression);
221
+ else if (ts.isArrayLiteralExpression(node))
222
+ node.elements.forEach((element) => {
223
+ if (!ts.isSpreadElement(element) && !ts.isOmittedExpression(element))
224
+ flow(element);
225
+ });
226
+ else if (ts.isPropertyAssignment(node))
227
+ flow(node.initializer);
228
+ else if (ts.isShorthandPropertyAssignment(node) && ts.isIdentifier(node.name))
229
+ flow(node.name);
230
+ else if (ts.isBinaryExpression(node) &&
231
+ node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
232
+ !ts.isObjectLiteralExpression(node.left) &&
233
+ !ts.isArrayLiteralExpression(node.left)) {
234
+ const left = node.left;
235
+ sites.push({ source: node.right, target: () => this.checker.getTypeAtLocation(left) });
236
+ }
237
+ else if ((ts.isAsExpression(node) || ts.isTypeAssertion(node)) && !accessedInPlace(node)) {
238
+ const typeNode = node.type;
239
+ sites.push({
240
+ source: node.expression,
241
+ target: () => this.checker.getTypeFromTypeNode(typeNode),
242
+ });
243
+ }
244
+ if (ts.isArrowFunction(node) || ts.isFunctionExpression(node))
245
+ node.parameters.forEach((param, index) => {
246
+ if (param.type !== undefined)
247
+ callbackParams.push({ fn: node, index, name: param.name });
248
+ });
249
+ ts.forEachChild(node, visit);
250
+ };
251
+ for (const source of this.sources)
252
+ if (!source.isDeclarationFile)
253
+ visit(source);
254
+ if (sites.length === 0 && callbackParams.length === 0)
255
+ return;
256
+ // One batched checker round trip for every node the scan reads: flow
257
+ // sites sit in bodies whose reachability is not known yet, so the
258
+ // facade has not prefetched them.
259
+ const sourceNodes = sites.map((site) => this.flowSourceNode(site.source));
260
+ const observedNodes = observed.map((expr) => this.flowSourceNode(expr));
261
+ const containerNodes = observedContainers.map((expr) => this.flowSourceNode(expr));
262
+ const looseNodes = looseArguments.map((expr) => this.flowSourceNode(expr));
263
+ const writeNodes = writes.map((w) => this.flowSourceNode(w.receiver));
264
+ attempt(() => this.checker.prefetchClassCollection([
265
+ ...sourceNodes,
266
+ ...callbackParams.map((param) => param.name),
267
+ ...observedNodes,
268
+ ...containerNodes,
269
+ ...looseNodes,
270
+ ...writeNodes,
271
+ ], []));
272
+ const marked = protocols;
273
+ const facts = {
274
+ observed: new Set(),
275
+ written: new Map(),
276
+ loose: [],
277
+ bases: new Map(),
278
+ origins: new Map(),
279
+ targets: new Map(),
280
+ flows: new Map(),
281
+ genericObservers: new Set(),
282
+ };
283
+ const typeOf = (node) => attempt(() => this.checker.getTypeAtLocation(node));
284
+ for (const node of [...observedNodes, ...containerNodes]) {
285
+ const type = typeOf(node);
286
+ if (type === undefined)
287
+ continue;
288
+ this.collectSymbols(type, facts.observed, 0);
289
+ // A type parameter observed in a generic function or class: calls of
290
+ // it observe whatever their arguments hold.
291
+ const parts = type.isUnionType() ? ts.constituentTypes(type) : [type];
292
+ for (const part of parts) {
293
+ if ((part.flags & ts.TypeFlags.TypeParameter) === 0)
294
+ continue;
295
+ const symbol = part.getSymbol();
296
+ for (const declaration of symbol ? this.checker.declarationsOf(symbol) : [])
297
+ if (ts.isTypeParameterDeclaration(declaration) && declaration.parent !== undefined)
298
+ facts.genericObservers.add(declaration.parent);
299
+ }
300
+ }
301
+ for (let i = 0; i < writes.length; i++) {
302
+ const symbols = new Set();
303
+ // `this` has the polymorphic this type: the write lands on the
304
+ // enclosing class's instance.
305
+ const owner = writeNodes[i].kind === ts.SyntaxKind.ThisKeyword ? thisClass(writeNodes[i]) : undefined;
306
+ const ownerSymbol = owner?.name ? this.checker.getSymbolAtLocation(owner.name) : undefined;
307
+ if (ownerSymbol !== undefined)
308
+ symbols.add(ownerSymbol);
309
+ else {
310
+ const type = typeOf(writeNodes[i]);
311
+ if (type === undefined)
312
+ continue;
313
+ this.collectSymbols(type, symbols, 2);
314
+ }
315
+ const name = writes[i].name;
316
+ for (const symbol of symbols) {
317
+ const names = facts.written.get(symbol);
318
+ if (name === null)
319
+ facts.written.set(symbol, "all");
320
+ else if (names === undefined)
321
+ facts.written.set(symbol, new Set([name]));
322
+ else if (names !== "all")
323
+ names.add(name);
324
+ }
325
+ }
326
+ for (let i = 0; i < looseArguments.length; i++) {
327
+ const type = typeOf(looseNodes[i]);
328
+ if (type === undefined)
329
+ continue;
330
+ const symbols = new Set();
331
+ this.collectSymbols(type, symbols, 0);
332
+ if (symbols.size > 0)
333
+ facts.loose.push({ node: looseArguments[i], call: looseCalls[i], symbols });
334
+ }
335
+ this.facts = facts;
336
+ const pending = [];
337
+ for (let i = 0; i < sites.length; i++) {
338
+ const site = sites[i];
339
+ const source = sourceNodes[i];
340
+ const sourceType = attempt(() => this.checker.getTypeAtLocation(source));
341
+ if (sourceType !== undefined)
342
+ pending.push({ sourceType, target: site.target });
343
+ }
344
+ for (const { fn, index, name } of callbackParams) {
345
+ attempt(() => {
346
+ // Only structural declared types can lose identity; the contextual
347
+ // signature is a checker round trip, so query it for those alone.
348
+ const declared = this.checker.getTypeAtLocation(name);
349
+ const parts = declared.isUnionType() ? ts.constituentTypes(declared) : [declared];
350
+ if (!parts.some((part) => this.isStructuralTarget(part)))
351
+ return;
352
+ const context = this.checker.getContextualType(fn);
353
+ if (context === undefined)
354
+ return;
355
+ for (const signature of this.checker.getCallSignatures(context)) {
356
+ const parameter = signature.getParameters()[index];
357
+ if (parameter === undefined)
358
+ continue;
359
+ pending.push({
360
+ sourceType: this.checker.getTypeOfSymbol(parameter),
361
+ target: () => declared,
362
+ });
363
+ }
364
+ });
365
+ }
366
+ // Every site is revisited until nothing new is marked, so each mark's
367
+ // flows and origins are complete.
368
+ const before = new Set(marked);
369
+ const targets = new Map();
370
+ let changed = true;
371
+ while (changed) {
372
+ changed = false;
373
+ for (let i = pending.length - 1; i >= 0; i--) {
374
+ const entry = pending[i];
375
+ if (attempt(() => this.carriesIdentity(entry.sourceType, marked)) !== true)
376
+ continue;
377
+ if (!targets.has(i))
378
+ targets.set(i, attempt(entry.target));
379
+ const target = targets.get(i);
380
+ if (target !== undefined &&
381
+ attempt(() => this.markTarget(entry.sourceType, target, marked)) === true)
382
+ changed = true;
383
+ }
384
+ }
385
+ // Keep the marks a copy would be told apart from, and the marks whose
386
+ // values flow into a kept view or another live protocol.
387
+ const added = [...marked].filter((symbol) => !before.has(symbol));
388
+ const kept = new Set(added.filter((symbol) => this.needsView(symbol, marked, facts)));
389
+ let grew = true;
390
+ while (grew) {
391
+ grew = false;
392
+ for (const symbol of added) {
393
+ if (kept.has(symbol))
394
+ continue;
395
+ for (const into of facts.flows.get(symbol) ?? NO_SYMBOLS) {
396
+ if (kept.has(into) || before.has(into)) {
397
+ kept.add(symbol);
398
+ grew = true;
399
+ break;
400
+ }
401
+ }
402
+ }
403
+ }
404
+ for (const symbol of added)
405
+ if (!kept.has(symbol))
406
+ marked.delete(symbol);
407
+ this.facts = null;
408
+ }
409
+ /** The symbols of `type`'s object parts: union and intersection members,
410
+ * and to `depth` levels the elements and type arguments they hold. */
411
+ collectSymbols(type, out, depth) {
412
+ if (type.isUnionType() || type.isIntersectionType()) {
413
+ for (const part of ts.constituentTypes(type))
414
+ this.collectSymbols(part, out, depth);
415
+ return;
416
+ }
417
+ if ((type.flags & ts.TypeFlags.Object) === 0)
418
+ return;
419
+ const symbol = type.getSymbol();
420
+ if (symbol !== undefined)
421
+ out.add(symbol);
422
+ if (depth >= 2 || !type.isTypeReference())
423
+ return;
424
+ for (const argument of this.checker.getTypeArguments(type))
425
+ this.collectSymbols(argument, out, depth + 1);
426
+ }
427
+ /** Program classes whose instances can be values of `source`: a class,
428
+ * its bases and the classes extending it, or the classes behind a marked
429
+ * structural type. */
430
+ originsOf(source, facts) {
431
+ const out = new Set();
432
+ const add = (type) => {
433
+ if (type.isUnionType() || type.isIntersectionType()) {
434
+ for (const part of ts.constituentTypes(type))
435
+ add(part);
436
+ return;
437
+ }
438
+ const symbol = type.getSymbol();
439
+ if (symbol === undefined)
440
+ return;
441
+ if (this.isProgramClassInstance(type)) {
442
+ for (const related of this.classFamily(symbol, facts))
443
+ out.add(related);
444
+ }
445
+ else {
446
+ for (const origin of facts.origins.get(symbol) ?? NO_SYMBOLS)
447
+ out.add(origin);
448
+ }
449
+ };
450
+ if (this.checker.isArrayType(source)) {
451
+ const [element] = this.checker.getTypeArguments(source);
452
+ if (element !== undefined)
453
+ add(element);
454
+ }
455
+ else
456
+ add(source);
457
+ return out;
458
+ }
459
+ /** A class symbol with its base classes and the program classes that
460
+ * extend it. */
461
+ classFamily(symbol, facts) {
462
+ const known = facts.bases.get(symbol);
463
+ if (known !== undefined)
464
+ return known;
465
+ const family = new Set([symbol]);
466
+ const addBases = (sym) => {
467
+ const declared = this.checker.getDeclaredTypeOfSymbol(sym);
468
+ const target = declared.isTypeReference() ? declared.getTarget() : declared;
469
+ if (!target?.isClassOrInterface())
470
+ return;
471
+ for (const base of this.checker.getBaseTypes(target)) {
472
+ const baseSymbol = base.getSymbol();
473
+ if (baseSymbol !== undefined && !family.has(baseSymbol)) {
474
+ family.add(baseSymbol);
475
+ addBases(baseSymbol);
476
+ }
477
+ }
478
+ };
479
+ addBases(symbol);
480
+ const declared = this.checker.getDeclaredTypeOfSymbol(symbol);
481
+ for (const entries of this.methods.values())
482
+ for (const declaration of entries) {
483
+ const other = declaration.name
484
+ ? this.checker.getSymbolAtLocation(declaration.name)
485
+ : undefined;
486
+ if (other !== undefined &&
487
+ !family.has(other) &&
488
+ this.checker.isTypeAssignableTo(this.checker.getDeclaredTypeOfSymbol(other), declared))
489
+ family.add(other);
490
+ }
491
+ const result = [...family];
492
+ facts.bases.set(symbol, result);
493
+ return result;
494
+ }
495
+ /** Whether the program can tell a copy of a value of the marked type
496
+ * `symbol` from the original: see markIdentityFlows. */
497
+ needsView(symbol, marked, facts) {
498
+ if (facts.observed.has(symbol) || facts.written.has(symbol))
499
+ return true;
500
+ const target = facts.targets.get(symbol);
501
+ if (target === undefined)
502
+ return true;
503
+ const names = new Set(this.checker.getPropertiesOfType(target).map((p) => p.name));
504
+ const origins = facts.origins.get(symbol) ?? new Set();
505
+ const writesNames = (written) => written === "all" || [...written].some((name) => names.has(name));
506
+ for (const origin of origins) {
507
+ const written = facts.written.get(origin);
508
+ if (written !== undefined && writesNames(written))
509
+ return true;
510
+ // An accessor runs on every read of the instance: a copy would keep
511
+ // the value of one call.
512
+ if (this.hasAccessorFor(origin, names))
513
+ return true;
514
+ }
515
+ // Writes through another live view of the same instances.
516
+ for (const [view, written] of facts.written) {
517
+ if (view === symbol || !marked.has(view) || !writesNames(written))
518
+ continue;
519
+ for (const origin of facts.origins.get(view) ?? NO_SYMBOLS)
520
+ if (origins.has(origin))
521
+ return true;
522
+ }
523
+ // An argument holding the type, passed where any value is accepted or
524
+ // to a generic function or class that observes its type parameter.
525
+ return facts.loose.some((arg) => arg.symbols.has(symbol) &&
526
+ attempt(() => {
527
+ const context = this.checker.getContextualType(arg.node);
528
+ if (context !== undefined && this.acceptsAnyValue(context))
529
+ return true;
530
+ if (facts.genericObservers.size === 0)
531
+ return false;
532
+ const signature = this.checker.getResolvedSignature(arg.call);
533
+ const declaration = signature && this.checker.signatureDeclaration(signature);
534
+ return (declaration !== undefined &&
535
+ (facts.genericObservers.has(declaration) ||
536
+ (declaration.parent !== undefined && facts.genericObservers.has(declaration.parent))));
537
+ }) === true);
538
+ }
539
+ /** Whether class `symbol` declares a get or set accessor for one of
540
+ * `names`. */
541
+ hasAccessorFor(symbol, names) {
542
+ const declared = this.checker.getDeclaredTypeOfSymbol(symbol);
543
+ return this.checker
544
+ .getPropertiesOfType(declared)
545
+ .some((property) => names.has(property.name) &&
546
+ this.checker
547
+ .declarationsOf(property)
548
+ .some((declaration) => ts.isGetAccessorDeclaration(declaration) ||
549
+ ts.isSetAccessorDeclaration(declaration)));
550
+ }
551
+ /** `unknown`, `any`, `object`, `{}` and type-parameter parameters, alone
552
+ * or in a union. */
553
+ acceptsAnyValue(type) {
554
+ if (type.isUnionType())
555
+ return ts.constituentTypes(type).some((p) => this.acceptsAnyValue(p));
556
+ if ((type.flags &
557
+ (ts.TypeFlags.Any |
558
+ ts.TypeFlags.Unknown |
559
+ ts.TypeFlags.NonPrimitive |
560
+ ts.TypeFlags.TypeParameter)) !==
561
+ 0)
562
+ return true;
563
+ return ((type.flags & ts.TypeFlags.Object) !== 0 &&
564
+ this.checker.getPropertiesOfType(type).length === 0 &&
565
+ this.checker.getCallSignatures(type).length === 0 &&
566
+ this.checker.getConstructSignatures(type).length === 0 &&
567
+ !this.checker.isArrayType(type));
568
+ }
569
+ /** The value a flow site hands over: casts through `unknown` or `any`
570
+ * keep the original value (`t as unknown as { v: number }`). */
571
+ /** The expression whose type a flow site carries: parentheses, non-null
572
+ * assertions and casts through `unknown`/`any` keep the value itself. */
573
+ flowSourceNode(expr) {
574
+ let inner = expr;
575
+ for (;;) {
576
+ if (ts.isParenthesizedExpression(inner) || ts.isNonNullExpression(inner)) {
577
+ inner = inner.expression;
578
+ continue;
579
+ }
580
+ if ((ts.isAsExpression(inner) || ts.isTypeAssertion(inner)) &&
581
+ (inner.type.kind === ts.SyntaxKind.UnknownKeyword ||
582
+ inner.type.kind === ts.SyntaxKind.AnyKeyword)) {
583
+ inner = inner.expression;
584
+ continue;
585
+ }
586
+ return inner;
587
+ }
588
+ }
589
+ isProgramClassInstance(type) {
590
+ const symbol = type.getSymbol();
591
+ return (symbol !== undefined &&
592
+ (symbol.flags & ts.SymbolFlags.Class) !== 0 &&
593
+ this.checker
594
+ .declarationsOf(symbol)
595
+ .some((declaration) => !declaration.getSourceFile().isDeclarationFile) &&
596
+ // `typeof C` (the class value) shares the class symbol.
597
+ this.checker.getConstructSignatures(type).length === 0);
598
+ }
599
+ carriesIdentity(type, marked) {
600
+ if (type.isUnionType() || type.isIntersectionType())
601
+ return ts.constituentTypes(type).some((part) => this.carriesIdentity(part, marked));
602
+ if ((type.flags & ts.TypeFlags.Object) === 0)
603
+ return false;
604
+ if (this.checker.isArrayType(type)) {
605
+ const [element] = this.checker.getTypeArguments(type);
606
+ return element !== undefined && this.carriesIdentity(element, marked);
607
+ }
608
+ if (this.isProgramClassInstance(type))
609
+ return true;
610
+ const symbol = type.getSymbol();
611
+ if (symbol === undefined)
612
+ return false;
613
+ if (marked.has(symbol))
614
+ return true;
615
+ // A program interface a class matches by its methods (protocol below).
616
+ return (this.methods.size > 0 &&
617
+ this.checker
618
+ .declarationsOf(symbol)
619
+ .some((declaration) => ts.isInterfaceDeclaration(declaration) &&
620
+ !declaration.getSourceFile().isDeclarationFile) &&
621
+ this.protocol(type));
622
+ }
623
+ /** A structural target that natively becomes a record: a program object
624
+ * type that is not a class instance, array, tuple, function or library
625
+ * type. */
626
+ isStructuralTarget(type) {
627
+ if ((type.flags & ts.TypeFlags.Object) === 0)
628
+ return false;
629
+ if (this.checker.isArrayType(type) || this.checker.isTupleType(type))
630
+ return false;
631
+ if (this.checker.getCallSignatures(type).length > 0)
632
+ return false;
633
+ if (this.checker.getConstructSignatures(type).length > 0)
634
+ return false;
635
+ const symbol = type.getSymbol();
636
+ if (symbol === undefined || (symbol.flags & ts.SymbolFlags.Class) !== 0)
637
+ return false;
638
+ const declarations = this.checker.declarationsOf(symbol);
639
+ return (declarations.length > 0 &&
640
+ declarations.every((declaration) => !declaration.getSourceFile().isDeclarationFile));
641
+ }
642
+ /** Marks the structural parts of `target` that the identity-carrying
643
+ * parts of `source` flow into. Answers whether anything new was marked. */
644
+ markTarget(source, target, marked) {
645
+ if (source.isUnionType()) {
646
+ let changed = false;
647
+ for (const part of ts.constituentTypes(source))
648
+ if (this.carriesIdentity(part, marked) && this.markTarget(part, target, marked))
649
+ changed = true;
650
+ return changed;
651
+ }
652
+ if (target.isUnionType()) {
653
+ // Only the arms that accept the value receive it. An accepting arm
654
+ // that holds it as itself (its own type, its class, or another
655
+ // representation with identity) needs no structural view.
656
+ const accepting = [];
657
+ for (const part of ts.constituentTypes(target)) {
658
+ if ((part.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Null)) !== 0)
659
+ continue;
660
+ if (part === source || part.getSymbol() === source.getSymbol())
661
+ return false;
662
+ if (!this.checker.isTypeAssignableTo(source, part))
663
+ continue;
664
+ if (!this.isStructuralTarget(part))
665
+ return false;
666
+ accepting.push(part);
667
+ }
668
+ let changed = false;
669
+ for (const part of accepting)
670
+ if (this.markTarget(source, part, marked))
671
+ changed = true;
672
+ return changed;
673
+ }
674
+ if (this.checker.isArrayType(source) && this.checker.isArrayType(target)) {
675
+ const [from] = this.checker.getTypeArguments(source);
676
+ const [to] = this.checker.getTypeArguments(target);
677
+ return from !== undefined && to !== undefined && this.markTarget(from, to, marked);
678
+ }
679
+ if (!this.isStructuralTarget(target))
680
+ return false;
681
+ const symbol = target.getSymbol();
682
+ if (symbol === undefined)
683
+ return false;
684
+ const from = source.getSymbol();
685
+ if (from === symbol)
686
+ return false;
687
+ const facts = this.facts;
688
+ if (facts !== null) {
689
+ // The flow: the source's classes and marked type reach `symbol`.
690
+ const origins = facts.origins.get(symbol) ?? new Set();
691
+ for (const origin of this.originsOf(source, facts))
692
+ origins.add(origin);
693
+ facts.origins.set(symbol, origins);
694
+ if (!facts.targets.has(symbol))
695
+ facts.targets.set(symbol, target);
696
+ if (from !== undefined && marked.has(from)) {
697
+ const flows = facts.flows.get(from) ?? new Set();
698
+ flows.add(symbol);
699
+ facts.flows.set(from, flows);
700
+ }
701
+ }
702
+ if (marked.has(symbol))
703
+ return false;
704
+ marked.add(symbol);
705
+ return true;
706
+ }
70
707
  /** Class-backed protocols require live receiver dispatch. Other interfaces,
71
708
  * including factories backed by static methods and ordinary records of
72
709
  * closures, retain their fixed native layouts. */
@@ -123,4 +760,102 @@ export class ClassProtocols {
123
760
  return false;
124
761
  }
125
762
  }
763
+ /** The empty answer of the symbol maps above. */
764
+ const NO_SYMBOLS = new Set();
765
+ /** Calls of these namespaces and functions observe whole objects. */
766
+ const OBSERVER_NAMESPACES = new Set(["console", "JSON", "Object", "Reflect", "util", "assert"]);
767
+ const OBSERVER_FUNCTIONS = new Set([
768
+ "structuredClone",
769
+ "String",
770
+ "inspect",
771
+ "format",
772
+ "isDeepStrictEqual",
773
+ // node:assert's named exports.
774
+ "strictEqual",
775
+ "notStrictEqual",
776
+ "deepStrictEqual",
777
+ "notDeepStrictEqual",
778
+ "deepEqual",
779
+ "notDeepEqual",
780
+ "equal",
781
+ "notEqual",
782
+ ]);
783
+ /** Constructors that key their arguments' values by identity. */
784
+ const IDENTITY_CONSTRUCTORS = new Set(["Set", "Map", "WeakSet", "WeakMap", "WeakRef"]);
785
+ /** Object statics that write their first argument. */
786
+ const OBJECT_WRITERS = new Set(["assign", "defineProperty", "defineProperties", "setPrototypeOf"]);
787
+ /** Methods that compare their arguments by identity (Set, Map, WeakMap,
788
+ * WeakSet and array searches). */
789
+ const IDENTITY_METHODS = new Set([
790
+ "add",
791
+ "has",
792
+ "delete",
793
+ "set",
794
+ "get",
795
+ "indexOf",
796
+ "lastIndexOf",
797
+ "includes",
798
+ ]);
799
+ function isNullishLiteral(node) {
800
+ while (ts.isParenthesizedExpression(node))
801
+ node = node.expression;
802
+ return (node.kind === ts.SyntaxKind.NullKeyword ||
803
+ (ts.isIdentifier(node) && node.text === "undefined") ||
804
+ ts.isVoidExpression(node));
805
+ }
806
+ /** A literal or `typeof` operand, which cannot hold an object. */
807
+ function isPrimitiveLiteral(node) {
808
+ while (ts.isParenthesizedExpression(node))
809
+ node = node.expression;
810
+ return (ts.isStringLiteralLike(node) ||
811
+ ts.isNumericLiteral(node) ||
812
+ ts.isBigIntLiteral(node) ||
813
+ ts.isTypeOfExpression(node) ||
814
+ node.kind === ts.SyntaxKind.TrueKeyword ||
815
+ node.kind === ts.SyntaxKind.FalseKeyword ||
816
+ ts.isTemplateExpression(node));
817
+ }
818
+ /** The class whose instance `this` is at `node`: the nearest enclosing
819
+ * class, unless a non-arrow function rebinds `this` first. */
820
+ function thisClass(node) {
821
+ for (let cur = node.parent; cur !== undefined; cur = cur.parent) {
822
+ if (ts.isClassDeclaration(cur) || ts.isClassExpression(cur))
823
+ return cur;
824
+ if (ts.isFunctionDeclaration(cur) || ts.isFunctionExpression(cur))
825
+ return undefined;
826
+ }
827
+ return undefined;
828
+ }
829
+ /** Whether `node` sits directly in a constructor body (not in a nested
830
+ * function). */
831
+ function inConstructor(node) {
832
+ for (let cur = node.parent; cur !== undefined; cur = cur.parent) {
833
+ if (ts.isConstructorDeclaration(cur))
834
+ return true;
835
+ if (ts.isFunctionLike(cur) || ts.isClassDeclaration(cur) || ts.isClassExpression(cur))
836
+ return false;
837
+ }
838
+ return false;
839
+ }
840
+ /** A checker query that may hit an upstream checker panic: the flow scan
841
+ * skips a site it cannot type rather than failing the build. */
842
+ function attempt(query) {
843
+ try {
844
+ return query();
845
+ }
846
+ catch {
847
+ return undefined;
848
+ }
849
+ }
850
+ /** `(value as T).member` and `(value as T)[key]`: the assertion is read or
851
+ * written member-wise in place, which lowers to the instance's own fields
852
+ * and creates no view that could outlive it. */
853
+ function accessedInPlace(node) {
854
+ let outer = node;
855
+ while (ts.isParenthesizedExpression(outer.parent))
856
+ outer = outer.parent;
857
+ const parent = outer.parent;
858
+ return ((ts.isPropertyAccessExpression(parent) || ts.isElementAccessExpression(parent)) &&
859
+ parent.expression === outer);
860
+ }
126
861
  //# sourceMappingURL=class-protocols.js.map