JintBinaryExpression.cs 20 KB

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