Skip to content

Commit ec8c61e

Browse files
sbomerCopilotsimonrozsival
authored
[TrimmableTypeMap] Skip UCO constructor generation for abstract types (#11461)
* Skip UCO constructor generation for abstract types Abstract types are never directly instantiated from Java — the ACW constructor's getClass() guard prevents activation. Generating UCO constructor wrappers for them is dead code and fails when the managed constructor is protected (which is legitimate for abstract types). This fixes XAGTT7009 errors when using the trimmable type map with abstract types like MAUI's CellAdapter that have protected constructors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Assisted-by: Claude:claude-opus-4.6-1m * Add test: abstract type with protected ctor builds with trimmable typemap Verifies that abstract Java peer types with protected constructors do not cause XAGTT7009 when using _AndroidTypeMapImplementation=trimmable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Assisted-by: Claude:claude-opus-4.6-1m * Add unit test: abstract type skips UCO constructor generation Verifies that BuildUcoConstructors produces no UCO constructors for abstract types, even when the Java constructor has SuperArgumentsString and no matching public managed constructor exists. This complements the existing Build_ExportConstructorWithoutMatchingManagedCtor_Throws test which covers the concrete type case. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Assisted-by: Claude:claude-opus-4.6-1m * Add IgnoreUnsupportedConfiguration guard to NativeAOT integration test Addresses Copilot review feedback: the test hard-codes NativeAOT without the IgnoreUnsupportedConfiguration guard, which can cause failures in environments where NativeAOT builds are skipped. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Assisted-by: Claude:claude-opus-4.6-1m --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Šimon Rozsíval <simon@rozsival.com>
1 parent a28e5f2 commit ec8c61e

3 files changed

Lines changed: 58 additions & 0 deletions

File tree

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,13 @@ static void BuildUcoConstructors (JavaPeerInfo peer, JavaPeerProxyData proxy)
356356
return;
357357
}
358358

359+
// Abstract types are never directly instantiated from Java — the ACW
360+
// constructor's getClass() guard prevents activation. Skip generating
361+
// UCO constructor wrappers for them.
362+
if (peer.IsAbstract) {
363+
return;
364+
}
365+
359366
foreach (var ctor in peer.JavaConstructors) {
360367
if (ctor.SuperArgumentsString != null && !ctor.HasMatchingManagedCtor) {
361368
throw new InvalidOperationException (

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,5 +394,36 @@ class ExportShapes : Java.Lang.Object {
394394
"assembly or the user's [Export] source. Offending warning lines:\n " +
395395
string.Join ("\n ", offending));
396396
}
397+
398+
[Test]
399+
public void Build_WithTrimmableTypeMap_AbstractTypeWithProtectedCtor_Succeeds ()
400+
{
401+
if (IgnoreUnsupportedConfiguration (AndroidRuntime.NativeAOT, release: true)) {
402+
return;
403+
}
404+
405+
var proj = new XamarinAndroidApplicationProject {
406+
IsRelease = true,
407+
};
408+
proj.SetRuntime (AndroidRuntime.NativeAOT);
409+
proj.SetProperty ("_AndroidTypeMapImplementation", "trimmable");
410+
proj.Sources.Add (new BuildItem.Source ("AbstractProvider.cs") {
411+
TextContent = () => @"
412+
namespace UnnamedProject {
413+
public abstract class AbstractProvider : Java.Lang.Object {
414+
protected AbstractProvider (Android.Content.Context context) { }
415+
public abstract string GetData ();
416+
}
417+
418+
public class ConcreteProvider : AbstractProvider {
419+
public ConcreteProvider (Android.Content.Context context) : base (context) { }
420+
public override string GetData () => ""hello"";
421+
}
422+
}"
423+
});
424+
425+
using var builder = CreateApkBuilder ();
426+
Assert.IsTrue (builder.Build (proj), "Build should have succeeded — abstract types with protected ctors should not cause XAGTT7009.");
427+
}
397428
}
398429
}

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,6 +1369,26 @@ public void Build_ExportConstructorWithoutMatchingManagedCtor_Throws ()
13691369
Assert.Contains ("no matching user-visible managed constructor", ex.Message);
13701370
Assert.Contains ("MyApp.MissingCtor", ex.Message);
13711371
}
1372+
1373+
[Fact]
1374+
public void Build_AbstractTypeWithProtectedCtor_NoUcoConstructors ()
1375+
{
1376+
var peer = MakeAcwPeer ("my/app/AbstractAdapter", "MyApp.AbstractAdapter", "App") with {
1377+
IsAbstract = true,
1378+
JavaConstructors = new List<JavaConstructorInfo> {
1379+
new JavaConstructorInfo {
1380+
ConstructorIndex = 0,
1381+
JniSignature = "(Landroid/content/Context;)V",
1382+
HasMatchingManagedCtor = false,
1383+
SuperArgumentsString = "p0",
1384+
},
1385+
},
1386+
};
1387+
var model = BuildModel (new [] { peer });
1388+
var proxy = model.ProxyTypes.FirstOrDefault (p => p.TypeName.Contains ("AbstractAdapter"));
1389+
Assert.NotNull (proxy);
1390+
Assert.Empty (proxy.UcoConstructors);
1391+
}
13721392
}
13731393

13741394
public class NativeRegistrations

0 commit comments

Comments
 (0)