namespace Xamarin.Forms.Build.Tasks
{
+ class TypeRefComparer : IEqualityComparer<TypeReference>
+ {
+ static string GetAssembly(TypeReference typeRef)
+ {
+ var md = typeRef.Scope as ModuleDefinition;
+ if (md != null)
+ return md.Assembly.FullName;
+ var anr = typeRef.Scope as AssemblyNameReference;
+ if (anr != null)
+ return anr.FullName;
+ throw new ArgumentOutOfRangeException(nameof(typeRef));
+ }
+
+ public bool Equals(TypeReference x, TypeReference y)
+ {
+ return GetAssembly(x) == GetAssembly(y) && x.FullName == y.FullName;
+ }
+
+ public int GetHashCode(TypeReference obj)
+ {
+ return $"{GetAssembly(obj)}//{obj.FullName}".GetHashCode();
+ }
+
+ static TypeRefComparer s_default;
+ public static TypeRefComparer Default => s_default ?? (s_default = new TypeRefComparer());
+ }
+
static class TypeReferenceExtensions
{
public static PropertyDefinition GetProperty(this TypeReference typeRef, Func<PropertyDefinition, bool> predicate,
return false;
}
+ static readonly string[] arrayInterfaces = {
+ "System.ICloneable",
+ "System.Collections.IEnumerable",
+ "System.Collections.IList",
+ "System.Collections.ICollection",
+ "System.Collections.IStructuralComparable",
+ "System.Collections.IStructuralEquatable",
+ };
+
+ static readonly string[] arrayGenericInterfaces = {
+ "System.Collections.Generic.IEnumerable`1",
+ "System.Collections.Generic.IList`1",
+ "System.Collections.Generic.ICollection`1",
+ "System.Collections.Generic.IReadOnlyCollection`1",
+ "System.Collections.Generic.IReadOnlyList`1",
+ };
+
public static bool InheritsFromOrImplements(this TypeReference typeRef, TypeReference baseClass)
{
- if (typeRef.FullName == baseClass.FullName)
+ if (TypeRefComparer.Default.Equals(typeRef, baseClass))
return true;
- var arrayInterfaces = new[]
- {
- "System.Collections.IEnumerable",
- "System.Collections.IList",
- "System.Collections.Collection"
- };
-
- var arrayGenericInterfaces = new[]
- {
- "System.Collections.IEnumerable`1",
- "System.Collections.Generic.IList`1",
- "System.Collections.Generic.IReadOnlyCollection<T>",
- "System.Collections.Generic.IReadOnlyList<T>",
- "System.Collections.Generic.Collection<T>"
- };
-
- if (typeRef.IsArray && baseClass.IsArray) {
- typeRef = typeRef.Resolve();
- baseClass = baseClass.Resolve();
- }
+ if (typeRef.IsValueType)
+ return false;
- if (typeRef.IsArray)
- {
+ if (typeRef.IsArray) {
+ var array = (ArrayType)typeRef;
var arrayType = typeRef.Resolve();
if (arrayInterfaces.Contains(baseClass.FullName))
return true;
- if (arrayGenericInterfaces.Contains(baseClass.Resolve().FullName) &&
- baseClass.IsGenericInstance &&
- (baseClass as GenericInstanceType).GenericArguments[0].FullName == arrayType.FullName)
+ if (array.IsVector && //generic interfaces are not implemented on multidimensional arrays
+ arrayGenericInterfaces.Contains(baseClass.Resolve().FullName) &&
+ baseClass.IsGenericInstance &&
+ TypeRefComparer.Default.Equals((baseClass as GenericInstanceType).GenericArguments[0], arrayType))
return true;
- return false;
+ return baseClass.FullName == "System.Object";
}
+ if (typeRef.FullName == "System.Object")
+ return false;
var typeDef = typeRef.Resolve();
- if (typeDef.FullName == baseClass.FullName)
+ if (TypeRefComparer.Default.Equals(typeDef, baseClass.Resolve()))
return true;
- if (typeDef.Interfaces.Any(ir => ir.FullName == baseClass.FullName))
+ if (typeDef.Interfaces.Any(ir => TypeRefComparer.Default.Equals(ir, baseClass)))
return true;
- if (typeDef.FullName == "System.Object")
- return false;
if (typeDef.BaseType == null)
return false;
- return typeDef.BaseType.InheritsFromOrImplements(baseClass);
+
+ typeRef = typeDef.BaseType.ResolveGenericParameters(typeRef);
+ return typeRef.InheritsFromOrImplements(baseClass);
}
public static CustomAttribute GetCustomAttribute(this TypeReference typeRef, TypeReference attribute)
return self.GetElementType().MakeGenericInstanceType(args.ToArray());
}
}
-}
\ No newline at end of file
+}
--- /dev/null
+using System;
+using NUnit.Framework;
+using Mono.Cecil;
+using Xamarin.Forms.Build.Tasks;
+using System.Collections.Generic;
+
+namespace Xamarin.Forms
+{
+ public class Effect
+ {
+ }
+}
+namespace Xamarin.Forms.Xaml.XamlcUnitTests
+{
+ [TestFixture]
+ public class TypeReferenceExtensionsTests
+ {
+ class Foo
+ {
+ }
+
+ class Foo<T> : Foo
+ {
+ }
+
+ class Bar<T> : Foo<T>
+ {
+ }
+
+ ModuleDefinition module;
+
+ [SetUp]
+ public void SetUp()
+ {
+ var resolver = new XamlCAssemblyResolver();
+ resolver.AddAssembly(Uri.UnescapeDataString((new UriBuilder(typeof(TypeReferenceExtensionsTests).Assembly.CodeBase)).Path));
+ resolver.AddAssembly(Uri.UnescapeDataString((new UriBuilder(typeof(BindableObject).Assembly.CodeBase)).Path));
+ resolver.AddAssembly(Uri.UnescapeDataString((new UriBuilder(typeof(object).Assembly.CodeBase)).Path));
+ resolver.AddAssembly(Uri.UnescapeDataString((new UriBuilder(typeof(IList<>).Assembly.CodeBase)).Path));
+ resolver.AddAssembly(Uri.UnescapeDataString((new UriBuilder(typeof(Queue<>).Assembly.CodeBase)).Path));
+
+ module = ModuleDefinition.CreateModule("foo", new ModuleParameters {
+ AssemblyResolver = resolver,
+ Kind = ModuleKind.NetModule
+ });
+ }
+
+ [TestCase(typeof(bool), typeof(BindableObject), ExpectedResult = false)]
+ [TestCase(typeof(Dictionary<string, string>), typeof(BindableObject), ExpectedResult = false)]
+ [TestCase(typeof(List<string>), typeof(BindableObject), ExpectedResult = false)]
+ [TestCase(typeof(List<Button>), typeof(BindableObject), ExpectedResult = false)]
+ [TestCase(typeof(Queue<KeyValuePair<string, string>>), typeof(BindableObject), ExpectedResult = false)]
+ [TestCase(typeof(double), typeof(double), ExpectedResult = true)]
+ [TestCase(typeof(object), typeof(IList<TriggerBase>), ExpectedResult = false)]
+ [TestCase(typeof(object), typeof(double), ExpectedResult = false)]
+ [TestCase(typeof(object), typeof(int?), ExpectedResult = false)]
+ [TestCase(typeof(object), typeof(object), ExpectedResult = true)]
+ [TestCase(typeof(sbyte), typeof(BindableObject), ExpectedResult = false)]
+ [TestCase(typeof(string[]), typeof(System.Collections.IEnumerable), ExpectedResult = true)]
+ [TestCase(typeof(string[]), typeof(object), ExpectedResult = true)]
+ [TestCase(typeof(string[]), typeof(string), ExpectedResult = false)]
+ [TestCase(typeof(string[]), typeof(BindingBase), ExpectedResult = false)]
+ [TestCase(typeof(string[]), typeof(IEnumerable<string>), ExpectedResult = true)]
+ [TestCase(typeof(Type), typeof(object), ExpectedResult = true)]
+ [TestCase(typeof(Type), typeof(Type), ExpectedResult = true)]
+ [TestCase(typeof(Type), typeof(BindableObject), ExpectedResult = false)]
+ [TestCase(typeof(System.Windows.Input.ICommand), typeof(System.Windows.Input.ICommand), ExpectedResult = true)]
+ [TestCase(typeof(System.Windows.Input.ICommand), typeof(BindingBase), ExpectedResult = false)]
+ [TestCase(typeof(BindingBase), typeof(BindingBase), ExpectedResult = true)]
+ [TestCase(typeof(BindingCondition), typeof(BindableObject), ExpectedResult = false)]
+ [TestCase(typeof(Button), typeof(BindableObject), ExpectedResult = true)]
+ [TestCase(typeof(Button), typeof(BindingBase), ExpectedResult = false)]
+ [TestCase(typeof(Button), typeof(View), ExpectedResult = true)]
+ [TestCase(typeof(Color), typeof(BindableObject), ExpectedResult = false)]
+ [TestCase(typeof(Color), typeof(BindingBase), ExpectedResult = false)]
+ [TestCase(typeof(Color), typeof(Color), ExpectedResult = true)]
+ [TestCase(typeof(ColumnDefinition), typeof(BindableObject), ExpectedResult = true)]
+ [TestCase(typeof(ColumnDefinition), typeof(BindingBase), ExpectedResult = false)]
+ [TestCase(typeof(ColumnDefinition), typeof(ColumnDefinitionCollection), ExpectedResult = false)]
+ [TestCase(typeof(Constraint), typeof(BindingBase), ExpectedResult = false)]
+ [TestCase(typeof(Constraint), typeof(Constraint), ExpectedResult = true)]
+ [TestCase(typeof(ConstraintExpression), typeof(BindableObject), ExpectedResult = false)]
+ [TestCase(typeof(ContentPage), typeof(BindableObject), ExpectedResult = true)]
+ [TestCase(typeof(ContentPage), typeof(Page), ExpectedResult = true)]
+ [TestCase(typeof(ContentView), typeof(BindableObject), ExpectedResult = true)]
+ [TestCase(typeof(ContentView[]), typeof(IList<ContentView>), ExpectedResult = true)]
+ [TestCase(typeof(MultiTrigger), typeof(IList<TriggerBase>), ExpectedResult = false)]
+ [TestCase(typeof(OnIdiom<double>), typeof(BindableObject), ExpectedResult = false)]
+ [TestCase(typeof(OnPlatform<string>), typeof(string), ExpectedResult = false)]
+ [TestCase(typeof(OnPlatform<string>), typeof(BindableObject), ExpectedResult = false)]
+ [TestCase(typeof(OnPlatform<string>), typeof(BindingBase), ExpectedResult = false)]
+ [TestCase(typeof(OnPlatform<FontAttributes>), typeof(BindableObject), ExpectedResult = false)]
+ [TestCase(typeof(StackLayout), typeof(Layout<View>), ExpectedResult = true)]
+ [TestCase(typeof(StackLayout), typeof(View), ExpectedResult = true)]
+ [TestCase(typeof(Foo<string>), typeof(Foo), ExpectedResult = true)]
+ [TestCase(typeof(Bar<string>), typeof(Foo), ExpectedResult = true)]
+ [TestCase(typeof(Bar<string>), typeof(Foo<string>), ExpectedResult = true)]
+ public bool TestInheritsFromOrImplements(Type typeRef, Type baseClass)
+ {
+ return TypeReferenceExtensions.InheritsFromOrImplements(module.Import(typeRef), module.Import(baseClass));
+ }
+
+ [Test]
+ public void TestSameTypeNamesFromDifferentAssemblies()
+ {
+ var core = typeof(BindableObject).Assembly;
+ var test = typeof(TypeReferenceExtensionsTests).Assembly;
+
+ Assert.False(TestInheritsFromOrImplements(test.GetType("Xamarin.Forms.Effect"), core.GetType("Xamarin.Forms.Effect")));
+ }
+ }
+}
\ No newline at end of file