diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs index 70521a1473f..149d13cfa0b 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs @@ -27,7 +27,7 @@ namespace Microsoft.Android.Sdk.TrimmableTypeMap; /// // Creates the managed peer when Java calls into .NET /// public override IJavaPeerable CreateInstance(IntPtr handle, JniHandleOwnership ownership) /// => new Activity(handle, ownership); // leaf ctor -/// // or: (Activity)RuntimeHelpers.GetUninitializedObject(typeof(Activity)); +/// // or: (Activity)CreateActivatedPeer(typeof(Activity), handle); /// // obj.BaseCtor(handle, ownership); // inherited ctor /// // or: new IOnClickListenerInvoker(handle, ownership); // interface invoker /// // or: null; // no activation @@ -43,7 +43,7 @@ namespace Microsoft.Android.Sdk.TrimmableTypeMap; /// [UnmanagedCallersOnly] /// public static void nctor_0_uco(IntPtr jnienv, IntPtr self) /// => new Activity(self, JniHandleOwnership.DoNotTransfer); -/// // or: var obj = (Activity)RuntimeHelpers.GetUninitializedObject(typeof(Activity)); +/// // or: var obj = (Activity)CreateActivatedPeer(typeof(Activity), self); /// // obj.BaseCtor(self, JniHandleOwnership.DoNotTransfer); /// /// // Registers JNI native methods (ACWs only): @@ -68,6 +68,7 @@ sealed class TypeMapAssemblyEmitter AssemblyReferenceHandle _javaInteropRef; + TypeReferenceHandle _javaPeerProxyBaseRef; TypeReferenceHandle _javaPeerProxyRef; TypeReferenceHandle _iJavaPeerableRef; TypeReferenceHandle _jniHandleOwnershipRef; @@ -79,10 +80,9 @@ sealed class TypeMapAssemblyEmitter TypeReferenceHandle _runtimeTypeHandleRef; TypeReferenceHandle _jniTypeRef; TypeReferenceHandle _notSupportedExceptionRef; - TypeReferenceHandle _runtimeHelpersRef; MemberReferenceHandle _getTypeFromHandleRef; - MemberReferenceHandle _getUninitializedObjectRef; + MemberReferenceHandle _createActivatedPeerRef; MemberReferenceHandle _notSupportedExceptionCtorRef; MemberReferenceHandle _jniObjectReferenceCtorRef; MemberReferenceHandle _jniEnvDeleteRefRef; @@ -163,6 +163,8 @@ void EmitCore (TypeMapAssemblyData model) void EmitTypeReferences () { var metadata = _pe.Metadata; + _javaPeerProxyBaseRef = metadata.AddTypeReference (_pe.MonoAndroidRef, + metadata.GetOrAddString ("Java.Interop"), metadata.GetOrAddString ("JavaPeerProxy")); _javaPeerProxyRef = metadata.AddTypeReference (_pe.MonoAndroidRef, metadata.GetOrAddString ("Java.Interop"), metadata.GetOrAddString ("JavaPeerProxy`1")); _iJavaPeerableRef = metadata.AddTypeReference (_javaInteropRef, @@ -185,8 +187,6 @@ void EmitTypeReferences () metadata.GetOrAddString ("Java.Interop"), metadata.GetOrAddString ("JniType")); _notSupportedExceptionRef = metadata.AddTypeReference (_pe.SystemRuntimeRef, metadata.GetOrAddString ("System"), metadata.GetOrAddString ("NotSupportedException")); - _runtimeHelpersRef = metadata.AddTypeReference (_pe.SystemRuntimeRef, - metadata.GetOrAddString ("System.Runtime.CompilerServices"), metadata.GetOrAddString ("RuntimeHelpers")); _jniNativeMethodRef = metadata.AddTypeReference (_javaInteropRef, metadata.GetOrAddString ("Java.Interop"), metadata.GetOrAddString ("JniNativeMethod")); @@ -208,10 +208,13 @@ void EmitMemberReferences () rt => rt.Type ().Type (_systemTypeRef, false), p => p.AddParameter ().Type ().Type (_runtimeTypeHandleRef, true))); - _getUninitializedObjectRef = _pe.AddMemberRef (_runtimeHelpersRef, "GetUninitializedObject", - sig => sig.MethodSignature ().Parameters (1, - rt => rt.Type ().Object (), - p => p.AddParameter ().Type ().Type (_systemTypeRef, false))); + _createActivatedPeerRef = _pe.AddMemberRef (_javaPeerProxyBaseRef, "CreateActivatedPeer", + sig => sig.MethodSignature ().Parameters (2, + rt => rt.Type ().Type (_iJavaPeerableRef, false), + p => { + p.AddParameter ().Type ().Type (_systemTypeRef, false); + p.AddParameter ().Type ().IntPtr (); + })); _notSupportedExceptionCtorRef = _pe.AddMemberRef (_notSupportedExceptionRef, ".ctor", sig => sig.MethodSignature (isInstanceMethod: true).Parameters (1, @@ -493,7 +496,8 @@ void EmitCreateInstanceInheritedCtor (EntityHandle targetTypeRef, ActivationCtor encoder.OpCode (ILOpCode.Ldtoken); encoder.Token (targetTypeRef); encoder.Call (_getTypeFromHandleRef); - encoder.Call (_getUninitializedObjectRef); + encoder.OpCode (ILOpCode.Ldarg_1); + encoder.Call (_createActivatedPeerRef); encoder.OpCode (ILOpCode.Castclass); encoder.Token (targetTypeRef); @@ -543,7 +547,7 @@ void EmitCreateInstanceViaJavaInteropNewobj (EntityHandle typeRef) /// /// Emits CreateInstance for JavaInterop-style activation (inherited ctor): - /// var obj = (TargetType)RuntimeHelpers.GetUninitializedObject(typeof(TargetType)); + /// var obj = (TargetType)CreateActivatedPeer(typeof(TargetType), handle); /// var jniRef = new JniObjectReference(handle); /// obj.BaseCtor(ref jniRef, JniObjectReferenceOptions.Copy); /// JNIEnv.DeleteRef(handle, ownership); @@ -555,11 +559,12 @@ void EmitCreateInstanceInheritedJavaInteropCtor (EntityHandle targetTypeRef, Act EmitCreateInstanceBodyWithLocals ( EncodeJniObjectReferenceLocal, encoder => { - // var obj = (TargetType)RuntimeHelpers.GetUninitializedObject(typeof(TargetType)); + // var obj = (TargetType)CreateActivatedPeer(typeof(TargetType), handle); encoder.OpCode (ILOpCode.Ldtoken); encoder.Token (targetTypeRef); encoder.Call (_getTypeFromHandleRef); - encoder.Call (_getUninitializedObjectRef); + encoder.OpCode (ILOpCode.Ldarg_1); // handle + encoder.Call (_createActivatedPeerRef); encoder.OpCode (ILOpCode.Castclass); encoder.Token (targetTypeRef); @@ -743,7 +748,8 @@ MethodDefinitionHandle EmitUcoConstructor (UcoConstructorData uco, JavaPeerProxy encoder.OpCode (ILOpCode.Ldtoken); encoder.Token (targetTypeRef); encoder.Call (_getTypeFromHandleRef); - encoder.Call (_getUninitializedObjectRef); + encoder.LoadArgument (1); // self + encoder.Call (_createActivatedPeerRef); encoder.OpCode (ILOpCode.Castclass); encoder.Token (targetTypeRef); } @@ -792,7 +798,8 @@ MethodDefinitionHandle EmitUcoConstructor (UcoConstructorData uco, JavaPeerProxy encoder.OpCode (ILOpCode.Ldtoken); encoder.Token (targetTypeRef); encoder.Call (_getTypeFromHandleRef); - encoder.Call (_getUninitializedObjectRef); + encoder.LoadArgument (1); // self + encoder.Call (_createActivatedPeerRef); encoder.OpCode (ILOpCode.Castclass); encoder.Token (targetTypeRef); diff --git a/src/Mono.Android/Java.Interop/JavaPeerProxy.cs b/src/Mono.Android/Java.Interop/JavaPeerProxy.cs index c4309d78f85..a2dc89ec4ef 100644 --- a/src/Mono.Android/Java.Interop/JavaPeerProxy.cs +++ b/src/Mono.Android/Java.Interop/JavaPeerProxy.cs @@ -18,10 +18,12 @@ namespace Java.Interop [AttributeUsage (AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] public abstract class JavaPeerProxy : Attribute { + const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; + protected JavaPeerProxy ( string jniName, Type targetType, - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + [DynamicallyAccessedMembers (Constructors)] Type? invokerType) { JniName = jniName ?? throw new ArgumentNullException (nameof (jniName)); @@ -52,7 +54,7 @@ protected JavaPeerProxy ( /// Gets the invoker type for interfaces and abstract classes. /// Returns null for concrete types that can be directly instantiated. /// - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + [DynamicallyAccessedMembers (Constructors)] public Type? InvokerType { get; } /// @@ -61,6 +63,22 @@ protected JavaPeerProxy ( /// /// A factory for creating containers of the target type, or null if not supported. public virtual JavaPeerContainerFactory? GetContainerFactory () => null; + /// + /// Creates the managed peer for inherited Java activation paths and binds it + /// to the provided JNI handle before the activation constructor runs. + /// + protected static IJavaPeerable CreateActivatedPeer ( + [DynamicallyAccessedMembers (Constructors)] Type type, + IntPtr handle) + { + var peer = (IJavaPeerable) System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject (type); + + peer.SetJniManagedPeerState (peer.JniManagedPeerState | JniManagedPeerStates.Replaceable | JniManagedPeerStates.Activatable); + + var reference = new JniObjectReference (handle); + JniEnvironment.Runtime.ValueManager.ConstructPeer (peer, ref reference, JniObjectReferenceOptions.Copy); + return peer; + } } /// diff --git a/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs b/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs index a99d95bfca0..0d59f3c642c 100644 --- a/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs +++ b/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs @@ -176,7 +176,7 @@ public void Generate_EmptyPeerList_ProducesValidAssembly () } [Fact] - public void Generate_SimpleActivity_UsesGetUninitializedObject () + public void Generate_SimpleActivity_UsesSharedActivationHelper () { var peers = ScanFixtures (); var simpleActivity = peers.First (p => p.JavaName == "my/app/SimpleActivity"); @@ -186,12 +186,11 @@ public void Generate_SimpleActivity_UsesGetUninitializedObject () using var stream = GenerateAssembly (new [] { simpleActivity }, "InheritedCtorTest"); using var pe = new PEReader (stream); var reader = pe.GetMetadataReader (); - var typeNames = GetTypeRefNames (reader); - Assert.Contains ("RuntimeHelpers", typeNames); var memberNames = GetMemberRefNames (reader); Assert.DoesNotContain ("CreateManagedPeer", memberNames); - Assert.Contains ("GetUninitializedObject", memberNames); + Assert.Contains ("CreateActivatedPeer", memberNames); + Assert.DoesNotContain ("GetUninitializedObject", memberNames); } [Fact] @@ -230,7 +229,8 @@ public void Generate_InheritedCtor_UcoUsesGuardAndInlinedActivation () var memberNames = GetMemberRefNames (reader); Assert.Contains ("get_WithinNewObjectScope", memberNames); - Assert.Contains ("GetUninitializedObject", memberNames); + Assert.Contains ("CreateActivatedPeer", memberNames); + Assert.DoesNotContain ("GetUninitializedObject", memberNames); Assert.DoesNotContain ("ActivateInstance", memberNames); Assert.DoesNotContain ("ActivatePeerFromJavaConstructor", memberNames); }