EsprimaExtensions.cs 16 KB

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