JintCallExpression.cs 9.6 KB

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