Skip to content

Commit d86898a

Browse files
Callback to native executable from HotSpot JVM (#13076)
1 parent 6e5816a commit d86898a

File tree

4 files changed

+89
-23
lines changed

4 files changed

+89
-23
lines changed

build.sbt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2666,9 +2666,13 @@ def customFrgaalJavaCompilerSettings(targetJdk: String) = {
26662666
// Ensure that our tooling uses the right Java version for checking the code.
26672667
Compile / javacOptions ++= Seq(
26682668
"-source",
2669-
frgaalSourceLevel,
2670-
"--enable-preview"
2671-
)
2669+
frgaalSourceLevel
2670+
) ++
2671+
(if (Integer.parseInt(targetJdk) <= 21) {
2672+
Seq("--enable-preview")
2673+
} else {
2674+
Seq("-target", "21")
2675+
})
26722676
)
26732677
}
26742678

@@ -4274,7 +4278,7 @@ lazy val `os-environment` =
42744278
.in(file("lib/java/os-environment"))
42754279
.enablePlugins(JPMSPlugin)
42764280
.settings(
4277-
frgaalJavaCompilerSetting,
4281+
customFrgaalJavaCompilerSettings("24"),
42784282
scalaModuleDependencySetting,
42794283
libraryDependencies ++= slf4jApi ++ Seq(
42804284
"org.graalvm.sdk" % "nativeimage" % graalMavenPackagesVersion % "provided",
Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,33 @@
11
package org.enso.os.environment.jni;
22

3-
import java.io.File;
4-
import java.io.FileWriter;
3+
import java.lang.foreign.Arena;
4+
import java.lang.foreign.FunctionDescriptor;
5+
import java.lang.foreign.Linker;
6+
import java.lang.foreign.MemorySegment;
7+
import java.lang.foreign.ValueLayout;
8+
import java.lang.invoke.MethodHandle;
59
import java.math.BigInteger;
610

7-
public final class TestMain {
11+
final class TestMain {
812
private TestMain() {}
913

10-
public static void main(String... args) throws Exception {
11-
var out = new File(args[0]);
12-
var n = Integer.parseInt(args[1]);
13-
try (java.io.FileWriter os = new FileWriter(out)) {
14-
os.write(factorial(n).toString());
15-
}
14+
static void main(String... args) throws Throwable {
15+
var jvmIsolate = Long.parseLong(args[0]);
16+
var fnCallbackAddress = MemorySegment.ofAddress(Long.parseLong(args[1]));
17+
var fnDescriptor =
18+
FunctionDescriptor.of(
19+
ValueLayout.JAVA_BOOLEAN,
20+
ValueLayout.JAVA_LONG,
21+
ValueLayout.JAVA_LONG,
22+
ValueLayout.ADDRESS);
23+
var fnHandle = Linker.nativeLinker().downcallHandle(fnCallbackAddress, fnDescriptor);
24+
var n = Integer.parseInt(args[2]);
25+
26+
var res = factorial(n).toString();
27+
reportResultToSvmIsolate(jvmIsolate, fnHandle, n, res);
1628
}
1729

18-
static BigInteger factorial(int n) {
30+
static BigInteger factorial(long n) {
1931
var acc = BigInteger.valueOf(1);
2032
for (; ; ) {
2133
acc = acc.multiply(BigInteger.valueOf(n));
@@ -25,4 +37,19 @@ static BigInteger factorial(int n) {
2537
}
2638
return acc;
2739
}
40+
41+
private static void reportResultToSvmIsolate(
42+
long jvmIsolate, MethodHandle fn, long key, String value) throws Throwable {
43+
try (var arena = Arena.ofConfined()) {
44+
var valueStr = arena.allocateFrom(value);
45+
Object result = fn.invoke(jvmIsolate, key, valueStr);
46+
if (!Boolean.TRUE.equals(result)) {
47+
var ex =
48+
new IllegalStateException(
49+
"Not correct result for " + key + " and value " + value + " result " + result);
50+
ex.printStackTrace();
51+
throw ex;
52+
}
53+
}
54+
}
2855
}

lib/java/os-environment/src/test/java/org/enso/os/environment/jni/LoadClassTest.java

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,17 @@
44
import static org.junit.Assert.assertTrue;
55

66
import java.io.File;
7-
import java.nio.file.Files;
7+
import java.util.HashMap;
8+
import java.util.Map;
89
import java.util.Random;
910
import org.enso.os.environment.jni.JNI.JValue;
11+
import org.graalvm.nativeimage.CurrentIsolate;
12+
import org.graalvm.nativeimage.IsolateThread;
1013
import org.graalvm.nativeimage.StackValue;
14+
import org.graalvm.nativeimage.c.function.CEntryPoint;
15+
import org.graalvm.nativeimage.c.function.CEntryPointLiteral;
16+
import org.graalvm.nativeimage.c.function.CFunctionPointer;
17+
import org.graalvm.nativeimage.c.type.CCharPointer;
1118
import org.graalvm.nativeimage.c.type.CTypeConversion;
1219
import org.junit.Test;
1320

@@ -27,6 +34,7 @@ private static JVM jvm() {
2734
JVM.create(
2835
path,
2936
"--module-path=" + MODULE_PATH,
37+
"--enable-native-access=org.enso.os.environment",
3038
"-Djdk.module.main=org.enso.os.environment",
3139
"-Dsay=Ahoj");
3240
}
@@ -108,14 +116,35 @@ public void setSystemProperty() {
108116

109117
@Test
110118
public void executeMainClass() throws Exception {
111-
var out = File.createTempFile("check-main", ".log");
119+
var jvmIsolate = CurrentIsolate.getCurrentThread().rawValue();
120+
var callbackFn = CALLBACK_FN.getFunctionPointer().rawValue();
112121
var gen = new Random();
122+
var n = 0L;
113123
for (var i = 0; i < 5; i++) {
114-
var n = gen.nextInt(10000, 20000);
115-
jvm().executeMain("org/enso/os/environment/jni/TestMain", out.getPath(), "" + n);
116-
var content = Files.readString(out.toPath());
117-
assertEquals("Factorial of " + n + " is the same", TestMain.factorial(n).toString(), content);
118-
out.delete();
124+
n += gen.nextLong(1000, 5000);
125+
var mainClass = "org/enso/os/environment/jni/TestMain";
126+
jvm().executeMain(mainClass, "" + jvmIsolate, "" + callbackFn, "" + n);
119127
}
128+
assertEquals("Five results found: " + CORRECT_RESULTS, 5, CORRECT_RESULTS.size());
120129
}
130+
131+
private static final Map<Long, String> CORRECT_RESULTS = new HashMap<>();
132+
133+
@CEntryPoint
134+
private static boolean acceptResultFromHotSpotJvm(
135+
IsolateThread threadId, long n, CCharPointer resultStr) {
136+
var result = CTypeConversion.toJavaString(resultStr);
137+
var ownResult = TestMain.factorial(n).toString();
138+
assertEquals("fac(" + n + ") is correct in both JVMs", ownResult, result);
139+
CORRECT_RESULTS.put(n, result);
140+
return ownResult.equals(result);
141+
}
142+
143+
private static final CEntryPointLiteral<CFunctionPointer> CALLBACK_FN =
144+
CEntryPointLiteral.create(
145+
LoadClassTest.class,
146+
"acceptResultFromHotSpotJvm",
147+
IsolateThread.class,
148+
long.class,
149+
CCharPointer.class);
121150
}

project/FrgaalJavaCompiler.scala

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,14 @@ object FrgaalJavaCompiler {
8484
shouldCompileModuleInfo = shouldCompileModuleInfo,
8585
shouldNotLimitModules = shouldNotLimitModules
8686
)
87-
val javaTools = sbt.internal.inc.javac
88-
.JavaTools(frgaalJavac, sbtCompilers.javaTools.javadoc())
87+
88+
var javac = if (Integer.parseInt(javaVersion) <= 21) {
89+
frgaalJavac
90+
} else {
91+
sbtCompilers.javaTools.javac()
92+
}
93+
val javadoc = sbtCompilers.javaTools.javadoc()
94+
val javaTools = sbt.internal.inc.javac.JavaTools(javac, javadoc)
8995
xsbti.compile.Compilers.of(sbtCompilers.scalac, javaTools)
9096
}
9197

0 commit comments

Comments
 (0)