Skip to content

Commit

Permalink
[Java.Interop] Optional "Standalone" Build Config (#1049)
Browse files Browse the repository at this point in the history
Context: 16e1ecd
Context: 312fbf4

It occurs to me that it would be easier for "external" developers to
use `Java.Interop.dll` if it didn't require building and distributing
any native libraries.  Furthermore, as of commit 312fbf4
(C#9 function pointer backend), it's *plausible* to make that work.

Let's do so.

Add a new `$(Standalone)` "configuration" property to
`src\Java.Interop\Java.Interop.csproj`; when True, the
`FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS` compiler define is set
instead of the `FEATURE_JNIENVIRONMENT_JI_PINVOKES` define.
This enables `JniEnvironment` to use C#9 function pointers instead of
P/Invokes to invoke the `JNIEnv` function pointers.

	% dotnet build src/Java.Interop/Java.Interop.csproj -p:Standalone=True
	% ikdasm src/Java.Interop/obj/Debug/net7.0/Java.Interop.dll | grep pinvoke
	# no matches
	# If `-p:Standalone=True` isn't set, then there are 188 matches.

Update `Java.Interop.dll` to compile when
`FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS` is set.

!!ABI BREAK!! `[Obsolete]` the method
`JniRuntime.GetAvailableInvocationPointers()`.  In retrospect this
never should have been exposed at this level of the stack, and its
existence was responsible for "really really bizarre" .NET Android
[app crashes][0] (due to static constructor orderings) when
*sometimes* `JniRuntime.Current` wasn't set "early enough":

	D Mono    : AOT: FOUND method Java.Interop.JniRuntime:GetAvailableInvocationPointers () [0x78e4da7960 - 0x78e4da7a7c 0x78e4de6840]
	D Mono    : AOT: FOUND method Java.Interop.JniRuntime:GetCreatedJavaVMs (intptr[],int,int&) [0x78e4ddd2b0 - 0x78e4ddd300 0x78e4de6bcd]
	D Mono    : AOT: NOT FOUND: Java.Interop.NativeMethods:java_interop_jvm_list (intptr[],int,int&).
	F monodroid-assembly: Internal p/invoke symbol 'java-interop @ java_interop_jvm_list' (hash: 0x58c48fc8b89cb484) not found in compile-time map.

*Nobody* should be using this method, largely given that only
Xamarin.Android and .NET Android apps currently use
`Java.Interop.dll`, and neither use
`JniRuntime.GetAvailableInvocationPointers()`.  Furthermore,
it *can't* work on Android, as Android doesn't provide a public
[`JNI_GetCreatedJavaVMs()`][1] symbol.

Update `build-tools/jnienv-gen` so that a `JniNativeMethods` class
is created which contains "human usable" ways to invoke `JNIEnv`
function pointers.  (Nobody wants to copy the expression
`(*((JNIEnv**)env))->ExceptionClear(env)` more than once, ever.
`JniNativeMethods.ExceptionClear(env)` is much nicer to write.)

Update `samples/Hello-Core` so that it sets `Standalone=True`.

Verification: After building `samples/Hello-Core`, the contained
`Java.Interop.dll` doesn't contain any `pinvokeimpl` methods:

	% dotnet build samples/Hello-Core
	% ikdasm samples/Hello-Core/bin/Debug/Java.Interop.dll | grep pinvoke
	# no matches

TODO: I also attempted to reduce the number of P/Invokes in
`Java.Runtime.Environment.dll`, with the hope that when *not* using
MonoVM it could be used without a native `java-interop` library.
This used [`System.Runtime.InteropServices.NativeLibrary`][2] to load
`JniRuntime.CreationOptions.JvmLibraryPath` and invoke the
`JNI_CreateJavaVM()` and `JNI_GetCreatedJavaVMs()` exports.

Unfortunately, this new backend crashes inexplicably when using
`dotnet test`.  The backend can now be selected by setting the
`JI_LOADER_TYPE` environment variable to one of:

  * `native-library`: the `NativeLibrary` backened, or
  * `java-interop`: the previous `java-interop` native lib backend.

This allows testing to work and CI to succeed:

	% dotnet test bin/TestDebug-net7.0/Java.Interop-Tests.dll
	# all good

while allowing us to separately explore why it crashes:

	% JI_LOADER_TYPE=native-library dotnet test bin/TestDebug-net7.0/Java.Interop-Tests.dll
	…
	# jonp: LoadJvmLibrary(…/libjli.dylib)=9056174496
	# jonp: JNI_CreateJavaVM=4561133901; JNI_GetCreatedJavaVMs=4561133970
	# jonp: executing JNI_CreateJavaVM=10fdd614d
	Error occurred during initialization of VM
	Could not reserve enough space in CodeHeap 'non-nmethods' (2496K)
	The active test run was aborted. Reason: Test host process crashed

	Test Run Aborted with error System.Exception: One or more errors occurred.
	 ---> System.Exception: Unable to read beyond the end of the stream.
	   at System.IO.BinaryReader.Read7BitEncodedInt()
	   at System.IO.BinaryReader.ReadString()
	   at Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.LengthPrefixCommunicationChannel.NotifyDataAvailable()
	   at Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.TcpClientExtensions.MessageLoopAsync(TcpClient client, ICommunicationChannel channel, Action`1 errorHandler, CancellationToken cancellationToken)
	   --- End of inner exception stack trace ---.

[0]: https://discord.com/channels/732297728826277939/732297837953679412/979054761603125319
[1]: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#JNI_GetCreatedJavaVMs
[2]: https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.nativelibrary?view=net-7.0
  • Loading branch information
jonpryor authored Nov 8, 2022
1 parent 5a0097b commit c6c487b
Show file tree
Hide file tree
Showing 12 changed files with 1,799 additions and 374 deletions.
51 changes: 47 additions & 4 deletions build-tools/jnienv-gen/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ static void GenerateFile (TextWriter o)
o.WriteLine ("//");
o.WriteLine ("// To make changes, edit monodroid/tools/jnienv-gen-interop and rerun");
o.WriteLine ();
o.WriteLine ("#if !FEATURE_JNIENVIRONMENT_SAFEHANDLES && !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ("#define FEATURE_JNIENVIRONMENT_SAFEHANDLES");
o.WriteLine ("#if !FEATURE_JNIENVIRONMENT_SAFEHANDLES && !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
o.WriteLine ("#define FEATURE_JNIENVIRONMENT_JI_PINVOKES");
o.WriteLine ("#endif // !FEATURE_JNIENVIRONMENT_SAFEHANDLES && !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS");
o.WriteLine ();
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
Expand Down Expand Up @@ -315,6 +315,9 @@ static void GenerateTypes (TextWriter o, HandleStyle style)
if (style == HandleStyle.JIIntPtrPinvokeWithErrors) {
GenerateNativeMethods (o, style);
}
if (style == HandleStyle.JIFunctionPtrWithErrors) {
GenerateJniNativeMethods (o, style);
}

var visibilities = new Dictionary<string, string> {
{ ArrayOperationsCategory, "public" },
Expand Down Expand Up @@ -367,6 +370,46 @@ static void GenerateNativeMethods (TextWriter o, HandleStyle style)
o.WriteLine ();
}

static void GenerateJniNativeMethods (TextWriter o, HandleStyle style)
{
o.WriteLine ("\tstatic partial class JniNativeMethods {");
o.WriteLine ();
foreach (var entry in JNIEnvEntries) {
if (entry.Parameters == null)
continue;
if (entry.IsPrivate || entry.CustomWrapper)
continue;

var returnType = entry.ReturnType.GetMarshalType (HandleStyle.JIFunctionPtrWithErrors, isReturn: true, isPinvoke: true);

o.WriteLine ();
o.WriteLine ("\t\t[System.Runtime.CompilerServices.MethodImpl (System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]");
o.Write ("\t\tinternal static unsafe ");
o.Write (entry.ReturnType.GetMarshalType (HandleStyle.JIFunctionPtrWithErrors, isReturn: true, isPinvoke: true));
o.Write ($" {entry.Name} (IntPtr env");
foreach (var p in entry.Parameters) {
o.Write (", ");
o.Write (p.Type.GetMarshalType (HandleStyle.JIFunctionPtrWithErrors, isReturn: false, isPinvoke: true));
o.Write ($" {Escape (p.Name)}");
}
o.WriteLine (")");
o.WriteLine ("\t\t{");
o.Write ("\t\t\t");
if (returnType != "void") {
o.Write ("return ");
}
o.Write ($"(*((JNIEnv**)env))->{entry.Name} (env");
foreach (var p in entry.Parameters) {
o.Write (", ");
o.Write (Escape (p.Name));
}
o.WriteLine (");");
o.WriteLine ("\t\t}");
}
o.WriteLine ("\t}");
o.WriteLine ();
}

static string GetPinvokeName (string name)
{
var sb = new StringBuilder ("java_interop_jnienv_".Length + name.Length * 2);
Expand Down Expand Up @@ -427,7 +470,7 @@ static void GenerateJniEnv (TextWriter o, string type, string visibility, Handle
GetPinvokeName (entry.Name),
entry.Throws ? ", out thrown" : "");
} else if (style == HandleStyle.JIFunctionPtrWithErrors) {
o.Write ($"(*((JNIEnv**)__env))->{entry.Name} (__env");
o.Write ($"JniNativeMethods.{entry.Name} (__env");
} else {
o.Write ("__info.Invoker.{0} (__info.EnvironmentPointer", entry.Name);
}
Expand All @@ -444,7 +487,7 @@ static void GenerateJniEnv (TextWriter o, string type, string visibility, Handle
}
o.WriteLine (");");
if (style == HandleStyle.JIFunctionPtrWithErrors && entry.Throws) {
o.WriteLine ("\t\t\tIntPtr thrown = (*((JNIEnv**)__env))->ExceptionOccurred (__env);");
o.WriteLine ("\t\t\tIntPtr thrown = JniNativeMethods.ExceptionOccurred (__env);");
}
CleanupParameters (o, entry.Parameters, style);
RaiseException (o, entry, style);
Expand Down
4 changes: 3 additions & 1 deletion samples/Hello-Core/Hello-Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Java.Interop\Java.Interop.csproj" />
<ProjectReference Include="..\..\src\Java.Interop\Java.Interop.csproj"
AdditionalProperties="Standalone=True"
/>
<ProjectReference Include="..\..\src\Java.Runtime.Environment\Java.Runtime.Environment.csproj" />
</ItemGroup>

Expand Down
4 changes: 2 additions & 2 deletions samples/Hello-Java.Base/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ static unsafe void CreateAnotherJVM ()
t.Start ();
waitForCreation.Wait ();
*/
foreach (var h in JniRuntime.GetAvailableInvocationPointers ()) {
foreach (var h in vm.GetAvailableInvocationPointers ()) {
Console.WriteLine ("WITHIN: GetCreatedJavaVMs: {0}", h);
}
// exitThread.Signal ();
}
foreach (var h in JniRuntime.GetAvailableInvocationPointers ()) {
foreach (var h in JniRuntime.GetRegisteredRuntimes ()) {
Console.WriteLine ("POST: GetCreatedJavaVMs: {0}", h);
}
}
Expand Down
9 changes: 8 additions & 1 deletion src/Java.Interop/Java.Interop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
<NoWarn>$(NoWarn);1591</NoWarn>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\product.snk</AssemblyOriginatorKeyFile>
<DefineConstants>INTEROP;FEATURE_JNIENVIRONMENT_JI_PINVOKES;FEATURE_JNIOBJECTREFERENCE_INTPTRS;INTERNAL_NULLABLE_ATTRIBUTES;$(JavaInteropDefineConstants)</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
Expand All @@ -31,6 +30,7 @@
</PropertyGroup>
<Import Project="..\..\TargetFrameworkDependentValues.props" />
<PropertyGroup>
<DefineConstants>INTEROP;FEATURE_JNIOBJECTREFERENCE_INTPTRS;INTERNAL_NULLABLE_ATTRIBUTES;$(JavaInteropDefineConstants)</DefineConstants>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework.ToLowerInvariant())\</IntermediateOutputPath>
<OutputPath>$(ToolOutputFullPath)</OutputPath>
<DocumentationFile>$(ToolOutputFullPath)Java.Interop.xml</DocumentationFile>
Expand All @@ -39,6 +39,13 @@
<LangVersion Condition=" '$(LangVersion)' == '' ">8.0</LangVersion>
<Version>$(JICoreLibVersion)</Version>
</PropertyGroup>
<PropertyGroup Condition=" '$(Standalone)' == 'True' ">
<DefineConstants Condition=" '$(JIBuildingForNetCoreApp)' == 'True' ">FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS;$(DefineConstants)</DefineConstants>
<DefineConstants Condition=" '$(JIBuildingForNetCoreApp)' != 'True' ">FEATURE_JNIENVIRONMENT_JI_PINVOKES;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Standalone)' != 'True' ">
<DefineConstants>FEATURE_JNIENVIRONMENT_JI_PINVOKES;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Condition=" '$(TargetFramework)' == 'netstandard2.0' " Include="..\utils\NullableAttributes.cs" />
<Compile Remove="Java.Interop\JniLocationException.cs" />
Expand Down
77 changes: 61 additions & 16 deletions src/Java.Interop/Java.Interop/JniEnvironment.Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace Java.Interop
{
Expand Down Expand Up @@ -44,22 +45,13 @@ static unsafe JniObjectReference TryFindClass (string classname, bool throwOnErr
throw new ArgumentException ("'classname' cannot be a zero-length string.", nameof (classname));

var info = JniEnvironment.CurrentInfo;
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
IntPtr thrown;
var c = NativeMethods.java_interop_jnienv_find_class (info.EnvironmentPointer, out thrown, classname);
if (thrown == IntPtr.Zero) {
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
if (TryRawFindClass (info.EnvironmentPointer, classname, out var c, out var thrown)) {
var r = new JniObjectReference (c, JniObjectReferenceType.Local);
JniEnvironment.LogCreateLocalRef (r);
return r;
}

// If the Java-side exception stack trace is *lost* a'la 89a5a229,
// change `false` to `true` and rebuild+re-run.
#if false
NativeMethods.java_interop_jnienv_exception_describe (info.EnvironmentPointer);
#endif

NativeMethods.java_interop_jnienv_exception_clear (info.EnvironmentPointer);
RawExceptionClear (info.EnvironmentPointer);

var findClassThrown = new JniObjectReference (thrown, JniObjectReferenceType.Local);
LogCreateLocalRef (findClassThrown);
Expand All @@ -70,18 +62,18 @@ static unsafe JniObjectReference TryFindClass (string classname, bool throwOnErr
var __args = stackalloc JniArgumentValue [1];
__args [0] = new JniArgumentValue (java);

c = NativeMethods.java_interop_jnienv_call_object_method_a (info.EnvironmentPointer, out thrown, info.Runtime.ClassLoader.Handle, info.Runtime.ClassLoader_LoadClass.ID, (IntPtr) __args);
c = RawCallObjectMethodA (info.EnvironmentPointer, out thrown, info.Runtime.ClassLoader.Handle, info.Runtime.ClassLoader_LoadClass.ID, (IntPtr) __args);
JniObjectReference.Dispose (ref java);
if (thrown == IntPtr.Zero) {
(pendingException as IJavaPeerable)?.Dispose ();
var r = new JniObjectReference (c, JniObjectReferenceType.Local);
JniEnvironment.LogCreateLocalRef (r);
return r;
}
NativeMethods.java_interop_jnienv_exception_clear (info.EnvironmentPointer);
RawExceptionClear (info.EnvironmentPointer);

if (pendingException != null) {
NativeMethods.java_interop_jnienv_delete_local_ref (info.EnvironmentPointer, thrown);
JniEnvironment.References.RawDeleteLocalRef (info.EnvironmentPointer, thrown);
}
else {
var loadClassThrown = new JniObjectReference (thrown, JniObjectReferenceType.Local);
Expand All @@ -95,7 +87,7 @@ static unsafe JniObjectReference TryFindClass (string classname, bool throwOnErr
return default;
}
throw pendingException!;
#endif // !FEATURE_JNIENVIRONMENT_JI_PINVOKES
#endif // !(FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
var c = info.Invoker.FindClass (info.EnvironmentPointer, classname);
var thrown = info.Invoker.ExceptionOccurred (info.EnvironmentPointer);
Expand Down Expand Up @@ -137,6 +129,59 @@ static unsafe JniObjectReference TryFindClass (string classname, bool throwOnErr
#endif // !FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
}

static bool TryRawFindClass (IntPtr env, string classname, out IntPtr klass, out IntPtr thrown)
{
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
klass = NativeMethods.java_interop_jnienv_find_class (env, out thrown, classname);
if (thrown == IntPtr.Zero) {
return true;
}
#endif // !FEATURE_JNIENVIRONMENT_JI_PINVOKES
#if FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
var _classname_ptr = Marshal.StringToCoTaskMemUTF8 (classname);
klass = JniNativeMethods.FindClass (env, _classname_ptr);
thrown = JniNativeMethods.ExceptionOccurred (env);
Marshal.ZeroFreeCoTaskMemUTF8 (_classname_ptr);
if (thrown == IntPtr.Zero) {
return true;
}
#endif // !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
return false;
}

static void RawExceptionClear (IntPtr env)
{
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
// If the Java-side exception stack trace is *lost* a'la 89a5a229,
// change `false` to `true` and rebuild+re-run.
#if false
NativeMethods.java_interop_jnienv_exception_describe (env);
#endif // FEATURE_JNIENVIRONMENT_JI_PINVOKES

NativeMethods.java_interop_jnienv_exception_clear (env);
#elif FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
// If the Java-side exception stack trace is *lost* a'la 89a5a229,
// change `false` to `true` and rebuild+re-run.
#if false
JniNativeMethods.ExceptionDescribe (env);
#endif
JniNativeMethods.ExceptionClear (env);
#endif // FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
}

static IntPtr RawCallObjectMethodA (IntPtr env, out IntPtr thrown, IntPtr instance, IntPtr jmethodID, IntPtr args)
{
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
return NativeMethods.java_interop_jnienv_call_object_method_a (env, out thrown, instance, jmethodID, args);
#elif FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
var r = JniNativeMethods.CallObjectMethodA (env, instance, jmethodID, args);
thrown = JniNativeMethods.ExceptionOccurred (env);
return r;
#else // FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
return IntPtr.Zero;
#endif // FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
}

#if NET
public static bool TryFindClass (string classname, out JniObjectReference instance)
{
Expand Down
36 changes: 25 additions & 11 deletions src/Java.Interop/Java.Interop/JniEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,34 @@ internal static void LogCreateLocalRef (IntPtr value)
}
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS

#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
partial class References {
internal static int GetJavaVM (IntPtr jnienv, out IntPtr vm)

internal static unsafe int GetJavaVM (IntPtr jnienv, out IntPtr vm)
{
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
return NativeMethods.java_interop_jnienv_get_java_vm (jnienv, out vm);
#elif FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
IntPtr _vm;
int r = JniNativeMethods.GetJavaVM (jnienv, &_vm);
vm = _vm;
return r;
#else
Invoker = CreateInvoker (environmentPointer);
var r = Invoker.GetJavaVM (EnvironmentPointer, out vm);
return r;
#endif
}

internal static void RawDeleteLocalRef (IntPtr env, IntPtr localRef)
{
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
NativeMethods.java_interop_jnienv_delete_local_ref (env, localRef);
#elif FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
JniNativeMethods.DeleteLocalRef (env, localRef);
#endif // FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
}
}
#endif // !FEATURE_JNIENVIRONMENT_JI_PINVOKES

}

sealed class JniEnvironmentInfo : IDisposable {
Expand Down Expand Up @@ -206,13 +226,7 @@ public IntPtr EnvironmentPointer {

environmentPointer = value;
IntPtr vmh = IntPtr.Zero;
int r = 0;
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
r = JniEnvironment.References.GetJavaVM (EnvironmentPointer, out vmh);
#else
Invoker = CreateInvoker (environmentPointer);
r = Invoker.GetJavaVM (EnvironmentPointer, out vmh);
#endif // #if !FEATURE_JNIENVIRONMENT_JI_PINVOKES
int r = JniEnvironment.References.GetJavaVM (EnvironmentPointer, out vmh);
if (r < 0)
throw new InvalidOperationException ("JNIEnv::GetJavaVM() returned: " + r.ToString ());

Expand Down Expand Up @@ -285,7 +299,7 @@ public void Dispose ()
};
#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES

#if !FEATURE_JNIENVIRONMENT_JI_PINVOKES
#if !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES
internal JniEnvironmentInvoker Invoker {get; private set;}

static unsafe JniEnvironmentInvoker CreateInvoker (IntPtr handle)
Expand Down
40 changes: 9 additions & 31 deletions src/Java.Interop/Java.Interop/JniRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -78,13 +79,6 @@ interface ISetRuntime {
}
}

partial class NativeMethods {
const string JavaInteropLibrary = "java-interop";

[DllImport (JavaInteropLibrary, CallingConvention=CallingConvention.Cdecl)]
internal static extern int java_interop_jvm_list ([Out] IntPtr[]? handles, int bufLen, out int nVMs);
}

public partial class JniRuntime : IDisposable
{
const int JNI_OK = 0;
Expand All @@ -107,22 +101,14 @@ public static IEnumerable<JniRuntime> GetRegisteredRuntimes ()
}
}

internal static int GetCreatedJavaVMs (IntPtr[]? handles, int bufLen, out int nVMs)
{
return NativeMethods.java_interop_jvm_list (handles, bufLen, out nVMs);
}

[Obsolete ("Not sensible/usable at this level, and cannot work on e.g. Android. " +
"Try Java.Interop.JreRuntime.GetAvailableInvocationPointers() in Java.Runtime.Environment.dll, " +
"or rethink your structure.", error: true)]
[SuppressMessage ("Design", "CA1024:Use properties where appropriate",
Justification = "ABI compatibility")]
public static IEnumerable<IntPtr> GetAvailableInvocationPointers ()
{
int nVMs;
int r = GetCreatedJavaVMs (null, 0, out nVMs);
if (r != 0)
throw new NotSupportedException ("JNI_GetCreatedJavaVMs() returned: " + r.ToString ());
var handles = new IntPtr [nVMs];
r = GetCreatedJavaVMs (handles, handles.Length, out nVMs);
if (r != 0)
throw new InvalidOperationException ("JNI_GetCreatedJavaVMs() [take 2!] returned: " + r.ToString ());
return handles;
throw new NotSupportedException ();
}

static JniRuntime? current;
Expand All @@ -143,17 +129,9 @@ public static JniRuntime CurrentRuntime {
return c!;
}
if (count > 1)
throw new NotSupportedException (string.Format ("Found {0} Java Runtimes. Don't know which to use. Use JniRuntime.SetCurrent().", count));
throw new NotSupportedException (string.Format ("Found {0} known Java Runtime instances. Don't know which to use. Use JniRuntime.SetCurrent().", count));
Debug.Assert (count == 0);
var available = GetAvailableInvocationPointers ().FirstOrDefault ();
if (available == IntPtr.Zero)
throw new NotSupportedException ("No available Java runtime to attach to. Please create one.");
var options = new CreationOptions () {
DestroyRuntimeOnDispose = false,
InvocationPointer = available,
};
// Sets `current`
return new JniRuntime (options);
throw new NotSupportedException ("No available Java runtime to attach to. Please create one.");
}
}

Expand Down
Loading

0 comments on commit c6c487b

Please sign in to comment.