|
@@ -1,3 +1,5 @@
|
|
|
|
+using System.Diagnostics.CodeAnalysis;
|
|
|
|
+using System.Runtime.CompilerServices;
|
|
using Esprima.Ast;
|
|
using Esprima.Ast;
|
|
using Jint.Native;
|
|
using Jint.Native;
|
|
using Jint.Native.Function;
|
|
using Jint.Native.Function;
|
|
@@ -69,50 +71,18 @@ namespace Jint.Runtime.Interpreter.Expressions
|
|
|
|
|
|
protected override ExpressionResult EvaluateInternal(EvaluationContext context)
|
|
protected override ExpressionResult EvaluateInternal(EvaluationContext context)
|
|
{
|
|
{
|
|
- return NormalCompletion(_calleeExpression is JintSuperExpression
|
|
|
|
- ? SuperCall(context)
|
|
|
|
- : Call(context)
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- 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)
|
|
|
|
|
|
+ if (_calleeExpression._expression.Type == Nodes.Super)
|
|
{
|
|
{
|
|
- ExceptionHelper.ThrowTypeError(engine.Realm, "Not a constructor");
|
|
|
|
|
|
+ return NormalCompletion(SuperCall(context));
|
|
}
|
|
}
|
|
|
|
|
|
- var argList = ArgumentListEvaluation(context);
|
|
|
|
- var result = ((IConstructor) func).Construct(argList, newTarget ?? JsValue.Undefined);
|
|
|
|
- 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;
|
|
|
|
- }
|
|
|
|
|
|
+ // https://tc39.es/ecma262/#sec-function-calls
|
|
|
|
|
|
- /// <summary>
|
|
|
|
- /// https://tc39.es/ecma262/#sec-function-calls
|
|
|
|
- /// </summary>
|
|
|
|
- private JsValue Call(EvaluationContext context)
|
|
|
|
- {
|
|
|
|
var reference = _calleeExpression.Evaluate(context).Value;
|
|
var reference = _calleeExpression.Evaluate(context).Value;
|
|
|
|
|
|
if (ReferenceEquals(reference, Undefined.Instance))
|
|
if (ReferenceEquals(reference, Undefined.Instance))
|
|
{
|
|
{
|
|
- return Undefined.Instance;
|
|
|
|
|
|
+ return NormalCompletion(Undefined.Instance);
|
|
}
|
|
}
|
|
|
|
|
|
var engine = context.Engine;
|
|
var engine = context.Engine;
|
|
@@ -120,48 +90,29 @@ namespace Jint.Runtime.Interpreter.Expressions
|
|
|
|
|
|
if (func.IsNullOrUndefined() && _expression.IsOptional())
|
|
if (func.IsNullOrUndefined() && _expression.IsOptional())
|
|
{
|
|
{
|
|
- return Undefined.Instance;
|
|
|
|
|
|
+ return NormalCompletion(Undefined.Instance);
|
|
}
|
|
}
|
|
|
|
|
|
- if (reference is Reference referenceRecord
|
|
|
|
|
|
+ var referenceRecord = reference as Reference;
|
|
|
|
+ if (referenceRecord != null
|
|
&& !referenceRecord.IsPropertyReference()
|
|
&& !referenceRecord.IsPropertyReference()
|
|
&& referenceRecord.GetReferencedName() == CommonProperties.Eval
|
|
&& referenceRecord.GetReferencedName() == CommonProperties.Eval
|
|
&& ReferenceEquals(func, engine.Realm.Intrinsics.Eval))
|
|
&& ReferenceEquals(func, engine.Realm.Intrinsics.Eval))
|
|
{
|
|
{
|
|
- var argList = ArgumentListEvaluation(context);
|
|
|
|
- if (argList.Length == 0)
|
|
|
|
- {
|
|
|
|
- return 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 value;
|
|
|
|
|
|
+ return HandleEval(context, func, engine, referenceRecord);
|
|
}
|
|
}
|
|
|
|
|
|
var thisCall = (CallExpression) _expression;
|
|
var thisCall = (CallExpression) _expression;
|
|
var tailCall = IsInTailPosition(thisCall);
|
|
var tailCall = IsInTailPosition(thisCall);
|
|
- return EvaluateCall(context, func, reference, thisCall.Arguments, tailCall);
|
|
|
|
- }
|
|
|
|
|
|
|
|
- /// <summary>
|
|
|
|
- /// https://tc39.es/ecma262/#sec-evaluatecall
|
|
|
|
- /// </summary>
|
|
|
|
- private JsValue EvaluateCall(EvaluationContext context, JsValue func, object reference, in NodeList<Expression> arguments, bool tailPosition)
|
|
|
|
- {
|
|
|
|
- JsValue thisValue;
|
|
|
|
- var referenceRecord = reference as Reference;
|
|
|
|
- var engine = context.Engine;
|
|
|
|
|
|
+ // https://tc39.es/ecma262/#sec-evaluatecall
|
|
|
|
+
|
|
|
|
+ JsValue thisObject;
|
|
if (referenceRecord is not null)
|
|
if (referenceRecord is not null)
|
|
{
|
|
{
|
|
if (referenceRecord.IsPropertyReference())
|
|
if (referenceRecord.IsPropertyReference())
|
|
{
|
|
{
|
|
- thisValue = referenceRecord.GetThisValue();
|
|
|
|
|
|
+ thisObject = referenceRecord.GetThisValue();
|
|
}
|
|
}
|
|
else
|
|
else
|
|
{
|
|
{
|
|
@@ -171,52 +122,142 @@ namespace Jint.Runtime.Interpreter.Expressions
|
|
if (baseValue.IsNullOrUndefined()
|
|
if (baseValue.IsNullOrUndefined()
|
|
&& engine._referenceResolver.TryUnresolvableReference(engine, referenceRecord, out var value))
|
|
&& engine._referenceResolver.TryUnresolvableReference(engine, referenceRecord, out var value))
|
|
{
|
|
{
|
|
- thisValue = value;
|
|
|
|
|
|
+ thisObject = value;
|
|
}
|
|
}
|
|
else
|
|
else
|
|
{
|
|
{
|
|
var refEnv = (EnvironmentRecord) baseValue;
|
|
var refEnv = (EnvironmentRecord) baseValue;
|
|
- thisValue = refEnv.WithBaseObject();
|
|
|
|
|
|
+ thisObject = refEnv.WithBaseObject();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
else
|
|
{
|
|
{
|
|
- thisValue = Undefined.Instance;
|
|
|
|
|
|
+ thisObject = Undefined.Instance;
|
|
}
|
|
}
|
|
|
|
|
|
- var argList = ArgumentListEvaluation(context);
|
|
|
|
|
|
+ var arguments = ArgumentListEvaluation(context);
|
|
|
|
|
|
if (!func.IsObject() && !engine._referenceResolver.TryGetCallable(engine, reference, out func))
|
|
if (!func.IsObject() && !engine._referenceResolver.TryGetCallable(engine, reference, out func))
|
|
{
|
|
{
|
|
- var message = referenceRecord == null
|
|
|
|
- ? reference + " is not a function"
|
|
|
|
- : $"Property '{referenceRecord.GetReferencedName()}' of object is not a function";
|
|
|
|
- ExceptionHelper.ThrowTypeError(engine.Realm, message);
|
|
|
|
|
|
+ ThrowMemberisNotFunction(referenceRecord, reference, engine);
|
|
}
|
|
}
|
|
|
|
|
|
var callable = func as ICallable;
|
|
var callable = func as ICallable;
|
|
if (callable is null)
|
|
if (callable is null)
|
|
{
|
|
{
|
|
- var message = $"{referenceRecord?.GetReferencedName() ?? reference} is not a function";
|
|
|
|
- ExceptionHelper.ThrowTypeError(engine.Realm, message);
|
|
|
|
|
|
+ ThrowReferenceNotFunction(referenceRecord, reference, engine);
|
|
}
|
|
}
|
|
|
|
|
|
- if (tailPosition)
|
|
|
|
|
|
+ if (tailCall)
|
|
{
|
|
{
|
|
// TODO tail call
|
|
// TODO tail call
|
|
// PrepareForTailCall();
|
|
// PrepareForTailCall();
|
|
}
|
|
}
|
|
|
|
|
|
- var result = engine.Call(callable, thisValue, argList, _calleeExpression);
|
|
|
|
|
|
+ // ensure logic is in sync between Call, Construct and JintCallExpression!
|
|
|
|
|
|
- if (!_cached && argList.Length > 0)
|
|
|
|
|
|
+ JsValue result;
|
|
|
|
+ if (callable is FunctionInstance functionInstance)
|
|
{
|
|
{
|
|
- engine._jsValueArrayPool.ReturnArray(argList);
|
|
|
|
|
|
+ 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);
|
|
engine._referencePool.Return(referenceRecord);
|
|
- return result;
|
|
|
|
|
|
+ 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 ?? JsValue.Undefined);
|
|
|
|
+ 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>
|
|
/// <summary>
|