diff --git a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs index 5513b257..b4150d60 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs @@ -26,7 +26,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Numerics; using System.Text; #if !NETFX_CORE using NUnit.Framework; @@ -39,6 +38,9 @@ using Newtonsoft.Json; using System.IO; using System.Xml; using Newtonsoft.Json.Utilities; +#if !(NET20 || NET35 || SILVERLIGHT || PORTABLE40 || PORTABLE) +using System.Numerics; +#endif namespace Newtonsoft.Json.Tests { diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj index 4c6692d6..cbee8ff8 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj @@ -238,6 +238,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj index a6a73c28..16358bd4 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj @@ -280,6 +280,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net40.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net40.csproj index 0de0113d..3b46222d 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net40.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net40.csproj @@ -398,6 +398,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable.csproj index c9156e94..54b89ce2 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable.csproj @@ -277,6 +277,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable40.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable40.csproj index 23145fd4..53bd1940 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable40.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable40.csproj @@ -279,6 +279,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index 4c508c25..b6a794d0 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -342,6 +342,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index f776820d..2fa9c1a8 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -75,4 +75,4 @@ using System.Security; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("5.0.4.16023")] +[assembly: AssemblyFileVersion("5.0.4.16024")] diff --git a/Src/Newtonsoft.Json.Tests/Utilities/ExpressionReflectionDelegateFactoryTests.cs b/Src/Newtonsoft.Json.Tests/Utilities/ExpressionReflectionDelegateFactoryTests.cs new file mode 100644 index 00000000..c0351f73 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Utilities/ExpressionReflectionDelegateFactoryTests.cs @@ -0,0 +1,302 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !(PORTABLE40 || NET20 || NET35) +using System; +using System.Diagnostics; +using System.Reflection; +#if !NETFX_CORE +using NUnit.Framework; +#else +using Microsoft.VisualStudio.TestPlatform.UnitTestFramework; +using TestFixture = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestClassAttribute; +using Test = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestMethodAttribute; +#endif +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Tests.TestObjects; +using Newtonsoft.Json.Tests.Serialization; + +namespace Newtonsoft.Json.Tests.Utilities +{ + [TestFixture] + public class ExpressionReflectionDelegateFactoryTests : TestFixtureBase + { + [Test] + public void DefaultConstructor() + { + Func create = ExpressionReflectionDelegateFactory.Instance.CreateDefaultConstructor(typeof(Movie)); + + Movie m = (Movie)create(); + Assert.IsNotNull(m); + } + + [Test] + public void DefaultConstructor_Struct() + { + Func create = ExpressionReflectionDelegateFactory.Instance.CreateDefaultConstructor(typeof(StructTest)); + + StructTest m = (StructTest)create(); + Assert.IsNotNull(m); + } + + [Test] + public void DefaultConstructor_Abstract() + { + ExceptionAssert.Throws("Cannot create an abstract class.", + () => + { + Func create = ExpressionReflectionDelegateFactory.Instance.CreateDefaultConstructor(typeof(Type)); + + create(); + }); + } + + [Test] + public void CreatePropertySetter() + { + Action setter = ExpressionReflectionDelegateFactory.Instance.CreateSet(typeof(Movie).GetProperty("Name")); + + Movie m = new Movie(); + + setter(m, "OH HAI!"); + + Assert.AreEqual("OH HAI!", m.Name); + } + + [Test] + public void CreatePropertyGetter() + { + Func getter = ExpressionReflectionDelegateFactory.Instance.CreateGet(typeof(Movie).GetProperty("Name")); + + Movie m = new Movie(); + m.Name = "OH HAI!"; + + object value = getter(m); + + Assert.AreEqual("OH HAI!", value); + } + + [Test] + public void CreateMethodCall() + { + MethodCall method = ExpressionReflectionDelegateFactory.Instance.CreateMethodCall(typeof(Movie).GetMethod("ToString")); + + Movie m = new Movie(); + object result = method(m); + Assert.AreEqual("Newtonsoft.Json.Tests.TestObjects.Movie", result); + + method = ExpressionReflectionDelegateFactory.Instance.CreateMethodCall(typeof(Movie).GetMethod("Equals")); + + result = method(m, m); + Assert.AreEqual(true, result); + } + + [Test] + public void CreateMethodCall_Constructor() + { + MethodCall method = ExpressionReflectionDelegateFactory.Instance.CreateMethodCall(typeof(Movie).GetConstructor(new Type[0])); + + object result = method(null); + + Assert.IsTrue(result is Movie); + } + + public static class StaticTestClass + { + public static string StringField; + public static string StringProperty { get; set; } + } + + [Test] + public void GetStatic() + { + StaticTestClass.StringField = "Field!"; + StaticTestClass.StringProperty = "Property!"; + + Func getter = ExpressionReflectionDelegateFactory.Instance.CreateGet(typeof(StaticTestClass).GetProperty("StringProperty")); + + object v = getter(null); + Assert.AreEqual(StaticTestClass.StringProperty, v); + + getter = ExpressionReflectionDelegateFactory.Instance.CreateGet(typeof(StaticTestClass).GetField("StringField")); + + v = getter(null); + Assert.AreEqual(StaticTestClass.StringField, v); + } + + [Test] + public void SetStatic() + { + Action setter = ExpressionReflectionDelegateFactory.Instance.CreateSet(typeof(StaticTestClass).GetProperty("StringProperty")); + + setter(null, "New property!"); + Assert.AreEqual("New property!", StaticTestClass.StringProperty); + + setter = ExpressionReflectionDelegateFactory.Instance.CreateSet(typeof(StaticTestClass).GetField("StringField")); + + setter(null, "New field!"); + Assert.AreEqual("New field!", StaticTestClass.StringField); + } + + public class FieldsTestClass + { + public string StringField; + public bool BoolField; + + public readonly int IntReadOnlyField = int.MaxValue; + } + + [Test] + public void CreateGetField() + { + FieldsTestClass c = new FieldsTestClass + { + BoolField = true, + StringField = "String!" + }; + + Func getter = ExpressionReflectionDelegateFactory.Instance.CreateGet(typeof(FieldsTestClass).GetField("StringField")); + + object value = getter(c); + Assert.AreEqual("String!", value); + + getter = ExpressionReflectionDelegateFactory.Instance.CreateGet(typeof(FieldsTestClass).GetField("BoolField")); + + value = getter(c); + Assert.AreEqual(true, value); + } + + [Test] + public void CreateSetField_ReadOnly() + { + FieldsTestClass c = new FieldsTestClass(); + + Action setter = ExpressionReflectionDelegateFactory.Instance.CreateSet(typeof(FieldsTestClass).GetField("IntReadOnlyField")); + + setter(c, int.MinValue); + Assert.AreEqual(int.MinValue, c.IntReadOnlyField); + } + + [Test] + public void CreateSetField() + { + FieldsTestClass c = new FieldsTestClass(); + + Action setter = ExpressionReflectionDelegateFactory.Instance.CreateSet(typeof(FieldsTestClass).GetField("StringField")); + + setter(c, "String!"); + Assert.AreEqual("String!", c.StringField); + + setter = ExpressionReflectionDelegateFactory.Instance.CreateSet(typeof(FieldsTestClass).GetField("BoolField")); + + setter(c, true); + Assert.AreEqual(true, c.BoolField); + } + + [Test] + public void SetOnStruct() + { + object structTest = new StructTest(); + + Action setter = ExpressionReflectionDelegateFactory.Instance.CreateSet(typeof(StructTest).GetProperty("StringProperty")); + + setter(structTest, "Hi1"); + Assert.AreEqual("Hi1", ((StructTest)structTest).StringProperty); + + setter = ExpressionReflectionDelegateFactory.Instance.CreateSet(typeof(StructTest).GetField("StringField")); + + setter(structTest, "Hi2"); + Assert.AreEqual("Hi2", ((StructTest)structTest).StringField); + } + + [Test] + public void CreateGetWithBadObjectTarget() + { + ExceptionAssert.Throws("Unable to cast object of type 'Newtonsoft.Json.Tests.TestObjects.Person' to type 'Newtonsoft.Json.Tests.TestObjects.Movie'.", + () => + { + Person p = new Person(); + p.Name = "Hi"; + + Func setter = ExpressionReflectionDelegateFactory.Instance.CreateGet(typeof(Movie).GetProperty("Name")); + + setter(p); + }); + } + + [Test] + public void CreateSetWithBadObjectTarget() + { + ExceptionAssert.Throws("Unable to cast object of type 'Newtonsoft.Json.Tests.TestObjects.Person' to type 'Newtonsoft.Json.Tests.TestObjects.Movie'.", + () => + { + Person p = new Person(); + Movie m = new Movie(); + + Action setter = ExpressionReflectionDelegateFactory.Instance.CreateSet(typeof(Movie).GetProperty("Name")); + + setter(m, "Hi"); + + Assert.AreEqual(m.Name, "Hi"); + + setter(p, "Hi"); + + Assert.AreEqual(p.Name, "Hi"); + }); + } + + [Test] + public void CreateSetWithBadObjectValue() + { + ExceptionAssert.Throws("Unable to cast object of type 'System.Version' to type 'System.String'.", + () => + { + Movie m = new Movie(); + + Action setter = ExpressionReflectionDelegateFactory.Instance.CreateSet(typeof(Movie).GetProperty("Name")); + + setter(m, new Version("1.1.1.1")); + }); + } + + [Test] + public void CreateStaticMethodCall() + { + MethodInfo castMethodInfo = typeof(JsonSerializerTest.DictionaryKey).GetMethod("op_Implicit", new[] { typeof(string) }); + + Assert.IsNotNull(castMethodInfo); + + MethodCall call = ExpressionReflectionDelegateFactory.Instance.CreateMethodCall(castMethodInfo); + + object result = call(null, "First!"); + Assert.IsNotNull(result); + + JsonSerializerTest.DictionaryKey key = (JsonSerializerTest.DictionaryKey) result; + Assert.AreEqual("First!", key.Value); + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj index 40cba141..9b25fe98 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj @@ -107,6 +107,7 @@ + @@ -114,6 +115,10 @@ + + + + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj index 9825915f..d68bcab8 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj @@ -119,6 +119,7 @@ + @@ -226,7 +227,9 @@ + + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj index bbab0fe9..14e005fb 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj @@ -119,6 +119,7 @@ + @@ -225,7 +226,9 @@ + + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj index ee32ed37..d71f7dc7 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj @@ -147,6 +147,7 @@ + @@ -190,6 +191,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj index 00ac7103..ef3e2a85 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj @@ -154,6 +154,7 @@ + @@ -201,6 +202,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index faff3245..5eae5612 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -154,6 +154,7 @@ + @@ -201,6 +202,7 @@ + diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 668b655c..deaf24ef 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -92,6 +92,6 @@ using System.Security; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("5.0.4.16023")] +[assembly: AssemblyFileVersion("5.0.4.16024")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 9d458d07..f42ebf30 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -926,6 +926,8 @@ namespace Newtonsoft.Json.Serialization valueProvider = new DynamicValueProvider(member); else valueProvider = new ReflectionValueProvider(member); +#elif !(PORTABLE40) + valueProvider = new ExpressionValueProvider(member); #else valueProvider = new ReflectionValueProvider(member); #endif diff --git a/Src/Newtonsoft.Json/Serialization/ExpressionValueProvider.cs b/Src/Newtonsoft.Json/Serialization/ExpressionValueProvider.cs new file mode 100644 index 00000000..faa3213b --- /dev/null +++ b/Src/Newtonsoft.Json/Serialization/ExpressionValueProvider.cs @@ -0,0 +1,113 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !(NET20 || NET35 || PORTABLE40) +using System; +using System.Collections.Generic; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#endif +using System.Text; +using System.Reflection; +using Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Get and set values for a using dynamic methods. + /// + public class ExpressionValueProvider : IValueProvider + { + private readonly MemberInfo _memberInfo; + private Func _getter; + private Action _setter; + + /// + /// Initializes a new instance of the class. + /// + /// The member info. + public ExpressionValueProvider(MemberInfo memberInfo) + { + ValidationUtils.ArgumentNotNull(memberInfo, "memberInfo"); + _memberInfo = memberInfo; + } + + /// + /// Sets the value. + /// + /// The target to set the value on. + /// The value to set on the target. + public void SetValue(object target, object value) + { + try + { + if (_setter == null) + _setter = ExpressionReflectionDelegateFactory.Instance.CreateSet(_memberInfo); + +#if DEBUG + // dynamic method doesn't check whether the type is 'legal' to set + // add this check for unit tests + if (value == null) + { + if (!ReflectionUtils.IsNullable(ReflectionUtils.GetMemberUnderlyingType(_memberInfo))) + throw new JsonSerializationException("Incompatible value. Cannot set {0} to null.".FormatWith(CultureInfo.InvariantCulture, _memberInfo)); + } + else if (!ReflectionUtils.GetMemberUnderlyingType(_memberInfo).IsAssignableFrom(value.GetType())) + { + throw new JsonSerializationException("Incompatible value. Cannot set {0} to type {1}.".FormatWith(CultureInfo.InvariantCulture, _memberInfo, value.GetType())); + } +#endif + + _setter(target, value); + } + catch (Exception ex) + { + throw new JsonSerializationException("Error setting value to '{0}' on '{1}'.".FormatWith(CultureInfo.InvariantCulture, _memberInfo.Name, target.GetType()), ex); + } + } + + /// + /// Gets the value. + /// + /// The target to get the value from. + /// The value. + public object GetValue(object target) + { + try + { + if (_getter == null) + _getter = ExpressionReflectionDelegateFactory.Instance.CreateGet(_memberInfo); + + return _getter(target); + } + catch (Exception ex) + { + throw new JsonSerializationException("Error getting value from '{0}' on '{1}'.".FormatWith(CultureInfo.InvariantCulture, _memberInfo.Name, target.GetType()), ex); + } + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs index baf6d657..ec55e700 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs @@ -436,9 +436,13 @@ namespace Newtonsoft.Json.Serialization #if !(SILVERLIGHT || PORTABLE40 || PORTABLE || NETFX_CORE) if (DynamicCodeGeneration) return DynamicReflectionDelegateFactory.Instance; -#endif return LateBoundReflectionDelegateFactory.Instance; +#elif !(PORTABLE40) + return ExpressionReflectionDelegateFactory.Instance; +#else + return LateBoundReflectionDelegateFactory.Instance; +#endif } } } diff --git a/Src/Newtonsoft.Json/Utilities/ExpressionReflectionDelegateFactory.cs b/Src/Newtonsoft.Json/Utilities/ExpressionReflectionDelegateFactory.cs new file mode 100644 index 00000000..8416e035 --- /dev/null +++ b/Src/Newtonsoft.Json/Utilities/ExpressionReflectionDelegateFactory.cs @@ -0,0 +1,249 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !(PORTABLE40 || NET20 || NET35) +using System; +using System.Linq.Expressions; +using System.Reflection; + +namespace Newtonsoft.Json.Utilities +{ + internal class ExpressionReflectionDelegateFactory : ReflectionDelegateFactory + { + private static readonly ExpressionReflectionDelegateFactory _instance = new ExpressionReflectionDelegateFactory(); + + internal static ReflectionDelegateFactory Instance + { + get { return _instance; } + } + + public override MethodCall CreateMethodCall(MethodBase method) + { + ValidationUtils.ArgumentNotNull(method, "method"); + + Type type = typeof(object); + + ParameterExpression targetParameterExpression = Expression.Parameter(type, "target"); + ParameterExpression argsParameterExpression = Expression.Parameter(typeof(object[]), "args"); + + ParameterInfo[] parametersInfo = method.GetParameters(); + + Expression[] argsExpression = new Expression[parametersInfo.Length]; + + for (int i = 0; i < parametersInfo.Length; i++) + { + Expression indexExpression = Expression.Constant(i); + + Expression paramAccessorExpression = Expression.ArrayIndex(argsParameterExpression, indexExpression); + + paramAccessorExpression = EnsureCastExpression(paramAccessorExpression, parametersInfo[i].ParameterType); + + argsExpression[i] = paramAccessorExpression; + } + + Expression callExpression; + if (method.IsConstructor) + { + callExpression = Expression.New((ConstructorInfo)method, argsExpression); + } + else if (method.IsStatic) + { + callExpression = Expression.Call((MethodInfo)method, argsExpression); + } + else + { + Expression readParameter = EnsureCastExpression(targetParameterExpression, method.DeclaringType); + + callExpression = Expression.Call(readParameter, (MethodInfo)method, argsExpression); + } + + if (method is MethodInfo) + callExpression = EnsureCastExpression(callExpression, type); + + LambdaExpression lambdaExpression = Expression.Lambda(typeof(MethodCall), callExpression, targetParameterExpression, argsParameterExpression); + + MethodCall compiled = (MethodCall)lambdaExpression.Compile(); + return compiled; + } + + public override Func CreateDefaultConstructor(Type type) + { + ValidationUtils.ArgumentNotNull(type, "type"); + + // avoid error from expressions compiler because of abstract class + if (type.IsAbstract()) + return () => (T)Activator.CreateInstance(type); + + Type resultType = typeof(T); + + Expression expression = Expression.New(type); + + expression = EnsureCastExpression(expression, resultType); + + LambdaExpression lambdaExpression = Expression.Lambda(typeof(Func), expression); + + Func compiled = (Func)lambdaExpression.Compile(); + return compiled; + } + + public override Func CreateGet(PropertyInfo propertyInfo) + { + ValidationUtils.ArgumentNotNull(propertyInfo, "propertyInfo"); + + Type instanceType = typeof(T); + Type resultType = typeof(object); + + ParameterExpression parameterExpression = Expression.Parameter(instanceType, "instance"); + Expression resultExpression; + + MethodInfo getMethod = propertyInfo.GetGetMethod(true); + + if (getMethod.IsStatic) + { + resultExpression = Expression.MakeMemberAccess(null, propertyInfo); + } + else + { + Expression readParameter = EnsureCastExpression(parameterExpression, propertyInfo.DeclaringType); + + resultExpression = Expression.MakeMemberAccess(readParameter, propertyInfo); + } + + resultExpression = EnsureCastExpression(resultExpression, resultType); + + LambdaExpression lambdaExpression = Expression.Lambda(typeof(Func), resultExpression, parameterExpression); + + Func compiled = (Func)lambdaExpression.Compile(); + return compiled; + } + + public override Func CreateGet(FieldInfo fieldInfo) + { + ValidationUtils.ArgumentNotNull(fieldInfo, "fieldInfo"); + + ParameterExpression sourceParameter = Expression.Parameter(typeof(T), "source"); + + Expression fieldExpression; + if (fieldInfo.IsStatic) + { + fieldExpression = Expression.Field(null, fieldInfo); + } + else + { + Expression sourceExpression = EnsureCastExpression(sourceParameter, fieldInfo.DeclaringType); + + fieldExpression = Expression.Field(sourceExpression, fieldInfo); + } + + fieldExpression = EnsureCastExpression(fieldExpression, typeof(object)); + + Func compiled = Expression.Lambda>(fieldExpression, sourceParameter).Compile(); + return compiled; + } + + public override Action CreateSet(FieldInfo fieldInfo) + { + ValidationUtils.ArgumentNotNull(fieldInfo, "fieldInfo"); + + // use reflection for structs + // expression doesn't correctly set value + if (fieldInfo.DeclaringType.IsValueType() || fieldInfo.IsInitOnly) + return LateBoundReflectionDelegateFactory.Instance.CreateSet(fieldInfo); + + ParameterExpression sourceParameterExpression = Expression.Parameter(typeof(T), "source"); + ParameterExpression valueParameterExpression = Expression.Parameter(typeof(object), "value"); + + Expression fieldExpression; + if (fieldInfo.IsStatic) + { + fieldExpression = Expression.Field(null, fieldInfo); + } + else + { + Expression sourceExpression = EnsureCastExpression(sourceParameterExpression, fieldInfo.DeclaringType); + + fieldExpression = Expression.Field(sourceExpression, fieldInfo); + } + + Expression valueExpression = EnsureCastExpression(valueParameterExpression, fieldExpression.Type); + + BinaryExpression assignExpression = Expression.Assign(fieldExpression, valueExpression); + + LambdaExpression lambdaExpression = Expression.Lambda(typeof(Action), assignExpression, sourceParameterExpression, valueParameterExpression); + + Action compiled = (Action)lambdaExpression.Compile(); + return compiled; + } + + public override Action CreateSet(PropertyInfo propertyInfo) + { + ValidationUtils.ArgumentNotNull(propertyInfo, "propertyInfo"); + + // use reflection for structs + // expression doesn't correctly set value + if (propertyInfo.DeclaringType.IsValueType()) + return LateBoundReflectionDelegateFactory.Instance.CreateSet(propertyInfo); + + Type instanceType = typeof(T); + Type valueType = typeof(object); + + ParameterExpression instanceParameter = Expression.Parameter(instanceType, "instance"); + + ParameterExpression valueParameter = Expression.Parameter(valueType, "value"); + Expression readValueParameter = EnsureCastExpression(valueParameter, propertyInfo.PropertyType); + + MethodInfo setMethod = propertyInfo.GetSetMethod(true); + + Expression setExpression; + if (setMethod.IsStatic) + { + setExpression = Expression.Call(setMethod, readValueParameter); + } + else + { + Expression readInstanceParameter = EnsureCastExpression(instanceParameter, propertyInfo.DeclaringType); + + setExpression = Expression.Call(readInstanceParameter, setMethod, readValueParameter); + } + + LambdaExpression lambdaExpression = Expression.Lambda(typeof(Action), setExpression, instanceParameter, valueParameter); + + Action compiled = (Action)lambdaExpression.Compile(); + return compiled; + } + + private Expression EnsureCastExpression(Expression expression, Type targetType) + { + Type expressionType = expression.Type; + + // check if a cast or conversion is required + if (expressionType == targetType || (!expressionType.IsValueType() && targetType.IsAssignableFrom(expressionType))) + return expression; + + return Expression.Convert(expression, targetType); + } + } +} +#endif \ No newline at end of file