Skip to content

Commit 4078920

Browse files
committed
feat: java 25, graalvm reachability metadata
1 parent 29b3e1b commit 4078920

12 files changed

Lines changed: 746 additions & 18 deletions

File tree

.github/workflows/build-core.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ jobs:
1717
- uses: actions/checkout@v4
1818
with:
1919
submodules: recursive
20-
- name: Set up JDK 24 (for JNI)
20+
- name: Set up JDK 25 (for JNI)
2121
uses: actions/setup-java@v4
2222
with:
23-
java-version: '24'
24-
distribution: 'temurin'
23+
java-version: '25'
24+
distribution: 'graalvm'
2525
- name: Setup Gradle
2626
uses: gradle/actions/setup-gradle@v3
2727
- name: Download jextract

.github/workflows/build-natives.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ jobs:
2626
- uses: actions/checkout@v4
2727
with:
2828
submodules: recursive
29-
- name: Set up JDK 24 (for JNI)
29+
- name: Set up JDK 25 (for JNI)
3030
uses: actions/setup-java@v4
3131
with:
32-
java-version: '24'
33-
distribution: 'temurin'
32+
java-version: '25'
33+
distribution: 'graalvm'
3434
- name: Setup Gradle
3535
uses: gradle/actions/setup-gradle@v3
3636
- name: Setup cmake

build.gradle.kts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ tasks.withType<JavaCompile> {
4848
tasks.javadoc {
4949
(options as StandardJavadocDocletOptions).run {
5050
encoding = "UTF-8"
51-
addStringOption("source", "23")
51+
addStringOption("source", "25")
5252
}
5353
}
5454

@@ -61,8 +61,8 @@ java {
6161
withJavadocJar()
6262

6363
toolchain {
64-
languageVersion.set(JavaLanguageVersion.of(24))
65-
vendor.set(JvmVendorSpec.ADOPTIUM)
64+
languageVersion.set(JavaLanguageVersion.of(25))
65+
vendor.set(JvmVendorSpec.GRAAL_VM)
6666
}
6767
}
6868

buildSrc/build.gradle.kts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,13 @@ repositories {
66
mavenCentral()
77
gradlePluginPortal()
88
}
9+
10+
java {
11+
// toolchain {
12+
// languageVersion.set(JavaLanguageVersion.of(25))
13+
// }
14+
}
15+
16+
kotlin {
17+
jvmToolchain(24)
18+
}

example/build.gradle.kts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
plugins {
2+
java
3+
application
4+
}
5+
6+
repositories {
7+
mavenCentral()
8+
}
9+
10+
dependencies {
11+
implementation(project(":"))
12+
implementation(project(":native"))
13+
}
14+
15+
java {
16+
toolchain {
17+
languageVersion.set(JavaLanguageVersion.of(25))
18+
vendor.set(JvmVendorSpec.GRAAL_VM)
19+
}
20+
}
21+
22+
application {
23+
mainClass = "net.hollowcube.luau.ForceLoadAll"
24+
applicationDefaultJvmArgs = listOf("-agentlib:native-image-agent=config-output-dir=./agent-out/")
25+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package net.hollowcube.luau;
2+
3+
import net.hollowcube.luau.compiler.LuauCompiler;
4+
5+
import java.util.ArrayList;
6+
import java.util.List;
7+
import java.util.Map;
8+
9+
public class Example {
10+
static void main() throws Exception {
11+
var source = """
12+
print('hello from lua')
13+
14+
local arr = m2.newarray()
15+
arr:push(1)
16+
arr:push(2)
17+
m2.show(arr)
18+
19+
print(m2.add(1, 2))
20+
print(m2.sub(1, 2))
21+
abc()
22+
""";
23+
var bytecode = LuauCompiler.DEFAULT.compile(source);
24+
25+
LuaState global = LuaState.newState();
26+
try {
27+
global.openLibs();
28+
29+
global.newMetaTable("myarray");
30+
global.pushString("__index");
31+
global.pushValue(-2);
32+
global.setTable(-3);
33+
global.registerLib(null, Map.of(
34+
"push", state -> {
35+
List<Integer> arr = (List<Integer>) state.checkUserDataArg(1, "myarray");
36+
int value = state.checkIntegerArg(2);
37+
arr.add(value);
38+
return 0;
39+
}
40+
));
41+
42+
global.registerLib("m2", Map.of(
43+
"add", state -> {
44+
int left = state.checkIntegerArg(1);
45+
int right = state.checkIntegerArg(2);
46+
state.pushInteger(left + right);
47+
return 1;
48+
},
49+
"sub", state -> {
50+
int left = state.checkIntegerArg(1);
51+
int right = state.checkIntegerArg(2);
52+
state.pushInteger(left - right);
53+
// state.error("error from java");
54+
return 1;
55+
},
56+
"newarray", state -> {
57+
58+
List<Integer> arr = new ArrayList<>();
59+
state.newUserData(arr);
60+
61+
// Assign the metatable. todo: the library should just handle this IMO
62+
state.getMetaTable("myarray");
63+
state.setMetaTable(-2);
64+
65+
return 1;
66+
},
67+
"show", state -> {
68+
List<Integer> arr = (List<Integer>) state.checkUserDataArg(1, "myarray");
69+
System.out.println("array: " + arr);
70+
return 0;
71+
}
72+
));
73+
global.pushCFunction(_ -> {
74+
System.out.println("hello from java");
75+
return 0;
76+
}, "abc");
77+
global.setGlobal("abc");
78+
79+
// After this point it is invalid to do any setglobal calls.
80+
// We should check this in java land because it segfaults.
81+
global.sandbox();
82+
83+
LuaState thread = global.newThread();
84+
thread.sandboxThread();
85+
// Now ready for running untrusted code.
86+
thread.load("main.lua", bytecode);
87+
thread.pcall(0, 0);
88+
89+
global.pop(1); // the thread was added to the stack, remove it so that it can be garbage collected.
90+
91+
} finally {
92+
global.close();
93+
}
94+
}
95+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package net.hollowcube.luau;
2+
3+
import net.hollowcube.luau.compiler.LuauCompiler;
4+
import net.hollowcube.luau.internal.compiler.lua_CompileOptions;
5+
import net.hollowcube.luau.internal.compiler.luacode_h;
6+
import net.hollowcube.luau.internal.vm.*;
7+
8+
import java.lang.foreign.Arena;
9+
import java.util.*;
10+
11+
public class ForceLoadAll {
12+
static void main() {
13+
var _ = LuauCompiler.DEFAULT;
14+
var _ = LuaState.LIGHTUSERDATA_TAG_LIMIT;
15+
16+
var classes = Set.of(
17+
lua_CompileOptions.class,
18+
luacode_h.class,
19+
lua_Alloc.class,
20+
lua_Callbacks.class,
21+
lua_CFunction.class,
22+
lua_h.class,
23+
lua_newuserdatadtor$dtor.class,
24+
luaL_Reg.class,
25+
lualib_h.class
26+
);
27+
var upcalls = new HashMap<Class<?>, Object>();
28+
upcalls.put(lua_CFunction.class, (lua_CFunction.Function) (_) -> unreachable());
29+
upcalls.put(lua_Alloc.class, (lua_Alloc.Function) (_, _, _, _) -> unreachable());
30+
upcalls.put(lua_newuserdatadtor$dtor.class, (lua_newuserdatadtor$dtor.Function) (_) -> unreachable());
31+
upcalls.put(lua_Callbacks.onallocate.class, (lua_Callbacks.onallocate.Function) (_, _, _) -> unreachable());
32+
upcalls.put(lua_Callbacks.debugprotectederror.class,
33+
(lua_Callbacks.debugprotectederror.Function) (_) -> unreachable());
34+
upcalls.put(lua_Callbacks.debuginterrupt.class,
35+
(lua_Callbacks.debuginterrupt.Function) (_, _) -> unreachable());
36+
upcalls.put(lua_Callbacks.debugstep.class, (lua_Callbacks.debugstep.Function) (_, _) -> unreachable());
37+
upcalls.put(lua_Callbacks.debugbreak.class, (lua_Callbacks.debugbreak.Function) (_, _) -> unreachable());
38+
upcalls.put(lua_Callbacks.useratom.class, (lua_Callbacks.useratom.Function) (_, _) -> unreachable());
39+
upcalls.put(lua_Callbacks.userthread.class, (lua_Callbacks.userthread.Function) (_, _) -> unreachable());
40+
upcalls.put(lua_Callbacks.panic.class, (lua_Callbacks.panic.Function) (_, _) -> unreachable());
41+
upcalls.put(lua_Callbacks.interrupt.class, (lua_Callbacks.interrupt.Function) (_, _) -> unreachable());
42+
43+
var toLoad = new ArrayDeque<>(classes);
44+
while (!toLoad.isEmpty()) {
45+
var classToLoad = toLoad.pop();
46+
try {
47+
System.out.println("load: " + classToLoad.getName());
48+
var theClass = Class.forName(classToLoad.getName());
49+
toLoad.addAll(List.of(theClass.getDeclaredClasses()));
50+
51+
for (var field : theClass.getDeclaredFields()) {
52+
if (!field.getName().equals("UP$MH")) continue;
53+
54+
var functionClass = theClass.getDeclaredClasses()[0];
55+
var instance = Objects.requireNonNull(upcalls.get(theClass),
56+
"no upcall for " + theClass.getName());
57+
try (var arena = Arena.ofConfined()) {
58+
theClass.getDeclaredMethod("allocate", functionClass, Arena.class)
59+
.invoke(null, instance, arena);
60+
}
61+
}
62+
} catch (Exception e) {
63+
e.printStackTrace();
64+
}
65+
}
66+
}
67+
68+
private static <T> T unreachable() {
69+
throw new RuntimeException("unreachable");
70+
}
71+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists

native/build.gradle.kts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,14 @@ tasks.register("luauStaticToShared") {
3939
val cmakeLists = buildProjectDir.resolve("luau/CMakeLists.txt")
4040
val luacodeHeader = buildProjectDir.resolve("luau/Compiler/include/luacode.h")
4141
val luacodeSource = buildProjectDir.resolve("luau/Compiler/src/lcode.cpp")
42-
inputs.files(cmakeLists, luacodeHeader, luacodeSource)
42+
43+
// Input files from the original project directory (source of truth)
44+
inputs.files(
45+
layout.projectDirectory.file("luau/CMakeLists.txt"),
46+
layout.projectDirectory.file("luau/Compiler/include/luacode.h"),
47+
layout.projectDirectory.file("luau/Compiler/src/lcode.cpp")
48+
)
49+
// Output files in the build directory (modified versions)
4350
outputs.files(cmakeLists, luacodeHeader, luacodeSource)
4451

4552
doLast {
@@ -62,8 +69,17 @@ tasks.register<Exec>("prepNative") {
6269
workingDir = file(layout.buildDirectory).resolve("cmake")
6370
standardOutput = System.out
6471

65-
inputs.dir(buildProjectDir)
66-
outputs.dir(file(layout.buildDirectory))
72+
// More specific inputs - only the modified source files and CMakeLists.txt
73+
inputs.files(
74+
buildProjectDir.resolve("luau/CMakeLists.txt"),
75+
buildProjectDir.resolve("CMakeLists.txt")
76+
)
77+
inputs.dir(buildProjectDir.resolve("luau")).withPathSensitivity(PathSensitivity.RELATIVE)
78+
inputs.dir(buildProjectDir.resolve("src")).withPathSensitivity(PathSensitivity.RELATIVE)
79+
80+
// Output specifically to cmake directory to avoid circular dependencies
81+
outputs.dir(workingDir)
82+
outputs.file(workingDir.resolve("CMakeCache.txt"))
6783

6884
doFirst { mkdir(workingDir) }
6985

@@ -91,8 +107,16 @@ tasks.register<Exec>("buildNative") {
91107
workingDir = file(layout.buildDirectory).resolve("cmake")
92108
standardOutput = System.out
93109

94-
inputs.dir(workingDir)
95-
outputs.dir(workingDir.resolve("lib"))
110+
inputs.file(workingDir.resolve("CMakeCache.txt"))
111+
inputs.files(workingDir.resolve("Makefile")).optional()
112+
113+
// More specific outputs based on the actual library files we expect
114+
val libDir = if (getOsName() == "windows") {
115+
workingDir.resolve("lib/${buildType}")
116+
} else {
117+
workingDir.resolve("lib")
118+
}
119+
outputs.dir(libDir)
96120

97121
val cmake: String? by project.extra
98122
commandLine(

settings.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
plugins {
2-
id("org.gradle.toolchains.foojay-resolver-convention").version("0.10.0")
2+
id("org.gradle.toolchains.foojay-resolver-convention").version("1.0.0")
33
}
44

55
rootProject.name = "luau-java"
66
include("native")
7+
include("example")
78

0 commit comments

Comments
 (0)