@scriptc/compiler 0.0.34 → 0.0.36

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 (424) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +7 -6
  2. package/dist/backend/build-cache.d.ts +44 -0
  3. package/dist/backend/build-cache.js +241 -0
  4. package/dist/backend/build-cache.js.map +1 -0
  5. package/dist/backend/{emission/emit-async.d.ts → c/async.d.ts} +21 -30
  6. package/dist/backend/{emission/emit-async.js → c/async.js} +270 -286
  7. package/dist/backend/c/async.js.map +1 -0
  8. package/dist/backend/{emission/emitter.d.ts → c/c-emitter.d.ts} +6 -23
  9. package/dist/backend/{emission/emitter.js → c/c-emitter.js} +53 -107
  10. package/dist/backend/c/c-emitter.js.map +1 -0
  11. package/dist/backend/c/exprs.d.ts +8 -0
  12. package/dist/backend/c/exprs.js +7913 -0
  13. package/dist/backend/c/exprs.js.map +1 -0
  14. package/dist/backend/{emission/emit-island.d.ts → c/island.d.ts} +5 -5
  15. package/dist/backend/{emission/emit-island.js → c/island.js} +24 -22
  16. package/dist/backend/c/island.js.map +1 -0
  17. package/dist/backend/{emission → c}/may-throw.d.ts +1 -1
  18. package/dist/backend/{emission → c}/may-throw.js +1 -1
  19. package/dist/backend/c/may-throw.js.map +1 -0
  20. package/dist/backend/{emission/emit-shapes.d.ts → c/shapes.d.ts} +19 -19
  21. package/dist/backend/{emission/emit-shapes.js → c/shapes.js} +78 -87
  22. package/dist/backend/c/shapes.js.map +1 -0
  23. package/dist/backend/{emission/emit-stmts.d.ts → c/stmts.d.ts} +12 -12
  24. package/dist/backend/{emission/emit-stmts.js → c/stmts.js} +377 -343
  25. package/dist/backend/c/stmts.js.map +1 -0
  26. package/dist/backend/{emission/emit-types.d.ts → c/types.d.ts} +1 -1
  27. package/dist/backend/{emission/emit-types.js → c/types.js} +42 -286
  28. package/dist/backend/c/types.js.map +1 -0
  29. package/dist/backend/c/types.test.js +25 -0
  30. package/dist/backend/c/types.test.js.map +1 -0
  31. package/dist/backend/{emission/emit-walkers.d.ts → c/walkers.d.ts} +19 -41
  32. package/dist/backend/{emission/emit-walkers.js → c/walkers.js} +238 -298
  33. package/dist/backend/c/walkers.js.map +1 -0
  34. package/dist/backend/ffi-callbacks.d.ts +1 -1
  35. package/dist/backend/ffi-callbacks.js +4 -3
  36. package/dist/backend/ffi-callbacks.js.map +1 -1
  37. package/dist/backend/{library-identity.d.ts → library-identity-markers.d.ts} +0 -4
  38. package/dist/backend/{library-identity.js → library-identity-markers.js} +10 -9
  39. package/dist/backend/library-identity-markers.js.map +1 -0
  40. package/dist/backend/llvm/blocks.d.ts +4 -0
  41. package/dist/backend/llvm/blocks.js +29 -0
  42. package/dist/backend/llvm/blocks.js.map +1 -1
  43. package/dist/backend/llvm/classes.d.ts +2 -19
  44. package/dist/backend/llvm/classes.js +20 -52
  45. package/dist/backend/llvm/classes.js.map +1 -1
  46. package/dist/backend/llvm/common.d.ts +4 -0
  47. package/dist/backend/llvm/common.js +27 -0
  48. package/dist/backend/llvm/common.js.map +1 -0
  49. package/dist/backend/llvm/dyn.d.ts +3 -8
  50. package/dist/backend/llvm/dyn.js +152 -224
  51. package/dist/backend/llvm/dyn.js.map +1 -1
  52. package/dist/backend/llvm/emitter.d.ts +4 -1
  53. package/dist/backend/llvm/emitter.js +514 -9981
  54. package/dist/backend/llvm/emitter.js.map +1 -1
  55. package/dist/backend/llvm/expr-async.d.ts +4 -0
  56. package/dist/backend/llvm/expr-async.js +726 -0
  57. package/dist/backend/llvm/expr-async.js.map +1 -0
  58. package/dist/backend/llvm/expr-bytes.d.ts +13 -0
  59. package/dist/backend/llvm/expr-bytes.js +456 -0
  60. package/dist/backend/llvm/expr-bytes.js.map +1 -0
  61. package/dist/backend/llvm/expr-callbacks.d.ts +22 -0
  62. package/dist/backend/llvm/expr-callbacks.js +520 -0
  63. package/dist/backend/llvm/expr-callbacks.js.map +1 -0
  64. package/dist/backend/llvm/expr-calls.d.ts +2 -0
  65. package/dist/backend/llvm/expr-calls.js +618 -0
  66. package/dist/backend/llvm/expr-calls.js.map +1 -0
  67. package/dist/backend/llvm/expr-containers.d.ts +25 -0
  68. package/dist/backend/llvm/expr-containers.js +623 -0
  69. package/dist/backend/llvm/expr-containers.js.map +1 -0
  70. package/dist/backend/llvm/expr-context.d.ts +255 -0
  71. package/dist/backend/llvm/expr-context.js +2 -0
  72. package/dist/backend/llvm/expr-context.js.map +1 -0
  73. package/dist/backend/llvm/expr-control.d.ts +2 -0
  74. package/dist/backend/llvm/expr-control.js +473 -0
  75. package/dist/backend/llvm/expr-control.js.map +1 -0
  76. package/dist/backend/llvm/expr-dispatch.d.ts +4 -0
  77. package/dist/backend/llvm/expr-dispatch.js +151 -0
  78. package/dist/backend/llvm/expr-dispatch.js.map +1 -0
  79. package/dist/backend/llvm/expr-dynamic.d.ts +2 -0
  80. package/dist/backend/llvm/expr-dynamic.js +810 -0
  81. package/dist/backend/llvm/expr-dynamic.js.map +1 -0
  82. package/dist/backend/llvm/expr-island.d.ts +15 -0
  83. package/dist/backend/llvm/expr-island.js +690 -0
  84. package/dist/backend/llvm/expr-island.js.map +1 -0
  85. package/dist/backend/llvm/expr-primitives.d.ts +6 -0
  86. package/dist/backend/llvm/expr-primitives.js +622 -0
  87. package/dist/backend/llvm/expr-primitives.js.map +1 -0
  88. package/dist/backend/llvm/expr-records.d.ts +9 -0
  89. package/dist/backend/llvm/expr-records.js +299 -0
  90. package/dist/backend/llvm/expr-records.js.map +1 -0
  91. package/dist/backend/llvm/expr-stream-bridges.d.ts +13 -0
  92. package/dist/backend/llvm/expr-stream-bridges.js +396 -0
  93. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -0
  94. package/dist/backend/llvm/expr-stream-callbacks.d.ts +12 -0
  95. package/dist/backend/llvm/expr-stream-callbacks.js +360 -0
  96. package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -0
  97. package/dist/backend/llvm/lib-dispatch.d.ts +5 -0
  98. package/dist/backend/llvm/lib-dispatch.js +267 -0
  99. package/dist/backend/llvm/lib-dispatch.js.map +1 -0
  100. package/dist/backend/llvm/lib-filesystem.d.ts +6 -0
  101. package/dist/backend/llvm/lib-filesystem.js +594 -0
  102. package/dist/backend/llvm/lib-filesystem.js.map +1 -0
  103. package/dist/backend/llvm/lib-network.d.ts +2 -0
  104. package/dist/backend/llvm/lib-network.js +429 -0
  105. package/dist/backend/llvm/lib-network.js.map +1 -0
  106. package/dist/backend/llvm/lib-process.d.ts +5 -0
  107. package/dist/backend/llvm/lib-process.js +585 -0
  108. package/dist/backend/llvm/lib-process.js.map +1 -0
  109. package/dist/backend/llvm/lib-shared.d.ts +6 -0
  110. package/dist/backend/llvm/lib-shared.js +687 -0
  111. package/dist/backend/llvm/lib-shared.js.map +1 -0
  112. package/dist/backend/llvm/lib-stream.d.ts +2 -0
  113. package/dist/backend/llvm/lib-stream.js +470 -0
  114. package/dist/backend/llvm/lib-stream.js.map +1 -0
  115. package/dist/backend/llvm/shapes.d.ts +12 -2
  116. package/dist/backend/llvm/shapes.js +66 -307
  117. package/dist/backend/llvm/shapes.js.map +1 -1
  118. package/dist/backend/llvm/shapes.test.js +52 -0
  119. package/dist/backend/llvm/shapes.test.js.map +1 -0
  120. package/dist/backend/llvm/split.d.ts +43 -0
  121. package/dist/backend/llvm/split.js +227 -0
  122. package/dist/backend/llvm/split.js.map +1 -0
  123. package/dist/backend/llvm/split.test.d.ts +1 -0
  124. package/dist/backend/llvm/split.test.js +102 -0
  125. package/dist/backend/llvm/split.test.js.map +1 -0
  126. package/dist/backend/llvm/unsupported.d.ts +1 -1
  127. package/dist/backend/llvm/walkers.d.ts +2 -3
  128. package/dist/backend/llvm/walkers.js +164 -218
  129. package/dist/backend/llvm/walkers.js.map +1 -1
  130. package/dist/backend/native-codegen.d.ts +38 -0
  131. package/dist/backend/native-codegen.js +261 -0
  132. package/dist/backend/native-codegen.js.map +1 -0
  133. package/dist/backend/native-codegen.test.d.ts +1 -0
  134. package/dist/backend/native-codegen.test.js +188 -0
  135. package/dist/backend/native-codegen.test.js.map +1 -0
  136. package/dist/backend/native-link-info.d.ts +96 -0
  137. package/dist/backend/native-link-info.js +219 -0
  138. package/dist/backend/native-link-info.js.map +1 -0
  139. package/dist/backend/native-link-info.test.d.ts +1 -0
  140. package/dist/backend/native-link-info.test.js +134 -0
  141. package/dist/backend/native-link-info.test.js.map +1 -0
  142. package/dist/backend/{cc.d.ts → native-toolchain.d.ts} +139 -47
  143. package/dist/backend/{cc.js → native-toolchain.js} +1008 -910
  144. package/dist/backend/native-toolchain.js.map +1 -0
  145. package/dist/backend/native-toolchain.test.d.ts +1 -0
  146. package/dist/backend/{cc-cache.test.js → native-toolchain.test.js} +1297 -127
  147. package/dist/backend/native-toolchain.test.js.map +1 -0
  148. package/dist/backend/object-localize.js +1 -1
  149. package/dist/backend/object-localize.test.js +20 -0
  150. package/dist/backend/object-localize.test.js.map +1 -1
  151. package/dist/backend/runtime-abi.d.ts +10 -0
  152. package/dist/backend/runtime-abi.js +11 -0
  153. package/dist/backend/runtime-abi.js.map +1 -0
  154. package/dist/backend/runtime-pack.d.ts +137 -0
  155. package/dist/backend/runtime-pack.js +400 -0
  156. package/dist/backend/runtime-pack.js.map +1 -0
  157. package/dist/backend/runtime-pack.test.d.ts +1 -0
  158. package/dist/backend/runtime-pack.test.js +438 -0
  159. package/dist/backend/runtime-pack.test.js.map +1 -0
  160. package/dist/backend/targets.d.ts +30 -0
  161. package/dist/backend/targets.js +46 -0
  162. package/dist/backend/targets.js.map +1 -0
  163. package/dist/backend/targets.test.d.ts +1 -0
  164. package/dist/backend/targets.test.js +19 -0
  165. package/dist/backend/targets.test.js.map +1 -0
  166. package/dist/backend/vendor-archives.d.ts +41 -0
  167. package/dist/backend/vendor-archives.js +462 -0
  168. package/dist/backend/vendor-archives.js.map +1 -0
  169. package/dist/compat/fetch-profile.d.ts +0 -1
  170. package/dist/compat/fetch-profile.js +0 -1
  171. package/dist/compat/fetch-profile.js.map +1 -1
  172. package/dist/coverage/report.js +3 -3
  173. package/dist/coverage/report.js.map +1 -1
  174. package/dist/coverage/surface-manifest.js +25 -32
  175. package/dist/coverage/surface-manifest.js.map +1 -1
  176. package/dist/diagnostics/diagnostic.d.ts +11 -12
  177. package/dist/diagnostics/diagnostic.js +19 -17
  178. package/dist/diagnostics/diagnostic.js.map +1 -1
  179. package/dist/diagnostics/render.d.ts +1 -1
  180. package/dist/diagnostics/render.js +1 -1
  181. package/dist/diagnostics/render.js.map +1 -1
  182. package/dist/errors.d.ts +5 -0
  183. package/dist/errors.js +9 -0
  184. package/dist/errors.js.map +1 -0
  185. package/dist/executable/executable-cache.d.ts +86 -0
  186. package/dist/executable/executable-cache.js +398 -0
  187. package/dist/executable/executable-cache.js.map +1 -0
  188. package/dist/executable/executable-cache.test.d.ts +1 -0
  189. package/dist/executable/executable-cache.test.js +300 -0
  190. package/dist/executable/executable-cache.test.js.map +1 -0
  191. package/dist/ffi/{profile.d.ts → ffi-manifest.d.ts} +7 -2
  192. package/dist/ffi/{profile.js → ffi-manifest.js} +2 -1
  193. package/dist/ffi/ffi-manifest.js.map +1 -0
  194. package/dist/frontend/builtin-modules.d.ts +21 -0
  195. package/dist/frontend/builtin-modules.js +82 -0
  196. package/dist/frontend/builtin-modules.js.map +1 -0
  197. package/dist/frontend/cjs-lexer.d.ts +7 -0
  198. package/dist/frontend/cjs-lexer.js +15 -17
  199. package/dist/frontend/cjs-lexer.js.map +1 -1
  200. package/dist/frontend/dts-paths.d.ts +26 -0
  201. package/dist/frontend/dts-paths.js +41 -0
  202. package/dist/frontend/dts-paths.js.map +1 -0
  203. package/dist/frontend/input-tracker.d.ts +0 -2
  204. package/dist/frontend/input-tracker.js +1 -4
  205. package/dist/frontend/input-tracker.js.map +1 -1
  206. package/dist/frontend/lib-contract.d.ts +1 -5
  207. package/dist/frontend/lib-contract.js +1 -1
  208. package/dist/frontend/lib-contract.js.map +1 -1
  209. package/dist/frontend/lib-exports.d.ts +1 -1
  210. package/dist/frontend/lowering/call-position.d.ts +4 -0
  211. package/dist/frontend/lowering/call-position.js +7 -0
  212. package/dist/frontend/lowering/call-position.js.map +1 -0
  213. package/dist/frontend/lowering/callback-arg.d.ts +20 -0
  214. package/dist/frontend/lowering/callback-arg.js +44 -0
  215. package/dist/frontend/lowering/callback-arg.js.map +1 -0
  216. package/dist/frontend/lowering/lib-boundary.d.ts +1 -1
  217. package/dist/frontend/lowering/lib-boundary.js +34 -34
  218. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  219. package/dist/frontend/lowering/lower-assert.d.ts +3 -3
  220. package/dist/frontend/lowering/lower-assert.js +214 -241
  221. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  222. package/dist/frontend/lowering/lower-builtins.d.ts +74 -146
  223. package/dist/frontend/lowering/lower-builtins.js +1126 -1234
  224. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  225. package/dist/frontend/lowering/lower-calls.d.ts +56 -188
  226. package/dist/frontend/lowering/lower-calls.js +1409 -1494
  227. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  228. package/dist/frontend/lowering/lower-classes.d.ts +62 -125
  229. package/dist/frontend/lowering/lower-classes.js +832 -866
  230. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  231. package/dist/frontend/lowering/lower-comptime.d.ts +5 -12
  232. package/dist/frontend/lowering/lower-comptime.js +32 -31
  233. package/dist/frontend/lowering/lower-comptime.js.map +1 -1
  234. package/dist/frontend/lowering/lower-containers.d.ts +35 -135
  235. package/dist/frontend/lowering/lower-containers.js +1394 -1715
  236. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  237. package/dist/frontend/lowering/lower-dgram.d.ts +3 -7
  238. package/dist/frontend/lowering/lower-dgram.js +97 -111
  239. package/dist/frontend/lowering/lower-dgram.js.map +1 -1
  240. package/dist/frontend/lowering/lower-enums.d.ts +4 -4
  241. package/dist/frontend/lowering/lower-enums.js +30 -30
  242. package/dist/frontend/lowering/lower-enums.js.map +1 -1
  243. package/dist/frontend/lowering/{lower-emitter.d.ts → lower-event-emitter.d.ts} +7 -18
  244. package/dist/frontend/lowering/{lower-emitter.js → lower-event-emitter.js} +172 -174
  245. package/dist/frontend/lowering/lower-event-emitter.js.map +1 -0
  246. package/dist/frontend/lowering/lower-expando.d.ts +5 -5
  247. package/dist/frontend/lowering/lower-expando.js +40 -40
  248. package/dist/frontend/lowering/lower-expando.js.map +1 -1
  249. package/dist/frontend/lowering/lower-exprs.d.ts +56 -264
  250. package/dist/frontend/lowering/lower-exprs.js +1644 -1704
  251. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  252. package/dist/frontend/lowering/lower-generators.d.ts +6 -6
  253. package/dist/frontend/lowering/lower-generators.js +73 -72
  254. package/dist/frontend/lowering/lower-generators.js.map +1 -1
  255. package/dist/frontend/lowering/lower-inspect.d.ts +5 -17
  256. package/dist/frontend/lowering/lower-inspect.js +236 -238
  257. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  258. package/dist/frontend/lowering/lower-island.d.ts +34 -45
  259. package/dist/frontend/lowering/lower-island.js +440 -511
  260. package/dist/frontend/lowering/lower-island.js.map +1 -1
  261. package/dist/frontend/lowering/lower-mixins.d.ts +7 -10
  262. package/dist/frontend/lowering/lower-mixins.js +86 -86
  263. package/dist/frontend/lowering/lower-mixins.js.map +1 -1
  264. package/dist/frontend/lowering/lower-modules.d.ts +12 -18
  265. package/dist/frontend/lowering/lower-modules.js +284 -283
  266. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  267. package/dist/frontend/lowering/lower-namespaces.d.ts +23 -49
  268. package/dist/frontend/lowering/lower-namespaces.js +123 -115
  269. package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
  270. package/dist/frontend/lowering/lower-server.d.ts +18 -25
  271. package/dist/frontend/lowering/lower-server.js +899 -899
  272. package/dist/frontend/lowering/lower-server.js.map +1 -1
  273. package/dist/frontend/lowering/lower-stmts.d.ts +30 -149
  274. package/dist/frontend/lowering/lower-stmts.js +1176 -1229
  275. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  276. package/dist/frontend/lowering/lower-stream.d.ts +12 -12
  277. package/dist/frontend/lowering/lower-stream.js +300 -301
  278. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  279. package/dist/frontend/lowering/lower-test.d.ts +5 -5
  280. package/dist/frontend/lowering/lower-test.js +82 -82
  281. package/dist/frontend/lowering/lower-test.js.map +1 -1
  282. package/dist/frontend/lowering/lowerer.d.ts +66 -20
  283. package/dist/frontend/lowering/lowerer.js +421 -198
  284. package/dist/frontend/lowering/lowerer.js.map +1 -1
  285. package/dist/frontend/lowering/pairs-snapshot.d.ts +11 -0
  286. package/dist/frontend/lowering/pairs-snapshot.js +85 -0
  287. package/dist/frontend/lowering/pairs-snapshot.js.map +1 -0
  288. package/dist/frontend/lowering/static-this.d.ts +6 -0
  289. package/dist/frontend/lowering/static-this.js +24 -0
  290. package/dist/frontend/lowering/static-this.js.map +1 -0
  291. package/dist/frontend/lowering/surfaces.d.ts +14 -33
  292. package/dist/frontend/lowering/surfaces.js +64 -64
  293. package/dist/frontend/lowering/surfaces.js.map +1 -1
  294. package/dist/frontend/npm-static-rewrite.js +6 -28
  295. package/dist/frontend/npm-static-rewrite.js.map +1 -1
  296. package/dist/frontend/npm-static.d.ts +0 -10
  297. package/dist/frontend/npm-static.js +4 -7
  298. package/dist/frontend/npm-static.js.map +1 -1
  299. package/dist/frontend/npm.d.ts +0 -24
  300. package/dist/frontend/npm.js +11 -85
  301. package/dist/frontend/npm.js.map +1 -1
  302. package/dist/frontend/program.d.ts +3 -1
  303. package/dist/frontend/program.js +51 -17
  304. package/dist/frontend/program.js.map +1 -1
  305. package/dist/frontend/provenance-registry.d.ts +0 -2
  306. package/dist/frontend/provenance-registry.js +0 -10
  307. package/dist/frontend/provenance-registry.js.map +1 -1
  308. package/dist/frontend/provenance.js +4 -7
  309. package/dist/frontend/provenance.js.map +1 -1
  310. package/dist/frontend/resolve.d.ts +7 -2
  311. package/dist/frontend/resolve.js +12 -17
  312. package/dist/frontend/resolve.js.map +1 -1
  313. package/dist/frontend/resolve.test.d.ts +1 -0
  314. package/dist/frontend/resolve.test.js +29 -0
  315. package/dist/frontend/resolve.test.js.map +1 -0
  316. package/dist/frontend/ts7/adapter.d.ts +2 -2
  317. package/dist/frontend/ts7/adapter.js +1 -1
  318. package/dist/frontend/ts7/adapter.js.map +1 -1
  319. package/dist/frontend/ts7/checker.d.ts +40 -2
  320. package/dist/frontend/ts7/checker.js +182 -54
  321. package/dist/frontend/ts7/checker.js.map +1 -1
  322. package/dist/frontend/ts7/enums.js +2 -1
  323. package/dist/frontend/ts7/enums.js.map +1 -1
  324. package/dist/frontend/ts7/{program.js → program-adapter.js} +7 -6
  325. package/dist/frontend/ts7/program-adapter.js.map +1 -0
  326. package/dist/frontend/ts7/world-check.d.ts +1 -3
  327. package/dist/frontend/ts7/world-check.js +3 -1
  328. package/dist/frontend/ts7/world-check.js.map +1 -1
  329. package/dist/frontend/tsc-codes.d.ts +8 -0
  330. package/dist/frontend/tsc-codes.js +109 -0
  331. package/dist/frontend/tsc-codes.js.map +1 -0
  332. package/dist/frontend/{types.d.ts → type-mapper.d.ts} +2 -25
  333. package/dist/frontend/{types.js → type-mapper.js} +31 -52
  334. package/dist/frontend/type-mapper.js.map +1 -0
  335. package/dist/frontend/workspace-registry.d.ts +26 -0
  336. package/dist/frontend/workspace-registry.js +71 -0
  337. package/dist/frontend/workspace-registry.js.map +1 -0
  338. package/dist/index.d.ts +115 -30
  339. package/dist/index.js +650 -146
  340. package/dist/index.js.map +1 -1
  341. package/dist/ir/analysis.d.ts +37 -0
  342. package/dist/ir/analysis.js +118 -0
  343. package/dist/ir/analysis.js.map +1 -0
  344. package/dist/ir/analysis.test.d.ts +1 -0
  345. package/dist/ir/analysis.test.js +21 -0
  346. package/dist/ir/analysis.test.js.map +1 -0
  347. package/dist/ir/build.d.ts +9 -0
  348. package/dist/ir/build.js +33 -0
  349. package/dist/ir/build.js.map +1 -0
  350. package/dist/ir/integer-loops.d.ts +1 -1
  351. package/dist/ir/{nodes.d.ts → ir.d.ts} +60 -26
  352. package/dist/ir/{nodes.js → ir.js} +173 -141
  353. package/dist/ir/ir.js.map +1 -0
  354. package/dist/ir/ir.test.d.ts +1 -0
  355. package/dist/ir/ir.test.js +17 -0
  356. package/dist/ir/ir.test.js.map +1 -0
  357. package/dist/ir/serialize.d.ts +1 -1
  358. package/dist/ir/serialize.js +2 -1
  359. package/dist/ir/serialize.js.map +1 -1
  360. package/dist/ir/validate.d.ts +1 -1
  361. package/dist/ir/validate.js +16 -5
  362. package/dist/ir/validate.js.map +1 -1
  363. package/dist/library/cache-primitives.d.ts +24 -0
  364. package/dist/library/cache-primitives.js +83 -0
  365. package/dist/library/cache-primitives.js.map +1 -0
  366. package/dist/library/cache-reset.test.d.ts +1 -0
  367. package/dist/library/cache-reset.test.js +47 -0
  368. package/dist/library/cache-reset.test.js.map +1 -0
  369. package/dist/library/compiler-self-identity.d.ts +23 -0
  370. package/dist/library/compiler-self-identity.js +84 -0
  371. package/dist/library/compiler-self-identity.js.map +1 -0
  372. package/dist/library/fence-eval.d.ts +3 -1
  373. package/dist/library/fence-eval.js +5 -1
  374. package/dist/library/fence-eval.js.map +1 -1
  375. package/dist/library/int-infer.d.ts +1 -36
  376. package/dist/library/int-infer.js +17 -17
  377. package/dist/library/int-infer.js.map +1 -1
  378. package/dist/library/{early-cache.d.ts → library-cache.d.ts} +1 -1
  379. package/dist/library/{early-cache.js → library-cache.js} +37 -111
  380. package/dist/library/library-cache.js.map +1 -0
  381. package/dist/library/library-cache.test.d.ts +1 -0
  382. package/dist/library/{early-cache.test.js → library-cache.test.js} +2 -2
  383. package/dist/library/library-cache.test.js.map +1 -0
  384. package/dist/library/{profile.d.ts → library-profile.d.ts} +4 -7
  385. package/dist/library/{profile.js → library-profile.js} +18 -11
  386. package/dist/library/library-profile.js.map +1 -0
  387. package/dist/library/semantic-source.js +2 -1
  388. package/dist/library/semantic-source.js.map +1 -1
  389. package/dist/library/sidecar.d.ts +5 -2
  390. package/dist/library/sidecar.js +11 -5
  391. package/dist/library/sidecar.js.map +1 -1
  392. package/dist/startup-cache.d.ts +6 -0
  393. package/dist/startup-cache.js +6 -0
  394. package/dist/startup-cache.js.map +1 -0
  395. package/package.json +7 -2
  396. package/surface-manifest.json +1 -1
  397. package/dist/backend/cc-cache.test.js.map +0 -1
  398. package/dist/backend/cc.js.map +0 -1
  399. package/dist/backend/emission/emit-async.js.map +0 -1
  400. package/dist/backend/emission/emit-exprs.d.ts +0 -13
  401. package/dist/backend/emission/emit-exprs.js +0 -7407
  402. package/dist/backend/emission/emit-exprs.js.map +0 -1
  403. package/dist/backend/emission/emit-island.js.map +0 -1
  404. package/dist/backend/emission/emit-shapes.js.map +0 -1
  405. package/dist/backend/emission/emit-stmts.js.map +0 -1
  406. package/dist/backend/emission/emit-types.js.map +0 -1
  407. package/dist/backend/emission/emit-walkers.js.map +0 -1
  408. package/dist/backend/emission/emitter.js.map +0 -1
  409. package/dist/backend/emission/may-throw.js.map +0 -1
  410. package/dist/backend/library-identity.js.map +0 -1
  411. package/dist/ffi/profile.js.map +0 -1
  412. package/dist/frontend/lowering/lower-emitter.js.map +0 -1
  413. package/dist/frontend/shared.d.ts +0 -82
  414. package/dist/frontend/shared.js +0 -308
  415. package/dist/frontend/shared.js.map +0 -1
  416. package/dist/frontend/ts7/program.js.map +0 -1
  417. package/dist/frontend/types.js.map +0 -1
  418. package/dist/ir/nodes.js.map +0 -1
  419. package/dist/library/early-cache.js.map +0 -1
  420. package/dist/library/early-cache.test.js.map +0 -1
  421. package/dist/library/profile.js.map +0 -1
  422. /package/dist/backend/{cc-cache.test.d.ts → c/types.test.d.ts} +0 -0
  423. /package/dist/{library/early-cache.test.d.ts → backend/llvm/shapes.test.d.ts} +0 -0
  424. /package/dist/frontend/ts7/{program.d.ts → program-adapter.d.ts} +0 -0
@@ -5,7 +5,8 @@ import { chmod, cp, mkdir, mkdtemp, readdir, readFile, rm, stat, symlink, utimes
5
5
  import { tmpdir } from "node:os";
6
6
  import { delimiter, join } from "node:path";
7
7
  import { afterAll, afterEach, expect, test as vitestTest } from "vitest";
8
- import { cacheTargetIdentity, ccVersionOnce, compileC, compileLibArchive, implicitDependencyProbeIncludes, parseLinkTraceFiles, resolveBuildCacheRoot, runtimeFingerprint, runtimeSrcDir, stageRuntimeObjects, toolchainEnvironmentCachePolicy, toolchainEnvironmentFingerprint, vendorCacheBuildIdentity, vendorCacheTargetFlavor, } from "./cc.js";
8
+ import { cacheTargetIdentity, ccVersion, compileC, compileLibArchive, executableSectionEliminationFlags, executableNativeEnvironmentFingerprint, implicitDependencyProbeIncludes, parseLinkTraceFiles, resolveBuildCacheRoot, runtimeFingerprint, runtimeSrcDir, stageRuntimeObjects, supportedNativeCacheWarmProfiles, toolchainEnvironmentCachePolicy, toolchainEnvironmentFingerprint, vendorCacheBuildIdentity, vendorCacheTargetFlavor, warmNativeCaches, } from "./native-toolchain.js";
9
+ import { splitLlvmProgram } from "./llvm/split.js";
9
10
  const scratch = [];
10
11
  const TEST_CACHE_IDENTITY = "cc-cache-tests-v1";
11
12
  const originalStableToolchain = process.env["SCRIPTC_TEST_STABLE_TOOLCHAIN"];
@@ -41,10 +42,20 @@ const test = Object.assign((name, ...args) => Reflect.apply(cacheTestSelected(na
41
42
  const trustInstrumentedCompilerWrapper = () => {
42
43
  process.env["SCRIPTC_TEST_TRUST_COMPILER_WRAPPER"] = "1";
43
44
  };
44
- const zigExecutable = (process.env["PATH"] ?? "")
45
+ const executableOnPath = (name) => (process.env["PATH"] ?? "")
45
46
  .split(delimiter)
46
- .map((entry) => join(entry === "" ? process.cwd() : entry, "zig"))
47
+ .map((entry) => join(entry === "" ? process.cwd() : entry, name))
47
48
  .find((candidate) => existsSync(candidate));
49
+ const zigExecutable = executableOnPath("zig");
50
+ const clangExecutable = executableOnPath("clang");
51
+ const arExecutable = executableOnPath("ar");
52
+ const ldExecutable = executableOnPath("ld");
53
+ const objcopyExecutable = executableOnPath("objcopy");
54
+ const nativeShardMergeAvailable = process.platform === "darwin"
55
+ ? ldExecutable !== undefined
56
+ : process.platform === "linux"
57
+ ? ldExecutable !== undefined && objcopyExecutable !== undefined
58
+ : process.platform === "win32" && process.arch === "x64";
48
59
  const completeArtifacts = async (root, kind) => (await readdir(join(root, kind))).filter((name) => !name.endsWith(".sha256"));
49
60
  const cacheTreeBytes = async (root) => {
50
61
  let total = 0;
@@ -85,6 +96,51 @@ test("the production cache root follows overrides, platform defaults, and the ha
85
96
  expect(resolveBuildCacheRoot({}, "darwin", "/Users/tester")).toBe("/Users/tester/Library/Caches/scriptc/build");
86
97
  expect(resolveBuildCacheRoot({ LOCALAPPDATA: "/Users/tester/AppData/Local" }, "win32", "/Users/tester")).toBe("/Users/tester/AppData/Local/scriptc/cache/build");
87
98
  });
99
+ test("executable section elimination flags are target-aware and never enter library recipes", () => {
100
+ expect(executableSectionEliminationFlags("darwin")).toEqual({
101
+ compile: [],
102
+ link: ["-Wl,-dead_strip"],
103
+ });
104
+ expect(executableSectionEliminationFlags("linux")).toEqual({
105
+ compile: ["-ffunction-sections", "-fdata-sections"],
106
+ link: ["-Wl,--gc-sections"],
107
+ });
108
+ expect(executableSectionEliminationFlags("win32")).toEqual({
109
+ compile: ["-ffunction-sections", "-fdata-sections"],
110
+ link: ["-Wl,--gc-sections"],
111
+ });
112
+ expect(executableSectionEliminationFlags("wasi")).toEqual({ compile: [], link: [] });
113
+ });
114
+ test.skipIf(process.platform === "win32")("the early executable identity follows a compiler selected behind a stable driver", async () => {
115
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-effective-compiler-"));
116
+ scratch.push(dir);
117
+ const binDir = join(dir, "bin");
118
+ const selector = join(dir, "selected");
119
+ const firstCompiler = join(dir, "clang-first");
120
+ const secondCompiler = join(dir, "clang-second");
121
+ await mkdir(binDir);
122
+ await Promise.all([
123
+ writeFile(firstCompiler, "#!/bin/sh\nexit 0\n"),
124
+ writeFile(secondCompiler, "#!/bin/sh\nexit 0\n"),
125
+ writeFile(join(binDir, "clang"), "#!/bin/sh\nselected=$(cat \"$SCRIPTC_TEST_EFFECTIVE_COMPILER\")\nprintf '\"%s\" \"-cc1\"\\n' \"$selected\" >&2\n"),
126
+ writeFile(selector, `${firstCompiler}\n`),
127
+ ]);
128
+ await Promise.all([
129
+ chmod(firstCompiler, 0o755),
130
+ chmod(secondCompiler, 0o755),
131
+ chmod(join(binDir, "clang"), 0o755),
132
+ ]);
133
+ const env = {
134
+ ...process.env,
135
+ PATH: `${binDir}${delimiter}${process.env["PATH"] ?? ""}`,
136
+ SCRIPTC_TEST_EFFECTIVE_COMPILER: selector,
137
+ };
138
+ const first = await executableNativeEnvironmentFingerprint(env);
139
+ expect(await executableNativeEnvironmentFingerprint(env)).toBe(first);
140
+ await writeFile(selector, `${secondCompiler}\n`);
141
+ const second = await executableNativeEnvironmentFingerprint(env);
142
+ expect(second).not.toBe(first);
143
+ });
88
144
  test("native cache identities separate host architectures while cross targets remain explicit", () => {
89
145
  expect(cacheTargetIdentity({ target: null }, "darwin", "arm64")).toBe("native:darwin:arm64");
90
146
  expect(cacheTargetIdentity({ target: null }, "darwin", "x64")).toBe("native:darwin:x64");
@@ -93,6 +149,95 @@ test("native cache identities separate host architectures while cross targets re
93
149
  expect(vendorCacheTargetFlavor({ target: null }, "darwin", "x64")).toBe("native-darwin-x64");
94
150
  expect(vendorCacheTargetFlavor({ target: "x86_64-linux-gnu.2.36" }, "darwin", "arm64")).toBe("x86_64-linux-gnu.2.36");
95
151
  });
152
+ test.skipIf(process.platform === "win32" || zigExecutable === undefined ||
153
+ clangExecutable === undefined || arExecutable === undefined)("targetless Zig vendor caches are separate from host-clang and reusable", async () => {
154
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-zig-vendor-cache-"));
155
+ scratch.push(dir);
156
+ const cacheRoot = join(dir, "cache");
157
+ const vendorRoot = join(dir, "vendor-cache");
158
+ const cPath = join(dir, "program.c");
159
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
160
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
161
+ const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
162
+ const oldCc = process.env["SCRIPTC_CC"];
163
+ const oldTarget = process.env["SCRIPTC_TARGET"];
164
+ try {
165
+ await writeFile(cPath, "int main(void) { return 0; }\n");
166
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
167
+ process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = vendorRoot;
168
+ delete process.env["SCRIPTC_NO_CACHE"];
169
+ delete process.env["SCRIPTC_TARGET"];
170
+ process.env["SCRIPTC_CC"] = "clang";
171
+ await compileC({
172
+ cPath,
173
+ outPath: join(dir, "host"),
174
+ cacheIdentity: TEST_CACHE_IDENTITY,
175
+ dynamic: true,
176
+ net: true,
177
+ http: true,
178
+ tls: true,
179
+ zlib: true,
180
+ // Keep the complete executable tier out of this test: the second Zig
181
+ // invocation must walk the vendor cache and prove its artifacts are
182
+ // reusable independently of the output path.
183
+ systemLibraries: ["m"],
184
+ });
185
+ const hostEngine = (await readdir(vendorRoot)).find((name) => /^3c8f3d689539-plain-/.test(name));
186
+ const hostTls = (await readdir(vendorRoot)).find((name) => name.startsWith("mbedtls-"));
187
+ expect(hostEngine).toBeDefined();
188
+ expect(hostTls).toBeDefined();
189
+ process.env["SCRIPTC_CC"] = "zigcc";
190
+ const zigOptions = {
191
+ cPath,
192
+ cacheIdentity: TEST_CACHE_IDENTITY,
193
+ dynamic: true,
194
+ net: true,
195
+ http: true,
196
+ tls: true,
197
+ zlib: true,
198
+ systemLibraries: ["m"],
199
+ };
200
+ await compileC({ ...zigOptions, outPath: join(dir, "zig-first") });
201
+ let vendorEntries = await readdir(vendorRoot);
202
+ expect(vendorEntries.filter((name) => /^3c8f3d689539-plain-/.test(name))).toHaveLength(2);
203
+ expect(vendorEntries.filter((name) => name.startsWith("mbedtls-")).length).toBe(2);
204
+ // Host clang uses system zlib, so only the Zig build materializes a zlib
205
+ // object family in the shared vendor root.
206
+ expect(vendorEntries.filter((name) => name.startsWith("zlib-")).length).toBe(1);
207
+ // Invalidate the host archive. A targetless Zig rebuild must continue to
208
+ // use the separately keyed Zig archive instead of repairing or consuming
209
+ // the host-clang entry.
210
+ await writeFile(join(vendorRoot, hostEngine, "libqjs.a"), "host archive intentionally invalid\n");
211
+ await writeFile(join(vendorRoot, hostTls, "libmbedtls.a"), "host archive intentionally invalid\n");
212
+ await compileC({ ...zigOptions, outPath: join(dir, "zig-second") });
213
+ vendorEntries = await readdir(vendorRoot);
214
+ expect(vendorEntries.filter((name) => /^3c8f3d689539-plain-/.test(name))).toHaveLength(2);
215
+ expect(await readFile(join(vendorRoot, hostEngine, "libqjs.a"), "utf8")).toBe("host archive intentionally invalid\n");
216
+ expect(await readFile(join(vendorRoot, hostTls, "libmbedtls.a"), "utf8")).toBe("host archive intentionally invalid\n");
217
+ }
218
+ finally {
219
+ if (oldCacheDir === undefined)
220
+ delete process.env["SCRIPTC_CACHE_DIR"];
221
+ else
222
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
223
+ if (oldNoCache === undefined)
224
+ delete process.env["SCRIPTC_NO_CACHE"];
225
+ else
226
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
227
+ if (oldVendorCacheDir === undefined)
228
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
229
+ else
230
+ process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
231
+ if (oldCc === undefined)
232
+ delete process.env["SCRIPTC_CC"];
233
+ else
234
+ process.env["SCRIPTC_CC"] = oldCc;
235
+ if (oldTarget === undefined)
236
+ delete process.env["SCRIPTC_TARGET"];
237
+ else
238
+ process.env["SCRIPTC_TARGET"] = oldTarget;
239
+ }
240
+ }, 600_000);
96
241
  test("Zig COFF dry-run parsing retains every linker input on its single command line", async () => {
97
242
  const dir = await mkdtemp(join(tmpdir(), "scriptc-link-trace-"));
98
243
  scratch.push(dir);
@@ -142,9 +287,11 @@ test("the toolchain environment joins cache identities", () => {
142
287
  completeArtifacts: false,
143
288
  runtimeObjects: false,
144
289
  });
290
+ // scriptc invokes its compiler and archiver directly; conventional build-
291
+ // system variables do not alter those commands.
145
292
  expect(toolchainEnvironmentCachePolicy({ CFLAGS: "-I/headers" })).toEqual({
146
- completeArtifacts: false,
147
- runtimeObjects: false,
293
+ completeArtifacts: true,
294
+ runtimeObjects: true,
148
295
  });
149
296
  expect(toolchainEnvironmentCachePolicy({ ZIG_LIB_DIR: "/zig/lib" })).toEqual({
150
297
  completeArtifacts: false,
@@ -154,8 +301,9 @@ test("the toolchain environment joins cache identities", () => {
154
301
  completeArtifacts: false,
155
302
  runtimeObjects: false,
156
303
  });
157
- expect(vendorCacheBuildIdentity(base, "compiler-one")).not.toBe(vendorCacheBuildIdentity(base, "compiler-two"));
158
- expect(vendorCacheBuildIdentity(base, "compiler-one")).not.toBe(vendorCacheBuildIdentity(toolchainEnvironmentFingerprint({ MACOSX_DEPLOYMENT_TARGET: "11.0" }), "compiler-one"));
304
+ expect(vendorCacheBuildIdentity(base, "compiler-one", "sources-one")).not.toBe(vendorCacheBuildIdentity(base, "compiler-two", "sources-one"));
305
+ expect(vendorCacheBuildIdentity(base, "compiler-one", "sources-one")).not.toBe(vendorCacheBuildIdentity(toolchainEnvironmentFingerprint({ MACOSX_DEPLOYMENT_TARGET: "11.0" }), "compiler-one", "sources-one"));
306
+ expect(vendorCacheBuildIdentity(base, "compiler-one", "sources-one")).not.toBe(vendorCacheBuildIdentity(base, "compiler-one", "sources-two"));
159
307
  });
160
308
  test.skipIf(process.platform === "win32")("compiler version probes use and remove a private working directory", async () => {
161
309
  const dir = await mkdtemp(join(tmpdir(), "scriptc-version-probe-"));
@@ -174,7 +322,7 @@ printf 'fake zig cc version probe\n'
174
322
  // Zig 0.16 creates cwd/a.o for `zig cc --version`. Model that side
175
323
  // effect directly so this regression does not depend on Zig being
176
324
  // installed in every generic test shard.
177
- await ccVersionOnce([fakeCompiler, "cc"], `version-probe-${dir}`);
325
+ await ccVersion([fakeCompiler, "cc"], `version-probe-${dir}`);
178
326
  expect(await readdir(probeTmp)).toEqual([]);
179
327
  }
180
328
  finally {
@@ -305,6 +453,69 @@ test("implicit dependency seeds include separately compiled vendor system header
305
453
  // to the native object/cache identity.
306
454
  expect(includes).toContain("<stdatomic.h>");
307
455
  });
456
+ test.skipIf(process.platform === "win32" || clangExecutable === undefined || arExecutable === undefined ||
457
+ ldExecutable === undefined)("public native builds re-probe ccache availability after PATH changes", async () => {
458
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-ccache-reset-"));
459
+ scratch.push(dir);
460
+ const binDir = join(dir, "bin");
461
+ const cacheRoot = join(dir, "cache");
462
+ const cPath = join(dir, "program.c");
463
+ const ccacheLog = join(dir, "ccache.log");
464
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
465
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
466
+ const oldPath = process.env["PATH"];
467
+ const oldCcacheLog = process.env["SCRIPTC_TEST_CCACHE_LOG"];
468
+ try {
469
+ await Promise.all([mkdir(binDir), mkdir(cacheRoot, { mode: 0o700 })]);
470
+ await Promise.all([
471
+ symlink(clangExecutable, join(binDir, "clang")),
472
+ symlink(arExecutable, join(binDir, "ar")),
473
+ symlink(ldExecutable, join(binDir, "ld")),
474
+ writeFile(cPath, "int main(void) { return 0; }\n"),
475
+ ]);
476
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
477
+ process.env["PATH"] = binDir;
478
+ delete process.env["SCRIPTC_NO_CACHE"];
479
+ await compileC({
480
+ cPath,
481
+ outPath: join(dir, "first"),
482
+ cacheIdentity: TEST_CACHE_IDENTITY,
483
+ regex: true,
484
+ });
485
+ await writeFile(join(binDir, "ccache"), `#!/bin/sh
486
+ if [ "$1" = "--version" ]; then exit 0; fi
487
+ printf '%s\n' "$*" >> "$SCRIPTC_TEST_CCACHE_LOG"
488
+ exec "$@"
489
+ `);
490
+ await chmod(join(binDir, "ccache"), 0o755);
491
+ process.env["SCRIPTC_TEST_CCACHE_LOG"] = ccacheLog;
492
+ await compileC({
493
+ cPath,
494
+ outPath: join(dir, "second"),
495
+ cacheIdentity: TEST_CACHE_IDENTITY,
496
+ dynamic: true,
497
+ });
498
+ expect((await readFile(ccacheLog, "utf8")).trim()).not.toBe("");
499
+ }
500
+ finally {
501
+ if (oldCacheDir === undefined)
502
+ delete process.env["SCRIPTC_CACHE_DIR"];
503
+ else
504
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
505
+ if (oldNoCache === undefined)
506
+ delete process.env["SCRIPTC_NO_CACHE"];
507
+ else
508
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
509
+ if (oldPath === undefined)
510
+ delete process.env["PATH"];
511
+ else
512
+ process.env["PATH"] = oldPath;
513
+ if (oldCcacheLog === undefined)
514
+ delete process.env["SCRIPTC_TEST_CCACHE_LOG"];
515
+ else
516
+ process.env["SCRIPTC_TEST_CCACHE_LOG"] = oldCcacheLog;
517
+ }
518
+ });
308
519
  test.skipIf(process.platform === "win32")("opaque compiler wrappers bypass persistent caches", async () => {
309
520
  const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-compiler-wrapper-"));
310
521
  scratch.push(dir);
@@ -1682,41 +1893,46 @@ test("the hard disable bypasses vendor prerequisite caches", async () => {
1682
1893
  process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
1683
1894
  }
1684
1895
  });
1685
- test.skipIf(process.platform !== "darwin")("vendor object caches separate deployment-target environments", async () => {
1686
- const dir = await mkdtemp(join(tmpdir(), "scriptc-vendor-environment-"));
1896
+ test.skipIf(process.platform === "win32")("shared vendor prerequisites re-key when vendored source bytes change", async () => {
1897
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-vendor-source-identity-"));
1687
1898
  scratch.push(dir);
1688
1899
  const cacheRoot = join(dir, "cache");
1689
- const vendorCacheRoot = join(dir, "vendor-cache");
1900
+ const fakeRuntimeRoot = join(dir, "runtime");
1901
+ const fakeRuntime = join(fakeRuntimeRoot, "src");
1902
+ const originalRuntime = runtimeSrcDir();
1690
1903
  const cPath = join(dir, "program.c");
1691
1904
  const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
1692
1905
  const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
1906
+ const oldRuntimeDir = process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"];
1693
1907
  const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1694
- const oldDeploymentTarget = process.env["MACOSX_DEPLOYMENT_TARGET"];
1695
1908
  try {
1909
+ await Promise.all([
1910
+ cp(originalRuntime, fakeRuntime, { recursive: true }),
1911
+ mkdir(join(fakeRuntimeRoot, "vendor"), { recursive: true }).then(() => Promise.all(["quickjs-ng", "ryu"].map((name) => cp(join(originalRuntime, "..", "vendor", name), join(fakeRuntimeRoot, "vendor", name), { recursive: true })))),
1912
+ ]);
1696
1913
  process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
1697
- process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = vendorCacheRoot;
1914
+ process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"] = fakeRuntime;
1698
1915
  delete process.env["SCRIPTC_NO_CACHE"];
1916
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1699
1917
  await writeFile(cPath, "int main(void) { return 0; }\n");
1700
- process.env["MACOSX_DEPLOYMENT_TARGET"] = "14.0";
1701
1918
  await compileC({
1702
1919
  cPath,
1703
- outPath: join(dir, "newer"),
1920
+ outPath: join(dir, "first"),
1704
1921
  cacheIdentity: TEST_CACHE_IDENTITY,
1705
1922
  regex: true,
1706
1923
  });
1707
- process.env["MACOSX_DEPLOYMENT_TARGET"] = "11.0";
1924
+ const vendorRoot = join(cacheRoot, "vendor");
1925
+ expect((await readdir(vendorRoot)).filter((name) => name.includes("-lre-"))).toHaveLength(1);
1926
+ const libregexp = join(fakeRuntimeRoot, "vendor", "quickjs-ng", "libregexp.c");
1927
+ await writeFile(libregexp, `${await readFile(libregexp, "utf8")}\n/* cache identity probe */\n`);
1928
+ await writeFile(cPath, "int main(void) { return 0; } /* second */\n");
1708
1929
  await compileC({
1709
1930
  cPath,
1710
- outPath: join(dir, "older"),
1931
+ outPath: join(dir, "second"),
1711
1932
  cacheIdentity: TEST_CACHE_IDENTITY,
1712
1933
  regex: true,
1713
1934
  });
1714
- const lreCaches = (await readdir(vendorCacheRoot)).filter((name) => name.includes("-lre-"));
1715
- expect(lreCaches).toHaveLength(2);
1716
- const minVersions = lreCaches.map((cache) => execFileSync("vtool", ["-show-build", join(vendorCacheRoot, cache, "libregexp.o")], {
1717
- encoding: "utf8",
1718
- }).match(/minos\s+(\S+)/)?.[1]);
1719
- expect(new Set(minVersions)).toEqual(new Set(["11.0", "14.0"]));
1935
+ expect((await readdir(vendorRoot)).filter((name) => name.includes("-lre-"))).toHaveLength(2);
1720
1936
  }
1721
1937
  finally {
1722
1938
  if (oldCacheDir === undefined)
@@ -1727,56 +1943,48 @@ test.skipIf(process.platform !== "darwin")("vendor object caches separate deploy
1727
1943
  delete process.env["SCRIPTC_NO_CACHE"];
1728
1944
  else
1729
1945
  process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
1946
+ if (oldRuntimeDir === undefined)
1947
+ delete process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"];
1948
+ else
1949
+ process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"] = oldRuntimeDir;
1730
1950
  if (oldVendorCacheDir === undefined)
1731
1951
  delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1732
1952
  else
1733
1953
  process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
1734
- if (oldDeploymentTarget === undefined)
1735
- delete process.env["MACOSX_DEPLOYMENT_TARGET"];
1736
- else
1737
- process.env["MACOSX_DEPLOYMENT_TARGET"] = oldDeploymentTarget;
1738
1954
  }
1739
- });
1740
- test("complete binary hits precede missing vendor prerequisite materialization", async () => {
1741
- const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-vendor-hit-"));
1955
+ }, 120_000);
1956
+ test.skipIf(process.platform === "win32")("uncached arbitrary C keeps vendor prerequisites outside a read-only runtime package", async () => {
1957
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-readonly-runtime-"));
1742
1958
  scratch.push(dir);
1743
- const cacheRoot = join(dir, "cache");
1744
- const vendorCacheRoot = join(dir, "vendor-cache");
1959
+ const fakeRuntimeRoot = join(dir, "runtime");
1960
+ const fakeRuntime = join(fakeRuntimeRoot, "src");
1961
+ const fakeVendor = join(fakeRuntimeRoot, "vendor");
1962
+ const originalRuntime = runtimeSrcDir();
1745
1963
  const cPath = join(dir, "program.c");
1746
1964
  const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
1747
1965
  const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
1966
+ const oldRuntimeDir = process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"];
1748
1967
  const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1749
1968
  try {
1750
- process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
1751
- process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = vendorCacheRoot;
1969
+ await Promise.all([
1970
+ cp(originalRuntime, fakeRuntime, { recursive: true }),
1971
+ mkdir(fakeVendor, { recursive: true }).then(() => Promise.all(["quickjs-ng", "ryu"].map((name) => cp(join(originalRuntime, "..", "vendor", name), join(fakeVendor, name), {
1972
+ recursive: true,
1973
+ })))),
1974
+ ]);
1975
+ process.env["SCRIPTC_CACHE_DIR"] = join(dir, "cache");
1976
+ process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"] = fakeRuntime;
1752
1977
  delete process.env["SCRIPTC_NO_CACHE"];
1753
- await writeFile(cPath, '#include <stdio.h>\nint main(void) { puts("cached"); return 0; }\n');
1754
- const firstOut = join(dir, "first");
1755
- await compileC({
1756
- cPath,
1757
- outPath: firstOut,
1758
- cacheIdentity: TEST_CACHE_IDENTITY,
1759
- regex: true,
1760
- });
1761
- expect(execFileSync(firstOut, { encoding: "utf8" }).trim()).toBe("cached");
1762
- expect(await completeArtifacts(cacheRoot, "bin")).toHaveLength(1);
1763
- expect((await stat(vendorCacheRoot)).isDirectory()).toBe(true);
1764
- // Simulate a package reinstall: the per-package vendor build disappears,
1765
- // while the per-user complete executable remains. The hit must not
1766
- // recreate that directory even though the compiler remains available for
1767
- // fresh dependency discovery.
1768
- await rm(vendorCacheRoot, { recursive: true, force: true });
1769
- const hitOut = firstOut;
1770
- await compileC({
1771
- cPath,
1772
- outPath: hitOut,
1773
- cacheIdentity: TEST_CACHE_IDENTITY,
1774
- regex: true,
1775
- });
1776
- expect(execFileSync(hitOut, { encoding: "utf8" }).trim()).toBe("cached");
1777
- await expect(stat(vendorCacheRoot)).rejects.toMatchObject({ code: "ENOENT" });
1978
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1979
+ await writeFile(cPath, "int main(void) { return 0; }\n");
1980
+ await chmod(fakeVendor, 0o555);
1981
+ const outPath = join(dir, "program");
1982
+ await compileC({ cPath, outPath, regex: true });
1983
+ expect((await stat(outPath)).isFile()).toBe(true);
1984
+ await expect(stat(join(fakeVendor, ".cache"))).rejects.toMatchObject({ code: "ENOENT" });
1778
1985
  }
1779
1986
  finally {
1987
+ await chmod(fakeVendor, 0o755).catch(() => undefined);
1780
1988
  if (oldCacheDir === undefined)
1781
1989
  delete process.env["SCRIPTC_CACHE_DIR"];
1782
1990
  else
@@ -1785,37 +1993,41 @@ test("complete binary hits precede missing vendor prerequisite materialization",
1785
1993
  delete process.env["SCRIPTC_NO_CACHE"];
1786
1994
  else
1787
1995
  process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
1996
+ if (oldRuntimeDir === undefined)
1997
+ delete process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"];
1998
+ else
1999
+ process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"] = oldRuntimeDir;
1788
2000
  if (oldVendorCacheDir === undefined)
1789
2001
  delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1790
2002
  else
1791
2003
  process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
1792
2004
  }
1793
- });
1794
- test("arbitrary C bypasses persistent artifacts so same-path header edits cannot go stale", async () => {
1795
- const dir = await mkdtemp(join(tmpdir(), "scriptc-arbitrary-c-cache-"));
2005
+ }, 120_000);
2006
+ test("native cache warming seeds exact runtime and vendor families without complete binaries", async () => {
2007
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-warm-"));
1796
2008
  scratch.push(dir);
1797
2009
  const cacheRoot = join(dir, "cache");
1798
- const cPath = join(dir, "program.c");
1799
- const headerPath = join(dir, "value.h");
2010
+ const vendorCacheRoot = join(cacheRoot, "vendor");
1800
2011
  const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
1801
2012
  const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
2013
+ const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1802
2014
  try {
1803
2015
  process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
2016
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1804
2017
  delete process.env["SCRIPTC_NO_CACHE"];
1805
- await writeFile(cPath, '#include <stdio.h>\n#include "value.h"\nint main(void) { puts(VALUE); return 0; }\n');
1806
- await writeFile(headerPath, '#define VALUE "one"\n');
1807
- const firstOut = join(dir, "first");
1808
- await compileC({ cPath, outPath: firstOut });
1809
- expect(execFileSync(firstOut, { encoding: "utf8" }).trim()).toBe("one");
1810
- await writeFile(headerPath, '#define VALUE "two"\n');
1811
- const secondOut = join(dir, "second");
1812
- await compileC({ cPath, outPath: secondOut });
1813
- expect(execFileSync(secondOut, { encoding: "utf8" }).trim()).toBe("two");
1814
- await expect(stat(join(cacheRoot, "bin"))).rejects.toMatchObject({ code: "ENOENT" });
1815
- const archiveOut = join(dir, "program.lib.a");
1816
- await compileLibArchive({ cPath, outPath: archiveOut });
1817
- expect((await stat(archiveOut)).isFile()).toBe(true);
1818
- await expect(stat(join(cacheRoot, "lib"))).rejects.toMatchObject({ code: "ENOENT" });
2018
+ const warmed = await warmNativeCaches({ profiles: ["runtime", "tls", "dynamic"] });
2019
+ expect(warmed.cacheRoot).toBe(cacheRoot);
2020
+ expect(warmed.profiles.map(({ profile }) => profile)).toEqual(["runtime", "tls", "dynamic"]);
2021
+ expect(warmed.profiles.every(({ elapsedMs }) => elapsedMs >= 0)).toBe(true);
2022
+ expect((await readdir(join(cacheRoot, "obj"), { withFileTypes: true })).filter((e) => e.isDirectory())).toHaveLength(3);
2023
+ expect(await readdir(join(cacheRoot, "bin")).catch(() => [])).toEqual([]);
2024
+ const vendorEntries = await readdir(vendorCacheRoot);
2025
+ const tlsEntry = vendorEntries.find((name) => name.startsWith("mbedtls-"));
2026
+ const engineEntry = vendorEntries.find((name) => /^3c8f3d689539-plain-/.test(name));
2027
+ expect(tlsEntry).toBeDefined();
2028
+ expect(engineEntry).toBeDefined();
2029
+ expect((await stat(join(vendorCacheRoot, tlsEntry, "libmbedtls.a.sha256"))).isFile()).toBe(true);
2030
+ expect((await stat(join(vendorCacheRoot, engineEntry, "libqjs.a.sha256"))).isFile()).toBe(true);
1819
2031
  }
1820
2032
  finally {
1821
2033
  if (oldCacheDir === undefined)
@@ -1826,34 +2038,44 @@ test("arbitrary C bypasses persistent artifacts so same-path header edits cannot
1826
2038
  delete process.env["SCRIPTC_NO_CACHE"];
1827
2039
  else
1828
2040
  process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
2041
+ if (oldVendorCacheDir === undefined)
2042
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
2043
+ else
2044
+ process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
1829
2045
  }
1830
- });
1831
- test("cached translation units keep the compiler-visible source path in their identity", async () => {
1832
- const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-source-path-"));
2046
+ }, 120_000);
2047
+ test("damaged shared vendor archives are rejected and rebuilt before linking", async () => {
2048
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-vendor-integrity-"));
1833
2049
  scratch.push(dir);
1834
2050
  const cacheRoot = join(dir, "cache");
1835
- const aPath = join(dir, "a.c");
1836
- const bPath = join(dir, "b.c");
1837
- const headerPath = join(dir, "cache_path_header.h");
1838
- const source = '#include <stdio.h>\n#include "cache_path_header.h"\nint main(void) { puts(__FILE__); puts(CACHE_PATH_HEADER); return 0; }\n';
2051
+ const cPath = join(dir, "program.c");
2052
+ const outPath = join(dir, "program");
1839
2053
  const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
1840
2054
  const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
2055
+ const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
2056
+ const corruption = Buffer.from("scriptc-corrupt-vendor-archive\n");
1841
2057
  try {
1842
2058
  process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
1843
2059
  delete process.env["SCRIPTC_NO_CACHE"];
1844
- await Promise.all([
1845
- writeFile(aPath, source),
1846
- writeFile(bPath, source),
1847
- writeFile(headerPath, '#define CACHE_PATH_HEADER "header"\n'),
1848
- ]);
1849
- const aOut = join(dir, "a-out", "program");
1850
- const bOut = join(dir, "b-out", "program");
1851
- await Promise.all([mkdir(join(dir, "a-out")), mkdir(join(dir, "b-out"))]);
1852
- await compileC({ cPath: aPath, outPath: aOut, cacheIdentity: TEST_CACHE_IDENTITY });
1853
- await compileC({ cPath: bPath, outPath: bOut, cacheIdentity: TEST_CACHE_IDENTITY });
1854
- expect(execFileSync(aOut, { encoding: "utf8" }).trim()).toBe(`${aPath}\nheader`);
1855
- expect(execFileSync(bOut, { encoding: "utf8" }).trim()).toBe(`${bPath}\nheader`);
1856
- expect(await completeArtifacts(cacheRoot, "bin")).toHaveLength(2);
2060
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
2061
+ await writeFile(cPath, "int main(void) { return 0; }\n");
2062
+ await warmNativeCaches({ profiles: ["dynamic"] });
2063
+ const vendorRoot = join(cacheRoot, "vendor");
2064
+ const engineDir = (await readdir(vendorRoot)).find((name) => /^3c8f3d689539-plain-/.test(name));
2065
+ expect(engineDir).toBeDefined();
2066
+ const engineArchive = join(vendorRoot, engineDir, "libqjs.a");
2067
+ expect((await stat(`${engineArchive}.sha256`)).isFile()).toBe(true);
2068
+ await writeFile(engineArchive, corruption);
2069
+ await compileC({
2070
+ cPath,
2071
+ outPath,
2072
+ cacheIdentity: TEST_CACHE_IDENTITY,
2073
+ dynamic: true,
2074
+ });
2075
+ expect((await stat(outPath)).isFile()).toBe(true);
2076
+ const repaired = await readFile(engineArchive);
2077
+ expect(repaired).not.toEqual(corruption);
2078
+ expect((await readFile(`${engineArchive}.sha256`, "utf8")).trim()).toBe(createHash("sha256").update(repaired).digest("hex"));
1857
2079
  }
1858
2080
  finally {
1859
2081
  if (oldCacheDir === undefined)
@@ -1864,36 +2086,358 @@ test("cached translation units keep the compiler-visible source path in their id
1864
2086
  delete process.env["SCRIPTC_NO_CACHE"];
1865
2087
  else
1866
2088
  process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
2089
+ if (oldVendorCacheDir === undefined)
2090
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
2091
+ else
2092
+ process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
1867
2093
  }
1868
- });
1869
- test.skipIf(process.platform === "win32")("cache publication compiles the exact translation-unit bytes used by its key", async () => {
1870
- const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-tu-race-"));
2094
+ }, 120_000);
2095
+ test("dynamic builds promote staged vendor archives before bounded LRU eviction", async () => {
2096
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-vendor-lru-"));
1871
2097
  scratch.push(dir);
1872
2098
  const cacheRoot = join(dir, "cache");
1873
- const binDir = join(dir, "bin");
1874
2099
  const cPath = join(dir, "program.c");
1875
- const signal = join(dir, "compile-started");
1876
- const release = join(dir, "compile-release");
2100
+ const outPath = join(dir, "program");
1877
2101
  const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
1878
2102
  const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
1879
- const oldPath = process.env["PATH"];
1880
- const oldRealClang = process.env["SCRIPTC_TEST_REAL_CLANG"];
1881
- const oldRaceSource = process.env["SCRIPTC_TEST_RACE_SOURCE"];
1882
- const oldRaceSignal = process.env["SCRIPTC_TEST_RACE_SIGNAL"];
1883
- const oldRaceRelease = process.env["SCRIPTC_TEST_RACE_RELEASE"];
1884
- const originalClang = (oldPath ?? "")
1885
- .split(delimiter)
1886
- .map((entry) => join(entry === "" ? process.cwd() : entry, "clang"))
1887
- .find((candidate) => existsSync(candidate));
1888
- expect(originalClang).toBeDefined();
1889
- const source = (value) => `#include <stdio.h>\nint main(void) { puts("${value}"); return 0; }\n`;
1890
- const waitForSignal = async () => {
1891
- const deadline = Date.now() + 10_000;
1892
- while (!existsSync(signal)) {
1893
- if (Date.now() > deadline)
1894
- throw new Error("timed out waiting for the compiler wrapper");
1895
- await new Promise((resolveWait) => setTimeout(resolveWait, 10));
1896
- }
2103
+ const oldMax = process.env["SCRIPTC_CACHE_MAX_MB"];
2104
+ const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
2105
+ try {
2106
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
2107
+ delete process.env["SCRIPTC_NO_CACHE"];
2108
+ delete process.env["SCRIPTC_CACHE_MAX_MB"];
2109
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
2110
+ await writeFile(cPath, "int main(void) { return 0; }\n");
2111
+ await warmNativeCaches({ profiles: ["dynamic"] });
2112
+ const vendorRoot = join(cacheRoot, "vendor");
2113
+ const engineDir = (await readdir(vendorRoot)).find((name) => /^3c8f3d689539-plain-/.test(name));
2114
+ expect(engineDir).toBeDefined();
2115
+ const engineArchive = join(vendorRoot, engineDir, "libqjs.a");
2116
+ const initialBytes = await cacheTreeBytes(cacheRoot);
2117
+ const capMb = Math.max(4, Math.ceil(initialBytes * 2 / (1024 * 1024)));
2118
+ const filler = join(cacheRoot, "filler.bin");
2119
+ const staleArchiveTime = new Date("2000-01-01T00:00:00.000Z");
2120
+ const fillerTime = new Date("2001-01-01T00:00:00.000Z");
2121
+ await writeFile(filler, Buffer.alloc(capMb * 1024 * 1024));
2122
+ await utimes(engineArchive, staleArchiveTime, staleArchiveTime);
2123
+ await utimes(filler, fillerTime, fillerTime);
2124
+ process.env["SCRIPTC_CACHE_MAX_MB"] = String(capMb);
2125
+ await compileC({
2126
+ cPath,
2127
+ outPath,
2128
+ cacheIdentity: TEST_CACHE_IDENTITY,
2129
+ dynamic: true,
2130
+ });
2131
+ expect((await stat(outPath)).isFile()).toBe(true);
2132
+ expect((await stat(engineArchive)).mtimeMs).toBeGreaterThan(fillerTime.getTime());
2133
+ expect(await cacheTreeBytes(cacheRoot)).toBeLessThanOrEqual(capMb * 1024 * 1024);
2134
+ }
2135
+ finally {
2136
+ if (oldCacheDir === undefined)
2137
+ delete process.env["SCRIPTC_CACHE_DIR"];
2138
+ else
2139
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
2140
+ if (oldNoCache === undefined)
2141
+ delete process.env["SCRIPTC_NO_CACHE"];
2142
+ else
2143
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
2144
+ if (oldMax === undefined)
2145
+ delete process.env["SCRIPTC_CACHE_MAX_MB"];
2146
+ else
2147
+ process.env["SCRIPTC_CACHE_MAX_MB"] = oldMax;
2148
+ if (oldVendorCacheDir === undefined)
2149
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
2150
+ else
2151
+ process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
2152
+ }
2153
+ }, 120_000);
2154
+ test("native cache warming follows the hard cache disable", async () => {
2155
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
2156
+ try {
2157
+ process.env["SCRIPTC_NO_CACHE"] = "1";
2158
+ await expect(warmNativeCaches({ profiles: ["runtime"] })).rejects.toThrow("native build cache is disabled");
2159
+ }
2160
+ finally {
2161
+ if (oldNoCache === undefined)
2162
+ delete process.env["SCRIPTC_NO_CACHE"];
2163
+ else
2164
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
2165
+ }
2166
+ });
2167
+ test("native cache warming refuses environments that disable persistent objects", async () => {
2168
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-warm-disabled-"));
2169
+ scratch.push(dir);
2170
+ const cacheRoot = join(dir, "cache");
2171
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
2172
+ const oldCpath = process.env["CPATH"];
2173
+ try {
2174
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
2175
+ process.env["CPATH"] = dir;
2176
+ await expect(warmNativeCaches({ profiles: ["runtime"] })).rejects.toThrow("requires a persistently cacheable compiler environment");
2177
+ expect(await readdir(cacheRoot)).toEqual([]);
2178
+ }
2179
+ finally {
2180
+ if (oldCacheDir === undefined)
2181
+ delete process.env["SCRIPTC_CACHE_DIR"];
2182
+ else
2183
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
2184
+ if (oldCpath === undefined)
2185
+ delete process.env["CPATH"];
2186
+ else
2187
+ process.env["CPATH"] = oldCpath;
2188
+ }
2189
+ });
2190
+ test("native cache warm profiles follow target capabilities", () => {
2191
+ expect(supportedNativeCacheWarmProfiles({
2192
+ argv: ["zig", "cc"],
2193
+ target: "wasm32-wasi",
2194
+ zigTarget: "wasm32-wasi",
2195
+ targetArgs: [],
2196
+ linkArgs: [],
2197
+ })).toEqual([]);
2198
+ expect(supportedNativeCacheWarmProfiles({
2199
+ argv: ["zig", "cc"],
2200
+ target: "aarch64-apple-ios",
2201
+ zigTarget: "aarch64-ios.15.0",
2202
+ targetArgs: [],
2203
+ linkArgs: [],
2204
+ })).toEqual([]);
2205
+ });
2206
+ test("parallel native cache warming fails cleanly when the bounded cache cannot retain its profiles", async () => {
2207
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-warm-cap-"));
2208
+ scratch.push(dir);
2209
+ const cacheRoot = join(dir, "cache");
2210
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
2211
+ const oldMax = process.env["SCRIPTC_CACHE_MAX_MB"];
2212
+ try {
2213
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
2214
+ process.env["SCRIPTC_CACHE_MAX_MB"] = "2.5";
2215
+ await expect(warmNativeCaches()).rejects.toThrow("SCRIPTC_CACHE_MAX_MB is too small to retain the requested native cache warm profiles");
2216
+ expect(await readdir(cacheRoot)).not.toEqual([]);
2217
+ }
2218
+ finally {
2219
+ if (oldCacheDir === undefined)
2220
+ delete process.env["SCRIPTC_CACHE_DIR"];
2221
+ else
2222
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
2223
+ if (oldMax === undefined)
2224
+ delete process.env["SCRIPTC_CACHE_MAX_MB"];
2225
+ else
2226
+ process.env["SCRIPTC_CACHE_MAX_MB"] = oldMax;
2227
+ }
2228
+ }, 120_000);
2229
+ test.skipIf(process.platform !== "darwin")("vendor object caches separate deployment-target environments", async () => {
2230
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-vendor-environment-"));
2231
+ scratch.push(dir);
2232
+ const cacheRoot = join(dir, "cache");
2233
+ const vendorCacheRoot = join(dir, "vendor-cache");
2234
+ const cPath = join(dir, "program.c");
2235
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
2236
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
2237
+ const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
2238
+ const oldDeploymentTarget = process.env["MACOSX_DEPLOYMENT_TARGET"];
2239
+ try {
2240
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
2241
+ process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = vendorCacheRoot;
2242
+ delete process.env["SCRIPTC_NO_CACHE"];
2243
+ await writeFile(cPath, "int main(void) { return 0; }\n");
2244
+ process.env["MACOSX_DEPLOYMENT_TARGET"] = "14.0";
2245
+ await compileC({
2246
+ cPath,
2247
+ outPath: join(dir, "newer"),
2248
+ cacheIdentity: TEST_CACHE_IDENTITY,
2249
+ regex: true,
2250
+ });
2251
+ process.env["MACOSX_DEPLOYMENT_TARGET"] = "11.0";
2252
+ await compileC({
2253
+ cPath,
2254
+ outPath: join(dir, "older"),
2255
+ cacheIdentity: TEST_CACHE_IDENTITY,
2256
+ regex: true,
2257
+ });
2258
+ const lreCaches = (await readdir(vendorCacheRoot)).filter((name) => name.includes("-lre-"));
2259
+ expect(lreCaches).toHaveLength(2);
2260
+ const minVersions = lreCaches.map((cache) => execFileSync("vtool", ["-show-build", join(vendorCacheRoot, cache, "libregexp.o")], {
2261
+ encoding: "utf8",
2262
+ }).match(/minos\s+(\S+)/)?.[1]);
2263
+ expect(new Set(minVersions)).toEqual(new Set(["11.0", "14.0"]));
2264
+ }
2265
+ finally {
2266
+ if (oldCacheDir === undefined)
2267
+ delete process.env["SCRIPTC_CACHE_DIR"];
2268
+ else
2269
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
2270
+ if (oldNoCache === undefined)
2271
+ delete process.env["SCRIPTC_NO_CACHE"];
2272
+ else
2273
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
2274
+ if (oldVendorCacheDir === undefined)
2275
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
2276
+ else
2277
+ process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
2278
+ if (oldDeploymentTarget === undefined)
2279
+ delete process.env["MACOSX_DEPLOYMENT_TARGET"];
2280
+ else
2281
+ process.env["MACOSX_DEPLOYMENT_TARGET"] = oldDeploymentTarget;
2282
+ }
2283
+ });
2284
+ test("complete binary hits precede missing vendor prerequisite materialization", async () => {
2285
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-vendor-hit-"));
2286
+ scratch.push(dir);
2287
+ const cacheRoot = join(dir, "cache");
2288
+ const vendorCacheRoot = join(dir, "vendor-cache");
2289
+ const cPath = join(dir, "program.c");
2290
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
2291
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
2292
+ const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
2293
+ try {
2294
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
2295
+ process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = vendorCacheRoot;
2296
+ delete process.env["SCRIPTC_NO_CACHE"];
2297
+ await writeFile(cPath, '#include <stdio.h>\nint main(void) { puts("cached"); return 0; }\n');
2298
+ const firstOut = join(dir, "first");
2299
+ await compileC({
2300
+ cPath,
2301
+ outPath: firstOut,
2302
+ cacheIdentity: TEST_CACHE_IDENTITY,
2303
+ regex: true,
2304
+ });
2305
+ expect(execFileSync(firstOut, { encoding: "utf8" }).trim()).toBe("cached");
2306
+ expect(await completeArtifacts(cacheRoot, "bin")).toHaveLength(1);
2307
+ expect((await stat(vendorCacheRoot)).isDirectory()).toBe(true);
2308
+ // Simulate a package reinstall: the per-package vendor build disappears,
2309
+ // while the per-user complete executable remains. The hit must not
2310
+ // recreate that directory even though the compiler remains available for
2311
+ // fresh dependency discovery.
2312
+ await rm(vendorCacheRoot, { recursive: true, force: true });
2313
+ const hitOut = firstOut;
2314
+ await compileC({
2315
+ cPath,
2316
+ outPath: hitOut,
2317
+ cacheIdentity: TEST_CACHE_IDENTITY,
2318
+ regex: true,
2319
+ });
2320
+ expect(execFileSync(hitOut, { encoding: "utf8" }).trim()).toBe("cached");
2321
+ await expect(stat(vendorCacheRoot)).rejects.toMatchObject({ code: "ENOENT" });
2322
+ }
2323
+ finally {
2324
+ if (oldCacheDir === undefined)
2325
+ delete process.env["SCRIPTC_CACHE_DIR"];
2326
+ else
2327
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
2328
+ if (oldNoCache === undefined)
2329
+ delete process.env["SCRIPTC_NO_CACHE"];
2330
+ else
2331
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
2332
+ if (oldVendorCacheDir === undefined)
2333
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
2334
+ else
2335
+ process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
2336
+ }
2337
+ });
2338
+ test("arbitrary C bypasses persistent artifacts so same-path header edits cannot go stale", async () => {
2339
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-arbitrary-c-cache-"));
2340
+ scratch.push(dir);
2341
+ const cacheRoot = join(dir, "cache");
2342
+ const cPath = join(dir, "program.c");
2343
+ const headerPath = join(dir, "value.h");
2344
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
2345
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
2346
+ try {
2347
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
2348
+ delete process.env["SCRIPTC_NO_CACHE"];
2349
+ await writeFile(cPath, '#include <stdio.h>\n#include "value.h"\nint main(void) { puts(VALUE); return 0; }\n');
2350
+ await writeFile(headerPath, '#define VALUE "one"\n');
2351
+ const firstOut = join(dir, "first");
2352
+ await compileC({ cPath, outPath: firstOut });
2353
+ expect(execFileSync(firstOut, { encoding: "utf8" }).trim()).toBe("one");
2354
+ await writeFile(headerPath, '#define VALUE "two"\n');
2355
+ const secondOut = join(dir, "second");
2356
+ await compileC({ cPath, outPath: secondOut });
2357
+ expect(execFileSync(secondOut, { encoding: "utf8" }).trim()).toBe("two");
2358
+ await expect(stat(join(cacheRoot, "bin"))).rejects.toMatchObject({ code: "ENOENT" });
2359
+ const archiveOut = join(dir, "program.lib.a");
2360
+ await compileLibArchive({ cPath, outPath: archiveOut });
2361
+ expect((await stat(archiveOut)).isFile()).toBe(true);
2362
+ await expect(stat(join(cacheRoot, "lib"))).rejects.toMatchObject({ code: "ENOENT" });
2363
+ }
2364
+ finally {
2365
+ if (oldCacheDir === undefined)
2366
+ delete process.env["SCRIPTC_CACHE_DIR"];
2367
+ else
2368
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
2369
+ if (oldNoCache === undefined)
2370
+ delete process.env["SCRIPTC_NO_CACHE"];
2371
+ else
2372
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
2373
+ }
2374
+ });
2375
+ test("cached translation units keep the compiler-visible source path in their identity", async () => {
2376
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-source-path-"));
2377
+ scratch.push(dir);
2378
+ const cacheRoot = join(dir, "cache");
2379
+ const aPath = join(dir, "a.c");
2380
+ const bPath = join(dir, "b.c");
2381
+ const headerPath = join(dir, "cache_path_header.h");
2382
+ const source = '#include <stdio.h>\n#include "cache_path_header.h"\nint main(void) { puts(__FILE__); puts(CACHE_PATH_HEADER); return 0; }\n';
2383
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
2384
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
2385
+ try {
2386
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
2387
+ delete process.env["SCRIPTC_NO_CACHE"];
2388
+ await Promise.all([
2389
+ writeFile(aPath, source),
2390
+ writeFile(bPath, source),
2391
+ writeFile(headerPath, '#define CACHE_PATH_HEADER "header"\n'),
2392
+ ]);
2393
+ const aOut = join(dir, "a-out", "program");
2394
+ const bOut = join(dir, "b-out", "program");
2395
+ await Promise.all([mkdir(join(dir, "a-out")), mkdir(join(dir, "b-out"))]);
2396
+ await compileC({ cPath: aPath, outPath: aOut, cacheIdentity: TEST_CACHE_IDENTITY });
2397
+ await compileC({ cPath: bPath, outPath: bOut, cacheIdentity: TEST_CACHE_IDENTITY });
2398
+ expect(execFileSync(aOut, { encoding: "utf8" }).trim()).toBe(`${aPath}\nheader`);
2399
+ expect(execFileSync(bOut, { encoding: "utf8" }).trim()).toBe(`${bPath}\nheader`);
2400
+ expect(await completeArtifacts(cacheRoot, "bin")).toHaveLength(2);
2401
+ }
2402
+ finally {
2403
+ if (oldCacheDir === undefined)
2404
+ delete process.env["SCRIPTC_CACHE_DIR"];
2405
+ else
2406
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
2407
+ if (oldNoCache === undefined)
2408
+ delete process.env["SCRIPTC_NO_CACHE"];
2409
+ else
2410
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
2411
+ }
2412
+ });
2413
+ test.skipIf(process.platform === "win32")("cache publication compiles the exact translation-unit bytes used by its key", async () => {
2414
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-tu-race-"));
2415
+ scratch.push(dir);
2416
+ const cacheRoot = join(dir, "cache");
2417
+ const binDir = join(dir, "bin");
2418
+ const cPath = join(dir, "program.c");
2419
+ const signal = join(dir, "compile-started");
2420
+ const release = join(dir, "compile-release");
2421
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
2422
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
2423
+ const oldPath = process.env["PATH"];
2424
+ const oldRealClang = process.env["SCRIPTC_TEST_REAL_CLANG"];
2425
+ const oldRaceSource = process.env["SCRIPTC_TEST_RACE_SOURCE"];
2426
+ const oldRaceSignal = process.env["SCRIPTC_TEST_RACE_SIGNAL"];
2427
+ const oldRaceRelease = process.env["SCRIPTC_TEST_RACE_RELEASE"];
2428
+ const originalClang = (oldPath ?? "")
2429
+ .split(delimiter)
2430
+ .map((entry) => join(entry === "" ? process.cwd() : entry, "clang"))
2431
+ .find((candidate) => existsSync(candidate));
2432
+ expect(originalClang).toBeDefined();
2433
+ const source = (value) => `#include <stdio.h>\nint main(void) { puts("${value}"); return 0; }\n`;
2434
+ const waitForSignal = async () => {
2435
+ const deadline = Date.now() + 10_000;
2436
+ while (!existsSync(signal)) {
2437
+ if (Date.now() > deadline)
2438
+ throw new Error("timed out waiting for the compiler wrapper");
2439
+ await new Promise((resolveWait) => setTimeout(resolveWait, 10));
2440
+ }
1897
2441
  };
1898
2442
  try {
1899
2443
  await mkdir(binDir);
@@ -2183,7 +2727,11 @@ done
2183
2727
  exec "$SCRIPTC_TEST_REAL_CLANG" "$@"
2184
2728
  `);
2185
2729
  await chmod(wrapper, 0o755);
2186
- await writeFile(cPath, "int main(void) { return 0; }\n");
2730
+ // Keep the injected datum reachable. With executable section GC an
2731
+ // unreferenced test-only global is rightly removed before the binary
2732
+ // assertion below can observe it.
2733
+ await writeFile(cPath, "extern int scriptc_runtime_race_marker(void);\n" +
2734
+ "int main(void) { return scriptc_runtime_race_marker(); }\n");
2187
2735
  process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
2188
2736
  process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"] = fakeRuntime;
2189
2737
  process.env["SCRIPTC_TEST_REAL_CLANG"] = realClang;
@@ -2208,7 +2756,11 @@ exec "$SCRIPTC_TEST_REAL_CLANG" "$@"
2208
2756
  throw new Error("timed out waiting for runtime compile");
2209
2757
  await new Promise((resolveWait) => setTimeout(resolveWait, 10));
2210
2758
  }
2211
- await writeFile(raceSource, `${originalRuntimeSource}\nconst char scriptc_runtime_race_marker[] = "${marker}";\n`);
2759
+ await writeFile(raceSource, `${originalRuntimeSource}\n` +
2760
+ `volatile const char scriptc_runtime_race_marker_data[] = "${marker}";\n` +
2761
+ "int scriptc_runtime_race_marker(void) {\n" +
2762
+ " return scriptc_runtime_race_marker_data[0] == '\\0';\n" +
2763
+ "}\n");
2212
2764
  await writeFile(release, "go");
2213
2765
  await firstBuild;
2214
2766
  expect((await readFile(firstOut)).includes(Buffer.from(marker))).toBe(true);
@@ -2434,7 +2986,78 @@ test("frontend-generated same-output builds no-op only while output and dependen
2434
2986
  delete process.env["SCRIPTC_NO_CACHE"];
2435
2987
  else
2436
2988
  process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
2437
- process.umask(oldUmask);
2989
+ process.umask(oldUmask);
2990
+ }
2991
+ });
2992
+ test("pre-section-GC output-local stamps cannot restore an old executable", async () => {
2993
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-section-gc-local-stamp-"));
2994
+ scratch.push(dir);
2995
+ const cacheRoot = join(dir, "cache");
2996
+ const cPath = join(dir, "program.c");
2997
+ const outPath = join(dir, "program");
2998
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
2999
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3000
+ try {
3001
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
3002
+ delete process.env["SCRIPTC_NO_CACHE"];
3003
+ await writeFile(cPath, "int main(void) { return 0; }\n");
3004
+ await compileC({ cPath, outPath, cacheIdentity: "scriptc-generated-v1" });
3005
+ const stampPath = join(cacheRoot, "local", createHash("sha256").update(outPath).digest("hex"));
3006
+ const current = JSON.parse(await readFile(stampPath, "utf8"));
3007
+ const legacy = { ...current, key: "old-non-gc-output", version: 1 };
3008
+ await writeFile(stampPath, `${JSON.stringify(legacy)}\n`);
3009
+ const pinnedTime = new Date("2001-01-01T00:00:00.000Z");
3010
+ await utimes(outPath, pinnedTime, pinnedTime);
3011
+ await compileC({ cPath, outPath, cacheIdentity: "scriptc-generated-v1" });
3012
+ expect((await stat(outPath)).mtimeMs).toBeGreaterThan(pinnedTime.getTime());
3013
+ }
3014
+ finally {
3015
+ if (oldCacheDir === undefined)
3016
+ delete process.env["SCRIPTC_CACHE_DIR"];
3017
+ else
3018
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
3019
+ if (oldNoCache === undefined)
3020
+ delete process.env["SCRIPTC_NO_CACHE"];
3021
+ else
3022
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
3023
+ }
3024
+ });
3025
+ test("artifact-ready callbacks expose native dependencies on builds and validated hits", async () => {
3026
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-artifact-ready-"));
3027
+ scratch.push(dir);
3028
+ const cacheRoot = join(dir, "cache");
3029
+ const cPath = join(dir, "program.c");
3030
+ const outPath = join(dir, "program");
3031
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
3032
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3033
+ try {
3034
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
3035
+ delete process.env["SCRIPTC_NO_CACHE"];
3036
+ await writeFile(cPath, "int main(void) { return 0; }\n");
3037
+ const observations = [];
3038
+ const build = () => compileC({
3039
+ cPath,
3040
+ outPath,
3041
+ cacheIdentity: "scriptc-generated-v1",
3042
+ onArtifactReady: async ({ dependencies }) => {
3043
+ observations.push(dependencies.map((dependency) => dependency.path));
3044
+ },
3045
+ });
3046
+ await build();
3047
+ await build();
3048
+ expect(observations).toHaveLength(2);
3049
+ expect(observations[0].length).toBeGreaterThan(0);
3050
+ expect(observations[1]).toEqual(observations[0]);
3051
+ }
3052
+ finally {
3053
+ if (oldCacheDir === undefined)
3054
+ delete process.env["SCRIPTC_CACHE_DIR"];
3055
+ else
3056
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
3057
+ if (oldNoCache === undefined)
3058
+ delete process.env["SCRIPTC_NO_CACHE"];
3059
+ else
3060
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
2438
3061
  }
2439
3062
  });
2440
3063
  test.skipIf(process.platform === "win32")("output-local hits follow symlinked header targets", async () => {
@@ -2515,7 +3138,7 @@ test("fresh processes preserve output-local dependency coverage after source edi
2515
3138
  delete childEnv["SCRIPTC_NO_CACHE"];
2516
3139
  delete childEnv["SCRIPTC_TEST_STABLE_TOOLCHAIN"];
2517
3140
  delete childEnv["SCRIPTC_TEST_TRUST_COMPILER_WRAPPER"];
2518
- await writeFile(helperPath, `import { compileC } from ${JSON.stringify(new URL("./cc.ts", import.meta.url).href)};\n` +
3141
+ await writeFile(helperPath, `import { compileC } from ${JSON.stringify(new URL("./native-toolchain.ts", import.meta.url).href)};\n` +
2519
3142
  `await compileC({ cPath: process.argv[2], outPath: process.argv[3], cacheIdentity: "scriptc-generated-v1" });\n`);
2520
3143
  const compileInFreshProcess = () => {
2521
3144
  execFileSync(process.execPath, ["--import", "tsx", helperPath, cPath, outPath], {
@@ -2867,6 +3490,553 @@ test("library identity edits reuse the cached large program object", async () =>
2867
3490
  process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
2868
3491
  }
2869
3492
  });
3493
+ test.skipIf(!nativeShardMergeAvailable)("LLVM library shards reuse unaffected program objects after a localized edit", async () => {
3494
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-program-shards-"));
3495
+ scratch.push(dir);
3496
+ const cacheRoot = join(dir, "cache");
3497
+ const cPath = join(dir, "program.ll");
3498
+ const outPath = join(dir, "program.lib.a");
3499
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
3500
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3501
+ const source = (value) => `%Pair = type { i64, i64 }
3502
+ @private_value = internal global i64 ${value}
3503
+
3504
+ define internal i64 @left() {
3505
+ entry:
3506
+ %v = load i64, ptr @private_value
3507
+ ret i64 %v
3508
+ }
3509
+
3510
+ define internal i64 @right() {
3511
+ entry:
3512
+ %v = call i64 @left()
3513
+ ret i64 %v
3514
+ }
3515
+
3516
+ define i64 @scriptc_public_value() {
3517
+ entry:
3518
+ %v = call i64 @right()
3519
+ ret i64 %v
3520
+ }
3521
+ `;
3522
+ try {
3523
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
3524
+ delete process.env["SCRIPTC_NO_CACHE"];
3525
+ await mkdir(cacheRoot, { mode: 0o700 });
3526
+ const build = async (value) => {
3527
+ const programSource = source(value);
3528
+ const split = splitLlvmProgram(programSource, { minimumBytes: 0, targetBytes: 64 * 1024 });
3529
+ expect(split?.shards).toHaveLength(3);
3530
+ await writeFile(cPath, programSource);
3531
+ await compileLibArchive({
3532
+ cPath,
3533
+ programSource,
3534
+ programShards: split.shards,
3535
+ programPublicSymbols: split.publicSymbols,
3536
+ outPath,
3537
+ cacheIdentity: TEST_CACHE_IDENTITY,
3538
+ optimization: "dev",
3539
+ });
3540
+ };
3541
+ await build(7);
3542
+ const initial = (await readdir(join(cacheRoot, "program-shard")))
3543
+ .filter((name) => !name.endsWith(".sha256"));
3544
+ expect(initial).toHaveLength(3);
3545
+ await build(9);
3546
+ const after = (await readdir(join(cacheRoot, "program-shard")))
3547
+ .filter((name) => !name.endsWith(".sha256"));
3548
+ expect(after).toHaveLength(4);
3549
+ expect(initial.filter((name) => after.includes(name))).toHaveLength(3);
3550
+ const old = new Date("2000-01-01T00:00:00.000Z");
3551
+ await Promise.all(after.map((name) => utimes(join(cacheRoot, "program-shard", name), old, old)));
3552
+ await build(9);
3553
+ for (const name of after) {
3554
+ // The completed archive hit returns before shard lookup/merge; exact
3555
+ // repeats do not even touch the per-bucket object cache.
3556
+ expect((await stat(join(cacheRoot, "program-shard", name))).mtimeMs).toBe(old.getTime());
3557
+ }
3558
+ const probeSource = join(dir, "probe.c");
3559
+ const probe = join(dir, "probe");
3560
+ await writeFile(probeSource, "#include <stdio.h>\nlong long scriptc_public_value(void);\nint main(void) { printf(\"%lld\\n\", scriptc_public_value()); }\n");
3561
+ execFileSync("clang", [probeSource, outPath, "-lm", "-o", probe]);
3562
+ expect(execFileSync(probe, { encoding: "utf8" })).toBe("9\n");
3563
+ }
3564
+ finally {
3565
+ if (oldCacheDir === undefined)
3566
+ delete process.env["SCRIPTC_CACHE_DIR"];
3567
+ else
3568
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
3569
+ if (oldNoCache === undefined)
3570
+ delete process.env["SCRIPTC_NO_CACHE"];
3571
+ else
3572
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
3573
+ }
3574
+ });
3575
+ test.skipIf(!nativeShardMergeAvailable)("LLVM executable dev shards reuse unaffected program objects after a localized edit", async () => {
3576
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-exe-program-shards-"));
3577
+ scratch.push(dir);
3578
+ const cacheRoot = join(dir, "cache");
3579
+ const cPath = join(dir, "program.ll");
3580
+ const outPath = join(dir, "program");
3581
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
3582
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3583
+ const source = (value) => `@private_value = internal global i64 ${value}
3584
+
3585
+ define internal i64 @left() {
3586
+ entry:
3587
+ %v = load i64, ptr @private_value
3588
+ ret i64 %v
3589
+ }
3590
+
3591
+ define internal i64 @right() {
3592
+ entry:
3593
+ %v = call i64 @left()
3594
+ ret i64 %v
3595
+ }
3596
+
3597
+ define i32 @main() {
3598
+ entry:
3599
+ %v = call i64 @right()
3600
+ %exit = trunc i64 %v to i32
3601
+ ret i32 %exit
3602
+ }
3603
+ `;
3604
+ try {
3605
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
3606
+ delete process.env["SCRIPTC_NO_CACHE"];
3607
+ await mkdir(cacheRoot, { mode: 0o700 });
3608
+ const build = async (value) => {
3609
+ const programSource = source(value);
3610
+ const split = splitLlvmProgram(programSource, { minimumBytes: 0, targetBytes: 64 * 1024 });
3611
+ expect(split?.shards).toHaveLength(3);
3612
+ await writeFile(cPath, programSource);
3613
+ await compileC({
3614
+ cPath,
3615
+ programShards: split.shards,
3616
+ programPublicSymbols: split.publicSymbols,
3617
+ outPath,
3618
+ cacheIdentity: TEST_CACHE_IDENTITY,
3619
+ optimization: "dev",
3620
+ });
3621
+ };
3622
+ await build(7);
3623
+ const initial = (await readdir(join(cacheRoot, "program-shard")))
3624
+ .filter((name) => !name.endsWith(".sha256"));
3625
+ expect(initial).toHaveLength(3);
3626
+ expect(spawnSync(outPath).status).toBe(7);
3627
+ await build(9);
3628
+ const after = (await readdir(join(cacheRoot, "program-shard")))
3629
+ .filter((name) => !name.endsWith(".sha256"));
3630
+ expect(after).toHaveLength(4);
3631
+ expect(initial.filter((name) => after.includes(name))).toHaveLength(3);
3632
+ expect(spawnSync(outPath).status).toBe(9);
3633
+ const old = new Date("2000-01-01T00:00:00.000Z");
3634
+ await Promise.all(after.map((name) => utimes(join(cacheRoot, "program-shard", name), old, old)));
3635
+ await build(9);
3636
+ for (const name of after) {
3637
+ // The completed executable hit returns before shard lookup/merge.
3638
+ expect((await stat(join(cacheRoot, "program-shard", name))).mtimeMs).toBe(old.getTime());
3639
+ }
3640
+ }
3641
+ finally {
3642
+ if (oldCacheDir === undefined)
3643
+ delete process.env["SCRIPTC_CACHE_DIR"];
3644
+ else
3645
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
3646
+ if (oldNoCache === undefined)
3647
+ delete process.env["SCRIPTC_NO_CACHE"];
3648
+ else
3649
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
3650
+ }
3651
+ });
3652
+ test.skipIf(process.platform === "win32" || clangExecutable === undefined ||
3653
+ ldExecutable === undefined || (process.platform === "linux" && objcopyExecutable === undefined))("LLVM executable shards fall back without publishing a shard-derived binary", async () => {
3654
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-exe-program-shard-fallback-"));
3655
+ scratch.push(dir);
3656
+ const binDir = join(dir, "bin");
3657
+ const cacheRoot = join(dir, "cache");
3658
+ const cPath = join(dir, "program.ll");
3659
+ const outPath = join(dir, "program");
3660
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
3661
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3662
+ const oldPath = process.env["PATH"];
3663
+ const oldRealLd = process.env["SCRIPTC_TEST_REAL_LD"];
3664
+ const programSource = [
3665
+ "define i32 @main() {",
3666
+ "entry:",
3667
+ " ret i32 7",
3668
+ "}",
3669
+ "",
3670
+ "define internal i64 @other_value() {",
3671
+ "entry:",
3672
+ " ret i64 9",
3673
+ "}",
3674
+ "",
3675
+ ].join("\n");
3676
+ try {
3677
+ await Promise.all([mkdir(binDir), mkdir(cacheRoot, { mode: 0o700 })]);
3678
+ await Promise.all([
3679
+ symlink(clangExecutable, join(binDir, "clang")),
3680
+ ...(process.platform === "linux"
3681
+ ? [symlink(objcopyExecutable, join(binDir, "objcopy"))]
3682
+ : []),
3683
+ writeFile(join(binDir, "ld"), "#!/bin/sh\nfor arg in \"$@\"; do if [ \"$arg\" = -r ]; then exit 1; fi; done\nexec \"$SCRIPTC_TEST_REAL_LD\" \"$@\"\n"),
3684
+ writeFile(cPath, programSource),
3685
+ ]);
3686
+ await chmod(join(binDir, "ld"), 0o755);
3687
+ process.env["PATH"] = binDir;
3688
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
3689
+ process.env["SCRIPTC_TEST_REAL_LD"] = ldExecutable;
3690
+ delete process.env["SCRIPTC_NO_CACHE"];
3691
+ await compileC({
3692
+ cPath,
3693
+ programShards: [
3694
+ {
3695
+ name: "program-f000.ll",
3696
+ source: "define i32 @main() {\nentry:\n ret i32 7\n}\ndeclare hidden i64 @other_value()\n",
3697
+ },
3698
+ {
3699
+ name: "program-f001.ll",
3700
+ source: "declare i32 @main()\ndefine hidden i64 @other_value() {\nentry:\n ret i64 9\n}\n",
3701
+ },
3702
+ ],
3703
+ programPublicSymbols: ["main"],
3704
+ outPath,
3705
+ cacheIdentity: TEST_CACHE_IDENTITY,
3706
+ optimization: "dev",
3707
+ });
3708
+ expect(spawnSync(outPath).status).toBe(7);
3709
+ // The canonical retry is valid output, but cannot occupy a key describing
3710
+ // a successful shard merge. Repairing the merge tool must retry the mode.
3711
+ expect(await readdir(join(cacheRoot, "bin")).catch(() => [])).toEqual([]);
3712
+ expect(await readdir(join(cacheRoot, "program-shard")).catch(() => [])).toEqual([]);
3713
+ }
3714
+ finally {
3715
+ if (oldCacheDir === undefined)
3716
+ delete process.env["SCRIPTC_CACHE_DIR"];
3717
+ else
3718
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
3719
+ if (oldNoCache === undefined)
3720
+ delete process.env["SCRIPTC_NO_CACHE"];
3721
+ else
3722
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
3723
+ if (oldPath === undefined)
3724
+ delete process.env["PATH"];
3725
+ else
3726
+ process.env["PATH"] = oldPath;
3727
+ if (oldRealLd === undefined)
3728
+ delete process.env["SCRIPTC_TEST_REAL_LD"];
3729
+ else
3730
+ process.env["SCRIPTC_TEST_REAL_LD"] = oldRealLd;
3731
+ }
3732
+ });
3733
+ test.skipIf(!nativeShardMergeAvailable)("LLVM merged-object and archive caches separate shard projections and public-symbol keep sets", async () => {
3734
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-program-shard-abi-"));
3735
+ scratch.push(dir);
3736
+ const cacheRoot = join(dir, "cache");
3737
+ const cPath = join(dir, "program.ll");
3738
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
3739
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3740
+ const programSource = [
3741
+ "define i64 @foo() {",
3742
+ "entry:",
3743
+ " ret i64 1",
3744
+ "}",
3745
+ "",
3746
+ "define i64 @bar() {",
3747
+ "entry:",
3748
+ " ret i64 2",
3749
+ "}",
3750
+ "",
3751
+ ].join("\n");
3752
+ const programShards = [
3753
+ {
3754
+ name: "program-f000.ll",
3755
+ source: "define i64 @foo() {\nentry:\n ret i64 1\n}\ndeclare i64 @bar()\n",
3756
+ },
3757
+ {
3758
+ name: "program-f001.ll",
3759
+ source: "declare i64 @foo()\ndefine i64 @bar() {\nentry:\n ret i64 2\n}\n",
3760
+ },
3761
+ ];
3762
+ try {
3763
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
3764
+ delete process.env["SCRIPTC_NO_CACHE"];
3765
+ await mkdir(cacheRoot, { mode: 0o700 });
3766
+ await writeFile(cPath, programSource);
3767
+ const build = async (symbol, outPath, shards = programShards) => {
3768
+ await compileLibArchive({
3769
+ cPath,
3770
+ programSource,
3771
+ programShards: shards,
3772
+ programPublicSymbols: [symbol],
3773
+ outPath,
3774
+ cacheIdentity: TEST_CACHE_IDENTITY,
3775
+ optimization: "dev",
3776
+ });
3777
+ };
3778
+ await build("foo", join(dir, "foo.lib.a"));
3779
+ const barArchive = join(dir, "bar.lib.a");
3780
+ await build("bar", barArchive);
3781
+ expect(await completeArtifacts(cacheRoot, "lib")).toHaveLength(2);
3782
+ expect((await readdir(join(cacheRoot, "program-obj"))).filter((name) => !name.endsWith(".sha256"))).toHaveLength(2);
3783
+ const probeSource = join(dir, "probe.c");
3784
+ const probe = join(dir, "probe");
3785
+ await writeFile(probeSource, "long long bar(void);\nint main(void) { return bar() != 2; }\n");
3786
+ execFileSync("clang", [probeSource, barArchive, "-lm", "-o", probe]);
3787
+ expect(execFileSync(probe, { encoding: "utf8" })).toBe("");
3788
+ // The canonical TU and ABI stay fixed, but the exact shard projection
3789
+ // changes. The merged-object tier must not reuse the previous layout.
3790
+ await build("bar", join(dir, "bar-commented.lib.a"), [
3791
+ { ...programShards[0], source: `; projection edit\n${programShards[0].source}` },
3792
+ programShards[1],
3793
+ ]);
3794
+ expect(await completeArtifacts(cacheRoot, "lib")).toHaveLength(3);
3795
+ expect((await readdir(join(cacheRoot, "program-obj"))).filter((name) => !name.endsWith(".sha256"))).toHaveLength(3);
3796
+ }
3797
+ finally {
3798
+ if (oldCacheDir === undefined)
3799
+ delete process.env["SCRIPTC_CACHE_DIR"];
3800
+ else
3801
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
3802
+ if (oldNoCache === undefined)
3803
+ delete process.env["SCRIPTC_NO_CACHE"];
3804
+ else
3805
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
3806
+ }
3807
+ });
3808
+ test.skipIf(process.platform === "win32" || clangExecutable === undefined || arExecutable === undefined ||
3809
+ ldExecutable === undefined || (process.platform === "linux" && objcopyExecutable === undefined))("LLVM merged-object caches follow in-place shard merge-tool replacements", async () => {
3810
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-program-shard-merge-tool-"));
3811
+ scratch.push(dir);
3812
+ const binDir = join(dir, "bin");
3813
+ const cacheRoot = join(dir, "cache");
3814
+ const mergeLog = join(dir, "merge.log");
3815
+ const cPath = join(dir, "program.ll");
3816
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
3817
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3818
+ const oldPath = process.env["PATH"];
3819
+ const oldRealLd = process.env["SCRIPTC_TEST_REAL_LD"];
3820
+ const oldMergeLog = process.env["SCRIPTC_TEST_MERGE_LOG"];
3821
+ const programSource = [
3822
+ "define i64 @foo() {",
3823
+ "entry:",
3824
+ " ret i64 1",
3825
+ "}",
3826
+ "",
3827
+ "define i64 @bar() {",
3828
+ "entry:",
3829
+ " ret i64 2",
3830
+ "}",
3831
+ "",
3832
+ ].join("\n");
3833
+ const programShards = [
3834
+ {
3835
+ name: "program-f000.ll",
3836
+ source: "define i64 @foo() {\nentry:\n ret i64 1\n}\ndeclare i64 @bar()\n",
3837
+ },
3838
+ {
3839
+ name: "program-f001.ll",
3840
+ source: "declare i64 @foo()\ndefine i64 @bar() {\nentry:\n ret i64 2\n}\n",
3841
+ },
3842
+ ];
3843
+ const ldWrapper = (generation) => `#!/bin/sh
3844
+ printf '${generation}\\n' >> "$SCRIPTC_TEST_MERGE_LOG"
3845
+ exec "$SCRIPTC_TEST_REAL_LD" "$@"
3846
+ `;
3847
+ try {
3848
+ await Promise.all([mkdir(binDir), mkdir(cacheRoot, { mode: 0o700 })]);
3849
+ await Promise.all([
3850
+ symlink(clangExecutable, join(binDir, "clang")),
3851
+ symlink(arExecutable, join(binDir, "ar")),
3852
+ ...(process.platform === "linux"
3853
+ ? [symlink(objcopyExecutable, join(binDir, "objcopy"))]
3854
+ : []),
3855
+ writeFile(join(binDir, "ld"), ldWrapper("first")),
3856
+ writeFile(cPath, programSource),
3857
+ ]);
3858
+ await chmod(join(binDir, "ld"), 0o755);
3859
+ process.env["PATH"] = binDir;
3860
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
3861
+ process.env["SCRIPTC_TEST_REAL_LD"] = ldExecutable;
3862
+ process.env["SCRIPTC_TEST_MERGE_LOG"] = mergeLog;
3863
+ delete process.env["SCRIPTC_NO_CACHE"];
3864
+ const build = (outPath) => compileLibArchive({
3865
+ cPath,
3866
+ programSource,
3867
+ programShards,
3868
+ programPublicSymbols: ["foo", "bar"],
3869
+ outPath,
3870
+ cacheIdentity: TEST_CACHE_IDENTITY,
3871
+ optimization: "dev",
3872
+ });
3873
+ await build(join(dir, "first.lib.a"));
3874
+ await writeFile(join(binDir, "ld"), ldWrapper("second"));
3875
+ await chmod(join(binDir, "ld"), 0o755);
3876
+ await build(join(dir, "second.lib.a"));
3877
+ expect((await readFile(mergeLog, "utf8")).trim().split("\n")).toEqual(["first", "second"]);
3878
+ expect(await completeArtifacts(cacheRoot, "lib")).toHaveLength(2);
3879
+ expect((await readdir(join(cacheRoot, "program-obj"))).filter((name) => !name.endsWith(".sha256"))).toHaveLength(2);
3880
+ // The compiler and shard IR did not change; only merged outputs split by
3881
+ // localization-tool identity.
3882
+ expect((await readdir(join(cacheRoot, "program-shard"))).filter((name) => !name.endsWith(".sha256"))).toHaveLength(2);
3883
+ }
3884
+ finally {
3885
+ if (oldCacheDir === undefined)
3886
+ delete process.env["SCRIPTC_CACHE_DIR"];
3887
+ else
3888
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
3889
+ if (oldNoCache === undefined)
3890
+ delete process.env["SCRIPTC_NO_CACHE"];
3891
+ else
3892
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
3893
+ if (oldPath === undefined)
3894
+ delete process.env["PATH"];
3895
+ else
3896
+ process.env["PATH"] = oldPath;
3897
+ if (oldRealLd === undefined)
3898
+ delete process.env["SCRIPTC_TEST_REAL_LD"];
3899
+ else
3900
+ process.env["SCRIPTC_TEST_REAL_LD"] = oldRealLd;
3901
+ if (oldMergeLog === undefined)
3902
+ delete process.env["SCRIPTC_TEST_MERGE_LOG"];
3903
+ else
3904
+ process.env["SCRIPTC_TEST_MERGE_LOG"] = oldMergeLog;
3905
+ }
3906
+ });
3907
+ test.skipIf(process.platform === "win32" || clangExecutable === undefined || arExecutable === undefined)("LLVM library shards fall back to the canonical TU without host merge tools", async () => {
3908
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-program-shard-fallback-"));
3909
+ scratch.push(dir);
3910
+ const binDir = join(dir, "bin");
3911
+ const cPath = join(dir, "program.ll");
3912
+ const outPath = join(dir, "program.lib.a");
3913
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
3914
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3915
+ const oldPath = process.env["PATH"];
3916
+ const programSource = "define i64 @public_value() {\nentry:\n ret i64 7\n}\n";
3917
+ try {
3918
+ await mkdir(binDir);
3919
+ await Promise.all([
3920
+ symlink(clangExecutable, join(binDir, "clang")),
3921
+ symlink(arExecutable, join(binDir, "ar")),
3922
+ ]);
3923
+ process.env["PATH"] = binDir;
3924
+ process.env["SCRIPTC_NO_CACHE"] = "1";
3925
+ delete process.env["SCRIPTC_CACHE_DIR"];
3926
+ await writeFile(cPath, programSource);
3927
+ await compileLibArchive({
3928
+ cPath,
3929
+ programSource,
3930
+ // If compilation reaches either shard, this build fails. Missing ld
3931
+ // (and objcopy on Linux) must select the valid canonical TU instead.
3932
+ programShards: [
3933
+ { name: "program-f000.ll", source: "not valid LLVM IR\n" },
3934
+ { name: "program-f001.ll", source: "also not valid LLVM IR\n" },
3935
+ ],
3936
+ programPublicSymbols: ["public_value"],
3937
+ outPath,
3938
+ optimization: "dev",
3939
+ });
3940
+ expect(await readFile(outPath)).not.toHaveLength(0);
3941
+ }
3942
+ finally {
3943
+ if (oldCacheDir === undefined)
3944
+ delete process.env["SCRIPTC_CACHE_DIR"];
3945
+ else
3946
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
3947
+ if (oldNoCache === undefined)
3948
+ delete process.env["SCRIPTC_NO_CACHE"];
3949
+ else
3950
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
3951
+ if (oldPath === undefined)
3952
+ delete process.env["PATH"];
3953
+ else
3954
+ process.env["PATH"] = oldPath;
3955
+ }
3956
+ });
3957
+ test.skipIf(process.platform === "win32" || clangExecutable === undefined || arExecutable === undefined ||
3958
+ (process.platform === "linux" && objcopyExecutable === undefined))("LLVM library shards fall back when an available host merge tool fails", async () => {
3959
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-program-shard-merge-failure-"));
3960
+ scratch.push(dir);
3961
+ const binDir = join(dir, "bin");
3962
+ const cacheRoot = join(dir, "cache");
3963
+ const cPath = join(dir, "program.ll");
3964
+ const outPath = join(dir, "program.lib.a");
3965
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
3966
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3967
+ const oldPath = process.env["PATH"];
3968
+ const programSource = [
3969
+ "define i64 @public_value() {",
3970
+ "entry:",
3971
+ " ret i64 7",
3972
+ "}",
3973
+ "",
3974
+ "define i64 @other_value() {",
3975
+ "entry:",
3976
+ " ret i64 9",
3977
+ "}",
3978
+ "",
3979
+ ].join("\n");
3980
+ try {
3981
+ await Promise.all([mkdir(binDir), mkdir(cacheRoot, { mode: 0o700 })]);
3982
+ await Promise.all([
3983
+ symlink(clangExecutable, join(binDir, "clang")),
3984
+ symlink(arExecutable, join(binDir, "ar")),
3985
+ ...(process.platform === "linux"
3986
+ ? [symlink(objcopyExecutable, join(binDir, "objcopy"))]
3987
+ : []),
3988
+ writeFile(join(binDir, "ld"), "#!/bin/sh\nexit 1\n"),
3989
+ writeFile(cPath, programSource),
3990
+ ]);
3991
+ await chmod(join(binDir, "ld"), 0o755);
3992
+ process.env["PATH"] = binDir;
3993
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
3994
+ delete process.env["SCRIPTC_NO_CACHE"];
3995
+ await compileLibArchive({
3996
+ cPath,
3997
+ programSource,
3998
+ programShards: [
3999
+ {
4000
+ name: "program-f000.ll",
4001
+ source: "define i64 @public_value() {\nentry:\n ret i64 7\n}\ndeclare i64 @other_value()\n",
4002
+ },
4003
+ {
4004
+ name: "program-f001.ll",
4005
+ source: "declare i64 @public_value()\ndefine i64 @other_value() {\nentry:\n ret i64 9\n}\n",
4006
+ },
4007
+ ],
4008
+ programPublicSymbols: ["public_value", "other_value"],
4009
+ outPath,
4010
+ cacheIdentity: TEST_CACHE_IDENTITY,
4011
+ optimization: "dev",
4012
+ });
4013
+ const probeSource = join(dir, "probe.c");
4014
+ const probe = join(dir, "probe");
4015
+ await writeFile(probeSource, "long long public_value(void);\nint main(void) { return public_value() != 7; }\n");
4016
+ execFileSync(clangExecutable, [probeSource, outPath, "-lm", "-o", probe]);
4017
+ expect(execFileSync(probe, { encoding: "utf8" })).toBe("");
4018
+ // The canonical retry is valid for this invocation, but must not occupy a
4019
+ // key describing merged shard bytes. A repaired merge tool retries the
4020
+ // optimization instead of receiving a poisoned completed/object hit.
4021
+ expect(await readdir(join(cacheRoot, "lib")).catch(() => [])).toEqual([]);
4022
+ expect(await readdir(join(cacheRoot, "program-obj")).catch(() => [])).toEqual([]);
4023
+ expect(await readdir(join(cacheRoot, "program-shard")).catch(() => [])).toEqual([]);
4024
+ }
4025
+ finally {
4026
+ if (oldCacheDir === undefined)
4027
+ delete process.env["SCRIPTC_CACHE_DIR"];
4028
+ else
4029
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
4030
+ if (oldNoCache === undefined)
4031
+ delete process.env["SCRIPTC_NO_CACHE"];
4032
+ else
4033
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
4034
+ if (oldPath === undefined)
4035
+ delete process.env["PATH"];
4036
+ else
4037
+ process.env["PATH"] = oldPath;
4038
+ }
4039
+ });
2870
4040
  test.skipIf(process.platform === "win32" || zigExecutable === undefined)("cross-ELF localized archives retain unreferenced identity roots", async () => {
2871
4041
  const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-localized-identity-"));
2872
4042
  scratch.push(dir);
@@ -2933,4 +4103,4 @@ test.skipIf(process.platform === "win32" || zigExecutable === undefined)("cross-
2933
4103
  process.env["SCRIPTC_TARGET"] = oldTarget;
2934
4104
  }
2935
4105
  });
2936
- //# sourceMappingURL=cc-cache.test.js.map
4106
+ //# sourceMappingURL=native-toolchain.test.js.map