Skip to content

JVM Backend

Chris Michael edited this page Jun 5, 2026 · 45 revisions

Meris

JVM Backend

meris compile source.meris --out classes
java -cp "classes:compiler/build/install/meris/lib/*" meris.generated.ModuleKt

Status: implemented for the constructs listed below. Generated names and layouts are deterministic for tests, not a stable public ABI.

Supported Today

Construct JVM behavior
value / data declarations Emits final classes with public final fields and constructors.
Variant type declarations Emits root and variant case artifact classes.
Top-level functions Emits static module methods.
Script entry points Emits Java-compatible main(String[]).
Hidden runtime context Generated Meris methods receive MerisRuntime as a hidden first parameter; public wrappers construct the default runtime.
Literals, locals, calls, arithmetic, booleans Emits ordinary bytecode.
Product constructor calls Emits new calls against generated product constructors.
Product field access Emits GETFIELD against generated product classes.
Typed variant constructor calls Emits concrete variant case classes assignable to the root variant type. Fieldless variant values use the same path without an empty call.
Structural equality Emits value equality for primitives and strings, runtime carrier equality for Unit, Option, and List, and generated equals(Object) / hashCode() for product and variant artifact classes.
match Dispatches on generated variant classes and binds selected fields.
if Emits branch labels with one checked result type.
raise Throws JvmRuntimeError with variant name and payload fields.
handle Catches JvmRuntimeError, binds payload fields, and rethrows unmatched failures.
Args, Env, Json, Http, Fs bridges Calls JVM host APIs and converts failures to typed runtime errors. Json includes default typed encode/decode and explicit product field-name codec plans.
Server.* response constructors Emits public runtime response/status/content/header carrier calls and typed response-construction failures.
routes declarations Emits internal static descriptor methods for checked route graph metadata.
Checked Server applications Emits internal static descriptor methods for checked application metadata, including resources, body/response codecs, function codec callable metadata, default product Json codec plan metadata, custom Json codec plan metadata, route codec attachment metadata, route graph, and pipeline metadata. Route codec attachment is internal descriptor metadata, not public ABI.
Internal Server HTTP host adapter Serves checked route graph or application descriptors through a JDK host.
meris serve route graph startup Loads generated route graph descriptors in memory and starts the internal HTTP host.
Packaged Server descriptor startup Loads compiled application descriptor metadata from project output and starts the internal HTTP host.
java fun Emits INVOKESTATIC for the supported static Java interop subset.
java new Emits NEW, DUP, and INVOKESPECIAL <init> for declared Java constructors.
java method Emits INVOKEVIRTUAL or INVOKEINTERFACE for borrowed Java-handle receivers.

Not Supported Yet

Construct Status
General Java interop Not implemented. The current subset is explicit and compile-only.
Source-free packaged Server runtime Limited support. Packaged descriptor startup runs route handlers, ordinary pipeline middleware, entered-stage request cleanup hooks, supported application resource lifecycle calls including metadata-backed generated product and variant resource values, non-empty owned-resource application environments, function-backed body/response codec hooks for primitives, runtime carriers, and metadata-backed generated product or variant descriptor values, default product Json body/response codec plans including List<Product> response values, top-level List<Product> / Option<Product> request bodies, and mixed one-layer List<Option<Product>> / Option<List<Product>> request bodies, custom Json body/response codec plans, and packaged static roots from compiled descriptor/ABI metadata. Same-kind or deeper erased request-body containers, arbitrary external dependency injection, dependency resolution, and jar assembly remain follow-up work.
Stable JVM ABI Not promised.
Source-level Clock standard module Implemented for Clock.UnixSeconds() through the hidden runtime context.

Java Interop

java fun is compile-only. The JVM backend derives the Java descriptor from checked Meris types and emits INVOKESTATIC against the declared binary owner and method. The type checker resolves the owner and exact public static method on the compiler classpath before lowering. When the owner is a Java interface, emission uses an interface method reference; java.util.List.of is part of the supported static interop shape.

The supported subset is:

Shape JVM behavior
Int, Bool, String parameters Passed directly.
Int, Bool, Unit returns Returned directly.
String nonnull Returned directly as String.
String nullable Wrapped as Meris Option<String>.
String platform unsafe Wrapped as Meris Option<String>; unsafe is valid only here.
throws Java.Exception as Error.Variant Catches the declared Java exception and throws JvmRuntimeError.
throws Java.Exception as Error.Variant(Field = message) Catches the declared Java exception and carries mapped payload fields.
List copy Element Copies a Java List or object array into dev.meris.runtime.List.
List copy Option<Element> Copies a Java List or object array and preserves null scalar elements as Option.none().
List copy List<Element> Recursively copies nested Java collections when leaves are supported scalar or explicit optional scalar elements.
List nullable copy Element Copies a present Java List, then wraps it as Option<List<Element>>.
List platform unsafe copy Element Same runtime shape as nullable collection copy.

Exception mappings may target fieldless or payload typed-error variants. Payload translations must map every target field. The first supported source is message, exposed as Option<String> from Throwable.getMessage(). Each Java exception class may appear once per java fun.

java type declares an opaque handle for one Java binary owner. java new constructors return matching handles marked owned nonnull. java method calls take the borrowed handle as the first ordinary function parameter. Constructors and instance methods use the same checked classpath resolution, return adapters, collection-copy adapters, declared exception translation, and panic-level unchecked exception policy as the static boundary.

The collection copy adapter is valid only on List returns in Meris source. Collection nullability applies to the Java collection object. Object arrays can back non-null List copy Element declarations, including array returns whose elements are Java lists for nested copy adapters. Array-backed nullable collection returns and primitive arrays are not supported yet. Element nullability requires an explicit Option<Element> copy adapter.

Generated Owners

Generated classes live under meris.generated by default. Source packages extend that root. For example:

package app.users
value UserId(Raw: String)

emits a JVM owner under meris/generated/app/users/UserId.

Release Smoke

The release smoke script runs the installed CLI lane and the extracted distZip and distTar archive lanes. It prints SHA-256 checksums for the archives, extracts each archive into an isolated temporary directory, runs the same CLI smoke suite through each extracted bin/meris, and executes generated entry classes with the runtime libraries from that lane.

The smoke fixture includes product constructors, typed variant constructors, match, selected field binding, compile-only java fun, Java String nullability, Java interface static calls, declared Java exception translation, Java collection copy, standard-library file output, local HTTP text-client coverage for Http.GetText and Http.PostText, generated class count checks, and packaged meris serve coverage for manifest-declared Server package metadata plus missing classpath diagnostics. It is a release linkage check, not a replacement for backend unit tests.

Related

Clone this wiki locally