xbintsc 0.5.23 → 0.5.32

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 (481) hide show
  1. package/AGENTS.md +11 -2
  2. package/README.md +47 -16
  3. package/README.zh-CN.md +21 -2
  4. package/dist/scripts/support-matrix.d.ts +18 -0
  5. package/dist/scripts/support-matrix.js +141 -0
  6. package/dist/scripts/support-matrix.js.map +1 -0
  7. package/dist/src/ast/common.d.ts +2 -2
  8. package/dist/src/ast/declarations.d.ts +7 -1
  9. package/dist/src/ast/expressions.d.ts +5 -0
  10. package/dist/src/ast/kinds.d.ts +2 -0
  11. package/dist/src/ast/kinds.js +2 -0
  12. package/dist/src/ast/kinds.js.map +1 -1
  13. package/dist/src/binder/binder.d.ts +2 -1
  14. package/dist/src/binder/binder.js +2 -0
  15. package/dist/src/binder/binder.js.map +1 -1
  16. package/dist/src/binder/declarations.d.ts +9 -0
  17. package/dist/src/binder/declarations.js +64 -0
  18. package/dist/src/binder/declarations.js.map +1 -1
  19. package/dist/src/binder/references.js +99 -4
  20. package/dist/src/binder/references.js.map +1 -1
  21. package/dist/src/binder/types.d.ts +32 -0
  22. package/dist/src/binder/types.js.map +1 -1
  23. package/dist/src/canonical.d.ts +29 -0
  24. package/dist/src/canonical.js +35 -0
  25. package/dist/src/canonical.js.map +1 -0
  26. package/dist/src/checker/assignability.d.ts +13 -0
  27. package/dist/src/checker/assignability.js +146 -0
  28. package/dist/src/checker/assignability.js.map +1 -0
  29. package/dist/src/checker/calls.d.ts +13 -0
  30. package/dist/src/checker/calls.js +127 -0
  31. package/dist/src/checker/calls.js.map +1 -0
  32. package/dist/src/checker/checker.d.ts +26 -0
  33. package/dist/src/checker/checker.js +31 -0
  34. package/dist/src/checker/checker.js.map +1 -0
  35. package/dist/src/checker/declarations.d.ts +9 -0
  36. package/dist/src/checker/declarations.js +15 -0
  37. package/dist/src/checker/declarations.js.map +1 -0
  38. package/dist/src/checker/flow.d.ts +21 -0
  39. package/dist/src/checker/flow.js +241 -0
  40. package/dist/src/checker/flow.js.map +1 -0
  41. package/dist/src/checker/globals.d.ts +9 -0
  42. package/dist/src/checker/globals.js +29 -0
  43. package/dist/src/checker/globals.js.map +1 -0
  44. package/dist/src/checker/source.d.ts +38 -0
  45. package/dist/src/checker/source.js +48 -0
  46. package/dist/src/checker/source.js.map +1 -0
  47. package/dist/src/checker/type-infer.d.ts +18 -0
  48. package/dist/src/checker/type-infer.js +173 -0
  49. package/dist/src/checker/type-infer.js.map +1 -0
  50. package/dist/src/checker/type-model.d.ts +73 -0
  51. package/dist/src/checker/type-model.js +151 -0
  52. package/dist/src/checker/type-model.js.map +1 -0
  53. package/dist/src/checker/type-system.d.ts +58 -0
  54. package/dist/src/checker/type-system.js +569 -0
  55. package/dist/src/checker/type-system.js.map +1 -0
  56. package/dist/src/checker/typecheck-types.d.ts +22 -0
  57. package/dist/src/checker/typecheck-types.js +109 -0
  58. package/dist/src/checker/typecheck-types.js.map +1 -0
  59. package/dist/src/checker/typecheck.d.ts +62 -0
  60. package/dist/src/checker/typecheck.js +574 -0
  61. package/dist/src/checker/typecheck.js.map +1 -0
  62. package/dist/src/checker/undefined.d.ts +14 -0
  63. package/dist/src/checker/undefined.js +32 -0
  64. package/dist/src/checker/undefined.js.map +1 -0
  65. package/dist/src/checker/walk.d.ts +10 -0
  66. package/dist/src/checker/walk.js +15 -0
  67. package/dist/src/checker/walk.js.map +1 -0
  68. package/dist/src/cli/help.d.ts +7 -0
  69. package/dist/src/cli/help.js +64 -0
  70. package/dist/src/cli/help.js.map +1 -0
  71. package/dist/src/cli/hints.js +2 -1
  72. package/dist/src/cli/hints.js.map +1 -1
  73. package/dist/src/cli/main.js +12 -48
  74. package/dist/src/cli/main.js.map +1 -1
  75. package/dist/src/codegen/generator/calls/access.js +8 -8
  76. package/dist/src/codegen/generator/calls/access.js.map +1 -1
  77. package/dist/src/codegen/generator/calls/invocation.js +9 -4
  78. package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
  79. package/dist/src/codegen/generator/calls/super.d.ts +9 -1
  80. package/dist/src/codegen/generator/calls/super.js +56 -5
  81. package/dist/src/codegen/generator/calls/super.js.map +1 -1
  82. package/dist/src/codegen/generator/context.d.ts +13 -2
  83. package/dist/src/codegen/generator/context.js +25 -19
  84. package/dist/src/codegen/generator/context.js.map +1 -1
  85. package/dist/src/codegen/generator/debug.js +2 -1
  86. package/dist/src/codegen/generator/debug.js.map +1 -1
  87. package/dist/src/codegen/generator/expressions/primary.d.ts +4 -1
  88. package/dist/src/codegen/generator/expressions/primary.js +68 -8
  89. package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
  90. package/dist/src/codegen/generator/generator.js +11 -1
  91. package/dist/src/codegen/generator/generator.js.map +1 -1
  92. package/dist/src/codegen/generator/module.d.ts +3 -0
  93. package/dist/src/codegen/generator/module.js +116 -26
  94. package/dist/src/codegen/generator/module.js.map +1 -1
  95. package/dist/src/codegen/generator/state.d.ts +11 -1
  96. package/dist/src/codegen/generator/state.js.map +1 -1
  97. package/dist/src/codegen/generator/statements/variables.js +26 -7
  98. package/dist/src/codegen/generator/statements/variables.js.map +1 -1
  99. package/dist/src/codegen/generator/tables.d.ts +2 -0
  100. package/dist/src/codegen/generator/tables.js +4 -0
  101. package/dist/src/codegen/generator/tables.js.map +1 -1
  102. package/dist/src/driver/build-key.d.ts +2 -0
  103. package/dist/src/driver/build-key.js +1 -0
  104. package/dist/src/driver/build-key.js.map +1 -1
  105. package/dist/src/driver/bundler/graph.js +5 -1
  106. package/dist/src/driver/bundler/graph.js.map +1 -1
  107. package/dist/src/driver/bundler/merge.js +16 -1
  108. package/dist/src/driver/bundler/merge.js.map +1 -1
  109. package/dist/src/driver/bundler/types.d.ts +12 -0
  110. package/dist/src/driver/compiler.d.ts +1 -1
  111. package/dist/src/driver/compiler.js +3 -0
  112. package/dist/src/driver/compiler.js.map +1 -1
  113. package/dist/src/driver/fingerprint.js +2 -1
  114. package/dist/src/driver/fingerprint.js.map +1 -1
  115. package/dist/src/driver/frontend.d.ts +8 -6
  116. package/dist/src/driver/frontend.js +4 -0
  117. package/dist/src/driver/frontend.js.map +1 -1
  118. package/dist/src/driver/options.d.ts +6 -0
  119. package/dist/src/driver/version.d.ts +1 -1
  120. package/dist/src/driver/version.js +1 -1
  121. package/dist/src/extensions/gui/css.d.ts +26 -0
  122. package/dist/src/extensions/gui/css.js +100 -0
  123. package/dist/src/extensions/gui/css.js.map +1 -0
  124. package/dist/src/extensions/gui/html.js +28 -7
  125. package/dist/src/extensions/gui/html.js.map +1 -1
  126. package/dist/src/extensions/gui/index.js +79 -2
  127. package/dist/src/extensions/gui/index.js.map +1 -1
  128. package/dist/src/extensions/gui/three.d.ts +16 -0
  129. package/dist/src/extensions/gui/three.js +22 -0
  130. package/dist/src/extensions/gui/three.js.map +1 -0
  131. package/dist/src/extensions/media/index.d.ts +6 -5
  132. package/dist/src/extensions/media/index.js +12 -14
  133. package/dist/src/extensions/media/index.js.map +1 -1
  134. package/dist/src/extensions/registry.d.ts +38 -0
  135. package/dist/src/extensions/registry.js +10 -0
  136. package/dist/src/extensions/registry.js.map +1 -1
  137. package/dist/src/index.d.ts +1 -0
  138. package/dist/src/index.js +1 -0
  139. package/dist/src/index.js.map +1 -1
  140. package/dist/src/parser/declarations/classes.js +7 -0
  141. package/dist/src/parser/declarations/classes.js.map +1 -1
  142. package/dist/src/parser/expressions.js +14 -1
  143. package/dist/src/parser/expressions.js.map +1 -1
  144. package/dist/src/parser/literals.js +6 -2
  145. package/dist/src/parser/literals.js.map +1 -1
  146. package/dist/tests/binder/binder.test.js +29 -0
  147. package/dist/tests/binder/binder.test.js.map +1 -1
  148. package/dist/tests/canonical.test.d.ts +1 -0
  149. package/dist/tests/canonical.test.js +19 -0
  150. package/dist/tests/canonical.test.js.map +1 -0
  151. package/dist/tests/checker/checker.test.d.ts +1 -0
  152. package/dist/tests/checker/checker.test.js +245 -0
  153. package/dist/tests/checker/checker.test.js.map +1 -0
  154. package/dist/tests/cli/check.test.js +20 -0
  155. package/dist/tests/cli/check.test.js.map +1 -1
  156. package/dist/tests/conformance/matrix.test.d.ts +6 -0
  157. package/dist/tests/conformance/matrix.test.js +23 -0
  158. package/dist/tests/conformance/matrix.test.js.map +1 -0
  159. package/dist/tests/conformance/test262/manifest.d.ts +115 -0
  160. package/dist/tests/conformance/test262/manifest.js +257 -0
  161. package/dist/tests/conformance/test262/manifest.js.map +1 -0
  162. package/dist/tests/conformance/test262.test.d.ts +11 -0
  163. package/dist/tests/conformance/test262.test.js +74 -0
  164. package/dist/tests/conformance/test262.test.js.map +1 -0
  165. package/dist/tests/docs/structure.test.js +9 -11
  166. package/dist/tests/docs/structure.test.js.map +1 -1
  167. package/dist/tests/e2e/child-process.test.js +3 -2
  168. package/dist/tests/e2e/child-process.test.js.map +1 -1
  169. package/dist/tests/e2e/compile-builtins.test.js +45 -0
  170. package/dist/tests/e2e/compile-builtins.test.js.map +1 -1
  171. package/dist/tests/e2e/compile-objects-classes.test.js +77 -0
  172. package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
  173. package/dist/tests/e2e/differential-syntax.test.d.ts +5 -0
  174. package/dist/tests/e2e/differential-syntax.test.js +95 -0
  175. package/dist/tests/e2e/differential-syntax.test.js.map +1 -0
  176. package/dist/tests/e2e/differential-values.test.js +15 -0
  177. package/dist/tests/e2e/differential-values.test.js.map +1 -1
  178. package/dist/tests/e2e/gui-browser.test.d.ts +13 -0
  179. package/dist/tests/e2e/gui-browser.test.js +163 -0
  180. package/dist/tests/e2e/gui-browser.test.js.map +1 -0
  181. package/dist/tests/e2e/gui-effects.test.d.ts +6 -0
  182. package/dist/tests/e2e/gui-effects.test.js +262 -0
  183. package/dist/tests/e2e/gui-effects.test.js.map +1 -0
  184. package/dist/tests/e2e/gui-example-browser.test.js +2 -2
  185. package/dist/tests/e2e/gui-example-browser.test.js.map +1 -1
  186. package/dist/tests/e2e/gui-example-canvas.test.js +5 -4
  187. package/dist/tests/e2e/gui-example-canvas.test.js.map +1 -1
  188. package/dist/tests/e2e/gui-example-svg.test.js +4 -3
  189. package/dist/tests/e2e/gui-example-svg.test.js.map +1 -1
  190. package/dist/tests/e2e/gui-example-webgl.test.d.ts +10 -0
  191. package/dist/tests/e2e/gui-example-webgl.test.js +75 -0
  192. package/dist/tests/e2e/gui-example-webgl.test.js.map +1 -0
  193. package/dist/tests/e2e/gui-example-webgl2.test.d.ts +8 -0
  194. package/dist/tests/e2e/gui-example-webgl2.test.js +72 -0
  195. package/dist/tests/e2e/gui-example-webgl2.test.js.map +1 -0
  196. package/dist/tests/e2e/gui-example.test.js +4 -3
  197. package/dist/tests/e2e/gui-example.test.js.map +1 -1
  198. package/dist/tests/e2e/gui-helpers.js +4 -3
  199. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  200. package/dist/tests/e2e/gui-layout.test.js +48 -0
  201. package/dist/tests/e2e/gui-layout.test.js.map +1 -1
  202. package/dist/tests/e2e/gui-media.test.js +10 -8
  203. package/dist/tests/e2e/gui-media.test.js.map +1 -1
  204. package/dist/tests/e2e/gui-webgl.test.d.ts +11 -0
  205. package/dist/tests/e2e/gui-webgl.test.js +179 -0
  206. package/dist/tests/e2e/gui-webgl.test.js.map +1 -0
  207. package/dist/tests/e2e/gui-webgl2.test.d.ts +10 -0
  208. package/dist/tests/e2e/gui-webgl2.test.js +238 -0
  209. package/dist/tests/e2e/gui-webgl2.test.js.map +1 -0
  210. package/dist/tests/e2e/harness.d.ts +9 -0
  211. package/dist/tests/e2e/harness.js +14 -2
  212. package/dist/tests/e2e/harness.js.map +1 -1
  213. package/dist/tests/e2e/native-extensions.test.js +2 -1
  214. package/dist/tests/e2e/native-extensions.test.js.map +1 -1
  215. package/dist/tests/e2e/property-descriptors.test.js +20 -0
  216. package/dist/tests/e2e/property-descriptors.test.js.map +1 -1
  217. package/dist/tests/e2e/unicode-output.test.js +5 -3
  218. package/dist/tests/e2e/unicode-output.test.js.map +1 -1
  219. package/dist/tests/e2e/weak-collections.test.js +16 -2
  220. package/dist/tests/e2e/weak-collections.test.js.map +1 -1
  221. package/dist/tests/extensions/gui.test.js +41 -0
  222. package/dist/tests/extensions/gui.test.js.map +1 -1
  223. package/dist/tests/extensions/media.test.js +7 -3
  224. package/dist/tests/extensions/media.test.js.map +1 -1
  225. package/dist/tests/helpers.d.ts +1 -0
  226. package/dist/tests/helpers.js +1 -0
  227. package/dist/tests/helpers.js.map +1 -1
  228. package/dist/tests/parser/declarations.test.js +20 -0
  229. package/dist/tests/parser/declarations.test.js.map +1 -1
  230. package/dist/tests/parser/expressions.test.js +36 -0
  231. package/dist/tests/parser/expressions.test.js.map +1 -1
  232. package/dist/tests/platform.d.ts +32 -0
  233. package/dist/tests/platform.js +40 -0
  234. package/dist/tests/platform.js.map +1 -0
  235. package/dist/tests/platform.test.d.ts +12 -0
  236. package/dist/tests/platform.test.js +51 -0
  237. package/dist/tests/platform.test.js.map +1 -0
  238. package/dist/tests/version.test.js +4 -4
  239. package/dist/tests/version.test.js.map +1 -1
  240. package/doc/ai/README.md +10 -4
  241. package/doc/ai/build-recipe.md +1 -0
  242. package/doc/ai/cli.md +17 -5
  243. package/doc/ai/contributing.md +59 -2
  244. package/doc/ai/extensions.md +38 -7
  245. package/doc/ai/language-support.md +25 -16
  246. package/doc/ai/troubleshooting.md +12 -8
  247. package/doc/ai/zh-CN/README.md +9 -4
  248. package/doc/ai/zh-CN/build-recipe.md +1 -0
  249. package/doc/ai/zh-CN/cli.md +13 -3
  250. package/doc/ai/zh-CN/contributing.md +49 -3
  251. package/doc/ai/zh-CN/extensions.md +33 -6
  252. package/doc/ai/zh-CN/language-support.md +23 -14
  253. package/doc/ai/zh-CN/troubleshooting.md +10 -6
  254. package/doc/gui/api.md +47 -1
  255. package/doc/gui/build.md +72 -16
  256. package/doc/gui/canvas.md +11 -3
  257. package/doc/gui/dom.md +33 -18
  258. package/doc/gui/known-issues.md +58 -15
  259. package/doc/gui/layout.md +18 -0
  260. package/doc/gui/media.md +17 -13
  261. package/doc/gui/rendering.md +50 -3
  262. package/doc/gui/runtime.md +18 -0
  263. package/doc/gui/webgl.md +256 -0
  264. package/doc/gui-scripts.md +7 -0
  265. package/doc/gui.md +108 -17
  266. package/doc/implemented/binder.md +2 -0
  267. package/doc/implemented/checker.md +117 -0
  268. package/doc/implemented/cli.md +13 -1
  269. package/doc/implemented/codegen.md +3 -2
  270. package/doc/implemented/driver.md +2 -2
  271. package/doc/implemented/operators.md +2 -1
  272. package/doc/implemented/parser.md +3 -2
  273. package/doc/implemented/pipeline.md +8 -1
  274. package/doc/implemented/stdlib.md +2 -1
  275. package/doc/implemented/tests.md +3 -1
  276. package/doc/implemented.md +2 -0
  277. package/doc/npm-package.md +419 -0
  278. package/doc/requirements.md +22 -10
  279. package/doc/support-matrix.md +83 -0
  280. package/doc/unimplemented/classes.md +3 -0
  281. package/doc/unimplemented/deviations.md +3 -2
  282. package/doc/unimplemented/toolchain.md +1 -1
  283. package/doc/unimplemented/types.md +24 -8
  284. package/doc/unimplemented.md +1 -1
  285. package/doc/zh-CN/gui/api.md +43 -1
  286. package/doc/zh-CN/gui/build.md +64 -15
  287. package/doc/zh-CN/gui/canvas.md +9 -2
  288. package/doc/zh-CN/gui/dom.md +20 -7
  289. package/doc/zh-CN/gui/known-issues.md +46 -13
  290. package/doc/zh-CN/gui/layout.md +14 -0
  291. package/doc/zh-CN/gui/media.md +15 -11
  292. package/doc/zh-CN/gui/rendering.md +41 -4
  293. package/doc/zh-CN/gui/runtime.md +16 -0
  294. package/doc/zh-CN/gui/webgl.md +115 -0
  295. package/doc/zh-CN/gui-scripts.md +5 -0
  296. package/doc/zh-CN/gui.md +89 -14
  297. package/doc/zh-CN/implemented/binder.md +2 -0
  298. package/doc/zh-CN/implemented/checker.md +99 -0
  299. package/doc/zh-CN/implemented/cli.md +12 -1
  300. package/doc/zh-CN/implemented/codegen.md +3 -2
  301. package/doc/zh-CN/implemented/driver.md +2 -2
  302. package/doc/zh-CN/implemented/operators.md +1 -1
  303. package/doc/zh-CN/implemented/parser.md +3 -2
  304. package/doc/zh-CN/implemented/pipeline.md +3 -1
  305. package/doc/zh-CN/implemented/stdlib.md +2 -1
  306. package/doc/zh-CN/implemented/tests.md +3 -1
  307. package/doc/zh-CN/implemented.md +2 -0
  308. package/doc/zh-CN/npm-package.md +394 -0
  309. package/doc/zh-CN/post/announce.md +47 -47
  310. package/doc/zh-CN/requirements.md +16 -7
  311. package/doc/zh-CN/support-matrix.md +82 -0
  312. package/doc/zh-CN/unimplemented/classes.md +3 -0
  313. package/doc/zh-CN/unimplemented/deviations.md +3 -2
  314. package/doc/zh-CN/unimplemented/toolchain.md +1 -1
  315. package/doc/zh-CN/unimplemented/types.md +16 -6
  316. package/doc/zh-CN/unimplemented.md +1 -1
  317. package/llms.txt +6 -1
  318. package/package.json +2 -1
  319. package/runtime/ext_gui/audio.cpp +316 -0
  320. package/runtime/ext_gui/browser.cpp +573 -0
  321. package/runtime/ext_gui/browser_escape.cpp +85 -0
  322. package/runtime/ext_gui/browser_loop.cpp +83 -0
  323. package/runtime/ext_gui/canvas.cpp +12 -21
  324. package/runtime/ext_gui/canvas_internal.h +10 -0
  325. package/runtime/ext_gui/canvas_raster.cpp +79 -1
  326. package/runtime/ext_gui/clip_values.cpp +246 -0
  327. package/runtime/ext_gui/css.cpp +94 -0
  328. package/runtime/ext_gui/css.h +22 -1
  329. package/runtime/ext_gui/css_match.cpp +3 -0
  330. package/runtime/ext_gui/css_selector.cpp +9 -0
  331. package/runtime/ext_gui/css_sheet.cpp +89 -45
  332. package/runtime/ext_gui/document.cpp +4 -1
  333. package/runtime/ext_gui/document.h +2 -0
  334. package/runtime/ext_gui/dom.h +16 -0
  335. package/runtime/ext_gui/dom_api.cpp +37 -0
  336. package/runtime/ext_gui/dom_api_canvas.cpp +17 -80
  337. package/runtime/ext_gui/dom_api_canvas_extra.cpp +130 -1
  338. package/runtime/ext_gui/dom_api_canvas_pending.cpp +41 -0
  339. package/runtime/ext_gui/dom_api_document.cpp +96 -5
  340. package/runtime/ext_gui/dom_api_events.cpp +20 -0
  341. package/runtime/ext_gui/dom_api_internal.h +278 -0
  342. package/runtime/ext_gui/dom_api_proto.cpp +6 -0
  343. package/runtime/ext_gui/dom_api_webgl.cpp +376 -0
  344. package/runtime/ext_gui/dom_api_webgl2.cpp +434 -0
  345. package/runtime/ext_gui/dom_api_webgl2_draw.cpp +269 -0
  346. package/runtime/ext_gui/dom_api_webgl_buffer.cpp +212 -0
  347. package/runtime/ext_gui/dom_api_webgl_rt.h +30 -0
  348. package/runtime/ext_gui/dom_api_webgl_shader.cpp +443 -0
  349. package/runtime/ext_gui/dom_api_webgl_state.cpp +444 -0
  350. package/runtime/ext_gui/dom_api_webgl_texture.cpp +445 -0
  351. package/runtime/ext_gui/effect_values.cpp +312 -0
  352. package/runtime/ext_gui/gradient_values.cpp +476 -0
  353. package/runtime/ext_gui/grid_values.cpp +230 -0
  354. package/runtime/ext_gui/gui.cpp +90 -23
  355. package/runtime/ext_gui/gui_canvas.cpp +50 -2
  356. package/runtime/ext_gui/gui_engine.h +71 -0
  357. package/runtime/ext_gui/gui_events.cpp +4 -1
  358. package/runtime/ext_gui/gui_media.cpp +1 -1
  359. package/runtime/ext_gui/gui_webgl.cpp +83 -0
  360. package/runtime/ext_gui/layout.cpp +21 -0
  361. package/runtime/ext_gui/layout_flow.cpp +81 -4
  362. package/runtime/ext_gui/layout_flow_internal.h +31 -0
  363. package/runtime/ext_gui/layout_grid.cpp +374 -0
  364. package/runtime/ext_gui/media.h +1 -1
  365. package/runtime/ext_gui/media_api.h +2 -2
  366. package/runtime/ext_gui/media_query.cpp +211 -0
  367. package/runtime/ext_gui/paint.cpp +111 -77
  368. package/runtime/ext_gui/paint.h +33 -3
  369. package/runtime/ext_gui/paint_background.cpp +229 -0
  370. package/runtime/ext_gui/paint_clip.cpp +268 -0
  371. package/runtime/ext_gui/paint_effects.cpp +324 -0
  372. package/runtime/ext_gui/paint_internal.h +85 -0
  373. package/runtime/ext_gui/renderer.cpp +8 -31
  374. package/runtime/ext_gui/renderer_clip.cpp +196 -0
  375. package/runtime/ext_gui/renderer_image.cpp +13 -8
  376. package/runtime/ext_gui/renderer_internal.h +15 -8
  377. package/runtime/ext_gui/renderer_text.cpp +6 -31
  378. package/runtime/ext_gui/style.cpp +343 -20
  379. package/runtime/ext_gui/style.h +144 -3
  380. package/runtime/ext_gui/style_print.cpp +134 -1
  381. package/runtime/ext_gui/style_values.cpp +152 -126
  382. package/runtime/ext_gui/style_values_internal.h +35 -0
  383. package/runtime/ext_gui/webgl.h +466 -0
  384. package/runtime/ext_gui/webgl2_constants.h +274 -0
  385. package/runtime/ext_gui/webgl_buffer.cpp +565 -0
  386. package/runtime/ext_gui/webgl_constants.h +317 -0
  387. package/runtime/ext_gui/webgl_context.cpp +466 -0
  388. package/runtime/ext_gui/webgl_draw.cpp +495 -0
  389. package/runtime/ext_gui/webgl_glsl.h +125 -0
  390. package/runtime/ext_gui/webgl_glsl_compile.cpp +431 -0
  391. package/runtime/ext_gui/webgl_glsl_rewrite.cpp +349 -0
  392. package/runtime/ext_gui/webgl_glsl_scan.cpp +581 -0
  393. package/runtime/ext_gui/webgl_msl.cpp +62 -0
  394. package/runtime/ext_gui/webgl_msl.h +33 -0
  395. package/runtime/ext_gui/webgl_pipeline.cpp +274 -0
  396. package/runtime/ext_gui/webgl_program_internal.h +18 -0
  397. package/runtime/ext_gui/webgl_shader.cpp +582 -0
  398. package/runtime/ext_gui/webgl_texture.cpp +293 -0
  399. package/runtime/ext_gui/webgl_uniform.cpp +167 -0
  400. package/runtime/ext_gui/webgl_webgl2.cpp +224 -0
  401. package/runtime/ext_gui/window.cpp +9 -0
  402. package/runtime/ext_media/media.cpp +1 -1
  403. package/runtime/ext_media/media_bridge.c +9 -3
  404. package/runtime/ext_node/path/path.c +7 -7
  405. package/runtime/ext_node/url/url.c +4 -2
  406. package/runtime/rt.h +15 -0
  407. package/runtime/rt_internal.h +1 -0
  408. package/runtime/xt_constructors.c +2 -1
  409. package/runtime/xt_containers/access.inc +8 -0
  410. package/runtime/xt_containers/objects.inc +22 -2
  411. package/runtime/xt_io/exception.inc +4 -2
  412. package/runtime/xt_regex/api.inc +2 -0
  413. package/runtime/xt_regex/matcher.inc +16 -3
  414. package/runtime/xt_regex/parser.inc +8 -2
  415. package/runtime/xt_regex/types.inc +3 -0
  416. package/runtime/xt_stdlib/math.inc +62 -0
  417. package/runtime/xt_stdlib2/date.inc +7 -1
  418. package/runtime/xt_stdlib2/objects.inc +14 -0
  419. package/runtime/xt_stdlib2/regexp-match.inc +60 -21
  420. package/runtime/xt_stdlib2/regexp.inc +1 -0
  421. package/runtime/xt_typed_array.c +1 -1
  422. package/scripts/build-gui.ts +90 -3
  423. package/scripts/build-media.ts +323 -58
  424. package/scripts/setup-vs-toolchain.ps1 +152 -0
  425. package/scripts/support-matrix.ts +167 -0
  426. package/src/ast/common.ts +2 -0
  427. package/src/ast/declarations.ts +12 -1
  428. package/src/ast/expressions.ts +6 -0
  429. package/src/ast/kinds.ts +2 -0
  430. package/src/binder/binder.ts +3 -0
  431. package/src/binder/declarations.ts +70 -0
  432. package/src/binder/references.ts +97 -4
  433. package/src/binder/types.ts +30 -0
  434. package/src/canonical.ts +37 -0
  435. package/src/checker/assignability.ts +134 -0
  436. package/src/checker/calls.ts +135 -0
  437. package/src/checker/checker.ts +47 -0
  438. package/src/checker/declarations.ts +26 -0
  439. package/src/checker/flow.ts +236 -0
  440. package/src/checker/globals.ts +40 -0
  441. package/src/checker/source.ts +53 -0
  442. package/src/checker/type-infer.ts +185 -0
  443. package/src/checker/type-model.ts +212 -0
  444. package/src/checker/type-system.ts +578 -0
  445. package/src/checker/typecheck-types.ts +112 -0
  446. package/src/checker/typecheck.ts +586 -0
  447. package/src/checker/undefined.ts +43 -0
  448. package/src/checker/walk.ts +16 -0
  449. package/src/cli/help.ts +65 -0
  450. package/src/cli/hints.ts +2 -1
  451. package/src/cli/main.ts +12 -49
  452. package/src/codegen/generator/calls/access.ts +8 -8
  453. package/src/codegen/generator/calls/invocation.ts +8 -5
  454. package/src/codegen/generator/calls/super.ts +67 -5
  455. package/src/codegen/generator/context.ts +31 -19
  456. package/src/codegen/generator/debug.ts +2 -1
  457. package/src/codegen/generator/expressions/primary.ts +84 -13
  458. package/src/codegen/generator/generator.ts +11 -1
  459. package/src/codegen/generator/module.ts +115 -24
  460. package/src/codegen/generator/state.ts +11 -1
  461. package/src/codegen/generator/statements/variables.ts +26 -6
  462. package/src/codegen/generator/tables.ts +5 -0
  463. package/src/driver/build-key.ts +3 -0
  464. package/src/driver/bundler/graph.ts +6 -2
  465. package/src/driver/bundler/merge.ts +14 -1
  466. package/src/driver/bundler/types.ts +13 -0
  467. package/src/driver/compiler.ts +4 -1
  468. package/src/driver/fingerprint.ts +2 -1
  469. package/src/driver/frontend.ts +13 -2
  470. package/src/driver/options.ts +6 -0
  471. package/src/driver/version.ts +1 -1
  472. package/src/extensions/gui/css.ts +113 -0
  473. package/src/extensions/gui/html.ts +31 -11
  474. package/src/extensions/gui/index.ts +81 -2
  475. package/src/extensions/gui/three.ts +25 -0
  476. package/src/extensions/media/index.ts +13 -16
  477. package/src/extensions/registry.ts +48 -0
  478. package/src/index.ts +1 -0
  479. package/src/parser/declarations/classes.ts +7 -0
  480. package/src/parser/expressions.ts +13 -1
  481. package/src/parser/literals.ts +6 -2
@@ -13,6 +13,8 @@
13
13
  * 1. fetch the pinned SDL3 source into `vendor/SDL` (gitignored),
14
14
  * 2. build `libSDL3.a` with CMake,
15
15
  * 2b. fetch + build static FreeType and HarfBuzz for text,
16
+ * 2c. fetch + build static glslang (GLSL ES -> SPIR-V) and SPIRV-Cross
17
+ * (SPIR-V -> Metal Shading Language),
16
18
  * 3. compile the engine translation units,
17
19
  * 4. merge everything into `gui.a` (libtool on macOS, `ar -M` elsewhere;
18
20
  * `gui.lib` on Windows, built from MSVC-compatible COFF objects).
@@ -28,6 +30,7 @@
28
30
  * SDL3_TAG SDL3 git tag to check out (default: release-3.2.10)
29
31
  * FREETYPE_VERSION FreeType version (default: 2.13.3)
30
32
  * HARFBUZZ_VERSION HarfBuzz version (default: 10.1.0)
33
+ * SPIRV_CROSS_VERSION SPIRV-Cross tag (default: vulkan-sdk-1.4.313.0)
31
34
  *
32
35
  * The C/C++ compiler is the same clang the driver resolves (see
33
36
  * `src/driver/toolchain-provider.ts`), so the archive always matches the ABI of
@@ -63,6 +66,17 @@ const freetypeSrc = join(vendorDir, "freetype");
63
66
  const freetypeBuild = join(vendorDir, "freetype-build");
64
67
  const harfbuzzSrc = join(vendorDir, "harfbuzz");
65
68
  const harfbuzzBuild = join(vendorDir, "harfbuzz-build");
69
+ /* glslang translates the GLSL ES that WebGL programs supply at run time; it is
70
+ * built statically and merged into the archive, so a compiled binary needs no
71
+ * Vulkan SDK. The SPIR-V headers are its only external dependency. */
72
+ const glslangSrc = join(vendorDir, "glslang");
73
+ const glslangBuild = join(vendorDir, "glslang-build");
74
+ const spirvHeadersSrc = join(glslangSrc, "External", "spirv-headers");
75
+ /* SPIRV-Cross turns the same SPIR-V into the Metal Shading Language that
76
+ * SDL_GPU's Metal backend consumes at run time (macOS). It needs no external
77
+ * headers: its own parser reads the module that glslang produced. */
78
+ const spirvCrossSrc = join(vendorDir, "spirv-cross");
79
+ const spirvCrossBuild = join(vendorDir, "spirv-cross-build");
66
80
  const objDir = join(root, "build", "gui-obj");
67
81
  const outDir = join(runtimeDir, "lib", platformSlug());
68
82
 
@@ -108,8 +122,14 @@ function cmakeToolchainArgs(): string[] {
108
122
  const SDL_TAG = process.env.SDL3_TAG || "release-3.2.10";
109
123
  const FREETYPE_VERSION = process.env.FREETYPE_VERSION || "2.13.3";
110
124
  const HARFBUZZ_VERSION = process.env.HARFBUZZ_VERSION || "10.1.0";
125
+ const GLSLANG_VERSION = process.env.GLSLANG_VERSION || "16.6.0";
126
+ const SPIRV_HEADERS_VERSION = process.env.SPIRV_HEADERS_VERSION || "vulkan-sdk-1.4.363.0";
127
+ const SPIRV_CROSS_VERSION = process.env.SPIRV_CROSS_VERSION || "vulkan-sdk-1.4.313.0";
111
128
  const FREETYPE_URL = `https://download.savannah.gnu.org/releases/freetype/freetype-${FREETYPE_VERSION}.tar.xz`;
112
129
  const HARFBUZZ_URL = `https://github.com/harfbuzz/harfbuzz/releases/download/${HARFBUZZ_VERSION}/harfbuzz-${HARFBUZZ_VERSION}.tar.xz`;
130
+ const GLSLANG_URL = `https://codeload.github.com/KhronosGroup/glslang/tar.gz/refs/tags/${GLSLANG_VERSION}`;
131
+ const SPIRV_HEADERS_URL = `https://codeload.github.com/KhronosGroup/SPIRV-Headers/tar.gz/refs/tags/${SPIRV_HEADERS_VERSION}`;
132
+ const SPIRV_CROSS_URL = `https://codeload.github.com/KhronosGroup/SPIRV-Cross/tar.gz/refs/tags/${SPIRV_CROSS_VERSION}`;
113
133
 
114
134
  function fail(message: string): never {
115
135
  console.error(`xbintsc: ${message}`);
@@ -326,6 +346,17 @@ function freetypeArchive(): string | undefined {
326
346
  function harfbuzzArchive(): string | undefined {
327
347
  return builtLibrary(harfbuzzBuild, ["libharfbuzz.a", "harfbuzz.lib"], /^harfbuzz[-\d.]*\.lib$/);
328
348
  }
349
+ function glslangArchive(): string | undefined {
350
+ return builtLibrary(join(glslangBuild, "glslang"), ["libglslang.a", "glslang.lib"]);
351
+ }
352
+ function glslangLimitsArchive(): string | undefined {
353
+ return builtLibrary(join(glslangBuild, "glslang"),
354
+ ["libglslang-default-resource-limits.a", "glslang-default-resource-limits.lib"]);
355
+ }
356
+ /* SPIRV-Cross's MSL target pulls in its GLSL base class and the core module. */
357
+ function spirvCrossArchive(name: string): string | undefined {
358
+ return builtLibrary(spirvCrossBuild, [`lib${name}.a`, `${name}.lib`]);
359
+ }
329
360
 
330
361
  /* -- 1. SDL3 source ------------------------------------------------------- */
331
362
 
@@ -434,6 +465,50 @@ if (!existsSync(harfbuzzArchive() ?? "")) {
434
465
  console.log(`xbintsc: reusing existing ${harfbuzzArchive()}`);
435
466
  }
436
467
 
468
+ /* -- 2c. glslang (WebGL GLSL ES -> SPIR-V) -------------------------------- */
469
+
470
+ ensureTarballSource("glslang", GLSLANG_URL, `glslang-${GLSLANG_VERSION}`);
471
+ ensureTarballSource(join("glslang", "External", "spirv-headers"), SPIRV_HEADERS_URL,
472
+ `SPIRV-Headers-${SPIRV_HEADERS_VERSION}`);
473
+
474
+ if (!existsSync(glslangArchive() ?? "")) {
475
+ const cmake = configureCmake("glslang", glslangSrc, glslangBuild, [
476
+ "-DCMAKE_BUILD_TYPE=Release",
477
+ "-DBUILD_SHARED_LIBS=OFF",
478
+ "-DENABLE_OPT=OFF",
479
+ "-DENABLE_HLSL=OFF",
480
+ "-DENABLE_GLSLANG_BINARIES=OFF",
481
+ "-DGLSLANG_TESTS=OFF",
482
+ ]);
483
+ console.log("xbintsc: building static glslang");
484
+ run(cmake, ["--build", glslangBuild, "--config", "Release", "--parallel"]);
485
+ } else {
486
+ console.log(`xbintsc: reusing existing ${glslangArchive()}`);
487
+ }
488
+
489
+ /* -- 2d. SPIRV-Cross (SPIR-V -> Metal Shading Language) ------------------- */
490
+
491
+ ensureTarballSource("spirv-cross", SPIRV_CROSS_URL, `SPIRV-Cross-${SPIRV_CROSS_VERSION}`);
492
+
493
+ if (!existsSync(spirvCrossArchive("spirv-cross-msl") ?? "")) {
494
+ const cmake = configureCmake("SPIRV-Cross", spirvCrossSrc, spirvCrossBuild, [
495
+ "-DCMAKE_BUILD_TYPE=Release",
496
+ "-DSPIRV_CROSS_CLI=OFF",
497
+ "-DSPIRV_CROSS_ENABLE_TESTS=OFF",
498
+ /* Only the MSL target is used; skip the C++, HLSL, reflection and C APIs
499
+ * so the archive stays small. MSL links against the GLSL base class. */
500
+ "-DSPIRV_CROSS_ENABLE_HLSL=OFF",
501
+ "-DSPIRV_CROSS_ENABLE_CPP=OFF",
502
+ "-DSPIRV_CROSS_ENABLE_REFLECT=OFF",
503
+ "-DSPIRV_CROSS_ENABLE_UTIL=OFF",
504
+ "-DSPIRV_CROSS_ENABLE_C_API=OFF",
505
+ ]);
506
+ console.log("xbintsc: building static SPIRV-Cross");
507
+ run(cmake, ["--build", spirvCrossBuild, "--config", "Release", "--parallel"]);
508
+ } else {
509
+ console.log(`xbintsc: reusing existing ${spirvCrossArchive("spirv-cross-msl")}`);
510
+ }
511
+
437
512
  /* -- 3. engine objects ---------------------------------------------------- */
438
513
 
439
514
  mkdirSync(objDir, { recursive: true });
@@ -443,6 +518,9 @@ const includeFlags = [
443
518
  "-I", join(freetypeSrc, "include"),
444
519
  "-I", join(freetypeBuild, "include"),
445
520
  "-I", join(harfbuzzSrc, "src"),
521
+ "-I", glslangSrc,
522
+ "-I", join(spirvHeadersSrc, "include"),
523
+ "-I", spirvCrossSrc,
446
524
  ];
447
525
  const sources = readdirSync(engineDir)
448
526
  .filter((entry) => entry.endsWith(".cpp"))
@@ -470,10 +548,19 @@ for (const source of sources) {
470
548
  const sdl = sdlArchive();
471
549
  const freetype = freetypeArchive();
472
550
  const harfbuzz = harfbuzzArchive();
473
- if (!sdl || !freetype || !harfbuzz) {
474
- fail("one of the SDL3/FreeType/HarfBuzz archives was not found after building");
551
+ const glslang = glslangArchive();
552
+ const glslangLimits = glslangLimitsArchive();
553
+ const spirvCrossCore = spirvCrossArchive("spirv-cross-core");
554
+ const spirvCrossGlsl = spirvCrossArchive("spirv-cross-glsl");
555
+ const spirvCrossMsl = spirvCrossArchive("spirv-cross-msl");
556
+ if (!sdl || !freetype || !harfbuzz || !glslang || !glslangLimits ||
557
+ !spirvCrossCore || !spirvCrossGlsl || !spirvCrossMsl) {
558
+ fail("one of the SDL3/FreeType/HarfBuzz/glslang/SPIRV-Cross archives was not found after building");
475
559
  }
476
- const libraries = [sdl, freetype, harfbuzz];
560
+ const libraries = [
561
+ sdl, freetype, harfbuzz, glslang, glslangLimits,
562
+ spirvCrossCore, spirvCrossGlsl, spirvCrossMsl,
563
+ ];
477
564
 
478
565
  mkdirSync(outDir, { recursive: true });
479
566
  rmSync(output, { force: true });
@@ -2,38 +2,53 @@
2
2
  /**
3
3
  * Build the media extension archive:
4
4
  *
5
- * runtime/lib/<os>-<arch>/media.a
5
+ * runtime/lib/<os>-<arch>/media.a (POSIX: ELF/Mach-O objects)
6
+ * runtime/lib/<os>-<arch>/media.lib (Windows: MSVC/COFF objects)
6
7
  *
7
- * The GUI engine (`gui.a`) deliberately contains no decoder. This archive adds
8
- * `<video>`/`<audio>` support: it bundles the FFmpeg-backed player from
9
- * `runtime/ext_media/media.cpp` together with a minimal static FFmpeg, and the
10
- * `media` extension links it only when the program asks for it. A program built
11
- * with `--ext gui` alone therefore stays FFmpeg-free.
8
+ * The GUI engine (`gui.a` / `gui.lib`) deliberately contains no decoder. This
9
+ * archive adds `<video>`/`<audio>` support: it bundles the FFmpeg-backed player
10
+ * from `runtime/ext_media/media.cpp` together with a minimal static FFmpeg, and
11
+ * the `media` extension links it only when the program asks for it. A program
12
+ * built with `--ext gui` alone therefore stays FFmpeg-free.
12
13
  *
13
14
  * Steps:
14
15
  * 1. fetch the pinned FFmpeg release tarball into `vendor/ffmpeg`,
15
16
  * 2. configure and build a minimal static FFmpeg into
16
17
  * `vendor/ffmpeg-build/dist` (reused on later runs),
17
18
  * 3. compile the player against those headers,
18
- * 4. merge the player object and the FFmpeg libraries into `media.a`
19
- * (libtool on macOS, `ar -M` elsewhere).
19
+ * 4. merge the player object and the FFmpeg libraries into the archive
20
+ * (libtool on macOS, `ar -M`/`llvm-ar -M` elsewhere).
21
+ *
22
+ * Windows uses FFmpeg's own MSVC toolchain mode: `configure --toolchain=msvc
23
+ * --cc=clang-cl` produces COFF objects compiled against the static MSVC CRT,
24
+ * which is the ABI `gui.lib`, the xbintsc runtime and the final link use. The
25
+ * compiler chain is entirely Visual Studio (clang-cl, cl.exe, lib.exe and
26
+ * llvm-ar); no additional C/C++ toolchain is involved.
27
+ *
28
+ * FFmpeg's `configure` is a POSIX shell script and its Makefiles require GNU
29
+ * make, so those two build drivers remain. The shell comes from Git for Windows
30
+ * (already required to clone SDL3), and make is a native GNU make on PATH or
31
+ * `xbintsc_MAKE`; no additional POSIX environment is required. Because native
32
+ * make cannot pass the MSVC `/showIncludes` awk pipeline through the Windows
33
+ * command line unchanged, the generated `SRC_PATH` is rewritten to a
34
+ * drive-letter path and dependency generation is skipped. Headers/libraries are
35
+ * copied into the prefix instead of running `make install`.
20
36
  *
21
37
  * Environment:
22
38
  * xbintsc_CXX C++ compiler override (default: clang++ next to clang)
23
39
  * xbintsc_AR archiver override (default: llvm-ar next to clang, then ar)
40
+ * xbintsc_SHELL POSIX shell override (Windows; default: Git for Windows bash)
41
+ * xbintsc_MAKE GNU make override (Windows; default: `make` on PATH)
24
42
  * FFMPEG_VERSION FFmpeg release (default: 7.1)
25
- *
26
- * Windows is not supported yet: FFmpeg needs its own `configure`/`make`, and an
27
- * MSVC-compatible build has not been produced (see `doc/gui/known-issues.md`).
28
43
  */
29
44
 
30
- import { existsSync, mkdirSync, renameSync, rmSync } from "node:fs";
45
+ import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
31
46
  import { availableParallelism } from "node:os";
32
47
  import { spawnSync, type SpawnSyncReturns } from "node:child_process";
33
48
  import { dirname, join, resolve } from "node:path";
34
49
  import { fileURLToPath } from "node:url";
35
50
  import { platformSlug } from "../src/driver/paths.js";
36
- import { probeClang, realRunner } from "../src/driver/toolchain.js";
51
+ import { isMsvcTarget, probeClang, realRunner } from "../src/driver/toolchain.js";
37
52
  import { resolveToolchain } from "../src/driver/toolchain-provider.js";
38
53
 
39
54
  const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
@@ -41,11 +56,16 @@ const runtimeDir = join(root, "runtime");
41
56
  const sourceDir = join(runtimeDir, "ext_media");
42
57
  const vendorDir = join(root, "vendor");
43
58
  const ffmpegSrc = join(vendorDir, "ffmpeg");
44
- const ffmpegBuild = join(vendorDir, "ffmpeg-build");
59
+ const isWindows = process.platform === "win32";
60
+ /* The MSVC configure/build tree is kept apart from the POSIX one: a shared tree
61
+ * would carry `CC=clang`/`CC=clang-cl` and a POSIX/Windows `SRC_PATH` from the
62
+ * other platform into the next configure. */
63
+ const ffmpegBuild = join(vendorDir, isWindows ? "ffmpeg-build-msvc" : "ffmpeg-build");
45
64
  const ffmpegInstall = join(ffmpegBuild, "dist");
46
65
  const objDir = join(root, "build", "media-obj");
47
66
  const outDir = join(runtimeDir, "lib", platformSlug());
48
- const output = join(outDir, "media.a");
67
+ const output = join(outDir, isWindows ? "media.lib" : "media.a");
68
+ const object = join(objDir, isWindows ? "media.obj" : "media.o");
49
69
 
50
70
  const FFMPEG_VERSION = process.env.FFMPEG_VERSION || "7.1";
51
71
  const FFMPEG_URL = `https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.xz`;
@@ -74,7 +94,7 @@ function fail(message: string): never {
74
94
  function run(
75
95
  command: string,
76
96
  args: readonly string[],
77
- options: { input?: string; quietStderr?: boolean; cwd?: string } = {},
97
+ options: { input?: string; quietStderr?: boolean; cwd?: string; env?: NodeJS.ProcessEnv } = {},
78
98
  ): SpawnSyncReturns<string> {
79
99
  const quiet = options.quietStderr === true;
80
100
  const result = spawnSync(command, args, {
@@ -82,6 +102,7 @@ function run(
82
102
  encoding: "utf8",
83
103
  input: options.input,
84
104
  cwd: options.cwd,
105
+ env: options.env,
85
106
  });
86
107
  if (result.status !== 0) {
87
108
  if (quiet && result.stderr) process.stderr.write(result.stderr);
@@ -104,7 +125,7 @@ function firstAvailable(candidates: readonly (string | undefined)[]): string | u
104
125
  /** The tool that sits next to `clang` in the same LLVM installation. */
105
126
  function siblingTool(clangPath: string, name: string): string | undefined {
106
127
  if (!clangPath.includes("/") && !clangPath.includes("\\")) return undefined;
107
- const candidate = join(dirname(clangPath), `${name}.exe`);
128
+ const candidate = join(dirname(clangPath), `${name}${isWindows ? ".exe" : ""}`);
108
129
  if (existsSync(candidate)) return candidate;
109
130
  const bare = join(dirname(clangPath), name);
110
131
  return existsSync(bare) ? bare : undefined;
@@ -116,6 +137,12 @@ function findClangxx(clangPath: string): string {
116
137
  if (override) {
117
138
  const probe = probeClang(realRunner, override);
118
139
  if (!probe.responds) fail(`xbintsc_CXX ${override} does not respond to --version`);
140
+ if (isWindows && (probe.version === undefined || (probe.target !== undefined && !isMsvcTarget(probe.target)))) {
141
+ fail(
142
+ `xbintsc_CXX ${override} is not an MSVC-targeting clang++ (target ${probe.target ?? "unknown"}); ` +
143
+ "the Windows media build requires one, for example x86_64-pc-windows-msvc.",
144
+ );
145
+ }
119
146
  return override;
120
147
  }
121
148
  const sibling = siblingTool(clangPath, "clang++");
@@ -128,7 +155,9 @@ function findClangxx(clangPath: string): string {
128
155
  /** Resolve the archiver that matches the compiler, preferring LLVM's own. */
129
156
  function findArchiver(clangPath: string): string {
130
157
  const sibling = siblingTool(clangPath, "llvm-ar");
131
- const archiver = firstAvailable([process.env.xbintsc_AR, sibling, "llvm-ar", "ar"]);
158
+ /* Windows always uses llvm-ar: a non-LLVM `ar` there may write a different
159
+ * archive flavour than the COFF one the linker wants. */
160
+ const archiver = firstAvailable([process.env.xbintsc_AR, sibling, "llvm-ar", isWindows ? undefined : "ar"]);
132
161
  if (!archiver) fail("no archiver found; install llvm-ar or set xbintsc_AR");
133
162
  return archiver;
134
163
  }
@@ -161,47 +190,284 @@ function ffmpegStaticLibraries(): string[] | undefined {
161
190
  return libraries.every((library) => existsSync(library)) ? libraries : undefined;
162
191
  }
163
192
 
164
- if (process.platform === "win32") {
193
+ /** True when `dist` holds both the libraries and the installed headers. */
194
+ function ffmpegInstalled(): boolean {
195
+ return ffmpegStaticLibraries() !== undefined && existsSync(join(ffmpegInstall, "include", "libavutil", "avutil.h"));
196
+ }
197
+
198
+ /** Arguments shared by the POSIX and Windows FFmpeg configure runs. */
199
+ function ffmpegConfigureArgs(): string[] {
200
+ const prefix = isWindows ? ffmpegInstall.replace(/\\/g, "/") : ffmpegInstall;
201
+ const args = [
202
+ `--prefix=${prefix}`,
203
+ "--enable-static", "--disable-shared",
204
+ "--disable-programs", "--disable-doc",
205
+ "--disable-htmlpages", "--disable-manpages", "--disable-podpages", "--disable-txtpages",
206
+ "--disable-avdevice", "--disable-avfilter", "--disable-postproc",
207
+ "--disable-everything",
208
+ "--enable-avformat", "--enable-avcodec", "--enable-avutil", "--enable-swscale", "--enable-swresample",
209
+ "--disable-network", "--disable-autodetect", "--disable-x86asm", "--disable-debug",
210
+ ...FFMPEG_COMPONENTS,
211
+ ];
212
+ /* `-fPIC` is meaningless to the MSVC ABI; FFmpeg's msvc flag filter drops it,
213
+ * but omitting it keeps the configuration honest. */
214
+ if (!isWindows) args.splice(2, 0, "--enable-pic");
215
+ return args;
216
+ }
217
+
218
+ /* -- Windows: a POSIX shell + GNU make driving clang-cl -------------------- */
219
+
220
+ /** Drive-letter path in the `/d/dir/file` form the POSIX shell and configure use. */
221
+ function toPosixPath(path: string): string {
222
+ const absolute = resolve(path);
223
+ const match = /^([A-Za-z]):[\\/](.*)$/.exec(absolute);
224
+ if (!match) return absolute.replace(/\\/g, "/");
225
+ return `/${match[1].toLowerCase()}/${match[2].replace(/\\/g, "/")}`;
226
+ }
227
+
228
+ /** `D:\dir` form for the native make parser (it does not understand `/d/dir`). */
229
+ function toWindowsPath(path: string): string {
230
+ return resolve(path).replace(/\\/g, "/");
231
+ }
232
+
233
+ /** Quote one word for a POSIX shell command line built by hand. */
234
+ function shellQuote(word: string): string {
235
+ return `'${word.replace(/'/g, `'\\''`)}'`;
236
+ }
237
+
238
+ /** Git for Windows' bash, which supplies the POSIX shell FFmpeg's configure needs. */
239
+ function findPosixShell(): string {
240
+ const programFiles = process.env.ProgramFiles ?? "C:\\Program Files";
241
+ const localAppData = process.env.LOCALAPPDATA ?? "";
242
+ const git = resolveOnPath("git");
243
+ const candidates = [
244
+ process.env.xbintsc_SHELL,
245
+ git ? resolve(dirname(git), "..", "bin", "bash.exe") : undefined,
246
+ join(programFiles, "Git", "bin", "bash.exe"),
247
+ join(localAppData, "Programs", "Git", "bin", "bash.exe"),
248
+ join(programFiles, "Git", "usr", "bin", "bash.exe"),
249
+ ].filter((candidate): candidate is string => Boolean(candidate));
250
+ const shell = firstAvailable(candidates);
251
+ if (!shell) {
252
+ fail(
253
+ "the Windows media build needs a POSIX shell to run FFmpeg's configure; " +
254
+ "install Git for Windows (which also supplies git) or set xbintsc_SHELL.",
255
+ );
256
+ }
257
+ return shell;
258
+ }
259
+
260
+ /** First match for `name` on PATH, as an absolute path. */
261
+ function resolveOnPath(name: string): string | undefined {
262
+ const result = spawnSync("where.exe", [name], { encoding: "utf8" });
263
+ if (result.status !== 0) return undefined;
264
+ return result.stdout.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0);
265
+ }
266
+
267
+ /** Native GNU make to drive the FFmpeg build. */
268
+ function findWindowsMake(): string {
269
+ const candidates = [process.env.xbintsc_MAKE, resolveOnPath("make")].filter(
270
+ (candidate): candidate is string => Boolean(candidate),
271
+ );
272
+ for (const candidate of candidates) {
273
+ if (isRunnable(candidate)) return candidate;
274
+ }
275
+ fail(
276
+ "the Windows media build needs GNU make to run FFmpeg's Makefile; install it " +
277
+ "(`choco install make`) or set xbintsc_MAKE to a GNU make binary.",
278
+ );
279
+ }
280
+
281
+ /** Resolve `clang-cl` from the same LLVM as the driver's clang. */
282
+ function findClangCl(clangPath: string): string {
283
+ const sibling = siblingTool(clangPath, "clang-cl");
284
+ if (sibling) return sibling;
285
+ const onPath = resolveOnPath("clang-cl");
286
+ if (onPath) return onPath;
287
+ fail(
288
+ `no clang-cl found next to ${clangPath} or on PATH; the Windows media build needs the LLVM bundled ` +
289
+ "with Visual Studio (VC\\Tools\\Llvm\\<arch>\\bin\\clang-cl.exe) so FFmpeg can use the MSVC ABI.",
290
+ );
291
+ }
292
+
293
+ /** Installation path reported by `vswhere`, when it is available. */
294
+ function vswhereInstallation(): string | undefined {
295
+ const programFilesX86 = process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
296
+ const vswhere = join(programFilesX86, "Microsoft Visual Studio", "Installer", "vswhere.exe");
297
+ if (!existsSync(vswhere)) return undefined;
298
+ const result = spawnSync(
299
+ vswhere,
300
+ [
301
+ "-latest", "-products", "*",
302
+ "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
303
+ "-property", "installationPath",
304
+ ],
305
+ { encoding: "utf8" },
306
+ );
307
+ const root = result.status === 0 ? result.stdout.split(/\r?\n/)[0]?.trim() : undefined;
308
+ return root && existsSync(root) ? root : undefined;
309
+ }
310
+
311
+ /** Directory holding `cl.exe` (and `lib.exe`), which FFmpeg's msvc toolchain probes. */
312
+ function findMsvcBin(clangPath: string): string {
313
+ const roots = [process.env.ProgramFiles, process.env["ProgramFiles(x86)"], process.env.ProgramW6432]
314
+ .filter((root): root is string => Boolean(root));
315
+ /* A clang from Visual Studio's own LLVM names the installation it belongs to;
316
+ * vswhere covers a clang from a separate LLVM when VS is installed elsewhere. */
317
+ const fromClang = /^(.*[\\/]Microsoft Visual Studio[\\/]\d{4}[\\/][^\\/]+)[\\/]VC[\\/]Tools[\\/]Llvm[\\/]/i.exec(
318
+ clangPath,
319
+ )?.[1];
320
+ const fromVswhere = vswhereInstallation();
321
+ const msvcDirs: string[] = [];
322
+ for (const extra of [fromClang, fromVswhere]) {
323
+ if (extra) msvcDirs.push(join(extra, "VC", "Tools", "MSVC"));
324
+ }
325
+ for (const root of roots) {
326
+ for (const year of ["2022", "2019"]) {
327
+ for (const edition of ["BuildTools", "Community", "Professional", "Enterprise"]) {
328
+ msvcDirs.push(join(root, "Microsoft Visual Studio", year, edition, "VC", "Tools", "MSVC"));
329
+ }
330
+ }
331
+ }
332
+ const host = process.arch === "arm64" ? "Hostarm64" : "Hostx64";
333
+ const target = process.arch === "arm64" ? "arm64" : "x64";
334
+ for (const msvc of msvcDirs) {
335
+ if (!existsSync(msvc)) continue;
336
+ const versions = readdirSync(msvc).sort((a, b) => b.localeCompare(a, undefined, { numeric: true }));
337
+ for (const version of versions) {
338
+ const bin = join(msvc, version, "bin", host, target);
339
+ if (existsSync(join(bin, "cl.exe"))) return bin;
340
+ }
341
+ }
165
342
  fail(
166
- "the media extension is not available on Windows yet: FFmpeg needs a POSIX " +
167
- "configure/make build (see doc/gui/known-issues.md)",
343
+ "the Windows media build needs the MSVC C++ toolchain (FFmpeg's configure probes cl.exe/lib.exe); " +
344
+ "install the 'Desktop development with C++' workload in Visual Studio or the Build Tools.",
168
345
  );
169
346
  }
170
347
 
348
+ interface WindowsBuildTools {
349
+ readonly shell: string;
350
+ readonly make: string;
351
+ readonly env: NodeJS.ProcessEnv;
352
+ }
353
+
354
+ /** Environment for configure/make: the resolved clang-cl and MSVC tools first. */
355
+ function windowsBuildEnv(shell: string, clangCl: string, msvcBin: string): NodeJS.ProcessEnv {
356
+ const env: NodeJS.ProcessEnv = { ...process.env };
357
+ const pathKey = Object.keys(env).find((key) => key.toUpperCase() === "PATH") ?? "Path";
358
+ const usrBin = resolve(dirname(shell), "..", "usr", "bin");
359
+ const parts = [dirname(clangCl), msvcBin, existsSync(usrBin) ? usrBin : "", env[pathKey] ?? ""];
360
+ env[pathKey] = parts.filter((part) => part.length > 0).join(";");
361
+ return env;
362
+ }
363
+
364
+ /** Rewrite the POSIX `SRC_PATH` configure wrote so a native make can parse it. */
365
+ function patchNativeMakePaths(): void {
366
+ const posixSource = toPosixPath(ffmpegSrc);
367
+ const posixInstall = toPosixPath(ffmpegInstall);
368
+ for (const file of [join(ffmpegBuild, "Makefile"), join(ffmpegBuild, "ffbuild", "config.mak")]) {
369
+ if (!existsSync(file)) continue;
370
+ const patched = readFileSync(file, "utf8")
371
+ .split(posixSource).join(toWindowsPath(ffmpegSrc))
372
+ .split(posixInstall).join(toWindowsPath(ffmpegInstall));
373
+ writeFileSync(file, patched);
374
+ }
375
+ }
376
+
377
+ /** Copy every public `.h` under `source` into `destination`, recursively. */
378
+ function copyHeaders(source: string, destination: string): void {
379
+ mkdirSync(destination, { recursive: true });
380
+ for (const entry of readdirSync(source, { withFileTypes: true })) {
381
+ const from = join(source, entry.name);
382
+ const to = join(destination, entry.name);
383
+ if (entry.isDirectory()) {
384
+ copyHeaders(from, to);
385
+ } else if (entry.isFile() && entry.name.endsWith(".h")) {
386
+ copyFileSync(from, to);
387
+ }
388
+ }
389
+ }
390
+
391
+ /** Copy FFmpeg headers and static libraries into the `dist` prefix. */
392
+ function installFfmpegWindows(): void {
393
+ const includeDir = join(ffmpegInstall, "include");
394
+ for (const name of FFMPEG_LIBRARY_NAMES) {
395
+ const library = name.replace(/\.a$/, "");
396
+ copyHeaders(join(ffmpegSrc, library), join(includeDir, library));
397
+ }
398
+ /* Generated headers live in the build tree, not in the sources. */
399
+ for (const generated of ["avconfig.h", "ffversion.h"]) {
400
+ const from = join(ffmpegBuild, "libavutil", generated);
401
+ if (existsSync(from)) copyFileSync(from, join(includeDir, "libavutil", generated));
402
+ }
403
+ const libDir = join(ffmpegInstall, "lib");
404
+ mkdirSync(libDir, { recursive: true });
405
+ for (const name of FFMPEG_LIBRARY_NAMES) {
406
+ copyFileSync(join(ffmpegBuild, name.replace(/\.a$/, ""), name), join(libDir, name));
407
+ }
408
+ }
409
+
410
+ function buildFfmpegWindows(shell: string, clangCl: string, msvcBin: string): void {
411
+ const make = findWindowsMake();
412
+ const tools: WindowsBuildTools = {
413
+ shell,
414
+ make,
415
+ env: windowsBuildEnv(shell, clangCl, msvcBin),
416
+ };
417
+ console.log(`xbintsc: configuring FFmpeg ${FFMPEG_VERSION} for the MSVC ABI with clang-cl`);
418
+ mkdirSync(ffmpegBuild, { recursive: true });
419
+ run(tools.shell, [
420
+ toPosixPath(join(ffmpegSrc, "configure")),
421
+ "--toolchain=msvc",
422
+ "--cc=clang-cl", "--cxx=clang-cl", "--ld=clang-cl", "--ar=lib.exe",
423
+ ...ffmpegConfigureArgs(),
424
+ ], { cwd: ffmpegBuild, env: tools.env });
425
+
426
+ /* Native make cannot pass FFmpeg's `/showIncludes` awk pipeline (the MSVC
427
+ * backslash escapes are consumed by the Windows command line); the `.d` files
428
+ * only affect incremental rebuilds, so generation is skipped. The source path
429
+ * configure wrote is POSIX-shaped and is rewritten for native make. */
430
+ patchNativeMakePaths();
431
+ const depOverrides = " CCDEP=true CXXDEP=true ASDEP=true HOSTCCDEP=true";
432
+ console.log(`xbintsc: building minimal static FFmpeg ${FFMPEG_VERSION} with clang-cl`);
433
+ run(tools.shell, ["-lc", `${shellQuote(tools.make)} -j ${availableParallelism()}${depOverrides}`], {
434
+ cwd: ffmpegBuild,
435
+ env: tools.env,
436
+ });
437
+ installFfmpegWindows();
438
+ }
439
+
440
+ /* -- 1. FFmpeg source ------------------------------------------------------ */
441
+
442
+ ensureFfmpegSource();
443
+
171
444
  /* One toolchain for the player and FFmpeg: the same clang the driver uses, its
172
445
  * sibling clang++, and the matching llvm-ar. Mixing another C++ runtime or
173
446
  * object format would break the final link. */
174
447
  const clang = driverClang();
175
448
  const clangxx = findClangxx(clang);
176
449
  const archiver = findArchiver(clang);
177
-
178
- /* -- 1. FFmpeg source ------------------------------------------------------ */
179
-
180
- ensureFfmpegSource();
450
+ const windowsTools = isWindows
451
+ ? {
452
+ shell: findPosixShell(),
453
+ clangCl: findClangCl(clang),
454
+ msvcBin: findMsvcBin(clang),
455
+ }
456
+ : undefined;
181
457
 
182
458
  /* -- 2. minimal static FFmpeg --------------------------------------------- */
183
459
 
184
- let ffmpegLibraries = ffmpegStaticLibraries();
185
- if (!ffmpegLibraries) {
186
- console.log(`xbintsc: building minimal static FFmpeg ${FFMPEG_VERSION} with ${clang}`);
187
- mkdirSync(ffmpegBuild, { recursive: true });
188
- run(join(ffmpegSrc, "configure"), [
189
- `--prefix=${ffmpegInstall}`,
190
- "--enable-static", "--disable-shared",
191
- "--disable-programs", "--disable-doc",
192
- "--disable-htmlpages", "--disable-manpages", "--disable-podpages", "--disable-txtpages",
193
- "--disable-avdevice", "--disable-avfilter", "--disable-postproc",
194
- "--disable-everything",
195
- "--enable-avformat", "--enable-avcodec", "--enable-avutil", "--enable-swscale", "--enable-swresample",
196
- "--disable-network", "--disable-autodetect", "--disable-x86asm", "--disable-debug", "--enable-pic",
197
- `--cc=${clang}`,
198
- `--cxx=${clangxx}`,
199
- ...FFMPEG_COMPONENTS,
200
- ], { cwd: ffmpegBuild });
201
- run("make", ["-j", String(availableParallelism())], { cwd: ffmpegBuild });
202
- run("make", ["install"], { cwd: ffmpegBuild });
203
- ffmpegLibraries = ffmpegStaticLibraries();
204
- if (!ffmpegLibraries) fail("FFmpeg build did not produce the expected static libraries");
460
+ if (!ffmpegInstalled()) {
461
+ if (windowsTools) {
462
+ buildFfmpegWindows(windowsTools.shell, windowsTools.clangCl, windowsTools.msvcBin);
463
+ } else {
464
+ console.log(`xbintsc: building minimal static FFmpeg ${FFMPEG_VERSION} with ${clang}`);
465
+ mkdirSync(ffmpegBuild, { recursive: true });
466
+ run(join(ffmpegSrc, "configure"), ffmpegConfigureArgs(), { cwd: ffmpegBuild });
467
+ run("make", ["-j", String(availableParallelism())], { cwd: ffmpegBuild });
468
+ run("make", ["install"], { cwd: ffmpegBuild });
469
+ }
470
+ if (!ffmpegInstalled()) fail("FFmpeg build did not produce the expected static libraries");
205
471
  } else {
206
472
  console.log(`xbintsc: reusing existing ${ffmpegInstall}/lib`);
207
473
  }
@@ -212,30 +478,29 @@ if (!ffmpegLibraries) {
212
478
  * because the *source* root contains a file literally named `version` that
213
479
  * would shadow the C++ standard header for any `.cpp` that includes it. */
214
480
  mkdirSync(objDir, { recursive: true });
215
- const object = join(objDir, "media.o");
216
481
  const playerSource = join(sourceDir, "media.cpp");
217
482
  console.log("xbintsc: compiling media.cpp");
218
- run(clangxx, [
219
- "-std=c++17", "-O2", "-Wall", "-Wextra", "-fPIC",
220
- "-I", runtimeDir,
221
- "-I", join(ffmpegInstall, "include"),
222
- "-c", playerSource,
223
- "-o", object,
224
- ]);
483
+ const cxxFlags = ["-std=c++17", "-O2", "-Wall", "-Wextra", "-I", runtimeDir, "-I", join(ffmpegInstall, "include")];
484
+ /* The engine and the player are C++ with the static CRT (`libcpmt`), matching
485
+ * FFmpeg's clang-cl objects and the xbintsc runtime; see `scripts/build-gui.ts`. */
486
+ if (isWindows) cxxFlags.push("-Xclang", "--dependent-lib=libcpmt");
487
+ else cxxFlags.push("-fPIC");
488
+ run(clangxx, [...cxxFlags, "-c", playerSource, "-o", object]);
225
489
 
226
- /* -- 4. merge into media.a ------------------------------------------------- */
490
+ /* -- 4. merge into the archive -------------------------------------------- */
227
491
 
228
492
  mkdirSync(outDir, { recursive: true });
229
493
  rmSync(output, { force: true });
230
494
  if (process.platform === "darwin") {
231
- run("libtool", ["-static", "-o", output, object, ...ffmpegLibraries], { quietStderr: true });
495
+ run("libtool", ["-static", "-o", output, object, ...ffmpegStaticLibraries()!], { quietStderr: true });
232
496
  } else {
233
- /* `ar -M` (GNU/LLVM) merges archives through an MRI script. */
497
+ /* `ar -M` (GNU/LLVM) merges archives through an MRI script. Windows paths are
498
+ * normalised to `/` so the MRI parser does not read `\` as an escape. */
234
499
  const mri = (path: string): string => path.replace(/\\/g, "/");
235
500
  const script = [
236
501
  `create ${mri(output)}`,
237
502
  `addmod ${mri(object)}`,
238
- ...ffmpegLibraries.map((library) => `addlib ${mri(library)}`),
503
+ ...ffmpegStaticLibraries()!.map((library) => `addlib ${mri(library)}`),
239
504
  "save",
240
505
  "end",
241
506
  ].join("\n");