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.
- package/README.md +13 -5
- package/README.zh-CN.md +13 -6
- package/dist/src/ast/common.d.ts +23 -0
- package/dist/src/cli/main.js +68 -24
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/calls/invocation.js +5 -1
- package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +50 -0
- package/dist/src/codegen/generator/context.js +124 -13
- package/dist/src/codegen/generator/context.js.map +1 -1
- package/dist/src/codegen/generator/debug.d.ts +33 -0
- package/dist/src/codegen/generator/debug.js +97 -0
- package/dist/src/codegen/generator/debug.js.map +1 -0
- package/dist/src/codegen/generator/expressions/assignment.js +9 -5
- package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
- package/dist/src/codegen/generator/expressions/operators.js +35 -9
- package/dist/src/codegen/generator/expressions/operators.js.map +1 -1
- package/dist/src/codegen/generator/expressions/primary.js +5 -2
- package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
- package/dist/src/codegen/generator/generator.d.ts +3 -1
- package/dist/src/codegen/generator/generator.js +3 -1
- package/dist/src/codegen/generator/generator.js.map +1 -1
- package/dist/src/codegen/generator/module.d.ts +1 -0
- package/dist/src/codegen/generator/module.js +76 -6
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/numeric.d.ts +26 -0
- package/dist/src/codegen/generator/numeric.js +127 -0
- package/dist/src/codegen/generator/numeric.js.map +1 -0
- package/dist/src/codegen/generator/state.d.ts +17 -0
- package/dist/src/codegen/generator/state.js +5 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements/control-flow.js +1 -1
- package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
- package/dist/src/codegen/generator/statements/dispatch.js +3 -0
- package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
- package/dist/src/codegen/generator/statements/variables.js +8 -2
- package/dist/src/codegen/generator/statements/variables.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +7 -0
- package/dist/src/codegen/generator/tables.js +25 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/merge.js +41 -5
- package/dist/src/driver/bundler/merge.js.map +1 -1
- package/dist/src/driver/compiler.d.ts +22 -3
- package/dist/src/driver/compiler.js +49 -58
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/fingerprint.d.ts +27 -0
- package/dist/src/driver/fingerprint.js +88 -0
- package/dist/src/driver/fingerprint.js.map +1 -0
- package/dist/src/driver/toolchain-provider.d.ts +9 -2
- package/dist/src/driver/toolchain-provider.js +15 -15
- package/dist/src/driver/toolchain-provider.js.map +1 -1
- package/dist/src/driver/toolchain.d.ts +58 -1
- package/dist/src/driver/toolchain.js +169 -22
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/extensions/gui/index.js +10 -2
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/parser/expressions.js +20 -1
- package/dist/src/parser/expressions.js.map +1 -1
- package/dist/tests/cli/main.test.js +41 -1
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/codegen/expressions.test.js +14 -3
- package/dist/tests/codegen/expressions.test.js.map +1 -1
- package/dist/tests/codegen/llvm.test.js +23 -0
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/driver/toolchain-provider.test.js +49 -2
- package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
- package/dist/tests/driver/toolchain.test.js +62 -3
- package/dist/tests/driver/toolchain.test.js.map +1 -1
- package/dist/tests/e2e/child-process.test.d.ts +10 -0
- package/dist/tests/e2e/child-process.test.js +59 -0
- package/dist/tests/e2e/child-process.test.js.map +1 -0
- package/dist/tests/e2e/compile-objects-classes.test.js +47 -0
- package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
- package/dist/tests/e2e/error-stack.test.d.ts +9 -0
- package/dist/tests/e2e/error-stack.test.js +94 -0
- package/dist/tests/e2e/error-stack.test.js.map +1 -0
- package/dist/tests/e2e/gui-example-svg.test.d.ts +21 -0
- package/dist/tests/e2e/gui-example-svg.test.js +105 -0
- package/dist/tests/e2e/gui-example-svg.test.js.map +1 -0
- package/dist/tests/e2e/gui-transform.test.d.ts +7 -0
- package/dist/tests/e2e/gui-transform.test.js +198 -0
- package/dist/tests/e2e/gui-transform.test.js.map +1 -0
- package/dist/tests/e2e/string-concat.test.d.ts +9 -0
- package/dist/tests/e2e/string-concat.test.js +102 -0
- package/dist/tests/e2e/string-concat.test.js.map +1 -0
- package/dist/tests/helpers.d.ts +9 -0
- package/dist/tests/helpers.js +26 -3
- package/dist/tests/helpers.js.map +1 -1
- package/dist/tests/parser/expressions.test.js +7 -0
- package/dist/tests/parser/expressions.test.js.map +1 -1
- package/dist/tests/version.test.d.ts +1 -0
- package/dist/tests/version.test.js +24 -0
- package/dist/tests/version.test.js.map +1 -0
- package/doc/DESIGN.md +20 -12
- package/doc/ai/cli.md +7 -0
- package/doc/ai/contributing.md +7 -1
- package/doc/ai/extensions.md +5 -1
- package/doc/ai/language-support.md +4 -4
- package/doc/ai/troubleshooting.md +39 -2
- package/doc/ai/zh-CN/cli.md +2 -0
- package/doc/ai/zh-CN/contributing.md +6 -2
- package/doc/ai/zh-CN/extensions.md +5 -1
- package/doc/ai/zh-CN/language-support.md +4 -4
- package/doc/ai/zh-CN/troubleshooting.md +35 -2
- package/doc/gui/animation.md +53 -2
- package/doc/gui/api.md +1 -1
- package/doc/gui/dom.md +6 -1
- package/doc/gui/input.md +4 -1
- package/doc/gui/known-issues.md +16 -4
- package/doc/gui/layout.md +36 -0
- package/doc/gui/rendering.md +15 -0
- package/doc/gui.md +32 -3
- package/doc/implemented.md +4 -4
- package/doc/requirements.md +13 -4
- package/doc/unimplemented.md +11 -10
- package/doc/zh-CN/DESIGN.md +15 -9
- package/doc/zh-CN/gui/animation.md +49 -2
- package/doc/zh-CN/gui/api.md +1 -1
- package/doc/zh-CN/gui/dom.md +6 -1
- package/doc/zh-CN/gui/input.md +2 -0
- package/doc/zh-CN/gui/known-issues.md +12 -2
- package/doc/zh-CN/gui/layout.md +31 -0
- package/doc/zh-CN/gui/rendering.md +11 -0
- package/doc/zh-CN/gui.md +28 -3
- package/doc/zh-CN/implemented.md +4 -4
- package/doc/zh-CN/requirements.md +12 -3
- package/doc/zh-CN/unimplemented.md +10 -9
- package/package.json +3 -1
- package/runtime/ext_gui/document.cpp +21 -3
- package/runtime/ext_gui/document.h +4 -1
- package/runtime/ext_gui/dom_api_node.cpp +6 -4
- package/runtime/ext_gui/layout.cpp +78 -1
- package/runtime/ext_gui/layout.h +21 -0
- package/runtime/ext_gui/paint.cpp +48 -15
- package/runtime/ext_gui/paint.h +8 -2
- package/runtime/ext_gui/renderer.cpp +4 -2
- package/runtime/ext_gui/renderer_image.cpp +2 -1
- package/runtime/ext_gui/renderer_internal.h +6 -1
- package/runtime/ext_gui/renderer_text.cpp +37 -15
- package/runtime/ext_gui/style.h +10 -0
- package/runtime/ext_gui/style_print.cpp +6 -0
- package/runtime/ext_gui/style_values.cpp +43 -0
- package/runtime/ext_gui/transform.cpp +412 -0
- package/runtime/ext_gui/transform.h +112 -0
- package/runtime/ext_gui/window.cpp +6 -4
- package/runtime/ext_node/buffer/parts/construction.inc +2 -2
- package/runtime/ext_node/buffer/parts/methods.inc +1 -1
- package/runtime/ext_node/buffer/parts/search.inc +1 -1
- package/runtime/ext_node/child_process/child_process.c +25 -0
- package/runtime/ext_node/fs/fd_ops.c +1 -1
- package/runtime/rt.h +17 -1
- package/runtime/rt_internal.h +47 -4
- package/runtime/xt_alloc.c +14 -2
- package/runtime/xt_bigint/conversions.inc +1 -1
- package/runtime/xt_builtins.c +136 -14
- package/runtime/xt_containers/access.inc +17 -16
- package/runtime/xt_containers/objects.inc +4 -3
- package/runtime/xt_io/exception.inc +171 -2
- package/runtime/xt_io/inspect.inc +15 -13
- package/runtime/xt_io.c +5 -0
- package/runtime/xt_stdlib/arrays.inc +2 -2
- package/runtime/xt_stdlib/dispatch.inc +2 -2
- package/runtime/xt_stdlib/strings.inc +33 -21
- package/runtime/xt_stdlib2/array-statics.inc +2 -1
- package/runtime/xt_stdlib2/arrays.inc +1 -1
- package/runtime/xt_stdlib2/error.inc +22 -6
- package/runtime/xt_stdlib2/json.inc +3 -2
- package/runtime/xt_stdlib2/number-string-statics.inc +3 -1
- package/runtime/xt_stdlib2/regexp-match.inc +48 -33
- package/runtime/xt_stdlib2/strings.inc +23 -14
- package/runtime/xt_symbol.c +1 -1
- package/runtime/xt_values/comparison.inc +2 -2
- package/runtime/xt_values/constructors.inc +10 -5
- package/runtime/xt_values/conversions.inc +4 -3
- package/runtime/xt_values/strings.inc +78 -4
- package/scripts/bench.ts +172 -0
- package/scripts/build-runtime.ts +2 -1
- package/scripts/check-version.ts +53 -0
- package/src/ast/common.ts +23 -0
- package/src/cli/main.ts +68 -26
- package/src/codegen/generator/calls/invocation.ts +5 -0
- package/src/codegen/generator/context.ts +129 -18
- package/src/codegen/generator/debug.ts +132 -0
- package/src/codegen/generator/expressions/assignment.ts +9 -5
- package/src/codegen/generator/expressions/operators.ts +47 -8
- package/src/codegen/generator/expressions/primary.ts +5 -2
- package/src/codegen/generator/generator.ts +18 -2
- package/src/codegen/generator/module.ts +78 -6
- package/src/codegen/generator/numeric.ts +158 -0
- package/src/codegen/generator/state.ts +22 -0
- package/src/codegen/generator/statements/control-flow.ts +1 -1
- package/src/codegen/generator/statements/dispatch.ts +3 -0
- package/src/codegen/generator/statements/variables.ts +8 -2
- package/src/codegen/generator/tables.ts +26 -0
- package/src/driver/bundler/merge.ts +39 -5
- package/src/driver/compiler.ts +68 -56
- package/src/driver/fingerprint.ts +85 -0
- package/src/driver/toolchain-provider.ts +17 -16
- package/src/driver/toolchain.ts +210 -21
- package/src/extensions/gui/index.ts +10 -2
- package/src/parser/expressions.ts +20 -1
package/scripts/bench.ts
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Build and time a small, fixed benchmark suite.
|
|
4
|
+
*
|
|
5
|
+
* The numbers are wall-clock times on the current machine, so they are only
|
|
6
|
+
* useful for comparing revisions of the compiler (or the same revision against
|
|
7
|
+
* Node): run `npm run bench` before and after a change to the runtime or
|
|
8
|
+
* codegen. Each program is compiled with the normal driver (cache bypassed) and
|
|
9
|
+
* executed several times; the table reports the fastest run to reduce noise.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* npm run bench # every benchmark, 3 runs, with Node for scale
|
|
13
|
+
* npm run bench -- --runs 5 # more runs (minimum is reported)
|
|
14
|
+
* npm run bench -- --filter prop
|
|
15
|
+
* npm run bench -- --no-node # skip the Node comparison
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { spawnSync } from "node:child_process";
|
|
19
|
+
import { mkdirSync, statSync, writeFileSync } from "node:fs";
|
|
20
|
+
import { dirname, join, resolve } from "node:path";
|
|
21
|
+
import { fileURLToPath } from "node:url";
|
|
22
|
+
import { build } from "../src/driver/index.js";
|
|
23
|
+
|
|
24
|
+
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
25
|
+
const outDir = join(root, "build", "bench");
|
|
26
|
+
const cacheDir = join(root, "build", "bench-cache");
|
|
27
|
+
|
|
28
|
+
interface Benchmark {
|
|
29
|
+
readonly name: string;
|
|
30
|
+
/** Plain JavaScript so the same source can run under Node. */
|
|
31
|
+
readonly source: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const BENCHMARKS: readonly Benchmark[] = [
|
|
35
|
+
{
|
|
36
|
+
name: "numeric",
|
|
37
|
+
source: `
|
|
38
|
+
let total = 0;
|
|
39
|
+
for (let i = 0; i < 50000000; i++) total = total + i;
|
|
40
|
+
console.log(total);
|
|
41
|
+
`,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "property",
|
|
45
|
+
source: `
|
|
46
|
+
const object = { x: 0 };
|
|
47
|
+
for (let i = 0; i < 5000000; i++) object.x = object.x + 1;
|
|
48
|
+
console.log(object.x);
|
|
49
|
+
`,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "array",
|
|
53
|
+
source: `
|
|
54
|
+
const values = [];
|
|
55
|
+
for (let i = 0; i < 3000000; i++) values[i] = i;
|
|
56
|
+
let total = 0;
|
|
57
|
+
for (let i = 0; i < 3000000; i++) total = total + values[i];
|
|
58
|
+
console.log(total);
|
|
59
|
+
`,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "string-concat",
|
|
63
|
+
source: `
|
|
64
|
+
let text = "";
|
|
65
|
+
for (let i = 0; i < 100000; i++) text = text + "a";
|
|
66
|
+
console.log(text.length);
|
|
67
|
+
`,
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
interface Options {
|
|
72
|
+
readonly runs: number;
|
|
73
|
+
readonly filter?: string;
|
|
74
|
+
readonly node: boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function parseOptions(argv: readonly string[]): Options {
|
|
78
|
+
let runs = 3;
|
|
79
|
+
let filter: string | undefined;
|
|
80
|
+
let node = true;
|
|
81
|
+
for (let index = 0; index < argv.length; index++) {
|
|
82
|
+
const arg = argv[index]!;
|
|
83
|
+
if (arg === "--no-node") node = false;
|
|
84
|
+
else if (arg === "--runs") runs = Math.max(1, Number(argv[++index]) || 1);
|
|
85
|
+
else if (arg.startsWith("--runs=")) runs = Math.max(1, Number(arg.slice("--runs=".length)) || 1);
|
|
86
|
+
else if (arg === "--filter") filter = argv[++index];
|
|
87
|
+
else if (arg.startsWith("--filter=")) filter = arg.slice("--filter=".length);
|
|
88
|
+
else if (arg === "--help" || arg === "-h") {
|
|
89
|
+
process.stdout.write(
|
|
90
|
+
"usage: npm run bench [-- --runs N] [--filter <substr>] [--no-node]\n",
|
|
91
|
+
);
|
|
92
|
+
process.exit(0);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return { runs, filter, node };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function minTimeMs(runs: number, execute: () => void): number {
|
|
99
|
+
let best = Number.POSITIVE_INFINITY;
|
|
100
|
+
for (let run = 0; run < runs; run++) {
|
|
101
|
+
const start = process.hrtime.bigint();
|
|
102
|
+
execute();
|
|
103
|
+
const elapsed = Number(process.hrtime.bigint() - start) / 1e6;
|
|
104
|
+
if (elapsed < best) best = elapsed;
|
|
105
|
+
}
|
|
106
|
+
return best;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function formatMs(value: number | undefined): string {
|
|
110
|
+
return value === undefined ? "-" : `${value.toFixed(1)} ms`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function main(): number {
|
|
114
|
+
const options = parseOptions(process.argv.slice(2));
|
|
115
|
+
mkdirSync(outDir, { recursive: true });
|
|
116
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
117
|
+
const benchmarks = BENCHMARKS.filter(
|
|
118
|
+
(benchmark) => options.filter === undefined || benchmark.name.includes(options.filter),
|
|
119
|
+
);
|
|
120
|
+
if (benchmarks.length === 0) {
|
|
121
|
+
process.stderr.write(`xbintsc: no benchmark matches '${options.filter}'\n`);
|
|
122
|
+
return 1;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const rows: { name: string; ours?: number; node?: number; bytes?: number }[] = [];
|
|
126
|
+
for (const benchmark of benchmarks) {
|
|
127
|
+
const entry = join(outDir, `${benchmark.name}.ts`);
|
|
128
|
+
writeFileSync(entry, benchmark.source);
|
|
129
|
+
const result = build(entry, {
|
|
130
|
+
emit: "exe",
|
|
131
|
+
outDir: join(outDir, "bin"),
|
|
132
|
+
cacheDir,
|
|
133
|
+
force: true,
|
|
134
|
+
});
|
|
135
|
+
const errors = result.diagnostics.filter((diagnostic) => diagnostic.category === "error");
|
|
136
|
+
if (errors.length > 0) {
|
|
137
|
+
for (const error of errors) process.stderr.write(`xbintsc: ${error.message}\n`);
|
|
138
|
+
return 1;
|
|
139
|
+
}
|
|
140
|
+
const bytes = statSync(result.outputPath).size;
|
|
141
|
+
const ours = minTimeMs(options.runs, () => {
|
|
142
|
+
const executed = spawnSync(result.outputPath, [], { stdio: "ignore" });
|
|
143
|
+
if (executed.status !== 0) throw new Error(`${benchmark.name} exited with ${executed.status}`);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
let nodeTime: number | undefined;
|
|
147
|
+
if (options.node) {
|
|
148
|
+
const script = join(outDir, `${benchmark.name}.mjs`);
|
|
149
|
+
writeFileSync(script, benchmark.source);
|
|
150
|
+
nodeTime = minTimeMs(options.runs, () => {
|
|
151
|
+
const executed = spawnSync(process.execPath, [script], { stdio: "ignore" });
|
|
152
|
+
if (executed.status !== 0) throw new Error(`${benchmark.name} (node) exited with ${executed.status}`);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
rows.push({ name: benchmark.name, ours, node: nodeTime, bytes });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const header = ["benchmark", "xbintsc", "node", "size"];
|
|
159
|
+
process.stdout.write(
|
|
160
|
+
`${header[0]!.padEnd(16)}${header[1]!.padStart(12)}${header[2]!.padStart(12)}${header[3]!.padStart(12)}\n`,
|
|
161
|
+
);
|
|
162
|
+
for (const row of rows) {
|
|
163
|
+
const size = row.bytes === undefined ? "-" : `${Math.round(row.bytes / 1024)} KB`;
|
|
164
|
+
process.stdout.write(
|
|
165
|
+
`${row.name.padEnd(16)}${formatMs(row.ours).padStart(12)}${formatMs(row.node).padStart(12)}${size.padStart(12)}\n`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
process.stdout.write(`\nfastest of ${options.runs} run(s) on ${process.platform}-${process.arch}\n`);
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
process.exit(main());
|
package/scripts/build-runtime.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { fileURLToPath } from "node:url";
|
|
|
21
21
|
import { RUNTIME_SOURCES } from "../src/driver/compiler.js";
|
|
22
22
|
import { platformSlug } from "../src/driver/paths.js";
|
|
23
23
|
import { resolveToolchain } from "../src/driver/toolchain-provider.js";
|
|
24
|
+
import { RUNTIME_CFLAGS } from "../src/driver/toolchain.js";
|
|
24
25
|
import { nodeExtension } from "../src/extensions/node/index.js";
|
|
25
26
|
|
|
26
27
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
@@ -72,7 +73,7 @@ function compile(sources: readonly string[], extension: string): string[] {
|
|
|
72
73
|
const object = join(objDir, `${extension}_${index}_${basename(source, ".c")}.o`);
|
|
73
74
|
const result = spawnSync(
|
|
74
75
|
clang,
|
|
75
|
-
[
|
|
76
|
+
[...RUNTIME_CFLAGS, "-c", source, "-o", object, `-I${runtimeDir}`],
|
|
76
77
|
{ stdio: "inherit", env: childEnv },
|
|
77
78
|
);
|
|
78
79
|
if (result.status !== 0) {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Verify that `COMPILER_VERSION` (reported by `xbintsc version` / `doctor` and
|
|
4
|
+
* embedded in the incremental cache key) matches the version in `package.json`
|
|
5
|
+
* (which names the release archives).
|
|
6
|
+
*
|
|
7
|
+
* Release automation runs this before tagging. A mismatch means an upgraded
|
|
8
|
+
* compiler can reuse binaries produced by the previous one while presenting the
|
|
9
|
+
* wrong version to bug reports.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
13
|
+
import { dirname, join, resolve } from "node:path";
|
|
14
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
15
|
+
|
|
16
|
+
/** Read the version literal from the compiler source. */
|
|
17
|
+
export function compilerVersionOf(root: string): string {
|
|
18
|
+
const text = readFileSync(join(root, "src", "driver", "compiler.ts"), "utf8");
|
|
19
|
+
const match = /export const COMPILER_VERSION = "([^"]+)"/.exec(text);
|
|
20
|
+
if (!match) throw new Error("COMPILER_VERSION is not declared in src/driver/compiler.ts");
|
|
21
|
+
return match[1]!;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Read the published version from package.json. */
|
|
25
|
+
export function packageVersionOf(root: string): string {
|
|
26
|
+
const parsed = JSON.parse(readFileSync(join(root, "package.json"), "utf8")) as { version?: string };
|
|
27
|
+
if (!parsed.version) throw new Error("package.json has no version field");
|
|
28
|
+
return parsed.version;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Returns a human-readable mismatch message, or `undefined` when in sync. */
|
|
32
|
+
export function checkCompilerVersion(root: string): string | undefined {
|
|
33
|
+
const packageVersion = packageVersionOf(root);
|
|
34
|
+
const compilerVersion = compilerVersionOf(root);
|
|
35
|
+
if (packageVersion === compilerVersion) return undefined;
|
|
36
|
+
return (
|
|
37
|
+
`COMPILER_VERSION is "${compilerVersion}" but package.json is "${packageVersion}".\n` +
|
|
38
|
+
'Update COMPILER_VERSION in src/driver/compiler.ts (it keys the incremental cache), or fix package.json.'
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const invokedDirectly =
|
|
43
|
+
process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
44
|
+
|
|
45
|
+
if (invokedDirectly) {
|
|
46
|
+
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
47
|
+
const mismatch = checkCompilerVersion(root);
|
|
48
|
+
if (mismatch) {
|
|
49
|
+
process.stderr.write(`xbintsc: version mismatch\n${mismatch}\n`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
process.stdout.write(`xbintsc: version ${compilerVersionOf(root)} matches package.json\n`);
|
|
53
|
+
}
|
package/src/ast/common.ts
CHANGED
|
@@ -207,4 +207,27 @@ export interface SourceFileNode extends Node {
|
|
|
207
207
|
readonly statements: Statement[];
|
|
208
208
|
readonly fileName: string;
|
|
209
209
|
readonly text: string;
|
|
210
|
+
/**
|
|
211
|
+
* For a bundled program: the original module each node was parsed from.
|
|
212
|
+
*
|
|
213
|
+
* The bundler merges every module into one `SourceFile`, but the nodes keep
|
|
214
|
+
* their module-local `start`/`end` offsets. Diagnostics must therefore report
|
|
215
|
+
* the module a node came from, not the entry file, or a cross-module error
|
|
216
|
+
* shows the wrong file and a nonsensical excerpt. Absent for a single-file
|
|
217
|
+
* compile, where `fileName` is the only source.
|
|
218
|
+
*/
|
|
219
|
+
readonly origins?: Map<object, string>;
|
|
220
|
+
/**
|
|
221
|
+
* For a bundled program: the original text of every module, keyed like
|
|
222
|
+
* {@link origins}. A node's `start` is relative to its module's text, so
|
|
223
|
+
* computing a frame's line number needs that text rather than the merged
|
|
224
|
+
* concatenation in `text`.
|
|
225
|
+
*/
|
|
226
|
+
readonly moduleTexts?: Map<string, string>;
|
|
227
|
+
/**
|
|
228
|
+
* Bundler-generated symbol prefixes (`m0_`, `m1_`, …). Display names (stack
|
|
229
|
+
* frames, symbol suffixes) strip them so a bundled `fail()` still shows as
|
|
230
|
+
* `fail`, not `m1_fail`.
|
|
231
|
+
*/
|
|
232
|
+
readonly modulePrefixes?: readonly string[];
|
|
210
233
|
}
|
package/src/cli/main.ts
CHANGED
|
@@ -18,11 +18,11 @@ import { dirname, resolve } from "node:path";
|
|
|
18
18
|
import { pathToFileURL } from "node:url";
|
|
19
19
|
import { formatDiagnostic, type Diagnostic } from "../diagnostics/diagnostic.js";
|
|
20
20
|
import { SourceFile } from "../diagnostics/source.js";
|
|
21
|
-
import { build, compileEntry, COMPILER_VERSION, type EmitKind } from "../driver/compiler.js";
|
|
21
|
+
import { build, compileEntry, COMPILER_VERSION, type EmitKind, type PhaseTiming } from "../driver/compiler.js";
|
|
22
22
|
import { findRuntimeDir, platformSlug } from "../driver/paths.js";
|
|
23
23
|
import { runtimeLibDir } from "../driver/runtime-lib.js";
|
|
24
|
-
import { resolveToolchain } from "../driver/toolchain-provider.js";
|
|
25
|
-
import { realRunner, ToolchainError } from "../driver/toolchain.js";
|
|
24
|
+
import { resolveToolchain, type ResolvedToolchain } from "../driver/toolchain-provider.js";
|
|
25
|
+
import { MINIMUM_CLANG_MAJOR, probeClang, realRunner, ToolchainError } from "../driver/toolchain.js";
|
|
26
26
|
import {
|
|
27
27
|
DOC_CLI,
|
|
28
28
|
DOC_TROUBLESHOOTING,
|
|
@@ -154,23 +154,39 @@ function buildRegistry(extNames: readonly string[], nativeManifests: readonly st
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
function printDiagnostics(diagnostics: readonly Diagnostic[], fileName: string, io: CliIo): void {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
157
|
+
/* A diagnostic can come from any module in a bundled program, so load (and
|
|
158
|
+
* cache) the source of the file it names instead of always using the entry:
|
|
159
|
+
* reusing the entry's text is what used to print another file's line with a
|
|
160
|
+
* caret under unrelated code. */
|
|
161
|
+
const sources = new Map<string, SourceFile | undefined>();
|
|
162
|
+
const sourceFor = (name: string): SourceFile | undefined => {
|
|
163
|
+
if (!sources.has(name)) {
|
|
164
|
+
try {
|
|
165
|
+
sources.set(name, new SourceFile(name, readFileSync(name, "utf8")));
|
|
166
|
+
} catch {
|
|
167
|
+
sources.set(name, undefined);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return sources.get(name);
|
|
171
|
+
};
|
|
163
172
|
for (const diagnostic of diagnostics) {
|
|
164
173
|
// Driver-level diagnostics (icon, config, …) have no source location.
|
|
165
174
|
if (diagnostic.fileName === undefined && diagnostic.start === undefined) {
|
|
166
175
|
io.stderr(`error TS${diagnostic.code}: ${diagnostic.message}\n`);
|
|
167
176
|
continue;
|
|
168
177
|
}
|
|
178
|
+
const source = sourceFor(diagnostic.fileName ?? fileName);
|
|
169
179
|
io.stderr(formatDiagnostic(diagnostic, source) + "\n");
|
|
170
180
|
}
|
|
171
181
|
for (const hint of documentationHints(diagnostics)) emitHint(io, hint);
|
|
172
182
|
}
|
|
173
183
|
|
|
184
|
+
/** Print the phases collected by `--time`. */
|
|
185
|
+
function printTimings(timings: readonly PhaseTiming[], io: CliIo): void {
|
|
186
|
+
const text = timings.map((timing) => `${timing.phase} ${timing.ms}ms`).join(", ");
|
|
187
|
+
io.stdout(`xbintsc: timing (${text})\n`);
|
|
188
|
+
}
|
|
189
|
+
|
|
174
190
|
/** Print one `hint:` line unless the caller asked for plain output. */
|
|
175
191
|
function emitHint(io: CliIo, line: string): void {
|
|
176
192
|
if (hintsDisabled) return;
|
|
@@ -237,6 +253,8 @@ Options:
|
|
|
237
253
|
--app-id <id> macOS bundle identifier (e.g. com.example.demo)
|
|
238
254
|
--force Ignore the incremental cache
|
|
239
255
|
--verbose Print progress information
|
|
256
|
+
--time Print per-phase build timings
|
|
257
|
+
--debug Emit debug info (DWARF/CodeView) and keep frame pointers
|
|
240
258
|
--no-hints Do not append the "hint: … see <doc>" line to failures
|
|
241
259
|
|
|
242
260
|
xbintsc compiles a subset of TypeScript to a native binary. Types are erased
|
|
@@ -247,26 +265,47 @@ Docs: doc/ai/README.md (task guide) - doc/ai/language-support.md (subset) -
|
|
|
247
265
|
doc/ai/troubleshooting.md (failures) - llms.txt (index of every document)
|
|
248
266
|
`;
|
|
249
267
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
268
|
+
/**
|
|
269
|
+
* Report the resolved toolchain and runtime locations.
|
|
270
|
+
*
|
|
271
|
+
* Returns non-zero when the resolved compiler cannot build anything (missing,
|
|
272
|
+
* or older than {@link MINIMUM_CLANG_MAJOR}), so CI and scripts can use it as a
|
|
273
|
+
* preflight instead of discovering the problem halfway through a build.
|
|
274
|
+
*/
|
|
256
275
|
function doctor(io: CliIo): number {
|
|
257
276
|
io.stdout(`xbintsc ${COMPILER_VERSION}\n`);
|
|
258
277
|
io.stdout(`platform : ${platformSlug()}\n`);
|
|
259
278
|
|
|
279
|
+
let toolchain: ResolvedToolchain | undefined;
|
|
280
|
+
let usable = false;
|
|
260
281
|
try {
|
|
261
|
-
|
|
262
|
-
const
|
|
282
|
+
toolchain = resolveToolchain();
|
|
283
|
+
const probe = probeClang(realRunner, toolchain.clang, toolchain.env);
|
|
263
284
|
io.stdout(`toolchain : ${toolchain.clang} (${toolchain.source})\n`);
|
|
264
|
-
io.stdout(`compiler : ${
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
285
|
+
io.stdout(`compiler : ${probe.summary || "unknown"}\n`);
|
|
286
|
+
const version = toolchain.version ?? probe.version;
|
|
287
|
+
if (!version) {
|
|
288
|
+
// An explicit wrapper need not print a clang-style version; let it run.
|
|
289
|
+
usable = true;
|
|
290
|
+
io.stdout(`status : version unknown (requires clang >= ${MINIMUM_CLANG_MAJOR})\n`);
|
|
291
|
+
} else if (version.major >= MINIMUM_CLANG_MAJOR) {
|
|
292
|
+
usable = true;
|
|
293
|
+
io.stdout(`status : ok (requires clang >= ${MINIMUM_CLANG_MAJOR})\n`);
|
|
294
|
+
} else {
|
|
295
|
+
io.stdout(
|
|
296
|
+
`status : too old: clang ${version.major}.${version.minor}.${version.patch} ` +
|
|
297
|
+
`(requires >= ${MINIMUM_CLANG_MAJOR})\n`,
|
|
298
|
+
);
|
|
299
|
+
io.stdout(" set xbintsc_CLANG to a newer clang; on Windows Visual Studio ships one under\n");
|
|
300
|
+
io.stdout(" <VS install>\\VC\\Tools\\Llvm\\x64\\bin\\clang.exe. See doc/requirements.md.\n");
|
|
301
|
+
}
|
|
302
|
+
} catch (error) {
|
|
303
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
304
|
+
io.stdout("toolchain : not usable\n");
|
|
305
|
+
for (const line of message.split("\n")) {
|
|
306
|
+
if (line.trim().length > 0) io.stdout(` ${line.trim()}\n`);
|
|
307
|
+
}
|
|
308
|
+
io.stdout(" See doc/requirements.md for the per-platform install steps, or set xbintsc_CLANG.\n");
|
|
270
309
|
}
|
|
271
310
|
|
|
272
311
|
try {
|
|
@@ -282,13 +321,13 @@ function doctor(io: CliIo): number {
|
|
|
282
321
|
}
|
|
283
322
|
|
|
284
323
|
try {
|
|
285
|
-
|
|
324
|
+
if (!toolchain) throw new Error("no toolchain");
|
|
286
325
|
const resource = resolveResourceCompiler(realRunner, toolchain.clang);
|
|
287
326
|
io.stdout(`icon res : ${resource ? `${resource.tool} (${resource.kind})` : "none (.exe icon disabled)"}\n`);
|
|
288
327
|
} catch {
|
|
289
328
|
io.stdout("icon res : unknown\n");
|
|
290
329
|
}
|
|
291
|
-
return 0;
|
|
330
|
+
return usable ? 0 : 1;
|
|
292
331
|
}
|
|
293
332
|
|
|
294
333
|
type ConfigResolution =
|
|
@@ -388,7 +427,7 @@ function dispatch(argv: readonly string[], io: CliIo): number {
|
|
|
388
427
|
const extNames = listFlag(args.flags, "ext") ?? resolved.config.extensions ?? [];
|
|
389
428
|
const nativeManifests = listFlag(args.flags, "ext-native") ?? resolved.config.extNative ?? [];
|
|
390
429
|
const registry = buildRegistry(extNames, nativeManifests);
|
|
391
|
-
const { ir, diagnostics } = compileEntry(entry, registry);
|
|
430
|
+
const { ir, diagnostics } = compileEntry(entry, registry, { debug: Boolean(args.flags.get("debug")) });
|
|
392
431
|
if (diagnostics.some((d) => d.category === "error")) {
|
|
393
432
|
printDiagnostics(diagnostics, entry, io);
|
|
394
433
|
return 1;
|
|
@@ -423,6 +462,7 @@ function dispatch(argv: readonly string[], io: CliIo): number {
|
|
|
423
462
|
verbose: Boolean(args.flags.get("verbose")),
|
|
424
463
|
extensions: registry,
|
|
425
464
|
app: mergeAppConfig(args.flags, config),
|
|
465
|
+
debug: Boolean(args.flags.get("debug")),
|
|
426
466
|
});
|
|
427
467
|
|
|
428
468
|
if (result.diagnostics.some((d) => d.category === "error")) {
|
|
@@ -430,6 +470,8 @@ function dispatch(argv: readonly string[], io: CliIo): number {
|
|
|
430
470
|
return 1;
|
|
431
471
|
}
|
|
432
472
|
|
|
473
|
+
if (args.flags.has("time") && result.timings) printTimings(result.timings, io);
|
|
474
|
+
|
|
433
475
|
if (command === "build") {
|
|
434
476
|
const bundle = result.bundlePath ? ` (bundle: ${result.bundlePath})` : "";
|
|
435
477
|
io.stdout(`xbintsc: wrote ${result.outputPath}${result.cached ? " (cached)" : ""}${bundle}\n`);
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
BUILTIN_METHODS,
|
|
20
20
|
CONSOLE_METHODS,
|
|
21
21
|
CTOR_FUNCTIONS,
|
|
22
|
+
ERROR_CONSTRUCTORS,
|
|
22
23
|
GLOBAL_FUNCTIONS,
|
|
23
24
|
MATH_FUNCTIONS,
|
|
24
25
|
NAMESPACE_STATICS,
|
|
@@ -142,6 +143,10 @@ export const invocationCallMethods: InvocationCallMethods = {
|
|
|
142
143
|
}
|
|
143
144
|
const ctor = !symbol ? CTOR_FUNCTIONS[(callee as Identifier).text] : undefined;
|
|
144
145
|
if (ctor) {
|
|
146
|
+
// `Error("x")` without `new`: point the innermost frame at the call.
|
|
147
|
+
if (ERROR_CONSTRUCTORS[(callee as Identifier).text]) {
|
|
148
|
+
this.emit(` call void @xt_stack_set_line(i32 ${this.lineOf(node)})`);
|
|
149
|
+
}
|
|
145
150
|
const args = this.emitArguments(node.arguments);
|
|
146
151
|
const result = this.reg();
|
|
147
152
|
this.emit(` ${result} = call i64 @${ctor}(i32 ${args.argc}, i64* ${args.ptr})`);
|