Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixing the type system so that it will correctly resolve virtual meth…

…od overrides for methods that derive from a generic type
  • Loading branch information...
commit 91b851d40bc3bd75e8a82508a7d7b09e33347822 1 parent 06bdd59
@flaub authored
View
1  src/DotWeb.Utility/Cecil/MethodReferenceExtenstions.cs
@@ -18,6 +18,7 @@
using System;
using Mono.Cecil;
using System.Text;
+using System.Linq;
namespace DotWeb.Utility.Cecil
{
View
120 src/DotWeb.Utility/Cecil/TypeSystem.cs
@@ -27,38 +27,73 @@
namespace DotWeb.Utility.Cecil
{
- class VirtualsDictionary
+ public class VirtualsDictionary
{
- private Dictionary<string, MethodDefinition> methods = new Dictionary<string, MethodDefinition>();
-
- public MethodDefinition FindMethodBySignature(MethodDefinition methodDef) {
- MethodDefinition virtualMethod = null;
- if (!this.methods.TryGetValue(methodDef.GetMethodSignature(), out virtualMethod)) {
- if (!this.methods.TryGetValue(methodDef.GetMethodSignatureWithTypeName(), out virtualMethod)) {
- //Debug.Fail("Missing base method for virtual");
- return null;
+ private HashSet<MethodDefinition> methods = new HashSet<MethodDefinition>();
+
+ public static MethodReference NormalizeMethod(GenericInstanceType declaringType, MethodReference methodRef) {
+ var typeDef = declaringType.Resolve();
+ var genericTypesByName = new Dictionary<string, TypeReference>();
+ for (int i = 0; i < declaringType.GenericArguments.Count; i++) {
+ var genericArgument = declaringType.GenericArguments[i];
+ var genericParameter = typeDef.GenericParameters[i];
+ genericTypesByName.Add(genericParameter.Name, genericArgument);
+ }
+
+ var returnType = methodRef.ReturnType.ReturnType;
+ if (returnType is GenericParameter) {
+ // try to resolve the type based on the context of the declaringType
+ returnType = genericTypesByName[returnType.Name];
+ }
+
+ var reference = new MethodReference(
+ methodRef.Name,
+ typeDef,
+ returnType,
+ methodRef.HasThis,
+ methodRef.ExplicitThis,
+ MethodCallingConvention.Generic);
+
+ foreach (ParameterDefinition parameter in methodRef.Parameters) {
+ var parameterType = parameter.ParameterType;
+ var genericParameter = parameterType as GenericParameter;
+ if (genericParameter != null) {
+ TypeReference found;
+ if (genericTypesByName.TryGetValue(genericParameter.Name, out found)) {
+ parameterType = found;
+ }
}
+ reference.Parameters.Add(new ParameterDefinition(parameterType));
}
- return virtualMethod;
+
+ return reference;
+ }
+
+ public MethodDefinition FindMethodBySignature(TypeReference typeRef, MethodReference methodToFind) {
+ var genericInstanceType = typeRef as GenericInstanceType;
+ if (genericInstanceType != null) {
+ methodToFind = NormalizeMethod(genericInstanceType, methodToFind);
+ }
+
+ var methodToFindSig = methodToFind.GetMethodSignature();
+ var methodToFindSigWithTypeName = methodToFind.GetMethodSignatureWithTypeName();
+
+ foreach (var virtualMethod in this.methods) {
+ var sig = virtualMethod.GetMethodSignature();
+ if (sig == methodToFindSig ||
+ sig == methodToFindSigWithTypeName) {
+ return virtualMethod;
+ }
+ }
+
+ return null;
}
public void CollectVirtualMethods(TypeDefinition typeDef) {
foreach (MethodDefinition method in typeDef.Methods) {
if (method.IsVirtual && !method.IsAbstract) {
- // use raw method signature for key, to make sure it's unique in the case of
- // overriden methods with the same method name, which can happen if the 'new'
- // keyword is used on a method definition
- this.methods.Add(method.GetMethodSignature(), method);
+ this.methods.Add(method);
}
-
- //if (method.HasOverrides) {
- // Debug.Assert(method.IsVirtual);
- // foreach (MethodReference overrideRef in method.Overrides) {
- // var overrideDef = overrideRef.Resolve();
- // var signature = overrideDef.GetMethodSignature();
- // virtuals.Add(signature, overrideDef);
- // }
- //}
}
}
@@ -78,7 +113,7 @@ public static class Names
private IAssemblyResolver asmResolver;
private Dictionary<TypeDefinition, TypeSet> baseToDerviedMap = new Dictionary<TypeDefinition, TypeSet>();
private Dictionary<MethodDefinition, MethodSet> virtualMethodOverrides = new Dictionary<MethodDefinition, MethodSet>();
- private List<AssemblyDefinition> assemblies = new List<AssemblyDefinition>();
+ private HashSet<AssemblyDefinition> assemblies = new HashSet<AssemblyDefinition>();
private AssemblyDefinition asmSystem;
public TypeSystem(IAssemblyResolver resolver) {
@@ -103,7 +138,7 @@ public static class Names
LoadAssembly(child.Name.FullName);
}
- this.assemblies.AddUnique(asmDef);
+ this.assemblies.Add(asmDef);
return asmDef;
}
@@ -139,11 +174,12 @@ public static class Names
return result;
}
- private void ProcessMethodOverrides(VirtualsDictionary virtuals, TypeDefinition baseDef) {
+ private void ProcessMethodOverrides(VirtualsDictionary virtuals, TypeReference baseRef) {
if (virtuals.Count > 0) {
+ var baseDef = baseRef.Resolve();
foreach (MethodDefinition baseMethod in baseDef.Methods) {
if (baseMethod.IsVirtual) {
- var overridenMethod = virtuals.FindMethodBySignature(baseMethod);
+ var overridenMethod = virtuals.FindMethodBySignature(baseRef, baseMethod);
if (overridenMethod != null) {
var overrides = GetOverridesForVirtualMethod(baseMethod);
overrides.Add(overridenMethod);
@@ -153,38 +189,34 @@ public static class Names
}
}
+ private void ProcessInterfaces(VirtualsDictionary virtuals, TypeReference typeRef) {
+ var typeDef = typeRef.Resolve();
+ foreach (TypeReference iface in typeDef.Interfaces) {
+ var ifaceDef = iface.Resolve();
+ var ifaceSet = GetSubclasses(ifaceDef);
+ ifaceSet.Add(typeDef);
+ ProcessMethodOverrides(virtuals, iface);
+ }
+ }
+
private void ProcessType(TypeDefinition typeDef) {
var virtuals = new VirtualsDictionary();
virtuals.CollectVirtualMethods(typeDef);
var baseType = typeDef.BaseType;
while (baseType != null) {
- var baseDef = baseType.Resolve();
var baseSet = GetSubclasses(baseType);
baseSet.Add(typeDef);
- ProcessMethodOverrides(virtuals, baseDef);
+ ProcessMethodOverrides(virtuals, baseType);
- ProcessInterfaces(virtuals, baseDef);
+ ProcessInterfaces(virtuals, baseType);
+ var baseDef = baseType.Resolve();
baseType = baseDef.BaseType;
}
ProcessInterfaces(virtuals, typeDef);
-
- foreach (TypeDefinition nested in typeDef.NestedTypes) {
- ProcessType(nested);
- }
- }
-
- private void ProcessInterfaces(VirtualsDictionary virtuals, TypeDefinition typeDef) {
- foreach (TypeReference iface in typeDef.Interfaces) {
- var ifaceDef = iface.Resolve();
- var ifaceSet = GetSubclasses(ifaceDef);
- ifaceSet.Add(typeDef);
-
- ProcessMethodOverrides(virtuals, ifaceDef);
- }
}
public TypeDefinition GetTypeDefinition(string typeName) {
View
6 test/DotWeb.Functional.Test/Client/DictionaryTest.cs
@@ -27,10 +27,14 @@ class DictionaryTest : JsScript
this.view.AreStringsEqual("empty", "{}", () => dict);
this.view.AreEqual("dict.Count == 0", 0, () => dict.Count);
- this.view.AreStringsEqual("dict.Add(\"key\", \"value\")", "", () => {
+ this.view.AreStringsEqual("dict.Add(\"key\", \"value\")", "{key: value}", () => {
dict.Add("key", "value");
return dict;
});
+ this.view.AreStringsEqual("dict.Add(\"other\", \"other\")", "{key: value, other: other}", () => {
+ dict.Add("other", "other");
+ return dict;
+ });
}
}
}
View
4 test/DotWeb.Translator.Test/DotWeb.Translator.Test.csproj
@@ -31,6 +31,10 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
+ <Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\Depends\log4net.dll</HintPath>
+ </Reference>
<Reference Include="Mono.Cecil, Version=0.6.9.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\Depends\Mono.Cecil.dll</HintPath>
View
110 test/DotWeb.Translator.Test/TypeSystemTest.cs
@@ -20,6 +20,7 @@
using Mono.Cecil;
using DotWeb.Utility.Cecil;
using System.Linq;
+using System.Collections.Generic;
namespace DotWeb.Translator.Test
{
@@ -129,8 +130,117 @@ public class TypeSystemTest
Assert.IsTrue(overrides.Contains(overridenMethod2));
}
+ [Test]
+ public void TestGetCurrent() {
+ var typeSystem = new TypeSystem(this.resolver);
+ var sysDef = typeSystem.LoadAssembly("DotWeb.System");
+
+ // System.Collections.Generic.IEnumerator<KeyValuePair<TKey, TValue>>
+ var iface = sysDef.MainModule.Types["System.Collections.Generic.IEnumerator`1"];
+ // System.Collections.Generic.Dictionary<TKey, TValue>.Enumerator
+ var impl = sysDef.MainModule.Types["System.Collections.Generic.Dictionary`2/Enumerator"];
+
+ var ifaceMethod = FindMethodByName(iface.Methods, "get_Current");
+ var overrides = typeSystem.GetOverridesForVirtualMethod(ifaceMethod);
+ Assert.Greater(overrides.Count, 0);
+
+ var overridenMethod = FindMethodByName(impl.Methods, "get_Current");
+ Assert.IsTrue(overrides.Contains(overridenMethod));
+ }
+
private MethodDefinition FindMethodByName(MethodDefinitionCollection methods, string name) {
return methods.Cast<MethodDefinition>().Where(x => x.Name == name).SingleOrDefault();
}
+
+ [Test]
+ public void TestVirtualsMapper() {
+ var asm = this.resolver.Resolve("DotWeb.Translator.Test");
+
+ var genericInterface = asm.MainModule.Types["DotWeb.Translator.Test.TypeSystemTest/GenericInterface`2"];
+ var genericImpl = asm.MainModule.Types["DotWeb.Translator.Test.TypeSystemTest/GenericImpl`2"];
+ var partialImpl1 = asm.MainModule.Types["DotWeb.Translator.Test.TypeSystemTest/PartialImpl1`1"];
+ var partialImpl2 = asm.MainModule.Types["DotWeb.Translator.Test.TypeSystemTest/PartialImpl2`1"];
+ var concreteImpl = asm.MainModule.Types["DotWeb.Translator.Test.TypeSystemTest/ConcreteImpl"];
+
+ var baseMethod = genericInterface.Methods.GetMethod("Method").First();
+ var genericMethod = genericImpl.Methods.GetMethod("Method").First();
+ var partial1Method = partialImpl1.Methods.GetMethod("Method").First();
+ var partial2Method = partialImpl2.Methods.GetMethod("Method").First();
+ var concreteMethod = concreteImpl.Methods.GetMethod("Method").First();
+
+ var genericIface = genericImpl.Interfaces[0] as GenericInstanceType;
+ var partial1Iface = partialImpl1.Interfaces[0] as GenericInstanceType;
+ var partial2Iface = partialImpl2.Interfaces[0] as GenericInstanceType;
+ var concreteIface = concreteImpl.Interfaces[0] as GenericInstanceType;
+
+ var a1 = genericIface.GenericArguments[0];
+ var a2 = genericIface.GenericArguments[1];
+
+ var b1 = partial1Iface.GenericArguments[0];
+ var b2 = partial1Iface.GenericArguments[1];
+
+ var c1 = partial2Iface.GenericArguments[0];
+ var c2 = partial2Iface.GenericArguments[1];
+
+ var d1 = concreteIface.GenericArguments[0];
+ var d2 = concreteIface.GenericArguments[1];
+
+ var x1 = genericInterface.GenericParameters[0];
+ var x2 = genericInterface.GenericParameters[1];
+
+ Assert.AreEqual(concreteMethod.GetMethodSignature(), VirtualsDictionary.NormalizeMethod(concreteIface, baseMethod).GetMethodSignature());
+ Assert.AreEqual(partial1Method.GetMethodSignature(), VirtualsDictionary.NormalizeMethod(partial1Iface, baseMethod).GetMethodSignature());
+ Assert.AreEqual(partial2Method.GetMethodSignature(), VirtualsDictionary.NormalizeMethod(partial2Iface, baseMethod).GetMethodSignature());
+ Assert.AreEqual(genericMethod.GetMethodSignature(), VirtualsDictionary.NormalizeMethod(genericIface, baseMethod).GetMethodSignature());
+ }
+
+ interface GenericInterface<T, U>
+ {
+ void Method<X>(X x, T t, U u);
+ }
+
+ public class GenericImpl<T, U> : GenericInterface<T, U>
+ {
+ #region GenericInterface<T,U> Members
+
+ public void Method<X>(X x, T t, U u) {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+
+ public class PartialImpl1<T> : GenericInterface<T, string>
+ {
+ #region GenericInterface<T,string> Members
+
+ public void Method<X>(X x, T t, string u) {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+
+ public class PartialImpl2<T> : GenericInterface<string, T>
+ {
+ #region GenericInterface<string,T> Members
+
+ public void Method<X>(X x, string t, T u) {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+
+ public class ConcreteImpl : GenericInterface<string, string>
+ {
+ #region GenericInterface<string,string> Members
+
+ public void Method<X>(X x, string t, string u) {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.