ClassDefinition.cs 9.3 KB

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