JintCallExpression.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. using System.Diagnostics.CodeAnalysis;
  2. using System.Runtime.CompilerServices;
  3. using Esprima.Ast;
  4. using Jint.Native;
  5. using Jint.Native.Function;
  6. using Jint.Native.Object;
  7. using Jint.Runtime.Environments;
  8. using Jint.Runtime.References;
  9. namespace Jint.Runtime.Interpreter.Expressions
  10. {
  11. internal sealed class JintCallExpression : JintExpression
  12. {
  13. private CachedArgumentsHolder _cachedArguments = null!;
  14. private bool _cached;
  15. private JintExpression _calleeExpression = null!;
  16. private bool _hasSpreads;
  17. public JintCallExpression(CallExpression expression) : base(expression)
  18. {
  19. _initialized = false;
  20. }
  21. protected override void Initialize(EvaluationContext context)
  22. {
  23. var engine = context.Engine;
  24. var expression = (CallExpression) _expression;
  25. _calleeExpression = Build(engine, expression.Callee);
  26. var cachedArgumentsHolder = new CachedArgumentsHolder
  27. {
  28. JintArguments = new JintExpression[expression.Arguments.Count]
  29. };
  30. static bool CanSpread(Node? e)
  31. {
  32. return e?.Type == Nodes.SpreadElement || e is AssignmentExpression { Right.Type: Nodes.SpreadElement };
  33. }
  34. bool cacheable = true;
  35. for (var i = 0; i < expression.Arguments.Count; i++)
  36. {
  37. var expressionArgument = expression.Arguments[i];
  38. cachedArgumentsHolder.JintArguments[i] = Build(engine, expressionArgument);
  39. cacheable &= expressionArgument.Type == Nodes.Literal;
  40. _hasSpreads |= CanSpread(expressionArgument);
  41. if (expressionArgument is ArrayExpression ae)
  42. {
  43. for (var elementIndex = 0; elementIndex < ae.Elements.Count; elementIndex++)
  44. {
  45. _hasSpreads |= CanSpread(ae.Elements[elementIndex]);
  46. }
  47. }
  48. }
  49. if (cacheable)
  50. {
  51. _cached = true;
  52. var arguments = System.Array.Empty<JsValue>();
  53. if (cachedArgumentsHolder.JintArguments.Length > 0)
  54. {
  55. arguments = new JsValue[cachedArgumentsHolder.JintArguments.Length];
  56. BuildArguments(context, cachedArgumentsHolder.JintArguments, arguments);
  57. }
  58. cachedArgumentsHolder.CachedArguments = arguments;
  59. }
  60. _cachedArguments = cachedArgumentsHolder;
  61. }
  62. protected override ExpressionResult EvaluateInternal(EvaluationContext context)
  63. {
  64. if (_calleeExpression._expression.Type == Nodes.Super)
  65. {
  66. return NormalCompletion(SuperCall(context));
  67. }
  68. // https://tc39.es/ecma262/#sec-function-calls
  69. var reference = _calleeExpression.Evaluate(context).Value;
  70. if (ReferenceEquals(reference, Undefined.Instance))
  71. {
  72. return NormalCompletion(Undefined.Instance);
  73. }
  74. var engine = context.Engine;
  75. var func = engine.GetValue(reference, false);
  76. if (func.IsNullOrUndefined() && _expression.IsOptional())
  77. {
  78. return NormalCompletion(Undefined.Instance);
  79. }
  80. var referenceRecord = reference as Reference;
  81. if (referenceRecord != null
  82. && !referenceRecord.IsPropertyReference()
  83. && referenceRecord.GetReferencedName() == CommonProperties.Eval
  84. && ReferenceEquals(func, engine.Realm.Intrinsics.Eval))
  85. {
  86. return HandleEval(context, func, engine, referenceRecord);
  87. }
  88. var thisCall = (CallExpression) _expression;
  89. var tailCall = IsInTailPosition(thisCall);
  90. // https://tc39.es/ecma262/#sec-evaluatecall
  91. JsValue thisObject;
  92. if (referenceRecord is not null)
  93. {
  94. if (referenceRecord.IsPropertyReference())
  95. {
  96. thisObject = referenceRecord.GetThisValue();
  97. }
  98. else
  99. {
  100. var baseValue = referenceRecord.GetBase();
  101. // deviation from the spec to support null-propagation helper
  102. if (baseValue.IsNullOrUndefined()
  103. && engine._referenceResolver.TryUnresolvableReference(engine, referenceRecord, out var value))
  104. {
  105. thisObject = value;
  106. }
  107. else
  108. {
  109. var refEnv = (EnvironmentRecord) baseValue;
  110. thisObject = refEnv.WithBaseObject();
  111. }
  112. }
  113. }
  114. else
  115. {
  116. thisObject = Undefined.Instance;
  117. }
  118. var arguments = ArgumentListEvaluation(context);
  119. if (!func.IsObject() && !engine._referenceResolver.TryGetCallable(engine, reference, out func))
  120. {
  121. ThrowMemberisNotFunction(referenceRecord, reference, engine);
  122. }
  123. var callable = func as ICallable;
  124. if (callable is null)
  125. {
  126. ThrowReferenceNotFunction(referenceRecord, reference, engine);
  127. }
  128. if (tailCall)
  129. {
  130. // TODO tail call
  131. // PrepareForTailCall();
  132. }
  133. // ensure logic is in sync between Call, Construct and JintCallExpression!
  134. JsValue result;
  135. if (callable is FunctionInstance functionInstance)
  136. {
  137. var callStack = engine.CallStack;
  138. var recursionDepth = callStack.Push(functionInstance, _calleeExpression, engine.ExecutionContext);
  139. if (recursionDepth > engine.Options.Constraints.MaxRecursionDepth)
  140. {
  141. // automatically pops the current element as it was never reached
  142. ExceptionHelper.ThrowRecursionDepthOverflowException(callStack);
  143. }
  144. try
  145. {
  146. result = functionInstance.Call(thisObject, arguments);
  147. }
  148. finally
  149. {
  150. // if call stack was reset due to recursive call to engine or similar, we might not have it anymore
  151. if (callStack.Count > 0)
  152. {
  153. callStack.Pop();
  154. }
  155. }
  156. }
  157. else
  158. {
  159. result = callable.Call(thisObject, arguments);
  160. }
  161. if (!_cached && arguments.Length > 0)
  162. {
  163. engine._jsValueArrayPool.ReturnArray(arguments);
  164. }
  165. engine._referencePool.Return(referenceRecord);
  166. return NormalCompletion(result);
  167. }
  168. [DoesNotReturn]
  169. [MethodImpl(MethodImplOptions.NoInlining)]
  170. private static void ThrowReferenceNotFunction(Reference? referenceRecord1, object reference, Engine engine)
  171. {
  172. var message = $"{referenceRecord1?.GetReferencedName() ?? reference} is not a function";
  173. ExceptionHelper.ThrowTypeError(engine.Realm, message);
  174. }
  175. [DoesNotReturn]
  176. [MethodImpl(MethodImplOptions.NoInlining)]
  177. private static void ThrowMemberisNotFunction(Reference? referenceRecord1, object reference, Engine engine)
  178. {
  179. var message = referenceRecord1 == null
  180. ? reference + " is not a function"
  181. : $"Property '{referenceRecord1.GetReferencedName()}' of object is not a function";
  182. ExceptionHelper.ThrowTypeError(engine.Realm, message);
  183. }
  184. private ExpressionResult HandleEval(EvaluationContext context, JsValue func, Engine engine, Reference referenceRecord)
  185. {
  186. var argList = ArgumentListEvaluation(context);
  187. if (argList.Length == 0)
  188. {
  189. return NormalCompletion(Undefined.Instance);
  190. }
  191. var evalFunctionInstance = (EvalFunctionInstance) func;
  192. var evalArg = argList[0];
  193. var strictCaller = StrictModeScope.IsStrictModeCode;
  194. var evalRealm = evalFunctionInstance._realm;
  195. var direct = !_expression.IsOptional();
  196. var value = evalFunctionInstance.PerformEval(evalArg, evalRealm, strictCaller, direct);
  197. engine._referencePool.Return(referenceRecord);
  198. return NormalCompletion(value);
  199. }
  200. private JsValue SuperCall(EvaluationContext context)
  201. {
  202. var engine = context.Engine;
  203. var thisEnvironment = (FunctionEnvironmentRecord) engine.ExecutionContext.GetThisEnvironment();
  204. var newTarget = engine.GetNewTarget(thisEnvironment);
  205. var func = GetSuperConstructor(thisEnvironment);
  206. if (func is null || !func.IsConstructor)
  207. {
  208. ExceptionHelper.ThrowTypeError(engine.Realm, "Not a constructor");
  209. }
  210. var argList = ArgumentListEvaluation(context);
  211. var result = ((IConstructor) func).Construct(argList, newTarget ?? JsValue.Undefined);
  212. var thisER = (FunctionEnvironmentRecord) engine.ExecutionContext.GetThisEnvironment();
  213. return thisER.BindThisValue(result);
  214. }
  215. /// <summary>
  216. /// https://tc39.es/ecma262/#sec-getsuperconstructor
  217. /// </summary>
  218. private static ObjectInstance? GetSuperConstructor(FunctionEnvironmentRecord thisEnvironment)
  219. {
  220. var envRec = thisEnvironment;
  221. var activeFunction = envRec._functionObject;
  222. var superConstructor = activeFunction.GetPrototypeOf();
  223. return superConstructor;
  224. }
  225. /// <summary>
  226. /// https://tc39.es/ecma262/#sec-isintailposition
  227. /// </summary>
  228. private static bool IsInTailPosition(CallExpression call)
  229. {
  230. // TODO tail calls
  231. return false;
  232. }
  233. private JsValue[] ArgumentListEvaluation(EvaluationContext context)
  234. {
  235. var cachedArguments = _cachedArguments;
  236. var arguments = Array.Empty<JsValue>();
  237. if (_cached)
  238. {
  239. arguments = cachedArguments.CachedArguments;
  240. }
  241. else
  242. {
  243. if (cachedArguments.JintArguments.Length > 0)
  244. {
  245. if (_hasSpreads)
  246. {
  247. arguments = BuildArgumentsWithSpreads(context, cachedArguments.JintArguments);
  248. }
  249. else
  250. {
  251. arguments = context.Engine._jsValueArrayPool.RentArray(cachedArguments.JintArguments.Length);
  252. BuildArguments(context, cachedArguments.JintArguments, arguments);
  253. }
  254. }
  255. }
  256. return arguments;
  257. }
  258. private sealed class CachedArgumentsHolder
  259. {
  260. internal JintExpression[] JintArguments = Array.Empty<JintExpression>();
  261. internal JsValue[] CachedArguments = Array.Empty<JsValue>();
  262. }
  263. }
  264. }