diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 589e5cd32..638176463 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,7 +20,7 @@ jobs: - name: Test run: dotnet test --no-build -c Debug Mono.Cecil.sln linux: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v1 - name: Build diff --git a/Directory.Build.props b/Directory.Build.props index cadf4b9f2..e853ad2a1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -22,7 +22,7 @@ true - + diff --git a/Mono.Cecil.Tests.props b/Mono.Cecil.Tests.props index 35cbbfc8b..e20ab0191 100644 --- a/Mono.Cecil.Tests.props +++ b/Mono.Cecil.Tests.props @@ -4,10 +4,10 @@ - 3.11.0 + 3.14.0 - 3.12.0 + 4.5.0 diff --git a/Mono.Cecil/AssemblyReader.cs b/Mono.Cecil/AssemblyReader.cs index 0756fa884..4564071e8 100644 --- a/Mono.Cecil/AssemblyReader.cs +++ b/Mono.Cecil/AssemblyReader.cs @@ -2508,7 +2508,7 @@ public Collection ReadCustomAttributes (ICustomAttributeProvide if (module.IsWindowsMetadata ()) foreach (var custom_attribute in custom_attributes) - WindowsRuntimeProjections.Project (owner, custom_attribute); + WindowsRuntimeProjections.Project (owner, custom_attributes, custom_attribute); return custom_attributes; } diff --git a/Mono.Cecil/GenericParameter.cs b/Mono.Cecil/GenericParameter.cs index 30dd73382..de746449b 100644 --- a/Mono.Cecil/GenericParameter.cs +++ b/Mono.Cecil/GenericParameter.cs @@ -165,6 +165,11 @@ public bool HasDefaultConstructorConstraint { set { attributes = attributes.SetAttributes ((ushort) GenericParameterAttributes.DefaultConstructorConstraint, value); } } + public bool AllowByRefLikeConstraint { + get { return attributes.GetAttributes ((ushort) GenericParameterAttributes.AllowByRefLikeConstraint); } + set { attributes = attributes.SetAttributes ((ushort) GenericParameterAttributes.AllowByRefLikeConstraint, value); } + } + #endregion public GenericParameter (IGenericParameterProvider owner) diff --git a/Mono.Cecil/GenericParameterAttributes.cs b/Mono.Cecil/GenericParameterAttributes.cs index 149582cdb..51f094f47 100644 --- a/Mono.Cecil/GenericParameterAttributes.cs +++ b/Mono.Cecil/GenericParameterAttributes.cs @@ -22,6 +22,7 @@ public enum GenericParameterAttributes : ushort { SpecialConstraintMask = 0x001c, ReferenceTypeConstraint = 0x0004, NotNullableValueTypeConstraint = 0x0008, - DefaultConstructorConstraint = 0x0010 + DefaultConstructorConstraint = 0x0010, + AllowByRefLikeConstraint = 0x0020, } } diff --git a/Mono.Cecil/Import.cs b/Mono.Cecil/Import.cs index 37cf932ae..6948359e9 100644 --- a/Mono.Cecil/Import.cs +++ b/Mono.Cecil/Import.cs @@ -159,6 +159,19 @@ enum ImportGenericKind { { typeof (object), ElementType.Object }, }; + TypeReference ImportType (Type type, ImportGenericContext context, Type [] required_modifiers, Type [] optional_modifiers) + { + var import = ImportType (type, context); + + foreach (var modifier in required_modifiers) + import = new RequiredModifierType (ImportType (modifier, context), import); + + foreach (var modifier in optional_modifiers) + import = new OptionalModifierType (ImportType (modifier, context), import); + + return import; + } + TypeReference ImportType (Type type, ImportGenericContext context) { return ImportType (type, context, ImportGenericKind.Open); @@ -349,7 +362,7 @@ FieldReference ImportField (SR.FieldInfo field, ImportGenericContext context) return new FieldReference { Name = field.Name, DeclaringType = declaring_type, - FieldType = ImportType (field.FieldType, context), + FieldType = ImportType (field.FieldType, context, field.GetRequiredCustomModifiers (), field.GetOptionalCustomModifiers ()), }; } finally { context.Pop (); @@ -393,15 +406,17 @@ MethodReference ImportMethod (SR.MethodBase method, ImportGenericContext context try { var method_info = method as SR.MethodInfo; reference.ReturnType = method_info != null - ? ImportType (method_info.ReturnType, context) + ? ImportType (method_info.ReturnType, context, method_info.ReturnParameter.GetRequiredCustomModifiers (), method_info.ReturnParameter.GetOptionalCustomModifiers ()) : ImportType (typeof (void), default (ImportGenericContext)); var parameters = method.GetParameters (); var reference_parameters = reference.Parameters; - for (int i = 0; i < parameters.Length; i++) + for (int i = 0; i < parameters.Length; i++) { + var parameter = parameters [i]; reference_parameters.Add ( - new ParameterDefinition (ImportType (parameters [i].ParameterType, context))); + new ParameterDefinition (ImportType (parameter.ParameterType, context, parameter.GetRequiredCustomModifiers (), parameter.GetOptionalCustomModifiers ()))); + } reference.DeclaringType = declaring_type; diff --git a/Mono.Cecil/MetadataResolver.cs b/Mono.Cecil/MetadataResolver.cs index 6511b4187..29b786293 100644 --- a/Mono.Cecil/MetadataResolver.cs +++ b/Mono.Cecil/MetadataResolver.cs @@ -227,6 +227,22 @@ public virtual MethodDefinition Resolve (MethodReference method) if (!type.HasMethods) return null; + + // This is here to handle privatescope. Aka CompilerControlled. + // GetMethod cannot correctly resolve a privatescope method when the method name is the same as another method in the type. + // because GetMethod operates on a MethodReference which doesn't have access to the MethodAttributes. + // privatescope methods are always private. There should also never be a MethodReference to a privatescope method. + // in other words, privatescope methods should always be referenced by their MethodDefinition. + // + // privatescope methods aside, if method is ever a MethodDefinition we don't need to go searching all of the methods on the type + // we can return the method directly. This avoids the cost of a linear search. + // + // So we have this optimization opportunity here. + // And we need to handle privatescope methods somehow, because GetMethod can't. + // + // and 2 birds one stone. This if check covers the optimization and privatescope handling. + if (method is MethodDefinition definition) + return definition; return GetMethod (type, method); } @@ -264,6 +280,9 @@ public static MethodDefinition GetMethod (Collection methods, if (!AreSame (method.ReturnType, reference.ReturnType)) continue; + if (method.HasThis != reference.HasThis) + continue; + if (method.IsVarArg () != reference.IsVarArg ()) continue; @@ -331,6 +350,35 @@ static bool AreSame (TypeSpecification a, TypeSpecification b) if (a.IsArray) return AreSame ((ArrayType) a, (ArrayType) b); + if (a.IsFunctionPointer) + return AreSame ((FunctionPointerType) a, (FunctionPointerType) b); + + return true; + } + + static bool AreSame (FunctionPointerType a, FunctionPointerType b) + { + if (a.HasThis != b.HasThis) + return false; + + if (a.CallingConvention != b.CallingConvention) + return false; + + if (!AreSame (a.ReturnType, b.ReturnType)) + return false; + + if (a.ContainsGenericParameter != b.ContainsGenericParameter) + return false; + + if (a.HasParameters != b.HasParameters) + return false; + + if (!a.HasParameters) + return true; + + if (!AreSame (a.Parameters, b.Parameters)) + return false; + return true; } diff --git a/Mono.Cecil/WindowsRuntimeProjections.cs b/Mono.Cecil/WindowsRuntimeProjections.cs index 6e83ba66e..b96a891b2 100644 --- a/Mono.Cecil/WindowsRuntimeProjections.cs +++ b/Mono.Cecil/WindowsRuntimeProjections.cs @@ -241,7 +241,7 @@ public static void Project (TypeDefinition type) treatment = TypeDefinitionTreatment.PrefixWindowsRuntimeName; if (treatment == TypeDefinitionTreatment.PrefixWindowsRuntimeName || treatment == TypeDefinitionTreatment.NormalType) - if (!type.IsInterface && HasAttribute (type, "Windows.UI.Xaml", "TreatAsAbstractComposableClassAttribute")) + if (!type.IsInterface && HasAttribute (type.CustomAttributes, "Windows.UI.Xaml", "TreatAsAbstractComposableClassAttribute")) treatment |= TypeDefinitionTreatment.Abstract; } else if (metadata_kind == MetadataKind.ManagedWindowsMetadata && IsClrImplementationType (type)) @@ -860,7 +860,7 @@ AssemblyNameReference GetAssemblyReference (string name) throw new Exception (); } - public static void Project (ICustomAttributeProvider owner, CustomAttribute attribute) + public static void Project (ICustomAttributeProvider owner, Collection owner_attributes, CustomAttribute attribute) { if (!IsWindowsAttributeUsageAttribute (owner, attribute)) return; @@ -876,7 +876,7 @@ public static void Project (ICustomAttributeProvider owner, CustomAttribute attr } if (treatment == CustomAttributeValueTreatment.None) { - var multiple = HasAttribute (type, "Windows.Foundation.Metadata", "AllowMultipleAttribute"); + var multiple = HasAttribute (owner_attributes, "Windows.Foundation.Metadata", "AllowMultipleAttribute"); treatment = multiple ? CustomAttributeValueTreatment.AllowMultiple : CustomAttributeValueTreatment.AllowSingle; } @@ -905,9 +905,9 @@ static bool IsWindowsAttributeUsageAttribute (ICustomAttributeProvider owner, Cu return declaring_type.Name == "AttributeUsageAttribute" && declaring_type.Namespace == /*"Windows.Foundation.Metadata"*/"System"; } - static bool HasAttribute (TypeDefinition type, string @namespace, string name) + static bool HasAttribute (Collection attributes, string @namespace, string name) { - foreach (var attribute in type.CustomAttributes) { + foreach (var attribute in attributes) { var attribute_type = attribute.AttributeType; if (attribute_type.Name == name && attribute_type.Namespace == @namespace) return true; diff --git a/Mono.Security.Cryptography/CryptoService.cs b/Mono.Security.Cryptography/CryptoService.cs index 3877debfa..1da81b1e1 100644 --- a/Mono.Security.Cryptography/CryptoService.cs +++ b/Mono.Security.Cryptography/CryptoService.cs @@ -26,6 +26,8 @@ namespace Mono.Cecil { static class CryptoService { + static SHA1 CreateSHA1 () => new SHA1CryptoServiceProvider (); + public static byte [] GetPublicKey (WriterParameters parameters) { using (var rsa = parameters.CreateRSA ()) { @@ -93,7 +95,7 @@ static byte [] HashStream (Stream stream, ImageWriter writer, out int strong_nam + (strong_name_directory.VirtualAddress - text.VirtualAddress)); var strong_name_length = (int) strong_name_directory.Size; - var sha1 = new SHA1Managed (); + var sha1 = CreateSHA1 (); var buffer = new byte [buffer_size]; using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write)) { stream.Seek (0, SeekOrigin.Begin); @@ -131,7 +133,7 @@ public static byte [] ComputeHash (Stream stream) { const int buffer_size = 8192; - var sha1 = new SHA1Managed (); + var sha1 = CreateSHA1 (); var buffer = new byte [buffer_size]; using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write)) @@ -142,7 +144,7 @@ public static byte [] ComputeHash (Stream stream) public static byte [] ComputeHash (params ByteBuffer [] buffers) { - var sha1 = new SHA1Managed (); + var sha1 = CreateSHA1 (); using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write)) { for (int i = 0; i < buffers.Length; i++) { diff --git a/ProjectInfo.cs b/ProjectInfo.cs index fe891346c..c7ffe5f04 100644 --- a/ProjectInfo.cs +++ b/ProjectInfo.cs @@ -15,6 +15,6 @@ [assembly: ComVisible (false)] -[assembly: AssemblyVersion ("0.11.4.0")] -[assembly: AssemblyFileVersion ("0.11.4.0")] -[assembly: AssemblyInformationalVersion ("0.11.4.0")] +[assembly: AssemblyVersion ("0.11.5.0")] +[assembly: AssemblyFileVersion ("0.11.5.0")] +[assembly: AssemblyInformationalVersion ("0.11.5.0")] diff --git a/Test/Mono.Cecil.Tests/ImageReadTests.cs b/Test/Mono.Cecil.Tests/ImageReadTests.cs index f663b60ca..fb5c558c6 100644 --- a/Test/Mono.Cecil.Tests/ImageReadTests.cs +++ b/Test/Mono.Cecil.Tests/ImageReadTests.cs @@ -182,7 +182,7 @@ public void MetroAssembly () [Test] public void WindowsRuntimeComponentAssembly () { - var resolver = WindowsRuntimeAssemblyResolver.CreateInstance (); + var resolver = WindowsRuntimeAssemblyResolver.CreateInstance (applyWindowsRuntimeProjections: false); if (resolver == null) return; diff --git a/Test/Mono.Cecil.Tests/MethodTests.cs b/Test/Mono.Cecil.Tests/MethodTests.cs index b878613c0..821fb139e 100644 --- a/Test/Mono.Cecil.Tests/MethodTests.cs +++ b/Test/Mono.Cecil.Tests/MethodTests.cs @@ -2,8 +2,9 @@ using System.Linq; using Mono.Cecil; +using Mono.Cecil.Cil; using Mono.Cecil.Metadata; - +using Mono.Collections.Generic; using NUnit.Framework; namespace Mono.Cecil.Tests { @@ -221,5 +222,153 @@ public void ReturnParameterMethod () Assert.IsNotNull (method); Assert.AreEqual (method, method.MethodReturnType.Parameter.Method); } + + [Test] + public void InstanceAndStaticMethodComparison () + { + TestIL ("others.il", module => { + var others = module.GetType ("Others"); + var instance_method = others.Methods.Single (m => m.Name == "SameMethodNameInstanceStatic" && m.HasThis); + var static_method_reference = new MethodReference ("SameMethodNameInstanceStatic", instance_method.ReturnType, others) + { + HasThis = false + }; + + Assert.AreNotEqual(instance_method, static_method_reference.Resolve ()); + }); + } + + [Test] + public void FunctionPointerArgumentOverload () + { + TestIL ("others.il", module => { + var others = module.GetType ("Others"); + var overloaded_methods = others.Methods.Where (m => m.Name == "OverloadedWithFpArg").ToArray (); + // Manually create the function-pointer type so `AreSame` won't exit early due to reference equality + var overloaded_method_int_reference = new MethodReference ("OverloadedWithFpArg", module.TypeSystem.Void, others) + { + HasThis = false, + Parameters = { new ParameterDefinition ("X", ParameterAttributes.None, new FunctionPointerType () { + HasThis = false, + ReturnType = module.TypeSystem.Int32, + Parameters = { new ParameterDefinition (module.TypeSystem.Int32) } + }) } + }; + + var overloaded_method_long_reference = new MethodReference ("OverloadedWithFpArg", module.TypeSystem.Void, others) + { + HasThis = false, + Parameters = { new ParameterDefinition ("X", ParameterAttributes.None, new FunctionPointerType () { + HasThis = false, + ReturnType = module.TypeSystem.Int32, + Parameters = { new ParameterDefinition (module.TypeSystem.Int64) } + }) } + }; + + var overloaded_method_cdecl_reference = new MethodReference ("OverloadedWithFpArg", module.TypeSystem.Void, others) + { + HasThis = false, + Parameters = { new ParameterDefinition ("X", ParameterAttributes.None, new FunctionPointerType () { + CallingConvention = MethodCallingConvention.C, + HasThis = false, + ReturnType = module.TypeSystem.Int32, + Parameters = { new ParameterDefinition (module.TypeSystem.Int32) } + }) } + }; + + + Assert.AreEqual (overloaded_methods[0], overloaded_method_int_reference.Resolve ()); + Assert.AreEqual (overloaded_methods[1], overloaded_method_long_reference.Resolve ()); + Assert.AreEqual (overloaded_methods[2], overloaded_method_cdecl_reference.Resolve ()); + }); + } + + [Test] + public void PrivateScope () + { + TestIL ("privatescope.il", module => { + var foo = module.GetType ("Foo"); + var call_same_name_methods = foo.GetMethod ("CallSameNameMethods"); + var call_instructions = call_same_name_methods.Body.Instructions + .Where (ins => ins.OpCode.Code == Code.Call) + .ToArray (); + + var first_same_name_index = 2; + + // The first method will be the normal non-privatescope method. + var first_call_resolved = ((MethodReference)call_instructions [0].Operand).Resolve (); + var expected_first_call_resolved = foo.Methods [first_same_name_index]; + Assert.IsFalse(first_call_resolved.IsCompilerControlled); + Assert.AreEqual(expected_first_call_resolved, first_call_resolved); + + // This is the first privatescope method. + var second_call_resolved = ((MethodReference)call_instructions [1].Operand).Resolve(); + var expected_second_call_resolved = foo.Methods [first_same_name_index + 1]; + + // Sanity check to make sure the ordering assumptions were correct. + Assert.IsTrue(expected_second_call_resolved.IsCompilerControlled, "The expected method should have been compiler controlled."); + + // The equality failure isn't going to be very helpful since both methods will have the same ToString value, + // so before we assert equality, we'll assert that the method is compiler controlled because that is the key difference + Assert.IsTrue(second_call_resolved.IsCompilerControlled, "Expected the method reference to resolve to a compiler controlled method"); + Assert.AreEqual(expected_second_call_resolved, second_call_resolved); + + // This is the second privatescope method. + var third_call_resolved = ((MethodReference)call_instructions [2].Operand).Resolve (); + var expected_third_call_resolved = foo.Methods [first_same_name_index + 2]; + + // Sanity check to make sure the ordering assumptions were correct. + Assert.IsTrue(expected_third_call_resolved.IsCompilerControlled, "The expected method should have been compiler controlled."); + + // The equality failure isn't going to be very helpful since both methods will have the same ToString value, + // so before we assert equality, we'll assert that the method is compiler controlled because that is the key difference + Assert.IsTrue(third_call_resolved.IsCompilerControlled, "Expected the method reference to resolve to a compiler controlled method"); + Assert.AreEqual(expected_third_call_resolved, third_call_resolved); + }); + } + + [Test] + public void PrivateScopeGeneric () + { + TestIL ("privatescope.il", module => { + var foo = module.GetType ("Foo"); + var call_same_name_methods = foo.GetMethod ("CallSameNameMethodsGeneric"); + var call_instructions = call_same_name_methods.Body.Instructions + .Where (ins => ins.OpCode.Code == Code.Call) + .ToArray (); + + var first_same_name_generic_index = 6; + + // The first method will be the normal non-privatescope method. + var first_call_resolved = ((MethodReference)call_instructions [0].Operand).Resolve(); + var expected_first_call_resolved = foo.Methods [first_same_name_generic_index]; + Assert.IsFalse(first_call_resolved.IsCompilerControlled); + Assert.AreEqual(expected_first_call_resolved, first_call_resolved); + + // This is the first privatescope method. + var second_call_resolved = ((MethodReference)call_instructions [1].Operand).Resolve(); + var expected_second_call_resolved = foo.Methods [first_same_name_generic_index + 1]; + + // Sanity check to make sure the ordering assumptions were correct. + Assert.IsTrue(expected_second_call_resolved.IsCompilerControlled, "The expected method should have been compiler controlled."); + + // The equality failure isn't going to be very helpful since both methods will have the same ToString value, + // so before we assert equality, we'll assert that the method is compiler controlled because that is the key difference + Assert.IsTrue (second_call_resolved.IsCompilerControlled, "Expected the method reference to resolve to a compiler controlled method"); + Assert.AreEqual(expected_second_call_resolved, second_call_resolved); + + // This is the second privatescope method. + var third_call_resolved = ((MethodReference)call_instructions [2].Operand).Resolve(); + var expected_third_call_resolved = foo.Methods [first_same_name_generic_index + 2]; + + // Sanity check to make sure the ordering assumptions were correct. + Assert.IsTrue(expected_third_call_resolved.IsCompilerControlled, "The expected method should have been compiler controlled."); + + // The equality failure isn't going to be very helpful since both methods will have the same ToString value, + // so before we assert equality, we'll assert that the method is compiler controlled because that is the key difference + Assert.IsTrue(third_call_resolved.IsCompilerControlled, "Expected the method reference to resolve to a compiler controlled method"); + Assert.AreEqual(expected_third_call_resolved, third_call_resolved); + }); + } } } diff --git a/Test/Mono.Cecil.Tests/WindowsRuntimeAssemblyResolver.cs b/Test/Mono.Cecil.Tests/WindowsRuntimeAssemblyResolver.cs index 65616294f..4f6aaf6ec 100644 --- a/Test/Mono.Cecil.Tests/WindowsRuntimeAssemblyResolver.cs +++ b/Test/Mono.Cecil.Tests/WindowsRuntimeAssemblyResolver.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using Microsoft.Win32; namespace Mono.Cecil.Tests { @@ -10,12 +11,12 @@ public class WindowsRuntimeAssemblyResolver : DefaultAssemblyResolver { readonly Dictionary assemblies = new Dictionary (); - public static WindowsRuntimeAssemblyResolver CreateInstance () + public static WindowsRuntimeAssemblyResolver CreateInstance (bool applyWindowsRuntimeProjections) { if (Platform.OnMono) return null; try { - return new WindowsRuntimeAssemblyResolver (); + return new WindowsRuntimeAssemblyResolver (applyWindowsRuntimeProjections); } catch { return null; } @@ -30,20 +31,30 @@ public override AssemblyDefinition Resolve (AssemblyNameReference name) return base.Resolve (name); } - private WindowsRuntimeAssemblyResolver () + private WindowsRuntimeAssemblyResolver (bool applyWindowsRuntimeProjections) { - LoadWindowsSdk ("v8.1", "8.1", (installationFolder) => { - var fileName = Path.Combine (installationFolder, @"References\CommonConfiguration\Neutral\Annotated\Windows.winmd"); - var assembly = AssemblyDefinition.ReadAssembly (fileName); - Register (assembly); - }); + var readerParameters = new ReaderParameters { + ApplyWindowsRuntimeProjections = applyWindowsRuntimeProjections + }; LoadWindowsSdk ("v10.0", "10", (installationFolder) => { var referencesFolder = Path.Combine (installationFolder, "References"); var assemblies = Directory.GetFiles (referencesFolder, "*.winmd", SearchOption.AllDirectories); + + var latestVersionDir = Directory.GetDirectories (Path.Combine (installationFolder, "UnionMetadata")) + .Where (d => Path.GetFileName (d) != "Facade") + .OrderBy (d => Path.GetFileName (d)) + .Last (); + + var windowsWinMdPath = Path.Combine (latestVersionDir, "Windows.winmd"); + if (!File.Exists (windowsWinMdPath)) + throw new FileNotFoundException (windowsWinMdPath); + + var windowsWinmdAssembly = AssemblyDefinition.ReadAssembly (windowsWinMdPath, readerParameters); + Register (windowsWinmdAssembly); foreach (var assemblyPath in assemblies) { - var assembly = AssemblyDefinition.ReadAssembly (assemblyPath); + var assembly = AssemblyDefinition.ReadAssembly (assemblyPath, readerParameters); Register (assembly); } }); diff --git a/Test/Mono.Cecil.Tests/WindowsRuntimeProjectionsTests.cs b/Test/Mono.Cecil.Tests/WindowsRuntimeProjectionsTests.cs index 538727329..ae443930b 100644 --- a/Test/Mono.Cecil.Tests/WindowsRuntimeProjectionsTests.cs +++ b/Test/Mono.Cecil.Tests/WindowsRuntimeProjectionsTests.cs @@ -17,19 +17,21 @@ public abstract class BaseWindowsRuntimeProjectionsTests : BaseTestFixture { protected abstract string [] ManagedClassTypeNames { get; } protected abstract string [] CustomListTypeNames { get; } - [Test] - public void CanReadMetadataType () + [TestCase (true)] + [TestCase (false)] + public void CanReadMetadataType (bool readSdkAssembliesWithApplyWindowsRuntimeProjections) { if (Platform.OnMono) return; TestModule (ModuleName, (module) => { Assert.AreEqual (ExpectedMetadataKind, module.MetadataKind); - }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (), applyWindowsRuntimeProjections: true); + }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (readSdkAssembliesWithApplyWindowsRuntimeProjections), applyWindowsRuntimeProjections: true); } - [Test] - public void CanProjectParametersAndReturnTypes () + [TestCase (true)] + [TestCase (false)] + public void CanProjectParametersAndReturnTypes (bool readSdkAssembliesWithApplyWindowsRuntimeProjections) { if (Platform.OnMono) return; @@ -48,11 +50,12 @@ public void CanProjectParametersAndReturnTypes () Assert.AreEqual (listSetter.Parameters.Count, 1); Assert.AreEqual (listSetter.Parameters [0].ParameterType.FullName, "System.Collections.Generic.IList`1"); } - }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (), applyWindowsRuntimeProjections: true); + }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (readSdkAssembliesWithApplyWindowsRuntimeProjections), applyWindowsRuntimeProjections: true); } - [Test] - public void CanProjectInterfaces () + [TestCase (true)] + [TestCase (false)] + public void CanProjectInterfaces (bool readSdkAssembliesWithApplyWindowsRuntimeProjections) { if (Platform.OnMono) return; @@ -64,16 +67,41 @@ public void CanProjectInterfaces () Assert.IsNotNull (type.Interfaces.SingleOrDefault (i => i.InterfaceType.FullName == "System.Collections.Generic.IList`1")); Assert.IsNotNull (type.Interfaces.SingleOrDefault (i => i.InterfaceType.FullName == "System.Collections.Generic.IEnumerable`1")); } - }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (), applyWindowsRuntimeProjections: true); + }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (readSdkAssembliesWithApplyWindowsRuntimeProjections), applyWindowsRuntimeProjections: true); + } + + /// + /// This test exists to verify a StackOverflowException that started happening with https://github.com/jbevain/cecil/pull/843 + /// and was fixed by https://github.com/jbevain/cecil/pull/879 + /// + /// The windows runtime sdk assemblies must be read with ApplyWindowsRuntimeProjections in order for the StackOverflowException to happen + /// + [TestCase (true)] + [TestCase (false)] + public void CanAvoidCircleAttributeReading (bool readSdkAssembliesWithApplyWindowsRuntimeProjections) + { + if (Platform.OnMono) + return; + + TestModule (ModuleName, (module) => { + + var windowsWinMd = module.AssemblyResolver.Resolve (new AssemblyNameReference ("Windows", null)); + + var problematicType = windowsWinMd.MainModule.GetType ("Windows.Foundation.Metadata.ActivatableAttribute"); + + Assert.Greater (problematicType.CustomAttributes.Count, 0, "Expected one or more attributes"); + + }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (readSdkAssembliesWithApplyWindowsRuntimeProjections), applyWindowsRuntimeProjections: true); } - [Test] - public void CanStripType () + [TestCase (true)] + [TestCase (false)] + public void CanStripType (bool readSdkAssembliesWithApplyWindowsRuntimeProjections) { if (Platform.OnMono) return; - var assemblyResolver = WindowsRuntimeAssemblyResolver.CreateInstance (); + var assemblyResolver = WindowsRuntimeAssemblyResolver.CreateInstance (readSdkAssembliesWithApplyWindowsRuntimeProjections); TestModule (ModuleName, (originalModule) => { var types = CustomListTypeNames.Select (typeName => originalModule.Types.Single (t => t.Name == typeName)).ToArray (); @@ -107,8 +135,9 @@ public class ManagedWindowsRuntimeProjectionsTests : BaseWindowsRuntimeProjectio protected override string [] CustomListTypeNames { get { return new [] { "CustomList", "CustomList" }; } } - [Test] - public void CanProjectClasses () + [TestCase (true)] + [TestCase (false)] + public void CanProjectClasses (bool readSdkAssembliesWithApplyWindowsRuntimeProjections) { if (Platform.OnMono) return; @@ -129,11 +158,12 @@ public void CanProjectClasses () var winrtSomeOtherClassType = module.Types.Single (t => t.Name == "SomeOtherClass"); Assert.AreEqual ("SomeOtherClass", winrtSomeOtherClassType.WindowsRuntimeProjection.Name); Assert.AreEqual (TypeDefinitionTreatment.PrefixWindowsRuntimeName, winrtSomeOtherClassType.WindowsRuntimeProjection.Treatment); - }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (), applyWindowsRuntimeProjections: true); + }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (readSdkAssembliesWithApplyWindowsRuntimeProjections), applyWindowsRuntimeProjections: true); } - [Test] - public void VerifyTypeReferenceToProjectedTypeInAttributeArgumentReferencesUnmangledTypeName() + [TestCase (true)] + [TestCase (false)] + public void VerifyTypeReferenceToProjectedTypeInAttributeArgumentReferencesUnmangledTypeName(bool readSdkAssembliesWithApplyWindowsRuntimeProjections) { if (Platform.OnMono) return; @@ -147,7 +177,7 @@ public void VerifyTypeReferenceToProjectedTypeInAttributeArgumentReferencesUnman var attributeArgument = (TypeReference)attribute.ConstructorArguments[0].Value; Assert.AreEqual("ManagedWinmd.ClassWithAsyncMethod/d__0", attributeArgument.FullName); - }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance(), applyWindowsRuntimeProjections: true); + }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance(readSdkAssembliesWithApplyWindowsRuntimeProjections), applyWindowsRuntimeProjections: true); } } @@ -162,8 +192,9 @@ public class NativeWindowsRuntimeProjectionsTests : BaseWindowsRuntimeProjection protected override string [] CustomListTypeNames { get { return new [] { "CustomList" }; } } - [Test] - public void CanProjectAndRedirectInterfaces () + [TestCase (true)] + [TestCase (false)] + public void CanProjectAndRedirectInterfaces (bool readSdkAssembliesWithApplyWindowsRuntimeProjections) { if (Platform.OnMono) return; @@ -213,11 +244,12 @@ public void CanProjectAndRedirectInterfaces () Assert.AreEqual (0, customPropertySetClass.Interfaces[6].CustomAttributes.Count); Assert.AreEqual ("Windows.Foundation.Collections.IIterable`1>", customPropertySetClass.Interfaces[6].InterfaceType.FullName); - }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (), applyWindowsRuntimeProjections: true); + }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (readSdkAssembliesWithApplyWindowsRuntimeProjections), applyWindowsRuntimeProjections: true); } - [Test] - public void CanProjectInterfaceMethods () + [TestCase (true)] + [TestCase (false)] + public void CanProjectInterfaceMethods (bool readSdkAssembliesWithApplyWindowsRuntimeProjections) { if (Platform.OnMono) return; @@ -256,11 +288,12 @@ public void CanProjectInterfaceMethods () Assert.AreEqual (customListClass.Methods[25].FullName, "System.Boolean NativeWinmd.CustomList::Remove(System.Int32)"); Assert.AreEqual (customListClass.Methods[26].FullName, "System.Collections.Generic.IEnumerator`1 NativeWinmd.CustomList::GetEnumerator()"); Assert.AreEqual (customListClass.Methods[27].FullName, "System.Collections.IEnumerator NativeWinmd.CustomList::GetEnumerator()"); - }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (), applyWindowsRuntimeProjections: true); + }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (readSdkAssembliesWithApplyWindowsRuntimeProjections), applyWindowsRuntimeProjections: true); } - [Test] - public void CanProjectMethodOverrides () + [TestCase (true)] + [TestCase (false)] + public void CanProjectMethodOverrides (bool readSdkAssembliesWithApplyWindowsRuntimeProjections) { if (Platform.OnMono) return; @@ -299,7 +332,7 @@ public void CanProjectMethodOverrides () Assert.AreEqual (customListClass.Methods[26].Overrides[0].FullName, "System.Collections.Generic.IEnumerator`1 System.Collections.Generic.IEnumerable`1::GetEnumerator()"); Assert.AreEqual (customListClass.Methods[27].Overrides[0].FullName, "System.Collections.IEnumerator System.Collections.IEnumerable::GetEnumerator()"); - }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (), applyWindowsRuntimeProjections: true); + }, verify: false, assemblyResolver: WindowsRuntimeAssemblyResolver.CreateInstance (readSdkAssembliesWithApplyWindowsRuntimeProjections), applyWindowsRuntimeProjections: true); } } } diff --git a/Test/Resources/il/others.il b/Test/Resources/il/others.il index 60713af30..7be104208 100644 --- a/Test/Resources/il/others.il +++ b/Test/Resources/il/others.il @@ -78,4 +78,29 @@ .other instance void Others::dang_Handler (class [mscorlib]System.EventHandler) .other instance void Others::fang_Handler (class [mscorlib]System.EventHandler) } + + .method public static void OverloadedWithFpArg(method int32 *(int32) X) cil managed + { + ret + } + + .method public static void OverloadedWithFpArg(method int32 *(int64) X) cil managed + { + ret + } + + .method public static void OverloadedWithFpArg(method unmanaged cdecl int32 *(int32) X) cil managed + { + ret + } + + .method public instance void SameMethodNameInstanceStatic() cil managed + { + ret + } + + .method public static void SameMethodNameInstanceStatic() cil managed + { + ret + } // end of static method MethodNameTests::MethodName } diff --git a/Test/Resources/il/privatescope.il b/Test/Resources/il/privatescope.il new file mode 100644 index 000000000..12aadee1e --- /dev/null +++ b/Test/Resources/il/privatescope.il @@ -0,0 +1,77 @@ +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89) + .ver 2:0:0:0 +} + +.assembly PrivateScope {} + +.module PrivateScope.dll + +.class private auto ansi Foo { + + .method public specialname rtspecialname instance void .ctor () cil managed + { + ldarg.0 + call instance void [mscorlib]System.Object::.ctor () + ret + } + + .method public instance void CallSameNameMethods() cil managed + { + ldarg.0 + call instance void Foo::'SameName'() + ldarg.0 + call instance void Foo::'SameName$PST0600A3BA'() + ldarg.0 + call instance void Foo::'SameName$PST0600A3BC'() + ret + } + + .method public hidebysig + instance void 'SameName' () cil managed + { + ret + } + + .method privatescope + instance void 'SameName$PST0600A3BA' () cil managed + { + ret + } + + .method privatescope + instance void 'SameName$PST0600A3BC' () cil managed + { + ret + } + + .method public instance void CallSameNameMethodsGeneric() cil managed + { + ldarg.0 + call instance void Foo::'SameNameGeneric'() + ldarg.0 + call instance void Foo::'SameNameGeneric$PST0600A3BD'() + ldarg.0 + call instance void Foo::'SameNameGeneric$PST0600A3BE'() + ret + } + + .method public hidebysig + instance void 'SameNameGeneric' () cil managed + { + ret + } + + .method privatescope + instance void 'SameNameGeneric$PST0600A3BD' () cil managed + { + ret + } + + .method privatescope + instance void 'SameNameGeneric$PST0600A3BE' () cil managed + { + ret + } +} \ No newline at end of file