@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
@@ -1,10 +1,11 @@
1
1
  import * as ts from "../../ts7/adapter.js";
2
2
  import {} from "../lowerer.js";
3
3
  import { locOf } from "../../program.js";
4
- import { DYN, F64, STRING, VOID } from "../../../ir/ir.js";
5
- import { boolLit, numLit } from "../../../ir/build.js";
4
+ import { BOOL, DYN, F64, STRING, VOID, arrayOf, BYTES_U8, } from "../../../ir/ir.js";
5
+ import { boolLit, dynUndefinedExpr, numLit } from "../../../ir/build.js";
6
6
  import { READLINE_DOCUMENTED_OPTIONS, fenceOrDropOptionKey } from "../surfaces.js";
7
7
  import { streamSidesOf } from "../lower-stream.js";
8
+ import { voidizedCallback } from "../lower-server.js";
8
9
  /** The ambient module declaring `d`, or null. */
9
10
  function ambientModuleOf(d) {
10
11
  let up = d.parent;
@@ -39,7 +40,18 @@ export function isReadlineTyped(lowerer, node) {
39
40
  });
40
41
  }
41
42
  /** The runtime's listener event numbers (scr_readline.c). */
42
- const RL_EVENTS = { line: 0, close: 1, pause: 2, resume: 3, error: 4 };
43
+ const RL_EVENTS = {
44
+ line: 0,
45
+ close: 1,
46
+ pause: 2,
47
+ resume: 3,
48
+ error: 4,
49
+ history: 5,
50
+ SIGINT: 6,
51
+ SIGTSTP: 7,
52
+ SIGCONT: 8,
53
+ };
54
+ const RL_EVENT_LIST = '"line", "close", "pause", "resume", "error", "history", "SIGINT", "SIGTSTP" and "SIGCONT"';
43
55
  /** True when the call's resolved signature answers a Promise — the
44
56
  * readline/promises question. */
45
57
  function returnsPromise(lowerer, call) {
@@ -64,19 +76,164 @@ function questionSignal(lowerer, node) {
64
76
  }
65
77
  return signal;
66
78
  }
79
+ /** True when `node`'s checker type is readline/promises' Readline. */
80
+ function isReadlineUiTyped(lowerer, node) {
81
+ const t = lowerer.checker.getTypeAtLocation(node);
82
+ const sym = t.getAliasSymbol() ?? t.getSymbol();
83
+ if (sym?.name !== "Readline")
84
+ return false;
85
+ return lowerer.checker.declarationsOf(sym).some((d) => {
86
+ if (!ts.isClassDeclaration(d) && !ts.isInterfaceDeclaration(d))
87
+ return false;
88
+ if (!lowerer.isStdlibFile(d.getSourceFile()))
89
+ return false;
90
+ const spec = ambientModuleOf(d);
91
+ return spec === "readline/promises" || spec === "node:readline/promises";
92
+ });
93
+ }
94
+ /** The process.stdout/stderr target (1/2) of `node`, or 0. */
95
+ function processOutputOf(lowerer, node) {
96
+ if (!ts.isPropertyAccessExpression(node))
97
+ return 0;
98
+ const member = lowerer.stdlibGlobalMember(node, "process");
99
+ return member === "stdout" ? 1 : member === "stderr" ? 2 : 0;
100
+ }
101
+ /** new Readline(stream, options?) from readline/promises: the stream is
102
+ * process.stdout, process.stderr or a writable stream (checked with
103
+ * isWritable at runtime), the options an object literal with a boolean
104
+ * autoCommit. Null for other constructors. */
105
+ export function lowerReadlineUiNew(lowerer, expr) {
106
+ if (!isReadlineUiTyped(lowerer, expr))
107
+ return null;
108
+ const loc = locOf(expr);
109
+ const args = expr.arguments ?? [];
110
+ if (args.length < 1 || args.length > 2 || args.some(ts.isSpreadElement)) {
111
+ lowerer.noLowering(`new Readline with ${args.length} arguments`, expr);
112
+ }
113
+ const fd = processOutputOf(lowerer, args[0]);
114
+ let stream = null;
115
+ if (fd === 0) {
116
+ stream = lowerer.lowerExpr(args[0]);
117
+ const info = stream.type.kind === "object" ? lowerer.classes.get(stream.type.className) : undefined;
118
+ const sides = streamSidesOf(lowerer, info);
119
+ if (sides !== "w" && sides !== "rw") {
120
+ lowerer.noLowering("new Readline with this stream", args[0], "process.stdout, process.stderr and writable streams are the supported streams");
121
+ }
122
+ }
123
+ let autoCommit = boolLit(false, loc);
124
+ const opts = args[1];
125
+ if (opts !== undefined && !isNullish(opts)) {
126
+ if (!ts.isObjectLiteralExpression(opts)) {
127
+ lowerer.noLowering("new Readline with options that are not an object literal", opts, "write the options inline: new Readline(stream, { autoCommit: true })");
128
+ }
129
+ for (const p of opts.properties) {
130
+ if (ts.isPropertyAssignment(p) &&
131
+ ts.isIdentifier(p.name) &&
132
+ p.name.text === "autoCommit" &&
133
+ !isNullish(p.initializer)) {
134
+ if (!(lowerer.typeOf(p.initializer).flags & ts.TypeFlags.BooleanLike)) {
135
+ lowerer.noLowering("the Readline option 'autoCommit' with a value that is not a boolean", p.initializer);
136
+ }
137
+ autoCommit = lowerer.lowerExprExpecting(p.initializer, BOOL);
138
+ }
139
+ else if (!(ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "autoCommit")) {
140
+ lowerer.noLowering("new Readline with this option", p, "autoCommit is the supported option");
141
+ }
142
+ }
143
+ }
144
+ if (stream !== null) {
145
+ return { kind: "libCall", fn: "rl.uiOpenStream", args: [stream, autoCommit], type: F64, loc };
146
+ }
147
+ return { kind: "libCall", fn: "rl.uiOpen", args: [numLit(fd, loc), autoCommit], type: F64, loc };
148
+ }
149
+ /** Readline methods: cursorTo(x, y?), moveCursor(dx, dy), clearLine(dir),
150
+ * clearScreenDown() and rollback() answer the receiver; commit() answers
151
+ * a promise. */
152
+ function lowerReadlineUiCall(lowerer, call, access) {
153
+ const name = access.name.text;
154
+ const loc = locOf(call);
155
+ const args = call.arguments;
156
+ const plan = {
157
+ cursorTo: ["rl.uiCursorTo", 1, 2],
158
+ moveCursor: ["rl.uiMoveCursor", 2, 2],
159
+ clearLine: ["rl.uiClearLine", 1, 1],
160
+ clearScreenDown: ["rl.uiClearScreenDown", 0, 0],
161
+ rollback: ["rl.uiRollback", 0, 0],
162
+ commit: ["rl.uiCommit", 0, 0],
163
+ };
164
+ const entry = plan[name];
165
+ if (entry === undefined ||
166
+ args.length < entry[1] ||
167
+ args.length > entry[2] ||
168
+ args.some(ts.isSpreadElement)) {
169
+ lowerer.noLowering(`readlinePromises.Readline.${name}`, call, "cursorTo, moveCursor, clearLine, clearScreenDown, commit and rollback are the supported Readline members", lowerer.checker.getSymbolAtLocation(access.name));
170
+ }
171
+ const l = lowerer.declareHiddenLocal("%rlUi", F64);
172
+ const recv = { kind: "varRef", localId: l.id, type: F64, loc };
173
+ const stmts = [
174
+ {
175
+ kind: "varDecl",
176
+ localId: l.id,
177
+ init: lowerer.lowerExprExpecting(access.expression, F64),
178
+ loc,
179
+ },
180
+ ];
181
+ if (name === "commit") {
182
+ return {
183
+ kind: "seqExpr",
184
+ stmts,
185
+ result: {
186
+ kind: "libCall",
187
+ fn: "rl.uiCommit",
188
+ args: [recv],
189
+ type: { kind: "promise", inner: VOID },
190
+ loc,
191
+ },
192
+ type: { kind: "promise", inner: VOID },
193
+ loc,
194
+ };
195
+ }
196
+ const number = (n) => {
197
+ if (!(lowerer.typeOf(n).flags & ts.TypeFlags.NumberLike)) {
198
+ lowerer.noLowering(`Readline.${name} with an argument that is not a number`, n);
199
+ }
200
+ return lowerer.lowerExprExpecting(n, F64);
201
+ };
202
+ const values = [recv];
203
+ if (name === "cursorTo") {
204
+ // cursorTo(x, y?): [handle, x, whether y is given, y]
205
+ const y = args[1];
206
+ const hasY = y !== undefined && !isNullish(y);
207
+ values.push(number(args[0]), boolLit(hasY, loc), hasY ? number(y) : numLit(0, loc));
208
+ }
209
+ else {
210
+ for (const n of args)
211
+ values.push(number(n));
212
+ }
213
+ stmts.push({
214
+ kind: "exprStmt",
215
+ expr: { kind: "libCall", fn: entry[0], args: values, type: VOID, loc },
216
+ loc,
217
+ });
218
+ return { kind: "seqExpr", stmts, result: recv, type: F64, loc };
219
+ }
67
220
  /** Method calls on readline Interface receivers (both modules):
68
221
  * - question(query, cb) — the callback form; question(query, { signal }?)
69
222
  * — the promise form (readline/promises);
70
- * - close(), pause(), resume(), prompt(), setPrompt(p), getPrompt(),
71
- * write(data);
223
+ * - close(), pause(), resume(), prompt(preserveCursor?), setPrompt(p),
224
+ * getPrompt(), write(data, key?);
72
225
  * - on/once/addListener for 'line' ((input: string) or no parameters),
73
- * 'close'/'pause'/'resume' (no parameters) and 'error' ((err: Error) or
74
- * no parameters), as statements.
226
+ * 'error' ((err: Error) or no parameters), 'history' ((history:
227
+ * string[]) or no parameters), and 'close'/'pause'/'resume'/'SIGINT'/
228
+ * 'SIGTSTP'/'SIGCONT' (no parameters), as statements.
75
229
  * Everything else the lib declares fences member-qualified. Null for
76
230
  * non-Interface receivers. */
77
231
  export function lowerReadlineMethodCall(lowerer, call, access) {
78
232
  if (call.questionDotToken || access.questionDotToken)
79
233
  return null;
234
+ if (isReadlineUiTyped(lowerer, access.expression) && lowerer.isStdlibMember(access)) {
235
+ return lowerReadlineUiCall(lowerer, call, access);
236
+ }
80
237
  if (!isReadlineTyped(lowerer, access.expression))
81
238
  return null;
82
239
  if (!lowerer.isStdlibMember(access))
@@ -123,13 +280,15 @@ export function lowerReadlineMethodCall(lowerer, call, access) {
123
280
  if ((name === "pause" || name === "resume") && !ts.isExpressionStatement(call.parent)) {
124
281
  lowerer.unsupported("SC1090", call, `using the result of readline ${name}() (call it as its own statement)`);
125
282
  }
126
- // prompt(preserveCursor) only moves the cursor of a terminal
127
- // interface: a boolean literal is accepted and has no effect here.
283
+ // prompt(preserveCursor) keeps a terminal interface's cursor
128
284
  const flag = args[0];
129
- if (flag !== undefined &&
130
- flag.kind !== ts.SyntaxKind.TrueKeyword &&
131
- flag.kind !== ts.SyntaxKind.FalseKeyword) {
132
- lowerer.noLowering("readline prompt() with a non-literal preserveCursor", flag, "preserveCursor only affects terminal interfaces — pass a boolean literal or omit it");
285
+ if (flag !== undefined && flag.kind !== ts.SyntaxKind.FalseKeyword) {
286
+ if (!(lowerer.typeOf(flag).flags & ts.TypeFlags.BooleanLike)) {
287
+ lowerer.noLowering("readline prompt() with a preserveCursor that is not a boolean", flag, "pass a boolean or omit it");
288
+ }
289
+ const recv = receiver();
290
+ const keep = lowerer.lowerExprExpecting(flag, BOOL);
291
+ return { kind: "libCall", fn: "rl.promptWith", args: [recv, keep], type: VOID, loc };
133
292
  }
134
293
  return { kind: "libCall", fn: simpleFn, args: [receiver()], type: VOID, loc };
135
294
  }
@@ -141,29 +300,72 @@ export function lowerReadlineMethodCall(lowerer, call, access) {
141
300
  if (name === "getPrompt" && args.length === 0) {
142
301
  return { kind: "libCall", fn: "rl.getPrompt", args: [receiver()], type: STRING, loc };
143
302
  }
144
- if (name === "write" && args.length === 1) {
303
+ if (name === "getCursorPos" && args.length === 0) {
304
+ // { rows, cols } of the prompt and the line before the cursor
305
+ const t = lowerer.mapTypeOf(lowerer.typeOf(call));
306
+ const shape = t?.kind === "record" ? lowerer.shapes.get(t.shapeId) : undefined;
307
+ if (t?.kind !== "record" ||
308
+ !shape ||
309
+ !shape.fields.every((f) => (f.name === "rows" || f.name === "cols") && f.type.kind === "f64")) {
310
+ lowerer.badType(call, lowerer.typeOf(call));
311
+ }
312
+ const l = lowerer.declareHiddenLocal("%rl", F64);
313
+ const recv = { kind: "varRef", localId: l.id, type: F64, loc };
314
+ return {
315
+ kind: "seqExpr",
316
+ stmts: [{ kind: "varDecl", localId: l.id, init: receiver(), loc }],
317
+ result: {
318
+ kind: "recordLit",
319
+ fields: shape.fields.map((f) => ({
320
+ name: f.name,
321
+ value: {
322
+ kind: "libCall",
323
+ fn: "rl.cursorPos",
324
+ args: [recv, numLit(f.name === "rows" ? 0 : 1, loc)],
325
+ type: F64,
326
+ loc,
327
+ },
328
+ })),
329
+ type: t,
330
+ loc,
331
+ },
332
+ type: t,
333
+ loc,
334
+ };
335
+ }
336
+ if (name === "write" && args.length === 1 && !isNullish(args[0])) {
145
337
  const recv = receiver();
146
338
  const data = lowerer.lowerExprExpecting(args[0], STRING);
147
339
  return { kind: "libCall", fn: "rl.write", args: [recv, data], type: VOID, loc };
148
340
  }
341
+ if (name === "write" && (args.length === 1 || args.length === 2)) {
342
+ return lowerWriteKey(lowerer, receiver(), args[0], args[1], loc);
343
+ }
149
344
  if ((name === "on" || name === "once" || name === "addListener") && args.length === 2) {
150
345
  const evT = lowerer.typeOf(args[0]);
151
346
  const event = evT.isStringLiteralType() ? evT.value : null;
152
347
  const ev = event === null ? undefined : RL_EVENTS[event];
153
348
  if (ev === undefined) {
154
- lowerer.noLowering(`readline.Interface.${name}(${event === null ? "non-literal event" : `"${event}"`}, ...)`, args[0], '"line", "close", "pause", "resume" and "error" are the supported readline events');
349
+ lowerer.noLowering(`readline.Interface.${name}(${event === null ? "non-literal event" : `"${event}"`}, ...)`, args[0], `${RL_EVENT_LIST} are the supported readline events`);
155
350
  }
156
351
  if (!ts.isExpressionStatement(call.parent)) {
157
352
  lowerer.unsupported("SC1090", call, "chaining readline listener registration (register each listener as its own statement)");
158
353
  }
159
354
  const recv = receiver();
160
355
  const cb = lowerer.lowerExpr(args[1]);
161
- const want = event === "line" ? "string" : event === "error" ? "error" : null;
356
+ const want = event === "line"
357
+ ? "string"
358
+ : event === "error"
359
+ ? "error"
360
+ : event === "history"
361
+ ? "history"
362
+ : null;
162
363
  const param = cb.type.kind === "func" ? cb.type.params[0] : undefined;
163
364
  const paramOk = param === undefined ||
164
365
  (want !== null && param.kind === "dyn") ||
165
366
  (want === "string" && param.kind === "string") ||
166
- (want === "error" && param.kind === "object" && param.className === "Error");
367
+ (want === "error" && param.kind === "object" && param.className === "Error") ||
368
+ (want === "history" && param.kind === "array" && param.elem.kind === "string");
167
369
  if (cb.type.kind !== "func" ||
168
370
  cb.type.ret.kind !== "void" ||
169
371
  cb.type.params.length > (want === null ? 0 : 1) ||
@@ -172,7 +374,9 @@ export function lowerReadlineMethodCall(lowerer, call, access) {
172
374
  ? "'line' listeners other than (input: string) => void"
173
375
  : want === "error"
174
376
  ? "'error' listeners other than (err: Error) => void"
175
- : `'${event}' listeners with parameters or a return value (use ())`);
377
+ : want === "history"
378
+ ? "'history' listeners other than (history: string[]) => void"
379
+ : `'${event}' listeners with parameters or a return value (use ())`);
176
380
  }
177
381
  return {
178
382
  kind: "libCall",
@@ -182,7 +386,338 @@ export function lowerReadlineMethodCall(lowerer, call, access) {
182
386
  loc,
183
387
  };
184
388
  }
185
- lowerer.noLowering(`readline.Interface.${name}`, call, "question, close, pause, resume, prompt, setPrompt, getPrompt, write, for await, and on/once for 'line', 'close', 'pause', 'resume' and 'error' are the supported Interface members", lowerer.checker.getSymbolAtLocation(access.name));
389
+ lowerer.noLowering(`readline.Interface.${name}`, call, `question, close, pause, resume, prompt, setPrompt, getPrompt, write, for await, and on/once for ${RL_EVENT_LIST.replace(/"/g, "'")} are the supported Interface members`, lowerer.checker.getSymbolAtLocation(access.name));
390
+ }
391
+ function isNullish(node) {
392
+ return (node.kind === ts.SyntaxKind.NullKeyword || (ts.isIdentifier(node) && node.text === "undefined"));
393
+ }
394
+ /** write(data, key): data a string, null or undefined; key an object
395
+ * literal of name, ctrl, meta and shift. rl.writeKey's arguments: the
396
+ * interface, whether data is present, the data, whether the key is, whether
397
+ * its name is, the name and the three flags. */
398
+ function lowerWriteKey(lowerer, recv, dataNode, keyNode, loc) {
399
+ const empty = { kind: "strLit", value: "", type: STRING, loc };
400
+ const hasData = !isNullish(dataNode);
401
+ const data = hasData ? lowerer.lowerExprExpecting(dataNode, STRING) : empty;
402
+ let hasKey = false;
403
+ let hasName = false;
404
+ let keyName = empty;
405
+ const flags = {
406
+ ctrl: boolLit(false, loc),
407
+ meta: boolLit(false, loc),
408
+ shift: boolLit(false, loc),
409
+ };
410
+ if (keyNode !== undefined && !isNullish(keyNode)) {
411
+ if (!ts.isObjectLiteralExpression(keyNode)) {
412
+ lowerer.noLowering("readline write() with a key that is not an object literal", keyNode, "write the key inline: write(null, { ctrl: true, name: 'u' })");
413
+ }
414
+ hasKey = true;
415
+ for (const p of keyNode.properties) {
416
+ const member = ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) ? p.name.text : null;
417
+ const value = member !== null ? p.initializer : null;
418
+ if (member === "name" && value !== null && !isNullish(value)) {
419
+ hasName = true;
420
+ keyName = lowerer.lowerExprExpecting(value, STRING);
421
+ }
422
+ else if (member === "name") {
423
+ // an undefined name is no name
424
+ }
425
+ else if (member !== null && value !== null && flags[member] !== undefined) {
426
+ if (!(lowerer.typeOf(value).flags & ts.TypeFlags.BooleanLike)) {
427
+ lowerer.noLowering(`a readline write() key '${member}' that is not a boolean`, value);
428
+ }
429
+ flags[member] = lowerer.lowerExprExpecting(value, BOOL);
430
+ }
431
+ else {
432
+ lowerer.noLowering("readline write() with this key member", p, "name, ctrl, meta and shift are the supported key members");
433
+ }
434
+ }
435
+ }
436
+ return {
437
+ kind: "libCall",
438
+ fn: "rl.writeKey",
439
+ args: [
440
+ recv,
441
+ boolLit(hasData, loc),
442
+ data,
443
+ boolLit(hasKey, loc),
444
+ boolLit(hasName, loc),
445
+ keyName,
446
+ flags.ctrl,
447
+ flags.meta,
448
+ flags.shift,
449
+ ],
450
+ type: VOID,
451
+ loc,
452
+ };
453
+ }
454
+ /** readline.emitKeypressEvents(stream[, interface]): process.stdin or a
455
+ * readable stream; the interface is a readline Interface or an object
456
+ * literal with escapeCodeTimeout. rl.keypressStdin / rl.keypressStream
457
+ * take the interface handle (0 for none) and the timeout (NaN: the
458
+ * interface's, or 500). */
459
+ function lowerEmitKeypressEvents(lowerer, expr, loc) {
460
+ const args = expr.arguments;
461
+ if (args.length < 1 || args.length > 2 || args.some(ts.isSpreadElement)) {
462
+ lowerer.noLowering(`readline.emitKeypressEvents with ${args.length} arguments`, expr);
463
+ }
464
+ const stmts = [];
465
+ const streamNode = args[0];
466
+ let stream = null;
467
+ const stdin = ts.isPropertyAccessExpression(streamNode) &&
468
+ lowerer.stdlibGlobalMember(streamNode, "process") === "stdin";
469
+ if (!stdin) {
470
+ stream = lowerer.lowerExpr(streamNode);
471
+ const info = stream.type.kind === "object" ? lowerer.classes.get(stream.type.className) : undefined;
472
+ const sides = streamSidesOf(lowerer, info);
473
+ if (sides !== "r" && sides !== "rw") {
474
+ lowerer.noLowering("readline.emitKeypressEvents with this stream", streamNode, "process.stdin and readable streams are the supported streams");
475
+ }
476
+ const l = lowerer.declareHiddenLocal("%rlStream", stream.type);
477
+ stmts.push({ kind: "varDecl", localId: l.id, init: stream, loc });
478
+ stream = { kind: "varRef", localId: l.id, type: stream.type, loc };
479
+ }
480
+ let iface = numLit(0, loc);
481
+ let timeout = numLit(NaN, loc);
482
+ const ifaceNode = args[1];
483
+ if (ifaceNode !== undefined && !isNullish(ifaceNode)) {
484
+ if (isReadlineTyped(lowerer, ifaceNode)) {
485
+ iface = lowerer.lowerExprExpecting(ifaceNode, F64);
486
+ }
487
+ else if (ts.isObjectLiteralExpression(ifaceNode)) {
488
+ for (const p of ifaceNode.properties) {
489
+ if (ts.isPropertyAssignment(p) &&
490
+ ts.isIdentifier(p.name) &&
491
+ p.name.text === "escapeCodeTimeout" &&
492
+ lowerer.typeOf(p.initializer).flags & ts.TypeFlags.NumberLike) {
493
+ timeout = lowerer.lowerExprExpecting(p.initializer, F64);
494
+ }
495
+ else {
496
+ lowerer.noLowering("readline.emitKeypressEvents with this interface member", p, "a number escapeCodeTimeout is the supported member");
497
+ }
498
+ }
499
+ }
500
+ else {
501
+ lowerer.noLowering("readline.emitKeypressEvents with this interface", ifaceNode, "pass a readline Interface or { escapeCodeTimeout }");
502
+ }
503
+ }
504
+ const call = stream === null
505
+ ? { kind: "libCall", fn: "rl.keypressStdin", args: [iface, timeout], type: VOID, loc }
506
+ : {
507
+ kind: "libCall",
508
+ fn: "rl.keypressStream",
509
+ args: [stream, iface, timeout],
510
+ type: VOID,
511
+ loc,
512
+ };
513
+ return stmts.length === 0 ? call : { kind: "seqExpr", stmts, result: call, type: VOID, loc };
514
+ }
515
+ /** Checks a 'keypress' listener (process.stdin or a readable stream): the
516
+ * first argument is undefined for escape sequences, so a typed first
517
+ * parameter must admit undefined, and the key is readline.Key. */
518
+ export function checkKeypressListener(lowerer, node, cb) {
519
+ if (cb.type.kind === "dyn")
520
+ return;
521
+ if (cb.type.kind !== "func" || cb.type.params.length > 2) {
522
+ lowerer.noLowering("'keypress' listeners with more than two parameters", node, "(str, key) => void listeners are supported");
523
+ }
524
+ const first = cb.type.params[0];
525
+ if (first !== undefined && first.kind !== "dyn") {
526
+ const def = first.kind === "union" ? lowerer.unions.get(first.unionId) : undefined;
527
+ const ok = !!def &&
528
+ def.arms.some((a) => a.kind === "undefinedT") &&
529
+ def.arms.every((a) => a.kind === "undefinedT" || a.kind === "string");
530
+ if (!ok) {
531
+ lowerer.noLowering(`a 'keypress' listener whose first parameter is '${lowerer.fmt(first)}'`, node, "the first argument is undefined for escape sequences — declare it as string | undefined");
532
+ }
533
+ }
534
+ const second = cb.type.params[1];
535
+ if (second !== undefined && second.kind !== "dyn") {
536
+ lowerer.noLowering(`a 'keypress' listener whose second parameter is '${lowerer.fmt(second)}'`, node, "declare the key as readline.Key");
537
+ }
538
+ }
539
+ /** process.stdin's 'keypress' listener as the checked-dynamic function the
540
+ * runtime calls with (str, key). */
541
+ export function stdinKeypressListener(lowerer, node) {
542
+ const cb = lowerer.lowerExpr(node);
543
+ checkKeypressListener(lowerer, node, cb);
544
+ return lowerer.coerceToExpected(cb, DYN);
545
+ }
546
+ const CURSOR_HELPERS = {
547
+ cursorTo: 0,
548
+ moveCursor: 1,
549
+ clearLine: 2,
550
+ clearScreenDown: 3,
551
+ };
552
+ /** readline.cursorTo(stream, x[, y][, callback]), moveCursor(stream, dx,
553
+ * dy[, callback]), clearLine(stream, dir[, callback]) and
554
+ * clearScreenDown(stream[, callback]). The stream is process.stdout,
555
+ * process.stderr, a writable stream, or null/undefined (nothing is
556
+ * written and the callback runs on the next tick). The arguments are
557
+ * evaluated in order; rl.seq checks them (NaN coordinates throw
558
+ * ERR_INVALID_ARG_VALUE) and builds the escape sequence, which goes
559
+ * through the stream's own write. The result is write()'s boolean, or
560
+ * true. */
561
+ export function lowerReadlineCursorCall(lowerer, expr, member, loc) {
562
+ if (member === "emitKeypressEvents")
563
+ return lowerEmitKeypressEvents(lowerer, expr, loc);
564
+ const kind = CURSOR_HELPERS[member];
565
+ if (kind === undefined)
566
+ return null;
567
+ const args = [...expr.arguments];
568
+ if (args.some(ts.isSpreadElement)) {
569
+ lowerer.noLowering(`readline.${member} with a spread argument`, expr);
570
+ }
571
+ const numbers = kind === 0 ? 2 : kind === 1 ? 2 : kind === 2 ? 1 : 0;
572
+ const isFunc = (n) => lowerer.mapTypeOf(lowerer.typeOf(n))?.kind === "func";
573
+ // cursorTo(stream, x, callback): the third argument is the callback
574
+ let numberNodes = args.slice(1, 1 + numbers);
575
+ let cbNode = args[1 + numbers];
576
+ if (kind === 0 && args[2] !== undefined && isFunc(args[2])) {
577
+ numberNodes = [args[1]];
578
+ cbNode = args[2];
579
+ }
580
+ if (args.length > 2 + numbers || args.length < 1 + (kind === 0 ? 1 : numbers)) {
581
+ lowerer.noLowering(`readline.${member} with ${args.length} arguments`, expr);
582
+ }
583
+ if (cbNode !== undefined && isNullish(cbNode))
584
+ cbNode = undefined;
585
+ const stmts = [];
586
+ const hold = (name, value) => {
587
+ const l = lowerer.declareHiddenLocal(name, value.type);
588
+ stmts.push({ kind: "varDecl", localId: l.id, init: value, loc });
589
+ return { kind: "varRef", localId: l.id, type: value.type, loc };
590
+ };
591
+ // the stream
592
+ const streamNode = args[0];
593
+ let target;
594
+ const proc = ts.isPropertyAccessExpression(streamNode) && lowerer.stdlibGlobalMember(streamNode, "process");
595
+ if (proc === "stdout" || proc === "stderr") {
596
+ target = proc;
597
+ }
598
+ else if (isNullish(streamNode)) {
599
+ target = "none";
600
+ }
601
+ else {
602
+ const v = lowerer.lowerExpr(streamNode);
603
+ const info = v.type.kind === "object" ? lowerer.classes.get(v.type.className) : undefined;
604
+ const sides = streamSidesOf(lowerer, info);
605
+ if (sides !== "w" && sides !== "rw") {
606
+ lowerer.noLowering(`readline.${member} with this stream`, streamNode, "process.stdout, process.stderr, writable streams, null and undefined are the supported streams");
607
+ }
608
+ target = hold("%rlStream", v);
609
+ }
610
+ // the coordinates (cursorTo's x may be left undefined: then nothing is
611
+ // written, or ERR_INVALID_CURSOR_POS when y is given)
612
+ const values = [];
613
+ let hasB = false;
614
+ let noX = false;
615
+ for (let i = 0; i < numberNodes.length; i++) {
616
+ const n = numberNodes[i];
617
+ if (kind === 0 && isNullish(n)) {
618
+ if (i === 0) {
619
+ noX = true;
620
+ values.push(numLit(0, loc));
621
+ }
622
+ continue;
623
+ }
624
+ if (!(lowerer.typeOf(n).flags & ts.TypeFlags.NumberLike)) {
625
+ lowerer.noLowering(`readline.${member} with an argument that is not a number`, n);
626
+ }
627
+ values.push(hold("%rlArg", lowerer.lowerExprExpecting(n, F64)));
628
+ if (i === 1)
629
+ hasB = true;
630
+ }
631
+ // the callback: () => void, as the lib declares it
632
+ let cb = null;
633
+ if (cbNode !== undefined) {
634
+ const c = lowerer.lowerExpr(cbNode);
635
+ if (c.type.kind !== "func" || c.type.params.length > 0 || c.type.ret.kind !== "void") {
636
+ lowerer.noLowering(`readline.${member} callbacks with parameters or a return value`, cbNode, "() => void callbacks are supported");
637
+ }
638
+ cb = hold("%rlCallback", target === "stdout" || target === "stderr" ? voidizedCallback(lowerer, c, loc) : c);
639
+ }
640
+ const seqKind = noX && hasB && target !== "none" ? 4 : kind;
641
+ const seq = hold("%rlSeq", {
642
+ kind: "libCall",
643
+ fn: "rl.seq",
644
+ args: [
645
+ numLit(seqKind, loc),
646
+ values[0] ?? numLit(0, loc),
647
+ values[1] ?? numLit(0, loc),
648
+ boolLit(hasB, loc),
649
+ ],
650
+ type: STRING,
651
+ loc,
652
+ });
653
+ const skipped = () => {
654
+ const later = cb
655
+ ? [
656
+ {
657
+ kind: "exprStmt",
658
+ expr: { kind: "libCall", fn: "process.nextTick", args: [cb], type: VOID, loc },
659
+ loc,
660
+ },
661
+ ]
662
+ : [];
663
+ return { kind: "seqExpr", stmts: later, result: boolLit(true, loc), type: BOOL, loc };
664
+ };
665
+ let result;
666
+ if (target === "none" || (noX && !hasB)) {
667
+ result = skipped();
668
+ }
669
+ else {
670
+ let write;
671
+ if (target === "stdout" || target === "stderr") {
672
+ const utf8 = { kind: "strLit", value: "utf8", type: STRING, loc };
673
+ write = cb
674
+ ? {
675
+ kind: "libCall",
676
+ fn: target === "stdout" ? "process.stdoutWriteBytesCb" : "process.stderrWriteBytesCb",
677
+ args: [
678
+ { kind: "libCall", fn: "buffer.fromStr", args: [seq, utf8], type: BYTES_U8, loc },
679
+ utf8,
680
+ cb,
681
+ ],
682
+ type: BOOL,
683
+ loc,
684
+ }
685
+ : {
686
+ kind: "libCall",
687
+ fn: target === "stdout" ? "process.stdoutWrite" : "process.stderrWrite",
688
+ args: [seq],
689
+ type: BOOL,
690
+ loc,
691
+ };
692
+ }
693
+ else {
694
+ write = {
695
+ kind: "libCall",
696
+ fn: "writable.writeStr",
697
+ args: cb ? [target, seq, cb] : [target, seq],
698
+ type: BOOL,
699
+ loc,
700
+ };
701
+ }
702
+ result =
703
+ kind === 1
704
+ ? {
705
+ kind: "ternary",
706
+ cond: {
707
+ kind: "libCall",
708
+ fn: "rl.moves",
709
+ args: [values[0], values[1]],
710
+ type: BOOL,
711
+ loc,
712
+ },
713
+ then: write,
714
+ else_: skipped(),
715
+ type: BOOL,
716
+ loc,
717
+ }
718
+ : write;
719
+ }
720
+ return { kind: "seqExpr", stmts, result, type: BOOL, loc };
186
721
  }
187
722
  /** The interface's input: process.stdin (null) or a readable stream. */
188
723
  function lowerInput(lowerer, node) {
@@ -217,31 +752,84 @@ function lowerOutput(lowerer, node) {
217
752
  }
218
753
  return v;
219
754
  }
220
- /** createInterface({ input, output?, terminal?: false, crlfDelay?,
221
- * prompt?, signal? }) or createInterface(input, output?), from
222
- * node:readline or node:readline/promises. The input is process.stdin
223
- * or a readable stream; the output process.stdout, a writable stream, or
224
- * absent (prompts are then not written). rl.create's arguments: the mode
225
- * literal (1 stream input, 2 stream output, 4 stdout output, 8 signal,
226
- * 16 prompt), then the input and output streams when present, crlfDelay,
227
- * the prompt and the signal when present. */
228
- export function lowerReadlineCreateCall(lowerer, expr, loc) {
755
+ /** createInterface({ input, output?, terminal?, crlfDelay?, prompt?,
756
+ * signal?, ... }) or createInterface(input, output?, completer?,
757
+ * terminal?), from node:readline or node:readline/promises. The input is process.stdin or a readable
758
+ * stream; the output process.stdout, a writable stream, or absent
759
+ * (prompts are then not written). rl.create's arguments: the mode literal
760
+ * (1 stream input, 2 stream output, 4 stdout output, 8 signal, 16
761
+ * prompt), then the input and output streams when present, crlfDelay,
762
+ * the prompt and the signal when present. An interface that can be a
763
+ * terminal one (a `terminal` option that is not false, or none with
764
+ * process.stdout as the output, which is a terminal when it is a TTY)
765
+ * goes through rl.terminal with the completer. */
766
+ export function lowerReadlineCreateCall(lowerer, expr, loc, promises) {
229
767
  let input = null;
230
768
  let output = "none";
231
769
  let crlf = numLit(0, loc);
232
770
  let prompt = null;
233
771
  let signal = null;
772
+ // the history and editing options (scr_rl_check_options' flags)
773
+ let tabSize = null;
774
+ let escapeTimeout = null;
775
+ let historySize = null;
776
+ let history = null;
777
+ let removeDups = null;
778
+ let completer = null;
779
+ let completerValue = null; // set in setCompleter
780
+ let completerAt = -1;
781
+ // the terminal option: null when absent, else the boolean (a literal
782
+ // false drops it)
783
+ let terminal = null; // set in setTerminal
784
+ let terminalFalse = false;
785
+ const pre = [];
786
+ // Only terminal interfaces call the completer. A checked-dynamic value
787
+ // must be callable (Node's ERR_INVALID_ARG_VALUE, after the history
788
+ // checks); a function is not checked. The slot in `pre` is filled once
789
+ // the terminal mode is known.
790
+ const setCompleter = (value) => {
791
+ completerValue = lowerer.lowerExpr(value);
792
+ completerAt = pre.length;
793
+ pre.push({ kind: "exprStmt", expr: completerValue, loc });
794
+ };
795
+ const setTerminal = (value) => {
796
+ if (isNullish(value))
797
+ return;
798
+ if (value.kind === ts.SyntaxKind.FalseKeyword) {
799
+ terminalFalse = true;
800
+ return;
801
+ }
802
+ if (!(lowerer.typeOf(value).flags & ts.TypeFlags.BooleanLike)) {
803
+ lowerer.noLowering("the createInterface option 'terminal' with a value that is not a boolean", value);
804
+ }
805
+ const local = lowerer.declareHiddenLocal("%rlTerminal", BOOL);
806
+ pre.push({
807
+ kind: "varDecl",
808
+ localId: local.id,
809
+ init: lowerer.lowerExprExpecting(value, BOOL),
810
+ loc,
811
+ });
812
+ terminal = { kind: "varRef", localId: local.id, type: BOOL, loc };
813
+ };
234
814
  const optsNode = expr.arguments[0];
235
815
  if (optsNode === undefined) {
236
816
  lowerer.noLowering("createInterface without arguments", expr, "pass { input: process.stdin, output: process.stdout } or createInterface(input, output)");
237
817
  }
238
818
  if (!ts.isObjectLiteralExpression(optsNode)) {
239
- if (expr.arguments.length > 2) {
240
- lowerer.noLowering("createInterface with a completer or terminal argument", expr.arguments[2], "the positional form createInterface(input, output) is supported (terminal line editing has no lowering)");
819
+ // createInterface(input, output?, completer?, terminal?)
820
+ if (expr.arguments.length > 4) {
821
+ lowerer.noLowering(`createInterface with ${expr.arguments.length} arguments`, expr.arguments[4], "pass createInterface(input, output, completer, terminal) or one options object");
241
822
  }
242
823
  input = lowerInput(lowerer, optsNode);
243
824
  if (expr.arguments[1] !== undefined)
244
825
  output = lowerOutput(lowerer, expr.arguments[1]);
826
+ const completerNode = expr.arguments[2];
827
+ if (completerNode !== undefined &&
828
+ !(ts.isIdentifier(completerNode) && completerNode.text === "undefined")) {
829
+ setCompleter(completerNode);
830
+ }
831
+ if (expr.arguments[3] !== undefined)
832
+ setTerminal(expr.arguments[3]);
245
833
  }
246
834
  else {
247
835
  if (expr.arguments.length > 1) {
@@ -249,38 +837,67 @@ export function lowerReadlineCreateCall(lowerer, expr, loc) {
249
837
  }
250
838
  let sawInput = false;
251
839
  for (const p of optsNode.properties) {
252
- if (!ts.isPropertyAssignment(p) || !ts.isIdentifier(p.name)) {
253
- lowerer.noLowering("createInterface with this options shape", p, "spreads, computed keys, and shorthand options have no lowering — write each member inline");
840
+ const shorthand = ts.isShorthandPropertyAssignment(p);
841
+ if (!(shorthand || (ts.isPropertyAssignment(p) && ts.isIdentifier(p.name)))) {
842
+ lowerer.noLowering("createInterface with this options shape", p, "spreads and computed keys have no lowering — write each member inline");
254
843
  }
255
844
  const member = p.name.text;
845
+ // `{ input }` reads the binding of the same name
846
+ const value = shorthand
847
+ ? p.name
848
+ : p.initializer;
849
+ const numeric = () => {
850
+ if (!(lowerer.typeOf(value).flags & ts.TypeFlags.NumberLike)) {
851
+ lowerer.noLowering(`the createInterface option '${member}' with a value that is not a number`, value);
852
+ }
853
+ return lowerer.lowerExprExpecting(value, F64);
854
+ };
256
855
  if (member === "input") {
257
- input = lowerInput(lowerer, p.initializer);
856
+ input = lowerInput(lowerer, value);
258
857
  sawInput = true;
259
858
  }
260
859
  else if (member === "output") {
261
- output = lowerOutput(lowerer, p.initializer);
860
+ output = lowerOutput(lowerer, value);
262
861
  }
263
- else if (member === "terminal") {
264
- const t = lowerer.typeOf(p.initializer);
265
- if (!(t.flags & ts.TypeFlags.BooleanLiteral) ||
266
- lowerer.checker.typeToString(t) !== "false") {
267
- lowerer.noLowering("createInterface with terminal enabled", p.initializer, "terminal line editing has no lowering — the pipe behavior is what compiles");
862
+ else if (member === "tabSize") {
863
+ tabSize = numeric();
864
+ }
865
+ else if (member === "escapeCodeTimeout") {
866
+ escapeTimeout = numeric();
867
+ }
868
+ else if (member === "historySize") {
869
+ historySize = numeric();
870
+ }
871
+ else if (member === "history") {
872
+ const h = lowerer.lowerExpr(value);
873
+ if (h.type.kind !== "array" || h.type.elem.kind !== "string") {
874
+ lowerer.noLowering("createInterface with a history that is not a string array", value, "pass the history as a string[]");
875
+ }
876
+ history = h;
877
+ }
878
+ else if (member === "removeHistoryDuplicates") {
879
+ if (!(lowerer.typeOf(value).flags & ts.TypeFlags.BooleanLike)) {
880
+ lowerer.noLowering("the createInterface option 'removeHistoryDuplicates' with a value that is not a boolean", value);
268
881
  }
882
+ removeDups = lowerer.lowerExprExpecting(value, BOOL);
883
+ }
884
+ else if (member === "completer") {
885
+ setCompleter(value);
886
+ }
887
+ else if (member === "terminal") {
888
+ setTerminal(value);
269
889
  }
270
890
  else if (member === "crlfDelay") {
271
- crlf = lowerer.lowerExprExpecting(p.initializer, F64);
891
+ crlf = lowerer.lowerExprExpecting(value, F64);
272
892
  }
273
893
  else if (member === "prompt") {
274
- prompt = lowerer.lowerExprExpecting(p.initializer, STRING);
894
+ prompt = lowerer.lowerExprExpecting(value, STRING);
275
895
  }
276
896
  else if (member === "signal") {
277
- signal = lowerer.lowerExprExpecting(p.initializer, DYN);
278
- }
279
- else if (member === "completer") {
280
- lowerer.noLowering("createInterface with a completer", p, "tab completion needs an interactive terminal — the lowered interface reads piped lines (terminal: false)");
897
+ signal = lowerer.lowerExprExpecting(value, DYN);
281
898
  }
282
899
  else {
283
- fenceOrDropOptionKey(lowerer, p, member, "createInterface", READLINE_DOCUMENTED_OPTIONS, "input, output, terminal: false, crlfDelay, prompt and signal are the supported options");
900
+ fenceOrDropOptionKey(lowerer, p, member, "createInterface", READLINE_DOCUMENTED_OPTIONS, "input, output, terminal, crlfDelay, prompt, signal, history, historySize, removeHistoryDuplicates, completer, tabSize and escapeCodeTimeout are the supported options");
284
901
  // An undocumented key, dropped like Node drops it.
285
902
  }
286
903
  }
@@ -303,12 +920,160 @@ export function lowerReadlineCreateCall(lowerer, expr, loc) {
303
920
  args.push(prompt);
304
921
  if (signal !== null)
305
922
  args.push(signal);
306
- return {
923
+ const create = {
307
924
  kind: "libCall",
308
925
  fn: signal !== null ? "rl.createSignal" : "rl.create",
309
926
  args,
310
927
  type: F64,
311
928
  loc,
312
929
  };
930
+ // terminal mode: 0 the option's value, 2 output.isTTY of process.stdout
931
+ const terminalMode = terminal !== null ? 0 : !terminalFalse && output === "stdout" ? 2 : null;
932
+ // The completer is kept as a checked-dynamic value when the interface
933
+ // can be a terminal one; a function is otherwise only evaluated.
934
+ let completerChecked = false;
935
+ if (completerValue !== null && (terminalMode !== null || completerValue.type.kind !== "func")) {
936
+ const local = lowerer.declareHiddenLocal("%rlCompleter", DYN);
937
+ pre[completerAt] = {
938
+ kind: "varDecl",
939
+ localId: local.id,
940
+ init: lowerer.coerceToExpected(completerValue, DYN),
941
+ loc,
942
+ };
943
+ completer = { kind: "varRef", localId: local.id, type: DYN, loc };
944
+ completerChecked = completerValue.type.kind !== "func";
945
+ }
946
+ if (pre.length === 0 &&
947
+ !tabSize &&
948
+ !escapeTimeout &&
949
+ !historySize &&
950
+ !history &&
951
+ !removeDups &&
952
+ !completer &&
953
+ terminalMode === null) {
954
+ return create;
955
+ }
956
+ // The numeric options are evaluated once, checked in Node's order
957
+ // before the interface opens, then stored on it with the history.
958
+ const stmts = [...pre];
959
+ const temp = (name, v) => {
960
+ if (v === null)
961
+ return numLit(0, loc);
962
+ const l = lowerer.declareHiddenLocal(name, F64);
963
+ stmts.push({ kind: "varDecl", localId: l.id, init: v, loc });
964
+ return { kind: "varRef", localId: l.id, type: F64, loc };
965
+ };
966
+ const flags = (tabSize ? 1 : 0) | (escapeTimeout ? 2 : 0) | (historySize ? 4 : 0);
967
+ const tab = temp("%rlTabSize", tabSize);
968
+ const esc = temp("%rlEscapeCodeTimeout", escapeTimeout);
969
+ const size = temp("%rlHistorySize", historySize);
970
+ if (flags !== 0) {
971
+ stmts.push({
972
+ kind: "exprStmt",
973
+ expr: {
974
+ kind: "libCall",
975
+ fn: "rl.checkOptions",
976
+ args: [numLit(flags, loc), tab, esc, size],
977
+ type: VOID,
978
+ loc,
979
+ },
980
+ loc,
981
+ });
982
+ }
983
+ if (completer && completerChecked) {
984
+ stmts.push({
985
+ kind: "exprStmt",
986
+ expr: { kind: "libCall", fn: "rl.checkCompleter", args: [completer], type: VOID, loc },
987
+ loc,
988
+ });
989
+ }
990
+ const id = lowerer.declareHiddenLocal("%rl", F64);
991
+ const idRef = { kind: "varRef", localId: id.id, type: F64, loc };
992
+ stmts.push({ kind: "varDecl", localId: id.id, init: create, loc });
993
+ stmts.push({
994
+ kind: "exprStmt",
995
+ expr: {
996
+ kind: "libCall",
997
+ fn: "rl.setOptions",
998
+ args: [idRef, numLit(flags, loc), size, removeDups ?? boolLit(false, loc), tab, esc],
999
+ type: VOID,
1000
+ loc,
1001
+ },
1002
+ loc,
1003
+ });
1004
+ if (history) {
1005
+ stmts.push({
1006
+ kind: "exprStmt",
1007
+ expr: { kind: "libCall", fn: "rl.setHistory", args: [idRef, history], type: VOID, loc },
1008
+ loc,
1009
+ });
1010
+ }
1011
+ if (terminalMode !== null) {
1012
+ stmts.push({
1013
+ kind: "exprStmt",
1014
+ expr: {
1015
+ kind: "libCall",
1016
+ fn: "rl.terminal",
1017
+ args: [
1018
+ idRef,
1019
+ numLit(terminalMode, loc),
1020
+ terminal ?? boolLit(false, loc),
1021
+ completer ?? dynUndefinedExpr(loc),
1022
+ boolLit(promises, loc),
1023
+ ],
1024
+ type: VOID,
1025
+ loc,
1026
+ },
1027
+ loc,
1028
+ });
1029
+ }
1030
+ return { kind: "seqExpr", stmts, result: idRef, type: F64, loc };
1031
+ }
1032
+ /** Property reads on an Interface: `history` (the history option itself,
1033
+ * or the interface's own empty array), `historySize`, `historyIndex`,
1034
+ * `crlfDelay`, `tabSize`, `escapeCodeTimeout`, `line`, `terminal` and
1035
+ * `cursor` (a non-terminal interface's undefined cursor throws SC2020).
1036
+ * Null for other receivers. */
1037
+ export function lowerReadlineProperty(lowerer, access) {
1038
+ if (access.questionDotToken)
1039
+ return null;
1040
+ if (!isReadlineTyped(lowerer, access.expression))
1041
+ return null;
1042
+ if (!lowerer.isStdlibMember(access))
1043
+ return null;
1044
+ const name = access.name.text;
1045
+ const loc = locOf(access);
1046
+ const numeric = {
1047
+ historySize: 0,
1048
+ historyIndex: 1,
1049
+ crlfDelay: 2,
1050
+ tabSize: 3,
1051
+ escapeCodeTimeout: 4,
1052
+ };
1053
+ if (name === "history") {
1054
+ const recv = lowerer.lowerExprExpecting(access.expression, F64);
1055
+ return { kind: "libCall", fn: "rl.history", args: [recv], type: arrayOf(STRING), loc };
1056
+ }
1057
+ if (numeric[name] !== undefined) {
1058
+ const recv = lowerer.lowerExprExpecting(access.expression, F64);
1059
+ return {
1060
+ kind: "libCall",
1061
+ fn: "rl.numProp",
1062
+ args: [recv, numLit(numeric[name], loc)],
1063
+ type: F64,
1064
+ loc,
1065
+ };
1066
+ }
1067
+ const reads = {
1068
+ line: ["rl.line", STRING],
1069
+ terminal: ["rl.isTerminal", BOOL],
1070
+ cursor: ["rl.cursor", F64],
1071
+ };
1072
+ const read = reads[name];
1073
+ if (read !== undefined) {
1074
+ const recv = lowerer.lowerExprExpecting(access.expression, F64);
1075
+ return { kind: "libCall", fn: read[0], args: [recv], type: read[1], loc };
1076
+ }
1077
+ return null;
313
1078
  }
314
1079
  //# sourceMappingURL=readline.js.map