Pangram verdict · v3.3
We believe that this document is fully AI-generated
AI likelihood · overall
AIArticle text · 756 words · 4 segments analyzed
Kotlin → WebAssembly · a compiler written in CA Kotlin compilerthat runs in a browser tab. minikotlin is written from scratch in C and emits WebAssembly GC bytecode by hand — no JVM, no LLVM, no Binaryen, no Gradle. The compiler is itself compiled to WASM, so .kt source goes in and a running .wasm module comes out, entirely in the tab. backendWASM-GCstructs · call_ref · EH servernoneruns client-side end-to-end tests366frontend: 657 runtime deps0nothing installed
greeter — minikotlin Studio // Main.kt + Greeter.kt compile as one unit fun main() { val g = Greeter("WebAssembly") println(g.greet()) (1..3).forEach { println("tick $it") } } build 2 .kt → main.wasm · ok, 41ms Hello, WebAssembly tick 1 tick 2 tick 3
the pipeline .kt→ lex→ parse→ sema→ HIR→ MIR→ WASM-GC→ run
01One pass, all the way down to bytecode. No intermediate VM, no external backend. The frontend — lexer, parser, semantic analysis (it’s called mkf) — hands off to two of its own IRs before writing WASM-GC by hand.
input Kotlin source Multiple .kt files, compiled as one unit so they can see each other. frontend · mkf lex · parse · sema Names, types and smart-casts resolved. 657 frontend tests. high IR HIR A desugared, typed tree that still sits close to the language.
mid IR MIR Lowered to ops, locals, struct layouts and vtables. codegen WASM-GC Bytecode emitted directly. No LLVM, no Binaryen in the loop. output main.wasm Instantiated and run in the same browser tab. The compiler ships as WASM itself, so it runs where your code runs — no toolchain to install.
02The Kotlin it speaks today. Not a token subset. These are lowered properly onto the WASM-GC type system — each one has end-to-end tests behind it.
Classes & objectsobject model Inheritance (open/override), interfaces with default methods, data class with generated equals/hashCode/copy, enum, and named, companion & anonymous object expressions. Sealed & smart-castscontrol flow sealed hierarchies with exhaustive when, is checks compiled to ref.test, and flow-sensitive smart-casting that holds across branches. Null safetytypes Nullable types end to end — ?. safe calls, ?: elvis and !! assertions — including nullable primitives, boxed through Any. Genericstypes Type parameters on functions and classes — fun <T> id(x: T): T — lowered over a boxed Any representation. Operators & extensionsergonomics Operator overloading (plus, get, …) dispatched to the LHS class, extension functions in their own namespace, and custom accessors with a backing field. Coroutinesnon-blocking launch, delay and coroutineScope — real suspension compiled as CPS over closures, with no Asyncify, no JSPI and no threads. Standard libraryhand-written String/Char operations, list higher-order functions (map/filter/forEach…), kotlin.math, and the scope functions let/apply/run/also/with.
03How a Kotlin idea becomes a WASM instruction. The lowering is the interesting part of any compiler.
Four real ones — each maps a language construct onto a concrete WASM-GC mechanism, written by hand.
L.01 class instance → struct.new Every class becomes a GC struct type; properties are real struct fields. Allocation is struct.new, not a hand-rolled heap of bytes. L.02 virtual call → call_ref Open and overridden methods go through a per-class vtable. A virtual call is a function-reference load followed by call_ref — true dynamic dispatch. L.03 type check → ref.test An is check and a when (x) { is T -> } arm compile to ref.test, and the narrowed value is reused through a ref.cast — smart-casting for free. L.04 coroutine → CPS closure A suspension point splits the function at the seam and captures the rest as a continuation. A bare delay hands a token to the host and resumes from setTimeout — genuinely off the stack.
04A specimen, compiled and run. Everything below is supported Kotlin. The Studio highlights it with the compiler’s own lexer, then runs the resulting WASM in place.
Race.kt import kotlinx.coroutines.*
sealed class Lane(val id: Int) class Fast : Lane(1) class Slow : Lane(2)
fun Lane.pace(): Long = when (this) { is Fast -> 120 is Slow -> 300 }
fun main() = runBlocking { val lanes = listOf(Fast(), Slow()) coroutineScope { lanes.forEach { lane -> launch { delay(lane.pace()) println("lane ${lane.id} in") } } } println("race over") }
Two coroutines, actually racing. Each launch suspends at its delay and yields. The faster lane resumes first; coroutineScope waits for both children before the last line runs. No blocking and no Asyncify — the suspension is compiled into continuation closures.
The sealed Lane, the when (this) { is … } dispatch and the Lane.pace() extension are all lowered for real, not interpreted. > lane 1 in > lane 2 in > race over