xbintsc 0.3.6 → 0.3.8
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 +56 -32
- package/README.zh-CN.md +53 -30
- package/dist/src/ast/expressions.d.ts +2 -1
- package/dist/src/cli/main.d.ts +1 -0
- package/dist/src/cli/main.js +13 -1
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/driver/compiler.js +22 -4
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/extensions/native.d.ts +92 -0
- package/dist/src/extensions/native.js +217 -0
- package/dist/src/extensions/native.js.map +1 -0
- package/dist/src/extensions/registry.d.ts +13 -0
- package/dist/src/extensions/registry.js +10 -0
- package/dist/src/extensions/registry.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/lexer/scanner/numbers.js +8 -3
- package/dist/src/lexer/scanner/numbers.js.map +1 -1
- package/dist/src/parser/expressions.js +1 -1
- package/dist/src/parser/expressions.js.map +1 -1
- package/dist/tests/cli/main.test.js +15 -0
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/e2e/native-extensions.test.d.ts +13 -0
- package/dist/tests/e2e/native-extensions.test.js +175 -0
- package/dist/tests/e2e/native-extensions.test.js.map +1 -0
- package/dist/tests/extensions/native.test.d.ts +1 -0
- package/dist/tests/extensions/native.test.js +101 -0
- package/dist/tests/extensions/native.test.js.map +1 -0
- package/dist/tests/lexer/numbers.test.js +9 -0
- package/dist/tests/lexer/numbers.test.js.map +1 -1
- package/dist/tests/parser/literals.test.js +7 -0
- package/dist/tests/parser/literals.test.js.map +1 -1
- package/package.json +1 -1
- package/runtime/xt_ext.h +109 -0
- package/runtime/xt_ext.rs +173 -0
- package/src/ast/expressions.ts +2 -1
- package/src/cli/main.ts +13 -1
- package/src/driver/compiler.ts +25 -4
- package/src/extensions/native.ts +270 -0
- package/src/extensions/registry.ts +21 -0
- package/src/index.ts +10 -0
- package/src/lexer/scanner/numbers.ts +7 -3
- package/src/parser/expressions.ts +1 -1
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end tests for native (C++ / Rust) extensions.
|
|
3
|
+
*
|
|
4
|
+
* Each case compiles a tiny native library to an `extern "C"` object/archive,
|
|
5
|
+
* describes it with a manifest and links it into a TypeScript program. The
|
|
6
|
+
* suite is skipped when the corresponding native toolchain is unavailable, so
|
|
7
|
+
* on a bare CI image nothing is compiled. Windows is skipped here (see
|
|
8
|
+
* examples/extensions/README.md): this suite resolves a compiler/archiver from
|
|
9
|
+
* `PATH`, which on Windows is the MSVC-ABI clang, while xbintsc links with the
|
|
10
|
+
* bundled MinGW-w64 toolchain. Windows coverage lives in the `compile-examples`
|
|
11
|
+
* CI job, which points `CXX`/`AR` at that bundled toolchain.
|
|
12
|
+
*/
|
|
13
|
+
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { join, resolve } from "node:path";
|
|
16
|
+
import { spawnSync } from "node:child_process";
|
|
17
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
18
|
+
import { build } from "../../src/driver/compiler.js";
|
|
19
|
+
import { createDefaultRegistry } from "../../src/extensions/registry.js";
|
|
20
|
+
import { nativeExtensionFromManifest } from "../../src/extensions/native.js";
|
|
21
|
+
import { findRuntimeDir } from "../../src/driver/paths.js";
|
|
22
|
+
function firstAvailable(candidates) {
|
|
23
|
+
for (const candidate of candidates) {
|
|
24
|
+
if (!candidate)
|
|
25
|
+
continue;
|
|
26
|
+
const result = spawnSync(candidate, ["--version"], { encoding: "utf8" });
|
|
27
|
+
if (!result.error && result.status === 0)
|
|
28
|
+
return candidate;
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
/** Like {@link firstAvailable}, but tolerates tools whose `--version` exits non-zero (Apple `ar`). */
|
|
33
|
+
function firstRunnable(candidates) {
|
|
34
|
+
for (const candidate of candidates) {
|
|
35
|
+
if (!candidate)
|
|
36
|
+
continue;
|
|
37
|
+
if (!spawnSync(candidate, ["--version"], { encoding: "utf8" }).error)
|
|
38
|
+
return candidate;
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
const cxx = firstAvailable([process.env.xbintsc_CXX, "clang++", "c++", "g++"]);
|
|
43
|
+
const ar = firstRunnable([process.env.xbintsc_AR, "llvm-ar", "ar"]);
|
|
44
|
+
const cargo = firstAvailable([process.env.CARGO, "cargo"]);
|
|
45
|
+
/**
|
|
46
|
+
* Compile a C++ source into a static archive next to it.
|
|
47
|
+
*/
|
|
48
|
+
function buildCppArchive(dir, source, stem) {
|
|
49
|
+
const sourcePath = join(dir, `${stem}.cpp`);
|
|
50
|
+
const objectPath = join(dir, `${stem}.o`);
|
|
51
|
+
const archivePath = join(dir, `lib${stem}.a`);
|
|
52
|
+
writeFileSync(sourcePath, source);
|
|
53
|
+
const compiled = spawnSync(cxx, ["-O2", "-fPIC", "-I", findRuntimeDir(), "-c", sourcePath, "-o", objectPath], { encoding: "utf8" });
|
|
54
|
+
expect(compiled.status, compiled.stderr).toBe(0);
|
|
55
|
+
const archived = spawnSync(ar, ["rcs", archivePath, objectPath], { encoding: "utf8" });
|
|
56
|
+
expect(archived.status, archived.stderr).toBe(0);
|
|
57
|
+
return archivePath;
|
|
58
|
+
}
|
|
59
|
+
// Skipped on Windows: this suite uses PATH clang (MSVC ABI); see the file header.
|
|
60
|
+
describe.skipIf(!cxx || !ar || process.platform === "win32")("native C++ extension", () => {
|
|
61
|
+
let workdir;
|
|
62
|
+
beforeAll(() => {
|
|
63
|
+
workdir = mkdtempSync(join(tmpdir(), "xbintsc-cxx-"));
|
|
64
|
+
});
|
|
65
|
+
afterAll(() => {
|
|
66
|
+
rmSync(workdir, { recursive: true, force: true });
|
|
67
|
+
});
|
|
68
|
+
it("links a C++ archive and calls into it from TypeScript", () => {
|
|
69
|
+
buildCppArchive(workdir, `
|
|
70
|
+
#include "xt_ext.h"
|
|
71
|
+
#include <string>
|
|
72
|
+
#include <algorithm>
|
|
73
|
+
|
|
74
|
+
XT_EXT_FN(mathx_add) {
|
|
75
|
+
return xt_number(xt_ext_number(argc, argv, 0, 0) + xt_ext_number(argc, argv, 1, 0));
|
|
76
|
+
}
|
|
77
|
+
XT_EXT_FN(mathx_reverse) {
|
|
78
|
+
size_t length = 0;
|
|
79
|
+
const char *text = xt_ext_string(argc, argv, 0, &length);
|
|
80
|
+
if (text == NULL) return xt_ext_string_value("", 0);
|
|
81
|
+
std::string reversed(text, length);
|
|
82
|
+
std::reverse(reversed.begin(), reversed.end());
|
|
83
|
+
return xt_ext_string_from(reversed);
|
|
84
|
+
}
|
|
85
|
+
`, "mathx");
|
|
86
|
+
const manifestPath = join(workdir, "cpp.manifest.json");
|
|
87
|
+
writeFileSync(manifestPath, JSON.stringify({
|
|
88
|
+
name: "mathx-cpp",
|
|
89
|
+
objects: ["libmathx.a"],
|
|
90
|
+
linkerFlagsByPlatform: {
|
|
91
|
+
linux: ["-lstdc++"],
|
|
92
|
+
darwin: ["-lc++"],
|
|
93
|
+
},
|
|
94
|
+
builtins: { cppClamp: { symbol: "mathx_add" } },
|
|
95
|
+
modules: {
|
|
96
|
+
mathx: {
|
|
97
|
+
exports: {
|
|
98
|
+
add: { symbol: "mathx_add" },
|
|
99
|
+
reverse: { symbol: "mathx_reverse" },
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
}));
|
|
104
|
+
const entry = join(workdir, "demo.ts");
|
|
105
|
+
writeFileSync(entry, `
|
|
106
|
+
import { add, reverse } from "mathx";
|
|
107
|
+
console.log("add", add(2, 3));
|
|
108
|
+
console.log("reverse", reverse("xbintsc"));
|
|
109
|
+
console.log("builtin", cppClamp(7, 1));
|
|
110
|
+
`);
|
|
111
|
+
const extensions = createDefaultRegistry().register(nativeExtensionFromManifest(manifestPath));
|
|
112
|
+
const result = build(entry, { emit: "exe", outDir: join(workdir, "out"), cacheDir: join(workdir, ".cache"), extensions });
|
|
113
|
+
expect(result.diagnostics.filter((d) => d.category === "error")).toEqual([]);
|
|
114
|
+
const executed = spawnSync(result.outputPath, [], { encoding: "utf8" });
|
|
115
|
+
expect(executed.status, executed.stderr).toBe(0);
|
|
116
|
+
expect(executed.stdout.trim()).toBe(["add 5", "reverse cstnibx", "builtin 8"].join("\n"));
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
// Skipped on Windows: this suite uses PATH clang (MSVC ABI); see the file header.
|
|
120
|
+
describe.skipIf(!cargo || !cxx || !ar || process.platform === "win32")("native Rust extension", () => {
|
|
121
|
+
let workdir;
|
|
122
|
+
beforeAll(() => {
|
|
123
|
+
workdir = mkdtempSync(join(tmpdir(), "xbintsc-rust-"));
|
|
124
|
+
writeFileSync(join(workdir, "Cargo.toml"), `[package]
|
|
125
|
+
name = "mathx"
|
|
126
|
+
version = "0.1.0"
|
|
127
|
+
edition = "2021"
|
|
128
|
+
|
|
129
|
+
[lib]
|
|
130
|
+
name = "mathx"
|
|
131
|
+
crate-type = ["staticlib"]
|
|
132
|
+
|
|
133
|
+
[profile.release]
|
|
134
|
+
opt-level = 3
|
|
135
|
+
`);
|
|
136
|
+
const sourceDir = join(workdir, "src");
|
|
137
|
+
mkdirSync(sourceDir, { recursive: true });
|
|
138
|
+
writeFileSync(join(sourceDir, "lib.rs"), `
|
|
139
|
+
include!("${resolve(findRuntimeDir(), "xt_ext.rs").replace(/\\/g, "/")}");
|
|
140
|
+
|
|
141
|
+
#[no_mangle]
|
|
142
|
+
pub extern "C" fn mathx_add(argc: i32, argv: *const XtValue) -> XtValue {
|
|
143
|
+
xt_number(arg_number(argc, argv, 0, 0.0) + arg_number(argc, argv, 1, 0.0))
|
|
144
|
+
}
|
|
145
|
+
`);
|
|
146
|
+
});
|
|
147
|
+
afterAll(() => {
|
|
148
|
+
rmSync(workdir, { recursive: true, force: true });
|
|
149
|
+
});
|
|
150
|
+
it("links a Rust staticlib and calls into it from TypeScript", () => {
|
|
151
|
+
const built = spawnSync(cargo, ["build", "--release"], { cwd: workdir, encoding: "utf8" });
|
|
152
|
+
expect(built.status, built.stderr).toBe(0);
|
|
153
|
+
const archive = join(workdir, "target", "release", "libmathx.a");
|
|
154
|
+
expect(readFileSync(archive).length).toBeGreaterThan(0);
|
|
155
|
+
const manifestPath = join(workdir, "rust.manifest.json");
|
|
156
|
+
writeFileSync(manifestPath, JSON.stringify({
|
|
157
|
+
name: "mathx-rust",
|
|
158
|
+
objects: ["target/release/libmathx.a"],
|
|
159
|
+
linkerFlagsByPlatform: {
|
|
160
|
+
linux: ["-lpthread", "-ldl", "-lm"],
|
|
161
|
+
darwin: ["-liconv", "-framework", "Security"],
|
|
162
|
+
},
|
|
163
|
+
modules: { mathx: { exports: { add: { symbol: "mathx_add" } } } },
|
|
164
|
+
}));
|
|
165
|
+
const entry = join(workdir, "demo.ts");
|
|
166
|
+
writeFileSync(entry, `import { add } from "mathx";\nconsole.log("add", add(20, 22));\n`);
|
|
167
|
+
const extensions = createDefaultRegistry().register(nativeExtensionFromManifest(manifestPath));
|
|
168
|
+
const result = build(entry, { emit: "exe", outDir: join(workdir, "out"), cacheDir: join(workdir, ".cache"), extensions });
|
|
169
|
+
expect(result.diagnostics.filter((d) => d.category === "error")).toEqual([]);
|
|
170
|
+
const executed = spawnSync(result.outputPath, [], { encoding: "utf8" });
|
|
171
|
+
expect(executed.status, executed.stderr).toBe(0);
|
|
172
|
+
expect(executed.stdout.trim()).toBe("add 42");
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
//# sourceMappingURL=native-extensions.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-extensions.test.js","sourceRoot":"","sources":["../../../tests/e2e/native-extensions.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
5
|
+
import { NativeExtensionError, loadNativeManifest, nativeExtensionFromManifest, parseNativeManifest, } from "../../src/extensions/native.js";
|
|
6
|
+
import { ExtensionRegistry, createDefaultRegistry } from "../../src/extensions/registry.js";
|
|
7
|
+
describe("parseNativeManifest", () => {
|
|
8
|
+
it("normalises a full manifest", () => {
|
|
9
|
+
const manifest = parseNativeManifest({
|
|
10
|
+
name: "mathx",
|
|
11
|
+
description: "C++ helpers",
|
|
12
|
+
objects: ["build/libmathx.a"],
|
|
13
|
+
linkerFlags: ["-lm"],
|
|
14
|
+
linkerFlagsByPlatform: { linux: ["-lstdc++"], darwin: ["-lc++"] },
|
|
15
|
+
builtins: { fastAdd: { symbol: "mathx_add" } },
|
|
16
|
+
modules: {
|
|
17
|
+
mathx: {
|
|
18
|
+
exports: {
|
|
19
|
+
add: { symbol: "mathx_add" },
|
|
20
|
+
join: { namespace: "path", method: "join" },
|
|
21
|
+
Thing: { symbol: "xt_thing_ctor", isConstructor: true },
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
expect(manifest.name).toBe("mathx");
|
|
27
|
+
expect(manifest.objects).toEqual(["build/libmathx.a"]);
|
|
28
|
+
expect(manifest.linkerFlagsByPlatform?.darwin).toEqual(["-lc++"]);
|
|
29
|
+
expect(manifest.builtins?.fastAdd?.symbol).toBe("mathx_add");
|
|
30
|
+
expect(manifest.modules?.mathx?.exports?.join).toEqual({ namespace: "path", method: "join" });
|
|
31
|
+
expect(manifest.modules?.mathx?.exports?.Thing?.isConstructor).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
it("defaults omitted collections to empty", () => {
|
|
34
|
+
const manifest = parseNativeManifest({ name: "bare" });
|
|
35
|
+
expect(manifest.objects).toEqual([]);
|
|
36
|
+
expect(manifest.linkerFlags).toEqual([]);
|
|
37
|
+
expect(manifest.builtins).toBeUndefined();
|
|
38
|
+
expect(manifest.modules).toBeUndefined();
|
|
39
|
+
});
|
|
40
|
+
it("rejects a missing or empty name", () => {
|
|
41
|
+
expect(() => parseNativeManifest({})).toThrow(NativeExtensionError);
|
|
42
|
+
expect(() => parseNativeManifest({ name: " " })).toThrow(/non-empty 'name'/);
|
|
43
|
+
});
|
|
44
|
+
it("rejects a builtin without a symbol", () => {
|
|
45
|
+
expect(() => parseNativeManifest({ name: "x", builtins: { f: {} } })).toThrow(/'symbol'/);
|
|
46
|
+
});
|
|
47
|
+
it("rejects an export with neither symbol nor namespace", () => {
|
|
48
|
+
expect(() => parseNativeManifest({ name: "x", modules: { m: { exports: { f: {} } } } })).toThrow(/needs a 'symbol' or a 'namespace'/);
|
|
49
|
+
});
|
|
50
|
+
it("rejects non-string linker flags", () => {
|
|
51
|
+
expect(() => parseNativeManifest({ name: "x", linkerFlags: [42] })).toThrow(/array of strings/);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe("nativeExtensionFromManifest", () => {
|
|
55
|
+
let workdir;
|
|
56
|
+
beforeAll(() => {
|
|
57
|
+
workdir = mkdtempSync(join(tmpdir(), "xbintsc-native-"));
|
|
58
|
+
});
|
|
59
|
+
afterAll(() => {
|
|
60
|
+
rmSync(workdir, { recursive: true, force: true });
|
|
61
|
+
});
|
|
62
|
+
function writeManifest(name, contents) {
|
|
63
|
+
const path = join(workdir, name);
|
|
64
|
+
writeFileSync(path, JSON.stringify(contents));
|
|
65
|
+
return path;
|
|
66
|
+
}
|
|
67
|
+
it("resolves objects relative to the manifest and verifies they exist", () => {
|
|
68
|
+
writeFileSync(join(workdir, "libmathx.a"), "fake archive");
|
|
69
|
+
const manifestPath = writeManifest("mathx.manifest.json", {
|
|
70
|
+
name: "mathx",
|
|
71
|
+
objects: ["libmathx.a"],
|
|
72
|
+
linkerFlagsByPlatform: { [process.platform]: ["-lc++"] },
|
|
73
|
+
builtins: { fastAdd: { symbol: "mathx_add" } },
|
|
74
|
+
});
|
|
75
|
+
const extension = nativeExtensionFromManifest(manifestPath);
|
|
76
|
+
expect(extension.name).toBe("mathx");
|
|
77
|
+
expect(extension.nativeObjects()).toEqual([resolve(workdir, "libmathx.a")]);
|
|
78
|
+
expect(extension.linkerFlags()).toEqual(["-lc++"]);
|
|
79
|
+
expect(extension.builtins().fastAdd?.symbol).toBe("mathx_add");
|
|
80
|
+
});
|
|
81
|
+
it("throws a helpful error for a missing object", () => {
|
|
82
|
+
const manifestPath = writeManifest("missing.manifest.json", {
|
|
83
|
+
name: "missing",
|
|
84
|
+
objects: ["libnope.a"],
|
|
85
|
+
});
|
|
86
|
+
expect(() => nativeExtensionFromManifest(manifestPath)).toThrow(/references missing object/);
|
|
87
|
+
});
|
|
88
|
+
it("reports invalid JSON", () => {
|
|
89
|
+
const path = join(workdir, "broken.json");
|
|
90
|
+
writeFileSync(path, "{ not json");
|
|
91
|
+
expect(() => loadNativeManifest(path)).toThrow(/not valid JSON/);
|
|
92
|
+
});
|
|
93
|
+
it("flattens native objects through the registry", () => {
|
|
94
|
+
writeFileSync(join(workdir, "libdemo.a"), "fake");
|
|
95
|
+
const manifestPath = writeManifest("demo.manifest.json", { name: "demo", objects: ["libdemo.a"] });
|
|
96
|
+
const registry = createDefaultRegistry().register(nativeExtensionFromManifest(manifestPath));
|
|
97
|
+
expect(registry.nativeObjects()).toEqual([resolve(workdir, "libdemo.a")]);
|
|
98
|
+
expect(new ExtensionRegistry().nativeObjects()).toEqual([]);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
//# sourceMappingURL=native.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native.test.js","sourceRoot":"","sources":["../../../tests/extensions/native.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,15 @@ describe("numeric literal edge cases", () => {
|
|
|
14
14
|
expect(diagnostics.some((d) => d.code === DiagnosticCode.InvalidNumber)).toBe(true);
|
|
15
15
|
expect(tokens[0].kind).toBe(TokenKind.BigIntLiteral);
|
|
16
16
|
});
|
|
17
|
+
it("preserves arbitrary-precision BigInt values exactly", () => {
|
|
18
|
+
const { tokens, diagnostics } = lex("123456789012345678901234567890n");
|
|
19
|
+
expect(diagnostics).toHaveLength(0);
|
|
20
|
+
expect(tokens[0]).toMatchObject({
|
|
21
|
+
kind: TokenKind.BigIntLiteral,
|
|
22
|
+
text: "123456789012345678901234567890",
|
|
23
|
+
value: 123456789012345678901234567890n,
|
|
24
|
+
});
|
|
25
|
+
});
|
|
17
26
|
it("reports an invalid numeric literal and recovers with value 0", () => {
|
|
18
27
|
const { tokens, diagnostics } = lex("0b2");
|
|
19
28
|
expect(diagnostics.some((d) => d.code === DiagnosticCode.InvalidNumber)).toBe(true);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"numbers.test.js","sourceRoot":"","sources":["../../../tests/lexer/numbers.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAErE,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,WAAW,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,cAAc,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACzF,MAAM,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;QAC5C,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpF,MAAM,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpF,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,cAAc,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"numbers.test.js","sourceRoot":"","sources":["../../../tests/lexer/numbers.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAErE,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,WAAW,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,cAAc,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACzF,MAAM,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;QAC5C,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpF,MAAM,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,iCAAiC,CAAC,CAAC;QACvE,MAAM,CAAC,WAAW,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;YAC9B,IAAI,EAAE,SAAS,CAAC,aAAa;YAC7B,IAAI,EAAE,gCAAgC;YACtC,KAAK,EAAE,+BAA+B;SACvC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpF,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,cAAc,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -41,6 +41,13 @@ describe("literal expressions", () => {
|
|
|
41
41
|
expect(named.name.text).toBe("Named");
|
|
42
42
|
expect(named.typeParameters).toHaveLength(1);
|
|
43
43
|
});
|
|
44
|
+
it("keeps BigInt literal values exact as bigint", () => {
|
|
45
|
+
const { init, diagnostics } = initializerOf("const a = 123456789012345678901234567890n;");
|
|
46
|
+
expect(diagnostics).toHaveLength(0);
|
|
47
|
+
expect(init.kind).toBe(SyntaxKind.BigIntLiteral);
|
|
48
|
+
expect(typeof init.value).toBe("bigint");
|
|
49
|
+
expect(init.value).toBe(123456789012345678901234567890n);
|
|
50
|
+
});
|
|
44
51
|
it("parses regular expression literals with flags", () => {
|
|
45
52
|
const { init } = initializerOf("const r = /ab+c/gi;");
|
|
46
53
|
expect(init.kind).toBe(SyntaxKind.RegularExpressionLiteral);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"literals.test.js","sourceRoot":"","sources":["../../../tests/parser/literals.test.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAErE,SAAS,aAAa,CAAC,MAAc;IACnC,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,WAAW,GAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAS,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAChF,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,WAAW,EAAE,WAAW,EAAE,CAAC;AACxD,CAAC;AAED,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,aAAa,CAAC,qBAAqB,CAAC,CAAC;QACnE,MAAM,CAAC,WAAW,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC;QAC1D,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,aAAa,CAAC,4CAA4C,CAAC,CAAC;QAC1F,MAAM,CAAC,WAAW,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;QAC1C,MAAM,CAAC,EAAE,CAAC,KAAK,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACjE,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC,oBAAoB,CAAC,CAAC;QACpD,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,sCAAsC,CAAC,CAAC;QACvE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC;QACtD,MAAM,CAAC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,aAAa,CAAC,qBAAqB,CAAC,CAAC;QACjE,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;QACxD,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;QAEvC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,aAAa,CAAC,6CAA6C,CAAC,CAAC;QAClG,MAAM,CAAC,WAAW,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,qBAAqB,CAAC,CAAC;QACtD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,wBAAwB,CAAC,CAAC;QAC5D,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACjD,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,aAAa,CAAC,yBAAyB,CAAC,CAAC;QACvE,MAAM,CAAC,WAAW,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;QACnD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACjD,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACnD,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7F,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"literals.test.js","sourceRoot":"","sources":["../../../tests/parser/literals.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/runtime/xt_ext.h
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc native-extension helper header.
|
|
3
|
+
*
|
|
4
|
+
* Include this from a C or C++ extension that xbintsc links into a generated
|
|
5
|
+
* binary. It pulls in the runtime ABI (`rt.h`) and adds small helpers for the
|
|
6
|
+
* common job of reading the `(argc, argv)` arguments and building result
|
|
7
|
+
* values. In C++ everything is wrapped in `extern "C"` by `rt.h`, so a
|
|
8
|
+
* definition like
|
|
9
|
+
*
|
|
10
|
+
* XT_EXT_FN(mathx_add) {
|
|
11
|
+
* double a = xt_ext_number(argc, argv, 0, 0);
|
|
12
|
+
* double b = xt_ext_number(argc, argv, 1, 0);
|
|
13
|
+
* return xt_number(a + b);
|
|
14
|
+
* }
|
|
15
|
+
*
|
|
16
|
+
* exports a symbol callable from TypeScript (via a native manifest) without any
|
|
17
|
+
* name mangling.
|
|
18
|
+
*
|
|
19
|
+
* Build the extension with the same clang that xbintsc uses, keeping the
|
|
20
|
+
* runtime directory on the include path:
|
|
21
|
+
*
|
|
22
|
+
* clang++ -O2 -fPIC -I<runtime> -c mathx.cpp -o mathx.o
|
|
23
|
+
* ar rcs libmathx.a mathx.o
|
|
24
|
+
*/
|
|
25
|
+
#ifndef XT_EXT_H
|
|
26
|
+
#define XT_EXT_H
|
|
27
|
+
|
|
28
|
+
#include <stddef.h>
|
|
29
|
+
#include <stdint.h>
|
|
30
|
+
|
|
31
|
+
#include "rt.h"
|
|
32
|
+
|
|
33
|
+
#ifdef __cplusplus
|
|
34
|
+
#define XT_EXT_FN(name) extern "C" xt_value name(int32_t argc, xt_value *argv)
|
|
35
|
+
#else
|
|
36
|
+
#define XT_EXT_FN(name) xt_value name(int32_t argc, xt_value *argv)
|
|
37
|
+
#endif
|
|
38
|
+
|
|
39
|
+
#ifdef __cplusplus
|
|
40
|
+
extern "C" {
|
|
41
|
+
#endif
|
|
42
|
+
|
|
43
|
+
/*
|
|
44
|
+
* XT_EXT_FN(name) declares/defines an exported entry point with the signature
|
|
45
|
+
* the code generator uses for builtins and module exports:
|
|
46
|
+
*
|
|
47
|
+
* xt_value name(int32_t argc, xt_value *argv);
|
|
48
|
+
*
|
|
49
|
+
* In C++ it also gives the definition C linkage, so the symbol is never
|
|
50
|
+
* name-mangled and the manifest can reference it directly.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/** The `index`-th argument, or `undefined` when it was not supplied. */
|
|
54
|
+
static inline xt_value xt_ext_arg(int32_t argc, xt_value *argv, int32_t index) {
|
|
55
|
+
if (argv == NULL || index < 0 || index >= argc) return XT_UNDEFINED;
|
|
56
|
+
return argv[index];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** The `index`-th argument coerced to a number, or `fallback` when absent. */
|
|
60
|
+
static inline double xt_ext_number(int32_t argc, xt_value *argv, int32_t index, double fallback) {
|
|
61
|
+
xt_value value = xt_ext_arg(argc, argv, index);
|
|
62
|
+
if (XT_IS_UNDEFINED(value)) return fallback;
|
|
63
|
+
return xt_to_number(value);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The `index`-th argument coerced to a 32-bit integer. */
|
|
67
|
+
static inline int32_t xt_ext_int(int32_t argc, xt_value *argv, int32_t index, int32_t fallback) {
|
|
68
|
+
return (int32_t)xt_ext_number(argc, argv, index, (double)fallback);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The `index`-th argument coerced to a boolean. */
|
|
72
|
+
static inline int xt_ext_bool(int32_t argc, xt_value *argv, int32_t index, int fallback) {
|
|
73
|
+
xt_value value = xt_ext_arg(argc, argv, index);
|
|
74
|
+
if (XT_IS_UNDEFINED(value)) return fallback;
|
|
75
|
+
return xt_truthy(value);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Borrow the UTF-8 bytes of the `index`-th argument when it is a string.
|
|
80
|
+
* Returns `NULL` and stores 0 in `*length` otherwise. The pointer stays valid
|
|
81
|
+
* for the lifetime of the value (the runtime arena never frees).
|
|
82
|
+
*/
|
|
83
|
+
static inline const char *xt_ext_string(int32_t argc, xt_value *argv, int32_t index, size_t *length) {
|
|
84
|
+
xt_value value = xt_ext_arg(argc, argv, index);
|
|
85
|
+
if (!XT_IS_STRING(value)) {
|
|
86
|
+
if (length != NULL) *length = 0;
|
|
87
|
+
return NULL;
|
|
88
|
+
}
|
|
89
|
+
if (length != NULL) *length = (size_t)xt_string_length_value(value);
|
|
90
|
+
return xt_string_data(value);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Build a string value from bytes. */
|
|
94
|
+
static inline xt_value xt_ext_string_value(const char *data, size_t length) {
|
|
95
|
+
return xt_string_new(data, length);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
#ifdef __cplusplus
|
|
99
|
+
} /* extern "C" */
|
|
100
|
+
|
|
101
|
+
#include <string>
|
|
102
|
+
|
|
103
|
+
/** Convenience overload for `std::string` / string literals in C++. */
|
|
104
|
+
static inline xt_value xt_ext_string_from(const std::string &text) {
|
|
105
|
+
return xt_string_new(text.data(), text.size());
|
|
106
|
+
}
|
|
107
|
+
#endif /* __cplusplus */
|
|
108
|
+
|
|
109
|
+
#endif /* XT_EXT_H */
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// xbintsc native-extension bindings for Rust.
|
|
2
|
+
//
|
|
3
|
+
// Include this file from a Rust `staticlib`/`cdylib` that xbintsc links into a
|
|
4
|
+
// generated binary:
|
|
5
|
+
//
|
|
6
|
+
// // src/lib.rs
|
|
7
|
+
// include!("../../runtime/xt_ext.rs");
|
|
8
|
+
//
|
|
9
|
+
// #[no_mangle]
|
|
10
|
+
// pub extern "C" fn mathx_add(argc: i32, argv: *const XtValue) -> XtValue {
|
|
11
|
+
// let a = arg_number(argc, argv, 0, 0.0);
|
|
12
|
+
// let b = arg_number(argc, argv, 1, 0.0);
|
|
13
|
+
// xt_number(a + b)
|
|
14
|
+
// }
|
|
15
|
+
//
|
|
16
|
+
// The exported functions use the runtime ABI, so a native manifest maps them
|
|
17
|
+
// exactly like C or C++ symbols: `xt_value name(int32_t argc, xt_value *argv)`.
|
|
18
|
+
//
|
|
19
|
+
// Build with the runtime directory reachable and produce a static library:
|
|
20
|
+
//
|
|
21
|
+
// cargo build --release # crate-type = ["staticlib"]
|
|
22
|
+
// # -> target/release/libmathx.a
|
|
23
|
+
|
|
24
|
+
/// A JavaScript value: a 64-bit NaN-boxed word (see `runtime/rt.h`).
|
|
25
|
+
pub type XtValue = u64;
|
|
26
|
+
|
|
27
|
+
pub const XT_TAG_MASK: XtValue = 0xFFFF_0000_0000_0000;
|
|
28
|
+
pub const XT_NUMBER_MASK: XtValue = 0xFFF8_0000_0000_0000;
|
|
29
|
+
|
|
30
|
+
pub const XT_UNDEFINED: XtValue = 0xFFF8_0000_0000_0000;
|
|
31
|
+
pub const XT_NULL: XtValue = 0xFFF9_0000_0000_0000;
|
|
32
|
+
pub const XT_FALSE: XtValue = 0xFFFA_0000_0000_0000;
|
|
33
|
+
pub const XT_TAG_STRING: XtValue = 0xFFFC_0000_0000_0000;
|
|
34
|
+
|
|
35
|
+
// Raw runtime entry points. They are wrapped in safe helpers below so that
|
|
36
|
+
// extension authors never have to spell `unsafe` just to build a value.
|
|
37
|
+
extern "C" {
|
|
38
|
+
#[link_name = "xt_number"]
|
|
39
|
+
fn raw_xt_number(d: f64) -> XtValue;
|
|
40
|
+
#[link_name = "xt_string_new"]
|
|
41
|
+
fn raw_xt_string_new(data: *const u8, len: usize) -> XtValue;
|
|
42
|
+
#[link_name = "xt_string_data"]
|
|
43
|
+
fn raw_xt_string_data(value: XtValue) -> *const u8;
|
|
44
|
+
#[link_name = "xt_string_length_value"]
|
|
45
|
+
fn raw_xt_string_length_value(value: XtValue) -> i32;
|
|
46
|
+
#[link_name = "xt_truthy"]
|
|
47
|
+
fn raw_xt_truthy(value: XtValue) -> i32;
|
|
48
|
+
#[link_name = "xt_to_number"]
|
|
49
|
+
fn raw_xt_to_number(value: XtValue) -> f64;
|
|
50
|
+
#[link_name = "xt_to_string"]
|
|
51
|
+
fn raw_xt_to_string(value: XtValue) -> XtValue;
|
|
52
|
+
#[link_name = "xt_array_new"]
|
|
53
|
+
fn raw_xt_array_new(count: i32, items: *mut XtValue) -> XtValue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/// Box an `f64` into a JavaScript number value.
|
|
57
|
+
#[inline]
|
|
58
|
+
pub fn xt_number(d: f64) -> XtValue {
|
|
59
|
+
unsafe { raw_xt_number(d) }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// Build a string value from a byte slice (assumed UTF-8).
|
|
63
|
+
#[inline]
|
|
64
|
+
pub fn xt_string_new(data: *const u8, len: usize) -> XtValue {
|
|
65
|
+
unsafe { raw_xt_string_new(data, len) }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/// Pointer to the first byte of a string value's payload.
|
|
69
|
+
#[inline]
|
|
70
|
+
pub fn xt_string_data(value: XtValue) -> *const u8 {
|
|
71
|
+
unsafe { raw_xt_string_data(value) }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// Byte length of a string value.
|
|
75
|
+
#[inline]
|
|
76
|
+
pub fn xt_string_length_value(value: XtValue) -> i32 {
|
|
77
|
+
unsafe { raw_xt_string_length_value(value) }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/// JavaScript truthiness of a value (`0`/`1`).
|
|
81
|
+
#[inline]
|
|
82
|
+
pub fn xt_truthy(value: XtValue) -> i32 {
|
|
83
|
+
unsafe { raw_xt_truthy(value) }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// Convert a value to a number, coercing non-numbers like the runtime does.
|
|
87
|
+
#[inline]
|
|
88
|
+
pub fn xt_to_number(value: XtValue) -> f64 {
|
|
89
|
+
unsafe { raw_xt_to_number(value) }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// Convert a value to a string value.
|
|
93
|
+
#[inline]
|
|
94
|
+
pub fn xt_to_string(value: XtValue) -> XtValue {
|
|
95
|
+
unsafe { raw_xt_to_string(value) }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/// Build an array from `count` items.
|
|
99
|
+
#[inline]
|
|
100
|
+
pub fn xt_array_new(count: i32, items: *mut XtValue) -> XtValue {
|
|
101
|
+
unsafe { raw_xt_array_new(count, items) }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// Whether a value carries the number (unboxed double) representation.
|
|
105
|
+
#[inline]
|
|
106
|
+
pub fn is_number(value: XtValue) -> bool {
|
|
107
|
+
(value & XT_NUMBER_MASK) != XT_NUMBER_MASK
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/// Whether a value is a string.
|
|
111
|
+
#[inline]
|
|
112
|
+
pub fn is_string(value: XtValue) -> bool {
|
|
113
|
+
(value & XT_TAG_MASK) == XT_TAG_STRING
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/// Reinterpret an unboxed number value as `f64`.
|
|
117
|
+
#[inline]
|
|
118
|
+
pub fn as_f64(value: XtValue) -> f64 {
|
|
119
|
+
f64::from_bits(value)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/// Read argument `index`, returning `undefined` when it was not supplied.
|
|
123
|
+
///
|
|
124
|
+
/// `argv` may be null when a function is called with no arguments.
|
|
125
|
+
#[inline]
|
|
126
|
+
pub fn arg(argc: i32, argv: *const XtValue, index: usize) -> XtValue {
|
|
127
|
+
if argv.is_null() || index >= argc as usize {
|
|
128
|
+
return XT_UNDEFINED;
|
|
129
|
+
}
|
|
130
|
+
unsafe { *argv.add(index) }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/// Read argument `index` coerced to a number, or `fallback` when absent.
|
|
134
|
+
#[inline]
|
|
135
|
+
pub fn arg_number(argc: i32, argv: *const XtValue, index: usize, fallback: f64) -> f64 {
|
|
136
|
+
let value = arg(argc, argv, index);
|
|
137
|
+
if value == XT_UNDEFINED {
|
|
138
|
+
return fallback;
|
|
139
|
+
}
|
|
140
|
+
xt_to_number(value)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/// Read argument `index` coerced to a boolean.
|
|
144
|
+
#[inline]
|
|
145
|
+
pub fn arg_bool(argc: i32, argv: *const XtValue, index: usize, fallback: bool) -> bool {
|
|
146
|
+
let value = arg(argc, argv, index);
|
|
147
|
+
if value == XT_UNDEFINED {
|
|
148
|
+
return fallback;
|
|
149
|
+
}
|
|
150
|
+
xt_truthy(value) != 0
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// Borrow argument `index` as a UTF-8 string slice, or `None` when it is not a
|
|
154
|
+
/// string. The bytes live in the runtime arena and never move.
|
|
155
|
+
#[inline]
|
|
156
|
+
pub fn arg_str<'a>(argc: i32, argv: *const XtValue, index: usize) -> Option<&'a str> {
|
|
157
|
+
let value = arg(argc, argv, index);
|
|
158
|
+
if !is_string(value) {
|
|
159
|
+
return None;
|
|
160
|
+
}
|
|
161
|
+
let length = xt_string_length_value(value) as usize;
|
|
162
|
+
let data = xt_string_data(value);
|
|
163
|
+
if data.is_null() || length == 0 {
|
|
164
|
+
return Some("");
|
|
165
|
+
}
|
|
166
|
+
unsafe { Some(core::str::from_utf8_unchecked(core::slice::from_raw_parts(data, length))) }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/// Build a string value from a Rust string slice.
|
|
170
|
+
#[inline]
|
|
171
|
+
pub fn string_from(text: &str) -> XtValue {
|
|
172
|
+
xt_string_new(text.as_ptr(), text.len())
|
|
173
|
+
}
|