Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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):
Expand All @@ -68,6 +68,7 @@ sealed class TypeMapAssemblyEmitter

AssemblyReferenceHandle _javaInteropRef;

TypeReferenceHandle _javaPeerProxyBaseRef;
TypeReferenceHandle _javaPeerProxyRef;
TypeReferenceHandle _iJavaPeerableRef;
TypeReferenceHandle _jniHandleOwnershipRef;
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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"));
Expand All @@ -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,
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -543,7 +547,7 @@ void EmitCreateInstanceViaJavaInteropNewobj (EntityHandle typeRef)

/// <summary>
/// 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);
Expand All @@ -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);

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);

Expand Down
22 changes: 20 additions & 2 deletions src/Mono.Android/Java.Interop/JavaPeerProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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.
/// </summary>
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
[DynamicallyAccessedMembers (Constructors)]
public Type? InvokerType { get; }

/// <summary>
Expand All @@ -61,6 +63,22 @@ protected JavaPeerProxy (
/// </summary>
/// <returns>A factory for creating containers of the target type, or null if not supported.</returns>
public virtual JavaPeerContainerFactory? GetContainerFactory () => null;
/// <summary>
/// Creates the managed peer for inherited Java activation paths and binds it
/// to the provided JNI handle before the activation constructor runs.
/// </summary>
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;
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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]
Expand Down Expand Up @@ -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);
}
Expand Down