Skip to content

Commit ed2ce35

Browse files
Create JVM with options
1 parent 14aabfa commit ed2ce35

File tree

7 files changed

+88
-30
lines changed

7 files changed

+88
-30
lines changed

lib/java/os-environment/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
requires org.apache.commons.io;
1010

1111
exports org.enso.os.environment;
12+
exports org.enso.os.environment.jni;
1213
exports org.enso.os.environment.chdir;
1314
exports org.enso.os.environment.trash;
1415
exports org.enso.os.environment.directories;

lib/java/os-environment/src/main/java/org/enso/os/environment/jni/JNI.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,25 @@
1818
import org.graalvm.word.PointerBase;
1919

2020
@CContext(JNIDirectives.class)
21-
final class JNI {
21+
public final class JNI {
2222
@CConstant
2323
static native int JNI_OK();
2424

2525
@CConstant
2626
static native int JNI_ERR();
2727

28-
/* unknown error */
2928
@CConstant
3029
static native int JNI_EDETACHED();
3130

32-
/* thread detached from the VM */
3331
@CConstant
3432
static native int JNI_EVERSION();
3533

36-
/* JNI version error */
3734
@CConstant
3835
static native int JNI_ENOMEM();
3936

40-
/* not enough memory */
4137
@CConstant
4238
static native int JNI_EEXIST();
4339

44-
/* VM already created */
4540
@CConstant
4641
static native int JNI_EINVAL();
4742

lib/java/os-environment/src/main/java/org/enso/os/environment/jni/JNINativeInterface.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
@CContext(value = JNIDirectives.class)
99
@CStruct(value = "JNINativeInterface_", addStructKeyword = true)
10-
interface JNINativeInterface extends PointerBase {
10+
public interface JNINativeInterface extends PointerBase {
1111

1212
@CField(value = "NewString")
1313
JNI.NewString getNewString();

lib/java/os-environment/src/main/java/org/enso/os/environment/jni/JVM.java

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,20 @@
22

33
import org.enso.common.Platform;
44
import org.graalvm.nativeimage.StackValue;
5+
import org.graalvm.nativeimage.UnmanagedMemory;
6+
import org.graalvm.nativeimage.c.struct.SizeOf;
7+
import org.graalvm.nativeimage.c.type.CTypeConversion;
58
import org.graalvm.word.WordFactory;
69

10+
/** Represents a JVM inside of current process. */
711
public final class JVM {
812
private final JNIBoot.JNICreateJavaVMPointer createJvmFn;
13+
private final String[] options;
914
private JNI.JNIEnv env = WordFactory.nullPointer();
1015

11-
JVM(JNIBoot.JNICreateJavaVMPointer factory) {
16+
JVM(JNIBoot.JNICreateJavaVMPointer factory, String[] options) {
1217
this.createJvmFn = factory;
18+
this.options = options;
1319
}
1420

1521
/**
@@ -19,11 +25,13 @@ public final class JVM {
1925
* @param javaHome path where the JDK is installed
2026
* @return new instance of the JVM
2127
*/
22-
public static JVM create(String javaHome) {
23-
return switch (Platform.getOperatingSystem()) {
24-
case WINDOWS -> WindowsJVM.createImpl(javaHome);
25-
case LINUX, MACOS -> PosixJVM.createImpl(javaHome);
26-
};
28+
public static JVM create(String javaHome, String... options) {
29+
var createJvmFn =
30+
switch (Platform.getOperatingSystem()) {
31+
case WINDOWS -> WindowsJVM.createImpl(javaHome);
32+
case LINUX, MACOS -> PosixJVM.createImpl(javaHome);
33+
};
34+
return new JVM(createJvmFn, options);
2735
}
2836

2937
/**
@@ -40,18 +48,31 @@ public final JNI.JNIEnv env() {
4048

4149
private synchronized JNI.JNIEnv initializeEnv() {
4250
var jvmArgs = StackValue.get(JNIBoot.Args.class);
43-
jvmArgs.nOptions(0);
44-
var options = StackValue.get(1, JNIBoot.Option.class);
45-
jvmArgs.options(options);
51+
jvmArgs.nOptions(options.length);
52+
var sizeOfOption = SizeOf.get(JNIBoot.Option.class);
53+
JNIBoot.Option jvmOpts = UnmanagedMemory.calloc(options.length * sizeOfOption);
54+
var holder = new CTypeConversion.CCharPointerHolder[options.length];
55+
for (var i = 0; i < options.length; i++) {
56+
holder[i] = CTypeConversion.toCString(options[i]);
57+
var nth = jvmOpts.addressOf(i);
58+
nth.setOptionString(holder[i].get());
59+
nth.setExtraInfo(WordFactory.nullPointer());
60+
}
61+
jvmArgs.options(jvmOpts);
4662
jvmArgs.version(JNI.JNI_VERSION_10());
47-
jvmArgs.nOptions(0);
4863
jvmArgs.ignoreUnrecognized(false);
4964

5065
var jvmPtr = StackValue.get(JNI.JavaVMPointer.class);
5166
var envPtr = StackValue.get(JNI.JNIEnvPointer.class);
5267

5368
int res = createJvmFn.call(jvmPtr, envPtr, jvmArgs);
54-
assert res == 0;
69+
assert res == 0 : "Error creating JVM: " + res;
70+
71+
for (var i = 0; i < options.length; i++) {
72+
holder[i].close();
73+
}
74+
UnmanagedMemory.free(jvmOpts);
75+
5576
return envPtr.readJNIEnv();
5677
}
5778
}

lib/java/os-environment/src/main/java/org/enso/os/environment/jni/PosixJVM.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,7 @@
1212

1313
@CContext(PosixJVM.Direct.class)
1414
final class PosixJVM {
15-
16-
static JVM createImpl(String javaHome) {
17-
var createJvmFn = findCreateJavaSymbol(javaHome);
18-
return new JVM(createJvmFn);
19-
}
20-
21-
private static JNIBoot.JNICreateJavaVMPointer findCreateJavaSymbol(String javaHome) {
15+
static JNIBoot.JNICreateJavaVMPointer createImpl(String javaHome) {
2216
var libJvmPath = findDynamicLibrary(javaHome).getPath();
2317
try (var libPath = CTypeConversion.toCString(libJvmPath);
2418
var createJvm = CTypeConversion.toCString("JNI_CreateJavaVM")) {

lib/java/os-environment/src/main/java/org/enso/os/environment/jni/WindowsJVM.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,14 @@
1515

1616
@CContext(WindowsJVM.Direct.class)
1717
final class WindowsJVM {
18-
static JVM createImpl(String javaHome) {
18+
static JNICreateJavaVMPointer createImpl(String javaHome) {
1919
var dllPath = findDynamicLibrary(javaHome).getPath();
2020

2121
try (var libPath = CTypeConversion.toCString(dllPath);
2222
var createJvm = CTypeConversion.toCString("JNI_CreateJavaVM")) {
2323
var dll = LoadLibraryA(libPath.get());
2424
assert dll.isNonNull();
25-
JNICreateJavaVMPointer cStringCreateJvm = GetProcAddress(dll, createJvm.get());
26-
27-
return new JVM(cStringCreateJvm);
25+
return GetProcAddress(dll, createJvm.get());
2826
}
2927
}
3028

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

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,25 @@
33
import static org.junit.Assert.assertEquals;
44
import static org.junit.Assert.assertTrue;
55

6+
import org.enso.os.environment.jni.JNI.JValue;
67
import org.graalvm.nativeimage.StackValue;
78
import org.graalvm.nativeimage.c.type.CTypeConversion;
89
import org.junit.Test;
910

1011
public class LoadClassTest {
1112
private static final String PATH = System.getProperty("java.home");
13+
private static JVM jvm;
14+
15+
private static JNI.JNIEnv env() {
16+
if (jvm == null) {
17+
jvm = JVM.create(PATH, "-Dsay=Ahoj");
18+
}
19+
return jvm.env();
20+
}
1221

1322
@Test
1423
public void invokeParseShortMethod() {
15-
var env = JVM.create(PATH).env();
24+
var env = env();
1625
assertTrue("JNI created", env.isNonNull());
1726

1827
var findClassFn = env.getFunctions().getFindClass();
@@ -38,4 +47,44 @@ public void invokeParseShortMethod() {
3847
assertEquals(345, res);
3948
}
4049
}
50+
51+
@Test
52+
public void setSystemProperty() {
53+
var env = env();
54+
assertTrue("JNI created", env.isNonNull());
55+
56+
var findClassFn = env.getFunctions().getFindClass();
57+
var getStaticMethodIDFn = env.getFunctions().getGetStaticMethodID();
58+
var newStringFn = env.getFunctions().getNewStringUTF();
59+
var strLengthFn = env.getFunctions().getGetStringUTFLength();
60+
var strCharsFn = env.getFunctions().getGetStringUTFChars();
61+
var strReleaseFn = env.getFunctions().getReleaseStringUTFChars();
62+
var callStaticMethodFn = env.getFunctions().getCallStaticObjectMethodA();
63+
64+
try (var systemName = CTypeConversion.toCString("java/lang/System");
65+
var getPropertyName = CTypeConversion.toCString("getProperty");
66+
var getPropertySig = CTypeConversion.toCString("(Ljava/lang/String;)Ljava/lang/String;");
67+
var propName = CTypeConversion.toCString("say"); ) {
68+
var System = findClassFn.call(env, systemName.get());
69+
70+
assertTrue("System class is loaded", System.isNonNull());
71+
72+
var valueOf =
73+
getStaticMethodIDFn.call(env, System, getPropertyName.get(), getPropertySig.get());
74+
assertTrue("getProperty method found", valueOf.isNonNull());
75+
76+
var args = StackValue.get(JNI.JValue.class);
77+
var str = newStringFn.call(env, propName.get());
78+
args.setJObject(str);
79+
var res = (JNI.JString) callStaticMethodFn.call(env, System, valueOf, args);
80+
assertTrue("There should be a property 'say' defined", res.isNonNull());
81+
var len = strLengthFn.call(env, res);
82+
assertEquals("'Ahoj' has four letters", 4, len);
83+
var valueFalse = StackValue.get(JValue.class);
84+
valueFalse.setBoolean(false);
85+
var chars = strCharsFn.call(env, res, valueFalse);
86+
assertEquals("Ahoj", CTypeConversion.toJavaString(chars));
87+
strReleaseFn.call(env, res, chars);
88+
}
89+
}
4190
}

0 commit comments

Comments
 (0)