JintBinaryExpression.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Linq;
  4. using Esprima.Ast;
  5. using Jint.Extensions;
  6. using Jint.Native;
  7. using Jint.Native.Object;
  8. using Jint.Runtime.Interop;
  9. namespace Jint.Runtime.Interpreter.Expressions
  10. {
  11. internal abstract class JintBinaryExpression : JintExpression
  12. {
  13. #if NETSTANDARD
  14. private static readonly ConcurrentDictionary<(string OperatorName, System.Type Left, System.Type Right), MethodDescriptor> _knownOperators =
  15. new ConcurrentDictionary<(string OperatorName, System.Type Left, System.Type Right), MethodDescriptor>();
  16. #else
  17. private static readonly ConcurrentDictionary<string, MethodDescriptor> _knownOperators = new ConcurrentDictionary<string, MethodDescriptor>();
  18. #endif
  19. private readonly JintExpression _left;
  20. private readonly JintExpression _right;
  21. private JintBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  22. {
  23. _left = Build(_engine, expression.Left);
  24. _right = Build(_engine, expression.Right);
  25. }
  26. protected bool TryOperatorOverloading(string clrName, out object result)
  27. {
  28. var leftValue = _left.GetValue();
  29. var rightValue = _right.GetValue();
  30. var left = leftValue.ToObject();
  31. var right = rightValue.ToObject();
  32. if (left != null && right != null)
  33. {
  34. var leftType = left.GetType();
  35. var rightType = right.GetType();
  36. var arguments = new[] { leftValue, rightValue };
  37. #if NETSTANDARD
  38. var key = (clrName, leftType, rightType);
  39. #else
  40. var key = $"{clrName}->{leftType}->{rightType}";
  41. #endif
  42. var method = _knownOperators.GetOrAdd(key, _ =>
  43. {
  44. var leftMethods = leftType.GetOperatorOverloadMethods();
  45. var rightMethods = rightType.GetOperatorOverloadMethods();
  46. var methods = leftMethods.Concat(rightMethods).Where(x => x.Name == clrName && x.GetParameters().Length == 2);
  47. var _methods = MethodDescriptor.Build(methods.ToArray());
  48. return TypeConverter.FindBestMatch(_engine, _methods, _ => arguments).FirstOrDefault()?.Item1;
  49. });
  50. if (method != null)
  51. {
  52. result = method.Call(_engine, null, arguments);
  53. return true;
  54. }
  55. }
  56. result = null;
  57. return false;
  58. }
  59. internal static JintExpression Build(Engine engine, BinaryExpression expression)
  60. {
  61. JintBinaryExpression result;
  62. switch (expression.Operator)
  63. {
  64. case BinaryOperator.StrictlyEqual:
  65. result = new StrictlyEqualBinaryExpression(engine, expression);
  66. break;
  67. case BinaryOperator.StricltyNotEqual:
  68. result = new StrictlyNotEqualBinaryExpression(engine, expression);
  69. break;
  70. case BinaryOperator.Less:
  71. result = new LessBinaryExpression(engine, expression);
  72. break;
  73. case BinaryOperator.Greater:
  74. result = new GreaterBinaryExpression(engine, expression);
  75. break;
  76. case BinaryOperator.Plus:
  77. result = new PlusBinaryExpression(engine, expression);
  78. break;
  79. case BinaryOperator.Minus:
  80. result = new MinusBinaryExpression(engine, expression);
  81. break;
  82. case BinaryOperator.Times:
  83. result = new TimesBinaryExpression(engine, expression);
  84. break;
  85. case BinaryOperator.Divide:
  86. result = new DivideBinaryExpression(engine, expression);
  87. break;
  88. case BinaryOperator.Equal:
  89. result = new EqualBinaryExpression(engine, expression);
  90. break;
  91. case BinaryOperator.NotEqual:
  92. result = new EqualBinaryExpression(engine, expression, invert: true);
  93. break;
  94. case BinaryOperator.GreaterOrEqual:
  95. result = new CompareBinaryExpression(engine, expression, leftFirst: true);
  96. break;
  97. case BinaryOperator.LessOrEqual:
  98. result = new CompareBinaryExpression(engine, expression, leftFirst: false);
  99. break;
  100. case BinaryOperator.BitwiseAnd:
  101. case BinaryOperator.BitwiseOr:
  102. case BinaryOperator.BitwiseXOr:
  103. case BinaryOperator.LeftShift:
  104. case BinaryOperator.RightShift:
  105. case BinaryOperator.UnsignedRightShift:
  106. result = new BitwiseBinaryExpression(engine, expression);
  107. break;
  108. case BinaryOperator.InstanceOf:
  109. result = new InstanceOfBinaryExpression(engine, expression);
  110. break;
  111. case BinaryOperator.Exponentiation:
  112. result = new ExponentiationBinaryExpression(engine, expression);
  113. break;
  114. case BinaryOperator.Modulo:
  115. result = new ModuloBinaryExpression(engine, expression);
  116. break;
  117. case BinaryOperator.In:
  118. result = new InBinaryExpression(engine, expression);
  119. break;
  120. default:
  121. result = ExceptionHelper.ThrowArgumentOutOfRangeException<JintBinaryExpression>(nameof(expression.Operator), "cannot handle operator");
  122. break;
  123. }
  124. if (expression.Operator != BinaryOperator.InstanceOf
  125. && expression.Operator != BinaryOperator.In
  126. && expression.Left is Literal leftLiteral
  127. && expression.Right is Literal rightLiteral)
  128. {
  129. var lval = JintLiteralExpression.ConvertToJsValue(leftLiteral);
  130. var rval = JintLiteralExpression.ConvertToJsValue(rightLiteral);
  131. if (lval is not null && rval is not null)
  132. {
  133. // we have fixed result
  134. return new JintConstantExpression(engine, expression, result.GetValue());
  135. }
  136. }
  137. return result;
  138. }
  139. public override JsValue GetValue()
  140. {
  141. // need to notify correct node when taking shortcut
  142. _engine._lastSyntaxNode = _expression;
  143. // we always create a JsValue
  144. return (JsValue) EvaluateInternal();
  145. }
  146. public static bool StrictlyEqual(JsValue x, JsValue y)
  147. {
  148. var typeX = x._type & ~InternalTypes.InternalFlags;
  149. var typeY = y._type & ~InternalTypes.InternalFlags;
  150. if (typeX != typeY)
  151. {
  152. if (typeX == InternalTypes.Integer)
  153. {
  154. typeX = InternalTypes.Number;
  155. }
  156. if (typeY == InternalTypes.Integer)
  157. {
  158. typeY = InternalTypes.Number;
  159. }
  160. if (typeX != typeY)
  161. {
  162. return false;
  163. }
  164. }
  165. if (typeX == InternalTypes.Undefined || typeX == InternalTypes.Null)
  166. {
  167. return true;
  168. }
  169. if (typeX == InternalTypes.Integer)
  170. {
  171. return x.AsInteger() == y.AsInteger();
  172. }
  173. if (typeX == InternalTypes.Number)
  174. {
  175. var nx = ((JsNumber) x)._value;
  176. var ny = ((JsNumber) y)._value;
  177. return !double.IsNaN(nx) && !double.IsNaN(ny) && nx == ny;
  178. }
  179. if ((typeX & InternalTypes.String) != 0)
  180. {
  181. return x.ToString() == y.ToString();
  182. }
  183. if (typeX == InternalTypes.Boolean)
  184. {
  185. return ((JsBoolean) x)._value == ((JsBoolean) y)._value;
  186. }
  187. if ((typeX & InternalTypes.Object) != 0 && x.AsObject() is IObjectWrapper xw)
  188. {
  189. var yw = y.AsObject() as IObjectWrapper;
  190. if (yw == null)
  191. return false;
  192. return Equals(xw.Target, yw.Target);
  193. }
  194. return x == y;
  195. }
  196. private sealed class StrictlyEqualBinaryExpression : JintBinaryExpression
  197. {
  198. public StrictlyEqualBinaryExpression(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 equal = StrictlyEqual(left, right);
  206. return equal ? JsBoolean.True : JsBoolean.False;
  207. }
  208. }
  209. private sealed class StrictlyNotEqualBinaryExpression : JintBinaryExpression
  210. {
  211. public StrictlyNotEqualBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  212. {
  213. }
  214. protected override object EvaluateInternal()
  215. {
  216. var left = _left.GetValue();
  217. var right = _right.GetValue();
  218. return StrictlyEqual(left, right)
  219. ? JsBoolean.False
  220. : JsBoolean.True;
  221. }
  222. }
  223. private sealed class LessBinaryExpression : JintBinaryExpression
  224. {
  225. public LessBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  226. {
  227. }
  228. protected override object EvaluateInternal()
  229. {
  230. if (_engine.Options._IsOperatorOverloadingAllowed
  231. && TryOperatorOverloading("op_LessThan", out var opResult))
  232. {
  233. return opResult;
  234. }
  235. var left = _left.GetValue();
  236. var right = _right.GetValue();
  237. var value = Compare(left, right);
  238. return value._type == InternalTypes.Undefined
  239. ? JsBoolean.False
  240. : value;
  241. }
  242. }
  243. private sealed class GreaterBinaryExpression : JintBinaryExpression
  244. {
  245. public GreaterBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  246. {
  247. }
  248. protected override object EvaluateInternal()
  249. {
  250. if (_engine.Options._IsOperatorOverloadingAllowed
  251. && TryOperatorOverloading("op_GreaterThan", out var opResult))
  252. {
  253. return opResult;
  254. }
  255. var left = _left.GetValue();
  256. var right = _right.GetValue();
  257. var value = Compare(right, left, false);
  258. return value._type == InternalTypes.Undefined
  259. ? JsBoolean.False
  260. : value;
  261. }
  262. }
  263. private sealed class PlusBinaryExpression : JintBinaryExpression
  264. {
  265. public PlusBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  266. {
  267. }
  268. protected override object EvaluateInternal()
  269. {
  270. if (_engine.Options._IsOperatorOverloadingAllowed
  271. && TryOperatorOverloading("op_Addition", out var opResult))
  272. {
  273. return opResult;
  274. }
  275. var left = _left.GetValue();
  276. var right = _right.GetValue();
  277. if (AreIntegerOperands(left, right))
  278. {
  279. return JsNumber.Create(left.AsInteger() + right.AsInteger());
  280. }
  281. var lprim = TypeConverter.ToPrimitive(left);
  282. var rprim = TypeConverter.ToPrimitive(right);
  283. return lprim.IsString() || rprim.IsString()
  284. ? JsString.Create(TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim))
  285. : JsNumber.Create(TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim));
  286. }
  287. }
  288. private sealed class MinusBinaryExpression : JintBinaryExpression
  289. {
  290. public MinusBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  291. {
  292. }
  293. protected override object EvaluateInternal()
  294. {
  295. if (_engine.Options._IsOperatorOverloadingAllowed
  296. && TryOperatorOverloading("op_Subtraction", out var opResult))
  297. {
  298. return opResult;
  299. }
  300. var left = _left.GetValue();
  301. var right = _right.GetValue();
  302. return AreIntegerOperands(left, right)
  303. ? JsNumber.Create(left.AsInteger() - right.AsInteger())
  304. : JsNumber.Create(TypeConverter.ToNumber(left) - TypeConverter.ToNumber(right));
  305. }
  306. }
  307. private sealed class TimesBinaryExpression : JintBinaryExpression
  308. {
  309. public TimesBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  310. {
  311. }
  312. protected override object EvaluateInternal()
  313. {
  314. if (_engine.Options._IsOperatorOverloadingAllowed
  315. && TryOperatorOverloading("op_Multiply", out var opResult))
  316. {
  317. return opResult;
  318. }
  319. var left = _left.GetValue();
  320. var right = _right.GetValue();
  321. if (AreIntegerOperands(left, right))
  322. {
  323. return JsNumber.Create((long) left.AsInteger() * right.AsInteger());
  324. }
  325. if (left.IsUndefined() || right.IsUndefined())
  326. {
  327. return Undefined.Instance;
  328. }
  329. return JsNumber.Create(TypeConverter.ToNumber(left) * TypeConverter.ToNumber(right));
  330. }
  331. }
  332. private sealed class DivideBinaryExpression : JintBinaryExpression
  333. {
  334. public DivideBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  335. {
  336. }
  337. protected override object EvaluateInternal()
  338. {
  339. if (_engine.Options._IsOperatorOverloadingAllowed
  340. && TryOperatorOverloading("op_Division", out var opResult))
  341. {
  342. return opResult;
  343. }
  344. var left = _left.GetValue();
  345. var right = _right.GetValue();
  346. return Divide(left, right);
  347. }
  348. }
  349. private sealed class EqualBinaryExpression : JintBinaryExpression
  350. {
  351. private readonly bool _invert;
  352. public EqualBinaryExpression(Engine engine, BinaryExpression expression, bool invert = false) : base(engine, expression)
  353. {
  354. _invert = invert;
  355. }
  356. protected override object EvaluateInternal()
  357. {
  358. if (_engine.Options._IsOperatorOverloadingAllowed
  359. && TryOperatorOverloading(_invert ? "op_Inequality" : "op_Equality", out var opResult))
  360. {
  361. return opResult;
  362. }
  363. var left = _left.GetValue();
  364. var right = _right.GetValue();
  365. return Equal(left, right) == !_invert
  366. ? JsBoolean.True
  367. : JsBoolean.False;
  368. }
  369. }
  370. private sealed class CompareBinaryExpression : JintBinaryExpression
  371. {
  372. private readonly bool _leftFirst;
  373. public CompareBinaryExpression(Engine engine, BinaryExpression expression, bool leftFirst) : base(engine, expression)
  374. {
  375. _leftFirst = leftFirst;
  376. }
  377. protected override object EvaluateInternal()
  378. {
  379. if (_engine.Options._IsOperatorOverloadingAllowed
  380. && TryOperatorOverloading(_leftFirst ? "op_GreaterThanOrEqual" : "op_LessThanOrEqual", out var opResult))
  381. {
  382. return opResult;
  383. }
  384. var leftValue = _left.GetValue();
  385. var rightValue = _right.GetValue();
  386. var left = _leftFirst ? leftValue : rightValue;
  387. var right = _leftFirst ? rightValue : leftValue;
  388. var value = Compare(left, right, _leftFirst);
  389. return value.IsUndefined() || ((JsBoolean) value)._value
  390. ? JsBoolean.False
  391. : JsBoolean.True;
  392. }
  393. }
  394. private sealed class InstanceOfBinaryExpression : JintBinaryExpression
  395. {
  396. public InstanceOfBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  397. {
  398. }
  399. protected override object EvaluateInternal()
  400. {
  401. var value = _left.GetValue();
  402. return value.InstanceofOperator(_right.GetValue())
  403. ? JsBoolean.True
  404. : JsBoolean.False;
  405. }
  406. }
  407. private sealed class ExponentiationBinaryExpression : JintBinaryExpression
  408. {
  409. public ExponentiationBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  410. {
  411. }
  412. protected override object EvaluateInternal()
  413. {
  414. var left = _left.GetValue();
  415. var right = _right.GetValue();
  416. return JsNumber.Create(Math.Pow(TypeConverter.ToNumber(left), TypeConverter.ToNumber(right)));
  417. }
  418. }
  419. private sealed class InBinaryExpression : JintBinaryExpression
  420. {
  421. public InBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  422. {
  423. }
  424. protected override object EvaluateInternal()
  425. {
  426. var left = _left.GetValue();
  427. var right = _right.GetValue();
  428. if (!(right is ObjectInstance oi))
  429. {
  430. return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "in can only be used with an object");
  431. }
  432. return oi.HasProperty(left) ? JsBoolean.True : JsBoolean.False;
  433. }
  434. }
  435. private sealed class ModuloBinaryExpression : JintBinaryExpression
  436. {
  437. public ModuloBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  438. {
  439. }
  440. protected override object EvaluateInternal()
  441. {
  442. if (_engine.Options._IsOperatorOverloadingAllowed
  443. && TryOperatorOverloading("op_Modulus", out var opResult))
  444. {
  445. return opResult;
  446. }
  447. var left = _left.GetValue();
  448. var right = _right.GetValue();
  449. if (AreIntegerOperands(left, right))
  450. {
  451. var leftInteger = left.AsInteger();
  452. var rightInteger = right.AsInteger();
  453. if (leftInteger > 0 && rightInteger != 0)
  454. {
  455. return JsNumber.Create(leftInteger % rightInteger);
  456. }
  457. }
  458. if (left.IsUndefined() || right.IsUndefined())
  459. {
  460. return Undefined.Instance;
  461. }
  462. return JsNumber.Create(TypeConverter.ToNumber(left) % TypeConverter.ToNumber(right));
  463. }
  464. }
  465. private sealed class BitwiseBinaryExpression : JintBinaryExpression
  466. {
  467. private string OperatorClrName
  468. {
  469. get
  470. {
  471. switch (_operator)
  472. {
  473. case BinaryOperator.BitwiseAnd:
  474. return "op_BitwiseAnd";
  475. case BinaryOperator.BitwiseOr:
  476. return "op_BitwiseOr";
  477. case BinaryOperator.BitwiseXOr:
  478. return "op_ExclusiveOr";
  479. case BinaryOperator.LeftShift:
  480. return "op_LeftShift";
  481. case BinaryOperator.RightShift:
  482. return "op_RightShift";
  483. case BinaryOperator.UnsignedRightShift:
  484. return "op_UnsignedRightShift";
  485. default:
  486. return null;
  487. }
  488. }
  489. }
  490. private readonly BinaryOperator _operator;
  491. public BitwiseBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
  492. {
  493. _operator = expression.Operator;
  494. }
  495. protected override object EvaluateInternal()
  496. {
  497. if (_engine.Options._IsOperatorOverloadingAllowed
  498. && TryOperatorOverloading(OperatorClrName, out var opResult))
  499. {
  500. return opResult;
  501. }
  502. var left = _left.GetValue();
  503. var right = _right.GetValue();
  504. if (AreIntegerOperands(left, right))
  505. {
  506. int leftValue = left.AsInteger();
  507. int rightValue = right.AsInteger();
  508. switch (_operator)
  509. {
  510. case BinaryOperator.BitwiseAnd:
  511. return JsNumber.Create(leftValue & rightValue);
  512. case BinaryOperator.BitwiseOr:
  513. return
  514. JsNumber.Create(leftValue | rightValue);
  515. case BinaryOperator.BitwiseXOr:
  516. return
  517. JsNumber.Create(leftValue ^ rightValue);
  518. case BinaryOperator.LeftShift:
  519. return JsNumber.Create(leftValue << (int) ((uint) rightValue & 0x1F));
  520. case BinaryOperator.RightShift:
  521. return JsNumber.Create(leftValue >> (int) ((uint) rightValue & 0x1F));
  522. case BinaryOperator.UnsignedRightShift:
  523. return JsNumber.Create((uint) leftValue >> (int) ((uint) rightValue & 0x1F));
  524. default:
  525. return ExceptionHelper.ThrowArgumentOutOfRangeException<object>(nameof(_operator),
  526. "unknown shift operator");
  527. }
  528. }
  529. return EvaluateNonInteger(left, right);
  530. }
  531. private object EvaluateNonInteger(JsValue left, JsValue right)
  532. {
  533. switch (_operator)
  534. {
  535. case BinaryOperator.BitwiseAnd:
  536. return JsNumber.Create(TypeConverter.ToInt32(left) & TypeConverter.ToInt32(right));
  537. case BinaryOperator.BitwiseOr:
  538. return
  539. JsNumber.Create(TypeConverter.ToInt32(left) | TypeConverter.ToInt32(right));
  540. case BinaryOperator.BitwiseXOr:
  541. return
  542. JsNumber.Create(TypeConverter.ToInt32(left) ^ TypeConverter.ToInt32(right));
  543. case BinaryOperator.LeftShift:
  544. return JsNumber.Create(TypeConverter.ToInt32(left) <<
  545. (int) (TypeConverter.ToUint32(right) & 0x1F));
  546. case BinaryOperator.RightShift:
  547. return JsNumber.Create(TypeConverter.ToInt32(left) >>
  548. (int) (TypeConverter.ToUint32(right) & 0x1F));
  549. case BinaryOperator.UnsignedRightShift:
  550. return JsNumber.Create((uint) TypeConverter.ToInt32(left) >>
  551. (int) (TypeConverter.ToUint32(right) & 0x1F));
  552. default:
  553. return ExceptionHelper.ThrowArgumentOutOfRangeException<object>(nameof(_operator),
  554. "unknown shift operator");
  555. }
  556. }
  557. }
  558. }
  559. }