JintFunctionDefinition.cs 14 KB

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