Skip to content

terrarium-earth/jvm-multiplatform

Repository files navigation

jvm-multiplatform

A collection of multiplatform utilities for the JVM

These utilities utilize the static linkage for handling common code, rather than dynamic linking (Jars in the classpath) which is what's usually used in the JVM Ecosystem

Virtual SourceSets

The base for any jvm-multiplatform utility to work, as it allows the statically linked source sets to work

  • In Java, by including the common code in the same compilation
  • In Kotlin by utilizing a multiplatform structure while still compiling only for the JVM platform
sourceSets {
    platform {
        // Add main as a common, while the platform source set is the one expected to be runnable
        staticLinkage.link(sourceSets.main)
    }
}

Java

The jvm-multiplatform collection includes @Expect/@Actual annotations and class extensions for the Java platform, examples are as follows:

Expect/Actual

Allow replacing expected platform stubs with actualized platform logic, similarly to kotlin's expect/actual

// Common.java
package a.b.c;

import net.msrandom.multiplatform.annotations.Expect;

@Expect
public class Common {
    public final int field;

    public Common(int value);

    public int getter();
}
// CommonActual.java
package a.b.c;

import net.msrandom.multiplatform.annotations.Actual;

@Actual
public class CommonActual {
    @Actual
    public final int field;

    @Actual
    public CommonActual(int value) {
        this.field = value;
    }

    @Actual
    public int getter() {
        return privateGet();
    }

    private int privateGet() {
        return field;
    }
}

Alternatively, you can do static fields/methods instead of full classes/types

// Common.java
package a.b.c;

import net.msrandom.multiplatform.annotations.Expect;

public class Common {
    @Expect
    public static final float CONSTANT;

    @Expect
    public static void printConstant();
}
// CommonActual.java
package a.b.c;

import net.msrandom.multiplatform.annotations.Actual;

public class CommonActual {
    @Actual
    public static final float CONSTANT = 3.14;

    @Actual
    public static void printConstant() {
        System.out.println(CONSTANT);
    }
}

Can be configured as follows:

// Assuming the previous structure of common main + platform source set
tasks.compileJava {
    options.compilerArgs.add("-AgenerateExpectStubs")
}

dependencies {
    compileOnly("net.msrandom:java-expect-actual-annotations:1.0.0")
    annotationProcessor("net.msrandom:java-expect-actual-processor:1.0.8")
}

Class Extensions

More granular than @Expect/@Actual, but allows injecting methods and fields into a base class and shadowing existing members. Useful for interface injection, along with adding platform specific code.

// Common.java
package a.b.c;

public class Common {
    public final int field;

    public Common(int value) {
        this.field = value;
    }

    public void printExtensionElement() {
        System.out.println(this.injected);
    }
}
// CommonExtension.java
package a.b.c;

import net.msrandom.classextensions.ClassExtension;
import net.msrandom.classextensions.ExtensionShadow;
import net.msrandom.classextensions.ExtensionInject;

@ClassExtension(Common.class)
public class CommonExtension implements SomeInterface {
    @ExtensionShadow
    public final int field;

    @ExtensionInject
    private String injected;

    @ExtensionInject
    public CommonExtension() {
        this(0);
    }

    @ExtensionInject
    @Override
    public void interfaceMethod() {
        System.out.println("Extension injected");
    }
}

Class extensions can be applied using a Gradle Plugin, for both the Java and Kotlin versions

plugins {
    id "net.msrandom.classextensions"
}

Kotlin

For kotlin, while you already have expect/actual, you also get class extensions, along with a stub annotation processor that will allow you to compile common sources to the JVM platform

Class Extensions

Very similar to Java's, allows injecting properties, constructors and methods to common classes

// Common.kt
package a.b.c

class Common(val field: Int)
// CommonExtension.kt
package a.b.c

@ClassExtension(Common::class)
class CommonExtension : SomeInterface {
    @ExtensionShadow
    val field: Int = 0

    @ExtensionInject
    override fun interfaceMethod() {
        println("Extension injected")
    }
}

KMP Stubs

To allow compiling common source sets to the JVM bytecode format:

import net.msrandom.stub.Stub

// Will generate `actual fun myFunction(): Int = TODO()`
@Stub
expect fun myFunction(): Int

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

Packages

No packages published

Contributors 3

  •  
  •  
  •