using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; using DynamicExpresso.Reflection; namespace DynamicExpresso { public class Identifier { public Expression Expression { get; private set; } public string Name { get; private set; } public Identifier(string name, Expression expression) { if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException("name"); if (expression == null) throw new ArgumentNullException("expression"); Expression = expression; Name = name; } } internal class FunctionIdentifier : Identifier { internal FunctionIdentifier(string name, Delegate value) : base(name, new MethodGroupExpression(value)) { } internal void AddOverload(Delegate overload) { ((MethodGroupExpression)Expression).AddOverload(overload); } } /// /// Custom expression that simulates a method group (ie. a group of methods with the same name). /// It's used when custom functions are added to the interpreter via . /// internal class MethodGroupExpression : Expression { public class Overload { public Delegate Delegate { get; } private MethodBase _method; public MethodBase Method { get { if (_method == null) _method = Delegate.Method; return _method; } } // we'll most likely never need this: it was needed before https://github.com/dotnet/roslyn/pull/53402 private MethodBase _invokeMethod; public MethodBase InvokeMethod { get { if (_invokeMethod == null) _invokeMethod = MemberFinder.FindInvokeMethod(Delegate.GetType()); return _invokeMethod; } } public Overload(Delegate @delegate) { Delegate = @delegate; } } private readonly List _overloads = new List(); internal IReadOnlyCollection Overloads { get { return _overloads.AsReadOnly(); } } internal MethodGroupExpression(Delegate overload) { AddOverload(overload); } internal void AddOverload(Delegate overload) { // remove any existing delegate with the exact same signature RemoveDelegateSignature(overload); _overloads.Add(new Overload(overload)); } private void RemoveDelegateSignature(Delegate overload) { _overloads.RemoveAll(del => HasSameSignature(overload.Method, del.Delegate.Method)); } private static bool HasSameSignature(MethodInfo method, MethodInfo other) { if (method.ReturnType != other.ReturnType) return false; var param = method.GetParameters(); var oParam = other.GetParameters(); if (param.Length != oParam.Length) return false; for (var i = 0; i < param.Length; i++) { var p = param[i]; var q = oParam[i]; if (p.ParameterType != q.ParameterType || p.HasDefaultValue != q.HasDefaultValue) return false; } return true; } /// /// The resolution process will find the best overload for the given arguments, /// which we then need to match to the correct delegate. /// internal Delegate FindUsedOverload(bool usedInvokeMethod, MethodData methodData) { foreach (var overload in _overloads) { if (usedInvokeMethod) { if (methodData.MethodBase == overload.InvokeMethod) return overload.Delegate; } else { if (methodData.MethodBase == overload.Method) return overload.Delegate; } } // this should never happen throw new InvalidOperationException("No overload matches the method"); } } }