JintExpression.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. using System.Numerics;
  2. using System.Runtime.CompilerServices;
  3. using Esprima.Ast;
  4. using Jint.Native;
  5. using Jint.Native.Array;
  6. using Jint.Native.Iterator;
  7. using Jint.Native.Number;
  8. using Jint.Runtime.References;
  9. namespace Jint.Runtime.Interpreter.Expressions
  10. {
  11. internal abstract class JintExpression
  12. {
  13. // require sub-classes to set to false explicitly to skip virtual call
  14. protected bool _initialized = true;
  15. protected internal readonly Expression _expression;
  16. protected JintExpression(Expression expression)
  17. {
  18. _expression = expression;
  19. }
  20. /// <summary>
  21. /// Resolves the underlying value for this expression.
  22. /// By default uses the Engine for resolving.
  23. /// </summary>
  24. /// <param name="context"></param>
  25. /// <seealso cref="JintLiteralExpression"/>
  26. public virtual JsValue GetValue(EvaluationContext context)
  27. {
  28. var result = Evaluate(context);
  29. if (result is not Reference reference)
  30. {
  31. return (JsValue) result;
  32. }
  33. return context.Engine.GetValue(reference, true);
  34. }
  35. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  36. public object Evaluate(EvaluationContext context)
  37. {
  38. var oldSyntaxElement = context.LastSyntaxElement;
  39. context.PrepareFor(_expression);
  40. if (!_initialized)
  41. {
  42. Initialize(context);
  43. _initialized = true;
  44. }
  45. var result = EvaluateInternal(context);
  46. context.LastSyntaxElement = oldSyntaxElement;
  47. return result;
  48. }
  49. /// <summary>
  50. /// Opportunity to build one-time structures and caching based on lexical context.
  51. /// </summary>
  52. /// <param name="context"></param>
  53. protected virtual void Initialize(EvaluationContext context)
  54. {
  55. }
  56. protected abstract object EvaluateInternal(EvaluationContext context);
  57. /// <summary>
  58. /// If we'd get Esprima source, we would just refer to it, but this makes error messages easier to decipher.
  59. /// </summary>
  60. internal string SourceText => ToString(_expression) ?? "*unknown*";
  61. internal static string? ToString(Expression expression)
  62. {
  63. while (true)
  64. {
  65. if (expression is Literal literal)
  66. {
  67. return EsprimaExtensions.LiteralKeyToString(literal);
  68. }
  69. if (expression is Identifier identifier)
  70. {
  71. return identifier.Name;
  72. }
  73. if (expression is MemberExpression memberExpression)
  74. {
  75. return ToString(memberExpression.Object) + "." + ToString(memberExpression.Property);
  76. }
  77. if (expression is CallExpression callExpression)
  78. {
  79. expression = callExpression.Callee;
  80. continue;
  81. }
  82. return null;
  83. }
  84. }
  85. protected internal static JintExpression Build(Expression expression)
  86. {
  87. var result = expression.Type switch
  88. {
  89. Nodes.AssignmentExpression => JintAssignmentExpression.Build((AssignmentExpression) expression),
  90. Nodes.ArrayExpression => JintArrayExpression.Build((ArrayExpression) expression),
  91. Nodes.ArrowFunctionExpression => new JintArrowFunctionExpression((ArrowFunctionExpression) expression),
  92. Nodes.BinaryExpression => JintBinaryExpression.Build((BinaryExpression) expression),
  93. Nodes.CallExpression => new JintCallExpression((CallExpression) expression),
  94. Nodes.ConditionalExpression => new JintConditionalExpression((ConditionalExpression) expression),
  95. Nodes.FunctionExpression => new JintFunctionExpression((FunctionExpression) expression),
  96. Nodes.Identifier => new JintIdentifierExpression((Identifier) expression),
  97. Nodes.Literal => JintLiteralExpression.Build((Literal) expression),
  98. Nodes.LogicalExpression => ((BinaryExpression) expression).Operator switch
  99. {
  100. BinaryOperator.LogicalAnd => new JintLogicalAndExpression((BinaryExpression) expression),
  101. BinaryOperator.LogicalOr => new JintLogicalOrExpression((BinaryExpression) expression),
  102. BinaryOperator.NullishCoalescing => new NullishCoalescingExpression((BinaryExpression) expression),
  103. _ => null
  104. },
  105. Nodes.MemberExpression => new JintMemberExpression((MemberExpression) expression),
  106. Nodes.NewExpression => new JintNewExpression((NewExpression) expression),
  107. Nodes.ObjectExpression => JintObjectExpression.Build((ObjectExpression) expression),
  108. Nodes.SequenceExpression => new JintSequenceExpression((SequenceExpression) expression),
  109. Nodes.ThisExpression => new JintThisExpression((ThisExpression) expression),
  110. Nodes.UpdateExpression => new JintUpdateExpression((UpdateExpression) expression),
  111. Nodes.UnaryExpression => JintUnaryExpression.Build((UnaryExpression) expression),
  112. Nodes.SpreadElement => new JintSpreadExpression((SpreadElement) expression),
  113. Nodes.TemplateLiteral => new JintTemplateLiteralExpression((TemplateLiteral) expression),
  114. Nodes.TaggedTemplateExpression => new JintTaggedTemplateExpression((TaggedTemplateExpression) expression),
  115. Nodes.ClassExpression => new JintClassExpression((ClassExpression) expression),
  116. Nodes.Import => new JintImportExpression((Import) expression),
  117. Nodes.Super => new JintSuperExpression((Super) expression),
  118. Nodes.MetaProperty => new JintMetaPropertyExpression((MetaProperty) expression),
  119. Nodes.ChainExpression => ((ChainExpression) expression).Expression.Type == Nodes.CallExpression
  120. ? new JintCallExpression((CallExpression) ((ChainExpression) expression).Expression)
  121. : new JintMemberExpression((MemberExpression) ((ChainExpression) expression).Expression),
  122. Nodes.AwaitExpression => new JintAwaitExpression((AwaitExpression) expression),
  123. _ => null
  124. };
  125. if (result is null)
  126. {
  127. ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(expression), $"unsupported expression type '{expression.Type}'");
  128. }
  129. return result;
  130. }
  131. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  132. protected static JsValue Divide(EvaluationContext context, JsValue left, JsValue right)
  133. {
  134. JsValue result;
  135. if (AreIntegerOperands(left, right))
  136. {
  137. result = DivideInteger(left, right);
  138. }
  139. else if (JintBinaryExpression.AreNonBigIntOperands(left, right))
  140. {
  141. result = DivideComplex(left, right);
  142. }
  143. else
  144. {
  145. JintBinaryExpression.AssertValidBigIntArithmeticOperands(left, right);
  146. var x = TypeConverter.ToBigInt(left);
  147. var y = TypeConverter.ToBigInt(right);
  148. if (y == 0)
  149. {
  150. ExceptionHelper.ThrowRangeError(context.Engine.Realm, "Division by zero");
  151. }
  152. result = JsBigInt.Create(x / y);
  153. }
  154. return result;
  155. }
  156. private static JsValue DivideInteger(JsValue lval, JsValue rval)
  157. {
  158. var lN = lval.AsInteger();
  159. var rN = rval.AsInteger();
  160. if (lN == 0 && rN == 0)
  161. {
  162. return JsNumber.DoubleNaN;
  163. }
  164. if (rN == 0)
  165. {
  166. return lN > 0 ? double.PositiveInfinity : double.NegativeInfinity;
  167. }
  168. if (lN % rN == 0)
  169. {
  170. return lN / rN;
  171. }
  172. return (double) lN / rN;
  173. }
  174. private static JsValue DivideComplex(JsValue lval, JsValue rval)
  175. {
  176. if (lval.IsUndefined() || rval.IsUndefined())
  177. {
  178. return Undefined.Instance;
  179. }
  180. else
  181. {
  182. var lN = TypeConverter.ToNumber(lval);
  183. var rN = TypeConverter.ToNumber(rval);
  184. if (double.IsNaN(rN) || double.IsNaN(lN))
  185. {
  186. return JsNumber.DoubleNaN;
  187. }
  188. if (double.IsInfinity(lN) && double.IsInfinity(rN))
  189. {
  190. return JsNumber.DoubleNaN;
  191. }
  192. if (double.IsInfinity(lN) && rN == 0)
  193. {
  194. if (NumberInstance.IsNegativeZero(rN))
  195. {
  196. return -lN;
  197. }
  198. return lN;
  199. }
  200. if (lN == 0 && rN == 0)
  201. {
  202. return JsNumber.DoubleNaN;
  203. }
  204. if (rN == 0)
  205. {
  206. if (NumberInstance.IsNegativeZero(rN))
  207. {
  208. return lN > 0 ? -double.PositiveInfinity : -double.NegativeInfinity;
  209. }
  210. return lN > 0 ? double.PositiveInfinity : double.NegativeInfinity;
  211. }
  212. return lN / rN;
  213. }
  214. }
  215. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  216. protected static JsValue Compare(JsValue x, JsValue y, bool leftFirst = true) =>
  217. x._type == y._type && x._type == InternalTypes.Integer
  218. ? CompareInteger(x, y, leftFirst)
  219. : CompareComplex(x, y, leftFirst);
  220. private static JsValue CompareInteger(JsValue x, JsValue y, bool leftFirst)
  221. {
  222. int nx, ny;
  223. if (leftFirst)
  224. {
  225. nx = x.AsInteger();
  226. ny = y.AsInteger();
  227. }
  228. else
  229. {
  230. ny = y.AsInteger();
  231. nx = x.AsInteger();
  232. }
  233. return nx < ny;
  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);
  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;
  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 true;
  276. }
  277. if (double.IsNegativeInfinity(numberB))
  278. {
  279. return false;
  280. }
  281. var normalized = new BigInteger(Math.Ceiling(numberB));
  282. return TypeConverter.ToBigInt(px) < normalized;
  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);
  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 false;
  300. }
  301. if (double.IsNegativeInfinity(numberA))
  302. {
  303. return 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 Undefined.Instance;
  313. }
  314. if (nx == ny)
  315. {
  316. return false;
  317. }
  318. if (double.IsPositiveInfinity(nx))
  319. {
  320. return false;
  321. }
  322. if (double.IsPositiveInfinity(ny))
  323. {
  324. return true;
  325. }
  326. if (double.IsNegativeInfinity(ny))
  327. {
  328. return false;
  329. }
  330. if (double.IsNegativeInfinity(nx))
  331. {
  332. return true;
  333. }
  334. return nx < ny;
  335. }
  336. return string.CompareOrdinal(TypeConverter.ToString(x), TypeConverter.ToString(y)) < 0;
  337. }
  338. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  339. protected static void BuildArguments(EvaluationContext context, JintExpression[] jintExpressions, JsValue[] targetArray)
  340. {
  341. for (uint i = 0; i < (uint) jintExpressions.Length; i++)
  342. {
  343. targetArray[i] = jintExpressions[i].GetValue(context).Clone();
  344. }
  345. }
  346. protected static JsValue[] BuildArgumentsWithSpreads(EvaluationContext context, JintExpression[] jintExpressions)
  347. {
  348. var args = new List<JsValue>(jintExpressions.Length);
  349. foreach (var jintExpression in jintExpressions)
  350. {
  351. if (jintExpression is JintSpreadExpression jse)
  352. {
  353. jse.GetValueAndCheckIterator(context, out var objectInstance, out var iterator);
  354. // optimize for array unless someone has touched the iterator
  355. if (objectInstance is ArrayInstance { HasOriginalIterator: true } ai)
  356. {
  357. var length = ai.GetLength();
  358. for (uint j = 0; j < length; ++j)
  359. {
  360. ai.TryGetValue(j, out var value);
  361. args.Add(value);
  362. }
  363. }
  364. else
  365. {
  366. var protocol = new ArraySpreadProtocol(context.Engine, args, iterator!);
  367. protocol.Execute();
  368. }
  369. }
  370. else
  371. {
  372. args.Add(jintExpression.GetValue(context).Clone());
  373. }
  374. }
  375. return args.ToArray();
  376. }
  377. private sealed class ArraySpreadProtocol : IteratorProtocol
  378. {
  379. private readonly List<JsValue> _instance;
  380. public ArraySpreadProtocol(
  381. Engine engine,
  382. List<JsValue> instance,
  383. IteratorInstance iterator) : base(engine, iterator, 0)
  384. {
  385. _instance = instance;
  386. }
  387. protected override void ProcessItem(JsValue[] args, JsValue currentValue)
  388. {
  389. _instance.Add(currentValue);
  390. }
  391. }
  392. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  393. protected static bool AreIntegerOperands(JsValue left, JsValue right)
  394. {
  395. return left._type == right._type && left._type == InternalTypes.Integer;
  396. }
  397. }
  398. }