EsprimaExtensions.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. #nullable enable
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Runtime.CompilerServices;
  5. using Esprima.Ast;
  6. using Jint.Native;
  7. using Jint.Native.Function;
  8. using Jint.Native.Object;
  9. using Jint.Runtime;
  10. using Jint.Runtime.Environments;
  11. using Jint.Runtime.Interpreter;
  12. using Jint.Runtime.Interpreter.Expressions;
  13. namespace Jint
  14. {
  15. public static class EsprimaExtensions
  16. {
  17. public static JsValue GetKey(this ClassProperty property, Engine engine) => GetKey(property.Key, engine, property.Computed);
  18. public static JsValue GetKey(this Expression expression, Engine engine, bool resolveComputed = false)
  19. {
  20. if (expression is Literal literal)
  21. {
  22. return LiteralKeyToString(literal);
  23. }
  24. if (!resolveComputed && expression is Identifier identifier)
  25. {
  26. return identifier.Name;
  27. }
  28. if (!resolveComputed || !TryGetComputedPropertyKey(expression, engine, out var propertyKey))
  29. {
  30. ExceptionHelper.ThrowArgumentException("Unable to extract correct key, node type: " + expression.Type);
  31. return null;
  32. }
  33. return propertyKey;
  34. }
  35. private static bool TryGetComputedPropertyKey<T>(T expression, Engine engine, out JsValue propertyKey)
  36. where T : Expression
  37. {
  38. if (expression.Type is Nodes.Identifier
  39. or Nodes.CallExpression
  40. or Nodes.BinaryExpression
  41. or Nodes.UpdateExpression
  42. or Nodes.AssignmentExpression
  43. or Nodes.UnaryExpression
  44. || expression is StaticMemberExpression)
  45. {
  46. propertyKey = TypeConverter.ToPropertyKey(JintExpression.Build(engine, expression).GetValue());
  47. return true;
  48. }
  49. propertyKey = string.Empty;
  50. return false;
  51. }
  52. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  53. internal static bool IsFunctionDefinition<T>(this T node) where T : Node
  54. {
  55. var type = node.Type;
  56. return type
  57. is Nodes.FunctionExpression
  58. or Nodes.ArrowFunctionExpression
  59. or Nodes.ArrowParameterPlaceHolder
  60. or Nodes.ClassExpression;
  61. }
  62. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  63. internal static bool HasName<T>(this T node) where T : Node
  64. {
  65. if (!node.IsFunctionDefinition())
  66. {
  67. return false;
  68. }
  69. if ((node as IFunction)?.Id is not null)
  70. {
  71. return true;
  72. }
  73. if ((node as ClassExpression)?.Id is not null)
  74. {
  75. return true;
  76. }
  77. return false;
  78. }
  79. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  80. internal static bool IsAnonymousFunctionDefinition<T>(this T node) where T : Node
  81. {
  82. if (!node.IsFunctionDefinition())
  83. {
  84. return false;
  85. }
  86. if (node.HasName())
  87. {
  88. return false;
  89. }
  90. return true;
  91. }
  92. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  93. internal static bool IsOptional<T>(this T node) where T : Expression
  94. {
  95. switch (node)
  96. {
  97. case MemberExpression { Optional: true }:
  98. case CallExpression { Optional: true }:
  99. return true;
  100. default:
  101. return false;
  102. }
  103. }
  104. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  105. internal static string LiteralKeyToString(Literal literal)
  106. {
  107. // prevent conversion to scientific notation
  108. if (literal.Value is double d)
  109. {
  110. return TypeConverter.ToString(d);
  111. }
  112. return literal.Value as string ?? Convert.ToString(literal.Value, provider: null);
  113. }
  114. internal static void GetBoundNames(this VariableDeclaration variableDeclaration, List<string> target)
  115. {
  116. ref readonly var declarations = ref variableDeclaration.Declarations;
  117. for (var i = 0; i < declarations.Count; i++)
  118. {
  119. var declaration = declarations[i];
  120. GetBoundNames(declaration.Id, target);
  121. }
  122. }
  123. internal static void GetBoundNames(this Node? parameter, List<string> target)
  124. {
  125. if (parameter is null || parameter.Type == Nodes.Literal)
  126. {
  127. return;
  128. }
  129. // try to get away without a loop
  130. if (parameter is Identifier id)
  131. {
  132. target.Add(id.Name!);
  133. return;
  134. }
  135. if (parameter is VariableDeclaration variableDeclaration)
  136. {
  137. variableDeclaration.GetBoundNames(target);
  138. return;
  139. }
  140. while (true)
  141. {
  142. if (parameter is Identifier identifier)
  143. {
  144. target.Add(identifier.Name!);
  145. return;
  146. }
  147. if (parameter is RestElement restElement)
  148. {
  149. parameter = restElement.Argument;
  150. continue;
  151. }
  152. else if (parameter is ArrayPattern arrayPattern)
  153. {
  154. ref readonly var arrayPatternElements = ref arrayPattern.Elements;
  155. for (var i = 0; i < arrayPatternElements.Count; i++)
  156. {
  157. var expression = arrayPatternElements[i];
  158. GetBoundNames(expression, target);
  159. }
  160. }
  161. else if (parameter is ObjectPattern objectPattern)
  162. {
  163. ref readonly var objectPatternProperties = ref objectPattern.Properties;
  164. for (var i = 0; i < objectPatternProperties.Count; i++)
  165. {
  166. var property = objectPatternProperties[i];
  167. if (property is Property p)
  168. {
  169. GetBoundNames(p.Value, target);
  170. }
  171. else
  172. {
  173. GetBoundNames((RestElement) property, target);
  174. }
  175. }
  176. }
  177. else if (parameter is AssignmentPattern assignmentPattern)
  178. {
  179. parameter = assignmentPattern.Left;
  180. if (assignmentPattern.Right is ClassExpression classExpression)
  181. {
  182. // TODO check if there's more generic rule
  183. if (classExpression.Id is not null)
  184. {
  185. target.Add(classExpression.Id.Name!);
  186. }
  187. }
  188. continue;
  189. }
  190. break;
  191. }
  192. }
  193. internal static void BindingInitialization(
  194. this Expression? expression,
  195. Engine engine,
  196. JsValue value,
  197. EnvironmentRecord env)
  198. {
  199. if (expression is Identifier identifier)
  200. {
  201. var catchEnvRecord = (DeclarativeEnvironmentRecord) env;
  202. catchEnvRecord.CreateMutableBindingAndInitialize(identifier.Name, canBeDeleted: false, value);
  203. }
  204. else if (expression is BindingPattern bindingPattern)
  205. {
  206. BindingPatternAssignmentExpression.ProcessPatterns(
  207. engine,
  208. bindingPattern,
  209. value,
  210. env);
  211. }
  212. }
  213. /// <summary>
  214. /// https://tc39.es/ecma262/#sec-runtime-semantics-definemethod
  215. /// </summary>
  216. internal static Record DefineMethod(this ClassProperty m, ObjectInstance obj, ObjectInstance? functionPrototype = null)
  217. {
  218. var engine = obj.Engine;
  219. var property = TypeConverter.ToPropertyKey(m.GetKey(engine));
  220. var prototype = functionPrototype ?? engine.Realm.Intrinsics.Function.PrototypeObject;
  221. var function = m.Value as IFunction;
  222. if (function is null)
  223. {
  224. ExceptionHelper.ThrowSyntaxError(engine.Realm);
  225. }
  226. var functionDefinition = new JintFunctionDefinition(engine, function);
  227. var closure = new ScriptFunctionInstance(
  228. engine,
  229. functionDefinition,
  230. engine.ExecutionContext.LexicalEnvironment,
  231. functionDefinition.ThisMode,
  232. prototype);
  233. closure.MakeMethod(obj);
  234. return new Record(property, closure);
  235. }
  236. internal readonly struct Record
  237. {
  238. public Record(JsValue key, ScriptFunctionInstance closure)
  239. {
  240. Key = key;
  241. Closure = closure;
  242. }
  243. public readonly JsValue Key;
  244. public readonly ScriptFunctionInstance Closure;
  245. }
  246. }
  247. }