Skip to content

Commit c6c487b

Browse files
authored
[Java.Interop] Optional "Standalone" Build Config (#1049)
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
1 parent 5a0097b commit c6c487b

File tree

12 files changed

+1799
-374
lines changed

12 files changed

+1799
-374
lines changed

build-tools/jnienv-gen/Generator.cs

+47-4
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ static void GenerateFile (TextWriter o)
8787
o.WriteLine ("//");
8888
o.WriteLine ("// To make changes, edit monodroid/tools/jnienv-gen-interop and rerun");
8989
o.WriteLine ();
90-
o.WriteLine ("#if !FEATURE_JNIENVIRONMENT_SAFEHANDLES && !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS");
91-
o.WriteLine ("#define FEATURE_JNIENVIRONMENT_SAFEHANDLES");
90+
o.WriteLine ("#if !FEATURE_JNIENVIRONMENT_SAFEHANDLES && !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
91+
o.WriteLine ("#define FEATURE_JNIENVIRONMENT_JI_PINVOKES");
9292
o.WriteLine ("#endif // !FEATURE_JNIENVIRONMENT_SAFEHANDLES && !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS");
9393
o.WriteLine ();
9494
o.WriteLine ("#if FEATURE_JNIENVIRONMENT_SAFEHANDLES && FEATURE_JNIENVIRONMENT_JI_INTPTRS");
@@ -315,6 +315,9 @@ static void GenerateTypes (TextWriter o, HandleStyle style)
315315
if (style == HandleStyle.JIIntPtrPinvokeWithErrors) {
316316
GenerateNativeMethods (o, style);
317317
}
318+
if (style == HandleStyle.JIFunctionPtrWithErrors) {
319+
GenerateJniNativeMethods (o, style);
320+
}
318321

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

373+
static void GenerateJniNativeMethods (TextWriter o, HandleStyle style)
374+
{
375+
o.WriteLine ("\tstatic partial class JniNativeMethods {");
376+
o.WriteLine ();
377+
foreach (var entry in JNIEnvEntries) {
378+
if (entry.Parameters == null)
379+
continue;
380+
if (entry.IsPrivate || entry.CustomWrapper)
381+
continue;
382+
383+
var returnType = entry.ReturnType.GetMarshalType (HandleStyle.JIFunctionPtrWithErrors, isReturn: true, isPinvoke: true);
384+
385+
o.WriteLine ();
386+
o.WriteLine ("\t\t[System.Runtime.CompilerServices.MethodImpl (System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]");
387+
o.Write ("\t\tinternal static unsafe ");
388+
o.Write (entry.ReturnType.GetMarshalType (HandleStyle.JIFunctionPtrWithErrors, isReturn: true, isPinvoke: true));
389+
o.Write ($" {entry.Name} (IntPtr env");
390+
foreach (var p in entry.Parameters) {
391+
o.Write (", ");
392+
o.Write (p.Type.GetMarshalType (HandleStyle.JIFunctionPtrWithErrors, isReturn: false, isPinvoke: true));
393+
o.Write ($" {Escape (p.Name)}");
394+
}
395+
o.WriteLine (")");
396+
o.WriteLine ("\t\t{");
397+
o.Write ("\t\t\t");
398+
if (returnType != "void") {
399+
o.Write ("return ");
400+
}
401+
o.Write ($"(*((JNIEnv**)env))->{entry.Name} (env");
402+
foreach (var p in entry.Parameters) {
403+
o.Write (", ");
404+
o.Write (Escape (p.Name));
405+
}
406+
o.WriteLine (");");
407+
o.WriteLine ("\t\t}");
408+
}
409+
o.WriteLine ("\t}");
410+
o.WriteLine ();
411+
}
412+
370413
static string GetPinvokeName (string name)
371414
{
372415
var sb = new StringBuilder ("java_interop_jnienv_".Length + name.Length * 2);
@@ -427,7 +470,7 @@ static void GenerateJniEnv (TextWriter o, string type, string visibility, Handle
427470
GetPinvokeName (entry.Name),
428471
entry.Throws ? ", out thrown" : "");
429472
} else if (style == HandleStyle.JIFunctionPtrWithErrors) {
430-
o.Write ($"(*((JNIEnv**)__env))->{entry.Name} (__env");
473+
o.Write ($"JniNativeMethods.{entry.Name} (__env");
431474
} else {
432475
o.Write ("__info.Invoker.{0} (__info.EnvironmentPointer", entry.Name);
433476
}
@@ -444,7 +487,7 @@ static void GenerateJniEnv (TextWriter o, string type, string visibility, Handle
444487
}
445488
o.WriteLine (");");
446489
if (style == HandleStyle.JIFunctionPtrWithErrors && entry.Throws) {
447-
o.WriteLine ("\t\t\tIntPtr thrown = (*((JNIEnv**)__env))->ExceptionOccurred (__env);");
490+
o.WriteLine ("\t\t\tIntPtr thrown = JniNativeMethods.ExceptionOccurred (__env);");
448491
}
449492
CleanupParameters (o, entry.Parameters, style);
450493
RaiseException (o, entry, style);

samples/Hello-Core/Hello-Core.csproj

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
</ItemGroup>
1515

1616
<ItemGroup>
17-
<ProjectReference Include="..\..\src\Java.Interop\Java.Interop.csproj" />
17+
<ProjectReference Include="..\..\src\Java.Interop\Java.Interop.csproj"
18+
AdditionalProperties="Standalone=True"
19+
/>
1820
<ProjectReference Include="..\..\src\Java.Runtime.Environment\Java.Runtime.Environment.csproj" />
1921
</ItemGroup>
2022

samples/Hello-Java.Base/Program.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,12 @@ static unsafe void CreateAnotherJVM ()
105105
t.Start ();
106106
waitForCreation.Wait ();
107107
*/
108-
foreach (var h in JniRuntime.GetAvailableInvocationPointers ()) {
108+
foreach (var h in vm.GetAvailableInvocationPointers ()) {
109109
Console.WriteLine ("WITHIN: GetCreatedJavaVMs: {0}", h);
110110
}
111111
// exitThread.Signal ();
112112
}
113-
foreach (var h in JniRuntime.GetAvailableInvocationPointers ()) {
113+
foreach (var h in JniRuntime.GetRegisteredRuntimes ()) {
114114
Console.WriteLine ("POST: GetCreatedJavaVMs: {0}", h);
115115
}
116116
}

src/Java.Interop/Java.Interop.csproj

+8-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
<NoWarn>$(NoWarn);1591</NoWarn>
2121
<SignAssembly>true</SignAssembly>
2222
<AssemblyOriginatorKeyFile>..\..\product.snk</AssemblyOriginatorKeyFile>
23-
<DefineConstants>INTEROP;FEATURE_JNIENVIRONMENT_JI_PINVOKES;FEATURE_JNIOBJECTREFERENCE_INTPTRS;INTERNAL_NULLABLE_ATTRIBUTES;$(JavaInteropDefineConstants)</DefineConstants>
2423
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
2524
<Nullable>enable</Nullable>
2625
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
@@ -31,6 +30,7 @@
3130
</PropertyGroup>
3231
<Import Project="..\..\TargetFrameworkDependentValues.props" />
3332
<PropertyGroup>
33+
<DefineConstants>INTEROP;FEATURE_JNIOBJECTREFERENCE_INTPTRS;INTERNAL_NULLABLE_ATTRIBUTES;$(JavaInteropDefineConstants)</DefineConstants>
3434
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework.ToLowerInvariant())\</IntermediateOutputPath>
3535
<OutputPath>$(ToolOutputFullPath)</OutputPath>
3636
<DocumentationFile>$(ToolOutputFullPath)Java.Interop.xml</DocumentationFile>
@@ -39,6 +39,13 @@
3939
<LangVersion Condition=" '$(LangVersion)' == '' ">8.0</LangVersion>
4040
<Version>$(JICoreLibVersion)</Version>
4141
</PropertyGroup>
42+
<PropertyGroup Condition=" '$(Standalone)' == 'True' ">
43+
<DefineConstants Condition=" '$(JIBuildingForNetCoreApp)' == 'True' ">FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS;$(DefineConstants)</DefineConstants>
44+
<DefineConstants Condition=" '$(JIBuildingForNetCoreApp)' != 'True' ">FEATURE_JNIENVIRONMENT_JI_PINVOKES;$(DefineConstants)</DefineConstants>
45+
</PropertyGroup>
46+
<PropertyGroup Condition=" '$(Standalone)' != 'True' ">
47+
<DefineConstants>FEATURE_JNIENVIRONMENT_JI_PINVOKES;$(DefineConstants)</DefineConstants>
48+
</PropertyGroup>
4249
<ItemGroup>
4350
<Compile Condition=" '$(TargetFramework)' == 'netstandard2.0' " Include="..\utils\NullableAttributes.cs" />
4451
<Compile Remove="Java.Interop\JniLocationException.cs" />

src/Java.Interop/Java.Interop/JniEnvironment.Types.cs

+61-16
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Diagnostics;
55
using System.Collections.Generic;
66
using System.Text;
7+
using System.Runtime.InteropServices;
78

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

4647
var info = JniEnvironment.CurrentInfo;
47-
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
48-
IntPtr thrown;
49-
var c = NativeMethods.java_interop_jnienv_find_class (info.EnvironmentPointer, out thrown, classname);
50-
if (thrown == IntPtr.Zero) {
48+
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
49+
if (TryRawFindClass (info.EnvironmentPointer, classname, out var c, out var thrown)) {
5150
var r = new JniObjectReference (c, JniObjectReferenceType.Local);
5251
JniEnvironment.LogCreateLocalRef (r);
5352
return r;
5453
}
55-
56-
// If the Java-side exception stack trace is *lost* a'la 89a5a229,
57-
// change `false` to `true` and rebuild+re-run.
58-
#if false
59-
NativeMethods.java_interop_jnienv_exception_describe (info.EnvironmentPointer);
60-
#endif
61-
62-
NativeMethods.java_interop_jnienv_exception_clear (info.EnvironmentPointer);
54+
RawExceptionClear (info.EnvironmentPointer);
6355

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

73-
c = NativeMethods.java_interop_jnienv_call_object_method_a (info.EnvironmentPointer, out thrown, info.Runtime.ClassLoader.Handle, info.Runtime.ClassLoader_LoadClass.ID, (IntPtr) __args);
65+
c = RawCallObjectMethodA (info.EnvironmentPointer, out thrown, info.Runtime.ClassLoader.Handle, info.Runtime.ClassLoader_LoadClass.ID, (IntPtr) __args);
7466
JniObjectReference.Dispose (ref java);
7567
if (thrown == IntPtr.Zero) {
7668
(pendingException as IJavaPeerable)?.Dispose ();
7769
var r = new JniObjectReference (c, JniObjectReferenceType.Local);
7870
JniEnvironment.LogCreateLocalRef (r);
7971
return r;
8072
}
81-
NativeMethods.java_interop_jnienv_exception_clear (info.EnvironmentPointer);
73+
RawExceptionClear (info.EnvironmentPointer);
8274

8375
if (pendingException != null) {
84-
NativeMethods.java_interop_jnienv_delete_local_ref (info.EnvironmentPointer, thrown);
76+
JniEnvironment.References.RawDeleteLocalRef (info.EnvironmentPointer, thrown);
8577
}
8678
else {
8779
var loadClassThrown = new JniObjectReference (thrown, JniObjectReferenceType.Local);
@@ -95,7 +87,7 @@ static unsafe JniObjectReference TryFindClass (string classname, bool throwOnErr
9587
return default;
9688
}
9789
throw pendingException!;
98-
#endif // !FEATURE_JNIENVIRONMENT_JI_PINVOKES
90+
#endif // !(FEATURE_JNIENVIRONMENT_JI_PINVOKES || FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS)
9991
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
10092
var c = info.Invoker.FindClass (info.EnvironmentPointer, classname);
10193
var thrown = info.Invoker.ExceptionOccurred (info.EnvironmentPointer);
@@ -137,6 +129,59 @@ static unsafe JniObjectReference TryFindClass (string classname, bool throwOnErr
137129
#endif // !FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
138130
}
139131

132+
static bool TryRawFindClass (IntPtr env, string classname, out IntPtr klass, out IntPtr thrown)
133+
{
134+
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
135+
klass = NativeMethods.java_interop_jnienv_find_class (env, out thrown, classname);
136+
if (thrown == IntPtr.Zero) {
137+
return true;
138+
}
139+
#endif // !FEATURE_JNIENVIRONMENT_JI_PINVOKES
140+
#if FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
141+
var _classname_ptr = Marshal.StringToCoTaskMemUTF8 (classname);
142+
klass = JniNativeMethods.FindClass (env, _classname_ptr);
143+
thrown = JniNativeMethods.ExceptionOccurred (env);
144+
Marshal.ZeroFreeCoTaskMemUTF8 (_classname_ptr);
145+
if (thrown == IntPtr.Zero) {
146+
return true;
147+
}
148+
#endif // !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
149+
return false;
150+
}
151+
152+
static void RawExceptionClear (IntPtr env)
153+
{
154+
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
155+
// If the Java-side exception stack trace is *lost* a'la 89a5a229,
156+
// change `false` to `true` and rebuild+re-run.
157+
#if false
158+
NativeMethods.java_interop_jnienv_exception_describe (env);
159+
#endif // FEATURE_JNIENVIRONMENT_JI_PINVOKES
160+
161+
NativeMethods.java_interop_jnienv_exception_clear (env);
162+
#elif FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
163+
// If the Java-side exception stack trace is *lost* a'la 89a5a229,
164+
// change `false` to `true` and rebuild+re-run.
165+
#if false
166+
JniNativeMethods.ExceptionDescribe (env);
167+
#endif
168+
JniNativeMethods.ExceptionClear (env);
169+
#endif // FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
170+
}
171+
172+
static IntPtr RawCallObjectMethodA (IntPtr env, out IntPtr thrown, IntPtr instance, IntPtr jmethodID, IntPtr args)
173+
{
174+
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
175+
return NativeMethods.java_interop_jnienv_call_object_method_a (env, out thrown, instance, jmethodID, args);
176+
#elif FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
177+
var r = JniNativeMethods.CallObjectMethodA (env, instance, jmethodID, args);
178+
thrown = JniNativeMethods.ExceptionOccurred (env);
179+
return r;
180+
#else // FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
181+
return IntPtr.Zero;
182+
#endif // FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
183+
}
184+
140185
#if NET
141186
public static bool TryFindClass (string classname, out JniObjectReference instance)
142187
{

src/Java.Interop/Java.Interop/JniEnvironment.cs

+25-11
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,34 @@ internal static void LogCreateLocalRef (IntPtr value)
170170
}
171171
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
172172

173-
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
174173
partial class References {
175-
internal static int GetJavaVM (IntPtr jnienv, out IntPtr vm)
174+
175+
internal static unsafe int GetJavaVM (IntPtr jnienv, out IntPtr vm)
176176
{
177+
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
177178
return NativeMethods.java_interop_jnienv_get_java_vm (jnienv, out vm);
179+
#elif FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
180+
IntPtr _vm;
181+
int r = JniNativeMethods.GetJavaVM (jnienv, &_vm);
182+
vm = _vm;
183+
return r;
184+
#else
185+
Invoker = CreateInvoker (environmentPointer);
186+
var r = Invoker.GetJavaVM (EnvironmentPointer, out vm);
187+
return r;
188+
#endif
189+
}
190+
191+
internal static void RawDeleteLocalRef (IntPtr env, IntPtr localRef)
192+
{
193+
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
194+
NativeMethods.java_interop_jnienv_delete_local_ref (env, localRef);
195+
#elif FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
196+
JniNativeMethods.DeleteLocalRef (env, localRef);
197+
#endif // FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
178198
}
179199
}
180-
#endif // !FEATURE_JNIENVIRONMENT_JI_PINVOKES
200+
181201
}
182202

183203
sealed class JniEnvironmentInfo : IDisposable {
@@ -206,13 +226,7 @@ public IntPtr EnvironmentPointer {
206226

207227
environmentPointer = value;
208228
IntPtr vmh = IntPtr.Zero;
209-
int r = 0;
210-
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
211-
r = JniEnvironment.References.GetJavaVM (EnvironmentPointer, out vmh);
212-
#else
213-
Invoker = CreateInvoker (environmentPointer);
214-
r = Invoker.GetJavaVM (EnvironmentPointer, out vmh);
215-
#endif // #if !FEATURE_JNIENVIRONMENT_JI_PINVOKES
229+
int r = JniEnvironment.References.GetJavaVM (EnvironmentPointer, out vmh);
216230
if (r < 0)
217231
throw new InvalidOperationException ("JNIEnv::GetJavaVM() returned: " + r.ToString ());
218232

@@ -285,7 +299,7 @@ public void Dispose ()
285299
};
286300
#endif // FEATURE_JNIENVIRONMENT_SAFEHANDLES
287301

288-
#if !FEATURE_JNIENVIRONMENT_JI_PINVOKES
302+
#if !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES
289303
internal JniEnvironmentInvoker Invoker {get; private set;}
290304

291305
static unsafe JniEnvironmentInvoker CreateInvoker (IntPtr handle)

src/Java.Interop/Java.Interop/JniRuntime.cs

+9-31
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System;
44
using System.Collections.Generic;
55
using System.Diagnostics;
6+
using System.Diagnostics.CodeAnalysis;
67
using System.Linq;
78
using System.Reflection;
89
using System.Runtime.InteropServices;
@@ -78,13 +79,6 @@ interface ISetRuntime {
7879
}
7980
}
8081

81-
partial class NativeMethods {
82-
const string JavaInteropLibrary = "java-interop";
83-
84-
[DllImport (JavaInteropLibrary, CallingConvention=CallingConvention.Cdecl)]
85-
internal static extern int java_interop_jvm_list ([Out] IntPtr[]? handles, int bufLen, out int nVMs);
86-
}
87-
8882
public partial class JniRuntime : IDisposable
8983
{
9084
const int JNI_OK = 0;
@@ -107,22 +101,14 @@ public static IEnumerable<JniRuntime> GetRegisteredRuntimes ()
107101
}
108102
}
109103

110-
internal static int GetCreatedJavaVMs (IntPtr[]? handles, int bufLen, out int nVMs)
111-
{
112-
return NativeMethods.java_interop_jvm_list (handles, bufLen, out nVMs);
113-
}
114-
104+
[Obsolete ("Not sensible/usable at this level, and cannot work on e.g. Android. " +
105+
"Try Java.Interop.JreRuntime.GetAvailableInvocationPointers() in Java.Runtime.Environment.dll, " +
106+
"or rethink your structure.", error: true)]
107+
[SuppressMessage ("Design", "CA1024:Use properties where appropriate",
108+
Justification = "ABI compatibility")]
115109
public static IEnumerable<IntPtr> GetAvailableInvocationPointers ()
116110
{
117-
int nVMs;
118-
int r = GetCreatedJavaVMs (null, 0, out nVMs);
119-
if (r != 0)
120-
throw new NotSupportedException ("JNI_GetCreatedJavaVMs() returned: " + r.ToString ());
121-
var handles = new IntPtr [nVMs];
122-
r = GetCreatedJavaVMs (handles, handles.Length, out nVMs);
123-
if (r != 0)
124-
throw new InvalidOperationException ("JNI_GetCreatedJavaVMs() [take 2!] returned: " + r.ToString ());
125-
return handles;
111+
throw new NotSupportedException ();
126112
}
127113

128114
static JniRuntime? current;
@@ -143,17 +129,9 @@ public static JniRuntime CurrentRuntime {
143129
return c!;
144130
}
145131
if (count > 1)
146-
throw new NotSupportedException (string.Format ("Found {0} Java Runtimes. Don't know which to use. Use JniRuntime.SetCurrent().", count));
132+
throw new NotSupportedException (string.Format ("Found {0} known Java Runtime instances. Don't know which to use. Use JniRuntime.SetCurrent().", count));
147133
Debug.Assert (count == 0);
148-
var available = GetAvailableInvocationPointers ().FirstOrDefault ();
149-
if (available == IntPtr.Zero)
150-
throw new NotSupportedException ("No available Java runtime to attach to. Please create one.");
151-
var options = new CreationOptions () {
152-
DestroyRuntimeOnDispose = false,
153-
InvocationPointer = available,
154-
};
155-
// Sets `current`
156-
return new JniRuntime (options);
134+
throw new NotSupportedException ("No available Java runtime to attach to. Please create one.");
157135
}
158136
}
159137

0 commit comments

Comments
 (0)