JintCallExpression.cs 6.8 KB

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