EsprimaExtensions.cs 16 KB

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