JintCallExpression.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. using System;
  2. using Esprima.Ast;
  3. using Jint.Native;
  4. using Jint.Native.Function;
  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 readonly bool _isDebugMode;
  12. private readonly int _maxRecursionDepth;
  13. private readonly JintExpression _calleeExpression;
  14. private bool _hasSpreads;
  15. public JintCallExpression(Engine engine, CallExpression expression) : base(engine, expression)
  16. {
  17. _initialized = false;
  18. _isDebugMode = engine.Options.IsDebugMode;
  19. _maxRecursionDepth = engine.Options.MaxRecursionDepth;
  20. _calleeExpression = Build(engine, expression.Callee);
  21. }
  22. protected override void Initialize()
  23. {
  24. var expression = (CallExpression) _expression;
  25. var cachedArgumentsHolder = new CachedArgumentsHolder
  26. {
  27. JintArguments = new JintExpression[expression.Arguments.Count]
  28. };
  29. bool CanSpread(INode e)
  30. {
  31. return e?.Type == Nodes.SpreadElement
  32. || e is AssignmentExpression ae && ae.Right?.Type == Nodes.SpreadElement;
  33. }
  34. bool cacheable = true;
  35. for (var i = 0; i < expression.Arguments.Count; i++)
  36. {
  37. var expressionArgument = (Expression) expression.Arguments[i];
  38. cachedArgumentsHolder.JintArguments[i] = Build(_engine, expressionArgument);
  39. cacheable &= expressionArgument is Literal;
  40. _hasSpreads |= CanSpread(expressionArgument);
  41. if (expressionArgument is ArrayExpression ae)
  42. {
  43. for (var elementIndex = 0; elementIndex < ae.Elements.Count; elementIndex++)
  44. {
  45. _hasSpreads |= CanSpread(ae.Elements[elementIndex]);
  46. }
  47. }
  48. }
  49. if (cacheable)
  50. {
  51. expression.Cached = true;
  52. var arguments = ArrayExt.Empty<JsValue>();
  53. if (cachedArgumentsHolder.JintArguments.Length > 0)
  54. {
  55. arguments = new JsValue[cachedArgumentsHolder.JintArguments.Length];
  56. BuildArguments(cachedArgumentsHolder.JintArguments, arguments);
  57. }
  58. cachedArgumentsHolder.CachedArguments = arguments;
  59. }
  60. expression.CachedArguments = cachedArgumentsHolder;
  61. }
  62. protected override object EvaluateInternal()
  63. {
  64. var callee = _calleeExpression.Evaluate();
  65. var expression = (CallExpression) _expression;
  66. if (_isDebugMode)
  67. {
  68. _engine.DebugHandler.AddToDebugCallStack(expression);
  69. }
  70. // todo: implement as in http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.4
  71. var cachedArguments = (CachedArgumentsHolder) expression.CachedArguments;
  72. var arguments = ArrayExt.Empty<JsValue>();
  73. if (expression.Cached)
  74. {
  75. arguments = cachedArguments.CachedArguments;
  76. }
  77. else
  78. {
  79. if (cachedArguments.JintArguments.Length > 0)
  80. {
  81. if (_hasSpreads)
  82. {
  83. arguments = BuildArgumentsWithSpreads(cachedArguments.JintArguments);
  84. }
  85. else
  86. {
  87. arguments = _engine._jsValueArrayPool.RentArray(cachedArguments.JintArguments.Length);
  88. BuildArguments(cachedArguments.JintArguments, arguments);
  89. }
  90. }
  91. }
  92. var func = _engine.GetValue(callee, false);
  93. var r = callee as Reference;
  94. if (_maxRecursionDepth >= 0)
  95. {
  96. var stackItem = new CallStackElement(expression, func, r?._name ?? "anonymous function");
  97. var recursionDepth = _engine.CallStack.Push(stackItem);
  98. if (recursionDepth > _maxRecursionDepth)
  99. {
  100. _engine.CallStack.Pop();
  101. ExceptionHelper.ThrowRecursionDepthOverflowException(_engine.CallStack, stackItem.ToString());
  102. }
  103. }
  104. if (func._type == Types.Undefined)
  105. {
  106. ExceptionHelper.ThrowTypeError(_engine, r == null ? "" : $"Object has no method '{r.GetReferencedName()}'");
  107. }
  108. if (func._type != Types.Object)
  109. {
  110. if (_engine._referenceResolver == null || !_engine._referenceResolver.TryGetCallable(_engine, callee, out func))
  111. {
  112. ExceptionHelper.ThrowTypeError(_engine,
  113. r == null ? "" : $"Property '{r.GetReferencedName()}' of object is not a function");
  114. }
  115. }
  116. if (!(func is ICallable callable))
  117. {
  118. return ExceptionHelper.ThrowTypeError<object>(_engine);
  119. }
  120. var thisObject = Undefined.Instance;
  121. if (r != null)
  122. {
  123. if (r.IsPropertyReference())
  124. {
  125. thisObject = r._baseValue;
  126. }
  127. else
  128. {
  129. var env = (EnvironmentRecord) r._baseValue;
  130. thisObject = env.ImplicitThisValue();
  131. }
  132. // is it a direct call to eval ? http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.1.1
  133. if (r._name == "eval" && callable is EvalFunctionInstance instance)
  134. {
  135. var value = instance.Call(thisObject, arguments, true);
  136. _engine._referencePool.Return(r);
  137. return value;
  138. }
  139. }
  140. var result = callable.Call(thisObject, arguments);
  141. if (_isDebugMode)
  142. {
  143. _engine.DebugHandler.PopDebugCallStack();
  144. }
  145. if (_maxRecursionDepth >= 0)
  146. {
  147. _engine.CallStack.Pop();
  148. }
  149. if (!expression.Cached && arguments.Length > 0)
  150. {
  151. _engine._jsValueArrayPool.ReturnArray(arguments);
  152. }
  153. _engine._referencePool.Return(r);
  154. return result;
  155. }
  156. internal class CachedArgumentsHolder
  157. {
  158. internal JintExpression[] JintArguments;
  159. internal JsValue[] CachedArguments;
  160. }
  161. }
  162. }