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

  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 sealed class PlusAssign : BinaryOp
  23.     {
  24.         private Plus binOp;
  25.         private object metaData = null;
  26.        
  27.         internal PlusAssign(Context context, AST operand1, AST operand2) : base(context, operand1, operand2, JSToken.Plus)
  28.         {
  29.             this.binOp = new Plus(context, operand1, operand2);
  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.EvaluatePlus(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.String || this.type2 == Typeob.String)
  70.                 return Typeob.String;
  71.             if (Convert.IsPrimitiveNumericType(this.type1))
  72.                 if (Convert.IsPromotableTo(this.type2, this.type1) || ((this.operand2 is ConstantWrapper) && ((ConstantWrapper)this.operand2).IsAssignableTo(this.type1)))
  73.                     return this.type1;
  74.                 else if (Convert.IsPrimitiveNumericType(this.type1) && Convert.IsPrimitiveNumericTypeFitForDouble(this.type2))
  75.                     return Typeob.Double;
  76.             return Typeob.Object;
  77.         }
  78.        
  79.         internal override AST PartiallyEvaluate()
  80.         {
  81.             this.operand1 = this.operand1.PartiallyEvaluateAsReference();
  82.             this.operand2 = this.operand2.PartiallyEvaluate();
  83.             this.binOp = new Plus(this.context, this.operand1, this.operand2);
  84.             this.operand1.SetPartialValue(this.binOp);
  85.             if (this.Engine.doFast) {
  86.                 Binding b = this.operand1 as Binding;
  87.                 if (b != null && b.member is JSVariableField) {
  88.                     TypeExpression te = ((JSVariableField)b.member).type;
  89.                     if (te != null && te.InferType(null) == Typeob.String)
  90.                         this.operand1.context.HandleError(JSError.StringConcatIsSlow);
  91.                 }
  92.             }
  93.             return this;
  94.         }
  95.        
  96.         private void TranslateToILForNoOverloadCase(ILGenerator il, Type rtype)
  97.         {
  98.             Type lhtype = Convert.ToType(this.operand1.InferType(null));
  99.             Type rhtype = Convert.ToType(this.operand2.InferType(null));
  100.             Type rt = Typeob.Object;
  101.             if (lhtype == Typeob.String || rhtype == Typeob.String)
  102.                 rt = Typeob.String;
  103.             else if (rtype == Typeob.Void || rtype == lhtype || Convert.IsPrimitiveNumericType(lhtype) && (Convert.IsPromotableTo(rhtype, lhtype) || ((this.operand2 is ConstantWrapper) && ((ConstantWrapper)this.operand2).IsAssignableTo(lhtype))))
  104.                 rt = lhtype;
  105.             if (rt == Typeob.SByte || rt == Typeob.Int16)
  106.                 rt = Typeob.Int32;
  107.             else if (rt == Typeob.Byte || rt == Typeob.UInt16)
  108.                 rt = Typeob.UInt32;
  109.            
  110.             // If we have "unsigned += signed" or "signed += unsigned" then generating the
  111.             // correct code gets quite complicated. Just go late-bound for this edge case.
  112.             if (this.operand2 is ConstantWrapper) {
  113.                 if (!((ConstantWrapper)this.operand2).IsAssignableTo(rt)) {
  114.                     // eg: "var u : byte = 123; u += -100;" should go late bound because
  115.                     // of signed/unsigned mismatch but "u += 1" should not.
  116.                     rt = Typeob.Object;
  117.                 }
  118.             }
  119.             else {
  120.                 if ((Convert.IsPrimitiveSignedNumericType(rhtype) && Convert.IsPrimitiveUnsignedIntegerType(lhtype)) || (Convert.IsPrimitiveUnsignedIntegerType(rhtype) && Convert.IsPrimitiveSignedIntegerType(lhtype)))
  121.                     rt = Typeob.Object;
  122.             }
  123.            
  124.             this.operand1.TranslateToILPreSetPlusGet(il);
  125.             Convert.Emit(this, il, lhtype, rt);
  126.             this.operand2.TranslateToIL(il, rt);
  127.             if (rt == Typeob.Object || rt == Typeob.String) {
  128.                 il.Emit(OpCodes.Call, CompilerGlobals.plusDoOpMethod);
  129.                 rt = Typeob.Object;
  130.             }
  131.             else if (rt == Typeob.Double || rt == Typeob.Single)
  132.                 il.Emit(OpCodes.Add);
  133.             else if (rt == Typeob.Int32 || rt == Typeob.Int64 || rt == Typeob.Int16 || rt == Typeob.SByte)
  134.                 il.Emit(OpCodes.Add_Ovf);
  135.             else
  136.                 il.Emit(OpCodes.Add_Ovf_Un);
  137.             if (rtype != Typeob.Void) {
  138.                 LocalBuilder result = il.DeclareLocal(rt);
  139.                 il.Emit(OpCodes.Dup);
  140.                 il.Emit(OpCodes.Stloc, result);
  141.                 Convert.Emit(this, il, rt, lhtype);
  142.                 this.operand1.TranslateToILSet(il);
  143.                 il.Emit(OpCodes.Ldloc, result);
  144.                 Convert.Emit(this, il, rt, rtype);
  145.             }
  146.             else {
  147.                 Convert.Emit(this, il, rt, lhtype);
  148.                 this.operand1.TranslateToILSet(il);
  149.             }
  150.         }
  151.        
  152.         internal override void TranslateToIL(ILGenerator il, Type rtype)
  153.         {
  154.             if (this.metaData == null) {
  155.                 TranslateToILForNoOverloadCase(il, rtype);
  156.                 return;
  157.             }
  158.             if (this.metaData is MethodInfo) {
  159.                 object result = null;
  160.                 MethodInfo oper = (MethodInfo)this.metaData;
  161.                 Type type = Convert.ToType(this.operand1.InferType(null));
  162.                 ParameterInfo[] pars = oper.GetParameters();
  163.                 this.operand1.TranslateToILPreSetPlusGet(il);
  164.                 Convert.Emit(this, il, type, pars[0].ParameterType);
  165.                 this.operand2.TranslateToIL(il, pars[1].ParameterType);
  166.                 il.Emit(OpCodes.Call, oper);
  167.                 if (rtype != Typeob.Void) {
  168.                     result = il.DeclareLocal(rtype);
  169.                     il.Emit(OpCodes.Dup);
  170.                     Convert.Emit(this, il, type, rtype);
  171.                     il.Emit(OpCodes.Stloc, (LocalBuilder)result);
  172.                 }
  173.                 Convert.Emit(this, il, oper.ReturnType, type);
  174.                 this.operand1.TranslateToILSet(il);
  175.                 if (rtype != Typeob.Void)
  176.                     il.Emit(OpCodes.Ldloc, (LocalBuilder)result);
  177.             }
  178.             else {
  179.                 //Getting here is just too bad. We do not know until the code runs whether or not to call an overloaded operator method.
  180.                 //Compile operands to objects and devolve the decision making to run time thunks
  181.                 Type type = Convert.ToType(this.operand1.InferType(null));
  182.                 LocalBuilder result = il.DeclareLocal(Typeob.Object);
  183.                 this.operand1.TranslateToILPreSetPlusGet(il);
  184.                 Convert.Emit(this, il, type, Typeob.Object);
  185.                 il.Emit(OpCodes.Stloc, result);
  186.                 il.Emit(OpCodes.Ldloc, (LocalBuilder)this.metaData);
  187.                 il.Emit(OpCodes.Ldloc, result);
  188.                 this.operand2.TranslateToIL(il, Typeob.Object);
  189.                 il.Emit(OpCodes.Call, CompilerGlobals.evaluatePlusMethod);
  190.                 if (rtype != Typeob.Void) {
  191.                     il.Emit(OpCodes.Dup);
  192.                     il.Emit(OpCodes.Stloc, result);
  193.                 }
  194.                 Convert.Emit(this, il, Typeob.Object, type);
  195.                 this.operand1.TranslateToILSet(il);
  196.                 if (rtype != Typeob.Void) {
  197.                     il.Emit(OpCodes.Ldloc, result);
  198.                     Convert.Emit(this, il, Typeob.Object, rtype);
  199.                 }
  200.             }
  201.         }
  202.        
  203.         internal override void TranslateToILInitializer(ILGenerator il)
  204.         {
  205.             IReflect rtype = this.InferType(null);
  206.             this.operand1.TranslateToILInitializer(il);
  207.             this.operand2.TranslateToILInitializer(il);
  208.             if (rtype != Typeob.Object)
  209.                 return;
  210.             this.metaData = il.DeclareLocal(Typeob.Plus);
  211.             il.Emit(OpCodes.Newobj, CompilerGlobals.plusConstructor);
  212.             il.Emit(OpCodes.Stloc, (LocalBuilder)this.metaData);
  213.         }
  214.     }
  215.    
  216. }

Developer Fusion