JintBinaryExpression.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. using System;
  2. using Esprima.Ast;
  3. using Jint.Native;
  4. using Jint.Native.Function;
  5. using Jint.Native.Object;
  6. using Jint.Runtime.Interop;
  7. namespace Jint.Runtime.Interpreter.Expressions
  8. {
  9. internal abstract class JintBinaryExpression : JintExpression
  10. {
  11. private readonly JintExpression _left;
  12. private readonly JintExpression _right;
  13. private readonly BinaryOperator _operatorType;
  14. private JintBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  15. {
  16. _left = Build(engine, expression.Left);
  17. _right = Build(engine, expression.Right);
  18. _operatorType = expression.Operator;
  19. }
  20. internal static JintExpression Build(Engine engine, BinaryExpression expression)
  21. {
  22. JintExpression result;
  23. switch (expression.Operator)
  24. {
  25. case BinaryOperator.StrictlyEqual:
  26. result = new StrictlyEqualBinaryExpression(engine, expression);
  27. break;
  28. case BinaryOperator.StricltyNotEqual:
  29. result = new StrictlyNotEqualBinaryExpression(engine, expression);
  30. break;
  31. case BinaryOperator.Less:
  32. result = new LessBinaryExpression(engine, expression);
  33. break;
  34. case BinaryOperator.Greater:
  35. result = new GreaterBinaryExpression(engine, expression);
  36. break;
  37. default:
  38. result = new JintGenericBinaryExpression(engine, expression);
  39. break;
  40. }
  41. if (expression.Left.Type == Nodes.Literal
  42. && expression.Right.Type == Nodes.Literal
  43. && expression.Operator != BinaryOperator.InstanceOf
  44. && expression.Operator != BinaryOperator.In)
  45. {
  46. // calculate eagerly
  47. // TODO result = new JintConstantExpression(engine, (JsValue) result.Evaluate());
  48. }
  49. return result;
  50. }
  51. public static bool StrictlyEqual(JsValue x, JsValue y)
  52. {
  53. var typeX = x.Type;
  54. var typeY = y.Type;
  55. if (typeX != typeY)
  56. {
  57. return false;
  58. }
  59. switch (typeX)
  60. {
  61. case Types.Undefined:
  62. case Types.Null:
  63. return true;
  64. case Types.Number:
  65. var nx = ((JsNumber) x)._value;
  66. var ny = ((JsNumber) y)._value;
  67. return !double.IsNaN(nx) && !double.IsNaN(ny) && nx == ny;
  68. case Types.String:
  69. return x.AsStringWithoutTypeCheck() == y.AsStringWithoutTypeCheck();
  70. case Types.Boolean:
  71. return ((JsBoolean) x)._value == ((JsBoolean) y)._value;
  72. case Types.Object when x.AsObject() is IObjectWrapper xw:
  73. var yw = y.AsObject() as IObjectWrapper;
  74. if (yw == null)
  75. return false;
  76. return Equals(xw.Target, yw.Target);
  77. case Types.None:
  78. return true;
  79. default:
  80. return x == y;
  81. }
  82. }
  83. private sealed class JintGenericBinaryExpression : JintBinaryExpression
  84. {
  85. private readonly Func<JsValue, JsValue, JsValue> _operator;
  86. public JintGenericBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  87. {
  88. switch (_operatorType)
  89. {
  90. case BinaryOperator.Plus:
  91. _operator = (left, right) =>
  92. {
  93. var lprim = TypeConverter.ToPrimitive(left);
  94. var rprim = TypeConverter.ToPrimitive(right);
  95. return lprim.IsString() || rprim.IsString()
  96. ? (JsValue) JsString.Create(TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim))
  97. : JsNumber.Create(TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim));
  98. };
  99. break;
  100. case BinaryOperator.Minus:
  101. _operator = (left, right) => JsNumber.Create(TypeConverter.ToNumber(left) - TypeConverter.ToNumber(right));
  102. break;
  103. case BinaryOperator.Times:
  104. _operator = (left, right) => left.IsUndefined() || right.IsUndefined()
  105. ? Undefined.Instance
  106. : JsNumber.Create(TypeConverter.ToNumber(left) * TypeConverter.ToNumber(right));
  107. break;
  108. case BinaryOperator.Divide:
  109. _operator = Divide;
  110. break;
  111. case BinaryOperator.Modulo:
  112. _operator = (left, right) => left.IsUndefined() || right.IsUndefined()
  113. ? Undefined.Instance
  114. : TypeConverter.ToNumber(left) % TypeConverter.ToNumber(right);
  115. break;
  116. case BinaryOperator.Equal:
  117. _operator = (left, right) => Equal(left, right)
  118. ? JsBoolean.True
  119. : JsBoolean.False;
  120. break;
  121. case BinaryOperator.NotEqual:
  122. _operator = (left, right) => Equal(left, right) ? JsBoolean.False : JsBoolean.True;
  123. break;
  124. case BinaryOperator.GreaterOrEqual:
  125. _operator = (left, right) =>
  126. {
  127. var value = Compare(left, right);
  128. if (value.IsUndefined() || ((JsBoolean) value)._value)
  129. {
  130. value = JsBoolean.False;
  131. }
  132. else
  133. {
  134. value = JsBoolean.True;
  135. }
  136. return value;
  137. };
  138. break;
  139. case BinaryOperator.LessOrEqual:
  140. _operator = (left, right) =>
  141. {
  142. var value = Compare(right, left, false);
  143. if (value.IsUndefined() || ((JsBoolean) value)._value)
  144. {
  145. value = JsBoolean.False;
  146. }
  147. else
  148. {
  149. value = JsBoolean.True;
  150. }
  151. return value;
  152. };
  153. break;
  154. case BinaryOperator.BitwiseAnd:
  155. _operator = (left, right) => JsNumber.Create(TypeConverter.ToInt32(left) & TypeConverter.ToInt32(right));
  156. break;
  157. case BinaryOperator.BitwiseOr:
  158. _operator = (left, right) => JsNumber.Create(TypeConverter.ToInt32(left) | TypeConverter.ToInt32(right));
  159. break;
  160. case BinaryOperator.BitwiseXOr:
  161. _operator = (left, right) => JsNumber.Create(TypeConverter.ToInt32(left) ^ TypeConverter.ToInt32(right));
  162. break;
  163. case BinaryOperator.LeftShift:
  164. _operator = (left, right) => JsNumber.Create(TypeConverter.ToInt32(left) << (int) (TypeConverter.ToUint32(right) & 0x1F));
  165. break;
  166. case BinaryOperator.RightShift:
  167. _operator = (left, right) => JsNumber.Create(TypeConverter.ToInt32(left) >> (int) (TypeConverter.ToUint32(right) & 0x1F));
  168. break;
  169. case BinaryOperator.UnsignedRightShift:
  170. _operator = (left, right) => JsNumber.Create((uint) TypeConverter.ToInt32(left) >> (int) (TypeConverter.ToUint32(right) & 0x1F));
  171. break;
  172. case BinaryOperator.Exponentiation:
  173. _operator = (left, right) => JsNumber.Create(Math.Pow(TypeConverter.ToNumber(left), TypeConverter.ToNumber(right)));
  174. break;
  175. case BinaryOperator.InstanceOf:
  176. _operator = (left, right) =>
  177. {
  178. if (!(right is FunctionInstance f))
  179. {
  180. return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "instanceof can only be used with a function object");
  181. }
  182. return f.HasInstance(left) ? JsBoolean.True : JsBoolean.False;
  183. };
  184. break;
  185. case BinaryOperator.In:
  186. _operator = (left, right) =>
  187. {
  188. if (!(right is ObjectInstance oi))
  189. {
  190. return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "in can only be used with an object");
  191. }
  192. return oi.HasProperty(TypeConverter.ToString(left)) ? JsBoolean.True : JsBoolean.False;
  193. };
  194. break;
  195. default:
  196. _operator = ExceptionHelper.ThrowNotImplementedException<Func<JsValue, JsValue, JsValue>>();
  197. break;
  198. }
  199. }
  200. protected override object EvaluateInternal()
  201. {
  202. var left = _left.GetValue();
  203. var right = _right.GetValue();
  204. return _operator(left, right);
  205. }
  206. }
  207. private sealed class StrictlyEqualBinaryExpression : JintBinaryExpression
  208. {
  209. public StrictlyEqualBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  210. {
  211. }
  212. protected override object EvaluateInternal()
  213. {
  214. var left = _left.GetValue();
  215. var right = _right.GetValue();
  216. return StrictlyEqual(left, right) ? JsBoolean.True : JsBoolean.False;
  217. }
  218. }
  219. private sealed class StrictlyNotEqualBinaryExpression : JintBinaryExpression
  220. {
  221. public StrictlyNotEqualBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  222. {
  223. }
  224. protected override object EvaluateInternal()
  225. {
  226. var left = _left.GetValue();
  227. var right = _right.GetValue();
  228. return StrictlyEqual(left, right) ? JsBoolean.False : JsBoolean.True;
  229. }
  230. }
  231. private sealed class LessBinaryExpression : JintBinaryExpression
  232. {
  233. public LessBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  234. {
  235. }
  236. protected override object EvaluateInternal()
  237. {
  238. var left = _left.GetValue();
  239. var right = _right.GetValue();
  240. var value = Compare(left, right);
  241. if (value._type == Types.Undefined)
  242. {
  243. value = JsBoolean.False;
  244. }
  245. return value;
  246. }
  247. }
  248. private sealed class GreaterBinaryExpression : JintBinaryExpression
  249. {
  250. public GreaterBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  251. {
  252. }
  253. protected override object EvaluateInternal()
  254. {
  255. var left = _left.GetValue();
  256. var right = _right.GetValue();
  257. var value = Compare(right, left, false);
  258. if (value._type == Types.Undefined)
  259. {
  260. value = JsBoolean.False;
  261. }
  262. return value;
  263. }
  264. }
  265. }
  266. }