JintExpression.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  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. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  124. protected static JsValue Divide(EvaluationContext context, JsValue left, JsValue right)
  125. {
  126. JsValue result;
  127. if (AreIntegerOperands(left, right))
  128. {
  129. result = DivideInteger(left, right);
  130. }
  131. else if (JintBinaryExpression.AreNonBigIntOperands(left, right))
  132. {
  133. result = DivideComplex(left, right);
  134. }
  135. else
  136. {
  137. JintBinaryExpression.AssertValidBigIntArithmeticOperands(left, right);
  138. var x = TypeConverter.ToBigInt(left);
  139. var y = TypeConverter.ToBigInt(right);
  140. if (y == 0)
  141. {
  142. Throw.RangeError(context.Engine.Realm, "Division by zero");
  143. }
  144. result = JsBigInt.Create(x / y);
  145. }
  146. return result;
  147. }
  148. private static JsValue DivideInteger(JsValue lval, JsValue rval)
  149. {
  150. var lN = lval.AsInteger();
  151. var rN = rval.AsInteger();
  152. if (lN == 0 && rN == 0)
  153. {
  154. return JsNumber.DoubleNaN;
  155. }
  156. if (rN == 0)
  157. {
  158. return lN > 0 ? double.PositiveInfinity : double.NegativeInfinity;
  159. }
  160. if (lN % rN == 0 && (lN != 0 || rN > 0))
  161. {
  162. return JsNumber.Create(lN / rN);
  163. }
  164. return (double) lN / rN;
  165. }
  166. private static JsValue DivideComplex(JsValue lval, JsValue rval)
  167. {
  168. if (lval.IsUndefined() || rval.IsUndefined())
  169. {
  170. return JsValue.Undefined;
  171. }
  172. else
  173. {
  174. var lN = TypeConverter.ToNumber(lval);
  175. var rN = TypeConverter.ToNumber(rval);
  176. if (double.IsNaN(rN) || double.IsNaN(lN))
  177. {
  178. return JsNumber.DoubleNaN;
  179. }
  180. if (double.IsInfinity(lN) && double.IsInfinity(rN))
  181. {
  182. return JsNumber.DoubleNaN;
  183. }
  184. if (double.IsInfinity(lN) && rN == 0)
  185. {
  186. if (NumberInstance.IsNegativeZero(rN))
  187. {
  188. return -lN;
  189. }
  190. return lN;
  191. }
  192. if (lN == 0 && rN == 0)
  193. {
  194. return JsNumber.DoubleNaN;
  195. }
  196. if (rN == 0)
  197. {
  198. if (NumberInstance.IsNegativeZero(rN))
  199. {
  200. return lN > 0 ? -double.PositiveInfinity : -double.NegativeInfinity;
  201. }
  202. return lN > 0 ? double.PositiveInfinity : double.NegativeInfinity;
  203. }
  204. return lN / rN;
  205. }
  206. }
  207. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  208. protected static JsValue Compare(JsValue x, JsValue y, bool leftFirst = true) =>
  209. x.IsNumber() && y.IsNumber()
  210. ? CompareNumber(x, y, leftFirst)
  211. : CompareComplex(x, y, leftFirst);
  212. private static JsValue CompareNumber(JsValue x, JsValue y, bool leftFirst)
  213. {
  214. double nx, ny;
  215. if (leftFirst)
  216. {
  217. nx = x.AsNumber();
  218. ny = y.AsNumber();
  219. }
  220. else
  221. {
  222. ny = y.AsNumber();
  223. nx = x.AsNumber();
  224. }
  225. if (x.IsInteger() && y.IsInteger())
  226. {
  227. return (int) nx < (int) ny ? JsBoolean.True : JsBoolean.False;
  228. }
  229. if (!double.IsInfinity(nx) && !double.IsInfinity(ny) && !double.IsNaN(nx) && !double.IsNaN(ny))
  230. {
  231. return nx < ny ? JsBoolean.True : JsBoolean.False;
  232. }
  233. return CompareComplex(x, y, leftFirst);
  234. }
  235. private static JsValue CompareComplex(JsValue x, JsValue y, bool leftFirst)
  236. {
  237. JsValue px, py;
  238. if (leftFirst)
  239. {
  240. px = TypeConverter.ToPrimitive(x, Types.Number);
  241. py = TypeConverter.ToPrimitive(y, Types.Number);
  242. }
  243. else
  244. {
  245. py = TypeConverter.ToPrimitive(y, Types.Number);
  246. px = TypeConverter.ToPrimitive(x, Types.Number);
  247. }
  248. var typea = px.Type;
  249. var typeb = py.Type;
  250. if (typea != Types.String || typeb != Types.String)
  251. {
  252. if (typea == Types.BigInt || typeb == Types.BigInt)
  253. {
  254. if (typea == typeb)
  255. {
  256. return TypeConverter.ToBigInt(px) < TypeConverter.ToBigInt(py) ? JsBoolean.True : JsBoolean.False;
  257. }
  258. if (typea == Types.BigInt)
  259. {
  260. if (py is JsString jsStringY)
  261. {
  262. if (!TypeConverter.TryStringToBigInt(jsStringY.ToString(), out var temp))
  263. {
  264. return JsValue.Undefined;
  265. }
  266. return TypeConverter.ToBigInt(px) < temp ? JsBoolean.True : JsBoolean.False;
  267. }
  268. var numberB = TypeConverter.ToNumber(py);
  269. if (double.IsNaN(numberB))
  270. {
  271. return JsValue.Undefined;
  272. }
  273. if (double.IsPositiveInfinity(numberB))
  274. {
  275. return JsBoolean.True;
  276. }
  277. if (double.IsNegativeInfinity(numberB))
  278. {
  279. return JsBoolean.False;
  280. }
  281. var normalized = new BigInteger(Math.Ceiling(numberB));
  282. return TypeConverter.ToBigInt(px) < normalized ? JsBoolean.True : JsBoolean.False;
  283. }
  284. if (px is JsString jsStringX)
  285. {
  286. if (!TypeConverter.TryStringToBigInt(jsStringX.ToString(), out var temp))
  287. {
  288. return JsValue.Undefined;
  289. }
  290. return temp < TypeConverter.ToBigInt(py) ? JsBoolean.True : JsBoolean.False;
  291. }
  292. var numberA = TypeConverter.ToNumber(px);
  293. if (double.IsNaN(numberA))
  294. {
  295. return JsValue.Undefined;
  296. }
  297. if (double.IsPositiveInfinity(numberA))
  298. {
  299. return JsBoolean.False;
  300. }
  301. if (double.IsNegativeInfinity(numberA))
  302. {
  303. return JsBoolean.True;
  304. }
  305. var normalizedA = new BigInteger(Math.Floor(numberA));
  306. return normalizedA < TypeConverter.ToBigInt(py);
  307. }
  308. var nx = TypeConverter.ToNumber(px);
  309. var ny = TypeConverter.ToNumber(py);
  310. if (double.IsNaN(nx) || double.IsNaN(ny))
  311. {
  312. return JsValue.Undefined;
  313. }
  314. if (nx == ny)
  315. {
  316. return JsBoolean.False;
  317. }
  318. if (double.IsPositiveInfinity(nx))
  319. {
  320. return JsBoolean.False;
  321. }
  322. if (double.IsPositiveInfinity(ny))
  323. {
  324. return JsBoolean.True;
  325. }
  326. if (double.IsNegativeInfinity(ny))
  327. {
  328. return JsBoolean.False;
  329. }
  330. if (double.IsNegativeInfinity(nx))
  331. {
  332. return JsBoolean.True;
  333. }
  334. return nx < ny ? JsBoolean.True : JsBoolean.False;
  335. }
  336. return string.CompareOrdinal(TypeConverter.ToString(x), TypeConverter.ToString(y)) < 0 ? JsBoolean.True : JsBoolean.False;
  337. }
  338. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  339. protected static bool AreIntegerOperands(JsValue left, JsValue right)
  340. {
  341. return left._type == right._type && left._type == InternalTypes.Integer;
  342. }
  343. }