JintBinaryExpression.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. using System;
  2. using Esprima.Ast;
  3. using Jint.Native;
  4. using Jint.Native.Object;
  5. using Jint.Runtime.Interop;
  6. namespace Jint.Runtime.Interpreter.Expressions
  7. {
  8. internal abstract class JintBinaryExpression : JintExpression
  9. {
  10. private readonly JintExpression _left;
  11. private readonly JintExpression _right;
  12. private JintBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  13. {
  14. _left = Build(engine, expression.Left);
  15. _right = Build(engine, expression.Right);
  16. }
  17. internal static JintExpression Build(Engine engine, BinaryExpression expression)
  18. {
  19. JintBinaryExpression result;
  20. switch (expression.Operator)
  21. {
  22. case BinaryOperator.StrictlyEqual:
  23. result = new StrictlyEqualBinaryExpression(engine, expression);
  24. break;
  25. case BinaryOperator.StricltyNotEqual:
  26. result = new StrictlyNotEqualBinaryExpression(engine, expression);
  27. break;
  28. case BinaryOperator.Less:
  29. result = new LessBinaryExpression(engine, expression);
  30. break;
  31. case BinaryOperator.Greater:
  32. result = new GreaterBinaryExpression(engine, expression);
  33. break;
  34. case BinaryOperator.Plus:
  35. result = new PlusBinaryExpression(engine, expression);
  36. break;
  37. case BinaryOperator.Minus:
  38. result = new MinusBinaryExpression(engine, expression);
  39. break;
  40. case BinaryOperator.Times:
  41. result = new TimesBinaryExpression(engine, expression);
  42. break;
  43. case BinaryOperator.Divide:
  44. result = new DivideBinaryExpression(engine, expression);
  45. break;
  46. case BinaryOperator.Equal:
  47. result = new EqualBinaryExpression(engine, expression);
  48. break;
  49. case BinaryOperator.NotEqual:
  50. result = new EqualBinaryExpression(engine, expression, invert: true);
  51. break;
  52. case BinaryOperator.GreaterOrEqual:
  53. result = new CompareBinaryExpression(engine, expression, leftFirst: true);
  54. break;
  55. case BinaryOperator.LessOrEqual:
  56. result = new CompareBinaryExpression(engine, expression, leftFirst: false);
  57. break;
  58. case BinaryOperator.BitwiseAnd:
  59. case BinaryOperator.BitwiseOr:
  60. case BinaryOperator.BitwiseXOr:
  61. case BinaryOperator.LeftShift:
  62. case BinaryOperator.RightShift:
  63. case BinaryOperator.UnsignedRightShift:
  64. result = new BitwiseBinaryExpression(engine, expression);
  65. break;
  66. case BinaryOperator.InstanceOf:
  67. result = new InstanceOfBinaryExpression(engine, expression);
  68. break;
  69. case BinaryOperator.Exponentiation:
  70. result = new ExponentiationBinaryExpression(engine, expression);
  71. break;
  72. case BinaryOperator.Modulo:
  73. result = new ModuloBinaryExpression(engine, expression);
  74. break;
  75. case BinaryOperator.In:
  76. result = new InBinaryExpression(engine, expression);
  77. break;
  78. default:
  79. result = ExceptionHelper.ThrowArgumentOutOfRangeException<JintBinaryExpression>(nameof(expression.Operator), "cannot handle operator");
  80. break;
  81. }
  82. if (expression.Operator != BinaryOperator.InstanceOf
  83. && expression.Operator != BinaryOperator.In
  84. && expression.Left is Literal leftLiteral
  85. && expression.Right is Literal rightLiteral)
  86. {
  87. var lval = JintLiteralExpression.ConvertToJsValue(leftLiteral);
  88. var rval = JintLiteralExpression.ConvertToJsValue(rightLiteral);
  89. if (lval is not null && rval is not null)
  90. {
  91. // we have fixed result
  92. return new JintConstantExpression(engine, expression, result.GetValue());
  93. }
  94. }
  95. return result;
  96. }
  97. public override JsValue GetValue()
  98. {
  99. // need to notify correct node when taking shortcut
  100. _engine._lastSyntaxNode = _expression;
  101. // we always create a JsValue
  102. return (JsValue) EvaluateInternal();
  103. }
  104. public static bool StrictlyEqual(JsValue x, JsValue y)
  105. {
  106. var typeX = x._type & ~InternalTypes.InternalFlags;
  107. var typeY = y._type & ~InternalTypes.InternalFlags;
  108. if (typeX != typeY)
  109. {
  110. if (typeX == InternalTypes.Integer)
  111. {
  112. typeX = InternalTypes.Number;
  113. }
  114. if (typeY == InternalTypes.Integer)
  115. {
  116. typeY = InternalTypes.Number;
  117. }
  118. if (typeX != typeY)
  119. {
  120. return false;
  121. }
  122. }
  123. if (typeX == InternalTypes.Undefined || typeX == InternalTypes.Null)
  124. {
  125. return true;
  126. }
  127. if (typeX == InternalTypes.Integer)
  128. {
  129. return x.AsInteger() == y.AsInteger();
  130. }
  131. if (typeX == InternalTypes.Number)
  132. {
  133. var nx = ((JsNumber) x)._value;
  134. var ny = ((JsNumber) y)._value;
  135. return !double.IsNaN(nx) && !double.IsNaN(ny) && nx == ny;
  136. }
  137. if ((typeX & InternalTypes.String) != 0)
  138. {
  139. return x.ToString() == y.ToString();
  140. }
  141. if (typeX == InternalTypes.Boolean)
  142. {
  143. return ((JsBoolean) x)._value == ((JsBoolean) y)._value;
  144. }
  145. if ((typeX & InternalTypes.Object) != 0 && x.AsObject() is IObjectWrapper xw)
  146. {
  147. var yw = y.AsObject() as IObjectWrapper;
  148. if (yw == null)
  149. return false;
  150. return Equals(xw.Target, yw.Target);
  151. }
  152. return x == y;
  153. }
  154. private sealed class StrictlyEqualBinaryExpression : JintBinaryExpression
  155. {
  156. public StrictlyEqualBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  157. {
  158. }
  159. protected override object EvaluateInternal()
  160. {
  161. var left = _left.GetValue();
  162. var right = _right.GetValue();
  163. var equal = StrictlyEqual(left, right);
  164. return equal ? JsBoolean.True : JsBoolean.False;
  165. }
  166. }
  167. private sealed class StrictlyNotEqualBinaryExpression : JintBinaryExpression
  168. {
  169. public StrictlyNotEqualBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  170. {
  171. }
  172. protected override object EvaluateInternal()
  173. {
  174. var left = _left.GetValue();
  175. var right = _right.GetValue();
  176. return StrictlyEqual(left, right)
  177. ? JsBoolean.False
  178. : JsBoolean.True;
  179. }
  180. }
  181. private sealed class LessBinaryExpression : JintBinaryExpression
  182. {
  183. public LessBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  184. {
  185. }
  186. protected override object EvaluateInternal()
  187. {
  188. var left = _left.GetValue();
  189. var right = _right.GetValue();
  190. var value = Compare(left, right);
  191. return value._type == InternalTypes.Undefined
  192. ? JsBoolean.False
  193. : value;
  194. }
  195. }
  196. private sealed class GreaterBinaryExpression : JintBinaryExpression
  197. {
  198. public GreaterBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  199. {
  200. }
  201. protected override object EvaluateInternal()
  202. {
  203. var left = _left.GetValue();
  204. var right = _right.GetValue();
  205. var value = Compare(right, left, false);
  206. return value._type == InternalTypes.Undefined
  207. ? JsBoolean.False
  208. : value;
  209. }
  210. }
  211. private sealed class PlusBinaryExpression : JintBinaryExpression
  212. {
  213. public PlusBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  214. {
  215. }
  216. protected override object EvaluateInternal()
  217. {
  218. var left = _left.GetValue();
  219. var right = _right.GetValue();
  220. if (AreIntegerOperands(left, right))
  221. {
  222. return JsNumber.Create(left.AsInteger() + right.AsInteger());
  223. }
  224. var lprim = TypeConverter.ToPrimitive(left);
  225. var rprim = TypeConverter.ToPrimitive(right);
  226. return lprim.IsString() || rprim.IsString()
  227. ? JsString.Create(TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim))
  228. : JsNumber.Create(TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim));
  229. }
  230. }
  231. private sealed class MinusBinaryExpression : JintBinaryExpression
  232. {
  233. public MinusBinaryExpression(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. return AreIntegerOperands(left, right)
  241. ? JsNumber.Create(left.AsInteger() - right.AsInteger())
  242. : JsNumber.Create(TypeConverter.ToNumber(left) - TypeConverter.ToNumber(right));
  243. }
  244. }
  245. private sealed class TimesBinaryExpression : JintBinaryExpression
  246. {
  247. public TimesBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  248. {
  249. }
  250. protected override object EvaluateInternal()
  251. {
  252. var left = _left.GetValue();
  253. var right = _right.GetValue();
  254. if (AreIntegerOperands(left, right))
  255. {
  256. return JsNumber.Create((long) left.AsInteger() * right.AsInteger());
  257. }
  258. if (left.IsUndefined() || right.IsUndefined())
  259. {
  260. return Undefined.Instance;
  261. }
  262. return JsNumber.Create(TypeConverter.ToNumber(left) * TypeConverter.ToNumber(right));
  263. }
  264. }
  265. private sealed class DivideBinaryExpression : JintBinaryExpression
  266. {
  267. public DivideBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  268. {
  269. }
  270. protected override object EvaluateInternal()
  271. {
  272. var left = _left.GetValue();
  273. var right = _right.GetValue();
  274. return Divide(left, right);
  275. }
  276. }
  277. private sealed class EqualBinaryExpression : JintBinaryExpression
  278. {
  279. private readonly bool _invert;
  280. public EqualBinaryExpression(Engine engine, BinaryExpression expression, bool invert = false) : base(engine, expression)
  281. {
  282. _invert = invert;
  283. }
  284. protected override object EvaluateInternal()
  285. {
  286. var left = _left.GetValue();
  287. var right = _right.GetValue();
  288. return Equal(left, right) == !_invert
  289. ? JsBoolean.True
  290. : JsBoolean.False;
  291. }
  292. }
  293. private sealed class CompareBinaryExpression : JintBinaryExpression
  294. {
  295. private readonly bool _leftFirst;
  296. public CompareBinaryExpression(Engine engine, BinaryExpression expression, bool leftFirst) : base(engine, expression)
  297. {
  298. _leftFirst = leftFirst;
  299. }
  300. protected override object EvaluateInternal()
  301. {
  302. var leftValue = _left.GetValue();
  303. var rightValue = _right.GetValue();
  304. var left = _leftFirst ? leftValue : rightValue;
  305. var right = _leftFirst ? rightValue : leftValue;
  306. var value = Compare(left, right, _leftFirst);
  307. return value.IsUndefined() || ((JsBoolean) value)._value
  308. ? JsBoolean.False
  309. : JsBoolean.True;
  310. }
  311. }
  312. private sealed class InstanceOfBinaryExpression : JintBinaryExpression
  313. {
  314. public InstanceOfBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  315. {
  316. }
  317. protected override object EvaluateInternal()
  318. {
  319. var value = _left.GetValue();
  320. return value.InstanceofOperator(_right.GetValue())
  321. ? JsBoolean.True
  322. : JsBoolean.False;
  323. }
  324. }
  325. private sealed class ExponentiationBinaryExpression : JintBinaryExpression
  326. {
  327. public ExponentiationBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  328. {
  329. }
  330. protected override object EvaluateInternal()
  331. {
  332. var left = _left.GetValue();
  333. var right = _right.GetValue();
  334. return JsNumber.Create(Math.Pow(TypeConverter.ToNumber(left), TypeConverter.ToNumber(right)));
  335. }
  336. }
  337. private sealed class InBinaryExpression : JintBinaryExpression
  338. {
  339. public InBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  340. {
  341. }
  342. protected override object EvaluateInternal()
  343. {
  344. var left = _left.GetValue();
  345. var right = _right.GetValue();
  346. if (!(right is ObjectInstance oi))
  347. {
  348. return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "in can only be used with an object");
  349. }
  350. return oi.HasProperty(left) ? JsBoolean.True : JsBoolean.False;
  351. }
  352. }
  353. private sealed class ModuloBinaryExpression : JintBinaryExpression
  354. {
  355. public ModuloBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  356. {
  357. }
  358. protected override object EvaluateInternal()
  359. {
  360. var left = _left.GetValue();
  361. var right = _right.GetValue();
  362. if (AreIntegerOperands(left, right))
  363. {
  364. var leftInteger = left.AsInteger();
  365. var rightInteger = right.AsInteger();
  366. if (leftInteger > 0 && rightInteger != 0)
  367. {
  368. return JsNumber.Create(leftInteger % rightInteger);
  369. }
  370. }
  371. if (left.IsUndefined() || right.IsUndefined())
  372. {
  373. return Undefined.Instance;
  374. }
  375. return JsNumber.Create(TypeConverter.ToNumber(left) % TypeConverter.ToNumber(right)); }
  376. }
  377. private sealed class BitwiseBinaryExpression : JintBinaryExpression
  378. {
  379. private readonly BinaryOperator _operator;
  380. public BitwiseBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  381. {
  382. _operator = expression.Operator;
  383. }
  384. protected override object EvaluateInternal()
  385. {
  386. var left = _left.GetValue();
  387. var right = _right.GetValue();
  388. if (AreIntegerOperands(left, right))
  389. {
  390. int leftValue = left.AsInteger();
  391. int rightValue = right.AsInteger();
  392. switch (_operator)
  393. {
  394. case BinaryOperator.BitwiseAnd:
  395. return JsNumber.Create(leftValue & rightValue);
  396. case BinaryOperator.BitwiseOr:
  397. return
  398. JsNumber.Create(leftValue | rightValue);
  399. case BinaryOperator.BitwiseXOr:
  400. return
  401. JsNumber.Create(leftValue ^ rightValue);
  402. case BinaryOperator.LeftShift:
  403. return JsNumber.Create(leftValue << (int) ((uint) rightValue & 0x1F));
  404. case BinaryOperator.RightShift:
  405. return JsNumber.Create(leftValue >> (int) ((uint) rightValue & 0x1F));
  406. case BinaryOperator.UnsignedRightShift:
  407. return JsNumber.Create((uint) leftValue >> (int) ((uint) rightValue & 0x1F));
  408. default:
  409. return ExceptionHelper.ThrowArgumentOutOfRangeException<object>(nameof(_operator),
  410. "unknown shift operator");
  411. }
  412. }
  413. return EvaluateNonInteger(left, right);
  414. }
  415. private object EvaluateNonInteger(JsValue left, JsValue right)
  416. {
  417. switch (_operator)
  418. {
  419. case BinaryOperator.BitwiseAnd:
  420. return JsNumber.Create(TypeConverter.ToInt32(left) & TypeConverter.ToInt32(right));
  421. case BinaryOperator.BitwiseOr:
  422. return
  423. JsNumber.Create(TypeConverter.ToInt32(left) | TypeConverter.ToInt32(right));
  424. case BinaryOperator.BitwiseXOr:
  425. return
  426. JsNumber.Create(TypeConverter.ToInt32(left) ^ TypeConverter.ToInt32(right));
  427. case BinaryOperator.LeftShift:
  428. return JsNumber.Create(TypeConverter.ToInt32(left) <<
  429. (int) (TypeConverter.ToUint32(right) & 0x1F));
  430. case BinaryOperator.RightShift:
  431. return JsNumber.Create(TypeConverter.ToInt32(left) >>
  432. (int) (TypeConverter.ToUint32(right) & 0x1F));
  433. case BinaryOperator.UnsignedRightShift:
  434. return JsNumber.Create((uint) TypeConverter.ToInt32(left) >>
  435. (int) (TypeConverter.ToUint32(right) & 0x1F));
  436. default:
  437. return ExceptionHelper.ThrowArgumentOutOfRangeException<object>(nameof(_operator),
  438. "unknown shift operator");
  439. }
  440. }
  441. }
  442. }
  443. }