ClassDefinition.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. using Esprima;
  2. using Esprima.Ast;
  3. using Jint.Native.Object;
  4. using Jint.Runtime;
  5. using Jint.Runtime.Descriptors;
  6. using Jint.Runtime.Environments;
  7. using Jint.Runtime.Interpreter;
  8. using Jint.Runtime.Interpreter.Expressions;
  9. namespace Jint.Native.Function
  10. {
  11. internal sealed class ClassDefinition
  12. {
  13. private static readonly MethodDefinition _superConstructor;
  14. internal static CallExpression _defaultSuperCall;
  15. private static readonly MethodDefinition _emptyConstructor;
  16. internal readonly string? _className;
  17. private readonly Expression? _superClass;
  18. private readonly ClassBody _body;
  19. static ClassDefinition()
  20. {
  21. // generate missing constructor AST only once
  22. static MethodDefinition CreateConstructorMethodDefinition(string source)
  23. {
  24. var script = new JavaScriptParser().ParseScript(source);
  25. var classDeclaration = (ClassDeclaration) script.Body[0];
  26. return (MethodDefinition) classDeclaration.Body.Body[0];
  27. }
  28. _superConstructor = CreateConstructorMethodDefinition("class temp { constructor(...args) { super(...args); } }");
  29. _defaultSuperCall = (CallExpression) ((ExpressionStatement) _superConstructor.Value.Body.Body[0]).Expression;
  30. _emptyConstructor = CreateConstructorMethodDefinition("class temp { constructor() {} }");
  31. }
  32. public ClassDefinition(
  33. string? className,
  34. Expression? superClass,
  35. ClassBody body)
  36. {
  37. _className = className;
  38. _superClass = superClass;
  39. _body = body;
  40. }
  41. public void Initialize()
  42. {
  43. }
  44. /// <summary>
  45. /// https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
  46. /// </summary>
  47. public JsValue BuildConstructor(
  48. EvaluationContext context,
  49. EnvironmentRecord env)
  50. {
  51. // A class definition is always strict mode code.
  52. using var _ = new StrictModeScope(true, true);
  53. var engine = context.Engine;
  54. var classScope = JintEnvironment.NewDeclarativeEnvironment(engine, env);
  55. if (_className is not null)
  56. {
  57. classScope.CreateImmutableBinding(_className, true);
  58. }
  59. var outerPrivateEnvironment = engine.ExecutionContext.PrivateEnvironment;
  60. var classPrivateEnvironment = JintEnvironment.NewPrivateEnvironment(engine, outerPrivateEnvironment);
  61. /*
  62. 6. If ClassBodyopt is present, then
  63. a. For each String dn of the PrivateBoundIdentifiers of ClassBodyopt, do
  64. i. If classPrivateEnvironment.[[Names]] contains a Private Name whose [[Description]] is dn, then
  65. 1. Assert: This is only possible for getter/setter pairs.
  66. ii. Else,
  67. 1. Let name be a new Private Name whose [[Description]] value is dn.
  68. 2. Append name to classPrivateEnvironment.[[Names]].
  69. */
  70. ObjectInstance? protoParent = null;
  71. ObjectInstance? constructorParent = null;
  72. if (_superClass is null)
  73. {
  74. protoParent = engine.Realm.Intrinsics.Object.PrototypeObject;
  75. constructorParent = engine.Realm.Intrinsics.Function.PrototypeObject;
  76. }
  77. else
  78. {
  79. engine.UpdateLexicalEnvironment(classScope);
  80. var superclass = JintExpression.Build(_superClass).GetValue(context);
  81. engine.UpdateLexicalEnvironment(env);
  82. if (superclass.IsNull())
  83. {
  84. protoParent = null;
  85. constructorParent = engine.Realm.Intrinsics.Function.PrototypeObject;
  86. }
  87. else if (!superclass.IsConstructor)
  88. {
  89. ExceptionHelper.ThrowTypeError(engine.Realm, "super class is not a constructor");
  90. }
  91. else
  92. {
  93. var temp = superclass.Get("prototype");
  94. if (temp is ObjectInstance protoParentObject)
  95. {
  96. protoParent = protoParentObject;
  97. }
  98. else if (temp.IsNull())
  99. {
  100. // OK
  101. }
  102. else
  103. {
  104. ExceptionHelper.ThrowTypeError(engine.Realm, "cannot resolve super class prototype chain");
  105. return default;
  106. }
  107. constructorParent = (ObjectInstance) superclass;
  108. }
  109. }
  110. var proto = new ObjectInstance(engine)
  111. {
  112. _prototype = protoParent
  113. };
  114. MethodDefinition? constructor = null;
  115. var classBody = _body.Body;
  116. for (var i = 0; i < classBody.Count; ++i)
  117. {
  118. if (classBody[i] is MethodDefinition { Kind: PropertyKind.Constructor } c)
  119. {
  120. constructor = c;
  121. break;
  122. }
  123. }
  124. constructor ??= _superClass != null
  125. ? _superConstructor
  126. : _emptyConstructor;
  127. engine.UpdateLexicalEnvironment(classScope);
  128. ScriptFunctionInstance F;
  129. try
  130. {
  131. var constructorInfo = constructor.DefineMethod(proto, constructorParent);
  132. F = constructorInfo.Closure;
  133. var name = env is ModuleEnvironmentRecord ? _className : _className ?? "";
  134. if (name is not null)
  135. {
  136. F.SetFunctionName(name);
  137. }
  138. F.MakeConstructor(writableProperty: false, proto);
  139. F._constructorKind = _superClass is null ? ConstructorKind.Base : ConstructorKind.Derived;
  140. F.MakeClassConstructor();
  141. proto.CreateMethodProperty(CommonProperties.Constructor, F);
  142. foreach (var classProperty in _body.Body)
  143. {
  144. if (classProperty is not MethodDefinition m || m.Kind == PropertyKind.Constructor)
  145. {
  146. continue;
  147. }
  148. var target = !m.Static ? proto : F;
  149. var value = MethodDefinitionEvaluation(engine, target, m);
  150. if (engine._activeEvaluationContext!.IsAbrupt())
  151. {
  152. return value;
  153. }
  154. }
  155. }
  156. finally
  157. {
  158. engine.UpdateLexicalEnvironment(env);
  159. engine.UpdatePrivateEnvironment(outerPrivateEnvironment);
  160. }
  161. if (_className is not null)
  162. {
  163. classScope.InitializeBinding(_className, F);
  164. }
  165. /*
  166. 28. Set F.[[PrivateMethods]] to instancePrivateMethods.
  167. 29. Set F.[[Fields]] to instanceFields.
  168. 30. For each PrivateElement method of staticPrivateMethods, do
  169. a. Perform ! PrivateMethodOrAccessorAdd(method, F).
  170. 31. For each element fieldRecord of staticFields, do
  171. a. Let result be DefineField(F, fieldRecord).
  172. b. If result is an abrupt completion, then
  173. i. Set the running execution context's PrivateEnvironment to outerPrivateEnvironment.
  174. ii. Return result.
  175. */
  176. engine.UpdatePrivateEnvironment(outerPrivateEnvironment);
  177. return F;
  178. }
  179. /// <summary>
  180. /// https://tc39.es/ecma262/#sec-runtime-semantics-methoddefinitionevaluation
  181. /// </summary>
  182. private static JsValue MethodDefinitionEvaluation(
  183. Engine engine,
  184. ObjectInstance obj,
  185. MethodDefinition method)
  186. {
  187. if (method.Kind != PropertyKind.Get && method.Kind != PropertyKind.Set)
  188. {
  189. var methodDef = method.DefineMethod(obj);
  190. methodDef.Closure.SetFunctionName(methodDef.Key);
  191. var desc = new PropertyDescriptor(methodDef.Closure, PropertyFlag.NonEnumerable);
  192. obj.DefinePropertyOrThrow(methodDef.Key, desc);
  193. }
  194. else
  195. {
  196. var value = method.TryGetKey(engine);
  197. if (engine._activeEvaluationContext!.IsAbrupt())
  198. {
  199. return value;
  200. }
  201. var propKey = TypeConverter.ToPropertyKey(value);
  202. var function = method.Value as IFunction;
  203. if (function is null)
  204. {
  205. ExceptionHelper.ThrowSyntaxError(obj.Engine.Realm);
  206. }
  207. var closure = new ScriptFunctionInstance(
  208. obj.Engine,
  209. function,
  210. obj.Engine.ExecutionContext.LexicalEnvironment,
  211. true);
  212. closure.SetFunctionName(propKey, method.Kind == PropertyKind.Get ? "get" : "set");
  213. closure.MakeMethod(obj);
  214. var propDesc = new GetSetPropertyDescriptor(
  215. method.Kind == PropertyKind.Get ? closure : null,
  216. method.Kind == PropertyKind.Set ? closure : null,
  217. PropertyFlag.Configurable);
  218. obj.DefinePropertyOrThrow(propKey, propDesc);
  219. }
  220. return obj;
  221. }
  222. }
  223. }