ClassDefinition.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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. Identifier? className,
  33. Expression? superClass,
  34. ClassBody body)
  35. {
  36. _className = className?.Name;
  37. _superClass = superClass;
  38. _body = body;
  39. }
  40. /// <summary>
  41. /// https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
  42. /// </summary>
  43. public ScriptFunctionInstance BuildConstructor(
  44. EvaluationContext context,
  45. EnvironmentRecord env)
  46. {
  47. // A class definition is always strict mode code.
  48. using var _ = new StrictModeScope(true, true);
  49. var engine = context.Engine;
  50. var classScope = JintEnvironment.NewDeclarativeEnvironment(engine, env);
  51. if (_className is not null)
  52. {
  53. classScope.CreateImmutableBinding(_className, true);
  54. }
  55. ObjectInstance? protoParent = null;
  56. ObjectInstance? constructorParent = null;
  57. if (_superClass is null)
  58. {
  59. protoParent = engine.Realm.Intrinsics.Object.PrototypeObject;
  60. constructorParent = engine.Realm.Intrinsics.Function.PrototypeObject;
  61. }
  62. else
  63. {
  64. engine.UpdateLexicalEnvironment(classScope);
  65. var superclass = JintExpression.Build(engine, _superClass).GetValue(context).Value;
  66. engine.UpdateLexicalEnvironment(env);
  67. if (superclass.IsNull())
  68. {
  69. protoParent = null;
  70. constructorParent = engine.Realm.Intrinsics.Function.PrototypeObject;
  71. }
  72. else if (!superclass!.IsConstructor)
  73. {
  74. ExceptionHelper.ThrowTypeError(engine.Realm, "super class is not a constructor");
  75. }
  76. else
  77. {
  78. var temp = superclass.Get("prototype");
  79. if (temp is ObjectInstance protoParentObject)
  80. {
  81. protoParent = protoParentObject;
  82. }
  83. else if (temp._type == InternalTypes.Null)
  84. {
  85. // OK
  86. }
  87. else
  88. {
  89. ExceptionHelper.ThrowTypeError(engine.Realm);
  90. return null!;
  91. }
  92. constructorParent = (ObjectInstance) superclass;
  93. }
  94. }
  95. var proto = new ObjectInstance(engine)
  96. {
  97. _prototype = protoParent
  98. };
  99. MethodDefinition? constructor = null;
  100. var classBody = _body.Body;
  101. for (var i = 0; i < classBody.Count; ++i)
  102. {
  103. if (classBody[i].Kind == PropertyKind.Constructor)
  104. {
  105. constructor = (MethodDefinition) classBody[i];
  106. break;
  107. }
  108. }
  109. constructor ??= _superClass != null
  110. ? _superConstructor
  111. : _emptyConstructor;
  112. engine.UpdateLexicalEnvironment(classScope);
  113. ScriptFunctionInstance F;
  114. try
  115. {
  116. var constructorInfo = constructor.DefineMethod(proto, constructorParent);
  117. F = constructorInfo.Closure;
  118. if (_className is not null)
  119. {
  120. F.SetFunctionName(_className);
  121. }
  122. F.MakeConstructor(false, proto);
  123. F._constructorKind = _superClass is null ? ConstructorKind.Base : ConstructorKind.Derived;
  124. F.MakeClassConstructor();
  125. proto.CreateMethodProperty(CommonProperties.Constructor, F);
  126. foreach (var classProperty in _body.Body)
  127. {
  128. if (classProperty is not MethodDefinition m || m.Kind == PropertyKind.Constructor)
  129. {
  130. continue;
  131. }
  132. var target = !m.Static ? proto : F;
  133. PropertyDefinitionEvaluation(engine, target, m);
  134. }
  135. }
  136. finally
  137. {
  138. engine.UpdateLexicalEnvironment(env);
  139. }
  140. if (_className is not null)
  141. {
  142. classScope.InitializeBinding(_className, F);
  143. }
  144. return F;
  145. }
  146. /// <summary>
  147. /// https://tc39.es/ecma262/#sec-method-definitions-runtime-semantics-propertydefinitionevaluation
  148. /// </summary>
  149. private static void PropertyDefinitionEvaluation(
  150. Engine engine,
  151. ObjectInstance obj,
  152. MethodDefinition method)
  153. {
  154. if (method.Kind != PropertyKind.Get && method.Kind != PropertyKind.Set)
  155. {
  156. var methodDef = method.DefineMethod(obj);
  157. methodDef.Closure.SetFunctionName(methodDef.Key);
  158. var desc = new PropertyDescriptor(methodDef.Closure, PropertyFlag.NonEnumerable);
  159. obj.DefinePropertyOrThrow(methodDef.Key, desc);
  160. }
  161. else
  162. {
  163. var propKey = TypeConverter.ToPropertyKey(method.GetKey(engine));
  164. var function = method.Value as IFunction;
  165. if (function is null)
  166. {
  167. ExceptionHelper.ThrowSyntaxError(obj.Engine.Realm);
  168. }
  169. var closure = new ScriptFunctionInstance(
  170. obj.Engine,
  171. function,
  172. obj.Engine.ExecutionContext.LexicalEnvironment,
  173. true);
  174. closure.SetFunctionName(propKey, method.Kind == PropertyKind.Get ? "get" : "set");
  175. closure.MakeMethod(obj);
  176. var propDesc = new GetSetPropertyDescriptor(
  177. method.Kind == PropertyKind.Get ? closure : null,
  178. method.Kind == PropertyKind.Set ? closure : null,
  179. PropertyFlag.Configurable);
  180. obj.DefinePropertyOrThrow(propKey, propDesc);
  181. }
  182. }
  183. }
  184. }