EsprimaExtensions.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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. or Nodes.MemberExpression)
  45. {
  46. var context = engine._activeEvaluationContext;
  47. propertyKey = TypeConverter.ToPropertyKey(JintExpression.Build(engine, expression).GetValue(context).Value);
  48. return true;
  49. }
  50. propertyKey = string.Empty;
  51. return false;
  52. }
  53. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  54. internal static bool IsFunctionDefinition<T>(this T node) where T : Node
  55. {
  56. var type = node.Type;
  57. return type
  58. is Nodes.FunctionExpression
  59. or Nodes.ArrowFunctionExpression
  60. or Nodes.ArrowParameterPlaceHolder
  61. or Nodes.ClassExpression;
  62. }
  63. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  64. internal static bool HasName<T>(this T node) where T : Node
  65. {
  66. if (!node.IsFunctionDefinition())
  67. {
  68. return false;
  69. }
  70. if ((node as IFunction)?.Id is not null)
  71. {
  72. return true;
  73. }
  74. if ((node as ClassExpression)?.Id is not null)
  75. {
  76. return true;
  77. }
  78. return false;
  79. }
  80. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  81. internal static bool IsAnonymousFunctionDefinition<T>(this T node) where T : Node
  82. {
  83. if (!node.IsFunctionDefinition())
  84. {
  85. return false;
  86. }
  87. if (node.HasName())
  88. {
  89. return false;
  90. }
  91. return true;
  92. }
  93. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  94. internal static bool IsOptional<T>(this T node) where T : Expression
  95. {
  96. switch (node)
  97. {
  98. case MemberExpression { Optional: true }:
  99. case CallExpression { Optional: true }:
  100. return true;
  101. default:
  102. return false;
  103. }
  104. }
  105. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  106. internal static string LiteralKeyToString(Literal literal)
  107. {
  108. // prevent conversion to scientific notation
  109. if (literal.Value is double d)
  110. {
  111. return TypeConverter.ToString(d);
  112. }
  113. return literal.Value as string ?? Convert.ToString(literal.Value, provider: null);
  114. }
  115. internal static void GetBoundNames(this VariableDeclaration variableDeclaration, List<string> target)
  116. {
  117. ref readonly var declarations = ref variableDeclaration.Declarations;
  118. for (var i = 0; i < declarations.Count; i++)
  119. {
  120. var declaration = declarations[i];
  121. GetBoundNames(declaration.Id, target);
  122. }
  123. }
  124. internal static void GetBoundNames(this Node? parameter, List<string> target)
  125. {
  126. if (parameter is null || parameter.Type == Nodes.Literal)
  127. {
  128. return;
  129. }
  130. // try to get away without a loop
  131. if (parameter is Identifier id)
  132. {
  133. target.Add(id.Name!);
  134. return;
  135. }
  136. if (parameter is VariableDeclaration variableDeclaration)
  137. {
  138. variableDeclaration.GetBoundNames(target);
  139. return;
  140. }
  141. while (true)
  142. {
  143. if (parameter is Identifier identifier)
  144. {
  145. target.Add(identifier.Name!);
  146. return;
  147. }
  148. if (parameter is RestElement restElement)
  149. {
  150. parameter = restElement.Argument;
  151. continue;
  152. }
  153. else if (parameter is ArrayPattern arrayPattern)
  154. {
  155. ref readonly var arrayPatternElements = ref arrayPattern.Elements;
  156. for (var i = 0; i < arrayPatternElements.Count; i++)
  157. {
  158. var expression = arrayPatternElements[i];
  159. GetBoundNames(expression, target);
  160. }
  161. }
  162. else if (parameter is ObjectPattern objectPattern)
  163. {
  164. ref readonly var objectPatternProperties = ref objectPattern.Properties;
  165. for (var i = 0; i < objectPatternProperties.Count; i++)
  166. {
  167. var property = objectPatternProperties[i];
  168. if (property is Property p)
  169. {
  170. GetBoundNames(p.Value, target);
  171. }
  172. else
  173. {
  174. GetBoundNames((RestElement) property, target);
  175. }
  176. }
  177. }
  178. else if (parameter is AssignmentPattern assignmentPattern)
  179. {
  180. parameter = assignmentPattern.Left;
  181. continue;
  182. }
  183. break;
  184. }
  185. }
  186. internal static void BindingInitialization(
  187. this Expression? expression,
  188. EvaluationContext context,
  189. JsValue value,
  190. EnvironmentRecord env)
  191. {
  192. if (expression is Identifier identifier)
  193. {
  194. var catchEnvRecord = (DeclarativeEnvironmentRecord) env;
  195. catchEnvRecord.CreateMutableBindingAndInitialize(identifier.Name, canBeDeleted: false, value);
  196. }
  197. else if (expression is BindingPattern bindingPattern)
  198. {
  199. BindingPatternAssignmentExpression.ProcessPatterns(
  200. context,
  201. bindingPattern,
  202. value,
  203. env);
  204. }
  205. }
  206. /// <summary>
  207. /// https://tc39.es/ecma262/#sec-runtime-semantics-definemethod
  208. /// </summary>
  209. internal static Record DefineMethod(this ClassProperty m, ObjectInstance obj, ObjectInstance? functionPrototype = null)
  210. {
  211. var engine = obj.Engine;
  212. var property = TypeConverter.ToPropertyKey(m.GetKey(engine));
  213. var prototype = functionPrototype ?? engine.Realm.Intrinsics.Function.PrototypeObject;
  214. var function = m.Value as IFunction;
  215. if (function is null)
  216. {
  217. ExceptionHelper.ThrowSyntaxError(engine.Realm);
  218. }
  219. var functionDefinition = new JintFunctionDefinition(engine, function);
  220. var closure = new ScriptFunctionInstance(
  221. engine,
  222. functionDefinition,
  223. engine.ExecutionContext.LexicalEnvironment,
  224. functionDefinition.ThisMode,
  225. prototype);
  226. closure.MakeMethod(obj);
  227. return new Record(property, closure);
  228. }
  229. internal readonly record struct Record(JsValue Key, ScriptFunctionInstance Closure);
  230. }
  231. }