/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy 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. dnSpy 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 dnSpy. If not, see . */ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using dnlib.DotNet; using dnlib.DotNet.Emit; using dnlib.PE; namespace dnSpy.Contracts.Decompiler { /// /// Extensions /// public static class Extensions { /// /// /// /// /// public static ITypeDefOrRef? GetScopeType(this ITypeDefOrRef? type) { if (type is TypeSpec ts) { var sig = ts.TypeSig.RemovePinnedAndModifiers(); if (sig is GenericInstSig gis) return gis.GenericType?.TypeDefOrRef; if (sig is TypeDefOrRefSig tdrs) return tdrs.TypeDefOrRef; } return type; } /// /// /// /// /// public static ITypeDefOrRef? GetScopeTypeDefOrRef(this IType? type) { var t = type.GetScopeType(); if (t is ITypeDefOrRef tdr) return tdr; if (t is TypeSig sig) return sig.ToTypeDefOrRef(); Debug2.Assert(t is null); return null; } /// /// /// /// /// public static IType? GetScopeType(this IType? type) { if (type is TypeDef td) return td; if (type is TypeRef tr) return tr; if (!(type is TypeSig sig)) { if (!(type is TypeSpec ts)) return type; sig = ts.TypeSig; } sig = sig.RemovePinnedAndModifiers(); if (sig is GenericInstSig gis) return gis.GenericType?.TypeDefOrRef; if (sig is TypeDefOrRefSig tdrs) return tdrs.TypeDefOrRef; return type; } /// /// Checks whether a custom attribute exists /// /// Custom attribute provider /// Namespace of custom attribute /// Name of custom attribute /// public static bool IsDefined(this IHasCustomAttribute? provider, UTF8String? @namespace, UTF8String? name) { if (provider is null || provider.CustomAttributes.Count == 0) return false; foreach (var ca in provider.CustomAttributes) { if (ca.AttributeType is TypeRef tr) { if (tr.Namespace == @namespace && tr.Name == name) return true; continue; } if (ca.AttributeType is TypeDef td) { if (td.Namespace == @namespace && td.Name == name) return true; continue; } } return false; } /// /// Gets the RVA and file offset of a member definition. Returns false if the RVA and /// file offsets aren't known or if there's no RVA (eg. there's no method body) /// /// Member /// Updated with the RVA /// Updated with the file offset /// public static bool GetRVA(this IMemberDef? member, out uint rva, out long fileOffset) { rva = 0; fileOffset = 0; if (member is MethodDef method) rva = (uint)method.RVA; else if (member is FieldDef field) rva = (uint)field.RVA; if (rva == 0) return false; var fo = member!.Module.ToFileOffset(rva); if (fo is null) return false; fileOffset = fo.Value; return true; } /// /// Converts an RVA to a file offset /// /// Module /// RVA /// public static uint? ToFileOffset(this ModuleDef? module, uint rva) { var m = module as ModuleDefMD;//TODO: Support CorModuleDef if (m is null) return null; return (uint)m.Metadata.PEImage.ToFileOffset((RVA)rva); } /// /// Gets the size of the code in the method body /// /// Method body, can be null /// public static int GetCodeSize(this CilBody? body) { if (body is null || body.Instructions.Count == 0) return 0; var instr = body.Instructions[body.Instructions.Count - 1]; return (int)instr.Offset + instr.GetSize(); } /// /// Gets all parameters /// /// Method /// public static IList GetParameters(this IMethod? method) { if (method is null || method.MethodSig is null) return new List(); if (method is MethodDef md) return md.Parameters; var list = new List(); int paramIndex = 0, methodSigIndex = 0; if (method.MethodSig.HasThis) list.Add(new Parameter(paramIndex++, Parameter.HIDDEN_THIS_METHOD_SIG_INDEX, method.DeclaringType.ToTypeSig())); foreach (var type in method.MethodSig.GetParams()) list.Add(new Parameter(paramIndex++, methodSigIndex++, type)); return list; } static IEnumerable GetAllMethods(this PropertyDef p) { foreach (var m in p.GetMethods) yield return m; foreach (var m in p.SetMethods) yield return m; foreach (var m in p.OtherMethods) yield return m; } static IEnumerable GetAllMethods(this EventDef e) { if (e.AddMethod is not null) yield return e.AddMethod; if (e.InvokeMethod is not null) yield return e.InvokeMethod; if (e.RemoveMethod is not null) yield return e.RemoveMethod; foreach (var m in e.OtherMethods) yield return m; } /// /// Gets all methods that are part of properties or events /// /// Type /// public static HashSet GetPropertyAndEventMethods(this TypeDef type) { var hash = new HashSet(); foreach (var p in type.Properties) { foreach (var m in p.GetAllMethods()) hash.Add(m); } foreach (var e in type.Events) { foreach (var m in e.GetAllMethods()) hash.Add(m); } hash.Remove(null); return hash; } /// /// Checks whether is an indexer property /// /// Property to check /// public static bool IsIndexer(this PropertyDef? property) { if (property is null || property.PropertySig.GetParamCount() == 0) return false; var accessor = property.GetMethod ?? property.SetMethod; var basePropDef = property; if (accessor is not null && accessor.HasOverrides) { var baseAccessor = accessor.Overrides.First().MethodDeclaration.ResolveMethodDef(); if (baseAccessor is not null) { foreach (var baseProp in baseAccessor.DeclaringType.Properties) { if (baseProp.GetMethod == baseAccessor || baseProp.SetMethod == baseAccessor) { basePropDef = baseProp; break; } } } else return false; } var defaultMemberName = GetDefaultMemberName(basePropDef.DeclaringType); if (defaultMemberName == basePropDef.Name) return true; return false; } static string? GetDefaultMemberName(TypeDef type) { if (type is null) return null; foreach (var ca in type.CustomAttributes.FindAll("System.Reflection.DefaultMemberAttribute")) { if (ca.Constructor is not null && ca.Constructor.FullName == @"System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)" && ca.ConstructorArguments.Count == 1 && ca.ConstructorArguments[0].Value is UTF8String) { return (UTF8String)ca.ConstructorArguments[0].Value; } } return null; } /// /// Resolves a type /// /// Type /// public static TypeDef? Resolve(this IType? type) => type?.GetScopeTypeDefOrRef().ResolveTypeDef(); /// /// Returns true if the fields can be sorted and false if the original metadata order must be used /// /// Type /// public static bool CanSortFields(this TypeDef type) => type.IsAutoLayout; /// /// Returns true if the methods can be sorted and false if the original metadata order must be used /// /// Type /// public static bool CanSortMethods(this TypeDef type) => !(type.IsInterface && type.IsImport); /// /// Gets all methods, properties and events. They're returned in the original metadata order. /// /// Type /// public static IEnumerable GetNonSortedMethodsPropertiesEvents(this TypeDef type) { var hash = new HashSet(); var defs = new List<(IMemberDef def, List list)>(); foreach (var p in type.Properties) { var methods = new List(p.GetAllMethods()); foreach (var m in methods) hash.Add(m); methods.Sort((a, b) => a.MDToken.Raw.CompareTo(b.MDToken.Raw)); defs.Add((p, methods)); } foreach (var e in type.Events) { var methods = new List(e.GetAllMethods()); foreach (var m in methods) hash.Add(m); methods.Sort((a, b) => a.MDToken.Raw.CompareTo(b.MDToken.Raw)); defs.Add((e, methods)); } foreach (var m in type.Methods) { if (hash.Contains(m)) continue; defs.Add((m, new List { m })); } defs.Sort((a, b) => { if (a.list.Count == 0 || b.list.Count == 0) return b.list.Count.CompareTo(a.list.Count); return a.list[0].MDToken.Raw.CompareTo(b.list[0].MDToken.Raw); }); return defs.Select(a => a.def); } } }