ClassDefinition.cs 7.2 KB

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