diff --git a/src/Java.Interop/Java.Interop/JniRuntime.cs b/src/Java.Interop/Java.Interop/JniRuntime.cs index cd377f3f2..75b1b4485 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.cs @@ -165,8 +165,8 @@ protected JniRuntime (CreationOptions options) { if (options == null) throw new ArgumentNullException (nameof (options)); - if (options.InvocationPointer == IntPtr.Zero) - throw new ArgumentException ("options.InvocationPointer is null", nameof (options)); + if (options.InvocationPointer == IntPtr.Zero && options.EnvironmentPointer == IntPtr.Zero) + throw new ArgumentException ("Need either options.InvocationPointer or options.EnvironmentPointer!", nameof (options)); TrackIDs = options.TrackIDs; DestroyRuntimeOnDispose = options.DestroyRuntimeOnDispose; @@ -175,7 +175,12 @@ protected JniRuntime (CreationOptions options) NewObjectRequired = options.NewObjectRequired; JniVersion = options.JniVersion; - InvocationPointer = options.InvocationPointer; + + if (options.InvocationPointer == IntPtr.Zero && options.EnvironmentPointer != IntPtr.Zero) { + InvocationPointer = GetInvocationPointerFromEnvironmentPointer (options.EnvironmentPointer); + } else { + InvocationPointer = options.InvocationPointer; + } Invoker = CreateInvoker (InvocationPointer); SetValueManager (options); @@ -230,6 +235,26 @@ protected JniRuntime (CreationOptions options) #endif // !XA_JI_EXCLUDE } + static unsafe IntPtr GetInvocationPointerFromEnvironmentPointer (IntPtr envp) + { + IntPtr vm = IntPtr.Zero; +#if FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS + if (JniNativeMethods.GetJavaVM (envp, &vm) is int r && + r != JNI_OK) { + throw new InvalidOperationException ($"Could not obtain JavaVM* from JNIEnv*; JNIEnv::GetJavaVM() returned {r}!"); + } +#elif FEATURE_JNIENVIRONMENT_JI_PINVOKES + if (NativeMethods.java_interop_jnienv_get_java_vm (envp, out vm) is int r && + r != JNI_OK) { + throw new InvalidOperationException ($"Could not obtain JavaVM* from JNIEnv*; JNIEnv::GetJavaVM() returned {r}!"); + } +#else // !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES + throw new NotSupportedException ("Cannot obtain JavaVM* from JNIEnv*! " + + "Rebuild with FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS or FEATURE_JNIENVIRONMENT_JI_PINVOKES set!"); +#endif // !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES + return vm; + } + T SetRuntime (T value) where T : class, ISetRuntime { diff --git a/src/Java.Runtime.Environment/Java.Interop/JreRuntime.cs b/src/Java.Runtime.Environment/Java.Interop/JreRuntime.cs index aace91706..4d0d27a03 100644 --- a/src/Java.Runtime.Environment/Java.Interop/JreRuntime.cs +++ b/src/Java.Runtime.Environment/Java.Interop/JreRuntime.cs @@ -39,11 +39,7 @@ public class JreRuntimeOptions : JniRuntime.CreationOptions { public JreRuntimeOptions () { JniVersion = JniVersion.v1_2; - ClassPath = new Collection () { - Path.Combine ( - Path.GetDirectoryName (typeof (JreRuntimeOptions).Assembly.Location) ?? throw new NotSupportedException (), - "java-interop.jar"), - }; + ClassPath = new Collection (); } public JreRuntimeOptions AddOption (string option) @@ -80,7 +76,9 @@ static unsafe JreRuntimeOptions CreateJreVM (JreRuntimeOptions builder) { if (builder == null) throw new ArgumentNullException ("builder"); - if (string.IsNullOrEmpty (builder.JvmLibraryPath)) + if (builder.InvocationPointer == IntPtr.Zero && + builder.EnvironmentPointer == IntPtr.Zero && + string.IsNullOrEmpty (builder.JvmLibraryPath)) throw new InvalidOperationException ($"Member `{nameof (JreRuntimeOptions)}.{nameof (JreRuntimeOptions.JvmLibraryPath)}` must be set."); builder.LibraryHandler = JvmLibraryHandler.Create (); @@ -99,11 +97,21 @@ static unsafe JreRuntimeOptions CreateJreVM (JreRuntimeOptions builder) builder.ObjectReferenceManager = builder.ObjectReferenceManager ?? new ManagedObjectReferenceManager (builder.JniGlobalReferenceLogWriter, builder.JniLocalReferenceLogWriter); } - if (builder.InvocationPointer != IntPtr.Zero) + if (builder.InvocationPointer != IntPtr.Zero || builder.EnvironmentPointer != IntPtr.Zero) return builder; builder.LibraryHandler.LoadJvmLibrary (builder.JvmLibraryPath!); + if (!builder.ClassPath.Any (p => p.EndsWith ("java-interop.jar", StringComparison.OrdinalIgnoreCase))) { + var loc = typeof (JreRuntimeOptions).Assembly.Location; + var dir = string.IsNullOrEmpty (loc) ? null : Path.GetDirectoryName (loc); + var jij = string.IsNullOrEmpty (dir) ? null : Path.Combine (dir, "java-interop.jar"); + if (!File.Exists (jij)) { + throw new InvalidOperationException ($"`java-interop.jar` is required. Please add to `JreRuntimeOptions.ClassPath`. Tried to find it in `{jij}`."); + } + builder.ClassPath.Add (jij); + } + var args = new JavaVMInitArgs () { version = builder.JniVersion, nOptions = builder.Options.Count + 1, diff --git a/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs index f6f8c3858..865074ef6 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs @@ -36,6 +36,25 @@ public void JDK_OnlySupportsOneVM () Assert.Fail ("Expected NotSupportedException; got: {0}", e); } } + + [Test] + public void UseInvocationPointerOnNewThread () + { + var InvocationPointer = JniRuntime.CurrentRuntime.InvocationPointer; + + var t = new Thread (() => { + try { + var second = new JreRuntimeOptions () { + InvocationPointer = InvocationPointer, + }.CreateJreVM (); + } + catch (Exception e) { + Assert.Fail ("Expected no exception, got: {0}", e); + } + }); + t.Start (); + t.Join (); + } #endif // !__ANDROID__ [Test]