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

369 lines
15 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 class Relational : BinaryOp{
private Object metaData = null;
internal Relational(Context context, AST operand1, AST operand2, JSToken operatorTok)
: base(context, operand1, operand2, operatorTok) {
}
public Relational(int operatorTok)
: base(null, null, null, (JSToken)operatorTok){
}
internal override Object Evaluate(){
Object v1 = this.operand1.Evaluate();
Object v2 = this.operand2.Evaluate();
double result = this.EvaluateRelational(v1, v2);
switch (this.operatorTok){
case JSToken.GreaterThan:
return result > 0;
case JSToken.GreaterThanEqual:
return result >= 0;
case JSToken.LessThan:
return result < 0;
case JSToken.LessThanEqual:
return result <= 0;
default:
throw new JScriptException(JSError.InternalError, this.context);
}
}
#if !DEBUG
[DebuggerStepThroughAttribute]
[DebuggerHiddenAttribute]
#endif
public double EvaluateRelational(Object v1, Object v2){
if (v1 is Int32){
if (v2 is Int32)
return ((double)(int)v1) - (double)(int)v2;
else if (v2 is Double)
return ((Int32)v1) - (Double)v2;
}else if (v1 is Double){
if (v2 is Double){
double d1 = (Double)v1;
double d2 = (Double)v2;
if (d1 == d2) return 0; //d1 and d2 could be infinities
return d1 - d2;
}else if (v2 is Int32)
return ((Double)v1) - (Int32)v2;
}
IConvertible ic1 = Convert.GetIConvertible(v1);
IConvertible ic2 = Convert.GetIConvertible(v2);
TypeCode t1 = Convert.GetTypeCode(v1, ic1);
TypeCode t2 = Convert.GetTypeCode(v2, ic2);
if (t1 == TypeCode.Object && t2 == TypeCode.Object){
MethodInfo oper = this.GetOperator(v1.GetType(), v2.GetType());
if (oper != null){
bool result = Convert.ToBoolean(oper.Invoke(null, (BindingFlags)0, JSBinder.ob, new Object[]{v1, v2}, null));
switch (this.operatorTok){
case JSToken.GreaterThan:
case JSToken.GreaterThanEqual:
return result ? 1 : -1;
case JSToken.LessThan:
case JSToken.LessThanEqual:
return result ? -1 : 1;
default:
throw new JScriptException(JSError.InternalError, this.context);
}
}
}
return JScriptCompare2(v1, v2, ic1, ic2, t1, t2);
}
internal override IReflect InferType(JSField inference_target){
return Typeob.Boolean;
}
public static double JScriptCompare(Object v1, Object v2){
if (v1 is Int32){
if (v2 is Int32)
return ((Int32)v1) - (Int32)v2;
else if (v2 is Double)
return ((Int32)v1) - (Double)v2;
}else if (v1 is Double){
if (v2 is Double){
double d1 = (Double)v1;
double d2 = (Double)v2;
if (d1 == d2) return 0; //d1 and d2 could be infinities
return d1 - d2;
}else if (v2 is Int32)
return ((Double)v1) - (Int32)v2;
}
IConvertible ic1 = Convert.GetIConvertible(v1);
IConvertible ic2 = Convert.GetIConvertible(v2);
TypeCode t1 = Convert.GetTypeCode(v1, ic1);
TypeCode t2 = Convert.GetTypeCode(v2, ic2);
return JScriptCompare2(v1, v2, ic1, ic2, t1, t2);
}
private static double JScriptCompare2(Object v1, Object v2, IConvertible ic1, IConvertible ic2, TypeCode t1, TypeCode t2){
if (t1 == TypeCode.Object){
v1 = Convert.ToPrimitive(v1, PreferredType.Number, ref ic1);
t1 = Convert.GetTypeCode(v1, ic1);
}
if (t2 == TypeCode.Object){
v2 = Convert.ToPrimitive(v2, PreferredType.Number, ref ic2);
t2 = Convert.GetTypeCode(v2, ic2);
}
switch (t1){
case TypeCode.Char:
if (t2 == TypeCode.String) return String.CompareOrdinal(Convert.ToString(v1, ic1), ic2.ToString(null));
goto case TypeCode.UInt16;
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
long l = ic1.ToInt64(null);
switch (t2){
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
return l - ic2.ToInt64(null);
case TypeCode.UInt64:
if (l < 0) return -1;
ulong ul2 = ic2.ToUInt64(null);
if (((ulong)l) < ul2) return -1;
if (((ulong)l) == ul2) return 0;
return 1;
case TypeCode.Single:
case TypeCode.Double:
return ((double)l) - ic2.ToDouble(null);
case TypeCode.Decimal:
return (double)(new Decimal(l) - ic2.ToDecimal(null));
default:
Object bd2 = Convert.ToNumber(v2, ic2);
return JScriptCompare2(v1, bd2, ic1, Convert.GetIConvertible(bd2), t1, TypeCode.Double);
}
case TypeCode.UInt64:
ulong ul = ic1.ToUInt64(null);
switch (t2){
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
long l2 = ic2.ToInt64(null);
if (l2 < 0) return 1;
if (ul == (ulong)l2) return 0;
return -1;
case TypeCode.UInt64:
ulong ul2 = ic2.ToUInt64(null);
if (ul < ul2) return -1;
if (ul == ul2) return 0;
return 1;
case TypeCode.Single:
case TypeCode.Double:
return ((double)ul) - ic2.ToDouble(null);
case TypeCode.Decimal:
return (double)(new Decimal(ul) - ic2.ToDecimal(null));
default:
Object bd2 = Convert.ToNumber(v2, ic2);
return JScriptCompare2(v1, bd2, ic1, Convert.GetIConvertible(bd2), t1, TypeCode.Double);
}
case TypeCode.Decimal:
Decimal dec1 = ic1.ToDecimal(null);
switch (t2){
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
return (double)(dec1 - new Decimal(ic2.ToInt64(null)));
case TypeCode.UInt64:
return (double)(dec1 - new Decimal(ic2.ToUInt64(null)));
case TypeCode.Single:
case TypeCode.Double:
return (double)(dec1 - new Decimal(ic2.ToDouble(null)));
case TypeCode.Decimal:
return (double)(dec1 - ic2.ToDecimal(null));
default:
return (double)(dec1 - new Decimal(Convert.ToNumber(v2, ic2)));
}
case TypeCode.String:
switch (t2){
case TypeCode.String: return String.CompareOrdinal(ic1.ToString(null), ic2.ToString(null));
case TypeCode.Char : return String.CompareOrdinal(ic1.ToString(null), Convert.ToString(v2, ic2));
}
goto default;
default:
double d1 = Convert.ToNumber(v1, ic1);
double d2 = Convert.ToNumber(v2, ic2);
if (d1 == d2) return 0; //d1 and d2 could be infinities
return d1 - d2;
}
}
internal override void TranslateToConditionalBranch(ILGenerator il, bool branchIfTrue, Label label, bool shortForm){
Type t1 = this.type1;
Type t2 = this.type2;
Type t3 = Typeob.Object;
if (t1.IsPrimitive && t2.IsPrimitive){
t3 = Typeob.Double;
if (Convert.IsPromotableTo(t1, t2))
t3 = t2;
else if (Convert.IsPromotableTo(t2, t1))
t3 = t1;
else if (t1 == Typeob.Int64 || t1 == Typeob.UInt64 || t2 == Typeob.Int64 || t2 == Typeob.UInt64)
t3 = Typeob.Object; //Cannot convert these to double without loss of precision in some cases, so use the helper method
}
if (t3 == Typeob.SByte || t3 == Typeob.Int16)
t3 = Typeob.Int32;
else if (t3 == Typeob.Byte || t3 == Typeob.UInt16)
t3 = Typeob.UInt32;
if (this.metaData == null){
this.operand1.TranslateToIL(il, t3);
this.operand2.TranslateToIL(il, t3);
if (t3 == Typeob.Object){
il.Emit(OpCodes.Call, CompilerGlobals.jScriptCompareMethod);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Conv_R8);
t3 = Typeob.Double;
}
}else 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);
if (branchIfTrue)
il.Emit(shortForm ? OpCodes.Brtrue_S : OpCodes.Brtrue, label);
else
il.Emit(shortForm ? OpCodes.Brfalse_S : OpCodes.Brfalse, label);
return;
}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
il.Emit(OpCodes.Ldloc, (LocalBuilder)this.metaData);
this.operand1.TranslateToIL(il, Typeob.Object);
this.operand2.TranslateToIL(il, Typeob.Object);
il.Emit(OpCodes.Call, CompilerGlobals.evaluateRelationalMethod);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Conv_R8);
t3 = Typeob.Double;
}
if (branchIfTrue){
if (t3 == Typeob.UInt32 || t3 == Typeob.UInt64)
switch (this.operatorTok){
case JSToken.GreaterThan:
il.Emit(shortForm ? OpCodes.Bgt_Un_S : OpCodes.Bgt_Un, label); break;
case JSToken.GreaterThanEqual:
il.Emit(shortForm ? OpCodes.Bge_Un_S : OpCodes.Bge_Un, label); break;
case JSToken.LessThan:
il.Emit(shortForm ? OpCodes.Blt_Un_S : OpCodes.Blt_Un, label); break;
case JSToken.LessThanEqual:
il.Emit(shortForm ? OpCodes.Ble_Un_S : OpCodes.Ble_Un, label); break;
default:
throw new JScriptException(JSError.InternalError, this.context);
}
else
switch (this.operatorTok){
case JSToken.GreaterThan:
il.Emit(shortForm ? OpCodes.Bgt_S : OpCodes.Bgt, label); break;
case JSToken.GreaterThanEqual:
il.Emit(shortForm ? OpCodes.Bge_S : OpCodes.Bge, label); break;
case JSToken.LessThan:
il.Emit(shortForm ? OpCodes.Blt_S : OpCodes.Blt, label); break;
case JSToken.LessThanEqual:
il.Emit(shortForm ? OpCodes.Ble_S : OpCodes.Ble, label); break;
default:
throw new JScriptException(JSError.InternalError, this.context);
}
}else{
if (t3 == Typeob.Int32 || t3 == Typeob.Int64)
switch (this.operatorTok){
case JSToken.GreaterThan:
il.Emit(shortForm ? OpCodes.Ble_S : OpCodes.Ble, label); break;
case JSToken.GreaterThanEqual:
il.Emit(shortForm ? OpCodes.Blt_S : OpCodes.Blt, label); break;
case JSToken.LessThan:
il.Emit(shortForm ? OpCodes.Bge_S : OpCodes.Bge, label); break;
case JSToken.LessThanEqual:
il.Emit(shortForm ? OpCodes.Bgt_S : OpCodes.Bgt, label); break;
default:
throw new JScriptException(JSError.InternalError, this.context);
}
else
switch (this.operatorTok){
case JSToken.GreaterThan:
il.Emit(shortForm ? OpCodes.Ble_Un_S : OpCodes.Ble_Un, label); break;
case JSToken.GreaterThanEqual:
il.Emit(shortForm ? OpCodes.Blt_Un_S : OpCodes.Blt_Un, label); break;
case JSToken.LessThan:
il.Emit(shortForm ? OpCodes.Bge_Un_S : OpCodes.Bge_Un, label); break;
case JSToken.LessThanEqual:
il.Emit(shortForm ? OpCodes.Bgt_Un_S : OpCodes.Bgt_Un, label); break;
default:
throw new JScriptException(JSError.InternalError, this.context);
}
}
}
internal override void TranslateToIL(ILGenerator il, Type rtype){
Label true_label = il.DefineLabel();
Label done_label = il.DefineLabel();
this.TranslateToConditionalBranch(il, true, true_label, true);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Br_S, done_label);
il.MarkLabel(true_label);
il.Emit(OpCodes.Ldc_I4_1);
il.MarkLabel(done_label);
Convert.Emit(this, il, Typeob.Boolean, rtype);
}
internal override void TranslateToILInitializer(ILGenerator il){
this.operand1.TranslateToILInitializer(il);
this.operand2.TranslateToILInitializer(il);
MethodInfo oper = this.GetOperator(this.operand1.InferType(null), this.operand2.InferType(null));
if (oper != null){
this.metaData = oper;
return;
}
if ((this.type1.IsPrimitive || Typeob.JSObject.IsAssignableFrom(this.type1))
&& (this.type2.IsPrimitive || Typeob.JSObject.IsAssignableFrom(this.type2)))
return;
this.metaData = il.DeclareLocal(typeof(Relational));
ConstantWrapper.TranslateToILInt(il, (int)this.operatorTok);
il.Emit(OpCodes.Newobj, CompilerGlobals.relationalConstructor);
il.Emit(OpCodes.Stloc, (LocalBuilder)this.metaData);
}
}
}