ClassDefinition.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. #nullable enable
  2. using Esprima;
  3. using Esprima.Ast;
  4. using Jint.Native.Object;
  5. using Jint.Runtime;
  6. using Jint.Runtime.Descriptors;
  7. using Jint.Runtime.Environments;
  8. using Jint.Runtime.Interpreter;
  9. using Jint.Runtime.Interpreter.Expressions;
  10. namespace Jint.Native.Function
  11. {
  12. internal sealed class ClassDefinition
  13. {
  14. private static readonly MethodDefinition _superConstructor;
  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 parser = new JavaScriptParser(source);
  25. var script = parser.ParseScript();
  26. var classDeclaration = (ClassDeclaration) script.Body[0];
  27. return (MethodDefinition) classDeclaration.Body.Body[0]!;
  28. }
  29. _superConstructor = CreateConstructorMethodDefinition("class temp { constructor(...args) { super(...args); } }");
  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 Completion 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(engine, _superClass).GetValue(context).Value;
  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. if (_className is not null)
  134. {
  135. F.SetFunctionName(_className);
  136. }
  137. F.MakeConstructor(writableProperty: false, proto);
  138. F._constructorKind = _superClass is null ? ConstructorKind.Base : ConstructorKind.Derived;
  139. F.MakeClassConstructor();
  140. proto.CreateMethodProperty(CommonProperties.Constructor, F);
  141. foreach (var classProperty in _body.Body)
  142. {
  143. if (classProperty is not MethodDefinition m || m.Kind == PropertyKind.Constructor)
  144. {
  145. continue;
  146. }
  147. var target = !m.Static ? proto : F;
  148. var completion = MethodDefinitionEvaluation(engine, target, m);
  149. if (completion.IsAbrupt())
  150. {
  151. return completion;
  152. }
  153. }
  154. }
  155. finally
  156. {
  157. engine.UpdateLexicalEnvironment(env);
  158. engine.UpdatePrivateEnvironment(outerPrivateEnvironment);
  159. }
  160. if (_className is not null)
  161. {
  162. classScope.InitializeBinding(_className, F);
  163. }
  164. /*
  165. 28. Set F.[[PrivateMethods]] to instancePrivateMethods.
  166. 29. Set F.[[Fields]] to instanceFields.
  167. 30. For each PrivateElement method of staticPrivateMethods, do
  168. a. Perform ! PrivateMethodOrAccessorAdd(method, F).
  169. 31. For each element fieldRecord of staticFields, do
  170. a. Let result be DefineField(F, fieldRecord).
  171. b. If result is an abrupt completion, then
  172. i. Set the running execution context's PrivateEnvironment to outerPrivateEnvironment.
  173. ii. Return result.
  174. */
  175. engine.UpdatePrivateEnvironment(outerPrivateEnvironment);
  176. return new Completion(CompletionType.Normal, F, _body.Location);
  177. }
  178. /// <summary>
  179. /// https://tc39.es/ecma262/#sec-runtime-semantics-methoddefinitionevaluation
  180. /// </summary>
  181. private static Completion MethodDefinitionEvaluation(
  182. Engine engine,
  183. ObjectInstance obj,
  184. MethodDefinition method)
  185. {
  186. if (method.Kind != PropertyKind.Get && method.Kind != PropertyKind.Set)
  187. {
  188. var methodDef = method.DefineMethod(obj);
  189. methodDef.Closure.SetFunctionName(methodDef.Key);
  190. var desc = new PropertyDescriptor(methodDef.Closure, PropertyFlag.NonEnumerable);
  191. obj.DefinePropertyOrThrow(methodDef.Key, desc);
  192. }
  193. else
  194. {
  195. var completion = method.TryGetKey(engine);
  196. if (completion.IsAbrupt())
  197. {
  198. return completion;
  199. }
  200. var propKey = TypeConverter.ToPropertyKey(completion.Value);
  201. var function = method.Value as IFunction;
  202. if (function is null)
  203. {
  204. ExceptionHelper.ThrowSyntaxError(obj.Engine.Realm);
  205. }
  206. var closure = new ScriptFunctionInstance(
  207. obj.Engine,
  208. function,
  209. obj.Engine.ExecutionContext.LexicalEnvironment,
  210. true);
  211. closure.SetFunctionName(propKey, method.Kind == PropertyKind.Get ? "get" : "set");
  212. closure.MakeMethod(obj);
  213. var propDesc = new GetSetPropertyDescriptor(
  214. method.Kind == PropertyKind.Get ? closure : null,
  215. method.Kind == PropertyKind.Set ? closure : null,
  216. PropertyFlag.Configurable);
  217. obj.DefinePropertyOrThrow(propKey, propDesc);
  218. }
  219. return new Completion(CompletionType.Normal, obj, method.Location);
  220. }
  221. }
  222. }