Skip to content

Commit b22aef2

Browse files
Port default-ctor nctor IL to TrackedInstructionEncoder
After rebasing onto main (which merged #11260 adding maxstack tracking), the new 0-arg nctor codegen path in EmitUcoMethod was using the pre-#11260 bare OpCode + Token style. Switch to the tracked encoder helpers (LoadToken, CastClass, Call(parameterCount,…), Callvirt(parameterCount,…)) so the emitted method's maxstack is computed correctly, matching every other IL site in TypeMapAssemblyEmitter. Also update Generate_InheritedCtor_* / Generate_InheritedJavaInteropCtor_* generator tests to reflect the new nctor contract: the default-ctor branch references the target type's parameterless .ctor(), SetPeerReference and MarkActivationPeerReplaceable — and no longer references the legacy (IntPtr, JniHandleOwnership) / (JniObjectReference&, JniObjectReferenceOptions) activation ctor on the inherited base. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 324b7f0 commit b22aef2

2 files changed

Lines changed: 27 additions & 17 deletions

File tree

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,29 +1067,26 @@ MethodDefinitionHandle EmitUcoConstructor (UcoConstructorData uco, JavaPeerProxy
10671067
MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig,
10681068
encodeSig,
10691069
(encoder, cfb) => EmitUcoConstructorBodyWithMarshal (encoder, cfb, enc => {
1070-
enc.OpCode (ILOpCode.Ldtoken);
1071-
enc.Token (targetTypeRef);
1072-
enc.Call (_getTypeFromHandleRef);
1073-
enc.Call (_getUninitializedObjectRef);
1074-
enc.OpCode (ILOpCode.Castclass);
1075-
enc.Token (targetTypeRef);
1070+
enc.LoadToken (targetTypeRef);
1071+
enc.Call (_getTypeFromHandleRef, parameterCount: 1, returnsValue: true);
1072+
enc.Call (_getUninitializedObjectRef, parameterCount: 1, returnsValue: true);
1073+
enc.CastClass (targetTypeRef);
10761074
enc.StoreLocal (4);
10771075

10781076
enc.LoadLocalAddress (3); // jniRef
10791077
enc.LoadArgument (1); // self
10801078
enc.LoadConstantI4 (0); // JniObjectReferenceType.Invalid
1081-
enc.Call (_jniObjectReferenceCtorRef);
1079+
enc.Call (_jniObjectReferenceCtorRef, parameterCount: 2, isInstance: true);
10821080

10831081
enc.LoadLocal (4);
10841082
enc.LoadLocal (3);
1085-
enc.OpCode (ILOpCode.Callvirt);
1086-
enc.Token (_setPeerReferenceRef);
1083+
enc.Callvirt (_setPeerReferenceRef, parameterCount: 1);
10871084

10881085
enc.LoadLocal (4);
1089-
enc.Call (defaultCtorRef);
1086+
enc.Call (defaultCtorRef, parameterCount: 0, isInstance: true);
10901087

10911088
enc.LoadArgument (1); // self
1092-
enc.Call (_markActivationPeerReplaceableRef);
1089+
enc.Call (_markActivationPeerReplaceableRef, parameterCount: 1);
10931090
}),
10941091
blob => EncodeUcoConstructorLocals_DefaultConstructor (blob, targetTypeRef));
10951092
AddUnmanagedCallersOnlyAttribute (defaultCtorHandle);
@@ -1133,7 +1130,7 @@ MethodDefinitionHandle EmitUcoConstructor (UcoConstructorData uco, JavaPeerProxy
11331130
enc.Call (ctorRef, parameterCount: 2, isInstance: true);
11341131
}
11351132
enc.LoadArgument (1); // self
1136-
enc.Call (_markActivationPeerReplaceableRef);
1133+
enc.Call (_markActivationPeerReplaceableRef, parameterCount: 1);
11371134
}),
11381135
EncodeUcoConstructorLocals_JavaInterop);
11391136
} else {
@@ -1164,7 +1161,7 @@ MethodDefinitionHandle EmitUcoConstructor (UcoConstructorData uco, JavaPeerProxy
11641161
enc.Call (ctorRef, parameterCount: 2, isInstance: true);
11651162
}
11661163
enc.LoadArgument (1); // self
1167-
enc.Call (_markActivationPeerReplaceableRef);
1164+
enc.Call (_markActivationPeerReplaceableRef, parameterCount: 1);
11681165
}),
11691166
EncodeUcoConstructorLocals_Standard);
11701167
}

tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ public void Generate_LeafCtor_DoesNotUseCreateManagedPeer ()
288288
}
289289

290290
[Fact]
291-
public void Generate_InheritedCtor_ReferencesGuardAndActivationCtor ()
291+
public void Generate_InheritedCtor_NctorUcoCallsDefaultConstructor ()
292292
{
293293
var peers = ScanFixtures ();
294294
var simpleActivity = peers.First (p => p.JavaName == "my/app/SimpleActivity");
@@ -302,18 +302,24 @@ public void Generate_InheritedCtor_ReferencesGuardAndActivationCtor ()
302302

303303
Assert.Contains ("ShouldSkipActivation", memberNames);
304304
Assert.Contains ("GetUninitializedObject", memberNames);
305+
Assert.Contains ("SetPeerReference", memberNames);
306+
Assert.Contains ("MarkActivationPeerReplaceable", memberNames);
305307
Assert.DoesNotContain ("Invoke", memberNames);
306308
Assert.DoesNotContain ("ActivateInstance", memberNames);
307309
Assert.DoesNotContain ("ActivatePeerFromJavaConstructor", memberNames);
308310

309-
Assert.NotEmpty (FindCtorMemberRefs (reader, "Android.App", "Activity",
311+
// The new no-arg nctor codegen calls the target type's parameterless .ctor()
312+
// directly, not the legacy (IntPtr, JniHandleOwnership) activation ctor on the base.
313+
Assert.NotEmpty (FindCtorMemberRefs (reader, "MyApp", "SimpleActivity"));
314+
Assert.Empty (FindCtorMemberRefs (reader, "Android.App", "Activity",
310315
"System.IntPtr", "Android.Runtime.JniHandleOwnership"));
316+
311317
var nctorMethodHandle = FindNctorUcoMethod (reader);
312318
Assert.False (nctorMethodHandle.IsNil, "SimpleActivity should have a nctor_*_uco method");
313319
}
314320

315321
[Fact]
316-
public void Generate_InheritedJavaInteropCtor_ReferencesActivationCtor ()
322+
public void Generate_InheritedJavaInteropCtor_NctorUcoCallsDefaultConstructor ()
317323
{
318324
var peer = MakeAcwPeer ("test/JiInheritedTarget", "Test.JiInheritedTarget", "TestAsm") with {
319325
ActivationCtor = new ActivationCtorInfo {
@@ -332,10 +338,17 @@ public void Generate_InheritedJavaInteropCtor_ReferencesActivationCtor ()
332338

333339
var memberNames = GetMemberRefNames (reader);
334340
Assert.Contains ("GetUninitializedObject", memberNames);
341+
Assert.Contains ("SetPeerReference", memberNames);
342+
Assert.Contains ("MarkActivationPeerReplaceable", memberNames);
335343
Assert.DoesNotContain ("Invoke", memberNames);
336344

337-
Assert.NotEmpty (FindCtorMemberRefs (reader, "Test", "JiInheritedBase",
345+
// The new no-arg nctor codegen calls the target type's parameterless .ctor()
346+
// directly, not the JI-style (JniObjectReference&, JniObjectReferenceOptions)
347+
// activation ctor on the inherited base.
348+
Assert.NotEmpty (FindCtorMemberRefs (reader, "Test", "JiInheritedTarget"));
349+
Assert.Empty (FindCtorMemberRefs (reader, "Test", "JiInheritedBase",
338350
"Java.Interop.JniObjectReference&", "Java.Interop.JniObjectReferenceOptions"));
351+
339352
var nctorMethodHandle = FindNctorUcoMethod (reader);
340353
Assert.False (nctorMethodHandle.IsNil, "The ACW peer should have a nctor_*_uco method");
341354
}

0 commit comments

Comments
 (0)