package com.oracle.svm.jni.functions;
import static com.oracle.svm.jni.nativeapi.JNIVersion.JNI_VERSION_1_1;
import static com.oracle.svm.jni.nativeapi.JNIVersion.JNI_VERSION_1_2;
import static com.oracle.svm.jni.nativeapi.JNIVersion.JNI_VERSION_1_4;
import static com.oracle.svm.jni.nativeapi.JNIVersion.JNI_VERSION_1_6;
import static com.oracle.svm.jni.nativeapi.JNIVersion.JNI_VERSION_1_8;
import java.util.ArrayList;
import org.graalvm.compiler.serviceprovider.IsolateUtil;
import org.graalvm.nativeimage.Isolate;
import org.graalvm.nativeimage.LogHandler;
import org.graalvm.nativeimage.c.function.CEntryPoint;
import org.graalvm.nativeimage.c.struct.SizeOf;
import org.graalvm.nativeimage.c.type.CCharPointer;
import org.graalvm.nativeimage.c.type.CIntPointer;
import org.graalvm.nativeimage.c.type.CTypeConversion;
import org.graalvm.nativeimage.c.type.WordPointer;
import org.graalvm.word.Pointer;
import org.graalvm.word.WordFactory;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.c.function.CEntryPointActions;
import com.oracle.svm.core.c.function.CEntryPointErrors;
import com.oracle.svm.core.c.function.CEntryPointOptions;
import com.oracle.svm.core.c.function.CEntryPointOptions.NoEpilogue;
import com.oracle.svm.core.c.function.CEntryPointOptions.NoPrologue;
import com.oracle.svm.core.c.function.CEntryPointOptions.Publish;
import com.oracle.svm.core.c.function.CEntryPointSetup;
import com.oracle.svm.core.c.function.CEntryPointSetup.LeaveDetachThreadEpilogue;
import com.oracle.svm.core.c.function.CEntryPointSetup.LeaveTearDownIsolateEpilogue;
import com.oracle.svm.core.jdk.RuntimeSupport;
import com.oracle.svm.core.log.FunctionPointerLogHandler;
import com.oracle.svm.core.monitor.MonitorSupport;
import com.oracle.svm.core.option.RuntimeOptionParser;
import com.oracle.svm.core.thread.JavaThreads;
import com.oracle.svm.core.util.Utf8;
import com.oracle.svm.jni.JNIJavaVMList;
import com.oracle.svm.jni.JNIObjectHandles;
import com.oracle.svm.jni.JNIThreadLocalEnvironment;
import com.oracle.svm.jni.JNIThreadOwnedMonitors;
import com.oracle.svm.jni.functions.JNIFunctions.Support.JNIJavaVMEnterAttachThreadEnsureJavaThreadPrologue;
import com.oracle.svm.jni.functions.JNIFunctions.Support.JNIJavaVMEnterAttachThreadManualJavaThreadPrologue;
import com.oracle.svm.jni.functions.JNIInvocationInterface.Support.JNIGetEnvPrologue;
import com.oracle.svm.jni.nativeapi.JNIEnvironmentPointer;
import com.oracle.svm.jni.nativeapi.JNIErrors;
import com.oracle.svm.jni.nativeapi.JNIJavaVM;
import com.oracle.svm.jni.nativeapi.JNIJavaVMAttachArgs;
import com.oracle.svm.jni.nativeapi.JNIJavaVMInitArgs;
import com.oracle.svm.jni.nativeapi.JNIJavaVMOption;
import com.oracle.svm.jni.nativeapi.JNIJavaVMPointer;
import com.oracle.svm.jni.nativeapi.JNIVersion;
final class JNIInvocationInterface {
static class Exports {
@CEntryPoint(name = "JNI_GetCreatedJavaVMs")
@CEntryPointOptions(prologue = NoPrologue.class, epilogue = NoEpilogue.class, publishAs = Publish.SymbolOnly, include = CEntryPointOptions.NotIncludedAutomatically.class)
@Uninterruptible(reason = "No Java context.")
static int JNI_GetCreatedJavaVMs(JNIJavaVMPointer vmBuf, int bufLen, CIntPointer nVMs) {
JNIJavaVMList.gather(vmBuf, bufLen, nVMs);
return JNIErrors.JNI_OK();
}
static class JNICreateJavaVMPrologue {
@SuppressWarnings("unused")
static void enter(JNIJavaVMPointer vmBuf, JNIEnvironmentPointer penv, JNIJavaVMInitArgs vmArgs) {
if (!SubstrateOptions.SpawnIsolates.getValue()) {
int error = CEntryPointActions.enterIsolate((Isolate) CEntryPointSetup.SINGLE_ISOLATE_SENTINEL);
if (error == CEntryPointErrors.NO_ERROR) {
CEntryPointActions.leave();
CEntryPointActions.bailoutInPrologue(JNIErrors.JNI_EEXIST());
} else if (error != CEntryPointErrors.UNINITIALIZED_ISOLATE) {
CEntryPointActions.bailoutInPrologue(JNIErrors.JNI_EEXIST());
}
}
int error = CEntryPointActions.enterCreateIsolate(WordFactory.nullPointer());
if (error == CEntryPointErrors.NO_ERROR) {
} else if (error == CEntryPointErrors.UNSPECIFIED) {
CEntryPointActions.bailoutInPrologue(JNIErrors.JNI_ERR());
} else if (error == CEntryPointErrors.MAP_HEAP_FAILED || error == CEntryPointErrors.RESERVE_ADDRESS_SPACE_FAILED || error == CEntryPointErrors.INSUFFICIENT_ADDRESS_SPACE) {
CEntryPointActions.bailoutInPrologue(JNIErrors.JNI_ENOMEM());
} else {
error = -1000000000 - error;
if (error == JNIErrors.JNI_OK() || error >= -100) {
error = JNIErrors.JNI_ERR();
}
CEntryPointActions.bailoutInPrologue(error);
}
}
}
@CEntryPoint(name = "JNI_CreateJavaVM")
@CEntryPointOptions(prologue = JNICreateJavaVMPrologue.class, publishAs = Publish.SymbolOnly, include = CEntryPointOptions.NotIncludedAutomatically.class)
static int JNI_CreateJavaVM(JNIJavaVMPointer vmBuf, JNIEnvironmentPointer penv, JNIJavaVMInitArgs vmArgs) {
WordPointer javavmIdPointer = WordFactory.nullPointer();
if (vmArgs.isNonNull()) {
Pointer p = (Pointer) vmArgs.getOptions();
int count = vmArgs.getNOptions();
ArrayList<String> options = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
JNIJavaVMOption option = (JNIJavaVMOption) p.add(i * SizeOf.get(JNIJavaVMOption.class));
CCharPointer str = option.getOptionString();
if (str.isNonNull()) {
String optionString = CTypeConversion.toJavaString(option.getOptionString());
if (!FunctionPointerLogHandler.parseVMOption(optionString, option.getExtraInfo())) {
if (optionString.equals("_javavm_id")) {
javavmIdPointer = option.getExtraInfo();
} else {
options.add(optionString);
}
}
}
}
FunctionPointerLogHandler.afterParsingVMOptions();
RuntimeOptionParser.parseAndConsumeAllOptions(options.toArray(new String[0]));
}
JNIJavaVM javavm = JNIFunctionTables.singleton().getGlobalJavaVM();
JNIJavaVMList.addJavaVM(javavm);
if (javavmIdPointer.isNonNull()) {
long javavmId = IsolateUtil.getIsolateID();
javavmIdPointer.write(WordFactory.pointer(javavmId));
}
RuntimeSupport.getRuntimeSupport().addTearDownHook(new Runnable() {
@Override
public void run() {
JNIJavaVMList.removeJavaVM(javavm);
}
});
vmBuf.write(javavm);
penv.write(JNIThreadLocalEnvironment.getAddress());
return JNIErrors.JNI_OK();
}
@CEntryPoint(name = "JNI_GetDefaultJavaVMInitArgs")
@CEntryPointOptions(prologue = NoPrologue.class, epilogue = NoEpilogue.class, publishAs = Publish.SymbolOnly, include = CEntryPointOptions.NotIncludedAutomatically.class)
@Uninterruptible(reason = "No Java context")
static int JNI_GetDefaultJavaVMInitArgs(JNIJavaVMInitArgs vmArgs) {
int version = vmArgs.getVersion();
if (version == JNI_VERSION_1_8() || version == JNI_VERSION_1_6() || version == JNI_VERSION_1_4() || version == JNI_VERSION_1_2()) {
return JNIErrors.JNI_OK();
}
if (version == JNI_VERSION_1_1()) {
vmArgs.setVersion(JNI_VERSION_1_2());
}
return JNIErrors.JNI_ERR();
}
}
@CEntryPoint
@CEntryPointOptions(prologue = JNIJavaVMEnterAttachThreadManualJavaThreadPrologue.class, publishAs = Publish.NotPublished, include = CEntryPointOptions.NotIncludedAutomatically.class)
static int AttachCurrentThread(JNIJavaVM vm, JNIEnvironmentPointer penv, JNIJavaVMAttachArgs args) {
return Support.attachCurrentThread(vm, penv, args, false);
}
@CEntryPoint
@CEntryPointOptions(prologue = JNIJavaVMEnterAttachThreadManualJavaThreadPrologue.class, publishAs = Publish.NotPublished, include = CEntryPointOptions.NotIncludedAutomatically.class)
static int AttachCurrentThreadAsDaemon(JNIJavaVM vm, JNIEnvironmentPointer penv, JNIJavaVMAttachArgs args) {
return Support.attachCurrentThread(vm, penv, args, true);
}
@CEntryPoint
@CEntryPointOptions(prologue = JNIJavaVMEnterAttachThreadEnsureJavaThreadPrologue.class, epilogue = LeaveDetachThreadEpilogue.class, publishAs = Publish.NotPublished, include = CEntryPointOptions.NotIncludedAutomatically.class)
static int DetachCurrentThread(JNIJavaVM vm) {
int result = JNIErrors.JNI_OK();
if (!vm.equal(JNIFunctionTables.singleton().getGlobalJavaVM())) {
result = JNIErrors.JNI_ERR();
}
Support.releaseCurrentThreadOwnedMonitors();
return result;
}
@CEntryPoint
@CEntryPointOptions(prologue = JNIJavaVMEnterAttachThreadEnsureJavaThreadPrologue.class, epilogue = LeaveTearDownIsolateEpilogue.class, publishAs = Publish.NotPublished, include = CEntryPointOptions.NotIncludedAutomatically.class)
@SuppressWarnings("unused")
static int DestroyJavaVM(JNIJavaVM vm) {
JavaThreads.singleton().joinAllNonDaemons();
return JNIErrors.JNI_OK();
}
@CEntryPoint
@CEntryPointOptions(prologue = JNIGetEnvPrologue.class, publishAs = Publish.NotPublished, include = CEntryPointOptions.NotIncludedAutomatically.class)
@SuppressWarnings("unused")
static int GetEnv(JNIJavaVM vm, WordPointer env, int version) {
env.write(JNIThreadLocalEnvironment.getAddress());
return JNIErrors.JNI_OK();
}
static class Support {
static class JNIGetEnvPrologue {
static void enter(JNIJavaVM vm, WordPointer env, int version) {
if (vm.isNull() || env.isNull()) {
CEntryPointActions.bailoutInPrologue(JNIErrors.JNI_ERR());
}
if (version != JNI_VERSION_1_8() && version != JNI_VERSION_1_6() && version != JNI_VERSION_1_4() && version != JNI_VERSION_1_2() && version != JNI_VERSION_1_1()) {
env.write(WordFactory.nullPointer());
CEntryPointActions.bailoutInPrologue(JNIErrors.JNI_EVERSION());
}
if (!CEntryPointActions.isCurrentThreadAttachedTo(vm.getFunctions().getIsolate())) {
env.write(WordFactory.nullPointer());
CEntryPointActions.bailoutInPrologue(JNIErrors.JNI_EDETACHED());
}
if (CEntryPointActions.enterIsolate(vm.getFunctions().getIsolate()) != 0) {
CEntryPointActions.bailoutInPrologue(JNIErrors.JNI_ERR());
}
}
}
static int attachCurrentThread(JNIJavaVM vm, JNIEnvironmentPointer penv, JNIJavaVMAttachArgs args, boolean asDaemon) {
if (vm.equal(JNIFunctionTables.singleton().getGlobalJavaVM())) {
penv.write(JNIThreadLocalEnvironment.getAddress());
ThreadGroup group = null;
String name = null;
if (args.isNonNull() && args.getVersion() != JNIVersion.JNI_VERSION_1_1()) {
group = JNIObjectHandles.getObject(args.getGroup());
name = Utf8.utf8ToString(args.getName());
}
JavaThreads.ensureJavaThread(name, group, asDaemon);
return JNIErrors.JNI_OK();
}
return JNIErrors.JNI_ERR();
}
static void releaseCurrentThreadOwnedMonitors() {
JNIThreadOwnedMonitors.forEach((obj, depth) -> {
for (int i = 0; i < depth; i++) {
MonitorSupport.singleton().monitorExit(obj);
}
assert !Thread.holdsLock(obj);
});
}
public static JNIJavaVM getGlobalJavaVM() {
return JNIFunctionTables.singleton().getGlobalJavaVM();
}
}
}