JintArrayExpression.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. using Esprima.Ast;
  2. using Jint.Native;
  3. using Jint.Native.Iterator;
  4. namespace Jint.Runtime.Interpreter.Expressions
  5. {
  6. internal sealed class JintArrayExpression : JintExpression
  7. {
  8. private JintExpression?[] _expressions = Array.Empty<JintExpression?>();
  9. private bool _hasSpreads;
  10. private bool _initialized;
  11. private JintArrayExpression(ArrayExpression expression) : base(expression)
  12. {
  13. }
  14. public static JintExpression Build(ArrayExpression expression)
  15. {
  16. return expression.Elements.Count == 0
  17. ? JintEmptyArrayExpression.Instance
  18. : new JintArrayExpression(expression);
  19. }
  20. private void Initialize()
  21. {
  22. ref readonly var elements = ref ((ArrayExpression) _expression).Elements;
  23. var expressions = _expressions = new JintExpression[((ArrayExpression) _expression).Elements.Count];
  24. for (var n = 0; n < expressions.Length; n++)
  25. {
  26. var expr = elements[n];
  27. if (expr != null)
  28. {
  29. var expression = Build(expr);
  30. expressions[n] = expression;
  31. _hasSpreads |= expr.Type == Nodes.SpreadElement;
  32. }
  33. }
  34. // we get called from nested spread expansion in call
  35. _initialized = true;
  36. }
  37. protected override object EvaluateInternal(EvaluationContext context)
  38. {
  39. if (!_initialized)
  40. {
  41. Initialize();
  42. _initialized = true;
  43. }
  44. var engine = context.Engine;
  45. var a = engine.Realm.Intrinsics.Array.ArrayCreate(_hasSpreads ? 0 : (uint) _expressions.Length);
  46. uint arrayIndexCounter = 0;
  47. foreach (var expr in _expressions)
  48. {
  49. if (expr == null)
  50. {
  51. a.SetIndexValue(arrayIndexCounter++, null, updateLength: false);
  52. }
  53. else if (_hasSpreads && expr is JintSpreadExpression jse)
  54. {
  55. jse.GetValueAndCheckIterator(context, out var objectInstance, out var iterator);
  56. // optimize for array
  57. if (objectInstance is JsArray ai)
  58. {
  59. var length = ai.GetLength();
  60. var newLength = arrayIndexCounter + length;
  61. a.EnsureCapacity(newLength);
  62. a.CopyValues(ai, sourceStartIndex: 0, targetStartIndex: arrayIndexCounter, length);
  63. arrayIndexCounter += length;
  64. a.SetLength(newLength);
  65. }
  66. else
  67. {
  68. var protocol = new ArraySpreadProtocol(engine, a, iterator!, arrayIndexCounter);
  69. protocol.Execute();
  70. arrayIndexCounter += protocol._addedCount;
  71. }
  72. }
  73. else
  74. {
  75. var value = expr.GetValue(context);
  76. a.SetIndexValue(arrayIndexCounter++, value, updateLength: false);
  77. }
  78. }
  79. if (_hasSpreads)
  80. {
  81. a.SetLength(arrayIndexCounter);
  82. }
  83. return a;
  84. }
  85. private sealed class ArraySpreadProtocol : IteratorProtocol
  86. {
  87. private readonly JsArray _instance;
  88. private long _index;
  89. internal uint _addedCount;
  90. public ArraySpreadProtocol(
  91. Engine engine,
  92. JsArray instance,
  93. IteratorInstance iterator,
  94. long startIndex) : base(engine, iterator, 0)
  95. {
  96. _instance = instance;
  97. _index = startIndex - 1;
  98. }
  99. protected override void ProcessItem(JsValue[] arguments, JsValue currentValue)
  100. {
  101. _index++;
  102. _addedCount++;
  103. _instance.SetIndexValue((uint) _index, currentValue, updateLength: false);
  104. }
  105. }
  106. internal sealed class JintEmptyArrayExpression : JintExpression
  107. {
  108. public static JintEmptyArrayExpression Instance = new(new ArrayExpression(NodeList.Create(System.Linq.Enumerable.Empty<Expression?>())));
  109. private JintEmptyArrayExpression(Expression expression) : base(expression)
  110. {
  111. }
  112. protected override object EvaluateInternal(EvaluationContext context)
  113. {
  114. return new JsArray(context.Engine, Array.Empty<JsValue>());
  115. }
  116. public override JsValue GetValue(EvaluationContext context)
  117. {
  118. return new JsArray(context.Engine, Array.Empty<JsValue>());
  119. }
  120. }
  121. }
  122. }