JintCallExpression.cs 6.7 KB

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