relaxnative 0.1.0-beta.1
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/LICENSE +21 -0
- package/README.md +592 -0
- package/dist/chunk-22W76CYR.js +607 -0
- package/dist/chunk-24NXCU65.js +254 -0
- package/dist/chunk-2APMRURB.js +65 -0
- package/dist/chunk-2CHBHJPT.js +607 -0
- package/dist/chunk-2I4JHZI7.js +287 -0
- package/dist/chunk-2JOHYYQO.js +607 -0
- package/dist/chunk-3GW77EWF.js +505 -0
- package/dist/chunk-5J5CAKCD.js +266 -0
- package/dist/chunk-5NTDZ7YZ.js +377 -0
- package/dist/chunk-5TA6MROS.js +529 -0
- package/dist/chunk-5WVEBKMJ.js +1019 -0
- package/dist/chunk-6O5TIEEI.js +545 -0
- package/dist/chunk-6XU5DETO.js +896 -0
- package/dist/chunk-7BIZ6P3B.js +176 -0
- package/dist/chunk-7DKO777J.js +285 -0
- package/dist/chunk-7JYWUH4Y.js +268 -0
- package/dist/chunk-7NMCEP2V.js +756 -0
- package/dist/chunk-A7N4YBP2.js +379 -0
- package/dist/chunk-AZTCDV6R.js +572 -0
- package/dist/chunk-B34XEGM6.js +559 -0
- package/dist/chunk-BFHBLVXW.js +607 -0
- package/dist/chunk-BLOJ33LO.js +65 -0
- package/dist/chunk-BYPXCWTI.js +375 -0
- package/dist/chunk-C4KJD2AN.js +1044 -0
- package/dist/chunk-CJALJTRQ.js +814 -0
- package/dist/chunk-D4PK367Z.js +627 -0
- package/dist/chunk-DCWBZPEV.js +287 -0
- package/dist/chunk-DI7KSUEC.js +676 -0
- package/dist/chunk-DQ2KXIOO.js +665 -0
- package/dist/chunk-DV5STE3W.js +986 -0
- package/dist/chunk-EG5KNEKP.js +1027 -0
- package/dist/chunk-EOA2OWFA.js +1020 -0
- package/dist/chunk-ES3B6EZJ.js +56 -0
- package/dist/chunk-ETIXNPU5.js +741 -0
- package/dist/chunk-EUZBU2H7.js +824 -0
- package/dist/chunk-F6V7XDEB.js +150 -0
- package/dist/chunk-FNJKUFNF.js +1019 -0
- package/dist/chunk-FZB37DWL.js +453 -0
- package/dist/chunk-G3NDHZNZ.js +453 -0
- package/dist/chunk-G4YR34LE.js +410 -0
- package/dist/chunk-GU4XXISM.js +264 -0
- package/dist/chunk-GVPSQXGJ.js +1027 -0
- package/dist/chunk-HD5C4RNU.js +676 -0
- package/dist/chunk-HDIVY47T.js +287 -0
- package/dist/chunk-HFLRTDNK.js +985 -0
- package/dist/chunk-HGWRCVQ5.js +287 -0
- package/dist/chunk-HRG3SVKK.js +995 -0
- package/dist/chunk-HUGFULJ3.js +1027 -0
- package/dist/chunk-IDYSBXYS.js +344 -0
- package/dist/chunk-ISDDUQVI.js +1019 -0
- package/dist/chunk-IZ632ZCJ.js +286 -0
- package/dist/chunk-J5XI4L52.js +218 -0
- package/dist/chunk-JTIO7BUH.js +582 -0
- package/dist/chunk-JTWSFMF2.js +1020 -0
- package/dist/chunk-K5TV62T4.js +736 -0
- package/dist/chunk-K7MTG53V.js +985 -0
- package/dist/chunk-KGLZB3H2.js +676 -0
- package/dist/chunk-KYAB35P5.js +741 -0
- package/dist/chunk-KYDW3YVX.js +453 -0
- package/dist/chunk-L3MEMPRH.js +361 -0
- package/dist/chunk-LFTO3Z7N.js +757 -0
- package/dist/chunk-LLZ4I4OR.js +405 -0
- package/dist/chunk-LMRUM4U4.js +207 -0
- package/dist/chunk-LT5OGU6T.js +559 -0
- package/dist/chunk-LZQQOC3M.js +741 -0
- package/dist/chunk-LZYUNF6Q.js +1017 -0
- package/dist/chunk-MCTPVW4G.js +453 -0
- package/dist/chunk-MGLWXBIB.js +65 -0
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/chunk-MTE6XDGC.js +541 -0
- package/dist/chunk-NDJUNDAE.js +676 -0
- package/dist/chunk-NG7SNFUD.js +1027 -0
- package/dist/chunk-ONVWKYK7.js +739 -0
- package/dist/chunk-OVMTFGE7.js +1042 -0
- package/dist/chunk-P5RQPRJ4.js +741 -0
- package/dist/chunk-PFABSW6Y.js +401 -0
- package/dist/chunk-PVVUJA2M.js +65 -0
- package/dist/chunk-Q3CDTGTX.js +676 -0
- package/dist/chunk-QKAKWDOQ.js +967 -0
- package/dist/chunk-QMPRDU6I.js +598 -0
- package/dist/chunk-R5U7XKVJ.js +16 -0
- package/dist/chunk-RB6RHB6C.js +254 -0
- package/dist/chunk-RD2K7ODW.js +175 -0
- package/dist/chunk-RHBHJND2.js +582 -0
- package/dist/chunk-RUP6POSE.js +453 -0
- package/dist/chunk-RYNSM23L.js +582 -0
- package/dist/chunk-S72S7XXS.js +255 -0
- package/dist/chunk-SDV5AAPW.js +213 -0
- package/dist/chunk-STXQPXUY.js +242 -0
- package/dist/chunk-TUSF5AEG.js +140 -0
- package/dist/chunk-V74SNTE6.js +736 -0
- package/dist/chunk-VHM5XETC.js +453 -0
- package/dist/chunk-VKQIXLNL.js +273 -0
- package/dist/chunk-VPG23Z7Y.js +545 -0
- package/dist/chunk-VSP234PR.js +627 -0
- package/dist/chunk-WH4JPUWF.js +598 -0
- package/dist/chunk-WLAUJL3K.js +409 -0
- package/dist/chunk-WXCN2QJ7.js +350 -0
- package/dist/chunk-X3JZKLJC.js +896 -0
- package/dist/chunk-X7SAP7FC.js +582 -0
- package/dist/chunk-XEH6PRYE.js +968 -0
- package/dist/chunk-XI65CAQV.js +211 -0
- package/dist/chunk-Y7OSHR6W.js +235 -0
- package/dist/chunk-YN4WUMVD.js +1020 -0
- package/dist/chunk-YUWJ2C4Y.js +1020 -0
- package/dist/chunk-YXLBPWNU.js +263 -0
- package/dist/chunk-YYJJHO7R.js +407 -0
- package/dist/chunk-Z2RBHUIH.js +757 -0
- package/dist/chunk-Z6G3KIOM.js +1027 -0
- package/dist/chunk-ZPPXCDSH.js +361 -0
- package/dist/chunk-ZRTY24SZ.js +582 -0
- package/dist/chunk-ZSDFBCQG.js +741 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +339 -0
- package/dist/esmLoader.d.ts +10 -0
- package/dist/esmLoader.js +112 -0
- package/dist/index.d.ts +407 -0
- package/dist/index.js +126 -0
- package/dist/memory/memory.selftest.d.ts +2 -0
- package/dist/memory/memory.selftest.js +25 -0
- package/dist/worker/processEntry.d.ts +2 -0
- package/dist/worker/processEntry.js +160 -0
- package/dist/worker/workerEntry.d.ts +2 -0
- package/dist/worker/workerEntry.js +27 -0
- package/native/examples/add.c +6 -0
- package/native/examples/add_test.c +23 -0
- package/native/relaxnative_test.h +36 -0
- package/package.json +81 -0
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
import {
|
|
2
|
+
wrapFunctions
|
|
3
|
+
} from "./chunk-XI65CAQV.js";
|
|
4
|
+
import {
|
|
5
|
+
InvalidFreeError,
|
|
6
|
+
MemoryError,
|
|
7
|
+
NativeBuffer,
|
|
8
|
+
NativePointer,
|
|
9
|
+
NullPointerError,
|
|
10
|
+
UseAfterFreeError,
|
|
11
|
+
loadFfi
|
|
12
|
+
} from "./chunk-IDYSBXYS.js";
|
|
13
|
+
import {
|
|
14
|
+
__export
|
|
15
|
+
} from "./chunk-R5U7XKVJ.js";
|
|
16
|
+
|
|
17
|
+
// src/compiler/detectPlatform.ts
|
|
18
|
+
function detectPlatform() {
|
|
19
|
+
const platform = process.platform;
|
|
20
|
+
const arch = process.arch;
|
|
21
|
+
return {
|
|
22
|
+
platform,
|
|
23
|
+
arch,
|
|
24
|
+
isWindows: platform === "win32",
|
|
25
|
+
isMac: platform === "darwin",
|
|
26
|
+
isLinux: platform === "linux"
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/compiler/detectCCompiler.ts
|
|
31
|
+
import { execFileSync } from "child_process";
|
|
32
|
+
|
|
33
|
+
// src/utils/which.ts
|
|
34
|
+
import { accessSync, constants } from "fs";
|
|
35
|
+
import { delimiter } from "path";
|
|
36
|
+
function which(cmd) {
|
|
37
|
+
const paths = process.env.PATH?.split(delimiter) ?? [];
|
|
38
|
+
for (const p of paths) {
|
|
39
|
+
const full = `${p}/${cmd}`;
|
|
40
|
+
try {
|
|
41
|
+
accessSync(full, constants.X_OK);
|
|
42
|
+
return full;
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/compiler/detectCCompiler.ts
|
|
50
|
+
function getVersion(path) {
|
|
51
|
+
try {
|
|
52
|
+
return execFileSync(path, ["--version"], { encoding: "utf8" }).split("\n")[0].trim();
|
|
53
|
+
} catch {
|
|
54
|
+
return "unknown";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function detectCCompiler() {
|
|
58
|
+
const candidates = [
|
|
59
|
+
process.env.CC,
|
|
60
|
+
"clang",
|
|
61
|
+
"gcc",
|
|
62
|
+
"cc",
|
|
63
|
+
"cl"
|
|
64
|
+
].filter(Boolean);
|
|
65
|
+
for (const name of candidates) {
|
|
66
|
+
const resolved = name.includes("/") ? name : which(name);
|
|
67
|
+
if (!resolved) continue;
|
|
68
|
+
const version = getVersion(resolved);
|
|
69
|
+
return {
|
|
70
|
+
kind: "c",
|
|
71
|
+
path: resolved,
|
|
72
|
+
version,
|
|
73
|
+
vendor: resolved.includes("clang") ? "clang" : resolved.includes("gcc") ? "gcc" : resolved.includes("cl") ? "msvc" : "unknown"
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
throw new Error("No C compiler found (gcc/clang/cl)");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/compiler/detectRustCompiler.ts
|
|
80
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
81
|
+
function detectRustCompiler() {
|
|
82
|
+
const rustc = process.env.RUSTC ?? which("rustc");
|
|
83
|
+
if (!rustc) return null;
|
|
84
|
+
const version = execFileSync2(rustc, ["--version"], {
|
|
85
|
+
encoding: "utf8"
|
|
86
|
+
}).trim();
|
|
87
|
+
return {
|
|
88
|
+
kind: "rust",
|
|
89
|
+
path: rustc,
|
|
90
|
+
version,
|
|
91
|
+
vendor: "rust"
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/compiler/sanityCheck.ts
|
|
96
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
97
|
+
import { writeFileSync, mkdtempSync } from "fs";
|
|
98
|
+
import { tmpdir } from "os";
|
|
99
|
+
import { join } from "path";
|
|
100
|
+
function sanityCheckC(compiler) {
|
|
101
|
+
const dir = mkdtempSync(join(tmpdir(), "relaxnative-"));
|
|
102
|
+
const source = join(dir, "test.c");
|
|
103
|
+
const output = join(dir, "test");
|
|
104
|
+
writeFileSync(source, "int main(){return 0;}");
|
|
105
|
+
try {
|
|
106
|
+
execFileSync3(
|
|
107
|
+
compiler.path,
|
|
108
|
+
compiler.vendor === "msvc" ? [source] : [source, "-o", output],
|
|
109
|
+
{ stdio: "ignore" }
|
|
110
|
+
);
|
|
111
|
+
} catch (err) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`C compiler failed sanity check: ${compiler.path}`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/compiler/detect.ts
|
|
119
|
+
function detectCompilers() {
|
|
120
|
+
const platform = detectPlatform();
|
|
121
|
+
const c = detectCCompiler();
|
|
122
|
+
sanityCheckC(c);
|
|
123
|
+
const rust = detectRustCompiler();
|
|
124
|
+
return {
|
|
125
|
+
platform,
|
|
126
|
+
c,
|
|
127
|
+
rust
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/compiler/compileNative.ts
|
|
132
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
133
|
+
import { mkdirSync } from "fs";
|
|
134
|
+
|
|
135
|
+
// src/compiler/buildCommand.ts
|
|
136
|
+
import { basename } from "path";
|
|
137
|
+
|
|
138
|
+
// src/compiler/detectLanguage.ts
|
|
139
|
+
function detectLanguage(filePath) {
|
|
140
|
+
if (filePath.endsWith(".c")) return "c";
|
|
141
|
+
if (filePath.endsWith(".cpp") || filePath.endsWith(".cc") || filePath.endsWith(".cxx"))
|
|
142
|
+
return "cpp";
|
|
143
|
+
if (filePath.endsWith(".rs")) return "rust";
|
|
144
|
+
throw new Error(`Unsupported native file: ${filePath}`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/compiler/outputNaming.ts
|
|
148
|
+
function getSharedLibName(baseName, platform) {
|
|
149
|
+
if (platform.isWindows) return `${baseName}.dll`;
|
|
150
|
+
if (platform.isMac) return `lib${baseName}.dylib`;
|
|
151
|
+
return `lib${baseName}.so`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/compiler/buildCommand.ts
|
|
155
|
+
function buildCompileCommand(compiler, platform, request) {
|
|
156
|
+
const language = detectLanguage(request.sourcePath);
|
|
157
|
+
const baseName = basename(request.sourcePath).split(".")[0];
|
|
158
|
+
const outputName = getSharedLibName(baseName, platform);
|
|
159
|
+
const outputPath = `${request.outDir}/${outputName}`;
|
|
160
|
+
const flags = request.flags ?? [];
|
|
161
|
+
let command = [];
|
|
162
|
+
if (language === "c" || language === "cpp") {
|
|
163
|
+
if (compiler.vendor === "msvc") {
|
|
164
|
+
command = [
|
|
165
|
+
request.sourcePath,
|
|
166
|
+
"/LD",
|
|
167
|
+
`/Fe:${outputPath}`,
|
|
168
|
+
...flags
|
|
169
|
+
];
|
|
170
|
+
} else {
|
|
171
|
+
command = [
|
|
172
|
+
request.sourcePath,
|
|
173
|
+
"-shared",
|
|
174
|
+
"-fPIC",
|
|
175
|
+
"-o",
|
|
176
|
+
outputPath,
|
|
177
|
+
...flags
|
|
178
|
+
];
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (language === "rust") {
|
|
182
|
+
command = [
|
|
183
|
+
request.sourcePath,
|
|
184
|
+
"--crate-type",
|
|
185
|
+
"cdylib",
|
|
186
|
+
"-O",
|
|
187
|
+
"-o",
|
|
188
|
+
outputPath,
|
|
189
|
+
...flags
|
|
190
|
+
];
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
language,
|
|
194
|
+
outputPath,
|
|
195
|
+
command
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/compiler/compileNative.ts
|
|
200
|
+
function compileNative(compiler, platform, request) {
|
|
201
|
+
mkdirSync(request.outDir, { recursive: true });
|
|
202
|
+
const result = buildCompileCommand(compiler, platform, request);
|
|
203
|
+
try {
|
|
204
|
+
execFileSync4(
|
|
205
|
+
compiler.kind === "rust" ? compiler.path : compiler.path,
|
|
206
|
+
result.command,
|
|
207
|
+
{ stdio: "inherit" }
|
|
208
|
+
);
|
|
209
|
+
} catch (err) {
|
|
210
|
+
throw new Error(
|
|
211
|
+
`Native compilation failed for ${request.sourcePath}`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/compiler/compileWithCache.ts
|
|
218
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
219
|
+
|
|
220
|
+
// src/cache/hash.ts
|
|
221
|
+
import { readFileSync } from "fs";
|
|
222
|
+
import crypto from "crypto";
|
|
223
|
+
function computeHash(input) {
|
|
224
|
+
const source = readFileSync(input.sourcePath, "utf8");
|
|
225
|
+
const hash = crypto.createHash("sha256");
|
|
226
|
+
hash.update(source);
|
|
227
|
+
hash.update(input.compiler.path);
|
|
228
|
+
hash.update(input.compiler.version);
|
|
229
|
+
hash.update(input.flags.join(" "));
|
|
230
|
+
hash.update(input.platform);
|
|
231
|
+
return hash.digest("hex");
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// src/cache/cacheManager.ts
|
|
235
|
+
import { existsSync, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync2 } from "fs";
|
|
236
|
+
import { join as join3 } from "path";
|
|
237
|
+
|
|
238
|
+
// src/cache/cachePaths.ts
|
|
239
|
+
import { homedir } from "os";
|
|
240
|
+
import { join as join2 } from "path";
|
|
241
|
+
function getCacheRoot() {
|
|
242
|
+
return join2(homedir(), ".relaxnative", "cache");
|
|
243
|
+
}
|
|
244
|
+
function getCacheEntry(hash) {
|
|
245
|
+
return join2(getCacheRoot(), hash);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// src/cache/cacheManager.ts
|
|
249
|
+
function cacheExists(hash) {
|
|
250
|
+
return existsSync(getCacheEntry(hash));
|
|
251
|
+
}
|
|
252
|
+
function loadCacheEntry(hash) {
|
|
253
|
+
const metaPath = join3(getCacheEntry(hash), "meta.json");
|
|
254
|
+
const raw = readFileSync2(metaPath, "utf8");
|
|
255
|
+
return JSON.parse(raw);
|
|
256
|
+
}
|
|
257
|
+
function saveCacheEntry(entry) {
|
|
258
|
+
const dir = getCacheEntry(entry.hash);
|
|
259
|
+
mkdirSync2(dir, { recursive: true });
|
|
260
|
+
writeFileSync2(
|
|
261
|
+
join3(dir, "meta.json"),
|
|
262
|
+
JSON.stringify(entry, null, 2)
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// src/compiler/compileWithCache.ts
|
|
267
|
+
function compileWithCache(compiler, platform, request) {
|
|
268
|
+
const flags = request.flags ?? [];
|
|
269
|
+
const hash = computeHash({
|
|
270
|
+
sourcePath: request.sourcePath,
|
|
271
|
+
compiler,
|
|
272
|
+
flags,
|
|
273
|
+
platform: `${platform.platform}-${platform.arch}`
|
|
274
|
+
});
|
|
275
|
+
if (cacheExists(hash)) {
|
|
276
|
+
const entry = loadCacheEntry(hash);
|
|
277
|
+
return {
|
|
278
|
+
language: compiler.kind === "rust" ? "rust" : "c",
|
|
279
|
+
outputPath: entry.outputPath,
|
|
280
|
+
command: []
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
const cacheDir = getCacheEntry(hash);
|
|
284
|
+
mkdirSync3(cacheDir, { recursive: true });
|
|
285
|
+
const result = compileNative(compiler, platform, {
|
|
286
|
+
...request,
|
|
287
|
+
outDir: cacheDir
|
|
288
|
+
});
|
|
289
|
+
saveCacheEntry({
|
|
290
|
+
hash,
|
|
291
|
+
sourcePath: request.sourcePath,
|
|
292
|
+
outputPath: result.outputPath,
|
|
293
|
+
compilerPath: compiler.path,
|
|
294
|
+
compilerVersion: compiler.version,
|
|
295
|
+
flags,
|
|
296
|
+
platform: `${platform.platform}-${platform.arch}`,
|
|
297
|
+
createdAt: Date.now()
|
|
298
|
+
});
|
|
299
|
+
return result;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/parser/index.ts
|
|
303
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
304
|
+
|
|
305
|
+
// src/parser/loadParser.ts
|
|
306
|
+
import Parser from "tree-sitter";
|
|
307
|
+
import C from "tree-sitter-c";
|
|
308
|
+
import CPP from "tree-sitter-cpp";
|
|
309
|
+
import Rust from "tree-sitter-rust";
|
|
310
|
+
function createParser(lang) {
|
|
311
|
+
const parser = new Parser();
|
|
312
|
+
if (lang === "c") parser.setLanguage(C);
|
|
313
|
+
if (lang === "cpp") parser.setLanguage(CPP);
|
|
314
|
+
if (lang === "rust") parser.setLanguage(Rust);
|
|
315
|
+
return parser;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// src/parser/parseC.ts
|
|
319
|
+
function parseCFunctions(tree) {
|
|
320
|
+
const functions = [];
|
|
321
|
+
function visit(node) {
|
|
322
|
+
if (node.type === "function_definition") {
|
|
323
|
+
const declarator = node.childForFieldName("declarator");
|
|
324
|
+
const typeNode = node.childForFieldName("type");
|
|
325
|
+
if (!declarator || !typeNode) return;
|
|
326
|
+
const nameNode = declarator.childForFieldName("declarator");
|
|
327
|
+
const name = nameNode?.text ?? declarator.text?.split("(")[0]?.trim();
|
|
328
|
+
if (!name) return;
|
|
329
|
+
const paramsNode = declarator.childForFieldName("parameters");
|
|
330
|
+
const params = paramsNode?.children?.filter((c) => c.type === "parameter_declaration").map((p) => ({
|
|
331
|
+
name: p.childForFieldName("declarator")?.text ?? "arg",
|
|
332
|
+
type: mapCType(
|
|
333
|
+
// For pointer params, tree-sitter C often stores `*` in the declarator,
|
|
334
|
+
// not in the `type` field. Combine both so we can detect pointers.
|
|
335
|
+
`${p.childForFieldName("type")?.text ?? ""}${p.childForFieldName("declarator")?.text ?? ""}`
|
|
336
|
+
)
|
|
337
|
+
})) ?? [];
|
|
338
|
+
functions.push({
|
|
339
|
+
name,
|
|
340
|
+
returnType: mapCType(typeNode.text),
|
|
341
|
+
params,
|
|
342
|
+
sourceLine: node.startPosition.row + 1
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
for (const child of node.children ?? []) visit(child);
|
|
346
|
+
}
|
|
347
|
+
visit(tree.rootNode);
|
|
348
|
+
return functions;
|
|
349
|
+
}
|
|
350
|
+
function mapCType(type) {
|
|
351
|
+
if (!type) return "unknown";
|
|
352
|
+
if (/\b(u?int8_t|unsigned\s+char|uint8_t)\b/.test(type) && type.includes("*")) return "buffer";
|
|
353
|
+
if (type.includes("int")) return "int";
|
|
354
|
+
if (type.includes("long")) return "long";
|
|
355
|
+
if (type.includes("float")) return "float";
|
|
356
|
+
if (type.includes("double")) return "double";
|
|
357
|
+
if (type.includes("char") && type.includes("*")) return "buffer";
|
|
358
|
+
if (type.includes("*")) return "pointer";
|
|
359
|
+
if (type.includes("void")) return "void";
|
|
360
|
+
return "unknown";
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// src/parser/parseRust.ts
|
|
364
|
+
function parseRustFunctions(tree) {
|
|
365
|
+
const functions = [];
|
|
366
|
+
function visit(node) {
|
|
367
|
+
if (node.type === "function_item" && node.text.includes("pub extern")) {
|
|
368
|
+
const nameNode = node.childForFieldName("name");
|
|
369
|
+
const paramsNode = node.childForFieldName("parameters");
|
|
370
|
+
const returnNode = node.childForFieldName("return_type");
|
|
371
|
+
const params = paramsNode?.children?.filter((c) => c.type === "parameter").map((p) => ({
|
|
372
|
+
name: p.childForFieldName("pattern")?.text ?? "arg",
|
|
373
|
+
type: mapRustType(p.childForFieldName("type")?.text)
|
|
374
|
+
})) ?? [];
|
|
375
|
+
functions.push({
|
|
376
|
+
name: nameNode?.text ?? "unknown",
|
|
377
|
+
returnType: mapRustType(returnNode?.text),
|
|
378
|
+
params,
|
|
379
|
+
sourceLine: node.startPosition.row + 1
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
for (const child of node.children ?? []) visit(child);
|
|
383
|
+
}
|
|
384
|
+
visit(tree.rootNode);
|
|
385
|
+
return functions;
|
|
386
|
+
}
|
|
387
|
+
function mapRustType(type) {
|
|
388
|
+
if (!type) return "void";
|
|
389
|
+
if (type.includes("i32")) return "int";
|
|
390
|
+
if (type.includes("i64")) return "long";
|
|
391
|
+
if (type.includes("f32")) return "float";
|
|
392
|
+
if (type.includes("f64")) return "double";
|
|
393
|
+
if (type.includes("*const i8")) return "char*";
|
|
394
|
+
if (type.includes("*")) return "pointer";
|
|
395
|
+
return "unknown";
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/parser/validateFunctions.ts
|
|
399
|
+
function validateFunctions(funcs) {
|
|
400
|
+
return funcs.filter((fn) => {
|
|
401
|
+
if (fn.returnType === "unknown") return false;
|
|
402
|
+
if (fn.params.some((p) => p.type === "unknown")) return false;
|
|
403
|
+
return true;
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// src/parser/generateBindings.ts
|
|
408
|
+
function generateBindings(functions) {
|
|
409
|
+
const entries = functions.map((f) => [
|
|
410
|
+
f.name,
|
|
411
|
+
{
|
|
412
|
+
name: f.name,
|
|
413
|
+
returns: f.returnType,
|
|
414
|
+
args: f.params.map((p) => p.type),
|
|
415
|
+
// optional execution hints
|
|
416
|
+
mode: f.annotations?.mode,
|
|
417
|
+
cost: f.annotations?.cost
|
|
418
|
+
}
|
|
419
|
+
]);
|
|
420
|
+
return {
|
|
421
|
+
functions: Object.fromEntries(entries)
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// src/parser/index.ts
|
|
426
|
+
function parseAnnotationsForFunctions(source, funcs) {
|
|
427
|
+
const lines = source.split(/\r?\n/);
|
|
428
|
+
for (const fn of funcs) {
|
|
429
|
+
if (!fn.sourceLine) continue;
|
|
430
|
+
const idx = Math.max(0, fn.sourceLine - 1);
|
|
431
|
+
const start = Math.max(0, idx - 3);
|
|
432
|
+
const window = lines.slice(start, idx).join("\n");
|
|
433
|
+
const mode = /@async\b/.test(window) ? "async" : /@sync\b/.test(window) ? "sync" : void 0;
|
|
434
|
+
const costMatch = window.match(/@cost\s+(low|medium|high)\b/);
|
|
435
|
+
const cost = costMatch?.[1] ?? void 0;
|
|
436
|
+
if (mode || cost) {
|
|
437
|
+
fn.annotations = { ...fn.annotations ?? {}, mode, cost };
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return funcs;
|
|
441
|
+
}
|
|
442
|
+
function parseNativeSource(filePath, lang) {
|
|
443
|
+
const source = readFileSync3(filePath, "utf8");
|
|
444
|
+
const parser = createParser(lang);
|
|
445
|
+
const tree = parser.parse(source);
|
|
446
|
+
const rawFunctions = lang === "rust" ? parseRustFunctions(tree) : parseCFunctions(tree);
|
|
447
|
+
const annotated = parseAnnotationsForFunctions(source, rawFunctions);
|
|
448
|
+
const validFunctions = validateFunctions(annotated);
|
|
449
|
+
return generateBindings(validFunctions);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// src/loader.ts
|
|
453
|
+
async function loadNative(sourcePath, options) {
|
|
454
|
+
const { c, rust, platform } = detectCompilers();
|
|
455
|
+
const language = detectLanguage(sourcePath);
|
|
456
|
+
const compiler = language === "rust" ? rust : c;
|
|
457
|
+
if (!compiler) {
|
|
458
|
+
throw new Error(`No compiler for ${language}`);
|
|
459
|
+
}
|
|
460
|
+
const compileResult = compileWithCache(compiler, platform, {
|
|
461
|
+
sourcePath,
|
|
462
|
+
outDir: ".cache/native"
|
|
463
|
+
});
|
|
464
|
+
const bindings = parseNativeSource(sourcePath, language);
|
|
465
|
+
const config = options?.config;
|
|
466
|
+
if (config?.functionMode) {
|
|
467
|
+
for (const [name, mode] of Object.entries(config.functionMode)) {
|
|
468
|
+
if (bindings.functions?.[name]) {
|
|
469
|
+
bindings.functions[name] = {
|
|
470
|
+
...bindings.functions[name],
|
|
471
|
+
mode
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
if (config?.defaultMode) {
|
|
477
|
+
for (const [name, binding] of Object.entries(bindings.functions ?? {})) {
|
|
478
|
+
if (!binding.mode) {
|
|
479
|
+
bindings.functions[name] = { ...binding, mode: config.defaultMode };
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
globalThis.__bindings = bindings;
|
|
484
|
+
const api = loadFfi(compileResult.outputPath, bindings);
|
|
485
|
+
const isolation = options?.isolation ?? "worker";
|
|
486
|
+
return wrapFunctions(api, compileResult.outputPath, bindings, { isolation });
|
|
487
|
+
}
|
|
488
|
+
async function loadNativeWithBindings(sourcePath, options) {
|
|
489
|
+
const { c, rust, platform } = detectCompilers();
|
|
490
|
+
const language = detectLanguage(sourcePath);
|
|
491
|
+
const compiler = language === "rust" ? rust : c;
|
|
492
|
+
if (!compiler) {
|
|
493
|
+
throw new Error(`No compiler for ${language}`);
|
|
494
|
+
}
|
|
495
|
+
const compileResult = compileWithCache(compiler, platform, {
|
|
496
|
+
sourcePath,
|
|
497
|
+
outDir: ".cache/native"
|
|
498
|
+
});
|
|
499
|
+
const bindings = parseNativeSource(sourcePath, language);
|
|
500
|
+
const config = options?.config;
|
|
501
|
+
if (config?.functionMode) {
|
|
502
|
+
for (const [name, mode] of Object.entries(config.functionMode)) {
|
|
503
|
+
if (bindings.functions?.[name]) {
|
|
504
|
+
bindings.functions[name] = {
|
|
505
|
+
...bindings.functions[name],
|
|
506
|
+
mode
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (config?.defaultMode) {
|
|
512
|
+
for (const [name, binding] of Object.entries(bindings.functions ?? {})) {
|
|
513
|
+
if (!binding.mode) {
|
|
514
|
+
bindings.functions[name] = { ...binding, mode: config.defaultMode };
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
globalThis.__bindings = bindings;
|
|
519
|
+
const api = loadFfi(compileResult.outputPath, bindings);
|
|
520
|
+
const isolation = options?.isolation ?? "worker";
|
|
521
|
+
const mod = wrapFunctions(api, compileResult.outputPath, bindings, { isolation });
|
|
522
|
+
return { mod, bindings };
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// src/memory/nativeMemory.ts
|
|
526
|
+
import koffi from "koffi";
|
|
527
|
+
function allocRaw(size) {
|
|
528
|
+
if (!Number.isInteger(size) || size <= 0) {
|
|
529
|
+
throw new TypeError(`native.alloc(size): size must be a positive integer, got ${size}`);
|
|
530
|
+
}
|
|
531
|
+
const handle = koffi.alloc("char", size);
|
|
532
|
+
const ab = koffi.view(handle, size);
|
|
533
|
+
const view = new Uint8Array(ab);
|
|
534
|
+
return { handle, view };
|
|
535
|
+
}
|
|
536
|
+
function freeRaw(ptr) {
|
|
537
|
+
koffi.free(ptr);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// src/memory/index.ts
|
|
541
|
+
var memory_exports = {};
|
|
542
|
+
__export(memory_exports, {
|
|
543
|
+
InvalidFreeError: () => InvalidFreeError,
|
|
544
|
+
MemoryError: () => MemoryError,
|
|
545
|
+
NativeBuffer: () => NativeBuffer,
|
|
546
|
+
NativePointer: () => NativePointer,
|
|
547
|
+
NullPointerError: () => NullPointerError,
|
|
548
|
+
UseAfterFreeError: () => UseAfterFreeError,
|
|
549
|
+
alloc: () => alloc,
|
|
550
|
+
allocRaw: () => allocRaw,
|
|
551
|
+
free: () => free,
|
|
552
|
+
freeRaw: () => freeRaw
|
|
553
|
+
});
|
|
554
|
+
function alloc(size, opts) {
|
|
555
|
+
const allocation = allocRaw(size);
|
|
556
|
+
return new NativeBuffer(allocation, { ownership: "js", autoFree: opts?.autoFree });
|
|
557
|
+
}
|
|
558
|
+
function free(ptr) {
|
|
559
|
+
if (NativeBuffer.isNativeBuffer(ptr)) {
|
|
560
|
+
ptr.free();
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
if (NativePointer.isNativePointer(ptr)) {
|
|
564
|
+
throw new InvalidFreeError(
|
|
565
|
+
`native.free(ptr): cannot free a raw NativePointer (unknown allocator). Use a NativeBuffer allocated by native.alloc().`
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
throw new TypeError("native.free(ptr): expected NativeBuffer or NativePointer");
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
export {
|
|
572
|
+
detectCompilers,
|
|
573
|
+
compileNative,
|
|
574
|
+
compileWithCache,
|
|
575
|
+
loadNative,
|
|
576
|
+
loadNativeWithBindings,
|
|
577
|
+
allocRaw,
|
|
578
|
+
freeRaw,
|
|
579
|
+
alloc,
|
|
580
|
+
free,
|
|
581
|
+
memory_exports
|
|
582
|
+
};
|