diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index d46a2032..4a828d1f 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -159,6 +159,7 @@ + diff --git a/de4dot.code/renamer/Renamer.cs b/de4dot.code/renamer/Renamer.cs index 3b2db69a..71e60b25 100644 --- a/de4dot.code/renamer/Renamer.cs +++ b/de4dot.code/renamer/Renamer.cs @@ -40,6 +40,8 @@ namespace de4dot.renamer { Log.n("Renaming all obfuscated symbols"); modules.initialize(); + modules.initializeVirtualMembers(); + modules.cleanUp(); } } } diff --git a/de4dot.code/renamer/asmmodules/EventDef.cs b/de4dot.code/renamer/asmmodules/EventDef.cs index 002b7058..fcae2f11 100644 --- a/de4dot.code/renamer/asmmodules/EventDef.cs +++ b/de4dot.code/renamer/asmmodules/EventDef.cs @@ -42,5 +42,13 @@ namespace de4dot.renamer.asmmodules { yield return m; } } + + public bool isVirtual() { + foreach (var method in methodDefinitions()) { + if (method.IsVirtual) + return true; + } + return false; + } } } diff --git a/de4dot.code/renamer/asmmodules/ExternalAssemblies.cs b/de4dot.code/renamer/asmmodules/ExternalAssemblies.cs index 0a8b75da..83f0d38e 100644 --- a/de4dot.code/renamer/asmmodules/ExternalAssemblies.cs +++ b/de4dot.code/renamer/asmmodules/ExternalAssemblies.cs @@ -39,9 +39,12 @@ namespace de4dot.renamer.asmmodules { return null; } - public void unload() { - foreach (var module in asmDef.Modules) + public void unload(string asmFullName) { + foreach (var module in asmDef.Modules) { DotNetUtils.typeCaches.invalidate(module); + AssemblyResolver.Instance.removeModule(module); + } + AssemblyResolver.Instance.removeModule(asmFullName); } } @@ -50,7 +53,7 @@ namespace de4dot.renamer.asmmodules { Dictionary assemblies = new Dictionary(); ExternalAssembly load(TypeReference type) { - var asmFullName = DotNetUtils.getFullAssemblyName(type.Scope); + var asmFullName = DotNetUtils.getFullAssemblyName(type); ExternalAssembly asm; if (assemblies.TryGetValue(asmFullName, out asm)) return asm; @@ -93,10 +96,10 @@ namespace de4dot.renamer.asmmodules { } public void unloadAll() { - foreach (var asm in assemblies.Values) { - if (asm == null) + foreach (var pair in assemblies) { + if (pair.Value == null) continue; - asm.unload(); + pair.Value.unload(pair.Key); } assemblies.Clear(); } diff --git a/de4dot.code/renamer/asmmodules/MethodDef.cs b/de4dot.code/renamer/asmmodules/MethodDef.cs index 0a777ca9..8c396d57 100644 --- a/de4dot.code/renamer/asmmodules/MethodDef.cs +++ b/de4dot.code/renamer/asmmodules/MethodDef.cs @@ -28,5 +28,17 @@ namespace de4dot.renamer.asmmodules { public MethodDef(MethodDefinition methodDefinition, TypeDef owner, int index) : base(methodDefinition, owner, index) { } + + public bool isPublic() { + return MethodDefinition.IsPublic; + } + + public bool isVirtual() { + return MethodDefinition.IsVirtual; + } + + public bool isNewSlot() { + return MethodDefinition.IsNewSlot; + } } } diff --git a/de4dot.code/renamer/asmmodules/MethodNameScopes.cs b/de4dot.code/renamer/asmmodules/MethodNameScopes.cs new file mode 100644 index 00000000..4e190689 --- /dev/null +++ b/de4dot.code/renamer/asmmodules/MethodNameScopes.cs @@ -0,0 +1,82 @@ +/* + Copyright (C) 2011 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; +using System.Collections.Generic; + +namespace de4dot.renamer.asmmodules { + class MethodNameScope { + List methods = new List(); + + public List Methods { + get { return methods; } + } + + public int Count { + get { return methods.Count; } + } + + public void add(MethodDef method) { + methods.Add(method); + } + + public void merge(MethodNameScope other) { + if (this == other) + return; + methods.AddRange(other.methods); + } + } + + class MethodNameScopes { + Dictionary methodScopes = new Dictionary(); + + public void same(MethodDef a, MethodDef b) { + merge(get(a), get(b)); + } + + public void add(MethodDef methodDef) { + get(methodDef); + } + + MethodNameScope get(MethodDef method) { + if (!method.isVirtual()) + throw new ApplicationException("Not a virtual method"); + MethodNameScope scope; + if (!methodScopes.TryGetValue(method, out scope)) { + methodScopes[method] = scope = new MethodNameScope(); + scope.add(method); + } + return scope; + } + + void merge(MethodNameScope a, MethodNameScope b) { + if (a == b) + return; + + if (a.Count < b.Count) { + MethodNameScope tmp = a; + a = b; + b = tmp; + } + a.merge(b); + foreach (var methodDef in b.Methods) + methodScopes[methodDef] = a; + } + } +} diff --git a/de4dot.code/renamer/asmmodules/Modules.cs b/de4dot.code/renamer/asmmodules/Modules.cs index 64e65c5c..934e6aa3 100644 --- a/de4dot.code/renamer/asmmodules/Modules.cs +++ b/de4dot.code/renamer/asmmodules/Modules.cs @@ -29,7 +29,7 @@ namespace de4dot.renamer.asmmodules { Dictionary modulesDict = new Dictionary(); AssemblyHash assemblyHash = new AssemblyHash(); - List allTypes = new List(); //TODO: Do we need this? + List allTypes = new List(); List baseTypes = new List(); //TODO: Do we need this? List nonNestedTypes; //TODO: Do we need this? @@ -240,6 +240,18 @@ namespace de4dot.renamer.asmmodules { return otherTypesDict[key] = typeDef; } + public void initializeVirtualMembers() { + var scopes = new MethodNameScopes(); + foreach (var typeDef in allTypes) + typeDef.initializeVirtualMembers(scopes, this); + } + + public void cleanUp() { + externalAssemblies.unloadAll(); + foreach (var module in DotNetUtils.typeCaches.invalidateAll()) + AssemblyResolver.Instance.removeModule(module); + } + // Returns null if it's a non-loaded module/assembly IEnumerable findModules(TypeReference type) { var scope = type.Scope; diff --git a/de4dot.code/renamer/asmmodules/PropertyDef.cs b/de4dot.code/renamer/asmmodules/PropertyDef.cs index a4592127..446203e0 100644 --- a/de4dot.code/renamer/asmmodules/PropertyDef.cs +++ b/de4dot.code/renamer/asmmodules/PropertyDef.cs @@ -40,5 +40,13 @@ namespace de4dot.renamer.asmmodules { yield return m; } } + + public bool isVirtual() { + foreach (var method in methodDefinitions()) { + if (method.IsVirtual) + return true; + } + return false; + } } } diff --git a/de4dot.code/renamer/asmmodules/TypeDef.cs b/de4dot.code/renamer/asmmodules/TypeDef.cs index b6380c55..e67d8294 100644 --- a/de4dot.code/renamer/asmmodules/TypeDef.cs +++ b/de4dot.code/renamer/asmmodules/TypeDef.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Generic; using Mono.Cecil; +using de4dot.blocks; namespace de4dot.renamer.asmmodules { class TypeInfo { @@ -29,6 +30,172 @@ namespace de4dot.renamer.asmmodules { this.typeReference = typeReference; this.typeDef = typeDef; } + + public TypeInfo(TypeInfo other, GenericInstanceType git) { + this.typeReference = TypeReferenceInstance.make(other.typeReference, git); + this.typeDef = other.typeDef; + } + + public override int GetHashCode() { + return typeDef.GetHashCode() + + MemberReferenceHelper.typeHashCode(typeReference); + } + + public override bool Equals(object obj) { + var other = obj as TypeInfo; + if (other == null) + return false; + return typeDef == other.typeDef && + MemberReferenceHelper.compareTypes(typeReference, other.typeReference); + } + + public override string ToString() { + return typeReference.ToString(); + } + } + + class MethodInst { + public MethodDef origMethodDef; + public MethodReference methodReference; + + public MethodInst(MethodDef origMethodDef, MethodReference methodReference) { + this.origMethodDef = origMethodDef; + this.methodReference = methodReference; + } + + public override string ToString() { + return methodReference.ToString(); + } + } + + class MethodInstances { + Dictionary> methodInstances = new Dictionary>(); + + public void initializeFrom(MethodInstances other, GenericInstanceType git) { + foreach (var list in other.methodInstances.Values) { + foreach (var methodInst in list) { + MethodReference newMethod = MethodReferenceInstance.make(methodInst.methodReference, git); + add(new MethodInst(methodInst.origMethodDef, newMethod)); + } + } + } + + public void add(MethodInst methodInst) { + List list; + var key = new MethodReferenceKey(methodInst.methodReference); + if (methodInst.origMethodDef.isNewSlot() || !methodInstances.TryGetValue(key, out list)) + methodInstances[key] = list = new List(); + list.Add(methodInst); + } + + public List lookup(MethodReference methodReference) { + List list; + methodInstances.TryGetValue(new MethodReferenceKey(methodReference), out list); + return list; + } + + public IEnumerable> getMethods() { + return methodInstances.Values; + } + } + + // Keeps track which methods of an interface have been implemented + class InterfaceMethodInfo { + TypeInfo iface; + Dictionary ifaceMethodToClassMethod = new Dictionary(); + + public TypeInfo IFace { + get { return iface; } + } + + public Dictionary IfaceMethodToClassMethod { + get { return ifaceMethodToClassMethod; } + } + + public InterfaceMethodInfo(TypeInfo iface) { + this.iface = iface; + foreach (var methodDef in iface.typeDef.getAllMethods()) + ifaceMethodToClassMethod[methodDef] = null; + } + + public InterfaceMethodInfo(TypeInfo iface, InterfaceMethodInfo other) { + this.iface = iface; + foreach (var key in other.ifaceMethodToClassMethod.Keys) + ifaceMethodToClassMethod[key] = other.ifaceMethodToClassMethod[key]; + } + + public void merge(InterfaceMethodInfo other) { + foreach (var key in other.ifaceMethodToClassMethod.Keys) { + if (other.ifaceMethodToClassMethod[key] == null) + continue; + if (ifaceMethodToClassMethod[key] != null) + throw new ApplicationException("Interface method already initialized"); + ifaceMethodToClassMethod[key] = other.ifaceMethodToClassMethod[key]; + } + } + + public void addMethod(MethodDef ifaceMethod, MethodDef classMethod) { + if (!ifaceMethodToClassMethod.ContainsKey(ifaceMethod)) + throw new ApplicationException("Could not find interface method"); + ifaceMethodToClassMethod[ifaceMethod] = classMethod; + } + + public void addMethodIfEmpty(MethodDef ifaceMethod, MethodDef classMethod) { + if (ifaceMethodToClassMethod[ifaceMethod] == null) + ifaceMethodToClassMethod[ifaceMethod] = classMethod; + } + + public override string ToString() { + return iface.ToString(); + } + } + + class InterfaceMethodInfos { + Dictionary interfaceMethods = new Dictionary(); + + public IEnumerable AllInfos { + get { return interfaceMethods.Values; } + } + + public void initializeFrom(InterfaceMethodInfos other, GenericInstanceType git) { + foreach (var pair in other.interfaceMethods) { + var oldTypeInfo = pair.Value.IFace; + var newTypeInfo = new TypeInfo(oldTypeInfo, git); + var oldKey = new TypeReferenceKey(oldTypeInfo.typeReference); + var newKey = new TypeReferenceKey(newTypeInfo.typeReference); + + InterfaceMethodInfo newMethodsInfo = new InterfaceMethodInfo(newTypeInfo, other.interfaceMethods[oldKey]); + if (interfaceMethods.ContainsKey(newKey)) + newMethodsInfo.merge(interfaceMethods[newKey]); + interfaceMethods[newKey] = newMethodsInfo; + } + } + + public void addInterface(TypeInfo iface) { + var key = new TypeReferenceKey(iface.typeReference); + if (!interfaceMethods.ContainsKey(key)) + interfaceMethods[key] = new InterfaceMethodInfo(iface); + } + + public void addMethod(TypeInfo iface, MethodDef ifaceMethod, MethodDef classMethod) { + addMethod(iface.typeReference, ifaceMethod, classMethod); + } + + public void addMethod(TypeReference iface, MethodDef ifaceMethod, MethodDef classMethod) { + InterfaceMethodInfo info; + var key = new TypeReferenceKey(iface); + if (!interfaceMethods.TryGetValue(key, out info)) + throw new ApplicationException("Could not find interface"); + info.addMethod(ifaceMethod, classMethod); + } + + public void addMethodIfEmpty(TypeInfo iface, MethodDef ifaceMethod, MethodDef classMethod) { + InterfaceMethodInfo info; + var key = new TypeReferenceKey(iface.typeReference); + if (!interfaceMethods.TryGetValue(key, out info)) + throw new ApplicationException("Could not find interface"); + info.addMethodIfEmpty(ifaceMethod, classMethod); + } } class TypeDef : Ref { @@ -42,6 +209,11 @@ namespace de4dot.renamer.asmmodules { internal IList derivedTypes = new List(); Module module; + bool initializeVirtualMembersCalled = false; + MethodInstances virtualMethodInstances = new MethodInstances(); + Dictionary allImplementedInterfaces = new Dictionary(); + InterfaceMethodInfos interfaceMethodInfos = new InterfaceMethodInfos(); + public bool HasModule { get { return module != null; } } @@ -101,6 +273,10 @@ namespace de4dot.renamer.asmmodules { return fields.find(fr); } + public IEnumerable getAllMethods() { + return methods.getAll(); + } + public void addMembers() { var type = TypeDefinition; @@ -113,5 +289,273 @@ namespace de4dot.renamer.asmmodules { for (int i = 0; i < type.Properties.Count; i++) add(new PropertyDef(type.Properties[i], this, i)); } + + public void initializeVirtualMembers(MethodNameScopes scopes, IResolver resolver) { + if (initializeVirtualMembersCalled) + return; + initializeVirtualMembersCalled = true; + + foreach (var iface in interfaces) + iface.typeDef.initializeVirtualMembers(scopes, resolver); + if (baseType != null) + baseType.typeDef.initializeVirtualMembers(scopes, resolver); + + foreach (var methodDef in methods.getAll()) { + if (methodDef.isVirtual()) + scopes.add(methodDef); + } + + instantiateVirtualMembers(scopes); + initializeInterfaceMethods(scopes); + } + + void initializeAllInterfaces() { + if (baseType != null) + initializeInterfaces(baseType); + + foreach (var iface in interfaces) { + allImplementedInterfaces[iface] = true; + interfaceMethodInfos.addInterface(iface); + initializeInterfaces(iface); + } + } + + void initializeInterfaces(TypeInfo typeInfo) { + var git = typeInfo.typeReference as GenericInstanceType; + interfaceMethodInfos.initializeFrom(typeInfo.typeDef.interfaceMethodInfos, git); + foreach (var info in typeInfo.typeDef.allImplementedInterfaces.Keys) { + var newTypeInfo = new TypeInfo(info, git); + allImplementedInterfaces[newTypeInfo] = true; + } + } + + void initializeInterfaceMethods(MethodNameScopes scopes) { + initializeAllInterfaces(); + + if (TypeDefinition.IsInterface) + return; + + //--- Partition II 12.2 Implementing virtual methods on interfaces: + //--- The VES shall use the following algorithm to determine the appropriate + //--- implementation of an interface's virtual abstract methods: + //--- + //--- * If the base class implements the interface, start with the same virtual methods + //--- that it provides; otherwise, create an interface that has empty slots for all + //--- virtual functions. + // Done. See initializeAllInterfaces(). + + var methodsDict = new Dictionary(); + + //--- * If this class explicitly specifies that it implements the interface (i.e., the + //--- interfaces that appear in this class‘ InterfaceImpl table, §22.23) + //--- * If the class defines any public virtual newslot methods whose name and + //--- signature match a virtual method on the interface, then use these new virtual + //--- methods to implement the corresponding interface method. + if (interfaces.Count > 0) { + methodsDict.Clear(); + foreach (var method in methods.getAll()) { + if (!method.isPublic() || !method.isVirtual() || !method.isNewSlot()) + continue; + methodsDict[new MethodReferenceKey(method.MethodDefinition)] = method; + } + + foreach (var ifaceInfo in interfaces) { + foreach (var methodsList in ifaceInfo.typeDef.virtualMethodInstances.getMethods()) { + if (methodsList.Count != 1) // Never happens + throw new ApplicationException("Interface with more than one method in the list"); + var methodInst = methodsList[0]; + var ifaceMethod = methodInst.origMethodDef; + if (!ifaceMethod.isVirtual()) + continue; + var ifaceMethodReference = MethodReferenceInstance.make(methodInst.methodReference, ifaceInfo.typeReference as GenericInstanceType); + MethodDef classMethod; + var key = new MethodReferenceKey(ifaceMethodReference); + if (!methodsDict.TryGetValue(key, out classMethod)) + continue; + interfaceMethodInfos.addMethod(ifaceInfo, ifaceMethod, classMethod); + } + } + } + + //--- * If there are any virtual methods in the interface that still have empty slots, + //--- see if there are any public virtual methods, but not public virtual newslot + //--- methods, available on this class (directly or inherited) having the same name + //--- and signature, then use these to implement the corresponding methods on the + //--- interface. + methodsDict.Clear(); + foreach (var methodInstList in virtualMethodInstances.getMethods()) { + // This class' method is at the end + for (int i = methodInstList.Count - 1; i >= 0; i--) { + var classMethod = methodInstList[i]; + // These methods are guaranteed to be virtual. + // We should allow newslot methods, despite what the official doc says. + if (!classMethod.origMethodDef.isPublic()) + continue; + methodsDict[new MethodReferenceKey(classMethod.methodReference)] = classMethod.origMethodDef; + break; + } + } + foreach (var ifaceInfo in allImplementedInterfaces.Keys) { + foreach (var methodsList in ifaceInfo.typeDef.virtualMethodInstances.getMethods()) { + if (methodsList.Count != 1) // Never happens + throw new ApplicationException("Interface with more than one method in the list"); + var ifaceMethod = methodsList[0].origMethodDef; + if (!ifaceMethod.isVirtual()) + continue; + var ifaceMethodRef = MethodReferenceInstance.make(ifaceMethod.MethodDefinition, ifaceInfo.typeReference as GenericInstanceType); + MethodDef classMethod; + var key = new MethodReferenceKey(ifaceMethodRef); + if (!methodsDict.TryGetValue(key, out classMethod)) + continue; + interfaceMethodInfos.addMethodIfEmpty(ifaceInfo, ifaceMethod, classMethod); + } + } + + //--- * Apply all MethodImpls that are specified for this class, thereby placing + //--- explicitly specified virtual methods into the interface in preference to those + //--- inherited or chosen by name matching. + methodsDict.Clear(); + var ifaceMethodsDict = new Dictionary(); + var overrideMethods = new Dictionary(); + foreach (var ifaceInfo in allImplementedInterfaces.Keys) { + var git = ifaceInfo.typeReference as GenericInstanceType; + foreach (var ifaceMethod in ifaceInfo.typeDef.methods.getAll()) { + MethodReference ifaceMethodReference = ifaceMethod.MethodDefinition; + if (git != null) + ifaceMethodReference = simpleClone(ifaceMethod.MethodDefinition, git); + ifaceMethodsDict[new MethodReferenceAndDeclaringTypeKey(ifaceMethodReference)] = ifaceMethod; + } + } + foreach (var classMethod in methods.getAll()) { + if (!classMethod.isVirtual()) + continue; + foreach (var overrideMethod in classMethod.MethodDefinition.Overrides) { + MethodDef ifaceMethod; + var key = new MethodReferenceAndDeclaringTypeKey(overrideMethod); + if (!ifaceMethodsDict.TryGetValue(key, out ifaceMethod)) { + // We couldn't find the interface method (eg. interface not resolved) or + // it overrides a base class method, and not an interface method. + continue; + } + + interfaceMethodInfos.addMethod(overrideMethod.DeclaringType, ifaceMethod, classMethod); + overrideMethods[classMethod] = true; + } + } + + //--- * If the current class is not abstract and there are any interface methods that + //--- still have empty slots, then the program is invalid. + // Check it anyway. C# requires a method, even if it's abstract. I don't think anyone + // writes pure CIL assemblies. + foreach (var info in interfaceMethodInfos.AllInfos) { + foreach (var pair in info.IfaceMethodToClassMethod) { + if (pair.Value != null) + continue; + if (!resolvedAllInterfaces() || !resolvedBaseClasses()) + continue; + string errMsg = string.Format( + "Could not find interface method {0} ({1:X8}). Type: {2} ({3:X8})", + pair.Key.MethodDefinition, + pair.Key.MethodDefinition.MetadataToken.ToInt32(), + TypeDefinition, + TypeDefinition.MetadataToken.ToInt32()); + // Ignore if COM class + if (!hasAttribute("System.Runtime.InteropServices.TypeLibTypeAttribute")) + throw new ApplicationException(errMsg); + Log.e("{0}", errMsg); + } + } + + foreach (var info in interfaceMethodInfos.AllInfos) { + foreach (var pair in info.IfaceMethodToClassMethod) { + if (pair.Value == null) + continue; + if (overrideMethods.ContainsKey(pair.Value)) + continue; + scopes.same(pair.Key, pair.Value); + } + } + } + + bool hasAttribute(string name) { + foreach (var attr in TypeDefinition.CustomAttributes) { + if (attr.AttributeType.FullName == name) + return true; + } + return false; + } + + // Returns true if all interfaces have been resolved + bool? resolvedAllInterfacesResult; + bool resolvedAllInterfaces() { + if (!resolvedAllInterfacesResult.HasValue) { + resolvedAllInterfacesResult = true; // If we find a circular reference + resolvedAllInterfacesResult = resolvedAllInterfacesInternal(); + } + return resolvedAllInterfacesResult.Value; + } + bool resolvedAllInterfacesInternal() { + if (TypeDefinition.Interfaces.Count != interfaces.Count) + return false; + foreach (var ifaceInfo in interfaces) { + if (!ifaceInfo.typeDef.resolvedAllInterfaces()) + return false; + } + return true; + } + + // Returns true if all base classes have been resolved + bool? resolvedBaseClassesResult; + bool resolvedBaseClasses() { + if (!resolvedBaseClassesResult.HasValue) { + resolvedBaseClassesResult = true; // If we find a circular reference + resolvedBaseClassesResult = resolvedBaseClassesInternal(); + } + return resolvedBaseClassesResult.Value; + } + bool resolvedBaseClassesInternal() { + if (TypeDefinition.BaseType == null) + return true; + if (baseType == null) + return false; + return baseType.typeDef.resolvedBaseClasses(); + } + + MethodReference simpleClone(MethodReference methodReference, TypeReference declaringType) { + var m = new MethodReference(methodReference.Name, methodReference.MethodReturnType.ReturnType, declaringType); + m.MethodReturnType.ReturnType = methodReference.MethodReturnType.ReturnType; + m.HasThis = methodReference.HasThis; + m.ExplicitThis = methodReference.ExplicitThis; + m.CallingConvention = methodReference.CallingConvention; + foreach (var p in methodReference.Parameters) + m.Parameters.Add(new ParameterDefinition(p.Name, p.Attributes, p.ParameterType)); + foreach (var gp in methodReference.GenericParameters) + m.GenericParameters.Add(new GenericParameter(declaringType)); + return m; + } + + void instantiateVirtualMembers(MethodNameScopes scopes) { + if (!TypeDefinition.IsInterface) { + if (baseType != null) + virtualMethodInstances.initializeFrom(baseType.typeDef.virtualMethodInstances, baseType.typeReference as GenericInstanceType); + + // Figure out which methods we override in the base class + foreach (var methodDef in methods.getAll()) { + if (!methodDef.isVirtual() || methodDef.isNewSlot()) + continue; + var methodInstList = virtualMethodInstances.lookup(methodDef.MethodDefinition); + if (methodInstList == null) + continue; + foreach (var methodInst in methodInstList) + scopes.same(methodDef, methodInst.origMethodDef); + } + } + + foreach (var methodDef in methods.getAll()) { + if (!methodDef.isVirtual()) + continue; + virtualMethodInstances.add(new MethodInst(methodDef, methodDef.MethodDefinition)); + } + } } }