PageRenderTime 54ms CodeModel.GetById 11ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 0ms

/WCFWebApi/src/Microsoft.Json/System/Json/JsonValueDynamicMetaObject.cs

#
C# | 1067 lines | 693 code | 150 blank | 224 comment | 149 complexity | 859cd634438c5865541e6eafd23aeef8 MD5 | raw file
   1// <copyright file="JsonValueDynamicMetaObject.cs" company="Microsoft Corporation">
   2//   Copyright (c) Microsoft Corporation.  All rights reserved.
   3// </copyright>
   4
   5// TODO: Remove CODEPLEX define once CSDMain 234546 has been resolved
   6#define CODEPLEX
   7namespace System.Json
   8{
   9    using System;
  10    using System.Collections.Generic;
  11    using System.Diagnostics.CodeAnalysis;
  12    using System.Dynamic;
  13    using System.Linq.Expressions;
  14    using System.Reflection;
  15    using System.Runtime.Serialization.Json;
  16    using Microsoft.Server.Common;
  17    
  18    /// <summary>
  19    /// This class provides dynamic behavior support for the JsonValue types.
  20    /// </summary>
  21    internal class JsonValueDynamicMetaObject : DynamicMetaObject
  22    {
  23        private static readonly MethodInfo GetValueByIndexMethodInfo = typeof(JsonValue).GetMethod("GetValue", new Type[] { typeof(int) });
  24        private static readonly MethodInfo GetValueByKeyMethodInfo = typeof(JsonValue).GetMethod("GetValue", new Type[] { typeof(string) });
  25        private static readonly MethodInfo SetValueByIndexMethodInfo = typeof(JsonValue).GetMethod("SetValue", new Type[] { typeof(int), typeof(object) });
  26        private static readonly MethodInfo SetValueByKeyMethodInfo = typeof(JsonValue).GetMethod("SetValue", new Type[] { typeof(string), typeof(object) });
  27        private static readonly MethodInfo CastValueMethodInfo = typeof(JsonValue).GetMethod("CastValue", new Type[] { typeof(JsonValue) });
  28        private static readonly MethodInfo ChangeTypeMethodInfo = typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(Type) });
  29#if CODEPLEX
  30        private static readonly MethodInfo ReadAsMethodInfo = typeof(JsonValue).GetMethod("ReadAs", new Type[] { typeof(Type) });
  31#endif
  32
  33        /// <summary>
  34        /// Class constructor.
  35        /// </summary>
  36        /// <param name="parameter">The expression representing this <see cref="DynamicMetaObject"/> during the dynamic binding process.</param>
  37        /// <param name="value">The runtime value represented by the <see cref="DynamicMetaObject"/>.</param>
  38        internal JsonValueDynamicMetaObject(Expression parameter, JsonValue value)
  39            : base(parameter, BindingRestrictions.Empty, value)
  40        {
  41        }
  42#if CODEPLEX
  43        /// <summary>
  44        /// Represents the level of support for operations.
  45        /// </summary>
  46        private enum OperationSupport
  47        {
  48            /// <summary>
  49            /// Operation fully supported on operands.
  50            /// </summary>
  51            Supported,
  52
  53            /// <summary>
  54            /// Operation not supported on operand.
  55            /// </summary>
  56            NotSupported,
  57
  58            /// <summary>
  59            /// Operation not supported on a <see cref="JsonValue "/> instance of certain <see cref="JsonType"/> type.
  60            /// </summary>
  61            NotSupportedOnJsonType,
  62
  63            /// <summary>
  64            /// Operation not supported on second operand type.
  65            /// </summary>
  66            NotSupportedOnOperand,
  67
  68            /// <summary>
  69            ///  Operation not supported on second operand's value type.
  70            /// </summary>
  71            NotSupportedOnValueType
  72        }
  73#endif
  74
  75        /// <summary>
  76        /// Gets the default binding restrictions for this type.
  77        /// </summary>
  78        private BindingRestrictions DefaultRestrictions
  79        {
  80            get { return BindingRestrictions.GetTypeRestriction(this.Expression, this.LimitType); }
  81        }
  82#if CODEPLEX
  83        /// <summary>
  84        /// Performs the binding of the dynamic unary operation.
  85        /// </summary>
  86        /// <param name="binder">An instance of the <see cref="UnaryOperationBinder"/> that represents the details of the dynamic operation.</param>
  87        /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
  88        public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder)
  89        {
  90            if (binder == null)
  91            {
  92                throw Fx.Exception.ArgumentNull("binder");
  93            }
  94
  95            Expression operExpression = null;
  96            JsonValue jsonValue = this.Value as JsonValue;
  97
  98            if (jsonValue is JsonPrimitive)
  99            {
 100                OperationSupport supportValue = GetUnaryOperationSupport(binder.Operation, jsonValue);
 101
 102                if (supportValue == OperationSupport.Supported)
 103                {
 104                    Type operationReturnType = this.GetUnaryOperationReturnType(binder);
 105
 106                    Expression instance = Expression.Convert(this.Expression, this.LimitType);
 107                    Expression thisExpression = Expression.Convert(Expression.Call(instance, ReadAsMethodInfo, new Expression[] { Expression.Constant(operationReturnType) }), operationReturnType);
 108
 109                    operExpression = JsonValueDynamicMetaObject.GetUnaryOperationExpression(binder.Operation, thisExpression);
 110                }
 111            }
 112
 113            if (operExpression == null)
 114            {
 115                operExpression = JsonValueDynamicMetaObject.GetOperationErrorExpression(OperationSupport.NotSupportedOnJsonType, binder.Operation, jsonValue, null);
 116            }
 117
 118            operExpression = Expression.Convert(operExpression, binder.ReturnType);
 119
 120            return new DynamicMetaObject(operExpression, this.DefaultRestrictions);
 121        }
 122
 123        /// <summary>
 124        /// Performs the binding of the dynamic binary operation.
 125        /// </summary>
 126        /// <param name="binder">An instance of the <see cref="BinaryOperationBinder"/> that represents the details of the dynamic operation.</param>
 127        /// <param name="arg">An instance of the <see cref="DynamicMetaObject"/> representing the right hand side of the binary operation.</param>
 128        /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
 129        public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg)
 130        {
 131            if (binder == null)
 132            {
 133                throw Fx.Exception.ArgumentNull("binder");
 134            }
 135
 136            if (arg == null)
 137            {
 138                throw Fx.Exception.ArgumentNull("arg");
 139            }
 140
 141            Expression thisExpression = this.Expression;
 142            Expression otherExpression = arg.Expression;
 143            Expression operExpression = null;
 144
 145            JsonValue otherValue = arg.Value as JsonValue;
 146            JsonValue thisValue = this.Value as JsonValue;
 147
 148            OperationSupport supportValue = JsonValueDynamicMetaObject.GetBinaryOperationSupport(binder.Operation, thisValue, arg.Value);
 149
 150            if (supportValue == OperationSupport.Supported)
 151            {
 152                if (otherValue != null)
 153                {
 154                    if (thisValue is JsonPrimitive && otherValue is JsonPrimitive)
 155                    {
 156                        //// operation on primitive types.
 157
 158                        JsonValueDynamicMetaObject.GetBinaryOperandExpressions(binder.Operation, this, arg, ref thisExpression, ref otherExpression);
 159                    }
 160                    else
 161                    {
 162                        //// operation on JsonValue types.
 163
 164                        thisExpression = Expression.Convert(thisExpression, typeof(JsonValue));
 165                        otherExpression = Expression.Convert(otherExpression, typeof(JsonValue));
 166                    }
 167                }
 168                else
 169                {
 170                    if (arg.Value != null)
 171                    {
 172                        //// operation on JSON primitive and CLR primitive
 173
 174                        JsonValueDynamicMetaObject.GetBinaryOperandExpressions(binder.Operation, this, arg, ref thisExpression, ref otherExpression);
 175                    }
 176                    else
 177                    {
 178                        //// operation on JsonValue and null.
 179
 180                        thisExpression = Expression.Convert(thisExpression, typeof(JsonValue));
 181
 182                        if (thisValue.JsonType == JsonType.Default)
 183                        {
 184                            thisExpression = Expression.Constant(null);
 185                        }
 186                    }
 187                }
 188
 189                operExpression = JsonValueDynamicMetaObject.GetBinaryOperationExpression(binder.Operation, thisExpression, otherExpression);
 190            }
 191
 192            if (operExpression == null)
 193            {
 194                operExpression = JsonValueDynamicMetaObject.GetOperationErrorExpression(supportValue, binder.Operation, thisValue, arg.Value);
 195            }
 196
 197            operExpression = Expression.Convert(operExpression, typeof(object));
 198
 199            return new DynamicMetaObject(operExpression, this.DefaultRestrictions);
 200        }
 201#endif
 202
 203        /// <summary>
 204        /// Implements dynamic cast for JsonValue types.
 205        /// </summary>
 206        /// <param name="binder">An instance of the <see cref="ConvertBinder"/> that represents the details of the dynamic operation.</param>
 207        /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
 208        public override DynamicMetaObject BindConvert(ConvertBinder binder)
 209        {
 210            if (binder == null)
 211            {
 212                throw Fx.Exception.ArgumentNull("binder");
 213            }
 214
 215            Expression expression = this.Expression;
 216
 217            bool implicitCastSupported =
 218                binder.Type.IsAssignableFrom(this.LimitType) ||
 219                binder.Type == typeof(IEnumerable<KeyValuePair<string, JsonValue>>) ||
 220                binder.Type == typeof(IDynamicMetaObjectProvider) ||
 221                binder.Type == typeof(object);
 222
 223            if (!implicitCastSupported)
 224            {
 225                if (JsonValue.IsSupportedExplicitCastType(binder.Type))
 226                {
 227                    Expression instance = Expression.Convert(this.Expression, this.LimitType);
 228                    expression = Expression.Call(CastValueMethodInfo.MakeGenericMethod(binder.Type), new Expression[] { instance });
 229                }
 230                else
 231                {
 232                    string exceptionMessage = SR.CannotCastJsonValue(this.LimitType.FullName, binder.Type.FullName);
 233                    expression = Expression.Throw(Expression.Constant(new InvalidCastException(exceptionMessage)), typeof(object));
 234                }
 235            }
 236
 237            expression = Expression.Convert(expression, binder.Type);
 238
 239            return new DynamicMetaObject(expression, this.DefaultRestrictions);
 240        }
 241
 242        /// <summary>
 243        /// Implements setter for dynamic indexer by index (JsonArray)
 244        /// </summary>
 245        /// <param name="binder">An instance of the <see cref="GetIndexBinder"/> that represents the details of the dynamic operation.</param>
 246        /// <param name="indexes">An array of <see cref="DynamicMetaObject"/> instances - indexes for the get index operation.</param>
 247        /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
 248        public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes)
 249        {
 250            if (binder == null)
 251            {
 252                throw Fx.Exception.ArgumentNull("binder");
 253            }
 254
 255            if (indexes == null)
 256            {
 257                throw Fx.Exception.ArgumentNull("indexes");
 258            }
 259
 260            Expression indexExpression;
 261            if (!JsonValueDynamicMetaObject.TryGetIndexExpression(indexes, out indexExpression))
 262            {
 263                return new DynamicMetaObject(indexExpression, this.DefaultRestrictions);
 264            }
 265
 266            MethodInfo methodInfo = indexExpression.Type == typeof(string) ? GetValueByKeyMethodInfo : GetValueByIndexMethodInfo;
 267            Expression[] args = new Expression[] { indexExpression };
 268
 269            return this.GetMethodMetaObject(methodInfo, args);
 270        }
 271
 272        /// <summary>
 273        /// Implements getter for dynamic indexer by index (JsonArray).
 274        /// </summary>
 275        /// <param name="binder">An instance of the <see cref="SetIndexBinder"/> that represents the details of the dynamic operation.</param>
 276        /// <param name="indexes">An array of <see cref="DynamicMetaObject"/> instances - indexes for the set index operation.</param>
 277        /// <param name="value">The <see cref="DynamicMetaObject"/> representing the value for the set index operation.</param>
 278        /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
 279        public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value)
 280        {
 281            if (binder == null)
 282            {
 283                throw Fx.Exception.ArgumentNull("binder");
 284            }
 285
 286            if (indexes == null)
 287            {
 288                throw Fx.Exception.ArgumentNull("indexes");
 289            }
 290
 291            if (value == null)
 292            {
 293                throw Fx.Exception.ArgumentNull("value");
 294            }
 295
 296            Expression indexExpression;
 297            if (!JsonValueDynamicMetaObject.TryGetIndexExpression(indexes, out indexExpression))
 298            {
 299                return new DynamicMetaObject(indexExpression, this.DefaultRestrictions);
 300            }
 301
 302            MethodInfo methodInfo = indexExpression.Type == typeof(string) ? SetValueByKeyMethodInfo : SetValueByIndexMethodInfo;
 303            Expression[] args = new Expression[] { indexExpression, Expression.Convert(value.Expression, typeof(object)) };
 304
 305            return this.GetMethodMetaObject(methodInfo, args);
 306        }
 307
 308        /// <summary>
 309        /// Implements getter for dynamic indexer by key (JsonObject).
 310        /// </summary>
 311        /// <param name="binder">An instance of the <see cref="GetMemberBinder"/> that represents the details of the dynamic operation.</param>
 312        /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
 313        public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
 314        {
 315            if (binder == null)
 316            {
 317                throw Fx.Exception.ArgumentNull("binder");
 318            }
 319
 320            PropertyInfo propInfo = this.LimitType.GetProperty(binder.Name, BindingFlags.Instance | BindingFlags.Public);
 321
 322            if (propInfo != null)
 323            {
 324                return base.BindGetMember(binder);
 325            }
 326
 327            Expression[] args = new Expression[] { Expression.Constant(binder.Name) };
 328
 329            return this.GetMethodMetaObject(GetValueByKeyMethodInfo, args);
 330        }
 331
 332        /// <summary>
 333        /// Implements setter for dynamic indexer by key (JsonObject).
 334        /// </summary>
 335        /// <param name="binder">An instance of the <see cref="SetMemberBinder"/> that represents the details of the dynamic operation.</param>
 336        /// <param name="value">The <see cref="DynamicMetaObject"/> representing the value for the set member operation.</param>
 337        /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
 338        public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
 339        {
 340            if (binder == null)
 341            {
 342                throw Fx.Exception.ArgumentNull("binder");
 343            }
 344
 345            if (value == null)
 346            {
 347                throw Fx.Exception.ArgumentNull("value");
 348            }
 349
 350            Expression[] args = new Expression[] { Expression.Constant(binder.Name), Expression.Convert(value.Expression, typeof(object)) };
 351
 352            return this.GetMethodMetaObject(SetValueByKeyMethodInfo, args);
 353        }
 354
 355        /// <summary>
 356        /// Performs the binding of the dynamic invoke member operation.
 357        /// Implemented to support extension methods defined in <see cref="JsonValueExtensions"/> type.
 358        /// </summary>
 359        /// <param name="binder">An instance of the InvokeMemberBinder that represents the details of the dynamic operation.</param>
 360        /// <param name="args">An array of DynamicMetaObject instances - arguments to the invoke member operation.</param>
 361        /// <returns>The new DynamicMetaObject representing the result of the binding.</returns>
 362        public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
 363        {
 364            if (binder == null)
 365            {
 366                throw Fx.Exception.ArgumentNull("binder");
 367            }
 368
 369            if (args == null)
 370            {
 371                throw Fx.Exception.ArgumentNull("args");
 372            }
 373
 374            List<Type> argTypeList = new List<Type>();
 375
 376            for (int idx = 0; idx < args.Length; idx++)
 377            {
 378                argTypeList.Add(args[idx].LimitType);
 379            }
 380
 381            MethodInfo methodInfo = this.Value.GetType().GetMethod(binder.Name, argTypeList.ToArray());
 382
 383            if (methodInfo == null)
 384            {
 385                argTypeList.Insert(0, typeof(JsonValue));
 386
 387                Type[] argTypes = argTypeList.ToArray();
 388
 389                methodInfo = JsonValueDynamicMetaObject.GetExtensionMethod(typeof(JsonValueExtensions), binder.Name, argTypes);
 390
 391                if (methodInfo != null)
 392                {
 393                    Expression thisInstance = Expression.Convert(this.Expression, this.LimitType);
 394                    Expression[] argsExpression = new Expression[argTypes.Length];
 395
 396                    argsExpression[0] = thisInstance;
 397                    for (int i = 0; i < args.Length; i++)
 398                    {
 399                        argsExpression[i + 1] = args[i].Expression;
 400                    }
 401
 402                    Expression callExpression = Expression.Call(methodInfo, argsExpression);
 403
 404                    if (methodInfo.ReturnType == typeof(void))
 405                    {
 406                        callExpression = Expression.Block(callExpression, Expression.Default(binder.ReturnType));
 407                    }
 408                    else
 409                    {
 410                        callExpression = Expression.Convert(Expression.Call(methodInfo, argsExpression), binder.ReturnType);
 411                    }
 412
 413                    return new DynamicMetaObject(callExpression, this.DefaultRestrictions);
 414                }
 415            }
 416
 417            return base.BindInvokeMember(binder, args);
 418        }
 419
 420        /// <summary>
 421        /// Returns the enumeration of all dynamic member names.
 422        /// </summary>
 423        /// <returns>An <see cref="IEnumerable{T}"/> of string reprenseting the dynamic member names.</returns>
 424        public override IEnumerable<string> GetDynamicMemberNames()
 425        {
 426            JsonValue jsonValue = this.Value as JsonValue;
 427
 428            if (jsonValue != null)
 429            {
 430                List<string> names = new List<string>();
 431
 432                foreach (KeyValuePair<string, JsonValue> pair in jsonValue)
 433                {
 434                    names.Add(pair.Key);
 435                }
 436
 437                return names;
 438            }
 439
 440            return base.GetDynamicMemberNames();
 441        }
 442#if CODEPLEX
 443        /// <summary>
 444        /// Gets the operation support value for the specified operation on the specified operand.
 445        /// </summary>
 446        /// <param name="operation">The operation type.</param>
 447        /// <param name="thisValue">The JsonValue instance to check operation for.</param>
 448        /// <returns>An <see cref="OperationSupport"/> value.</returns>
 449        private static OperationSupport GetUnaryOperationSupport(ExpressionType operation, JsonValue thisValue)
 450        {
 451            //// Unary operators: +, -, !, ~, false (&&), true (||)
 452            //// unsupported: ++, --
 453
 454            switch (operation)
 455            {
 456                case ExpressionType.UnaryPlus:
 457                case ExpressionType.Negate:
 458                case ExpressionType.OnesComplement:
 459                case ExpressionType.IsFalse:
 460                case ExpressionType.IsTrue:
 461                    break;
 462
 463                case ExpressionType.Not:
 464                    ////  The DLR converts the 'Not' operation into a 'OnesComplement' operation for integer numbers, need to block that scenario.
 465                    bool boolVal;
 466                    if (!thisValue.TryReadAs<bool>(out boolVal))
 467                    {
 468                        return OperationSupport.NotSupportedOnOperand;
 469                    }
 470
 471                    break;
 472
 473                default:
 474                    return OperationSupport.NotSupported;
 475            }
 476
 477            return OperationSupport.Supported;
 478        }
 479
 480        /// <summary>
 481        /// Gets the operation support value for the specified operation and operands.
 482        /// </summary>
 483        /// <param name="operation">The operation type.</param>
 484        /// <param name="thisValue">The JsonValue instance to check operation for.</param>
 485        /// <param name="operand">The second operand instance.</param>
 486        /// <returns>An <see cref="OperationSupport"/> value.</returns>
 487        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity",
 488            Justification = "It doesn't make sense to break up this method.")]
 489        private static OperationSupport GetBinaryOperationSupport(ExpressionType operation, JsonValue thisValue, object operand)
 490        {
 491            //// Supported binary operators: +, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, <=
 492
 493            bool isCompareOperation = false;
 494
 495            JsonValue otherValue = operand as JsonValue;
 496
 497            switch (operation)
 498            {
 499                //// supported binary operations
 500
 501                case ExpressionType.Add:
 502                case ExpressionType.Subtract:
 503                case ExpressionType.Multiply:
 504                case ExpressionType.Divide:
 505                case ExpressionType.Modulo:
 506                case ExpressionType.And:
 507                case ExpressionType.Or:
 508                case ExpressionType.ExclusiveOr:
 509                case ExpressionType.LeftShift:
 510                case ExpressionType.RightShift:
 511                case ExpressionType.GreaterThan:
 512                case ExpressionType.GreaterThanOrEqual:
 513                case ExpressionType.LessThan:
 514                case ExpressionType.LessThanOrEqual:
 515                    break;
 516
 517                //// compare operations:
 518                case ExpressionType.Equal:
 519                case ExpressionType.NotEqual:
 520                    isCompareOperation = true;
 521                    break;
 522
 523                default:
 524                    return OperationSupport.NotSupported;
 525            }
 526
 527            if (operand != null)
 528            {
 529                bool thisIsPrimitive = thisValue is JsonPrimitive;
 530
 531                if (otherValue != null)
 532                {
 533                    if (!(otherValue is JsonPrimitive) || !thisIsPrimitive)
 534                    {
 535                        //// When either value is non-primitive it must be a compare operation.
 536
 537                        if (!isCompareOperation)
 538                        {
 539                            return OperationSupport.NotSupportedOnJsonType;
 540                        }
 541                    }
 542                }
 543                else
 544                {
 545                    //// if operand is not a JsonValue it must be a primitive CLR type and first operand must be a JsonPrimitive.
 546
 547                    if (!thisIsPrimitive)
 548                    {
 549                        return OperationSupport.NotSupportedOnJsonType;
 550                    }
 551
 552                    JsonPrimitive primitiveValue = null;
 553
 554                    if (!JsonPrimitive.TryCreate(operand, out primitiveValue))
 555                    {
 556                        return OperationSupport.NotSupportedOnValueType;
 557                    }
 558                }
 559            }
 560            else
 561            {
 562                //// when operand is null only compare operations are valid.
 563
 564                if (!isCompareOperation)
 565                {
 566                    return OperationSupport.NotSupportedOnOperand;
 567                }
 568            }
 569
 570            return OperationSupport.Supported;
 571        }
 572
 573        /// <summary>
 574        /// Returns an expression representing a unary operation based on the specified operation type.
 575        /// </summary>
 576        /// <param name="operation">The operation type.</param>
 577        /// <param name="thisExpression">The operand.</param>
 578        /// <returns>The expression representing the unary operation.</returns>
 579        private static Expression GetUnaryOperationExpression(ExpressionType operation, Expression thisExpression)
 580        {
 581            //// Unary operators: +, -, !, ~, false (&&), true (||)
 582            //// unsupported: ++, --
 583
 584            Expression operExpression = null;
 585
 586            try
 587            {
 588                switch (operation)
 589                {
 590                    case ExpressionType.UnaryPlus:
 591                        operExpression = Expression.UnaryPlus(thisExpression);
 592                        break;
 593                    case ExpressionType.Negate:
 594                        operExpression = Expression.Negate(thisExpression);
 595                        break;
 596                    case ExpressionType.Not:
 597                        operExpression = Expression.Not(thisExpression);
 598                        break;
 599                    case ExpressionType.OnesComplement:
 600                        operExpression = Expression.OnesComplement(thisExpression);
 601                        break;
 602                    case ExpressionType.IsFalse:
 603                        operExpression = Expression.IsFalse(thisExpression);
 604                        break;
 605                    case ExpressionType.IsTrue:
 606                        operExpression = Expression.IsTrue(thisExpression);
 607                        break;
 608                }
 609            }
 610            catch (InvalidOperationException ex)
 611            {
 612                operExpression = Expression.Throw(Expression.Constant(ex), typeof(object));
 613            }
 614
 615            return operExpression;
 616        }
 617
 618        /// <summary>
 619        /// Updates the <see cref="Expression"/> tree for the operands of the specified operation.
 620        /// </summary>
 621        /// <param name="operation">The operation to evalutes.</param>
 622        /// <param name="thisOperand">The first operand.</param>
 623        /// <param name="otherOperand">The second operand.</param>
 624        /// <param name="thisExpression">The <see cref="Expression"/> for the first operand.</param>
 625        /// <param name="otherExpression">The <see cref="Expression"/> for the second operand.</param>
 626        private static void GetBinaryOperandExpressions(ExpressionType operation, DynamicMetaObject thisOperand, DynamicMetaObject otherOperand, ref Expression thisExpression, ref Expression otherExpression)
 627        {
 628            JsonValue thisValue = thisOperand.Value as JsonValue;
 629            JsonValue otherValue = otherOperand.Value as JsonValue;
 630
 631            Type thisType = thisValue.Read().GetType();
 632            Type otherType = otherValue != null ? otherValue.Read().GetType() : otherOperand.Value.GetType();
 633            Type coercedType;
 634
 635            if (JsonValueDynamicMetaObject.TryCoerceType(operation, thisType, otherType, out coercedType))
 636            {
 637                thisType = otherType = coercedType;
 638            }
 639            else if (JsonValueDynamicMetaObject.TryCoerceSpecialTypes(thisOperand, otherOperand, out coercedType))
 640            {
 641                thisType = otherType = coercedType;
 642            }
 643
 644            thisExpression = Expression.Convert(thisExpression, thisOperand.LimitType);
 645            thisExpression = Expression.Convert(Expression.Call(thisExpression, ReadAsMethodInfo, new Expression[] { Expression.Constant(thisType) }), thisType);
 646
 647            otherExpression = Expression.Convert(otherExpression, otherOperand.LimitType);
 648            if (otherValue != null)
 649            {
 650                otherExpression = Expression.Convert(Expression.Call(otherExpression, ReadAsMethodInfo, new Expression[] { Expression.Constant(otherType) }), otherType);
 651            }
 652            else if (otherOperand.LimitType != otherType)
 653            {
 654                otherExpression = Expression.Convert(otherExpression, otherType);
 655            }
 656        }
 657
 658        /// <summary>
 659        /// Returns an Expression representing a binary operation based on the specified operation type.
 660        /// </summary>
 661        /// <param name="operation">The operation type.</param>
 662        /// <param name="thisExpression">An expression representing the left operand.</param>
 663        /// <param name="otherExpression">An expression representing the right operand.</param>
 664        /// <returns>The expression representing the binary operation.</returns>
 665        private static Expression GetBinaryOperationExpression(ExpressionType operation, Expression thisExpression, Expression otherExpression)
 666        {
 667            //// Binary operators: +, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, <=
 668            //// The '&&' and '||' operators are conditional versions of the '&' and '|' operators.
 669            //// Unsupported: Compound assignment operators.
 670
 671            Expression operExpression = null;
 672
 673            try
 674            {
 675                switch (operation)
 676                {
 677                    case ExpressionType.Equal:
 678                        operExpression = Expression.Equal(thisExpression, otherExpression);
 679                        break;
 680                    case ExpressionType.NotEqual:
 681                        operExpression = Expression.NotEqual(thisExpression, otherExpression);
 682                        break;
 683                    case ExpressionType.GreaterThan:
 684                        operExpression = Expression.GreaterThan(thisExpression, otherExpression);
 685                        break;
 686                    case ExpressionType.GreaterThanOrEqual:
 687                        operExpression = Expression.GreaterThanOrEqual(thisExpression, otherExpression);
 688                        break;
 689                    case ExpressionType.LessThan:
 690                        operExpression = Expression.LessThan(thisExpression, otherExpression);
 691                        break;
 692                    case ExpressionType.LessThanOrEqual:
 693                        operExpression = Expression.LessThanOrEqual(thisExpression, otherExpression);
 694                        break;
 695                    case ExpressionType.LeftShift:
 696                        operExpression = Expression.LeftShift(thisExpression, otherExpression);
 697                        break;
 698                    case ExpressionType.RightShift:
 699                        operExpression = Expression.RightShift(thisExpression, otherExpression);
 700                        break;
 701                    case ExpressionType.And:
 702                        operExpression = Expression.And(thisExpression, otherExpression);
 703                        break;
 704                    case ExpressionType.Or:
 705                        operExpression = Expression.Or(thisExpression, otherExpression);
 706                        break;
 707                    case ExpressionType.ExclusiveOr:
 708                        operExpression = Expression.ExclusiveOr(thisExpression, otherExpression);
 709                        break;
 710                    case ExpressionType.Add:
 711                        operExpression = Expression.Add(thisExpression, otherExpression);
 712                        break;
 713                    case ExpressionType.Subtract:
 714                        operExpression = Expression.Subtract(thisExpression, otherExpression);
 715                        break;
 716                    case ExpressionType.Multiply:
 717                        operExpression = Expression.Multiply(thisExpression, otherExpression);
 718                        break;
 719                    case ExpressionType.Divide:
 720                        operExpression = Expression.Divide(thisExpression, otherExpression);
 721                        break;
 722                    case ExpressionType.Modulo:
 723                        operExpression = Expression.Modulo(thisExpression, otherExpression);
 724                        break;
 725                }
 726            }
 727            catch (InvalidOperationException ex)
 728            {
 729                operExpression = Expression.Throw(Expression.Constant(ex), typeof(object));
 730            }
 731
 732            return operExpression;
 733        }
 734
 735        /// <summary>
 736        /// Returns an expression representing a 'throw' instruction based on the specified <see cref="OperationSupport"/> value.
 737        /// </summary>
 738        /// <param name="supportValue">The <see cref="OperationSupport"/> value.</param>
 739        /// <param name="operation">The operation type.</param>
 740        /// <param name="thisValue">The operation left operand.</param>
 741        /// <param name="operand">The operation right operand.</param>
 742        /// <returns>A <see cref="Expression"/> representing a 'throw' instruction.</returns>
 743        private static Expression GetOperationErrorExpression(OperationSupport supportValue, ExpressionType operation, JsonValue thisValue, object operand)
 744        {
 745            string exceptionMessage;
 746            string operandTypeName = operand != null ? operand.GetType().FullName : "<null>";
 747
 748            switch (supportValue)
 749            {
 750                default:
 751                case OperationSupport.NotSupported:
 752                case OperationSupport.NotSupportedOnJsonType:
 753                case OperationSupport.NotSupportedOnValueType:
 754                    exceptionMessage = SR.OperatorNotDefinedForJsonType(operation, thisValue.JsonType);
 755                    break;
 756
 757                case OperationSupport.NotSupportedOnOperand:
 758                    exceptionMessage = SR.OperatorNotAllowedOnOperands(operation, thisValue.GetType().FullName, operandTypeName);
 759                    break;
 760            }
 761
 762            return Expression.Throw(Expression.Constant(new InvalidOperationException(exceptionMessage)), typeof(object));
 763        }
 764#endif
 765
 766        /// <summary>
 767        /// Gets a <see cref="MethodInfo"/> instance for the specified method name in the specified type.
 768        /// </summary>
 769        /// <param name="extensionProviderType">The extension provider type.</param>
 770        /// <param name="methodName">The name of the method to get the info for.</param>
 771        /// <param name="argTypes">The types of the method arguments.</param>
 772        /// <returns>A <see cref="MethodInfo"/>instance or null if the method cannot be resolved.</returns>
 773        private static MethodInfo GetExtensionMethod(Type extensionProviderType, string methodName, Type[] argTypes)
 774        {
 775            MethodInfo methodInfo = null;
 776            MethodInfo[] methods = extensionProviderType.GetMethods();
 777
 778            foreach (MethodInfo info in methods)
 779            {
 780                if (info.Name == methodName)
 781                {
 782                    methodInfo = info;
 783
 784                    if (!info.IsGenericMethodDefinition)
 785                    {
 786                        bool paramsMatch = true;
 787                        ParameterInfo[] args = methodInfo.GetParameters();
 788
 789                        if (args.Length == argTypes.Length)
 790                        {
 791                            for (int idx = 0; idx < args.Length; idx++)
 792                            {
 793                                if (!args[idx].ParameterType.IsAssignableFrom(argTypes[idx]))
 794                                {
 795                                    paramsMatch = false;
 796                                    break;
 797                                }
 798                            }
 799
 800                            if (paramsMatch)
 801                            {
 802                                break;
 803                            }
 804                        }
 805                    }
 806                }
 807            }
 808
 809            return methodInfo;
 810        }
 811
 812        /// <summary>
 813        /// Attempts to get an expression for an index parameter.
 814        /// </summary>
 815        /// <param name="indexes">The operation indexes parameter.</param>
 816        /// <param name="expression">A <see cref="Expression"/> to be initialized to the index expression if the operation is successful, otherwise an error expression.</param>
 817        /// <returns>true the operation is successful, false otherwise.</returns>
 818        private static bool TryGetIndexExpression(DynamicMetaObject[] indexes, out Expression expression)
 819        {
 820            if (indexes.Length == 1 && indexes[0] != null && indexes[0].Value != null)
 821            {
 822                DynamicMetaObject index = indexes[0];
 823                Type indexType = indexes[0].Value.GetType();
 824
 825                switch (Type.GetTypeCode(indexType))
 826                {
 827                    case TypeCode.Char:
 828                    case TypeCode.Int16:
 829                    case TypeCode.UInt16:
 830                    case TypeCode.Byte:
 831                    case TypeCode.SByte:
 832                        Expression argExp = Expression.Convert(index.Expression, typeof(object));
 833                        Expression typeExp = Expression.Constant(typeof(int));
 834                        expression = Expression.Convert(Expression.Call(ChangeTypeMethodInfo, new Expression[] { argExp, typeExp }), typeof(int));
 835                        return true;
 836
 837                    case TypeCode.Int32:
 838                    case TypeCode.String:
 839                        expression = index.Expression;
 840                        return true;
 841                }
 842
 843                expression = Expression.Throw(Expression.Constant(new ArgumentException(SR.InvalidIndexType(indexType))), typeof(object));
 844                return false;
 845            }
 846
 847            expression = Expression.Throw(Expression.Constant(new ArgumentException(SR.NonSingleNonNullIndexNotSupported)), typeof(object));
 848            return false;
 849        }
 850#if CODEPLEX
 851        /// <summary>
 852        /// Attempts to coerce the operand types on a binary operation for some special type and value cases as treated by JsonValue:
 853        /// "true" and "false" can be converted to boolean.
 854        /// Guid, DateTime and other types can be converted to string.
 855        /// </summary>
 856        /// <param name="thisOperand">The first operand.</param>
 857        /// <param name="otherOperand">The second operand</param>
 858        /// <param name="coercedType">On success, this parameter contains the coerced type.</param>
 859        /// <returns>true if the coercion is performed, false otherwise.</returns>
 860        private static bool TryCoerceSpecialTypes(DynamicMetaObject thisOperand, DynamicMetaObject otherOperand, out Type coercedType)
 861        {
 862            JsonValue thisValue = thisOperand.Value as JsonValue;
 863            JsonValue otherValue = otherOperand.Value as JsonValue;
 864
 865            if (thisValue is JsonPrimitive)
 866            {
 867                Type thisType = thisValue.Read().GetType();
 868
 869                if (thisType != otherOperand.LimitType)
 870                {
 871                    if (otherOperand.LimitType == typeof(string) || (thisType == typeof(string) && otherValue == null))
 872                    {
 873                        object value;
 874                        if (thisValue.TryReadAs(otherOperand.LimitType, out value))
 875                        {
 876                            coercedType = otherOperand.LimitType;
 877                            return true;
 878                        }
 879                    }
 880                }
 881            }
 882
 883            coercedType = default(Type);
 884            return false;
 885        }
 886
 887        /// <summary>
 888        /// Attempts to coerce one of the specified types to the other if needed and if possible.
 889        /// </summary>
 890        /// <param name="operation">The operation for the type coercion.</param>
 891        /// <param name="thisType">The type of the first operand.</param>
 892        /// <param name="otherType">The type of the second operand.</param>
 893        /// <param name="coercedType">The coerced type.</param>
 894        /// <returns>true if the type is coerced, false otherwise.</returns>
 895        private static bool TryCoerceType(ExpressionType operation, Type thisType, Type otherType, out Type coercedType)
 896        {
 897            //// Supported coercion operators: +, -, *, /, %, ==, !=, >, <, >=, <=
 898
 899            if (thisType != otherType)
 900            {
 901                switch (operation)
 902                {
 903                    case ExpressionType.Add:
 904                    case ExpressionType.Subtract:
 905                    case ExpressionType.Multiply:
 906                    case ExpressionType.Divide:
 907                    case ExpressionType.Modulo:
 908                    case ExpressionType.Equal:
 909                    case ExpressionType.NotEqual:
 910                    case ExpressionType.GreaterThan:
 911                    case ExpressionType.GreaterThanOrEqual:
 912                    case ExpressionType.LessThan:
 913                    case ExpressionType.LessThanOrEqual:
 914
 915                        if (ImplicitNumericTypeConverter.TryGetCoercionType(thisType, otherType, out coercedType))
 916                        {
 917                            return true;
 918                        }
 919
 920                        break;
 921                }
 922            }
 923            
 924            coercedType = default(Type);
 925            return false;
 926        }
 927
 928        /// <summary>
 929        /// Gets the return type for unary operations.
 930        /// </summary>
 931        /// <param name="binder">The unary operation binder.</param>
 932        /// <returns>The type representing the operation return type.</returns>
 933        private Type GetUnaryOperationReturnType(UnaryOperationBinder binder)
 934        {
 935            JsonValue thisValue = this.Value as JsonValue;
 936
 937            Type returnType = binder.ReturnType == typeof(object) ? thisValue.Read().GetType() : binder.ReturnType;
 938
 939            //// The DLR sets the binder.ReturnType for the unary 'Not' operation as 'object' as opposed to 'bool', 
 940            //// we need to detect this case and fix up the type to enable boolean conversions from strings.
 941
 942            if (returnType == typeof(string) && binder.Operation == ExpressionType.Not)
 943            {
 944                bool boolVal;
 945                if (thisValue.TryReadAs<bool>(out boolVal))
 946                {
 947                    returnType = typeof(bool);
 948                }
 949            }
 950
 951            return returnType;
 952        }
 953#endif
 954
 955        /// <summary>
 956        /// Gets a <see cref="DynamicMetaObject"/> for a method call.
 957        /// </summary>
 958        /// <param name="methodInfo">Info for the method to be performed.</param>
 959        /// <param name="args">expression array representing the method arguments</param>
 960        /// <returns>A meta object for the method call.</returns>
 961        private DynamicMetaObject GetMethodMetaObject(MethodInfo methodInfo, Expression[] args)
 962        {
 963            Expression instance = Expression.Convert(this.Expression, this.LimitType);
 964            Expression methodCall = Expression.Call(instance, methodInfo, args);
 965            BindingRestrictions restrictions = this.DefaultRestrictions;
 966
 967            DynamicMetaObject metaObj = new DynamicMetaObject(methodCall, restrictions);
 968
 969            return metaObj;
 970        }
 971#if CODEPLEX
 972
 973        /// <summary>
 974        /// Helper class for numeric type coercion support.
 975        /// </summary>
 976        private static class ImplicitNumericTypeConverter
 977        {
 978            /// <summary>
 979            /// Table of implicit conversion types, the 'values' in the dictionary represent the type values the 'key' type
 980            /// can be converted to.
 981            /// </summary>
 982            /// <remarks>
 983            /// Observe that this table is not the full conversion table, for storage optimization some types have been factored
 984            /// out into a list; the algorithm that uses this table knows about this optimization.
 985            /// For reference see Implicit Conversion table in the MSDN http://msdn.microsoft.com/en-us/library/y5b434w4.aspx
 986            ///     sbyte   -> short, int, long, float, double, or decimal
 987            ///     byte    -> short, ushort, int, uint, long, ulong, float, double, or decimal
 988            ///     short   -> int, long, float, double, or decimal
 989            ///     ushort  -> int, uint, long, ulong, float, double, or decimal
 990            ///     int     -> long, float, double, or decimal
 991            ///     uint    -> long, ulong, float, double, or decimal
 992            ///     long    -> float, double, or decimal
 993            ///     char    -> ushort, int, uint, long, ulong, float, double, or decimal
 994            ///     float   -> double
 995            ///     ulong   -> float, double, or decimal
 996            /// </remarks>
 997            private static Dictionary<string, List<string>> partialConversionTable = new Dictionary<string, List<string>>
 998            {
 999                { typeof(sbyte).Name, new List<string>() { typeof(short).Name,  typeof(int).Name } },
1000                { typeof(byte).Name, new List<string>() { typeof(short).Name,  typeof(ushort).Name,  typeof(int).Name,  typeof(uint).Name,  typeof(ulong).Name } },
1001                { typeof(short).Name, new List<string>() { typeof(int).Name } },
1002                { typeof(ushort).Name, new List<string>() { typeof(int).Name,  typeof(uint).Name,  typeof(ulong).Name } },
1003                { typeof(int).Name,  new List<string>() { } },
1004                { typeof(uint).Name,  new List<string>() { typeof(ulong).Name } },
1005                { typeof(long).Name,  new List<string>() { } },
1006                { typeof(char).Name,  new List<string>() { typeof(ushort).Name,  typeof(int).Name,  typeof(uint).Name,  typeof(ulong).Name } },
1007                { typeof(ulong).Name,  new List<string>() { } },
1008                ////{ typeof(float).Name,  new List<string>() { typeof(double).Name }},
1009            };
1010
1011            /// <summary>
1012            /// List of types most other types can be coerced to.
1013            /// </summary>
1014            private static List<string> universalCoercionTypes = new List<string> { typeof(long).Name,  typeof(float).Name, typeof(double).Name, typeof(decimal).Name };
1015
1016            /// <summary>
1017            /// Attempts to coerce one type to another.
1018            /// </summary>
1019            /// <param name="thisType">The first type to be evaluated.</param>
1020            /// <param name="otherType">The second type to be evaluated.</param>
1021            /// <param name="coercedType">The coerced resulting type to be used.</param>
1022            /// <returns>true if the coercion exists, false otherwise.</returns>
1023            public static bool TryGetCoercionType(Type thisType, Type otherType, out Type coercedType)
1024            {
1025                //// checks covering for storage optimizations in the implicit numeric converstion table
1026
1027                Type typeofLong = typeof(long);
1028                Type typeofULong = typeof(ulong);
1029
1030                // special-case ulong type since it cannot be coerced to long which is part of the universal coercion list.
1031                if ((thisType == typeofULong && otherType == typeofLong) || (thisType == typeofLong && otherType == typeofULong))
1032                {
1033                    coercedType = default(Type);
1034                    return false;
1035                }
1036
1037                Type typeofFloat = typeof(float);
1038                Type typeofDouble = typeof(double);
1039                
1040                // special-case float since it can be coerced to double only.
1041                if ((thisType == typeofFloat && otherType == typeofDouble) || (otherType == typeofFloat && thisType == typeofDouble))
1042                {
1043                    coercedType = typeofDouble;
1044                    return true;
1045                }
1046
1047                if (partialConversionTable.ContainsKey(thisType.Name) && 
1048                    (partialConversionTable[thisType.Name].Contains(otherType.Name) || universalCoercionTypes.Contains(otherType.Name)))
1049                {
1050                    coercedType = otherType;
1051                    return true;
1052                }
1053
1054                if (partialConversionTable.ContainsKey(otherType.Name) && 
1055                    (partialConversionTable[otherType.Name].Contains(thisType.Name) || universalCoercionTypes.Contains(thisType.Name)))
1056                {
1057                    coercedType = thisType;
1058                    return true;
1059                }
1060
1061                coercedType = default(Type);
1062                return false;
1063            }
1064        }
1065#endif
1066    }
1067}