relaxnative 0.1.4 → 0.1.6
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/dist/chunk-5OGJZMKW.js +847 -0
- package/dist/chunk-7DCYYU42.js +898 -0
- package/dist/chunk-AIVJY3DV.js +836 -0
- package/dist/chunk-BNACBSBG.js +898 -0
- package/dist/chunk-BUS5X56T.js +898 -0
- package/dist/chunk-CNN72QHA.js +440 -0
- package/dist/chunk-DLPCZKK2.js +506 -0
- package/dist/chunk-DQABBOH7.js +898 -0
- package/dist/chunk-GKVUUIR2.js +443 -0
- package/dist/chunk-GKW6DOAN.js +832 -0
- package/dist/chunk-HWCDBP76.js +506 -0
- package/dist/chunk-JVROH6IA.js +832 -0
- package/dist/chunk-L6YEDT5D.js +832 -0
- package/dist/chunk-NOZYUZGQ.js +832 -0
- package/dist/chunk-PZVREJJS.js +492 -0
- package/dist/chunk-SOYA3COP.js +898 -0
- package/dist/chunk-TLZQADFD.js +898 -0
- package/dist/chunk-VWV37Q74.js +832 -0
- package/dist/chunk-WCOPTYON.js +560 -0
- package/dist/chunk-XY35XW5T.js +498 -0
- package/dist/chunk-ZVAHPSM6.js +898 -0
- package/dist/cli.js +3 -3
- package/dist/index.js +6 -6
- package/dist/memory/memory.selftest.js +2 -2
- package/dist/worker/processEntry.js +1 -1
- package/dist/worker/workerEntry.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,847 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatBindingSignature,
|
|
3
|
+
logDebug,
|
|
4
|
+
traceDebug,
|
|
5
|
+
traceInfo,
|
|
6
|
+
wrapFunctions
|
|
7
|
+
} from "./chunk-HBO7F7P4.js";
|
|
8
|
+
import {
|
|
9
|
+
InvalidFreeError,
|
|
10
|
+
MemoryError,
|
|
11
|
+
NativeBuffer,
|
|
12
|
+
NativePointer,
|
|
13
|
+
NullPointerError,
|
|
14
|
+
UseAfterFreeError,
|
|
15
|
+
loadFfi
|
|
16
|
+
} from "./chunk-GKVUUIR2.js";
|
|
17
|
+
import {
|
|
18
|
+
__export
|
|
19
|
+
} from "./chunk-MLKGABMK.js";
|
|
20
|
+
|
|
21
|
+
// src/compiler/detectPlatform.ts
|
|
22
|
+
function detectPlatform() {
|
|
23
|
+
const platform = process.platform;
|
|
24
|
+
const arch = process.arch;
|
|
25
|
+
return {
|
|
26
|
+
platform,
|
|
27
|
+
arch,
|
|
28
|
+
isWindows: platform === "win32",
|
|
29
|
+
isMac: platform === "darwin",
|
|
30
|
+
isLinux: platform === "linux"
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/compiler/detectCCompiler.ts
|
|
35
|
+
import { execFileSync } from "child_process";
|
|
36
|
+
|
|
37
|
+
// src/utils/which.ts
|
|
38
|
+
import { accessSync, constants } from "fs";
|
|
39
|
+
import { delimiter } from "path";
|
|
40
|
+
function which(cmd) {
|
|
41
|
+
const paths = process.env.PATH?.split(delimiter) ?? [];
|
|
42
|
+
for (const p of paths) {
|
|
43
|
+
const full = `${p}/${cmd}`;
|
|
44
|
+
try {
|
|
45
|
+
accessSync(full, constants.X_OK);
|
|
46
|
+
return full;
|
|
47
|
+
} catch {
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// src/compiler/detectCCompiler.ts
|
|
54
|
+
function getVersion(path) {
|
|
55
|
+
try {
|
|
56
|
+
return execFileSync(path, ["--version"], { encoding: "utf8" }).split("\n")[0].trim();
|
|
57
|
+
} catch {
|
|
58
|
+
return "unknown";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function detectCCompiler() {
|
|
62
|
+
const candidates = [
|
|
63
|
+
process.env.CC,
|
|
64
|
+
"clang",
|
|
65
|
+
"gcc",
|
|
66
|
+
"cc",
|
|
67
|
+
"cl"
|
|
68
|
+
].filter(Boolean);
|
|
69
|
+
for (const name of candidates) {
|
|
70
|
+
const resolved = name.includes("/") ? name : which(name);
|
|
71
|
+
if (!resolved) continue;
|
|
72
|
+
const version = getVersion(resolved);
|
|
73
|
+
return {
|
|
74
|
+
kind: "c",
|
|
75
|
+
path: resolved,
|
|
76
|
+
version,
|
|
77
|
+
vendor: resolved.includes("clang") ? "clang" : resolved.includes("gcc") ? "gcc" : resolved.includes("cl") ? "msvc" : "unknown"
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
throw new Error("No C compiler found (gcc/clang/cl)");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/compiler/detectRustCompiler.ts
|
|
84
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
85
|
+
function detectRustCompiler() {
|
|
86
|
+
const rustc = process.env.RUSTC ?? which("rustc");
|
|
87
|
+
if (!rustc) return null;
|
|
88
|
+
const version = execFileSync2(rustc, ["--version"], {
|
|
89
|
+
encoding: "utf8"
|
|
90
|
+
}).trim();
|
|
91
|
+
return {
|
|
92
|
+
kind: "rust",
|
|
93
|
+
path: rustc,
|
|
94
|
+
version,
|
|
95
|
+
vendor: "rust"
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/compiler/sanityCheck.ts
|
|
100
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
101
|
+
import { writeFileSync, mkdtempSync } from "fs";
|
|
102
|
+
import { tmpdir } from "os";
|
|
103
|
+
import { join } from "path";
|
|
104
|
+
function sanityCheckC(compiler) {
|
|
105
|
+
const dir = mkdtempSync(join(tmpdir(), "relaxnative-"));
|
|
106
|
+
const source = join(dir, "test.c");
|
|
107
|
+
const output = join(dir, "test");
|
|
108
|
+
writeFileSync(source, "int main(){return 0;}");
|
|
109
|
+
try {
|
|
110
|
+
execFileSync3(
|
|
111
|
+
compiler.path,
|
|
112
|
+
compiler.vendor === "msvc" ? [source] : [source, "-o", output],
|
|
113
|
+
{ stdio: "ignore" }
|
|
114
|
+
);
|
|
115
|
+
} catch (err) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`C compiler failed sanity check: ${compiler.path}`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/compiler/detect.ts
|
|
123
|
+
function detectCompilers() {
|
|
124
|
+
const platform = detectPlatform();
|
|
125
|
+
const c = detectCCompiler();
|
|
126
|
+
sanityCheckC(c);
|
|
127
|
+
const rust = detectRustCompiler();
|
|
128
|
+
return {
|
|
129
|
+
platform,
|
|
130
|
+
c,
|
|
131
|
+
rust
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/cache/cachePaths.ts
|
|
136
|
+
import { homedir } from "os";
|
|
137
|
+
import { join as join2 } from "path";
|
|
138
|
+
function homeDir() {
|
|
139
|
+
return process.env.HOME ?? homedir();
|
|
140
|
+
}
|
|
141
|
+
function getCacheRoot() {
|
|
142
|
+
return join2(homeDir(), ".relaxnative", "cache");
|
|
143
|
+
}
|
|
144
|
+
function getCacheEntry(hash) {
|
|
145
|
+
return join2(getCacheRoot(), hash);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/compiler/compileNative.ts
|
|
149
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
150
|
+
import { mkdirSync } from "fs";
|
|
151
|
+
|
|
152
|
+
// src/compiler/buildCommand.ts
|
|
153
|
+
import { basename } from "path";
|
|
154
|
+
|
|
155
|
+
// src/compiler/detectLanguage.ts
|
|
156
|
+
function detectLanguage(filePath) {
|
|
157
|
+
if (filePath.endsWith(".c")) return "c";
|
|
158
|
+
if (filePath.endsWith(".cpp") || filePath.endsWith(".cc") || filePath.endsWith(".cxx"))
|
|
159
|
+
return "cpp";
|
|
160
|
+
if (filePath.endsWith(".rs")) return "rust";
|
|
161
|
+
throw new Error(`Unsupported native file: ${filePath}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/compiler/outputNaming.ts
|
|
165
|
+
function getSharedLibName(baseName, platform) {
|
|
166
|
+
if (platform.isWindows) return `${baseName}.dll`;
|
|
167
|
+
if (platform.isMac) return `lib${baseName}.dylib`;
|
|
168
|
+
return `lib${baseName}.so`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/compiler/buildCommand.ts
|
|
172
|
+
function buildCompileCommand(compiler, platform, request) {
|
|
173
|
+
const language = detectLanguage(request.sourcePath);
|
|
174
|
+
const baseName = basename(request.sourcePath).split(".")[0];
|
|
175
|
+
const outputName = getSharedLibName(baseName, platform);
|
|
176
|
+
const outputPath = `${request.outDir}/${outputName}`;
|
|
177
|
+
const userFlags = request.flags ?? [];
|
|
178
|
+
const sources = [request.sourcePath, ...request.sources ?? []];
|
|
179
|
+
const includePaths = request.includePaths ?? [];
|
|
180
|
+
const libraryPaths = request.libraryPaths ?? [];
|
|
181
|
+
const libraries = request.libraries ?? [];
|
|
182
|
+
const includeFlags = compiler.vendor === "msvc" ? includePaths.map((p) => `/I${p}`) : includePaths.flatMap((p) => ["-I", p]);
|
|
183
|
+
const libPathFlags = compiler.vendor === "msvc" ? libraryPaths.map((p) => `/LIBPATH:${p}`) : libraryPaths.flatMap((p) => ["-L", p]);
|
|
184
|
+
const libFlags = compiler.vendor === "msvc" ? libraries.map((l) => `${l}.lib`) : libraries.flatMap((l) => ["-l", l]);
|
|
185
|
+
const flags = [...includeFlags, ...libPathFlags, ...libFlags, ...userFlags];
|
|
186
|
+
let command = [];
|
|
187
|
+
if (language === "c" || language === "cpp") {
|
|
188
|
+
if (compiler.vendor === "msvc") {
|
|
189
|
+
command = [
|
|
190
|
+
...sources,
|
|
191
|
+
"/LD",
|
|
192
|
+
`/Fe:${outputPath}`,
|
|
193
|
+
...flags
|
|
194
|
+
];
|
|
195
|
+
} else {
|
|
196
|
+
command = [
|
|
197
|
+
...sources,
|
|
198
|
+
"-shared",
|
|
199
|
+
"-fPIC",
|
|
200
|
+
"-o",
|
|
201
|
+
outputPath,
|
|
202
|
+
...flags
|
|
203
|
+
];
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (language === "rust") {
|
|
207
|
+
command = [
|
|
208
|
+
request.sourcePath,
|
|
209
|
+
"--crate-type",
|
|
210
|
+
"cdylib",
|
|
211
|
+
"-O",
|
|
212
|
+
"-o",
|
|
213
|
+
outputPath,
|
|
214
|
+
...flags
|
|
215
|
+
];
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
language,
|
|
219
|
+
outputPath,
|
|
220
|
+
command
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// src/compiler/compileNative.ts
|
|
225
|
+
function parseDiagnostics(text) {
|
|
226
|
+
const out = [];
|
|
227
|
+
const lines = String(text ?? "").split(/\r?\n/);
|
|
228
|
+
const ccRe = /^(.*?):(\d+):(\d+):\s*(warning|error|note):\s*(.*)$/;
|
|
229
|
+
for (const l of lines) {
|
|
230
|
+
const m = l.match(ccRe);
|
|
231
|
+
if (m) {
|
|
232
|
+
out.push({
|
|
233
|
+
file: m[1],
|
|
234
|
+
line: Number(m[2]),
|
|
235
|
+
col: Number(m[3]),
|
|
236
|
+
severity: m[4],
|
|
237
|
+
message: m[5],
|
|
238
|
+
raw: l
|
|
239
|
+
});
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
const m2 = l.match(/^\s*-->\s*(.*?):(\d+):(\d+)\s*$/);
|
|
243
|
+
if (m2) {
|
|
244
|
+
const prev = out[out.length - 1];
|
|
245
|
+
if (prev && !prev.file) {
|
|
246
|
+
prev.file = m2[1];
|
|
247
|
+
prev.line = Number(m2[2]);
|
|
248
|
+
prev.col = Number(m2[3]);
|
|
249
|
+
prev.raw += `
|
|
250
|
+
${l}`;
|
|
251
|
+
} else {
|
|
252
|
+
out.push({ severity: "error", file: m2[1], line: Number(m2[2]), col: Number(m2[3]), message: "", raw: l });
|
|
253
|
+
}
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
const m3 = l.match(/^(error|warning|note)(?:\[[^\]]+\])?:\s*(.*)$/);
|
|
257
|
+
if (m3) {
|
|
258
|
+
out.push({ severity: m3[1], message: m3[2], raw: l });
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return out;
|
|
263
|
+
}
|
|
264
|
+
function formatDiagnostics(diags) {
|
|
265
|
+
if (!diags.length) return "";
|
|
266
|
+
const lines = [];
|
|
267
|
+
for (const d of diags) {
|
|
268
|
+
const loc = d.file && d.line != null ? `${d.file}:${d.line}:${d.col ?? 0}` : d.file ?? "";
|
|
269
|
+
const head = loc ? `${loc} - ${d.severity}` : d.severity;
|
|
270
|
+
const msg = d.message ? `: ${d.message}` : "";
|
|
271
|
+
lines.push(`${head}${msg}`);
|
|
272
|
+
}
|
|
273
|
+
return lines.join("\n");
|
|
274
|
+
}
|
|
275
|
+
function compileNative(compiler, platform, request) {
|
|
276
|
+
mkdirSync(request.outDir, { recursive: true });
|
|
277
|
+
const result = buildCompileCommand(compiler, platform, request);
|
|
278
|
+
try {
|
|
279
|
+
logDebug("compile", { compiler: compiler.path, cmd: result.command, sourcePath: request.sourcePath });
|
|
280
|
+
execFileSync4(compiler.path, result.command, {
|
|
281
|
+
stdio: "inherit"
|
|
282
|
+
});
|
|
283
|
+
} catch (err) {
|
|
284
|
+
try {
|
|
285
|
+
const out = execFileSync4(compiler.path, result.command, {
|
|
286
|
+
encoding: "utf8",
|
|
287
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
288
|
+
});
|
|
289
|
+
logDebug("compile recovered on retry", { sourcePath: request.sourcePath, outLen: out?.length ?? 0 });
|
|
290
|
+
} catch (err2) {
|
|
291
|
+
const stdout = String(err2?.stdout ?? "");
|
|
292
|
+
const stderr = String(err2?.stderr ?? "");
|
|
293
|
+
const diags = parseDiagnostics([stderr, stdout].filter(Boolean).join("\n"));
|
|
294
|
+
const formatted = formatDiagnostics(diags);
|
|
295
|
+
const details = formatted ? `
|
|
296
|
+
|
|
297
|
+
${formatted}` : stderr || stdout ? `
|
|
298
|
+
|
|
299
|
+
${(stderr || stdout).trim()}` : "";
|
|
300
|
+
throw new Error(`Native compilation failed for ${request.sourcePath}${details}`);
|
|
301
|
+
}
|
|
302
|
+
throw new Error(`Native compilation failed for ${request.sourcePath}`);
|
|
303
|
+
}
|
|
304
|
+
return result;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// src/compiler/compileWithCache.ts
|
|
308
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
309
|
+
|
|
310
|
+
// src/cache/hash.ts
|
|
311
|
+
import { readFileSync } from "fs";
|
|
312
|
+
import crypto from "crypto";
|
|
313
|
+
function computeHash(input) {
|
|
314
|
+
const source = readFileSync(input.sourcePath, "utf8");
|
|
315
|
+
const hash = crypto.createHash("sha256");
|
|
316
|
+
hash.update(source);
|
|
317
|
+
hash.update(input.compiler.path);
|
|
318
|
+
hash.update(input.compiler.version);
|
|
319
|
+
hash.update(input.flags.join(" "));
|
|
320
|
+
hash.update(input.platform);
|
|
321
|
+
return hash.digest("hex");
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// src/cache/cacheManager.ts
|
|
325
|
+
import { existsSync, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync2 } from "fs";
|
|
326
|
+
import { join as join3 } from "path";
|
|
327
|
+
function cacheExists(hash) {
|
|
328
|
+
return existsSync(getCacheEntry(hash));
|
|
329
|
+
}
|
|
330
|
+
function loadCacheEntry(hash) {
|
|
331
|
+
const metaPath = join3(getCacheEntry(hash), "meta.json");
|
|
332
|
+
const raw = readFileSync2(metaPath, "utf8");
|
|
333
|
+
const entry = JSON.parse(raw);
|
|
334
|
+
try {
|
|
335
|
+
const next = {
|
|
336
|
+
...entry,
|
|
337
|
+
lastAccessAt: Date.now()
|
|
338
|
+
};
|
|
339
|
+
writeFileSync2(metaPath, JSON.stringify(next, null, 2));
|
|
340
|
+
return next;
|
|
341
|
+
} catch {
|
|
342
|
+
return entry;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
function saveCacheEntry(entry) {
|
|
346
|
+
const dir = getCacheEntry(entry.hash);
|
|
347
|
+
mkdirSync2(dir, { recursive: true });
|
|
348
|
+
writeFileSync2(
|
|
349
|
+
join3(dir, "meta.json"),
|
|
350
|
+
JSON.stringify(entry, null, 2)
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/compiler/compileWithCache.ts
|
|
355
|
+
function compileWithCache(compiler, platform, request) {
|
|
356
|
+
const flags = request.flags ?? [];
|
|
357
|
+
const hash = computeHash({
|
|
358
|
+
sourcePath: request.sourcePath,
|
|
359
|
+
compiler,
|
|
360
|
+
flags,
|
|
361
|
+
platform: `${platform.platform}-${platform.arch}`
|
|
362
|
+
});
|
|
363
|
+
if (cacheExists(hash)) {
|
|
364
|
+
logDebug("cache hit", { hash, sourcePath: request.sourcePath });
|
|
365
|
+
const entry = loadCacheEntry(hash);
|
|
366
|
+
return {
|
|
367
|
+
language: compiler.kind === "rust" ? "rust" : "c",
|
|
368
|
+
outputPath: entry.outputPath,
|
|
369
|
+
command: []
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
logDebug("cache miss", { hash, sourcePath: request.sourcePath });
|
|
373
|
+
const cacheDir = getCacheEntry(hash);
|
|
374
|
+
mkdirSync3(cacheDir, { recursive: true });
|
|
375
|
+
const result = compileNative(compiler, platform, {
|
|
376
|
+
...request,
|
|
377
|
+
outDir: cacheDir
|
|
378
|
+
});
|
|
379
|
+
saveCacheEntry({
|
|
380
|
+
hash,
|
|
381
|
+
sourcePath: request.sourcePath,
|
|
382
|
+
outputPath: result.outputPath,
|
|
383
|
+
compilerPath: compiler.path,
|
|
384
|
+
compilerVersion: compiler.version,
|
|
385
|
+
flags,
|
|
386
|
+
platform: `${platform.platform}-${platform.arch}`,
|
|
387
|
+
createdAt: Date.now(),
|
|
388
|
+
lastAccessAt: Date.now()
|
|
389
|
+
});
|
|
390
|
+
return result;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// src/dev/hotReload.ts
|
|
394
|
+
import { watch } from "fs";
|
|
395
|
+
import { dirname } from "path";
|
|
396
|
+
|
|
397
|
+
// src/parser/index.ts
|
|
398
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
399
|
+
|
|
400
|
+
// src/parser/loadParser.ts
|
|
401
|
+
import Parser from "tree-sitter";
|
|
402
|
+
import C from "tree-sitter-c";
|
|
403
|
+
import CPP from "tree-sitter-cpp";
|
|
404
|
+
import Rust from "tree-sitter-rust";
|
|
405
|
+
function createParser(lang) {
|
|
406
|
+
const parser = new Parser();
|
|
407
|
+
if (lang === "c") parser.setLanguage(C);
|
|
408
|
+
if (lang === "cpp") parser.setLanguage(CPP);
|
|
409
|
+
if (lang === "rust") parser.setLanguage(Rust);
|
|
410
|
+
return parser;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// src/parser/parseC.ts
|
|
414
|
+
function parseCFunctions(tree) {
|
|
415
|
+
const functions = [];
|
|
416
|
+
const varargs = [];
|
|
417
|
+
function visit(node) {
|
|
418
|
+
if (node.type === "function_definition") {
|
|
419
|
+
const declarator = node.childForFieldName("declarator");
|
|
420
|
+
const typeNode = node.childForFieldName("type");
|
|
421
|
+
if (!declarator || !typeNode) return;
|
|
422
|
+
const nameNode = declarator.childForFieldName("declarator");
|
|
423
|
+
const name = nameNode?.text ?? declarator.text?.split("(")[0]?.trim();
|
|
424
|
+
if (!name) return;
|
|
425
|
+
const paramsNode = declarator.childForFieldName("parameters");
|
|
426
|
+
const hasVarargs = (paramsNode?.children ?? []).some(
|
|
427
|
+
(c) => c?.type === "variadic_parameter" || c?.type === "..." || c?.text === "..."
|
|
428
|
+
);
|
|
429
|
+
if (hasVarargs) {
|
|
430
|
+
varargs.push({ name, line: node.startPosition.row + 1 });
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
const params = paramsNode?.children?.filter((c) => c.type === "parameter_declaration").map((p) => ({
|
|
434
|
+
name: p.childForFieldName("declarator")?.text ?? "arg",
|
|
435
|
+
type: mapCType(
|
|
436
|
+
// For pointer params, tree-sitter C often stores `*` in the declarator,
|
|
437
|
+
// not in the `type` field. Combine both so we can detect pointers.
|
|
438
|
+
`${p.childForFieldName("type")?.text ?? ""}${p.childForFieldName("declarator")?.text ?? ""}`
|
|
439
|
+
)
|
|
440
|
+
})) ?? [];
|
|
441
|
+
functions.push({
|
|
442
|
+
name,
|
|
443
|
+
returnType: mapCType(typeNode.text),
|
|
444
|
+
params,
|
|
445
|
+
sourceLine: node.startPosition.row + 1
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
for (const child of node.children ?? []) visit(child);
|
|
449
|
+
}
|
|
450
|
+
visit(tree.rootNode);
|
|
451
|
+
if (varargs.length) {
|
|
452
|
+
const msg = varargs.map((v) => `- ${v.name} (line ${v.line})`).join("\n");
|
|
453
|
+
throw new Error(
|
|
454
|
+
`Unsupported native API: variadic functions (varargs, ",") are not supported.
|
|
455
|
+
|
|
456
|
+
Relaxnative can't safely infer varargs ABI/calling conventions from source.
|
|
457
|
+
Please write a fixed-signature wrapper function in C/C++ and export that instead.
|
|
458
|
+
|
|
459
|
+
Found varargs functions:
|
|
460
|
+
${msg}`
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
return functions;
|
|
464
|
+
}
|
|
465
|
+
function mapCType(type) {
|
|
466
|
+
if (!type) return "unknown";
|
|
467
|
+
const t = type.replace(/\s+/g, " ").trim();
|
|
468
|
+
if (/\bconst\s+char\s*\*/.test(t)) return "cstring";
|
|
469
|
+
if (/\bchar\b/.test(t) && t.includes("*")) return "buffer";
|
|
470
|
+
if (/\b(u?int8_t|unsigned\s+char|uint8_t)\b/.test(t) && t.includes("*")) return "buffer";
|
|
471
|
+
if (t.includes("*")) {
|
|
472
|
+
if (t.includes("**")) {
|
|
473
|
+
if (/\bdouble\b/.test(t)) return "pointer<pointer<double>>";
|
|
474
|
+
if (/\bfloat\b/.test(t)) return "pointer<pointer<float>>";
|
|
475
|
+
if (/\buint8_t\b/.test(t)) return "pointer<pointer<uint8_t>>";
|
|
476
|
+
if (/\bint8_t\b/.test(t)) return "pointer<pointer<int8_t>>";
|
|
477
|
+
if (/\buint16_t\b/.test(t)) return "pointer<pointer<uint16_t>>";
|
|
478
|
+
if (/\bint16_t\b/.test(t)) return "pointer<pointer<int16_t>>";
|
|
479
|
+
if (/\buint32_t\b/.test(t)) return "pointer<pointer<uint32_t>>";
|
|
480
|
+
if (/\bint32_t\b/.test(t)) return "pointer<pointer<int32_t>>";
|
|
481
|
+
if (/\buint64_t\b/.test(t)) return "pointer<pointer<uint64_t>>";
|
|
482
|
+
if (/\bint64_t\b/.test(t)) return "pointer<pointer<int64_t>>";
|
|
483
|
+
if (/\bunsigned\s+int\b/.test(t)) return "pointer<pointer<uint>>";
|
|
484
|
+
if (/\bint\b/.test(t)) return "pointer<pointer<int>>";
|
|
485
|
+
if (/\blong\b/.test(t)) return "pointer<pointer<long>>";
|
|
486
|
+
if (/\bchar\b/.test(t)) return "pointer<pointer<char>>";
|
|
487
|
+
return "pointer<pointer<void>>";
|
|
488
|
+
}
|
|
489
|
+
if (/\bdouble\b/.test(t)) return "pointer<double>";
|
|
490
|
+
if (/\bfloat\b/.test(t)) return "pointer<float>";
|
|
491
|
+
if (/\buint8_t\b/.test(t)) return "pointer<uint8_t>";
|
|
492
|
+
if (/\bint8_t\b/.test(t)) return "pointer<int8_t>";
|
|
493
|
+
if (/\buint16_t\b/.test(t)) return "pointer<uint16_t>";
|
|
494
|
+
if (/\bint16_t\b/.test(t)) return "pointer<int16_t>";
|
|
495
|
+
if (/\buint32_t\b/.test(t)) return "pointer<uint32_t>";
|
|
496
|
+
if (/\bint32_t\b/.test(t)) return "pointer<int32_t>";
|
|
497
|
+
if (/\buint64_t\b/.test(t)) return "pointer<uint64_t>";
|
|
498
|
+
if (/\bint64_t\b/.test(t)) return "pointer<int64_t>";
|
|
499
|
+
if (/\bint\b/.test(t)) return "pointer<int>";
|
|
500
|
+
}
|
|
501
|
+
if (/\buint8_t\b/.test(t)) return "uint8_t";
|
|
502
|
+
if (/\bint8_t\b/.test(t)) return "int8_t";
|
|
503
|
+
if (/\buint16_t\b/.test(t)) return "uint16_t";
|
|
504
|
+
if (/\bint16_t\b/.test(t)) return "int16_t";
|
|
505
|
+
if (/\buint32_t\b/.test(t)) return "uint32_t";
|
|
506
|
+
if (/\bint32_t\b/.test(t)) return "int32_t";
|
|
507
|
+
if (/\buint64_t\b/.test(t)) return "uint64_t";
|
|
508
|
+
if (/\bint64_t\b/.test(t)) return "int64_t";
|
|
509
|
+
if (/\bunsigned\s+int\b/.test(t)) return "uint";
|
|
510
|
+
if (/\bsize_t\b/.test(t)) return "size_t";
|
|
511
|
+
if (t.includes("int")) return "int";
|
|
512
|
+
if (t.includes("long")) return "long";
|
|
513
|
+
if (t.includes("float")) return "float";
|
|
514
|
+
if (t.includes("double")) return "double";
|
|
515
|
+
if (t.includes("char") && t.includes("*")) return "buffer";
|
|
516
|
+
if (t.includes("*")) return "pointer";
|
|
517
|
+
if (t.includes("void")) return "void";
|
|
518
|
+
return "unknown";
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// src/parser/parseRust.ts
|
|
522
|
+
function parseRustFunctions(tree) {
|
|
523
|
+
const functions = [];
|
|
524
|
+
function visit(node) {
|
|
525
|
+
if (node.type === "function_item" && node.text.includes("pub extern")) {
|
|
526
|
+
const nameNode = node.childForFieldName("name");
|
|
527
|
+
const paramsNode = node.childForFieldName("parameters");
|
|
528
|
+
const returnNode = node.childForFieldName("return_type");
|
|
529
|
+
const params = paramsNode?.children?.filter((c) => c.type === "parameter").map((p) => ({
|
|
530
|
+
name: p.childForFieldName("pattern")?.text ?? "arg",
|
|
531
|
+
type: mapRustType(p.childForFieldName("type")?.text)
|
|
532
|
+
})) ?? [];
|
|
533
|
+
functions.push({
|
|
534
|
+
name: nameNode?.text ?? "unknown",
|
|
535
|
+
returnType: mapRustType(returnNode?.text),
|
|
536
|
+
params,
|
|
537
|
+
sourceLine: node.startPosition.row + 1
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
for (const child of node.children ?? []) visit(child);
|
|
541
|
+
}
|
|
542
|
+
visit(tree.rootNode);
|
|
543
|
+
return functions;
|
|
544
|
+
}
|
|
545
|
+
function mapRustType(type) {
|
|
546
|
+
if (!type) return "void";
|
|
547
|
+
const t = type.replace(/\s+/g, " ").trim();
|
|
548
|
+
if (/\bi32\b/.test(t)) return "int";
|
|
549
|
+
if (/\bi64\b/.test(t)) return "long";
|
|
550
|
+
if (/\bu32\b/.test(t)) return "uint32_t";
|
|
551
|
+
if (/\bu64\b/.test(t)) return "uint64_t";
|
|
552
|
+
if (/\bf32\b/.test(t)) return "float";
|
|
553
|
+
if (/\bf64\b/.test(t)) return "double";
|
|
554
|
+
if (/\*const\s+i8\b/.test(t) || /\*mut\s+i8\b/.test(t)) return "char*";
|
|
555
|
+
if (t.includes("*")) {
|
|
556
|
+
if (/\*const\s+f64\b/.test(t) || /\*mut\s+f64\b/.test(t)) return "pointer<double>";
|
|
557
|
+
if (/\*const\s+f32\b/.test(t) || /\*mut\s+f32\b/.test(t)) return "pointer<float>";
|
|
558
|
+
if (/\*const\s+i32\b/.test(t) || /\*mut\s+i32\b/.test(t)) return "pointer<int>";
|
|
559
|
+
if (/\*const\s+u32\b/.test(t) || /\*mut\s+u32\b/.test(t)) return "pointer<uint32_t>";
|
|
560
|
+
if (/\*const\s+u8\b/.test(t) || /\*mut\s+u8\b/.test(t)) return "pointer<uint8_t>";
|
|
561
|
+
if (/\*const\s+i8\b/.test(t) || /\*mut\s+i8\b/.test(t)) return "pointer<int8_t>";
|
|
562
|
+
return "pointer";
|
|
563
|
+
}
|
|
564
|
+
return "unknown";
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// src/parser/validateFunctions.ts
|
|
568
|
+
function validateFunctions(funcs) {
|
|
569
|
+
return funcs.filter((fn) => {
|
|
570
|
+
if (fn.returnType === "unknown") return false;
|
|
571
|
+
if (fn.params.some((p) => p.type === "unknown")) return false;
|
|
572
|
+
return true;
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// src/parser/generateBindings.ts
|
|
577
|
+
function generateBindings(functions) {
|
|
578
|
+
const entries = functions.map((f) => [
|
|
579
|
+
f.name,
|
|
580
|
+
{
|
|
581
|
+
name: f.name,
|
|
582
|
+
returns: f.returnType,
|
|
583
|
+
args: f.params.map((p) => p.type),
|
|
584
|
+
// optional execution hints
|
|
585
|
+
mode: f.annotations?.mode,
|
|
586
|
+
cost: f.annotations?.cost
|
|
587
|
+
}
|
|
588
|
+
]);
|
|
589
|
+
return {
|
|
590
|
+
functions: Object.fromEntries(entries)
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// src/parser/index.ts
|
|
595
|
+
function parseAnnotationsForFunctions(source, funcs) {
|
|
596
|
+
const lines = source.split(/\r?\n/);
|
|
597
|
+
for (const fn of funcs) {
|
|
598
|
+
if (!fn.sourceLine) continue;
|
|
599
|
+
const idx = Math.max(0, fn.sourceLine - 1);
|
|
600
|
+
const start = Math.max(0, idx - 3);
|
|
601
|
+
const window = lines.slice(start, idx).join("\n");
|
|
602
|
+
const mode = /@async\b/.test(window) ? "async" : /@sync\b/.test(window) ? "sync" : void 0;
|
|
603
|
+
const costMatch = window.match(/@cost\s+(low|medium|high)\b/);
|
|
604
|
+
const cost = costMatch?.[1] ?? void 0;
|
|
605
|
+
if (mode || cost) {
|
|
606
|
+
fn.annotations = { ...fn.annotations ?? {}, mode, cost };
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return funcs;
|
|
610
|
+
}
|
|
611
|
+
function parseNativeSource(filePath, lang) {
|
|
612
|
+
const source = readFileSync3(filePath, "utf8");
|
|
613
|
+
const parser = createParser(lang);
|
|
614
|
+
const tree = parser.parse(source);
|
|
615
|
+
const rawFunctions = lang === "rust" ? parseRustFunctions(tree) : parseCFunctions(tree);
|
|
616
|
+
const annotated = parseAnnotationsForFunctions(source, rawFunctions);
|
|
617
|
+
const validFunctions = validateFunctions(annotated);
|
|
618
|
+
return generateBindings(validFunctions);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// src/dev/hotReload.ts
|
|
622
|
+
function log(msg) {
|
|
623
|
+
console.log(msg);
|
|
624
|
+
}
|
|
625
|
+
function fingerprintBindings(bindings) {
|
|
626
|
+
const entries = Object.entries(bindings?.functions ?? {}).map(([k, v]) => {
|
|
627
|
+
return `${k}:${v.returns}(${(v.args ?? []).join(",")})`;
|
|
628
|
+
});
|
|
629
|
+
entries.sort();
|
|
630
|
+
return entries.join("|");
|
|
631
|
+
}
|
|
632
|
+
async function loadNativeDevHot(sourcePath, opts) {
|
|
633
|
+
const enabled = process.env.RELAXNATIVE_DEV === "1";
|
|
634
|
+
if (!enabled) {
|
|
635
|
+
throw new Error("Hot reload is only available when RELAXNATIVE_DEV=1");
|
|
636
|
+
}
|
|
637
|
+
const { c, rust, platform } = detectCompilers();
|
|
638
|
+
const isolation = opts?.isolation ?? "worker";
|
|
639
|
+
let current = null;
|
|
640
|
+
const buildOnce = () => {
|
|
641
|
+
const language = detectLanguage(sourcePath);
|
|
642
|
+
const compiler = language === "rust" ? rust : c;
|
|
643
|
+
if (!compiler) throw new Error(`No compiler for ${language}`);
|
|
644
|
+
const t0 = process.hrtime.bigint();
|
|
645
|
+
const compileRes = compileWithCache(compiler, platform, {
|
|
646
|
+
sourcePath,
|
|
647
|
+
outDir: ".cache/native"
|
|
648
|
+
});
|
|
649
|
+
const bindings = parseNativeSource(sourcePath, language);
|
|
650
|
+
const api = loadFfi(compileRes.outputPath, bindings);
|
|
651
|
+
const mod = wrapFunctions(api, compileRes.outputPath, bindings, { isolation });
|
|
652
|
+
const abi = fingerprintBindings(bindings);
|
|
653
|
+
const t1 = process.hrtime.bigint();
|
|
654
|
+
const durationMs = Number(t1 - t0) / 1e6;
|
|
655
|
+
return { libPath: compileRes.outputPath, bindings, abi, api, mod, durationMs };
|
|
656
|
+
};
|
|
657
|
+
const first = buildOnce();
|
|
658
|
+
current = { ...first, mod: first.mod };
|
|
659
|
+
const stable = new Proxy(
|
|
660
|
+
{},
|
|
661
|
+
{
|
|
662
|
+
get(_t, prop) {
|
|
663
|
+
if (prop === "__isHotReloadProxy") return true;
|
|
664
|
+
if (prop === "then") return void 0;
|
|
665
|
+
const target = current?.mod;
|
|
666
|
+
if (!target) return void 0;
|
|
667
|
+
return target[prop];
|
|
668
|
+
},
|
|
669
|
+
ownKeys() {
|
|
670
|
+
return Reflect.ownKeys(current?.mod ?? {});
|
|
671
|
+
},
|
|
672
|
+
getOwnPropertyDescriptor(_t, prop) {
|
|
673
|
+
return Object.getOwnPropertyDescriptor(current?.mod ?? {}, prop);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
);
|
|
677
|
+
const watcher = watch(dirname(sourcePath), { persistent: false }, (event, filename) => {
|
|
678
|
+
if (!filename) return;
|
|
679
|
+
if (!String(filename).endsWith(sourcePath.split("/").pop() ?? "")) return;
|
|
680
|
+
try {
|
|
681
|
+
const next = buildOnce();
|
|
682
|
+
if (current && next.abi !== current.abi) {
|
|
683
|
+
log(`[relaxnative] ABI change detected in ${sourcePath}; reload requires restart`);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
current = { ...next, mod: next.mod };
|
|
687
|
+
const ms = next.durationMs;
|
|
688
|
+
log(`[relaxnative] Recompiled ${sourcePath} (${ms.toFixed(0)}ms)`);
|
|
689
|
+
opts?.onReload?.({ sourcePath, durationMs: ms });
|
|
690
|
+
} catch (err) {
|
|
691
|
+
log(`[relaxnative] Hot reload failed for ${sourcePath}: ${err?.message ?? String(err)}`);
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
return {
|
|
695
|
+
mod: stable,
|
|
696
|
+
close() {
|
|
697
|
+
watcher.close();
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// src/loader.ts
|
|
703
|
+
async function buildNative(sourcePath, options) {
|
|
704
|
+
traceInfo("loadNative.build.begin", { sourcePath });
|
|
705
|
+
const { c, rust, platform } = detectCompilers();
|
|
706
|
+
const language = detectLanguage(sourcePath);
|
|
707
|
+
const compiler = language === "rust" ? rust : c;
|
|
708
|
+
if (!compiler) {
|
|
709
|
+
traceDebug("loadNative.build.noCompiler", { sourcePath, language });
|
|
710
|
+
throw new Error(`No compiler for ${language}`);
|
|
711
|
+
}
|
|
712
|
+
const compileResult = compileWithCache(compiler, platform, {
|
|
713
|
+
sourcePath,
|
|
714
|
+
outDir: ".cache/native",
|
|
715
|
+
...options?.build ?? {}
|
|
716
|
+
});
|
|
717
|
+
traceDebug("loadNative.build.compiled", {
|
|
718
|
+
sourcePath,
|
|
719
|
+
language,
|
|
720
|
+
outputPath: compileResult.outputPath
|
|
721
|
+
});
|
|
722
|
+
const bindings = parseNativeSource(sourcePath, language);
|
|
723
|
+
const funcs = Object.values(bindings?.functions ?? {});
|
|
724
|
+
traceDebug("loadNative.bindings", {
|
|
725
|
+
sourcePath,
|
|
726
|
+
functionCount: funcs.length,
|
|
727
|
+
functions: funcs.map((b) => ({
|
|
728
|
+
name: b?.name,
|
|
729
|
+
mode: b?.mode,
|
|
730
|
+
cost: b?.cost,
|
|
731
|
+
signature: formatBindingSignature(b)
|
|
732
|
+
}))
|
|
733
|
+
});
|
|
734
|
+
const config = options?.config;
|
|
735
|
+
if (config?.functionMode) {
|
|
736
|
+
for (const [name, mode] of Object.entries(config.functionMode)) {
|
|
737
|
+
if (bindings.functions?.[name]) {
|
|
738
|
+
bindings.functions[name] = {
|
|
739
|
+
...bindings.functions[name],
|
|
740
|
+
mode
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
if (config?.defaultMode) {
|
|
746
|
+
for (const [name, binding] of Object.entries(bindings.functions ?? {})) {
|
|
747
|
+
if (!binding.mode) {
|
|
748
|
+
bindings.functions[name] = { ...binding, mode: config.defaultMode };
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
globalThis.__bindings = bindings;
|
|
753
|
+
const api = loadFfi(compileResult.outputPath, bindings);
|
|
754
|
+
traceInfo("loadNative.build.done", {
|
|
755
|
+
sourcePath,
|
|
756
|
+
libPath: compileResult.outputPath,
|
|
757
|
+
exports: Object.keys(api ?? {})
|
|
758
|
+
});
|
|
759
|
+
return { libPath: compileResult.outputPath, bindings, api };
|
|
760
|
+
}
|
|
761
|
+
async function loadNative(sourcePath, options) {
|
|
762
|
+
traceInfo("loadNative.begin", { sourcePath, isolation: options?.isolation ?? "worker" });
|
|
763
|
+
if (process.env.RELAXNATIVE_DEV === "1") {
|
|
764
|
+
if (options?.config) {
|
|
765
|
+
throw new Error("RELAXNATIVE_DEV=1: loadNative() does not support config overrides yet");
|
|
766
|
+
}
|
|
767
|
+
const handle = await loadNativeDevHot(sourcePath, { isolation: options?.isolation });
|
|
768
|
+
return handle.mod;
|
|
769
|
+
}
|
|
770
|
+
const { libPath, bindings, api } = await buildNative(sourcePath, options);
|
|
771
|
+
const isolation = options?.isolation ?? "worker";
|
|
772
|
+
const mod = wrapFunctions(api, libPath, bindings, { isolation });
|
|
773
|
+
traceInfo("loadNative.done", { sourcePath, isolation, functions: Object.keys(bindings?.functions ?? {}) });
|
|
774
|
+
return mod;
|
|
775
|
+
}
|
|
776
|
+
async function loadNativeWithBindings(sourcePath, options) {
|
|
777
|
+
const { libPath, bindings, api } = await buildNative(sourcePath, options);
|
|
778
|
+
if (options?.mutateBindings) {
|
|
779
|
+
options.mutateBindings(bindings);
|
|
780
|
+
}
|
|
781
|
+
const isolation = options?.isolation ?? "worker";
|
|
782
|
+
const mod = wrapFunctions(api, libPath, bindings, { isolation });
|
|
783
|
+
return { mod, bindings };
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// src/memory/nativeMemory.ts
|
|
787
|
+
import koffi from "koffi";
|
|
788
|
+
function allocRaw(size) {
|
|
789
|
+
if (!Number.isInteger(size) || size <= 0) {
|
|
790
|
+
throw new TypeError(`native.alloc(size): size must be a positive integer, got ${size}`);
|
|
791
|
+
}
|
|
792
|
+
const handle = koffi.alloc("char", size);
|
|
793
|
+
const ab = koffi.view(handle, size);
|
|
794
|
+
const view = new Uint8Array(ab);
|
|
795
|
+
return { handle, view };
|
|
796
|
+
}
|
|
797
|
+
function freeRaw(ptr) {
|
|
798
|
+
koffi.free(ptr);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// src/memory/index.ts
|
|
802
|
+
var memory_exports = {};
|
|
803
|
+
__export(memory_exports, {
|
|
804
|
+
InvalidFreeError: () => InvalidFreeError,
|
|
805
|
+
MemoryError: () => MemoryError,
|
|
806
|
+
NativeBuffer: () => NativeBuffer,
|
|
807
|
+
NativePointer: () => NativePointer,
|
|
808
|
+
NullPointerError: () => NullPointerError,
|
|
809
|
+
UseAfterFreeError: () => UseAfterFreeError,
|
|
810
|
+
alloc: () => alloc,
|
|
811
|
+
allocRaw: () => allocRaw,
|
|
812
|
+
free: () => free,
|
|
813
|
+
freeRaw: () => freeRaw
|
|
814
|
+
});
|
|
815
|
+
function alloc(size, opts) {
|
|
816
|
+
const allocation = allocRaw(size);
|
|
817
|
+
return new NativeBuffer(allocation, { ownership: "js", autoFree: opts?.autoFree });
|
|
818
|
+
}
|
|
819
|
+
function free(ptr) {
|
|
820
|
+
if (NativeBuffer.isNativeBuffer(ptr)) {
|
|
821
|
+
ptr.free();
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
if (NativePointer.isNativePointer(ptr)) {
|
|
825
|
+
throw new InvalidFreeError(
|
|
826
|
+
`native.free(ptr): cannot free a raw NativePointer (unknown allocator). Use a NativeBuffer allocated by native.alloc().`
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
throw new TypeError("native.free(ptr): expected NativeBuffer or NativePointer");
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
export {
|
|
833
|
+
detectCompilers,
|
|
834
|
+
getCacheRoot,
|
|
835
|
+
detectLanguage,
|
|
836
|
+
compileNative,
|
|
837
|
+
compileWithCache,
|
|
838
|
+
parseNativeSource,
|
|
839
|
+
loadNativeDevHot,
|
|
840
|
+
loadNative,
|
|
841
|
+
loadNativeWithBindings,
|
|
842
|
+
allocRaw,
|
|
843
|
+
freeRaw,
|
|
844
|
+
alloc,
|
|
845
|
+
free,
|
|
846
|
+
memory_exports
|
|
847
|
+
};
|