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

  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.    
  22.     internal class NumericBinaryAssign : BinaryOp
  23.     {
  24.         private NumericBinary binOp;
  25.         private object metaData;
  26.        
  27.         internal NumericBinaryAssign(Context context, AST operand1, AST operand2, JSToken operatorTok) : base(context, operand1, operand2, operatorTok)
  28.         {
  29.             this.binOp = new NumericBinary(context, operand1, operand2, operatorTok);
  30.             this.metaData = null;
  31.         }
  32.        
  33.         internal override object Evaluate()
  34.         {
  35.             object v1 = this.operand1.Evaluate();
  36.             object v2 = this.operand2.Evaluate();
  37.             object result = this.binOp.EvaluateNumericBinary(v1, v2);
  38.             try {
  39.                 this.operand1.SetValue(result);
  40.                 return result;
  41.             }
  42.             catch (JScriptException e) {
  43.                 if (e.context == null)
  44.                     e.context = this.context;
  45.                 throw e;
  46.             }
  47.             catch (Exception e) {
  48.                 throw new JScriptException(e, this.context);
  49.             }
  50.             catch {
  51.                 throw new JScriptException(JSError.NonClsException, this.context);
  52.             }
  53.         }
  54.        
  55.         internal override IReflect InferType(JSField inference_target)
  56.         {
  57.             Debug.Assert(Globals.TypeRefs.InReferenceContext(this.type1));
  58.             Debug.Assert(Globals.TypeRefs.InReferenceContext(this.type2));
  59.             MethodInfo oper;
  60.             if (this.type1 == null || inference_target != null) {
  61.                 oper = this.GetOperator(this.operand1.InferType(inference_target), this.operand2.InferType(inference_target));
  62.             }
  63.             else
  64.                 oper = this.GetOperator(this.type1, this.type2);
  65.             if (oper != null) {
  66.                 this.metaData = oper;
  67.                 return oper.ReturnType;
  68.             }
  69.             if (this.type1 == Typeob.Char && this.operatorTok == JSToken.Minus) {
  70.                 TypeCode t2 = Type.GetTypeCode(this.type2);
  71.                 if (Convert.IsPrimitiveNumericTypeCode(t2) || t2 == TypeCode.Boolean)
  72.                     return Typeob.Char;
  73.                 else if (t2 == TypeCode.Char)
  74.                     return Typeob.Int32;
  75.             }
  76.             if (Convert.IsPrimitiveNumericType(this.type1))
  77.                 if (Convert.IsPromotableTo(this.type2, this.type1) || ((this.operand2 is ConstantWrapper) && ((ConstantWrapper)this.operand2).IsAssignableTo(this.type1)))
  78.                     return this.type1;
  79.                 else if (Convert.IsPrimitiveNumericType(this.type1) && Convert.IsPrimitiveNumericTypeFitForDouble(this.type2))
  80.                     return Typeob.Double;
  81.             return Typeob.Object;
  82.         }
  83.        
  84.         internal override AST PartiallyEvaluate()
  85.         {
  86.             this.operand1 = this.operand1.PartiallyEvaluateAsReference();
  87.             this.operand2 = this.operand2.PartiallyEvaluate();
  88.             this.binOp = new NumericBinary(this.context, this.operand1, this.operand2, this.operatorTok);
  89.             this.operand1.SetPartialValue(this.binOp);
  90.             return this;
  91.         }
  92.        
  93.         private void TranslateToILForNoOverloadCase(ILGenerator il, Type rtype)
  94.         {
  95.             Type lhtype = Convert.ToType(this.operand1.InferType(null));
  96.             Type rhtype = Convert.ToType(this.operand2.InferType(null));
  97.             Type rt = Typeob.Double;
  98.             if (this.operatorTok != JSToken.Divide && (rtype == Typeob.Void || rtype == lhtype || Convert.IsPrimitiveNumericType(lhtype)) && (Convert.IsPromotableTo(rhtype, lhtype) || ((this.operand2 is ConstantWrapper) && ((ConstantWrapper)this.operand2).IsAssignableTo(lhtype))))
  99.                 rt = lhtype;
  100.             if (rt == Typeob.SByte || rt == Typeob.Int16)
  101.                 rt = Typeob.Int32;
  102.             else if (rt == Typeob.Byte || rt == Typeob.UInt16 || rt == Typeob.Char)
  103.                 rt = Typeob.UInt32;
  104.            
  105.             // If we have "unsigned -= signed" or "signed -= unsigned" then generating the
  106.             // correct code gets quite complicated. Just go late-bound for this edge case.
  107.             if (this.operand2 is ConstantWrapper) {
  108.                 if (!((ConstantWrapper)this.operand2).IsAssignableTo(rt)) {
  109.                     // eg: "var u : byte = 123; u -= -100;" should go late bound because
  110.                     // of signed/unsigned mismatch but "u -= 1" should not.
  111.                     rt = Typeob.Object;
  112.                 }
  113.             }
  114.             else {
  115.                 if ((Convert.IsPrimitiveSignedNumericType(rhtype) && Convert.IsPrimitiveUnsignedIntegerType(lhtype)) || (Convert.IsPrimitiveUnsignedIntegerType(rhtype) && Convert.IsPrimitiveSignedIntegerType(lhtype)))
  116.                     rt = Typeob.Object;
  117.             }
  118.            
  119.             this.operand1.TranslateToILPreSetPlusGet(il);
  120.             Convert.Emit(this, il, lhtype, rt);
  121.             this.operand2.TranslateToIL(il, rt);
  122.             if (rt == Typeob.Object) {
  123.                 il.Emit(OpCodes.Ldc_I4, (int)this.operatorTok);
  124.                 il.Emit(OpCodes.Call, CompilerGlobals.numericbinaryDoOpMethod);
  125.             }
  126.             else if (rt == Typeob.Double || rt == Typeob.Single) {
  127.                 switch (this.operatorTok) {
  128.                     case JSToken.Divide:
  129.                         il.Emit(OpCodes.Div);
  130.                         break;
  131.                     case JSToken.Minus:
  132.                         il.Emit(OpCodes.Sub);
  133.                         break;
  134.                     case JSToken.Modulo:
  135.                         il.Emit(OpCodes.Rem);
  136.                         break;
  137.                     case JSToken.Multiply:
  138.                         il.Emit(OpCodes.Mul);
  139.                         break;
  140.                     default:
  141.                         throw new JScriptException(JSError.InternalError, this.context);
  142.                         break;
  143.                 }
  144.             }
  145.             else if (rt == Typeob.Int32 || rt == Typeob.Int64 || rt == Typeob.Int16 || rt == Typeob.SByte) {
  146.                 switch (this.operatorTok) {
  147.                     case JSToken.Divide:
  148.                         il.Emit(OpCodes.Div);
  149.                         break;
  150.                     case JSToken.Minus:
  151.                         il.Emit(OpCodes.Sub_Ovf);
  152.                         break;
  153.                     case JSToken.Modulo:
  154.                         il.Emit(OpCodes.Rem);
  155.                         break;
  156.                     case JSToken.Multiply:
  157.                         il.Emit(OpCodes.Mul_Ovf);
  158.                         break;
  159.                     default:
  160.                         throw new JScriptException(JSError.InternalError, this.context);
  161.                         break;
  162.                 }
  163.             }
  164.             else {
  165.                 switch (this.operatorTok) {
  166.                     case JSToken.Divide:
  167.                         il.Emit(OpCodes.Div);
  168.                         break;
  169.                     case JSToken.Minus:
  170.                         il.Emit(OpCodes.Sub_Ovf_Un);
  171.                         break;
  172.                     case JSToken.Modulo:
  173.                         il.Emit(OpCodes.Rem);
  174.                         break;
  175.                     case JSToken.Multiply:
  176.                         il.Emit(OpCodes.Mul_Ovf_Un);
  177.                         break;
  178.                     default:
  179.                         throw new JScriptException(JSError.InternalError, this.context);
  180.                         break;
  181.                 }
  182.             }
  183.             if (rtype != Typeob.Void) {
  184.                 LocalBuilder result = il.DeclareLocal(rt);
  185.                 il.Emit(OpCodes.Dup);
  186.                 il.Emit(OpCodes.Stloc, result);
  187.                 Convert.Emit(this, il, rt, lhtype);
  188.                 this.operand1.TranslateToILSet(il);
  189.                 il.Emit(OpCodes.Ldloc, result);
  190.                 Convert.Emit(this, il, rt, rtype);
  191.             }
  192.             else {
  193.                 Convert.Emit(this, il, rt, lhtype);
  194.                 this.operand1.TranslateToILSet(il);
  195.             }
  196.         }
  197.        
  198.         internal override void TranslateToIL(ILGenerator il, Type rtype)
  199.         {
  200.             if (this.metaData == null) {
  201.                 TranslateToILForNoOverloadCase(il, rtype);
  202.                 return;
  203.             }
  204.             if (this.metaData is MethodInfo) {
  205.                 object result = null;
  206.                 MethodInfo oper = (MethodInfo)this.metaData;
  207.                 Type type = Convert.ToType(this.operand1.InferType(null));
  208.                 ParameterInfo[] pars = oper.GetParameters();
  209.                 this.operand1.TranslateToILPreSetPlusGet(il);
  210.                 Convert.Emit(this, il, type, pars[0].ParameterType);
  211.                 this.operand2.TranslateToIL(il, pars[1].ParameterType);
  212.                 il.Emit(OpCodes.Call, oper);
  213.                 if (rtype != Typeob.Void) {
  214.                     result = il.DeclareLocal(rtype);
  215.                     il.Emit(OpCodes.Dup);
  216.                     Convert.Emit(this, il, type, rtype);
  217.                     il.Emit(OpCodes.Stloc, (LocalBuilder)result);
  218.                 }
  219.                 Convert.Emit(this, il, oper.ReturnType, type);
  220.                 this.operand1.TranslateToILSet(il);
  221.                 if (rtype != Typeob.Void)
  222.                     il.Emit(OpCodes.Ldloc, (LocalBuilder)result);
  223.             }
  224.             else {
  225.                 //Getting here is just too bad. We do not know until the code runs whether or not to call an overloaded operator method.
  226.                 //Compile operands to objects and devolve the decision making to run time thunks
  227.                 Type type = Convert.ToType(this.operand1.InferType(null));
  228.                 LocalBuilder result = il.DeclareLocal(Typeob.Object);
  229.                 this.operand1.TranslateToILPreSetPlusGet(il);
  230.                 Convert.Emit(this, il, type, Typeob.Object);
  231.                 il.Emit(OpCodes.Stloc, result);
  232.                 il.Emit(OpCodes.Ldloc, (LocalBuilder)this.metaData);
  233.                 il.Emit(OpCodes.Ldloc, result);
  234.                 this.operand2.TranslateToIL(il, Typeob.Object);
  235.                 il.Emit(OpCodes.Call, CompilerGlobals.evaluateNumericBinaryMethod);
  236.                 if (rtype != Typeob.Void) {
  237.                     il.Emit(OpCodes.Dup);
  238.                     il.Emit(OpCodes.Stloc, result);
  239.                 }
  240.                 Convert.Emit(this, il, Typeob.Object, type);
  241.                 this.operand1.TranslateToILSet(il);
  242.                 if (rtype != Typeob.Void) {
  243.                     il.Emit(OpCodes.Ldloc, result);
  244.                     Convert.Emit(this, il, Typeob.Object, rtype);
  245.                 }
  246.             }
  247.         }
  248.        
  249.         internal override void TranslateToILInitializer(ILGenerator il)
  250.         {
  251.             IReflect rtype = this.InferType(null);
  252.             this.operand1.TranslateToILInitializer(il);
  253.             this.operand2.TranslateToILInitializer(il);
  254.             if (rtype != Typeob.Object)
  255.                 return;
  256.             this.metaData = il.DeclareLocal(Typeob.NumericBinary);
  257.             ConstantWrapper.TranslateToILInt(il, (int)this.operatorTok);
  258.             il.Emit(OpCodes.Newobj, CompilerGlobals.numericBinaryConstructor);
  259.             il.Emit(OpCodes.Stloc, (LocalBuilder)this.metaData);
  260.         }
  261.     }
  262. }

Developer Fusion