V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
// Minimal browser WASI shim. Loaded by index.html, which sets
|
|
|
|
|
// window.CRAFTER_WASM_URL before this script runs so a single runtime.js
|
|
|
|
|
// can serve any output name. Most syscalls return 0 — enough to host a
|
|
|
|
|
// hello-world that uses fd_write (stdout) and the args/environ probes
|
|
|
|
|
// libc invokes during startup. Extend as needed.
|
|
|
|
|
|
|
|
|
|
const textEncoder = new TextEncoder();
|
|
|
|
|
|
|
|
|
|
class Wasi {
|
|
|
|
|
#encodedStdin;
|
|
|
|
|
#envEncodedStrings;
|
|
|
|
|
#argEncodedStrings;
|
|
|
|
|
instance;
|
2026-05-18 05:23:11 +02:00
|
|
|
// VFS: file basename → Uint8Array, populated by EnableWasiBrowserRuntime's
|
|
|
|
|
// preload step before instantiateStreaming runs. Used by path_open / fd_read
|
|
|
|
|
// / fd_seek / fd_tell / fd_filestat_get / fd_close so wasi-libc's
|
|
|
|
|
// std::ifstream and friends actually work in the browser.
|
|
|
|
|
vfs;
|
|
|
|
|
#fdTable;
|
|
|
|
|
#nextFd;
|
|
|
|
|
#decoder;
|
V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
|
2026-05-18 05:23:11 +02:00
|
|
|
constructor({ env, stdin, args, vfs }) {
|
V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
this.#encodedStdin = textEncoder.encode(stdin);
|
|
|
|
|
const envStrings = Object.entries(env).map(([k, v]) => `${k}=${v}`);
|
|
|
|
|
this.#envEncodedStrings = envStrings.map(s => textEncoder.encode(s + "\0"));
|
|
|
|
|
this.#argEncodedStrings = args.map(s => textEncoder.encode(s + "\0"));
|
2026-05-18 05:23:11 +02:00
|
|
|
this.vfs = vfs || new Map();
|
|
|
|
|
this.#fdTable = new Map();
|
|
|
|
|
this.#nextFd = 100; // stay clear of stdio
|
|
|
|
|
this.#decoder = new TextDecoder();
|
V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
this.bind();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bind() {
|
2026-05-18 05:23:11 +02:00
|
|
|
// wasi imports are looked up as plain function references at
|
|
|
|
|
// instantiate time, so any method that touches `this` MUST be
|
|
|
|
|
// explicitly bound here. Anything purely no-op (returning 0) can
|
|
|
|
|
// stay unbound.
|
|
|
|
|
const m = [
|
|
|
|
|
"args_get", "args_sizes_get",
|
|
|
|
|
"environ_get", "environ_sizes_get",
|
|
|
|
|
"fd_read", "fd_write", "fd_close", "fd_seek", "fd_tell",
|
|
|
|
|
"fd_filestat_get", "fd_fdstat_get",
|
|
|
|
|
"fd_prestat_get", "fd_prestat_dir_name",
|
|
|
|
|
"path_open",
|
|
|
|
|
];
|
|
|
|
|
for (const name of m) this[name] = this[name].bind(this);
|
V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
args_sizes_get(argCountPtr, argBufferSizePtr) {
|
|
|
|
|
const argByteLength = this.#argEncodedStrings.reduce((sum, val) => sum + val.byteLength, 0);
|
|
|
|
|
const countPointerBuffer = new Uint32Array(this.instance.exports.memory.buffer, argCountPtr, 1);
|
|
|
|
|
const sizePointerBuffer = new Uint32Array(this.instance.exports.memory.buffer, argBufferSizePtr, 1);
|
|
|
|
|
countPointerBuffer[0] = this.#argEncodedStrings.length;
|
|
|
|
|
sizePointerBuffer[0] = argByteLength;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
args_get(argsPtr, argBufferPtr) {
|
|
|
|
|
const argsByteLength = this.#argEncodedStrings.reduce((sum, val) => sum + val.byteLength, 0);
|
|
|
|
|
const argsPointerBuffer = new Uint32Array(this.instance.exports.memory.buffer, argsPtr, this.#argEncodedStrings.length);
|
|
|
|
|
const argsBuffer = new Uint8Array(this.instance.exports.memory.buffer, argBufferPtr, argsByteLength);
|
|
|
|
|
|
|
|
|
|
let pointerOffset = 0;
|
|
|
|
|
for (let i = 0; i < this.#argEncodedStrings.length; i++) {
|
|
|
|
|
argsPointerBuffer[i] = argBufferPtr + pointerOffset;
|
|
|
|
|
argsBuffer.set(this.#argEncodedStrings[i], pointerOffset);
|
|
|
|
|
pointerOffset += this.#argEncodedStrings[i].byteLength;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
fd_write(fd, iovsPtr, iovsLength, bytesWrittenPtr) {
|
|
|
|
|
const iovs = new Uint32Array(this.instance.exports.memory.buffer, iovsPtr, iovsLength * 2);
|
|
|
|
|
if (fd === 1 || fd === 2) {
|
|
|
|
|
let text = "";
|
|
|
|
|
let totalBytesWritten = 0;
|
|
|
|
|
const decoder = new TextDecoder();
|
|
|
|
|
for (let i = 0; i < iovsLength * 2; i += 2) {
|
|
|
|
|
const offset = iovs[i];
|
|
|
|
|
const length = iovs[i + 1];
|
|
|
|
|
text += decoder.decode(new Int8Array(this.instance.exports.memory.buffer, offset, length));
|
|
|
|
|
totalBytesWritten += length;
|
|
|
|
|
}
|
|
|
|
|
const dataView = new DataView(this.instance.exports.memory.buffer);
|
|
|
|
|
dataView.setInt32(bytesWrittenPtr, totalBytesWritten, true);
|
|
|
|
|
(fd === 2 ? console.error : console.log)(text);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
fd_read(fd, iovsPtr, iovsLength, bytesReadPtr) {
|
|
|
|
|
const memory = new Uint8Array(this.instance.exports.memory.buffer);
|
|
|
|
|
const iovs = new Uint32Array(this.instance.exports.memory.buffer, iovsPtr, iovsLength * 2);
|
2026-05-18 05:23:11 +02:00
|
|
|
const dataView = new DataView(this.instance.exports.memory.buffer);
|
V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
let totalBytesRead = 0;
|
|
|
|
|
if (fd === 0) {
|
|
|
|
|
for (let i = 0; i < iovsLength * 2; i += 2) {
|
|
|
|
|
const offset = iovs[i];
|
|
|
|
|
const length = iovs[i + 1];
|
|
|
|
|
const chunk = this.#encodedStdin.slice(0, length);
|
|
|
|
|
this.#encodedStdin = this.#encodedStdin.slice(length);
|
|
|
|
|
memory.set(chunk, offset);
|
|
|
|
|
totalBytesRead += chunk.byteLength;
|
|
|
|
|
if (this.#encodedStdin.length === 0) break;
|
|
|
|
|
}
|
|
|
|
|
dataView.setInt32(bytesReadPtr, totalBytesRead, true);
|
2026-05-18 05:23:11 +02:00
|
|
|
return 0;
|
V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
}
|
2026-05-18 05:23:11 +02:00
|
|
|
const entry = this.#fdTable.get(fd);
|
|
|
|
|
if (!entry) {
|
|
|
|
|
dataView.setInt32(bytesReadPtr, 0, true);
|
|
|
|
|
return 8; // EBADF
|
|
|
|
|
}
|
|
|
|
|
const file = this.vfs.get(entry.name);
|
|
|
|
|
for (let i = 0; i < iovsLength * 2; i += 2) {
|
|
|
|
|
const offset = iovs[i];
|
|
|
|
|
const length = iovs[i + 1];
|
|
|
|
|
const remaining = file.byteLength - entry.offset;
|
|
|
|
|
if (remaining <= 0) break;
|
|
|
|
|
const n = Math.min(length, remaining);
|
|
|
|
|
memory.set(file.subarray(entry.offset, entry.offset + n), offset);
|
|
|
|
|
entry.offset += n;
|
|
|
|
|
totalBytesRead += n;
|
|
|
|
|
if (n < length) break;
|
|
|
|
|
}
|
|
|
|
|
dataView.setInt32(bytesReadPtr, totalBytesRead, true);
|
V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
fd_advise() { return 0; }
|
2026-05-18 05:23:11 +02:00
|
|
|
fd_close(fd) {
|
|
|
|
|
this.#fdTable.delete(fd);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
fd_fdstat_get(fd, statPtr) {
|
|
|
|
|
// 24 bytes: filetype(1) + flags(2) + padding + rights_base(8) + rights_inheriting(8).
|
|
|
|
|
const dv = new DataView(this.instance.exports.memory.buffer);
|
|
|
|
|
const isFile = this.#fdTable.has(fd);
|
|
|
|
|
const filetype = isFile ? 4 : 0; // 4 = regular_file
|
|
|
|
|
dv.setUint8(statPtr + 0, filetype);
|
|
|
|
|
dv.setUint16(statPtr + 2, 0, true);
|
|
|
|
|
dv.setBigUint64(statPtr + 8, 0xFFFFFFFFFFFFFFFFn, true);
|
|
|
|
|
dv.setBigUint64(statPtr + 16, 0xFFFFFFFFFFFFFFFFn, true);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
// wasi-libc walks preopens starting at fd=3 until fd_prestat_get returns
|
|
|
|
|
// EBADF, then resolves every relative open against one of the discovered
|
|
|
|
|
// dirs. Without at least one preopen, std::ifstream et al can't open any
|
|
|
|
|
// path. We expose a single "/" preopen on fd=3, rooted at the VFS map.
|
|
|
|
|
fd_prestat_get(fd, prestatPtr) {
|
|
|
|
|
const dv = new DataView(this.instance.exports.memory.buffer);
|
|
|
|
|
if (fd === 3) {
|
|
|
|
|
// prestat_t: tag(u8) + padding to 4 + u.dir.pr_name_len(u32). 8 bytes.
|
|
|
|
|
dv.setUint8(prestatPtr + 0, 0); // tag = preopentype_dir
|
|
|
|
|
dv.setUint32(prestatPtr + 4, 1, true); // strlen("/")
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return 8; // EBADF
|
|
|
|
|
}
|
|
|
|
|
fd_prestat_dir_name(fd, pathPtr, pathLen) {
|
|
|
|
|
if (fd !== 3) return 8;
|
|
|
|
|
const memory = new Uint8Array(this.instance.exports.memory.buffer);
|
|
|
|
|
const name = textEncoder.encode("/");
|
|
|
|
|
memory.set(name.subarray(0, Math.min(name.byteLength, pathLen)), pathPtr);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
clock_res_get() { return 0; }
|
|
|
|
|
clock_time_get() { return 0; }
|
2026-05-18 05:23:11 +02:00
|
|
|
fd_seek(fd, offsetLow, whence, newOffsetPtr) {
|
|
|
|
|
// offsetLow is a BigInt under wasi-snapshot-preview1's two-i32 ABI?
|
|
|
|
|
// No — wasi-snapshot-preview1 fd_seek's signature is (fd, filedelta:i64,
|
|
|
|
|
// whence:u8, newoffset:ptr<filesize>). JS receives the i64 as BigInt.
|
|
|
|
|
const entry = this.#fdTable.get(fd);
|
|
|
|
|
const dv = new DataView(this.instance.exports.memory.buffer);
|
|
|
|
|
if (!entry) {
|
|
|
|
|
dv.setBigUint64(newOffsetPtr, 0n, true);
|
|
|
|
|
return 8;
|
|
|
|
|
}
|
|
|
|
|
const delta = typeof offsetLow === "bigint" ? Number(offsetLow) : Number(offsetLow);
|
|
|
|
|
const file = this.vfs.get(entry.name);
|
|
|
|
|
let newOff;
|
|
|
|
|
switch (whence) {
|
|
|
|
|
case 0: newOff = delta; break; // SEEK_SET
|
|
|
|
|
case 1: newOff = entry.offset + delta; break; // SEEK_CUR
|
|
|
|
|
case 2: newOff = file.byteLength + delta; break; // SEEK_END
|
|
|
|
|
default: return 28; // EINVAL
|
|
|
|
|
}
|
|
|
|
|
if (newOff < 0) newOff = 0;
|
|
|
|
|
if (newOff > file.byteLength) newOff = file.byteLength;
|
|
|
|
|
entry.offset = newOff;
|
|
|
|
|
dv.setBigUint64(newOffsetPtr, BigInt(newOff), true);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
fd_tell(fd, offsetPtr) {
|
|
|
|
|
const entry = this.#fdTable.get(fd);
|
|
|
|
|
const dv = new DataView(this.instance.exports.memory.buffer);
|
|
|
|
|
if (!entry) { dv.setBigUint64(offsetPtr, 0n, true); return 8; }
|
|
|
|
|
dv.setBigUint64(offsetPtr, BigInt(entry.offset), true);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
fd_allocate() { return 0; }
|
|
|
|
|
fd_datasync() { return 0; }
|
|
|
|
|
fd_fdstat_set_flags() { return 0; }
|
|
|
|
|
fd_fdstat_set_rights() { return 0; }
|
2026-05-18 05:23:11 +02:00
|
|
|
fd_filestat_get(fd, statPtr) {
|
|
|
|
|
// Layout: dev(8) ino(8) filetype(1) +pad nlink(8) size(8) atim(8) mtim(8) ctim(8) = 64 bytes.
|
|
|
|
|
const dv = new DataView(this.instance.exports.memory.buffer);
|
|
|
|
|
const entry = this.#fdTable.get(fd);
|
|
|
|
|
if (!entry) return 8;
|
|
|
|
|
const file = this.vfs.get(entry.name);
|
|
|
|
|
dv.setBigUint64(statPtr + 0, 0n, true);
|
|
|
|
|
dv.setBigUint64(statPtr + 8, 0n, true);
|
|
|
|
|
dv.setUint8(statPtr + 16, 4); // filetype = regular_file
|
|
|
|
|
dv.setBigUint64(statPtr + 24, 1n, true);
|
|
|
|
|
dv.setBigUint64(statPtr + 32, BigInt(file.byteLength), true);
|
|
|
|
|
dv.setBigUint64(statPtr + 40, 0n, true);
|
|
|
|
|
dv.setBigUint64(statPtr + 48, 0n, true);
|
|
|
|
|
dv.setBigUint64(statPtr + 56, 0n, true);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
fd_filestat_set_size() { return 0; }
|
|
|
|
|
fd_filestat_set_times() { return 0; }
|
|
|
|
|
fd_pread() { return 0; }
|
|
|
|
|
fd_pwrite() { return 0; }
|
|
|
|
|
fd_readdir() { return 0; }
|
|
|
|
|
fd_renumber() { return 0; }
|
|
|
|
|
fd_sync() { return 0; }
|
|
|
|
|
fd_tell() { return 0; }
|
|
|
|
|
path_create_directory() { return 0; }
|
|
|
|
|
path_filestat_get() { return 0; }
|
|
|
|
|
path_filestat_set_times() { return 0; }
|
|
|
|
|
path_link() { return 0; }
|
2026-05-18 05:23:11 +02:00
|
|
|
path_open(_dirfd, _dirflags, pathPtr, pathLen, _oflags, _rightsBase, _rightsInh, _fdflags, openedFdPtr) {
|
|
|
|
|
const memory = new Uint8Array(this.instance.exports.memory.buffer);
|
|
|
|
|
const dv = new DataView(this.instance.exports.memory.buffer);
|
|
|
|
|
const raw = this.#decoder.decode(memory.subarray(pathPtr, pathPtr + pathLen));
|
|
|
|
|
// wasi-libc may pass paths like "./font.ttf" or "/font.ttf" or "font.ttf"
|
|
|
|
|
// depending on how std::filesystem::path was constructed. Reduce to basename.
|
|
|
|
|
const base = raw.split(/[\\/]/).filter(Boolean).pop() || raw;
|
|
|
|
|
if (!this.vfs.has(base)) {
|
|
|
|
|
dv.setInt32(openedFdPtr, -1, true);
|
|
|
|
|
return 44; // ENOENT
|
|
|
|
|
}
|
|
|
|
|
const fd = this.#nextFd++;
|
|
|
|
|
this.#fdTable.set(fd, { name: base, offset: 0 });
|
|
|
|
|
dv.setInt32(openedFdPtr, fd, true);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
path_readlink() { return 0; }
|
|
|
|
|
path_remove_directory() { return 0; }
|
|
|
|
|
path_rename() { return 0; }
|
|
|
|
|
path_symlink() { return 0; }
|
|
|
|
|
path_unlink_file() { return 0; }
|
|
|
|
|
poll_oneoff() { return 0; }
|
|
|
|
|
sched_yield() { return 0; }
|
|
|
|
|
random_get() { return 0; }
|
|
|
|
|
sock_accept() { return 0; }
|
|
|
|
|
sock_recv() { return 0; }
|
|
|
|
|
sock_send() { return 0; }
|
|
|
|
|
sock_shutdown() { return 0; }
|
|
|
|
|
environ_get(environPtr, environBufferPtr) {
|
|
|
|
|
const envByteLength = this.#envEncodedStrings.reduce((sum, val) => sum + val.byteLength, 0);
|
|
|
|
|
const environsPointerBuffer = new Uint32Array(this.instance.exports.memory.buffer, environPtr, this.#envEncodedStrings.length);
|
|
|
|
|
const environsBuffer = new Uint8Array(this.instance.exports.memory.buffer, environBufferPtr, envByteLength);
|
|
|
|
|
|
|
|
|
|
let pointerOffset = 0;
|
|
|
|
|
for (let i = 0; i < this.#envEncodedStrings.length; i++) {
|
|
|
|
|
environsPointerBuffer[i] = environBufferPtr + pointerOffset;
|
|
|
|
|
environsBuffer.set(this.#envEncodedStrings[i], pointerOffset);
|
|
|
|
|
pointerOffset += this.#envEncodedStrings[i].byteLength;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
environ_sizes_get(environCountPtr, environBufferSizePtr) {
|
|
|
|
|
const envByteLength = this.#envEncodedStrings.reduce((sum, val) => sum + val.byteLength, 0);
|
|
|
|
|
const countPointerBuffer = new Uint32Array(this.instance.exports.memory.buffer, environCountPtr, 1);
|
|
|
|
|
const sizePointerBuffer = new Uint32Array(this.instance.exports.memory.buffer, environBufferSizePtr, 1);
|
|
|
|
|
countPointerBuffer[0] = this.#envEncodedStrings.length;
|
|
|
|
|
sizePointerBuffer[0] = envByteLength;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
proc_exit(code) {
|
2026-05-18 05:23:11 +02:00
|
|
|
// Throw a sentinel so the wasm stack unwinds back to runtime.js
|
|
|
|
|
// WITHOUT executing the `unreachable` instruction wasi-libc emits
|
|
|
|
|
// after __wasi_proc_exit (which is declared noreturn). Trapping
|
|
|
|
|
// there would mark the wasm call as crashed and may interrupt any
|
|
|
|
|
// browser-side render loop that called _Exit on purpose (e.g.
|
|
|
|
|
// Crafter::Window::StartSync on DOM, which hands the loop to rAF
|
|
|
|
|
// and then exits main without running static destructors).
|
V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
console.log(`[wasi] proc_exit(${code})`);
|
2026-05-18 05:23:11 +02:00
|
|
|
const e = new Error(`wasi proc_exit(${code})`);
|
|
|
|
|
e.crafterWasiExit = code;
|
|
|
|
|
throw e;
|
V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const wasmUrl = window.CRAFTER_WASM_URL;
|
|
|
|
|
if (!wasmUrl) {
|
|
|
|
|
throw new Error("runtime.js: window.CRAFTER_WASM_URL is not set (set it in index.html before loading runtime.js)");
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-18 05:23:11 +02:00
|
|
|
// Preload asset files listed in files.json (emitted by
|
|
|
|
|
// EnableWasiBrowserRuntime) into an in-memory VFS so wasi-libc's file
|
|
|
|
|
// syscalls work against them. Browser builds otherwise can't open assets
|
|
|
|
|
// shipped alongside the .wasm — sync XHR is too deprecated to rely on.
|
2026-05-19 03:28:27 +02:00
|
|
|
//
|
|
|
|
|
// Manifest entries are relative paths under the bin dir (the layout
|
|
|
|
|
// `cfg.assets` produces — e.g. "assets/Inter.ttf",
|
|
|
|
|
// "mods/3DForts_Base/foo.cmesh"). We fetch each at its full path but
|
|
|
|
|
// key the VFS by basename so `path_open`'s basename-reduction can find
|
|
|
|
|
// it regardless of the C++ side's cwd or prefix.
|
2026-05-18 05:23:11 +02:00
|
|
|
const vfs = new Map();
|
|
|
|
|
try {
|
|
|
|
|
const manifestResp = await fetch("files.json");
|
|
|
|
|
if (manifestResp.ok) {
|
|
|
|
|
const names = await manifestResp.json();
|
|
|
|
|
await Promise.all(names.map(async (name) => {
|
|
|
|
|
const r = await fetch(name);
|
2026-05-19 03:28:27 +02:00
|
|
|
if (!r.ok) {
|
|
|
|
|
console.warn(`[wasi] failed to preload ${name}: HTTP ${r.status}`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const base = name.split(/[\\/]/).filter(Boolean).pop() || name;
|
|
|
|
|
vfs.set(base, new Uint8Array(await r.arrayBuffer()));
|
2026-05-18 05:23:11 +02:00
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.warn("[wasi] no files.json manifest (or fetch failed); file I/O syscalls will return ENOENT:", e.message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const wasi = new Wasi({ stdin: "", env: {}, args: [], vfs });
|
|
|
|
|
|
|
|
|
|
// Modules that need env imports (Crafter.Graphics DOM mode, etc.) ship a
|
|
|
|
|
// co-located env.js that sets `window.crafter_webbuild_env`. The
|
|
|
|
|
// `EnableWasiBrowserRuntime` injects each one as a regular <script> tag
|
|
|
|
|
// before this module script, so by the time we instantiate they've all
|
|
|
|
|
// populated the global. Pure-WASI builds leave the global undefined and
|
|
|
|
|
// we pass an empty object — extra imports on the JS side are harmless
|
|
|
|
|
// (WebAssembly only complains about *missing* declared imports).
|
|
|
|
|
if (!window.crafter_webbuild_env) {
|
|
|
|
|
window.crafter_webbuild_env = {};
|
|
|
|
|
}
|
|
|
|
|
// Some env.js bridges (notably the Crafter.CppDOM one) expect an indirect
|
|
|
|
|
// function table they can push function references onto. Set it up so we
|
|
|
|
|
// don't break compatibility with that pattern.
|
|
|
|
|
window.crafter_webbuild_env.table = new WebAssembly.Table({ initial: 4, element: "anyfunc" });
|
|
|
|
|
|
|
|
|
|
// Async env bridges (notably the WebGPU one in Crafter.Graphics) set
|
|
|
|
|
// `window.crafter_webbuild_env_ready` to a Promise that resolves once
|
|
|
|
|
// their init (adapter/device requests, pipeline compilation, etc.) is
|
|
|
|
|
// finished AND env.* has been swapped from sync stubs to real impls.
|
|
|
|
|
// We MUST await this BEFORE WebAssembly.instantiateStreaming — the wasm
|
|
|
|
|
// snapshots its import functions at instantiate time, so stubs captured
|
|
|
|
|
// then are baked in. Sibling <script type="module"> top-level awaits
|
|
|
|
|
// aren't reliably serialized cross-browser, so we can't rely on dom-*
|
|
|
|
|
// scripts' own TLAs to block us here.
|
|
|
|
|
if (window.crafter_webbuild_env_ready) {
|
|
|
|
|
try {
|
|
|
|
|
await window.crafter_webbuild_env_ready;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("[crafter] env bridge init failed:", e);
|
|
|
|
|
// Continue to instantiation anyway — the bridge's stubs throw a
|
|
|
|
|
// clearer error from their own call sites.
|
|
|
|
|
}
|
|
|
|
|
}
|
V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
|
|
|
|
|
const { instance } = await WebAssembly.instantiateStreaming(fetch(wasmUrl), {
|
|
|
|
|
wasi_snapshot_preview1: wasi,
|
2026-05-18 05:23:11 +02:00
|
|
|
env: window.crafter_webbuild_env,
|
V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
});
|
|
|
|
|
wasi.instance = instance;
|
2026-05-18 05:23:11 +02:00
|
|
|
// Alias under both names — `crafter_wasi` is the canonical, but the older
|
|
|
|
|
// CppDOM env.js reads from `crafter_webbuild_wasi`. Keep both wired so
|
|
|
|
|
// either generation of env.js works against this runtime.
|
V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
window.crafter_wasi = wasi;
|
2026-05-18 05:23:11 +02:00
|
|
|
window.crafter_webbuild_wasi = wasi;
|
V2: WASI, -r flag, CI pipeline, examples & tests cleanup
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
-D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
set in the templated index.html so a single shim handles any output name
-r run flag in the CLI: build then exec the artifact (host targets only;
rejects libraries; auto .exe/.wasm extension handling)
CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master
mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
'd' to lib names and breaks linking); cross-compile cmake flags
(CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread
GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.
Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
|
|
|
|
2026-05-18 05:23:11 +02:00
|
|
|
try {
|
|
|
|
|
instance.exports._start();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
if (e && typeof e.crafterWasiExit === "number") {
|
|
|
|
|
// Clean exit from std::_Exit / __wasi_proc_exit. The wasm instance
|
|
|
|
|
// remains valid; any rAF callback registered before exit keeps
|
|
|
|
|
// running. (See Crafter::Window::StartSync DOM impl.)
|
|
|
|
|
} else {
|
|
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
}
|