The Labs \ Source Viewer \ SSCLI \ Microsoft.JScript \ NumericUnary

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. namespace Microsoft.JScript
  16. {
  17.    
  18.     using System;
  19.     using System.Reflection;
  20.     using System.Reflection.Emit;
  21.     using System.Diagnostics;
  22.    
  23.     public sealed class NumericUnary : UnaryOp
  24.     {
  25.         private object metaData = null;
  26.         private JSToken operatorTok;
  27.         private MethodInfo operatorMeth;
  28.         private Type type;
  29.        
  30.         internal NumericUnary(Context context, AST operand, JSToken operatorTok) : base(context, operand)
  31.         {
  32.             this.operatorTok = operatorTok;
  33.             this.operatorMeth = null;
  34.             this.type = null;
  35.         }
  36.        
  37.         public NumericUnary(int operatorTok) : this(null, null, (JSToken)operatorTok)
  38.         {
  39.         }
  40.        
  41.         internal override object Evaluate()
  42.         {
  43.             return this.EvaluateUnary(this.operand.Evaluate());
  44.         }
  45.        
  46.         #if !DEBUG
  47.         [DebuggerStepThroughAttribute()]
  48.         [DebuggerHiddenAttribute()]
  49.         #endif
  50.         public object EvaluateUnary(object v)
  51.         {
  52.             IConvertible ic = Convert.GetIConvertible(v);
  53.             switch (Convert.GetTypeCode(v, ic)) {
  54.                 case TypeCode.Empty:
  55.                     return this.EvaluateUnary(Double.NaN);
  56.                 case TypeCode.DBNull:
  57.                     return this.EvaluateUnary(0);
  58.                 case TypeCode.Boolean:
  59.                     return this.EvaluateUnary(ic.ToBoolean(null) ? 1 : 0);
  60.                 case TypeCode.Char:
  61.                     return this.EvaluateUnary((int)ic.ToChar(null));
  62.                 case TypeCode.SByte:
  63.                 case TypeCode.Byte:
  64.                 case TypeCode.Int16:
  65.                 case TypeCode.UInt16:
  66.                 case TypeCode.Int32:
  67.                    
  68.                     int i = ic.ToInt32(null);
  69.                     switch (this.operatorTok) {
  70.                         case JSToken.BitwiseNot:
  71.                             return ~i;
  72.                         case JSToken.LogicalNot:
  73.                             return i == 0;
  74.                         case JSToken.Minus:
  75.                             if (i == 0)
  76.                                 return -(double)i;
  77.                             if (i == Int32.MinValue)
  78.                                 return (ulong)-(double)i;
  79.                             return -i;
  80.                         case JSToken.Plus:
  81.                             return i;
  82.                         default:
  83.                             throw new JScriptException(JSError.InternalError, this.context);
  84.                             break;
  85.                     }
  86.                     break;
  87.                 case TypeCode.UInt32:
  88.                    
  89.                     uint ui = ic.ToUInt32(null);
  90.                     switch (this.operatorTok) {
  91.                         case JSToken.BitwiseNot:
  92.                             return ~ui;
  93.                         case JSToken.LogicalNot:
  94.                             return ui == 0;
  95.                         case JSToken.Minus:
  96.                             if (ui != 0 && ui <= Int32.MaxValue)
  97.                                 return -(int)ui;
  98.                             else
  99.                                 return -(double)ui;
  100.                             break;
  101.                         case JSToken.Plus:
  102.                             return ui;
  103.                         default:
  104.                             throw new JScriptException(JSError.InternalError, this.context);
  105.                             break;
  106.                     }
  107.                     break;
  108.                 case TypeCode.Int64:
  109.                    
  110.                     long l = ic.ToInt64(null);
  111.                     switch (this.operatorTok) {
  112.                         case JSToken.BitwiseNot:
  113.                             return ~l;
  114.                         case JSToken.LogicalNot:
  115.                             return l == 0;
  116.                         case JSToken.Minus:
  117.                             if (l == 0 || l == Int64.MinValue)
  118.                                 return -(double)l;
  119.                             return -l;
  120.                         case JSToken.Plus:
  121.                             return l;
  122.                         default:
  123.                             throw new JScriptException(JSError.InternalError, this.context);
  124.                             break;
  125.                     }
  126.                     break;
  127.                 case TypeCode.UInt64:
  128.                    
  129.                     ulong ul = ic.ToUInt64(null);
  130.                     switch (this.operatorTok) {
  131.                         case JSToken.BitwiseNot:
  132.                             return ~ul;
  133.                         case JSToken.LogicalNot:
  134.                             return ul == 0;
  135.                         case JSToken.Minus:
  136.                             if (ul != 0 && ul <= Int64.MaxValue)
  137.                                 return -(long)ul;
  138.                             else
  139.                                 return -(double)ul;
  140.                             break;
  141.                         case JSToken.Plus:
  142.                             return ul;
  143.                         default:
  144.                             throw new JScriptException(JSError.InternalError, this.context);
  145.                             break;
  146.                     }
  147.                     break;
  148.                 case TypeCode.Single:
  149.                 case TypeCode.Double:
  150.                    
  151.                     double d = ic.ToDouble(null);
  152.                     switch (this.operatorTok) {
  153.                         case JSToken.BitwiseNot:
  154.                             return ~(int)Runtime.DoubleToInt64(d);
  155.                         case JSToken.LogicalNot:
  156.                             return !Convert.ToBoolean(d);
  157.                         case JSToken.Minus:
  158.                             return -d;
  159.                         case JSToken.Plus:
  160.                             return d;
  161.                         default:
  162.                             throw new JScriptException(JSError.InternalError, this.context);
  163.                             break;
  164.                     }
  165.                     break;
  166.                 case TypeCode.String:
  167.                    
  168.                     goto no_overload_case;
  169.                     break;
  170.             }
  171.            
  172.             MethodInfo oper = this.GetOperator(v.GetType());
  173.             if (oper != null)
  174.                 return oper.Invoke(null, (BindingFlags)0, JSBinder.ob, new object[] {v}, null);
  175.             no_overload_case:
  176.             switch (this.operatorTok) {
  177.                 case JSToken.BitwiseNot:
  178.                     return ~Convert.ToInt32(v, ic);
  179.                 case JSToken.LogicalNot:
  180.                     return !Convert.ToBoolean(v, ic);
  181.                 case JSToken.Minus:
  182.                     return -Convert.ToNumber(v, ic);
  183.                 case JSToken.Plus:
  184.                     return Convert.ToNumber(v, ic);
  185.                 default:
  186.                     throw new JScriptException(JSError.InternalError, this.context);
  187.                     break;
  188.             }
  189.         }
  190.        
  191.         private MethodInfo GetOperator(IReflect ir)
  192.         {
  193.             Type t = ir is Type ? (Type)ir : Typeob.Object;
  194.             if (this.type == t)
  195.                 return this.operatorMeth;
  196.             this.type = t;
  197.             if (Convert.IsPrimitiveNumericType(t) || Typeob.JSObject.IsAssignableFrom(t)) {
  198.                 this.operatorMeth = null;
  199.                 return null;
  200.             }
  201.             switch (this.operatorTok) {
  202.                 case JSToken.BitwiseNot:
  203.                     this.operatorMeth = t.GetMethod("op_OnesComplement", BindingFlags.Public | BindingFlags.Static, JSBinder.ob, new Type[] {t}, null);
  204.                     break;
  205.                 case JSToken.LogicalNot:
  206.                     this.operatorMeth = t.GetMethod("op_LogicalNot", BindingFlags.Public | BindingFlags.Static, JSBinder.ob, new Type[] {t}, null);
  207.                     break;
  208.                 case JSToken.Minus:
  209.                     this.operatorMeth = t.GetMethod("op_UnaryNegation", BindingFlags.Public | BindingFlags.Static, JSBinder.ob, new Type[] {t}, null);
  210.                     break;
  211.                 case JSToken.Plus:
  212.                     this.operatorMeth = t.GetMethod("op_UnaryPlus", BindingFlags.Public | BindingFlags.Static, JSBinder.ob, new Type[] {t}, null);
  213.                     break;
  214.                 default:
  215.                     throw new JScriptException(JSError.InternalError, this.context);
  216.                     break;
  217.             }
  218.             if (this.operatorMeth == null || (operatorMeth.Attributes & MethodAttributes.SpecialName) == 0 || operatorMeth.GetParameters().Length != 1)
  219.                 this.operatorMeth = null;
  220.             if (this.operatorMeth != null)
  221.                 this.operatorMeth = new JSMethodInfo(this.operatorMeth);
  222.             return this.operatorMeth;
  223.         }
  224.        
  225.         internal override IReflect InferType(JSField inference_target)
  226.         {
  227.             Debug.Assert(Globals.TypeRefs.InReferenceContext(this.type));
  228.             MethodInfo oper;
  229.             if (this.type == null || inference_target != null) {
  230.                 oper = this.GetOperator(this.operand.InferType(inference_target));
  231.             }
  232.             else
  233.                 oper = this.GetOperator(this.type);
  234.             if (oper != null) {
  235.                 this.metaData = oper;
  236.                 return oper.ReturnType;
  237.             }
  238.             if (this.operatorTok == JSToken.LogicalNot)
  239.                 return Typeob.Boolean;
  240.             switch (Type.GetTypeCode(this.type)) {
  241.                 case TypeCode.Empty:
  242.                     return this.operatorTok == JSToken.BitwiseNot ? Typeob.Int32 : Typeob.Double;
  243.                 case TypeCode.Object:
  244.                     return Typeob.Object;
  245.                 case TypeCode.DBNull:
  246.                     return Typeob.Int32;
  247.                 case TypeCode.Boolean:
  248.                     return Typeob.Int32;
  249.                 case TypeCode.SByte:
  250.                     return (this.operatorTok == JSToken.BitwiseNot) ? Typeob.SByte : Typeob.Int32;
  251.                 case TypeCode.Char:
  252.                     return Typeob.Int32;
  253.                 case TypeCode.Byte:
  254.                     return (this.operatorTok == JSToken.BitwiseNot) ? Typeob.Byte : Typeob.Int32;
  255.                 case TypeCode.Int16:
  256.                     return (this.operatorTok == JSToken.BitwiseNot) ? Typeob.Int16 : Typeob.Int32;
  257.                 case TypeCode.UInt16:
  258.                     return (this.operatorTok == JSToken.BitwiseNot) ? Typeob.UInt16 : Typeob.Int32;
  259.                 case TypeCode.Int32:
  260.                     return Typeob.Int32;
  261.                 case TypeCode.UInt32:
  262.                     return this.operatorTok == JSToken.Minus ? Typeob.Double : Typeob.UInt32;
  263.                 case TypeCode.Int64:
  264.                     return Typeob.Int64;
  265.                 case TypeCode.UInt64:
  266.                     return this.operatorTok == JSToken.Minus ? Typeob.Double : Typeob.UInt64;
  267.                 case TypeCode.Single:
  268.                 case TypeCode.Double:
  269.                 case TypeCode.String:
  270.                     return this.operatorTok == JSToken.BitwiseNot ? Typeob.Int32 : Typeob.Double;
  271.             }
  272.             if (Typeob.JSObject.IsAssignableFrom(this.type))
  273.                 return Typeob.Double;
  274.             else
  275.                 return Typeob.Object;
  276.         }
  277.        
  278.         internal override void TranslateToConditionalBranch(ILGenerator il, bool branchIfTrue, Label label, bool shortForm)
  279.         {
  280.             if (this.operatorTok == JSToken.LogicalNot)
  281.                 this.operand.TranslateToConditionalBranch(il, !branchIfTrue, label, shortForm);
  282.             else
  283.                 base.TranslateToConditionalBranch(il, branchIfTrue, label, shortForm);
  284.         }
  285.        
  286.         internal override void TranslateToIL(ILGenerator il, Type rtype)
  287.         {
  288.             if (this.metaData == null) {
  289.                 Type rt = this.operatorTok == JSToken.LogicalNot ? Typeob.Boolean : Typeob.Double;
  290.                 if (Convert.IsPrimitiveNumericType(rtype) && Convert.IsPromotableTo(this.type, rtype))
  291.                     rt = rtype;
  292.                 if (this.operatorTok == JSToken.BitwiseNot && !Convert.IsPrimitiveIntegerType(rt)) {
  293.                     rt = this.type;
  294.                     if (!Convert.IsPrimitiveIntegerType(rt))
  295.                         rt = Typeob.Int32;
  296.                 }
  297.                 this.operand.TranslateToIL(il, this.type);
  298.                 Convert.Emit(this, il, this.type, rt, true);
  299.                 switch (this.operatorTok) {
  300.                     case JSToken.BitwiseNot:
  301.                         il.Emit(OpCodes.Not);
  302.                         break;
  303.                     case JSToken.LogicalNot:
  304.                         Convert.Emit(this, il, rt, Typeob.Boolean, true);
  305.                         rt = Typeob.Boolean;
  306.                         il.Emit(OpCodes.Ldc_I4_0);
  307.                         il.Emit(OpCodes.Ceq);
  308.                         break;
  309.                     case JSToken.Minus:
  310.                         il.Emit(OpCodes.Neg);
  311.                         break;
  312.                     case JSToken.Plus:
  313.                         break;
  314.                     default:
  315.                         throw new JScriptException(JSError.InternalError, this.context);
  316.                         break;
  317.                 }
  318.                 Convert.Emit(this, il, rt, rtype);
  319.                 return;
  320.             }
  321.             if (this.metaData is MethodInfo) {
  322.                 MethodInfo oper = (MethodInfo)this.metaData;
  323.                 ParameterInfo[] pars = oper.GetParameters();
  324.                 this.operand.TranslateToIL(il, pars[0].ParameterType);
  325.                 il.Emit(OpCodes.Call, oper);
  326.                 Convert.Emit(this, il, oper.ReturnType, rtype);
  327.                 return;
  328.             }
  329.             //Getting here is just too bad. We do not know until the code runs whether or not to call an overloaded operator method.
  330.             //Compile operands to objects and devolve the decision making to run time thunks
  331.             il.Emit(OpCodes.Ldloc, (LocalBuilder)this.metaData);
  332.             this.operand.TranslateToIL(il, Typeob.Object);
  333.             il.Emit(OpCodes.Call, CompilerGlobals.evaluateUnaryMethod);
  334.             Convert.Emit(this, il, Typeob.Object, rtype);
  335.         }
  336.        
  337.         internal override void TranslateToILInitializer(ILGenerator il)
  338.         {
  339.             IReflect rtype = this.InferType(null);
  340.             this.operand.TranslateToILInitializer(il);
  341.             if (rtype != Typeob.Object)
  342.                 return;
  343.             this.metaData = il.DeclareLocal(Typeob.NumericUnary);
  344.             ConstantWrapper.TranslateToILInt(il, (int)this.operatorTok);
  345.             il.Emit(OpCodes.Newobj, CompilerGlobals.numericUnaryConstructor);
  346.             il.Emit(OpCodes.Stloc, (LocalBuilder)this.metaData);
  347.         }
  348.        
  349.     }
  350. }

Developer Fusion