JintFunctionDefinition.cs 13 KB

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