From 9f21312b7ba8c427513e67a8fb35ef242cf4ef7c Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 10 Sep 2025 10:49:53 -0700 Subject: [PATCH 01/10] Clean build for consistency --- Directory.Build.props | 2 +- src/java-interop/java-interop.targets | 12 ++++++------ tests/NativeTiming/NativeTiming.targets | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 5da999fb1..c132f796b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -47,7 +47,7 @@ False False - obj\ + $(MSBuildProjectDirectory)\obj\ diff --git a/src/java-interop/java-interop.targets b/src/java-interop/java-interop.targets index 9d35aaa47..9b31afb14 100644 --- a/src/java-interop/java-interop.targets +++ b/src/java-interop/java-interop.targets @@ -35,13 +35,13 @@ - <_MonoNativePath>$(NuGetPackageRoot)microsoft.netcore.app.runtime.mono.$(NETCoreSdkRuntimeIdentifier)/$(DotNetRuntimePacksVersion)/runtimes/$(NETCoreSdkRuntimeIdentifier)/native/ + <_MonoNativePath>$(NuGetPackageRoot)\microsoft.netcore.app.runtime.mono.$(NETCoreSdkRuntimeIdentifier)/$(DotNetRuntimePacksVersion)/runtimes/$(NETCoreSdkRuntimeIdentifier)/native/ <_MonoIncludePath>$(_MonoNativePath)include/mono-2.0 <_DEnableMono>-DENABLE_MONO_INTEGRATION=ON <_DEnableOsxArchitectures Condition=" $([MSBuild]::IsOSPlatform ('osx')) ">"-DENABLE_OSX_ARCHITECTURES=$(_CmakeOsxArch)" <_DMonoDirs>"-DMONO_INCLUDE_LIST=$(_MonoIncludePath)" <_DJdkDirs>"-DJDK_INCLUDE_LIST=@(JdkIncludePath, ';')" - <_DJni_c>"-DJNI_C_PATH=$(MSBuildThisFileDirectory)$(IntermediateOutputPath)jni.c" + <_DJni_c>"-DJNI_C_PATH=$(IntermediateOutputPath)jni.c" <_MonoLinkFlags Condition=" $([MSBuild]::IsOSPlatform ('windows')) " >$(MSBuildThisFileDirectory)coreclr.lib <_MonoLinkFlags Condition=" !$([MSBuild]::IsOSPlatform ('windows')) ">-L $(_MonoNativePath) -lcoreclr <_DMonoLinkFlags>"-DMONO_LINK_FLAGS=$(_MonoLinkFlags)" @@ -71,7 +71,7 @@ <_Cmake Include="CmakePath=$(CmakePath)" /> <_Cmake Include="CmakeGenerator=$(CmakeGenerator)" /> <_Cmake Include="CmakeSourceDir=$(MSBuildThisFileDirectory)" /> - <_Cmake Include="CmakeBuildDir=$(MSBuildThisFileDirectory)$(IntermediateOutputPath)" /> + <_Cmake Include="CmakeBuildDir=$(IntermediateOutputPath)" /> <_Cmake Include="CmakeExtraArgs=$(_ExtraArgs)" /> - + Inputs="$(MSBuildThisFileDirectory)coreclr.def" + Outputs="$(MSBuildThisFileDirectory)coreclr.lib"> + <_Cmake Include="CmakeGenerator=$(CmakeGenerator)" /> <_Cmake Include="CmakeSourceDir=$(MSBuildThisFileDirectory)" /> - <_Cmake Include="CmakeBuildDir=$(MSBuildThisFileDirectory)$(IntermediateOutputPath)%(_NativeTimingLib.Dir)" /> + <_Cmake Include="CmakeBuildDir=$(IntermediateOutputPath)%(_NativeTimingLib.Dir)" /> <_Cmake Include="CmakeExtraArgs=$(_JdkDirs)" /> Date: Wed, 10 Sep 2025 10:50:59 -0700 Subject: [PATCH 02/10] Update JniValueManager to only look for single best type for peer --- .../JniRuntime.JniValueManager.cs | 324 ++++++++++++------ 1 file changed, 219 insertions(+), 105 deletions(-) diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs index 1ce3e6543..baff66b8b 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs @@ -14,46 +14,49 @@ namespace Java.Interop { - public class JniSurfacedPeerInfo { + public class JniSurfacedPeerInfo + { - public int JniIdentityHashCode {get; private set;} - public WeakReference SurfacedPeer {get; private set;} + public int JniIdentityHashCode { get; private set; } + public WeakReference SurfacedPeer { get; private set; } public JniSurfacedPeerInfo (int jniIdentityHashCode, WeakReference surfacedPeer) { - JniIdentityHashCode = jniIdentityHashCode; - SurfacedPeer = surfacedPeer; + JniIdentityHashCode = jniIdentityHashCode; + SurfacedPeer = surfacedPeer; } } partial class JniRuntime { - partial class CreationOptions { - public JniValueManager? ValueManager {get; set;} + partial class CreationOptions + { + public JniValueManager? ValueManager { get; set; } } - internal JniValueManager? valueManager; - public JniValueManager ValueManager { + internal JniValueManager? valueManager; + public JniValueManager ValueManager { get => valueManager ?? throw new NotSupportedException (); } partial void SetValueManager (CreationOptions options) { - var manager = options.ValueManager; + var manager = options.ValueManager; if (manager == null) throw new ArgumentException ( "No JniValueManager specified in JniRuntime.CreationOptions.ValueManager.", nameof (options)); - valueManager = SetRuntime (manager); + valueManager = SetRuntime (manager); } - public abstract partial class JniValueManager : ISetRuntime, IDisposable { + public abstract partial class JniValueManager : ISetRuntime, IDisposable + { internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; - JniRuntime? runtime; - bool disposed; - public JniRuntime Runtime { + JniRuntime? runtime; + bool disposed; + public JniRuntime Runtime { get => runtime ?? throw new NotSupportedException (); } @@ -86,7 +89,7 @@ protected virtual void Dispose (bool disposing) public abstract void FinalizePeer (IJavaPeerable value); - public abstract List GetSurfacedPeers (); + public abstract List GetSurfacedPeers (); public abstract void ActivatePeer (IJavaPeerable? self, JniObjectReference reference, ConstructorInfo cinfo, object? []? argumentValues); @@ -95,7 +98,7 @@ public void ConstructPeer (IJavaPeerable peer, ref JniObjectReference reference, if (peer == null) throw new ArgumentNullException (nameof (peer)); - var newRef = peer.PeerReference; + var newRef = peer.PeerReference; if (newRef.IsValid) { JniObjectReference.Dispose (ref reference, options); @@ -105,17 +108,17 @@ public void ConstructPeer (IJavaPeerable peer, ref JniObjectReference reference, return; } JniObjectReference.Dispose (ref reference, options); - newRef = newRef.NewGlobalRef (); + newRef = newRef.NewGlobalRef (); } else if (options == JniObjectReferenceOptions.None) { // `reference` is likely *InvalidJniObjectReference, and can't be touched return; } else if (!reference.IsValid) { throw new ArgumentException ("JNI Object Reference is invalid.", nameof (reference)); } else { - newRef = reference; + newRef = reference; if ((options & JniObjectReferenceOptions.Copy) == JniObjectReferenceOptions.Copy) { - newRef = reference.NewGlobalRef (); + newRef = reference.NewGlobalRef (); } JniObjectReference.Dispose (ref reference, options); @@ -222,7 +225,7 @@ public virtual void DisposePeerUnlessReferenced (IJavaPeerable value) if (!reference.IsValid) return null; - var t = PeekPeer (reference); + var t = PeekPeer (reference); if (t == null) return t; @@ -234,15 +237,15 @@ public virtual void DisposePeerUnlessReferenced (IJavaPeerable value) protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (true)] out object? result) { - result = null; - var p = value as JavaProxyObject; + result = null; + var p = value as JavaProxyObject; if (p != null) { - result = p.Value; + result = p.Value; return true; } - var x = value as JavaProxyThrowable; + var x = value as JavaProxyThrowable; if (x != null) { - result = x.Exception; + result = x.Exception; return true; } return false; @@ -250,7 +253,7 @@ protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (tr object? PeekBoxedObject (JniObjectReference reference) { - var t = PeekPeer (reference); + var t = PeekPeer (reference); if (t == null) return null; object? r; @@ -284,7 +287,7 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) return null; } - var peeked = PeekPeer (reference); + var peeked = PeekPeer (reference); if (peeked != null && (targetType == null || targetType.IsAssignableFrom (peeked.GetType ()))) { @@ -292,7 +295,72 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) } return CreatePeer (ref reference, JniObjectReferenceOptions.Copy, targetType); } +#if false + class AndroidTypeMapUniverse { } + public IJavaPeerable? CreatePeer( + ref JniObjectReference reference, + JniObjectReferenceOptions options, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType) + { + if (disposed) + throw new ObjectDisposedException (GetType ().Name); + + if (!reference.IsValid) { + return null; + } + + targetType = targetType ?? typeof (JavaObject); + targetType = GetPeerType (targetType); + if (!typeof (IJavaPeerable).IsAssignableFrom (targetType)) + throw new ArgumentException ($"targetType `{targetType.AssemblyQualifiedName}` must implement IJavaPeerable!", nameof (targetType)); + IReadOnlyDictionary typemap = System.Runtime.InteropServices.TypeMapping.GetOrCreateExternalTypeMapping(); + string? className; + JniObjectReference refClass; + Type? mappedType = null; + do { + refClass = JniEnvironment.Types.GetObjectClass (reference); + className = JniEnvironment.Types.GetJniTypeNameFromClass (refClass); + if (className is not null && typemap.TryGetValue (className, out mappedType)) { + JniObjectReference.Dispose (ref refClass); + break; + } + var oldClass = refClass; + refClass = JniEnvironment.Types.GetSuperclass (refClass); + JniObjectReference.Dispose (ref oldClass); + } + while (refClass.IsValid); + + if (mappedType is null) { + throw new InvalidOperationException ("Java object type does not have mapping to .NET type."); + } + if (!mappedType.IsAssignableTo(targetType)) { + throw new InvalidOperationException("Mapped .NET type is not assignable to target type."); + } + + var managedObject = GetUninitializedObject (mappedType); + var constructed = false; + try { + constructed = TryConstructPeer (managedObject, ref reference, options, mappedType); + } finally { + if (!constructed) { + GC.SuppressFinalize (managedObject); + managedObject = null; + } + } + return managedObject; + static IJavaPeerable GetUninitializedObject ( + [DynamicallyAccessedMembers (Constructors)] + Type type) + { + var v = (IJavaPeerable) System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject (type); + v.SetJniManagedPeerState (JniManagedPeerStates.Replaceable | JniManagedPeerStates.Activatable); + return v; + } + + } +#else public virtual IJavaPeerable? CreatePeer ( ref JniObjectReference reference, JniObjectReferenceOptions transfer, @@ -306,18 +374,18 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) return null; } - targetType = targetType ?? typeof (JavaObject); - targetType = GetPeerType (targetType); + targetType = targetType ?? typeof (JavaObject); + targetType = GetPeerType (targetType); if (!typeof (IJavaPeerable).IsAssignableFrom (targetType)) throw new ArgumentException ($"targetType `{targetType.AssemblyQualifiedName}` must implement IJavaPeerable!", nameof (targetType)); - var targetSig = Runtime.TypeManager.GetTypeSignature (targetType); + var targetSig = Runtime.TypeManager.GetTypeSignature (targetType); if (!targetSig.IsValid || targetSig.SimpleReference == null) { throw new ArgumentException ($"Could not determine Java type corresponding to `{targetType.AssemblyQualifiedName}`.", nameof (targetType)); } - var refClass = JniEnvironment.Types.GetObjectClass (reference); + var refClass = JniEnvironment.Types.GetObjectClass (reference); JniObjectReference targetClass; try { targetClass = JniEnvironment.Types.FindClass (targetSig.SimpleReference); @@ -369,13 +437,13 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) } } - var super = JniEnvironment.Types.GetSuperclass (klass); + var super = JniEnvironment.Types.GetSuperclass (klass); jniTypeName = super.IsValid ? JniEnvironment.Types.GetJniTypeNameFromClass (super) : null; JniObjectReference.Dispose (ref klass, JniObjectReferenceOptions.CopyAndDispose); - klass = super; + klass = super; } JniObjectReference.Dispose (ref klass, JniObjectReferenceOptions.CopyAndDispose); @@ -385,12 +453,54 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) [return: DynamicallyAccessedMembers (Constructors)] Type? GetTypeAssignableTo (JniTypeSignature sig, Type targetType) { - foreach (var t in Runtime.TypeManager.GetTypes (sig)) { - if (targetType.IsAssignableFrom (t)) { - return t; + string[] sdkAssemblyNames = ["Mono.Android", "Java.Base", "Java.Runtime", "Java.Interop"]; + // Find single best instance + Type? best = null; + foreach (Type type in Runtime.TypeManager.GetTypes (sig)) { + if (best is null) { + best = type; + continue; + } + if (type == best) + continue; + // Types in sdk assemblies should be first in the list + if ((uint)Array.IndexOf(sdkAssemblyNames, best.Module.Assembly.GetName ().Name) > + (uint)Array.IndexOf(sdkAssemblyNames, type.Module.Assembly.GetName ().Name)) { + best = type; + continue; + } + // We found the `Invoker` type *before* the declared type + // Fix things up so the abstract type is first, and the `Invoker` is considered a duplicate. + if ((type.IsAbstract || type.IsInterface) && + !best.IsAbstract && + !best.IsInterface && + type.IsAssignableFrom (best)) { + best = type; + continue; + } + + // If the type is a derived type of the current best, then it is better + if (type.IsAssignableTo(best)) + { + best = type; + continue; + } + + // we found a generic subclass of a non-generic type + if (type.IsGenericType && + !best.IsGenericType && + type.IsAssignableTo (best)) { + best = type; + continue; } } + if (best is not null && best.IsAssignableTo (targetType)) + return best; return null; + //throw new InvalidOperationException ( + // $"Best type for instance of Java class '{sig.Name}' is '{best.FullName}', " + // + $"which is not assignable to target type '{targetType.FullName}'." + // + $"\nOther mapped types:\n{string.Join("\n", Runtime.TypeManager.GetTypes(sig).Select(t => t.FullName))}"); } } @@ -400,16 +510,16 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) [DynamicallyAccessedMembers (Constructors)] Type type) { - type = Runtime.TypeManager.GetInvokerType (type) ?? type; + type = Runtime.TypeManager.GetInvokerType (type) ?? type; - var self = GetUninitializedObject (type); + var self = GetUninitializedObject (type); var constructed = false; try { constructed = TryConstructPeer (self, ref reference, options, type); } finally { if (!constructed) { GC.SuppressFinalize (self); - self = null; + self = null; } } return self; @@ -418,15 +528,16 @@ static IJavaPeerable GetUninitializedObject ( [DynamicallyAccessedMembers (Constructors)] Type type) { - var v = (IJavaPeerable) System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject (type); + var v = (IJavaPeerable) System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject (type); v.SetJniManagedPeerState (JniManagedPeerStates.Replaceable | JniManagedPeerStates.Activatable); return v; } } +#endif - const BindingFlags ActivationConstructorBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; - static readonly Type ByRefJniObjectReference = typeof (JniObjectReference).MakeByRefType (); - static readonly Type[] JIConstructorSignature = new Type [] { ByRefJniObjectReference, typeof (JniObjectReferenceOptions) }; + const BindingFlags ActivationConstructorBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + static readonly Type ByRefJniObjectReference = typeof (JniObjectReference).MakeByRefType (); + static readonly Type [] JIConstructorSignature = new Type [] { ByRefJniObjectReference, typeof (JniObjectReferenceOptions) }; protected virtual bool TryConstructPeer ( @@ -438,12 +549,12 @@ protected virtual bool TryConstructPeer ( { var c = type.GetConstructor (ActivationConstructorBindingFlags, null, JIConstructorSignature, null); if (c != null) { - var args = new object[] { + var args = new object [] { reference, options, }; c.Invoke (self, args); - reference = (JniObjectReference) args [0]; + reference = (JniObjectReference) args [0]; return true; } return false; @@ -465,7 +576,7 @@ protected virtual bool TryConstructPeer ( return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); } - var boxed = PeekBoxedObject (reference); + var boxed = PeekBoxedObject (reference); if (boxed != null) { JniObjectReference.Dispose (ref reference, options); if (targetType != null) @@ -478,14 +589,14 @@ protected virtual bool TryConstructPeer ( // Let's hope this is an IJavaPeerable! return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); } - var marshaler = GetValueMarshaler (targetType); + var marshaler = GetValueMarshaler (targetType); return marshaler.CreateValue (ref reference, options, targetType); } [return: MaybeNull] public T CreateValue< [DynamicallyAccessedMembers (Constructors)] - T + T > ( ref JniObjectReference reference, JniObjectReferenceOptions options, @@ -509,21 +620,21 @@ public T CreateValue< typeof (T)), nameof (targetType)); - var boxed = PeekBoxedObject (reference); + var boxed = PeekBoxedObject (reference); if (boxed != null) { JniObjectReference.Dispose (ref reference, options); return (T) Convert.ChangeType (boxed, targetType ?? typeof (T)); } - targetType = targetType ?? typeof (T); + targetType = targetType ?? typeof (T); if (typeof (IJavaPeerable).IsAssignableFrom (targetType)) { -#pragma warning disable CS8600,CS8601 // Possible null reference assignment. +#pragma warning disable CS8600, CS8601 // Possible null reference assignment. return (T) JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); -#pragma warning restore CS8600,CS8601 // Possible null reference assignment. +#pragma warning restore CS8600, CS8601 // Possible null reference assignment. } - var marshaler = GetValueMarshaler (); + var marshaler = GetValueMarshaler (); return marshaler.CreateGenericValue (ref reference, options, targetType); } @@ -565,25 +676,25 @@ public T CreateValue< // Let's hope this is an IJavaPeerable! return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); } - var marshaler = GetValueMarshaler (targetType); + var marshaler = GetValueMarshaler (targetType); return marshaler.CreateValue (ref reference, options, targetType); } [return: MaybeNull] public T GetValue< [DynamicallyAccessedMembers (Constructors)] - T + T > ( IntPtr handle) { - var r = new JniObjectReference (handle); + var r = new JniObjectReference (handle); return GetValue (ref r, JniObjectReferenceOptions.Copy); } [return: MaybeNull] public T GetValue< [DynamicallyAccessedMembers (Constructors)] - T + T > ( ref JniObjectReference reference, JniObjectReferenceOptions options, @@ -604,7 +715,7 @@ public T GetValue< typeof (T)), nameof (targetType)); - targetType = targetType ?? typeof (T); + targetType = targetType ?? typeof (T); var existing = PeekValue (reference); if (existing != null && (targetType == null || targetType.IsAssignableFrom (existing.GetType ()))) { @@ -613,12 +724,12 @@ public T GetValue< } if (typeof (IJavaPeerable).IsAssignableFrom (targetType)) { -#pragma warning disable CS8600,CS8601 // Possible null reference assignment. +#pragma warning disable CS8600, CS8601 // Possible null reference assignment. return (T) JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); -#pragma warning restore CS8600,CS8601 // Possible null reference assignment. +#pragma warning restore CS8600, CS8601 // Possible null reference assignment. } - var marshaler = GetValueMarshaler (); + var marshaler = GetValueMarshaler (); return marshaler.CreateGenericValue (ref reference, options, targetType); } @@ -626,14 +737,14 @@ public T GetValue< public JniValueMarshaler GetValueMarshaler< [DynamicallyAccessedMembers (Constructors)] - T + T > () { if (disposed) throw new ObjectDisposedException (GetType ().Name); - var m = GetValueMarshaler (typeof (T)); - var r = m as JniValueMarshaler; + var m = GetValueMarshaler (typeof (T)); + var r = m as JniValueMarshaler; if (r != null) return r; lock (Marshalers) { @@ -653,7 +764,7 @@ public JniValueMarshaler GetValueMarshaler (Type type) if (type.ContainsGenericParameters) throw new ArgumentException ("Generic type definitions are not supported.", nameof (type)); - var marshalerAttr = type.GetCustomAttribute (); + var marshalerAttr = type.GetCustomAttribute (); if (marshalerAttr != null) return (JniValueMarshaler) Activator.CreateInstance (marshalerAttr.MarshalerType)!; @@ -669,7 +780,7 @@ public JniValueMarshaler GetValueMarshaler (Type type) } - var listType = GetListType (type); + var listType = GetListType (type); if (listType != null) { var elementType = listType.GenericTypeArguments [0]; if (elementType.IsValueType) { @@ -689,7 +800,7 @@ public JniValueMarshaler GetValueMarshaler (Type type) return GetValueMarshalerCore (type); } - static Type? GetListType(Type type) + static Type? GetListType (Type type) { foreach (var iface in GetInterfaces (type).Concat (new [] { type })) { if (typeof (IList<>).IsAssignableFrom (iface.IsGenericType ? iface.GetGenericTypeDefinition () : iface)) @@ -721,7 +832,7 @@ static MethodInfo MakeGenericMethod (MethodInfo method, Type type) => static JniValueMarshaler GetObjectArrayMarshalerHelper< [DynamicallyAccessedMembers (Constructors)] - T + T > () { return JavaObjectArray.Instance; @@ -734,12 +845,13 @@ protected virtual JniValueMarshaler GetValueMarshalerCore (Type type) } } - sealed class VoidValueMarshaler : JniValueMarshaler { + sealed class VoidValueMarshaler : JniValueMarshaler + { - internal static VoidValueMarshaler Instance = new VoidValueMarshaler (); + internal static VoidValueMarshaler Instance = new VoidValueMarshaler (); public override Type MarshalType { - get {return typeof (void);} + get { return typeof (void); } } public override object? CreateValue ( @@ -762,9 +874,10 @@ public override void DestroyArgumentState (object? value, ref JniValueMarshalerS } } - sealed class JavaPeerableValueMarshaler : JniValueMarshaler { + sealed class JavaPeerableValueMarshaler : JniValueMarshaler + { - internal static JavaPeerableValueMarshaler Instance = new JavaPeerableValueMarshaler (); + internal static JavaPeerableValueMarshaler Instance = new JavaPeerableValueMarshaler (); [return: MaybeNull] public override IJavaPeerable? CreateGenericValue ( @@ -773,26 +886,26 @@ sealed class JavaPeerableValueMarshaler : JniValueMarshaler { [DynamicallyAccessedMembers (Constructors)] Type? targetType) { - var jvm = JniEnvironment.Runtime; - var marshaler = jvm.ValueManager.GetValueMarshaler (targetType ?? typeof(IJavaPeerable)); + var jvm = JniEnvironment.Runtime; + var marshaler = jvm.ValueManager.GetValueMarshaler (targetType ?? typeof (IJavaPeerable)); if (marshaler != Instance) return (IJavaPeerable) marshaler.CreateValue (ref reference, options, targetType)!; return jvm.ValueManager.CreatePeer (ref reference, options, targetType); } - public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull]IJavaPeerable? value, ParameterAttributes synchronize) + public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull] IJavaPeerable? value, ParameterAttributes synchronize) { if (value == null || !value.PeerReference.IsValid) return new JniValueMarshalerState (); - var r = value.PeerReference.NewLocalRef (); + var r = value.PeerReference.NewLocalRef (); return new JniValueMarshalerState (r); } - public override void DestroyGenericArgumentState ([MaybeNull]IJavaPeerable? value, ref JniValueMarshalerState state, ParameterAttributes synchronize) + public override void DestroyGenericArgumentState ([MaybeNull] IJavaPeerable? value, ref JniValueMarshalerState state, ParameterAttributes synchronize) { - var r = state.ReferenceValue; + var r = state.ReferenceValue; JniObjectReference.Dispose (ref r); - state = new JniValueMarshalerState (); + state = new JniValueMarshalerState (); } [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] @@ -809,13 +922,13 @@ public override Expression CreateParameterFromManagedExpression (JniValueMarshal [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] Expression CreateIntermediaryExpressionFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) { - var r = Expression.Variable (typeof (JniObjectReference), sourceValue.Name + "_ref"); + var r = Expression.Variable (typeof (JniObjectReference), sourceValue.Name + "_ref"); context.LocalVariables.Add (r); context.CreationStatements.Add ( Expression.IfThenElse ( - test: Expression.Equal (Expression.Constant (null), sourceValue), - ifTrue: Expression.Assign (r, Expression.New (typeof (JniObjectReference))), - ifFalse: Expression.Assign (r, Expression.Property (Expression.Convert (sourceValue, typeof (IJavaPeerable)), "PeerReference")))); + test: Expression.Equal (Expression.Constant (null), sourceValue), + ifTrue: Expression.Assign (r, Expression.New (typeof (JniObjectReference))), + ifFalse: Expression.Assign (r, Expression.Property (Expression.Convert (sourceValue, typeof (IJavaPeerable)), "PeerReference")))); return r; } @@ -833,14 +946,14 @@ public override Expression CreateParameterToManagedExpression (JniValueMarshaler { targetType ??= typeof (object); - var r = Expression.Variable (targetType, sourceValue.Name + "_val"); + var r = Expression.Variable (targetType, sourceValue.Name + "_val"); context.LocalVariables.Add (r); context.CreationStatements.Add ( Expression.Assign (r, Expression.Call ( context.ValueManager ?? Expression.Property (context.Runtime, "ValueManager"), "GetValue", - new[]{targetType}, + new [] { targetType }, sourceValue))); return r; } @@ -848,16 +961,16 @@ public override Expression CreateParameterToManagedExpression (JniValueMarshaler sealed class DelegatingValueMarshaler< [DynamicallyAccessedMembers (Constructors)] - T + T > : JniValueMarshaler { - JniValueMarshaler ValueMarshaler; + JniValueMarshaler ValueMarshaler; public DelegatingValueMarshaler (JniValueMarshaler valueMarshaler) { - ValueMarshaler = valueMarshaler; + ValueMarshaler = valueMarshaler; } [return: MaybeNull] @@ -870,12 +983,12 @@ public override T CreateGenericValue ( return (T) ValueMarshaler.CreateValue (ref reference, options, targetType ?? typeof (T))!; } - public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull]T value, ParameterAttributes synchronize) + public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull] T value, ParameterAttributes synchronize) { return ValueMarshaler.CreateObjectReferenceArgumentState (value, synchronize); } - public override void DestroyGenericArgumentState ([AllowNull]T value, ref JniValueMarshalerState state, ParameterAttributes synchronize) + public override void DestroyGenericArgumentState ([AllowNull] T value, ref JniValueMarshalerState state, ParameterAttributes synchronize) { ValueMarshaler.DestroyArgumentState (value, ref state, synchronize); } @@ -901,9 +1014,10 @@ public override Expression CreateReturnValueFromManagedExpression (JniValueMarsh } } - sealed class ProxyValueMarshaler : JniValueMarshaler { + sealed class ProxyValueMarshaler : JniValueMarshaler + { - internal static ProxyValueMarshaler Instance = new ProxyValueMarshaler (); + internal static ProxyValueMarshaler Instance = new ProxyValueMarshaler (); [return: MaybeNull] public override object? CreateGenericValue ( @@ -912,19 +1026,19 @@ sealed class ProxyValueMarshaler : JniValueMarshaler { [DynamicallyAccessedMembers (Constructors)] Type? targetType) { - var jvm = JniEnvironment.Runtime; + var jvm = JniEnvironment.Runtime; if (targetType == null || targetType == typeof (object)) { - targetType = jvm.ValueManager.GetRuntimeType (reference); + targetType = jvm.ValueManager.GetRuntimeType (reference); } if (targetType != null) { - var vm = jvm.ValueManager.GetValueMarshaler (targetType); + var vm = jvm.ValueManager.GetValueMarshaler (targetType); if (vm != Instance) { return vm.CreateValue (ref reference, options, targetType)!; } } - var target = jvm.ValueManager.PeekValue (reference); + var target = jvm.ValueManager.PeekValue (reference); if (target != null) { JniObjectReference.Dispose (ref reference, options); return target; @@ -933,31 +1047,31 @@ sealed class ProxyValueMarshaler : JniValueMarshaler { return jvm.ValueManager.CreatePeer (ref reference, options, targetType); } - public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull]object? value, ParameterAttributes synchronize) + public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull] object? value, ParameterAttributes synchronize) { if (value == null) return new JniValueMarshalerState (); - var jvm = JniEnvironment.Runtime; + var jvm = JniEnvironment.Runtime; - var vm = jvm.ValueManager.GetValueMarshaler (value.GetType ()); + var vm = jvm.ValueManager.GetValueMarshaler (value.GetType ()); if (vm != Instance) { - var s = vm.CreateObjectReferenceArgumentState (value, synchronize); + var s = vm.CreateObjectReferenceArgumentState (value, synchronize); return new JniValueMarshalerState (s, vm); } - var p = JavaProxyObject.GetProxy (value); + var p = JavaProxyObject.GetProxy (value); return new JniValueMarshalerState (p!.PeerReference.NewLocalRef ()); } public override void DestroyGenericArgumentState (object? value, ref JniValueMarshalerState state, ParameterAttributes synchronize) { - var vm = state.Extra as JniValueMarshaler; + var vm = state.Extra as JniValueMarshaler; if (vm != null) { vm.DestroyArgumentState (value, ref state, synchronize); return; } - var r = state.ReferenceValue; + var r = state.ReferenceValue; JniObjectReference.Dispose (ref r); state = new JniValueMarshalerState (); } From f7bfdcc36997909afe79af3e2ac4bc5fb442de79 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:26:23 -0700 Subject: [PATCH 03/10] Undo formatting changes --- .../JniRuntime.JniValueManager.cs | 281 +++++++----------- 1 file changed, 103 insertions(+), 178 deletions(-) diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs index baff66b8b..5ddd2a952 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs @@ -14,49 +14,46 @@ namespace Java.Interop { - public class JniSurfacedPeerInfo - { + public class JniSurfacedPeerInfo { - public int JniIdentityHashCode { get; private set; } - public WeakReference SurfacedPeer { get; private set; } + public int JniIdentityHashCode {get; private set;} + public WeakReference SurfacedPeer {get; private set;} public JniSurfacedPeerInfo (int jniIdentityHashCode, WeakReference surfacedPeer) { - JniIdentityHashCode = jniIdentityHashCode; - SurfacedPeer = surfacedPeer; + JniIdentityHashCode = jniIdentityHashCode; + SurfacedPeer = surfacedPeer; } } partial class JniRuntime { - partial class CreationOptions - { - public JniValueManager? ValueManager { get; set; } + partial class CreationOptions { + public JniValueManager? ValueManager {get; set;} } - internal JniValueManager? valueManager; - public JniValueManager ValueManager { + internal JniValueManager? valueManager; + public JniValueManager ValueManager { get => valueManager ?? throw new NotSupportedException (); } partial void SetValueManager (CreationOptions options) { - var manager = options.ValueManager; + var manager = options.ValueManager; if (manager == null) throw new ArgumentException ( "No JniValueManager specified in JniRuntime.CreationOptions.ValueManager.", nameof (options)); - valueManager = SetRuntime (manager); + valueManager = SetRuntime (manager); } - public abstract partial class JniValueManager : ISetRuntime, IDisposable - { + public abstract partial class JniValueManager : ISetRuntime, IDisposable { internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; - JniRuntime? runtime; - bool disposed; - public JniRuntime Runtime { + JniRuntime? runtime; + bool disposed; + public JniRuntime Runtime { get => runtime ?? throw new NotSupportedException (); } @@ -89,7 +86,7 @@ protected virtual void Dispose (bool disposing) public abstract void FinalizePeer (IJavaPeerable value); - public abstract List GetSurfacedPeers (); + public abstract List GetSurfacedPeers (); public abstract void ActivatePeer (IJavaPeerable? self, JniObjectReference reference, ConstructorInfo cinfo, object? []? argumentValues); @@ -98,7 +95,7 @@ public void ConstructPeer (IJavaPeerable peer, ref JniObjectReference reference, if (peer == null) throw new ArgumentNullException (nameof (peer)); - var newRef = peer.PeerReference; + var newRef = peer.PeerReference; if (newRef.IsValid) { JniObjectReference.Dispose (ref reference, options); @@ -108,17 +105,17 @@ public void ConstructPeer (IJavaPeerable peer, ref JniObjectReference reference, return; } JniObjectReference.Dispose (ref reference, options); - newRef = newRef.NewGlobalRef (); + newRef = newRef.NewGlobalRef (); } else if (options == JniObjectReferenceOptions.None) { // `reference` is likely *InvalidJniObjectReference, and can't be touched return; } else if (!reference.IsValid) { throw new ArgumentException ("JNI Object Reference is invalid.", nameof (reference)); } else { - newRef = reference; + newRef = reference; if ((options & JniObjectReferenceOptions.Copy) == JniObjectReferenceOptions.Copy) { - newRef = reference.NewGlobalRef (); + newRef = reference.NewGlobalRef (); } JniObjectReference.Dispose (ref reference, options); @@ -225,7 +222,7 @@ public virtual void DisposePeerUnlessReferenced (IJavaPeerable value) if (!reference.IsValid) return null; - var t = PeekPeer (reference); + var t = PeekPeer (reference); if (t == null) return t; @@ -237,15 +234,15 @@ public virtual void DisposePeerUnlessReferenced (IJavaPeerable value) protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (true)] out object? result) { - result = null; - var p = value as JavaProxyObject; + result = null; + var p = value as JavaProxyObject; if (p != null) { - result = p.Value; + result = p.Value; return true; } - var x = value as JavaProxyThrowable; + var x = value as JavaProxyThrowable; if (x != null) { - result = x.Exception; + result = x.Exception; return true; } return false; @@ -253,7 +250,7 @@ protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (tr object? PeekBoxedObject (JniObjectReference reference) { - var t = PeekPeer (reference); + var t = PeekPeer (reference); if (t == null) return null; object? r; @@ -287,7 +284,7 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) return null; } - var peeked = PeekPeer (reference); + var peeked = PeekPeer (reference); if (peeked != null && (targetType == null || targetType.IsAssignableFrom (peeked.GetType ()))) { @@ -295,72 +292,7 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) } return CreatePeer (ref reference, JniObjectReferenceOptions.Copy, targetType); } -#if false - class AndroidTypeMapUniverse { } - public IJavaPeerable? CreatePeer( - ref JniObjectReference reference, - JniObjectReferenceOptions options, - [DynamicallyAccessedMembers (Constructors)] - Type? targetType) - { - if (disposed) - throw new ObjectDisposedException (GetType ().Name); - - if (!reference.IsValid) { - return null; - } - targetType = targetType ?? typeof (JavaObject); - targetType = GetPeerType (targetType); - if (!typeof (IJavaPeerable).IsAssignableFrom (targetType)) - throw new ArgumentException ($"targetType `{targetType.AssemblyQualifiedName}` must implement IJavaPeerable!", nameof (targetType)); - IReadOnlyDictionary typemap = System.Runtime.InteropServices.TypeMapping.GetOrCreateExternalTypeMapping(); - string? className; - JniObjectReference refClass; - Type? mappedType = null; - do { - refClass = JniEnvironment.Types.GetObjectClass (reference); - className = JniEnvironment.Types.GetJniTypeNameFromClass (refClass); - if (className is not null && typemap.TryGetValue (className, out mappedType)) { - JniObjectReference.Dispose (ref refClass); - break; - } - var oldClass = refClass; - refClass = JniEnvironment.Types.GetSuperclass (refClass); - JniObjectReference.Dispose (ref oldClass); - } - while (refClass.IsValid); - - if (mappedType is null) { - throw new InvalidOperationException ("Java object type does not have mapping to .NET type."); - } - if (!mappedType.IsAssignableTo(targetType)) { - throw new InvalidOperationException("Mapped .NET type is not assignable to target type."); - } - - var managedObject = GetUninitializedObject (mappedType); - var constructed = false; - try { - constructed = TryConstructPeer (managedObject, ref reference, options, mappedType); - } finally { - if (!constructed) { - GC.SuppressFinalize (managedObject); - managedObject = null; - } - } - return managedObject; - - static IJavaPeerable GetUninitializedObject ( - [DynamicallyAccessedMembers (Constructors)] - Type type) - { - var v = (IJavaPeerable) System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject (type); - v.SetJniManagedPeerState (JniManagedPeerStates.Replaceable | JniManagedPeerStates.Activatable); - return v; - } - - } -#else public virtual IJavaPeerable? CreatePeer ( ref JniObjectReference reference, JniObjectReferenceOptions transfer, @@ -374,18 +306,18 @@ static IJavaPeerable GetUninitializedObject ( return null; } - targetType = targetType ?? typeof (JavaObject); - targetType = GetPeerType (targetType); + targetType = targetType ?? typeof (JavaObject); + targetType = GetPeerType (targetType); if (!typeof (IJavaPeerable).IsAssignableFrom (targetType)) throw new ArgumentException ($"targetType `{targetType.AssemblyQualifiedName}` must implement IJavaPeerable!", nameof (targetType)); - var targetSig = Runtime.TypeManager.GetTypeSignature (targetType); + var targetSig = Runtime.TypeManager.GetTypeSignature (targetType); if (!targetSig.IsValid || targetSig.SimpleReference == null) { throw new ArgumentException ($"Could not determine Java type corresponding to `{targetType.AssemblyQualifiedName}`.", nameof (targetType)); } - var refClass = JniEnvironment.Types.GetObjectClass (reference); + var refClass = JniEnvironment.Types.GetObjectClass (reference); JniObjectReference targetClass; try { targetClass = JniEnvironment.Types.FindClass (targetSig.SimpleReference); @@ -437,13 +369,13 @@ static IJavaPeerable GetUninitializedObject ( } } - var super = JniEnvironment.Types.GetSuperclass (klass); + var super = JniEnvironment.Types.GetSuperclass (klass); jniTypeName = super.IsValid ? JniEnvironment.Types.GetJniTypeNameFromClass (super) : null; JniObjectReference.Dispose (ref klass, JniObjectReferenceOptions.CopyAndDispose); - klass = super; + klass = super; } JniObjectReference.Dispose (ref klass, JniObjectReferenceOptions.CopyAndDispose); @@ -494,13 +426,10 @@ static IJavaPeerable GetUninitializedObject ( continue; } } + if (best is not null && best.IsAssignableTo (targetType)) return best; return null; - //throw new InvalidOperationException ( - // $"Best type for instance of Java class '{sig.Name}' is '{best.FullName}', " - // + $"which is not assignable to target type '{targetType.FullName}'." - // + $"\nOther mapped types:\n{string.Join("\n", Runtime.TypeManager.GetTypes(sig).Select(t => t.FullName))}"); } } @@ -510,16 +439,16 @@ static IJavaPeerable GetUninitializedObject ( [DynamicallyAccessedMembers (Constructors)] Type type) { - type = Runtime.TypeManager.GetInvokerType (type) ?? type; + type = Runtime.TypeManager.GetInvokerType (type) ?? type; - var self = GetUninitializedObject (type); + var self = GetUninitializedObject (type); var constructed = false; try { constructed = TryConstructPeer (self, ref reference, options, type); } finally { if (!constructed) { GC.SuppressFinalize (self); - self = null; + self = null; } } return self; @@ -528,16 +457,15 @@ static IJavaPeerable GetUninitializedObject ( [DynamicallyAccessedMembers (Constructors)] Type type) { - var v = (IJavaPeerable) System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject (type); + var v = (IJavaPeerable) System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject (type); v.SetJniManagedPeerState (JniManagedPeerStates.Replaceable | JniManagedPeerStates.Activatable); return v; } } -#endif - const BindingFlags ActivationConstructorBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; - static readonly Type ByRefJniObjectReference = typeof (JniObjectReference).MakeByRefType (); - static readonly Type [] JIConstructorSignature = new Type [] { ByRefJniObjectReference, typeof (JniObjectReferenceOptions) }; + const BindingFlags ActivationConstructorBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + static readonly Type ByRefJniObjectReference = typeof (JniObjectReference).MakeByRefType (); + static readonly Type[] JIConstructorSignature = new Type [] { ByRefJniObjectReference, typeof (JniObjectReferenceOptions) }; protected virtual bool TryConstructPeer ( @@ -549,12 +477,12 @@ protected virtual bool TryConstructPeer ( { var c = type.GetConstructor (ActivationConstructorBindingFlags, null, JIConstructorSignature, null); if (c != null) { - var args = new object [] { + var args = new object[] { reference, options, }; c.Invoke (self, args); - reference = (JniObjectReference) args [0]; + reference = (JniObjectReference) args [0]; return true; } return false; @@ -576,7 +504,7 @@ protected virtual bool TryConstructPeer ( return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); } - var boxed = PeekBoxedObject (reference); + var boxed = PeekBoxedObject (reference); if (boxed != null) { JniObjectReference.Dispose (ref reference, options); if (targetType != null) @@ -589,14 +517,14 @@ protected virtual bool TryConstructPeer ( // Let's hope this is an IJavaPeerable! return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); } - var marshaler = GetValueMarshaler (targetType); + var marshaler = GetValueMarshaler (targetType); return marshaler.CreateValue (ref reference, options, targetType); } [return: MaybeNull] public T CreateValue< [DynamicallyAccessedMembers (Constructors)] - T + T > ( ref JniObjectReference reference, JniObjectReferenceOptions options, @@ -620,21 +548,21 @@ public T CreateValue< typeof (T)), nameof (targetType)); - var boxed = PeekBoxedObject (reference); + var boxed = PeekBoxedObject (reference); if (boxed != null) { JniObjectReference.Dispose (ref reference, options); return (T) Convert.ChangeType (boxed, targetType ?? typeof (T)); } - targetType = targetType ?? typeof (T); + targetType = targetType ?? typeof (T); if (typeof (IJavaPeerable).IsAssignableFrom (targetType)) { -#pragma warning disable CS8600, CS8601 // Possible null reference assignment. +#pragma warning disable CS8600,CS8601 // Possible null reference assignment. return (T) JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); -#pragma warning restore CS8600, CS8601 // Possible null reference assignment. +#pragma warning restore CS8600,CS8601 // Possible null reference assignment. } - var marshaler = GetValueMarshaler (); + var marshaler = GetValueMarshaler (); return marshaler.CreateGenericValue (ref reference, options, targetType); } @@ -676,25 +604,25 @@ public T CreateValue< // Let's hope this is an IJavaPeerable! return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); } - var marshaler = GetValueMarshaler (targetType); + var marshaler = GetValueMarshaler (targetType); return marshaler.CreateValue (ref reference, options, targetType); } [return: MaybeNull] public T GetValue< [DynamicallyAccessedMembers (Constructors)] - T + T > ( IntPtr handle) { - var r = new JniObjectReference (handle); + var r = new JniObjectReference (handle); return GetValue (ref r, JniObjectReferenceOptions.Copy); } [return: MaybeNull] public T GetValue< [DynamicallyAccessedMembers (Constructors)] - T + T > ( ref JniObjectReference reference, JniObjectReferenceOptions options, @@ -715,7 +643,7 @@ public T GetValue< typeof (T)), nameof (targetType)); - targetType = targetType ?? typeof (T); + targetType = targetType ?? typeof (T); var existing = PeekValue (reference); if (existing != null && (targetType == null || targetType.IsAssignableFrom (existing.GetType ()))) { @@ -724,12 +652,12 @@ public T GetValue< } if (typeof (IJavaPeerable).IsAssignableFrom (targetType)) { -#pragma warning disable CS8600, CS8601 // Possible null reference assignment. +#pragma warning disable CS8600,CS8601 // Possible null reference assignment. return (T) JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); -#pragma warning restore CS8600, CS8601 // Possible null reference assignment. +#pragma warning restore CS8600,CS8601 // Possible null reference assignment. } - var marshaler = GetValueMarshaler (); + var marshaler = GetValueMarshaler (); return marshaler.CreateGenericValue (ref reference, options, targetType); } @@ -737,14 +665,14 @@ public T GetValue< public JniValueMarshaler GetValueMarshaler< [DynamicallyAccessedMembers (Constructors)] - T + T > () { if (disposed) throw new ObjectDisposedException (GetType ().Name); - var m = GetValueMarshaler (typeof (T)); - var r = m as JniValueMarshaler; + var m = GetValueMarshaler (typeof (T)); + var r = m as JniValueMarshaler; if (r != null) return r; lock (Marshalers) { @@ -764,7 +692,7 @@ public JniValueMarshaler GetValueMarshaler (Type type) if (type.ContainsGenericParameters) throw new ArgumentException ("Generic type definitions are not supported.", nameof (type)); - var marshalerAttr = type.GetCustomAttribute (); + var marshalerAttr = type.GetCustomAttribute (); if (marshalerAttr != null) return (JniValueMarshaler) Activator.CreateInstance (marshalerAttr.MarshalerType)!; @@ -780,7 +708,7 @@ public JniValueMarshaler GetValueMarshaler (Type type) } - var listType = GetListType (type); + var listType = GetListType (type); if (listType != null) { var elementType = listType.GenericTypeArguments [0]; if (elementType.IsValueType) { @@ -800,7 +728,7 @@ public JniValueMarshaler GetValueMarshaler (Type type) return GetValueMarshalerCore (type); } - static Type? GetListType (Type type) + static Type? GetListType(Type type) { foreach (var iface in GetInterfaces (type).Concat (new [] { type })) { if (typeof (IList<>).IsAssignableFrom (iface.IsGenericType ? iface.GetGenericTypeDefinition () : iface)) @@ -832,7 +760,7 @@ static MethodInfo MakeGenericMethod (MethodInfo method, Type type) => static JniValueMarshaler GetObjectArrayMarshalerHelper< [DynamicallyAccessedMembers (Constructors)] - T + T > () { return JavaObjectArray.Instance; @@ -845,13 +773,12 @@ protected virtual JniValueMarshaler GetValueMarshalerCore (Type type) } } - sealed class VoidValueMarshaler : JniValueMarshaler - { + sealed class VoidValueMarshaler : JniValueMarshaler { - internal static VoidValueMarshaler Instance = new VoidValueMarshaler (); + internal static VoidValueMarshaler Instance = new VoidValueMarshaler (); public override Type MarshalType { - get { return typeof (void); } + get {return typeof (void);} } public override object? CreateValue ( @@ -874,10 +801,9 @@ public override void DestroyArgumentState (object? value, ref JniValueMarshalerS } } - sealed class JavaPeerableValueMarshaler : JniValueMarshaler - { + sealed class JavaPeerableValueMarshaler : JniValueMarshaler { - internal static JavaPeerableValueMarshaler Instance = new JavaPeerableValueMarshaler (); + internal static JavaPeerableValueMarshaler Instance = new JavaPeerableValueMarshaler (); [return: MaybeNull] public override IJavaPeerable? CreateGenericValue ( @@ -886,26 +812,26 @@ sealed class JavaPeerableValueMarshaler : JniValueMarshaler [DynamicallyAccessedMembers (Constructors)] Type? targetType) { - var jvm = JniEnvironment.Runtime; - var marshaler = jvm.ValueManager.GetValueMarshaler (targetType ?? typeof (IJavaPeerable)); + var jvm = JniEnvironment.Runtime; + var marshaler = jvm.ValueManager.GetValueMarshaler (targetType ?? typeof(IJavaPeerable)); if (marshaler != Instance) return (IJavaPeerable) marshaler.CreateValue (ref reference, options, targetType)!; return jvm.ValueManager.CreatePeer (ref reference, options, targetType); } - public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull] IJavaPeerable? value, ParameterAttributes synchronize) + public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull]IJavaPeerable? value, ParameterAttributes synchronize) { if (value == null || !value.PeerReference.IsValid) return new JniValueMarshalerState (); - var r = value.PeerReference.NewLocalRef (); + var r = value.PeerReference.NewLocalRef (); return new JniValueMarshalerState (r); } - public override void DestroyGenericArgumentState ([MaybeNull] IJavaPeerable? value, ref JniValueMarshalerState state, ParameterAttributes synchronize) + public override void DestroyGenericArgumentState ([MaybeNull]IJavaPeerable? value, ref JniValueMarshalerState state, ParameterAttributes synchronize) { - var r = state.ReferenceValue; + var r = state.ReferenceValue; JniObjectReference.Dispose (ref r); - state = new JniValueMarshalerState (); + state = new JniValueMarshalerState (); } [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] @@ -922,13 +848,13 @@ public override Expression CreateParameterFromManagedExpression (JniValueMarshal [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] Expression CreateIntermediaryExpressionFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) { - var r = Expression.Variable (typeof (JniObjectReference), sourceValue.Name + "_ref"); + var r = Expression.Variable (typeof (JniObjectReference), sourceValue.Name + "_ref"); context.LocalVariables.Add (r); context.CreationStatements.Add ( Expression.IfThenElse ( - test: Expression.Equal (Expression.Constant (null), sourceValue), - ifTrue: Expression.Assign (r, Expression.New (typeof (JniObjectReference))), - ifFalse: Expression.Assign (r, Expression.Property (Expression.Convert (sourceValue, typeof (IJavaPeerable)), "PeerReference")))); + test: Expression.Equal (Expression.Constant (null), sourceValue), + ifTrue: Expression.Assign (r, Expression.New (typeof (JniObjectReference))), + ifFalse: Expression.Assign (r, Expression.Property (Expression.Convert (sourceValue, typeof (IJavaPeerable)), "PeerReference")))); return r; } @@ -946,14 +872,14 @@ public override Expression CreateParameterToManagedExpression (JniValueMarshaler { targetType ??= typeof (object); - var r = Expression.Variable (targetType, sourceValue.Name + "_val"); + var r = Expression.Variable (targetType, sourceValue.Name + "_val"); context.LocalVariables.Add (r); context.CreationStatements.Add ( Expression.Assign (r, Expression.Call ( context.ValueManager ?? Expression.Property (context.Runtime, "ValueManager"), "GetValue", - new [] { targetType }, + new[]{targetType}, sourceValue))); return r; } @@ -961,16 +887,16 @@ public override Expression CreateParameterToManagedExpression (JniValueMarshaler sealed class DelegatingValueMarshaler< [DynamicallyAccessedMembers (Constructors)] - T + T > : JniValueMarshaler { - JniValueMarshaler ValueMarshaler; + JniValueMarshaler ValueMarshaler; public DelegatingValueMarshaler (JniValueMarshaler valueMarshaler) { - ValueMarshaler = valueMarshaler; + ValueMarshaler = valueMarshaler; } [return: MaybeNull] @@ -983,12 +909,12 @@ public override T CreateGenericValue ( return (T) ValueMarshaler.CreateValue (ref reference, options, targetType ?? typeof (T))!; } - public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull] T value, ParameterAttributes synchronize) + public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull]T value, ParameterAttributes synchronize) { return ValueMarshaler.CreateObjectReferenceArgumentState (value, synchronize); } - public override void DestroyGenericArgumentState ([AllowNull] T value, ref JniValueMarshalerState state, ParameterAttributes synchronize) + public override void DestroyGenericArgumentState ([AllowNull]T value, ref JniValueMarshalerState state, ParameterAttributes synchronize) { ValueMarshaler.DestroyArgumentState (value, ref state, synchronize); } @@ -1014,10 +940,9 @@ public override Expression CreateReturnValueFromManagedExpression (JniValueMarsh } } - sealed class ProxyValueMarshaler : JniValueMarshaler - { + sealed class ProxyValueMarshaler : JniValueMarshaler { - internal static ProxyValueMarshaler Instance = new ProxyValueMarshaler (); + internal static ProxyValueMarshaler Instance = new ProxyValueMarshaler (); [return: MaybeNull] public override object? CreateGenericValue ( @@ -1026,19 +951,19 @@ sealed class ProxyValueMarshaler : JniValueMarshaler [DynamicallyAccessedMembers (Constructors)] Type? targetType) { - var jvm = JniEnvironment.Runtime; + var jvm = JniEnvironment.Runtime; if (targetType == null || targetType == typeof (object)) { - targetType = jvm.ValueManager.GetRuntimeType (reference); + targetType = jvm.ValueManager.GetRuntimeType (reference); } if (targetType != null) { - var vm = jvm.ValueManager.GetValueMarshaler (targetType); + var vm = jvm.ValueManager.GetValueMarshaler (targetType); if (vm != Instance) { return vm.CreateValue (ref reference, options, targetType)!; } } - var target = jvm.ValueManager.PeekValue (reference); + var target = jvm.ValueManager.PeekValue (reference); if (target != null) { JniObjectReference.Dispose (ref reference, options); return target; @@ -1047,31 +972,31 @@ sealed class ProxyValueMarshaler : JniValueMarshaler return jvm.ValueManager.CreatePeer (ref reference, options, targetType); } - public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull] object? value, ParameterAttributes synchronize) + public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull]object? value, ParameterAttributes synchronize) { if (value == null) return new JniValueMarshalerState (); - var jvm = JniEnvironment.Runtime; + var jvm = JniEnvironment.Runtime; - var vm = jvm.ValueManager.GetValueMarshaler (value.GetType ()); + var vm = jvm.ValueManager.GetValueMarshaler (value.GetType ()); if (vm != Instance) { - var s = vm.CreateObjectReferenceArgumentState (value, synchronize); + var s = vm.CreateObjectReferenceArgumentState (value, synchronize); return new JniValueMarshalerState (s, vm); } - var p = JavaProxyObject.GetProxy (value); + var p = JavaProxyObject.GetProxy (value); return new JniValueMarshalerState (p!.PeerReference.NewLocalRef ()); } public override void DestroyGenericArgumentState (object? value, ref JniValueMarshalerState state, ParameterAttributes synchronize) { - var vm = state.Extra as JniValueMarshaler; + var vm = state.Extra as JniValueMarshaler; if (vm != null) { vm.DestroyArgumentState (value, ref state, synchronize); return; } - var r = state.ReferenceValue; + var r = state.ReferenceValue; JniObjectReference.Dispose (ref r); state = new JniValueMarshalerState (); } From 28837b7b12786ceffe0de48c96e4165aaa1e9a70 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 11 Sep 2025 13:40:11 -0700 Subject: [PATCH 04/10] Don't use targetType in ValueManager.CreatePeer --- .../JniRuntime.JniValueManager.cs | 32 +++++-------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs index 5ddd2a952..375436e77 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs @@ -259,18 +259,6 @@ protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (tr : null; } - [return: DynamicallyAccessedMembers (Constructors)] - static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) - { - if (type == typeof (object)) - return typeof (JavaObject); - if (type == typeof (IJavaPeerable)) - return typeof (JavaObject); - if (type == typeof (Exception)) - return typeof (JavaException); - return type; - } - public IJavaPeerable? GetPeer ( JniObjectReference reference, [DynamicallyAccessedMembers (Constructors)] @@ -297,7 +285,7 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) ref JniObjectReference reference, JniObjectReferenceOptions transfer, [DynamicallyAccessedMembers (Constructors)] - Type? targetType) + Type? targetType = null) { if (disposed) throw new ObjectDisposedException (GetType ().Name); @@ -307,7 +295,6 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) } targetType = targetType ?? typeof (JavaObject); - targetType = GetPeerType (targetType); if (!typeof (IJavaPeerable).IsAssignableFrom (targetType)) throw new ArgumentException ($"targetType `{targetType.AssemblyQualifiedName}` must implement IJavaPeerable!", nameof (targetType)); @@ -336,7 +323,7 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) JniObjectReference.Dispose (ref targetClass); - var peer = CreatePeerInstance (ref refClass, targetType, ref reference, transfer); + var peer = CreatePeerInstance (ref refClass, ref reference, transfer); if (peer == null) { throw new NotSupportedException (string.Format ("Could not find an appropriate constructable wrapper type for Java type '{0}', targetType='{1}'.", JniEnvironment.Types.GetJniTypeNameFromInstance (reference), targetType)); @@ -347,8 +334,6 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) IJavaPeerable? CreatePeerInstance ( ref JniObjectReference klass, - [DynamicallyAccessedMembers (Constructors)] - Type targetType, ref JniObjectReference reference, JniObjectReferenceOptions transfer) { @@ -359,7 +344,7 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) if (!JniTypeSignature.TryParse (jniTypeName, out sig)) return null; - Type? type = GetTypeAssignableTo (sig, targetType); + Type? type = GetBestTypeForSignature (sig); if (type != null) { var peer = TryCreatePeerInstance (ref reference, transfer, type); @@ -379,13 +364,14 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) } JniObjectReference.Dispose (ref klass, JniObjectReferenceOptions.CopyAndDispose); - return TryCreatePeerInstance (ref reference, transfer, targetType); + // Instance has no mapped type + return null; [UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "Types returned here should be preserved via other means.")] [return: DynamicallyAccessedMembers (Constructors)] - Type? GetTypeAssignableTo (JniTypeSignature sig, Type targetType) + Type? GetTypeAssignableTo (JniTypeSignature sig) { - string[] sdkAssemblyNames = ["Mono.Android", "Java.Base", "Java.Runtime", "Java.Interop"]; + string[] sdkAssemblyNames = ["Mono.Android", "Java.Base", "Java.Interop"]; // Find single best instance Type? best = null; foreach (Type type in Runtime.TypeManager.GetTypes (sig)) { @@ -427,9 +413,7 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) } } - if (best is not null && best.IsAssignableTo (targetType)) - return best; - return null; + return best; } } From d38c7bacf123e1b379f47ba3894d96e5701a5013 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 11 Sep 2025 13:44:39 -0700 Subject: [PATCH 05/10] Don't hard code VS path --- src/java-interop/java-interop.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java-interop/java-interop.targets b/src/java-interop/java-interop.targets index 9b31afb14..fad633f88 100644 --- a/src/java-interop/java-interop.targets +++ b/src/java-interop/java-interop.targets @@ -88,7 +88,7 @@ Condition=" $([MSBuild]::IsOSPlatform ('windows')) And '$(NativeToolchainSupported)' == 'True' " Inputs="$(MSBuildThisFileDirectory)coreclr.def" Outputs="$(MSBuildThisFileDirectory)coreclr.lib"> - + Date: Thu, 11 Sep 2025 14:06:37 -0700 Subject: [PATCH 06/10] Update method name --- src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs index 375436e77..242b50045 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs @@ -369,7 +369,7 @@ protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (tr [UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "Types returned here should be preserved via other means.")] [return: DynamicallyAccessedMembers (Constructors)] - Type? GetTypeAssignableTo (JniTypeSignature sig) + Type? GetBestTypeForSignature (JniTypeSignature sig) { string[] sdkAssemblyNames = ["Mono.Android", "Java.Base", "Java.Interop"]; // Find single best instance From 7247fc532cbc989f300fad3e3293d9015d75a730 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 11 Sep 2025 14:29:00 -0700 Subject: [PATCH 07/10] Remove default parameter --- src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs index 242b50045..f1f1aa29e 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs @@ -285,7 +285,7 @@ protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (tr ref JniObjectReference reference, JniObjectReferenceOptions transfer, [DynamicallyAccessedMembers (Constructors)] - Type? targetType = null) + Type? targetType) { if (disposed) throw new ObjectDisposedException (GetType ().Name); From 9966391273c112cc545d2507166e4c8492b854cd Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:29:34 -0700 Subject: [PATCH 08/10] If we can't find the right type, trust the caller's targetType --- .../Java.Interop/JniRuntime.JniValueManager.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs index f1f1aa29e..5b6ee4795 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs @@ -323,7 +323,7 @@ protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (tr JniObjectReference.Dispose (ref targetClass); - var peer = CreatePeerInstance (ref refClass, ref reference, transfer); + var peer = CreatePeerInstance (ref refClass, targetType, ref reference, transfer); if (peer == null) { throw new NotSupportedException (string.Format ("Could not find an appropriate constructable wrapper type for Java type '{0}', targetType='{1}'.", JniEnvironment.Types.GetJniTypeNameFromInstance (reference), targetType)); @@ -334,6 +334,8 @@ protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (tr IJavaPeerable? CreatePeerInstance ( ref JniObjectReference klass, + [DynamicallyAccessedMembers (Constructors)] + Type targetType, ref JniObjectReference reference, JniObjectReferenceOptions transfer) { @@ -364,8 +366,9 @@ protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (tr } JniObjectReference.Dispose (ref klass, JniObjectReferenceOptions.CopyAndDispose); - // Instance has no mapped type - return null; + // If we have nothing in the hierarchy, assume caller knows best and create targetType + return TryCreatePeerInstance (ref reference, transfer, targetType); + [UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "Types returned here should be preserved via other means.")] [return: DynamicallyAccessedMembers (Constructors)] From 29c28b18bedf9978c798c7e10ea9bf8b965c37d3 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 11 Sep 2025 16:10:18 -0700 Subject: [PATCH 09/10] Add back GetPeerType too --- .../Java.Interop/JniRuntime.JniValueManager.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs index 5b6ee4795..8f16ca6a8 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs @@ -259,6 +259,18 @@ protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (tr : null; } + [return: DynamicallyAccessedMembers (Constructors)] + static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) + { + if (type == typeof (object)) + return typeof (JavaObject); + if (type == typeof (IJavaPeerable)) + return typeof (JavaObject); + if (type == typeof (Exception)) + return typeof (JavaException); + return type; + } + public IJavaPeerable? GetPeer ( JniObjectReference reference, [DynamicallyAccessedMembers (Constructors)] @@ -295,6 +307,7 @@ protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (tr } targetType = targetType ?? typeof (JavaObject); + targetType = GetPeerType(targetType); if (!typeof (IJavaPeerable).IsAssignableFrom (targetType)) throw new ArgumentException ($"targetType `{targetType.AssemblyQualifiedName}` must implement IJavaPeerable!", nameof (targetType)); From 56937c6d9fd62742ca5d9adfec7fce00ece9f93c Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 11 Sep 2025 16:23:38 -0700 Subject: [PATCH 10/10] Make sure mapped type is assignable to target type for tests --- src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs index 8f16ca6a8..4102c498e 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs @@ -360,7 +360,7 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) return null; Type? type = GetBestTypeForSignature (sig); - if (type != null) { + if (type != null && type.IsAssignableTo(targetType)) { var peer = TryCreatePeerInstance (ref reference, transfer, type); if (peer != null) {