A self-hosting JavaScript compiler and bare-metal OS, written entirely in JavaScript. Compiles JS to native x86-64 and AArch64 machine code — no interpreter, no VM, no libc. The compiler compiles itself. The OS boots on real hardware.
import { compileToLinuxExe } from './src/compiler/pipeline.js';
import { writeFileSync } from 'fs';
writeFileSync('hello', compileToLinuxExe(`
function fib(n) {
if (n < 2) return n;
return fib(n - 1) + fib(n - 2);
}
console.log(fib(30));
`));
// chmod +x hello && ./hello → 83204055,700 lines of JS. 1,214 tests. The natively-compiled compiler produces byte-for-byte identical ELF output to the Bun-hosted compiler. The kernel boots on QEMU and VMware via GRUB, with a GUI window manager, ext2 filesystem, full TCP/IP networking, TLS 1.3 encryption, a Bourne shell, fork/exec, and a built-in Claude AI assistant with 10 agentic tools.
bun install
bun test # 1,214 tests, both architectures# x86-64 (runs natively on Linux x86-64)
bun -e '
import { compileToLinuxExe } from "./src/compiler/pipeline.js";
import { writeFileSync } from "fs";
writeFileSync("out", compileToLinuxExe("console.log(6 * 7);"));
'
chmod +x out && ./out # 42
# AArch64 (requires qemu-aarch64-static on x86 hosts)
bun -e '
import { compileToLinuxExeAArch64 } from "./src/compiler/pipeline.js";
import { writeFileSync } from "fs";
writeFileSync("out", compileToLinuxExeAArch64("console.log(6 * 7);"));
'
chmod +x out && qemu-aarch64-static ./out # 42bun run tools/build-kernel.js # build kernel image
bash tools/make-iso.sh # create GRUB bootable ISO
# QEMU (serial I/O on stdio)
qemu-system-x86_64 -kernel build/ungodly.bin \
-vga std -serial stdio -m 256 -no-reboot
# With disk + networking
qemu-system-x86_64 -cdrom build/ungodly.iso \
-vga std -serial stdio -m 256 -no-reboot \
-drive file=disk.img,format=raw,if=none,id=disk0 \
-device ahci,id=ahci -device ide-hd,drive=disk0,bus=ahci.0 \
-netdev user,id=net0 -device e1000,netdev=net0| Feature | Example | Status |
|---|---|---|
| Integer + float arithmetic | (10.5 + 5) * 2 - 7 |
done |
| Variables, scoping | let x = 42; const y = x + 1; |
done |
| All operators | a > b ? a : b, x ?? y, a?.b |
done |
| Bitwise ops | 0xFF & 0x0F, x << 3, n >>> 0 |
done |
| All loop types | while, for, for-in, for-of, do-while |
done |
| Functions + recursion | function fib(n) { ... } |
done |
| Arrow functions | const f = (x) => x * 2; |
done |
| Closures + mutable captures | let x = 0; function inc() { x++; } |
done |
| Objects + prototypes | let o = { x: 1 }; o.x = 2; |
done |
| Arrays + methods | push, map, filter, reduce, sort, find, ... |
done |
| Strings + methods | concat, slice, indexOf, charCodeAt, template literals |
done |
| Classes + inheritance | class Foo extends Bar { ... } |
done |
new + constructors |
new Point(1, 2) |
done |
this binding |
methods, constructors, arrow this capture |
done |
| Destructuring | let { a, b } = obj; let [x, y] = arr; |
done |
| Switch + break | switch (x) { case 1: ... } |
done |
| Throw / try-catch | throw new Error("msg"), try { ... } catch (e) { ... } |
done |
typeof, instanceof |
typeof x === "string" |
done |
console.log |
multi-arg, all value types | done |
| Garbage collection | Cheney semi-space, conservative stack scanning | done |
| String interning | "hello" === "hello" → pointer compare |
done |
| NaN-boxing | int32 fast path, f64 fallback, pointer-biased | done |
| AArch64 cross-compile | same JS → ARM64 binary | done |
| Optimization passes | DCE, constant folding, GVN, type specialization | done |
| Self-hosting | compiler compiles itself, byte-for-byte identical | done |
| Promises + microtasks | new Promise(...), .then(), microtask queue |
done |
- No
async/await, generators - No regular expressions
- No modules (
import/export) — worked around by file concatenation - No
Proxy,Reflect,Symbol,WeakMap/WeakSet - No getters/setters
- No private class fields
JS source
│
▼
┌──────────────────┐
│ Parser │ Recursive-descent, ES2023 subset, ESTree AST
│ parser.js │ 1,510 lines
└────────┬─────────┘
│ AST
▼
┌──────────────────┐
│ Lowering │ AST → SSA IR with basic blocks, phi nodes
│ lower.js │ NaN-boxing, closure capture analysis,
│ │ mutable capture box cells, 30+ builtin interceptions
│ │ 3,831 lines
└────────┬─────────┘
│ IRFunction (SSA, 196 opcodes)
▼
┌──────────────────┐
│ Optimization │ constFold → DCE → typeSpec → constFold → GVN → DCE
│ optimize/ │ 914 lines
└────────┬─────────┘
│ optimized IR
▼
┌──────────────────┐
│ Register Alloc │ Linear scan on SSA live intervals
│ regalloc.js │ Dataflow liveness, call-aware spilling
│ │ 397 lines
└────────┬─────────┘
│ allocation map
▼
┌──────────────────┐
│ Instruction Sel │ IR → native machine code + runtime helpers
│ x86_64/isel.js │ 6,296 lines — System V ABI, 30+ __rt_* helpers
│ aarch64/isel.js │ 4,746 lines — AAPCS64, full feature parity
└────────┬─────────┘
│ machine code bytes
▼
┌──────────────────┐
│ ELF Linker │ ELF64 builder, single LOAD segment
│ elf.js │ x86_64 + aarch64 + flat binary output
└────────┬─────────┘
│
▼
Linux ELF binary — or — Bare-metal kernel image
Pointers: 0x0000_PPPP_PPPP_PPPP (raw, zero-cost dereference)
Int32: 0xFFFF_0000_IIII_IIII (tagged, extract with mask)
Double: raw IEEE754 + 0x0002_0000_0000_0000 (bias)
Boolean: false = 0x06, true = 0x07
Null: 0x02
Undefined: 0x0A
Flat inline storage, no hash table. Up to 128 properties per object.
[GC header: u64] [num_props: u64] [key0: u64] [val0: u64] [key1: u64] [val1: u64] ...
Property names are hashed at compile time (djb2 with high bit set). Lookup is linear scan.
Cheney semi-space collector. Two 256MB semispaces allocated via mmap (14MB on bare metal). Bump allocation into the from-space; when full, copy reachable objects to the to-space and swap. Conservative stack scanning for roots. GC control block pinned in R13 (x86-64) / X25 (AArch64). Bump pointer in R15 / X28.
Header format: (totalSize << 17) | (numScannable << 5) | (tag << 1) | 1
Tags: CLOSURE=0, OBJECT=1, STRING=2, ARRAY=3, BOX=4.
[GC header: 8] [code_ptr: 8 bytes] [capture0: 8] [capture1: 8] ...
Closure pointer passed in pinned register (R14 on x86-64, X26 on AArch64). Captures accessed as [closure_reg + 8 * (1 + slot)].
Mutable captures use heap-allocated box cells (GC_TAG_BOX). A box is a 24-byte GC object: [GC header: 8] [value: 8] [pad: 8]. Both the outer scope and all capturing closures share the same box, reading and writing through it. The lowerer's pre-analysis pass (_preAnalyzeMutableCaptures) identifies which variables need boxing.
Two representations:
- Data-segment strings:
[length: u64] [bytes...]— embedded in code section, interned - Heap strings:
[GC header: u64] [length: u64] [bytes...]— allocated by concat/slice
src/
compiler/
parser/
parser.js Recursive-descent ES2023 subset parser (1,510 lines)
token.js Token types
ir/
opcodes.js 196 IR opcodes with metadata flags
types.js IR type system (i32, i64, f64, ptr, jsval, bool)
builder.js IRFunction, BasicBlock, instruction pool
lowering/
lower.js AST → IR, scope analysis, closure capture (3,831 lines)
codegen/
regalloc.js Linear scan register allocator (397 lines)
i64.js 64-bit integer arithmetic (no BigInt)
x86_64/
regs.js x86-64 register definitions, System V ABI
assembler.js x86-64 instruction encoder (904 lines)
isel.js IR → x86-64 instruction selection (6,296 lines)
aarch64/
regs.js AArch64 register definitions, AAPCS64
assembler.js AArch64 instruction encoder (716 lines)
isel.js IR → AArch64 instruction selection (4,746 lines)
optimize/
dce.js Dead code elimination
constfold.js Constant folding + branch simplification
gvn.js Global value numbering (CSE)
typespec.js Type specialization (int32 inference)
utils.js Shared optimization utilities
linker/
elf.js ELF64 builder + flat binary output
pipeline.js Main entry: source → ELF (601 lines)
kernel/ 67 files, 33,200 lines
main.js Kernel entry, event loop, window manager (1,472 lines)
drivers/
serial.js COM1 serial I/O
vga.js VGA text mode + framebuffer (800x600x32, PSF font)
keyboard.js PS/2 keyboard ISR
mouse.js PS/2 mouse driver
pci.js PCI bus enumeration
pty.js Pseudo-terminal (PTY) for terminal emulator
e1000.js Intel e1000 NIC driver
ahci.js AHCI SATA disk driver
vmware.js VMware Tools guest agent
usb_scan.js USB device enumeration
xhci_core.js xHCI host controller
xhci_keyboard.js USB keyboard via xHCI
fs/
ext2.js ext2 filesystem (read/write, inode/block alloc, symlinks)
ramdisk.js Embedded ramdisk archive
vfs.js Virtual filesystem layer (10 FD types)
pipe.js Pipe IPC (fork/exec stdin piping)
net/
net.js Ethernet/IPv4/ARP/ICMP stack
tcp.js TCP (3-way handshake, sliding window, retransmit)
udp.js UDP
dhcp.js DHCP client (auto-configure IP/gateway/DNS)
dns.js DNS resolver
http.js HTTP/1.1 client + HTTPS (TLS 1.3)
claude_api.js Claude API client (SSE streaming, agentic tool loop)
oauth.js OAuth 2.0 device code flow
crypto/
chacha.js ChaCha20-Poly1305 AEAD + X25519 key exchange
sha256.js SHA-256 + HMAC-SHA256 + HKDF
tls.js TLS 1.3 handshake + record layer (ChaCha20-Poly1305-SHA256)
lib/
json.js JSON parser (arena-based)
sse.js Server-Sent Events parser
microtask.js Microtask queue (Promise resolution)
promise.js Promise implementation
tools/
tools.js 10 Claude tools (read/write/list/edit/search/exec/glob/fetch/search_web)
syscall/
dispatch.js Linux syscall trampoline (60+ syscalls)
dir_ops.js Directory syscalls (mkdir, rmdir, rename, getdents64)
file_ops.js File syscalls (open, read, write, stat, symlinks)
socket_ops.js Socket syscalls
shell/
eval.js JIT eval (compile JS → native → execute at runtime)
commands.js Shell command dispatch (sub-dispatcher pattern)
dispatch.js Shell dispatch router
claude_cmd.js Claude AI shell integration (agentic loop)
oauth_cmd.js OAuth authentication commands
eval_expr.js Expression evaluator (arithmetic, precedence)
exec.js Process execution (fork, exec, wait)
fs_cmds.js Filesystem shell commands
net_cmds.js Network shell commands
proc_cmds.js Process shell commands
misc_cmds.js Miscellaneous commands
vfs_cmds.js VFS shell commands (fd, open, close, pipe)
vga_helpers.js VGA output helpers
sh_lexer.js Bourne shell lexer
sh_parser.js Bourne shell parser
sh_exec.js Bourne shell execution
sh_runner.js Bourne shell runner
sh_shell.js Bourne shell main loop
sh_control.js Bourne shell control flow (if/while/for)
proc/
pmm.js Physical memory manager (bitmap allocator)
vmm.js Virtual memory (page tables, 4-level paging)
process.js Process table + context switching
elf.js ELF loader (static-PIE binaries, ring 3 exec)
runtime/
value.js NaN-boxing encode/decode/type checks
tools/
build-kernel.js Boot image builder (Multiboot, ISRs, page tables, 4,439 lines)
make-iso.sh GRUB bootable ISO creator
tests/ 1,214 tests across 23 test files
The kernel boots via Multiboot1 (GRUB or QEMU -kernel), sets up 4-level paging, SSE, PIT timer at 100 Hz, PS/2 keyboard + mouse ISRs, and enters a GUI window manager with terminal emulator. The embedded compiler (19 files, ~18K lines) enables JIT evaluation of arbitrary JS at the shell prompt.
Compositing window manager with mouse-driven drag/resize, terminal emulator with PTY backend, VGA framebuffer at 800x600x32.
55+ commands including:
| Command | Description |
|---|---|
help |
List available commands |
eval <expr> |
JIT-compile and execute JavaScript |
run <path.js> |
JIT compile + execute JS file from ext2 |
exec <path> |
Execute ELF binary (fork + exec) |
sh |
Enter Bourne shell mode |
cls |
Clear screen |
mem |
Show memory usage (GC heap, PMM) |
ps |
List processes |
kill <pid> |
Kill process |
ls [path] |
List directory contents (ext2) |
cat <path> |
Print file contents |
echo text > file |
Write/append with redirection |
cp <src> <dst> |
Copy file |
mv <src> <dst> |
Rename/move file |
mkdir <path> |
Create directory |
rm <path> |
Remove file/directory |
stat <path> |
Show file metadata |
head <path> [n] |
Print first N lines |
wc <path> |
Word/line/byte count |
hexdump <path> |
Hex dump file contents |
fd |
List open file descriptors |
open/close/readfd/writefd/pipe |
Low-level FD operations |
pci |
List PCI devices |
disk |
Show disk info (AHCI) |
net |
Show network configuration |
ping <ip> |
Send ICMP echo request |
dhcp |
Auto-configure network via DHCP |
dns <hostname> |
Resolve hostname via DNS |
http <url> |
HTTP GET request |
tcp <host> <port> |
Open TCP connection |
apikey <key> |
Set Claude API key |
claude <msg> |
Send message to Claude (streaming) |
claude |
Enter interactive Claude session |
oauth github |
Authenticate via OAuth 2.0 device flow |
Built-in sh command launches a Bourne-compatible shell with:
- Pipelines (
cmd1 | cmd2 | cmd3) - Redirections (
>,>>,<) - Control flow (
if/then/elif/else/fi,while/do/done,for/in/do/done) - Variables and substitution (
$VAR,$(command)) - Quoting (
"double",'single',\escape)
60+ Linux syscalls via SYSCALL instruction, enabling execution of unmodified static-PIE musl binaries in ring 3:
- Process: fork(57), wait4(61), exit(60), exit_group(231), clone(56)
- Files: open(2), openat(257), read(0), write(1), close(3), stat(4), fstat(5), lstat(6), lseek(8), readv(19), writev(20), pread64(17), getdents64(217), readlink(89), dup(32), dup2(33), dup3(292), fcntl(72), access(21), unlink(87), pipe2(293)
- Directories: mkdir(83), rmdir(84), rename(82), mkdirat(258), unlinkat(263), renameat(264), chdir(80), getcwd(79)
- Memory: mmap(9), mprotect(10), munmap(11), brk(12)
- Sockets: socket(41), connect(42), accept(43), sendto(44), recvfrom(45), bind(49), listen(50), setsockopt(54), getsockopt(55), accept4(288)
- Epoll: epoll_create1(291), epoll_ctl(233), epoll_wait(232)
- Signals: rt_sigaction(13), rt_sigprocmask(14), sigaltstack(131)
- Time: gettimeofday(96), clock_gettime(228), nanosleep(35)
- Misc: ioctl(16), poll(7), futex(202), statx(332), getrandom(318)
QuickJS runs on unGodly (822KB static-pie musl binary — fibonacci, closures, classes, Array ops all pass).
Full TCP/IP stack implemented from scratch in JS, running on bare metal:
- Link layer: Intel e1000 NIC driver (MMIO, descriptor rings, interrupt-driven)
- Network layer: IPv4, ARP (cache + request/reply), ICMP (ping)
- Transport layer: UDP, TCP (16 connections, 3-way handshake, sliding window, retransmit, FIN teardown)
- Application layer: DHCP client, DNS resolver, HTTP/1.1 client, HTTPS (TLS 1.3), fetch_url (redirect following), web_search (DuckDuckGo)
All crypto implemented from scratch in JS with integer-only arithmetic (no floats, no BigInt):
- ChaCha20: 256-bit stream cipher (quarter-round, 20 rounds)
- Poly1305: MAC authenticator (modular arithmetic mod 2^130 - 5)
- ChaCha20-Poly1305: AEAD construction (RFC 8439)
- X25519: Elliptic-curve Diffie-Hellman key exchange
- SHA-256: Hash function + HMAC-SHA256 + HKDF-SHA256
- TLS 1.3: Full handshake (ClientHello → ServerHello → encrypted application data)
The kernel includes a Claude API client that communicates over TLS 1.3. It supports:
- Streaming responses via Server-Sent Events (non-blocking, GUI stays responsive)
- Agentic tool loop with 10 tools: read_file, write_file, list_files, edit_file, search_files, execute_command, glob, fetch_url, web_search
- fetch_url: HTTP/HTTPS GET with redirect following (301/302/303/307/308)
- web_search: DuckDuckGo search with HTML-to-text stripping
- Conversation persistence across messages
- OAuth 2.0 device code flow for authentication
- API key injection via
.envfile at build time
Compiles JS source to a runnable x86-64 Linux ELF binary. Returns Uint8Array.
Compiles JS source to a runnable AArch64 Linux ELF binary. Returns Uint8Array.
Compiles JS source to raw machine code bytes. Options:
arch:'x86_64'(default) or'aarch64'name: function name (default'<main>')mode:'program'(default) or'function'
Compiles JS source to SSA IR. Returns an IRFunction object.
Compiles to a raw flat binary (no ELF headers). Used for bare-metal targets.
Compiles JS source with bare-metal intrinsics enabled. Sets up static GC heap, disables syscalls, enables __outb/__inb/__peek/__poke/__hlt/__cli/__sti intrinsics.
- M1 — Minimal compiler (parser, IR, x86-64 codegen, ELF linker)
- M2 — JS semantics (closures, objects, strings, NaN-boxing)
- M3 — Optimization (DCE, constant folding, GVN, type specialization)
- M4 — AArch64 backend (full feature parity)
- M5 — Runtime built-ins (string/array methods, error handling)
- M6 — Classes, prototypes,
for-of, Map/Set polyfills - M7 — Self-hosting (true bootstrap — byte-for-byte identical ELF)
- M1 — Boot on QEMU, VGA framebuffer, serial I/O, PS/2 keyboard
- M2 — Interactive shell, JIT eval, expression evaluator
- M3 — Memory manager (PMM/VMM), process model, context-switch scheduler, ELF loader
- M4 — Storage (PCI enumeration, AHCI SATA driver, ext2 filesystem, ramdisk)
- M5 — Networking (e1000 NIC, IPv4/ARP/ICMP, UDP, DHCP, DNS, TCP, HTTP)
- M6 — Linux syscall compat (ring 3 exec, mmap/brk/read/write/exit, static-PIE binaries)
- M7 — USB (xHCI host controller, USB keyboard)
- M8 — HTTP client + JSON parser
- M9 — Cryptography (ChaCha20-Poly1305, X25519, SHA-256, HMAC, HKDF)
- M10 — TLS 1.3 (full handshake, encrypted record layer)
- M11 — Claude API client (SSE streaming)
- M12 — Tool system (file read/write/list/edit/search on ext2)
- M13 — Claude shell integration (agentic tool loop, conversation persistence)
- M14 — OAuth 2.0, VMware Tools, serial input, edit/search tools
- M15 — Promises, microtask queue
- M16 — Linux syscall trampoline (60+ syscalls), threading (clone/futex), sockets, epoll, fork/wait4
- M17 — QuickJS on unGodly (musl static-pie)
- M18 — Bourne shell (lexer, parser, pipelines, control flow, redirections)
- M19 — Window manager + terminal emulator + REPL
- M20 — execve + child_process.spawnSync, mkdir/rmdir/rename syscalls
- M21 — Symlinks (ext2 inode type 0xA000, lstat, path resolution)
- M22 — stdin piping via spawnSync, conservative fork stack copy
- M23 — Non-blocking Claude streaming (PID 0 polling, GUI responsive during crypto)
- M24 — HTTPS fetch for arbitrary URLs (TLS retry, X25519 precomputation, ALPN)
- M25 — Tool dispatch fix (capture pressure reduction, 21 captures)
- M26 — Advanced tools (execute_command, glob with recursive BFS)
- M27 — Web tools (fetch_url with redirects, web_search via DuckDuckGo, HTML-to-text)
MIT