MEP 67. Mochi and Java package bridge
| Field | Value |
|---|---|
| MEP | 67 |
| Title | Mochi and Java package bridge |
| Author | Mochi core |
| Status | Draft |
| Type | Standards Track |
| Created | 2026-05-30 01:11 (GMT+7) |
| Depends | MEP-1 (Grammar, for the import java extension), MEP-2 (AST), MEP-4 (Type System), MEP-13 (ADTs and Match, for Result/Optional translation), MEP-57 (Mochi module and package system, for mochi.toml, mochi.lock, content-addressed object store, capability declarations, and OIDC trusted publishing) |
| Research | /docs/research/0067/ |
| Tracking | /docs/implementation/0067/ |
Abstract
Mochi today (May 2026, after MEP-57's source-level package system reached Draft) bridges Go, Python, TypeScript, Rust, Ruby, Erlang, and Kotlin. Java is the next target. The Java ecosystem (Maven Central, 600,000+ artifacts as of May 2026) hosts foundational libraries for HTTP (OkHttp, Ktor), JSON (Gson, Jackson), database access (JDBC, Hibernate), cloud SDKs (AWS, GCP, Azure Java SDKs), machine learning (DL4J, Weka), Android components, and countless enterprise frameworks that have no equivalent in Mochi's standard library.
MEP-67 specifies the bidirectional Java bridge: Mochi programs can consume any Maven Central JAR via import java "groupId:artifactId@version" as alias with no user-written boilerplate, and Mochi packages can publish JNI wrapper artifacts to Maven Central. The bridge operates through the JVM's JNI (Java Native Interface): the synthesised wrapper Java class exposes static JNI-callable methods that marshal arguments, dereference object handles, and return serialised values back to Go.
Type information comes from Java reflection via a small embedded JAR tool: the tool loads the upstream JAR, enumerates its public classes, reflects all public methods and fields, and emits a JSON surface document. The surface is then walked through a closed Java-to-Mochi type table. Items outside the table are skipped with a structured SkipReport (15 skip reasons covering raw Object, wildcards, inner classes, varargs, reflective types, deprecated items, functional interfaces, and more). No running JVM is required at import-resolve time: the reflection tool runs as a one-shot java -jar subprocess.
Motivation
-
Maven Central hosts 600,000+ artifacts. The Java/JVM ecosystem is the largest single package registry in the world by artifact count. Libraries like Guava, Apache Commons, Jackson, Log4j, Spring Boot, Hibernate, Kafka client, gRPC-Java, and Protobuf-Java are used by millions of projects. No Mochi interop path currently exists.
-
JNI is the canonical, stable foreign-function interface for the JVM. JNI has been part of the JVM specification since Java 1.1. It operates at the C-level ABI: native methods declared in Java are dispatched to C functions whose signatures follow a predictable
Java_ClassName_methodNamenaming convention. JNI is supported by every compliant JVM (HotSpot, OpenJ9, GraalVM, Android's ART) and does not require running with--enable-previewor any non-standard JVM flag. -
Java reflection provides complete, authoritative type information at build time. The
java.lang.reflectAPI enumerates all public methods, fields, and constructors of a class, including generic type signatures. This is strictly more accurate than external annotation databases: the reflection output is derived from the actual compiled bytecode the end user ships. -
The Maven coordinate system is the lingua franca of JVM dependency management.
groupId:artifactId:version(used by Maven, Gradle, sbt, Bazel rules_jvm_external, and Pants) is the universal identifier for JAR artifacts. Maven Central's HTTP API is stable, well-documented, and publicly accessible without authentication for read operations. -
The Sonatype Central Portal is the authoritative publish path for Maven Central. Since March 2024, Sonatype's Central Portal API (
central.sonatype.com) is the GA publish path for Maven Central, replacing the legacy OSSRH Nexus. It accepts a deployment bundle ZIP and provides a status-polling API.
Specification
1. Package layout
All bridge code lives under package3/java/:
package3/java/
errors/ -- SkipReason, SkipReport, BridgeError
maven/ -- Coordinate, Client (Maven Central HTTP), Cache (SHA-256 keyed)
reflect/ -- Surface, ClassInfo, Tool (embedded JAR executor), Erasure
typemap/ -- Kind, Mapping, Map(), AsyncPolicy, AsyncMapping, GenericSignature
wrapper/ -- WrapClass, Synth, EmitJavaSource, FutureWrapMethod, EmitFutureWrapper
emit/ -- EmitShimMochi, EmitSkipReport, CollectSkips
build/ -- Driver, Workspace, Pipeline, RunJavac, TargetJavaLibrary, RenderPOM
jni/ -- Runtime (CGO, java_jni tag), FutureRuntime, stubs for non-CGO
lock/ -- JavaPackage, Encode, Decode, Check (mochi.lock [[java-package]])
publish/ -- BuildBundle, DryRun, Client (Central Portal), SignBundle (Sigstore)
2. Maven coordinate syntax
Java imports use Maven coordinate notation extended with Mochi's @version pin:
The path format groupId:artifactId@version is validated at parse time by the parser.JavaImportRef function. The version is required (unlike import go, where stdlib imports omit the pin). An import without as <alias> is rejected with diagnostic P071.
3. Type mapping table
| Java type | Mochi Kind | FFI repr |
|---|---|---|
int | KindInt | int |
long | KindLong | long |
float | KindFloat | float |
double | KindDouble | double |
boolean | KindBool | int |
void | KindVoid | void |
java.lang.String | KindString | String |
byte[] | KindBytes | byte[] |
java.lang.Integer (boxed) | KindInt | int |
java.lang.Long (boxed) | KindLong | long |
java.util.List<T> | KindList | String (JSON) |
java.util.Map<K,V> | KindMap | String (JSON) |
java.util.Optional<T> | KindOptional | String (JSON) |
java.util.concurrent.CompletableFuture<T> | KindFuture | String (callback) |
| Unknown class | KindHandle | long (JNI ref) |
Skipped items (raw Object, wildcards ?, inner classes, varargs, reflective types, deprecated, synchronised, volatile, native, generic without monomorphisation, functional interfaces) are logged as SkipReport entries with one of the 15 SkipReason constants.
4. JNI wrapper pattern
For each public class the synthesiser generates a Java class MochiWrap_<ident> with one static method per bridgeable method:
public class MochiWrap_com_google_guava_Optional {
public static String MochiWrap_com_google_guava_Optional_get(long __handle) {
Object obj = MochiRuntime.deref(__handle);
return (String) ((com.google.common.base.Optional<String>)obj).get();
}
}
Instance methods receive a long __handle first argument (a JNI global reference stored in a process-wide table); static methods are called directly. Return values are marshalled to JNI-compatible types (String for complex types, primitive types directly).
5. Async bridge
CompletableFuture<T> return types are bridged via a callback pattern:
- Go registers a
FutureCallbackinjni.FutureRuntimeand gets back anint64handle. - The generated wrapper Java method calls
.thenAccept(v -> MochiRuntime.callback(cbHandle, v)). - The Java callback invokes the JNI native method
MochiRuntime.callback, which dispatches the Go callback in a new goroutine.
The AsyncPolicy enum (AsyncBlocking, AsyncGoroutine) selects between blocking the calling goroutine and dispatching via the callback registry.
6. mochi.lock integration
Each resolved Java import adds a [[java-package]] entry:
[[java-package]]
group = "com.google.guava"
artifact = "guava"
version = "33.0.0-jre"
source = { kind = "maven" }
jar-sha256 = "..."
jar-sha1 = "..."
surface-sha256 = "..."
wrapper-sha256 = "..."
dependencies = ["com.google.code.findbugs:[email protected]"]
mochi pkg lock --check fails if jar-sha256, jar-sha1, surface-sha256, or wrapper-sha256 drift from the locked values.
7. Maven Central publish flow
mochi pkg publish --to=maven-central assembles a Sonatype Central Portal upload bundle:
EmitTargetJavaLibrarypackages the wrapper.classfiles into a JAR and renders the POM.BuildBundleassembles the ZIP with JAR, POM, SHA-1, MD5, and optional GPG.ascsignatures.Client.UploadPOSTs the bundle tohttps://central.sonatype.com/api/v1/publisher/upload.Client.PollUntilPublishedpolls the deployment status untilPUBLISHEDorFAILED.SignBundlegenerates a Sigstore-keyless attestation bundle (in-toto v1.0 + Sigstore Bundle 0.3) binding the Maven coordinate and JAR SHA-256 to the CI OIDC token.
Phases
Target matrix
| Phase | Description | PR | Status |
|---|---|---|---|
| 0 | Skeleton: errors/, build/ driver and workspace | #22870 | LANDED |
| 1 | Maven Central HTTP client with SHA-1 verification | #22876 | LANDED |
| 2 | Content-addressed JAR cache (SHA-256 keyed) | #22884 | LANDED |
| 3 | JVM reflection tool (embedded JAR + mock executor) | #22893 | LANDED |
| 4 | Type mapping table (15 SkipReasons, primitives, boxed, generics) | #22905 | LANDED |
| 5 | JNI wrapper synthesiser (Synth + EmitJavaSource) | #22918 | LANDED |
| 6 | Mochi extern shim emitter (EmitShimMochi, CollectSkips) | #22932 | LANDED |
| 7 | import java grammar wiring (P070/P071 diagnostics, golden file) | #22944 | LANDED |
| 8 | JNI embedding driver (CGO + java_jni tag, stub for non-CGO) | #22954 | LANDED |
| 9 | Build orchestration pipeline and javac driver | #22967 | LANDED |
| 10 | mochi.lock integration ([[java-package]] TOML table) | #22979 | LANDED |
| 11 | TargetJavaLibrary emit (POM renderer, jar packager) | #22987 | LANDED |
| 12 | Maven Central publish flow (bundle ZIP, Central Portal client) | #22991 | LANDED |
| 13 | Sigstore attestation (in-toto v1.0 + Sigstore Bundle 0.3) | #22994 | LANDED |
| 14 | Async bridge: CompletableFuture bridge (typemap, wrapper, jni) | #23000 | LANDED |
| 15 | Generics and type-erasure utilities (GenericSignature, EraseType) | #23002 | LANDED |
All 16 phases landed 2026-05-29 through 2026-05-30.
Non-goals
- No Kotlin-specific support. Kotlin on the JVM uses Maven Central for distribution; MEP-70 covers Kotlin-specific bridge semantics (Kotlin metadata, coroutines). MEP-67 treats Kotlin JARs as plain Java JARs.
- No Android-specific support. Android JARs use the Android SDK; MEP-67 targets standard JARs that run on a desktop JVM.
- No
unsafeJNI. Raw pointer operations, JNIGetPrimitiveArrayCritical, andjava.nio.ByteBuffer.allocateDirectare outside scope. - No dynamic class loading at Mochi compile time. The reflection tool runs at build time; runtime class-loading is not tracked by the bridge.
- No support for
@FunctionalInterfacetypes without an explicit opt-in. Functional interfaces require a lambda / method-reference synthesis step that is out of scope for the initial 16 phases.
Appendix: Fixture corpus
The 20-artifact fixture corpus used for per-phase gate tests:
| Coordinate | Purpose |
|---|---|
com.google.guava:[email protected] | Core utilities |
com.fasterxml.jackson.core:[email protected] | JSON |
io.grpc:[email protected] | gRPC |
org.slf4j:[email protected] | Logging |
commons-lang:[email protected] | String utilities |
org.apache.commons:[email protected] | Maths |
org.apache.httpcomponents:[email protected] | HTTP client |
com.squareup.okhttp3:[email protected] | HTTP client |
com.google.protobuf:[email protected] | Protobuf |
org.slf4j:[email protected] | Logging impl |
junit:[email protected] | Testing |
org.mockito:[email protected] | Mocking |
org.postgresql:[email protected] | JDBC |
mysql:[email protected] | JDBC |
redis.clients:[email protected] | Redis client |
org.apache.kafka:[email protected] | Kafka |
software.amazon.awssdk:[email protected] | AWS S3 |
com.google.cloud:[email protected] | GCS |
org.deeplearning4j:[email protected] | ML |
javax.annotation:[email protected] | Annotations |
Acknowledgements
MEP-67 draws on the prior art of MEP-73 (Rust bridge, rustdoc-JSON ingest, package3/rust/ layout), MEP-70 (Kotlin bridge, Maven Central client, package3/kotlin/ layout), MEP-76 (Ruby bridge, publish bundle pattern), and MEP-57 (mochi.lock design). The JNI embedding pattern is inspired by cgo's CGO build tag mechanism.