A Java code-generation library that bridges C/C++ native code to JVM platforms — desktop, mobile, and web.
- Overview
- How It Works
- Supported Targets
- Code Block Convention
- WebIDL Bindings
- IDLBase API
- Requirements
- Getting Started
- Libraries Using jParser
- License
Inspired by gdx-jnigen, jParser lets you embed native C/C++ code directly inside Java source files using annotated comment blocks. Each block is translated into target-specific Java source code, enabling a single lib-base module to produce a bridge-agnostic lib-core API plus platform bridge outputs for JNI (desktop/mobile), FFM (desktop, Java 25+), and TeaVM (web via JS/WASM).
For web targets, jParser uses Emscripten to compile C/C++ into JS/WASM and TeaVM to generate the corresponding Java-to-JavaScript bridge via @JSBody annotations.
jParser consists of two main stages:
Reads the hand-written Java source in the lib-base module — which contains embedded native code blocks — and generates platform-specific Java source for each target:
| Output Module | Target | Description |
|---|---|---|
lib-core |
Core API | Generated bridge-agnostic API classes |
lib-desktop-jni |
JNI (Desktop) | Generated desktop JNI Java + native jars |
lib-android |
JNI (Android) | Generated Android JNI Java + Android packaging |
lib-teavm |
TeaVM | Generated @JSBody-annotated Java for web |
lib-desktop-ffm |
FFM | Generated FFM Java for desktop (Java 25+) |
Compiles the C/C++ source into platform-specific native libraries:
| Platform | Toolchain |
|---|---|
| Windows | MinGW64 or MSVC |
| Linux | GCC / G++ |
| macOS | Xcode CLI tools |
| Android | Android NDK |
| Web | Emscripten SDK |
| Target | Bridge | Platforms | Java Version |
|---|---|---|---|
| JNI | Java Native Interface | Windows, Linux, macOS, Android | 8+ |
| FFM | Foreign Function & Memory API | Windows, Linux, macOS | 22+ |
| TeaVM | JavaScript / WASM | Web browsers | 11+ |
In lib-base Java source files, native code is embedded via annotated comment blocks. jParser reads these blocks and generates the appropriate code for each target.
public class MyLib extends IDLBase {
// TeaVM replacement — generates @JSBody-annotated method for web
/*[-TEAVM;-REPLACE]
@org.teavm.jso.JSBody(params = {"this_addr"},
script = "var jsObj = [MODULE].wrapPointer(this_addr, [MODULE].MyType);"
+ "return jsObj.getValue();")
private static native int internal_native_getValue(int this_addr);
*/
// JNI native code block — compiled into C++ for desktop & mobile
/*[-JNI;-NATIVE]
MyType* obj = (MyType*)this_addr;
return obj->getValue();
*/
private static native int internal_native_getValue(long this_addr);
}| Command | Description |
|---|---|
-NATIVE |
Inline C/C++ code compiled for the target |
-ADD |
Adds code to the generated output |
-ADD_RAW |
Adds raw code without processing |
-REMOVE |
Removes code from the generated output |
-REPLACE |
Replaces the following method with the block content |
-REPLACE_BLOCK |
Replaces the following code block |
-IDL_SKIP |
Placed on a class comment to skip IDL generation for that class |
To reduce the effort of manually porting each method, jParser supports Emscripten WebIDL. Define a .idl file and jParser automatically generates binding code for all targets.
interface NormalClass {
void NormalClass();
long addIntValue(long value1, long value2);
static long subIntValue(long value1, long value2);
attribute long intValue;
attribute float floatValue;
};This generates fully working Java classes with native bindings for JNI, FFM, and TeaVM — no manual glue code required.
- IDL helper classes (
IDLInt,IDLIntArray, etc.) let you pass primitive pointers to C++. They work across Emscripten, desktop, and mobile. - C++ enums are converted into Java enums, each carrying the integer value from native code.
[Value]methods return a cached copy of the object. The cache is overwritten on each call — do not retain references.[NoDelete]classes should not havedispose()called. All other classes require explicit disposal.
Every native class extends IDLBase, which provides common memory-management functionality.
Important: jParser does not automatically dispose C++ objects. You must call
dispose()when you're done with an object to free native memory. Only objects you create or explicitly own require disposal. Creating and disposing native objects is expensive — avoid doing it every frame.
| Method | Description |
|---|---|
ClassName.native_new() |
Creates an empty instance without native data |
ClassName.NULL |
Returns a NULL instance — use instead of Java null for native parameters |
dispose() |
Deletes the native instance (only if owned) |
isDisposed() |
Checks whether the native instance has been disposed |
native_setVoid(...) |
Sets an integer or long memory address |
native_reset() |
Resets the instance to default state |
native_takeOwnership() |
Takes ownership, enabling dispose() to delete the object |
native_releaseOwnership() |
Releases ownership, preventing dispose() from deleting |
native_hasOwnership() |
Checks whether you own the native instance |
native_copy(...) |
Copies memory address and native data from another instance |
The
native_prefix is used to avoid naming conflicts with C/C++ methods.
| Requirement | Purpose |
|---|---|
| JDK 11+ | Building jParser tool modules |
| JDK 22+ (25 recommended) | FFM modules and FFM-based apps |
| MinGW64 or Visual Studio C++ | Windows native builds |
| GCC / G++ | Linux native builds |
| Xcode CLI tools | macOS native builds |
| Emscripten SDK | Web builds (JS/WASM) |
Windows (MSVC): Ensure
vcvarsall.batis on your systemPATH. It is typically located at:C:\Program Files\Microsoft Visual Studio\[Year]\[Edition]\VC\Auxiliary\Build\
For a complete working example, refer to the examples/TestLib module.
jParser projects follow a strict -base / -build / -core / -teavm convention:
| Module Suffix | Purpose |
|---|---|
lib-base |
Hand-written Java source with embedded native code blocks |
lib-build |
Build entry point — configures IDL, targets, runs generation + compilation |
lib-core |
Generated bridge-agnostic API output (do not hand-edit) |
lib-desktop-jni |
Generated desktop JNI Java output + JNI native libs (do not hand-edit) |
lib-android |
Generated Android JNI Java output + Android JNI packaging (do not hand-edit) |
lib-teavm |
Generated TeaVM Java output (do not hand-edit) |
lib-desktop-ffm |
Generated FFM Java output (do not hand-edit) |
# 1. Build runtime (required once)
./gradlew :idl:runtime:runtime-build:idl_helper_build_project_windows64_jni
# 2. Generate code + compile native library
./gradlew :examples:TestLib:lib:lib-build:TestLib_build_project_windows64_jni
# 3. Run the desktop app
./gradlew :examples:TestLib:app:desktop-jni:TestLib_run_app_desktop_jni
./gradlew :examples:TestLib:app:desktop-ffm:TestLib_run_app_desktop_ffmReplace
windows64withlinux64,mac64, ormacArmfor other platforms. Current desktop FFM app tasks configure Java toolchain24; ensure a Java 24 toolchain is available when running..._run_app_desktop_ffmtasks.
| Library | Description | Status |
|---|---|---|
| jWebGPU | WebGPU bindings for Java | Active |
| xImGui | Dear ImGui bindings for Java | Active |
| xJolt | Jolt Physics bindings for Java | Active |
| xLua | Lua bindings for Java | Active |
| xBullet | Bullet Physics bindings for Java | Active |
| gdx-box2d | Box2D bindings for libGDX | Inactive |
| gdx-physx | PhysX bindings for libGDX | Inactive |
If you find this project valuable and want to fuel its continued growth, please consider sponsoring it. Your support keeps the momentum going!
jParser is licensed under the Apache License 2.0.