<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />

DynamicProxyMetaObject<T>

using System; using System.Collections.Generic; using System.Dynamic; using System.Linq; using System.Linq.Expressions; namespace Newtonsoft.Json.Utilities { internal sealed class DynamicProxyMetaObject<T> : DynamicMetaObject { private delegate DynamicMetaObject Fallback (DynamicMetaObject errorSuggestion); private sealed class GetBinderAdapter : GetMemberBinder { internal GetBinderAdapter(InvokeMemberBinder binder) : base(binder.Name, binder.IgnoreCase) { } public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) { throw new NotSupportedException(); } } private readonly DynamicProxy<T> _proxy; private static Expression[] NoArgs => CollectionUtils.ArrayEmpty<Expression>(); internal DynamicProxyMetaObject(Expression expression, T value, DynamicProxy<T> proxy) : base(expression, BindingRestrictions.Empty, value) { _proxy = proxy; } private bool IsOverridden(string method) { return ReflectionUtils.IsMethodOverridden(_proxy.GetType(), typeof(DynamicProxy<T>), method); } public override DynamicMetaObject BindGetMember(GetMemberBinder binder) { if (!IsOverridden("TryGetMember")) return base.BindGetMember(binder); return CallMethodWithResult("TryGetMember", binder, NoArgs, (DynamicMetaObject e) => binder.FallbackGetMember(this, e), null); } public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) { if (!IsOverridden("TrySetMember")) return base.BindSetMember(binder, value); return CallMethodReturnLast("TrySetMember", binder, GetArgs(value), (DynamicMetaObject e) => binder.FallbackSetMember(this, value, e)); } public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) { if (!IsOverridden("TryDeleteMember")) return base.BindDeleteMember(binder); return CallMethodNoResult("TryDeleteMember", binder, NoArgs, (DynamicMetaObject e) => binder.FallbackDeleteMember(this, e)); } public override DynamicMetaObject BindConvert(ConvertBinder binder) { if (!IsOverridden("TryConvert")) return base.BindConvert(binder); return CallMethodWithResult("TryConvert", binder, NoArgs, (DynamicMetaObject e) => binder.FallbackConvert(this, e), null); } public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) { if (!IsOverridden("TryInvokeMember")) return base.BindInvokeMember(binder, args); Fallback fallback = (DynamicMetaObject e) => binder.FallbackInvokeMember(this, args, e); return BuildCallMethodWithResult("TryInvokeMember", binder, GetArgArray(args), BuildCallMethodWithResult("TryGetMember", new GetBinderAdapter(binder), NoArgs, fallback(null), (DynamicMetaObject e) => binder.FallbackInvoke(e, args, null)), null); } public override DynamicMetaObject BindCreateInstance(CreateInstanceBinder binder, DynamicMetaObject[] args) { if (!IsOverridden("TryCreateInstance")) return base.BindCreateInstance(binder, args); return CallMethodWithResult("TryCreateInstance", binder, GetArgArray(args), (DynamicMetaObject e) => binder.FallbackCreateInstance(this, args, e), null); } public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args) { if (!IsOverridden("TryInvoke")) return base.BindInvoke(binder, args); return CallMethodWithResult("TryInvoke", binder, GetArgArray(args), (DynamicMetaObject e) => binder.FallbackInvoke(this, args, e), null); } public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg) { if (!IsOverridden("TryBinaryOperation")) return base.BindBinaryOperation(binder, arg); return CallMethodWithResult("TryBinaryOperation", binder, GetArgs(arg), (DynamicMetaObject e) => binder.FallbackBinaryOperation(this, arg, e), null); } public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder) { if (!IsOverridden("TryUnaryOperation")) return base.BindUnaryOperation(binder); return CallMethodWithResult("TryUnaryOperation", binder, NoArgs, (DynamicMetaObject e) => binder.FallbackUnaryOperation(this, e), null); } public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes) { if (!IsOverridden("TryGetIndex")) return base.BindGetIndex(binder, indexes); return CallMethodWithResult("TryGetIndex", binder, GetArgArray(indexes), (DynamicMetaObject e) => binder.FallbackGetIndex(this, indexes, e), null); } public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value) { if (!IsOverridden("TrySetIndex")) return base.BindSetIndex(binder, indexes, value); return CallMethodReturnLast("TrySetIndex", binder, GetArgArray(indexes, value), (DynamicMetaObject e) => binder.FallbackSetIndex(this, indexes, value, e)); } public override DynamicMetaObject BindDeleteIndex(DeleteIndexBinder binder, DynamicMetaObject[] indexes) { if (!IsOverridden("TryDeleteIndex")) return base.BindDeleteIndex(binder, indexes); return CallMethodNoResult("TryDeleteIndex", binder, GetArgArray(indexes), (DynamicMetaObject e) => binder.FallbackDeleteIndex(this, indexes, e)); } private static IEnumerable<Expression> GetArgs(params DynamicMetaObject[] args) { return Enumerable.Select<DynamicMetaObject, Expression>((IEnumerable<DynamicMetaObject>)args, (Func<DynamicMetaObject, Expression>)delegate(DynamicMetaObject arg) { Expression expression = arg.Expression; if (!expression.Type.IsValueType()) return expression; return Expression.Convert(expression, typeof(object)); }); } private static Expression[] GetArgArray(DynamicMetaObject[] args) { return new NewArrayExpression[1] { Expression.NewArrayInit(typeof(object), GetArgs(args)) }; } private static Expression[] GetArgArray(DynamicMetaObject[] args, DynamicMetaObject value) { Expression expression = value.Expression; return new Expression[2] { Expression.NewArrayInit(typeof(object), GetArgs(args)), expression.Type.IsValueType() ? Expression.Convert(expression, typeof(object)) : expression }; } private static ConstantExpression Constant(DynamicMetaObjectBinder binder) { Type type = binder.GetType(); while (!type.IsVisible()) { type = type.BaseType(); } return Expression.Constant(binder, type); } private DynamicMetaObject CallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, IEnumerable<Expression> args, Fallback fallback, Fallback fallbackInvoke = null) { DynamicMetaObject fallbackResult = fallback(null); return BuildCallMethodWithResult(methodName, binder, args, fallbackResult, fallbackInvoke); } private DynamicMetaObject BuildCallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, IEnumerable<Expression> args, DynamicMetaObject fallbackResult, Fallback fallbackInvoke) { ParameterExpression parameterExpression = Expression.Parameter(typeof(object), null); IList<Expression> list = new List<Expression>(); list.Add(Expression.Convert(base.Expression, typeof(T))); list.Add(Constant(binder)); CollectionUtils.AddRange<Expression>(list, args); list.Add(parameterExpression); DynamicMetaObject dynamicMetaObject = new DynamicMetaObject(parameterExpression, BindingRestrictions.Empty); if ((object)binder.ReturnType != typeof(object)) dynamicMetaObject = new DynamicMetaObject(Expression.Convert(dynamicMetaObject.Expression, binder.ReturnType), dynamicMetaObject.Restrictions); if (fallbackInvoke != null) dynamicMetaObject = fallbackInvoke(dynamicMetaObject); return new DynamicMetaObject(Expression.Block(new ParameterExpression[1] { parameterExpression }, Expression.Condition(Expression.Call(Expression.Constant(_proxy), typeof(DynamicProxy<T>).GetMethod(methodName), list), dynamicMetaObject.Expression, fallbackResult.Expression, binder.ReturnType)), GetRestrictions().Merge(dynamicMetaObject.Restrictions).Merge(fallbackResult.Restrictions)); } private DynamicMetaObject CallMethodReturnLast(string methodName, DynamicMetaObjectBinder binder, IEnumerable<Expression> args, Fallback fallback) { DynamicMetaObject dynamicMetaObject = fallback(null); ParameterExpression parameterExpression = Expression.Parameter(typeof(object), null); IList<Expression> list = new List<Expression>(); list.Add(Expression.Convert(base.Expression, typeof(T))); list.Add(Constant(binder)); CollectionUtils.AddRange<Expression>(list, args); list[list.Count - 1] = Expression.Assign(parameterExpression, list[list.Count - 1]); return new DynamicMetaObject(Expression.Block(new ParameterExpression[1] { parameterExpression }, Expression.Condition(Expression.Call(Expression.Constant(_proxy), typeof(DynamicProxy<T>).GetMethod(methodName), list), parameterExpression, dynamicMetaObject.Expression, typeof(object))), GetRestrictions().Merge(dynamicMetaObject.Restrictions)); } private DynamicMetaObject CallMethodNoResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback) { DynamicMetaObject dynamicMetaObject = fallback(null); IList<Expression> list = new List<Expression>(); list.Add(Expression.Convert(base.Expression, typeof(T))); list.Add(Constant(binder)); CollectionUtils.AddRange<Expression>(list, (IEnumerable<Expression>)args); return new DynamicMetaObject(Expression.Condition(Expression.Call(Expression.Constant(_proxy), typeof(DynamicProxy<T>).GetMethod(methodName), list), Expression.Empty(), dynamicMetaObject.Expression, typeof(void)), GetRestrictions().Merge(dynamicMetaObject.Restrictions)); } private BindingRestrictions GetRestrictions() { if (base.Value != null || !base.HasValue) return BindingRestrictions.GetTypeRestriction(base.Expression, base.LimitType); return BindingRestrictions.GetInstanceRestriction(base.Expression, null); } public override IEnumerable<string> GetDynamicMemberNames() { return _proxy.GetDynamicMemberNames((T)base.Value); } } }