JintFunctionDefinition.cs 13 KB

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