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..8665c3f91ba 100644 --- a/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs +++ b/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs @@ -97,6 +97,72 @@ 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 (); + var typeNames = GetTypeRefNames (reader); + + Assert.DoesNotContain ("System.Xml.ReaderWriter", asmRefs); + Assert.DoesNotContain ("XmlReader", typeNames); + Assert.DoesNotContain ("XmlPullParserReader", typeNames); + Assert.DoesNotContain ("XmlResourceParserReader", typeNames); + Assert.DoesNotContain ("XmlReaderPullParser", typeNames); + Assert.DoesNotContain ("XmlReaderResourceParser", typeNames); + } + + [Fact] + public void Generate_ExportWithXmlMarshalling_EmitsAdapterReferences () + { + 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); + + var expectedAdapters = new [] { + (Key: "PullParserFromHandle", Parent: "XmlPullParserReader", Method: "FromJniHandle", ReturnType: "System.Xml.XmlReader", + ParameterTypes: new [] { "System.IntPtr", "Android.Runtime.JniHandleOwnership" }), + (Key: "ResourceParserFromHandle", Parent: "XmlResourceParserReader", Method: "FromJniHandle", ReturnType: "System.Xml.XmlReader", + ParameterTypes: new [] { "System.IntPtr", "Android.Runtime.JniHandleOwnership" }), + (Key: "PullParserToHandle", Parent: "XmlReaderPullParser", Method: "ToLocalJniHandle", ReturnType: "System.IntPtr", + ParameterTypes: new [] { "System.Xml.XmlReader" }), + (Key: "ResourceParserToHandle", Parent: "XmlReaderResourceParser", Method: "ToLocalJniHandle", ReturnType: "System.IntPtr", + ParameterTypes: new [] { "System.Xml.XmlReader" }), + }; + var adapterHandles = new Dictionary (); + foreach (var expected in expectedAdapters) { + var handle = FindMemberReferenceHandle (reader, "Android.Runtime", expected.Parent, expected.Method); + var signature = reader.GetMemberReference (handle).DecodeMethodSignature (SignatureTypeProvider.Instance, null); + + Assert.Equal (expected.ReturnType, signature.ReturnType); + Assert.Equal (expected.ParameterTypes, signature.ParameterTypes); + adapterHandles.Add (expected.Key, handle); + } + + var pullParserCalls = ReadMethodCallTokens (pe, reader, "n_readXml_uco_"); + Assert.Contains (MetadataTokens.GetToken (adapterHandles ["PullParserFromHandle"]), pullParserCalls); + Assert.Contains (MetadataTokens.GetToken (adapterHandles ["PullParserToHandle"]), pullParserCalls); + Assert.DoesNotContain (MetadataTokens.GetToken (adapterHandles ["ResourceParserFromHandle"]), pullParserCalls); + Assert.DoesNotContain (MetadataTokens.GetToken (adapterHandles ["ResourceParserToHandle"]), pullParserCalls); + + var resourceParserCalls = ReadMethodCallTokens (pe, reader, "n_readResourceXml_uco_"); + Assert.Contains (MetadataTokens.GetToken (adapterHandles ["ResourceParserFromHandle"]), resourceParserCalls); + Assert.Contains (MetadataTokens.GetToken (adapterHandles ["ResourceParserToHandle"]), resourceParserCalls); + Assert.DoesNotContain (MetadataTokens.GetToken (adapterHandles ["PullParserFromHandle"]), resourceParserCalls); + Assert.DoesNotContain (MetadataTokens.GetToken (adapterHandles ["PullParserToHandle"]), resourceParserCalls); + } + [Fact] public void Generate_InheritedGenericBaseCallback_UsesValueTypeGenericArgument () { @@ -1311,6 +1377,11 @@ static MemberReference FindCallbackMemberRef (MetadataReader reader, string meth } static MemberReferenceHandle FindCallbackMemberRefHandle (MetadataReader reader, string methodName, string parentNamespace, string parentName) + { + return FindMemberReferenceHandle (reader, parentNamespace, parentName, methodName); + } + + static MemberReferenceHandle FindMemberReferenceHandle (MetadataReader reader, string parentNamespace, string parentName, string methodName) { var refs = Enumerable.Range (1, reader.GetTableRowCount (TableIndex.MemberRef)) .Select (MetadataTokens.MemberReferenceHandle) @@ -1353,6 +1424,16 @@ static List ReadLdftnTokens (byte [] ilBytes) return ReadInlineMetadataTokens (ilBytes, 0xFE, 0x06); } + static List ReadMethodCallTokens (PEReader pe, MetadataReader reader, string methodNamePrefix) + { + var method = reader.MethodDefinitions + .Select (handle => reader.GetMethodDefinition (handle)) + .Single (method => reader.GetString (method.Name).StartsWith (methodNamePrefix, StringComparison.Ordinal)); + var ilBytes = pe.GetMethodBody (method.RelativeVirtualAddress).GetILBytes (); + Assert.NotNull (ilBytes); + return ReadCallTokens (ilBytes); + } + static List ReadCallTokens (byte [] ilBytes) { return ReadInlineMetadataTokens (ilBytes, 0x28);