From e70f93a6787f9b9a1bbf76fe4ca341981c652daf Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 9 Apr 2026 11:48:32 +0200 Subject: [PATCH] [TrimmableTypeMap] Pass TargetType and InvokerType through JavaPeerProxy constructor Replace virtual property overrides for both TargetType and InvokerType with constructor parameters. The generated proxy ctor now calls base(typeof(Target), typeof(Invoker)) or base(typeof(Target), null), storing both values in readonly fields. No virtual dispatch needed. Before (two virtual overrides per proxy): public override Type TargetType => typeof(Activity); public override Type? InvokerType => typeof(IActivityInvoker); After (ctor params, field reads): protected JavaPeerProxy(Type targetType, Type? invokerType) // Generated: base(typeof(Activity), typeof(IActivityInvoker)) This also fixes the original bug where get_InvokerType was emitted without MethodAttributes.Virtual, causing the base class to always return null. With the constructor approach, virtual dispatch is eliminated entirely for both properties. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Generator/TypeMapAssemblyEmitter.cs | 47 ++++++++----------- .../Java.Interop/JavaPeerProxy.cs | 26 +++++++--- 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs index 34bfd15a9b6..101eec9618c 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs @@ -207,7 +207,12 @@ void EmitTypeReferences () void EmitMemberReferences () { _baseCtorRef = _pe.AddMemberRef (_javaPeerProxyRef, ".ctor", - sig => sig.MethodSignature (isInstanceMethod: true).Parameters (0, rt => rt.Void (), p => { })); + sig => sig.MethodSignature (isInstanceMethod: true).Parameters (2, + rt => rt.Void (), + p => { + p.AddParameter ().Type ().Type (_systemTypeRef, false); + p.AddParameter ().Type ().Type (_systemTypeRef, false); + })); _getTypeFromHandleRef = _pe.AddMemberRef (_systemTypeRef, "GetTypeFromHandle", sig => sig.MethodSignature ().Parameters (1, @@ -360,12 +365,24 @@ void EmitProxyType (JavaPeerProxyData proxy, Dictionary sig.MethodSignature (isInstanceMethod: true).Parameters (0, rt => rt.Void (), p => { }), encoder => { encoder.OpCode (ILOpCode.Ldarg_0); + // arg 1: typeof(TargetType) + encoder.OpCode (ILOpCode.Ldtoken); + encoder.Token (_pe.ResolveTypeRef (proxy.TargetType)); + encoder.Call (_getTypeFromHandleRef); + // arg 2: typeof(InvokerType) or null + if (proxy.InvokerType != null) { + encoder.OpCode (ILOpCode.Ldtoken); + encoder.Token (_pe.ResolveTypeRef (proxy.InvokerType)); + encoder.Call (_getTypeFromHandleRef); + } else { + encoder.OpCode (ILOpCode.Ldnull); + } encoder.Call (_baseCtorRef); encoder.OpCode (ILOpCode.Ret); }); @@ -373,16 +390,6 @@ void EmitProxyType (JavaPeerProxyData proxy, Dictionary sig.MethodSignature (isInstanceMethod: true).Parameters (0, - rt => rt.Type ().Type (_systemTypeRef, false), - p => { }), - encoder => { - encoder.OpCode (ILOpCode.Ldtoken); - encoder.Token (handle); - encoder.Call (_getTypeFromHandleRef); - encoder.OpCode (ILOpCode.Ret); - }); - } - MethodDefinitionHandle EmitUcoMethod (UcoMethodData uco) { var jniParams = JniSignatureHelper.ParseParameterTypes (uco.JniSignature); diff --git a/src/Mono.Android/Java.Interop/JavaPeerProxy.cs b/src/Mono.Android/Java.Interop/JavaPeerProxy.cs index 0fe4b72cc48..08bc2c538f1 100644 --- a/src/Mono.Android/Java.Interop/JavaPeerProxy.cs +++ b/src/Mono.Android/Java.Interop/JavaPeerProxy.cs @@ -18,32 +18,42 @@ namespace Java.Interop [AttributeUsage (AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] public abstract class JavaPeerProxy : Attribute { + /// + /// Initializes a new proxy with the specified target and invoker types. + /// + /// The managed peer type this proxy represents. + /// The invoker type for interfaces/abstract classes, or null for concrete types. + protected JavaPeerProxy ( + Type targetType, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type? invokerType) + { + TargetType = targetType; + InvokerType = invokerType; + } + /// /// Creates an instance of the target type using the JNI handle and ownership semantics. /// This replaces the reflection-based constructor invocation used in the legacy path. /// - /// The JNI object reference handle. - /// How to handle JNI reference ownership. - /// A new instance of the target type wrapping the JNI handle, or null if activation is not supported. public abstract IJavaPeerable? CreateInstance (IntPtr handle, JniHandleOwnership transfer); /// /// Gets the target .NET type that this proxy represents. /// - public abstract Type TargetType { get; } + public Type TargetType { get; } /// /// Gets the invoker type for interfaces and abstract classes. /// Returns null for concrete types that can be directly instantiated. /// [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] - public virtual Type? InvokerType => null; + public Type? InvokerType { get; } /// /// Gets a factory for creating containers (arrays, collections) of the target type. /// Enables AOT-safe creation of generic collections without MakeGenericType(). /// - /// A factory for creating containers of the target type, or null if not supported. public virtual JavaPeerContainerFactory? GetContainerFactory () => null; } @@ -59,7 +69,9 @@ public abstract class JavaPeerProxy< T > : JavaPeerProxy where T : class, IJavaPeerable { - public override Type TargetType => typeof (T); + protected JavaPeerProxy ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type? invokerType) : base (typeof (T), invokerType) { } public override JavaPeerContainerFactory GetContainerFactory () => JavaPeerContainerFactory.Instance;