mirror of https://github.com/icsharpcode/ILSpy.git
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							465 lines
						
					
					
						
							17 KiB
						
					
					
				
			
		
		
	
	
							465 lines
						
					
					
						
							17 KiB
						
					
					
				using System; | 
						|
using System.Collections.Generic; | 
						|
using System.Linq; | 
						|
using ICSharpCode.Decompiler.TypeSystem.Implementation; | 
						|
using ICSharpCode.Decompiler.Util; | 
						|
using Mono.Cecil; | 
						|
 | 
						|
namespace ICSharpCode.Decompiler.TypeSystem | 
						|
{ | 
						|
	/// <summary> | 
						|
	/// Manages the NRefactory type system for the decompiler. | 
						|
	/// </summary> | 
						|
	/// <remarks> | 
						|
	/// This class is thread-safe. | 
						|
	/// </remarks> | 
						|
	public class DecompilerTypeSystem : IDecompilerTypeSystem | 
						|
	{ | 
						|
		readonly ModuleDefinition moduleDefinition; | 
						|
		readonly ICompilation compilation; | 
						|
		readonly ITypeResolveContext context; | 
						|
 | 
						|
		/// <summary> | 
						|
		/// CecilLoader used for converting cecil type references to ITypeReference. | 
						|
		/// May only be accessed within lock(typeReferenceCecilLoader). | 
						|
		/// </summary> | 
						|
		readonly CecilLoader typeReferenceCecilLoader; | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Dictionary for NRefactory->Cecil lookup. | 
						|
		/// May only be accessed within lock(entityDict) | 
						|
		/// </summary> | 
						|
		Dictionary<IUnresolvedEntity, MemberReference> entityDict = new Dictionary<IUnresolvedEntity, MemberReference>(); | 
						|
 | 
						|
		Dictionary<FieldReference, IField> fieldLookupCache = new Dictionary<FieldReference, IField>(); | 
						|
		Dictionary<PropertyReference, IProperty> propertyLookupCache = new Dictionary<PropertyReference, IProperty>(); | 
						|
		Dictionary<MethodReference, IMethod> methodLookupCache = new Dictionary<MethodReference, IMethod>(); | 
						|
		Dictionary<EventReference, IEvent> eventLookupCache = new Dictionary<EventReference, IEvent>(); | 
						|
 | 
						|
		public DecompilerTypeSystem(ModuleDefinition moduleDefinition) : this(moduleDefinition, new DecompilerSettings()) | 
						|
		{ | 
						|
		} | 
						|
 | 
						|
		public DecompilerTypeSystem(ModuleDefinition moduleDefinition, DecompilerSettings settings) | 
						|
		{ | 
						|
			if (moduleDefinition == null) | 
						|
				throw new ArgumentNullException(nameof(moduleDefinition)); | 
						|
			if (settings == null) | 
						|
				throw new ArgumentNullException(nameof(settings)); | 
						|
			this.moduleDefinition = moduleDefinition; | 
						|
			typeReferenceCecilLoader = new CecilLoader { | 
						|
				UseDynamicType = settings.Dynamic, | 
						|
				UseTupleTypes = settings.TupleTypes, | 
						|
			}; | 
						|
			CecilLoader cecilLoader = new CecilLoader { | 
						|
				IncludeInternalMembers = true, | 
						|
				LazyLoad = true, | 
						|
				OnEntityLoaded = StoreMemberReference, | 
						|
				ShortenInterfaceImplNames = false, | 
						|
				UseDynamicType = settings.Dynamic, | 
						|
				UseTupleTypes = settings.TupleTypes, | 
						|
			}; | 
						|
			typeReferenceCecilLoader.SetCurrentModule(moduleDefinition); | 
						|
			IUnresolvedAssembly mainAssembly = cecilLoader.LoadModule(moduleDefinition); | 
						|
			// Load referenced assemblies and type-forwarder references. | 
						|
			// This is necessary to make .NET Core/PCL binaries work better. | 
						|
			var referencedAssemblies = new List<IUnresolvedAssembly>(); | 
						|
			var assemblyReferenceQueue = new Queue<AssemblyNameReference>(moduleDefinition.AssemblyReferences); | 
						|
			var processedAssemblyReferences = new HashSet<AssemblyNameReference>(KeyComparer.Create((AssemblyNameReference reference) => reference.FullName)); | 
						|
			while (assemblyReferenceQueue.Count > 0) { | 
						|
				var asmRef = assemblyReferenceQueue.Dequeue(); | 
						|
				if (!processedAssemblyReferences.Add(asmRef)) | 
						|
					continue; | 
						|
				var asm = moduleDefinition.AssemblyResolver.Resolve(asmRef); | 
						|
				if (asm != null) { | 
						|
					referencedAssemblies.Add(cecilLoader.LoadAssembly(asm)); | 
						|
					foreach (var forwarder in asm.MainModule.ExportedTypes) { | 
						|
						if (!forwarder.IsForwarder || !(forwarder.Scope is AssemblyNameReference forwarderRef)) continue; | 
						|
						assemblyReferenceQueue.Enqueue(forwarderRef); | 
						|
					} | 
						|
				} | 
						|
			} | 
						|
			compilation = new SimpleCompilation(mainAssembly, referencedAssemblies); | 
						|
			// Primitive types are necessary to avoid assertions in ILReader. | 
						|
			// Fallback to MinimalCorlib to provide the primitive types. | 
						|
			if (compilation.FindType(KnownTypeCode.Void).Kind == TypeKind.Unknown || compilation.FindType(KnownTypeCode.Int32).Kind == TypeKind.Unknown) { | 
						|
				referencedAssemblies.Add(MinimalCorlib.Instance); | 
						|
				compilation = new SimpleCompilation(mainAssembly, referencedAssemblies); | 
						|
			} | 
						|
			context = new SimpleTypeResolveContext(compilation.MainAssembly); | 
						|
		} | 
						|
 | 
						|
		public ICompilation Compilation { | 
						|
			get { return compilation; } | 
						|
		} | 
						|
		 | 
						|
		public IAssembly MainAssembly { | 
						|
			get { return compilation.MainAssembly; } | 
						|
		} | 
						|
 | 
						|
		public ModuleDefinition ModuleDefinition { | 
						|
			get { return moduleDefinition; } | 
						|
		} | 
						|
 | 
						|
		void StoreMemberReference(IUnresolvedEntity entity, MemberReference mr) | 
						|
		{ | 
						|
			// This is a callback from the type system, which is multi-threaded and may be accessed externally | 
						|
			lock (entityDict) | 
						|
				entityDict[entity] = mr; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Retrieves the Cecil member definition for the specified member. | 
						|
		/// </summary> | 
						|
		/// <remarks> | 
						|
		/// Returns null if the member is not defined in the module being decompiled. | 
						|
		/// </remarks> | 
						|
		public MemberReference GetCecil(IUnresolvedEntity member) | 
						|
		{ | 
						|
			if (member == null) | 
						|
				return null; | 
						|
			lock (entityDict) { | 
						|
				MemberReference mr; | 
						|
				if (entityDict.TryGetValue(member, out mr)) | 
						|
					return mr; | 
						|
				return null; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Retrieves the Cecil member definition for the specified member. | 
						|
		/// </summary> | 
						|
		/// <remarks> | 
						|
		/// Returns null if the member is not defined in the module being decompiled. | 
						|
		/// </remarks> | 
						|
		public MemberReference GetCecil(IMember member) | 
						|
		{ | 
						|
			if (member == null) | 
						|
				return null; | 
						|
			return GetCecil(member.UnresolvedMember); | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Retrieves the Cecil type definition. | 
						|
		/// </summary> | 
						|
		/// <remarks> | 
						|
		/// Returns null if the type is not defined in the module being decompiled. | 
						|
		/// </remarks> | 
						|
		public TypeDefinition GetCecil(ITypeDefinition typeDefinition) | 
						|
		{ | 
						|
			if (typeDefinition == null) | 
						|
				return null; | 
						|
			return GetCecil(typeDefinition.Parts[0]) as TypeDefinition; | 
						|
		} | 
						|
 | 
						|
		#region Resolve Type | 
						|
		public IType Resolve(TypeReference typeReference, bool isFromSignature = false) | 
						|
		{ | 
						|
			if (typeReference == null) | 
						|
				return SpecialType.UnknownType; | 
						|
			// We need to skip SentinelType and PinnedType. | 
						|
			// But PinnedType can be nested within modopt, so we'll also skip those. | 
						|
			while (typeReference is OptionalModifierType || typeReference is RequiredModifierType) { | 
						|
				typeReference = ((TypeSpecification)typeReference).ElementType; | 
						|
				isFromSignature = true; | 
						|
			} | 
						|
			if (typeReference is SentinelType || typeReference is PinnedType) { | 
						|
				typeReference = ((TypeSpecification)typeReference).ElementType; | 
						|
				isFromSignature = true; | 
						|
			} | 
						|
			ITypeReference typeRef; | 
						|
			lock (typeReferenceCecilLoader) | 
						|
				typeRef = typeReferenceCecilLoader.ReadTypeReference(typeReference, isFromSignature: isFromSignature); | 
						|
			return typeRef.Resolve(context); | 
						|
		} | 
						|
 | 
						|
		IType ResolveInSignature(TypeReference typeReference) | 
						|
		{ | 
						|
			return Resolve(typeReference, isFromSignature: true); | 
						|
		} | 
						|
		#endregion | 
						|
 | 
						|
		#region Resolve Field | 
						|
		public IField Resolve(FieldReference fieldReference) | 
						|
		{ | 
						|
			if (fieldReference == null) | 
						|
				throw new ArgumentNullException(nameof(fieldReference)); | 
						|
			lock (fieldLookupCache) { | 
						|
				IField field; | 
						|
				if (!fieldLookupCache.TryGetValue(fieldReference, out field)) { | 
						|
					field = FindNonGenericField(fieldReference); | 
						|
					if (fieldReference.DeclaringType.IsGenericInstance) { | 
						|
						var git = (GenericInstanceType)fieldReference.DeclaringType; | 
						|
						var typeArguments = git.GenericArguments.SelectArray(ResolveInSignature); | 
						|
						field = (IField)field.Specialize(new TypeParameterSubstitution(typeArguments, null)); | 
						|
					} | 
						|
					fieldLookupCache.Add(fieldReference, field); | 
						|
				} | 
						|
				return field; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		IField FindNonGenericField(FieldReference fieldReference) | 
						|
		{ | 
						|
			ITypeDefinition typeDef = Resolve(fieldReference.DeclaringType).GetDefinition(); | 
						|
			if (typeDef == null) | 
						|
				return CreateFakeField(fieldReference); | 
						|
			foreach (IField field in typeDef.Fields) | 
						|
				if (field.Name == fieldReference.Name) | 
						|
					return field; | 
						|
			return CreateFakeField(fieldReference); | 
						|
		} | 
						|
 | 
						|
		IField CreateFakeField(FieldReference fieldReference) | 
						|
		{ | 
						|
			var declaringType = Resolve(fieldReference.DeclaringType); | 
						|
			var f = new DefaultUnresolvedField(); | 
						|
			f.Name = fieldReference.Name; | 
						|
			lock (typeReferenceCecilLoader) { | 
						|
				f.ReturnType = typeReferenceCecilLoader.ReadTypeReference(fieldReference.FieldType); | 
						|
			} | 
						|
			return new ResolvedFakeField(f, context.WithCurrentTypeDefinition(declaringType.GetDefinition()), declaringType); | 
						|
		} | 
						|
 | 
						|
		class ResolvedFakeField : DefaultResolvedField | 
						|
		{ | 
						|
			readonly IType declaringType; | 
						|
 | 
						|
			public ResolvedFakeField(DefaultUnresolvedField unresolved, ITypeResolveContext parentContext, IType declaringType) | 
						|
				: base(unresolved, parentContext) | 
						|
			{ | 
						|
				this.declaringType = declaringType; | 
						|
			} | 
						|
 | 
						|
			public override IType DeclaringType | 
						|
			{ | 
						|
				get { return declaringType; } | 
						|
			} | 
						|
		} | 
						|
		#endregion | 
						|
 | 
						|
		#region Resolve Method | 
						|
		public IMethod Resolve(MethodReference methodReference) | 
						|
		{ | 
						|
			if (methodReference == null) | 
						|
				throw new ArgumentNullException(nameof(methodReference)); | 
						|
			lock (methodLookupCache) { | 
						|
				IMethod method; | 
						|
				if (!methodLookupCache.TryGetValue(methodReference, out method)) { | 
						|
					method = FindNonGenericMethod(methodReference.GetElementMethod()); | 
						|
					if (methodReference.CallingConvention == MethodCallingConvention.VarArg) { | 
						|
						method = new VarArgInstanceMethod( | 
						|
							method, | 
						|
							methodReference.Parameters.SkipWhile(p => !p.ParameterType.IsSentinel).Select(p => ResolveInSignature(p.ParameterType)) | 
						|
						); | 
						|
					} else if (methodReference.IsGenericInstance || methodReference.DeclaringType.IsGenericInstance) { | 
						|
						IReadOnlyList<IType> classTypeArguments = null; | 
						|
						IReadOnlyList<IType> methodTypeArguments = null; | 
						|
						if (methodReference.IsGenericInstance) { | 
						|
							var gim = ((GenericInstanceMethod)methodReference); | 
						|
							methodTypeArguments = gim.GenericArguments.SelectArray(ResolveInSignature); | 
						|
						} | 
						|
						if (methodReference.DeclaringType.IsGenericInstance) { | 
						|
							var git = (GenericInstanceType)methodReference.DeclaringType; | 
						|
							classTypeArguments = git.GenericArguments.SelectArray(ResolveInSignature); | 
						|
						} | 
						|
						method = method.Specialize(new TypeParameterSubstitution(classTypeArguments, methodTypeArguments)); | 
						|
					} | 
						|
					methodLookupCache.Add(methodReference, method); | 
						|
				} | 
						|
				return method; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		IMethod FindNonGenericMethod(MethodReference methodReference) | 
						|
		{ | 
						|
			ITypeDefinition typeDef = Resolve(methodReference.DeclaringType).GetDefinition(); | 
						|
			if (typeDef == null) | 
						|
				return CreateFakeMethod(methodReference); | 
						|
			IEnumerable<IMethod> methods; | 
						|
			if (methodReference.Name == ".ctor") { | 
						|
				methods = typeDef.GetConstructors(); | 
						|
			} else if (methodReference.Name == ".cctor") { | 
						|
				return typeDef.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic); | 
						|
			} else { | 
						|
				methods = typeDef.GetMethods(m => m.Name == methodReference.Name, GetMemberOptions.IgnoreInheritedMembers) | 
						|
					.Concat(typeDef.GetAccessors(m => m.Name == methodReference.Name, GetMemberOptions.IgnoreInheritedMembers)); | 
						|
			} | 
						|
			if (methodReference.MetadataToken.TokenType == TokenType.Method) { | 
						|
				foreach (var method in methods) { | 
						|
					if (method.MetadataToken == methodReference.MetadataToken) | 
						|
						return method; | 
						|
				} | 
						|
			} | 
						|
			IType[] parameterTypes; | 
						|
			if (methodReference.CallingConvention == MethodCallingConvention.VarArg) { | 
						|
				parameterTypes = methodReference.Parameters | 
						|
					.TakeWhile(p => !p.ParameterType.IsSentinel) | 
						|
					.Select(p => ResolveInSignature(p.ParameterType)) | 
						|
					.Concat(new[] { SpecialType.ArgList }) | 
						|
					.ToArray(); | 
						|
			} else { | 
						|
				parameterTypes = methodReference.Parameters.SelectArray(p => ResolveInSignature(p.ParameterType)); | 
						|
			} | 
						|
			var returnType = ResolveInSignature(methodReference.ReturnType); | 
						|
			foreach (var method in methods) { | 
						|
				if (method.TypeParameters.Count != methodReference.GenericParameters.Count) | 
						|
					continue; | 
						|
				if (!CompareSignatures(method.Parameters, parameterTypes) || !CompareTypes(method.ReturnType, returnType)) | 
						|
					continue; | 
						|
				return method; | 
						|
			} | 
						|
			return CreateFakeMethod(methodReference); | 
						|
		} | 
						|
 | 
						|
		static readonly NormalizeTypeVisitor normalizeTypeVisitor = new NormalizeTypeVisitor { | 
						|
			ReplaceClassTypeParametersWithDummy = true, | 
						|
			ReplaceMethodTypeParametersWithDummy = true, | 
						|
		}; | 
						|
 | 
						|
		static bool CompareTypes(IType a, IType b) | 
						|
		{ | 
						|
			IType type1 = a.AcceptVisitor(normalizeTypeVisitor); | 
						|
			IType type2 = b.AcceptVisitor(normalizeTypeVisitor); | 
						|
			return type1.Equals(type2); | 
						|
		} | 
						|
		 | 
						|
		static bool IsVarArgMethod(IMethod method) | 
						|
		{ | 
						|
			return method.Parameters.Count > 0 && method.Parameters[method.Parameters.Count - 1].Type.Kind == TypeKind.ArgList; | 
						|
		} | 
						|
		 | 
						|
		static bool CompareSignatures(IReadOnlyList<IParameter> parameters, IType[] parameterTypes) | 
						|
		{ | 
						|
			if (parameterTypes.Length != parameters.Count) | 
						|
				return false; | 
						|
			for (int i = 0; i < parameterTypes.Length; i++) { | 
						|
				if (!CompareTypes(parameterTypes[i], parameters[i].Type)) | 
						|
					return false; | 
						|
			} | 
						|
			return true; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Create a dummy IMethod from the specified MethodReference | 
						|
		/// </summary> | 
						|
		IMethod CreateFakeMethod(MethodReference methodReference) | 
						|
		{ | 
						|
			var m = new DefaultUnresolvedMethod(); | 
						|
			ITypeReference declaringTypeReference; | 
						|
			lock (typeReferenceCecilLoader) { | 
						|
				declaringTypeReference = typeReferenceCecilLoader.ReadTypeReference(methodReference.DeclaringType); | 
						|
				if (methodReference.Name == ".ctor" || methodReference.Name == ".cctor") | 
						|
					m.SymbolKind = SymbolKind.Constructor; | 
						|
				m.Name = methodReference.Name; | 
						|
				m.ReturnType = typeReferenceCecilLoader.ReadTypeReference(methodReference.ReturnType); | 
						|
				m.IsStatic = !methodReference.HasThis; | 
						|
				for (int i = 0; i < methodReference.GenericParameters.Count; i++) { | 
						|
					m.TypeParameters.Add(new DefaultUnresolvedTypeParameter(SymbolKind.Method, i, methodReference.GenericParameters[i].Name)); | 
						|
				} | 
						|
				foreach (var p in methodReference.Parameters) { | 
						|
					m.Parameters.Add(new DefaultUnresolvedParameter(typeReferenceCecilLoader.ReadTypeReference(p.ParameterType), p.Name)); | 
						|
				} | 
						|
			} | 
						|
			var type = declaringTypeReference.Resolve(context); | 
						|
			return new ResolvedFakeMethod(m, context.WithCurrentTypeDefinition(type.GetDefinition()), type); | 
						|
		} | 
						|
 | 
						|
		class ResolvedFakeMethod : DefaultResolvedMethod | 
						|
		{ | 
						|
			readonly IType declaringType; | 
						|
 | 
						|
			public ResolvedFakeMethod(DefaultUnresolvedMethod unresolved, ITypeResolveContext parentContext, IType declaringType) | 
						|
				: base(unresolved, parentContext) | 
						|
			{ | 
						|
				this.declaringType = declaringType; | 
						|
			} | 
						|
 | 
						|
			public override IType DeclaringType | 
						|
			{ | 
						|
				get { return declaringType; } | 
						|
			} | 
						|
		} | 
						|
		#endregion | 
						|
 | 
						|
		#region Resolve Property | 
						|
		public IProperty Resolve(PropertyReference propertyReference) | 
						|
		{ | 
						|
			if (propertyReference == null) | 
						|
				throw new ArgumentNullException(nameof(propertyReference)); | 
						|
			lock (propertyLookupCache) { | 
						|
				IProperty property; | 
						|
				if (!propertyLookupCache.TryGetValue(propertyReference, out property)) { | 
						|
					property = FindNonGenericProperty(propertyReference); | 
						|
					if (propertyReference.DeclaringType.IsGenericInstance) { | 
						|
						var git = (GenericInstanceType)propertyReference.DeclaringType; | 
						|
						var typeArguments = git.GenericArguments.SelectArray(ResolveInSignature); | 
						|
						property = (IProperty)property.Specialize(new TypeParameterSubstitution(typeArguments, null)); | 
						|
					} | 
						|
					propertyLookupCache.Add(propertyReference, property); | 
						|
				} | 
						|
				return property; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		IProperty FindNonGenericProperty(PropertyReference propertyReference) | 
						|
		{ | 
						|
			ITypeDefinition typeDef = Resolve(propertyReference.DeclaringType).GetDefinition(); | 
						|
			if (typeDef == null) | 
						|
				return null; | 
						|
			var parameterTypes = propertyReference.Parameters.SelectArray(p => ResolveInSignature(p.ParameterType)); | 
						|
			var returnType = Resolve(propertyReference.PropertyType); | 
						|
			foreach (IProperty property in typeDef.Properties) { | 
						|
				if (property.Name == propertyReference.Name | 
						|
				    && CompareTypes(property.ReturnType, returnType) | 
						|
				    && CompareSignatures(property.Parameters, parameterTypes)) | 
						|
					return property; | 
						|
			} | 
						|
			return null; | 
						|
		} | 
						|
		#endregion | 
						|
 | 
						|
		#region Resolve Event | 
						|
		public IEvent Resolve(EventReference eventReference) | 
						|
		{ | 
						|
			if (eventReference == null) | 
						|
				throw new ArgumentNullException("propertyReference"); | 
						|
			lock (eventLookupCache) { | 
						|
				IEvent ev; | 
						|
				if (!eventLookupCache.TryGetValue(eventReference, out ev)) { | 
						|
					ev = FindNonGenericEvent(eventReference); | 
						|
					if (eventReference.DeclaringType.IsGenericInstance) { | 
						|
						var git = (GenericInstanceType)eventReference.DeclaringType; | 
						|
						var typeArguments = git.GenericArguments.SelectArray(ResolveInSignature); | 
						|
						ev = (IEvent)ev.Specialize(new TypeParameterSubstitution(typeArguments, null)); | 
						|
					} | 
						|
					eventLookupCache.Add(eventReference, ev); | 
						|
				} | 
						|
				return ev; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		IEvent FindNonGenericEvent(EventReference eventReference) | 
						|
		{ | 
						|
			ITypeDefinition typeDef = Resolve(eventReference.DeclaringType).GetDefinition(); | 
						|
			if (typeDef == null) | 
						|
				return null; | 
						|
			var returnType = Resolve(eventReference.EventType); | 
						|
			foreach (IEvent ev in typeDef.Events) { | 
						|
				if (ev.Name == eventReference.Name && CompareTypes(ev.ReturnType, returnType)) | 
						|
					return ev; | 
						|
			} | 
						|
			return null; | 
						|
		} | 
						|
		#endregion | 
						|
 | 
						|
		public IDecompilerTypeSystem GetSpecializingTypeSystem(TypeParameterSubstitution substitution) | 
						|
		{ | 
						|
			if (substitution.Equals(TypeParameterSubstitution.Identity)) { | 
						|
				return this; | 
						|
			} else { | 
						|
				return new SpecializingDecompilerTypeSystem(this, substitution); | 
						|
			} | 
						|
		} | 
						|
	} | 
						|
}
 | 
						|
 |