Run C applications/libraries on the JVM by translating MIR to Java source.
Prior art exists:
- NestedVM / Cibyl
- LLJVM-translator (based on LLJVM by David Roberts)
It should be possible to do the same thing with MIR but with a much smaller footprint.
The idea here is to reuse MIR for a much smaller footprint and generate Java sources (for easy experimentation and portability). Generating Java also keeps the door open to C# / Dart later.
C → MIR → Java sources → JVM
Because there is no goto instruction at source level in Java, control flow is expressed with a loop + switch/case over a label
while (true) {
switch (label) {
case 1: /* ... */ label = 2; break; // goto L2;
case 2: /* ... */ return;
}
}
- No native bindings allowed / unknown target platforms
- Deployment to environments where JNI is forbidden or native builds are impractical (e.g., sandboxes, strict IT policies).
- Toolchains like GWT or TeaVM where native code isn’t viable.
- Not enough time for a full port
- Run existing C code inside a Java app quickly, without a large rewrite.
- Progressive migration
- Translate incrementally: start with critical pieces via MIR→Java, keep the rest in C, iterate over time.
- Endianness: little-endian (LE)
- Pointer size: 64-bit (8 bytes)
- Memory layout: DATA | STACK | HEAP in a single byte[]
- Function pointers: small integers mapped to Java Method handles.
The default runtime intentionally stays minimal: memcpy, memset, very limited printf, basic strlen/strcpy, a few syscalls/stubs used by tests.
If you need more libc surface (and richer printf/vfprintf/…), you can link a small C standard library alongside the runtime.
Prereqs: make, a C99 compiler, and a JDK 1.2+
make
make m2j
cd mir2j
./compile-test.sh
or
./compile-raygui.sh
The generated raygui library also has its own repository : raygui4j
- Experimental. Focus is correctness and clarity of the translation path.
- Java sources are generated for readability and quick iteration (no bytecode gen yet).
MIR is tiny and fast, making it a great IR target for lightweight toolchains. The translator stays simple and the runtime small.
- Expand libc coverage as needed (either Java-side or via a small C shim)
- More intrinsics / syscalls when use-cases appear
- Performance passes once semantics are solid