hollow-cube / luau-java

Luau bindings for Java
MIT License
11 stars 0 forks source link

Luau (for Java)

license

Low level bindings for Luau.

Why Luau?

There is a lot of prior art for interacting with Lua to/from Java:

However, these solutions generally do not have strong sandboxing guarantees (or track record), or lose much of the performance benefit of Lua. Enter Luau. It has been proven at Roblox to be performant, well sandboxed, and easier to use through its introduction of progressive typing.

Install

luau-java and the associated native libraries are all available on maven central. All projects must depend on the main luau artifact, as well as at least one of the platform specific natives. It is valid to depend on multiple natives artifacts at the same time, they will not conflict.

[!IMPORTANT]
luau-java uses the new Foreign Function and Memory API (FFM) introduced as a preview feature in Java 21. This means that you will be required to enable preview features when using luau-java. This can be done by adding --enable-preview and --enable-native-access=ALL-UNNAMED to your JVM arguments.

Gradle ```groovy dependencies { implementation("dev.hollowcube:luau:${version}") implementation("dev.hollowcube:luau-natives-${platform}:${version}") } ```
Maven ```xml dev.hollowcube luau ${version} dev.hollowcube luau-natives-${platform} ${version} ```

Replace ${platform} and ${version} with one of the following entries. Note that the core library version may be different from the native library version.

Platform ${platform} ${version}
- -
Windows (x64) windows-x64
Linux (x64) linux-x64
macOS (x64) macos-x64
macOS (arm64) macos-arm64

Usage

A hello world print from Luau would look something like the following:

public class HelloWorld {
    public static void main(String[] args) throws LuauCompileException {
        final byte[] bytecode = LuauCompiler.DEFAULT.compile("""
                        print("Hello, Luau!")
                """);

        final LuaState state = LuaState.newState();
        try {
            state.openLibs(); // Open all libraries
            state.sandbox(); // Sandbox the global state so it cannot be edited by a script

            var thread = state.newThread();
            thread.sandboxThread(); // Create a mutable global env for scripts to use

            thread.load("helloworld.luau", bytecode); // Load the script into the VM
            thread.pcall(0, 0); // Eval the script

            state.pop(1); // Pop the thread off the stack
        } finally {
            // Always remember to close the state when you're done with it, or you will leak memory.
            state.close();
        }
    }
}

The test sources contain library examples, which should help you to get started.

Building from Source

Prerequisites: JDK 21+, CMake 3.15+, JExtract (Only required to update bindings)

git clone git@github.com:hollow-cube/luau-java.git --recurse-submodules && cd luau-java
./gradlew build

Updating Bindings

Bindings are generated using JExtract. They are already included in the repository inside of src/generated/java. They may need to be updated as Luau is updated.

./gradlew jextract

Contributing

Contributions via PRs and issues are always welcome.

License

This project is licensed under the MIT License.