// ==++== // // // Copyright (c) 2002 Microsoft Corporation. All rights reserved. // // The use and distribution terms for this software are contained in the file // named license.txt, which can be found in the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by the // terms of this license. // // You must not remove this notice, or any other, from this software. // // // ==--== namespace Microsoft.JScript { using System; using System.Reflection; using System.Reflection.Emit; internal class NumericBinaryAssign : BinaryOp{ private NumericBinary binOp; private Object metaData; internal NumericBinaryAssign(Context context, AST operand1, AST operand2, JSToken operatorTok) : base(context, operand1, operand2, operatorTok){ this.binOp = new NumericBinary(context, operand1, operand2, operatorTok); this.metaData = null; } internal override Object Evaluate(){ Object v1 = this.operand1.Evaluate(); Object v2 = this.operand2.Evaluate(); Object result = this.binOp.EvaluateNumericBinary(v1, v2); try{ this.operand1.SetValue(result); return result; }catch(JScriptException e){ if (e.context == null) e.context = this.context; throw e; }catch(Exception e){ throw new JScriptException(e, this.context); } } internal override IReflect InferType(JSField inference_target){ MethodInfo oper; if (this.type1 == null || inference_target != null){ oper = this.GetOperator(this.operand1.InferType(inference_target), this.operand2.InferType(inference_target)); }else oper = this.GetOperator(this.type1, this.type2); if (oper != null){ this.metaData = oper; return oper.ReturnType; } if (this.type1 == Typeob.Char && this.operatorTok == JSToken.Minus){ TypeCode t2 = Type.GetTypeCode(this.type2); if (Convert.IsPrimitiveNumericTypeCode(t2) || t2 == TypeCode.Boolean) return Typeob.Char; else if (t2 == TypeCode.Char) return Typeob.Int32; } if (Convert.IsPrimitiveNumericType(this.type1)) if (Convert.IsPromotableTo(this.type2, this.type1) || ((this.operand2 is ConstantWrapper) && ((ConstantWrapper)this.operand2).IsAssignableTo(this.type1))) return this.type1; else if (Convert.IsPrimitiveNumericType(this.type1) && Convert.IsPrimitiveNumericTypeFitForDouble(this.type2)) return Typeob.Double; return Typeob.Object; } internal override AST PartiallyEvaluate(){ this.operand1 = this.operand1.PartiallyEvaluateAsReference(); this.operand2 = this.operand2.PartiallyEvaluate(); this.binOp = new NumericBinary(this.context, this.operand1, this.operand2, this.operatorTok); this.operand1.SetPartialValue(this.binOp); return this; } private void TranslateToILForNoOverloadCase(ILGenerator il, Type rtype){ Type lhtype = Convert.ToType(this.operand1.InferType(null)); Type rhtype = Convert.ToType(this.operand2.InferType(null)); Type rt = Typeob.Double; 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)))) rt = lhtype; if (rt == Typeob.SByte || rt == Typeob.Int16) rt = Typeob.Int32; else if (rt == Typeob.Byte || rt == Typeob.UInt16 || rt == Typeob.Char) rt = Typeob.UInt32; // If we have "unsigned -= signed" or "signed -= unsigned" then generating the // correct code gets quite complicated. Just go late-bound for this edge case. if (this.operand2 is ConstantWrapper){ if (!((ConstantWrapper)this.operand2).IsAssignableTo(rt)){ // eg: "var u : byte = 123; u -= -100;" should go late bound because // of signed/unsigned mismatch but "u -= 1" should not. rt = Typeob.Object; } }else{ if ((Convert.IsPrimitiveSignedNumericType(rhtype) && Convert.IsPrimitiveUnsignedIntegerType(lhtype)) || (Convert.IsPrimitiveUnsignedIntegerType(rhtype) && Convert.IsPrimitiveSignedIntegerType(lhtype))) rt = Typeob.Object; } this.operand1.TranslateToILPreSetPlusGet(il); Convert.Emit(this, il, lhtype, rt); this.operand2.TranslateToIL(il, rt); if (rt == Typeob.Object){ il.Emit(OpCodes.Ldc_I4, (int)this.operatorTok); il.Emit(OpCodes.Call, CompilerGlobals.numericbinaryDoOpMethod); } else if (rt == Typeob.Double || rt == Typeob.Single){ switch (this.operatorTok){ case JSToken.Divide: il.Emit(OpCodes.Div); break; case JSToken.Minus: il.Emit(OpCodes.Sub); break; case JSToken.Modulo: il.Emit(OpCodes.Rem); break; case JSToken.Multiply: il.Emit(OpCodes.Mul); break; default: throw new JScriptException(JSError.InternalError, this.context); } }else if (rt == Typeob.Int32 || rt == Typeob.Int64 || rt == Typeob.Int16 || rt == Typeob.SByte){ switch (this.operatorTok){ case JSToken.Divide: il.Emit(OpCodes.Div); break; case JSToken.Minus: il.Emit(OpCodes.Sub_Ovf); break; case JSToken.Modulo: il.Emit(OpCodes.Rem); break; case JSToken.Multiply: il.Emit(OpCodes.Mul_Ovf); break; default: throw new JScriptException(JSError.InternalError, this.context); } }else{ switch (this.operatorTok){ case JSToken.Divide: il.Emit(OpCodes.Div); break; case JSToken.Minus: il.Emit(OpCodes.Sub_Ovf_Un); break; case JSToken.Modulo: il.Emit(OpCodes.Rem); break; case JSToken.Multiply: il.Emit(OpCodes.Mul_Ovf_Un); break; default: throw new JScriptException(JSError.InternalError, this.context); } } if (rtype != Typeob.Void){ LocalBuilder result = il.DeclareLocal(rt); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Stloc, result); Convert.Emit(this, il, rt, lhtype); this.operand1.TranslateToILSet(il); il.Emit(OpCodes.Ldloc, result); Convert.Emit(this, il, rt, rtype); }else{ Convert.Emit(this, il, rt, lhtype); this.operand1.TranslateToILSet(il); } } internal override void TranslateToIL(ILGenerator il, Type rtype){ if (this.metaData == null){ TranslateToILForNoOverloadCase(il, rtype); return; } if (this.metaData is MethodInfo){ Object result = null; MethodInfo oper = (MethodInfo)this.metaData; Type type = Convert.ToType(this.operand1.InferType(null)); ParameterInfo[] pars = oper.GetParameters(); this.operand1.TranslateToILPreSetPlusGet(il); Convert.Emit(this, il, type, pars[0].ParameterType); this.operand2.TranslateToIL(il, pars[1].ParameterType); il.Emit(OpCodes.Call, oper); if (rtype != Typeob.Void){ result = il.DeclareLocal(rtype); il.Emit(OpCodes.Dup); Convert.Emit(this, il, type, rtype); il.Emit(OpCodes.Stloc, (LocalBuilder)result); } Convert.Emit(this, il, oper.ReturnType, type); this.operand1.TranslateToILSet(il); if (rtype != Typeob.Void) il.Emit(OpCodes.Ldloc, (LocalBuilder)result); }else{ //Getting here is just too bad. We do not know until the code runs whether or not to call an overloaded operator method. //Compile operands to objects and devolve the decision making to run time thunks Type type = Convert.ToType(this.operand1.InferType(null)); LocalBuilder result = il.DeclareLocal(Typeob.Object); this.operand1.TranslateToILPreSetPlusGet(il); Convert.Emit(this, il, type, Typeob.Object); il.Emit(OpCodes.Stloc, result); il.Emit(OpCodes.Ldloc, (LocalBuilder)this.metaData); il.Emit(OpCodes.Ldloc, result); this.operand2.TranslateToIL(il, Typeob.Object); il.Emit(OpCodes.Call, CompilerGlobals.evaluateNumericBinaryMethod); if (rtype != Typeob.Void){ il.Emit(OpCodes.Dup); il.Emit(OpCodes.Stloc, result); } Convert.Emit(this, il, Typeob.Object, type); this.operand1.TranslateToILSet(il); if (rtype != Typeob.Void){ il.Emit(OpCodes.Ldloc, result); Convert.Emit(this, il, Typeob.Object, rtype); } } } internal override void TranslateToILInitializer(ILGenerator il){ IReflect rtype = this.InferType(null); this.operand1.TranslateToILInitializer(il); this.operand2.TranslateToILInitializer(il); if (rtype != Typeob.Object) return; this.metaData = il.DeclareLocal(typeof(NumericBinary)); ConstantWrapper.TranslateToILInt(il, (int)this.operatorTok); il.Emit(OpCodes.Newobj, CompilerGlobals.numericBinaryConstructor); il.Emit(OpCodes.Stloc, (LocalBuilder)this.metaData); } } }