xbintsc 0.3.79 → 0.3.87

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 (201) hide show
  1. package/README.md +13 -5
  2. package/README.zh-CN.md +13 -6
  3. package/dist/src/ast/common.d.ts +23 -0
  4. package/dist/src/cli/main.js +68 -24
  5. package/dist/src/cli/main.js.map +1 -1
  6. package/dist/src/codegen/generator/calls/invocation.js +5 -1
  7. package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
  8. package/dist/src/codegen/generator/context.d.ts +50 -0
  9. package/dist/src/codegen/generator/context.js +124 -13
  10. package/dist/src/codegen/generator/context.js.map +1 -1
  11. package/dist/src/codegen/generator/debug.d.ts +33 -0
  12. package/dist/src/codegen/generator/debug.js +97 -0
  13. package/dist/src/codegen/generator/debug.js.map +1 -0
  14. package/dist/src/codegen/generator/expressions/assignment.js +9 -5
  15. package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
  16. package/dist/src/codegen/generator/expressions/operators.js +35 -9
  17. package/dist/src/codegen/generator/expressions/operators.js.map +1 -1
  18. package/dist/src/codegen/generator/expressions/primary.js +5 -2
  19. package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
  20. package/dist/src/codegen/generator/generator.d.ts +3 -1
  21. package/dist/src/codegen/generator/generator.js +3 -1
  22. package/dist/src/codegen/generator/generator.js.map +1 -1
  23. package/dist/src/codegen/generator/module.d.ts +1 -0
  24. package/dist/src/codegen/generator/module.js +76 -6
  25. package/dist/src/codegen/generator/module.js.map +1 -1
  26. package/dist/src/codegen/generator/numeric.d.ts +26 -0
  27. package/dist/src/codegen/generator/numeric.js +127 -0
  28. package/dist/src/codegen/generator/numeric.js.map +1 -0
  29. package/dist/src/codegen/generator/state.d.ts +17 -0
  30. package/dist/src/codegen/generator/state.js +5 -0
  31. package/dist/src/codegen/generator/state.js.map +1 -1
  32. package/dist/src/codegen/generator/statements/control-flow.js +1 -1
  33. package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
  34. package/dist/src/codegen/generator/statements/dispatch.js +3 -0
  35. package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
  36. package/dist/src/codegen/generator/statements/variables.js +8 -2
  37. package/dist/src/codegen/generator/statements/variables.js.map +1 -1
  38. package/dist/src/codegen/generator/tables.d.ts +7 -0
  39. package/dist/src/codegen/generator/tables.js +25 -0
  40. package/dist/src/codegen/generator/tables.js.map +1 -1
  41. package/dist/src/driver/bundler/merge.js +41 -5
  42. package/dist/src/driver/bundler/merge.js.map +1 -1
  43. package/dist/src/driver/compiler.d.ts +22 -3
  44. package/dist/src/driver/compiler.js +49 -58
  45. package/dist/src/driver/compiler.js.map +1 -1
  46. package/dist/src/driver/fingerprint.d.ts +27 -0
  47. package/dist/src/driver/fingerprint.js +88 -0
  48. package/dist/src/driver/fingerprint.js.map +1 -0
  49. package/dist/src/driver/toolchain-provider.d.ts +9 -2
  50. package/dist/src/driver/toolchain-provider.js +15 -15
  51. package/dist/src/driver/toolchain-provider.js.map +1 -1
  52. package/dist/src/driver/toolchain.d.ts +58 -1
  53. package/dist/src/driver/toolchain.js +169 -22
  54. package/dist/src/driver/toolchain.js.map +1 -1
  55. package/dist/src/extensions/gui/index.js +10 -2
  56. package/dist/src/extensions/gui/index.js.map +1 -1
  57. package/dist/src/parser/expressions.js +20 -1
  58. package/dist/src/parser/expressions.js.map +1 -1
  59. package/dist/tests/cli/main.test.js +41 -1
  60. package/dist/tests/cli/main.test.js.map +1 -1
  61. package/dist/tests/codegen/expressions.test.js +14 -3
  62. package/dist/tests/codegen/expressions.test.js.map +1 -1
  63. package/dist/tests/codegen/llvm.test.js +23 -0
  64. package/dist/tests/codegen/llvm.test.js.map +1 -1
  65. package/dist/tests/driver/toolchain-provider.test.js +49 -2
  66. package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
  67. package/dist/tests/driver/toolchain.test.js +62 -3
  68. package/dist/tests/driver/toolchain.test.js.map +1 -1
  69. package/dist/tests/e2e/child-process.test.d.ts +10 -0
  70. package/dist/tests/e2e/child-process.test.js +59 -0
  71. package/dist/tests/e2e/child-process.test.js.map +1 -0
  72. package/dist/tests/e2e/compile-objects-classes.test.js +47 -0
  73. package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
  74. package/dist/tests/e2e/error-stack.test.d.ts +9 -0
  75. package/dist/tests/e2e/error-stack.test.js +94 -0
  76. package/dist/tests/e2e/error-stack.test.js.map +1 -0
  77. package/dist/tests/e2e/gui-example-svg.test.d.ts +21 -0
  78. package/dist/tests/e2e/gui-example-svg.test.js +105 -0
  79. package/dist/tests/e2e/gui-example-svg.test.js.map +1 -0
  80. package/dist/tests/e2e/gui-transform.test.d.ts +7 -0
  81. package/dist/tests/e2e/gui-transform.test.js +198 -0
  82. package/dist/tests/e2e/gui-transform.test.js.map +1 -0
  83. package/dist/tests/e2e/string-concat.test.d.ts +9 -0
  84. package/dist/tests/e2e/string-concat.test.js +102 -0
  85. package/dist/tests/e2e/string-concat.test.js.map +1 -0
  86. package/dist/tests/helpers.d.ts +9 -0
  87. package/dist/tests/helpers.js +26 -3
  88. package/dist/tests/helpers.js.map +1 -1
  89. package/dist/tests/parser/expressions.test.js +7 -0
  90. package/dist/tests/parser/expressions.test.js.map +1 -1
  91. package/dist/tests/version.test.d.ts +1 -0
  92. package/dist/tests/version.test.js +24 -0
  93. package/dist/tests/version.test.js.map +1 -0
  94. package/doc/DESIGN.md +20 -12
  95. package/doc/ai/cli.md +7 -0
  96. package/doc/ai/contributing.md +7 -1
  97. package/doc/ai/extensions.md +5 -1
  98. package/doc/ai/language-support.md +4 -4
  99. package/doc/ai/troubleshooting.md +39 -2
  100. package/doc/ai/zh-CN/cli.md +2 -0
  101. package/doc/ai/zh-CN/contributing.md +6 -2
  102. package/doc/ai/zh-CN/extensions.md +5 -1
  103. package/doc/ai/zh-CN/language-support.md +4 -4
  104. package/doc/ai/zh-CN/troubleshooting.md +35 -2
  105. package/doc/gui/animation.md +53 -2
  106. package/doc/gui/api.md +1 -1
  107. package/doc/gui/dom.md +6 -1
  108. package/doc/gui/input.md +4 -1
  109. package/doc/gui/known-issues.md +16 -4
  110. package/doc/gui/layout.md +36 -0
  111. package/doc/gui/rendering.md +15 -0
  112. package/doc/gui.md +32 -3
  113. package/doc/implemented.md +4 -4
  114. package/doc/requirements.md +13 -4
  115. package/doc/unimplemented.md +11 -10
  116. package/doc/zh-CN/DESIGN.md +15 -9
  117. package/doc/zh-CN/gui/animation.md +49 -2
  118. package/doc/zh-CN/gui/api.md +1 -1
  119. package/doc/zh-CN/gui/dom.md +6 -1
  120. package/doc/zh-CN/gui/input.md +2 -0
  121. package/doc/zh-CN/gui/known-issues.md +12 -2
  122. package/doc/zh-CN/gui/layout.md +31 -0
  123. package/doc/zh-CN/gui/rendering.md +11 -0
  124. package/doc/zh-CN/gui.md +28 -3
  125. package/doc/zh-CN/implemented.md +4 -4
  126. package/doc/zh-CN/requirements.md +12 -3
  127. package/doc/zh-CN/unimplemented.md +10 -9
  128. package/package.json +3 -1
  129. package/runtime/ext_gui/document.cpp +21 -3
  130. package/runtime/ext_gui/document.h +4 -1
  131. package/runtime/ext_gui/dom_api_node.cpp +6 -4
  132. package/runtime/ext_gui/layout.cpp +78 -1
  133. package/runtime/ext_gui/layout.h +21 -0
  134. package/runtime/ext_gui/paint.cpp +48 -15
  135. package/runtime/ext_gui/paint.h +8 -2
  136. package/runtime/ext_gui/renderer.cpp +4 -2
  137. package/runtime/ext_gui/renderer_image.cpp +2 -1
  138. package/runtime/ext_gui/renderer_internal.h +6 -1
  139. package/runtime/ext_gui/renderer_text.cpp +37 -15
  140. package/runtime/ext_gui/style.h +10 -0
  141. package/runtime/ext_gui/style_print.cpp +6 -0
  142. package/runtime/ext_gui/style_values.cpp +43 -0
  143. package/runtime/ext_gui/transform.cpp +412 -0
  144. package/runtime/ext_gui/transform.h +112 -0
  145. package/runtime/ext_gui/window.cpp +6 -4
  146. package/runtime/ext_node/buffer/parts/construction.inc +2 -2
  147. package/runtime/ext_node/buffer/parts/methods.inc +1 -1
  148. package/runtime/ext_node/buffer/parts/search.inc +1 -1
  149. package/runtime/ext_node/child_process/child_process.c +25 -0
  150. package/runtime/ext_node/fs/fd_ops.c +1 -1
  151. package/runtime/rt.h +17 -1
  152. package/runtime/rt_internal.h +47 -4
  153. package/runtime/xt_alloc.c +14 -2
  154. package/runtime/xt_bigint/conversions.inc +1 -1
  155. package/runtime/xt_builtins.c +136 -14
  156. package/runtime/xt_containers/access.inc +17 -16
  157. package/runtime/xt_containers/objects.inc +4 -3
  158. package/runtime/xt_io/exception.inc +171 -2
  159. package/runtime/xt_io/inspect.inc +15 -13
  160. package/runtime/xt_io.c +5 -0
  161. package/runtime/xt_stdlib/arrays.inc +2 -2
  162. package/runtime/xt_stdlib/dispatch.inc +2 -2
  163. package/runtime/xt_stdlib/strings.inc +33 -21
  164. package/runtime/xt_stdlib2/array-statics.inc +2 -1
  165. package/runtime/xt_stdlib2/arrays.inc +1 -1
  166. package/runtime/xt_stdlib2/error.inc +22 -6
  167. package/runtime/xt_stdlib2/json.inc +3 -2
  168. package/runtime/xt_stdlib2/number-string-statics.inc +3 -1
  169. package/runtime/xt_stdlib2/regexp-match.inc +48 -33
  170. package/runtime/xt_stdlib2/strings.inc +23 -14
  171. package/runtime/xt_symbol.c +1 -1
  172. package/runtime/xt_values/comparison.inc +2 -2
  173. package/runtime/xt_values/constructors.inc +10 -5
  174. package/runtime/xt_values/conversions.inc +4 -3
  175. package/runtime/xt_values/strings.inc +78 -4
  176. package/scripts/bench.ts +172 -0
  177. package/scripts/build-runtime.ts +2 -1
  178. package/scripts/check-version.ts +53 -0
  179. package/src/ast/common.ts +23 -0
  180. package/src/cli/main.ts +68 -26
  181. package/src/codegen/generator/calls/invocation.ts +5 -0
  182. package/src/codegen/generator/context.ts +129 -18
  183. package/src/codegen/generator/debug.ts +132 -0
  184. package/src/codegen/generator/expressions/assignment.ts +9 -5
  185. package/src/codegen/generator/expressions/operators.ts +47 -8
  186. package/src/codegen/generator/expressions/primary.ts +5 -2
  187. package/src/codegen/generator/generator.ts +18 -2
  188. package/src/codegen/generator/module.ts +78 -6
  189. package/src/codegen/generator/numeric.ts +158 -0
  190. package/src/codegen/generator/state.ts +22 -0
  191. package/src/codegen/generator/statements/control-flow.ts +1 -1
  192. package/src/codegen/generator/statements/dispatch.ts +3 -0
  193. package/src/codegen/generator/statements/variables.ts +8 -2
  194. package/src/codegen/generator/tables.ts +26 -0
  195. package/src/driver/bundler/merge.ts +39 -5
  196. package/src/driver/compiler.ts +68 -56
  197. package/src/driver/fingerprint.ts +85 -0
  198. package/src/driver/toolchain-provider.ts +17 -16
  199. package/src/driver/toolchain.ts +210 -21
  200. package/src/extensions/gui/index.ts +10 -2
  201. package/src/parser/expressions.ts +20 -1
@@ -6,6 +6,8 @@
6
6
  */
7
7
 
8
8
  import { spawnSync, type SpawnSyncOptions } from "node:child_process";
9
+ import { existsSync } from "node:fs";
10
+ import { join } from "node:path";
9
11
 
10
12
  export interface CommandResult {
11
13
  readonly status: number;
@@ -53,26 +55,176 @@ export class ToolchainError extends Error {
53
55
  }
54
56
  }
55
57
 
56
- /** Locate a clang-compatible C compiler, honouring `xbintsc_CLANG`. */
57
- export function findClang(runner: Runner = realRunner): string {
58
- const candidates = [
59
- process.env.xbintsc_CLANG,
60
- "clang",
61
- "clang-18",
62
- "clang-17",
63
- "clang-16",
64
- "cc",
65
- ].filter((candidate): candidate is string => Boolean(candidate));
66
- for (const candidate of candidates) {
67
- let status: number;
68
- try {
69
- status = runner.run(candidate, ["--version"]).status;
70
- } catch {
71
- continue;
58
+ /**
59
+ * The oldest clang/LLVM the emitted IR is known to compile with. LLVM 16 made
60
+ * opaque pointers the only pointer representation, and the IR emitted by
61
+ * `src/codegen` relies on that; clang 15 and older reject it with
62
+ * "'@.str.0' defined with type '[6 x i8]*' but expected 'i8*'".
63
+ */
64
+ export const MINIMUM_CLANG_MAJOR = 16;
65
+
66
+ export interface ClangVersion {
67
+ readonly major: number;
68
+ readonly minor: number;
69
+ readonly patch: number;
70
+ }
71
+
72
+ /** Result of probing one candidate with `--version`. */
73
+ export interface ClangProbe {
74
+ readonly command: string;
75
+ /** True when the command ran and exited 0. */
76
+ readonly responds: boolean;
77
+ readonly version?: ClangVersion;
78
+ /** First line of the version output, for diagnostics. */
79
+ readonly summary: string;
80
+ }
81
+
82
+ export interface ClangLocation {
83
+ readonly command: string;
84
+ readonly source: "env" | "system";
85
+ /** Absent when an explicit wrapper does not print a parseable version. */
86
+ readonly version?: ClangVersion;
87
+ }
88
+
89
+ /**
90
+ * Extract the LLVM version from a `--version` line. Accepts "clang version
91
+ * 19.1.5", "Apple clang version 15.0.0" and distribution prefixes such as
92
+ * "Ubuntu clang version 18.1.3"; returns `undefined` for anything else (notably
93
+ * gcc, which `cc` may resolve to).
94
+ */
95
+ export function parseClangVersion(output: string): ClangVersion | undefined {
96
+ const match = /clang version (\d+)\.(\d+)\.(\d+)/i.exec(output);
97
+ if (!match) return undefined;
98
+ return { major: Number(match[1]), minor: Number(match[2]), patch: Number(match[3]) };
99
+ }
100
+
101
+ function firstLineOf(text: string): string {
102
+ const end = text.indexOf("\n");
103
+ return (end === -1 ? text : text.slice(0, end)).trim();
104
+ }
105
+
106
+ /** Run `<command> --version` and classify the result. Never throws. */
107
+ export function probeClang(
108
+ runner: Runner,
109
+ command: string,
110
+ env?: Record<string, string>,
111
+ ): ClangProbe {
112
+ try {
113
+ const result = runner.run(command, ["--version"], env ? { env } : undefined);
114
+ const output = `${result.stdout}\n${result.stderr}`;
115
+ const version = parseClangVersion(output);
116
+ return {
117
+ command,
118
+ responds: result.status === 0,
119
+ ...(version ? { version } : {}),
120
+ summary: firstLineOf(result.stdout) || firstLineOf(result.stderr),
121
+ };
122
+ } catch {
123
+ return { command, responds: false, summary: "" };
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Visual Studio ships its own LLVM build (`VC\Tools\Llvm\x64\bin\clang.exe`).
129
+ * It is a perfectly good driver but is not on `PATH` in a plain shell, so probe
130
+ * the well-known install locations too. Only candidates that exist are
131
+ * returned, and only on Windows.
132
+ */
133
+ function visualStudioClangCandidates(): string[] {
134
+ if (process.platform !== "win32") return [];
135
+ const roots = [process.env.ProgramFiles, process.env["ProgramFiles(x86)"], process.env.ProgramW6432]
136
+ .filter((root): root is string => Boolean(root));
137
+ const arch = process.arch === "arm64" ? "ARM64" : "x64";
138
+ const candidates: string[] = [];
139
+ for (const root of roots) {
140
+ for (const year of ["2022", "2019"]) {
141
+ for (const edition of ["Community", "Professional", "Enterprise", "BuildTools"]) {
142
+ const llvm = join(root, "Microsoft Visual Studio", year, edition, "VC", "Tools", "Llvm");
143
+ candidates.push(join(llvm, arch, "bin", "clang.exe"));
144
+ candidates.push(join(llvm, "bin", "clang.exe"));
145
+ }
72
146
  }
73
- if (status === 0) return candidate;
147
+ candidates.push(join(root, "LLVM", "bin", "clang.exe"));
74
148
  }
75
- throw new ToolchainError("clang --version", 1, "No C compiler found. Set xbintsc_CLANG to a clang binary.");
149
+ return [...new Set(candidates)].filter((candidate) => existsSync(candidate));
150
+ }
151
+
152
+ /** Commands probed for a system compiler, in preference order. */
153
+ export function systemClangCandidates(): string[] {
154
+ const candidates = ["clang"];
155
+ for (let major = 20; major >= MINIMUM_CLANG_MAJOR; major--) candidates.push(`clang-${major}`);
156
+ candidates.push("cc");
157
+ candidates.push(...visualStudioClangCandidates());
158
+ return [...new Set(candidates)];
159
+ }
160
+
161
+ function tooOldError(probe: ClangProbe): ToolchainError {
162
+ const version = probe.version!;
163
+ const found = `clang ${version.major}.${version.minor}.${version.patch}`;
164
+ return new ToolchainError(
165
+ "clang --version",
166
+ 1,
167
+ `${probe.command} reports ${found}, but xbintsc requires clang ${MINIMUM_CLANG_MAJOR} or newer ` +
168
+ "(the emitted LLVM IR uses opaque pointers).\n" +
169
+ "Install a newer LLVM/clang (on Windows, the one bundled with Visual Studio works) or set " +
170
+ `xbintsc_CLANG to a clang ${MINIMUM_CLANG_MAJOR}+ binary.`,
171
+ );
172
+ }
173
+
174
+ function noCompilerError(tried: readonly ClangProbe[]): ToolchainError {
175
+ const responded = tried.filter((probe) => probe.responds);
176
+ const details = responded.map((probe) => {
177
+ const version = probe.version
178
+ ? `clang ${probe.version.major}.${probe.version.minor}.${probe.version.patch}`
179
+ : probe.summary || "unknown version";
180
+ return ` ${probe.command}: ${version}`;
181
+ });
182
+ const tooOld = responded.some((probe) => probe.version !== undefined && probe.version.major < MINIMUM_CLANG_MAJOR);
183
+ const reason = tooOld
184
+ ? `Only clang older than ${MINIMUM_CLANG_MAJOR} was found; xbintsc requires clang ${MINIMUM_CLANG_MAJOR}+ ` +
185
+ "(the emitted LLVM IR uses opaque pointers)."
186
+ : `No clang ${MINIMUM_CLANG_MAJOR}+ was found.`;
187
+ return new ToolchainError(
188
+ "clang --version",
189
+ 1,
190
+ [reason, ...(details.length > 0 ? ["Tested:", ...details] : []), "Set xbintsc_CLANG to a clang binary."].join("\n"),
191
+ );
192
+ }
193
+
194
+ /**
195
+ * Resolve the compiler to use, version-checked.
196
+ *
197
+ * An explicit `xbintsc_CLANG` / `xbintsc_TOOLCHAIN` wins even when it is older
198
+ * than the minimum: the user asked for it, so the clear "too old" error is more
199
+ * helpful than silently using another compiler. A broken explicit path falls
200
+ * back to the system search. System candidates are probed in order and the
201
+ * first one reporting clang >= {@link MINIMUM_CLANG_MAJOR} wins, so a modern
202
+ * clang installed next to an old `/usr/bin/clang` is still found.
203
+ */
204
+ export function locateClang(runner: Runner = realRunner): ClangLocation {
205
+ const explicit = process.env.xbintsc_CLANG ?? process.env.xbintsc_TOOLCHAIN;
206
+ const tried: ClangProbe[] = [];
207
+ if (explicit) {
208
+ const probe = probeClang(runner, explicit);
209
+ if (probe.responds) {
210
+ if (probe.version && probe.version.major < MINIMUM_CLANG_MAJOR) throw tooOldError(probe);
211
+ return { command: explicit, source: "env", ...(probe.version ? { version: probe.version } : {}) };
212
+ }
213
+ tried.push(probe);
214
+ }
215
+ for (const command of systemClangCandidates()) {
216
+ const probe = probeClang(runner, command);
217
+ tried.push(probe);
218
+ if (!probe.responds || !probe.version) continue;
219
+ if (probe.version.major < MINIMUM_CLANG_MAJOR) continue;
220
+ return { command, source: "system", version: probe.version };
221
+ }
222
+ throw noCompilerError(tried);
223
+ }
224
+
225
+ /** Locate a supported clang-compatible C compiler, or throw a clear error. */
226
+ export function findClang(runner: Runner = realRunner): string {
227
+ return locateClang(runner).command;
76
228
  }
77
229
 
78
230
  export interface CompileIrOptions {
@@ -81,13 +233,23 @@ export interface CompileIrOptions {
81
233
  readonly objectPath: string;
82
234
  readonly optimize: string;
83
235
  readonly env?: Record<string, string>;
236
+ /** Emit debug info (`-g`) and keep frame pointers for crash backtraces. */
237
+ readonly debug?: boolean;
84
238
  }
85
239
 
240
+ /**
241
+ * One function/data item per section so the linker can drop everything a
242
+ * program never references. This is what `--gc-sections` / `-dead_strip` need;
243
+ * the runtime is a set of translation units with many unused entry points.
244
+ */
245
+ const SECTION_FLAGS = ["-ffunction-sections", "-fdata-sections"];
246
+
86
247
  /** Compile an LLVM IR file to a native object file. */
87
248
  export function compileIr(runner: Runner, options: CompileIrOptions): void {
249
+ const debugFlags = options.debug ? ["-g", "-fno-omit-frame-pointer"] : [];
88
250
  const result = runner.run(
89
251
  options.clang,
90
- [`-O${options.optimize}`, "-c", options.irPath, "-o", options.objectPath],
252
+ [`-O${options.optimize}`, ...SECTION_FLAGS, ...debugFlags, "-c", options.irPath, "-o", options.objectPath],
91
253
  { env: options.env },
92
254
  );
93
255
  if (result.status !== 0) throw new ToolchainError(`${options.clang} ${options.irPath}`, result.status, result.stderr);
@@ -102,12 +264,39 @@ export interface LinkOptions {
102
264
  readonly env?: Record<string, string>;
103
265
  }
104
266
 
267
+ /** Platform flag that drops unreferenced sections (paired with {@link SECTION_FLAGS}). */
268
+ function deadStripFlag(): string[] {
269
+ if (process.platform === "win32") return []; // link.exe /OPT:REF is on by default
270
+ if (process.platform === "darwin") return ["-Wl,-dead_strip"];
271
+ return ["-Wl,--gc-sections"];
272
+ }
273
+
105
274
  export function link(runner: Runner, options: LinkOptions): void {
106
- const args = [`-O${options.optimize}`, ...options.objectPaths, "-o", options.outputPath, ...options.linkerFlags];
275
+ const args = [
276
+ `-O${options.optimize}`,
277
+ ...options.objectPaths,
278
+ "-o",
279
+ options.outputPath,
280
+ ...deadStripFlag(),
281
+ ...options.linkerFlags,
282
+ ];
107
283
  const result = runner.run(options.clang, args, { env: options.env });
108
284
  if (result.status !== 0) throw new ToolchainError(`${options.clang} link`, result.status, result.stderr);
109
285
  }
110
286
 
287
+ /**
288
+ * Flags shared by every runtime/extension C compile. Exported so the object
289
+ * cache key can change when they do: the cache is keyed by source content, and
290
+ * a flag change is a different object.
291
+ */
292
+ export const RUNTIME_CFLAGS = [
293
+ "-O2",
294
+ "-Wall",
295
+ "-Wextra",
296
+ "-D_CRT_SECURE_NO_WARNINGS",
297
+ ...SECTION_FLAGS,
298
+ ] as const;
299
+
111
300
  /** Compile a C source to an object file (used for the runtime and extensions). */
112
301
  export function compileC(
113
302
  runner: Runner,
@@ -119,7 +308,7 @@ export function compileC(
119
308
  ): void {
120
309
  const result = runner.run(
121
310
  clang,
122
- ["-O2", "-Wall", "-Wextra", "-D_CRT_SECURE_NO_WARNINGS", "-c", sourcePath, "-o", objectPath, `-I${includeDir}`],
311
+ [...RUNTIME_CFLAGS, "-c", sourcePath, "-o", objectPath, `-I${includeDir}`],
123
312
  { env },
124
313
  );
125
314
  if (result.status !== 0) throw new ToolchainError(`${clang} ${sourcePath}`, result.status, result.stderr);
@@ -98,8 +98,16 @@ export const guiExtension: Extension = {
98
98
  "-luser32", "-lgdi32", "-lshell32",
99
99
  "-lole32", "-loleaut32", "-luuid", "-ladvapi32",
100
100
  "-lwinmm", "-limm32", "-lversion", "-lsetupapi",
101
- /* The engine is C++; link the MSVC C++ runtime. */
102
- "-lmsvcprt",
101
+ /* The engine is C++, but `msvcprt` (the DLL C++ runtime) must not be
102
+ * named here: `gui.lib` is built with `CMAKE_MSVC_RUNTIME_LIBRARY=
103
+ * MultiThreaded` and every engine object records `libcpmt` (the static
104
+ * C++ runtime) as a dependent library — see `scripts/build-gui.ts`.
105
+ * The two runtimes define the same symbols, so requesting the DLL one
106
+ * as well fails the link with `LNK2038: mismatch detected for
107
+ * 'RuntimeLibrary'` plus a wall of `LNK2005: already defined` errors
108
+ * (surfacing as `clang: linker command failed with exit code 1169`).
109
+ * clang links the static `libcpmt` by default, which is the CRT the
110
+ * archive and the xbintsc runtime objects were both built against. */
103
111
  ];
104
112
  }
105
113
  return ["-lX11", "-lwayland-client", "-lEGL", "-lGL", "-ldl", "-lpthread", "-lstdc++"];
@@ -299,7 +299,26 @@ export const expressionMethods: ExpressionMethods = {
299
299
  },
300
300
 
301
301
  parseNewExpression(this: Parser): NewExpression {
302
- const start = this.parseExpected(TokenKind.NewKeyword).start;
302
+ const newToken = this.parseExpected(TokenKind.NewKeyword);
303
+ const start = newToken.start;
304
+ /* `new.target` is not part of the supported subset. Parsing it as a member
305
+ * access produced "Unexpected token '.'" pointing at the dot; report what
306
+ * is actually unsupported instead. */
307
+ if (this.at(TokenKind.Dot) && this.atAhead(1, TokenKind.Identifier) && this.lookAhead(1).text === "target") {
308
+ this.nextToken(); // .
309
+ const target = this.nextToken(); // target
310
+ this.error(DiagnosticCode.UnsupportedFeature, "xbintsc does not yet support new.target", {
311
+ ...newToken,
312
+ end: target.end,
313
+ });
314
+ const placeholder: Expression = {
315
+ kind: SyntaxKind.Identifier,
316
+ text: "undefined",
317
+ start: target.start,
318
+ end: target.end,
319
+ };
320
+ return { kind: SyntaxKind.NewExpression, expression: placeholder, typeArguments: [], arguments: [], start, end: target.end };
321
+ }
303
322
  const callee = this.parseMemberOnlyExpression();
304
323
  const typeArguments = this.at(TokenKind.LessThan) ? this.parseTypeArguments() : [];
305
324
  const args = this.at(TokenKind.OpenParen) ? this.parseArguments() : [];