123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- using System.Diagnostics.CodeAnalysis;
- using System.Runtime.CompilerServices;
- using Esprima.Ast;
- using Jint.Native;
- using Jint.Native.Function;
- using Jint.Native.Object;
- using Jint.Runtime.Environments;
- using Jint.Runtime.References;
- namespace Jint.Runtime.Interpreter.Expressions
- {
- internal sealed class JintCallExpression : JintExpression
- {
- private CachedArgumentsHolder _cachedArguments = null!;
- private bool _cached;
- private JintExpression _calleeExpression = null!;
- private bool _hasSpreads;
- public JintCallExpression(CallExpression expression) : base(expression)
- {
- _initialized = false;
- }
- protected override void Initialize(EvaluationContext context)
- {
- var engine = context.Engine;
- var expression = (CallExpression) _expression;
- ref readonly var expressionArguments = ref expression.Arguments;
- _calleeExpression = Build(engine, expression.Callee);
- var cachedArgumentsHolder = new CachedArgumentsHolder
- {
- JintArguments = new JintExpression[expressionArguments.Count]
- };
- static bool CanSpread(Node? e)
- {
- return e?.Type == Nodes.SpreadElement || e is AssignmentExpression { Right.Type: Nodes.SpreadElement };
- }
- var cacheable = true;
- for (var i = 0; i < expressionArguments.Count; i++)
- {
- var expressionArgument = expressionArguments[i];
- cachedArgumentsHolder.JintArguments[i] = Build(engine, expressionArgument);
- cacheable &= expressionArgument.Type == Nodes.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 ExpressionResult EvaluateInternal(EvaluationContext context)
- {
- if (_calleeExpression._expression.Type == Nodes.Super)
- {
- return NormalCompletion(SuperCall(context));
- }
- // https://tc39.es/ecma262/#sec-function-calls
- var reference = _calleeExpression.Evaluate(context).Value;
- if (ReferenceEquals(reference, Undefined.Instance))
- {
- return NormalCompletion(Undefined.Instance);
- }
- var engine = context.Engine;
- var func = engine.GetValue(reference, false);
- if (func.IsNullOrUndefined() && _expression.IsOptional())
- {
- return NormalCompletion(Undefined.Instance);
- }
- var referenceRecord = reference as Reference;
- if (ReferenceEquals(func, engine.Realm.Intrinsics.Eval)
- && referenceRecord != null
- && !referenceRecord.IsPropertyReference()
- && referenceRecord.GetReferencedName() == CommonProperties.Eval)
- {
- return HandleEval(context, func, engine, referenceRecord);
- }
- var thisCall = (CallExpression) _expression;
- var tailCall = IsInTailPosition(thisCall);
- // https://tc39.es/ecma262/#sec-evaluatecall
- JsValue thisObject;
- if (referenceRecord is not null)
- {
- if (referenceRecord.IsPropertyReference())
- {
- thisObject = referenceRecord.GetThisValue();
- }
- else
- {
- var baseValue = referenceRecord.GetBase();
- // deviation from the spec to support null-propagation helper
- if (baseValue.IsNullOrUndefined()
- && engine._referenceResolver.TryUnresolvableReference(engine, referenceRecord, out var value))
- {
- thisObject = value;
- }
- else
- {
- var refEnv = (EnvironmentRecord) baseValue;
- thisObject = refEnv.WithBaseObject();
- }
- }
- }
- else
- {
- thisObject = Undefined.Instance;
- }
- var arguments = ArgumentListEvaluation(context);
- if (!func.IsObject() && !engine._referenceResolver.TryGetCallable(engine, reference, out func))
- {
- ThrowMemberIsNotFunction(referenceRecord, reference, engine);
- }
- var callable = func as ICallable;
- if (callable is null)
- {
- ThrowReferenceNotFunction(referenceRecord, reference, engine);
- }
- if (tailCall)
- {
- // TODO tail call
- // PrepareForTailCall();
- }
- // ensure logic is in sync between Call, Construct and JintCallExpression!
- JsValue result;
- if (callable is FunctionInstance functionInstance)
- {
- var callStack = engine.CallStack;
- var recursionDepth = callStack.Push(functionInstance, _calleeExpression, engine.ExecutionContext);
- if (recursionDepth > engine.Options.Constraints.MaxRecursionDepth)
- {
- // automatically pops the current element as it was never reached
- ExceptionHelper.ThrowRecursionDepthOverflowException(callStack);
- }
- try
- {
- result = functionInstance.Call(thisObject, arguments);
- }
- finally
- {
- // if call stack was reset due to recursive call to engine or similar, we might not have it anymore
- if (callStack.Count > 0)
- {
- callStack.Pop();
- }
- }
- }
- else
- {
- result = callable.Call(thisObject, arguments);
- }
- if (!_cached && arguments.Length > 0)
- {
- engine._jsValueArrayPool.ReturnArray(arguments);
- }
- engine._referencePool.Return(referenceRecord);
- return NormalCompletion(result);
- }
- [DoesNotReturn]
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowReferenceNotFunction(Reference? referenceRecord1, object reference, Engine engine)
- {
- var message = $"{referenceRecord1?.GetReferencedName() ?? reference} is not a function";
- ExceptionHelper.ThrowTypeError(engine.Realm, message);
- }
- [DoesNotReturn]
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowMemberIsNotFunction(Reference? referenceRecord1, object reference, Engine engine)
- {
- var message = referenceRecord1 == null
- ? reference + " is not a function"
- : $"Property '{referenceRecord1.GetReferencedName()}' of object is not a function";
- ExceptionHelper.ThrowTypeError(engine.Realm, message);
- }
- private ExpressionResult HandleEval(EvaluationContext context, JsValue func, Engine engine, Reference referenceRecord)
- {
- var argList = ArgumentListEvaluation(context);
- if (argList.Length == 0)
- {
- return NormalCompletion(Undefined.Instance);
- }
- var evalFunctionInstance = (EvalFunctionInstance) func;
- var evalArg = argList[0];
- var strictCaller = StrictModeScope.IsStrictModeCode;
- var evalRealm = evalFunctionInstance._realm;
- var direct = !_expression.IsOptional();
- var value = evalFunctionInstance.PerformEval(evalArg, evalRealm, strictCaller, direct);
- engine._referencePool.Return(referenceRecord);
- return NormalCompletion(value);
- }
- private JsValue SuperCall(EvaluationContext context)
- {
- var engine = context.Engine;
- var thisEnvironment = (FunctionEnvironmentRecord) engine.ExecutionContext.GetThisEnvironment();
- var newTarget = engine.GetNewTarget(thisEnvironment);
- var func = GetSuperConstructor(thisEnvironment);
- if (func is null || !func.IsConstructor)
- {
- ExceptionHelper.ThrowTypeError(engine.Realm, "Not a constructor");
- }
- var argList = ArgumentListEvaluation(context);
- var result = ((IConstructor) func).Construct(argList, newTarget);
- var thisER = (FunctionEnvironmentRecord) engine.ExecutionContext.GetThisEnvironment();
- return thisER.BindThisValue(result);
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-getsuperconstructor
- /// </summary>
- private static ObjectInstance? GetSuperConstructor(FunctionEnvironmentRecord thisEnvironment)
- {
- var envRec = thisEnvironment;
- var activeFunction = envRec._functionObject;
- var superConstructor = activeFunction.GetPrototypeOf();
- return superConstructor;
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-isintailposition
- /// </summary>
- private static bool IsInTailPosition(CallExpression call)
- {
- // 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 sealed class CachedArgumentsHolder
- {
- internal JintExpression[] JintArguments = Array.Empty<JintExpression>();
- internal JsValue[] CachedArguments = Array.Empty<JsValue>();
- }
- }
- }
|