JintCallExpression.cs 11 KB

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