You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

209 lines
9.2 KiB

  1. #region License
  2. // Copyright (c) 2007 James Newton-King
  3. //
  4. // Permission is hereby granted, free of charge, to any person
  5. // obtaining a copy of this software and associated documentation
  6. // files (the "Software"), to deal in the Software without
  7. // restriction, including without limitation the rights to use,
  8. // copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following
  11. // conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be
  14. // included in all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  18. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  20. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  23. // OTHER DEALINGS IN THE SOFTWARE.
  24. #endregion
  25. #if !NET20
  26. using System;
  27. using System.Collections.Generic;
  28. using System.Dynamic;
  29. using System.Linq;
  30. using System.Linq.Expressions;
  31. #if !HAVE_REFLECTION_BINDER
  32. using System.Reflection;
  33. #else
  34. using Microsoft.CSharp.RuntimeBinder;
  35. #endif
  36. using System.Runtime.CompilerServices;
  37. using System.Text;
  38. using System.Globalization;
  39. using Newtonsoft.Json.Serialization;
  40. namespace Newtonsoft.Json.Utilities
  41. {
  42. internal static class DynamicUtils
  43. {
  44. internal static class BinderWrapper
  45. {
  46. #if !HAVE_REFLECTION_BINDER
  47. public const string CSharpAssemblyName = "Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
  48. private const string BinderTypeName = "Microsoft.CSharp.RuntimeBinder.Binder, " + CSharpAssemblyName;
  49. private const string CSharpArgumentInfoTypeName = "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo, " + CSharpAssemblyName;
  50. private const string CSharpArgumentInfoFlagsTypeName = "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, " + CSharpAssemblyName;
  51. private const string CSharpBinderFlagsTypeName = "Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, " + CSharpAssemblyName;
  52. private static object _getCSharpArgumentInfoArray;
  53. private static object _setCSharpArgumentInfoArray;
  54. private static MethodCall<object, object> _getMemberCall;
  55. private static MethodCall<object, object> _setMemberCall;
  56. private static bool _init;
  57. private static void Init()
  58. {
  59. if (!_init)
  60. {
  61. Type binderType = Type.GetType(BinderTypeName, false);
  62. if (binderType == null)
  63. {
  64. throw new InvalidOperationException("Could not resolve type '{0}'. You may need to add a reference to Microsoft.CSharp.dll to work with dynamic types.".FormatWith(CultureInfo.InvariantCulture, BinderTypeName));
  65. }
  66. // None
  67. _getCSharpArgumentInfoArray = CreateSharpArgumentInfoArray(0);
  68. // None, Constant | UseCompileTimeType
  69. _setCSharpArgumentInfoArray = CreateSharpArgumentInfoArray(0, 3);
  70. CreateMemberCalls();
  71. _init = true;
  72. }
  73. }
  74. private static object CreateSharpArgumentInfoArray(params int[] values)
  75. {
  76. Type csharpArgumentInfoType = Type.GetType(CSharpArgumentInfoTypeName);
  77. Type csharpArgumentInfoFlags = Type.GetType(CSharpArgumentInfoFlagsTypeName);
  78. Array a = Array.CreateInstance(csharpArgumentInfoType, values.Length);
  79. for (int i = 0; i < values.Length; i++)
  80. {
  81. MethodInfo createArgumentInfoMethod = csharpArgumentInfoType.GetMethod("Create", new[] { csharpArgumentInfoFlags, typeof(string) });
  82. object arg = createArgumentInfoMethod.Invoke(null, new object[] { 0, null });
  83. a.SetValue(arg, i);
  84. }
  85. return a;
  86. }
  87. private static void CreateMemberCalls()
  88. {
  89. Type csharpArgumentInfoType = Type.GetType(CSharpArgumentInfoTypeName, true);
  90. Type csharpBinderFlagsType = Type.GetType(CSharpBinderFlagsTypeName, true);
  91. Type binderType = Type.GetType(BinderTypeName, true);
  92. Type csharpArgumentInfoTypeEnumerableType = typeof(IEnumerable<>).MakeGenericType(csharpArgumentInfoType);
  93. MethodInfo getMemberMethod = binderType.GetMethod("GetMember", new[] { csharpBinderFlagsType, typeof(string), typeof(Type), csharpArgumentInfoTypeEnumerableType });
  94. _getMemberCall = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(getMemberMethod);
  95. MethodInfo setMemberMethod = binderType.GetMethod("SetMember", new[] { csharpBinderFlagsType, typeof(string), typeof(Type), csharpArgumentInfoTypeEnumerableType });
  96. _setMemberCall = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(setMemberMethod);
  97. }
  98. #endif
  99. public static CallSiteBinder GetMember(string name, Type context)
  100. {
  101. #if !HAVE_REFLECTION_BINDER
  102. Init();
  103. return (CallSiteBinder)_getMemberCall(null, 0, name, context, _getCSharpArgumentInfoArray);
  104. #else
  105. return Binder.GetMember(
  106. CSharpBinderFlags.None, name, context, new[] {CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)});
  107. #endif
  108. }
  109. public static CallSiteBinder SetMember(string name, Type context)
  110. {
  111. #if !HAVE_REFLECTION_BINDER
  112. Init();
  113. return (CallSiteBinder)_setMemberCall(null, 0, name, context, _setCSharpArgumentInfoArray);
  114. #else
  115. return Binder.SetMember(
  116. CSharpBinderFlags.None, name, context, new[]
  117. {
  118. CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
  119. CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null)
  120. });
  121. #endif
  122. }
  123. }
  124. public static IEnumerable<string> GetDynamicMemberNames(this IDynamicMetaObjectProvider dynamicProvider)
  125. {
  126. DynamicMetaObject metaObject = dynamicProvider.GetMetaObject(Expression.Constant(dynamicProvider));
  127. return metaObject.GetDynamicMemberNames();
  128. }
  129. }
  130. internal class NoThrowGetBinderMember : GetMemberBinder
  131. {
  132. private readonly GetMemberBinder _innerBinder;
  133. public NoThrowGetBinderMember(GetMemberBinder innerBinder)
  134. : base(innerBinder.Name, innerBinder.IgnoreCase)
  135. {
  136. _innerBinder = innerBinder;
  137. }
  138. public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
  139. {
  140. DynamicMetaObject retMetaObject = _innerBinder.Bind(target, CollectionUtils.ArrayEmpty<DynamicMetaObject>());
  141. NoThrowExpressionVisitor noThrowVisitor = new NoThrowExpressionVisitor();
  142. Expression resultExpression = noThrowVisitor.Visit(retMetaObject.Expression);
  143. DynamicMetaObject finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions);
  144. return finalMetaObject;
  145. }
  146. }
  147. internal class NoThrowSetBinderMember : SetMemberBinder
  148. {
  149. private readonly SetMemberBinder _innerBinder;
  150. public NoThrowSetBinderMember(SetMemberBinder innerBinder)
  151. : base(innerBinder.Name, innerBinder.IgnoreCase)
  152. {
  153. _innerBinder = innerBinder;
  154. }
  155. public override DynamicMetaObject FallbackSetMember(DynamicMetaObject target, DynamicMetaObject value, DynamicMetaObject errorSuggestion)
  156. {
  157. DynamicMetaObject retMetaObject = _innerBinder.Bind(target, new DynamicMetaObject[] { value });
  158. NoThrowExpressionVisitor noThrowVisitor = new NoThrowExpressionVisitor();
  159. Expression resultExpression = noThrowVisitor.Visit(retMetaObject.Expression);
  160. DynamicMetaObject finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions);
  161. return finalMetaObject;
  162. }
  163. }
  164. internal class NoThrowExpressionVisitor : ExpressionVisitor
  165. {
  166. internal static readonly object ErrorResult = new object();
  167. protected override Expression VisitConditional(ConditionalExpression node)
  168. {
  169. // if the result of a test is to throw an error, rewrite to result an error result value
  170. if (node.IfFalse.NodeType == ExpressionType.Throw)
  171. {
  172. return Expression.Condition(node.Test, node.IfTrue, Expression.Constant(ErrorResult));
  173. }
  174. return base.VisitConditional(node);
  175. }
  176. }
  177. }
  178. #endif