File: DynamicUtils.cs

package info (click to toggle)
mono 4.6.2.7+dfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 778,148 kB
  • ctags: 914,052
  • sloc: cs: 5,779,509; xml: 2,773,713; ansic: 432,645; sh: 14,749; makefile: 12,361; perl: 2,488; python: 1,434; cpp: 849; asm: 531; sql: 95; sed: 16; php: 1
file content (200 lines) | stat: -rw-r--r-- 8,307 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#if !(NET35 || NET20 || WINDOWS_PHONE)
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Globalization;
using Newtonsoft.Json.Serialization;

namespace Newtonsoft.Json.Utilities
{
  internal static class DynamicUtils
  {
    internal static class BinderWrapper
    {
#if !SILVERLIGHT
      public const string CSharpAssemblyName = "Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
#else
      public const string CSharpAssemblyName = "Microsoft.CSharp, Version=2.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35";
#endif

      private const string BinderTypeName = "Microsoft.CSharp.RuntimeBinder.Binder, " + CSharpAssemblyName;
      private const string CSharpArgumentInfoTypeName = "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo, " + CSharpAssemblyName;
      private const string CSharpArgumentInfoFlagsTypeName = "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, " + CSharpAssemblyName;
      private const string CSharpBinderFlagsTypeName = "Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, " + CSharpAssemblyName;

      private static object _getCSharpArgumentInfoArray;
      private static object _setCSharpArgumentInfoArray;
      private static MethodCall<object, object> _getMemberCall;
      private static MethodCall<object, object> _setMemberCall;
      private static bool _init;

      private static void Init()
      {
        if (!_init)
        {
          Type binderType = Type.GetType(BinderTypeName, false);
          if (binderType == null)
            throw new Exception("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));

          // None
          _getCSharpArgumentInfoArray = CreateSharpArgumentInfoArray(0);
          // None, Constant | UseCompileTimeType
          _setCSharpArgumentInfoArray = CreateSharpArgumentInfoArray(0, 3);
          CreateMemberCalls();

          _init = true;
        }
      }

      private static object CreateSharpArgumentInfoArray(params int[] values)
      {
        Type csharpArgumentInfoType = Type.GetType(CSharpArgumentInfoTypeName);
        Type csharpArgumentInfoFlags = Type.GetType(CSharpArgumentInfoFlagsTypeName);

        Array a = Array.CreateInstance(csharpArgumentInfoType, values.Length);

        for (int i = 0; i < values.Length; i++)
        {
          MethodInfo createArgumentInfoMethod = csharpArgumentInfoType.GetMethod("Create", BindingFlags.Public | BindingFlags.Static, null, new[] { csharpArgumentInfoFlags, typeof(string) }, null);
          object arg = createArgumentInfoMethod.Invoke(null, new object[] { 0, null });
          a.SetValue(arg, i);
        }

        return a;
      }

      private static void CreateMemberCalls()
      {
        Type csharpArgumentInfoType = Type.GetType(CSharpArgumentInfoTypeName);
        Type csharpBinderFlagsType = Type.GetType(CSharpBinderFlagsTypeName);
        Type binderType = Type.GetType(BinderTypeName);

        Type csharpArgumentInfoTypeEnumerableType = typeof(IEnumerable<>).MakeGenericType(csharpArgumentInfoType);

        MethodInfo getMemberMethod = binderType.GetMethod("GetMember", BindingFlags.Public | BindingFlags.Static, null, new[] { csharpBinderFlagsType, typeof(string), typeof(Type), csharpArgumentInfoTypeEnumerableType }, null);
        _getMemberCall = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(getMemberMethod);

        MethodInfo setMemberMethod = binderType.GetMethod("SetMember", BindingFlags.Public | BindingFlags.Static, null, new[] { csharpBinderFlagsType, typeof(string), typeof(Type), csharpArgumentInfoTypeEnumerableType }, null);
        _setMemberCall = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(setMemberMethod);
      }

      public static CallSiteBinder GetMember(string name, Type context)
      {
        Init();
        return (CallSiteBinder)_getMemberCall(null, 0, name, context, _getCSharpArgumentInfoArray);
      }

      public static CallSiteBinder SetMember(string name, Type context)
      {
        Init();
        return (CallSiteBinder)_setMemberCall(null, 0, name, context, _setCSharpArgumentInfoArray);
      }
    }

    public static bool TryGetMember(this IDynamicMetaObjectProvider dynamicProvider, string name, out object value)
    {
      ValidationUtils.ArgumentNotNull(dynamicProvider, "dynamicProvider");

      GetMemberBinder getMemberBinder = (GetMemberBinder) BinderWrapper.GetMember(name, typeof (DynamicUtils));

      CallSite<Func<CallSite, object, object>> callSite = CallSite<Func<CallSite, object, object>>.Create(new NoThrowGetBinderMember(getMemberBinder));

      object result = callSite.Target(callSite, dynamicProvider);

      if (!ReferenceEquals(result, NoThrowExpressionVisitor.ErrorResult))
      {
        value = result;
        return true;
      }
      else
      {
        value = null;
        return false;
      }
    }

    public static bool TrySetMember(this IDynamicMetaObjectProvider dynamicProvider, string name, object value)
    {
      ValidationUtils.ArgumentNotNull(dynamicProvider, "dynamicProvider");

      SetMemberBinder binder = (SetMemberBinder)BinderWrapper.SetMember(name, typeof(DynamicUtils));

      var setterSite = CallSite<Func<CallSite, object, object, object>>.Create(new NoThrowSetBinderMember(binder));

      object result = setterSite.Target(setterSite, dynamicProvider, value);

      return !ReferenceEquals(result, NoThrowExpressionVisitor.ErrorResult);
    }

    public static IEnumerable<string> GetDynamicMemberNames(this IDynamicMetaObjectProvider dynamicProvider)
    {
      DynamicMetaObject metaObject = dynamicProvider.GetMetaObject(Expression.Constant(dynamicProvider));
      return metaObject.GetDynamicMemberNames();
    }

    internal class NoThrowGetBinderMember : GetMemberBinder
    {
      private readonly GetMemberBinder _innerBinder;

      public NoThrowGetBinderMember(GetMemberBinder innerBinder)
        : base(innerBinder.Name, innerBinder.IgnoreCase)
      {
        _innerBinder = innerBinder;
      }

      public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
      {
        DynamicMetaObject retMetaObject = _innerBinder.Bind(target, new DynamicMetaObject[] { });

        NoThrowExpressionVisitor noThrowVisitor = new NoThrowExpressionVisitor();
        Expression resultExpression = noThrowVisitor.Visit(retMetaObject.Expression);

        DynamicMetaObject finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions);
        return finalMetaObject;
      }
    }

    internal class NoThrowSetBinderMember : SetMemberBinder
    {
      private readonly SetMemberBinder _innerBinder;

      public NoThrowSetBinderMember(SetMemberBinder innerBinder)
        : base(innerBinder.Name, innerBinder.IgnoreCase)
      {
        _innerBinder = innerBinder;
      }

      public override DynamicMetaObject FallbackSetMember(DynamicMetaObject target, DynamicMetaObject value, DynamicMetaObject errorSuggestion)
      {
        DynamicMetaObject retMetaObject = _innerBinder.Bind(target, new DynamicMetaObject[] { value });

        NoThrowExpressionVisitor noThrowVisitor = new NoThrowExpressionVisitor();
        Expression resultExpression = noThrowVisitor.Visit(retMetaObject.Expression);

        DynamicMetaObject finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions);
        return finalMetaObject;
      }
    }


    internal class NoThrowExpressionVisitor : ExpressionVisitor
    {
      internal static readonly object ErrorResult = new object();

      protected override Expression VisitConditional(ConditionalExpression node)
      {
        // if the result of a test is to throw an error, rewrite to result an error result value
        if (node.IfFalse.NodeType == ExpressionType.Throw)
          return Expression.Condition(node.Test, node.IfTrue, Expression.Constant(ErrorResult));

        return base.VisitConditional(node);
      }
    }
  }
}
#endif