JintCallExpression.cs 13 KB

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