2
// Copyright (c) 2007 James Newton-King
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
13
// The above copyright notice and this permission notice shall be
14
// included in all copies or substantial portions of the Software.
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.
26
#if !(NET35 || NET20 || WINDOWS_PHONE || PORTABLE)
28
using System.Collections.Generic;
31
using System.Linq.Expressions;
32
using System.Reflection;
34
namespace Newtonsoft.Json.Utilities
36
internal sealed class DynamicProxyMetaObject<T> : DynamicMetaObject
38
private readonly DynamicProxy<T> _proxy;
39
private readonly bool _dontFallbackFirst;
41
internal DynamicProxyMetaObject(Expression expression, T value, DynamicProxy<T> proxy, bool dontFallbackFirst)
42
: base(expression, BindingRestrictions.Empty, value)
45
_dontFallbackFirst = dontFallbackFirst;
48
private new T Value { get { return (T)base.Value; } }
50
private bool IsOverridden(string method)
52
return ReflectionUtils.IsMethodOverridden(_proxy.GetType(), typeof (DynamicProxy<T>), method);
55
public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
57
return IsOverridden("TryGetMember")
58
? CallMethodWithResult("TryGetMember", binder, NoArgs, e => binder.FallbackGetMember(this, e))
59
: base.BindGetMember(binder);
62
public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
64
return IsOverridden("TrySetMember")
65
? CallMethodReturnLast("TrySetMember", binder, GetArgs(value), e => binder.FallbackSetMember(this, value, e))
66
: base.BindSetMember(binder, value);
69
public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder)
71
return IsOverridden("TryDeleteMember")
72
? CallMethodNoResult("TryDeleteMember", binder, NoArgs, e => binder.FallbackDeleteMember(this, e))
73
: base.BindDeleteMember(binder);
77
public override DynamicMetaObject BindConvert(ConvertBinder binder)
79
return IsOverridden("TryConvert")
80
? CallMethodWithResult("TryConvert", binder, NoArgs, e => binder.FallbackConvert(this, e))
81
: base.BindConvert(binder);
84
public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
86
if (!IsOverridden("TryInvokeMember"))
87
return base.BindInvokeMember(binder, args);
90
// Generate a tree like:
94
// TryInvokeMember(payload, out result)
96
// : TryGetMember(payload, out result)
97
// ? FallbackInvoke(result)
101
// Then it calls FallbackInvokeMember with this tree as the
102
// "error", giving the language the option of using this
103
// tree or doing .NET binding.
105
Fallback fallback = e => binder.FallbackInvokeMember(this, args, e);
107
DynamicMetaObject call = BuildCallMethodWithResult(
111
BuildCallMethodWithResult(
113
new GetBinderAdapter(binder),
116
e => binder.FallbackInvoke(e, args, null)
121
return _dontFallbackFirst ? call : fallback(call);
125
public override DynamicMetaObject BindCreateInstance(CreateInstanceBinder binder, DynamicMetaObject[] args)
127
return IsOverridden("TryCreateInstance")
128
? CallMethodWithResult("TryCreateInstance", binder, GetArgArray(args), e => binder.FallbackCreateInstance(this, args, e))
129
: base.BindCreateInstance(binder, args);
132
public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args)
134
return IsOverridden("TryInvoke")
135
? CallMethodWithResult("TryInvoke", binder, GetArgArray(args), e => binder.FallbackInvoke(this, args, e))
136
: base.BindInvoke(binder, args);
139
public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg)
141
return IsOverridden("TryBinaryOperation")
142
? CallMethodWithResult("TryBinaryOperation", binder, GetArgs(arg), e => binder.FallbackBinaryOperation(this, arg, e))
143
: base.BindBinaryOperation(binder, arg);
146
public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder)
148
return IsOverridden("TryUnaryOperation")
149
? CallMethodWithResult("TryUnaryOperation", binder, NoArgs, e => binder.FallbackUnaryOperation(this, e))
150
: base.BindUnaryOperation(binder);
153
public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes)
155
return IsOverridden("TryGetIndex")
156
? CallMethodWithResult("TryGetIndex", binder, GetArgArray(indexes), e => binder.FallbackGetIndex(this, indexes, e))
157
: base.BindGetIndex(binder, indexes);
160
public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value)
162
return IsOverridden("TrySetIndex")
163
? CallMethodReturnLast("TrySetIndex", binder, GetArgArray(indexes, value), e => binder.FallbackSetIndex(this, indexes, value, e))
164
: base.BindSetIndex(binder, indexes, value);
167
public override DynamicMetaObject BindDeleteIndex(DeleteIndexBinder binder, DynamicMetaObject[] indexes)
169
return IsOverridden("TryDeleteIndex")
170
? CallMethodNoResult("TryDeleteIndex", binder, GetArgArray(indexes), e => binder.FallbackDeleteIndex(this, indexes, e))
171
: base.BindDeleteIndex(binder, indexes);
174
private delegate DynamicMetaObject Fallback(DynamicMetaObject errorSuggestion);
176
private readonly static Expression[] NoArgs = new Expression[0];
178
private static Expression[] GetArgs(params DynamicMetaObject[] args)
180
return args.Select(arg => Expression.Convert(arg.Expression, typeof(object))).ToArray();
183
private static Expression[] GetArgArray(DynamicMetaObject[] args)
185
return new[] { Expression.NewArrayInit(typeof(object), GetArgs(args)) };
188
private static Expression[] GetArgArray(DynamicMetaObject[] args, DynamicMetaObject value)
190
return new Expression[]
192
Expression.NewArrayInit(typeof(object), GetArgs(args)),
193
Expression.Convert(value.Expression, typeof(object))
197
private static ConstantExpression Constant(DynamicMetaObjectBinder binder)
199
Type t = binder.GetType();
200
while (!t.IsVisible())
202
return Expression.Constant(binder, t);
206
/// Helper method for generating a MetaObject which calls a
207
/// specific method on Dynamic that returns a result
209
private DynamicMetaObject CallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback, Fallback fallbackInvoke = null)
212
// First, call fallback to do default binding
213
// This produces either an error or a call to a .NET member
215
DynamicMetaObject fallbackResult = fallback(null);
217
DynamicMetaObject callDynamic = BuildCallMethodWithResult(methodName, binder, args, fallbackResult, fallbackInvoke);
220
// Now, call fallback again using our new MO as the error
221
// When we do this, one of two things can happen:
222
// 1. Binding will succeed, and it will ignore our call to
223
// the dynamic method, OR
224
// 2. Binding will fail, and it will use the MO we created
228
return _dontFallbackFirst ? callDynamic : fallback(callDynamic);
231
private DynamicMetaObject BuildCallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, DynamicMetaObject fallbackResult, Fallback fallbackInvoke)
234
// Build a new expression like:
237
// TryGetMember(payload, out result) ? fallbackInvoke(result) : fallbackResult
240
ParameterExpression result = Expression.Parameter(typeof(object), null);
242
IList<Expression> callArgs = new List<Expression>();
243
callArgs.Add(Expression.Convert(Expression, typeof(T)));
244
callArgs.Add(Constant(binder));
245
callArgs.AddRange(args);
246
callArgs.Add(result);
248
DynamicMetaObject resultMetaObject = new DynamicMetaObject(result, BindingRestrictions.Empty);
250
// Need to add a conversion if calling TryConvert
251
if (binder.ReturnType != typeof (object))
253
UnaryExpression convert = Expression.Convert(resultMetaObject.Expression, binder.ReturnType);
254
// will always be a cast or unbox
256
resultMetaObject = new DynamicMetaObject(convert, resultMetaObject.Restrictions);
259
if (fallbackInvoke != null)
260
resultMetaObject = fallbackInvoke(resultMetaObject);
262
DynamicMetaObject callDynamic = new DynamicMetaObject(
265
Expression.Condition(
267
Expression.Constant(_proxy),
268
typeof(DynamicProxy<T>).GetMethod(methodName),
271
resultMetaObject.Expression,
272
fallbackResult.Expression,
276
GetRestrictions().Merge(resultMetaObject.Restrictions).Merge(fallbackResult.Restrictions)
283
/// Helper method for generating a MetaObject which calls a
284
/// specific method on Dynamic, but uses one of the arguments for
287
private DynamicMetaObject CallMethodReturnLast(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback)
290
// First, call fallback to do default binding
291
// This produces either an error or a call to a .NET member
293
DynamicMetaObject fallbackResult = fallback(null);
296
// Build a new expression like:
299
// TrySetMember(payload, result = value) ? result : fallbackResult
302
ParameterExpression result = Expression.Parameter(typeof(object), null);
304
IList<Expression> callArgs = new List<Expression>();
305
callArgs.Add(Expression.Convert(Expression, typeof (T)));
306
callArgs.Add(Constant(binder));
307
callArgs.AddRange(args);
308
callArgs[args.Length + 1] = Expression.Assign(result, callArgs[args.Length + 1]);
310
DynamicMetaObject callDynamic = new DynamicMetaObject(
313
Expression.Condition(
315
Expression.Constant(_proxy),
316
typeof(DynamicProxy<T>).GetMethod(methodName),
320
fallbackResult.Expression,
324
GetRestrictions().Merge(fallbackResult.Restrictions)
328
// Now, call fallback again using our new MO as the error
329
// When we do this, one of two things can happen:
330
// 1. Binding will succeed, and it will ignore our call to
331
// the dynamic method, OR
332
// 2. Binding will fail, and it will use the MO we created
335
return _dontFallbackFirst ? callDynamic : fallback(callDynamic);
339
/// Helper method for generating a MetaObject which calls a
340
/// specific method on Dynamic, but uses one of the arguments for
343
private DynamicMetaObject CallMethodNoResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback)
346
// First, call fallback to do default binding
347
// This produces either an error or a call to a .NET member
349
DynamicMetaObject fallbackResult = fallback(null);
351
IList<Expression> callArgs = new List<Expression>();
352
callArgs.Add(Expression.Convert(Expression, typeof(T)));
353
callArgs.Add(Constant(binder));
354
callArgs.AddRange(args);
357
// Build a new expression like:
358
// if (TryDeleteMember(payload)) { } else { fallbackResult }
360
DynamicMetaObject callDynamic = new DynamicMetaObject(
361
Expression.Condition(
363
Expression.Constant(_proxy),
364
typeof(DynamicProxy<T>).GetMethod(methodName),
368
fallbackResult.Expression,
371
GetRestrictions().Merge(fallbackResult.Restrictions)
375
// Now, call fallback again using our new MO as the error
376
// When we do this, one of two things can happen:
377
// 1. Binding will succeed, and it will ignore our call to
378
// the dynamic method, OR
379
// 2. Binding will fail, and it will use the MO we created
382
return _dontFallbackFirst ? callDynamic : fallback(callDynamic);
386
/// Returns a Restrictions object which includes our current restrictions merged
387
/// with a restriction limiting our type
389
private BindingRestrictions GetRestrictions()
391
return (Value == null && HasValue)
392
? BindingRestrictions.GetInstanceRestriction(Expression, null)
393
: BindingRestrictions.GetTypeRestriction(Expression, LimitType);
396
public override IEnumerable<string> GetDynamicMemberNames()
398
return _proxy.GetDynamicMemberNames(Value);
401
// It is okay to throw NotSupported from this binder. This object
402
// is only used by DynamicObject.GetMember--it is not expected to
403
// (and cannot) implement binding semantics. It is just so the DO
404
// can use the Name and IgnoreCase properties.
405
private sealed class GetBinderAdapter : GetMemberBinder
407
internal GetBinderAdapter(InvokeMemberBinder binder) :
408
base(binder.Name, binder.IgnoreCase)
412
public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
414
throw new NotSupportedException();
b'\\ No newline at end of file'