Skip to content
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...
1 parent 06bdd59 commit 91b851d40bc3bd75e8a82508a7d7b09e33347822 @flaub committed Mar 27, 2010
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
+ }
}
}

0 comments on commit 91b851d

Please sign in to comment.
Something went wrong with that request. Please try again.