Skip to content

Commit 4bff351

Browse files
authored
Optimize Array.pop (#1680)
1 parent 675ad49 commit 4bff351

File tree

3 files changed

+73
-34
lines changed

3 files changed

+73
-34
lines changed

Jint/Engine.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ internal JsValue GetValue(Reference reference, bool returnReferenceToPool)
618618
return JsValue.Undefined;
619619
}
620620

621-
var callable = (ICallable) getter.AsObject();
621+
var callable = (ICallable) getter;
622622
return callable.Call(baseValue, Arguments.Empty);
623623
}
624624
}

Jint/Native/Array/ArrayInstance.cs

Lines changed: 66 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public class ArrayInstance : ObjectInstance, IEnumerable<JsValue>
2323

2424
private ObjectChangeFlags _objectChangeFlags;
2525

26+
private ArrayConstructor? _constructor;
27+
2628
private protected ArrayInstance(Engine engine, InternalTypes type) : base(engine, type: type)
2729
{
2830
_dense = System.Array.Empty<JsValue?>();
@@ -54,7 +56,8 @@ private protected ArrayInstance(Engine engine, JsValue[] items) : base(engine, t
5456

5557
private void InitializePrototypeAndValidateCapacity(Engine engine, uint capacity)
5658
{
57-
_prototype = engine.Realm.Intrinsics.Array.PrototypeObject;
59+
_constructor = engine.Realm.Intrinsics.Array;
60+
_prototype = _constructor.PrototypeObject;
5861

5962
if (capacity > 0 && capacity > engine.Options.Constraints.MaxArraySize)
6063
{
@@ -67,7 +70,7 @@ private void InitializePrototypeAndValidateCapacity(Engine engine, uint capacity
6770
public sealed override bool IsArray() => true;
6871

6972
internal sealed override bool HasOriginalIterator
70-
=> ReferenceEquals(Get(GlobalSymbolRegistry.Iterator), _engine.Realm.Intrinsics.Array.PrototypeObject._originalIteratorFunction);
73+
=> ReferenceEquals(Get(GlobalSymbolRegistry.Iterator), _constructor?.PrototypeObject._originalIteratorFunction);
7174

7275
/// <summary>
7376
/// Checks whether there have been changes to object prototype chain which could render fast access patterns impossible.
@@ -83,7 +86,7 @@ internal bool CanUseFastAccess
8386
}
8487

8588
if (_prototype is not ArrayPrototype arrayPrototype
86-
|| !ReferenceEquals(_prototype, _engine.Realm.Intrinsics.Array.PrototypeObject))
89+
|| !ReferenceEquals(_prototype, _constructor?.PrototypeObject))
8790
{
8891
// somebody has switched prototype
8992
return false;
@@ -96,7 +99,7 @@ internal bool CanUseFastAccess
9699
}
97100

98101
if (arrayPrototype.Prototype is not ObjectPrototype arrayPrototypePrototype
99-
|| !ReferenceEquals(arrayPrototypePrototype, _engine.Realm.Intrinsics.Array.PrototypeObject.Prototype))
102+
|| !ReferenceEquals(arrayPrototypePrototype, _constructor.PrototypeObject.Prototype))
100103
{
101104
return false;
102105
}
@@ -177,7 +180,7 @@ private bool DefineLength(PropertyDescriptor desc)
177180
{
178181
for (uint keyIndex = 0; keyIndex < _dense.Length; ++keyIndex)
179182
{
180-
if (_dense[keyIndex] == null)
183+
if (_dense[keyIndex] is null)
181184
{
182185
continue;
183186
}
@@ -284,15 +287,10 @@ private bool DefineOwnProperty(uint index, PropertyDescriptor desc)
284287
}
285288

286289
[MethodImpl(MethodImplOptions.AggressiveInlining)]
287-
internal uint GetLength()
288-
{
289-
if (_length is null)
290-
{
291-
return 0;
292-
}
290+
internal uint GetLength() => (uint) GetJsNumberLength()._value;
293291

294-
return (uint) ((JsNumber) _length._value!)._value;
295-
}
292+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
293+
private JsNumber GetJsNumberLength() => _length is null ? JsNumber.PositiveZero : (JsNumber) _length._value!;
296294

297295
protected sealed override void AddProperty(JsValue property, PropertyDescriptor descriptor)
298296
{
@@ -330,7 +328,7 @@ public sealed override List<JsValue> GetOwnPropertyKeys(Types types = Types.None
330328
var length = System.Math.Min(temp.Length, GetLength());
331329
for (var i = 0; i < length; i++)
332330
{
333-
if (temp[i] != null)
331+
if (temp[i] is not null)
334332
{
335333
properties.Add(JsString.Create(i));
336334
}
@@ -380,7 +378,7 @@ public sealed override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> Ge
380378
for (uint i = 0; i < length; i++)
381379
{
382380
var value = temp[i];
383-
if (value != null)
381+
if (value is not null)
384382
{
385383
if (_sparse is null || !_sparse.TryGetValue(i, out var descriptor) || descriptor is null)
386384
{
@@ -633,17 +631,19 @@ private void EnsureCorrectLength(uint index)
633631
}
634632

635633
[MethodImpl(MethodImplOptions.AggressiveInlining)]
636-
internal void SetLength(ulong length)
634+
internal void SetLength(ulong length) => SetLength(JsNumber.Create(length));
635+
636+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
637+
internal void SetLength(JsNumber length)
637638
{
638-
var number = JsNumber.Create(length);
639639
if (Extensible && _length!._flags == PropertyFlag.OnlyWritable)
640640
{
641-
_length!.Value = number;
641+
_length!.Value = length;
642642
}
643643
else
644644
{
645645
// slow path
646-
Set(CommonProperties.Length, number, true);
646+
Set(CommonProperties.Length, length, true);
647647
}
648648
}
649649

@@ -677,33 +677,44 @@ internal bool DeletePropertyOrThrow(uint index)
677677
return true;
678678
}
679679

680-
internal bool Delete(uint index)
680+
private bool Delete(uint index) => Delete(index, unwrapFromNonDataDescriptor: false, out _);
681+
682+
private bool Delete(uint index, bool unwrapFromNonDataDescriptor, out JsValue? deletedValue)
681683
{
684+
TryGetDescriptor(index, createIfMissing: false, out var desc);
685+
682686
// check fast path
683687
var temp = _dense;
684688
if (temp != null)
685689
{
686690
if (index < (uint) temp.Length)
687691
{
688-
if (!TryGetDescriptor(index, createIfMissing: false, out var descriptor) || descriptor.Configurable)
692+
if (desc is null || desc.Configurable)
689693
{
694+
deletedValue = temp[index];
690695
temp[index] = null;
691696
return true;
692697
}
693698
}
694699
}
695700

696-
if (!TryGetDescriptor(index, createIfMissing: false, out var desc))
701+
if (desc is null)
697702
{
703+
deletedValue = null;
698704
return true;
699705
}
700706

701707
if (desc.Configurable)
702708
{
703-
DeleteAt(index);
709+
_sparse!.Remove(index);
710+
deletedValue = desc.IsDataDescriptor() || unwrapFromNonDataDescriptor
711+
? UnwrapJsValue(desc)
712+
: null;
713+
704714
return true;
705715
}
706716

717+
deletedValue = null;
707718
return false;
708719
}
709720

@@ -728,21 +739,23 @@ internal bool DeleteAt(uint index)
728739

729740
private bool TryGetDescriptor(uint index, bool createIfMissing, [NotNullWhen(true)] out PropertyDescriptor? descriptor)
730741
{
742+
if (!createIfMissing && _sparse is null)
743+
{
744+
descriptor = null;
745+
return false;
746+
}
747+
731748
descriptor = null;
732749
var temp = _dense;
733750
if (temp != null)
734751
{
735752
if (index < (uint) temp.Length)
736753
{
737754
var value = temp[index];
738-
if (value != null)
755+
if (value is not null)
739756
{
740757
if (_sparse is null || !_sparse.TryGetValue(index, out descriptor) || descriptor is null)
741758
{
742-
if (!createIfMissing)
743-
{
744-
return false;
745-
}
746759
_sparse ??= new Dictionary<uint, PropertyDescriptor?>();
747760
_sparse[index] = descriptor = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
748761
}
@@ -913,7 +926,7 @@ private void ConvertToSparse()
913926
for (uint i = 0; i < (uint) temp.Length; ++i)
914927
{
915928
var value = temp[i];
916-
if (value != null)
929+
if (value is not null)
917930
{
918931
_sparse.TryGetValue(i, out var descriptor);
919932
descriptor ??= new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
@@ -1004,7 +1017,7 @@ private IEnumerable<IndexedEntry> Enumerate()
10041017
for (var i = 0; i < length; i++)
10051018
{
10061019
var value = temp[i];
1007-
if (value != null)
1020+
if (value is not null)
10081021
{
10091022
yield return new IndexedEntry(i, value);
10101023
}
@@ -1107,6 +1120,27 @@ public uint Push(JsValue[] values)
11071120
return (uint) n;
11081121
}
11091122

1123+
public JsValue Pop()
1124+
{
1125+
var len = GetJsNumberLength();
1126+
if (JsNumber.PositiveZero.Equals(len))
1127+
{
1128+
SetLength(len);
1129+
return Undefined;
1130+
}
1131+
1132+
var newLength = (uint) len._value - 1;
1133+
1134+
if (!Delete(newLength, unwrapFromNonDataDescriptor: true, out var element))
1135+
{
1136+
ExceptionHelper.ThrowTypeError(_engine.Realm);
1137+
}
1138+
1139+
SetLength(newLength);
1140+
1141+
return element ?? Undefined;
1142+
}
1143+
11101144
private bool CanSetLength()
11111145
{
11121146
if (!_length!.IsAccessorDescriptor())
@@ -1138,7 +1172,7 @@ internal JsArray Map(JsValue[] arguments)
11381172
var len = GetLength();
11391173

11401174
var callable = GetCallable(callbackfn);
1141-
var a = Engine.Realm.Intrinsics.Array.ArrayCreate(len);
1175+
var a = _engine.Realm.Intrinsics.Array.ArrayCreate(len);
11421176
var args = _engine._jsValueArrayPool.RentArray(3);
11431177
args[2] = this;
11441178
for (uint k = 0; k < len; k++)
@@ -1306,7 +1340,7 @@ internal void CopyValues(JsArray source, uint sourceStartIndex, uint targetStart
13061340
for (var i = sourceStartIndex; i < sourceStartIndex + length; ++i, j++)
13071341
{
13081342
JsValue? sourceValue;
1309-
if (i < (uint) sourceDense.Length && sourceDense[i] != null)
1343+
if (i < (uint) sourceDense.Length && sourceDense[i] is not null)
13101344
{
13111345
sourceValue = sourceDense[i];
13121346
}

Jint/Native/Array/ArrayPrototype.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1633,6 +1633,11 @@ public JsValue Push(JsValue thisObject, JsValue[] arguments)
16331633

16341634
public JsValue Pop(JsValue thisObject, JsValue[] arguments)
16351635
{
1636+
if (thisObject is JsArray { CanUseFastAccess: true } array)
1637+
{
1638+
return array.Pop();
1639+
}
1640+
16361641
var o = ArrayOperations.For(_realm, thisObject);
16371642
ulong len = o.GetLongLength();
16381643
if (len == 0)
@@ -1641,7 +1646,7 @@ public JsValue Pop(JsValue thisObject, JsValue[] arguments)
16411646
return Undefined;
16421647
}
16431648

1644-
len = len - 1;
1649+
len -= 1;
16451650
JsValue element = o.Get(len);
16461651
o.DeletePropertyOrThrow(len);
16471652
o.SetLength(len);

0 commit comments

Comments
 (0)