JintExpression.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. using System.Numerics;
  2. using System.Runtime.CompilerServices;
  3. using Jint.Native;
  4. using Jint.Native.Number;
  5. namespace Jint.Runtime.Interpreter.Expressions;
  6. internal abstract class JintExpression
  7. {
  8. protected internal readonly Expression _expression;
  9. protected JintExpression(Expression expression)
  10. {
  11. _expression = expression;
  12. }
  13. /// <summary>
  14. /// Resolves the underlying value for this expression.
  15. /// By default uses the Engine for resolving.
  16. /// </summary>
  17. /// <param name="context"></param>
  18. /// <seealso cref="JintLiteralExpression"/>
  19. public virtual JsValue GetValue(EvaluationContext context)
  20. {
  21. var result = Evaluate(context);
  22. if (result is not Reference reference)
  23. {
  24. return (JsValue) result;
  25. }
  26. return context.Engine.GetValue(reference, returnReferenceToPool: true);
  27. }
  28. [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)]
  29. public object Evaluate(EvaluationContext context)
  30. {
  31. var oldSyntaxElement = context.LastSyntaxElement;
  32. context.PrepareFor(_expression);
  33. var result = EvaluateInternal(context);
  34. context.LastSyntaxElement = oldSyntaxElement;
  35. return result;
  36. }
  37. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  38. internal object EvaluateWithoutNodeTracking(EvaluationContext context)
  39. {
  40. return EvaluateInternal(context);
  41. }
  42. protected abstract object EvaluateInternal(EvaluationContext context);
  43. /// <summary>
  44. /// If we'd get Esprima source, we would just refer to it, but this makes error messages easier to decipher.
  45. /// </summary>
  46. internal string SourceText => ToString(_expression) ?? "*unknown*";
  47. internal static string? ToString(Expression expression)
  48. {
  49. while (true)
  50. {
  51. if (expression is Literal literal)
  52. {
  53. return AstExtensions.LiteralKeyToString(literal);
  54. }
  55. if (expression is Identifier identifier)
  56. {
  57. return identifier.Name;
  58. }
  59. if (expression is MemberExpression memberExpression)
  60. {
  61. return ToString(memberExpression.Object) + "." + ToString(memberExpression.Property);
  62. }
  63. if (expression is CallExpression callExpression)
  64. {
  65. expression = callExpression.Callee;
  66. continue;
  67. }
  68. return null;
  69. }
  70. }
  71. protected internal static JintExpression Build(Expression expression)
  72. {
  73. if (expression.UserData is JintExpression preparedExpression)
  74. {
  75. return preparedExpression;
  76. }
  77. var result = expression.Type switch
  78. {
  79. NodeType.AssignmentExpression => JintAssignmentExpression.Build((AssignmentExpression) expression),
  80. NodeType.ArrayExpression => JintArrayExpression.Build((ArrayExpression) expression),
  81. NodeType.ArrowFunctionExpression => new JintArrowFunctionExpression((ArrowFunctionExpression) expression),
  82. NodeType.BinaryExpression => JintBinaryExpression.Build((NonLogicalBinaryExpression) expression),
  83. NodeType.CallExpression => new JintCallExpression((CallExpression) expression),
  84. NodeType.ConditionalExpression => new JintConditionalExpression((ConditionalExpression) expression),
  85. NodeType.FunctionExpression => new JintFunctionExpression((FunctionExpression) expression),
  86. NodeType.Identifier => new JintIdentifierExpression((Identifier) expression),
  87. NodeType.PrivateIdentifier => new JintPrivateIdentifierExpression((PrivateIdentifier) expression),
  88. NodeType.Literal => JintLiteralExpression.Build((Literal) expression),
  89. NodeType.LogicalExpression => ((LogicalExpression) expression).Operator switch
  90. {
  91. Operator.LogicalAnd => new JintLogicalAndExpression((LogicalExpression) expression),
  92. Operator.LogicalOr => new JintLogicalOrExpression((LogicalExpression) expression),
  93. Operator.NullishCoalescing => new NullishCoalescingExpression((LogicalExpression) expression),
  94. _ => null
  95. },
  96. NodeType.MemberExpression => new JintMemberExpression((MemberExpression) expression),
  97. NodeType.NewExpression => new JintNewExpression((NewExpression) expression),
  98. NodeType.ObjectExpression => JintObjectExpression.Build((ObjectExpression) expression),
  99. NodeType.SequenceExpression => new JintSequenceExpression((SequenceExpression) expression),
  100. NodeType.ThisExpression => new JintThisExpression((ThisExpression) expression),
  101. NodeType.UpdateExpression => new JintUpdateExpression((UpdateExpression) expression),
  102. NodeType.UnaryExpression => JintUnaryExpression.Build((NonUpdateUnaryExpression) expression),
  103. NodeType.SpreadElement => new JintSpreadExpression((SpreadElement) expression),
  104. NodeType.TemplateLiteral => new JintTemplateLiteralExpression((TemplateLiteral) expression),
  105. NodeType.TaggedTemplateExpression => new JintTaggedTemplateExpression((TaggedTemplateExpression) expression),
  106. NodeType.ClassExpression => new JintClassExpression((ClassExpression) expression),
  107. NodeType.ImportExpression => new JintImportExpression((ImportExpression) expression),
  108. NodeType.Super => new JintSuperExpression((Super) expression),
  109. NodeType.MetaProperty => new JintMetaPropertyExpression((MetaProperty) expression),
  110. NodeType.ChainExpression => ((ChainExpression) expression).Expression.Type == NodeType.CallExpression
  111. ? new JintCallExpression((CallExpression) ((ChainExpression) expression).Expression)
  112. : new JintMemberExpression((MemberExpression) ((ChainExpression) expression).Expression),
  113. NodeType.AwaitExpression => new JintAwaitExpression((AwaitExpression) expression),
  114. NodeType.YieldExpression => new JintYieldExpression((YieldExpression) expression),
  115. _ => null
  116. };
  117. if (result is null)
  118. {
  119. Throw.ArgumentOutOfRangeException(nameof(expression), $"unsupported expression type '{expression.Type}'");
  120. }
  121. return result;
  122. }
  123. protected static JsValue Remainder(EvaluationContext context, JsValue left, JsValue right)
  124. {
  125. var result = JsValue.Undefined;
  126. left = TypeConverter.ToNumeric(left);
  127. right = TypeConverter.ToNumeric(right);
  128. if (AreIntegerOperands(left, right))
  129. {
  130. var leftInteger = left.AsInteger();
  131. var rightInteger = right.AsInteger();
  132. if (rightInteger == 0)
  133. {
  134. result = JsNumber.DoubleNaN;
  135. }
  136. else
  137. {
  138. var modulo = leftInteger % rightInteger;
  139. if (modulo == 0 && leftInteger < 0)
  140. {
  141. result = JsNumber.NegativeZero;
  142. }
  143. else
  144. {
  145. result = JsNumber.Create(modulo);
  146. }
  147. }
  148. }
  149. else if (JintBinaryExpression.AreNonBigIntOperands(left, right))
  150. {
  151. var n = left.AsNumber();
  152. var d = right.AsNumber();
  153. if (double.IsNaN(n) || double.IsNaN(d) || double.IsInfinity(n))
  154. {
  155. result = JsNumber.DoubleNaN;
  156. }
  157. else if (double.IsInfinity(d))
  158. {
  159. result = n;
  160. }
  161. else if (NumberInstance.IsPositiveZero(d) || NumberInstance.IsNegativeZero(d))
  162. {
  163. result = JsNumber.DoubleNaN;
  164. }
  165. else if (NumberInstance.IsPositiveZero(n) || NumberInstance.IsNegativeZero(n))
  166. {
  167. result = n;
  168. }
  169. else
  170. {
  171. result = JsNumber.Create(n % d);
  172. }
  173. }
  174. else
  175. {
  176. JintBinaryExpression.AssertValidBigIntArithmeticOperands(left, right);
  177. var n = TypeConverter.ToBigInt(left);
  178. var d = TypeConverter.ToBigInt(right);
  179. if (d == 0)
  180. {
  181. Throw.RangeError(context.Engine.Realm, "Division by zero");
  182. }
  183. else if (n == 0)
  184. {
  185. result = JsBigInt.Zero;
  186. }
  187. else
  188. {
  189. result = JsBigInt.Create(n % d);
  190. }
  191. }
  192. return result;
  193. }
  194. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  195. protected static JsValue Divide(EvaluationContext context, JsValue left, JsValue right)
  196. {
  197. JsValue result;
  198. if (AreIntegerOperands(left, right))
  199. {
  200. result = DivideInteger(left, right);
  201. }
  202. else if (JintBinaryExpression.AreNonBigIntOperands(left, right))
  203. {
  204. result = DivideComplex(left, right);
  205. }
  206. else
  207. {
  208. JintBinaryExpression.AssertValidBigIntArithmeticOperands(left, right);
  209. var x = TypeConverter.ToBigInt(left);
  210. var y = TypeConverter.ToBigInt(right);
  211. if (y == 0)
  212. {
  213. Throw.RangeError(context.Engine.Realm, "Division by zero");
  214. }
  215. result = JsBigInt.Create(x / y);
  216. }
  217. return result;
  218. }
  219. private static JsValue DivideInteger(JsValue lval, JsValue rval)
  220. {
  221. var lN = lval.AsInteger();
  222. var rN = rval.AsInteger();
  223. if (lN == 0 && rN == 0)
  224. {
  225. return JsNumber.DoubleNaN;
  226. }
  227. if (rN == 0)
  228. {
  229. return lN > 0 ? double.PositiveInfinity : double.NegativeInfinity;
  230. }
  231. if (lN % rN == 0 && (lN != 0 || rN > 0))
  232. {
  233. return JsNumber.Create(lN / rN);
  234. }
  235. return (double) lN / rN;
  236. }
  237. private static JsValue DivideComplex(JsValue lval, JsValue rval)
  238. {
  239. if (lval.IsUndefined() || rval.IsUndefined())
  240. {
  241. return JsValue.Undefined;
  242. }
  243. else
  244. {
  245. var lN = TypeConverter.ToNumber(lval);
  246. var rN = TypeConverter.ToNumber(rval);
  247. if (double.IsNaN(rN) || double.IsNaN(lN))
  248. {
  249. return JsNumber.DoubleNaN;
  250. }
  251. if (double.IsInfinity(lN) && double.IsInfinity(rN))
  252. {
  253. return JsNumber.DoubleNaN;
  254. }
  255. if (double.IsInfinity(lN) && rN == 0)
  256. {
  257. if (NumberInstance.IsNegativeZero(rN))
  258. {
  259. return -lN;
  260. }
  261. return lN;
  262. }
  263. if (lN == 0 && rN == 0)
  264. {
  265. return JsNumber.DoubleNaN;
  266. }
  267. if (rN == 0)
  268. {
  269. if (NumberInstance.IsNegativeZero(rN))
  270. {
  271. return lN > 0 ? -double.PositiveInfinity : -double.NegativeInfinity;
  272. }
  273. return lN > 0 ? double.PositiveInfinity : double.NegativeInfinity;
  274. }
  275. return lN / rN;
  276. }
  277. }
  278. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  279. protected static JsValue Compare(JsValue x, JsValue y, bool leftFirst = true) =>
  280. x.IsNumber() && y.IsNumber()
  281. ? CompareNumber(x, y, leftFirst)
  282. : CompareComplex(x, y, leftFirst);
  283. private static JsValue CompareNumber(JsValue x, JsValue y, bool leftFirst)
  284. {
  285. double nx, ny;
  286. if (leftFirst)
  287. {
  288. nx = x.AsNumber();
  289. ny = y.AsNumber();
  290. }
  291. else
  292. {
  293. ny = y.AsNumber();
  294. nx = x.AsNumber();
  295. }
  296. if (x.IsInteger() && y.IsInteger())
  297. {
  298. return (int) nx < (int) ny ? JsBoolean.True : JsBoolean.False;
  299. }
  300. if (!double.IsInfinity(nx) && !double.IsInfinity(ny) && !double.IsNaN(nx) && !double.IsNaN(ny))
  301. {
  302. return nx < ny ? JsBoolean.True : JsBoolean.False;
  303. }
  304. return CompareComplex(x, y, leftFirst);
  305. }
  306. private static JsValue CompareComplex(JsValue x, JsValue y, bool leftFirst)
  307. {
  308. JsValue px, py;
  309. if (leftFirst)
  310. {
  311. px = TypeConverter.ToPrimitive(x, Types.Number);
  312. py = TypeConverter.ToPrimitive(y, Types.Number);
  313. }
  314. else
  315. {
  316. py = TypeConverter.ToPrimitive(y, Types.Number);
  317. px = TypeConverter.ToPrimitive(x, Types.Number);
  318. }
  319. var typea = px.Type;
  320. var typeb = py.Type;
  321. if (typea != Types.String || typeb != Types.String)
  322. {
  323. if (typea == Types.BigInt || typeb == Types.BigInt)
  324. {
  325. if (typea == typeb)
  326. {
  327. return TypeConverter.ToBigInt(px) < TypeConverter.ToBigInt(py) ? JsBoolean.True : JsBoolean.False;
  328. }
  329. if (typea == Types.BigInt)
  330. {
  331. if (py is JsString jsStringY)
  332. {
  333. if (!TypeConverter.TryStringToBigInt(jsStringY.ToString(), out var temp))
  334. {
  335. return JsValue.Undefined;
  336. }
  337. return TypeConverter.ToBigInt(px) < temp ? JsBoolean.True : JsBoolean.False;
  338. }
  339. var numberB = TypeConverter.ToNumber(py);
  340. if (double.IsNaN(numberB))
  341. {
  342. return JsValue.Undefined;
  343. }
  344. if (double.IsPositiveInfinity(numberB))
  345. {
  346. return JsBoolean.True;
  347. }
  348. if (double.IsNegativeInfinity(numberB))
  349. {
  350. return JsBoolean.False;
  351. }
  352. var normalized = new BigInteger(Math.Ceiling(numberB));
  353. return TypeConverter.ToBigInt(px) < normalized ? JsBoolean.True : JsBoolean.False;
  354. }
  355. if (px is JsString jsStringX)
  356. {
  357. if (!TypeConverter.TryStringToBigInt(jsStringX.ToString(), out var temp))
  358. {
  359. return JsValue.Undefined;
  360. }
  361. return temp < TypeConverter.ToBigInt(py) ? JsBoolean.True : JsBoolean.False;
  362. }
  363. var numberA = TypeConverter.ToNumber(px);
  364. if (double.IsNaN(numberA))
  365. {
  366. return JsValue.Undefined;
  367. }
  368. if (double.IsPositiveInfinity(numberA))
  369. {
  370. return JsBoolean.False;
  371. }
  372. if (double.IsNegativeInfinity(numberA))
  373. {
  374. return JsBoolean.True;
  375. }
  376. var normalizedA = new BigInteger(Math.Floor(numberA));
  377. return normalizedA < TypeConverter.ToBigInt(py);
  378. }
  379. var nx = TypeConverter.ToNumber(px);
  380. var ny = TypeConverter.ToNumber(py);
  381. if (double.IsNaN(nx) || double.IsNaN(ny))
  382. {
  383. return JsValue.Undefined;
  384. }
  385. if (nx == ny)
  386. {
  387. return JsBoolean.False;
  388. }
  389. if (double.IsPositiveInfinity(nx))
  390. {
  391. return JsBoolean.False;
  392. }
  393. if (double.IsPositiveInfinity(ny))
  394. {
  395. return JsBoolean.True;
  396. }
  397. if (double.IsNegativeInfinity(ny))
  398. {
  399. return JsBoolean.False;
  400. }
  401. if (double.IsNegativeInfinity(nx))
  402. {
  403. return JsBoolean.True;
  404. }
  405. return nx < ny ? JsBoolean.True : JsBoolean.False;
  406. }
  407. return string.CompareOrdinal(TypeConverter.ToString(x), TypeConverter.ToString(y)) < 0 ? JsBoolean.True : JsBoolean.False;
  408. }
  409. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  410. protected static bool AreIntegerOperands(JsValue left, JsValue right)
  411. {
  412. return left._type == right._type && left._type == InternalTypes.Integer;
  413. }
  414. }