123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- using Esprima;
- using Esprima.Ast;
- using Jint.Native.Object;
- using Jint.Runtime;
- using Jint.Runtime.Descriptors;
- using Jint.Runtime.Environments;
- using Jint.Runtime.Interpreter;
- using Jint.Runtime.Interpreter.Expressions;
- namespace Jint.Native.Function
- {
- internal sealed class ClassDefinition
- {
- private static readonly MethodDefinition _superConstructor;
- internal static CallExpression _defaultSuperCall;
- private static readonly MethodDefinition _emptyConstructor;
- internal readonly string? _className;
- private readonly Expression? _superClass;
- private readonly ClassBody _body;
- static ClassDefinition()
- {
- // generate missing constructor AST only once
- static MethodDefinition CreateConstructorMethodDefinition(string source)
- {
- var script = new JavaScriptParser().ParseScript(source);
- var classDeclaration = (ClassDeclaration) script.Body[0];
- return (MethodDefinition) classDeclaration.Body.Body[0];
- }
- _superConstructor = CreateConstructorMethodDefinition("class temp { constructor(...args) { super(...args); } }");
- _defaultSuperCall = (CallExpression) ((ExpressionStatement) _superConstructor.Value.Body.Body[0]).Expression;
- _emptyConstructor = CreateConstructorMethodDefinition("class temp { constructor() {} }");
- }
- public ClassDefinition(
- string? className,
- Expression? superClass,
- ClassBody body)
- {
- _className = className;
- _superClass = superClass;
- _body = body;
- }
- public void Initialize()
- {
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
- /// </summary>
- public JsValue BuildConstructor(
- EvaluationContext context,
- EnvironmentRecord env)
- {
- // A class definition is always strict mode code.
- using var _ = new StrictModeScope(true, true);
- var engine = context.Engine;
- var classScope = JintEnvironment.NewDeclarativeEnvironment(engine, env);
- if (_className is not null)
- {
- classScope.CreateImmutableBinding(_className, true);
- }
- var outerPrivateEnvironment = engine.ExecutionContext.PrivateEnvironment;
- var classPrivateEnvironment = JintEnvironment.NewPrivateEnvironment(engine, outerPrivateEnvironment);
- /*
- 6. If ClassBodyopt is present, then
- a. For each String dn of the PrivateBoundIdentifiers of ClassBodyopt, do
- i. If classPrivateEnvironment.[[Names]] contains a Private Name whose [[Description]] is dn, then
- 1. Assert: This is only possible for getter/setter pairs.
- ii. Else,
- 1. Let name be a new Private Name whose [[Description]] value is dn.
- 2. Append name to classPrivateEnvironment.[[Names]].
- */
- ObjectInstance? protoParent = null;
- ObjectInstance? constructorParent = null;
- if (_superClass is null)
- {
- protoParent = engine.Realm.Intrinsics.Object.PrototypeObject;
- constructorParent = engine.Realm.Intrinsics.Function.PrototypeObject;
- }
- else
- {
- engine.UpdateLexicalEnvironment(classScope);
- var superclass = JintExpression.Build(_superClass).GetValue(context);
- engine.UpdateLexicalEnvironment(env);
- if (superclass.IsNull())
- {
- protoParent = null;
- constructorParent = engine.Realm.Intrinsics.Function.PrototypeObject;
- }
- else if (!superclass.IsConstructor)
- {
- ExceptionHelper.ThrowTypeError(engine.Realm, "super class is not a constructor");
- }
- else
- {
- var temp = superclass.Get("prototype");
- if (temp is ObjectInstance protoParentObject)
- {
- protoParent = protoParentObject;
- }
- else if (temp.IsNull())
- {
- // OK
- }
- else
- {
- ExceptionHelper.ThrowTypeError(engine.Realm, "cannot resolve super class prototype chain");
- return default;
- }
- constructorParent = (ObjectInstance) superclass;
- }
- }
- var proto = new ObjectInstance(engine)
- {
- _prototype = protoParent
- };
- MethodDefinition? constructor = null;
- var classBody = _body.Body;
- for (var i = 0; i < classBody.Count; ++i)
- {
- if (classBody[i] is MethodDefinition { Kind: PropertyKind.Constructor } c)
- {
- constructor = c;
- break;
- }
- }
- constructor ??= _superClass != null
- ? _superConstructor
- : _emptyConstructor;
- engine.UpdateLexicalEnvironment(classScope);
- ScriptFunctionInstance F;
- try
- {
- var constructorInfo = constructor.DefineMethod(proto, constructorParent);
- F = constructorInfo.Closure;
- var name = env is ModuleEnvironmentRecord ? _className : _className ?? "";
- if (name is not null)
- {
- F.SetFunctionName(name);
- }
- F.MakeConstructor(writableProperty: false, proto);
- F._constructorKind = _superClass is null ? ConstructorKind.Base : ConstructorKind.Derived;
- F.MakeClassConstructor();
- proto.CreateMethodProperty(CommonProperties.Constructor, F);
- foreach (var classProperty in _body.Body)
- {
- if (classProperty is not MethodDefinition m || m.Kind == PropertyKind.Constructor)
- {
- continue;
- }
- var target = !m.Static ? proto : F;
- var value = MethodDefinitionEvaluation(engine, target, m);
- if (engine._activeEvaluationContext!.IsAbrupt())
- {
- return value;
- }
- }
- }
- finally
- {
- engine.UpdateLexicalEnvironment(env);
- engine.UpdatePrivateEnvironment(outerPrivateEnvironment);
- }
- if (_className is not null)
- {
- classScope.InitializeBinding(_className, F);
- }
- /*
- 28. Set F.[[PrivateMethods]] to instancePrivateMethods.
- 29. Set F.[[Fields]] to instanceFields.
- 30. For each PrivateElement method of staticPrivateMethods, do
- a. Perform ! PrivateMethodOrAccessorAdd(method, F).
- 31. For each element fieldRecord of staticFields, do
- a. Let result be DefineField(F, fieldRecord).
- b. If result is an abrupt completion, then
- i. Set the running execution context's PrivateEnvironment to outerPrivateEnvironment.
- ii. Return result.
- */
- engine.UpdatePrivateEnvironment(outerPrivateEnvironment);
- return F;
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-runtime-semantics-methoddefinitionevaluation
- /// </summary>
- private static JsValue MethodDefinitionEvaluation(
- Engine engine,
- ObjectInstance obj,
- MethodDefinition method)
- {
- if (method.Kind != PropertyKind.Get && method.Kind != PropertyKind.Set)
- {
- var methodDef = method.DefineMethod(obj);
- methodDef.Closure.SetFunctionName(methodDef.Key);
- var desc = new PropertyDescriptor(methodDef.Closure, PropertyFlag.NonEnumerable);
- obj.DefinePropertyOrThrow(methodDef.Key, desc);
- }
- else
- {
- var value = method.TryGetKey(engine);
- if (engine._activeEvaluationContext!.IsAbrupt())
- {
- return value;
- }
- var propKey = TypeConverter.ToPropertyKey(value);
- var function = method.Value as IFunction;
- if (function is null)
- {
- ExceptionHelper.ThrowSyntaxError(obj.Engine.Realm);
- }
- var closure = new ScriptFunctionInstance(
- obj.Engine,
- function,
- obj.Engine.ExecutionContext.LexicalEnvironment,
- true);
- closure.SetFunctionName(propKey, method.Kind == PropertyKind.Get ? "get" : "set");
- closure.MakeMethod(obj);
- var propDesc = new GetSetPropertyDescriptor(
- method.Kind == PropertyKind.Get ? closure : null,
- method.Kind == PropertyKind.Set ? closure : null,
- PropertyFlag.Configurable);
- obj.DefinePropertyOrThrow(propKey, propDesc);
- }
- return obj;
- }
- }
- }
|