浏览代码

Add easier APIs for interop usage (#1351)

Marko Lahma 2 年之前
父节点
当前提交
c680830d3b

+ 29 - 29
Jint.Tests.PublicInterface/ConstraintUsageTests.cs

@@ -1,42 +1,42 @@
 using Jint.Constraints;
 using Jint.Runtime;
 
-namespace Jint.Tests.PublicInterface
+namespace Jint.Tests.PublicInterface;
+
+public class ConstraintUsageTests
 {
-    public class ConstraintUsageTests
+    [Fact]
+    public void CanFindAndResetCancellationConstraint()
     {
-        [Fact]
-        public void CanFindAndResetCancellationConstraint()
-        {
-            using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(100));
-            var engine = new Engine(new Options().CancellationToken(cts.Token));
-
-            // expect constraint to abort execution due to timeout
-            Assert.Throws<ExecutionCanceledException>(WaitAndCompute);
+        using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(100));
+        var engine = new Engine(new Options().CancellationToken(cts.Token));
 
-            // ensure constraint can be obtained publicly
-            var cancellationConstraint = engine.FindConstraint<CancellationConstraint>();
-            Assert.NotNull(cancellationConstraint);
+        // expect constraint to abort execution due to timeout
+        Assert.Throws<ExecutionCanceledException>(WaitAndCompute);
 
-            // reset constraint, expect computation to finish this time
-            using var cts2 = new CancellationTokenSource(TimeSpan.FromMilliseconds(500));
-            cancellationConstraint.Reset(cts2.Token);
-            Assert.Equal("done", WaitAndCompute());
+        // ensure constraint can be obtained publicly
+        var cancellationConstraint = engine.FindConstraint<CancellationConstraint>();
+        Assert.NotNull(cancellationConstraint);
 
-            string WaitAndCompute()
-            {
-                var result = engine.Evaluate(@"
-                    function sleep(millisecondsTimeout) {
-                        var totalMilliseconds = new Date().getTime() + millisecondsTimeout;
+        // reset constraint, expect computation to finish this time
+        using var cts2 = new CancellationTokenSource(TimeSpan.FromMilliseconds(500));
+        cancellationConstraint.Reset(cts2.Token);
+        Assert.Equal("done", WaitAndCompute());
 
-                        while (new Date() < totalMilliseconds) { }
+        string WaitAndCompute()
+        {
+            var result = engine.Evaluate(@"
+                function sleep(millisecondsTimeout) {
+                    var x = 0;
+                    var totalMilliseconds = new Date().getTime() + millisecondsTimeout;
+                    while (new Date().getTime() < totalMilliseconds) {
+                        x++;
                     }
-
-                    sleep(200);
-                    return 'done';
-                ");
-                return result.AsString();
-            }
+                }
+                sleep(200);
+                return 'done';
+            ");
+            return result.AsString();
         }
     }
 }

+ 47 - 0
Jint.Tests.PublicInterface/RavenApiUsageTests.cs

@@ -1,7 +1,10 @@
 using Esprima.Ast;
 using Jint.Constraints;
+using Jint.Native;
 using Jint.Native.Array;
+using Jint.Native.Date;
 using Jint.Native.Function;
+using Jint.Native.Object;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 
@@ -78,4 +81,48 @@ public class RavenApiUsageTests
         var propertyDescriptor = ObjectWrapper.GetPropertyDescriptor(engine, obj, obj.GetType().GetProperty(nameof(DirectoryInfo.Name)));
         Assert.Equal("the-path", propertyDescriptor.Value);
     }
+
+    [Fact]
+    public void CanInjectConstructedObjects()
+    {
+        var engine = new Engine();
+        var obj = new ObjectInstance(engine);
+        obj.FastSetDataProperty("name", "test");
+
+        var emptyArray = new ArrayInstance(engine);
+
+        var array = new ArrayInstance(engine, new object[]
+        {
+            JsNumber.Create(1),
+            JsNumber.Create(2),
+            JsNumber.Create(3)
+        });
+        var date = new DateInstance(engine, new DateTime(2022, 10, 20));
+
+        engine.SetValue("obj", obj);
+        engine.SetValue("emptyArray", emptyArray);
+        engine.SetValue("array", array);
+        engine.SetValue("date", date);
+
+        Assert.Equal("test", engine.Evaluate("obj.name"));
+        Assert.Equal(0, engine.Evaluate("emptyArray.length"));
+        Assert.Equal(1, engine.Evaluate("array.findIndex(x => x === 2)"));
+        Assert.Equal(2022, engine.Evaluate("date.getFullYear()"));
+
+        array.Push(4);
+        array.Push(new JsValue[] { 5, 6 });
+
+        Assert.Equal(4, array[3]);
+        Assert.Equal(5, array[4]);
+        Assert.Equal(6, array[5]);
+
+        var i = 0;
+        foreach (var entry in array.GetEntries())
+        {
+            Assert.Equal(i.ToString(), entry.Key);
+            Assert.Equal(i + 1, entry.Value);
+            i++;
+        }
+        Assert.Equal(6, i);
+    }
 }

+ 1 - 1
Jint.Tests/Runtime/ArrayTests.cs

@@ -62,7 +62,7 @@ public class ArrayTests
     public void ArrayLengthFromInitialState()
     {
         var engine = new Engine();
-        var array = new ArrayInstance(engine, 0);
+        var array = new ArrayInstance(engine);
         var length = (int) array.Length;
         Assert.Equal(0, length);
     }

+ 2 - 2
Jint.Tests/Runtime/Domain/UuidConstructor.cs

@@ -58,8 +58,8 @@ namespace Jint.Tests.Runtime.Domain
 
         public void Configure()
         {
-            FastAddProperty("parse", new ClrFunctionInstance(Engine, "parse", Parse), true, false, true);
-            FastAddProperty("Empty", JsUuid.Empty, true, false, true);
+            FastSetProperty("parse", new PropertyDescriptor(new ClrFunctionInstance(Engine, "parse", Parse), true, false, true));
+            FastSetProperty("Empty", new PropertyDescriptor(JsUuid.Empty, true, false, true));
         }
 
         public UuidInstance Construct(JsUuid uuid) => new UuidInstance(Engine) { PrimitiveValue = uuid, _prototype = PrototypeObject };

+ 4 - 3
Jint.Tests/Runtime/Domain/UuidPrototype.cs

@@ -1,5 +1,6 @@
 using Jint.Native;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 
 namespace Jint.Tests.Runtime.Domain
@@ -30,15 +31,15 @@ namespace Jint.Tests.Runtime.Domain
                 _prototype = engine.Realm.Intrinsics.Object.PrototypeObject,
             };
 
-            obj.FastAddProperty("constructor", ctor, false, false, true);
+            obj.FastSetProperty("constructor", new PropertyDescriptor(ctor, false, false, true));
 
             return obj;
         }
 
         public void Configure()
         {
-            FastAddProperty("toString", new ClrFunctionInstance(Engine, "toString", ToGuidString), true, false, true);
-            FastAddProperty("valueOf", new ClrFunctionInstance(Engine, "valueOf", ValueOf), true, false, true);
+            FastSetProperty("toString", new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToGuidString), true, false, true));
+            FastSetProperty("valueOf", new PropertyDescriptor(new ClrFunctionInstance(Engine, "valueOf", ValueOf), true, false, true));
         }
     }
 }

+ 9 - 9
Jint.Tests/Runtime/InteropTests.cs

@@ -2689,8 +2689,8 @@ namespace Jint.Tests.Runtime
                 Console.WriteLine(message);
             }
 
-            engine.Realm.GlobalObject.FastAddProperty("global", engine.Realm.GlobalObject, true, true, true);
-            engine.Realm.GlobalObject.FastAddProperty("test", new DelegateWrapper(engine, (Action<string, object>) Test), true, true, true);
+            engine.Realm.GlobalObject.FastSetDataProperty("global", engine.Realm.GlobalObject);
+            engine.Realm.GlobalObject.FastSetDataProperty("test", new DelegateWrapper(engine, (Action<string, object>) Test));
 
             {
                 var ex = Assert.Throws<JavaScriptException>(() => engine.Realm.GlobalObject.ToObject());
@@ -3237,22 +3237,22 @@ try {
             };
 
             engine.SetValue("data", dictionary);
-            
+
             Assert.True(engine.Evaluate("Object.hasOwn(data, 'a')").AsBoolean());
             Assert.True(engine.Evaluate("data['a'] === 1").AsBoolean());
 
             engine.Evaluate("data['a'] = 42");
             Assert.True(engine.Evaluate("data['a'] === 42").AsBoolean());
-            
+
             Assert.Equal(42, dictionary["a"]);
 
             engine.Execute("delete data['a'];");
-            
+
             Assert.False(engine.Evaluate("Object.hasOwn(data, 'a')").AsBoolean());
             Assert.False(engine.Evaluate("data['a'] === 42").AsBoolean());
-            
+
             Assert.False(dictionary.ContainsKey("a"));
-            
+
             var engineNoWrite = new Engine(options => options.Strict().AllowClrWrite(false));
 
             dictionary = new Dictionary<string, int>
@@ -3260,9 +3260,9 @@ try {
                 { "a", 1 },
                 { "b", 2 }
             };
-            
+
             engineNoWrite.SetValue("data", dictionary);
-            
+
             var ex1 = Assert.Throws<JavaScriptException>(() => engineNoWrite.Evaluate("data['a'] = 42"));
             Assert.Equal("Cannot assign to read only property 'a' of System.Collections.Generic.Dictionary`2[System.String,System.Int32]", ex1.Message);
 

+ 2 - 2
Jint.Tests/Runtime/ObjectInstanceTests.cs

@@ -10,8 +10,8 @@ namespace Jint.Tests.Runtime
         {
             var engine = new Engine();
             var instance = new ObjectInstance(engine);
-            instance.FastAddProperty("bare", JsValue.Null, true, true, true);
-            instance.FastAddProperty("scope", JsValue.Null, true, true, true);
+            instance.FastSetDataProperty("bare", JsValue.Null);
+            instance.FastSetDataProperty("scope", JsValue.Null);
             instance.RemoveOwnProperty("bare");
             var propertyNames = instance.GetOwnProperties().Select(x => x.Key).ToList();
             Assert.Equal(new JsValue[] { "scope" }, propertyNames);

+ 3 - 3
Jint.Tests/Runtime/PromiseTests.cs

@@ -17,7 +17,7 @@ namespace Jint.Tests.Runtime
             Action<JsValue> resolveFunc = null;
 
             var engine = new Engine();
-            engine.SetValue('f', new Func<JsValue>(() =>
+            engine.SetValue("f", new Func<JsValue>(() =>
             {
                 var (promise, resolve, _) = engine.RegisterPromise();
                 resolveFunc = resolve;
@@ -36,7 +36,7 @@ namespace Jint.Tests.Runtime
             Action<JsValue> rejectFunc = null;
 
             var engine = new Engine();
-            engine.SetValue('f', new Func<JsValue>(() =>
+            engine.SetValue("f", new Func<JsValue>(() =>
             {
                 var (promise, _, reject) = engine.RegisterPromise();
                 rejectFunc = reject;
@@ -96,7 +96,7 @@ namespace Jint.Tests.Runtime
         public void Execute_ConcurrentNormalExecuteCall_WorksFine()
         {
             var engine = new Engine();
-            engine.SetValue('f', new Func<JsValue>(() => engine.RegisterPromise().Promise));
+            engine.SetValue("f", new Func<JsValue>(() => engine.RegisterPromise().Promise));
 
             engine.Execute("f();");
 

+ 8 - 8
Jint/Engine.cs

@@ -174,39 +174,39 @@ namespace Jint
             return context;
         }
 
-        public Engine SetValue(JsValue name, Delegate value)
+        public Engine SetValue(string name, Delegate value)
         {
-            Realm.GlobalObject.FastAddProperty(name, new DelegateWrapper(this, value), true, false, true);
+            Realm.GlobalObject.FastSetProperty(name, new PropertyDescriptor(new DelegateWrapper(this, value), true, false, true));
             return this;
         }
 
-        public Engine SetValue(JsValue name, string value)
+        public Engine SetValue(string name, string value)
         {
             return SetValue(name, new JsString(value));
         }
 
-        public Engine SetValue(JsValue name, double value)
+        public Engine SetValue(string name, double value)
         {
             return SetValue(name, JsNumber.Create(value));
         }
 
-        public Engine SetValue(JsValue name, int value)
+        public Engine SetValue(string name, int value)
         {
             return SetValue(name, JsNumber.Create(value));
         }
 
-        public Engine SetValue(JsValue name, bool value)
+        public Engine SetValue(string name, bool value)
         {
             return SetValue(name, value ? JsBoolean.True : JsBoolean.False);
         }
 
-        public Engine SetValue(JsValue name, JsValue value)
+        public Engine SetValue(string name, JsValue value)
         {
             Realm.GlobalObject.Set(name, value);
             return this;
         }
 
-        public Engine SetValue(JsValue name, object obj)
+        public Engine SetValue(string name, object obj)
         {
             var value = obj is Type t
                 ? TypeReference.CreateTypeReference(this, t)

+ 26 - 21
Jint/Native/Array/ArrayConstructor.cs

@@ -321,32 +321,38 @@ namespace Jint.Native.Array
 
         private ArrayInstance Construct(JsValue[] arguments, ulong capacity, ObjectInstance prototypeObject)
         {
-            var instance = ArrayCreate(capacity, prototypeObject);
-
-            if (arguments.Length == 1 && arguments.At(0).IsNumber())
-            {
-                var length = TypeConverter.ToNumber(arguments.At(0));
-                ValidateLength(length);
-                instance._length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
-            }
-            else if (arguments.Length == 1 && arguments[0] is IObjectWrapper objectWrapper)
+            ArrayInstance instance;
+            if (arguments.Length == 1)
             {
-                if (objectWrapper.Target is IEnumerable enumerable)
+                switch (arguments[0])
                 {
-                    return ConstructArrayFromIEnumerable(enumerable);
+                    case JsNumber number:
+                        ValidateLength(number._value);
+                        instance = ArrayCreate((ulong) number._value, prototypeObject);
+                        break;
+                    case IObjectWrapper objectWrapper:
+                        instance = objectWrapper.Target is IEnumerable enumerable
+                            ? ConstructArrayFromIEnumerable(enumerable)
+                            : ArrayCreate(0, prototypeObject);
+                        break;
+                    case ArrayInstance arrayInstance:
+                        // direct copy
+                        instance = (ArrayInstance) ConstructArrayFromArrayLike(Undefined, arrayInstance, null, this);
+                        break;
+                    default:
+                        instance = ArrayCreate(capacity, prototypeObject);
+                        instance._length!._value = JsNumber.PositiveZero;
+                        instance.Push(arguments);
+                        break;
                 }
             }
-            else if (arguments.Length == 1 && arguments[0] is ArrayInstance arrayInstance)
-            {
-                // direct copy
-                return (ArrayInstance) ConstructArrayFromArrayLike(Undefined, arrayInstance, null, this);
-            }
             else
             {
-                instance._length = new PropertyDescriptor(0, PropertyFlag.OnlyWritable);
+                instance = ArrayCreate((ulong) arguments.Length, prototypeObject);
+                instance._length!._value = JsNumber.PositiveZero;
                 if (arguments.Length > 0)
                 {
-                    PrototypeObject.Push(instance, arguments);
+                    instance.Push(arguments);
                 }
             }
 
@@ -364,10 +370,9 @@ namespace Jint.Native.Array
             }
 
             proto ??= PrototypeObject;
-            var instance = new ArrayInstance(Engine, (uint) length)
+            var instance = new ArrayInstance(Engine, (uint) length, (uint) length)
             {
-                _prototype = proto,
-                _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable)
+                _prototype = proto
             };
             return instance;
         }

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

@@ -21,8 +21,21 @@ namespace Jint.Native.Array
 
         private ObjectChangeFlags _objectChangeFlags;
 
-        public ArrayInstance(Engine engine, uint capacity = 0) : base(engine)
+        private protected ArrayInstance(Engine engine) : base(engine)
         {
+            _dense = System.Array.Empty<object?>();
+        }
+
+        /// <summary>
+        /// Creates a new array instance with defaults.
+        /// </summary>
+        /// <param name="engine">The engine that this array is bound to.</param>
+        /// <param name="capacity">The initial size of underlying data structure, if you know you're going to add N items, provide N.</param>
+        /// <param name="length">Sets the length of the array.</param>
+        public ArrayInstance(Engine engine, uint capacity = 0, uint length = 0) : base(engine)
+        {
+            _prototype = engine.Realm.Intrinsics.Array.PrototypeObject;
+
             if (capacity > engine.Options.Constraints.MaxArraySize)
             {
                 ThrowMaximumArraySizeReachedException(engine, capacity);
@@ -34,15 +47,21 @@ namespace Jint.Native.Array
             }
             else
             {
-                _sparse = new Dictionary<uint, object?>((int) (capacity <= 1024 ? capacity : 1024));
+                _sparse = new Dictionary<uint, object?>(1024);
             }
+
+            _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
         }
 
         /// <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 is of type object[] and it should only contain values inheriting from JsValue.
+        /// The array will be owned and modified by Jint afterwards.
         /// </summary>
-        public ArrayInstance(Engine engine, JsValue[] items) : base(engine)
+        public ArrayInstance(Engine engine, object[] items) : base(engine)
         {
+            _prototype = engine.Realm.Intrinsics.Array.PrototypeObject;
+
             int length;
             if (items == null || items.Length == 0)
             {
@@ -51,6 +70,11 @@ namespace Jint.Native.Array
             }
             else
             {
+                if (items.GetType() != typeof(object[]))
+                {
+                    ExceptionHelper.ThrowArgumentException("Supplied array must be of type object[] and should only contain values inheriting from JsValue");
+                }
+
                 _dense = items;
                 length = items.Length;
             }
@@ -364,6 +388,59 @@ namespace Jint.Native.Array
             return properties;
         }
 
+        /// <summary>
+        /// Returns key and value pairs for actual array entries, excludes parent and optionally length.
+        /// </summary>
+        /// <param name="includeLength">Whether to return length and it's value.</param>
+        public IEnumerable<KeyValuePair<string, JsValue>> GetEntries(bool includeLength = false)
+        {
+            var temp = _dense;
+            if (temp != null)
+            {
+                var length = System.Math.Min(temp.Length, GetLength());
+                for (var i = 0; i < length; i++)
+                {
+                    var value = temp[i];
+                    if (value != null)
+                    {
+                        var key = TypeConverter.ToString(i);
+                        if (value is not PropertyDescriptor descriptor)
+                        {
+                            yield return new KeyValuePair<string, JsValue>(key, (JsValue) value);
+                        }
+                        else
+                        {
+                            yield return new KeyValuePair<string, JsValue>(key, descriptor.Value);
+                        }
+                    }
+                }
+            }
+            else
+            {
+                foreach (var entry in _sparse!)
+                {
+                    var value = entry.Value;
+                    if (value is not null)
+                    {
+                        var key = TypeConverter.ToString(entry.Key);
+                        if (value is not PropertyDescriptor descriptor)
+                        {
+                            yield return new KeyValuePair<string, JsValue>(key, (JsValue) value);
+                        }
+                        else
+                        {
+                            yield return new KeyValuePair<string, JsValue>(key, descriptor.Value);
+                        }
+                    }
+                }
+            }
+
+            if (includeLength && _length != null)
+            {
+                yield return new KeyValuePair<string, JsValue>(CommonProperties.Length._value, _length.Value);
+            }
+        }
+
         public sealed override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
         {
             var temp = _dense;
@@ -898,7 +975,10 @@ namespace Jint.Native.Array
             return GetEnumerator();
         }
 
-        internal void Push(JsValue value)
+        /// <summary>
+        /// Pushes the value to the end of the array instance.
+        /// </summary>
+        public void Push(JsValue value)
         {
             var initialLength = GetLength();
             var newLength = initialLength + 1;
@@ -931,15 +1011,18 @@ namespace Jint.Native.Array
             }
         }
 
-        internal uint Push(JsValue[] arguments)
+        /// <summary>
+        /// Pushes the given values to the end of the array.
+        /// </summary>
+        public uint Push(JsValue[] values)
         {
             var initialLength = GetLength();
-            var newLength = initialLength + arguments.Length;
+            var newLength = initialLength + values.Length;
 
             // if we see that we are bringing more than normal growth algorithm handles, ensure capacity eagerly
             if (_dense != null
                 && initialLength != 0
-                && arguments.Length > initialLength * 2
+                && values.Length > initialLength * 2
                 && newLength <= MaxDenseArrayLength)
             {
                 EnsureCapacity((uint) newLength);
@@ -947,7 +1030,7 @@ namespace Jint.Native.Array
 
             var temp = _dense;
             ulong n = initialLength;
-            foreach (var argument in arguments)
+            foreach (var argument in values)
             {
                 if (n < ArrayOperations.MaxArrayLength)
                 {

+ 1 - 4
Jint/Native/Array/ArrayPrototype.cs

@@ -1540,10 +1540,7 @@ namespace Jint.Native.Array
                 }
                 if (!result.TryGetValue(key, out var list))
                 {
-                    result[key] = list = new ArrayInstance(_engine)
-                    {
-                        _length = new PropertyDescriptor(JsNumber.PositiveZero, PropertyFlag.OnlyWritable)
-                    };
+                    result[key] = list = new ArrayInstance(_engine);
                 }
 
                 list.SetIndexValue(list.GetLength(), kValue, updateLength: true);

+ 1 - 1
Jint/Native/Date/DateConstructor.cs

@@ -239,7 +239,7 @@ namespace Jint.Native.Date
                 static (engine, _, dateValue) => new DateInstance(engine, dateValue), time);
         }
 
-        private double FromDateTime(DateTime dt, bool negative = false)
+        internal double FromDateTime(DateTime dt, bool negative = false)
         {
             var convertToUtcAfter = dt.Kind == DateTimeKind.Unspecified;
 

+ 10 - 2
Jint/Native/Date/DateInstance.cs

@@ -14,9 +14,17 @@ public sealed class DateInstance : ObjectInstance
 
     internal double _dateValue;
 
-    public DateInstance(Engine engine, double dateValue)
-        : base(engine, ObjectClass.Date)
+    public DateInstance(Engine engine, DateTimeOffset value) : this(engine, value.UtcDateTime)
     {
+    }
+
+    public DateInstance(Engine engine, DateTime value) : this(engine, engine.Realm.Intrinsics.Date.FromDateTime(value))
+    {
+    }
+
+    public DateInstance(Engine engine, double dateValue) : base(engine, ObjectClass.Date)
+    {
+        _prototype = engine.Realm.Intrinsics.Date.PrototypeObject;
         _dateValue = dateValue.TimeClip();
     }
 

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

@@ -681,7 +681,7 @@ namespace Jint.Native.Json
                 Expect(":");
                 var value = ParseJsonValue();
 
-                obj.FastAddProperty(name, value, true, true, true);
+                obj.FastSetDataProperty(name, value);
 
                 if (!Match("}"))
                 {

+ 5 - 0
Jint/Native/Json/JsonSerializer.cs

@@ -30,6 +30,11 @@ namespace Jint.Native.Json
             _engine = engine;
         }
 
+        public JsValue Serialize(JsValue value)
+        {
+            return Serialize(value, JsValue.Undefined, JsValue.Undefined);
+        }
+
         public JsValue Serialize(JsValue value, JsValue replacer, JsValue space)
         {
             _stack = new ObjectTraverseStack(_engine);

+ 45 - 0
Jint/Native/Object/ObjectInstance.Fast.cs

@@ -0,0 +1,45 @@
+using System.Runtime.CompilerServices;
+using Jint.Runtime.Descriptors;
+
+namespace Jint.Native.Object;
+
+/// <summary>
+/// Fast access helpers which violate JavaScript specification, but helpful when accessed
+/// against ObjectInstance and prototype chain is intact.
+/// </summary>
+public partial class ObjectInstance
+{
+    /// <summary>
+    /// Creates data property without checking prototype, property existence and overwrites any data already present.
+    /// </summary>
+    /// <remarks>
+    /// Does not conform to JavaScript specification prototype etc. handling etc.
+    /// </remarks>
+    public void FastSetProperty(string name, PropertyDescriptor value)
+    {
+        SetProperty(name, value);
+    }
+
+    /// <summary>
+    /// Creates data property without checking prototype, property existence and overwrites any data already present.
+    /// </summary>
+    /// <remarks>
+    /// Does not conform to JavaScript specification prototype etc. handling etc.
+    /// </remarks>
+    public void FastSetProperty(JsValue property, PropertyDescriptor value)
+    {
+        SetProperty(property, value);
+    }
+
+    /// <summary>
+    /// Creates data property (configurable, enumerable, writable) without checking prototype, property existence and overwrites any data already present.
+    /// </summary>
+    /// <remarks>
+    /// Does not conform to JavaScript specification prototype etc. handling etc.
+    /// </remarks>
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public void FastSetDataProperty(string name, JsValue value)
+    {
+        SetProperty(name, new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable));
+    }
+}

+ 4 - 21
Jint/Native/Object/ObjectInstance.cs

@@ -17,7 +17,7 @@ using Jint.Runtime.Interop;
 
 namespace Jint.Native.Object
 {
-    public class ObjectInstance : JsValue, IEquatable<ObjectInstance>
+    public partial class ObjectInstance : JsValue, IEquatable<ObjectInstance>
     {
         private bool _initialized;
         private readonly ObjectClass _class;
@@ -853,31 +853,13 @@ namespace Jint.Native.Object
                 if (mutable != null)
                 {
                     // replace old with new type that supports get and set
-                    o.FastSetProperty(property, mutable);
+                    o.SetOwnProperty(property, mutable);
                 }
             }
 
             return true;
         }
 
-        /// <summary>
-        /// Optimized version of [[Put]] when the property is known to be undeclared already
-        /// </summary>
-        public void FastAddProperty(JsValue name, JsValue value, bool writable, bool enumerable, bool configurable)
-        {
-            SetOwnProperty(name, new PropertyDescriptor(value, writable, enumerable, configurable));
-        }
-
-        /// <summary>
-        /// Optimized version of [[Put]] when the property is known to be already declared
-        /// </summary>
-        /// <param name="name"></param>
-        /// <param name="value"></param>
-        public void FastSetProperty(JsValue name, PropertyDescriptor value)
-        {
-            SetOwnProperty(name, value);
-        }
-
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         protected internal void EnsureInitialized()
         {
@@ -1204,11 +1186,12 @@ namespace Jint.Native.Object
         /// https://tc39.es/ecma262/#sec-createdatapropertyorthrow
         /// </summary>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal bool CreateDataProperty(JsValue p, JsValue v)
+        public bool CreateDataProperty(JsValue p, JsValue v)
         {
             return DefineOwnProperty(p, new PropertyDescriptor(v, PropertyFlag.ConfigurableEnumerableWritable));
         }
 
+
         /// <summary>
         /// https://tc39.es/ecma262/#sec-createdataproperty
         /// </summary>

+ 4 - 4
Jint/Native/Promise/PromiseConstructor.cs

@@ -380,8 +380,8 @@ namespace Jint.Native.Promise
                                     alreadyCalled = true;
 
                                     var res = Engine.Realm.Intrinsics.Object.Construct(2);
-                                    res.FastAddProperty("status", "fulfilled", true, true, true);
-                                    res.FastAddProperty("value", args.At(0), true, true, true);
+                                    res.FastSetDataProperty("status", "fulfilled");
+                                    res.FastSetDataProperty("value", args.At(0));
                                     results[capturedIndex] = res;
 
                                     ResolveIfFinished();
@@ -397,8 +397,8 @@ namespace Jint.Native.Promise
                                     alreadyCalled = true;
 
                                     var res = Engine.Realm.Intrinsics.Object.Construct(2);
-                                    res.FastAddProperty("status", "rejected", true, true, true);
-                                    res.FastAddProperty("reason", args.At(0), true, true, true);
+                                    res.FastSetDataProperty("status", "rejected");
+                                    res.FastSetDataProperty("reason", args.At(0));
                                     results[capturedIndex] = res;
 
                                     ResolveIfFinished();

+ 2 - 2
Jint/Native/RegExp/RegExpPrototype.cs

@@ -875,8 +875,8 @@ namespace Jint.Native.RegExp
 
                 // "aaa".match() => [ '', index: 0, input: 'aaa' ]
                 var array = R.Engine.Realm.Intrinsics.Array.ArrayCreate(1);
-                array.FastAddProperty(PropertyIndex, lastIndex, true, true, true);
-                array.FastAddProperty(PropertyInput, s, true, true, true);
+                array.FastSetDataProperty(PropertyIndex._value, lastIndex);
+                array.FastSetDataProperty(PropertyInput._value, s);
                 array.SetIndexValue(0, JsString.Empty, updateLength: false);
                 return array;
             }

+ 5 - 13
Jint/Runtime/Interop/DefaultObjectConverter.cs

@@ -3,7 +3,6 @@ using System.Threading;
 using Jint.Native;
 using Jint.Native.Array;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 
 namespace Jint
@@ -155,23 +154,16 @@ namespace Jint
 
         private static JsValue ConvertArray(Engine e, object v)
         {
-            var array = (Array)v;
-            var arrayLength = (uint)array.Length;
-
-            var jsArray = new ArrayInstance(e, arrayLength)
-            {
-                _prototype = e.Realm.Intrinsics.Array.PrototypeObject
-            };
+            var array = (Array) v;
+            var arrayLength = (uint) array.Length;
 
+            var values = new object[arrayLength];
             for (uint i = 0; i < arrayLength; ++i)
             {
-                var jsItem = JsValue.FromObject(e, array.GetValue(i));
-                jsArray.WriteArrayValue(i, jsItem);
+                values[i] = JsValue.FromObject(e, array.GetValue(i));
             }
 
-            jsArray.SetOwnProperty(CommonProperties.Length,
-                new PropertyDescriptor(arrayLength, PropertyFlag.OnlyWritable));
-            return jsArray;
+            return new ArrayInstance(e, values);
         }
     }
 }

+ 2 - 1
Jint/Runtime/JavaScriptException.cs

@@ -3,6 +3,7 @@ using Jint.Native;
 using Jint.Native.Error;
 using Jint.Native.Object;
 using Jint.Pooling;
+using Jint.Runtime.Descriptors;
 
 namespace Jint.Runtime;
 
@@ -100,7 +101,7 @@ public class JavaScriptException : JintException
             else
             {
                 _callStack = engine.CallStack.BuildCallStackString(location);
-                errObj.FastAddProperty(CommonProperties.Stack, _callStack, false, false, false);
+                errObj.FastSetProperty(CommonProperties.Stack._value, new PropertyDescriptor(_callStack, false, false, false));
             }
         }