|
@@ -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>();
|
|
|
- }
|
|
|
}
|