Skip to main content

MEP 67. Mochi and Java package bridge

FieldValue
MEP67
TitleMochi and Java package bridge
AuthorMochi core
StatusDraft
TypeStandards Track
Created2026-05-30 01:11 (GMT+7)
DependsMEP-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

  1. 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.

  2. 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_methodName naming convention. JNI is supported by every compliant JVM (HotSpot, OpenJ9, GraalVM, Android's ART) and does not require running with --enable-preview or any non-standard JVM flag.

  3. Java reflection provides complete, authoritative type information at build time. The java.lang.reflect API 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.

  4. 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.

  5. 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:

import java "com.google.guava:[email protected]" as guava
import java "io.grpc:[email protected]" as grpc

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 typeMochi KindFFI repr
intKindIntint
longKindLonglong
floatKindFloatfloat
doubleKindDoubledouble
booleanKindBoolint
voidKindVoidvoid
java.lang.StringKindStringString
byte[]KindBytesbyte[]
java.lang.Integer (boxed)KindIntint
java.lang.Long (boxed)KindLonglong
java.util.List<T>KindListString (JSON)
java.util.Map<K,V>KindMapString (JSON)
java.util.Optional<T>KindOptionalString (JSON)
java.util.concurrent.CompletableFuture<T>KindFutureString (callback)
Unknown classKindHandlelong (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:

  1. Go registers a FutureCallback in jni.FutureRuntime and gets back an int64 handle.
  2. The generated wrapper Java method calls .thenAccept(v -> MochiRuntime.callback(cbHandle, v)).
  3. 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:

  1. EmitTargetJavaLibrary packages the wrapper .class files into a JAR and renders the POM.
  2. BuildBundle assembles the ZIP with JAR, POM, SHA-1, MD5, and optional GPG .asc signatures.
  3. Client.Upload POSTs the bundle to https://central.sonatype.com/api/v1/publisher/upload.
  4. Client.PollUntilPublished polls the deployment status until PUBLISHED or FAILED.
  5. SignBundle generates 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

PhaseDescriptionPRStatus
0Skeleton: errors/, build/ driver and workspace#22870LANDED
1Maven Central HTTP client with SHA-1 verification#22876LANDED
2Content-addressed JAR cache (SHA-256 keyed)#22884LANDED
3JVM reflection tool (embedded JAR + mock executor)#22893LANDED
4Type mapping table (15 SkipReasons, primitives, boxed, generics)#22905LANDED
5JNI wrapper synthesiser (Synth + EmitJavaSource)#22918LANDED
6Mochi extern shim emitter (EmitShimMochi, CollectSkips)#22932LANDED
7import java grammar wiring (P070/P071 diagnostics, golden file)#22944LANDED
8JNI embedding driver (CGO + java_jni tag, stub for non-CGO)#22954LANDED
9Build orchestration pipeline and javac driver#22967LANDED
10mochi.lock integration ([[java-package]] TOML table)#22979LANDED
11TargetJavaLibrary emit (POM renderer, jar packager)#22987LANDED
12Maven Central publish flow (bundle ZIP, Central Portal client)#22991LANDED
13Sigstore attestation (in-toto v1.0 + Sigstore Bundle 0.3)#22994LANDED
14Async bridge: CompletableFuture bridge (typemap, wrapper, jni)#23000LANDED
15Generics and type-erasure utilities (GenericSignature, EraseType)#23002LANDED

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 unsafe JNI. Raw pointer operations, JNI GetPrimitiveArrayCritical, and java.nio.ByteBuffer.allocateDirect are 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 @FunctionalInterface types 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:

CoordinatePurpose
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.