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

  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 Microsoft.JScript.Vsa;
  19.     using System;
  20.     using System.Reflection;
  21.     using System.Reflection.Emit;
  22.     using System.Diagnostics;
  23.    
  24.     public class Equality : BinaryOp
  25.     {
  26.         private object metaData = null;
  27.        
  28.         internal Equality(Context context, AST operand1, AST operand2, JSToken operatorTok) : base(context, operand1, operand2, operatorTok)
  29.         {
  30.         }
  31.        
  32.         public Equality(int operatorTok) : base(null, null, null, (JSToken)operatorTok)
  33.         {
  34.         }
  35.        
  36.         internal override object Evaluate()
  37.         {
  38.             bool result = this.EvaluateEquality(this.operand1.Evaluate(), this.operand2.Evaluate(), VsaEngine.executeForJSEE);
  39.             if (this.operatorTok == JSToken.Equal)
  40.                 return result;
  41.             else
  42.                 return !result;
  43.         }
  44.        
  45.         #if !DEBUG
  46.         [DebuggerStepThroughAttribute()]
  47.         [DebuggerHiddenAttribute()]
  48.         #endif
  49.         public bool EvaluateEquality(object v1, object v2)
  50.         {
  51.             return EvaluateEquality(v1, v2, false);
  52.         }
  53.        
  54.        
  55.         #if !DEBUG
  56.         [DebuggerStepThroughAttribute()]
  57.         [DebuggerHiddenAttribute()]
  58.         #endif
  59.         private bool EvaluateEquality(object v1, object v2, bool checkForDebuggerObjects)
  60.         {
  61.             if (v1 is string && v2 is string)
  62.                 return v1.Equals(v2);
  63.             if (v1 is Int32 && v2 is Int32)
  64.                 return ((int)v1) == (int)v2;
  65.             if (v1 is double && v2 is double)
  66.                 return ((double)v1) == (double)v2;
  67.             if ((v2 == null || v2 is DBNull || v2 is Missing) && !checkForDebuggerObjects)
  68.                 return (v1 == null || v1 is DBNull || v1 is Missing);
  69.             IConvertible ic1 = Convert.GetIConvertible(v1);
  70.             IConvertible ic2 = Convert.GetIConvertible(v2);
  71.             TypeCode t1 = Convert.GetTypeCode(v1, ic1);
  72.             TypeCode t2 = Convert.GetTypeCode(v2, ic2);
  73.             switch (t1) {
  74.                 case TypeCode.Empty:
  75.                 case TypeCode.DBNull:
  76.                     break;
  77.                 case TypeCode.Object:
  78.                     switch (t2) {
  79.                         case TypeCode.Empty:
  80.                         case TypeCode.DBNull:
  81.                             break;
  82.                         default:
  83.                             MethodInfo oper = this.GetOperator(v1.GetType(), v2.GetType());
  84.                             if (oper != null) {
  85.                                 bool result = (bool)oper.Invoke(null, (BindingFlags)0, JSBinder.ob, new object[] {v1, v2}, null);
  86.                                 if (this.operatorTok == JSToken.NotEqual)
  87.                                     return !result;
  88.                                 return result;
  89.                             }
  90.                             break;
  91.                     }
  92.                     break;
  93.                 default:
  94.                     switch (t2) {
  95.                         case TypeCode.Object:
  96.                             MethodInfo oper = this.GetOperator(v1.GetType(), v2.GetType());
  97.                             if (oper != null) {
  98.                                 bool result = (bool)oper.Invoke(null, (BindingFlags)0, JSBinder.ob, new object[] {v1, v2}, null);
  99.                                 if (this.operatorTok == JSToken.NotEqual)
  100.                                     return !result;
  101.                                 return result;
  102.                             }
  103.                             break;
  104.                     }
  105.                     break;
  106.             }
  107.             return Equality.JScriptEquals(v1, v2, ic1, ic2, t1, t2, checkForDebuggerObjects);
  108.         }
  109.        
  110.        
  111.        
  112.         public static bool JScriptEquals(object v1, object v2)
  113.         {
  114.             if (v1 is string && v2 is string)
  115.                 return v1.Equals(v2);
  116.             if (v1 is Int32 && v2 is Int32)
  117.                 return ((int)v1) == (int)v2;
  118.             if (v1 is double && v2 is double)
  119.                 return ((double)v1) == (double)v2;
  120.             if ((v2 == null || v2 is DBNull || v2 is Missing))
  121.                 return (v1 == null || v1 is DBNull || v1 is Missing);
  122.             IConvertible ic1 = Convert.GetIConvertible(v1);
  123.             IConvertible ic2 = Convert.GetIConvertible(v2);
  124.             TypeCode t1 = Convert.GetTypeCode(v1, ic1);
  125.             TypeCode t2 = Convert.GetTypeCode(v2, ic2);
  126.             return Equality.JScriptEquals(v1, v2, ic1, ic2, t1, t2, false);
  127.         }
  128.        
  129.         private static bool JScriptEquals(object v1, object v2, IConvertible ic1, IConvertible ic2, TypeCode t1, TypeCode t2, bool checkForDebuggerObjects)
  130.         {
  131.             if (StrictEquality.JScriptStrictEquals(v1, v2, ic1, ic2, t1, t2, checkForDebuggerObjects))
  132.                 return true;
  133.             if (t2 == TypeCode.Boolean) {
  134.                 v2 = ic2.ToBoolean(null) ? 1 : 0;
  135.                 ic2 = Convert.GetIConvertible(v2);
  136.                 return Equality.JScriptEquals(v1, v2, ic1, ic2, t1, TypeCode.Int32, false);
  137.             }
  138.             switch (t1) {
  139.                 case TypeCode.Empty:
  140.                     return t2 == TypeCode.Empty || t2 == TypeCode.DBNull || (t2 == TypeCode.Object && v2 is Missing);
  141.                 case TypeCode.Object:
  142.                     switch (t2) {
  143.                         case TypeCode.Empty:
  144.                         case TypeCode.DBNull:
  145.                             return v1 is Missing;
  146.                         case TypeCode.Char:
  147.                         case TypeCode.SByte:
  148.                         case TypeCode.Byte:
  149.                         case TypeCode.Int16:
  150.                         case TypeCode.UInt16:
  151.                         case TypeCode.Int32:
  152.                         case TypeCode.UInt32:
  153.                         case TypeCode.Int64:
  154.                         case TypeCode.UInt64:
  155.                         case TypeCode.Single:
  156.                         case TypeCode.Double:
  157.                         case TypeCode.Decimal:
  158.                         case TypeCode.String:
  159.                             IConvertible pvic1 = ic1;
  160.                             object pv1 = Convert.ToPrimitive(v1, PreferredType.Either, ref pvic1);
  161.                             if (pvic1 != null && pv1 != v1)
  162.                                 return Equality.JScriptEquals(pv1, v2, pvic1, ic2, pvic1.GetTypeCode(), t2, false);
  163.                             else
  164.                                 return false;
  165.                             break;
  166.                     }
  167.                     return false;
  168.                 case TypeCode.DBNull:
  169.                     return t2 == TypeCode.DBNull || t2 == TypeCode.Empty || (t2 == TypeCode.Object && v2 is Missing);
  170.                 case TypeCode.Boolean:
  171.                     v1 = ic1.ToBoolean(null) ? 1 : 0;
  172.                     ic1 = Convert.GetIConvertible(v1);
  173.                     return Equality.JScriptEquals(v1, v2, ic1, ic2, TypeCode.Int32, t2, false);
  174.                 case TypeCode.Char:
  175.                 case TypeCode.SByte:
  176.                 case TypeCode.Byte:
  177.                 case TypeCode.Int16:
  178.                 case TypeCode.UInt16:
  179.                 case TypeCode.Int32:
  180.                 case TypeCode.UInt32:
  181.                 case TypeCode.Int64:
  182.                 case TypeCode.UInt64:
  183.                 case TypeCode.Single:
  184.                 case TypeCode.Double:
  185.                 case TypeCode.Decimal:
  186.                     if (t2 == TypeCode.Object) {
  187.                         IConvertible pvic2 = ic2;
  188.                         object pv2 = Convert.ToPrimitive(v2, PreferredType.Either, ref pvic2);
  189.                         if (pvic2 != null && pv2 != v2)
  190.                             return Equality.JScriptEquals(v1, pv2, ic1, pvic2, t1, pvic2.GetTypeCode(), false);
  191.                         else
  192.                             return false;
  193.                     }
  194.                     if (t2 == TypeCode.String) {
  195.                         if (v1 is Enum)
  196.                             return Convert.ToString(v1).Equals(ic2.ToString(null));
  197.                         v2 = Convert.ToNumber(v2, ic2);
  198.                         ic2 = Convert.GetIConvertible(v2);
  199.                         return StrictEquality.JScriptStrictEquals(v1, v2, ic1, ic2, t1, TypeCode.Double, false);
  200.                     }
  201.                     return false;
  202.                 case TypeCode.DateTime:
  203.                     if (t2 == TypeCode.Object) {
  204.                         IConvertible pvic2 = ic2;
  205.                         object pv2 = Convert.ToPrimitive(v2, PreferredType.Either, ref pvic2);
  206.                         if (pv2 != null && pv2 != v2)
  207.                             return StrictEquality.JScriptStrictEquals(v1, pv2, ic1, pvic2, t1, pvic2.GetTypeCode(), false);
  208.                     }
  209.                     return false;
  210.                 case TypeCode.String:
  211.                     switch (t2) {
  212.                         case TypeCode.Object:
  213.                            
  214.                             {
  215.                                 IConvertible pvic2 = ic2;
  216.                                 object pv2 = Convert.ToPrimitive(v2, PreferredType.Either, ref pvic2);
  217.                                 if (pvic2 != null && pv2 != v2)
  218.                                     return Equality.JScriptEquals(v1, pv2, ic1, pvic2, t1, pvic2.GetTypeCode(), false);
  219.                                 else
  220.                                     return false;
  221.                             }
  222.                             break;
  223.                         case TypeCode.SByte:
  224.                         case TypeCode.Byte:
  225.                         case TypeCode.Int16:
  226.                         case TypeCode.UInt16:
  227.                         case TypeCode.Int32:
  228.                         case TypeCode.UInt32:
  229.                         case TypeCode.Int64:
  230.                         case TypeCode.UInt64:
  231.                         case TypeCode.Single:
  232.                         case TypeCode.Double:
  233.                         case TypeCode.Decimal:
  234.                             if (v2 is Enum)
  235.                                 return Convert.ToString(v2).Equals(ic1.ToString(null));
  236.                             v1 = Convert.ToNumber(v1, ic1);
  237.                             ic1 = Convert.GetIConvertible(v1);
  238.                             return StrictEquality.JScriptStrictEquals(v1, v2, ic1, ic2, TypeCode.Double, t2, false);
  239.                     }
  240.                     return false;
  241.             }
  242.             return false;
  243.         }
  244.        
  245.         internal override IReflect InferType(JSField inference_target)
  246.         {
  247.             return Typeob.Boolean;
  248.         }
  249.        
  250.         internal override void TranslateToConditionalBranch(ILGenerator il, bool branchIfTrue, Label label, bool shortForm)
  251.         {
  252.             if (this.metaData == null) {
  253.                 Type t1 = this.type1;
  254.                 Type t2 = this.type2;
  255.                 Type t3 = Typeob.Object;
  256.                 bool emitNullAndUndefined = true;
  257.                 if (t1.IsPrimitive && t2.IsPrimitive) {
  258.                     t3 = Typeob.Double;
  259.                     if (t1 == Typeob.Single || t2 == Typeob.Single)
  260.                         t3 = Typeob.Single;
  261.                     else if (Convert.IsPromotableTo(t1, t2))
  262.                         t3 = t2;
  263.                     else if (Convert.IsPromotableTo(t2, t1))
  264.                         t3 = t1;
  265.                 }
  266.                 else if (t1 == Typeob.String && (t2 == Typeob.String || t2 == Typeob.Empty || t2 == Typeob.Null)) {
  267.                     t3 = Typeob.String;
  268.                     if (t2 != Typeob.String) {
  269.                         emitNullAndUndefined = false;
  270.                         branchIfTrue = !branchIfTrue;
  271.                     }
  272.                 }
  273.                 else if ((t1 == Typeob.Empty || t1 == Typeob.Null) && t2 == Typeob.String) {
  274.                     t3 = Typeob.String;
  275.                     emitNullAndUndefined = false;
  276.                     branchIfTrue = !branchIfTrue;
  277.                 }
  278.                 if (t3 == Typeob.SByte || t3 == Typeob.Int16)
  279.                     t3 = Typeob.Int32;
  280.                 else if (t3 == Typeob.Byte || t3 == Typeob.UInt16)
  281.                     t3 = Typeob.UInt32;
  282.                 if (emitNullAndUndefined) {
  283.                     this.operand1.TranslateToIL(il, t3);
  284.                     this.operand2.TranslateToIL(il, t3);
  285.                     if (t3 == Typeob.Object)
  286.                         il.Emit(OpCodes.Call, CompilerGlobals.jScriptEqualsMethod);
  287.                     else if (t3 == Typeob.String)
  288.                         il.Emit(OpCodes.Call, CompilerGlobals.stringEqualsMethod);
  289.                 }
  290.                 else if (t1 == Typeob.String)
  291.                     this.operand1.TranslateToIL(il, t3);
  292.                 else if (t2 == Typeob.String)
  293.                     this.operand2.TranslateToIL(il, t3);
  294.                 if (branchIfTrue) {
  295.                     if (this.operatorTok == JSToken.Equal)
  296.                         if (t3 == Typeob.String || t3 == Typeob.Object)
  297.                             il.Emit(shortForm ? OpCodes.Brtrue_S : OpCodes.Brtrue, label);
  298.                         else
  299.                             il.Emit(shortForm ? OpCodes.Beq_S : OpCodes.Beq, label);
  300.                     else if (t3 == Typeob.String || t3 == Typeob.Object)
  301.                         il.Emit(shortForm ? OpCodes.Brfalse_S : OpCodes.Brfalse, label);
  302.                     else
  303.                         il.Emit(shortForm ? OpCodes.Bne_Un_S : OpCodes.Bne_Un, label);
  304.                 }
  305.                 else {
  306.                     if (this.operatorTok == JSToken.Equal)
  307.                         if (t3 == Typeob.String || t3 == Typeob.Object)
  308.                             il.Emit(shortForm ? OpCodes.Brfalse_S : OpCodes.Brfalse, label);
  309.                         else
  310.                             il.Emit(shortForm ? OpCodes.Bne_Un_S : OpCodes.Bne_Un, label);
  311.                     else if (t3 == Typeob.String || t3 == Typeob.Object)
  312.                         il.Emit(shortForm ? OpCodes.Brtrue_S : OpCodes.Brtrue, label);
  313.                     else
  314.                         il.Emit(shortForm ? OpCodes.Beq_S : OpCodes.Beq, label);
  315.                 }
  316.                 return;
  317.             }
  318.             if (this.metaData is MethodInfo) {
  319.                 MethodInfo oper = (MethodInfo)this.metaData;
  320.                 ParameterInfo[] pars = oper.GetParameters();
  321.                 this.operand1.TranslateToIL(il, pars[0].ParameterType);
  322.                 this.operand2.TranslateToIL(il, pars[1].ParameterType);
  323.                 il.Emit(OpCodes.Call, oper);
  324.                 if (branchIfTrue)
  325.                     il.Emit(shortForm ? OpCodes.Brtrue_S : OpCodes.Brtrue, label);
  326.                 else
  327.                     il.Emit(shortForm ? OpCodes.Brfalse_S : OpCodes.Brfalse, label);
  328.                 return;
  329.             }
  330.             //Getting here is just too bad. We do not know until the code runs whether or not to call an overloaded operator method.
  331.             //Compile operands to objects and devolve the decision making to run time thunks
  332.             il.Emit(OpCodes.Ldloc, (LocalBuilder)this.metaData);
  333.             this.operand1.TranslateToIL(il, Typeob.Object);
  334.             this.operand2.TranslateToIL(il, Typeob.Object);
  335.             il.Emit(OpCodes.Call, CompilerGlobals.evaluateEqualityMethod);
  336.             if (branchIfTrue) {
  337.                 if (this.operatorTok == JSToken.Equal)
  338.                     il.Emit(shortForm ? OpCodes.Brtrue_S : OpCodes.Brtrue, label);
  339.                 else
  340.                     il.Emit(shortForm ? OpCodes.Brfalse_S : OpCodes.Brfalse, label);
  341.             }
  342.             else {
  343.                 if (this.operatorTok == JSToken.Equal)
  344.                     il.Emit(shortForm ? OpCodes.Brfalse_S : OpCodes.Brfalse, label);
  345.                 else
  346.                     il.Emit(shortForm ? OpCodes.Brtrue_S : OpCodes.Brtrue, label);
  347.             }
  348.             return;
  349.         }
  350.        
  351.         internal override void TranslateToIL(ILGenerator il, Type rtype)
  352.         {
  353.             Label true_label = il.DefineLabel();
  354.             Label done_label = il.DefineLabel();
  355.             this.TranslateToConditionalBranch(il, true, true_label, true);
  356.             il.Emit(OpCodes.Ldc_I4_0);
  357.             il.Emit(OpCodes.Br_S, done_label);
  358.             il.MarkLabel(true_label);
  359.             il.Emit(OpCodes.Ldc_I4_1);
  360.             il.MarkLabel(done_label);
  361.             Convert.Emit(this, il, Typeob.Boolean, rtype);
  362.         }
  363.        
  364.         internal override void TranslateToILInitializer(ILGenerator il)
  365.         {
  366.             this.operand1.TranslateToILInitializer(il);
  367.             this.operand2.TranslateToILInitializer(il);
  368.             MethodInfo oper = this.GetOperator(this.operand1.InferType(null), this.operand2.InferType(null));
  369.             if (oper != null) {
  370.                 this.metaData = oper;
  371.                 return;
  372.             }
  373.             if (this.operand1 is ConstantWrapper) {
  374.                 object val = this.operand1.Evaluate();
  375.                 if (val == null)
  376.                     this.type1 = Typeob.Empty;
  377.                 else if (val is DBNull)
  378.                     this.type1 = Typeob.Null;
  379.             }
  380.             if (this.operand2 is ConstantWrapper) {
  381.                 object val = this.operand2.Evaluate();
  382.                 if (val == null)
  383.                     this.type2 = Typeob.Empty;
  384.                 else if (val is DBNull)
  385.                     this.type2 = Typeob.Null;
  386.             }
  387.             if (this.type1 == Typeob.Empty || this.type1 == Typeob.Null || this.type2 == Typeob.Empty || this.type2 == Typeob.Null)
  388.                 return;
  389.             if ((this.type1.IsPrimitive || this.type1 == Typeob.String || Typeob.JSObject.IsAssignableFrom(this.type1)) && (this.type2.IsPrimitive || this.type2 == Typeob.String || Typeob.JSObject.IsAssignableFrom(this.type2)))
  390.                 return;
  391.             this.metaData = il.DeclareLocal(Typeob.Equality);
  392.             ConstantWrapper.TranslateToILInt(il, (int)this.operatorTok);
  393.             il.Emit(OpCodes.Newobj, CompilerGlobals.equalityConstructor);
  394.             il.Emit(OpCodes.Stloc, (LocalBuilder)this.metaData);
  395.         }
  396.     }
  397. }

Developer Fusion