JintFunctionDefinition.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. using System.Runtime.CompilerServices;
  2. using Esprima.Ast;
  3. using Jint.Native;
  4. using Jint.Native.Function;
  5. using Jint.Runtime.Interpreter.Expressions;
  6. namespace Jint.Runtime.Interpreter;
  7. /// <summary>
  8. /// Works as memento for function execution. Optimization to cache things that don't change.
  9. /// </summary>
  10. internal sealed class JintFunctionDefinition
  11. {
  12. private readonly Engine _engine;
  13. private JintExpression? _bodyExpression;
  14. private JintStatementList? _bodyStatementList;
  15. public readonly string? Name;
  16. public readonly bool Strict;
  17. public readonly IFunction Function;
  18. private State? _state;
  19. public JintFunctionDefinition(
  20. Engine engine,
  21. IFunction function)
  22. {
  23. _engine = engine;
  24. Function = function;
  25. Name = !string.IsNullOrEmpty(function.Id?.Name) ? function.Id!.Name : null;
  26. Strict = function.Strict;
  27. }
  28. public FunctionThisMode ThisMode => Strict ? FunctionThisMode.Strict : FunctionThisMode.Global;
  29. /// <summary>
  30. /// https://tc39.es/ecma262/#sec-ordinarycallevaluatebody
  31. /// </summary>
  32. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  33. internal Completion EvaluateBody(EvaluationContext context, FunctionInstance functionObject, JsValue[] argumentsList)
  34. {
  35. Completion result;
  36. var argumentsInstance = _engine.FunctionDeclarationInstantiation(functionObject, argumentsList);
  37. if (Function.Expression)
  38. {
  39. // https://tc39.es/ecma262/#sec-runtime-semantics-evaluateconcisebody
  40. _bodyExpression ??= JintExpression.Build(_engine, (Expression) Function.Body);
  41. var jsValue = _bodyExpression.GetValue(context).GetValueOrDefault().Clone();
  42. result = new Completion(CompletionType.Return, jsValue, null, Function.Body);
  43. }
  44. else if (Function.Generator)
  45. {
  46. // TODO generators
  47. // result = EvaluateGeneratorBody(functionObject, argumentsList);
  48. _bodyStatementList ??= new JintStatementList(Function);
  49. result = _bodyStatementList.Execute(context);
  50. }
  51. else
  52. {
  53. // https://tc39.es/ecma262/#sec-runtime-semantics-evaluatefunctionbody
  54. _bodyStatementList ??= new JintStatementList(Function);
  55. result = _bodyStatementList.Execute(context);
  56. }
  57. argumentsInstance?.FunctionWasCalled();
  58. return result;
  59. }
  60. /// <summary>
  61. /// https://tc39.es/ecma262/#sec-runtime-semantics-evaluategeneratorbody
  62. /// </summary>
  63. private Completion EvaluateGeneratorBody(FunctionInstance functionObject, JsValue[] argumentsList)
  64. {
  65. ExceptionHelper.ThrowNotImplementedException("generators not implemented");
  66. return default;
  67. }
  68. internal State Initialize(FunctionInstance functionInstance)
  69. {
  70. return _state ??= DoInitialize(functionInstance);
  71. }
  72. internal sealed class State
  73. {
  74. public bool HasRestParameter;
  75. public int Length;
  76. public Key[] ParameterNames = null!;
  77. public bool HasDuplicates;
  78. public bool IsSimpleParameterList;
  79. public bool HasParameterExpressions;
  80. public bool ArgumentsObjectNeeded;
  81. public List<Key>? VarNames;
  82. public LinkedList<JintFunctionDefinition>? FunctionsToInitialize;
  83. public readonly HashSet<Key> FunctionNames = new();
  84. public LexicalVariableDeclaration[] LexicalDeclarations = Array.Empty<LexicalVariableDeclaration>();
  85. public HashSet<Key>? ParameterBindings;
  86. public List<VariableValuePair>? VarsToInitialize;
  87. internal struct VariableValuePair
  88. {
  89. public Key Name;
  90. public JsValue? InitialValue;
  91. }
  92. internal struct LexicalVariableDeclaration
  93. {
  94. public bool IsConstantDeclaration;
  95. public List<string> BoundNames;
  96. }
  97. }
  98. private State DoInitialize(FunctionInstance functionInstance)
  99. {
  100. var state = new State();
  101. ProcessParameters(Function, state, out var hasArguments);
  102. var hoistingScope = HoistingScope.GetFunctionLevelDeclarations(
  103. Strict,
  104. Function,
  105. collectVarNames: true,
  106. collectLexicalNames: true,
  107. checkArgumentsReference: true);
  108. var functionDeclarations = hoistingScope._functionDeclarations;
  109. var lexicalNames = hoistingScope._lexicalNames;
  110. state.VarNames = hoistingScope._varNames;
  111. LinkedList<JintFunctionDefinition>? functionsToInitialize = null;
  112. if (functionDeclarations != null)
  113. {
  114. functionsToInitialize = new LinkedList<JintFunctionDefinition>();
  115. for (var i = functionDeclarations.Count - 1; i >= 0; i--)
  116. {
  117. var d = functionDeclarations[i];
  118. var fn = d.Id!.Name;
  119. if (state.FunctionNames.Add(fn))
  120. {
  121. functionsToInitialize.AddFirst(new JintFunctionDefinition(_engine, d));
  122. }
  123. }
  124. }
  125. state.FunctionsToInitialize = functionsToInitialize;
  126. const string ParameterNameArguments = "arguments";
  127. state.ArgumentsObjectNeeded = true;
  128. if (functionInstance._thisMode == FunctionThisMode.Lexical)
  129. {
  130. state.ArgumentsObjectNeeded = false;
  131. }
  132. else if (hasArguments)
  133. {
  134. state.ArgumentsObjectNeeded = false;
  135. }
  136. else if (!state.HasParameterExpressions)
  137. {
  138. if (state.FunctionNames.Contains(ParameterNameArguments)
  139. || lexicalNames?.Contains(ParameterNameArguments) == true)
  140. {
  141. state.ArgumentsObjectNeeded = false;
  142. }
  143. }
  144. if (state.ArgumentsObjectNeeded && !_engine._isDebugMode)
  145. {
  146. // just one extra check...
  147. state.ArgumentsObjectNeeded = hoistingScope._hasArgumentsReference;
  148. }
  149. var parameterBindings = new HashSet<Key>(state.ParameterNames);
  150. if (state.ArgumentsObjectNeeded)
  151. {
  152. parameterBindings.Add(KnownKeys.Arguments);
  153. }
  154. state.ParameterBindings = parameterBindings;
  155. var varsToInitialize = new List<State.VariableValuePair>();
  156. if (!state.HasParameterExpressions)
  157. {
  158. var instantiatedVarNames = state.VarNames != null
  159. ? new HashSet<Key>(state.ParameterBindings)
  160. : new HashSet<Key>();
  161. for (var i = 0; i < state.VarNames?.Count; i++)
  162. {
  163. var n = state.VarNames[i];
  164. if (instantiatedVarNames.Add(n))
  165. {
  166. varsToInitialize.Add(new State.VariableValuePair
  167. {
  168. Name = n
  169. });
  170. }
  171. }
  172. }
  173. else
  174. {
  175. var instantiatedVarNames = state.VarNames != null
  176. ? new HashSet<Key>(state.ParameterBindings)
  177. : null;
  178. for (var i = 0; i < state.VarNames?.Count; i++)
  179. {
  180. var n = state.VarNames[i];
  181. if (instantiatedVarNames!.Add(n))
  182. {
  183. JsValue? initialValue = null;
  184. if (!state.ParameterBindings.Contains(n) || state.FunctionNames.Contains(n))
  185. {
  186. initialValue = JsValue.Undefined;
  187. }
  188. varsToInitialize.Add(new State.VariableValuePair
  189. {
  190. Name = n,
  191. InitialValue = initialValue
  192. });
  193. }
  194. }
  195. }
  196. state.VarsToInitialize = varsToInitialize;
  197. if (hoistingScope._lexicalDeclarations != null)
  198. {
  199. var _lexicalDeclarations = hoistingScope._lexicalDeclarations;
  200. var lexicalDeclarationsCount = _lexicalDeclarations.Count;
  201. var declarations = new State.LexicalVariableDeclaration[lexicalDeclarationsCount];
  202. for (var i = 0; i < lexicalDeclarationsCount; i++)
  203. {
  204. var d = _lexicalDeclarations[i];
  205. var boundNames = new List<string>();
  206. d.GetBoundNames(boundNames);
  207. declarations[i] = new State.LexicalVariableDeclaration
  208. {
  209. IsConstantDeclaration = d.IsConstantDeclaration(),
  210. BoundNames = boundNames
  211. };
  212. }
  213. state.LexicalDeclarations = declarations;
  214. }
  215. return state;
  216. }
  217. private static void GetBoundNames(
  218. Node? parameter,
  219. List<Key> target,
  220. bool checkDuplicates,
  221. ref bool _hasRestParameter,
  222. ref bool _hasParameterExpressions,
  223. ref bool _hasDuplicates,
  224. ref bool hasArguments)
  225. {
  226. if (parameter is Identifier identifier)
  227. {
  228. _hasDuplicates |= checkDuplicates && target.Contains(identifier.Name);
  229. target.Add(identifier.Name);
  230. hasArguments |= identifier.Name == "arguments";
  231. return;
  232. }
  233. while (true)
  234. {
  235. if (parameter is RestElement restElement)
  236. {
  237. _hasRestParameter = true;
  238. _hasParameterExpressions = true;
  239. parameter = restElement.Argument;
  240. continue;
  241. }
  242. if (parameter is ArrayPattern arrayPattern)
  243. {
  244. _hasParameterExpressions = true;
  245. ref readonly var arrayPatternElements = ref arrayPattern.Elements;
  246. for (var i = 0; i < arrayPatternElements.Count; i++)
  247. {
  248. var expression = arrayPatternElements[i];
  249. GetBoundNames(
  250. expression,
  251. target,
  252. checkDuplicates,
  253. ref _hasRestParameter,
  254. ref _hasParameterExpressions,
  255. ref _hasDuplicates,
  256. ref hasArguments);
  257. }
  258. }
  259. else if (parameter is ObjectPattern objectPattern)
  260. {
  261. _hasParameterExpressions = true;
  262. ref readonly var objectPatternProperties = ref objectPattern.Properties;
  263. for (var i = 0; i < objectPatternProperties.Count; i++)
  264. {
  265. var property = objectPatternProperties[i];
  266. if (property is Property p)
  267. {
  268. GetBoundNames(
  269. p.Value,
  270. target,
  271. checkDuplicates,
  272. ref _hasRestParameter,
  273. ref _hasParameterExpressions,
  274. ref _hasDuplicates,
  275. ref hasArguments);
  276. }
  277. else
  278. {
  279. _hasRestParameter = true;
  280. _hasParameterExpressions = true;
  281. parameter = ((RestElement) property).Argument;
  282. continue;
  283. }
  284. }
  285. }
  286. else if (parameter is AssignmentPattern assignmentPattern)
  287. {
  288. _hasParameterExpressions = true;
  289. parameter = assignmentPattern.Left;
  290. continue;
  291. }
  292. break;
  293. }
  294. }
  295. private static void ProcessParameters(
  296. IFunction function,
  297. State state,
  298. out bool hasArguments)
  299. {
  300. hasArguments = false;
  301. state.IsSimpleParameterList = true;
  302. ref readonly var functionDeclarationParams = ref function.Params;
  303. var count = functionDeclarationParams.Count;
  304. var parameterNames = new List<Key>(count);
  305. for (var i = 0; i < count; i++)
  306. {
  307. var parameter = functionDeclarationParams[i];
  308. if (parameter is Identifier id)
  309. {
  310. state.HasDuplicates |= parameterNames.Contains(id.Name);
  311. hasArguments = id.Name == "arguments";
  312. parameterNames.Add(id.Name);
  313. if (state.IsSimpleParameterList)
  314. {
  315. state.Length++;
  316. }
  317. }
  318. else if (parameter.Type != Nodes.Literal)
  319. {
  320. state.IsSimpleParameterList = false;
  321. GetBoundNames(
  322. parameter,
  323. parameterNames,
  324. checkDuplicates: true,
  325. ref state.HasRestParameter,
  326. ref state.HasParameterExpressions,
  327. ref state.HasDuplicates,
  328. ref hasArguments);
  329. }
  330. }
  331. state.ParameterNames = parameterNames.ToArray();
  332. }
  333. }