Files
yallie 9fa3874800 Added the contents of the archive.
Moved the original file to the archive subfolder.
2017-11-20 16:11:42 +03:00

565 lines
22 KiB
C#

// ==++==
//
//
// 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;
using System.Diagnostics;
public sealed class NumericBinary : BinaryOp{
private Object metaData = null;
internal NumericBinary(Context context, AST operand1, AST operand2, JSToken operatorTok)
: base(context, operand1, operand2, operatorTok) {
}
public NumericBinary(int operatorTok)
: base(null, null, null, (JSToken)operatorTok){
}
internal override Object Evaluate(){
return this.EvaluateNumericBinary(this.operand1.Evaluate(), this.operand2.Evaluate());
}
#if !DEBUG
[DebuggerStepThroughAttribute]
[DebuggerHiddenAttribute]
#endif
public Object EvaluateNumericBinary(Object v1, Object v2){
if (v1 is Int32 && v2 is Int32)
return NumericBinary.DoOp((Int32)v1, (Int32)v2, this.operatorTok);
else if (v1 is Double && v2 is Double)
return NumericBinary.DoOp((Double)v1, (Double)v2, this.operatorTok);
else
return this.EvaluateNumericBinary(v1, v2, this.operatorTok);
}
#if !DEBUG
[DebuggerStepThroughAttribute]
[DebuggerHiddenAttribute]
#endif
private Object EvaluateNumericBinary(Object v1, Object v2, JSToken operatorTok){
IConvertible ic1 = Convert.GetIConvertible(v1);
IConvertible ic2 = Convert.GetIConvertible(v2);
TypeCode t1 = Convert.GetTypeCode(v1, ic1);
TypeCode t2 = Convert.GetTypeCode(v2, ic2);
switch (t1){
case TypeCode.Empty:
return Double.NaN;
case TypeCode.DBNull:
return this.EvaluateNumericBinary(0, v2, operatorTok);
case TypeCode.Char: {
Object result;
int val = ic1.ToInt32(null);
switch (t2){
case TypeCode.Empty:
return Double.NaN;
case TypeCode.DBNull:
return NumericBinary.DoOp(val, 0, operatorTok);
case TypeCode.Boolean:
case TypeCode.Char:
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
result = NumericBinary.DoOp(val, ic2.ToInt32(null), operatorTok);
break;
case TypeCode.UInt32:
case TypeCode.Int64:
result = NumericBinary.DoOp((long)val, ic2.ToInt64(null), operatorTok);
break;
case TypeCode.UInt64:
result = NumericBinary.DoOp((double)val, ic2.ToDouble(null), operatorTok);
break;
case TypeCode.Single:
case TypeCode.Double:
result = NumericBinary.DoOp((double)ic1.ToInt32(null), ic2.ToDouble(null), operatorTok);
break;
case TypeCode.String:
result = NumericBinary.DoOp(val, Convert.ToNumber(v2, ic2), operatorTok);
break;
case TypeCode.Object:
case TypeCode.Decimal:
case TypeCode.DateTime:
default:
result = null;
break;
}
if (this.operatorTok == JSToken.Minus && result != null && t2 != TypeCode.Char){
return Convert.Coerce2(result, TypeCode.Char, false);
} else if (result != null)
return result;
break;
}
case TypeCode.Boolean:
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
{int val = ic1.ToInt32(null);
switch (t2){
case TypeCode.Empty:
return Double.NaN;
case TypeCode.DBNull:
return NumericBinary.DoOp(val, 0, operatorTok);
case TypeCode.Boolean:
case TypeCode.Char:
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
return NumericBinary.DoOp(val, ic2.ToInt32(null), operatorTok);
case TypeCode.UInt32:
case TypeCode.Int64:
return NumericBinary.DoOp((long)val, ic2.ToInt64(null), operatorTok);
case TypeCode.UInt64:
if (val >= 0)
return NumericBinary.DoOp((ulong)val, ic2.ToUInt64(null), operatorTok);
else
return NumericBinary.DoOp((double)val, ic2.ToDouble(null), operatorTok);
case TypeCode.Single:
case TypeCode.Double:
return NumericBinary.DoOp((double)val, ic2.ToDouble(null), operatorTok);
case TypeCode.Object:
case TypeCode.Decimal:
case TypeCode.DateTime:
case TypeCode.String:
break;
}
break;}
case TypeCode.UInt32:
{uint val = ic1.ToUInt32(null);
switch (t2){
case TypeCode.Empty:
return Double.NaN;
case TypeCode.DBNull:
return NumericBinary.DoOp(val, 0, operatorTok);
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.Int32:
int val2 = ic2.ToInt32(null);
if (val2 >= 0)
return NumericBinary.DoOp(val, (uint)val2, operatorTok);
else
return NumericBinary.DoOp((long)val, (long)val2, operatorTok);
case TypeCode.Int64:
return NumericBinary.DoOp((long)val, ic2.ToInt64(null), operatorTok);
case TypeCode.Boolean:
case TypeCode.Char:
case TypeCode.UInt16:
case TypeCode.UInt32:
return NumericBinary.DoOp(val, ic2.ToUInt32(null), operatorTok);
case TypeCode.UInt64:
return NumericBinary.DoOp((ulong)val, ic2.ToUInt64(null), operatorTok);
case TypeCode.Single:
case TypeCode.Double:
return NumericBinary.DoOp((double)val, ic2.ToDouble(null), operatorTok);
case TypeCode.Object:
case TypeCode.Decimal:
case TypeCode.DateTime:
case TypeCode.String:
break;
}
break;}
case TypeCode.Int64:
{long val = ic1.ToInt64(null);
switch (t2){
case TypeCode.Empty:
return Double.NaN;
case TypeCode.DBNull:
return NumericBinary.DoOp(val, 0, operatorTok);
case TypeCode.Boolean:
case TypeCode.Char:
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
return NumericBinary.DoOp(val, ic2.ToInt64(null), operatorTok);
case TypeCode.UInt64:
if (val >= 0)
return NumericBinary.DoOp((ulong)val, ic2.ToUInt64(null), operatorTok);
else
return NumericBinary.DoOp((double)val, ic2.ToDouble(null), operatorTok);
case TypeCode.Single:
case TypeCode.Double:
return NumericBinary.DoOp((double)val, ic2.ToDouble(null), operatorTok);
case TypeCode.Object:
case TypeCode.Decimal:
case TypeCode.DateTime:
case TypeCode.String:
break;
}
break;}
case TypeCode.UInt64:
{ulong val = ic1.ToUInt64(null);
switch (t2){
case TypeCode.Empty:
return Double.NaN;
case TypeCode.DBNull:
return NumericBinary.DoOp(val, 0, operatorTok);
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
long val2 = ic2.ToInt64(null);
if (val2 >= 0)
return NumericBinary.DoOp(val, (ulong)val2, operatorTok);
else
return NumericBinary.DoOp((double)val, (double)val2, operatorTok);
case TypeCode.Boolean:
case TypeCode.Char:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return NumericBinary.DoOp(val, ic2.ToUInt64(null), operatorTok);
case TypeCode.Single:
case TypeCode.Double:
return NumericBinary.DoOp((double)val, ic2.ToDouble(null), operatorTok);
case TypeCode.Object:
case TypeCode.Decimal:
case TypeCode.DateTime:
case TypeCode.String:
break;
}
break;}
case TypeCode.Single:
case TypeCode.Double:
{double d = ic1.ToDouble(null);
switch (t2){
case TypeCode.Empty:
return Double.NaN;
case TypeCode.DBNull:
return NumericBinary.DoOp(d, 0, operatorTok);
case TypeCode.Boolean:
case TypeCode.Char:
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
return NumericBinary.DoOp(d, (double)ic2.ToInt32(null), operatorTok);
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
return NumericBinary.DoOp(d, ic2.ToDouble(null), operatorTok);
case TypeCode.Object:
case TypeCode.Decimal:
case TypeCode.DateTime:
case TypeCode.String:
break;
}
break;}
case TypeCode.Object:
case TypeCode.Decimal:
case TypeCode.DateTime:
case TypeCode.String:
break;
}
if (v2 == null) return Double.NaN;
MethodInfo oper = this.GetOperator(v1.GetType(), v2.GetType());
if (oper != null)
return oper.Invoke(null, (BindingFlags)0, JSBinder.ob, new Object[]{v1, v2}, null);
else{
return NumericBinary.DoOp(v1, v2, ic1, ic2, operatorTok);
}
}
public static Object DoOp(Object v1, Object v2, JSToken operatorTok){
return DoOp(v1, v2, Convert.GetIConvertible(v1), Convert.GetIConvertible(v2), operatorTok);
}
private static Object DoOp(Object v1, Object v2, IConvertible ic1, IConvertible ic2, JSToken operatorTok){
if (operatorTok == JSToken.Minus){
IConvertible ic1a = ic1;
Object p1 = Convert.ToPrimitive(v1, PreferredType.Either, ref ic1a);
TypeCode t1 = Convert.GetTypeCode(p1, ic1a);
if (t1 == TypeCode.Char){
IConvertible ic2a = ic2;
Object p2 = Convert.ToPrimitive(v2, PreferredType.Either, ref ic2a);
TypeCode t2 = Convert.GetTypeCode(p2, ic2a);
if (t2 == TypeCode.String){
String str2 = ic2a.ToString(null);
if (str2.Length == 1){
t2 = TypeCode.Char;
p2 = str2[0];
ic2a = Convert.GetIConvertible(p2);
}
}
Object result = NumericBinary.DoOp(Convert.ToNumber(p1, ic1a), Convert.ToNumber(p2, ic2a), operatorTok);
if (t2 != TypeCode.Char)
result = Convert.Coerce2(result, TypeCode.Char, false);
return result;
}
}
return NumericBinary.DoOp(Convert.ToNumber(v1, ic1), Convert.ToNumber(v2, ic2), operatorTok);
}
private static Object DoOp(int x, int y, JSToken operatorTok){
switch (operatorTok){
case JSToken.Divide:
return ((double)x) / (double)y;
case JSToken.Minus:
try{
checked {return x - y;}
}catch(OverflowException){
return ((double)x) - (double)y;
}
case JSToken.Modulo:
if (x <= 0 || y <= 0)
return ((double)x) % (double)y; //Need to result in a signed 0
return x % y;
case JSToken.Multiply:
if (x == 0 || y == 0) return ((double)x) * (double)y; //Need to result in a signed 0
try{
checked {return x * y;}
}catch(OverflowException){
return ((double)x) * (double)y;
}
default:
throw new JScriptException(JSError.InternalError);
}
}
private static Object DoOp(uint x, uint y, JSToken operatorTok){
switch (operatorTok){
case JSToken.Divide:
return ((double)x) / (double)y;
case JSToken.Minus:
try{
checked {return x - y;}
}catch(OverflowException){
return ((double)x) - (double)y;
}
case JSToken.Modulo:
if (y == 0) return Double.NaN;
return x % y;
case JSToken.Multiply:
try{
checked {return x * y;}
}catch(OverflowException){
return ((double)x) * (double)y;
}
default:
throw new JScriptException(JSError.InternalError);
}
}
private static Object DoOp(long x, long y, JSToken operatorTok){
switch (operatorTok){
case JSToken.Divide:
return ((double)x) / (double)y;
case JSToken.Minus:
try{
checked {return x - y;}
}catch(OverflowException){
return ((double)x) - (double)y;
}
case JSToken.Modulo:
if (y == 0) return Double.NaN;
long result = x % y;
if (result == 0) //Need to result in a signed 0
if (x < 0)
if (y < 0) return 0; else return 1.0 / Double.NegativeInfinity;
else
if (y < 0) return 1.0 / Double.NegativeInfinity; else return 0;
return result;
case JSToken.Multiply:
if (x == 0 || y == 0) return ((double)x) * (double)y; //Need to result in a signed 0
try{
checked {return x * y;}
}catch(OverflowException){
return ((double)x) * (double)y;
}
default:
throw new JScriptException(JSError.InternalError);
}
}
private static Object DoOp(ulong x, ulong y, JSToken operatorTok){
switch (operatorTok){
case JSToken.Divide:
return ((double)x) / (double)y;
case JSToken.Minus:
try{
checked {return x - y;}
}catch(OverflowException){
return ((double)x) - (double)y;
}
case JSToken.Modulo:
if (y == 0) return Double.NaN;
return x % y;
case JSToken.Multiply:
try{
checked {return x * y;}
}catch(OverflowException){
return ((double)x) * (double)y;
}
default:
throw new JScriptException(JSError.InternalError);
}
}
private static Object DoOp(double x, double y, JSToken operatorTok){
switch (operatorTok){
case JSToken.Divide:
return x / y;
case JSToken.Minus:
return x - y;
case JSToken.Modulo:
return x % y;
case JSToken.Multiply:
return x * y;
default:
throw new JScriptException(JSError.InternalError);
}
}
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.IsPrimitiveNumericTypeFitForDouble(this.type1) || Typeob.JSObject.IsAssignableFrom(this.type1))
&& (Convert.IsPrimitiveNumericTypeFitForDouble(this.type2) || Typeob.JSObject.IsAssignableFrom(this.type2)))
return Typeob.Double;
else
return Typeob.Object;
}
internal override void TranslateToIL(ILGenerator il, Type rtype){
if (this.metaData == null){
Type rt = Typeob.Double;
if (Convert.IsPrimitiveNumericType(rtype) && Convert.IsPromotableTo(this.type1, rtype) && Convert.IsPromotableTo(this.type2, rtype))
rt = rtype;
if (this.operatorTok == JSToken.Divide)
rt = Typeob.Double;
else if (rt == Typeob.SByte || rt == Typeob.Int16)
rt = Typeob.Int32;
else if (rt == Typeob.Byte || rt == Typeob.UInt16 || rt == Typeob.Char)
rt = Typeob.UInt32;
this.operand1.TranslateToIL(il, rt);
this.operand2.TranslateToIL(il, rt);
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){
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 (Convert.ToType(this.InferType(null)) == Typeob.Char){
Convert.Emit(this, il, rt, Typeob.Char);
Convert.Emit(this, il, Typeob.Char, rtype);
} else
Convert.Emit(this, il, rt, rtype);
return;
}
if (this.metaData is MethodInfo){
MethodInfo oper = (MethodInfo)this.metaData;
ParameterInfo[] pars = oper.GetParameters();
this.operand1.TranslateToIL(il, pars[0].ParameterType);
this.operand2.TranslateToIL(il, pars[1].ParameterType);
il.Emit(OpCodes.Call, oper);
Convert.Emit(this, il, oper.ReturnType, rtype);
return;
}
//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
il.Emit(OpCodes.Ldloc, (LocalBuilder)this.metaData);
this.operand1.TranslateToIL(il, Typeob.Object);
this.operand2.TranslateToIL(il, Typeob.Object);
il.Emit(OpCodes.Call, CompilerGlobals.evaluateNumericBinaryMethod);
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);
}
}
}