@@ -5318,6 +5318,58 @@ namespace Js
5318
5318
JS_REENTRANT_UNLOCK (jsReentLock, return JavascriptArray::ReverseHelper (pArr, nullptr , obj, length.GetBigIndex (), scriptContext));
5319
5319
}
5320
5320
5321
+ bool JavascriptArray::HasAnyES5ArrayInPrototypeChain (JavascriptArray *arr, bool forceCheckProtoChain)
5322
+ {
5323
+ Assert (arr != nullptr );
5324
+
5325
+ #if ENABLE_COPYONACCESS_ARRAY
5326
+ JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(arr);
5327
+ #endif
5328
+
5329
+ bool hasAnyES5Array = false ;
5330
+
5331
+ // If there is no gap (unless forced) we are not filling from the prototype - so no point checking for ES5Array.
5332
+ if (forceCheckProtoChain || arr->IsFillFromPrototypes ())
5333
+ {
5334
+ RecyclableObject* prototype = arr->GetPrototype ();
5335
+
5336
+ while (JavascriptOperators::GetTypeId (prototype) != TypeIds_Null)
5337
+ {
5338
+ RecyclableObject* protoObj = prototype;
5339
+
5340
+ if (!(DynamicObject::IsAnyArray (protoObj) || JavascriptOperators::IsObject (protoObj))
5341
+ || JavascriptProxy::Is (protoObj)
5342
+ || protoObj->IsExternal ())
5343
+ {
5344
+ hasAnyES5Array = true ;
5345
+ break ;
5346
+ }
5347
+
5348
+ if (DynamicObject::IsAnyArray (protoObj))
5349
+ {
5350
+ if (ES5Array::Is (protoObj))
5351
+ {
5352
+ hasAnyES5Array = true ;
5353
+ break ;
5354
+ }
5355
+ }
5356
+ else if (DynamicType::Is (protoObj->GetTypeId ()))
5357
+ {
5358
+ DynamicObject* dynobj = DynamicObject::FromVar (protoObj);
5359
+ ArrayObject* objectArray = dynobj->GetObjectArray ();
5360
+ if (objectArray != nullptr && ES5Array::Is (objectArray))
5361
+ {
5362
+ hasAnyES5Array = true ;
5363
+ break ;
5364
+ }
5365
+ }
5366
+
5367
+ prototype = prototype->GetPrototype ();
5368
+ }
5369
+ }
5370
+ return hasAnyES5Array;
5371
+ }
5372
+
5321
5373
// Array.prototype.reverse as described in ES6.0 (draft 22) Section 22.1.3.20
5322
5374
template <typename T>
5323
5375
Var JavascriptArray::ReverseHelper (JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, ScriptContext* scriptContext)
@@ -5347,7 +5399,9 @@ namespace Js
5347
5399
5348
5400
ThrowTypeErrorOnFailureHelper h (scriptContext, methodName);
5349
5401
5350
- if (pArr)
5402
+ bool useNoSideEffectReverse = pArr != nullptr && !HasAnyES5ArrayInPrototypeChain (pArr);
5403
+
5404
+ if (useNoSideEffectReverse)
5351
5405
{
5352
5406
Recycler * recycler = scriptContext->GetRecycler ();
5353
5407
@@ -5373,6 +5427,12 @@ namespace Js
5373
5427
}
5374
5428
}
5375
5429
5430
+ // As we have already established that the FillFromPrototype should not change the bound of the array.
5431
+ if (length != (T)pArr->length )
5432
+ {
5433
+ Js::Throw::FatalInternalError ();
5434
+ }
5435
+
5376
5436
if (pArr->HasNoMissingValues () && pArr->head && pArr->head ->next )
5377
5437
{
5378
5438
// This function currently does not track missing values in the head segment if there are multiple segments
@@ -5679,23 +5739,33 @@ namespace Js
5679
5739
{
5680
5740
return res;
5681
5741
}
5682
- if (JavascriptArray::Is (args[0 ]))
5742
+
5743
+ bool useNoSideEffectShift = JavascriptArray::Is (args[0 ])
5744
+ && !JavascriptArray::FromVar (args[0 ])->IsCrossSiteObject ()
5745
+ && !HasAnyES5ArrayInPrototypeChain (JavascriptArray::FromVar (args[0 ]));
5746
+
5747
+ if (useNoSideEffectShift)
5683
5748
{
5684
5749
JavascriptArray * pArr = JavascriptArray::FromVar (args[0 ]);
5685
- #if ENABLE_COPYONACCESS_ARRAY
5686
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(pArr);
5687
- #endif
5688
5750
5689
5751
if (pArr->length == 0 )
5690
5752
{
5691
5753
return res;
5692
5754
}
5693
5755
5756
+ uint32 length = pArr->length ;
5757
+
5694
5758
if (pArr->IsFillFromPrototypes ())
5695
5759
{
5696
5760
JS_REENTRANT (jsReentLock, pArr->FillFromPrototypes (0 , pArr->length )); // We need find all missing value from [[proto]] object
5697
5761
}
5698
5762
5763
+ // As we have already established that the FillFromPrototype should not change the bound of the array.
5764
+ if (length != pArr->length )
5765
+ {
5766
+ Js::Throw::FatalInternalError ();
5767
+ }
5768
+
5699
5769
if (pArr->HasNoMissingValues () && pArr->head && pArr->head ->next )
5700
5770
{
5701
5771
// This function currently does not track missing values in the head segment if there are multiple segments
@@ -6137,6 +6207,11 @@ namespace Js
6137
6207
6138
6208
if (pArr)
6139
6209
{
6210
+ if (HasAnyES5ArrayInPrototypeChain (pArr))
6211
+ {
6212
+ JS_REENTRANT_UNLOCK (jsReentLock, return JavascriptArray::SliceObjectHelper (obj, start, 0u , newArr, newObj, newLen, scriptContext));
6213
+ }
6214
+
6140
6215
// If we constructed a new Array object, we have some nice helpers here
6141
6216
if (newArr && isBuiltinArrayCtor)
6142
6217
{
@@ -6662,24 +6737,32 @@ namespace Js
6662
6737
}
6663
6738
}
6664
6739
6665
- if (JavascriptArray::Is (args[0 ]))
6666
- {
6667
- #if ENABLE_COPYONACCESS_ARRAY
6668
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[0 ]);
6669
- #endif
6740
+ bool useNoSideEffectSort = JavascriptArray::Is (args[0 ])
6741
+ && !JavascriptArray::FromVar (args[0 ])->IsCrossSiteObject ()
6742
+ && !HasAnyES5ArrayInPrototypeChain (JavascriptArray::FromVar (args[0 ]));
6670
6743
6744
+ if (useNoSideEffectSort)
6745
+ {
6671
6746
JavascriptArray *arr = JavascriptArray::FromVar (args[0 ]);
6672
6747
6673
6748
if (arr->length <= 1 )
6674
6749
{
6675
6750
return args[0 ];
6676
6751
}
6677
6752
6753
+ uint32 length = arr->length ;
6754
+
6678
6755
if (arr->IsFillFromPrototypes ())
6679
6756
{
6680
6757
JS_REENTRANT (jsReentLock, arr->FillFromPrototypes (0 , arr->length )); // We need find all missing value from [[proto]] object
6681
6758
}
6682
6759
6760
+ // As we have already established that the FillFromPrototype should not change the bound of the array.
6761
+ if (length != arr->length )
6762
+ {
6763
+ Js::Throw::FatalInternalError ();
6764
+ }
6765
+
6683
6766
// Maintain nativity of the array only for the following cases (To favor inplace conversions - keeps the conversion cost less):
6684
6767
// - int cases for X86 and
6685
6768
// - FloatArray for AMD64
@@ -7677,22 +7760,38 @@ namespace Js
7677
7760
{
7678
7761
return res;
7679
7762
}
7680
- if (JavascriptArray::Is (args[0 ]) && !JavascriptArray::FromVar (args[0 ])->IsCrossSiteObject ())
7763
+
7764
+ JavascriptArray * pArr = nullptr ;
7765
+ if (JavascriptArray::Is (args[0 ])
7766
+ && !JavascriptArray::FromVar (args[0 ])->IsCrossSiteObject ())
7681
7767
{
7682
7768
#if ENABLE_COPYONACCESS_ARRAY
7683
7769
JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[0 ]);
7684
7770
#endif
7685
- JavascriptArray * pArr = JavascriptArray::FromVar (args[0 ]);
7771
+ pArr = JavascriptArray::FromVar (args[0 ]);
7772
+ }
7686
7773
7687
7774
uint32 unshiftElements = args.Info .Count - 1 ;
7688
7775
7776
+ // forceCheckProtoChain - since the array expand to accommodate new items thus we have to check if we have accessor on the proto chain.
7777
+ bool useNoSideEffectUnshift = pArr != nullptr && (unshiftElements == 0 || !HasAnyES5ArrayInPrototypeChain (pArr, true /* forceCheckProtoChain*/ ));
7778
+
7779
+ if (useNoSideEffectUnshift)
7780
+ {
7689
7781
if (unshiftElements > 0 )
7690
7782
{
7783
+ uint32 length = pArr->length ;
7691
7784
if (pArr->IsFillFromPrototypes ())
7692
7785
{
7693
7786
JS_REENTRANT (jsReentLock, pArr->FillFromPrototypes (0 , pArr->length )); // We need find all missing value from [[proto]] object
7694
7787
}
7695
7788
7789
+ // As we have already established that the FillFromPrototype should not change the bound of the array.
7790
+ if (length != pArr->length )
7791
+ {
7792
+ Js::Throw::FatalInternalError ();
7793
+ }
7794
+
7696
7795
// Pre-process: truncate overflowing elements to properties
7697
7796
bool newLenOverflowed = false ;
7698
7797
uint32 maxLen = MaxArrayLength - unshiftElements;
@@ -7795,7 +7894,6 @@ namespace Js
7795
7894
}
7796
7895
7797
7896
JS_REENTRANT (jsReentLock, BigIndex length = OP_GetLength (dynamicObject, scriptContext));
7798
- uint32 unshiftElements = args.Info .Count - 1 ;
7799
7897
if (unshiftElements > 0 )
7800
7898
{
7801
7899
uint32 MaxSpaceUint32 = MaxArrayLength - unshiftElements;
0 commit comments