From ea6a21c4f37d6e08b78ed4db5a415688378b240c Mon Sep 17 00:00:00 2001 From: Jon Hanna Date: Mon, 9 Jan 2017 01:44:56 +0000 Subject: [PATCH] Optimise DynamicProxyMetaObject (#1152) * Remove unnecessary ToArray call. * Remove Value property. One use compares with null, so cast to T is a waste. This leaves just one other use, so it can be inlined. * Remove _dontFallbackFirst Is always true. Remove it and branches for it being false. * Don't use Expression.Convert to reference types to object. The end-result is a no-op, so don't waste the allocation. --- Src/Newtonsoft.Json/Linq/JObject.cs | 2 +- Src/Newtonsoft.Json/Linq/JToken.cs | 2 +- Src/Newtonsoft.Json/Linq/JValue.cs | 2 +- .../Utilities/DynamicProxyMetaObject.cs | 74 +++++-------------- 4 files changed, 22 insertions(+), 58 deletions(-) diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index ffe6452c..da76cd63 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -800,7 +800,7 @@ namespace Newtonsoft.Json.Linq /// protected override DynamicMetaObject GetMetaObject(Expression parameter) { - return new DynamicProxyMetaObject(parameter, this, new JObjectDynamicProxy(), true); + return new DynamicProxyMetaObject(parameter, this, new JObjectDynamicProxy()); } private class JObjectDynamicProxy : DynamicProxy diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index d94e4a48..5e79dc98 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -2365,7 +2365,7 @@ namespace Newtonsoft.Json.Linq /// protected virtual DynamicMetaObject GetMetaObject(Expression parameter) { - return new DynamicProxyMetaObject(parameter, this, new DynamicProxy(), true); + return new DynamicProxyMetaObject(parameter, this, new DynamicProxy()); } /// diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index f4a5e033..bc77a93e 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -966,7 +966,7 @@ namespace Newtonsoft.Json.Linq /// protected override DynamicMetaObject GetMetaObject(Expression parameter) { - return new DynamicProxyMetaObject(parameter, this, new JValueDynamicProxy(), true); + return new DynamicProxyMetaObject(parameter, this, new JValueDynamicProxy()); } private class JValueDynamicProxy : DynamicProxy diff --git a/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs b/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs index 28321d55..3569d176 100644 --- a/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs +++ b/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs @@ -29,25 +29,17 @@ using System.Collections.Generic; using System.Dynamic; using System.Linq; using System.Linq.Expressions; -using System.Reflection; namespace Newtonsoft.Json.Utilities { internal sealed class DynamicProxyMetaObject : DynamicMetaObject { private readonly DynamicProxy _proxy; - private readonly bool _dontFallbackFirst; - internal DynamicProxyMetaObject(Expression expression, T value, DynamicProxy proxy, bool dontFallbackFirst) + internal DynamicProxyMetaObject(Expression expression, T value, DynamicProxy proxy) : base(expression, BindingRestrictions.Empty, value) { _proxy = proxy; - _dontFallbackFirst = dontFallbackFirst; - } - - private new T Value - { - get { return (T)base.Value; } } private bool IsOverridden(string method) @@ -108,7 +100,7 @@ namespace Newtonsoft.Json.Utilities // Fallback fallback = e => binder.FallbackInvokeMember(this, args, e); - DynamicMetaObject call = BuildCallMethodWithResult( + return BuildCallMethodWithResult( "TryInvokeMember", binder, GetArgArray(args), @@ -121,8 +113,6 @@ namespace Newtonsoft.Json.Utilities ), null ); - - return _dontFallbackFirst ? call : fallback(call); } public override DynamicMetaObject BindCreateInstance(CreateInstanceBinder binder, DynamicMetaObject[] args) @@ -178,9 +168,13 @@ namespace Newtonsoft.Json.Utilities private static readonly Expression[] NoArgs = new Expression[0]; - private static Expression[] GetArgs(params DynamicMetaObject[] args) + private static IEnumerable GetArgs(params DynamicMetaObject[] args) { - return args.Select(arg => Expression.Convert(arg.Expression, typeof(object))).ToArray(); + return args.Select(arg => + { + Expression exp = arg.Expression; + return exp.Type.IsValueType() ? Expression.Convert(exp, typeof(object)) : exp; + }); } private static Expression[] GetArgArray(DynamicMetaObject[] args) @@ -190,10 +184,11 @@ namespace Newtonsoft.Json.Utilities private static Expression[] GetArgArray(DynamicMetaObject[] args, DynamicMetaObject value) { - return new Expression[] + var exp = value.Expression; + return new[] { Expression.NewArrayInit(typeof(object), GetArgs(args)), - Expression.Convert(value.Expression, typeof(object)) + exp.Type.IsValueType() ? Expression.Convert(exp, typeof(object)) : exp }; } @@ -211,7 +206,7 @@ namespace Newtonsoft.Json.Utilities /// Helper method for generating a MetaObject which calls a /// specific method on Dynamic that returns a result /// - private DynamicMetaObject CallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback, Fallback fallbackInvoke = null) + private DynamicMetaObject CallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, IEnumerable args, Fallback fallback, Fallback fallbackInvoke = null) { // // First, call fallback to do default binding @@ -219,21 +214,10 @@ namespace Newtonsoft.Json.Utilities // DynamicMetaObject fallbackResult = fallback(null); - DynamicMetaObject callDynamic = BuildCallMethodWithResult(methodName, binder, args, fallbackResult, fallbackInvoke); - - // - // Now, call fallback again using our new MO as the error - // When we do this, one of two things can happen: - // 1. Binding will succeed, and it will ignore our call to - // the dynamic method, OR - // 2. Binding will fail, and it will use the MO we created - // above. - // - - return _dontFallbackFirst ? callDynamic : fallback(callDynamic); + return BuildCallMethodWithResult(methodName, binder, args, fallbackResult, fallbackInvoke); } - private DynamicMetaObject BuildCallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, DynamicMetaObject fallbackResult, Fallback fallbackInvoke) + private DynamicMetaObject BuildCallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, IEnumerable args, DynamicMetaObject fallbackResult, Fallback fallbackInvoke) { // // Build a new expression like: @@ -291,7 +275,7 @@ namespace Newtonsoft.Json.Utilities /// specific method on Dynamic, but uses one of the arguments for /// the result. /// - private DynamicMetaObject CallMethodReturnLast(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback) + private DynamicMetaObject CallMethodReturnLast(string methodName, DynamicMetaObjectBinder binder, IEnumerable args, Fallback fallback) { // // First, call fallback to do default binding @@ -312,9 +296,9 @@ namespace Newtonsoft.Json.Utilities callArgs.Add(Expression.Convert(Expression, typeof(T))); callArgs.Add(Constant(binder)); callArgs.AddRange(args); - callArgs[args.Length + 1] = Expression.Assign(result, callArgs[args.Length + 1]); + callArgs[callArgs.Count - 1] = Expression.Assign(result, callArgs[callArgs.Count - 1]); - DynamicMetaObject callDynamic = new DynamicMetaObject( + return new DynamicMetaObject( Expression.Block( new[] { result }, Expression.Condition( @@ -330,16 +314,6 @@ namespace Newtonsoft.Json.Utilities ), GetRestrictions().Merge(fallbackResult.Restrictions) ); - - // - // Now, call fallback again using our new MO as the error - // When we do this, one of two things can happen: - // 1. Binding will succeed, and it will ignore our call to - // the dynamic method, OR - // 2. Binding will fail, and it will use the MO we created - // above. - // - return _dontFallbackFirst ? callDynamic : fallback(callDynamic); } /// @@ -364,7 +338,7 @@ namespace Newtonsoft.Json.Utilities // Build a new expression like: // if (TryDeleteMember(payload)) { } else { fallbackResult } // - DynamicMetaObject callDynamic = new DynamicMetaObject( + return new DynamicMetaObject( Expression.Condition( Expression.Call( Expression.Constant(_proxy), @@ -377,16 +351,6 @@ namespace Newtonsoft.Json.Utilities ), GetRestrictions().Merge(fallbackResult.Restrictions) ); - - // - // Now, call fallback again using our new MO as the error - // When we do this, one of two things can happen: - // 1. Binding will succeed, and it will ignore our call to - // the dynamic method, OR - // 2. Binding will fail, and it will use the MO we created - // above. - // - return _dontFallbackFirst ? callDynamic : fallback(callDynamic); } /// @@ -402,7 +366,7 @@ namespace Newtonsoft.Json.Utilities public override IEnumerable GetDynamicMemberNames() { - return _proxy.GetDynamicMemberNames(Value); + return _proxy.GetDynamicMemberNames((T)Value); } // It is okay to throw NotSupported from this binder. This object