Skip to content

Commit 3d6166b

Browse files
committed
[Java.Runtime.Environment] Partial support for .NET Core
Enable C#8 [Nullable Reference Types][0] for `Java.Runtime.Environment.dll`. Add partial support for a "non-bridged backend", so that a `JniRuntime.JniValueManager` exists for .NET Core. This new "managed" backed is used if the Mono runtime is *not* used. To work, `ManagedValueManager` holds *strong* references to `IJavaPeerable` instances. As such, tests which required the use of GC integration are now "optional", conditional on the `!NO_GC_BRIDGE_SUPPORT` define. The `ManagedValueManager.CollectPeers()` method: 1. Releases all GREFs for all held peers. This allows Java to collect the Java peers. 2. Stops referencing all `IJavaPeerable` values. This allows the .NET GC to collect the `IJavaPeerable` values. There is no notification to the `IJavaPeerable` instances that this has happened. Update `Java.Interop-Tests.csproj` to define `NO_GC_BRIDGE_SUPPORT` and `NO_MARSHAL_MEMBER_BUILDER_SUPPORT` when building for .NET Core. This excludes all currently "troublesome"/non-passing tests. These changes allow all remaining `Java.Interop-Tests` unit tests to execute under .NET Core: % dotnet test -v diag '--logger:trx;verbosity=detailed' bin/TestDebug-netcoreapp3.1/Java.Interop-Tests.dll Passed! - Failed: 0, Passed: 617, Skipped: 1, Total: 618, Duration: 1 s Other changes: * The attempt to retain useful Java-side exceptions in 89a5a22 proved to be incomplete. Add a comment to invoke [`JNIEnv::ExceptionDescribe()`][1]. We don't always want this to be present, but when we do want it… * While `NO_MARSHAL_MEMBER_BUILDER_SUPPORT` is set -- which means that `Java.Interop.Export`-related tests aren't run -- there are some fixes for `Java.Interop.Export` & related unit tests for .NET Core, to avoid the use of generic delegate types and to avoid a `Type.GetType()` which is no longer needed. [0]: https://docs.microsoft.com/dotnet/csharp/nullable-references [1]: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#ExceptionDescribe
1 parent bba1f07 commit 3d6166b

File tree

17 files changed

+575
-84
lines changed

17 files changed

+575
-84
lines changed

build-tools/automation/azure-pipelines.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ variables:
2121
DotNetCoreVersion: 3.1.300
2222
HostedMacImage: macOS-10.15
2323
HostedWinVS2019: Hosted Windows 2019 with VS2019
24+
NetCoreTargetFrameworkPathSuffix: -netcoreapp3.1
2425

2526
jobs:
2627
- job: windows_build

build-tools/automation/templates/core-tests.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ steps:
6767
arguments: bin/Test$(Build.Configuration)/Java.Interop-Tests.dll
6868
continueOnError: true
6969

70+
- task: DotNetCoreCLI@2
71+
displayName: 'Tests: Java.Interop'
72+
condition: eq('${{ parameters.runNativeTests }}', 'true')
73+
inputs:
74+
command: test
75+
arguments: bin/Test$(Build.Configuration)$(NetCoreTargetFrameworkPathSuffix)/Java.Interop-Tests.dll
76+
continueOnError: true
77+
7078
- task: DotNetCoreCLI@2
7179
displayName: 'Tests: Java.Interop.Dynamic'
7280
condition: eq('${{ parameters.runNativeTests }}', 'true')

src/Java.Interop.Export/Java.Interop/MarshalMemberBuilder.cs

+6-1
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,12 @@ static Expression GetRuntime ()
370370
return Expression.Property (null, typeof (JniEnvironment), "Runtime");
371371
}
372372

373-
static MethodInfo FormatterServices_GetUninitializedObject = Type.GetType ("System.Runtime.Serialization.FormatterServices", throwOnError: true)
373+
static MethodInfo FormatterServices_GetUninitializedObject =
374+
#if NETFRAMEWORK || NET_2_0
375+
typeof (System.Runtime.Serialization.FormatterServices)
376+
#else // !(NETFRAMEWORK || NET_2_0)
377+
typeof (System.Runtime.CompilerServices.RuntimeHelpers)
378+
#endif // NETFRAMEWORK || NET_2_0
374379
.GetRuntimeMethod ("GetUninitializedObject", new[]{typeof (Type)});
375380
static MethodInfo IJavaPeerable_SetPeerReference = typeof (IJavaPeerable).GetRuntimeMethod ("SetPeerReference", new[]{typeof (JniObjectReference)});
376381

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

+14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#nullable enable
22

33
using System;
4+
using System.Diagnostics;
45
using System.Collections.Generic;
56
using System.Text;
67

@@ -47,6 +48,8 @@ public static unsafe JniObjectReference FindClass (string classname)
4748
return r;
4849
}
4950

51+
// NativeMethods.java_interop_jnienv_exception_describe (info.EnvironmentPointer);
52+
5053
NativeMethods.java_interop_jnienv_exception_clear (info.EnvironmentPointer);
5154

5255
var findClassThrown = new JniObjectReference (thrown, JniObjectReferenceType.Local);
@@ -167,6 +170,17 @@ public static void RegisterNatives (JniObjectReference type, JniNativeMethodRegi
167170

168171
public static void RegisterNatives (JniObjectReference type, JniNativeMethodRegistration [] methods, int numMethods)
169172
{
173+
#if DEBUG && NETCOREAPP
174+
foreach (var m in methods) {
175+
if (m.Marshaler.GetType ().GenericTypeArguments.Length != 0) {
176+
var method = m.Marshaler.Method;
177+
Debug.WriteLine ($"JNIEnv::RegisterNatives() given a generic delegate type. .NET Core doesn't like this.");
178+
Debug.WriteLine ($" Java: {m.Name}{m.Signature}");
179+
Debug.WriteLine ($" Marshaler Type={m.Marshaler.GetType ().FullName} Method={method.DeclaringType.FullName}.{method.Name}");
180+
}
181+
}
182+
#endif // DEBUG && NETCOREAPP
183+
170184
int r = _RegisterNatives (type, methods, numMethods);
171185

172186
if (r != 0) {

src/Java.Runtime.Environment/Java.Interop/JreRuntime.cs

+14-58
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ public class JreRuntimeOptions : JniRuntime.CreationOptions {
3131

3232
public Collection<string> ClassPath {get; private set;}
3333

34+
public TextWriter? JniGlobalReferenceLogWriter {get; set;}
35+
public TextWriter? JniLocalReferenceLogWriter {get; set;}
36+
3437
public JreRuntimeOptions ()
3538
{
3639
JniVersion = JniVersion.v1_2;
@@ -39,16 +42,6 @@ public JreRuntimeOptions ()
3942
Path.GetDirectoryName (typeof (JreRuntimeOptions).Assembly.Location),
4043
"java-interop.jar"),
4144
};
42-
43-
bool onMono = Type.GetType ("Mono.Runtime", throwOnError: false) != null;
44-
if (onMono) {
45-
ValueManager = ValueManager ?? new MonoRuntimeValueManager ();
46-
ObjectReferenceManager = ObjectReferenceManager ?? new MonoRuntimeObjectReferenceManager ();
47-
}
48-
else {
49-
ValueManager = ValueManager ?? new DummyValueManager ();
50-
ObjectReferenceManager = ObjectReferenceManager ?? new DummyObjectReferenceManager ();
51-
}
5245
}
5346

5447
public JreRuntimeOptions AddOption (string option)
@@ -87,12 +80,22 @@ static unsafe JreRuntimeOptions CreateJreVM (JreRuntimeOptions builder)
8780
if (builder == null)
8881
throw new ArgumentNullException ("builder");
8982

83+
bool onMono = Type.GetType ("Mono.Runtime", throwOnError: false) != null;
84+
if (onMono) {
85+
builder.ValueManager = builder.ValueManager ?? new MonoRuntimeValueManager ();
86+
builder.ObjectReferenceManager = builder.ObjectReferenceManager ?? new MonoRuntimeObjectReferenceManager ();
87+
}
88+
else {
89+
builder.ValueManager = builder.ValueManager ?? new ManagedValueManager ();
90+
builder.ObjectReferenceManager = builder.ObjectReferenceManager ?? new ManagedObjectReferenceManager (builder.JniGlobalReferenceLogWriter, builder.JniLocalReferenceLogWriter);
91+
}
92+
9093
if (builder.InvocationPointer != IntPtr.Zero)
9194
return builder;
9295

9396
if (!string.IsNullOrEmpty (builder.JvmLibraryPath)) {
9497
IntPtr errorPtr = IntPtr.Zero;
95-
int r = NativeMethods.java_interop_jvm_load_with_error_message (builder.JvmLibraryPath, out errorPtr);
98+
int r = NativeMethods.java_interop_jvm_load_with_error_message (builder.JvmLibraryPath!, out errorPtr);
9699
if (r != 0) {
97100
string error = Marshal.PtrToStringAnsi (errorPtr);
98101
NativeMethods.java_interop_free (errorPtr);
@@ -166,52 +169,5 @@ partial class NativeMethods {
166169
[DllImport (JavaInteropLib, CharSet=CharSet.Ansi, CallingConvention=CallingConvention.Cdecl)]
167170
internal static extern int java_interop_jvm_create (out IntPtr javavm, out IntPtr jnienv, ref JavaVMInitArgs args);
168171
}
169-
170-
class DummyValueManager : JniRuntime.JniValueManager {
171-
172-
public override void WaitForGCBridgeProcessing ()
173-
{
174-
}
175-
176-
public override void CollectPeers ()
177-
{
178-
}
179-
180-
public override void AddPeer (IJavaPeerable reference)
181-
{
182-
}
183-
184-
public override void RemovePeer (IJavaPeerable reference)
185-
{
186-
}
187-
188-
public override void FinalizePeer (IJavaPeerable reference)
189-
{
190-
}
191-
192-
public override List<JniSurfacedPeerInfo> GetSurfacedPeers ()
193-
{
194-
return null;
195-
}
196-
197-
public override IJavaPeerable PeekPeer (global::Java.Interop.JniObjectReference reference)
198-
{
199-
return null;
200-
}
201-
202-
public override void ActivatePeer (IJavaPeerable self, JniObjectReference reference, ConstructorInfo cinfo, object [] argumentValues)
203-
{
204-
}
205-
}
206-
207-
class DummyObjectReferenceManager : JniRuntime.JniObjectReferenceManager {
208-
public override int GlobalReferenceCount {
209-
get {return 0;}
210-
}
211-
212-
public override int WeakGlobalReferenceCount {
213-
get {return 0;}
214-
}
215-
}
216172
}
217173

0 commit comments

Comments
 (0)