JintCallExpression.cs 9.7 KB

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