JintCallExpression.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  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.CallStack;
  8. using Jint.Runtime.Environments;
  9. using Environment = Jint.Runtime.Environments.Environment;
  10. namespace Jint.Runtime.Interpreter.Expressions
  11. {
  12. internal sealed class JintCallExpression : JintExpression
  13. {
  14. private CachedArgumentsHolder _cachedArguments = null!;
  15. private bool _cached;
  16. private JintExpression _calleeExpression = null!;
  17. private bool _hasSpreads;
  18. private bool _initialized;
  19. public JintCallExpression(CallExpression expression) : base(expression)
  20. {
  21. }
  22. private void Initialize(EvaluationContext context)
  23. {
  24. var expression = (CallExpression) _expression;
  25. ref readonly var expressionArguments = ref expression.Arguments;
  26. _calleeExpression = Build(expression.Callee);
  27. var cachedArgumentsHolder = new CachedArgumentsHolder
  28. {
  29. JintArguments = new JintExpression[expressionArguments.Count]
  30. };
  31. static bool CanSpread(Node? e)
  32. {
  33. if (e is null)
  34. {
  35. return false;
  36. }
  37. return e.Type == Nodes.SpreadElement || e is AssignmentExpression { Right.Type: Nodes.SpreadElement };
  38. }
  39. var cacheable = true;
  40. for (var i = 0; i < expressionArguments.Count; i++)
  41. {
  42. var expressionArgument = expressionArguments[i];
  43. cachedArgumentsHolder.JintArguments[i] = Build(expressionArgument);
  44. cacheable &= expressionArgument.Type == Nodes.Literal;
  45. _hasSpreads |= CanSpread(expressionArgument);
  46. if (expressionArgument is ArrayExpression ae)
  47. {
  48. ref readonly var elements = ref ae.Elements;
  49. for (var elementIndex = 0; elementIndex < elements.Count; elementIndex++)
  50. {
  51. _hasSpreads |= CanSpread(elements[elementIndex]);
  52. }
  53. }
  54. }
  55. if (cacheable)
  56. {
  57. _cached = true;
  58. var arguments = Array.Empty<JsValue>();
  59. if (cachedArgumentsHolder.JintArguments.Length > 0)
  60. {
  61. arguments = new JsValue[cachedArgumentsHolder.JintArguments.Length];
  62. BuildArguments(context, cachedArgumentsHolder.JintArguments, arguments);
  63. }
  64. cachedArgumentsHolder.CachedArguments = arguments;
  65. }
  66. _cachedArguments = cachedArgumentsHolder;
  67. }
  68. protected override object EvaluateInternal(EvaluationContext context)
  69. {
  70. if (!_initialized)
  71. {
  72. Initialize(context);
  73. _initialized = true;
  74. }
  75. if (!context.Engine._stackGuard.TryEnterOnCurrentStack())
  76. {
  77. return StackGuard.RunOnEmptyStack(EvaluateInternal, context);
  78. }
  79. if (_calleeExpression._expression.Type == Nodes.Super)
  80. {
  81. return SuperCall(context);
  82. }
  83. // https://tc39.es/ecma262/#sec-function-calls
  84. var reference = _calleeExpression.Evaluate(context);
  85. if (ReferenceEquals(reference, JsValue.Undefined))
  86. {
  87. return JsValue.Undefined;
  88. }
  89. var engine = context.Engine;
  90. var func = engine.GetValue(reference, false);
  91. if (func.IsNullOrUndefined() && _expression.IsOptional())
  92. {
  93. return JsValue.Undefined;
  94. }
  95. var referenceRecord = reference as Reference;
  96. if (ReferenceEquals(func, engine.Realm.Intrinsics.Eval)
  97. && referenceRecord != null
  98. && !referenceRecord.IsPropertyReference
  99. && CommonProperties.Eval.Equals(referenceRecord.ReferencedName))
  100. {
  101. return HandleEval(context, func, engine, referenceRecord);
  102. }
  103. var thisCall = (CallExpression) _expression;
  104. var tailCall = IsInTailPosition(thisCall);
  105. // https://tc39.es/ecma262/#sec-evaluatecall
  106. JsValue thisObject;
  107. if (referenceRecord is not null)
  108. {
  109. if (referenceRecord.IsPropertyReference)
  110. {
  111. thisObject = referenceRecord.ThisValue;
  112. }
  113. else
  114. {
  115. var baseValue = referenceRecord.Base;
  116. // deviation from the spec to support null-propagation helper
  117. if (baseValue.IsNullOrUndefined()
  118. && engine._referenceResolver.TryUnresolvableReference(engine, referenceRecord, out var value))
  119. {
  120. thisObject = value;
  121. }
  122. else
  123. {
  124. var refEnv = (Environment) baseValue;
  125. thisObject = refEnv.WithBaseObject();
  126. }
  127. }
  128. }
  129. else
  130. {
  131. thisObject = JsValue.Undefined;
  132. }
  133. var arguments = ArgumentListEvaluation(context);
  134. if (!func.IsObject() && !engine._referenceResolver.TryGetCallable(engine, reference, out func))
  135. {
  136. ThrowMemberIsNotFunction(referenceRecord, reference, engine);
  137. }
  138. var callable = func as ICallable;
  139. if (callable is null)
  140. {
  141. ThrowReferenceNotFunction(referenceRecord, reference, engine);
  142. }
  143. if (tailCall)
  144. {
  145. // TODO tail call
  146. // PrepareForTailCall();
  147. }
  148. // ensure logic is in sync between Call, Construct and JintCallExpression!
  149. JsValue result;
  150. if (callable is Function functionInstance)
  151. {
  152. var callStack = engine.CallStack;
  153. var recursionDepth = callStack.Push(functionInstance, _calleeExpression, engine.ExecutionContext);
  154. if (recursionDepth > engine.Options.Constraints.MaxRecursionDepth)
  155. {
  156. // automatically pops the current element as it was never reached
  157. ExceptionHelper.ThrowRecursionDepthOverflowException(callStack);
  158. }
  159. try
  160. {
  161. result = functionInstance.Call(thisObject, arguments);
  162. }
  163. finally
  164. {
  165. // if call stack was reset due to recursive call to engine or similar, we might not have it anymore
  166. if (callStack.Count > 0)
  167. {
  168. callStack.Pop();
  169. }
  170. }
  171. }
  172. else
  173. {
  174. result = callable.Call(thisObject, arguments);
  175. }
  176. if (!_cached && arguments.Length > 0)
  177. {
  178. engine._jsValueArrayPool.ReturnArray(arguments);
  179. }
  180. engine._referencePool.Return(referenceRecord);
  181. return result;
  182. }
  183. [DoesNotReturn]
  184. [MethodImpl(MethodImplOptions.NoInlining)]
  185. private static void ThrowReferenceNotFunction(Reference? referenceRecord1, object reference, Engine engine)
  186. {
  187. var message = $"{referenceRecord1?.ReferencedName ?? reference} is not a function";
  188. ExceptionHelper.ThrowTypeError(engine.Realm, message);
  189. }
  190. [DoesNotReturn]
  191. [MethodImpl(MethodImplOptions.NoInlining)]
  192. private static void ThrowMemberIsNotFunction(Reference? referenceRecord1, object reference, Engine engine)
  193. {
  194. var message = referenceRecord1 == null
  195. ? reference + " is not a function"
  196. : $"Property '{referenceRecord1.ReferencedName}' of object is not a function";
  197. ExceptionHelper.ThrowTypeError(engine.Realm, message);
  198. }
  199. private JsValue HandleEval(EvaluationContext context, JsValue func, Engine engine, Reference referenceRecord)
  200. {
  201. var argList = ArgumentListEvaluation(context);
  202. if (argList.Length == 0)
  203. {
  204. return JsValue.Undefined;
  205. }
  206. var evalFunctionInstance = (EvalFunction) func;
  207. var evalArg = argList[0];
  208. var strictCaller = StrictModeScope.IsStrictModeCode;
  209. var evalRealm = evalFunctionInstance._realm;
  210. var direct = !_expression.IsOptional();
  211. var value = evalFunctionInstance.PerformEval(evalArg, evalRealm, strictCaller, direct);
  212. engine._referencePool.Return(referenceRecord);
  213. return value;
  214. }
  215. private ObjectInstance SuperCall(EvaluationContext context)
  216. {
  217. var engine = context.Engine;
  218. var thisEnvironment = (FunctionEnvironment) engine.ExecutionContext.GetThisEnvironment();
  219. var newTarget = engine.GetNewTarget(thisEnvironment);
  220. var func = GetSuperConstructor(thisEnvironment);
  221. if (func is null || !func.IsConstructor)
  222. {
  223. ExceptionHelper.ThrowTypeError(engine.Realm, "Not a constructor");
  224. }
  225. var defaultSuperCall = ReferenceEquals(_expression, ClassDefinition._defaultSuperCall);
  226. var argList = defaultSuperCall ? DefaultSuperCallArgumentListEvaluation(context) : ArgumentListEvaluation(context);
  227. var result = ((IConstructor) func).Construct(argList, newTarget);
  228. var thisER = (FunctionEnvironment) engine.ExecutionContext.GetThisEnvironment();
  229. thisER.BindThisValue(result);
  230. var F = thisER._functionObject;
  231. result.InitializeInstanceElements((ScriptFunction) F);
  232. return result;
  233. }
  234. /// <summary>
  235. /// https://tc39.es/ecma262/#sec-getsuperconstructor
  236. /// </summary>
  237. private static ObjectInstance? GetSuperConstructor(FunctionEnvironment thisEnvironment)
  238. {
  239. var envRec = thisEnvironment;
  240. var activeFunction = envRec._functionObject;
  241. var superConstructor = activeFunction.GetPrototypeOf();
  242. return superConstructor;
  243. }
  244. /// <summary>
  245. /// https://tc39.es/ecma262/#sec-isintailposition
  246. /// </summary>
  247. private static bool IsInTailPosition(CallExpression call)
  248. {
  249. // TODO tail calls
  250. return false;
  251. }
  252. private JsValue[] ArgumentListEvaluation(EvaluationContext context)
  253. {
  254. var cachedArguments = _cachedArguments;
  255. var arguments = Array.Empty<JsValue>();
  256. if (_cached)
  257. {
  258. arguments = cachedArguments.CachedArguments;
  259. }
  260. else
  261. {
  262. if (cachedArguments.JintArguments.Length > 0)
  263. {
  264. if (_hasSpreads)
  265. {
  266. arguments = BuildArgumentsWithSpreads(context, cachedArguments.JintArguments);
  267. }
  268. else
  269. {
  270. arguments = context.Engine._jsValueArrayPool.RentArray(cachedArguments.JintArguments.Length);
  271. BuildArguments(context, cachedArguments.JintArguments, arguments);
  272. }
  273. }
  274. }
  275. return arguments;
  276. }
  277. private JsValue[] DefaultSuperCallArgumentListEvaluation(EvaluationContext context)
  278. {
  279. // This branch behaves similarly to constructor(...args) { super(...args); }.
  280. // The most notable distinction is that while the aforementioned ECMAScript source text observably calls
  281. // the @@iterator method on %Array.prototype%, this function does not.
  282. var spreadExpression = (JintSpreadExpression) _cachedArguments.JintArguments[0];
  283. var array = (JsArray) spreadExpression._argument.GetValue(context);
  284. var length = array.GetLength();
  285. var args = new List<JsValue>((int) length);
  286. for (uint j = 0; j < length; ++j)
  287. {
  288. array.TryGetValue(j, out var value);
  289. args.Add(value);
  290. }
  291. return args.ToArray();
  292. }
  293. private sealed class CachedArgumentsHolder
  294. {
  295. internal JintExpression[] JintArguments = Array.Empty<JintExpression>();
  296. internal JsValue[] CachedArguments = Array.Empty<JsValue>();
  297. }
  298. }
  299. }