diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/Model/TypeMapAssemblyData.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/Model/TypeMapAssemblyData.cs
index 56484f7a99a..e2868a3b2c5 100644
--- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/Model/TypeMapAssemblyData.cs
+++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/Model/TypeMapAssemblyData.cs
@@ -182,7 +182,7 @@ sealed record UcoMethodData
/// An [UnmanagedCallersOnly] static wrapper for a constructor callback.
/// Signature must match the full JNI native method signature (jnienv + self + ctor params)
/// so the ABI is correct when JNI dispatches the call.
-/// Body: TrimmableNativeRegistration.ActivateInstance(self, typeof(TargetType)).
+/// Body: directly activates the target type using its generated activation ctor.
///
sealed record UcoConstructorData
{
@@ -192,7 +192,7 @@ sealed record UcoConstructorData
public required string WrapperName { get; init; }
///
- /// Target type to pass to ActivateInstance.
+ /// Target type to activate in the generated wrapper.
///
public required TypeRefData TargetType { get; init; }
diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/PEAssemblyBuilder.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/PEAssemblyBuilder.cs
index e08caa319a1..ddec657a085 100644
--- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/PEAssemblyBuilder.cs
+++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/PEAssemblyBuilder.cs
@@ -239,7 +239,7 @@ TypeDefinitionHandle GetOrCreateSizedType (int size)
int typeMethodStart = Metadata.GetRowCount (TableIndex.MethodDef) + 1;
var handle = Metadata.AddTypeDefinition (
- TypeAttributes.NestedPrivate | TypeAttributes.ExplicitLayout | TypeAttributes.Sealed | TypeAttributes.AnsiClass,
+ TypeAttributes.NestedAssembly | TypeAttributes.ExplicitLayout | TypeAttributes.Sealed | TypeAttributes.AnsiClass,
default,
Metadata.GetOrAddString ($"__utf8_{size}"),
Metadata.AddTypeReference (SystemRuntimeRef,
@@ -259,7 +259,7 @@ TypeDefinitionHandle GetOrCreateSizedType (int size)
///
public MethodDefinitionHandle EmitBody (string name, MethodAttributes attrs,
Action encodeSig, Action emitIL)
- => EmitBody (name, attrs, encodeSig, emitIL, encodeLocals: null);
+ => EmitBody (name, attrs, encodeSig, emitIL, encodeLocals: null, useBranches: false);
///
/// Emits a method body and definition with optional local variable declarations.
@@ -269,6 +269,11 @@ public MethodDefinitionHandle EmitBody (string name, MethodAttributes attrs,
/// and must write the full LOCAL_SIG blob (header 0x07,
/// compressed count, then each variable type).
///
+ ///
+ /// If true, creates a so the emitted IL can use
+ /// , ,
+ /// and .
+ ///
public MethodDefinitionHandle EmitBody (string name, MethodAttributes attrs,
Action encodeSig, Action emitIL,
Action? encodeLocals)
diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs
index 19c32e80522..63ccba17203 100644
--- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs
+++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs
@@ -43,7 +43,9 @@ namespace Microsoft.Android.Sdk.TrimmableTypeMap;
///
/// [UnmanagedCallersOnly]
/// public static void nctor_0_uco(IntPtr jnienv, IntPtr self)
-/// => TrimmableNativeRegistration.ActivateInstance(self, typeof(Activity));
+/// => new Activity(self, JniHandleOwnership.DoNotTransfer);
+/// // or: var obj = (Activity)RuntimeHelpers.GetUninitializedObject(typeof(Activity));
+/// // obj.BaseCtor(self, JniHandleOwnership.DoNotTransfer);
///
/// // Registers JNI native methods (ACWs only):
/// public void RegisterNatives(JniType jniType)
@@ -87,7 +89,6 @@ sealed class TypeMapAssemblyEmitter
MemberReferenceHandle _jniObjectReferenceCtorRef;
MemberReferenceHandle _jniEnvDeleteRefRef;
MemberReferenceHandle _withinNewObjectScopeRef;
- MemberReferenceHandle _activateInstanceRef;
MemberReferenceHandle _ucoAttrCtorRef;
BlobHandle _ucoAttrBlobHandle;
MemberReferenceHandle _typeMapAttrCtorRef2Arg;
@@ -244,17 +245,6 @@ void EmitMemberReferences ()
rt => rt.Type ().Boolean (),
p => { }));
- // TrimmableTypeMap.ActivateInstance(IntPtr, Type)
- var trimmableTypeMapRef = _pe.Metadata.AddTypeReference (_pe.MonoAndroidRef,
- _pe.Metadata.GetOrAddString ("Microsoft.Android.Runtime"), _pe.Metadata.GetOrAddString ("TrimmableTypeMap"));
- _activateInstanceRef = _pe.AddMemberRef (trimmableTypeMapRef, "ActivateInstance",
- sig => sig.MethodSignature ().Parameters (2,
- rt => rt.Void (),
- p => {
- p.AddParameter ().Type ().IntPtr ();
- p.AddParameter ().Type ().Type (_systemTypeRef, false);
- }));
-
// JniNativeMethod..ctor(byte*, byte*, IntPtr)
_jniNativeMethodCtorRef = _pe.AddMemberRef (_jniNativeMethodRef, ".ctor",
sig => sig.MethodSignature (isInstanceMethod: true).Parameters (3,
@@ -354,6 +344,16 @@ void EmitTypeMapAssociationAttributeCtorRef ()
void EmitProxyType (JavaPeerProxyData proxy, Dictionary wrapperHandles)
{
+ if (proxy.IsAcw) {
+ // RegisterNatives uses RVA-backed UTF-8 fields under .
+ // Materialize those helper types before adding the proxy TypeDef, otherwise the
+ // later RegisterNatives method can be attached to the helper type instead.
+ foreach (var reg in proxy.NativeRegistrations) {
+ _pe.GetOrAddUtf8Field (reg.JniMethodName);
+ _pe.GetOrAddUtf8Field (reg.JniSignature);
+ }
+ }
+
var metadata = _pe.Metadata;
var typeDefHandle = metadata.AddTypeDefinition (
TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class,
@@ -368,7 +368,7 @@ void EmitProxyType (JavaPeerProxyData proxy, Dictionary sig.MethodSignature (isInstanceMethod: true).Parameters (0, rt => rt.Void (), p => { }),
encoder => {
@@ -376,6 +376,7 @@ void EmitProxyType (JavaPeerProxyData proxy, Dictionary {
+ // Skip activation if the object is being created from managed code
+ // (e.g., JNIEnv.StartCreateInstance / JNIEnv.NewObject).
var skipLabel = encoder.DefineLabel ();
encoder.Call (_withinNewObjectScopeRef);
encoder.Branch (ILOpCode.Brtrue, skipLabel);
- encoder.LoadArgument (1); // jniSelf
- encoder.OpCode (ILOpCode.Ldtoken);
- encoder.Token (userTypeRef);
- encoder.Call (_getTypeFromHandleRef);
- encoder.Call (_activateInstanceRef);
-
- encoder.MarkLabel (skipLabel);
- encoder.OpCode (ILOpCode.Ret);
- },
- encodeLocals: null,
- useBranches: true);
- } else if (activationCtor.Style == ActivationCtorStyle.JavaInterop) {
- var ctorRef = AddJavaInteropActivationCtorRef (userTypeRef);
-
- handle = _pe.EmitBody (uco.WrapperName,
- MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig,
- encodeSig,
- encoder => {
- var skipLabel = encoder.DefineLabel ();
- encoder.Call (_withinNewObjectScopeRef);
- encoder.Branch (ILOpCode.Brtrue, skipLabel);
+ if (!activationCtor.IsOnLeafType) {
+ encoder.OpCode (ILOpCode.Ldtoken);
+ encoder.Token (targetTypeRef);
+ encoder.Call (_getTypeFromHandleRef);
+ encoder.Call (_getUninitializedObjectRef);
+ encoder.OpCode (ILOpCode.Castclass);
+ encoder.Token (targetTypeRef);
+ }
encoder.LoadLocalAddress (0);
encoder.LoadArgument (1); // self
encoder.Call (_jniObjectReferenceCtorRef);
- encoder.LoadLocalAddress (0);
- encoder.LoadConstantI4 (1); // JniObjectReferenceOptions.Copy
- encoder.OpCode (ILOpCode.Newobj);
- encoder.Token (ctorRef);
- encoder.OpCode (ILOpCode.Pop);
+ if (activationCtor.IsOnLeafType) {
+ encoder.LoadLocalAddress (0);
+ encoder.LoadConstantI4 (1); // JniObjectReferenceOptions.Copy
+ encoder.OpCode (ILOpCode.Newobj);
+ encoder.Token (ctorRef);
+ encoder.OpCode (ILOpCode.Pop);
+ } else {
+ encoder.LoadLocalAddress (0);
+ encoder.LoadConstantI4 (1); // JniObjectReferenceOptions.Copy
+ encoder.Call (ctorRef);
+ }
encoder.MarkLabel (skipLabel);
encoder.OpCode (ILOpCode.Ret);
@@ -783,21 +783,36 @@ MethodDefinitionHandle EmitUcoConstructor (UcoConstructorData uco, JavaPeerProxy
EncodeJniObjectReferenceLocal,
useBranches: true);
} else {
- var ctorRef = AddActivationCtorRef (userTypeRef);
+ var ctorRef = AddActivationCtorRef (
+ activationCtor.IsOnLeafType ? targetTypeRef : _pe.ResolveTypeRef (activationCtor.DeclaringType));
handle = _pe.EmitBody (uco.WrapperName,
MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig,
encodeSig,
encoder => {
+ // Skip activation if the object is being created from managed code
var skipLabel = encoder.DefineLabel ();
encoder.Call (_withinNewObjectScopeRef);
encoder.Branch (ILOpCode.Brtrue, skipLabel);
- encoder.LoadArgument (1); // self
- encoder.LoadConstantI4 (0); // JniHandleOwnership.DoNotTransfer
- encoder.OpCode (ILOpCode.Newobj);
- encoder.Token (ctorRef);
- encoder.OpCode (ILOpCode.Pop);
+ if (activationCtor.IsOnLeafType) {
+ encoder.LoadArgument (1); // self
+ encoder.LoadConstantI4 (0); // JniHandleOwnership.DoNotTransfer
+ encoder.OpCode (ILOpCode.Newobj);
+ encoder.Token (ctorRef);
+ encoder.OpCode (ILOpCode.Pop);
+ } else {
+ encoder.OpCode (ILOpCode.Ldtoken);
+ encoder.Token (targetTypeRef);
+ encoder.Call (_getTypeFromHandleRef);
+ encoder.Call (_getUninitializedObjectRef);
+ encoder.OpCode (ILOpCode.Castclass);
+ encoder.Token (targetTypeRef);
+
+ encoder.LoadArgument (1); // self
+ encoder.LoadConstantI4 (0); // JniHandleOwnership.DoNotTransfer
+ encoder.Call (ctorRef);
+ }
encoder.MarkLabel (skipLabel);
encoder.OpCode (ILOpCode.Ret);
@@ -805,7 +820,6 @@ MethodDefinitionHandle EmitUcoConstructor (UcoConstructorData uco, JavaPeerProxy
encodeLocals: null,
useBranches: true);
}
-
AddUnmanagedCallersOnlyAttribute (handle);
return handle;
}
diff --git a/src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.cs b/src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.cs
index b227292746d..4b46fee009c 100644
--- a/src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.cs
+++ b/src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.cs
@@ -147,39 +147,6 @@ internal bool TryCreatePeer (Type type, IntPtr handle, JniHandleOwnership transf
return GetProxyForManagedType (type)?.GetContainerFactory ();
}
- ///
- /// Creates a managed peer instance for a Java object being constructed.
- /// Called from generated UCO constructor wrappers (nctor_*_uco).
- ///
- internal static void ActivateInstance (IntPtr self, Type targetType)
- {
- var instance = s_instance;
- if (instance is null) {
- throw new InvalidOperationException ("TrimmableTypeMap has not been initialized.");
- }
-
- // Look up the proxy via JNI class name → TypeMap dictionary.
- // We can't use targetType.GetCustomAttribute() because the
- // self-application attribute is on the proxy type, not the target type.
- var selfRef = new JniObjectReference (self);
- var jniClass = JniEnvironment.Types.GetObjectClass (selfRef);
- var className = JniEnvironment.Types.GetJniTypeNameFromClass (jniClass);
- JniObjectReference.Dispose (ref jniClass);
-
- if (className is null || !instance._typeMap.TryGetValue (className, out var proxyType)) {
- throw new InvalidOperationException (
- $"Failed to create peer for type '{targetType.FullName}' (jniClass='{className}'). " +
- "Ensure the type has a generated proxy in the TypeMap assembly.");
- }
-
- var proxy = proxyType.GetCustomAttribute (inherit: false);
- if (proxy is null || proxy.CreateInstance (self, JniHandleOwnership.DoNotTransfer) is null) {
- throw new InvalidOperationException (
- $"Failed to create peer for type '{targetType.FullName}'. " +
- "Ensure the type has a generated proxy in the TypeMap assembly.");
- }
- }
-
[UnmanagedCallersOnly]
static void OnRegisterNatives (IntPtr jnienv, IntPtr klass, IntPtr nativeClassHandle)
{
diff --git a/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs b/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs
index 10cedc025ed..9853f392a56 100644
--- a/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs
+++ b/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs
@@ -189,7 +189,7 @@ public void Generate_LeafCtor_DoesNotUseCreateManagedPeer ()
}
[Fact]
- public void Generate_InheritedCtor_UcoUsesGuardWithoutReflectionFallback ()
+ public void Generate_InheritedCtor_UcoUsesGuardAndInlinedActivation ()
{
var peers = ScanFixtures ();
var simpleActivity = peers.First (p => p.JavaName == "my/app/SimpleActivity");
@@ -202,7 +202,8 @@ public void Generate_InheritedCtor_UcoUsesGuardWithoutReflectionFallback ()
var memberNames = GetMemberRefNames (reader);
Assert.Contains ("get_WithinNewObjectScope", memberNames);
- Assert.Contains ("ActivateInstance", memberNames);
+ Assert.Contains ("GetUninitializedObject", memberNames);
+ Assert.DoesNotContain ("ActivateInstance", memberNames);
Assert.DoesNotContain ("ActivatePeerFromJavaConstructor", memberNames);
}