cppbolt 0.1.1 → 1.0.0
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 +84 -62
- package/dist/compiler.d.ts +16 -0
- package/dist/compiler.js +92 -0
- package/dist/cppbolt.browser.js +509 -0
- package/dist/host.d.ts +45 -0
- package/dist/host.js +408 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +27 -0
- package/dist/runner.d.ts +29 -0
- package/dist/runner.js +40 -0
- package/package.json +15 -2
- package/wasm/cppbolt.wasm +0 -0
package/README.md
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
cppbolt compiles a subset of C++17 directly to WebAssembly without LLVM, Emscripten, or a native toolchain. This package contains the compiler built for `wasm32-unknown-unknown`; it exposes a C ABI and has no imports, so you can instantiate it with an empty import object in Node.js, browsers, or Web Workers.
|
|
4
4
|
|
|
5
|
-
The package contains the raw WebAssembly module with no JavaScript wrapper or CLI. You call the exports directly.
|
|
6
|
-
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
9
7
|
```sh
|
|
@@ -12,74 +10,86 @@ npm install cppbolt
|
|
|
12
10
|
|
|
13
11
|
## Usage
|
|
14
12
|
|
|
13
|
+
### Node.js: compile only
|
|
14
|
+
|
|
15
|
+
`createCompiler()` returns a compiler. `compile()` turns C++ source into a
|
|
16
|
+
complete WebAssembly module as a `Uint8Array`.
|
|
17
|
+
|
|
15
18
|
```js
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const inputPtr = compiler.alloc_buffer(input.length);
|
|
27
|
-
new Uint8Array(compiler.memory.buffer, inputPtr, input.length).set(input);
|
|
28
|
-
|
|
29
|
-
const resultPtr = compiler.compile_wasm(inputPtr, input.length);
|
|
30
|
-
compiler.dealloc_buffer(inputPtr, input.length);
|
|
31
|
-
|
|
32
|
-
if (resultPtr === 0) {
|
|
33
|
-
const length = compiler.get_error_len();
|
|
34
|
-
const bytes = new Uint8Array(compiler.memory.buffer, compiler.get_error_ptr(), length);
|
|
35
|
-
throw new Error(new TextDecoder().decode(bytes));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const length = compiler.get_result_len();
|
|
39
|
-
const output = new Uint8Array(compiler.memory.buffer, resultPtr, length).slice();
|
|
40
|
-
compiler.free_result(resultPtr, length);
|
|
41
|
-
return output;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const wasm = compile(`
|
|
45
|
-
#include <iostream>
|
|
46
|
-
|
|
47
|
-
int main() {
|
|
48
|
-
std::cout << "Hello from WebAssembly C++!" << std::endl;
|
|
49
|
-
return 0;
|
|
50
|
-
}
|
|
19
|
+
import { createCompiler } from "cppbolt";
|
|
20
|
+
|
|
21
|
+
const compiler = await createCompiler();
|
|
22
|
+
|
|
23
|
+
const wasm = compiler.compile(`
|
|
24
|
+
#include <iostream>
|
|
25
|
+
int main() {
|
|
26
|
+
std::cout << "cppbolt says hello!" << std::endl;
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
51
29
|
`);
|
|
52
30
|
```
|
|
53
31
|
|
|
54
|
-
|
|
32
|
+
### Node.js: compile and run
|
|
55
33
|
|
|
56
|
-
|
|
34
|
+
`compileAndRun()` compiles the source and immediately executes it, returning
|
|
35
|
+
its stdout and exit status.
|
|
57
36
|
|
|
58
|
-
|
|
37
|
+
```js
|
|
38
|
+
import { createCompiler, compileAndRun } from "cppbolt";
|
|
39
|
+
|
|
40
|
+
const compiler = await createCompiler();
|
|
41
|
+
|
|
42
|
+
const { stdout, exitCode } = await compileAndRun(
|
|
43
|
+
compiler,
|
|
44
|
+
`
|
|
45
|
+
#include <iostream>
|
|
46
|
+
int main() {
|
|
47
|
+
std::cout << "cppbolt says hello!" << std::endl;
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
`,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
console.log(stdout); // "cppbolt says hello!\n"
|
|
54
|
+
console.log(exitCode); // 0
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Browser (CDN `<script>` tag)
|
|
59
58
|
|
|
60
59
|
```html
|
|
61
|
-
<script
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
<script src="https://cdn.jsdelivr.net/npm/cppbolt@1.0.1/dist/cppbolt.browser.js"></script>
|
|
61
|
+
<script>
|
|
62
|
+
(async () => {
|
|
63
|
+
const compiler = await CppBolt.createCompiler();
|
|
64
|
+
const { stdout, exitCode } = await CppBolt.compileAndRun(compiler, `
|
|
65
|
+
#include <iostream>
|
|
66
|
+
int main() {
|
|
67
|
+
std::cout << "cppbolt says hello!" << std::endl;
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
70
|
+
`,
|
|
71
|
+
);
|
|
72
|
+
console.log(stdout, exitCode);
|
|
73
|
+
})();
|
|
65
74
|
</script>
|
|
66
75
|
```
|
|
67
76
|
|
|
68
|
-
`
|
|
77
|
+
`createCompiler()` resolves `wasm/cppbolt.wasm` relative to the script URL. When
|
|
78
|
+
using a bundler, import from `"cppbolt"` instead.
|
|
69
79
|
|
|
70
80
|
## Compiler API
|
|
71
81
|
|
|
72
82
|
Pointers are 32-bit offsets into the module's exported `memory`.
|
|
73
83
|
|
|
74
|
-
| Export
|
|
75
|
-
|
|
76
|
-
| `alloc_buffer`
|
|
77
|
-
| `dealloc_buffer` | `(ptr, size) -> void` | Releases an input buffer with the size passed to `alloc_buffer`.
|
|
78
|
-
| `compile_wasm`
|
|
79
|
-
| `get_result_len` | `() -> len`
|
|
80
|
-
| `free_result`
|
|
81
|
-
| `get_error_ptr`
|
|
82
|
-
| `get_error_len`
|
|
84
|
+
| Export | Signature | Description |
|
|
85
|
+
| ---------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
86
|
+
| `alloc_buffer` | `(size) -> ptr` | Allocates an uninitialized input buffer. |
|
|
87
|
+
| `dealloc_buffer` | `(ptr, size) -> void` | Releases an input buffer with the size passed to `alloc_buffer`. |
|
|
88
|
+
| `compile_wasm` | `(ptr, len) -> ptr` | Compiles UTF-8 source. Returns a pointer to the emitted module, or `0` on error. |
|
|
89
|
+
| `get_result_len` | `() -> len` | Returns the byte length of the result from the most recent `compile_wasm` call, or `0` if that call failed. |
|
|
90
|
+
| `free_result` | `(ptr, len) -> void` | Releases a result buffer returned by `compile_wasm`. |
|
|
91
|
+
| `get_error_ptr` | `() -> ptr` | Returns the address of the static error buffer. |
|
|
92
|
+
| `get_error_len` | `() -> len` | Returns the byte length of the diagnostic from the most recent failed `compile_wasm` call, or `0` if it succeeded. |
|
|
83
93
|
|
|
84
94
|
Ownership and lifetime rules:
|
|
85
95
|
|
|
@@ -104,17 +114,29 @@ See the [supported C++ subset](https://ajtimepyro.is-a.dev/cppbolt/book/user/sup
|
|
|
104
114
|
|
|
105
115
|
Emitted modules are WebAssembly binaries that import standard I/O functions from an `env` namespace. Every emitted module imports all five functions:
|
|
106
116
|
|
|
107
|
-
| Import
|
|
108
|
-
|
|
109
|
-
| `env.print_char`
|
|
110
|
-
| `env.print_int`
|
|
111
|
-
| `env.print_double` | `(f64, i32, i32, i32, i32) -> i32` | Format and write a double. Returns the character count.
|
|
112
|
-
| `env.read_char`
|
|
113
|
-
| `env.exit`
|
|
117
|
+
| Import | Signature | Purpose |
|
|
118
|
+
| ------------------ | ---------------------------------- | ------------------------------------------------------------ |
|
|
119
|
+
| `env.print_char` | `(i32) -> void` | Write one character byte to stdout. |
|
|
120
|
+
| `env.print_int` | `(i64, i32, i32, i32, i32) -> i32` | Format and write an integer. Returns the character count. |
|
|
121
|
+
| `env.print_double` | `(f64, i32, i32, i32, i32) -> i32` | Format and write a double. Returns the character count. |
|
|
122
|
+
| `env.read_char` | `() -> i32` | Read one character byte from stdin, or `-1` at end of input. |
|
|
123
|
+
| `env.exit` | `(i32) -> void` | Terminate with the given status code. |
|
|
114
124
|
|
|
115
125
|
Emitted modules export `memory`, `__stack_pointer`, and `__heap_base`, plus `main` if the program defines it. The compiler gives them a 256 MiB maximum linear memory, raised only when static data requires more.
|
|
116
126
|
|
|
117
|
-
|
|
127
|
+
### Host runtime API
|
|
128
|
+
|
|
129
|
+
The package ships a host for those imports, so you do not implement `env.*`
|
|
130
|
+
yourself.
|
|
131
|
+
|
|
132
|
+
- `createWasmHost(options?) -> WasmHost` builds the import environment. Options
|
|
133
|
+
are `stdin` (a string, or `() => number` for streaming input), `onStdout(chunk)`,
|
|
134
|
+
and `onExit(code)`. The result exposes `imports` for `WebAssembly.instantiate`
|
|
135
|
+
and `getStdout()`.
|
|
136
|
+
- `new CppBoltRunner(host).run(wasmBytes) -> Promise<RunResult>` instantiates a
|
|
137
|
+
module, calls `main` (or `_start`), and returns `{ stdout, exitCode }`. A
|
|
138
|
+
WebAssembly trap is reported as exit code 134 instead of throwing.
|
|
139
|
+
- `compileAndRun(compiler, source, options?)` compiles and runs in one call.
|
|
118
140
|
|
|
119
141
|
## Notes
|
|
120
142
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare class CompileError extends Error {
|
|
2
|
+
readonly diagnostic: string;
|
|
3
|
+
constructor(diagnostic: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class CppBoltCompiler {
|
|
6
|
+
private readonly _exports;
|
|
7
|
+
constructor(instance: WebAssembly.Instance);
|
|
8
|
+
compile(source: string): Uint8Array;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Load cppbolt.wasm and return a CppBoltCompiler.
|
|
12
|
+
*
|
|
13
|
+
* In Node.js, resolves wasm/cppbolt.wasm from the package directory.
|
|
14
|
+
* In a browser <script> tag, fetches it relative to the script's own URL.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createCompiler(): Promise<CppBoltCompiler>;
|
package/dist/compiler.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CppBoltCompiler = exports.CompileError = void 0;
|
|
4
|
+
exports.createCompiler = createCompiler;
|
|
5
|
+
function tryRequire(id) {
|
|
6
|
+
if (typeof require !== "function")
|
|
7
|
+
return null;
|
|
8
|
+
try {
|
|
9
|
+
return require(id);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const fs = tryRequire("fs");
|
|
16
|
+
const path = tryRequire("path");
|
|
17
|
+
const scriptSrc = typeof document !== "undefined" &&
|
|
18
|
+
document.currentScript instanceof HTMLScriptElement
|
|
19
|
+
? document.currentScript.src
|
|
20
|
+
: null;
|
|
21
|
+
function decodeString(memory, ptr, len) {
|
|
22
|
+
return new TextDecoder().decode(new Uint8Array(memory.buffer, ptr, len));
|
|
23
|
+
}
|
|
24
|
+
class CompileError extends Error {
|
|
25
|
+
diagnostic;
|
|
26
|
+
constructor(diagnostic) {
|
|
27
|
+
super(diagnostic);
|
|
28
|
+
this.name = "CompileError";
|
|
29
|
+
this.diagnostic = diagnostic;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.CompileError = CompileError;
|
|
33
|
+
class CppBoltCompiler {
|
|
34
|
+
_exports;
|
|
35
|
+
constructor(instance) {
|
|
36
|
+
this._exports = instance.exports;
|
|
37
|
+
}
|
|
38
|
+
compile(source) {
|
|
39
|
+
const ex = this._exports;
|
|
40
|
+
const mem = ex.memory;
|
|
41
|
+
const srcBytes = new TextEncoder().encode(source);
|
|
42
|
+
const srcPtr = ex.alloc_buffer(srcBytes.length);
|
|
43
|
+
new Uint8Array(mem.buffer, srcPtr, srcBytes.length).set(srcBytes);
|
|
44
|
+
const resultPtr = ex.compile_wasm(srcPtr, srcBytes.length);
|
|
45
|
+
ex.dealloc_buffer(srcPtr, srcBytes.length);
|
|
46
|
+
if (resultPtr === 0) {
|
|
47
|
+
throw new CompileError(decodeString(mem, ex.get_error_ptr(), ex.get_error_len()));
|
|
48
|
+
}
|
|
49
|
+
const resultLen = ex.get_result_len();
|
|
50
|
+
const output = new Uint8Array(mem.buffer, resultPtr, resultLen).slice();
|
|
51
|
+
ex.free_result(resultPtr, resultLen);
|
|
52
|
+
return output;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.CppBoltCompiler = CppBoltCompiler;
|
|
56
|
+
/**
|
|
57
|
+
* Load cppbolt.wasm and return a CppBoltCompiler.
|
|
58
|
+
*
|
|
59
|
+
* In Node.js, resolves wasm/cppbolt.wasm from the package directory.
|
|
60
|
+
* In a browser <script> tag, fetches it relative to the script's own URL.
|
|
61
|
+
*/
|
|
62
|
+
async function createCompiler() {
|
|
63
|
+
if (fs && path && typeof __dirname === "string") {
|
|
64
|
+
const localPath = path.join(__dirname, "..", "wasm", "cppbolt.wasm");
|
|
65
|
+
if (fs.existsSync(localPath)) {
|
|
66
|
+
return new CppBoltCompiler(await loadFromBytes(fs.readFileSync(localPath)));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const wasmUrl = scriptSrc
|
|
70
|
+
? new URL("../wasm/cppbolt.wasm", scriptSrc).href
|
|
71
|
+
: null;
|
|
72
|
+
if (wasmUrl) {
|
|
73
|
+
return new CppBoltCompiler(await loadFromResponse(await fetch(wasmUrl)));
|
|
74
|
+
}
|
|
75
|
+
throw new Error("cppbolt: could not locate wasm binary. " +
|
|
76
|
+
"Load this script from a URL (CDN / local server) or use the Node.js package.");
|
|
77
|
+
}
|
|
78
|
+
async function loadFromResponse(response) {
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
throw new Error(`cppbolt: failed to fetch wasm (HTTP ${response.status})`);
|
|
81
|
+
}
|
|
82
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
83
|
+
if (contentType.includes("application/wasm")) {
|
|
84
|
+
const { instance } = await WebAssembly.instantiateStreaming(response, {});
|
|
85
|
+
return instance;
|
|
86
|
+
}
|
|
87
|
+
return loadFromBytes(await response.arrayBuffer());
|
|
88
|
+
}
|
|
89
|
+
async function loadFromBytes(bytes) {
|
|
90
|
+
const { instance } = await WebAssembly.instantiate(bytes, {});
|
|
91
|
+
return instance;
|
|
92
|
+
}
|