EsprimaExtensions.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  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.Interpreter;
  11. using Jint.Runtime.Interpreter.Expressions;
  12. namespace Jint
  13. {
  14. public static class EsprimaExtensions
  15. {
  16. public static JsValue GetKey(this ClassProperty property, Engine engine) => GetKey(property.Key, engine, property.Computed);
  17. public static JsValue GetKey(this Expression expression, Engine engine, bool resolveComputed = false)
  18. {
  19. if (expression is Literal literal)
  20. {
  21. return LiteralKeyToString(literal);
  22. }
  23. if (!resolveComputed && expression is Identifier identifier)
  24. {
  25. return identifier.Name;
  26. }
  27. if (!resolveComputed || !TryGetComputedPropertyKey(expression, engine, out var propertyKey))
  28. {
  29. return ExceptionHelper.ThrowArgumentException<JsValue>("Unable to extract correct key, node type: " + expression.Type);
  30. }
  31. return propertyKey;
  32. }
  33. private static bool TryGetComputedPropertyKey<T>(T expression, Engine engine, out JsValue propertyKey)
  34. where T : Expression
  35. {
  36. if (expression.Type == Nodes.Identifier
  37. || expression.Type == Nodes.CallExpression
  38. || expression.Type == Nodes.BinaryExpression
  39. || expression.Type == Nodes.UpdateExpression
  40. || expression.Type == Nodes.AssignmentExpression
  41. || expression is StaticMemberExpression)
  42. {
  43. propertyKey = TypeConverter.ToPropertyKey(JintExpression.Build(engine, expression).GetValue());
  44. return true;
  45. }
  46. propertyKey = string.Empty;
  47. return false;
  48. }
  49. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  50. internal static bool IsFunctionWithName<T>(this T node) where T : Node
  51. {
  52. var type = node.Type;
  53. return type == Nodes.FunctionExpression
  54. || type == Nodes.ArrowFunctionExpression
  55. || type == Nodes.ArrowParameterPlaceHolder
  56. || type == Nodes.ClassExpression;
  57. }
  58. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  59. internal static string LiteralKeyToString(Literal literal)
  60. {
  61. // prevent conversion to scientific notation
  62. if (literal.Value is double d)
  63. {
  64. return TypeConverter.ToString(d);
  65. }
  66. return literal.Value as string ?? Convert.ToString(literal.Value, provider: null);
  67. }
  68. internal static void GetBoundNames(this VariableDeclaration variableDeclaration, List<string> target)
  69. {
  70. ref readonly var declarations = ref variableDeclaration.Declarations;
  71. for (var i = 0; i < declarations.Count; i++)
  72. {
  73. var declaration = declarations[i];
  74. GetBoundNames(declaration.Id, target);
  75. }
  76. }
  77. internal static void GetBoundNames(this Node? parameter, List<string> target)
  78. {
  79. if (parameter is null || parameter.Type == Nodes.Literal)
  80. {
  81. return;
  82. }
  83. // try to get away without a loop
  84. if (parameter is Identifier id)
  85. {
  86. target.Add(id.Name!);
  87. return;
  88. }
  89. while (true)
  90. {
  91. if (parameter is Identifier identifier)
  92. {
  93. target.Add(identifier.Name!);
  94. return;
  95. }
  96. if (parameter is RestElement restElement)
  97. {
  98. parameter = restElement.Argument;
  99. continue;
  100. }
  101. else if (parameter is ArrayPattern arrayPattern)
  102. {
  103. ref readonly var arrayPatternElements = ref arrayPattern.Elements;
  104. for (var i = 0; i < arrayPatternElements.Count; i++)
  105. {
  106. var expression = arrayPatternElements[i];
  107. GetBoundNames(expression, target);
  108. }
  109. }
  110. else if (parameter is ObjectPattern objectPattern)
  111. {
  112. ref readonly var objectPatternProperties = ref objectPattern.Properties;
  113. for (var i = 0; i < objectPatternProperties.Count; i++)
  114. {
  115. var property = objectPatternProperties[i];
  116. if (property is Property p)
  117. {
  118. GetBoundNames(p.Value, target);
  119. }
  120. else
  121. {
  122. GetBoundNames((RestElement) property, target);
  123. }
  124. }
  125. }
  126. else if (parameter is AssignmentPattern assignmentPattern)
  127. {
  128. parameter = assignmentPattern.Left;
  129. if (assignmentPattern.Right is ClassExpression classExpression)
  130. {
  131. // TODO check if there's more generic rule
  132. if (classExpression.Id is not null)
  133. {
  134. target.Add(classExpression.Id.Name!);
  135. }
  136. }
  137. continue;
  138. }
  139. break;
  140. }
  141. }
  142. /// <summary>
  143. /// https://tc39.es/ecma262/#sec-runtime-semantics-definemethod
  144. /// </summary>
  145. internal static Record DefineMethod(this ClassProperty m, ObjectInstance obj, ObjectInstance? functionPrototype = null)
  146. {
  147. var engine = obj.Engine;
  148. var property = TypeConverter.ToPropertyKey(m.GetKey(engine));
  149. var prototype = functionPrototype ?? engine.Function.PrototypeObject;
  150. var function = m.Value as IFunction ?? ExceptionHelper.ThrowSyntaxError<IFunction>(engine);
  151. var functionDefinition = new JintFunctionDefinition(engine, function);
  152. var functionThisMode = functionDefinition.Strict || engine._isStrict
  153. ? FunctionThisMode.Strict
  154. : FunctionThisMode.Global;
  155. var closure = new ScriptFunctionInstance(
  156. engine,
  157. functionDefinition,
  158. engine.ExecutionContext.LexicalEnvironment,
  159. functionThisMode,
  160. prototype);
  161. closure.MakeMethod(obj);
  162. return new Record(property, closure);
  163. }
  164. internal readonly struct Record
  165. {
  166. public Record(JsValue key, ScriptFunctionInstance closure)
  167. {
  168. Key = key;
  169. Closure = closure;
  170. }
  171. public readonly JsValue Key;
  172. public readonly ScriptFunctionInstance Closure;
  173. }
  174. }
  175. }