2
0

EsprimaExtensions.cs 8.4 KB

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