JintExpression.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. #nullable enable
  2. using System.Diagnostics;
  3. using System.Numerics;
  4. using System.Runtime.CompilerServices;
  5. using Esprima;
  6. using Esprima.Ast;
  7. using Jint.Native;
  8. using Jint.Native.Array;
  9. using Jint.Native.Iterator;
  10. using Jint.Native.Number;
  11. using Jint.Runtime.References;
  12. namespace Jint.Runtime.Interpreter.Expressions
  13. {
  14. /// <summary>
  15. /// Adapter to get different types of results, including Reference which is not a JsValue.
  16. /// </summary>
  17. internal readonly struct ExpressionResult
  18. {
  19. public readonly ExpressionCompletionType Type;
  20. public readonly Location Location;
  21. public readonly object Value;
  22. public ExpressionResult(ExpressionCompletionType type, object value, in Location location)
  23. {
  24. Type = type;
  25. Value = value;
  26. Location = location;
  27. }
  28. public bool IsAbrupt() => Type != ExpressionCompletionType.Normal && Type != ExpressionCompletionType.Reference;
  29. public static implicit operator ExpressionResult(in Completion result)
  30. {
  31. return new ExpressionResult((ExpressionCompletionType) result.Type, result.Value!, result.Location);
  32. }
  33. }
  34. internal enum ExpressionCompletionType : byte
  35. {
  36. Normal = 0,
  37. Return = 1,
  38. Throw = 2,
  39. Reference
  40. }
  41. internal abstract class JintExpression
  42. {
  43. // require sub-classes to set to false explicitly to skip virtual call
  44. protected bool _initialized = true;
  45. protected internal readonly Expression _expression;
  46. protected JintExpression(Expression expression)
  47. {
  48. _expression = expression;
  49. }
  50. /// <summary>
  51. /// Resolves the underlying value for this expression.
  52. /// By default uses the Engine for resolving.
  53. /// </summary>
  54. /// <param name="context"></param>
  55. /// <seealso cref="JintLiteralExpression"/>
  56. public virtual Completion GetValue(EvaluationContext context)
  57. {
  58. var result = Evaluate(context);
  59. if (result.Type != ExpressionCompletionType.Reference)
  60. {
  61. return new Completion((CompletionType) result.Type, (JsValue) result.Value, result.Location);
  62. }
  63. var jsValue = context.Engine.GetValue((Reference) result.Value, true);
  64. return new Completion(CompletionType.Normal, jsValue, null, _expression.Location);
  65. }
  66. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  67. public ExpressionResult Evaluate(EvaluationContext context)
  68. {
  69. context.LastSyntaxNode = _expression;
  70. if (!_initialized)
  71. {
  72. Initialize(context);
  73. _initialized = true;
  74. }
  75. return EvaluateInternal(context);
  76. }
  77. /// <summary>
  78. /// Opportunity to build one-time structures and caching based on lexical context.
  79. /// </summary>
  80. /// <param name="context"></param>
  81. protected virtual void Initialize(EvaluationContext context)
  82. {
  83. }
  84. protected abstract ExpressionResult EvaluateInternal(EvaluationContext context);
  85. /// <summary>
  86. /// https://tc39.es/ecma262/#sec-normalcompletion
  87. /// </summary>
  88. /// <remarks>
  89. /// We use custom type that is translated to Completion later on.
  90. /// </remarks>
  91. [DebuggerStepThrough]
  92. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  93. protected ExpressionResult NormalCompletion(JsValue value)
  94. {
  95. return new ExpressionResult(ExpressionCompletionType.Normal, value, _expression.Location);
  96. }
  97. protected ExpressionResult NormalCompletion(Reference value)
  98. {
  99. return new ExpressionResult(ExpressionCompletionType.Reference, value, _expression.Location);
  100. }
  101. /// <summary>
  102. /// https://tc39.es/ecma262/#sec-throwcompletion
  103. /// </summary>
  104. /// <remarks>
  105. /// We use custom type that is translated to Completion later on.
  106. /// </remarks>
  107. [DebuggerStepThrough]
  108. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  109. protected ExpressionResult ThrowCompletion(JsValue value)
  110. {
  111. return new ExpressionResult(ExpressionCompletionType.Throw, value, _expression.Location);
  112. }
  113. /// <summary>
  114. /// If we'd get Esprima source, we would just refer to it, but this makes error messages easier to decipher.
  115. /// </summary>
  116. internal string SourceText => ToString(_expression) ?? "*unknown*";
  117. internal static string? ToString(Expression expression)
  118. {
  119. while (true)
  120. {
  121. if (expression is Literal literal)
  122. {
  123. return EsprimaExtensions.LiteralKeyToString(literal);
  124. }
  125. if (expression is Identifier identifier)
  126. {
  127. return identifier.Name;
  128. }
  129. if (expression is MemberExpression memberExpression)
  130. {
  131. return ToString(memberExpression.Object) + "." + ToString(memberExpression.Property);
  132. }
  133. if (expression is CallExpression callExpression)
  134. {
  135. expression = callExpression.Callee;
  136. continue;
  137. }
  138. return null;
  139. }
  140. }
  141. protected internal static JintExpression Build(Engine engine, Expression expression)
  142. {
  143. var result = expression.Type switch
  144. {
  145. Nodes.AssignmentExpression => JintAssignmentExpression.Build(engine, (AssignmentExpression) expression),
  146. Nodes.ArrayExpression => new JintArrayExpression((ArrayExpression) expression),
  147. Nodes.ArrowFunctionExpression => new JintArrowFunctionExpression(engine, (ArrowFunctionExpression) expression),
  148. Nodes.BinaryExpression => JintBinaryExpression.Build(engine, (BinaryExpression) expression),
  149. Nodes.CallExpression => new JintCallExpression((CallExpression) expression),
  150. Nodes.ConditionalExpression => new JintConditionalExpression(engine, (ConditionalExpression) expression),
  151. Nodes.FunctionExpression => new JintFunctionExpression(engine, (FunctionExpression) expression),
  152. Nodes.Identifier => new JintIdentifierExpression((Identifier) expression),
  153. Nodes.Literal => JintLiteralExpression.Build((Literal) expression),
  154. Nodes.LogicalExpression => ((BinaryExpression) expression).Operator switch
  155. {
  156. BinaryOperator.LogicalAnd => new JintLogicalAndExpression((BinaryExpression) expression),
  157. BinaryOperator.LogicalOr => new JintLogicalOrExpression(engine, (BinaryExpression) expression),
  158. BinaryOperator.NullishCoalescing => new NullishCoalescingExpression(engine, (BinaryExpression) expression),
  159. _ => null
  160. },
  161. Nodes.MemberExpression => new JintMemberExpression((MemberExpression) expression),
  162. Nodes.NewExpression => new JintNewExpression((NewExpression) expression),
  163. Nodes.ObjectExpression => new JintObjectExpression((ObjectExpression) expression),
  164. Nodes.SequenceExpression => new JintSequenceExpression((SequenceExpression) expression),
  165. Nodes.ThisExpression => new JintThisExpression((ThisExpression) expression),
  166. Nodes.UpdateExpression => new JintUpdateExpression((UpdateExpression) expression),
  167. Nodes.UnaryExpression => JintUnaryExpression.Build(engine, (UnaryExpression) expression),
  168. Nodes.SpreadElement => new JintSpreadExpression(engine, (SpreadElement) expression),
  169. Nodes.TemplateLiteral => new JintTemplateLiteralExpression((TemplateLiteral) expression),
  170. Nodes.TaggedTemplateExpression => new JintTaggedTemplateExpression((TaggedTemplateExpression) expression),
  171. Nodes.ClassExpression => new JintClassExpression((ClassExpression) expression),
  172. Nodes.Import => new JintImportExpression((Import) expression),
  173. Nodes.Super => new JintSuperExpression((Super) expression),
  174. Nodes.MetaProperty => new JintMetaPropertyExpression((MetaProperty) expression),
  175. Nodes.ChainExpression => ((ChainExpression) expression).Expression.Type == Nodes.CallExpression
  176. ? new JintCallExpression((CallExpression) ((ChainExpression) expression).Expression)
  177. : new JintMemberExpression((MemberExpression) ((ChainExpression) expression).Expression),
  178. _ => null
  179. };
  180. if (result is null)
  181. {
  182. ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(expression), $"unsupported expression type '{expression.Type}'");
  183. }
  184. return result;
  185. }
  186. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  187. protected static JsValue Divide(EvaluationContext context, JsValue left, JsValue right)
  188. {
  189. JsValue result;
  190. if (AreIntegerOperands(left, right))
  191. {
  192. result = DivideInteger(left, right);
  193. }
  194. else if (JintBinaryExpression.AreNonBigIntOperands(left, right))
  195. {
  196. result = DivideComplex(left, right);
  197. }
  198. else
  199. {
  200. JintBinaryExpression.AssertValidBigIntArithmeticOperands(context, left, right);
  201. var x = TypeConverter.ToBigInt(left);
  202. var y = TypeConverter.ToBigInt(right);
  203. if (y == 0)
  204. {
  205. ExceptionHelper.ThrowRangeError(context.Engine.Realm, "Division by zero");
  206. }
  207. result = JsBigInt.Create(x / y);
  208. }
  209. return result;
  210. }
  211. private static JsValue DivideInteger(JsValue lval, JsValue rval)
  212. {
  213. var lN = lval.AsInteger();
  214. var rN = rval.AsInteger();
  215. if (lN == 0 && rN == 0)
  216. {
  217. return JsNumber.DoubleNaN;
  218. }
  219. if (rN == 0)
  220. {
  221. return lN > 0 ? double.PositiveInfinity : double.NegativeInfinity;
  222. }
  223. if (lN % rN == 0)
  224. {
  225. return lN / rN;
  226. }
  227. return (double) lN / rN;
  228. }
  229. private static JsValue DivideComplex(JsValue lval, JsValue rval)
  230. {
  231. if (lval.IsUndefined() || rval.IsUndefined())
  232. {
  233. return Undefined.Instance;
  234. }
  235. else
  236. {
  237. var lN = TypeConverter.ToNumber(lval);
  238. var rN = TypeConverter.ToNumber(rval);
  239. if (double.IsNaN(rN) || double.IsNaN(lN))
  240. {
  241. return JsNumber.DoubleNaN;
  242. }
  243. if (double.IsInfinity(lN) && double.IsInfinity(rN))
  244. {
  245. return JsNumber.DoubleNaN;
  246. }
  247. if (double.IsInfinity(lN) && rN == 0)
  248. {
  249. if (NumberInstance.IsNegativeZero(rN))
  250. {
  251. return -lN;
  252. }
  253. return lN;
  254. }
  255. if (lN == 0 && rN == 0)
  256. {
  257. return JsNumber.DoubleNaN;
  258. }
  259. if (rN == 0)
  260. {
  261. if (NumberInstance.IsNegativeZero(rN))
  262. {
  263. return lN > 0 ? -double.PositiveInfinity : -double.NegativeInfinity;
  264. }
  265. return lN > 0 ? double.PositiveInfinity : double.NegativeInfinity;
  266. }
  267. return lN / rN;
  268. }
  269. }
  270. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  271. protected static JsValue Compare(JsValue x, JsValue y, bool leftFirst = true) =>
  272. x._type == y._type && x._type == InternalTypes.Integer
  273. ? CompareInteger(x, y, leftFirst)
  274. : CompareComplex(x, y, leftFirst);
  275. private static JsValue CompareInteger(JsValue x, JsValue y, bool leftFirst)
  276. {
  277. int nx, ny;
  278. if (leftFirst)
  279. {
  280. nx = x.AsInteger();
  281. ny = y.AsInteger();
  282. }
  283. else
  284. {
  285. ny = y.AsInteger();
  286. nx = x.AsInteger();
  287. }
  288. return nx < ny;
  289. }
  290. private static JsValue CompareComplex(JsValue x, JsValue y, bool leftFirst)
  291. {
  292. JsValue px, py;
  293. if (leftFirst)
  294. {
  295. px = TypeConverter.ToPrimitive(x, Types.Number);
  296. py = TypeConverter.ToPrimitive(y, Types.Number);
  297. }
  298. else
  299. {
  300. py = TypeConverter.ToPrimitive(y, Types.Number);
  301. px = TypeConverter.ToPrimitive(x, Types.Number);
  302. }
  303. var typea = px.Type;
  304. var typeb = py.Type;
  305. if (typea != Types.String || typeb != Types.String)
  306. {
  307. if (typea == Types.BigInt || typeb == Types.BigInt)
  308. {
  309. if (typea == typeb)
  310. {
  311. return TypeConverter.ToBigInt(px) < TypeConverter.ToBigInt(py);
  312. }
  313. if (typea == Types.BigInt)
  314. {
  315. if (py is JsString jsStringY)
  316. {
  317. if (!TypeConverter.TryStringToBigInt(jsStringY.ToString(), out var temp))
  318. {
  319. return JsValue.Undefined;
  320. }
  321. return TypeConverter.ToBigInt(px) < temp;
  322. }
  323. var numberB = TypeConverter.ToNumber(py);
  324. if (double.IsNaN(numberB))
  325. {
  326. return JsValue.Undefined;
  327. }
  328. if (double.IsPositiveInfinity(numberB))
  329. {
  330. return true;
  331. }
  332. if (double.IsNegativeInfinity(numberB))
  333. {
  334. return false;
  335. }
  336. var normalized = new BigInteger(System.Math.Ceiling(numberB));
  337. return TypeConverter.ToBigInt(px) < normalized;
  338. }
  339. if (px is JsString jsStringX)
  340. {
  341. if (!TypeConverter.TryStringToBigInt(jsStringX.ToString(), out var temp))
  342. {
  343. return JsValue.Undefined;
  344. }
  345. return temp < TypeConverter.ToBigInt(py);
  346. }
  347. var numberA = TypeConverter.ToNumber(px);
  348. if (double.IsNaN(numberA))
  349. {
  350. return JsValue.Undefined;
  351. }
  352. if (double.IsPositiveInfinity(numberA))
  353. {
  354. return false;
  355. }
  356. if (double.IsNegativeInfinity(numberA))
  357. {
  358. return true;
  359. }
  360. var normalizedA = new BigInteger(System.Math.Floor(numberA));
  361. return normalizedA < TypeConverter.ToBigInt(py);
  362. }
  363. var nx = TypeConverter.ToNumber(px);
  364. var ny = TypeConverter.ToNumber(py);
  365. if (double.IsNaN(nx) || double.IsNaN(ny))
  366. {
  367. return Undefined.Instance;
  368. }
  369. if (nx == ny)
  370. {
  371. return false;
  372. }
  373. if (double.IsPositiveInfinity(nx))
  374. {
  375. return false;
  376. }
  377. if (double.IsPositiveInfinity(ny))
  378. {
  379. return true;
  380. }
  381. if (double.IsNegativeInfinity(ny))
  382. {
  383. return false;
  384. }
  385. if (double.IsNegativeInfinity(nx))
  386. {
  387. return true;
  388. }
  389. return nx < ny;
  390. }
  391. return string.CompareOrdinal(TypeConverter.ToString(x), TypeConverter.ToString(y)) < 0;
  392. }
  393. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  394. protected static void BuildArguments(EvaluationContext context, JintExpression[] jintExpressions, JsValue[] targetArray)
  395. {
  396. for (var i = 0; i < jintExpressions.Length; i++)
  397. {
  398. var completion = jintExpressions[i].GetValue(context);
  399. targetArray[i] = completion.Value!.Clone();
  400. }
  401. }
  402. protected static JsValue[] BuildArgumentsWithSpreads(EvaluationContext context, JintExpression[] jintExpressions)
  403. {
  404. var args = new System.Collections.Generic.List<JsValue>(jintExpressions.Length);
  405. for (var i = 0; i < jintExpressions.Length; i++)
  406. {
  407. var jintExpression = jintExpressions[i];
  408. if (jintExpression is JintSpreadExpression jse)
  409. {
  410. jse.GetValueAndCheckIterator(context, out var objectInstance, out var iterator);
  411. // optimize for array unless someone has touched the iterator
  412. if (objectInstance is ArrayInstance ai && ai.HasOriginalIterator)
  413. {
  414. var length = ai.GetLength();
  415. for (uint j = 0; j < length; ++j)
  416. {
  417. if (ai.TryGetValue(j, out var value))
  418. {
  419. args.Add(value);
  420. }
  421. }
  422. }
  423. else
  424. {
  425. var protocol = new ArraySpreadProtocol(context.Engine, args, iterator);
  426. protocol.Execute();
  427. }
  428. }
  429. else
  430. {
  431. var completion = jintExpression.GetValue(context);
  432. args.Add(completion.Value!.Clone());
  433. }
  434. }
  435. return args.ToArray();
  436. }
  437. private sealed class ArraySpreadProtocol : IteratorProtocol
  438. {
  439. private readonly System.Collections.Generic.List<JsValue> _instance;
  440. public ArraySpreadProtocol(
  441. Engine engine,
  442. System.Collections.Generic.List<JsValue> instance,
  443. IteratorInstance iterator) : base(engine, iterator, 0)
  444. {
  445. _instance = instance;
  446. }
  447. protected override void ProcessItem(JsValue[] args, JsValue currentValue)
  448. {
  449. _instance.Add(currentValue);
  450. }
  451. }
  452. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  453. protected static bool AreIntegerOperands(JsValue left, JsValue right)
  454. {
  455. return left._type == right._type && left._type == InternalTypes.Integer;
  456. }
  457. }
  458. }