JintFunctionDefinition.cs 13 KB

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