ClassDefinition.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. using Esprima;
  2. using Esprima.Ast;
  3. using Esprima.Utils;
  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. using Environment = Jint.Runtime.Environments.Environment;
  11. namespace Jint.Native.Function;
  12. internal sealed class ClassDefinition
  13. {
  14. private static readonly MethodDefinition _superConstructor;
  15. internal static CallExpression _defaultSuperCall;
  16. internal static readonly MethodDefinition _emptyConstructor;
  17. internal readonly string? _className;
  18. private readonly Expression? _superClass;
  19. private readonly ClassBody _body;
  20. static ClassDefinition()
  21. {
  22. // generate missing constructor AST only once
  23. static MethodDefinition CreateConstructorMethodDefinition(string source)
  24. {
  25. var script = new JavaScriptParser().ParseScript(source);
  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. _defaultSuperCall = (CallExpression) ((ExpressionStatement) _superConstructor.Value.Body.Body[0]).Expression;
  31. _emptyConstructor = CreateConstructorMethodDefinition("class temp { constructor() {} }");
  32. }
  33. public ClassDefinition(
  34. string? className,
  35. Expression? superClass,
  36. ClassBody body)
  37. {
  38. _className = className;
  39. _superClass = superClass;
  40. _body = body;
  41. }
  42. /// <summary>
  43. /// https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
  44. /// </summary>
  45. public JsValue BuildConstructor(EvaluationContext context, Environment 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 classEnv = JintEnvironment.NewDeclarativeEnvironment(engine, env);
  51. if (_className is not null)
  52. {
  53. classEnv.CreateImmutableBinding(_className, true);
  54. }
  55. var outerPrivateEnvironment = engine.ExecutionContext.PrivateEnvironment;
  56. var classPrivateEnvironment = JintEnvironment.NewPrivateEnvironment(engine, outerPrivateEnvironment);
  57. ObjectInstance? protoParent = null;
  58. ObjectInstance? constructorParent = null;
  59. if (_superClass is null)
  60. {
  61. protoParent = engine.Realm.Intrinsics.Object.PrototypeObject;
  62. constructorParent = engine.Realm.Intrinsics.Function.PrototypeObject;
  63. }
  64. else
  65. {
  66. engine.UpdateLexicalEnvironment(classEnv);
  67. var superclass = JintExpression.Build(_superClass).GetValue(context);
  68. engine.UpdateLexicalEnvironment(env);
  69. if (superclass.IsNull())
  70. {
  71. protoParent = null;
  72. constructorParent = engine.Realm.Intrinsics.Function.PrototypeObject;
  73. }
  74. else if (!superclass.IsConstructor)
  75. {
  76. ExceptionHelper.ThrowTypeError(engine.Realm, "super class is not a constructor");
  77. }
  78. else
  79. {
  80. var temp = superclass.Get(CommonProperties.Prototype);
  81. if (temp is ObjectInstance protoParentObject)
  82. {
  83. protoParent = protoParentObject;
  84. }
  85. else if (temp.IsNull())
  86. {
  87. // OK
  88. }
  89. else
  90. {
  91. ExceptionHelper.ThrowTypeError(engine.Realm, "cannot resolve super class prototype chain");
  92. return default;
  93. }
  94. constructorParent = (ObjectInstance) superclass;
  95. }
  96. }
  97. ObjectInstance proto = new JsObject(engine) { _prototype = protoParent };
  98. var privateBoundIdentifiers = new HashSet<PrivateIdentifier>(PrivateIdentifierNameComparer._instance);
  99. MethodDefinition? constructor = null;
  100. ref readonly var elements = ref _body.Body;
  101. var classBody = elements;
  102. for (var i = 0; i < classBody.Count; ++i)
  103. {
  104. var element = classBody[i];
  105. if (element is MethodDefinition { Kind: PropertyKind.Constructor } c)
  106. {
  107. constructor = c;
  108. }
  109. privateBoundIdentifiers.Clear();
  110. element.PrivateBoundIdentifiers(privateBoundIdentifiers);
  111. foreach (var name in privateBoundIdentifiers)
  112. {
  113. classPrivateEnvironment.Names.Add(name, new PrivateName(name));
  114. }
  115. }
  116. constructor ??= _superClass != null
  117. ? _superConstructor
  118. : _emptyConstructor;
  119. engine.UpdateLexicalEnvironment(classEnv);
  120. engine.UpdatePrivateEnvironment(classPrivateEnvironment);
  121. ScriptFunction F;
  122. try
  123. {
  124. var constructorInfo = constructor.DefineMethod(proto, constructorParent);
  125. F = constructorInfo.Closure;
  126. F.SetFunctionName(_className ?? "");
  127. F.MakeConstructor(writableProperty: false, proto);
  128. F._constructorKind = _superClass is null ? ConstructorKind.Base : ConstructorKind.Derived;
  129. F.MakeClassConstructor();
  130. proto.CreateMethodProperty(CommonProperties.Constructor, F);
  131. var instancePrivateMethods = new List<PrivateElement>();
  132. var staticPrivateMethods = new List<PrivateElement>();
  133. var instanceFields = new List<ClassFieldDefinition>();
  134. var staticElements = new List<object>();
  135. foreach (var e in elements)
  136. {
  137. if (e is MethodDefinition { Kind: PropertyKind.Constructor })
  138. {
  139. continue;
  140. }
  141. var isStatic = e is MethodDefinition { Static: true } or AccessorProperty { Static: true } or PropertyDefinition { Static: true } or StaticBlock;
  142. var target = !isStatic ? proto : F;
  143. var element = ClassElementEvaluation(engine, target, e);
  144. if (element is PrivateElement privateElement)
  145. {
  146. var container = !isStatic ? instancePrivateMethods : staticPrivateMethods;
  147. var index = container.FindIndex(x => string.Equals(x.Key.Description, privateElement.Key.Description, StringComparison.Ordinal));
  148. if (index != -1)
  149. {
  150. var pe = container[index];
  151. var combined = privateElement.Get is null
  152. ? new PrivateElement { Key = pe.Key, Kind = PrivateElementKind.Accessor, Get = pe.Get, Set = privateElement.Set }
  153. : new PrivateElement { Key = pe.Key, Kind = PrivateElementKind.Accessor, Get = privateElement.Get, Set = pe.Set };
  154. container[index] = combined;
  155. }
  156. else
  157. {
  158. container.Add(privateElement);
  159. }
  160. }
  161. else if (element is ClassFieldDefinition classFieldDefinition)
  162. {
  163. if (!isStatic)
  164. {
  165. instanceFields.Add(classFieldDefinition);
  166. }
  167. else
  168. {
  169. staticElements.Add(element);
  170. }
  171. }
  172. else if (element is ClassStaticBlockDefinition)
  173. {
  174. staticElements.Add(element);
  175. }
  176. }
  177. if (_className is not null)
  178. {
  179. classEnv.InitializeBinding(_className, F);
  180. }
  181. F._privateMethods = instancePrivateMethods;
  182. F._fields = instanceFields;
  183. for (var i = 0; i < staticPrivateMethods.Count; i++)
  184. {
  185. F.PrivateMethodOrAccessorAdd(staticPrivateMethods[i]);
  186. }
  187. for (var i = 0; i < staticElements.Count; i++)
  188. {
  189. var elementRecord = staticElements[i];
  190. if (elementRecord is ClassFieldDefinition classFieldDefinition)
  191. {
  192. ObjectInstance.DefineField(F, classFieldDefinition);
  193. }
  194. else
  195. {
  196. engine.Call(((ClassStaticBlockDefinition) elementRecord).BodyFunction, F);
  197. }
  198. }
  199. }
  200. finally
  201. {
  202. engine.UpdateLexicalEnvironment(env);
  203. engine.UpdatePrivateEnvironment(outerPrivateEnvironment);
  204. }
  205. return F;
  206. }
  207. /// <summary>
  208. /// https://tc39.es/ecma262/#sec-static-semantics-classelementevaluation
  209. /// </summary>
  210. private static object? ClassElementEvaluation(Engine engine, ObjectInstance target, ClassElement e)
  211. {
  212. return e switch
  213. {
  214. PropertyDefinition p => ClassFieldDefinitionEvaluation(engine, target, p),
  215. MethodDefinition m => MethodDefinitionEvaluation(engine, target, m, enumerable: false),
  216. StaticBlock s => ClassStaticBlockDefinitionEvaluation(engine, target, s),
  217. _ => null
  218. };
  219. }
  220. /// <summary>
  221. /// /https://tc39.es/ecma262/#sec-runtime-semantics-classfielddefinitionevaluation
  222. /// </summary>
  223. private static ClassFieldDefinition ClassFieldDefinitionEvaluation(Engine engine, ObjectInstance homeObject, PropertyDefinition fieldDefinition)
  224. {
  225. var name = fieldDefinition.GetKey(engine);
  226. ScriptFunction? initializer = null;
  227. if (fieldDefinition.Value is not null)
  228. {
  229. var intrinsics = engine.Realm.Intrinsics;
  230. var env = engine.ExecutionContext.LexicalEnvironment;
  231. var privateEnv = engine.ExecutionContext.PrivateEnvironment;
  232. var definition = new JintFunctionDefinition(new ClassFieldFunction(fieldDefinition.Value));
  233. initializer = intrinsics.Function.OrdinaryFunctionCreate(intrinsics.Function.PrototypeObject, definition, FunctionThisMode.Global, env, privateEnv);
  234. initializer.MakeMethod(homeObject);
  235. initializer._classFieldInitializerName = name;
  236. }
  237. return new ClassFieldDefinition { Name = name, Initializer = initializer };
  238. }
  239. private sealed class ClassFieldFunction : Node, IFunction
  240. {
  241. private readonly NodeList<Node> _nodeList;
  242. private readonly BlockStatement _statement;
  243. public ClassFieldFunction(Expression expression) : base(Nodes.ExpressionStatement)
  244. {
  245. var nodeList = NodeList.Create<Statement>(new [] { new ReturnStatement(expression) });
  246. _statement = new BlockStatement(nodeList);
  247. }
  248. protected override object Accept(AstVisitor visitor) => throw new NotImplementedException();
  249. public Identifier? Id => null;
  250. public ref readonly NodeList<Node> Params => ref _nodeList;
  251. public StatementListItem Body => _statement;
  252. public bool Generator => false;
  253. public bool Expression => false;
  254. public bool Strict => true;
  255. public bool Async => false;
  256. }
  257. /// <summary>
  258. /// https://tc39.es/ecma262/#sec-runtime-semantics-classstaticblockdefinitionevaluation
  259. /// </summary>
  260. private static ClassStaticBlockDefinition ClassStaticBlockDefinitionEvaluation(Engine engine, ObjectInstance homeObject, StaticBlock o)
  261. {
  262. var intrinsics = engine.Realm.Intrinsics;
  263. var definition = new JintFunctionDefinition(new ClassStaticBlockFunction(o));
  264. var lex = engine.ExecutionContext.LexicalEnvironment;
  265. var privateEnv = engine.ExecutionContext.PrivateEnvironment;
  266. var bodyFunction = intrinsics.Function.OrdinaryFunctionCreate(intrinsics.Function.PrototypeObject, definition, FunctionThisMode.Global, lex, privateEnv);
  267. bodyFunction.MakeMethod(homeObject);
  268. return new ClassStaticBlockDefinition { BodyFunction = bodyFunction };
  269. }
  270. private sealed class ClassStaticBlockFunction : Node, IFunction
  271. {
  272. private readonly BlockStatement _statement;
  273. private readonly NodeList<Node> _params;
  274. public ClassStaticBlockFunction(StaticBlock staticBlock) : base(Nodes.StaticBlock)
  275. {
  276. _statement = new BlockStatement(staticBlock.Body);
  277. _params = new NodeList<Node>();
  278. }
  279. protected override object Accept(AstVisitor visitor) => throw new NotImplementedException();
  280. public Identifier? Id => null;
  281. public ref readonly NodeList<Node> Params => ref _params;
  282. public StatementListItem Body => _statement;
  283. public bool Generator => false;
  284. public bool Expression => false;
  285. public bool Strict => false;
  286. public bool Async => false;
  287. }
  288. /// <summary>
  289. /// https://tc39.es/ecma262/#sec-runtime-semantics-methoddefinitionevaluation
  290. /// </summary>
  291. internal static PrivateElement? MethodDefinitionEvaluation<T>(
  292. Engine engine,
  293. ObjectInstance obj,
  294. T method,
  295. bool enumerable) where T : IProperty
  296. {
  297. var function = method.Value as IFunction;
  298. if (function is null)
  299. {
  300. ExceptionHelper.ThrowSyntaxError(obj.Engine.Realm);
  301. }
  302. if (method.Kind != PropertyKind.Get && method.Kind != PropertyKind.Set && !function.Generator)
  303. {
  304. var methodDef = method.DefineMethod(obj);
  305. methodDef.Closure.SetFunctionName(methodDef.Key);
  306. return DefineMethodProperty(obj, methodDef.Key, methodDef.Closure, enumerable);
  307. }
  308. var getter = method.Kind == PropertyKind.Get;
  309. var definition = new JintFunctionDefinition(function);
  310. var intrinsics = engine.Realm.Intrinsics;
  311. var value = method.TryGetKey(engine);
  312. var propKey = TypeConverter.ToPropertyKey(value);
  313. var env = engine.ExecutionContext.LexicalEnvironment;
  314. var privateEnv = engine.ExecutionContext.PrivateEnvironment;
  315. if (function.Generator)
  316. {
  317. var closure = intrinsics.Function.OrdinaryFunctionCreate(intrinsics.GeneratorFunction.PrototypeObject, definition, definition.ThisMode, env, privateEnv);
  318. closure.MakeMethod(obj);
  319. closure.SetFunctionName(propKey);
  320. var prototype = ObjectInstance.OrdinaryObjectCreate(engine, intrinsics.GeneratorFunction.PrototypeObject.PrototypeObject);
  321. closure.DefinePropertyOrThrow(CommonProperties.Prototype, new PropertyDescriptor(prototype, PropertyFlag.Writable));
  322. return DefineMethodProperty(obj, propKey, closure, enumerable);
  323. }
  324. else
  325. {
  326. var closure = intrinsics.Function.OrdinaryFunctionCreate(intrinsics.Function.PrototypeObject, definition, definition.ThisMode, env, privateEnv);
  327. closure.MakeMethod(obj);
  328. closure.SetFunctionName(propKey, getter ? "get" : "set");
  329. if (method.Key is PrivateIdentifier privateIdentifier)
  330. {
  331. return new PrivateElement
  332. {
  333. Key = privateEnv!.Names[privateIdentifier],
  334. Kind = PrivateElementKind.Accessor,
  335. Get = getter ? closure : null,
  336. Set = !getter ? closure : null
  337. };
  338. }
  339. var propDesc = new GetSetPropertyDescriptor(
  340. getter ? closure : null,
  341. !getter ? closure : null,
  342. PropertyFlag.Configurable);
  343. obj.DefinePropertyOrThrow(propKey, propDesc);
  344. }
  345. return null;
  346. }
  347. /// <summary>
  348. /// https://tc39.es/ecma262/#sec-definemethodproperty
  349. /// </summary>
  350. private static PrivateElement? DefineMethodProperty(ObjectInstance homeObject, JsValue key, ScriptFunction closure, bool enumerable)
  351. {
  352. if (key.IsPrivateName())
  353. {
  354. return new PrivateElement { Key = (PrivateName) key, Kind = PrivateElementKind.Method, Value = closure };
  355. }
  356. var desc = new PropertyDescriptor(closure, enumerable ? PropertyFlag.ConfigurableEnumerableWritable : PropertyFlag.NonEnumerable);
  357. homeObject.DefinePropertyOrThrow(key, desc);
  358. return null;
  359. }
  360. }