// Copyright (c) 2018 Daniel Grunwald // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.Linq; using SRM = System.Reflection.Metadata; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; using static ICSharpCode.Decompiler.Metadata.MetadataExtensions; using System.Diagnostics; using System.Collections.Immutable; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Options that control how metadata is represented in the type system. /// [Flags] public enum TypeSystemOptions { /// /// No options enabled; stay as close to the metadata as possible. /// None = 0, /// /// [DynamicAttribute] is used to replace 'object' types with the 'dynamic' type. /// /// If this option is not active, the 'dynamic' type is not used, and the attribute is preserved. /// Dynamic = 1, /// /// Tuple types are represented using the TupleType class. /// [TupleElementNames] is used to name the tuple elements. /// /// If this option is not active, the tuples are represented using their underlying type, and the attribute is preserved. /// Tuple = 2, /// /// If this option is active, [ExtensionAttribute] is removed and methods are marked as IsExtensionMethod. /// Otherwise, the attribute is preserved but the methods are not marked. /// ExtensionMethods = 4, /// /// Default settings: all features enabled. /// Default = Dynamic | Tuple | ExtensionMethods } /// /// Manages the NRefactory type system for the decompiler. /// /// /// This class is thread-safe. /// public class DecompilerTypeSystem : IDecompilerTypeSystem { readonly Metadata.PEFile moduleDefinition; readonly ICompilation compilation; readonly ITypeResolveContext context; readonly TypeSystemOptions typeSystemOptions; readonly MetadataAssembly mainAssembly; Dictionary fieldLookupCache = new Dictionary(); Dictionary propertyLookupCache = new Dictionary(); Dictionary methodLookupCache = new Dictionary(); Dictionary eventLookupCache = new Dictionary(); Dictionary moduleLookup = new Dictionary(); public DecompilerTypeSystem(Metadata.PEFile moduleDefinition) : this(moduleDefinition, new DecompilerSettings()) { } public DecompilerTypeSystem(Metadata.PEFile moduleDefinition, DecompilerSettings settings) { if (settings == null) throw new ArgumentNullException(nameof(settings)); this.moduleDefinition = moduleDefinition; typeSystemOptions = TypeSystemOptions.None; if (settings.Dynamic) typeSystemOptions |= TypeSystemOptions.Dynamic; if (settings.TupleTypes) typeSystemOptions |= TypeSystemOptions.Tuple; if (settings.ExtensionMethods) typeSystemOptions |= TypeSystemOptions.ExtensionMethods; MetadataLoader loader = new MetadataLoader { IncludeInternalMembers = true, ShortenInterfaceImplNames = false, Options = typeSystemOptions, }; IUnresolvedAssembly mainAssembly = loader.LoadModule(moduleDefinition); // Load referenced assemblies and type-forwarder references. // This is necessary to make .NET Core/PCL binaries work better. var referencedAssemblies = new List(); var assemblyReferenceQueue = new Queue(moduleDefinition.AssemblyReferences); var processedAssemblyReferences = new HashSet(KeyComparer.Create((Metadata.AssemblyReference 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) { IUnresolvedAssembly unresolvedAsm = loader.LoadModule(asm); referencedAssemblies.Add(unresolvedAsm); moduleLookup.Add(unresolvedAsm, asm); var metadata = asm.Metadata; foreach (var h in metadata.ExportedTypes) { var forwarder = metadata.GetExportedType(h); if (!forwarder.IsForwarder || forwarder.Implementation.Kind != SRM.HandleKind.AssemblyReference) continue; assemblyReferenceQueue.Enqueue(new Metadata.AssemblyReference(asm, (SRM.AssemblyReferenceHandle)forwarder.Implementation)); } } } 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 MetadataAssembly MainAssembly { get { return mainAssembly; } } public Metadata.PEFile ModuleDefinition { get { return moduleDefinition; } } public SRM.MetadataReader GetMetadata() => moduleDefinition.Metadata; public Metadata.PEFile GetModuleDefinition(IAssembly assembly) { if (assembly is MetadataAssembly asm) { return asm.PEFile; } return null; } public IMember ResolveAsMember(SRM.EntityHandle memberReference) { switch (memberReference.Kind) { case SRM.HandleKind.FieldDefinition: return ResolveAsField(memberReference); case SRM.HandleKind.MethodDefinition: return ResolveAsMethod(memberReference); case SRM.HandleKind.MemberReference: var mr = moduleDefinition.Metadata.GetMemberReference((SRM.MemberReferenceHandle)memberReference); switch (mr.GetKind()) { case SRM.MemberReferenceKind.Method: return ResolveAsMethod(memberReference); case SRM.MemberReferenceKind.Field: return ResolveAsField(memberReference); } throw new NotSupportedException(); case SRM.HandleKind.EventDefinition: return ResolveAsEvent(memberReference); case SRM.HandleKind.PropertyDefinition: return ResolveAsProperty(memberReference); case SRM.HandleKind.MethodSpecification: return ResolveAsMethod(memberReference); default: throw new NotSupportedException(); } } #region Resolve Type public IType ResolveAsType(SRM.EntityHandle typeReference) { return MetadataTypeReference.Resolve( typeReference, moduleDefinition.Metadata, mainAssembly.TypeProvider, new GenericContext(), typeSystemOptions ); } IType ResolveDeclaringType(SRM.EntityHandle declaringTypeReference) { // resolve without substituting dynamic/tuple types return MetadataTypeReference.Resolve( declaringTypeReference, moduleDefinition.Metadata, mainAssembly.TypeProvider, new GenericContext(context), typeSystemOptions & ~(TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple) ); } #endregion public SRM.MethodSignature DecodeMethodSignature(SRM.StandaloneSignatureHandle handle) { var standaloneSignature = moduleDefinition.Metadata.GetStandaloneSignature(handle); if (standaloneSignature.GetKind() != SRM.StandaloneSignatureKind.Method) throw new InvalidOperationException("Expected Method signature"); return standaloneSignature.DecodeMethodSignature( mainAssembly.TypeProvider, new GenericContext(context) ); } public ImmutableArray DecodeLocalSignature(SRM.StandaloneSignatureHandle handle) { var standaloneSignature = moduleDefinition.Metadata.GetStandaloneSignature(handle); if (standaloneSignature.GetKind() != SRM.StandaloneSignatureKind.LocalVariables) throw new InvalidOperationException("Expected Local signature"); return standaloneSignature.DecodeLocalSignature( mainAssembly.TypeProvider, new GenericContext(context) ); } #region Resolve Field public IField ResolveAsField(SRM.EntityHandle fieldReference) { /*if (fieldReference.IsNil) throw new ArgumentNullException(nameof(fieldReference)); if (fieldReference.Kind != SRM.HandleKind.FieldDefinition && fieldReference.Kind != SRM.HandleKind.MemberReference) throw new ArgumentException("HandleKind must be either FieldDefinition or MemberReference", nameof(fieldReference)); lock (fieldLookupCache) { if (!fieldLookupCache.TryGetValue(fieldReference, out IField field)) { var metadata = moduleDefinition.Metadata; IType declaringType; switch (fieldReference.Kind) { case SRM.HandleKind.FieldDefinition: var fieldDefHandle = (SRM.FieldDefinitionHandle)fieldReference; field = mainAssembly.GetDefinition(fieldDefHandle); if (field == null) { throw new NotImplementedException(); } break; case SRM.HandleKind.MemberReference: var memberRefHandle = (SRM.MemberReferenceHandle)fieldReference; var memberRef = metadata.GetMemberReference(memberRefHandle); Debug.Assert(memberRef.GetKind() == SRM.MemberReferenceKind.Field); declaringType = ResolveDeclaringType(memberRef.Parent); field = FindNonGenericField(metadata, memberRefHandle, declaringType); break; default: throw new NotSupportedException(); } if (declaringType.TypeArguments.Count > 0) { field = (IField)field.Specialize(new TypeParameterSubstitution(declaringType.TypeArguments, null)); } fieldLookupCache.Add(fieldReference, field); } return field; }*/ throw new NotImplementedException(); } /* IField FindNonGenericField(SRM.MetadataReader metadata, SRM.MemberReferenceHandle memberRefHandle, IType declaringType) { var memberRef = metadata.GetMemberReference(memberRefHandle); string name = metadata.GetString(memberRef.Name); ITypeDefinition typeDef = declaringType.GetDefinition(); if (typeDef != null) { foreach (IField field in typeDef.Fields) { if (field.Name == name) return field; } } var returnType = memberRef.DecodeFieldSignature(mainAssembly.TypeProvider, new GenericContext(context)); return new FakeField(compilation) { DeclaringType = declaringType, Name = name, ReturnType = returnType }; } */ #endregion #region Resolve Method public IMethod ResolveAsMethod(SRM.EntityHandle methodReference) { return MainAssembly.ResolveMethod(methodReference); } #endregion #region Resolve Property public IProperty ResolveAsProperty(SRM.EntityHandle propertyReference) { if (propertyReference.IsNil) throw new ArgumentNullException(nameof(propertyReference)); if (propertyReference.Kind != SRM.HandleKind.PropertyDefinition) throw new ArgumentException("HandleKind must be PropertyDefinition", nameof(propertyReference)); lock (propertyLookupCache) { IProperty property; if (!propertyLookupCache.TryGetValue(propertyReference, out property)) { var metadata = moduleDefinition.Metadata; property = FindNonGenericProperty(metadata, (SRM.PropertyDefinitionHandle)propertyReference); /*if (propertyReference.DeclaringType.IsGenericInstance) { var git = (GenericInstanceType)propertyReference.DeclaringType; var typeArguments = git.GenericArguments.SelectArray(Resolve); property = (IProperty)property.Specialize(new TypeParameterSubstitution(typeArguments, null)); }*/ propertyLookupCache.Add(propertyReference, property); } return property; } } IProperty FindNonGenericProperty(SRM.MetadataReader metadata, SRM.PropertyDefinitionHandle handle) { var propertyDefinition = metadata.GetPropertyDefinition(handle); var declaringType = metadata.GetMethodDefinition(propertyDefinition.GetAccessors().GetAny()).GetDeclaringType(); ITypeDefinition typeDef = ResolveDeclaringType(declaringType).GetDefinition(); if (typeDef == null) return null; foreach (IProperty property in typeDef.Properties) { if (property.MetadataToken == handle) return property; } return null; } #endregion #region Resolve Event public IEvent ResolveAsEvent(SRM.EntityHandle eventReference) { if (eventReference.IsNil) throw new ArgumentNullException(nameof(eventReference)); if (eventReference.Kind != SRM.HandleKind.EventDefinition) throw new ArgumentException("HandleKind must be EventDefinition", nameof(eventReference)); lock (eventLookupCache) { IEvent ev; if (!eventLookupCache.TryGetValue(eventReference, out ev)) { var metadata = moduleDefinition.Metadata; ev = FindNonGenericEvent(metadata, (SRM.EventDefinitionHandle)eventReference); /*if (eventReference.DeclaringType.IsGenericInstance) { var git = (GenericInstanceType)eventReference.DeclaringType; var typeArguments = git.GenericArguments.SelectArray(Resolve); ev = (IEvent)ev.Specialize(new TypeParameterSubstitution(typeArguments, null)); }*/ eventLookupCache.Add(eventReference, ev); } return ev; } } IEvent FindNonGenericEvent(SRM.MetadataReader metadata, SRM.EventDefinitionHandle handle) { var eventDefinition = metadata.GetEventDefinition(handle); var declaringType = metadata.GetMethodDefinition(eventDefinition.GetAccessors().GetAny()).GetDeclaringType(); ITypeDefinition typeDef = ResolveDeclaringType(declaringType).GetDefinition(); if (typeDef == null) return null; var returnType = ResolveAsType(eventDefinition.Type); string name = metadata.GetString(eventDefinition.Name); foreach (IEvent ev in typeDef.Events) { if (ev.MetadataToken == handle) return ev; } return null; } #endregion public IDecompilerTypeSystem GetSpecializingTypeSystem(TypeParameterSubstitution substitution) { if (substitution.Equals(TypeParameterSubstitution.Identity)) { return this; } else { return new SpecializingDecompilerTypeSystem(this, substitution); } } } }