2
0

JintCallExpression.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. using System.Diagnostics.CodeAnalysis;
  2. using System.Runtime.CompilerServices;
  3. using Jint.Native;
  4. using Jint.Native.Function;
  5. using Jint.Native.Object;
  6. using Jint.Runtime.CallStack;
  7. using Jint.Runtime.Environments;
  8. using Environment = Jint.Runtime.Environments.Environment;
  9. namespace Jint.Runtime.Interpreter.Expressions;
  10. internal sealed class JintCallExpression : JintExpression
  11. {
  12. private readonly ExpressionCache _arguments = new();
  13. private JintExpression _calleeExpression = null!;
  14. private bool _initialized;
  15. public JintCallExpression(CallExpression expression) : base(expression)
  16. {
  17. }
  18. private void Initialize(EvaluationContext context)
  19. {
  20. var expression = (CallExpression) _expression;
  21. _arguments.Initialize(context, expression.Arguments.AsSpan());
  22. _calleeExpression = Build(expression.Callee);
  23. }
  24. protected override object EvaluateInternal(EvaluationContext context)
  25. {
  26. if (!_initialized)
  27. {
  28. Initialize(context);
  29. _initialized = true;
  30. }
  31. if (!context.Engine._stackGuard.TryEnterOnCurrentStack())
  32. {
  33. return StackGuard.RunOnEmptyStack(EvaluateInternal, context);
  34. }
  35. if (_calleeExpression._expression.Type == NodeType.Super)
  36. {
  37. return SuperCall(context);
  38. }
  39. // https://tc39.es/ecma262/#sec-function-calls
  40. var reference = _calleeExpression.Evaluate(context);
  41. if (ReferenceEquals(reference, JsValue.Undefined))
  42. {
  43. return JsValue.Undefined;
  44. }
  45. var engine = context.Engine;
  46. var func = engine.GetValue(reference, false);
  47. if (func.IsNullOrUndefined() && _expression.IsOptional())
  48. {
  49. return JsValue.Undefined;
  50. }
  51. var referenceRecord = reference as Reference;
  52. if (ReferenceEquals(func, engine.Realm.Intrinsics.Eval)
  53. && referenceRecord != null
  54. && !referenceRecord.IsPropertyReference
  55. && CommonProperties.Eval.Equals(referenceRecord.ReferencedName))
  56. {
  57. return HandleEval(context, func, engine, referenceRecord);
  58. }
  59. var thisCall = (CallExpression) _expression;
  60. var tailCall = IsInTailPosition(thisCall);
  61. // https://tc39.es/ecma262/#sec-evaluatecall
  62. JsValue thisObject;
  63. if (referenceRecord is not null)
  64. {
  65. if (referenceRecord.IsPropertyReference)
  66. {
  67. thisObject = referenceRecord.ThisValue;
  68. }
  69. else
  70. {
  71. var baseValue = referenceRecord.Base;
  72. // deviation from the spec to support null-propagation helper
  73. if (baseValue.IsNullOrUndefined()
  74. && engine._referenceResolver.TryUnresolvableReference(engine, referenceRecord, out var value))
  75. {
  76. thisObject = value;
  77. }
  78. else
  79. {
  80. var refEnv = (Environment) baseValue;
  81. thisObject = refEnv.WithBaseObject();
  82. }
  83. }
  84. }
  85. else
  86. {
  87. thisObject = JsValue.Undefined;
  88. }
  89. var arguments = this._arguments.ArgumentListEvaluation(context, out var rented);
  90. if (!func.IsObject() && !engine._referenceResolver.TryGetCallable(engine, reference, out func))
  91. {
  92. ThrowMemberIsNotFunction(referenceRecord, reference, engine);
  93. }
  94. var callable = func as ICallable;
  95. if (callable is null)
  96. {
  97. ThrowReferenceNotFunction(referenceRecord, reference, engine);
  98. }
  99. if (tailCall)
  100. {
  101. // TODO tail call
  102. // PrepareForTailCall();
  103. }
  104. // ensure logic is in sync between Call, Construct and JintCallExpression!
  105. JsValue result;
  106. if (callable is Function functionInstance)
  107. {
  108. var callStack = engine.CallStack;
  109. var recursionDepth = callStack.Push(functionInstance, _calleeExpression, engine.ExecutionContext);
  110. if (recursionDepth > engine.Options.Constraints.MaxRecursionDepth)
  111. {
  112. // automatically pops the current element as it was never reached
  113. Throw.RecursionDepthOverflowException(callStack);
  114. }
  115. try
  116. {
  117. result = functionInstance.Call(thisObject, arguments);
  118. }
  119. finally
  120. {
  121. // if call stack was reset due to recursive call to engine or similar, we might not have it anymore
  122. if (callStack.Count > 0)
  123. {
  124. callStack.Pop();
  125. }
  126. }
  127. }
  128. else
  129. {
  130. result = callable.Call(thisObject, arguments);
  131. }
  132. if (rented)
  133. {
  134. engine._jsValueArrayPool.ReturnArray(arguments);
  135. }
  136. engine._referencePool.Return(referenceRecord);
  137. return result;
  138. }
  139. [DoesNotReturn]
  140. [MethodImpl(MethodImplOptions.NoInlining)]
  141. private static void ThrowReferenceNotFunction(Reference? referenceRecord1, object reference, Engine engine)
  142. {
  143. var message = $"{referenceRecord1?.ReferencedName ?? reference} is not a function";
  144. Throw.TypeError(engine.Realm, message);
  145. }
  146. [DoesNotReturn]
  147. [MethodImpl(MethodImplOptions.NoInlining)]
  148. private static void ThrowMemberIsNotFunction(Reference? referenceRecord1, object reference, Engine engine)
  149. {
  150. var message = referenceRecord1 == null
  151. ? reference + " is not a function"
  152. : $"Property '{referenceRecord1.ReferencedName}' of object is not a function";
  153. Throw.TypeError(engine.Realm, message);
  154. }
  155. private JsValue HandleEval(EvaluationContext context, JsValue func, Engine engine, Reference referenceRecord)
  156. {
  157. var argList = _arguments.ArgumentListEvaluation(context, out var rented);
  158. if (argList.Length == 0)
  159. {
  160. return JsValue.Undefined;
  161. }
  162. var evalFunctionInstance = (EvalFunction) func;
  163. var evalArg = argList[0];
  164. var strictCaller = StrictModeScope.IsStrictModeCode;
  165. var evalRealm = evalFunctionInstance._realm;
  166. var direct = !_expression.IsOptional();
  167. var value = evalFunctionInstance.PerformEval(evalArg, evalRealm, strictCaller, direct);
  168. if (rented)
  169. {
  170. engine._jsValueArrayPool.ReturnArray(argList);
  171. }
  172. engine._referencePool.Return(referenceRecord);
  173. return value;
  174. }
  175. private ObjectInstance SuperCall(EvaluationContext context)
  176. {
  177. var engine = context.Engine;
  178. var thisEnvironment = (FunctionEnvironment) engine.ExecutionContext.GetThisEnvironment();
  179. var newTarget = engine.GetNewTarget(thisEnvironment);
  180. var func = GetSuperConstructor(thisEnvironment);
  181. if (func is null || !func.IsConstructor)
  182. {
  183. Throw.TypeError(engine.Realm, "Not a constructor");
  184. }
  185. var rented = false;
  186. var defaultSuperCall = ReferenceEquals(_expression, ClassDefinition._defaultSuperCall);
  187. var argList = defaultSuperCall
  188. ? _arguments.DefaultSuperCallArgumentListEvaluation(context)
  189. : _arguments.ArgumentListEvaluation(context, out rented);
  190. var result = ((IConstructor) func).Construct(argList, newTarget);
  191. var thisER = (FunctionEnvironment) engine.ExecutionContext.GetThisEnvironment();
  192. thisER.BindThisValue(result);
  193. var F = thisER._functionObject;
  194. result.InitializeInstanceElements((ScriptFunction) F);
  195. if (rented)
  196. {
  197. engine._jsValueArrayPool.ReturnArray(argList);
  198. }
  199. return result;
  200. }
  201. /// <summary>
  202. /// https://tc39.es/ecma262/#sec-getsuperconstructor
  203. /// </summary>
  204. private static ObjectInstance? GetSuperConstructor(FunctionEnvironment thisEnvironment)
  205. {
  206. var envRec = thisEnvironment;
  207. var activeFunction = envRec._functionObject;
  208. var superConstructor = activeFunction.GetPrototypeOf();
  209. return superConstructor;
  210. }
  211. /// <summary>
  212. /// https://tc39.es/ecma262/#sec-isintailposition
  213. /// </summary>
  214. private static bool IsInTailPosition(CallExpression call)
  215. {
  216. // TODO tail calls
  217. return false;
  218. }
  219. }