hazzik / DelegateDecompiler

A library which is able to decompile a delegate or a method body to its lambda representation
MIT License
522 stars 62 forks source link

Auto Detect ShouldDecompile members #165

Open tb-mtg opened 3 years ago

tb-mtg commented 3 years ago

Currently I have to manually add each computed member name to a custom configuration.

It would be nice if the ShouldDecompile method below could automatically determine which properties should be decompiled:

  public class CustomDelegateDecompilerConfiguration : Configuration {
    public static CustomDelegateDecompilerConfiguration Instance { get; } = new CustomDelegateDecompilerConfiguration();
    public static void Enable() => Configuration.Configure(Instance);

    public CustomDelegateDecompilerConfiguration() {
      RegisterDecompileableMember<Person, string>(x => x.Name);

      RegisterDecompileableMembers(typeof(string), nameof(string.IsNullOrWhiteSpace));

      RegisterDecompileableMembers(typeof(CustomComputedMethods), new[] {
        nameof(CustomComputedMethods.PersonName),
        nameof(CustomComputedMethods.MonthInteger),
        nameof(CustomComputedMethods.WholeMonthsBetween),
        nameof(CustomComputedMethods.WholeYearsBetween)
      });

    }

    public HashSet<MemberInfo> DecompileableMembers { get; } = new HashSet<MemberInfo>();

    public override bool ShouldDecompile(MemberInfo memberInfo) => memberInfo.GetCustomAttributes(typeof(DecompileAttribute), true).Length > 0
      || memberInfo.GetCustomAttributes(typeof(ComputedAttribute), true).Length > 0
      || memberInfo.GetCustomAttributes(typeof(CompilerGeneratedAttribute), true).Length > 0
      || DecompileableMembers.Contains(memberInfo)
      //***  Would be nice if auto detection was possible with non-existing methods below ***
      //|| memberInfo.AutoProperty      (One with a backing field automatically generated by the compiler)
      //|| memberInfo.HasExpressionBody (One implemented using the => (lambda) syntax)
      //|| memberInfo.HasFunctionBody   (One implemented using the normal {...} syntax)
      ;

    public override void RegisterDecompileableMember(MemberInfo prop) => DecompileableMembers.Add(prop);

    public void RegisterDecompileableMember<T, TResult>(Expression<Func<T, TResult>> expression) where T : class => RegisterDecompileableMember(expression.Body.GetMemberInfo());

    public void RegisterDecompileableMembers(Type type, params string[] memberNames) {
      foreach(var tmi in type.GetMembers().Where(mi => memberNames.Contains(mi.Name))) {
        DecompileableMembers.Add(tmi);
      }
    }

    public void RegisterDecompileableMembers<T>(params string[] memberNames) where T : class => RegisterDecompileableMembers(typeof(T), memberNames);

  }

An example class:

  public class Person {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string AlternativeFirstName { get; set; }

    public string Name => string.Concat(AlternativeFirstName == string.Empty ? FirstName : AlternativeFirstName, " ", LastName);
  }

Some example extension Methods:

  public static class CustomComputedMethods {
    public static string PersonName(string firstName, string lastName, string knownAs) => (knownAs ?? firstName).Trim() + " " + lastName.Trim();
    public static long MonthInteger(this DateTime d) => checked(d.Year * 12 + d.Month);
    public static int WholeMonthsBetween(this DateTime d, DateTime maxDate) => (int)(maxDate.MonthInteger() - d.MonthInteger() - (d.Day > maxDate.Day ? 1 : 0));
    public static int WholeYearsBetween(this DateTime d, DateTime maxDate) => d.WholeMonthsBetween(maxDate) / 12;
  }