EsprimaExtensions.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. using System.Runtime.CompilerServices;
  2. using Esprima;
  3. using Esprima.Ast;
  4. using Jint.Native;
  5. using Jint.Native.Function;
  6. using Jint.Native.Object;
  7. using Jint.Runtime;
  8. using Jint.Runtime.Environments;
  9. using Jint.Runtime.Interpreter;
  10. using Jint.Runtime.Interpreter.Expressions;
  11. using Jint.Runtime.Modules;
  12. namespace Jint
  13. {
  14. public static class EsprimaExtensions
  15. {
  16. public static JsValue GetKey<T>(this T property, Engine engine) where T : IProperty => GetKey(property.Key, engine, property.Computed);
  17. public static JsValue GetKey(this Expression expression, Engine engine, bool resolveComputed = false)
  18. {
  19. var completion = TryGetKey(expression, engine, resolveComputed);
  20. if (completion.Value is not null)
  21. {
  22. return TypeConverter.ToPropertyKey(completion.Value);
  23. }
  24. ExceptionHelper.ThrowArgumentException("Unable to extract correct key, node type: " + expression.Type);
  25. return JsValue.Undefined;
  26. }
  27. internal static Completion TryGetKey<T>(this T property, Engine engine) where T : IProperty
  28. {
  29. return TryGetKey(property.Key, engine, property.Computed);
  30. }
  31. internal static Completion TryGetKey<T>(this T expression, Engine engine, bool resolveComputed) where T : Expression
  32. {
  33. JsValue key;
  34. if (expression is Literal literal)
  35. {
  36. key = literal.TokenType == TokenType.NullLiteral ? JsValue.Null : LiteralKeyToString(literal);
  37. }
  38. else if (!resolveComputed && expression is Identifier identifier)
  39. {
  40. key = identifier.Name;
  41. }
  42. else if (resolveComputed)
  43. {
  44. return TryGetComputedPropertyKey(expression, engine);
  45. }
  46. else
  47. {
  48. key = JsValue.Undefined;
  49. }
  50. return new Completion(CompletionType.Normal, key, expression);
  51. }
  52. private static Completion TryGetComputedPropertyKey<T>(T expression, Engine engine)
  53. where T : Expression
  54. {
  55. if (expression.Type is Nodes.Identifier
  56. or Nodes.CallExpression
  57. or Nodes.BinaryExpression
  58. or Nodes.UpdateExpression
  59. or Nodes.AssignmentExpression
  60. or Nodes.UnaryExpression
  61. or Nodes.MemberExpression
  62. or Nodes.LogicalExpression
  63. or Nodes.ConditionalExpression
  64. or Nodes.ArrowFunctionExpression
  65. or Nodes.FunctionExpression
  66. or Nodes.YieldExpression)
  67. {
  68. var context = engine._activeEvaluationContext;
  69. return JintExpression.Build(engine, expression).GetValue(context!);
  70. }
  71. return new Completion(CompletionType.Normal, JsValue.Undefined, expression);
  72. }
  73. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  74. internal static bool IsFunctionDefinition<T>(this T node) where T : Node
  75. {
  76. var type = node.Type;
  77. return type
  78. is Nodes.FunctionExpression
  79. or Nodes.ArrowFunctionExpression
  80. or Nodes.ArrowParameterPlaceHolder
  81. or Nodes.ClassExpression;
  82. }
  83. /// <summary>
  84. /// https://tc39.es/ecma262/#sec-static-semantics-isconstantdeclaration
  85. /// </summary>
  86. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  87. internal static bool IsConstantDeclaration(this Declaration d)
  88. {
  89. return d is VariableDeclaration { Kind: VariableDeclarationKind.Const };
  90. }
  91. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  92. internal static bool HasName<T>(this T node) where T : Node
  93. {
  94. if (!node.IsFunctionDefinition())
  95. {
  96. return false;
  97. }
  98. if ((node as IFunction)?.Id is not null)
  99. {
  100. return true;
  101. }
  102. if ((node as ClassExpression)?.Id is not null)
  103. {
  104. return true;
  105. }
  106. return false;
  107. }
  108. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  109. internal static bool IsAnonymousFunctionDefinition<T>(this T node) where T : Node
  110. {
  111. if (!node.IsFunctionDefinition())
  112. {
  113. return false;
  114. }
  115. if (node.HasName())
  116. {
  117. return false;
  118. }
  119. return true;
  120. }
  121. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  122. internal static bool IsOptional<T>(this T node) where T : Expression
  123. {
  124. switch (node)
  125. {
  126. case MemberExpression { Optional: true }:
  127. case CallExpression { Optional: true }:
  128. return true;
  129. default:
  130. return false;
  131. }
  132. }
  133. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  134. internal static string LiteralKeyToString(Literal literal)
  135. {
  136. // prevent conversion to scientific notation
  137. if (literal.Value is double d)
  138. {
  139. return TypeConverter.ToString(d);
  140. }
  141. return literal.Value as string ?? Convert.ToString(literal.Value, provider: null);
  142. }
  143. internal static void GetBoundNames(this VariableDeclaration variableDeclaration, List<string> target)
  144. {
  145. ref readonly var declarations = ref variableDeclaration.Declarations;
  146. for (var i = 0; i < declarations.Count; i++)
  147. {
  148. var declaration = declarations[i];
  149. GetBoundNames(declaration.Id, target);
  150. }
  151. }
  152. internal static void GetBoundNames(this Node? parameter, List<string> target)
  153. {
  154. if (parameter is null || parameter.Type == Nodes.Literal)
  155. {
  156. return;
  157. }
  158. // try to get away without a loop
  159. if (parameter is Identifier id)
  160. {
  161. target.Add(id.Name);
  162. return;
  163. }
  164. if (parameter is VariableDeclaration variableDeclaration)
  165. {
  166. variableDeclaration.GetBoundNames(target);
  167. return;
  168. }
  169. while (true)
  170. {
  171. if (parameter is Identifier identifier)
  172. {
  173. target.Add(identifier.Name);
  174. return;
  175. }
  176. if (parameter is RestElement restElement)
  177. {
  178. parameter = restElement.Argument;
  179. continue;
  180. }
  181. if (parameter is ArrayPattern arrayPattern)
  182. {
  183. ref readonly var arrayPatternElements = ref arrayPattern.Elements;
  184. for (var i = 0; i < arrayPatternElements.Count; i++)
  185. {
  186. var expression = arrayPatternElements[i];
  187. GetBoundNames(expression, target);
  188. }
  189. }
  190. else if (parameter is ObjectPattern objectPattern)
  191. {
  192. ref readonly var objectPatternProperties = ref objectPattern.Properties;
  193. for (var i = 0; i < objectPatternProperties.Count; i++)
  194. {
  195. var property = objectPatternProperties[i];
  196. if (property is Property p)
  197. {
  198. GetBoundNames(p.Value, target);
  199. }
  200. else
  201. {
  202. GetBoundNames((RestElement) property, target);
  203. }
  204. }
  205. }
  206. else if (parameter is AssignmentPattern assignmentPattern)
  207. {
  208. parameter = assignmentPattern.Left;
  209. continue;
  210. }
  211. else if (parameter is ClassDeclaration classDeclaration)
  212. {
  213. var name = classDeclaration.Id?.Name;
  214. if (name != null)
  215. {
  216. target.Add(name);
  217. }
  218. }
  219. break;
  220. }
  221. }
  222. internal static void BindingInitialization(
  223. this Node? expression,
  224. EvaluationContext context,
  225. JsValue value,
  226. EnvironmentRecord env)
  227. {
  228. if (expression is Identifier identifier)
  229. {
  230. var catchEnvRecord = (DeclarativeEnvironmentRecord) env;
  231. catchEnvRecord.CreateMutableBindingAndInitialize(identifier.Name, canBeDeleted: false, value);
  232. }
  233. else if (expression is BindingPattern bindingPattern)
  234. {
  235. BindingPatternAssignmentExpression.ProcessPatterns(
  236. context,
  237. bindingPattern,
  238. value,
  239. env);
  240. }
  241. }
  242. /// <summary>
  243. /// https://tc39.es/ecma262/#sec-runtime-semantics-definemethod
  244. /// </summary>
  245. internal static Record DefineMethod<T>(this T m, ObjectInstance obj, ObjectInstance? functionPrototype = null) where T : IProperty
  246. {
  247. var engine = obj.Engine;
  248. var propKey = TypeConverter.ToPropertyKey(m.GetKey(engine));
  249. var intrinsics = engine.Realm.Intrinsics;
  250. var runningExecutionContext = engine.ExecutionContext;
  251. var scope = runningExecutionContext.LexicalEnvironment;
  252. var privateScope= runningExecutionContext.PrivateEnvironment;
  253. var prototype = functionPrototype ?? intrinsics.Function.PrototypeObject;
  254. var function = m.Value as IFunction;
  255. if (function is null)
  256. {
  257. ExceptionHelper.ThrowSyntaxError(engine.Realm);
  258. }
  259. var definition = new JintFunctionDefinition(engine, function);
  260. var closure = intrinsics.Function.OrdinaryFunctionCreate(prototype, definition, definition.ThisMode, scope, privateScope);
  261. closure.MakeMethod(obj);
  262. return new Record(propKey, closure);
  263. }
  264. internal static void GetImportEntries(this ImportDeclaration import, List<ImportEntry> importEntries, HashSet<string> requestedModules)
  265. {
  266. var source = import.Source.StringValue!;
  267. var specifiers = import.Specifiers;
  268. requestedModules.Add(source);
  269. foreach (var specifier in specifiers)
  270. {
  271. switch (specifier)
  272. {
  273. case ImportNamespaceSpecifier namespaceSpecifier:
  274. importEntries.Add(new ImportEntry(source, "*", namespaceSpecifier.Local.GetModuleKey()));
  275. break;
  276. case ImportSpecifier importSpecifier:
  277. importEntries.Add(new ImportEntry(source, importSpecifier.Imported.GetModuleKey(), importSpecifier.Local.GetModuleKey()));
  278. break;
  279. case ImportDefaultSpecifier defaultSpecifier:
  280. importEntries.Add(new ImportEntry(source, "default", defaultSpecifier.Local.GetModuleKey()));
  281. break;
  282. }
  283. }
  284. }
  285. internal static void GetExportEntries(this ExportDeclaration export, List<ExportEntry> exportEntries, HashSet<string> requestedModules)
  286. {
  287. switch (export)
  288. {
  289. case ExportDefaultDeclaration defaultDeclaration:
  290. GetExportEntries(true, defaultDeclaration.Declaration, exportEntries);
  291. break;
  292. case ExportAllDeclaration allDeclaration:
  293. //Note: there is a pending PR for Esprima to support exporting an imported modules content as a namespace i.e. 'export * as ns from "mod"'
  294. requestedModules.Add(allDeclaration.Source.StringValue!);
  295. exportEntries.Add(new(allDeclaration.Exported?.GetModuleKey(), allDeclaration.Source.StringValue, "*", null));
  296. break;
  297. case ExportNamedDeclaration namedDeclaration:
  298. ref readonly var specifiers = ref namedDeclaration.Specifiers;
  299. if (specifiers.Count == 0)
  300. {
  301. GetExportEntries(false, namedDeclaration.Declaration!, exportEntries, namedDeclaration.Source?.StringValue);
  302. }
  303. else
  304. {
  305. for (var i = 0; i < specifiers.Count; i++)
  306. {
  307. var specifier = specifiers[i];
  308. if (namedDeclaration.Source != null)
  309. {
  310. exportEntries.Add(new(specifier.Exported.GetModuleKey(), namedDeclaration.Source.StringValue, specifier.Local.GetModuleKey(), null));
  311. }
  312. else
  313. {
  314. exportEntries.Add(new(specifier.Exported.GetModuleKey(), null, null, specifier.Local.GetModuleKey()));
  315. }
  316. }
  317. }
  318. if (namedDeclaration.Source is not null)
  319. {
  320. requestedModules.Add(namedDeclaration.Source.StringValue!);
  321. }
  322. break;
  323. }
  324. }
  325. private static void GetExportEntries(bool defaultExport, StatementListItem declaration, List<ExportEntry> exportEntries, string? moduleRequest = null)
  326. {
  327. var names = GetExportNames(declaration);
  328. if (names.Count == 0)
  329. {
  330. if (defaultExport)
  331. {
  332. exportEntries.Add(new("default", null, null, "*default*"));
  333. }
  334. }
  335. else
  336. {
  337. for (var i = 0; i < names.Count; i++)
  338. {
  339. var name = names[i];
  340. var exportName = defaultExport ? "default" : name;
  341. exportEntries.Add(new(exportName, moduleRequest, null, name));
  342. }
  343. }
  344. }
  345. private static List<string> GetExportNames(StatementListItem declaration)
  346. {
  347. var result = new List<string>();
  348. switch (declaration)
  349. {
  350. case FunctionDeclaration functionDeclaration:
  351. var funcName = functionDeclaration.Id?.Name;
  352. if (funcName is not null)
  353. {
  354. result.Add(funcName);
  355. }
  356. break;
  357. case ClassDeclaration classDeclaration:
  358. var className = classDeclaration.Id?.Name;
  359. if (className is not null)
  360. {
  361. result.Add(className);
  362. }
  363. break;
  364. case VariableDeclaration variableDeclaration:
  365. ref readonly var declarators = ref variableDeclaration.Declarations;
  366. for (var i = 0; i < declarators.Count; i++)
  367. {
  368. var declarator = declarators[i];
  369. var varName = declarator.Id.As<Identifier>()?.Name;
  370. if (varName is not null)
  371. {
  372. result.Add(varName);
  373. }
  374. }
  375. break;
  376. }
  377. return result;
  378. }
  379. private static string? GetModuleKey(this Expression expression)
  380. {
  381. return (expression as Identifier)?.Name ?? (expression as Literal)?.StringValue;
  382. }
  383. internal readonly record struct Record(JsValue Key, ScriptFunctionInstance Closure);
  384. /// <summary>
  385. /// Creates a dummy node that can be used when only location available and node is required.
  386. /// </summary>
  387. internal static SyntaxElement CreateLocationNode(in Location location)
  388. {
  389. return new MinimalSyntaxElement(location);
  390. }
  391. }
  392. internal sealed class MinimalSyntaxElement : SyntaxElement
  393. {
  394. public MinimalSyntaxElement(in Location location)
  395. {
  396. Location = location;
  397. }
  398. }
  399. }