Repository navigation
Add a Java bindings generator on top of the C bindings - #55
Merged
Merged
Conversation
This was referenced Oct 13, 2026
Collaborator
|
Don't add those comments to generated code: |
Like the Rust generator, it consumes the C bindings description JSON and emits sources that call the C library through the foreign function API of Java 22, so there's no JNI glue. Opaque classes are wrappers with cleaner-based ownership, exposed structs are views over native memory with the C++ layout, enums are newtype classes, and the rest of the mapping follows the Rust one. Overloads keep their C++ names when the Java parameter types differ. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Each class is a file, and a `std::function` instantiation from MeshLib produced a 190-character name that overflowed the Windows path length limit. Same truncation and hash suffix as the C generator applies to header names. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Generated on Linux by test/make_java.sh, which also compiled them and ran the smoke test there. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ATH) on Windows The directory of java.exe comes first in the default DLL search order, and the JDK ships freetype.dll, which shadowed the real one and made MeshLibC2.dll fail with "The specified procedure could not be found". With the altered search path the dependencies are looked up next to the library first. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
LoadLibraryExW failures were swallowed and only the generic message of the System.loadLibrary fallback was visible. Also keep the message when the static initializer of Ffi fails, since the later uses of the class only see a NoClassDefFoundError. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…es, no generated-code headers The same five points as in the Rust backend: the "generated, do not edit" headers are gone; the [[clang::lifetimebound]] relations choose what a returned reference keeps alive; every class gets upcastTo<Base>(), and staticDowncastFrom<Base>() / dynamicDowncastFrom<Base>() for the non-virtual and polymorphic bases, calling the cast functions of the C bindings; the std::function wrappers get of(lambda) and assignFn(lambda), built on ConstructEx/AssignEx through upcall stubs, with the lambda shared by the copies and its exceptions rethrown by the wrapper that called into C; examples/java is the same as examples/rust. A class can't be named like a segment of the base package anymore, the example found that case (example.h in package example). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
adalisk-emikhaylov
approved these changes
Oct 15, 2026
Fedr
added a commit
to MeshInspector/MeshLib
that referenced
this pull request
Oct 15, 2026
…CI (#7115) The MeshLib side of MeshInspector/mrbind#55: make -f scripts/mrbind/generate.mk TARGET=java generates source/MeshLibJava/src from the C bindings JSON and compiles meshlib.jar (Java 22+ foreign function API, no JNI). Java examples, MRTestJava (96 tests), a build-java-bindings composite action run on one Release job per OS and architecture (Windows against delvewheel-mangled DLLs, since java.exe already holds its own zip.dll), JavaSetupGuide plus Java tabs in the docs, and meshlib_<version>_java_bindings.zip in the release assets.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A Java backend, generated on top of the C bindings the same way the Rust one (#54) is. Stacked on #54, since it reads the C signatures and the
std::functioncallback types that PR added to the description JSON; rebased on its current head.What's generated
mrbind_gen_java --input-json c_desc.json --output-dir src --package com.example.mylib --imported-lib-name MyLib [--force-namespace MyLib]emits plain Java sources calling the C library through the foreign function API (java.lang.foreign, Java 22+), so there's no JNI glue and no dependencies:<package>.ffimirrors the C file hierarchy: one class per C file holding the downcallMethodHandleof every function (in nested classes of 200 handles each, to keep the static initializers under the bytecode limit). A function the library lacks gets a handle that throwsUnsatisfiedLinkErrorwhen called, so the rest of its class stays usable.MR::Foo→mr.foo), classes become classes, free functions become static methods of aFunctionsclass per package. Opaque classes areCppObjectwrappers that own their C++ object when created in Java (destroyed by aCleanerorclose()), and when returned by reference keep alive what the reference points into: the objects named by the[[clang::lifetimebound]]annotations, orthis/ the only object parameter without them.--expose-as-structclasses areCppStructviews over native memory with the C++ layout, built at runtime with the C alignment rules and checked against the sizes in the JSON. Enums are newtype classes with constants. Parameters take wrappers (nullable for pointers and default arguments),std::string-like ones takeString, scalars by const pointer take primitives. C++ exceptions becomeCppException. Anything the safe layer doesn't understand still gets a wrapper with rawMemorySegments and primitives, so the whole API is reachable.upcastTo<Base>(), plusstaticDowncastFrom<Base>()for the non-virtual bases anddynamicDowncastFrom<Base>()(null on failure) for the polymorphic ones, calling the cast functions of the C bindings (derived frominheritance_infowith the same rules as the C generator).std_function_...wrappers getof(lambda)andassignFn(lambda), built onConstructEx/AssignExthrough upcall stubs. The lambda implements a nestedFninterface with the natural Java types (scalars, enums, exposed structs by value, classes by reference, classes returned by value as owned wrappers); the copies of thestd::functionshare it through the user data callback; an exception it throws is rethrown by the wrapper that called into C once the call returns.opAdd,opEqual,opNotEqual,opAddAssign, ... Constructors are real Java constructors when unambiguous.LoadLibraryExW(LOAD_WITH_ALTERED_SEARCH_PATH), so its dependencies are found next to it, and the Windows error code is reported when that fails.See
docs/generating_java.mdfor the details and the known limitations.examples/javahas the same structure asexamples/rust.Testing
test/make_java.shgenerates the three test outputs, compiles them with-Xlint:all -Werror, and runstest/run_java/Main.java, which exercises opaque classes with string fields, copy constructors,std::vector<int>, enum default arguments, an exposed struct with arrays, a C++ exception turning into aCppException,std::functionfrom lambdas (shared by copies, class passed and returned by value, a lambda exception rethrown by the caller), and the casts. On the full test suite the generator wraps all 4668 functions, 433 of them with raw types, 0 skipped. Verified on Windows (MSYS2 clang64 for the C library, Temurin JDK 25), andexamples/java/run.shruns there too.On the full MeshLib API (37764 functions, 3157 classes) the generated sources compile in ~20 s with zero warnings; MeshInspector/MeshLib#7115 builds and runs them in CI on Ubuntu, Windows and macOS, x64 and arm64, with all 92 ported tests plus the callback and cast tests passing.
The committed
test/output_java*sources are generated on Linux like the C and Rust ones.🤖 Generated with Claude Code