فهرست منبع

Create ExpressionCache to unify constant lookups (#2037)

Marko Lahma 6 ماه پیش
والد
کامیت
d52688f4af

+ 190 - 0
Jint/Runtime/Interpreter/Expressions/ExpressionCache.cs

@@ -0,0 +1,190 @@
+using System.Runtime.CompilerServices;
+using Jint.Native;
+using Jint.Native.Iterator;
+
+namespace Jint.Runtime.Interpreter.Expressions;
+
+/// <summary>
+/// Optimizes constants values from expression array and only returns the actual JsValue in consecutive calls.
+/// </summary>
+internal sealed class ExpressionCache
+{
+    private object?[] _expressions = [];
+    private bool _fullyCached;
+
+    internal bool HasSpreads { get; private set; }
+
+    internal void Initialize(EvaluationContext context, ReadOnlySpan<Expression> arguments)
+    {
+        if (arguments.Length == 0)
+        {
+            _fullyCached = true;
+            _expressions = [];
+            return;
+        }
+
+        _expressions = new object?[arguments.Length];
+        _fullyCached = true;
+        for (var i = 0; i < (uint) arguments.Length; i++)
+        {
+            var argument = arguments[i];
+            if (argument is null)
+            {
+                _fullyCached = false;
+                continue;
+            }
+
+            var expression = JintExpression.Build(argument);
+
+            if (argument.Type == NodeType.Literal)
+            {
+                _expressions[i] = expression.GetValue(context).Clone();
+                continue;
+            }
+
+            _expressions[i] = expression;
+            _fullyCached &= argument.Type == NodeType.Literal;
+            HasSpreads |= CanSpread(argument);
+
+            if (argument.Type == NodeType.ArrayExpression)
+            {
+                ref readonly var elements = ref ((ArrayExpression) argument).Elements;
+                foreach (var e in elements.AsSpan())
+                {
+                    HasSpreads |= CanSpread(e);
+                }
+            }
+        }
+    }
+
+    public JsValue[] ArgumentListEvaluation(EvaluationContext context, out bool rented)
+    {
+        rented = false;
+        if (_fullyCached)
+        {
+            return Unsafe.As<JsValue[]>(_expressions);
+        }
+
+        if (HasSpreads)
+        {
+            var args = new List<JsValue>(_expressions.Length);
+            BuildArgumentsWithSpreads(context, args);
+            return args.ToArray();
+        }
+
+        var arguments = context.Engine._jsValueArrayPool.RentArray(_expressions.Length);
+        rented = true;
+
+        BuildArguments(context, arguments);
+
+        return arguments;
+    }
+
+    internal void BuildArguments(EvaluationContext context, JsValue[] targetArray)
+    {
+        var expressions = _expressions;
+        for (uint i = 0; i < (uint) expressions.Length; i++)
+        {
+            targetArray[i] = GetValue(context, expressions[i])!;
+        }
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public JsValue GetValue(EvaluationContext context, int index)
+    {
+        return GetValue(context, _expressions[index]);
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    private static JsValue GetValue(EvaluationContext context, object? value)
+    {
+        return value switch
+        {
+            JintExpression expression => expression.GetValue(context).Clone(),
+            _ => (JsValue) value!,
+        };
+    }
+
+    public bool IsAnonymousFunctionDefinition(int index)
+    {
+        var expressions = _expressions;
+        return index < expressions.Length && (expressions[index] as JintExpression)?._expression.IsAnonymousFunctionDefinition() == true;
+    }
+
+    private static bool CanSpread(Node? e)
+    {
+        if (e is null)
+        {
+            return false;
+        }
+
+        return e.Type == NodeType.SpreadElement || e is AssignmentExpression { Right.Type: NodeType.SpreadElement };
+    }
+
+    internal JsValue[] DefaultSuperCallArgumentListEvaluation(EvaluationContext context)
+    {
+        // This branch behaves similarly to constructor(...args) { super(...args); }.
+        // The most notable distinction is that while the aforementioned ECMAScript source text observably calls
+        // the @@iterator method on %Array.prototype%, this function does not.
+
+        var spreadExpression = (JintSpreadExpression) _expressions[0]!;
+        var array = (JsArray) spreadExpression._argument.GetValue(context);
+        var length = array.GetLength();
+        var args = new List<JsValue>((int) length);
+        for (uint j = 0; j < length; ++j)
+        {
+            array.TryGetValue(j, out var value);
+            args.Add(value);
+        }
+
+        return args.ToArray();
+    }
+
+    internal void BuildArgumentsWithSpreads(EvaluationContext context, List<JsValue> target)
+    {
+        foreach (var expression in _expressions)
+        {
+            if (expression is JintSpreadExpression jse)
+            {
+                jse.GetValueAndCheckIterator(context, out var objectInstance, out var iterator);
+                // optimize for array unless someone has touched the iterator
+                if (objectInstance is JsArray { HasOriginalIterator: true } ai)
+                {
+                    var length = ai.GetLength();
+                    for (uint j = 0; j < length; ++j)
+                    {
+                        ai.TryGetValue(j, out var value);
+                        target.Add(value);
+                    }
+                }
+                else
+                {
+                    var protocol = new ArraySpreadProtocol(context.Engine, target, iterator!);
+                    protocol.Execute();
+                }
+            }
+            else
+            {
+                target.Add(GetValue(context, expression)!);
+            }
+        }
+    }
+
+    private sealed class ArraySpreadProtocol : IteratorProtocol
+    {
+        private readonly List<JsValue> _instance;
+
+        public ArraySpreadProtocol(
+            Engine engine,
+            List<JsValue> instance,
+            IteratorInstance iterator) : base(engine, iterator, 0)
+        {
+            _instance = instance;
+        }
+
+        protected override void ProcessItem(JsValue[] arguments, JsValue currentValue)
+        {
+            _instance.Add(currentValue);
+        }
+    }
+}

+ 16 - 82
Jint/Runtime/Interpreter/Expressions/JintArrayExpression.cs

@@ -5,8 +5,7 @@ namespace Jint.Runtime.Interpreter.Expressions;
 
 internal sealed class JintArrayExpression : JintExpression
 {
-    private JintExpression?[] _expressions = Array.Empty<JintExpression?>();
-    private bool _hasSpreads;
+    private readonly ExpressionCache _arguments = new();
     private bool _initialized;
 
     private JintArrayExpression(ArrayExpression expression) : base(expression)
@@ -20,20 +19,9 @@ internal sealed class JintArrayExpression : JintExpression
             : new JintArrayExpression(expression);
     }
 
-    private void Initialize()
+    private void Initialize(EvaluationContext context)
     {
-        ref readonly var elements = ref ((ArrayExpression) _expression).Elements;
-        var expressions = _expressions = new JintExpression[((ArrayExpression) _expression).Elements.Count];
-        for (var n = 0; n < expressions.Length; n++)
-        {
-            var expr = elements[n];
-            if (expr != null)
-            {
-                var expression = Build(expr);
-                expressions[n] = expression;
-                _hasSpreads |= expr.Type == NodeType.SpreadElement;
-            }
-        }
+        _arguments.Initialize(context, ((ArrayExpression) _expression).Elements.AsSpan()!);
 
         // we get called from nested spread expansion in call
         _initialized = true;
@@ -43,82 +31,28 @@ internal sealed class JintArrayExpression : JintExpression
     {
         if (!_initialized)
         {
-            Initialize();
+            Initialize(context);
             _initialized = true;
         }
 
+        var expressions = ((ArrayExpression) _expression).Elements.AsSpan();
         var engine = context.Engine;
-        var a = engine.Realm.Intrinsics.Array.ArrayCreate(_hasSpreads ? 0 : (uint) _expressions.Length);
-
-        uint arrayIndexCounter = 0;
-        foreach (var expr in _expressions)
-        {
-            if (expr == null)
-            {
-                a.SetIndexValue(arrayIndexCounter++, null, updateLength: false);
-            }
-            else if (_hasSpreads && expr is JintSpreadExpression jse)
-            {
-                jse.GetValueAndCheckIterator(context, out var objectInstance, out var iterator);
-                // optimize for array
-                if (objectInstance is JsArray ai)
-                {
-                    var length = ai.GetLength();
-                    var newLength = arrayIndexCounter + length;
-                    a.EnsureCapacity(newLength);
-                    a.CopyValues(ai, sourceStartIndex: 0, targetStartIndex: arrayIndexCounter, length);
-                    arrayIndexCounter += length;
-                    a.SetLength(newLength);
-                }
-                else
-                {
-                    var protocol = new ArraySpreadProtocol(engine, a, iterator!, arrayIndexCounter);
-                    protocol.Execute();
-                    arrayIndexCounter += protocol._addedCount;
-                }
-            }
-            else
-            {
-                var value = expr.GetValue(context);
-                a.SetIndexValue(arrayIndexCounter++, value, updateLength: false);
-            }
-        }
-
-        if (_hasSpreads)
+        if (!_arguments.HasSpreads)
         {
-            a.SetLength(arrayIndexCounter);
+            var values = new JsValue[expressions.Length];
+            _arguments.BuildArguments(context, values);
+            return new JsArray(engine, values);
         }
 
-        return a;
-    }
-
-    private sealed class ArraySpreadProtocol : IteratorProtocol
-    {
-        private readonly JsArray _instance;
-        private long _index;
-        internal uint _addedCount;
-
-        public ArraySpreadProtocol(
-            Engine engine,
-            JsArray instance,
-            IteratorInstance iterator,
-            long startIndex) : base(engine, iterator, 0)
-        {
-            _instance = instance;
-            _index = startIndex - 1;
-        }
-
-        protected override void ProcessItem(JsValue[] arguments, JsValue currentValue)
-        {
-            _index++;
-            _addedCount++;
-            _instance.SetIndexValue((uint) _index, currentValue, updateLength: false);
-        }
+        var array = new List<JsValue>();
+        _arguments.BuildArgumentsWithSpreads(context, array);
+        return new JsArray(engine, array.ToArray());
     }
 
     internal sealed class JintEmptyArrayExpression : JintExpression
     {
-        public static JintEmptyArrayExpression Instance = new(new ArrayExpression(NodeList.From(Array.Empty<Expression?>())));
+        public static JintEmptyArrayExpression Instance =
+            new(new ArrayExpression(NodeList.From(Array.Empty<Expression?>())));
 
         private JintEmptyArrayExpression(Expression expression) : base(expression)
         {
@@ -126,12 +60,12 @@ internal sealed class JintArrayExpression : JintExpression
 
         protected override object EvaluateInternal(EvaluationContext context)
         {
-            return new JsArray(context.Engine, Array.Empty<JsValue>());
+            return new JsArray(context.Engine, []);
         }
 
         public override JsValue GetValue(EvaluationContext context)
         {
-            return new JsArray(context.Engine, Array.Empty<JsValue>());
+            return new JsArray(context.Engine, []);
         }
     }
 }

+ 23 - 108
Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs

@@ -11,11 +11,8 @@ namespace Jint.Runtime.Interpreter.Expressions;
 
 internal sealed class JintCallExpression : JintExpression
 {
-    private CachedArgumentsHolder _cachedArguments = null!;
-    private bool _cached;
-
+    private readonly ExpressionCache _arguments = new();
     private JintExpression _calleeExpression = null!;
-    private bool _hasSpreads;
     private bool _initialized;
 
     public JintCallExpression(CallExpression expression) : base(expression)
@@ -25,55 +22,8 @@ internal sealed class JintCallExpression : JintExpression
     private void Initialize(EvaluationContext context)
     {
         var expression = (CallExpression) _expression;
-        ref readonly var expressionArguments = ref expression.Arguments;
-
+        _arguments.Initialize(context, expression.Arguments.AsSpan());
         _calleeExpression = Build(expression.Callee);
-        var cachedArgumentsHolder = new CachedArgumentsHolder
-        {
-            JintArguments = new JintExpression[expressionArguments.Count]
-        };
-
-        static bool CanSpread(Node? e)
-        {
-            if (e is null)
-            {
-                return false;
-            }
-
-            return e.Type == NodeType.SpreadElement || e is AssignmentExpression { Right.Type: NodeType.SpreadElement };
-        }
-
-        var cacheable = true;
-        for (var i = 0; i < expressionArguments.Count; i++)
-        {
-            var expressionArgument = expressionArguments[i];
-            cachedArgumentsHolder.JintArguments[i] = Build(expressionArgument);
-            cacheable &= expressionArgument.Type == NodeType.Literal;
-            _hasSpreads |= CanSpread(expressionArgument);
-            if (expressionArgument is ArrayExpression ae)
-            {
-                ref readonly var elements = ref ae.Elements;
-                for (var elementIndex = 0; elementIndex < elements.Count; elementIndex++)
-                {
-                    _hasSpreads |= CanSpread(elements[elementIndex]);
-                }
-            }
-        }
-
-        if (cacheable)
-        {
-            _cached = true;
-            var arguments = Array.Empty<JsValue>();
-            if (cachedArgumentsHolder.JintArguments.Length > 0)
-            {
-                arguments = new JsValue[cachedArgumentsHolder.JintArguments.Length];
-                BuildArguments(context, cachedArgumentsHolder.JintArguments, arguments);
-            }
-
-            cachedArgumentsHolder.CachedArguments = arguments;
-        }
-
-        _cachedArguments = cachedArgumentsHolder;
     }
 
     protected override object EvaluateInternal(EvaluationContext context)
@@ -154,7 +104,7 @@ internal sealed class JintCallExpression : JintExpression
             thisObject = JsValue.Undefined;
         }
 
-        var arguments = ArgumentListEvaluation(context);
+        var arguments = this._arguments.ArgumentListEvaluation(context, out var rented);
 
         if (!func.IsObject() && !engine._referenceResolver.TryGetCallable(engine, reference, out func))
         {
@@ -205,7 +155,7 @@ internal sealed class JintCallExpression : JintExpression
             result = callable.Call(thisObject, arguments);
         }
 
-        if (!_cached && arguments.Length > 0)
+        if (rented)
         {
             engine._jsValueArrayPool.ReturnArray(arguments);
         }
@@ -234,7 +184,8 @@ internal sealed class JintCallExpression : JintExpression
 
     private JsValue HandleEval(EvaluationContext context, JsValue func, Engine engine, Reference referenceRecord)
     {
-        var argList = ArgumentListEvaluation(context);
+        var argList = _arguments.ArgumentListEvaluation(context, out var rented);
+
         if (argList.Length == 0)
         {
             return JsValue.Undefined;
@@ -246,7 +197,13 @@ internal sealed class JintCallExpression : JintExpression
         var evalRealm = evalFunctionInstance._realm;
         var direct = !_expression.IsOptional();
         var value = evalFunctionInstance.PerformEval(evalArg, evalRealm, strictCaller, direct);
+
+        if (rented)
+        {
+            engine._jsValueArrayPool.ReturnArray(argList);
+        }
         engine._referencePool.Return(referenceRecord);
+
         return value;
     }
 
@@ -261,8 +218,13 @@ internal sealed class JintCallExpression : JintExpression
             ExceptionHelper.ThrowTypeError(engine.Realm, "Not a constructor");
         }
 
+        var rented = false;
         var defaultSuperCall = ReferenceEquals(_expression, ClassDefinition._defaultSuperCall);
-        var argList = defaultSuperCall ? DefaultSuperCallArgumentListEvaluation(context) : ArgumentListEvaluation(context);
+
+        var argList = defaultSuperCall
+            ? _arguments.DefaultSuperCallArgumentListEvaluation(context)
+            : _arguments.ArgumentListEvaluation(context, out rented);
+
         var result = ((IConstructor) func).Construct(argList, newTarget);
 
         var thisER = (FunctionEnvironment) engine.ExecutionContext.GetThisEnvironment();
@@ -271,6 +233,11 @@ internal sealed class JintCallExpression : JintExpression
 
         result.InitializeInstanceElements((ScriptFunction) F);
 
+        if (rented)
+        {
+            engine._jsValueArrayPool.ReturnArray(argList);
+        }
+
         return result;
     }
 
@@ -293,56 +260,4 @@ internal sealed class JintCallExpression : JintExpression
         // TODO tail calls
         return false;
     }
-
-    private JsValue[] ArgumentListEvaluation(EvaluationContext context)
-    {
-        var cachedArguments = _cachedArguments;
-        var arguments = Array.Empty<JsValue>();
-        if (_cached)
-        {
-            arguments = cachedArguments.CachedArguments;
-        }
-        else
-        {
-            if (cachedArguments.JintArguments.Length > 0)
-            {
-                if (_hasSpreads)
-                {
-                    arguments = BuildArgumentsWithSpreads(context, cachedArguments.JintArguments);
-                }
-                else
-                {
-                    arguments = context.Engine._jsValueArrayPool.RentArray(cachedArguments.JintArguments.Length);
-                    BuildArguments(context, cachedArguments.JintArguments, arguments);
-                }
-            }
-        }
-
-        return arguments;
-    }
-
-    private JsValue[] DefaultSuperCallArgumentListEvaluation(EvaluationContext context)
-    {
-        // This branch behaves similarly to constructor(...args) { super(...args); }.
-        // The most notable distinction is that while the aforementioned ECMAScript source text observably calls
-        // the @@iterator method on %Array.prototype%, this function does not.
-
-        var spreadExpression = (JintSpreadExpression) _cachedArguments.JintArguments[0];
-        var array = (JsArray) spreadExpression._argument.GetValue(context);
-        var length = array.GetLength();
-        var args = new List<JsValue>((int) length);
-        for (uint j = 0; j < length; ++j)
-        {
-            array.TryGetValue(j, out var value);
-            args.Add(value);
-        }
-
-        return args.ToArray();
-    }
-
-    private sealed class CachedArgumentsHolder
-    {
-        internal JintExpression[] JintArguments = Array.Empty<JintExpression>();
-        internal JsValue[] CachedArguments = Array.Empty<JsValue>();
-    }
 }

+ 0 - 60
Jint/Runtime/Interpreter/Expressions/JintExpression.cs

@@ -404,66 +404,6 @@ internal abstract class JintExpression
         return string.CompareOrdinal(TypeConverter.ToString(x), TypeConverter.ToString(y)) < 0 ? JsBoolean.True : JsBoolean.False;
     }
 
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    protected static void BuildArguments(EvaluationContext context, JintExpression[] jintExpressions, JsValue[] targetArray)
-    {
-        for (uint i = 0; i < (uint) jintExpressions.Length; i++)
-        {
-            targetArray[i] = jintExpressions[i].GetValue(context).Clone();
-        }
-    }
-
-    protected static JsValue[] BuildArgumentsWithSpreads(EvaluationContext context, JintExpression[] jintExpressions)
-    {
-        var args = new List<JsValue>(jintExpressions.Length);
-        foreach (var jintExpression in jintExpressions)
-        {
-            if (jintExpression is JintSpreadExpression jse)
-            {
-                jse.GetValueAndCheckIterator(context, out var objectInstance, out var iterator);
-                // optimize for array unless someone has touched the iterator
-                if (objectInstance is JsArray { HasOriginalIterator: true } ai)
-                {
-                    var length = ai.GetLength();
-                    for (uint j = 0; j < length; ++j)
-                    {
-                        ai.TryGetValue(j, out var value);
-                        args.Add(value);
-                    }
-                }
-                else
-                {
-                    var protocol = new ArraySpreadProtocol(context.Engine, args, iterator!);
-                    protocol.Execute();
-                }
-            }
-            else
-            {
-                args.Add(jintExpression.GetValue(context).Clone());
-            }
-        }
-
-        return args.ToArray();
-    }
-
-    private sealed class ArraySpreadProtocol : IteratorProtocol
-    {
-        private readonly List<JsValue> _instance;
-
-        public ArraySpreadProtocol(
-            Engine engine,
-            List<JsValue> instance,
-            IteratorInstance iterator) : base(engine, iterator, 0)
-        {
-            _instance = instance;
-        }
-
-        protected override void ProcessItem(JsValue[] arguments, JsValue currentValue)
-        {
-            _instance.Add(currentValue);
-        }
-    }
-
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     protected static bool AreIntegerOperands(JsValue left, JsValue right)
     {

+ 11 - 36
Jint/Runtime/Interpreter/Expressions/JintNewExpression.cs

@@ -1,42 +1,27 @@
-using Jint.Native;
-
 namespace Jint.Runtime.Interpreter.Expressions;
 
 internal sealed class JintNewExpression : JintExpression
 {
+    private readonly ExpressionCache _arguments = new();
     private JintExpression _calleeExpression = null!;
-    private JintExpression[] _jintArguments = Array.Empty<JintExpression>();
-    private bool _hasSpreads;
     private bool _initialized;
 
     public JintNewExpression(NewExpression expression) : base(expression)
     {
     }
 
-    private void Initialize()
+    private void Initialize(EvaluationContext context)
     {
         var expression = (NewExpression) _expression;
+        _arguments.Initialize(context, expression.Arguments.AsSpan());
         _calleeExpression = Build(expression.Callee);
-
-        if (expression.Arguments.Count <= 0)
-        {
-            return;
-        }
-
-        _jintArguments = new JintExpression[expression.Arguments.Count];
-        for (var i = 0; i < _jintArguments.Length; i++)
-        {
-            var argument = expression.Arguments[i];
-            _jintArguments[i] = Build(argument);
-            _hasSpreads |= argument.Type == NodeType.SpreadElement;
-        }
     }
 
     protected override object EvaluateInternal(EvaluationContext context)
     {
         if (!_initialized)
         {
-            Initialize();
+            Initialize(context);
             _initialized = true;
         }
 
@@ -45,20 +30,7 @@ internal sealed class JintNewExpression : JintExpression
         // todo: optimize by defining a common abstract class or interface
         var jsValue = _calleeExpression.GetValue(context);
 
-        JsValue[] arguments;
-        if (_jintArguments.Length == 0)
-        {
-            arguments = Array.Empty<JsValue>();
-        }
-        else if (_hasSpreads)
-        {
-            arguments = BuildArgumentsWithSpreads(context, _jintArguments);
-        }
-        else
-        {
-            arguments = engine._jsValueArrayPool.RentArray(_jintArguments.Length);
-            BuildArguments(context, _jintArguments, arguments);
-        }
+        var arguments = _arguments.ArgumentListEvaluation(context, out var rented);
 
         // Reset the location to the "new" keyword so that if an Error object is
         // constructed below, the stack trace will capture the correct location.
@@ -66,14 +38,17 @@ internal sealed class JintNewExpression : JintExpression
 
         if (!jsValue.IsConstructor)
         {
-            ExceptionHelper.ThrowTypeError(engine.Realm, _calleeExpression.SourceText + " is not a constructor");
+            ExceptionHelper.ThrowTypeError(engine.Realm, $"{_calleeExpression.SourceText} is not a constructor");
         }
 
         // construct the new instance using the Function's constructor method
         var instance = engine.Construct(jsValue, arguments, jsValue, _calleeExpression);
 
-        engine._jsValueArrayPool.ReturnArray(arguments);
+        if (rented)
+        {
+            engine._jsValueArrayPool.ReturnArray(arguments);
+        }
 
         return instance;
     }
-}
+}

+ 15 - 21
Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs

@@ -11,8 +11,8 @@ namespace Jint.Runtime.Interpreter.Expressions;
 /// </summary>
 internal sealed class JintObjectExpression : JintExpression
 {
-    private JintExpression[] _valueExpressions = Array.Empty<JintExpression>();
-    private ObjectProperty?[] _properties = Array.Empty<ObjectProperty>();
+    private readonly ExpressionCache _valueExpressions = new();
+    private ObjectProperty?[] _properties = [];
 
     // check if we can do a shortcut when all are object properties
     // and don't require duplicate checking
@@ -63,13 +63,13 @@ internal sealed class JintObjectExpression : JintExpression
             : new JintObjectExpression(expression);
     }
 
-    private void Initialize()
+    private void Initialize(EvaluationContext context)
     {
         _canBuildFast = true;
         var expression = (ObjectExpression) _expression;
         ref readonly var properties = ref expression.Properties;
 
-        _valueExpressions = new JintExpression[properties.Count];
+        var valueExpressions = new Expression[properties.Count];
         _properties = new ObjectProperty[properties.Count];
 
         for (var i = 0; i < _properties.Length; i++)
@@ -94,7 +94,7 @@ internal sealed class JintObjectExpression : JintExpression
                 if (p.Kind is PropertyKind.Init)
                 {
                     var propertyValue = p.Value;
-                    _valueExpressions[i] = Build((Expression) propertyValue);
+                    valueExpressions[i] = (Expression) propertyValue;
                     _canBuildFast &= !propertyValue.IsFunctionDefinition();
                 }
                 else
@@ -106,7 +106,7 @@ internal sealed class JintObjectExpression : JintExpression
             {
                 _canBuildFast = false;
                 _properties[i] = null;
-                _valueExpressions[i] = Build(spreadElement.Argument);
+                valueExpressions[i] = spreadElement.Argument;
             }
             else
             {
@@ -115,13 +115,15 @@ internal sealed class JintObjectExpression : JintExpression
 
             _canBuildFast &= propName != null;
         }
+
+        _valueExpressions.Initialize(context, valueExpressions.AsSpan());
     }
 
     protected override object EvaluateInternal(EvaluationContext context)
     {
         if (!_initialized)
         {
-            Initialize();
+            Initialize(context);
             _initialized = true;
         }
 
@@ -140,8 +142,7 @@ internal sealed class JintObjectExpression : JintExpression
         for (var i = 0; i < _properties.Length; i++)
         {
             var objectProperty = _properties[i];
-            var valueExpression = _valueExpressions[i];
-            var propValue = valueExpression.GetValue(context).Clone();
+            var propValue = _valueExpressions.GetValue(context, i);
             properties[objectProperty!._key!] = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
         }
 
@@ -164,9 +165,9 @@ internal sealed class JintObjectExpression : JintExpression
             if (objectProperty is null)
             {
                 // spread
-                if (_valueExpressions[i].GetValue(context) is ObjectInstance source)
+                if (_valueExpressions.GetValue(context, i) is ObjectInstance source)
                 {
-                    source.CopyDataProperties(obj, null);
+                    source.CopyDataProperties(obj, excludedItems: null);
                 }
 
                 continue;
@@ -194,14 +195,7 @@ internal sealed class JintObjectExpression : JintExpression
 
             if (property.Kind == PropertyKind.Init)
             {
-                var expr = _valueExpressions[i];
-                var completion = expr.GetValue(context);
-                if (context.IsAbrupt())
-                {
-                    return completion;
-                }
-
-                var propValue = completion.Clone();
+                var propValue = _valueExpressions.GetValue(context, i)!;
                 if (string.Equals(objectProperty._key, "__proto__", StringComparison.Ordinal) && !objectProperty._value.Computed && !objectProperty._value.Shorthand)
                 {
                     if (propValue.IsObject() || propValue.IsNull())
@@ -211,7 +205,7 @@ internal sealed class JintObjectExpression : JintExpression
                     continue;
                 }
 
-                if (expr._expression.IsAnonymousFunctionDefinition())
+                if (_valueExpressions.IsAnonymousFunctionDefinition(i))
                 {
                     var closure = (Function) propValue;
                     closure.SetFunctionName(propName);
@@ -262,4 +256,4 @@ internal sealed class JintObjectExpression : JintExpression
             return new JsObject(context.Engine);
         }
     }
-}
+}