Skip to content

Conversation

@jtschuster
Copy link
Member

No description provided.

@jtschuster jtschuster marked this pull request as ready for review October 9, 2025 22:26
@jtschuster
Copy link
Member Author

/azp run

@azure-pipelines
Copy link

Commenter does not have sufficient privileges for PR 10533 in repo dotnet/android

@grendello
Copy link
Contributor

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jtschuster
Copy link
Member Author

AndroidTypeManager -> TypeMapTypeManager diff
diff --git a/AndroidTypeManager.cs "b/src\\Mono.Android\\Java.Interop\\TypeMapAttributeTypeManager.cs"
index 72b9e13fd..ee26be20a 100644
--- a/AndroidTypeManager.cs
+++ "b/src\\Mono.Android\\Java.Interop\\TypeMapAttributeTypeManager.cs"
@@ -10,78 +10,82 @@ using Java.Interop.Tools.TypeNameMappings;
 
 namespace Java.Interop
 {
-	class AndroidTypeManager : JniRuntime.JniTypeManager {
+	class TypeMapAttributeTypeManager : JniRuntime.JniTypeManager
+	{
 		struct JniRemappingReplacementMethod
 		{
 			public string target_type;
 			public string target_name;
-			public bool     is_static;
+			public bool is_static;
 		};
 
 		bool jniAddNativeMethodRegistrationAttributePresent;
+		static IReadOnlyDictionary<Type, Type> ProxyTypeMap { get; } = TypeMapping.GetOrCreateProxyTypeMapping<Java.Lang.Object> ();
+		static IReadOnlyDictionary<string, Type> ExternalTypeMap { get; } = TypeMapping.GetOrCreateExternalTypeMapping<Java.Lang.Object> ();
 
 		const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
 		const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods;
 		const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes;
 
-		public AndroidTypeManager (bool jniAddNativeMethodRegistrationAttributePresent)
+		public TypeMapAttributeTypeManager (bool jniAddNativeMethodRegistrationAttributePresent)
 		{
 			this.jniAddNativeMethodRegistrationAttributePresent = jniAddNativeMethodRegistrationAttributePresent;
 		}
 
 		protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpleReference)
 		{
+			if (!ExternalTypeMap.TryGetValue (jniSimpleReference, out Type? value)) {
+				if (Logger.LogAssembly) {
+					// Miss message is logged in the native runtime
+					JNIEnv.LogTypemapTrace (new System.Diagnostics.StackTrace (true));
+				}
+			} else {
+				yield return value;
+			}
+
 			foreach (var ti in base.GetTypesForSimpleReference (jniSimpleReference))
 				yield return ti;
-
-			var t = Java.Interop.TypeManager.GetJavaToManagedType (jniSimpleReference);
-			if (t != null)
-				yield return t;
 		}
 
 		protected override string? GetSimpleReference (Type type)
 		{
-			string? j = JNIEnv.TypemapManagedToJava (type);
-			if (j != null) {
-				return GetReplacementTypeCore (j) ?? j;
-			}
+			if (!ProxyTypeMap.TryGetValue (type, out Type? proxyType))
+				return null;
+			if (proxyType.GetCustomAttribute<TypeMapProxyAttribute> () is { } attr)
+				return GetReplacementTypeCore(attr.JniName) ?? attr.JniName;
 			return null;
 		}
 
 		protected override IEnumerable<string> GetSimpleReferences (Type type)
 		{
-			string? j = JNIEnv.TypemapManagedToJava (type);
-			j   = GetReplacementTypeCore (j) ?? j;
-
-			if (j != null) {
-				return new[]{j};
-			}
-			return Array.Empty<string> ();
+			if (GetSimpleReference (type) is { } str)
+				return [str];
+			return [];
 		}
 
 		protected override IReadOnlyList<string>? GetStaticMethodFallbackTypesCore (string jniSimpleReference)
 		{
-			ReadOnlySpan<char>  name    = jniSimpleReference;
-			int slash                   = name.LastIndexOf ('/');
-			var desugarType             = new StringBuilder (jniSimpleReference.Length + "Desugar".Length);
+			ReadOnlySpan<char> name = jniSimpleReference;
+			int slash = name.LastIndexOf ('/');
+			var desugarType = new StringBuilder (jniSimpleReference.Length + "Desugar".Length);
 			if (slash > 0) {
-				desugarType.Append (name.Slice (0, slash+1))
+				desugarType.Append (name.Slice (0, slash + 1))
 					.Append ("Desugar")
-					.Append (name.Slice (slash+1));
+					.Append (name.Slice (slash + 1));
 			} else {
 				desugarType.Append ("Desugar").Append (name);
 			}
 
-			var typeWithPrefix  = desugarType.ToString ();
-			var typeWithSuffix  = $"{jniSimpleReference}$-CC";
+			var typeWithPrefix = desugarType.ToString ();
+			var typeWithSuffix = $"{jniSimpleReference}$-CC";
 
-			var replacements    = new[]{
+			var replacements = new []{
 				GetReplacementTypeCore (typeWithPrefix) ?? typeWithPrefix,
 				GetReplacementTypeCore (typeWithSuffix) ?? typeWithSuffix,
 			};
 
 			if (Logger.LogAssembly) {
-				var message = $"Remapping type `{jniSimpleReference}` to one one of {{ `{replacements[0]}`, `{replacements[1]}` }}";
+				var message = $"Remapping type `{jniSimpleReference}` to one one of {{ `{replacements [0]}`, `{replacements [1]}` }}";
 				Logger.Log (LogLevel.Debug, "monodroid-assembly", message);
 			}
 			return replacements;
@@ -113,7 +117,7 @@ namespace Java.Interop
 			}
 
 			var method = new JniRemappingReplacementMethod ();
-			method = Marshal.PtrToStructure<JniRemappingReplacementMethod>(retInfo);
+			method = Marshal.PtrToStructure<JniRemappingReplacementMethod> (retInfo);
 			var newSignature = jniMethodSignature;
 
 			int? paramCount = null;
@@ -130,14 +134,14 @@ namespace Java.Interop
 			}
 
 			return new JniRuntime.ReplacementMethodInfo {
-					SourceJniType                   = jniSourceType,
-					SourceJniMethodName             = jniMethodName,
-					SourceJniMethodSignature        = jniMethodSignature,
-					TargetJniType                   = method.target_type,
-					TargetJniMethodName             = method.target_name,
-					TargetJniMethodSignature        = newSignature,
-					TargetJniMethodParameterCount   = paramCount,
-					TargetJniMethodInstanceToStatic = method.is_static,
+				SourceJniType = jniSourceType,
+				SourceJniMethodName = jniMethodName,
+				SourceJniMethodSignature = jniMethodSignature,
+				TargetJniType = method.target_type,
+				TargetJniMethodName = method.target_name,
+				TargetJniMethodSignature = newSignature,
+				TargetJniMethodParameterCount = paramCount,
+				TargetJniMethodInstanceToStatic = method.is_static,
 			};
 		}
 
@@ -174,7 +178,7 @@ namespace Java.Interop
 				if (dynamic_callback_gen == null)
 					throw new InvalidOperationException ("The referenced Mono.Android.Export.dll does not match the expected version. The required method was not found.");
 			}
-			return (Delegate)dynamic_callback_gen.Invoke (null, new object [] { method })!;
+			return (Delegate) dynamic_callback_gen.Invoke (null, new object [] { method })!;
 		}
 
 		static List<JniNativeMethodRegistration> sharedRegistrations = new List<JniNativeMethodRegistration> ();
@@ -210,7 +214,8 @@ namespace Java.Interop
 			return rv;
 		}
 
-		class MagicRegistrationMap {
+		class MagicRegistrationMap
+		{
 #pragma warning disable CS0649 // Field is never assigned to;
 			// assigned to in generated IL: https://github.com/xamarin/xamarin-android/blob/cbfa7e20acebd37b52ec4de9d5c1a4a66ddda799/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/MonoDroidMarkStep.cs#L204
 			static Dictionary<string, int>? typesMap;
@@ -268,7 +273,7 @@ namespace Java.Interop
 			try {
 				if (methods.IsEmpty) {
 					if (jniAddNativeMethodRegistrationAttributePresent)
-						base.RegisterNativeMembers (nativeClass, type, methods.ToString ());
+						base.RegisterNativeMembers (nativeClass, type, methods);
 					return;
 				} else if (FastRegisterNativeMembers (nativeClass, type, methods)) {
 					return;
@@ -277,7 +282,7 @@ namespace Java.Interop
 				int methodCount = CountMethods (methods);
 				if (methodCount < 1) {
 					if (jniAddNativeMethodRegistrationAttributePresent) {
-						base.RegisterNativeMembers (nativeClass, type, methods.ToString ());
+						base.RegisterNativeMembers (nativeClass, type, methods);
 					}
 					return;
 				}
@@ -325,7 +330,7 @@ namespace Java.Interop
 							}
 
 							GetCallbackHandler connector = (GetCallbackHandler) Delegate.CreateDelegate (typeof (GetCallbackHandler),
-							                                                                             callbackDeclaringType, callbackString.ToString ());
+																										 callbackDeclaringType, callbackString.ToString ());
 							callback = connector ();
 						}
 
@@ -344,15 +349,6 @@ namespace Java.Interop
 			} catch (Exception e) {
 				JniEnvironment.Runtime.RaisePendingException (e);
 			}
-
-			bool ShouldRegisterDynamically (string callbackTypeName, string callbackString, string typeName, string callbackName)
-			{
-				if (String.Compare (typeName, callbackTypeName, StringComparison.Ordinal) != 0) {
-					return false;
-				}
-
-				return String.Compare (callbackName, callbackString, StringComparison.Ordinal) == 0;
-			}
 		}
 
 		static int CountMethods (ReadOnlySpan<char> methodsSpan)
AndroidValueManager => TypeMapTypeManager diff
diff --git a/AndroidValueManager.cs "b/src\\Mono.Android\\Java.Interop\\TypeMapAttributeValueManager.cs"
index 022ba91f8..d6a0c56a2 100644
--- a/AndroidValueManager.cs
+++ "b/src\\Mono.Android\\Java.Interop\\TypeMapAttributeValueManager.cs"
@@ -8,9 +8,9 @@ using Android.Runtime;
 
 namespace Java.Interop
 {
-	class AndroidValueManager : JniRuntime.JniValueManager {
-
-		Dictionary<IntPtr, IdentityHashTargets>         instances       = new Dictionary<IntPtr, IdentityHashTargets> ();
+	class TypeMapAttributeValueManager : JniRuntime.JniValueManager
+	{
+		Dictionary<IntPtr, IdentityHashTargets> instances = new Dictionary<IntPtr, IdentityHashTargets> ();
 
 		public override void WaitForGCBridgeProcessing ()
 		{
@@ -28,11 +28,105 @@ namespace Java.Interop
 			if (!reference.IsValid)
 				return null;
 
-			var peer        = Java.Interop.TypeManager.CreateInstance (reference.Handle, JniHandleOwnership.DoNotTransfer, targetType) as IJavaPeerable;
+			var peer = CreateInstance (reference.Handle, JniHandleOwnership.DoNotTransfer, targetType) as IJavaPeerable;
 			JniObjectReference.Dispose (ref reference, options);
 			return peer;
 		}
 
+		internal static IJavaPeerable? CreateInstance (IntPtr handle, JniHandleOwnership transfer, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]Type? targetType)
+		{
+			Type? type = null;
+			IntPtr class_ptr = JNIEnv.GetObjectClass (handle);
+			string? class_name = TypeManager.GetClassName (class_ptr);
+			System.Diagnostics.Debug.Assert (class_name == JNIEnv.GetClassNameFromInstance (handle));
+			lock (TypeManagerMapDictionaries.AccessLock) {
+				while (class_ptr != IntPtr.Zero) {
+					if (class_name != null) {
+						var class_signature = JniTypeSignature.Parse (class_name);
+						type = JNIEnvInit.androidRuntime!.TypeManager.GetType (class_signature);
+						if (type != null) {
+							break;
+						}
+					}
+
+					IntPtr super_class_ptr = JNIEnv.GetSuperclass (class_ptr);
+					JNIEnv.DeleteLocalRef (class_ptr);
+					class_name = null;
+					class_ptr = super_class_ptr;
+					if (class_ptr != IntPtr.Zero) {
+						class_name = TypeManager.GetClassName (class_ptr);
+					}
+				}
+			}
+
+			if (class_ptr != IntPtr.Zero) {
+				JNIEnv.DeleteLocalRef (class_ptr);
+				class_ptr = IntPtr.Zero;
+			}
+
+			if (targetType != null &&
+					(type == null ||
+					 !targetType.IsAssignableFrom (type))) {
+				type = targetType;
+			}
+
+			if (type == null) {
+				class_name = JNIEnv.GetClassNameFromInstance (handle);
+				JNIEnv.DeleteRef (handle, transfer);
+				throw new NotSupportedException (
+						FormattableString.Invariant ($"Internal error finding wrapper class for '{class_name}'. (Where is the Java.Lang.Object wrapper?!)"),
+						TypeManager.CreateJavaLocationException ());
+			}
+
+			if (type.IsInterface || type.IsAbstract) {
+				var invokerType = JavaObjectExtensions.GetInvokerType (type);
+				if (invokerType == null)
+					throw new NotSupportedException ("Unable to find Invoker for type '" + type.FullName + "'. Was it linked away?",
+							TypeManager.CreateJavaLocationException ());
+				type = invokerType;
+			}
+
+			var typeSig  = JNIEnvInit.androidRuntime?.TypeManager.GetTypeSignature (type) ?? default;
+			if (!typeSig.IsValid || typeSig.SimpleReference == null) {
+				throw new ArgumentException ($"Could not determine Java type corresponding to `{type.AssemblyQualifiedName}`.", nameof (targetType));
+			}
+
+			JniObjectReference typeClass = default;
+			JniObjectReference handleClass = default;
+			try {
+				try {
+					typeClass = JniEnvironment.Types.FindClass (typeSig.SimpleReference);
+				} catch (Exception e) {
+					throw new ArgumentException ($"Could not find Java class `{typeSig.SimpleReference}`.",
+							nameof (targetType),
+							e);
+				}
+
+				handleClass = JniEnvironment.Types.GetObjectClass (new JniObjectReference (handle));
+				if (!JniEnvironment.Types.IsAssignableFrom (handleClass, typeClass)) {
+					return null;
+				}
+			} finally {
+				JniObjectReference.Dispose (ref handleClass);
+				JniObjectReference.Dispose (ref typeClass);
+			}
+
+			IJavaPeerable? result = null;
+
+			try {
+				result = (IJavaPeerable) TypeManager.CreateProxy (type, handle, transfer);
+				if (Java.Interop.Runtime.IsGCUserPeer (result.PeerReference.Handle)) {
+					result.SetJniManagedPeerState (JniManagedPeerStates.Replaceable | JniManagedPeerStates.Activatable);
+				}
+			} catch (MissingMethodException e) {
+				var key_handle  = JNIEnv.IdentityHash (handle);
+				JNIEnv.DeleteRef (handle, transfer);
+				throw new NotSupportedException (FormattableString.Invariant (
+					$"Unable to activate instance of type {type} from native handle 0x{handle:x} (key_handle 0x{key_handle:x})."), e);
+			}
+			return result;
+ 		}
+
 		public override void AddPeer (IJavaPeerable value)
 		{
 			if (value == null)
@@ -40,8 +134,8 @@ namespace Java.Interop
 			if (!value.PeerReference.IsValid)
 				throw new ArgumentException ("Must have a valid JNI object reference!", nameof (value));
 
-			var reference       = value.PeerReference;
-			var hash            = JNIEnv.IdentityHash (reference.Handle);
+			var reference = value.PeerReference;
+			var hash = JNIEnv.IdentityHash (reference.Handle);
 
 			AddPeer (value, reference, hash);
 		}
@@ -118,7 +212,7 @@ namespace Java.Interop
 
 		bool ShouldReplaceMapping (WeakReference<IJavaPeerable> current, JniObjectReference reference, IJavaPeerable value, out IJavaPeerable? target)
 		{
-			target      = null;
+			target = null;
 
 			if (current == null)
 				return true;
@@ -160,12 +254,12 @@ namespace Java.Interop
 			if (value == null)
 				throw new ArgumentNullException (nameof (value));
 
-			var reference       = value.PeerReference;
+			var reference = value.PeerReference;
 			if (!reference.IsValid) {
 				// Likely an idempotent DIspose(); ignore.
 				return;
 			}
-			var hash            = JNIEnv.IdentityHash (reference.Handle);
+			var hash = JNIEnv.IdentityHash (reference.Handle);
 
 			RemovePeer (value, hash);
 		}
@@ -199,11 +293,11 @@ namespace Java.Interop
 			if (!reference.IsValid)
 				return null;
 
-			var hash    = JNIEnv.IdentityHash (reference.Handle);
+			var hash = JNIEnv.IdentityHash (reference.Handle);
 			lock (instances) {
 				if (instances.TryGetValue (hash, out var targets)) {
 					for (int i = targets.Count - 1; i >= 0; i--) {
-						var wref    = targets [i];
+						var wref = targets [i];
 						if (!wref!.TryGetTarget (out var result) || !result.PeerReference.IsValid) {
 							targets.RemoveAt (i);
 							continue;
@@ -219,14 +313,14 @@ namespace Java.Interop
 
 		public override void ActivatePeer (IJavaPeerable? self, JniObjectReference reference, ConstructorInfo cinfo, object? []? argumentValues)
 		{
-			Java.Interop.TypeManager.Activate (reference.Handle, cinfo, argumentValues);
+			TypeManager.Activate (reference.Handle, cinfo, argumentValues);
 		}
 
-		protected override bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (true)]out object? result)
+		protected override bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (true)] out object? result)
 		{
 			var proxy = value as JavaProxyThrowable;
 			if (proxy != null) {
-				result  = proxy.InnerException;
+				result = proxy.InnerException;
 				return true;
 			}
 			return base.TryUnboxPeerObject (value, out result);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants