JintBinaryExpression.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  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 & ~InternalTypes.InternalFlags;
  110. var typeY = y._type & ~InternalTypes.InternalFlags;
  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. if (typeX == InternalTypes.Undefined || typeX == InternalTypes.Null)
  127. {
  128. return true;
  129. }
  130. if (typeX == InternalTypes.Integer)
  131. {
  132. return x.AsInteger() == y.AsInteger();
  133. }
  134. if (typeX == InternalTypes.Number)
  135. {
  136. var nx = ((JsNumber) x)._value;
  137. var ny = ((JsNumber) y)._value;
  138. return !double.IsNaN(nx) && !double.IsNaN(ny) && nx == ny;
  139. }
  140. if ((typeX & InternalTypes.String) != 0)
  141. {
  142. return x.AsStringWithoutTypeCheck() == y.AsStringWithoutTypeCheck();
  143. }
  144. if (typeX == InternalTypes.Boolean)
  145. {
  146. return ((JsBoolean) x)._value == ((JsBoolean) y)._value;
  147. }
  148. if ((typeX & InternalTypes.Object) != 0 && x.AsObject() is IObjectWrapper xw)
  149. {
  150. var yw = y.AsObject() as IObjectWrapper;
  151. if (yw == null)
  152. return false;
  153. return Equals(xw.Target, yw.Target);
  154. }
  155. return x == y;
  156. }
  157. private sealed class StrictlyEqualBinaryExpression : JintBinaryExpression
  158. {
  159. public StrictlyEqualBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  160. {
  161. }
  162. protected override object EvaluateInternal()
  163. {
  164. var left = _left.GetValue();
  165. var right = _right.GetValue();
  166. var equal = StrictlyEqual(left, right);
  167. return equal ? JsBoolean.True : JsBoolean.False;
  168. }
  169. }
  170. private sealed class StrictlyNotEqualBinaryExpression : JintBinaryExpression
  171. {
  172. public StrictlyNotEqualBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  173. {
  174. }
  175. protected override object EvaluateInternal()
  176. {
  177. var left = _left.GetValue();
  178. var right = _right.GetValue();
  179. return StrictlyEqual(left, right)
  180. ? JsBoolean.False
  181. : JsBoolean.True;
  182. }
  183. }
  184. private sealed class LessBinaryExpression : JintBinaryExpression
  185. {
  186. public LessBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  187. {
  188. }
  189. protected override object EvaluateInternal()
  190. {
  191. var left = _left.GetValue();
  192. var right = _right.GetValue();
  193. var value = Compare(left, right);
  194. return value._type == InternalTypes.Undefined
  195. ? JsBoolean.False
  196. : value;
  197. }
  198. }
  199. private sealed class GreaterBinaryExpression : JintBinaryExpression
  200. {
  201. public GreaterBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  202. {
  203. }
  204. protected override object EvaluateInternal()
  205. {
  206. var left = _left.GetValue();
  207. var right = _right.GetValue();
  208. var value = Compare(right, left, false);
  209. return value._type == InternalTypes.Undefined
  210. ? JsBoolean.False
  211. : value;
  212. }
  213. }
  214. private sealed class PlusBinaryExpression : JintBinaryExpression
  215. {
  216. public PlusBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  217. {
  218. }
  219. protected override object EvaluateInternal()
  220. {
  221. var left = _left.GetValue();
  222. var right = _right.GetValue();
  223. if (AreIntegerOperands(left, right))
  224. {
  225. return JsNumber.Create(left.AsInteger() + right.AsInteger());
  226. }
  227. var lprim = TypeConverter.ToPrimitive(left);
  228. var rprim = TypeConverter.ToPrimitive(right);
  229. return lprim.IsString() || rprim.IsString()
  230. ? (JsValue) JsString.Create(TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim))
  231. : JsNumber.Create(TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim));
  232. }
  233. }
  234. private sealed class MinusBinaryExpression : JintBinaryExpression
  235. {
  236. public MinusBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  237. {
  238. }
  239. protected override object EvaluateInternal()
  240. {
  241. var left = _left.GetValue();
  242. var right = _right.GetValue();
  243. return AreIntegerOperands(left, right)
  244. ? JsNumber.Create(left.AsInteger() - right.AsInteger())
  245. : JsNumber.Create(TypeConverter.ToNumber(left) - TypeConverter.ToNumber(right));
  246. }
  247. }
  248. private sealed class TimesBinaryExpression : JintBinaryExpression
  249. {
  250. public TimesBinaryExpression(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. if (AreIntegerOperands(left, right))
  258. {
  259. return JsNumber.Create((long) left.AsInteger() * right.AsInteger());
  260. }
  261. if (left.IsUndefined() || right.IsUndefined())
  262. {
  263. return Undefined.Instance;
  264. }
  265. return JsNumber.Create(TypeConverter.ToNumber(left) * TypeConverter.ToNumber(right));
  266. }
  267. }
  268. private sealed class DivideBinaryExpression : JintBinaryExpression
  269. {
  270. public DivideBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  271. {
  272. }
  273. protected override object EvaluateInternal()
  274. {
  275. var left = _left.GetValue();
  276. var right = _right.GetValue();
  277. return Divide(left, right);
  278. }
  279. }
  280. private sealed class EqualBinaryExpression : JintBinaryExpression
  281. {
  282. private readonly bool _invert;
  283. public EqualBinaryExpression(Engine engine, BinaryExpression expression, bool invert = false) : base(engine, expression)
  284. {
  285. _invert = invert;
  286. }
  287. protected override object EvaluateInternal()
  288. {
  289. var left = _left.GetValue();
  290. var right = _right.GetValue();
  291. return Equal(left, right) == !_invert
  292. ? JsBoolean.True
  293. : JsBoolean.False;
  294. }
  295. }
  296. private sealed class CompareBinaryExpression : JintBinaryExpression
  297. {
  298. private readonly bool _leftFirst;
  299. public CompareBinaryExpression(Engine engine, BinaryExpression expression, bool leftFirst) : base(engine, expression)
  300. {
  301. _leftFirst = leftFirst;
  302. }
  303. protected override object EvaluateInternal()
  304. {
  305. var leftValue = _left.GetValue();
  306. var rightValue = _right.GetValue();
  307. var left = _leftFirst ? leftValue : rightValue;
  308. var right = _leftFirst ? rightValue : leftValue;
  309. var value = Compare(left, right, _leftFirst);
  310. return value.IsUndefined() || ((JsBoolean) value)._value
  311. ? JsBoolean.False
  312. : JsBoolean.True;
  313. }
  314. }
  315. private sealed class InstanceOfBinaryExpression : JintBinaryExpression
  316. {
  317. public InstanceOfBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  318. {
  319. }
  320. protected override object EvaluateInternal()
  321. {
  322. var left = _left.GetValue();
  323. var right = _right.GetValue();
  324. if (!(right is FunctionInstance f))
  325. {
  326. return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "instanceof can only be used with a function object");
  327. }
  328. return f.HasInstance(left) ? JsBoolean.True : JsBoolean.False;
  329. }
  330. }
  331. private sealed class ExponentiationBinaryExpression : JintBinaryExpression
  332. {
  333. public ExponentiationBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  334. {
  335. }
  336. protected override object EvaluateInternal()
  337. {
  338. var left = _left.GetValue();
  339. var right = _right.GetValue();
  340. return JsNumber.Create(Math.Pow(TypeConverter.ToNumber(left), TypeConverter.ToNumber(right)));
  341. }
  342. }
  343. private sealed class InBinaryExpression : JintBinaryExpression
  344. {
  345. public InBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  346. {
  347. }
  348. protected override object EvaluateInternal()
  349. {
  350. var left = _left.GetValue();
  351. var right = _right.GetValue();
  352. if (!(right is ObjectInstance oi))
  353. {
  354. return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "in can only be used with an object");
  355. }
  356. return oi.HasProperty(left) ? JsBoolean.True : JsBoolean.False;
  357. }
  358. }
  359. private sealed class ModuloBinaryExpression : JintBinaryExpression
  360. {
  361. public ModuloBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  362. {
  363. }
  364. protected override object EvaluateInternal()
  365. {
  366. var left = _left.GetValue();
  367. var right = _right.GetValue();
  368. if (AreIntegerOperands(left, right))
  369. {
  370. var leftInteger = left.AsInteger();
  371. var rightInteger = right.AsInteger();
  372. if (leftInteger > 0 && rightInteger != 0)
  373. {
  374. return JsNumber.Create(leftInteger % rightInteger);
  375. }
  376. }
  377. if (left.IsUndefined() || right.IsUndefined())
  378. {
  379. return Undefined.Instance;
  380. }
  381. return JsNumber.Create(TypeConverter.ToNumber(left) % TypeConverter.ToNumber(right)); }
  382. }
  383. private sealed class BitwiseBinaryExpression : JintBinaryExpression
  384. {
  385. private readonly BinaryOperator _operator;
  386. public BitwiseBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  387. {
  388. _operator = expression.Operator;
  389. }
  390. protected override object EvaluateInternal()
  391. {
  392. var left = _left.GetValue();
  393. var right = _right.GetValue();
  394. if (AreIntegerOperands(left, right))
  395. {
  396. int leftValue = left.AsInteger();
  397. int rightValue = right.AsInteger();
  398. switch (_operator)
  399. {
  400. case BinaryOperator.BitwiseAnd:
  401. return JsNumber.Create(leftValue & rightValue);
  402. case BinaryOperator.BitwiseOr:
  403. return
  404. JsNumber.Create(leftValue | rightValue);
  405. case BinaryOperator.BitwiseXOr:
  406. return
  407. JsNumber.Create(leftValue ^ rightValue);
  408. case BinaryOperator.LeftShift:
  409. return JsNumber.Create(leftValue << (int) ((uint) rightValue & 0x1F));
  410. case BinaryOperator.RightShift:
  411. return JsNumber.Create(leftValue >> (int) ((uint) rightValue & 0x1F));
  412. case BinaryOperator.UnsignedRightShift:
  413. return JsNumber.Create((uint) leftValue >> (int) ((uint) rightValue & 0x1F));
  414. default:
  415. return ExceptionHelper.ThrowArgumentOutOfRangeException<object>(nameof(_operator),
  416. "unknown shift operator");
  417. }
  418. }
  419. return EvaluateNonInteger(left, right);
  420. }
  421. private object EvaluateNonInteger(JsValue left, JsValue right)
  422. {
  423. switch (_operator)
  424. {
  425. case BinaryOperator.BitwiseAnd:
  426. return JsNumber.Create(TypeConverter.ToInt32(left) & TypeConverter.ToInt32(right));
  427. case BinaryOperator.BitwiseOr:
  428. return
  429. JsNumber.Create(TypeConverter.ToInt32(left) | TypeConverter.ToInt32(right));
  430. case BinaryOperator.BitwiseXOr:
  431. return
  432. JsNumber.Create(TypeConverter.ToInt32(left) ^ TypeConverter.ToInt32(right));
  433. case BinaryOperator.LeftShift:
  434. return JsNumber.Create(TypeConverter.ToInt32(left) <<
  435. (int) (TypeConverter.ToUint32(right) & 0x1F));
  436. case BinaryOperator.RightShift:
  437. return JsNumber.Create(TypeConverter.ToInt32(left) >>
  438. (int) (TypeConverter.ToUint32(right) & 0x1F));
  439. case BinaryOperator.UnsignedRightShift:
  440. return JsNumber.Create((uint) TypeConverter.ToInt32(left) >>
  441. (int) (TypeConverter.ToUint32(right) & 0x1F));
  442. default:
  443. return ExceptionHelper.ThrowArgumentOutOfRangeException<object>(nameof(_operator),
  444. "unknown shift operator");
  445. }
  446. }
  447. }
  448. }
  449. }