2
0
Эх сурвалжийг харах

Fix some class handling issues (#1346)

Marko Lahma 2 жил өмнө
parent
commit
583aabbf05

+ 0 - 11
Jint.Tests.Test262/Test262Harness.settings.json

@@ -362,11 +362,6 @@
     "language/expressions/assignment/target-member-identifier-reference-undefined.js",
     "language/expressions/assignment/target-super-computed-reference-null.js",
     "language/expressions/assignment/target-super-identifier-reference-null.js",
-    "language/expressions/class/dstr/meth-ary-init-iter-get-err-array-prototype.js",
-    "language/expressions/class/dstr/meth-dflt-ary-init-iter-get-err-array-prototype.js",
-    "language/expressions/class/dstr/meth-static-ary-init-iter-get-err-array-prototype.js",
-    "language/expressions/class/dstr/meth-static-dflt-ary-init-iter-get-err-array-prototype.js",
-    "language/expressions/class/name.js",
     "language/expressions/compound-assignment/compound-assignment-operator-calls-putvalue-lref--v--1.js",
     "language/expressions/compound-assignment/compound-assignment-operator-calls-putvalue-lref--v--10.js",
     "language/expressions/compound-assignment/compound-assignment-operator-calls-putvalue-lref--v--11.js",
@@ -463,11 +458,6 @@
     "language/module-code/namespace/internals/object-keys-binding-uninit.js",
     "language/module-code/namespace/internals/object-propertyIsEnumerable-binding-uninit.js",
     "language/statementList/eval-block-with-statment-block.js",
-    "language/statements/class/dstr/meth-ary-init-iter-get-err-array-prototype.js",
-    "language/statements/class/dstr/meth-dflt-ary-init-iter-get-err-array-prototype.js",
-    "language/statements/class/dstr/meth-static-ary-init-iter-get-err-array-prototype.js",
-    "language/statements/class/dstr/meth-static-dflt-ary-init-iter-get-err-array-prototype.js",
-    "language/statements/class/subclass/default-constructor-spread-override.js",
     "language/statements/for-of/dstr/array-elem-put-obj-literal-prop-ref-init-active.js",
     "language/statements/for-of/dstr/array-elem-put-obj-literal-prop-ref-init.js",
     "language/statements/for-of/dstr/obj-id-init-let.js",
@@ -507,7 +497,6 @@
     "language/statements/switch/cptn-no-dflt-match-fall-thru-nrml.js",
     "language/statements/switch/scope-lex-async-function.js",
     "language/statements/switch/scope-lex-async-generator.js",
-    "language/statements/switch/scope-lex-class.js",
     "language/statements/switch/scope-lex-close-case.js",
     "language/statements/switch/scope-lex-close-dflt.js",
     "language/statements/switch/scope-lex-generator.js",

+ 20 - 0
Jint.Tests/Runtime/ClassTests.cs

@@ -0,0 +1,20 @@
+namespace Jint.Tests.Runtime;
+
+public class ClassTests
+{
+    [Fact]
+    public void IsBlockScoped()
+    {
+        const string Script = @"
+            class C {}
+            var c1 = C;
+            {
+              class C {}
+              var c2 = C;
+            }
+            return C === c1;";
+
+        var engine = new Engine();
+        Assert.True(engine.Evaluate(Script).AsBoolean());
+    }
+}

+ 10 - 9
Jint/HoistingScope.cs

@@ -94,7 +94,7 @@ namespace Jint
             for (var i = 0; i < statementListItems.Count; i++)
             {
                 var node = statementListItems[i];
-                if (node.Type != Nodes.VariableDeclaration && node.Type != Nodes.FunctionDeclaration)
+                if (node.Type != Nodes.VariableDeclaration && node.Type != Nodes.FunctionDeclaration && node.Type != Nodes.ClassDeclaration)
                 {
                     continue;
                 }
@@ -241,12 +241,13 @@ namespace Jint
             {
                 foreach (var childNode in node.ChildNodes)
                 {
-                    if (_checkArgumentsReference && childNode.Type == Nodes.Identifier)
+                    var childType = childNode.Type;
+                    if (_checkArgumentsReference && childType == Nodes.Identifier)
                     {
                         _hasArgumentsReference |= ((Identifier) childNode).Name == "arguments";
                     }
 
-                    if (childNode.Type == Nodes.VariableDeclaration)
+                    if (childType == Nodes.VariableDeclaration)
                     {
                         var variableDeclaration = (VariableDeclaration)childNode;
                         if (variableDeclaration.Kind == VariableDeclarationKind.Var)
@@ -285,23 +286,23 @@ namespace Jint
                             }
                         }
                     }
-                    else if (childNode.Type == Nodes.FunctionDeclaration
+                    else if (childType == Nodes.FunctionDeclaration
                              // in strict mode cannot include function declarations directly under block or case clauses
                              && (!_strict || parent is null || (node.Type != Nodes.BlockStatement && node.Type != Nodes.SwitchCase)))
                     {
                         _functions ??= new List<FunctionDeclaration>();
                         _functions.Add((FunctionDeclaration)childNode);
                     }
-                    else if (childNode.Type == Nodes.ClassDeclaration)
+                    else if (childType == Nodes.ClassDeclaration && parent is null or Module)
                     {
                         _lexicalDeclarations ??= new List<Declaration>();
                         _lexicalDeclarations.Add((Declaration) childNode);
                     }
 
-                    if (childNode.Type != Nodes.FunctionDeclaration
-                        && childNode.Type != Nodes.ArrowFunctionExpression
-                        && childNode.Type != Nodes.ArrowParameterPlaceHolder
-                        && childNode.Type != Nodes.FunctionExpression
+                    if (childType != Nodes.FunctionDeclaration
+                        && childType != Nodes.ArrowFunctionExpression
+                        && childType != Nodes.ArrowParameterPlaceHolder
+                        && childType != Nodes.FunctionExpression
                         && !childNode.ChildNodes.IsEmpty())
                     {
                         Visit(childNode, node);

+ 13 - 0
Jint/Native/Array/ArrayInstance.cs

@@ -870,6 +870,19 @@ namespace Jint.Native.Array
             _dense = newArray;
         }
 
+        public JsValue[] ToArray()
+        {
+            var length = GetLength();
+            var array = new JsValue[length];
+            for (uint i = 0; i < length; i++)
+            {
+                TryGetValue(i, out var outValue);
+                array[i] = outValue ?? Undefined;
+            }
+
+            return array;
+        }
+
         public IEnumerator<JsValue> GetEnumerator()
         {
             var length = GetLength();

+ 7 - 2
Jint/Native/Function/ClassDefinition.cs

@@ -12,6 +12,8 @@ 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;
@@ -29,6 +31,7 @@ namespace Jint.Native.Function
             }
 
             _superConstructor = CreateConstructorMethodDefinition("class temp { constructor(...args) { super(...args); } }");
+            _defaultSuperCall = (CallExpression) ((ExpressionStatement) _superConstructor.Value.Body.Body[0]).Expression;
             _emptyConstructor = CreateConstructorMethodDefinition("class temp { constructor() {} }");
         }
 
@@ -148,9 +151,11 @@ namespace Jint.Native.Function
             {
                 var constructorInfo = constructor.DefineMethod(proto, constructorParent);
                 F = constructorInfo.Closure;
-                if (_className is not null)
+
+                var name = env is ModuleEnvironmentRecord ? _className : _className ?? "";
+                if (name is not null)
                 {
-                    F.SetFunctionName(_className);
+                    F.SetFunctionName(name);
                 }
 
                 F.MakeConstructor(writableProperty: false, proto);

+ 10 - 15
Jint/Runtime/Environments/FunctionEnvironmentRecord.cs

@@ -271,14 +271,18 @@ namespace Jint.Runtime.Environments
                 ExceptionHelper.ThrowTypeError(_functionObject._realm, "Destructed parameter is null");
             }
 
-            ArrayInstance? array = null;
-            var arrayContents = System.Array.Empty<JsValue>();
-            if (argument.IsArray())
+            ArrayInstance? array;
+            if (argument is ArrayInstance { HasOriginalIterator: true } ai)
             {
-                array = argument.AsArray();
+                array = ai;
             }
-            else if (argument.IsObject() && argument.TryGetIterator(_functionObject._realm, out var iterator))
+            else
             {
+                if (!argument.TryGetIterator(_functionObject._realm, out var iterator))
+                {
+                    ExceptionHelper.ThrowTypeError(context.Engine.Realm, "object is not iterable");
+                }
+
                 array = _engine.Realm.Intrinsics.Array.ArrayCreate(0);
                 var max = arrayPattern.Elements.Count;
                 if (max > 0 && arrayPattern.Elements[max - 1]?.Type == Nodes.RestElement)
@@ -290,16 +294,7 @@ namespace Jint.Runtime.Environments
                 protocol.Execute();
             }
 
-            if (!ReferenceEquals(array, null))
-            {
-                arrayContents = new JsValue[array.GetLength()];
-
-                for (uint i = 0; i < (uint) arrayContents.Length; i++)
-                {
-                    arrayContents[i] = array.Get(i);
-                }
-            }
-
+            var arrayContents = array.ToArray();
             for (var i = 0; i < arrayPattern.Elements.Count; i++)
             {
                 SetFunctionParameter(context, arrayPattern.Elements[i], arrayContents, i, initiallyEmpty);

+ 28 - 4
Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs

@@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis;
 using System.Runtime.CompilerServices;
 using Esprima.Ast;
 using Jint.Native;
+using Jint.Native.Array;
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime.Environments;
@@ -24,8 +25,6 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         protected override void Initialize(EvaluationContext context)
         {
-            var engine = context.Engine;
-
             var expression = (CallExpression) _expression;
             ref readonly var expressionArguments = ref expression.Arguments;
 
@@ -37,7 +36,12 @@ namespace Jint.Runtime.Interpreter.Expressions
 
             static bool CanSpread(Node? e)
             {
-                return e?.Type == Nodes.SpreadElement || e is AssignmentExpression { Right.Type: Nodes.SpreadElement };
+                if (e is null)
+                {
+                    return false;
+                }
+
+                return e.Type == Nodes.SpreadElement || e is AssignmentExpression { Right.Type: Nodes.SpreadElement };
             }
 
             var cacheable = true;
@@ -247,7 +251,8 @@ namespace Jint.Runtime.Interpreter.Expressions
                 ExceptionHelper.ThrowTypeError(engine.Realm, "Not a constructor");
             }
 
-            var argList = ArgumentListEvaluation(context);
+            var defaultSuperCall = ReferenceEquals(_expression, ClassDefinition._defaultSuperCall);
+            var argList = defaultSuperCall ? DefaultSuperCallArgumentListEvaluation(context) : ArgumentListEvaluation(context);
             var result = ((IConstructor) func).Construct(argList, newTarget);
             var thisER = (FunctionEnvironmentRecord) engine.ExecutionContext.GetThisEnvironment();
             return thisER.BindThisValue(result);
@@ -300,6 +305,25 @@ namespace Jint.Runtime.Interpreter.Expressions
             return arguments;
         }
 
+        private JsValue[] DefaultSuperCallArgumentListEvaluation(EvaluationContext context)
+        {
+            // This branch behaves similarly to constructor(...args) { super(...args); }.
+            // The most notable distinction is that while the aforementioned ECMAScript source text observably calls
+            // the @@iterator method on %Array.prototype%, this function does not.
+
+            var spreadExpression = (JintSpreadExpression) _cachedArguments.JintArguments[0];
+            var array = (ArrayInstance) spreadExpression._argument.GetValue(context);
+            var length = array.GetLength();
+            var args = new List<JsValue>((int) length);
+            for (uint j = 0; j < length; ++j)
+            {
+                array.TryGetValue(j, out var value);
+                args.Add(value);
+            }
+
+            return args.ToArray();
+        }
+
         private sealed class CachedArgumentsHolder
         {
             internal JintExpression[] JintArguments = Array.Empty<JintExpression>();

+ 1 - 1
Jint/Runtime/Interpreter/Expressions/JintSpreadExpression.cs

@@ -6,7 +6,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 {
     internal sealed class JintSpreadExpression : JintExpression
     {
-        private readonly JintExpression _argument;
+        internal readonly JintExpression _argument;
         private readonly string? _argumentName;
 
         public JintSpreadExpression(SpreadElement expression) : base(expression)