From 4664f102604813450df501de33f548796ccb0732 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 17 Sep 2026 08:24:12 +0200 Subject: [PATCH] [trimmable-type-map] Avoid unused XML references Create XML marshalling metadata lazily so non-XML Export methods do not root System.Xml.ReaderWriter and System.Private.Xml. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Generator/ExportMethodDispatchEmitter.cs | 8 +- .../ExportMethodDispatchEmitterContext.cs | 137 +++++++++++++----- .../TypeMapAssemblyGeneratorTests.cs | 29 ++++ 3 files changed, 132 insertions(+), 42 deletions(-) diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ExportMethodDispatchEmitter.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ExportMethodDispatchEmitter.cs index 8a33dac9b4f..8cc1e386b74 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ExportMethodDispatchEmitter.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ExportMethodDispatchEmitter.cs @@ -444,12 +444,12 @@ bool TryEmitExportParameterArgument (TrackedInstructionEncoder encoder, ExportPa case ExportParameterKindInfo.XmlPullParser: encoder.LoadArgument (argumentIndex); encoder.LoadConstantI4 (0); - encoder.Call (_context.XmlPullParserReaderFromJniHandleRef, parameterCount: 2, returnsValue: true); + encoder.Call (_context.GetXmlPullParserReaderFromJniHandleRef (), parameterCount: 2, returnsValue: true); return true; case ExportParameterKindInfo.XmlResourceParser: encoder.LoadArgument (argumentIndex); encoder.LoadConstantI4 (0); - encoder.Call (_context.XmlResourceParserReaderFromJniHandleRef, parameterCount: 2, returnsValue: true); + encoder.Call (_context.GetXmlResourceParserReaderFromJniHandleRef (), parameterCount: 2, returnsValue: true); return true; default: return false; @@ -534,10 +534,10 @@ bool TryEmitExportParameterReturn (TrackedInstructionEncoder encoder, ExportPara encoder.Call (_context.OutputStreamAdapterToLocalJniHandleRef, parameterCount: 1, returnsValue: true); return true; case ExportParameterKindInfo.XmlPullParser: - encoder.Call (_context.XmlReaderPullParserToLocalJniHandleRef, parameterCount: 1, returnsValue: true); + encoder.Call (_context.GetXmlReaderPullParserToLocalJniHandleRef (), parameterCount: 1, returnsValue: true); return true; case ExportParameterKindInfo.XmlResourceParser: - encoder.Call (_context.XmlReaderResourceParserToLocalJniHandleRef, parameterCount: 1, returnsValue: true); + encoder.Call (_context.GetXmlReaderResourceParserToLocalJniHandleRef (), parameterCount: 1, returnsValue: true); return true; default: return false; diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ExportMethodDispatchEmitterContext.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ExportMethodDispatchEmitterContext.cs index 95afef57d86..eaf4a6287b8 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ExportMethodDispatchEmitterContext.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ExportMethodDispatchEmitterContext.cs @@ -10,6 +10,19 @@ namespace Microsoft.Android.Sdk.TrimmableTypeMap; /// sealed class ExportMethodDispatchEmitterContext { + readonly PEAssemblyBuilder _pe; + + TypeReferenceHandle _systemXmlReaderRef; + MemberReferenceHandle _xmlPullParserReaderFromJniHandleRef; + MemberReferenceHandle _xmlResourceParserReaderFromJniHandleRef; + MemberReferenceHandle _xmlReaderPullParserToLocalJniHandleRef; + MemberReferenceHandle _xmlReaderResourceParserToLocalJniHandleRef; + + ExportMethodDispatchEmitterContext (PEAssemblyBuilder pe) + { + _pe = pe; + } + public static ExportMethodDispatchEmitterContext Create ( PEAssemblyBuilder pe, TypeReferenceHandle iJavaPeerableRef, @@ -35,9 +48,6 @@ public static ExportMethodDispatchEmitterContext Create ( metadata.GetOrAddString ("System"), metadata.GetOrAddString ("Array")); var systemStreamRef = metadata.AddTypeReference (pe.SystemRuntimeRef, metadata.GetOrAddString ("System.IO"), metadata.GetOrAddString ("Stream")); - var systemXmlRef = pe.FindOrAddAssemblyRef ("System.Xml.ReaderWriter"); - var systemXmlReaderRef = metadata.AddTypeReference (systemXmlRef, - metadata.GetOrAddString ("System.Xml"), metadata.GetOrAddString ("XmlReader")); var inputStreamInvokerRef = metadata.AddTypeReference (pe.MonoAndroidRef, metadata.GetOrAddString ("Android.Runtime"), metadata.GetOrAddString ("InputStreamInvoker")); var outputStreamInvokerRef = metadata.AddTypeReference (pe.MonoAndroidRef, @@ -46,14 +56,6 @@ public static ExportMethodDispatchEmitterContext Create ( metadata.GetOrAddString ("Android.Runtime"), metadata.GetOrAddString ("InputStreamAdapter")); var outputStreamAdapterRef = metadata.AddTypeReference (pe.MonoAndroidRef, metadata.GetOrAddString ("Android.Runtime"), metadata.GetOrAddString ("OutputStreamAdapter")); - var xmlPullParserReaderRef = metadata.AddTypeReference (pe.MonoAndroidRef, - metadata.GetOrAddString ("Android.Runtime"), metadata.GetOrAddString ("XmlPullParserReader")); - var xmlResourceParserReaderRef = metadata.AddTypeReference (pe.MonoAndroidRef, - metadata.GetOrAddString ("Android.Runtime"), metadata.GetOrAddString ("XmlResourceParserReader")); - var xmlReaderPullParserRef = metadata.AddTypeReference (pe.MonoAndroidRef, - metadata.GetOrAddString ("Android.Runtime"), metadata.GetOrAddString ("XmlReaderPullParser")); - var xmlReaderResourceParserRef = metadata.AddTypeReference (pe.MonoAndroidRef, - metadata.GetOrAddString ("Android.Runtime"), metadata.GetOrAddString ("XmlReaderResourceParser")); var charSequenceRef = metadata.AddTypeReference (pe.MonoAndroidRef, metadata.GetOrAddString ("Android.Runtime"), metadata.GetOrAddString ("CharSequence")); var iCharSequenceRef = metadata.AddTypeReference (pe.MonoAndroidRef, @@ -71,8 +73,9 @@ public static ExportMethodDispatchEmitterContext Create ( var systemCollectionsICollectionRef = metadata.AddTypeReference (pe.SystemRuntimeRef, metadata.GetOrAddString ("System.Collections"), metadata.GetOrAddString ("ICollection")); - return new ExportMethodDispatchEmitterContext { + return new ExportMethodDispatchEmitterContext (pe) { IJavaObjectRef = iJavaObjectRef, + JniHandleOwnershipRef = jniHandleOwnershipRef, GetTypeFromHandleRef = getTypeFromHandleRef, JniEnvGetStringRef = pe.AddMemberRef (jniEnvRef, "GetString", sig => sig.MethodSignature ().Parameters (2, @@ -142,28 +145,6 @@ public static ExportMethodDispatchEmitterContext Create ( sig => sig.MethodSignature ().Parameters (1, rt => rt.Type ().IntPtr (), p => p.AddParameter ().Type ().Type (systemStreamRef, false))), - XmlPullParserReaderFromJniHandleRef = pe.AddMemberRef (xmlPullParserReaderRef, "FromJniHandle", - sig => sig.MethodSignature ().Parameters (2, - rt => rt.Type ().Type (systemXmlReaderRef, false), - p => { - p.AddParameter ().Type ().IntPtr (); - p.AddParameter ().Type ().Type (jniHandleOwnershipRef, true); - })), - XmlResourceParserReaderFromJniHandleRef = pe.AddMemberRef (xmlResourceParserReaderRef, "FromJniHandle", - sig => sig.MethodSignature ().Parameters (2, - rt => rt.Type ().Type (systemXmlReaderRef, false), - p => { - p.AddParameter ().Type ().IntPtr (); - p.AddParameter ().Type ().Type (jniHandleOwnershipRef, true); - })), - XmlReaderPullParserToLocalJniHandleRef = pe.AddMemberRef (xmlReaderPullParserRef, "ToLocalJniHandle", - sig => sig.MethodSignature ().Parameters (1, - rt => rt.Type ().IntPtr (), - p => p.AddParameter ().Type ().Type (systemXmlReaderRef, false))), - XmlReaderResourceParserToLocalJniHandleRef = pe.AddMemberRef (xmlReaderResourceParserRef, "ToLocalJniHandle", - sig => sig.MethodSignature ().Parameters (1, - rt => rt.Type ().IntPtr (), - p => p.AddParameter ().Type ().Type (systemXmlReaderRef, false))), CharSequenceToLocalJniHandleRef = pe.AddMemberRef (charSequenceRef, "ToLocalJniHandle", sig => sig.MethodSignature ().Parameters (1, rt => rt.Type ().IntPtr (), @@ -191,6 +172,89 @@ public static ExportMethodDispatchEmitterContext Create ( }; } + TypeReferenceHandle GetSystemXmlReaderRef () + { + if (!_systemXmlReaderRef.IsNil) { + return _systemXmlReaderRef; + } + + var metadata = _pe.Metadata; + var systemXmlRef = _pe.FindOrAddAssemblyRef ("System.Xml.ReaderWriter"); + _systemXmlReaderRef = metadata.AddTypeReference (systemXmlRef, + metadata.GetOrAddString ("System.Xml"), metadata.GetOrAddString ("XmlReader")); + return _systemXmlReaderRef; + } + + public MemberReferenceHandle GetXmlPullParserReaderFromJniHandleRef () + { + if (!_xmlPullParserReaderFromJniHandleRef.IsNil) { + return _xmlPullParserReaderFromJniHandleRef; + } + + var metadata = _pe.Metadata; + var xmlPullParserReaderRef = metadata.AddTypeReference (_pe.MonoAndroidRef, + metadata.GetOrAddString ("Android.Runtime"), metadata.GetOrAddString ("XmlPullParserReader")); + _xmlPullParserReaderFromJniHandleRef = _pe.AddMemberRef (xmlPullParserReaderRef, "FromJniHandle", + sig => sig.MethodSignature ().Parameters (2, + rt => rt.Type ().Type (GetSystemXmlReaderRef (), false), + p => { + p.AddParameter ().Type ().IntPtr (); + p.AddParameter ().Type ().Type (JniHandleOwnershipRef, true); + })); + return _xmlPullParserReaderFromJniHandleRef; + } + + public MemberReferenceHandle GetXmlResourceParserReaderFromJniHandleRef () + { + if (!_xmlResourceParserReaderFromJniHandleRef.IsNil) { + return _xmlResourceParserReaderFromJniHandleRef; + } + + var metadata = _pe.Metadata; + var xmlResourceParserReaderRef = metadata.AddTypeReference (_pe.MonoAndroidRef, + metadata.GetOrAddString ("Android.Runtime"), metadata.GetOrAddString ("XmlResourceParserReader")); + _xmlResourceParserReaderFromJniHandleRef = _pe.AddMemberRef (xmlResourceParserReaderRef, "FromJniHandle", + sig => sig.MethodSignature ().Parameters (2, + rt => rt.Type ().Type (GetSystemXmlReaderRef (), false), + p => { + p.AddParameter ().Type ().IntPtr (); + p.AddParameter ().Type ().Type (JniHandleOwnershipRef, true); + })); + return _xmlResourceParserReaderFromJniHandleRef; + } + + public MemberReferenceHandle GetXmlReaderPullParserToLocalJniHandleRef () + { + if (!_xmlReaderPullParserToLocalJniHandleRef.IsNil) { + return _xmlReaderPullParserToLocalJniHandleRef; + } + + var metadata = _pe.Metadata; + var xmlReaderPullParserRef = metadata.AddTypeReference (_pe.MonoAndroidRef, + metadata.GetOrAddString ("Android.Runtime"), metadata.GetOrAddString ("XmlReaderPullParser")); + _xmlReaderPullParserToLocalJniHandleRef = _pe.AddMemberRef (xmlReaderPullParserRef, "ToLocalJniHandle", + sig => sig.MethodSignature ().Parameters (1, + rt => rt.Type ().IntPtr (), + p => p.AddParameter ().Type ().Type (GetSystemXmlReaderRef (), false))); + return _xmlReaderPullParserToLocalJniHandleRef; + } + + public MemberReferenceHandle GetXmlReaderResourceParserToLocalJniHandleRef () + { + if (!_xmlReaderResourceParserToLocalJniHandleRef.IsNil) { + return _xmlReaderResourceParserToLocalJniHandleRef; + } + + var metadata = _pe.Metadata; + var xmlReaderResourceParserRef = metadata.AddTypeReference (_pe.MonoAndroidRef, + metadata.GetOrAddString ("Android.Runtime"), metadata.GetOrAddString ("XmlReaderResourceParser")); + _xmlReaderResourceParserToLocalJniHandleRef = _pe.AddMemberRef (xmlReaderResourceParserRef, "ToLocalJniHandle", + sig => sig.MethodSignature ().Parameters (1, + rt => rt.Type ().IntPtr (), + p => p.AddParameter ().Type ().Type (GetSystemXmlReaderRef (), false))); + return _xmlReaderResourceParserToLocalJniHandleRef; + } + public required TypeReferenceHandle IJavaObjectRef { get; init; } public required MemberReferenceHandle GetTypeFromHandleRef { get; init; } public required MemberReferenceHandle JniEnvGetStringRef { get; init; } @@ -204,10 +268,6 @@ public static ExportMethodDispatchEmitterContext Create ( public required MemberReferenceHandle OutputStreamInvokerFromJniHandleRef { get; init; } public required MemberReferenceHandle InputStreamAdapterToLocalJniHandleRef { get; init; } public required MemberReferenceHandle OutputStreamAdapterToLocalJniHandleRef { get; init; } - public required MemberReferenceHandle XmlPullParserReaderFromJniHandleRef { get; init; } - public required MemberReferenceHandle XmlResourceParserReaderFromJniHandleRef { get; init; } - public required MemberReferenceHandle XmlReaderPullParserToLocalJniHandleRef { get; init; } - public required MemberReferenceHandle XmlReaderResourceParserToLocalJniHandleRef { get; init; } public required MemberReferenceHandle CharSequenceToLocalJniHandleRef { get; init; } public required MemberReferenceHandle JavaListToLocalJniHandleRef { get; init; } public required MemberReferenceHandle JavaDictionaryToLocalJniHandleRef { get; init; } @@ -225,4 +285,5 @@ public static ExportMethodDispatchEmitterContext Create ( public required MemberReferenceHandle BeginMarshalMethodRef { get; init; } public required MemberReferenceHandle EndMarshalMethodRef { get; init; } public required MemberReferenceHandle OnUserUnhandledExceptionRef { get; init; } + public required TypeReferenceHandle JniHandleOwnershipRef { get; init; } } diff --git a/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs b/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs index c06151ad8c9..d0ec23d42ef 100644 --- a/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs +++ b/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs @@ -97,6 +97,35 @@ public void Generate_HasRequiredAssemblyReferences () Assert.Contains ("System.Runtime.InteropServices", asmRefs); } + [Fact] + public void Generate_ExportWithoutXmlMarshalling_DoesNotReferenceSystemXml () + { + var peer = ScanFixtures ().Single (p => p.JavaName == "my/app/ExportExample"); + using var stream = GenerateAssembly (new [] { peer }); + using var pe = new PEReader (stream); + var reader = pe.GetMetadataReader (); + var asmRefs = reader.AssemblyReferences + .Select (h => reader.GetString (reader.GetAssemblyReference (h).Name)) + .ToList (); + + Assert.DoesNotContain ("System.Xml.ReaderWriter", asmRefs); + Assert.DoesNotContain ("System.Private.Xml", asmRefs); + } + + [Fact] + public void Generate_ExportWithXmlMarshalling_ReferencesSystemXml () + { + var peer = ScanFixtures ().Single (p => p.JavaName == "my/app/ExportMarshallingShapes"); + using var stream = GenerateAssembly (new [] { peer }); + using var pe = new PEReader (stream); + var reader = pe.GetMetadataReader (); + var asmRefs = reader.AssemblyReferences + .Select (h => reader.GetString (reader.GetAssemblyReference (h).Name)) + .ToList (); + + Assert.Contains ("System.Xml.ReaderWriter", asmRefs); + } + [Fact] public void Generate_InheritedGenericBaseCallback_UsesValueTypeGenericArgument () {