Skip to content

Commit

Permalink
Move to itsallcode org (#2)
Browse files Browse the repository at this point in the history
* Move to itsallcode org

* Fix sonar warnings

* Add dependency submission workflow

---------

Co-authored-by: kaklakariada <[email protected]>
Co-authored-by: kaklakariada <[email protected]>
  • Loading branch information
3 people authored Sep 29, 2024
1 parent 818cbde commit 5419af8
Show file tree
Hide file tree
Showing 12 changed files with 347 additions and 107 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/dependency-submission.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Dependency Submission

on:
# Only runs on main branch
push:
branches: [ main ]

permissions:
contents: write # Required for dependency submission

jobs:
dependency-submission:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 23
- name: Generate and submit dependency graph
uses: gradle/actions/dependency-submission@v4
8 changes: 4 additions & 4 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "modifications",
"editor.formatOnSaveMode": "file",
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.generate.finalModifiers": "explicit",
Expand Down Expand Up @@ -29,7 +29,7 @@
},
"java.compile.nullAnalysis.mode": "automatic",
"sonarlint.connectedMode.project": {
"connectionId": "kaklakariada-github",
"projectKey": "luava"
"connectionId": "itsallcode",
"projectKey": "org.itsallcode:luava"
}
}
}
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

An experimental Java library for embedding a Lua VM.

[![Build](https://github.com/kaklakariada/java-lua/actions/workflows/build.yml/badge.svg)](https://github.com/kaklakariada/java-lua/actions/workflows/build.yml)
[![Build](https://github.com/itsallcode/luava/actions/workflows/build.yml/badge.svg)](https://github.com/itsallcode/luava/actions/workflows/build.yml)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=org.itsallcode%3Aluava&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=org.itsallcode%3Aluava)
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=org.itsallcode%3Aluava&metric=bugs)](https://sonarcloud.io/summary/new_code?id=org.itsallcode%3Aluava)
[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=org.itsallcode%3Aluava&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=org.itsallcode%3Aluava)
Expand All @@ -15,8 +15,14 @@ An experimental Java library for embedding a Lua VM.
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=org.itsallcode%3Aluava&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=org.itsallcode%3Aluava)
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=org.itsallcode%3Aluava&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=org.itsallcode%3Aluava)

This project allows executing Lua scripts from a Java application. It uses [Foreign Function & Memory API (JEP 454)](https://openjdk.org/jeps/454) for accessing the Lua C API.

## Development

### Native Interface

Build scripts generate native interface classes in `build/generated/sources/jextract` using [Jextract](https://github.com/openjdk/jextract). Scripts download and cache Jextract automatically during the build.

### Check for Dependency Updates

```sh
Expand Down
7 changes: 4 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ dependencies {

java {
toolchain {
def javaVersion = project.hasProperty('javaVersion') ? project.getProperty('javaVersion') : 22
def javaVersion = project.hasProperty('javaVersion') ? project.getProperty('javaVersion') : 23
languageVersion = JavaLanguageVersion.of(javaVersion)
}
}
Expand All @@ -41,7 +41,8 @@ jacocoTestReport {
sonarqube {
properties {
property "sonar.host.url", "https://sonarcloud.io"
property "sonar.organization", "kaklakariada-github"
property "sonar.organization", "itsallcode"
property "sonar.exclusions", "build/generated/sources/jextract/**"
}
}

Expand All @@ -66,7 +67,7 @@ testing {
}
}

OperatingSystem currentOs = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem
def currentOs = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem
task downloadJextract(type: DownloadTask) {
def getOs = {
if(currentOs.isMacOsX()) {
Expand Down
69 changes: 45 additions & 24 deletions src/main/java/org/itsallcode/luava/LowLevelLua.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@

import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.util.function.IntSupplier;
import java.util.logging.Logger;

import org.itsallcode.luava.ffi.Lua;
import org.itsallcode.luava.ffi.lua_KFunction;
import org.itsallcode.luava.ffi.lua_KFunction.Function;

class LowLevelLua implements AutoCloseable {
private static final Logger LOG = Logger.getLogger(LowLevelLua.class.getName());
private final Arena arena;
private final MemorySegment state;
final MemorySegment state;
private final LuaStack stack;

private LowLevelLua(final Arena arena, final MemorySegment state) {
LowLevelLua(final Arena arena, final MemorySegment state) {
this.arena = arena;
this.state = state;
this.stack = new LuaStack(state, arena);
Expand All @@ -24,34 +26,54 @@ static LowLevelLua create() {
return new LowLevelLua(arena, state);
}

LowLevelLua forState(final MemorySegment newState) {
return new LowLevelLua(arena, newState);
}

void openLibs() {
Lua.luaL_openlibs(state);
}

void pcall(final int nargs, final int nresults, final int errfunc, final long ctx) {
final Function function = (final MemorySegment l, final int status, final long ctx1) -> {
return 0;
};
pcall(nargs, nresults, errfunc, ctx, function);
void pcall(final int nargs, final int nresults) {
pcallk(nargs, nresults, 0, 0, null);
}

void pcall(final int nargs, final int nresults, final int errfunc) {
pcallk(nargs, nresults, errfunc, 0, null);
}

void pcall(final int nargs, final int nresults, final int errfunc, final long ctx,
/**
* This function behaves exactly like {@link #pcall(int, int, int, long)},
* except that it allows the
* called function to yield.
*
* @param nargs
* @param nresults
* @param msgHandler
* @param ctx
* @param upcallFunction
*/
private void pcallk(final int nargs, final int nresults, final int msgHandler, final long ctx,
final lua_KFunction.Function upcallFunction) {
final MemorySegment k = lua_KFunction.allocate(upcallFunction, arena);
final int error = Lua.lua_pcallk(state, nargs, nresults, errfunc, ctx, k);
if (error != 0) {
final String message = stack.toString(-1);
stack.pop(1);
throw new FunctionCallException("lua_pcallk", error, message);
}
LOG.info(
() -> "Calling function with " + nargs + " args and " + nresults + " results, msgHandler: " + msgHandler
+ " context: " + ctx);
final MemorySegment k = upcallFunction == null ? Lua.NULL() : lua_KFunction.allocate(upcallFunction, arena);
checkStatus("lua_pcallk", () -> Lua.lua_pcallk(state, nargs, nresults, msgHandler, ctx, k));
}

void loadString(final String chunk) {
final int error = Lua.luaL_loadstring(state, arena.allocateFrom(chunk));
if (error != 0) {
final String message = stack.toString(-1);
stack.pop(1);
throw new FunctionCallException("luaL_loadstring", error, message);
checkStatus("luaL_loadstring", () -> Lua.luaL_loadstring(state, arena.allocateFrom(chunk)));
}

void checkStatus(final String functionName, final IntSupplier nativeFunctionCall) {
final int status = nativeFunctionCall.getAsInt();
if (status != Lua.LUA_OK()) {
LOG.warning(
() -> "Lua API call '" + functionName + "' failed with status " + status
+ ": getting error message...");
final String message = stack.popString();
throw new FunctionCallException(functionName, status, message);
}
}

Expand All @@ -72,9 +94,8 @@ LuaTable table(final int idx) {
return new LuaTable(state, stack, arena, idx);
}

public LuaFunction function(final int idx) {
assertType(idx, LuaType.FUNCTION);
return new LuaFunction(state, stack, arena, idx);
public LuaFunction globalFunction(final String name) {
return new LuaFunction(this, name);
}

private void assertType(final int idx, final LuaType expectedType) {
Expand Down
89 changes: 65 additions & 24 deletions src/main/java/org/itsallcode/luava/LuaFunction.java
Original file line number Diff line number Diff line change
@@ -1,44 +1,85 @@
package org.itsallcode.luava;

import java.lang.foreign.Arena;
import static java.util.Collections.emptyList;

import java.lang.foreign.MemorySegment;
import java.util.Arrays;
import java.util.List;
import java.util.function.UnaryOperator;
import java.util.logging.Logger;

import org.itsallcode.luava.ffi.Lua;
import org.itsallcode.luava.ffi.lua_CFunction;

public class LuaFunction {
private static final Logger LOG = Logger.getLogger(LuaFunction.class.getName());
private final LowLevelLua lua;
private final String name;
private lua_CFunction.Function messageHandler;
private List<Object> argumentValues = emptyList();
private List<Class<?>> resultTypes = emptyList();

private final MemorySegment state;
private final LuaStack stack;
private final Arena arena;
private final int idx;
LuaFunction(final LowLevelLua lowLevelLua, final String name) {
this.lua = lowLevelLua;
this.name = name;
}

LuaFunction(final MemorySegment state, final LuaStack stack, final Arena arena, final int idx) {
this.state = state;
this.stack = stack;
this.arena = arena;
this.idx = idx;
@SuppressWarnings("java:S923") // Using varargs by intention
public LuaFunction argumentValues(final Object... argumentValues) {
this.argumentValues = Arrays.asList(argumentValues);
return this;
}

public void addArgInteger(final int value) {
stack.pushInteger(value);
@SuppressWarnings("java:S923") // Using varargs by intention
public LuaFunction resultTypes(final Class<?>... resultTypes) {
this.resultTypes = Arrays.asList(resultTypes);
return this;
}

public void call(final int nargs, final int nresults) {
call(nargs, nresults, 0, 0, Lua.NULL());
public LuaFunction messageUpdateHandler(final UnaryOperator<String> messageHandler) {
return messageHandler((final MemorySegment l) -> {
LOG.fine("Message handler: popping error message...");
final String msg = lua.stack().popString();
final String updatedMsg = messageHandler.apply(msg);
LOG.fine(() -> "Pushing updated error message '" + updatedMsg + "'");
lua.stack().pushString(updatedMsg);
return Lua.LUA_OK();
});
}

private void call(final int nargs, final int nresults, final int errfunc, final long ctx, final MemorySegment k) {
final int status = Lua.lua_pcallk(state, nargs, nresults, errfunc, ctx, k);
if (status != Lua.LUA_OK()) {
final String errorMessage = stack.toString(-1);
stack.pop(1);
throw new FunctionCallException("lua_pcallk", status, errorMessage);
public LuaFunction messageHandler(final lua_CFunction.Function messageHandler) {
this.messageHandler = messageHandler;
return this;
}

public List<Object> call() {
final int messageHandlerIdx = getMessageHandlerIdx();
lua.getGlobal(name);
pushArguments();
try {
lua.pcall(this.argumentValues.size(), this.resultTypes.size(), messageHandlerIdx);
return getResultValues();
} finally {
if (this.messageHandler != null) {
LOG.fine("Popping message handler");
lua.stack().pop();
}
}
}

public long getIntegerResult() {
final long value = stack.toInteger(-1);
stack.pop(1);
return value;
private void pushArguments() {
this.argumentValues.forEach(v -> lua.stack().pushObject(v));
}

private List<Object> getResultValues() {
return this.resultTypes.stream().map(t -> lua.stack().popObject(t)).toList();
}

private int getMessageHandlerIdx() {
if (messageHandler == null) {
return 0;
}
lua.stack().pushCFunction(messageHandler);
return lua.stack().getTop();
}
}
30 changes: 12 additions & 18 deletions src/main/java/org/itsallcode/luava/LuaInterpreter.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package org.itsallcode.luava;

public class LuaInterpreter implements AutoCloseable {
public final class LuaInterpreter implements AutoCloseable {

private final LowLevelLua lua;

public LuaInterpreter(final LowLevelLua lua) {
private LuaInterpreter(final LowLevelLua lua) {
this.lua = lua;
}

Expand All @@ -18,32 +18,28 @@ public void close() {
this.lua.close();
}

LuaStack stack() {
return lua.stack();
}

public String getGlobalString(final String name) {
lua.getGlobal(name);
final String value = lua.stack().toString(-1);
lua.stack().pop(1);
return value;
return lua.stack().popString();
}

public long getGlobalInteger(final String name) {
lua.getGlobal(name);
final long value = lua.stack().toInteger(-1);
lua.stack().pop(1);
return value;
return lua.stack().popInteger();
}

public double getGlobalNumber(final String name) {
lua.getGlobal(name);
final double value = lua.stack().toNumber(-1);
lua.stack().pop(1);
return value;
return lua.stack().popNumber();
}

public boolean getGlobalBoolean(final String name) {
lua.getGlobal(name);
final boolean value = lua.stack().toBoolean(-1);
lua.stack().pop(1);
return value;
return lua.stack().popBoolean();
}

public LuaTable getGlobalTable(final String name) {
Expand All @@ -52,8 +48,7 @@ public LuaTable getGlobalTable(final String name) {
}

public LuaFunction getGlobalFunction(final String name) {
lua.getGlobal(name);
return lua.function(-1);
return lua.globalFunction(name);
}

public void setGlobalString(final String name, final String value) {
Expand All @@ -78,7 +73,6 @@ public void setGlobalBoolean(final String name, final boolean value) {

public void exec(final String chunk) {
lua.loadString(chunk);
lua.pcall(0, 0, 0, 0);
lua.pcall(0, 0);
}

}
Loading

0 comments on commit 5419af8

Please sign in to comment.