Browse Source

Fix JsArray(PropertyDescriptor[]) to initialize prototype (#1360)

Marko Lahma 2 năm trước cách đây
mục cha
commit
11a47fc717

+ 51 - 13
Jint.Tests.PublicInterface/RavenApiUsageTests.cs

@@ -86,33 +86,51 @@ public class RavenApiUsageTests
         var obj = new JsObject(engine);
         var obj = new JsObject(engine);
         obj.FastSetDataProperty("name", "test");
         obj.FastSetDataProperty("name", "test");
 
 
-        var emptyArray = new JsArray(engine);
-
-        var array = new JsArray(engine, new object[]
+        var array1 = new JsArray(engine, new JsValue[]
         {
         {
             JsNumber.Create(1),
             JsNumber.Create(1),
             JsNumber.Create(2),
             JsNumber.Create(2),
             JsNumber.Create(3)
             JsNumber.Create(3)
         });
         });
-        var date = new JsDate(engine, new DateTime(2022, 10, 20));
+        engine.SetValue("array1", array1);
 
 
-        engine.SetValue("obj", obj);
-        engine.SetValue("emptyArray", emptyArray);
-        engine.SetValue("array", array);
-        engine.SetValue("date", date);
+        TestArrayAccess(engine, array1, "array1");
+
+        var array3 = new JsArray(engine, new []
+        {
+            new PropertyDescriptor(JsNumber.Create(1), true, true, true),
+            new PropertyDescriptor(JsNumber.Create(2), true, true, true),
+            new PropertyDescriptor(JsNumber.Create(3), true, true, true),
+        });
+        engine.SetValue("array3", array3);
+        TestArrayAccess(engine, array3, "array3");
 
 
+        engine.SetValue("obj", obj);
         Assert.Equal("test", engine.Evaluate("obj.name"));
         Assert.Equal("test", engine.Evaluate("obj.name"));
+
+        engine.SetValue("emptyArray", new JsArray(engine));
         Assert.Equal(0, engine.Evaluate("emptyArray.length"));
         Assert.Equal(0, engine.Evaluate("emptyArray.length"));
-        Assert.Equal(1, engine.Evaluate("array.findIndex(x => x === 2)"));
+        Assert.Equal(1, engine.Evaluate("emptyArray.push(1); return emptyArray.length"));
+
+        engine.SetValue("emptyArray", new JsArray(engine, Array.Empty<JsValue>()));
+        Assert.Equal(0, engine.Evaluate("emptyArray.length"));
+        Assert.Equal(1, engine.Evaluate("emptyArray.push(1); return emptyArray.length"));
+
+        engine.SetValue("emptyArray", new JsArray(engine, Array.Empty<PropertyDescriptor>()));
+        Assert.Equal(0, engine.Evaluate("emptyArray.length"));
+        Assert.Equal(1, engine.Evaluate("emptyArray.push(1); return emptyArray.length"));
+
+        engine.SetValue("date", new JsDate(engine, new DateTime(2022, 10, 20)));
         Assert.Equal(2022, engine.Evaluate("date.getFullYear()"));
         Assert.Equal(2022, engine.Evaluate("date.getFullYear()"));
+    }
+
+    private static void TestArrayAccess(Engine engine, JsArray array, string name)
+    {
+        Assert.Equal(1, engine.Evaluate($"{name}.findIndex(x => x === 2)"));
 
 
         array.Push(4);
         array.Push(4);
         array.Push(new JsValue[] { 5, 6 });
         array.Push(new JsValue[] { 5, 6 });
 
 
-        Assert.Equal(4, array[3]);
-        Assert.Equal(5, array[4]);
-        Assert.Equal(6, array[5]);
-
         var i = 0;
         var i = 0;
         foreach (var entry in array.GetEntries())
         foreach (var entry in array.GetEntries())
         {
         {
@@ -120,6 +138,26 @@ public class RavenApiUsageTests
             Assert.Equal(i + 1, entry.Value);
             Assert.Equal(i + 1, entry.Value);
             i++;
             i++;
         }
         }
+
         Assert.Equal(6, i);
         Assert.Equal(6, i);
+
+        array[0] = "";
+        array[1] = false;
+        array[2] = null;
+
+        Assert.Equal("", array[0]);
+        Assert.Equal(false, array[1]);
+        Assert.Equal(JsValue.Undefined, array[2]);
+        Assert.Equal(4, array[3]);
+        Assert.Equal(5, array[4]);
+        Assert.Equal(6, array[5]);
+
+        for (i = 0; i < 100; ++i)
+        {
+            array.Push(JsValue.Undefined);
+        }
+
+        Assert.Equal(106L, array.Length);
+        Assert.True(array.All(x => x is JsNumber or JsUndefined or JsNumber or JsString or JsBoolean));
     }
     }
 }
 }

+ 20 - 9
Jint/Native/Array/ArrayInstance.cs

@@ -47,7 +47,7 @@ namespace Jint.Native.Array
             _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
             _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
         }
         }
 
 
-        private protected ArrayInstance(Engine engine, object[] items) : base(engine)
+        private protected ArrayInstance(Engine engine, JsValue[] items) : base(engine)
         {
         {
             _prototype = engine.Realm.Intrinsics.Array.PrototypeObject;
             _prototype = engine.Realm.Intrinsics.Array.PrototypeObject;
 
 
@@ -59,11 +59,6 @@ namespace Jint.Native.Array
             }
             }
             else
             else
             {
             {
-                if (items.GetType() != typeof(object[]))
-                {
-                    ExceptionHelper.ThrowArgumentException("Supplied array must be of type object[] and should only contain values that inherit from JsValue or PropertyDescriptor or are null");
-                }
-
                 _dense = items;
                 _dense = items;
                 length = items.Length;
                 length = items.Length;
             }
             }
@@ -73,6 +68,8 @@ namespace Jint.Native.Array
 
 
         private protected ArrayInstance(Engine engine, PropertyDescriptor[] items) : base(engine)
         private protected ArrayInstance(Engine engine, PropertyDescriptor[] items) : base(engine)
         {
         {
+            _prototype = engine.Realm.Intrinsics.Array.PrototypeObject;
+
             int length;
             int length;
             if (items == null || items.Length == 0)
             if (items == null || items.Length == 0)
             {
             {
@@ -992,14 +989,13 @@ namespace Jint.Native.Array
             var canUseDirectIndexSet = temp != null && newLength <= temp.Length;
             var canUseDirectIndexSet = temp != null && newLength <= temp.Length;
 
 
             double n = initialLength;
             double n = initialLength;
-            var desc = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
             if (canUseDirectIndexSet)
             if (canUseDirectIndexSet)
             {
             {
-                temp![(uint) n] = desc;
+                temp![(uint) n] = value;
             }
             }
             else
             else
             {
             {
-                WriteValueSlow(n, desc);
+                WriteValueSlow(n, new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable));
             }
             }
 
 
             // check if we can set length fast without breaking ECMA specification
             // check if we can set length fast without breaking ECMA specification
@@ -1202,6 +1198,10 @@ namespace Jint.Native.Array
                 TryGetValue(index, out var kValue);
                 TryGetValue(index, out var kValue);
                 return kValue;
                 return kValue;
             }
             }
+            set
+            {
+                SetIndexValue(index, value, updateLength: true);
+            }
         }
         }
 
 
         public JsValue this[int index]
         public JsValue this[int index]
@@ -1220,6 +1220,17 @@ namespace Jint.Native.Array
                 }
                 }
                 return kValue;
                 return kValue;
             }
             }
+            set
+            {
+                if (index >= 0)
+                {
+                    SetIndexValue((uint) index, value, updateLength: true);
+                }
+                else
+                {
+                    Set(index, value);
+                }
+            }
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 2 - 2
Jint/Native/JsArray.cs

@@ -17,15 +17,15 @@ public sealed class JsArray : ArrayInstance
 
 
     /// <summary>
     /// <summary>
     /// Possibility to construct valid array fast.
     /// Possibility to construct valid array fast.
-    /// Requires that supplied array is of type object[] and it should only contain values that inherit from JsValue or PropertyDescriptor or are null.
     /// The array will be owned and modified by Jint afterwards.
     /// The array will be owned and modified by Jint afterwards.
     /// </summary>
     /// </summary>
-    public JsArray(Engine engine, object[] items) : base(engine, items)
+    public JsArray(Engine engine, JsValue[] items) : base(engine, items)
     {
     {
     }
     }
 
 
     /// <summary>
     /// <summary>
     /// Possibility to construct valid array fast, requires that supplied array does not have holes.
     /// Possibility to construct valid array fast, requires that supplied array does not have holes.
+    /// The array will be owned and modified by Jint afterwards.
     /// </summary>
     /// </summary>
     public JsArray(Engine engine, PropertyDescriptor[] items) : base(engine, items)
     public JsArray(Engine engine, PropertyDescriptor[] items) : base(engine, items)
     {
     {

+ 1 - 1
Jint/Native/DateInstance.cs → Jint/Native/JsDate.cs

@@ -5,7 +5,7 @@ using Jint.Runtime;
 
 
 namespace Jint.Native;
 namespace Jint.Native;
 
 
-public class JsDate : ObjectInstance
+public sealed class JsDate : ObjectInstance
 {
 {
     // Maximum allowed value to prevent DateTime overflow
     // Maximum allowed value to prevent DateTime overflow
     private static readonly long Max = (long) (DateTime.MaxValue - DateConstructor.Epoch).TotalMilliseconds;
     private static readonly long Max = (long) (DateTime.MaxValue - DateConstructor.Epoch).TotalMilliseconds;

+ 1 - 1
Jint/Native/Json/JsonParser.cs

@@ -10,7 +10,7 @@ using Range = Esprima.Range;
 
 
 namespace Jint.Native.Json
 namespace Jint.Native.Json
 {
 {
-    public class JsonParser
+    public sealed class JsonParser
     {
     {
         private readonly Engine _engine;
         private readonly Engine _engine;
 
 

+ 1 - 1
Jint/Native/Json/JsonSerializer.cs

@@ -13,7 +13,7 @@ using Jint.Runtime.Interop;
 
 
 namespace Jint.Native.Json
 namespace Jint.Native.Json
 {
 {
-    public class JsonSerializer
+    public sealed class JsonSerializer
     {
     {
         private readonly Engine _engine;
         private readonly Engine _engine;
         private ObjectTraverseStack _stack = null!;
         private ObjectTraverseStack _stack = null!;

+ 1 - 1
Jint/Native/RegExp/RegExpInstance.cs

@@ -5,7 +5,7 @@ using Jint.Runtime.Descriptors;
 
 
 namespace Jint.Native.RegExp
 namespace Jint.Native.RegExp
 {
 {
-    public class RegExpInstance : ObjectInstance
+    public sealed class RegExpInstance : ObjectInstance
     {
     {
         internal const string regExpForMatchingAllCharacters = "(?:)";
         internal const string regExpForMatchingAllCharacters = "(?:)";
         internal static readonly JsString PropertyLastIndex = new("lastIndex");
         internal static readonly JsString PropertyLastIndex = new("lastIndex");

+ 1 - 1
Jint/Native/Symbol/GlobalSymbolRegistry.cs

@@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis;
 
 
 namespace Jint.Native.Symbol
 namespace Jint.Native.Symbol
 {
 {
-    public class GlobalSymbolRegistry
+    public sealed class GlobalSymbolRegistry
     {
     {
         public static readonly JsSymbol AsyncIterator = new JsSymbol("Symbol.asyncIterator");
         public static readonly JsSymbol AsyncIterator = new JsSymbol("Symbol.asyncIterator");
         public static readonly JsSymbol HasInstance = new JsSymbol("Symbol.hasInstance");
         public static readonly JsSymbol HasInstance = new JsSymbol("Symbol.hasInstance");

+ 1 - 1
Jint/Runtime/Environments/FunctionEnvironmentRecord.cs

@@ -289,7 +289,7 @@ namespace Jint.Runtime.Environments
             uint count = restCount > 0 ? (uint) restCount : 0;
             uint count = restCount > 0 ? (uint) restCount : 0;
 
 
             uint targetIndex = 0;
             uint targetIndex = 0;
-            var rest = new object[count];
+            var rest = new JsValue[count];
             for (var argIndex = index; argIndex < arguments.Length; ++argIndex)
             for (var argIndex = index; argIndex < arguments.Length; ++argIndex)
             {
             {
                 rest[targetIndex++] = arguments[argIndex];
                 rest[targetIndex++] = arguments[argIndex];

+ 1 - 1
Jint/Runtime/Interop/DefaultObjectConverter.cs

@@ -156,7 +156,7 @@ namespace Jint
             var array = (Array) v;
             var array = (Array) v;
             var arrayLength = (uint) array.Length;
             var arrayLength = (uint) array.Length;
 
 
-            var values = new object[arrayLength];
+            var values = new JsValue[arrayLength];
             for (uint i = 0; i < arrayLength; ++i)
             for (uint i = 0; i < arrayLength; ++i)
             {
             {
                 values[i] = JsValue.FromObject(e, array.GetValue(i));
                 values[i] = JsValue.FromObject(e, array.GetValue(i));

+ 2 - 2
Jint/Runtime/Interpreter/Expressions/JintArrayExpression.cs

@@ -121,12 +121,12 @@ namespace Jint.Runtime.Interpreter.Expressions
 
 
             protected override object EvaluateInternal(EvaluationContext context)
             protected override object EvaluateInternal(EvaluationContext context)
             {
             {
-                return new JsArray(context.Engine, Array.Empty<object>());
+                return new JsArray(context.Engine, Array.Empty<JsValue>());
             }
             }
 
 
             public override JsValue GetValue(EvaluationContext context)
             public override JsValue GetValue(EvaluationContext context)
             {
             {
-                return new JsArray(context.Engine, Array.Empty<object>());
+                return new JsArray(context.Engine, Array.Empty<JsValue>());
             }
             }
         }
         }
     }
     }